[PORTCLS]
[reactos.git] / reactos / drivers / wdm / audio / backpln / portcls / irpstream.cpp
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/backpln/portcls/irpstream.cpp
5 * PURPOSE: IRP Stream handling
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9 #include "private.hpp"
10
11
12 class CIrpQueue : public IIrpQueue
13 {
14 public:
15 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
16
17 STDMETHODIMP_(ULONG) AddRef()
18 {
19 InterlockedIncrement(&m_Ref);
20 return m_Ref;
21 }
22 STDMETHODIMP_(ULONG) Release()
23 {
24 InterlockedDecrement(&m_Ref);
25
26 if (!m_Ref)
27 {
28 delete this;
29 return 0;
30 }
31 return m_Ref;
32 }
33 IMP_IIrpQueue;
34 CIrpQueue(IUnknown *OuterUnknown){}
35 virtual ~CIrpQueue(){}
36
37 protected:
38 volatile ULONG m_CurrentOffset;
39 LONG m_NumMappings;
40 ULONG m_NumDataAvailable;
41 PKSPIN_CONNECT m_ConnectDetails;
42 KSPIN_LOCK m_IrpListLock;
43 LIST_ENTRY m_IrpList;
44 LIST_ENTRY m_FreeIrpList;
45 PIRP m_Irp;
46
47 ULONG m_OutOfMapping;
48 ULONG m_MaxFrameSize;
49 ULONG m_Alignment;
50
51 LONG m_Ref;
52
53 };
54
55 #define OFFSET_HEADERINDEX (0)
56 #define OFFSET_STREAMHEADER (2)
57 #define OFFSET_HEADERCOUNT (3)
58
59
60 #define STREAMHEADER_INDEX(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX]))
61 #define STREAMHEADER_COUNT(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT]))
62 #define STREAMHEADER_CURRENT(Irp) (Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER])
63
64
65 NTSTATUS
66 NTAPI
67 CIrpQueue::QueryInterface(
68 IN REFIID refiid,
69 OUT PVOID* Output)
70 {
71 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
72 {
73 *Output = PVOID(PUNKNOWN(this));
74 PUNKNOWN(*Output)->AddRef();
75 return STATUS_SUCCESS;
76 }
77
78 return STATUS_UNSUCCESSFUL;
79 }
80
81 NTSTATUS
82 NTAPI
83 CIrpQueue::Init(
84 IN KSPIN_CONNECT *ConnectDetails,
85 IN ULONG FrameSize,
86 IN ULONG Alignment)
87 {
88 m_ConnectDetails = ConnectDetails;
89 m_MaxFrameSize = FrameSize;
90 m_Alignment = Alignment;
91
92 InitializeListHead(&m_IrpList);
93 InitializeListHead(&m_FreeIrpList);
94 KeInitializeSpinLock(&m_IrpListLock);
95
96 return STATUS_SUCCESS;
97 }
98
99 NTSTATUS
100 NTAPI
101 CIrpQueue::AddMapping(
102 IN PIRP Irp,
103 OUT PULONG Data)
104 {
105 PKSSTREAM_HEADER Header;
106 NTSTATUS Status = STATUS_SUCCESS;
107 PIO_STACK_LOCATION IoStack;
108 ULONG NumHeaders, NumData, Index;
109 PMDL Mdl;
110
111 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
112
113 // get current irp stack location
114 IoStack = IoGetCurrentIrpStackLocation(Irp);
115
116 if (!Irp->MdlAddress)
117 {
118 // ioctl from KsStudio
119 // Wdmaud already probes buffers, therefore no need to probe it again
120 // probe the stream irp
121 if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_WRITE_STREAM)
122 Status = KsProbeStreamIrp(Irp, KSSTREAM_WRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
123 else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_READ_STREAM)
124 Status = KsProbeStreamIrp(Irp, KSSTREAM_READ | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
125 else
126 PC_ASSERT(0);
127
128 // check for success
129 if (!NT_SUCCESS(Status))
130 {
131 DPRINT("KsProbeStreamIrp failed with %x\n", Status);
132 return Status;
133 }
134 }
135
136 // get first stream header
137
138 if (Irp->RequestorMode == UserMode)
139 Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
140 else
141 Header = (PKSSTREAM_HEADER)Irp->UserBuffer;
142
143 // sanity check
144 PC_ASSERT(Header);
145
146 // calculate num headers
147 NumHeaders = IoStack->Parameters.DeviceIoControl.OutputBufferLength / Header->Size;
148
149 // assume headers of same length
150 PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength % Header->Size == 0);
151
152
153 // get first audio buffer
154 Mdl = Irp->MdlAddress;
155 // sanity check
156 PC_ASSERT(Mdl);
157
158 // store the current stream header
159 Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER] = (PVOID)Header;
160 // store header count
161 Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT] = UlongToPtr(NumHeaders);
162
163 // store current header index
164 Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
165
166 NumData = 0;
167 // prepare all headers
168 for(Index = 0; Index < NumHeaders; Index++)
169 {
170 // sanity checks
171 PC_ASSERT(Header);
172 PC_ASSERT(Mdl);
173
174 if (Irp->RequestorMode == UserMode)
175 {
176 Header->Data = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
177 }
178
179 if (!Header->Data)
180 {
181 // insufficient resources
182 ExFreePool(Irp->AssociatedIrp.SystemBuffer);
183 Irp->AssociatedIrp.SystemBuffer = NULL;
184 // complete and forget request
185 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
186 Irp->IoStatus.Information = 0;
187
188 IoCompleteRequest(Irp, IO_NO_INCREMENT);
189 return STATUS_INSUFFICIENT_RESOURCES;
190 }
191
192 // increment num mappings
193 InterlockedIncrement(&m_NumMappings);
194
195 // increment available data
196 InterlockedExchangeAdd((PLONG)&m_NumDataAvailable,
197 (max(Header->DataUsed, Header->FrameExtent)));
198
199 NumData += max(Header->DataUsed, Header->FrameExtent);
200
201 // move to next header
202 Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
203
204 // move to next mdl
205 Mdl = Mdl->Next;
206 }
207
208 DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders, NumData, m_MaxFrameSize, m_NumDataAvailable);
209 *Data = NumData;
210
211 // mark irp as pending
212 IoMarkIrpPending(Irp);
213
214 // add irp to cancelable queue
215 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
216
217 // disable mapping failed status
218 m_OutOfMapping = FALSE;
219
220 // done
221 return Status;
222 }
223
224 NTSTATUS
225 NTAPI
226 CIrpQueue::GetMapping(
227 OUT PUCHAR * Buffer,
228 OUT PULONG BufferSize)
229 {
230 PIRP Irp;
231 ULONG Offset;
232 //PIO_STACK_LOCATION IoStack;
233 PKSSTREAM_HEADER StreamHeader;
234
235 // check if there is an irp in the partially processed
236 if (m_Irp)
237 {
238 // use last irp
239 if (m_Irp->Cancel == FALSE)
240 {
241 Irp = m_Irp;
242 Offset = m_CurrentOffset;
243 }
244 else
245 {
246 // irp has been cancelled
247 m_Irp->IoStatus.Status = STATUS_CANCELLED;
248 IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
249 m_Irp = Irp = NULL;
250 }
251 }
252 else
253 {
254 // get a fresh new irp from the queue
255 m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
256 m_CurrentOffset = Offset = 0;
257 }
258
259 if (!Irp)
260 {
261 // no irp buffer available
262 DPRINT("NoIrp\n");
263 return STATUS_UNSUCCESSFUL;
264 }
265
266 // get stream header
267 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
268
269 // sanity check
270 PC_ASSERT(StreamHeader);
271
272 // store buffersize
273 if (StreamHeader->DataUsed)
274 *BufferSize = StreamHeader->DataUsed - Offset;
275 else
276 *BufferSize = StreamHeader->FrameExtent - Offset;
277
278 PC_ASSERT(*BufferSize);
279
280 // store buffer
281 *Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
282
283 // unset flag that no irps are available
284 m_OutOfMapping = FALSE;
285
286 return STATUS_SUCCESS;
287 }
288
289 VOID
290 NTAPI
291 CIrpQueue::UpdateMapping(
292 IN ULONG BytesWritten)
293 {
294 PKSSTREAM_HEADER StreamHeader;
295 ULONG Size, NumData, Index;
296
297 if (!m_Irp)
298 {
299 // silence buffer was used
300 return;
301 }
302
303 // get stream header
304 StreamHeader = (PKSSTREAM_HEADER)STREAMHEADER_CURRENT(m_Irp);
305
306 // sanity check
307 // ASSERT(StreamHeader);
308
309 // add to current offset
310 InterlockedExchangeAdd((volatile PLONG)&m_CurrentOffset, (LONG)BytesWritten);
311
312 // decrement available data counter
313 m_NumDataAvailable -= BytesWritten;
314
315 if (StreamHeader->DataUsed)
316 Size = StreamHeader->DataUsed;
317 else
318 Size = StreamHeader->FrameExtent;
319
320 PC_ASSERT(Size);
321
322 if (m_CurrentOffset >= Size)
323 {
324 if (STREAMHEADER_INDEX(m_Irp) + 1 < STREAMHEADER_COUNT(m_Irp))
325 {
326 // the irp has at least one more stream header
327 m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(STREAMHEADER_INDEX(m_Irp) + 1);
328
329 // get next stream header
330 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
331
332 // store next stream header
333 STREAMHEADER_CURRENT(m_Irp) = (PVOID)StreamHeader;
334
335 // reset current offset
336 m_CurrentOffset = 0;
337
338 // done
339 return;
340 }
341
342 // irp has been processed completly
343 NumData = 0;
344 if (m_Irp->RequestorMode == KernelMode)
345 StreamHeader = (PKSSTREAM_HEADER)m_Irp->UserBuffer;
346 else
347 StreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
348
349 // loop all stream headers
350 for(Index = 0; Index < STREAMHEADER_COUNT(m_Irp); Index++)
351 {
352 PC_ASSERT(StreamHeader);
353
354 // add size of buffer
355 // depends on if the buffer is input / output
356 if (StreamHeader->DataUsed)
357 Size = StreamHeader->DataUsed;
358 else
359 Size = StreamHeader->FrameExtent;
360
361 // increment size
362 NumData += Size;
363
364 // get next stream header
365 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
366 }
367
368 if (m_ConnectDetails->Interface.Id == KSINTERFACE_STANDARD_LOOPED_STREAMING)
369 {
370 // looped streaming repeat the buffers untill
371 // the caller decides to stop the streams
372
373 // reset stream header index
374 m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
375 // re-insert irp
376 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
377 // clear current irp
378 m_Irp = NULL;
379 // reset offset
380 m_CurrentOffset = 0;
381 // increment available data
382 InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, NumData);
383 // done
384 return;
385 }
386
387 // store operation status
388 m_Irp->IoStatus.Status = STATUS_SUCCESS;
389 m_Irp->IoStatus.Information = 0;
390
391 // complete the request
392 IoCompleteRequest(m_Irp, IO_SOUND_INCREMENT);
393
394 // remove irp as it is complete
395 m_Irp = NULL;
396
397 // reset offset
398 m_CurrentOffset = 0;
399 }
400 }
401
402 ULONG
403 NTAPI
404 CIrpQueue::NumData()
405 {
406 // returns the amount of audio stream data available
407 return m_NumDataAvailable;
408 }
409
410 BOOL
411 NTAPI
412 CIrpQueue::CancelBuffers()
413 {
414 // is there an active irp
415 if (m_Irp)
416 {
417 // re-insert it to cancelable queue
418 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
419 //set it to zero
420 m_Irp = NULL;
421 }
422
423 // cancel all irps
424 KsCancelIo(&m_IrpList, &m_IrpListLock);
425 // reset number of mappings
426 m_NumMappings = 0;
427 // reset number of data available
428 m_NumDataAvailable = 0;
429
430 // done
431 return TRUE;
432 }
433
434 NTSTATUS
435 NTAPI
436 CIrpQueue::GetMappingWithTag(
437 IN PVOID Tag,
438 OUT PPHYSICAL_ADDRESS PhysicalAddress,
439 OUT PVOID *VirtualAddress,
440 OUT PULONG ByteCount,
441 OUT PULONG Flags)
442 {
443 PKSSTREAM_HEADER StreamHeader;
444 PIRP Irp;
445
446 *Flags = 0;
447 PC_ASSERT(Tag != NULL);
448
449 // get an irp from the queue
450 Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
451
452 // check if there is an irp
453 if (!Irp)
454 {
455 // no irp available
456 m_OutOfMapping = TRUE;
457 return STATUS_NOT_FOUND;
458 }
459
460 //FIXME support more than one stream header
461 PC_ASSERT(STREAMHEADER_COUNT(Irp) == 1);
462
463 // HACK get stream header
464 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
465
466 // store mapping in the free list
467 ExInterlockedInsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry, &m_IrpListLock);
468
469 // return mapping
470 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
471 *VirtualAddress = StreamHeader->Data;
472 *ByteCount = StreamHeader->DataUsed;
473
474 // decrement mapping count
475 InterlockedDecrement(&m_NumMappings);
476 // decrement num data available
477 m_NumDataAvailable -= StreamHeader->DataUsed;
478
479 // store tag in irp
480 Irp->Tail.Overlay.DriverContext[3] = Tag;
481
482 // done
483 return STATUS_SUCCESS;
484 }
485
486 NTSTATUS
487 NTAPI
488 CIrpQueue::ReleaseMappingWithTag(
489 IN PVOID Tag)
490 {
491 PIRP Irp;
492 PLIST_ENTRY CurEntry;
493 PKSSTREAM_HEADER StreamHeader;
494
495 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag);
496
497 // remove irp from used list
498 CurEntry = ExInterlockedRemoveHeadList(&m_FreeIrpList, &m_IrpListLock);
499 // sanity check
500 PC_ASSERT(CurEntry);
501
502 // get irp from list entry
503 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
504
505 // HACK get stream header
506 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
507
508 // driver must release items in the same order
509 PC_ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
510
511 // irp has been processed completly
512 Irp->IoStatus.Status = STATUS_SUCCESS;
513
514 // frame extend contains the original request size, DataUsed contains the real buffer size
515 // is different when kmixer performs channel conversion, upsampling etc
516
517 Irp->IoStatus.Information = StreamHeader->FrameExtent;
518
519 // complete the request
520 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
521
522 return STATUS_SUCCESS;
523 }
524
525 BOOLEAN
526 NTAPI
527 CIrpQueue::HasLastMappingFailed()
528 {
529 return m_OutOfMapping;
530 }
531
532 ULONG
533 NTAPI
534 CIrpQueue::GetCurrentIrpOffset()
535 {
536
537 return m_CurrentOffset;
538 }
539
540 BOOLEAN
541 NTAPI
542 CIrpQueue::GetAcquiredTagRange(
543 IN PVOID * FirstTag,
544 IN PVOID * LastTag)
545 {
546 KIRQL OldLevel;
547 BOOLEAN Ret = FALSE;
548 PIRP Irp;
549 PLIST_ENTRY CurEntry;
550
551 KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
552
553 if (!IsListEmpty(&m_FreeIrpList))
554 {
555 // get first entry
556 CurEntry = RemoveHeadList(&m_FreeIrpList);
557 // get irp from list entry
558 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
559
560 // get tag of first acquired buffer
561 *FirstTag = Irp->Tail.Overlay.DriverContext[3];
562
563 // put back irp
564 InsertHeadList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
565
566 // get last entry
567 CurEntry = RemoveTailList(&m_FreeIrpList);
568 // get irp from list entry
569 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
570
571 // get tag of first acquired buffer
572 *LastTag = Irp->Tail.Overlay.DriverContext[3];
573
574 // put back irp
575 InsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
576
577 // indicate success
578 Ret = TRUE;
579 }
580
581 // release lock
582 KeReleaseSpinLock(&m_IrpListLock, OldLevel);
583 // done
584 return Ret;
585 }
586
587 NTSTATUS
588 NTAPI
589 NewIrpQueue(
590 IN IIrpQueue **Queue)
591 {
592 CIrpQueue *This = new(NonPagedPool, TAG_PORTCLASS)CIrpQueue(NULL);
593 if (!This)
594 return STATUS_INSUFFICIENT_RESOURCES;
595
596 This->AddRef();
597
598 *Queue = (IIrpQueue*)This;
599 return STATUS_SUCCESS;
600 }
601