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
;
55 #define OFFSET_HEADERINDEX (0)
56 #define OFFSET_STREAMHEADER (2)
57 #define OFFSET_HEADERCOUNT (3)
60 #define STREAMHEADER_INDEX(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX]))
61 #define STREAMHEADER_COUNT(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT]))
62 #define STREAMHEADER_CURRENT(Irp) (Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER])
67 CIrpQueue::QueryInterface(
71 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
73 *Output
= PVOID(PUNKNOWN(this));
74 PUNKNOWN(*Output
)->AddRef();
75 return STATUS_SUCCESS
;
78 return STATUS_UNSUCCESSFUL
;
84 IN KSPIN_CONNECT
*ConnectDetails
,
88 m_ConnectDetails
= ConnectDetails
;
89 m_MaxFrameSize
= FrameSize
;
90 m_Alignment
= Alignment
;
92 InitializeListHead(&m_IrpList
);
93 InitializeListHead(&m_FreeIrpList
);
94 KeInitializeSpinLock(&m_IrpListLock
);
96 return STATUS_SUCCESS
;
101 CIrpQueue::AddMapping(
105 PKSSTREAM_HEADER Header
;
106 NTSTATUS Status
= STATUS_SUCCESS
;
107 PIO_STACK_LOCATION IoStack
;
108 ULONG NumHeaders
, NumData
, Index
;
111 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL
);
113 // get current irp stack location
114 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_SYSTEMADDRESS
, 0);
123 else if (IoStack
->Parameters
.DeviceIoControl
.IoControlCode
== IOCTL_KS_READ_STREAM
)
124 Status
= KsProbeStreamIrp(Irp
, KSSTREAM_READ
| KSPROBE_ALLOCATEMDL
| KSPROBE_PROBEANDLOCK
| KSPROBE_SYSTEMADDRESS
, 0);
129 if (!NT_SUCCESS(Status
))
131 DPRINT("KsProbeStreamIrp failed with %x\n", Status
);
136 // get first stream header
138 if (Irp
->RequestorMode
== UserMode
)
139 Header
= (PKSSTREAM_HEADER
)Irp
->AssociatedIrp
.SystemBuffer
;
141 Header
= (PKSSTREAM_HEADER
)Irp
->UserBuffer
;
146 // calculate num headers
147 NumHeaders
= IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
/ Header
->Size
;
149 // assume headers of same length
150 PC_ASSERT(IoStack
->Parameters
.DeviceIoControl
.OutputBufferLength
% Header
->Size
== 0);
153 // get first audio buffer
154 Mdl
= Irp
->MdlAddress
;
158 // store the current stream header
159 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_STREAMHEADER
] = (PVOID
)Header
;
160 // store header count
161 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERCOUNT
] = UlongToPtr(NumHeaders
);
163 // store current header index
164 Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(0);
167 // prepare all headers
168 for(Index
= 0; Index
< NumHeaders
; Index
++)
174 if (Irp
->RequestorMode
== UserMode
)
176 Header
->Data
= MmGetSystemAddressForMdlSafe(Mdl
, NormalPagePriority
);
181 // insufficient resources
182 ExFreePool(Irp
->AssociatedIrp
.SystemBuffer
);
183 Irp
->AssociatedIrp
.SystemBuffer
= NULL
;
184 // complete and forget request
185 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
186 Irp
->IoStatus
.Information
= 0;
188 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
189 return STATUS_INSUFFICIENT_RESOURCES
;
192 // increment num mappings
193 InterlockedIncrement(&m_NumMappings
);
195 // increment available data
196 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
,
197 (max(Header
->DataUsed
, Header
->FrameExtent
)));
199 NumData
+= max(Header
->DataUsed
, Header
->FrameExtent
);
201 // move to next header
202 Header
= (PKSSTREAM_HEADER
)((ULONG_PTR
)Header
+ Header
->Size
);
208 DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders
, NumData
, m_MaxFrameSize
, m_NumDataAvailable
);
211 // mark irp as pending
212 IoMarkIrpPending(Irp
);
214 // add irp to cancelable queue
215 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, Irp
, KsListEntryTail
, NULL
);
217 // disable mapping failed status
218 m_OutOfMapping
= FALSE
;
226 CIrpQueue::GetMapping(
228 OUT PULONG BufferSize
)
232 //PIO_STACK_LOCATION IoStack;
233 PKSSTREAM_HEADER StreamHeader
;
235 // check if there is an irp in the partially processed
239 if (m_Irp
->Cancel
== FALSE
)
242 Offset
= m_CurrentOffset
;
246 // irp has been cancelled
247 m_Irp
->IoStatus
.Status
= STATUS_CANCELLED
;
248 IoCompleteRequest(m_Irp
, IO_NO_INCREMENT
);
254 // get a fresh new irp from the queue
255 m_Irp
= Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
256 m_CurrentOffset
= Offset
= 0;
261 // no irp buffer available
263 return STATUS_UNSUCCESSFUL
;
267 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
270 PC_ASSERT(StreamHeader
);
273 if (StreamHeader
->DataUsed
)
274 *BufferSize
= StreamHeader
->DataUsed
- Offset
;
276 *BufferSize
= StreamHeader
->FrameExtent
- Offset
;
278 PC_ASSERT(*BufferSize
);
281 *Buffer
= &((PUCHAR
)StreamHeader
->Data
)[Offset
];
283 // unset flag that no irps are available
284 m_OutOfMapping
= FALSE
;
286 return STATUS_SUCCESS
;
291 CIrpQueue::UpdateMapping(
292 IN ULONG BytesWritten
)
294 PKSSTREAM_HEADER StreamHeader
;
295 ULONG Size
, NumData
, Index
;
296 //PMDL CurMdl, NextMdl;
300 // silence buffer was used
305 StreamHeader
= (PKSSTREAM_HEADER
)STREAMHEADER_CURRENT(m_Irp
);
308 // ASSERT(StreamHeader);
310 // add to current offset
311 InterlockedExchangeAdd((volatile PLONG
)&m_CurrentOffset
, (LONG
)BytesWritten
);
313 // decrement available data counter
314 m_NumDataAvailable
-= BytesWritten
;
316 if (StreamHeader
->DataUsed
)
317 Size
= StreamHeader
->DataUsed
;
319 Size
= StreamHeader
->FrameExtent
;
323 if (m_CurrentOffset
>= Size
)
325 if (STREAMHEADER_INDEX(m_Irp
) + 1 < STREAMHEADER_COUNT(m_Irp
))
327 // the irp has at least one more stream header
328 m_Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(STREAMHEADER_INDEX(m_Irp
) + 1);
330 // get next stream header
331 StreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamHeader
+ StreamHeader
->Size
);
333 // store next stream header
334 STREAMHEADER_CURRENT(m_Irp
) = (PVOID
)StreamHeader
;
336 // reset current offset
343 // irp has been processed completly
346 if (m_Irp
->RequestorMode
== KernelMode
)
347 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->UserBuffer
;
349 StreamHeader
= (PKSSTREAM_HEADER
)m_Irp
->AssociatedIrp
.SystemBuffer
;
351 // loop all stream headers
352 for(Index
= 0; Index
< STREAMHEADER_COUNT(m_Irp
); Index
++)
354 PC_ASSERT(StreamHeader
);
356 // add size of buffer
357 // depends on if the buffer is input / output
358 if (StreamHeader
->DataUsed
)
359 Size
= StreamHeader
->DataUsed
;
361 Size
= StreamHeader
->FrameExtent
;
366 // get next stream header
367 StreamHeader
= (PKSSTREAM_HEADER
)((ULONG_PTR
)StreamHeader
+ StreamHeader
->Size
);
370 if (m_ConnectDetails
->Interface
.Id
== KSINTERFACE_STANDARD_LOOPED_STREAMING
)
372 // looped streaming repeat the buffers untill
373 // the caller decides to stop the streams
375 // reset stream header index
376 m_Irp
->Tail
.Overlay
.DriverContext
[OFFSET_HEADERINDEX
] = UlongToPtr(0);
378 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
383 // increment available data
384 InterlockedExchangeAdd((PLONG
)&m_NumDataAvailable
, NumData
);
389 // now free allocated mdls
390 CurMdl
= m_Irp
->MdlAddress
;
391 for(Index
= 0; Index
< STREAMHEADER_COUNT(m_Irp
); Index
++)
397 NextMdl
= CurMdl
->Next
;
399 // check if mdl is locked
400 if (CurMdl
->MdlFlags
& MDL_PAGES_LOCKED
)
403 MmUnlockPages(CurMdl
);
409 // proceed to next mdl
413 // all mdls have been freed now
414 m_Irp
->MdlAddress
= NULL
;
416 // free allocated KSSTREAM_HEADER
417 ExFreePool(m_Irp
->AssociatedIrp
.SystemBuffer
);
419 // is this really needed?
420 m_Irp
->AssociatedIrp
.SystemBuffer
= NULL
;
423 // store operation status
424 m_Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
425 m_Irp
->IoStatus
.Information
= NumData
;
427 // complete the request
428 IoCompleteRequest(m_Irp
, IO_SOUND_INCREMENT
);
430 // remove irp as it is complete
442 // returns the amount of audio stream data available
443 return m_NumDataAvailable
;
448 CIrpQueue::CancelBuffers()
450 // is there an active irp
453 // re-insert it to cancelable queue
454 KsAddIrpToCancelableQueue(&m_IrpList
, &m_IrpListLock
, m_Irp
, KsListEntryTail
, NULL
);
460 KsCancelIo(&m_IrpList
, &m_IrpListLock
);
461 // reset number of mappings
463 // reset number of data available
464 m_NumDataAvailable
= 0;
472 CIrpQueue::GetMappingWithTag(
474 OUT PPHYSICAL_ADDRESS PhysicalAddress
,
475 OUT PVOID
*VirtualAddress
,
476 OUT PULONG ByteCount
,
479 PKSSTREAM_HEADER StreamHeader
;
483 PC_ASSERT(Tag
!= NULL
);
485 // get an irp from the queue
486 Irp
= KsRemoveIrpFromCancelableQueue(&m_IrpList
, &m_IrpListLock
, KsListEntryHead
, KsAcquireAndRemoveOnlySingleItem
);
488 // check if there is an irp
492 m_OutOfMapping
= TRUE
;
493 return STATUS_NOT_FOUND
;
496 //FIXME support more than one stream header
497 PC_ASSERT(STREAMHEADER_COUNT(Irp
) == 1);
499 // HACK get stream header
500 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
502 // store mapping in the free list
503 ExInterlockedInsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
, &m_IrpListLock
);
506 *PhysicalAddress
= MmGetPhysicalAddress(StreamHeader
->Data
);
507 *VirtualAddress
= StreamHeader
->Data
;
508 *ByteCount
= StreamHeader
->DataUsed
;
510 // decrement mapping count
511 InterlockedDecrement(&m_NumMappings
);
512 // decrement num data available
513 m_NumDataAvailable
-= StreamHeader
->DataUsed
;
516 Irp
->Tail
.Overlay
.DriverContext
[3] = Tag
;
519 return STATUS_SUCCESS
;
524 CIrpQueue::ReleaseMappingWithTag(
528 PLIST_ENTRY CurEntry
;
529 PKSSTREAM_HEADER StreamHeader
;
531 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag
);
533 // remove irp from used list
534 CurEntry
= ExInterlockedRemoveHeadList(&m_FreeIrpList
, &m_IrpListLock
);
538 // get irp from list entry
539 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
541 // HACK get stream header
542 StreamHeader
= (PKSSTREAM_HEADER
)Irp
->Tail
.Overlay
.DriverContext
[2];
544 // driver must release items in the same order
545 PC_ASSERT(Irp
->Tail
.Overlay
.DriverContext
[3] == Tag
);
547 // irp has been processed completly
548 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
550 // frame extend contains the original request size, DataUsed contains the real buffer size
551 // is different when kmixer performs channel conversion, upsampling etc
553 Irp
->IoStatus
.Information
= StreamHeader
->FrameExtent
;
555 // complete the request
556 IoCompleteRequest(Irp
, IO_SOUND_INCREMENT
);
558 return STATUS_SUCCESS
;
563 CIrpQueue::HasLastMappingFailed()
565 return m_OutOfMapping
;
570 CIrpQueue::GetCurrentIrpOffset()
573 return m_CurrentOffset
;
578 CIrpQueue::GetAcquiredTagRange(
585 PLIST_ENTRY CurEntry
;
587 KeAcquireSpinLock(&m_IrpListLock
, &OldLevel
);
589 if (!IsListEmpty(&m_FreeIrpList
))
592 CurEntry
= RemoveHeadList(&m_FreeIrpList
);
593 // get irp from list entry
594 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
596 // get tag of first acquired buffer
597 *FirstTag
= Irp
->Tail
.Overlay
.DriverContext
[3];
600 InsertHeadList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
);
603 CurEntry
= RemoveTailList(&m_FreeIrpList
);
604 // get irp from list entry
605 Irp
= (PIRP
)CONTAINING_RECORD(CurEntry
, IRP
, Tail
.Overlay
.ListEntry
);
607 // get tag of first acquired buffer
608 *LastTag
= Irp
->Tail
.Overlay
.DriverContext
[3];
611 InsertTailList(&m_FreeIrpList
, &Irp
->Tail
.Overlay
.ListEntry
);
618 KeReleaseSpinLock(&m_IrpListLock
, OldLevel
);
626 IN IIrpQueue
**Queue
)
628 CIrpQueue
*This
= new(NonPagedPool
, TAG_PORTCLASS
)CIrpQueue(NULL
);
630 return STATUS_INSUFFICIENT_RESOURCES
;
634 *Queue
= (IIrpQueue
*)This
;
635 return STATUS_SUCCESS
;