Sync to trunk (r44371)
[reactos.git] / reactos / drivers / wdm / audio / backpln / portcls / irpstream.cpp
1 /*
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
7 */
8
9 #include "private.hpp"
10
11
12 class CIrpQueue : public IIrpQueue
13 {
14 public:
15 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
16
17 STDMETHODIMP_(ULONG) AddRef()
18 {
19 InterlockedIncrement(&m_Ref);
20 return m_Ref;
21 }
22 STDMETHODIMP_(ULONG) Release()
23 {
24 InterlockedDecrement(&m_Ref);
25
26 if (!m_Ref)
27 {
28 delete this;
29 return 0;
30 }
31 return m_Ref;
32 }
33 IMP_IIrpQueue;
34 CIrpQueue(IUnknown *OuterUnknown){}
35 virtual ~CIrpQueue(){}
36
37 protected:
38 volatile ULONG m_CurrentOffset;
39 LONG m_NumMappings;
40 ULONG m_NumDataAvailable;
41 BOOL m_StartStream;
42 PKSPIN_CONNECT m_ConnectDetails;
43 PKSDATAFORMAT_WAVEFORMATEX m_DataFormat;
44
45 KSPIN_LOCK m_IrpListLock;
46 LIST_ENTRY m_IrpList;
47 LIST_ENTRY m_FreeIrpList;
48 PIRP m_Irp;
49 PVOID m_SilenceBuffer;
50
51 ULONG m_OutOfMapping;
52 ULONG m_MaxFrameSize;
53 ULONG m_Alignment;
54 ULONG m_MinimumDataThreshold;
55
56 LONG m_Ref;
57
58 };
59
60 #define OFFSET_HEADERINDEX (0)
61 #define OFFSET_STREAMHEADER (2)
62 #define OFFSET_HEADERCOUNT (3)
63
64
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])
68
69
70 NTSTATUS
71 NTAPI
72 CIrpQueue::QueryInterface(
73 IN REFIID refiid,
74 OUT PVOID* Output)
75 {
76 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
77 {
78 *Output = PVOID(PUNKNOWN(this));
79 PUNKNOWN(*Output)->AddRef();
80 return STATUS_SUCCESS;
81 }
82
83 return STATUS_UNSUCCESSFUL;
84 }
85
86 NTSTATUS
87 NTAPI
88 CIrpQueue::Init(
89 IN KSPIN_CONNECT *ConnectDetails,
90 IN PKSDATAFORMAT DataFormat,
91 IN PDEVICE_OBJECT DeviceObject,
92 IN ULONG FrameSize,
93 IN ULONG Alignment,
94 IN PVOID SilenceBuffer)
95 {
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;
102
103 InitializeListHead(&m_IrpList);
104 InitializeListHead(&m_FreeIrpList);
105 KeInitializeSpinLock(&m_IrpListLock);
106
107 return STATUS_SUCCESS;
108 }
109
110 NTSTATUS
111 NTAPI
112 CIrpQueue::AddMapping(
113 IN PIRP Irp,
114 OUT PULONG Data)
115 {
116 PKSSTREAM_HEADER Header;
117 NTSTATUS Status = STATUS_SUCCESS;
118 PIO_STACK_LOCATION IoStack;
119 ULONG NumHeaders, NumData, Index;
120 PMDL Mdl;
121
122 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
123
124 // get current irp stack location
125 IoStack = IoGetCurrentIrpStackLocation(Irp);
126
127 if (!Irp->MdlAddress)
128 {
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);
136 else
137 PC_ASSERT(0);
138
139 // check for success
140 if (!NT_SUCCESS(Status))
141 {
142 DPRINT("KsProbeStreamIrp failed with %x\n", Status);
143 return Status;
144 }
145 }
146
147 // get first stream header
148
149 if (Irp->RequestorMode == UserMode)
150 Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
151 else
152 Header = (PKSSTREAM_HEADER)Irp->UserBuffer;
153
154 // sanity check
155 PC_ASSERT(Header);
156
157 // calculate num headers
158 NumHeaders = IoStack->Parameters.DeviceIoControl.OutputBufferLength / Header->Size;
159
160 // assume headers of same length
161 PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength % Header->Size == 0);
162
163
164 // get first audio buffer
165 Mdl = Irp->MdlAddress;
166 // sanity check
167 PC_ASSERT(Mdl);
168
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);
173
174 // store current header index
175 Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
176
177 NumData = 0;
178 // prepare all headers
179 for(Index = 0; Index < NumHeaders; Index++)
180 {
181 // sanity checks
182 PC_ASSERT(Header);
183 PC_ASSERT(Mdl);
184
185 if (Irp->RequestorMode == UserMode)
186 {
187 Header->Data = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
188 }
189
190 if (!Header->Data)
191 {
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;
198
199 IoCompleteRequest(Irp, IO_NO_INCREMENT);
200 return STATUS_INSUFFICIENT_RESOURCES;
201 }
202
203 // increment num mappings
204 InterlockedIncrement(&m_NumMappings);
205
206 // increment available data
207 InterlockedExchangeAdd((PLONG)&m_NumDataAvailable,
208 (max(Header->DataUsed, Header->FrameExtent)));
209
210 NumData += max(Header->DataUsed, Header->FrameExtent);
211
212 // move to next header
213 Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
214
215 // move to next mdl
216 Mdl = Mdl->Next;
217 }
218
219 DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders, NumData, m_MaxFrameSize, m_NumDataAvailable);
220 *Data = NumData;
221
222 // mark irp as pending
223 IoMarkIrpPending(Irp);
224
225 // add irp to cancelable queue
226 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
227
228 // done
229 return Status;
230 }
231
232 NTSTATUS
233 NTAPI
234 CIrpQueue::GetMapping(
235 OUT PUCHAR * Buffer,
236 OUT PULONG BufferSize)
237 {
238 PIRP Irp;
239 ULONG Offset;
240 //PIO_STACK_LOCATION IoStack;
241 PKSSTREAM_HEADER StreamHeader;
242
243 // check if there is an irp in the partially processed
244 if (m_Irp)
245 {
246 // use last irp
247 if (m_Irp->Cancel == FALSE)
248 {
249 Irp = m_Irp;
250 Offset = m_CurrentOffset;
251 }
252 else
253 {
254 // irp has been cancelled
255 m_Irp->IoStatus.Status = STATUS_CANCELLED;
256 IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
257 m_Irp = Irp = NULL;
258 }
259 }
260 else
261 {
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;
265 }
266
267 if (!Irp)
268 {
269 DPRINT("NoIrp\n");
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;
279 }
280
281 // get stream header
282 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
283
284 // sanity check
285 PC_ASSERT(StreamHeader);
286
287 // store buffersize
288 if (StreamHeader->DataUsed)
289 *BufferSize = StreamHeader->DataUsed - Offset;
290 else
291 *BufferSize = StreamHeader->FrameExtent - Offset;
292
293 PC_ASSERT(*BufferSize);
294
295 // store buffer
296 *Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
297
298 // unset flag that no irps are available
299 m_OutOfMapping = FALSE;
300
301 return STATUS_SUCCESS;
302 }
303
304 VOID
305 NTAPI
306 CIrpQueue::UpdateMapping(
307 IN ULONG BytesWritten)
308 {
309 PKSSTREAM_HEADER StreamHeader;
310 ULONG Size, NumData, Index;
311
312 if (!m_Irp)
313 {
314 // silence buffer was used
315 return;
316 }
317
318 // get stream header
319 StreamHeader = (PKSSTREAM_HEADER)STREAMHEADER_CURRENT(m_Irp);
320
321 // sanity check
322 // ASSERT(StreamHeader);
323
324 // add to current offset
325 InterlockedExchangeAdd((volatile PLONG)&m_CurrentOffset, (LONG)BytesWritten);
326
327 // decrement available data counter
328 m_NumDataAvailable -= BytesWritten;
329
330 if (StreamHeader->DataUsed)
331 Size = StreamHeader->DataUsed;
332 else
333 Size = StreamHeader->FrameExtent;
334
335 PC_ASSERT(Size);
336
337 if (m_CurrentOffset >= Size)
338 {
339 if (STREAMHEADER_INDEX(m_Irp) + 1 < STREAMHEADER_COUNT(m_Irp))
340 {
341 // the irp has at least one more stream header
342 m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(STREAMHEADER_INDEX(m_Irp) + 1);
343
344 // get next stream header
345 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
346
347 // store next stream header
348 STREAMHEADER_CURRENT(m_Irp) = (PVOID)StreamHeader;
349
350 // reset current offset
351 m_CurrentOffset = 0;
352
353 // done
354 return;
355 }
356
357 // irp has been processed completly
358
359 NumData = 0;
360 if (m_Irp->RequestorMode == KernelMode)
361 StreamHeader = (PKSSTREAM_HEADER)m_Irp->UserBuffer;
362 else
363 StreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
364
365 // loop all stream headers
366 for(Index = 0; Index < STREAMHEADER_COUNT(m_Irp); Index++)
367 {
368 PC_ASSERT(StreamHeader);
369
370 // add size of buffer
371 // depends on if the buffer is input / output
372 if (StreamHeader->DataUsed)
373 Size = StreamHeader->DataUsed;
374 else
375 Size = StreamHeader->FrameExtent;
376
377 // increment size
378 NumData += Size;
379
380 // get next stream header
381 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
382 }
383
384 if (m_ConnectDetails->Interface.Id == KSINTERFACE_STANDARD_LOOPED_STREAMING)
385 {
386 // looped streaming repeat the buffers untill
387 // the caller decides to stop the streams
388
389 // reset stream header index
390 m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
391 // re-insert irp
392 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
393 // clear current irp
394 m_Irp = NULL;
395 // reset offset
396 m_CurrentOffset = 0;
397 // increment available data
398 InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, NumData);
399 // done
400 return;
401 }
402
403 m_Irp->IoStatus.Status = STATUS_SUCCESS;
404 m_Irp->IoStatus.Information = NumData;
405
406 // complete the request
407 IoCompleteRequest(m_Irp, IO_SOUND_INCREMENT);
408 // remove irp as it is complete
409 m_Irp = NULL;
410 m_CurrentOffset = 0;
411 }
412 }
413
414 ULONG
415 NTAPI
416 CIrpQueue::NumMappings()
417 {
418
419 // returns the amount of mappings available
420 return m_NumMappings;
421 }
422
423 ULONG
424 NTAPI
425 CIrpQueue::NumData()
426 {
427 // returns the amount of audio stream data available
428 return m_NumDataAvailable;
429 }
430
431
432 BOOL
433 NTAPI
434 CIrpQueue::MinimumDataAvailable()
435 {
436 BOOL Result;
437
438 if (m_StartStream)
439 return TRUE;
440
441 if (m_MinimumDataThreshold < m_NumDataAvailable)
442 {
443 m_StartStream = TRUE;
444 Result = TRUE;
445 }
446 else
447 {
448 Result = FALSE;
449 }
450 return Result;
451 }
452
453 BOOL
454 NTAPI
455 CIrpQueue::CancelBuffers()
456 {
457 // is there an active irp
458 if (m_Irp)
459 {
460 // re-insert it to cancelable queue
461 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
462 //set it to zero
463 m_Irp = NULL;
464 }
465
466 // cancel all irps
467 KsCancelIo(&m_IrpList, &m_IrpListLock);
468 // reset stream start flag
469 m_StartStream = FALSE;
470 // done
471 return TRUE;
472 }
473
474 VOID
475 NTAPI
476 CIrpQueue::UpdateFormat(
477 PKSDATAFORMAT DataFormat)
478 {
479 m_DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
480 m_MinimumDataThreshold = m_DataFormat->WaveFormatEx.nAvgBytesPerSec / 3;
481 m_StartStream = FALSE;
482 m_NumDataAvailable = 0;
483 }
484
485 NTSTATUS
486 NTAPI
487 CIrpQueue::GetMappingWithTag(
488 IN PVOID Tag,
489 OUT PPHYSICAL_ADDRESS PhysicalAddress,
490 OUT PVOID *VirtualAddress,
491 OUT PULONG ByteCount,
492 OUT PULONG Flags)
493 {
494 PKSSTREAM_HEADER StreamHeader;
495 PIRP Irp;
496
497 *Flags = 0;
498 PC_ASSERT(Tag != NULL);
499
500 // get an irp from the queue
501 Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
502
503 // check if there is an irp
504 if (!Irp)
505 {
506 // no irp available
507 m_OutOfMapping = TRUE;
508 m_StartStream = FALSE;
509 return STATUS_UNSUCCESSFUL;
510 }
511
512 //FIXME support more than one stream header
513 PC_ASSERT(STREAMHEADER_COUNT(Irp) == 1);
514
515 // HACK get stream header
516 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
517
518 // store mapping in the free list
519 ExInterlockedInsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry, &m_IrpListLock);
520
521 // return mapping
522 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
523 *VirtualAddress = StreamHeader->Data;
524 *ByteCount = StreamHeader->DataUsed;
525
526 // decrement mapping count
527 InterlockedDecrement(&m_NumMappings);
528 // decrement num data available
529 m_NumDataAvailable -= StreamHeader->DataUsed;
530
531 // store tag in irp
532 Irp->Tail.Overlay.DriverContext[3] = Tag;
533
534 // done
535 return STATUS_SUCCESS;
536 }
537
538 NTSTATUS
539 NTAPI
540 CIrpQueue::ReleaseMappingWithTag(
541 IN PVOID Tag)
542 {
543 PIRP Irp;
544 PLIST_ENTRY CurEntry;
545 PKSSTREAM_HEADER StreamHeader;
546
547 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag);
548
549 // remove irp from used list
550 CurEntry = ExInterlockedRemoveHeadList(&m_FreeIrpList, &m_IrpListLock);
551 // sanity check
552 PC_ASSERT(CurEntry);
553
554 // get irp from list entry
555 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
556
557 // HACK get stream header
558 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
559
560 // driver must release items in the same order
561 PC_ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
562
563 // irp has been processed completly
564 Irp->IoStatus.Status = STATUS_SUCCESS;
565
566 // frame extend contains the original request size, DataUsed contains the real buffer size
567 // is different when kmixer performs channel conversion, upsampling etc
568
569 Irp->IoStatus.Information = StreamHeader->FrameExtent;
570
571 // free stream header, no tag as wdmaud.drv allocates it atm
572 ExFreePool(StreamHeader);
573
574 // complete the request
575 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
576
577 return STATUS_SUCCESS;
578 }
579
580 BOOL
581 NTAPI
582 CIrpQueue::HasLastMappingFailed()
583 {
584 return m_OutOfMapping;
585 }
586
587 ULONG
588 NTAPI
589 CIrpQueue::GetCurrentIrpOffset()
590 {
591
592 return m_CurrentOffset;
593 }
594
595 VOID
596 NTAPI
597 CIrpQueue::SetMinimumDataThreshold(
598 ULONG MinimumDataThreshold)
599 {
600
601 m_MinimumDataThreshold = MinimumDataThreshold;
602 }
603
604 ULONG
605 NTAPI
606 CIrpQueue::GetMinimumDataThreshold()
607 {
608 return m_MinimumDataThreshold;
609 }
610
611
612 NTSTATUS
613 NTAPI
614 NewIrpQueue(
615 IN IIrpQueue **Queue)
616 {
617 CIrpQueue *This = new(NonPagedPool, TAG_PORTCLASS)CIrpQueue(NULL);
618 if (!This)
619 return STATUS_INSUFFICIENT_RESOURCES;
620
621 This->AddRef();
622
623 *Queue = (IIrpQueue*)This;
624 return STATUS_SUCCESS;
625 }
626