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 if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_WRITE_STREAM
)
122 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_WRITE
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_ALLOWFORMATCHANGE
| KSPROBE_SYSTEMADDRESS
, 0);
124 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_READ
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_ALLOWFORMATCHANGE
| KSPROBE_SYSTEMADDRESS
, 0);
127 if (!NT_SUCCESS(Status
))
129 DPRINT1("KsProbeStreamIrp failed with %x\n", Status
);
133 // get the stream header
134 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
136 PC_ASSERT(Irp
->MdlAddress
);
138 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
);
140 Header
->Data
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, NormalPagePriority
);
141 PC_ASSERT(Header
->Data
);
142 //PC_ASSERT(Header->Size == IoStack->Parameters.DeviceIoControl.OutputBufferLength);
145 Irp
->Tail
.Overlay
.DriverContext
[2] = (PVOID
)Header
;
150 // dont exceed max frame size
151 //PC_ASSERT(m_MaxFrameSize >= Header->DataUsed);
153 // increment num mappings
154 InterlockedIncrement(&m_NumMappings
);
156 // increment num data available
157 m_NumDataAvailable
+= Header
->DataUsed
;
159 // mark irp as pending
160 IoMarkIrpPending(Irp
);
162 // add irp to cancelable queue
163 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
171 CIrpQueue::GetMapping(
173 OUT PULONG BufferSize
)
177 //PIO_STACK_LOCATION IoStack;
178 PKSSTREAM_HEADER StreamHeader
;
180 // check if there is an irp in the partially processed
184 if (m_Irp
->Cancel
== FALSE
)
187 Offset
= m_CurrentOffset
;
191 // irp has been cancelled
192 m_Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
193 IoCompleteRequest(m_Irp
, IO_NO_INCREMENT
);
199 // get a fresh new irp from the queue
200 m_Irp
= Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
201 m_CurrentOffset
= Offset
= 0;
207 // no irp available, use silence buffer
208 *Buffer
= (PUCHAR
)m_SilenceBuffer
;
209 *BufferSize
= m_MaxFrameSize
;
210 // flag for port wave pci driver
211 m_OutOfMapping
= TRUE
;
212 // indicate flag to restart fast buffering
213 m_StartStream
= FALSE
;
214 return STATUS_SUCCESS
;
218 // get current irp stack location
219 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
222 StreamHeader
= (PKSSTREAM_HEADER
)IoStack
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
224 // HACK get stream header
225 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
229 PC_ASSERT(StreamHeader
);
232 if (StreamHeader
->DataUsed
)
233 *BufferSize
= StreamHeader
->DataUsed
- Offset
;
235 *BufferSize
= StreamHeader
->FrameExtent
- Offset
;
237 PC_ASSERT(*BufferSize
);
240 *Buffer
= &((PUCHAR
)StreamHeader
->Data
)[Offset
];
242 // unset flag that no irps are available
243 m_OutOfMapping
= FALSE
;
245 return STATUS_SUCCESS
;
250 CIrpQueue::UpdateMapping(
251 IN ULONG BytesWritten
)
253 PKSSTREAM_HEADER StreamHeader
;
258 // silence buffer was used
263 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->Tail
.Overlay
.DriverContext
[2];
266 // ASSERT(StreamHeader);
268 // add to current offset
269 m_CurrentOffset
+= BytesWritten
;
271 // decrement available data counter
272 m_NumDataAvailable
-= BytesWritten
;
274 if (StreamHeader
->DataUsed
)
275 Size
= StreamHeader
->DataUsed
;
277 Size
= StreamHeader
->FrameExtent
;
281 if (m_CurrentOffset
>= Size
)
283 // irp has been processed completly
284 m_Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
286 // frame extend contains the original request size, DataUsed contains the real buffer size
287 //is different when kmixer performs channel conversion, upsampling etc
289 m_Irp
->IoStatus
.Information
= Size
;
291 PC_ASSERT_IRQL(DISPATCH_LEVEL
);
292 MmUnlockPages(m_Irp
->MdlAddress
);
293 IoFreeMdl(m_Irp
->MdlAddress
);
294 m_Irp
->MdlAddress
= NULL
;
295 ExFreePool(m_Irp
->AssociatedIrp
.SystemBuffer
);
296 m_Irp
->AssociatedIrp
.SystemBuffer
= NULL
;
298 // complete the request
299 IoCompleteRequest(m_Irp
, IO_SOUND_INCREMENT
);
300 // remove irp as it is complete
308 CIrpQueue::NumMappings()
311 // returns the amount of mappings available
312 return m_NumMappings
;
319 // returns the amount of audio stream data available
320 return m_NumDataAvailable
;
326 CIrpQueue::MinimumDataAvailable()
333 if (m_MinimumDataThreshold
< m_NumDataAvailable
)
335 m_StartStream
= TRUE
;
347 CIrpQueue::CancelBuffers()
350 m_StartStream
= FALSE
;
356 CIrpQueue::UpdateFormat(
357 PKSDATAFORMAT DataFormat
)
359 m_DataFormat
= (PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
;
360 m_MinimumDataThreshold
= m_DataFormat
->WaveFormatEx
.nAvgBytesPerSec
/ 3;
361 m_StartStream
= FALSE
;
362 m_NumDataAvailable
= 0;
367 CIrpQueue::GetMappingWithTag(
369 OUT PPHYSICAL_ADDRESS PhysicalAddress
,
370 OUT PVOID
*VirtualAddress
,
371 OUT PULONG ByteCount
,
374 PKSSTREAM_HEADER StreamHeader
;
378 PC_ASSERT(Tag
!= NULL
);
380 // get an irp from the queue
381 Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
383 // check if there is an irp
387 m_OutOfMapping
= TRUE
;
388 m_StartStream
= FALSE
;
389 return STATUS_UNSUCCESSFUL
;
392 // HACK get stream header
393 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
395 // store mapping in the free list
396 ExInterlockedInsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
, &m_IrpListLock
);
399 *PhysicalAddress
= MmGetPhysicalAddress(StreamHeader
->Data
);
400 *VirtualAddress
= StreamHeader
->Data
;
401 *ByteCount
= StreamHeader
->DataUsed
;
403 // decrement mapping count
404 InterlockedDecrement(&m_NumMappings
);
405 // decrement num data available
406 m_NumDataAvailable
-= StreamHeader
->DataUsed
;
409 Irp
->Tail
.Overlay
.DriverContext
[3] = Tag
;
412 return STATUS_SUCCESS
;
417 CIrpQueue::ReleaseMappingWithTag(
421 PLIST_ENTRY CurEntry
;
422 PKSSTREAM_HEADER StreamHeader
;
424 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag
);
426 // remove irp from used list
427 CurEntry
= ExInterlockedRemoveHeadList(&m_FreeIrpList
, &m_IrpListLock
);
431 // get irp from list entry
432 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
434 // HACK get stream header
435 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
437 // driver must release items in the same order
438 PC_ASSERT(Irp
->Tail
.Overlay
.DriverContext
[3] == Tag
);
440 // irp has been processed completly
441 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
443 // frame extend contains the original request size, DataUsed contains the real buffer size
444 // is different when kmixer performs channel conversion, upsampling etc
446 Irp
->IoStatus
.Information
= StreamHeader
->FrameExtent
;
448 // free stream header, no tag as wdmaud.drv allocates it atm
449 ExFreePool(StreamHeader
);
451 // complete the request
452 IoCompleteRequest(Irp
, IO_SOUND_INCREMENT
);
454 return STATUS_SUCCESS
;
459 CIrpQueue::HasLastMappingFailed()
461 return m_OutOfMapping
;
466 CIrpQueue::PrintQueueStatus()
473 CIrpQueue::SetMinimumDataThreshold(
474 ULONG MinimumDataThreshold
)
477 m_MinimumDataThreshold
= MinimumDataThreshold
;
482 CIrpQueue::GetMinimumDataThreshold()
484 return m_MinimumDataThreshold
;
491 IN IIrpQueue
**Queue
)
493 CIrpQueue
*This
= new(NonPagedPool
, TAG_PORTCLASS
)CIrpQueue(NULL
);
495 return STATUS_INSUFFICIENT_RESOURCES
;
499 *Queue
= (IIrpQueue
*)This
;
500 return STATUS_SUCCESS
;