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 ULONG m_CurrentOffset
;
40 ULONG m_NumDataAvailable
;
42 KSPIN_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
;
62 CIrpQueue::QueryInterface(
66 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
68 *Output
= PVOID(PUNKNOWN(this));
69 PUNKNOWN(*Output
)->AddRef();
70 return STATUS_SUCCESS
;
73 return STATUS_UNSUCCESSFUL
;
79 IN KSPIN_CONNECT
*ConnectDetails
,
80 IN PKSDATAFORMAT DataFormat
,
81 IN PDEVICE_OBJECT DeviceObject
,
84 IN PVOID SilenceBuffer
)
86 m_ConnectDetails
= ConnectDetails
;
87 m_DataFormat
= (PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
;
88 m_MaxFrameSize
= FrameSize
;
89 m_SilenceBuffer
= SilenceBuffer
;
90 m_Alignment
= Alignment
;
91 m_MinimumDataThreshold
= ((PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
)->WaveFormatEx
.nAvgBytesPerSec
/ 3;
93 InitializeListHead(&m_IrpList
);
94 InitializeListHead(&m_FreeIrpList
);
95 KeInitializeSpinLock(&m_IrpListLock
);
97 return STATUS_SUCCESS
;
102 CIrpQueue::AddMapping(
107 PKSSTREAM_HEADER Header
;
108 NTSTATUS Status
= STATUS_SUCCESS
;
109 PIO_STACK_LOCATION IoStack
;
111 // get current irp stack location
112 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
116 if (!Irp
->MdlAddress
)
118 // ioctl from KsStudio
119 // Wdmaud already probes buffers, therefore no need to probe it again
120 // probe the stream irp
121 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_READ
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_ALLOWFORMATCHANGE
| KSPROBE_SYSTEMADDRESS
, 0);
124 if (!NT_SUCCESS(Status
))
126 DPRINT1("KsProbeStreamIrp failed with %x\n", Status
);
130 // get the stream header
131 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
133 PC_ASSERT(Irp
->MdlAddress
);
135 DPRINT1("Size %u DataUsed %u FrameExtent %u SizeHeader %u NumDataAvailable %u OutputLength %u\n", Header
->Size
, Header
->DataUsed
, Header
->FrameExtent
, sizeof(KSSTREAM_HEADER
), m_NumDataAvailable
, IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
);
137 Header
->Data
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, NormalPagePriority
);
138 PC_ASSERT(Header
->Data
);
139 //PC_ASSERT(Header->Size == IoStack->Parameters.DeviceIoControl.OutputBufferLength);
142 Irp
->Tail
.Overlay
.DriverContext
[2] = (PVOID
)Header
;
147 // dont exceed max frame size
148 //PC_ASSERT(m_MaxFrameSize >= Header->DataUsed);
150 // increment num mappings
151 InterlockedIncrement(&m_NumMappings
);
153 // increment num data available
154 m_NumDataAvailable
+= Header
->DataUsed
;
156 // mark irp as pending
157 IoMarkIrpPending(Irp
);
159 // add irp to cancelable queue
160 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
168 CIrpQueue::GetMapping(
170 OUT PULONG BufferSize
)
174 //PIO_STACK_LOCATION IoStack;
175 PKSSTREAM_HEADER StreamHeader
;
177 // check if there is an irp in the partially processed
181 if (m_Irp
->Cancel
== FALSE
)
184 Offset
= m_CurrentOffset
;
188 // irp has been cancelled
189 m_Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
190 IoCompleteRequest(m_Irp
, IO_NO_INCREMENT
);
196 // get a fresh new irp from the queue
197 m_Irp
= Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
198 m_CurrentOffset
= Offset
= 0;
204 return STATUS_UNSUCCESSFUL
;
205 // no irp available, use silence buffer
206 *Buffer
= (PUCHAR
)m_SilenceBuffer
;
207 *BufferSize
= m_MaxFrameSize
;
208 // flag for port wave pci driver
209 m_OutOfMapping
= TRUE
;
210 // indicate flag to restart fast buffering
211 m_StartStream
= FALSE
;
212 return STATUS_SUCCESS
;
216 // get current irp stack location
217 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
220 StreamHeader
= (PKSSTREAM_HEADER
)IoStack
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
222 // HACK get stream header
223 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
227 PC_ASSERT(StreamHeader
);
230 if (StreamHeader
->DataUsed
)
231 *BufferSize
= StreamHeader
->DataUsed
- Offset
;
233 *BufferSize
= StreamHeader
->FrameExtent
- Offset
;
235 PC_ASSERT(*BufferSize
);
238 *Buffer
= &((PUCHAR
)StreamHeader
->Data
)[Offset
];
240 // unset flag that no irps are available
241 m_OutOfMapping
= FALSE
;
243 return STATUS_SUCCESS
;
248 CIrpQueue::UpdateMapping(
249 IN ULONG BytesWritten
)
251 PKSSTREAM_HEADER StreamHeader
;
256 // silence buffer was used
261 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->Tail
.Overlay
.DriverContext
[2];
264 // ASSERT(StreamHeader);
266 // add to current offset
267 m_CurrentOffset
+= BytesWritten
;
269 // decrement available data counter
270 m_NumDataAvailable
-= BytesWritten
;
272 if (StreamHeader
->DataUsed
)
273 Size
= StreamHeader
->DataUsed
;
275 Size
= StreamHeader
->FrameExtent
;
279 if (m_CurrentOffset
>= Size
)
281 // irp has been processed completly
282 m_Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
284 // frame extend contains the original request size, DataUsed contains the real buffer size
285 //is different when kmixer performs channel conversion, upsampling etc
287 m_Irp
->IoStatus
.Information
= Size
;
289 PC_ASSERT_IRQL(DISPATCH_LEVEL
);
290 MmUnlockPages(m_Irp
->MdlAddress
);
291 IoFreeMdl(m_Irp
->MdlAddress
);
292 m_Irp
->MdlAddress
= NULL
;
293 ExFreePool(m_Irp
->AssociatedIrp
.SystemBuffer
);
294 m_Irp
->AssociatedIrp
.SystemBuffer
= NULL
;
296 // complete the request
297 IoCompleteRequest(m_Irp
, IO_SOUND_INCREMENT
);
298 // remove irp as it is complete
306 CIrpQueue::NumMappings()
309 // returns the amount of mappings available
310 return m_NumMappings
;
317 // returns the amount of audio stream data available
318 return m_NumDataAvailable
;
324 CIrpQueue::MinimumDataAvailable()
331 if (m_MinimumDataThreshold
< m_NumDataAvailable
)
333 m_StartStream
= TRUE
;
345 CIrpQueue::CancelBuffers()
348 m_StartStream
= FALSE
;
354 CIrpQueue::UpdateFormat(
355 PKSDATAFORMAT DataFormat
)
357 m_DataFormat
= (PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
;
358 m_MinimumDataThreshold
= m_DataFormat
->WaveFormatEx
.nAvgBytesPerSec
/ 3;
359 m_StartStream
= FALSE
;
360 m_NumDataAvailable
= 0;
365 CIrpQueue::GetMappingWithTag(
367 OUT PPHYSICAL_ADDRESS PhysicalAddress
,
368 OUT PVOID
*VirtualAddress
,
369 OUT PULONG ByteCount
,
372 PKSSTREAM_HEADER StreamHeader
;
376 PC_ASSERT(Tag
!= NULL
);
378 // get an irp from the queue
379 Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
381 // check if there is an irp
385 m_OutOfMapping
= TRUE
;
386 m_StartStream
= FALSE
;
387 return STATUS_UNSUCCESSFUL
;
390 // HACK get stream header
391 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
393 // store mapping in the free list
394 ExInterlockedInsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
, &m_IrpListLock
);
397 *PhysicalAddress
= MmGetPhysicalAddress(StreamHeader
->Data
);
398 *VirtualAddress
= StreamHeader
->Data
;
399 *ByteCount
= StreamHeader
->DataUsed
;
401 // decrement mapping count
402 InterlockedDecrement(&m_NumMappings
);
403 // decrement num data available
404 m_NumDataAvailable
-= StreamHeader
->DataUsed
;
407 Irp
->Tail
.Overlay
.DriverContext
[3] = Tag
;
410 return STATUS_SUCCESS
;
415 CIrpQueue::ReleaseMappingWithTag(
419 PLIST_ENTRY CurEntry
;
420 PKSSTREAM_HEADER StreamHeader
;
422 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag
);
424 // remove irp from used list
425 CurEntry
= ExInterlockedRemoveHeadList(&m_FreeIrpList
, &m_IrpListLock
);
429 // get irp from list entry
430 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
432 // HACK get stream header
433 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
435 // driver must release items in the same order
436 PC_ASSERT(Irp
->Tail
.Overlay
.DriverContext
[3] == Tag
);
438 // irp has been processed completly
439 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
441 // frame extend contains the original request size, DataUsed contains the real buffer size
442 // is different when kmixer performs channel conversion, upsampling etc
444 Irp
->IoStatus
.Information
= StreamHeader
->FrameExtent
;
446 // free stream header, no tag as wdmaud.drv allocates it atm
447 ExFreePool(StreamHeader
);
449 // complete the request
450 IoCompleteRequest(Irp
, IO_SOUND_INCREMENT
);
452 return STATUS_SUCCESS
;
457 CIrpQueue::HasLastMappingFailed()
459 return m_OutOfMapping
;
464 CIrpQueue::PrintQueueStatus()
471 CIrpQueue::SetMinimumDataThreshold(
472 ULONG MinimumDataThreshold
)
475 m_MinimumDataThreshold
= MinimumDataThreshold
;
480 CIrpQueue::GetMinimumDataThreshold()
482 return m_MinimumDataThreshold
;
489 IN IIrpQueue
**Queue
)
491 CIrpQueue
*This
= new(NonPagedPool
, TAG_PORTCLASS
)CIrpQueue(NULL
);
493 return STATUS_INSUFFICIENT_RESOURCES
;
497 *Queue
= (IIrpQueue
*)This
;
498 return STATUS_SUCCESS
;