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 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
113 // get current irp stack location
114 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
118 if (!Irp
->MdlAddress
)
120 // ioctl from KsStudio
121 // Wdmaud already probes buffers, therefore no need to probe it again
122 // probe the stream irp
123 if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_WRITE_STREAM
)
124 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_WRITE
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_ALLOWFORMATCHANGE
| KSPROBE_SYSTEMADDRESS
, 0);
126 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_READ
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_ALLOWFORMATCHANGE
| KSPROBE_SYSTEMADDRESS
, 0);
129 if (!NT_SUCCESS(Status
))
131 DPRINT("KsProbeStreamIrp failed with %x\n", Status
);
135 // get the stream header
136 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
138 PC_ASSERT(Irp
->MdlAddress
);
140 DPRINT("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
);
142 Header
->Data
= MmGetSystemAddressForMdlSafe(Irp
->MdlAddress
, NormalPagePriority
);
143 PC_ASSERT(Header
->Data
);
144 //PC_ASSERT(Header->Size == IoStack->Parameters.DeviceIoControl.OutputBufferLength);
147 Irp
->Tail
.Overlay
.DriverContext
[2] = (PVOID
)Header
;
152 // dont exceed max frame size
153 //PC_ASSERT(m_MaxFrameSize >= Header->DataUsed);
155 // increment num mappings
156 InterlockedIncrement(&m_NumMappings
);
158 // increment num data available
159 m_NumDataAvailable
+= Header
->DataUsed
;
161 // mark irp as pending
162 IoMarkIrpPending(Irp
);
164 // add irp to cancelable queue
165 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
173 CIrpQueue::GetMapping(
175 OUT PULONG BufferSize
)
179 //PIO_STACK_LOCATION IoStack;
180 PKSSTREAM_HEADER StreamHeader
;
182 // check if there is an irp in the partially processed
186 if (m_Irp
->Cancel
== FALSE
)
189 Offset
= m_CurrentOffset
;
193 // irp has been cancelled
194 m_Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
195 IoCompleteRequest(m_Irp
, IO_NO_INCREMENT
);
201 // get a fresh new irp from the queue
202 m_Irp
= Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
203 m_CurrentOffset
= Offset
= 0;
209 // no irp available, use silence buffer
210 *Buffer
= (PUCHAR
)m_SilenceBuffer
;
211 *BufferSize
= m_MaxFrameSize
;
212 // flag for port wave pci driver
213 m_OutOfMapping
= TRUE
;
214 // indicate flag to restart fast buffering
215 m_StartStream
= FALSE
;
216 return STATUS_SUCCESS
;
220 // get current irp stack location
221 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
224 StreamHeader
= (PKSSTREAM_HEADER
)IoStack
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
226 // HACK get stream header
227 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
231 PC_ASSERT(StreamHeader
);
234 if (StreamHeader
->DataUsed
)
235 *BufferSize
= StreamHeader
->DataUsed
- Offset
;
237 *BufferSize
= StreamHeader
->FrameExtent
- Offset
;
239 PC_ASSERT(*BufferSize
);
242 *Buffer
= &((PUCHAR
)StreamHeader
->Data
)[Offset
];
244 // unset flag that no irps are available
245 m_OutOfMapping
= FALSE
;
247 return STATUS_SUCCESS
;
252 CIrpQueue::UpdateMapping(
253 IN ULONG BytesWritten
)
255 PKSSTREAM_HEADER StreamHeader
;
260 // silence buffer was used
265 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->Tail
.Overlay
.DriverContext
[2];
268 // ASSERT(StreamHeader);
270 // add to current offset
271 m_CurrentOffset
+= BytesWritten
;
273 // decrement available data counter
274 m_NumDataAvailable
-= BytesWritten
;
276 if (StreamHeader
->DataUsed
)
277 Size
= StreamHeader
->DataUsed
;
279 Size
= StreamHeader
->FrameExtent
;
283 if (m_CurrentOffset
>= Size
)
285 // irp has been processed completly
286 m_Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
288 // frame extend contains the original request size, DataUsed contains the real buffer size
289 //is different when kmixer performs channel conversion, upsampling etc
291 m_Irp
->IoStatus
.Information
= Size
;
293 PC_ASSERT_IRQL(DISPATCH_LEVEL
);
294 MmUnlockPages(m_Irp
->MdlAddress
);
295 IoFreeMdl(m_Irp
->MdlAddress
);
296 m_Irp
->MdlAddress
= NULL
;
297 ExFreePool(m_Irp
->AssociatedIrp
.SystemBuffer
);
298 m_Irp
->AssociatedIrp
.SystemBuffer
= NULL
;
300 // complete the request
301 IoCompleteRequest(m_Irp
, IO_SOUND_INCREMENT
);
302 // remove irp as it is complete
310 CIrpQueue::NumMappings()
313 // returns the amount of mappings available
314 return m_NumMappings
;
321 // returns the amount of audio stream data available
322 return m_NumDataAvailable
;
328 CIrpQueue::MinimumDataAvailable()
335 if (m_MinimumDataThreshold
< m_NumDataAvailable
)
337 m_StartStream
= TRUE
;
349 CIrpQueue::CancelBuffers()
352 m_StartStream
= FALSE
;
358 CIrpQueue::UpdateFormat(
359 PKSDATAFORMAT DataFormat
)
361 m_DataFormat
= (PKSDATAFORMAT_WAVEFORMATEX
)DataFormat
;
362 m_MinimumDataThreshold
= m_DataFormat
->WaveFormatEx
.nAvgBytesPerSec
/ 3;
363 m_StartStream
= FALSE
;
364 m_NumDataAvailable
= 0;
369 CIrpQueue::GetMappingWithTag(
371 OUT PPHYSICAL_ADDRESS PhysicalAddress
,
372 OUT PVOID
*VirtualAddress
,
373 OUT PULONG ByteCount
,
376 PKSSTREAM_HEADER StreamHeader
;
380 PC_ASSERT(Tag
!= NULL
);
382 // get an irp from the queue
383 Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
385 // check if there is an irp
389 m_OutOfMapping
= TRUE
;
390 m_StartStream
= FALSE
;
391 return STATUS_UNSUCCESSFUL
;
394 // HACK get stream header
395 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
397 // store mapping in the free list
398 ExInterlockedInsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
, &m_IrpListLock
);
401 *PhysicalAddress
= MmGetPhysicalAddress(StreamHeader
->Data
);
402 *VirtualAddress
= StreamHeader
->Data
;
403 *ByteCount
= StreamHeader
->DataUsed
;
405 // decrement mapping count
406 InterlockedDecrement(&m_NumMappings
);
407 // decrement num data available
408 m_NumDataAvailable
-= StreamHeader
->DataUsed
;
411 Irp
->Tail
.Overlay
.DriverContext
[3] = Tag
;
414 return STATUS_SUCCESS
;
419 CIrpQueue::ReleaseMappingWithTag(
423 PLIST_ENTRY CurEntry
;
424 PKSSTREAM_HEADER StreamHeader
;
426 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag
);
428 // remove irp from used list
429 CurEntry
= ExInterlockedRemoveHeadList(&m_FreeIrpList
, &m_IrpListLock
);
433 // get irp from list entry
434 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
436 // HACK get stream header
437 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
439 // driver must release items in the same order
440 PC_ASSERT(Irp
->Tail
.Overlay
.DriverContext
[3] == Tag
);
442 // irp has been processed completly
443 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
445 // frame extend contains the original request size, DataUsed contains the real buffer size
446 // is different when kmixer performs channel conversion, upsampling etc
448 Irp
->IoStatus
.Information
= StreamHeader
->FrameExtent
;
450 // free stream header, no tag as wdmaud.drv allocates it atm
451 ExFreePool(StreamHeader
);
453 // complete the request
454 IoCompleteRequest(Irp
, IO_SOUND_INCREMENT
);
456 return STATUS_SUCCESS
;
461 CIrpQueue::HasLastMappingFailed()
463 return m_OutOfMapping
;
468 CIrpQueue::PrintQueueStatus()
475 CIrpQueue::SetMinimumDataThreshold(
476 ULONG MinimumDataThreshold
)
479 m_MinimumDataThreshold
= MinimumDataThreshold
;
484 CIrpQueue::GetMinimumDataThreshold()
486 return m_MinimumDataThreshold
;
493 IN IIrpQueue
**Queue
)
495 CIrpQueue
*This
= new(NonPagedPool
, TAG_PORTCLASS
)CIrpQueue(NULL
);
497 return STATUS_INSUFFICIENT_RESOURCES
;
501 *Queue
= (IIrpQueue
*)This
;
502 return STATUS_SUCCESS
;