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
12 class CIrpQueue
: public IIrpQueue
15 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
17 STDMETHODIMP_(ULONG
) AddRef()
19 InterlockedIncrement(&m_Ref
);
22 STDMETHODIMP_(ULONG
) Release()
24 InterlockedDecrement(&m_Ref
);
34 CIrpQueue(IUnknown
*OuterUnknown
){}
35 virtual ~CIrpQueue(){}
39 PKSPIN_CONNECT m_ConnectDetails
;
40 PKSPIN_DESCRIPTOR m_Descriptor
;
42 KSPIN_LOCK m_IrpListLock
;
44 LIST_ENTRY m_FreeIrpList
;
49 ULONG m_TagSupportEnabled
;
50 ULONG m_NumDataAvailable
;
51 volatile ULONG m_CurrentOffset
;
62 ULONG StreamHeaderCount
;
63 ULONG StreamHeaderIndex
;
64 ULONG TotalStreamData
;
66 PKSSTREAM_HEADER CurStreamHeader
;
69 }KSSTREAM_DATA
, *PKSSTREAM_DATA
;
71 #define STREAM_DATA_OFFSET (0)
76 CIrpQueue::QueryInterface(
80 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
82 *Output
= PVOID(PUNKNOWN(this));
83 PUNKNOWN(*Output
)->AddRef();
84 return STATUS_SUCCESS
;
87 return STATUS_UNSUCCESSFUL
;
93 IN PKSPIN_CONNECT ConnectDetails
,
94 IN PKSPIN_DESCRIPTOR Descriptor
,
97 IN ULONG TagSupportEnabled
)
99 m_ConnectDetails
= ConnectDetails
;
100 m_Descriptor
= Descriptor
;
101 m_MaxFrameSize
= FrameSize
;
102 m_Alignment
= Alignment
;
103 m_TagSupportEnabled
= TagSupportEnabled
;
105 InitializeListHead(&m_IrpList
);
106 InitializeListHead(&m_FreeIrpList
);
107 KeInitializeSpinLock(&m_IrpListLock
);
109 return STATUS_SUCCESS
;
114 CIrpQueue::AddMapping(
118 PKSSTREAM_HEADER Header
;
119 NTSTATUS Status
= STATUS_UNSUCCESSFUL
;
120 PIO_STACK_LOCATION IoStack
;
123 PKSSTREAM_DATA StreamData
;
125 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
127 // allocate stream data
128 StreamData
= (PKSSTREAM_DATA
)AllocateItem(NonPagedPool
, sizeof(KSSTREAM_DATA
), TAG_PORTCLASS
);
132 return STATUS_INSUFFICIENT_RESOURCES
;
135 // get current irp stack location
136 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
138 // lets probe the irp
139 if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_WRITE_STREAM
)
141 // probe IOCTL_KS_WRITE_STREAM
142 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_WRITE
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_SYSTEMADDRESS
, 0);
144 else if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_READ_STREAM
)
146 // probe IOCTL_KS_READ_STREAM
147 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_READ
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_SYSTEMADDRESS
, 0);
151 if (!NT_SUCCESS(Status
))
153 // irp probing failed
154 FreeItem(StreamData
, TAG_PORTCLASS
);
158 // get first stream header
159 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
162 StreamData
->CurStreamHeader
= Header
;
167 // first calculate the numbers of stream headers
168 Length
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
173 Length
-= Header
->Size
;
175 /* increment header count */
176 StreamData
->StreamHeaderCount
++;
178 if (m_Descriptor
->DataFlow
== KSPIN_DATAFLOW_IN
)
181 StreamData
->TotalStreamData
+= Header
->DataUsed
;
186 StreamData
->TotalStreamData
+= Header
->FrameExtent
;
189 /* move to next header */
190 Header
= (PKSSTREAM_HEADER
)((ULONG_PTR
)Header
+ Header
->Size
);
195 ASSERT(StreamData
->StreamHeaderCount
);
197 // allocate array for storing the pointers of the data */
198 StreamData
->Data
= (PVOID
*)AllocateItem(NonPagedPool
, sizeof(PVOID
) * StreamData
->StreamHeaderCount
, TAG_PORTCLASS
);
199 if (!StreamData
->Data
)
202 FreeItem(StreamData
, TAG_PORTCLASS
);
205 return STATUS_INSUFFICIENT_RESOURCES
;
208 if (m_TagSupportEnabled
)
210 // allocate array for storing the pointers of the data */
211 StreamData
->Tags
= (PVOID
*)AllocateItem(NonPagedPool
, sizeof(PVOID
) * StreamData
->StreamHeaderCount
, TAG_PORTCLASS
);
212 if (!StreamData
->Data
)
215 FreeItem(StreamData
->Data
, TAG_PORTCLASS
);
216 FreeItem(StreamData
, TAG_PORTCLASS
);
219 return STATUS_INSUFFICIENT_RESOURCES
;
224 // now get a system address for the user buffers
225 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
226 Mdl
= Irp
->MdlAddress
;
228 for(Index
= 0; Index
< StreamData
->StreamHeaderCount
; Index
++)
230 /* get system address */
231 StreamData
->Data
[Index
] = MmGetSystemAddressForMdlSafe(Mdl
, NormalPagePriority
);
233 /* check for success */
234 if (!StreamData
->Data
[Index
])
237 FreeItem(StreamData
->Data
, TAG_PORTCLASS
);
239 if (m_TagSupportEnabled
)
242 FreeItem(StreamData
->Tags
, TAG_PORTCLASS
);
245 FreeItem(StreamData
, TAG_PORTCLASS
);
247 return STATUS_INSUFFICIENT_RESOURCES
;
250 if (m_Descriptor
->DataFlow
== KSPIN_DATAFLOW_IN
)
252 // increment available data
253 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
, Header
->DataUsed
);
255 else if (m_Descriptor
->DataFlow
== KSPIN_DATAFLOW_OUT
)
257 // increment available data
258 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
, Header
->FrameExtent
);
261 // move to next header / mdl
263 Header
= (PKSSTREAM_HEADER
)((ULONG_PTR
)Header
+ Header
->Size
);
268 Irp
->Tail
.Overlay
.DriverContext
[STREAM_DATA_OFFSET
] = (PVOID
)StreamData
;
270 *Data
= StreamData
->TotalStreamData
;
272 // mark irp as pending
273 IoMarkIrpPending(Irp
);
275 // add irp to cancelable queue
276 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
278 // disable mapping failed status
279 m_OutOfMapping
= FALSE
;
282 return STATUS_SUCCESS
;
287 CIrpQueue::GetMapping(
289 OUT PULONG BufferSize
)
293 PKSSTREAM_DATA StreamData
;
295 // check if there is an irp in the partially processed
299 if (m_Irp
->Cancel
== FALSE
)
302 Offset
= m_CurrentOffset
;
306 // irp has been cancelled
307 m_Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
308 IoCompleteRequest(m_Irp
, IO_NO_INCREMENT
);
315 // get a fresh new irp from the queue
316 m_Irp
= Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
317 m_CurrentOffset
= Offset
= 0;
322 // no irp buffer available
323 return STATUS_UNSUCCESSFUL
;
327 StreamData
= (PKSSTREAM_DATA
)Irp
->Tail
.Overlay
.DriverContext
[STREAM_DATA_OFFSET
];
330 PC_ASSERT(StreamData
);
333 if (m_Descriptor
->DataFlow
== KSPIN_DATAFLOW_IN
)
336 *BufferSize
= StreamData
->CurStreamHeader
->DataUsed
- Offset
;
341 *BufferSize
= StreamData
->CurStreamHeader
->FrameExtent
- Offset
;
345 PC_ASSERT(*BufferSize
);
348 *Buffer
= &((PUCHAR
)StreamData
->Data
[StreamData
->StreamHeaderIndex
])[Offset
];
350 // unset flag that no irps are available
351 m_OutOfMapping
= FALSE
;
353 return STATUS_SUCCESS
;
358 CIrpQueue::UpdateMapping(
359 IN ULONG BytesWritten
)
361 PKSSTREAM_DATA StreamData
;
363 PIO_STACK_LOCATION IoStack
;
369 StreamData
= (PKSSTREAM_DATA
)m_Irp
->Tail
.Overlay
.DriverContext
[STREAM_DATA_OFFSET
];
374 // add to current offset
375 InterlockedExchangeAdd((volatile PLONG
)&m_CurrentOffset
, (LONG
)BytesWritten
);
377 if (m_Descriptor
->DataFlow
== KSPIN_DATAFLOW_OUT
)
379 // store written bytes (source pin)
380 StreamData
->CurStreamHeader
->DataUsed
+= BytesWritten
;
383 // decrement available data counter
384 m_NumDataAvailable
-= BytesWritten
;
386 // get audio buffer size
387 if (m_Descriptor
->DataFlow
== KSPIN_DATAFLOW_OUT
)
388 Size
= StreamData
->CurStreamHeader
->FrameExtent
;
390 Size
= StreamData
->CurStreamHeader
->DataUsed
;
395 if (m_CurrentOffset
>= Size
)
398 PC_ASSERT(Size
== m_CurrentOffset
);
400 if (StreamData
->StreamHeaderIndex
+ 1 < StreamData
->StreamHeaderCount
)
402 // move to next stream header
403 StreamData
->CurStreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamData
->CurStreamHeader
+ StreamData
->CurStreamHeader
->Size
);
405 // increment stream header index
406 StreamData
->StreamHeaderIndex
++;
416 // all stream buffers have been played
417 // check if this is a looped buffer
419 if (m_ConnectDetails
->Interface
.Id
== KSINTERFACE_STANDARD_LOOPED_STREAMING
)
421 // looped streaming repeat the buffers untill
422 // the caller decides to stop the streams
424 // reset stream header index
425 StreamData
->StreamHeaderIndex
= 0;
427 // reset stream header
428 StreamData
->CurStreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->AssociatedIrp
.SystemBuffer
;
430 // increment available data
431 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
, StreamData
->TotalStreamData
);
434 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
446 // free stream data array
447 FreeItem(StreamData
->Data
, TAG_PORTCLASS
);
449 if (m_TagSupportEnabled
)
452 FreeItem(StreamData
->Tags
, TAG_PORTCLASS
);
456 FreeItem(StreamData
, TAG_PORTCLASS
);
459 IoStack
= IoGetCurrentIrpStackLocation(m_Irp
);
461 // store operation status
462 m_Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
464 // store operation length
465 m_Irp
->IoStatus
.Information
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
467 // complete the request
468 IoCompleteRequest(m_Irp
, IO_SOUND_INCREMENT
);
470 // remove irp as it is complete
482 // returns the amount of audio stream data available
483 return m_NumDataAvailable
;
488 CIrpQueue::CancelBuffers()
490 //TODO: own cancel routine
492 // is there an active irp
495 // re-insert it to cancelable queue
496 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
502 KsCancelIo(&m_IrpList
, &m_IrpListLock
);
504 // reset number of data available
505 m_NumDataAvailable
= 0;
513 CIrpQueue::GetMappingWithTag(
515 OUT PPHYSICAL_ADDRESS PhysicalAddress
,
516 OUT PVOID
*VirtualAddress
,
517 OUT PULONG ByteCount
,
520 PKSSTREAM_DATA StreamData
;
523 PC_ASSERT(Tag
!= NULL
);
524 PC_ASSERT(PhysicalAddress
);
525 PC_ASSERT(VirtualAddress
);
526 PC_ASSERT(ByteCount
);
531 // get an irp from the queue
532 m_Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
535 // check if there is an irp
539 m_OutOfMapping
= TRUE
;
540 return STATUS_NOT_FOUND
;
544 StreamData
= (PKSSTREAM_DATA
)m_Irp
->Tail
.Overlay
.DriverContext
[STREAM_DATA_OFFSET
];
547 PC_ASSERT(StreamData
->StreamHeaderIndex
< StreamData
->StreamHeaderCount
);
550 *PhysicalAddress
= MmGetPhysicalAddress(StreamData
->Data
[StreamData
->StreamHeaderIndex
]);
551 *VirtualAddress
= StreamData
->Data
[StreamData
->StreamHeaderIndex
];
554 StreamData
->Tags
[StreamData
->StreamHeaderIndex
] = Tag
;
557 if (m_Descriptor
->DataFlow
== KSPIN_DATAFLOW_IN
)
560 *ByteCount
= StreamData
->CurStreamHeader
->DataUsed
;
562 // decrement num data available
563 m_NumDataAvailable
-= StreamData
->CurStreamHeader
->DataUsed
;
568 *ByteCount
= StreamData
->CurStreamHeader
->FrameExtent
;
570 // decrement num data available
571 m_NumDataAvailable
-= StreamData
->CurStreamHeader
->FrameExtent
;
574 if (StreamData
->StreamHeaderIndex
+ 1 == StreamData
->StreamHeaderCount
)
579 // insert mapping into free list
580 ExInterlockedInsertTailList(&m_FreeIrpList
, &m_Irp
->Tail
.Overlay
.ListEntry
, &m_IrpListLock
);
588 // one more mapping in the irp
591 // increment header index
592 StreamData
->StreamHeaderIndex
++;
594 // move to next header
595 StreamData
->CurStreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamData
->CurStreamHeader
+ StreamData
->CurStreamHeader
->Size
);
599 return STATUS_SUCCESS
;
604 CIrpQueue::ReleaseMappingWithTag(
608 PLIST_ENTRY CurEntry
;
609 PKSSTREAM_DATA StreamData
;
610 PIO_STACK_LOCATION IoStack
;
613 // first check if there is an active irp
616 // now check if there are already used mappings
617 StreamData
= (PKSSTREAM_DATA
)m_Irp
->Tail
.Overlay
.DriverContext
[STREAM_DATA_OFFSET
];
619 if (StreamData
->StreamHeaderIndex
)
621 // check if the released mapping is one current processed irps
622 for(Index
= 0; Index
< StreamData
->StreamHeaderIndex
; Index
++)
624 // check if it is the same tag
625 if (StreamData
->Tags
[Index
] == Tag
)
627 // mark mapping as released
628 StreamData
->Tags
[Index
] = NULL
;
631 return STATUS_SUCCESS
;
638 // remove irp from used list
639 CurEntry
= ExInterlockedRemoveHeadList(&m_FreeIrpList
, &m_IrpListLock
);
644 // get irp from list entry
645 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
648 StreamData
= (PKSSTREAM_DATA
)Irp
->Tail
.Overlay
.DriverContext
[STREAM_DATA_OFFSET
];
651 PC_ASSERT(StreamData
->StreamHeaderIndex
+ 1 == StreamData
->StreamHeaderCount
);
653 // check if the released mapping is one of these
654 for(Index
= 0; Index
< StreamData
->StreamHeaderCount
; Index
++)
656 if (StreamData
->Tags
[Index
] == Tag
)
658 // mark mapping as released
659 StreamData
->Tags
[Index
] = NULL
;
667 // we assume that mappings are released in the same order as they have been acquired
668 // therefore if the current mapping is not the searched one, it must have been already
671 PC_ASSERT(StreamData
->Tags
[Index
] == NULL
);
675 // check if this is the last one released mapping
676 if (Index
+ 1 == StreamData
->StreamHeaderCount
)
678 // last mapping released
679 // now check if this is a looped buffer
680 if (m_ConnectDetails
->Interface
.Id
== KSINTERFACE_STANDARD_LOOPED_STREAMING
)
682 // looped buffers are not completed when they have been played
683 // they are completed when the stream is set to stop
685 // reset stream header index
686 StreamData
->StreamHeaderIndex
= 0;
688 // reset stream header
689 StreamData
->CurStreamHeader
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
691 // increment available data
692 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
, StreamData
->TotalStreamData
);
695 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
698 return STATUS_SUCCESS
;
702 // time to complete non looped buffer
705 // free stream data array
706 FreeItem(StreamData
->Data
, TAG_PORTCLASS
);
708 // free stream tags array
709 FreeItem(StreamData
->Tags
, TAG_PORTCLASS
);
712 FreeItem(StreamData
, TAG_PORTCLASS
);
715 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
717 // store operation status
718 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
720 // store operation length
721 Irp
->IoStatus
.Information
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
;
723 // complete the request
724 IoCompleteRequest(Irp
, IO_SOUND_INCREMENT
);
727 return STATUS_SUCCESS
;
732 CIrpQueue::HasLastMappingFailed()
734 return m_OutOfMapping
;
739 CIrpQueue::GetCurrentIrpOffset()
742 return m_CurrentOffset
;
747 CIrpQueue::GetAcquiredTagRange(
754 //PLIST_ENTRY CurEntry;
755 //PKSSTREAM_DATA StreamData;
758 KeAcquireSpinLock(&m_IrpListLock
, &OldLevel
);
760 // initialize to zero
767 KeReleaseSpinLock(&m_IrpListLock
, OldLevel
);
775 IN IIrpQueue
**Queue
)
777 CIrpQueue
*This
= new(NonPagedPool
, TAG_PORTCLASS
)CIrpQueue(NULL
);
779 return STATUS_INSUFFICIENT_RESOURCES
;
783 *Queue
= (IIrpQueue
*)This
;
784 return STATUS_SUCCESS
;