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(){}
38 volatile ULONG m_CurrentOffset
;
40 ULONG m_NumDataAvailable
;
41 PKSPIN_CONNECT m_ConnectDetails
;
42 KSPIN_LOCK m_IrpListLock
;
44 LIST_ENTRY m_FreeIrpList
;
46 PVOID m_SilenceBuffer
;
56 #define OFFSET_HEADERINDEX (0)
57 #define OFFSET_STREAMHEADER (2)
58 #define OFFSET_HEADERCOUNT (3)
61 #define STREAMHEADER_INDEX(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX]))
62 #define STREAMHEADER_COUNT(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT]))
63 #define STREAMHEADER_CURRENT(Irp) (Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER])
68 CIrpQueue::QueryInterface(
72 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
74 *Output
= PVOID(PUNKNOWN(this));
75 PUNKNOWN(*Output
)->AddRef();
76 return STATUS_SUCCESS
;
79 return STATUS_UNSUCCESSFUL
;
85 IN KSPIN_CONNECT
*ConnectDetails
,
88 IN PVOID SilenceBuffer
)
90 m_ConnectDetails
= ConnectDetails
;
91 m_MaxFrameSize
= FrameSize
;
92 m_SilenceBuffer
= SilenceBuffer
;
93 m_Alignment
= Alignment
;
95 InitializeListHead(&m_IrpList
);
96 InitializeListHead(&m_FreeIrpList
);
97 KeInitializeSpinLock(&m_IrpListLock
);
99 return STATUS_SUCCESS
;
104 CIrpQueue::AddMapping(
108 PKSSTREAM_HEADER Header
;
109 NTSTATUS Status
= STATUS_SUCCESS
;
110 PIO_STACK_LOCATION IoStack
;
111 ULONG NumHeaders
, NumData
, Index
;
114 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
116 // get current irp stack location
117 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
119 if (!Irp
->MdlAddress
)
121 // ioctl from KsStudio
122 // Wdmaud already probes buffers, therefore no need to probe it again
123 // probe the stream irp
124 if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_WRITE_STREAM
)
125 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_WRITE
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_SYSTEMADDRESS
, 0);
126 else if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_READ_STREAM
)
127 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_READ
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_SYSTEMADDRESS
, 0);
132 if (!NT_SUCCESS(Status
))
134 DPRINT("KsProbeStreamIrp failed with %x\n", Status
);
139 // get first stream header
141 if (Irp
->RequestorMode
== UserMode
)
142 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
144 Header
= (PKSSTREAM_HEADER
)Irp
->UserBuffer
;
149 // calculate num headers
150 NumHeaders
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
/ Header
->Size
;
152 // assume headers of same length
153 PC_ASSERT(IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
% Header
->Size
== 0);
156 // get first audio buffer
157 Mdl
= Irp
->MdlAddress
;
161 // store the current stream header
162 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_STREAMHEADER
] = (PVOID
)Header
;
163 // store header count
164 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERCOUNT
] = UlongToPtr(NumHeaders
);
166 // store current header index
167 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(0);
170 // prepare all headers
171 for(Index
= 0; Index
< NumHeaders
; Index
++)
177 if (Irp
->RequestorMode
== UserMode
)
179 Header
->Data
= MmGetSystemAddressForMdlSafe(Mdl
, NormalPagePriority
);
184 // insufficient resources
185 ExFreePool(Irp
->AssociatedIrp
.SystemBuffer
);
186 Irp
->AssociatedIrp
.SystemBuffer
= NULL
;
187 // complete and forget request
188 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
189 Irp
->IoStatus
.Information
= 0;
191 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
192 return STATUS_INSUFFICIENT_RESOURCES
;
195 // increment num mappings
196 InterlockedIncrement(&m_NumMappings
);
198 // increment available data
199 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
,
200 (max(Header
->DataUsed
, Header
->FrameExtent
)));
202 NumData
+= max(Header
->DataUsed
, Header
->FrameExtent
);
204 // move to next header
205 Header
= (PKSSTREAM_HEADER
)((ULONG_PTR
)Header
+ Header
->Size
);
211 DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders
, NumData
, m_MaxFrameSize
, m_NumDataAvailable
);
214 // mark irp as pending
215 IoMarkIrpPending(Irp
);
217 // add irp to cancelable queue
218 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
220 // disable mapping failed status
221 m_OutOfMapping
= FALSE
;
229 CIrpQueue::GetMapping(
231 OUT PULONG BufferSize
)
235 //PIO_STACK_LOCATION IoStack;
236 PKSSTREAM_HEADER StreamHeader
;
238 // check if there is an irp in the partially processed
242 if (m_Irp
->Cancel
== FALSE
)
245 Offset
= m_CurrentOffset
;
249 // irp has been cancelled
250 m_Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
251 IoCompleteRequest(m_Irp
, IO_NO_INCREMENT
);
257 // get a fresh new irp from the queue
258 m_Irp
= Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
259 m_CurrentOffset
= Offset
= 0;
262 if (!Irp
&& m_SilenceBuffer
&& m_MaxFrameSize
)
265 // no irp available, use silence buffer
266 *Buffer
= (PUCHAR
)m_SilenceBuffer
;
267 *BufferSize
= m_MaxFrameSize
;
268 return STATUS_SUCCESS
;
273 // no irp buffer available
274 return STATUS_UNSUCCESSFUL
;
279 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
282 PC_ASSERT(StreamHeader
);
285 if (StreamHeader
->DataUsed
)
286 *BufferSize
= StreamHeader
->DataUsed
- Offset
;
288 *BufferSize
= StreamHeader
->FrameExtent
- Offset
;
290 PC_ASSERT(*BufferSize
);
293 *Buffer
= &((PUCHAR
)StreamHeader
->Data
)[Offset
];
295 // unset flag that no irps are available
296 m_OutOfMapping
= FALSE
;
298 return STATUS_SUCCESS
;
303 CIrpQueue::UpdateMapping(
304 IN ULONG BytesWritten
)
306 PKSSTREAM_HEADER StreamHeader
;
307 ULONG Size
, NumData
, Index
;
311 // silence buffer was used
316 StreamHeader
= (PKSSTREAM_HEADER
)STREAMHEADER_CURRENT(m_Irp
);
319 // ASSERT(StreamHeader);
321 // add to current offset
322 InterlockedExchangeAdd((volatile PLONG
)&m_CurrentOffset
, (LONG
)BytesWritten
);
324 // decrement available data counter
325 m_NumDataAvailable
-= BytesWritten
;
327 if (StreamHeader
->DataUsed
)
328 Size
= StreamHeader
->DataUsed
;
330 Size
= StreamHeader
->FrameExtent
;
334 if (m_CurrentOffset
>= Size
)
336 if (STREAMHEADER_INDEX(m_Irp
) + 1 < STREAMHEADER_COUNT(m_Irp
))
338 // the irp has at least one more stream header
339 m_Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(STREAMHEADER_INDEX(m_Irp
) + 1);
341 // get next stream header
342 StreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamHeader
+ StreamHeader
->Size
);
344 // store next stream header
345 STREAMHEADER_CURRENT(m_Irp
) = (PVOID
)StreamHeader
;
347 // reset current offset
354 // irp has been processed completly
357 if (m_Irp
->RequestorMode
== KernelMode
)
358 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->UserBuffer
;
360 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->AssociatedIrp
.SystemBuffer
;
362 // loop all stream headers
363 for(Index
= 0; Index
< STREAMHEADER_COUNT(m_Irp
); Index
++)
365 PC_ASSERT(StreamHeader
);
367 // add size of buffer
368 // depends on if the buffer is input / output
369 if (StreamHeader
->DataUsed
)
370 Size
= StreamHeader
->DataUsed
;
372 Size
= StreamHeader
->FrameExtent
;
377 // get next stream header
378 StreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamHeader
+ StreamHeader
->Size
);
381 if (m_ConnectDetails
->Interface
.Id
== KSINTERFACE_STANDARD_LOOPED_STREAMING
)
383 // looped streaming repeat the buffers untill
384 // the caller decides to stop the streams
386 // reset stream header index
387 m_Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(0);
389 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
394 // increment available data
395 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
, NumData
);
400 m_Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
401 m_Irp
->IoStatus
.Information
= NumData
;
403 // complete the request
404 IoCompleteRequest(m_Irp
, IO_SOUND_INCREMENT
);
405 // remove irp as it is complete
415 // returns the amount of audio stream data available
416 return m_NumDataAvailable
;
421 CIrpQueue::CancelBuffers()
423 // is there an active irp
426 // re-insert it to cancelable queue
427 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
433 KsCancelIo(&m_IrpList
, &m_IrpListLock
);
434 // reset number of mappings
436 // reset number of data available
437 m_NumDataAvailable
= 0;
445 CIrpQueue::GetMappingWithTag(
447 OUT PPHYSICAL_ADDRESS PhysicalAddress
,
448 OUT PVOID
*VirtualAddress
,
449 OUT PULONG ByteCount
,
452 PKSSTREAM_HEADER StreamHeader
;
456 PC_ASSERT(Tag
!= NULL
);
458 // get an irp from the queue
459 Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
461 // check if there is an irp
465 m_OutOfMapping
= TRUE
;
466 return STATUS_NOT_FOUND
;
469 //FIXME support more than one stream header
470 PC_ASSERT(STREAMHEADER_COUNT(Irp
) == 1);
472 // HACK get stream header
473 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
475 // store mapping in the free list
476 ExInterlockedInsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
, &m_IrpListLock
);
479 *PhysicalAddress
= MmGetPhysicalAddress(StreamHeader
->Data
);
480 *VirtualAddress
= StreamHeader
->Data
;
481 *ByteCount
= StreamHeader
->DataUsed
;
483 // decrement mapping count
484 InterlockedDecrement(&m_NumMappings
);
485 // decrement num data available
486 m_NumDataAvailable
-= StreamHeader
->DataUsed
;
489 Irp
->Tail
.Overlay
.DriverContext
[3] = Tag
;
492 return STATUS_SUCCESS
;
497 CIrpQueue::ReleaseMappingWithTag(
501 PLIST_ENTRY CurEntry
;
502 PKSSTREAM_HEADER StreamHeader
;
504 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag
);
506 // remove irp from used list
507 CurEntry
= ExInterlockedRemoveHeadList(&m_FreeIrpList
, &m_IrpListLock
);
511 // get irp from list entry
512 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
514 // HACK get stream header
515 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
517 // driver must release items in the same order
518 PC_ASSERT(Irp
->Tail
.Overlay
.DriverContext
[3] == Tag
);
520 // irp has been processed completly
521 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
523 // frame extend contains the original request size, DataUsed contains the real buffer size
524 // is different when kmixer performs channel conversion, upsampling etc
526 Irp
->IoStatus
.Information
= StreamHeader
->FrameExtent
;
528 // complete the request
529 IoCompleteRequest(Irp
, IO_SOUND_INCREMENT
);
531 return STATUS_SUCCESS
;
536 CIrpQueue::HasLastMappingFailed()
538 return m_OutOfMapping
;
543 CIrpQueue::GetCurrentIrpOffset()
546 return m_CurrentOffset
;
551 CIrpQueue::GetAcquiredTagRange(
558 PLIST_ENTRY CurEntry
;
560 KeAcquireSpinLock(&m_IrpListLock
, &OldLevel
);
562 if (!IsListEmpty(&m_FreeIrpList
))
565 CurEntry
= RemoveHeadList(&m_FreeIrpList
);
566 // get irp from list entry
567 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
569 // get tag of first acquired buffer
570 *FirstTag
= Irp
->Tail
.Overlay
.DriverContext
[3];
573 InsertHeadList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
);
576 CurEntry
= RemoveTailList(&m_FreeIrpList
);
577 // get irp from list entry
578 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
580 // get tag of first acquired buffer
581 *LastTag
= Irp
->Tail
.Overlay
.DriverContext
[3];
584 InsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
);
591 KeReleaseSpinLock(&m_IrpListLock
, OldLevel
);
599 IN IIrpQueue
**Queue
)
601 CIrpQueue
*This
= new(NonPagedPool
, TAG_PORTCLASS
)CIrpQueue(NULL
);
603 return STATUS_INSUFFICIENT_RESOURCES
;
607 *Queue
= (IIrpQueue
*)This
;
608 return STATUS_SUCCESS
;