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 #define NDEBUG
12 #include <debug.h>
13 
14 static
15 PIRP
RemoveHeadList_IRP(IN OUT PLIST_ENTRY QueueHead)16 RemoveHeadList_IRP(
17     IN OUT PLIST_ENTRY QueueHead)
18 {
19     PIRP Irp;
20     PLIST_ENTRY CurEntry;
21 
22     for (CurEntry = QueueHead->Flink; CurEntry != QueueHead; CurEntry = CurEntry->Flink)
23     {
24         /* Get the IRP offset */
25         Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
26 
27         /* Remove the cancel routine */
28         if (IoSetCancelRoutine(Irp, NULL))
29         {
30             /* Remove the IRP from the list and return it */
31             RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
32             return Irp;
33         }
34     }
35 
36     /* no non canceled irp has been found */
37     return NULL;
38 }
39 class CIrpQueue : public CUnknownImpl<IIrpQueue>
40 {
41 public:
42     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
43 
44     IMP_IIrpQueue;
CIrpQueue(IUnknown * OuterUnknown)45     CIrpQueue(IUnknown *OuterUnknown){}
~CIrpQueue()46     virtual ~CIrpQueue(){}
47 
48 protected:
49 
50     PKSPIN_CONNECT m_ConnectDetails;
51     PKSPIN_DESCRIPTOR m_Descriptor;
52 
53     KSPIN_LOCK m_IrpListLock;
54     LIST_ENTRY m_IrpList;
55     LIST_ENTRY m_FreeIrpList;
56 
57     ULONG m_MaxFrameSize;
58     ULONG m_Alignment;
59     ULONG m_TagSupportEnabled;
60 
61     ULONG m_StreamHeaderIndex;
62     ULONG m_TagIndex;
63     PKSSTREAM_HEADER m_CurStreamHeader;
64 
65     ULONG m_CurrentOffset;
66     PIRP m_Irp;
67 };
68 
69 typedef struct
70 {
71     PVOID Tag;
72     UCHAR Used;
73 }KSSTREAM_TAG, *PKSSTREAM_TAG;
74 
75 typedef struct
76 {
77     ULONG StreamHeaderCount;
78     ULONG nTags;
79 
80     PVOID * Data;
81     PKSSTREAM_TAG Tags;
82 }KSSTREAM_DATA, *PKSSTREAM_DATA;
83 
84 #define STREAM_DATA_OFFSET   (0)
85 
86 NTSTATUS
87 NTAPI
QueryInterface(IN REFIID refiid,OUT PVOID * Output)88 CIrpQueue::QueryInterface(
89     IN  REFIID refiid,
90     OUT PVOID* Output)
91 {
92     if (IsEqualGUIDAligned(refiid, IID_IUnknown))
93     {
94         *Output = PVOID(PUNKNOWN(this));
95         PUNKNOWN(*Output)->AddRef();
96         return STATUS_SUCCESS;
97     }
98 
99     return STATUS_UNSUCCESSFUL;
100 }
101 
102 NTSTATUS
103 NTAPI
Init(IN PKSPIN_CONNECT ConnectDetails,IN PKSPIN_DESCRIPTOR Descriptor,IN ULONG FrameSize,IN ULONG Alignment,IN ULONG TagSupportEnabled)104 CIrpQueue::Init(
105     IN PKSPIN_CONNECT ConnectDetails,
106     IN PKSPIN_DESCRIPTOR Descriptor,
107     IN ULONG FrameSize,
108     IN ULONG Alignment,
109     IN ULONG TagSupportEnabled)
110 {
111     m_ConnectDetails = ConnectDetails;
112     m_Descriptor = Descriptor;
113     m_MaxFrameSize = FrameSize;
114     m_Alignment = Alignment;
115     m_TagSupportEnabled = TagSupportEnabled;
116 
117     InitializeListHead(&m_IrpList);
118     InitializeListHead(&m_FreeIrpList);
119     KeInitializeSpinLock(&m_IrpListLock);
120 
121     return STATUS_SUCCESS;
122 }
123 
124 NTSTATUS
125 NTAPI
AddMapping(IN PIRP Irp,OUT PULONG Data)126 CIrpQueue::AddMapping(
127     IN PIRP Irp,
128     OUT PULONG Data)
129 {
130     PKSSTREAM_HEADER Header;
131     NTSTATUS Status = STATUS_UNSUCCESSFUL;
132     PIO_STACK_LOCATION IoStack;
133     ULONG Index, Length;
134     PMDL Mdl;
135     PKSSTREAM_DATA StreamData;
136     LONG TotalStreamData;
137     LONG StreamPageCount;
138     LONG HeaderLength;
139 
140     PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
141 
142     // allocate stream data
143     StreamData = (PKSSTREAM_DATA)AllocateItem(NonPagedPool, sizeof(KSSTREAM_DATA), TAG_PORTCLASS);
144     if (!StreamData)
145     {
146         // not enough memory
147         return STATUS_INSUFFICIENT_RESOURCES;
148     }
149 
150     // get current irp stack location
151     IoStack = IoGetCurrentIrpStackLocation(Irp);
152 
153     // lets probe the irp
154     if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_WRITE_STREAM)
155     {
156         // probe IOCTL_KS_WRITE_STREAM
157         Status = KsProbeStreamIrp(Irp, KSSTREAM_WRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
158     }
159     else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_READ_STREAM)
160     {
161         // probe IOCTL_KS_READ_STREAM
162         Status = KsProbeStreamIrp(Irp, KSSTREAM_READ  | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
163     }
164 
165     // check for success
166     if (!NT_SUCCESS(Status))
167     {
168         // irp probing failed
169         FreeItem(StreamData, TAG_PORTCLASS);
170         return Status;
171     }
172 
173     // get first stream header
174     Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
175 
176     // sanity check
177     PC_ASSERT(Header);
178 
179     // first calculate the numbers of stream headers
180     Length = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
181     Mdl = Irp->MdlAddress;
182 
183     TotalStreamData = 0;
184     StreamPageCount = 0;
185 
186     do
187     {
188         /* subtract size */
189         Length -= Header->Size;
190 
191         /* increment header count */
192         StreamData->StreamHeaderCount++;
193 
194         if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN)
195         {
196             // irp sink
197             HeaderLength = Header->DataUsed;
198         }
199         else
200         {
201             // irp source
202             HeaderLength = Header->FrameExtent;
203         }
204 
205         // increment available data
206         TotalStreamData += HeaderLength;
207 
208         // append page count
209         StreamPageCount += ADDRESS_AND_SIZE_TO_SPAN_PAGES(
210                                MmGetMdlByteOffset(Mdl), HeaderLength);
211 
212         // move to next header / mdl
213         Mdl = Mdl->Next;
214         Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
215 
216     }while(Length);
217 
218     // sanity check
219     ASSERT(StreamData->StreamHeaderCount);
220 
221     // allocate array for storing the pointers of the data */
222     StreamData->Data = (PVOID*)AllocateItem(NonPagedPool, sizeof(PVOID) * StreamData->StreamHeaderCount, TAG_PORTCLASS);
223     if (!StreamData->Data)
224     {
225         // out of memory
226         FreeItem(StreamData, TAG_PORTCLASS);
227 
228         // done
229         return STATUS_INSUFFICIENT_RESOURCES;
230     }
231 
232     if (m_TagSupportEnabled)
233     {
234         // allocate array for storing the pointers of the data */
235         StreamData->Tags = (PKSSTREAM_TAG)AllocateItem(NonPagedPool, sizeof(KSSTREAM_TAG) * StreamPageCount, TAG_PORTCLASS);
236         if (!StreamData->Data)
237         {
238             // out of memory
239             FreeItem(StreamData->Data, TAG_PORTCLASS);
240             FreeItem(StreamData, TAG_PORTCLASS);
241 
242             // done
243             return STATUS_INSUFFICIENT_RESOURCES;
244         }
245     }
246 
247     // now get a system address for the user buffers
248     Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
249     Mdl = Irp->MdlAddress;
250 
251     for(Index = 0; Index < StreamData->StreamHeaderCount; Index++)
252     {
253         /* get system address */
254         StreamData->Data[Index] = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
255 
256         /* check for success */
257         if (!StreamData->Data[Index])
258         {
259             // out of resources
260             FreeItem(StreamData->Data, TAG_PORTCLASS);
261 
262             if (m_TagSupportEnabled)
263             {
264                 // free tag array
265                 FreeItem(StreamData->Tags, TAG_PORTCLASS);
266             }
267 
268             FreeItem(StreamData, TAG_PORTCLASS);
269             // done
270             return STATUS_INSUFFICIENT_RESOURCES;
271         }
272 
273         // move to next header / mdl
274         Mdl = Mdl->Next;
275         Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
276 
277     }
278 
279     // store stream data
280     Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET] = (PVOID)StreamData;
281 
282     *Data = TotalStreamData;
283 
284     // mark irp as pending
285     IoMarkIrpPending(Irp);
286 
287     // add irp to cancelable queue
288     KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
289 
290     // done
291     return STATUS_SUCCESS;
292 }
293 
294 NTSTATUS
295 NTAPI
GetMapping(OUT PUCHAR * Buffer,OUT PULONG BufferSize)296 CIrpQueue::GetMapping(
297     OUT PUCHAR * Buffer,
298     OUT PULONG BufferSize)
299 {
300     PIRP Irp;
301     ULONG Offset;
302     PKSSTREAM_DATA StreamData;
303 
304     // check if there is an irp in the partially processed
305     if (m_Irp)
306     {
307         // use last irp
308         if (m_Irp->Cancel == FALSE)
309         {
310             Irp = m_Irp;
311             Offset = m_CurrentOffset;
312         }
313         else
314         {
315             // irp has been cancelled
316             m_Irp->IoStatus.Status = STATUS_CANCELLED;
317             IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
318             m_Irp = Irp = NULL;
319             m_CurrentOffset = 0;
320         }
321     }
322     else
323     {
324         // get a fresh new irp from the queue
325         m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
326         m_CurrentOffset = Offset = 0;
327 
328         if (m_Irp)
329         {
330             // reset stream header index
331             m_StreamHeaderIndex = 0;
332 
333             // reset stream header
334             m_CurStreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
335         }
336     }
337 
338     if (!Irp)
339     {
340         // no irp buffer available
341         return STATUS_UNSUCCESSFUL;
342     }
343 
344     // get stream data
345     StreamData = (PKSSTREAM_DATA)Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
346 
347     // sanity check
348     PC_ASSERT(StreamData);
349 
350     // get buffer size
351     if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_IN)
352     {
353         // sink pin
354         *BufferSize = m_CurStreamHeader->DataUsed - Offset;
355     }
356     else
357     {
358         // source pin
359         *BufferSize = m_CurStreamHeader->FrameExtent - Offset;
360     }
361 
362     // sanity check
363     PC_ASSERT(*BufferSize);
364 
365     // store buffer
366     *Buffer = &((PUCHAR)StreamData->Data[m_StreamHeaderIndex])[Offset];
367 
368     return STATUS_SUCCESS;
369 }
370 
371 VOID
372 NTAPI
UpdateMapping(IN ULONG BytesWritten)373 CIrpQueue::UpdateMapping(
374     IN ULONG BytesWritten)
375 {
376     PKSSTREAM_DATA StreamData;
377     ULONG Size;
378     PIO_STACK_LOCATION IoStack;
379     ULONG Index;
380     PMDL Mdl;
381 
382     // sanity check
383     ASSERT(m_Irp);
384 
385     // get stream data
386     StreamData = (PKSSTREAM_DATA)m_Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
387 
388     // sanity check
389     ASSERT(StreamData);
390 
391     // add to current offset
392     m_CurrentOffset += BytesWritten;
393 
394     if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
395     {
396         // store written bytes (source pin)
397         m_CurStreamHeader->DataUsed += BytesWritten;
398     }
399 
400     // get audio buffer size
401     if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
402         Size = m_CurStreamHeader->FrameExtent;
403     else
404         Size = m_CurStreamHeader->DataUsed;
405 
406     // sanity check
407     PC_ASSERT(Size);
408 
409     if (m_CurrentOffset >= Size)
410     {
411         // sanity check
412         PC_ASSERT(Size == m_CurrentOffset);
413 
414         if (m_StreamHeaderIndex + 1 < StreamData->StreamHeaderCount)
415         {
416             // move to next stream header
417             m_CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)m_CurStreamHeader + m_CurStreamHeader->Size);
418 
419             // increment stream header index
420             m_StreamHeaderIndex++;
421 
422             // reset offset
423             m_CurrentOffset = 0;
424 
425             // done
426             return;
427         }
428 
429         //
430         // all stream buffers have been played
431         // check if this is a looped buffer
432         //
433         if (m_ConnectDetails->Interface.Id == KSINTERFACE_STANDARD_LOOPED_STREAMING)
434         {
435             // looped streaming repeat the buffers untill
436             // the caller decides to stop the streams
437 
438             // re-insert irp
439             KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
440 
441             // clear current irp
442             m_Irp = NULL;
443 
444             // reset offset
445             m_CurrentOffset = 0;
446 
447             // done
448             return;
449         }
450 
451         Mdl = m_Irp->MdlAddress;
452         for(Index = 0; Index < StreamData->StreamHeaderCount; Index++)
453         {
454             MmUnmapLockedPages(StreamData->Data[Index], Mdl);
455             Mdl = Mdl->Next;
456         }
457 
458         // free stream data array
459         FreeItem(StreamData->Data, TAG_PORTCLASS);
460 
461         if (m_TagSupportEnabled)
462         {
463             // free tag array
464             FreeItem(StreamData->Tags, TAG_PORTCLASS);
465         }
466 
467         // free stream data
468         FreeItem(StreamData, TAG_PORTCLASS);
469 
470         // get io stack
471         IoStack = IoGetCurrentIrpStackLocation(m_Irp);
472 
473         // store operation status
474         m_Irp->IoStatus.Status = STATUS_SUCCESS;
475 
476         // store operation length
477         m_Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
478 
479         // complete the request
480         IoCompleteRequest(m_Irp, IO_SOUND_INCREMENT);
481 
482         // remove irp as it is complete
483         m_Irp = NULL;
484 
485         // reset offset
486         m_CurrentOffset = 0;
487     }
488 }
489 
490 ULONG
491 NTAPI
NumData()492 CIrpQueue::NumData()
493 {
494     KIRQL OldLevel;
495     ULONG NumDataAvailable;
496     PLIST_ENTRY CurEntry;
497     PIRP Irp;
498     ULONG CurrentOffset;
499     ULONG StreamHeaderIndex;
500     PKSSTREAM_HEADER CurStreamHeader;
501     PKSSTREAM_DATA StreamData;
502     ULONG Size;
503 
504     KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
505 
506     NumDataAvailable = 0;
507     CurEntry = &m_IrpList;
508 
509     // current IRP state
510     Irp = m_Irp;
511     CurrentOffset = m_CurrentOffset;
512     StreamHeaderIndex = m_StreamHeaderIndex;
513     CurStreamHeader = m_CurStreamHeader;
514 
515     while (TRUE)
516     {
517         if (Irp != NULL)
518         {
519             // get stream data
520             StreamData = (PKSSTREAM_DATA)Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
521 
522             // loop over stream headers
523             for (; StreamHeaderIndex < StreamData->StreamHeaderCount; StreamHeaderIndex++)
524             {
525                 // get audio buffer size
526                 if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
527                     Size = CurStreamHeader->FrameExtent;
528                 else
529                     Size = CurStreamHeader->DataUsed;
530 
531                 // increment available data
532                 NumDataAvailable += Size - CurrentOffset;
533                 CurrentOffset = 0;
534 
535                 // move to next stream header
536                 CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)CurStreamHeader + CurStreamHeader->Size);
537             }
538         }
539 
540         /* iterate to next entry */
541         CurEntry = CurEntry->Flink;
542 
543         /* is the end of list reached */
544         if (CurEntry == &m_IrpList)
545             break;
546 
547         /* get irp offset */
548         Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
549 
550         // next IRP state
551         CurrentOffset = 0;
552         StreamHeaderIndex = 0;
553         CurStreamHeader = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
554     }
555 
556     KeReleaseSpinLock(&m_IrpListLock, OldLevel);
557     return NumDataAvailable;
558 }
559 
560 BOOL
561 NTAPI
CancelBuffers()562 CIrpQueue::CancelBuffers()
563 {
564     //TODO: own cancel routine
565 
566     // is there an active irp
567     if (m_Irp)
568     {
569         // re-insert it to cancelable queue
570         KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
571         //set it to zero
572         m_Irp = NULL;
573     }
574 
575     // cancel all irps
576     KsCancelIo(&m_IrpList, &m_IrpListLock);
577 
578     // done
579     return TRUE;
580 }
581 
582 NTSTATUS
583 NTAPI
GetMappingWithTag(IN PVOID Tag,OUT PPHYSICAL_ADDRESS PhysicalAddress,OUT PVOID * VirtualAddress,OUT PULONG ByteCount,OUT PULONG Flags)584 CIrpQueue::GetMappingWithTag(
585     IN PVOID Tag,
586     OUT PPHYSICAL_ADDRESS  PhysicalAddress,
587     OUT PVOID  *VirtualAddress,
588     OUT PULONG  ByteCount,
589     OUT PULONG  Flags)
590 {
591     PKSSTREAM_DATA StreamData;
592     KIRQL OldLevel;
593     ULONG Size;
594     LPBYTE Data;
595 
596     /* sanity checks */
597     PC_ASSERT(PhysicalAddress);
598     PC_ASSERT(VirtualAddress);
599     PC_ASSERT(ByteCount);
600     PC_ASSERT(Flags);
601 
602     KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
603 
604     if (!m_Irp)
605     {
606         // get an irp from the queue
607         m_Irp = RemoveHeadList_IRP(&m_IrpList);
608 
609         // check if there is an irp
610         if (!m_Irp)
611         {
612             // no irp available
613             KeReleaseSpinLock(&m_IrpListLock, OldLevel);
614 
615             DPRINT("GetMappingWithTag no mapping available\n");
616             return STATUS_NOT_FOUND;
617         }
618 
619         // reset offset
620         m_CurrentOffset = 0;
621 
622         // reset tag index
623         m_TagIndex = 0;
624 
625         // reset stream header index
626         m_StreamHeaderIndex = 0;
627 
628         // reset stream header
629         m_CurStreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
630     }
631 
632     // get stream data
633     StreamData = (PKSSTREAM_DATA)m_Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
634 
635     // sanity check
636     PC_ASSERT(m_StreamHeaderIndex < StreamData->StreamHeaderCount);
637 
638     // store tag in irp
639     StreamData->Tags[m_TagIndex].Tag = Tag;
640     StreamData->Tags[m_TagIndex].Used = TRUE;
641     m_TagIndex++;
642 
643     // get audio buffer size
644     if (m_Descriptor->DataFlow == KSPIN_DATAFLOW_OUT)
645         Size = m_CurStreamHeader->FrameExtent;
646     else
647         Size = m_CurStreamHeader->DataUsed;
648 
649     // sanity check
650     PC_ASSERT(Size);
651 
652     // setup mapping
653     Data = (LPBYTE)StreamData->Data[m_StreamHeaderIndex] + m_CurrentOffset;
654     *VirtualAddress = Data;
655 
656     // get byte count
657     *ByteCount = (LPBYTE)ROUND_TO_PAGES(Data+1)-Data;
658     if (*ByteCount > (Size - m_CurrentOffset))
659         *ByteCount = (Size - m_CurrentOffset);
660     m_CurrentOffset += *ByteCount;
661 
662     if (m_CurrentOffset >= Size)
663     {
664         // sanity check
665         PC_ASSERT(Size == m_CurrentOffset);
666 
667         // increment header index
668         m_StreamHeaderIndex++;
669 
670         if (m_StreamHeaderIndex == StreamData->StreamHeaderCount)
671         {
672             // last mapping
673             *Flags = 1;
674 
675             //
676             StreamData->nTags = m_TagIndex;
677 
678             // insert mapping into free list
679             InsertTailList(&m_FreeIrpList, &m_Irp->Tail.Overlay.ListEntry);
680 
681             // clear irp
682             m_Irp = NULL;
683 
684         }
685         else
686         {
687             // one more mapping in the irp
688             *Flags = 0;
689 
690             // move to next header
691             m_CurStreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)m_CurStreamHeader + m_CurStreamHeader->Size);
692         }
693     }
694 
695     // get physical address
696     *PhysicalAddress = MmGetPhysicalAddress(*VirtualAddress);
697 
698     KeReleaseSpinLock(&m_IrpListLock, OldLevel);
699 
700     DPRINT("GetMappingWithTag Tag %p Buffer %p Flags %lu ByteCount %lx\n", Tag, VirtualAddress, *Flags, *ByteCount);
701     // done
702     return STATUS_SUCCESS;
703 }
704 
705 NTSTATUS
706 NTAPI
ReleaseMappingWithTag(IN PVOID Tag)707 CIrpQueue::ReleaseMappingWithTag(
708     IN PVOID Tag)
709 {
710     PIRP Irp;
711     PLIST_ENTRY CurEntry;
712     PKSSTREAM_DATA StreamData;
713     PIO_STACK_LOCATION IoStack;
714     ULONG Index;
715     KIRQL OldLevel;
716 
717     KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
718 
719     // check if used list empty
720     if (IsListEmpty(&m_FreeIrpList))
721     {
722         // get current irp
723         if (!m_Irp)
724         {
725             KeReleaseSpinLock(&m_IrpListLock, OldLevel);
726 
727             // this should not happen
728             DPRINT("ReleaseMappingWithTag Tag %p not found\n", Tag);
729             return STATUS_NOT_FOUND;
730         }
731 
732         Irp = m_Irp;
733     }
734     else
735     {
736         // remove irp from used list
737         CurEntry = RemoveHeadList(&m_FreeIrpList);
738 
739         // get irp from list entry
740         Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
741     }
742 
743     // get stream data
744     StreamData = (PKSSTREAM_DATA)Irp->Tail.Overlay.DriverContext[STREAM_DATA_OFFSET];
745 
746     // release oldest in use mapping
747     for (Index = 0; Index < StreamData->nTags; Index++)
748     {
749         if (StreamData->Tags[Index].Used != FALSE)
750         {
751             StreamData->Tags[Index].Used = FALSE;
752 
753             // Warn if wrong mapping released
754             if (StreamData->Tags[Index].Tag != Tag)
755             {
756                 DPRINT1("Mapping released out of order\n");
757             }
758 
759             break;
760         }
761     }
762 
763     // If this is the current IRP, do not complete
764     if (Irp == m_Irp)
765     {
766         KeReleaseSpinLock(&m_IrpListLock, OldLevel);
767         return STATUS_SUCCESS;
768     }
769 
770     // check if this is the last one released mapping
771     if (Index + 1 == StreamData->nTags)
772     {
773         // last mapping released
774         // now check if this is a looped buffer
775         if (m_ConnectDetails->Interface.Id == KSINTERFACE_STANDARD_LOOPED_STREAMING)
776         {
777             // looped buffers are not completed when they have been played
778             // they are completed when the stream is set to stop
779 
780             KeReleaseSpinLock(&m_IrpListLock, OldLevel);
781 
782             // re-insert irp
783             KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
784 
785             // done
786             return STATUS_SUCCESS;
787         }
788 
789         //
790         // time to complete non looped buffer
791         //
792 
793         KeReleaseSpinLock(&m_IrpListLock, OldLevel);
794 
795         // free stream data array
796         FreeItem(StreamData->Data, TAG_PORTCLASS);
797 
798         // free stream tags array
799         FreeItem(StreamData->Tags, TAG_PORTCLASS);
800 
801         // free stream data
802         FreeItem(StreamData, TAG_PORTCLASS);
803 
804         // get io stack
805         IoStack = IoGetCurrentIrpStackLocation(Irp);
806 
807         // store operation status
808         Irp->IoStatus.Status = STATUS_SUCCESS;
809 
810         // store operation length
811         Irp->IoStatus.Information = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
812 
813         // complete the request
814         IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
815     }
816     else
817     {
818         // there are still some headers not consumed
819         InsertHeadList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
820 
821         KeReleaseSpinLock(&m_IrpListLock, OldLevel);
822     }
823 
824     return STATUS_SUCCESS;
825 }
826 
827 ULONG
828 NTAPI
GetCurrentIrpOffset()829 CIrpQueue::GetCurrentIrpOffset()
830 {
831 
832     return m_CurrentOffset;
833 }
834 
835 BOOLEAN
836 NTAPI
GetAcquiredTagRange(IN PVOID * FirstTag,IN PVOID * LastTag)837 CIrpQueue::GetAcquiredTagRange(
838     IN PVOID * FirstTag,
839     IN PVOID * LastTag)
840 {
841     KIRQL OldLevel;
842     BOOLEAN Ret = FALSE;
843     //PIRP Irp;
844     //PLIST_ENTRY CurEntry;
845     //PKSSTREAM_DATA StreamData;
846 
847     // lock list
848     KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
849 
850     // initialize to zero
851     *FirstTag = NULL;
852     *LastTag = NULL;
853 
854     UNIMPLEMENTED;
855 
856     // release lock
857     KeReleaseSpinLock(&m_IrpListLock, OldLevel);
858     // done
859     return Ret;
860 }
861 
862 NTSTATUS
863 NTAPI
NewIrpQueue(IN IIrpQueue ** Queue)864 NewIrpQueue(
865     IN IIrpQueue **Queue)
866 {
867     CIrpQueue *This = new(NonPagedPool, TAG_PORTCLASS)CIrpQueue(NULL);
868     if (!This)
869         return STATUS_INSUFFICIENT_RESOURCES;
870 
871     This->AddRef();
872 
873     *Queue = (IIrpQueue*)This;
874     return STATUS_SUCCESS;
875 }
876