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
;
42 PKSPIN_CONNECT m_ConnectDetails
;
43 PKSDATAFORMAT_WAVEFORMATEX m_DataFormat
;
45 KSPIN_LOCK m_IrpListLock
;
47 LIST_ENTRY m_FreeIrpList
;
49 PVOID m_SilenceBuffer
;
54 ULONG m_MinimumDataThreshold
;
60 #define OFFSET_HEADERINDEX (0)
61 #define OFFSET_STREAMHEADER (2)
62 #define OFFSET_HEADERCOUNT (3)
65 #define STREAMHEADER_INDEX(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX]))
66 #define STREAMHEADER_COUNT(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT]))
67 #define STREAMHEADER_CURRENT(Irp) (Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER])
72 CIrpQueue::QueryInterface(
76 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
78 *Output
= PVOID(PUNKNOWN(this));
79 PUNKNOWN(*Output
)->AddRef();
80 return STATUS_SUCCESS
;
83 return STATUS_UNSUCCESSFUL
;
89 IN KSPIN_CONNECT
*ConnectDetails
,
90 IN PKSDATAFORMAT DataFormat
,
91 IN PDEVICE_OBJECT DeviceObject
,
94 IN PVOID SilenceBuffer
)
96 m_ConnectDetails
= ConnectDetails
;
97 m_DataFormat
= (PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
;
98 m_MaxFrameSize
= FrameSize
;
99 m_SilenceBuffer
= SilenceBuffer
;
100 m_Alignment
= Alignment
;
101 m_MinimumDataThreshold
= ((PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
)->WaveFormatEx
.nAvgBytesPerSec
/ 3;
103 InitializeListHead(&m_IrpList
);
104 InitializeListHead(&m_FreeIrpList
);
105 KeInitializeSpinLock(&m_IrpListLock
);
107 return STATUS_SUCCESS
;
112 CIrpQueue::AddMapping(
116 PKSSTREAM_HEADER Header
;
117 NTSTATUS Status
= STATUS_SUCCESS
;
118 PIO_STACK_LOCATION IoStack
;
119 ULONG NumHeaders
, NumData
, Index
;
122 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
124 // get current irp stack location
125 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
127 if (!Irp
->MdlAddress
)
129 // ioctl from KsStudio
130 // Wdmaud already probes buffers, therefore no need to probe it again
131 // probe the stream irp
132 if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_WRITE_STREAM
)
133 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_WRITE
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_SYSTEMADDRESS
, 0);
134 else if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_READ_STREAM
)
135 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_READ
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_SYSTEMADDRESS
, 0);
140 if (!NT_SUCCESS(Status
))
142 DPRINT("KsProbeStreamIrp failed with %x\n", Status
);
147 // get first stream header
149 if (Irp
->RequestorMode
== UserMode
)
150 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
152 Header
= (PKSSTREAM_HEADER
)Irp
->UserBuffer
;
157 // calculate num headers
158 NumHeaders
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
/ Header
->Size
;
160 // assume headers of same length
161 PC_ASSERT(IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
% Header
->Size
== 0);
164 // get first audio buffer
165 Mdl
= Irp
->MdlAddress
;
169 // store the current stream header
170 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_STREAMHEADER
] = (PVOID
)Header
;
171 // store header count
172 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERCOUNT
] = UlongToPtr(NumHeaders
);
174 // store current header index
175 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(0);
178 // prepare all headers
179 for(Index
= 0; Index
< NumHeaders
; Index
++)
185 if (Irp
->RequestorMode
== UserMode
)
187 Header
->Data
= MmGetSystemAddressForMdlSafe(Mdl
, NormalPagePriority
);
192 // insufficient resources
193 ExFreePool(Irp
->AssociatedIrp
.SystemBuffer
);
194 Irp
->AssociatedIrp
.SystemBuffer
= NULL
;
195 // complete and forget request
196 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
197 Irp
->IoStatus
.Information
= 0;
199 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
200 return STATUS_INSUFFICIENT_RESOURCES
;
203 // increment num mappings
204 InterlockedIncrement(&m_NumMappings
);
206 // increment available data
207 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
,
208 (max(Header
->DataUsed
, Header
->FrameExtent
)));
210 NumData
+= max(Header
->DataUsed
, Header
->FrameExtent
);
212 // move to next header
213 Header
= (PKSSTREAM_HEADER
)((ULONG_PTR
)Header
+ Header
->Size
);
219 DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders
, NumData
, m_MaxFrameSize
, m_NumDataAvailable
);
222 // mark irp as pending
223 IoMarkIrpPending(Irp
);
225 // add irp to cancelable queue
226 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
234 CIrpQueue::GetMapping(
236 OUT PULONG BufferSize
)
240 //PIO_STACK_LOCATION IoStack;
241 PKSSTREAM_HEADER StreamHeader
;
243 // check if there is an irp in the partially processed
247 if (m_Irp
->Cancel
== FALSE
)
250 Offset
= m_CurrentOffset
;
254 // irp has been cancelled
255 m_Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
256 IoCompleteRequest(m_Irp
, IO_NO_INCREMENT
);
262 // get a fresh new irp from the queue
263 m_Irp
= Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
264 m_CurrentOffset
= Offset
= 0;
270 return STATUS_UNSUCCESSFUL
;
271 // no irp available, use silence buffer
272 *Buffer
= (PUCHAR
)m_SilenceBuffer
;
273 *BufferSize
= m_MaxFrameSize
;
274 // flag for port wave pci driver
275 m_OutOfMapping
= TRUE
;
276 // indicate flag to restart fast buffering
277 m_StartStream
= FALSE
;
278 return STATUS_SUCCESS
;
282 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
285 PC_ASSERT(StreamHeader
);
288 if (StreamHeader
->DataUsed
)
289 *BufferSize
= StreamHeader
->DataUsed
- Offset
;
291 *BufferSize
= StreamHeader
->FrameExtent
- Offset
;
293 PC_ASSERT(*BufferSize
);
296 *Buffer
= &((PUCHAR
)StreamHeader
->Data
)[Offset
];
298 // unset flag that no irps are available
299 m_OutOfMapping
= FALSE
;
301 return STATUS_SUCCESS
;
306 CIrpQueue::UpdateMapping(
307 IN ULONG BytesWritten
)
309 PKSSTREAM_HEADER StreamHeader
;
310 ULONG Size
, NumData
, Index
;
314 // silence buffer was used
319 StreamHeader
= (PKSSTREAM_HEADER
)STREAMHEADER_CURRENT(m_Irp
);
322 // ASSERT(StreamHeader);
324 // add to current offset
325 InterlockedExchangeAdd((volatile PLONG
)&m_CurrentOffset
, (LONG
)BytesWritten
);
327 // decrement available data counter
328 m_NumDataAvailable
-= BytesWritten
;
330 if (StreamHeader
->DataUsed
)
331 Size
= StreamHeader
->DataUsed
;
333 Size
= StreamHeader
->FrameExtent
;
337 if (m_CurrentOffset
>= Size
)
339 if (STREAMHEADER_INDEX(m_Irp
) + 1 < STREAMHEADER_COUNT(m_Irp
))
341 // the irp has at least one more stream header
342 m_Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(STREAMHEADER_INDEX(m_Irp
) + 1);
344 // get next stream header
345 StreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamHeader
+ StreamHeader
->Size
);
347 // store next stream header
348 STREAMHEADER_CURRENT(m_Irp
) = (PVOID
)StreamHeader
;
350 // reset current offset
357 // irp has been processed completly
360 if (m_Irp
->RequestorMode
== KernelMode
)
361 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->UserBuffer
;
363 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->AssociatedIrp
.SystemBuffer
;
365 // loop all stream headers
366 for(Index
= 0; Index
< STREAMHEADER_COUNT(m_Irp
); Index
++)
368 PC_ASSERT(StreamHeader
);
370 // add size of buffer
371 // depends on if the buffer is input / output
372 if (StreamHeader
->DataUsed
)
373 Size
= StreamHeader
->DataUsed
;
375 Size
= StreamHeader
->FrameExtent
;
380 // get next stream header
381 StreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamHeader
+ StreamHeader
->Size
);
384 if (m_ConnectDetails
->Interface
.Id
== KSINTERFACE_STANDARD_LOOPED_STREAMING
)
386 // looped streaming repeat the buffers untill
387 // the caller decides to stop the streams
389 // reset stream header index
390 m_Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(0);
392 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
397 // increment available data
398 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
, NumData
);
403 m_Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
404 m_Irp
->IoStatus
.Information
= NumData
;
406 // complete the request
407 IoCompleteRequest(m_Irp
, IO_SOUND_INCREMENT
);
408 // remove irp as it is complete
416 CIrpQueue::NumMappings()
419 // returns the amount of mappings available
420 return m_NumMappings
;
427 // returns the amount of audio stream data available
428 return m_NumDataAvailable
;
434 CIrpQueue::MinimumDataAvailable()
441 if (m_MinimumDataThreshold
< m_NumDataAvailable
)
443 m_StartStream
= TRUE
;
455 CIrpQueue::CancelBuffers()
457 // is there an active irp
460 // re-insert it to cancelable queue
461 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
467 KsCancelIo(&m_IrpList
, &m_IrpListLock
);
468 // reset stream start flag
469 m_StartStream
= FALSE
;
476 CIrpQueue::UpdateFormat(
477 PKSDATAFORMAT DataFormat
)
479 m_DataFormat
= (PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
;
480 m_MinimumDataThreshold
= m_DataFormat
->WaveFormatEx
.nAvgBytesPerSec
/ 3;
481 m_StartStream
= FALSE
;
482 m_NumDataAvailable
= 0;
487 CIrpQueue::GetMappingWithTag(
489 OUT PPHYSICAL_ADDRESS PhysicalAddress
,
490 OUT PVOID
*VirtualAddress
,
491 OUT PULONG ByteCount
,
494 PKSSTREAM_HEADER StreamHeader
;
498 PC_ASSERT(Tag
!= NULL
);
500 // get an irp from the queue
501 Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
503 // check if there is an irp
507 m_OutOfMapping
= TRUE
;
508 m_StartStream
= FALSE
;
509 return STATUS_UNSUCCESSFUL
;
512 //FIXME support more than one stream header
513 PC_ASSERT(STREAMHEADER_COUNT(Irp
) == 1);
515 // HACK get stream header
516 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
518 // store mapping in the free list
519 ExInterlockedInsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
, &m_IrpListLock
);
522 *PhysicalAddress
= MmGetPhysicalAddress(StreamHeader
->Data
);
523 *VirtualAddress
= StreamHeader
->Data
;
524 *ByteCount
= StreamHeader
->DataUsed
;
526 // decrement mapping count
527 InterlockedDecrement(&m_NumMappings
);
528 // decrement num data available
529 m_NumDataAvailable
-= StreamHeader
->DataUsed
;
532 Irp
->Tail
.Overlay
.DriverContext
[3] = Tag
;
535 return STATUS_SUCCESS
;
540 CIrpQueue::ReleaseMappingWithTag(
544 PLIST_ENTRY CurEntry
;
545 PKSSTREAM_HEADER StreamHeader
;
547 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag
);
549 // remove irp from used list
550 CurEntry
= ExInterlockedRemoveHeadList(&m_FreeIrpList
, &m_IrpListLock
);
554 // get irp from list entry
555 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
557 // HACK get stream header
558 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
560 // driver must release items in the same order
561 PC_ASSERT(Irp
->Tail
.Overlay
.DriverContext
[3] == Tag
);
563 // irp has been processed completly
564 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
566 // frame extend contains the original request size, DataUsed contains the real buffer size
567 // is different when kmixer performs channel conversion, upsampling etc
569 Irp
->IoStatus
.Information
= StreamHeader
->FrameExtent
;
571 // free stream header, no tag as wdmaud.drv allocates it atm
572 ExFreePool(StreamHeader
);
574 // complete the request
575 IoCompleteRequest(Irp
, IO_SOUND_INCREMENT
);
577 return STATUS_SUCCESS
;
582 CIrpQueue::HasLastMappingFailed()
584 return m_OutOfMapping
;
589 CIrpQueue::GetCurrentIrpOffset()
592 return m_CurrentOffset
;
597 CIrpQueue::SetMinimumDataThreshold(
598 ULONG MinimumDataThreshold
)
601 m_MinimumDataThreshold
= MinimumDataThreshold
;
606 CIrpQueue::GetMinimumDataThreshold()
608 return m_MinimumDataThreshold
;
615 IN IIrpQueue
**Queue
)
617 CIrpQueue
*This
= new(NonPagedPool
, TAG_PORTCLASS
)CIrpQueue(NULL
);
619 return STATUS_INSUFFICIENT_RESOURCES
;
623 *Queue
= (IIrpQueue
*)This
;
624 return STATUS_SUCCESS
;