f3b1d7b0e2f47345b87a3f3bb9d5e30c77712758
[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 ULONG m_CurrentOffset;
39 LONG m_NumMappings;
40 ULONG m_NumDataAvailable;
41 BOOL m_StartStream;
42 KSPIN_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 PUCHAR Buffer,
114 IN ULONG BufferSize,
115 IN PIRP Irp)
116 {
117 PKSSTREAM_HEADER Header;
118 NTSTATUS Status = STATUS_SUCCESS;
119 PIO_STACK_LOCATION IoStack;
120 ULONG NumHeaders, NumData, Index;
121 PMDL Mdl;
122
123 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
124
125 // get current irp stack location
126 IoStack = IoGetCurrentIrpStackLocation(Irp);
127
128 PC_ASSERT(!Buffer);
129
130 if (!Irp->MdlAddress)
131 {
132 // ioctl from KsStudio
133 // Wdmaud already probes buffers, therefore no need to probe it again
134 // probe the stream irp
135 if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_WRITE_STREAM)
136 Status = KsProbeStreamIrp(Irp, KSSTREAM_WRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
137 else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_READ_STREAM)
138 Status = KsProbeStreamIrp(Irp, KSSTREAM_READ | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
139 else
140 PC_ASSERT(0);
141
142 // check for success
143 if (!NT_SUCCESS(Status))
144 {
145 DPRINT("KsProbeStreamIrp failed with %x\n", Status);
146 return Status;
147 }
148 }
149
150 // get first stream header
151 Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
152
153 // calculate num headers
154 NumHeaders = IoStack->Parameters.DeviceIoControl.OutputBufferLength / Header->Size;
155
156 // assume headers of same length
157 PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength % Header->Size == 0);
158
159
160 // get first audio buffer
161 Mdl = Irp->MdlAddress;
162
163
164 // store the current stream header
165 Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER] = (PVOID)Header;
166 // store header count
167 Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT] = UlongToPtr(NumHeaders);
168
169 // store current header index
170 Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
171
172
173 NumData = 0;
174 // prepare all headers
175 for(Index = 0; Index < NumHeaders; Index++)
176 {
177 // sanity checks
178 PC_ASSERT(Header);
179 PC_ASSERT(Mdl);
180
181 Header->Data = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
182
183 if (!Header->Data)
184 {
185 // insufficient resources
186 ExFreePool(Irp->AssociatedIrp.SystemBuffer);
187 Irp->AssociatedIrp.SystemBuffer = NULL;
188 // complete and forget request
189 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
190 Irp->IoStatus.Information = 0;
191
192 IoCompleteRequest(Irp, IO_NO_INCREMENT);
193 return STATUS_INSUFFICIENT_RESOURCES;
194 }
195
196 // increment num mappings
197 InterlockedIncrement(&m_NumMappings);
198
199 // increment available data
200 InterlockedExchangeAdd((PLONG)&m_NumDataAvailable,
201 (max(Header->DataUsed, Header->FrameExtent)));
202
203 NumData += max(Header->DataUsed, Header->FrameExtent);
204
205 // move to next header
206 Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
207
208 // move to next mdl
209 Mdl = Mdl->Next;
210 }
211
212 DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders, NumData, m_MaxFrameSize, m_NumDataAvailable);
213
214
215 // mark irp as pending
216 IoMarkIrpPending(Irp);
217
218 // add irp to cancelable queue
219 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
220
221 // done
222 return Status;
223 }
224
225 NTSTATUS
226 NTAPI
227 CIrpQueue::GetMapping(
228 OUT PUCHAR * Buffer,
229 OUT PULONG BufferSize)
230 {
231 PIRP Irp;
232 ULONG Offset;
233 //PIO_STACK_LOCATION IoStack;
234 PKSSTREAM_HEADER StreamHeader;
235
236 // check if there is an irp in the partially processed
237 if (m_Irp)
238 {
239 // use last irp
240 if (m_Irp->Cancel == FALSE)
241 {
242 Irp = m_Irp;
243 Offset = m_CurrentOffset;
244 }
245 else
246 {
247 // irp has been cancelled
248 m_Irp->IoStatus.Status = STATUS_CANCELLED;
249 IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
250 m_Irp = Irp = NULL;
251 }
252 }
253 else
254 {
255 // get a fresh new irp from the queue
256 m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
257 m_CurrentOffset = Offset = 0;
258 }
259
260 if (!Irp)
261 {
262 DPRINT("NoIrp\n");
263 return STATUS_UNSUCCESSFUL;
264 // no irp available, use silence buffer
265 *Buffer = (PUCHAR)m_SilenceBuffer;
266 *BufferSize = m_MaxFrameSize;
267 // flag for port wave pci driver
268 m_OutOfMapping = TRUE;
269 // indicate flag to restart fast buffering
270 m_StartStream = FALSE;
271 return STATUS_SUCCESS;
272 }
273
274 // get stream header
275 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
276
277 // sanity check
278 PC_ASSERT(StreamHeader);
279
280 // store buffersize
281 if (StreamHeader->DataUsed)
282 *BufferSize = StreamHeader->DataUsed - Offset;
283 else
284 *BufferSize = StreamHeader->FrameExtent - Offset;
285
286 PC_ASSERT(*BufferSize);
287
288 // store buffer
289 *Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
290
291 // unset flag that no irps are available
292 m_OutOfMapping = FALSE;
293
294 return STATUS_SUCCESS;
295 }
296
297 VOID
298 NTAPI
299 CIrpQueue::UpdateMapping(
300 IN ULONG BytesWritten)
301 {
302 PKSSTREAM_HEADER StreamHeader;
303 ULONG Size, NumData, Index;
304
305 if (!m_Irp)
306 {
307 // silence buffer was used
308 return;
309 }
310
311 // get stream header
312 StreamHeader = (PKSSTREAM_HEADER)STREAMHEADER_CURRENT(m_Irp);
313
314 // sanity check
315 // ASSERT(StreamHeader);
316
317 // add to current offset
318 m_CurrentOffset += BytesWritten;
319
320 // decrement available data counter
321 m_NumDataAvailable -= BytesWritten;
322
323 if (StreamHeader->DataUsed)
324 Size = StreamHeader->DataUsed;
325 else
326 Size = StreamHeader->FrameExtent;
327
328 PC_ASSERT(Size);
329
330 if (m_CurrentOffset >= Size)
331 {
332 if (STREAMHEADER_INDEX(m_Irp) + 1 < STREAMHEADER_COUNT(m_Irp))
333 {
334 // the irp has at least one more stream header
335 m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(STREAMHEADER_INDEX(m_Irp) + 1);
336
337 // get next stream header
338 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
339
340 // store next stream header
341 STREAMHEADER_CURRENT(m_Irp) = (PVOID)StreamHeader;
342
343 // reset current offset
344 m_CurrentOffset = 0;
345
346 // done
347 return;
348 }
349
350 // irp has been processed completly
351
352 NumData = 0;
353 StreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
354
355 // loop all stream headers
356 for(Index = 0; Index < STREAMHEADER_COUNT(m_Irp); Index++)
357 {
358 PC_ASSERT(StreamHeader);
359
360 // add size of buffer
361 // depends on if the buffer is input / output
362 if (StreamHeader->DataUsed)
363 Size = StreamHeader->DataUsed;
364 else
365 Size = StreamHeader->FrameExtent;
366
367 // increment size
368 NumData += Size;
369
370 // get next stream header
371 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
372 }
373
374 m_Irp->IoStatus.Status = STATUS_SUCCESS;
375 m_Irp->IoStatus.Information = NumData;
376
377 #if 0
378 PC_ASSERT_IRQL(DISPATCH_LEVEL);
379 MmUnlockPages(m_Irp->MdlAddress);
380 IoFreeMdl(m_Irp->MdlAddress);
381 m_Irp->MdlAddress = NULL;
382 ExFreePool(m_Irp->AssociatedIrp.SystemBuffer);
383 m_Irp->AssociatedIrp.SystemBuffer = NULL;
384 #endif
385
386 // complete the request
387 IoCompleteRequest(m_Irp, IO_SOUND_INCREMENT);
388 // remove irp as it is complete
389 m_Irp = NULL;
390 m_CurrentOffset = 0;
391 }
392 }
393
394 ULONG
395 NTAPI
396 CIrpQueue::NumMappings()
397 {
398
399 // returns the amount of mappings available
400 return m_NumMappings;
401 }
402
403 ULONG
404 NTAPI
405 CIrpQueue::NumData()
406 {
407 // returns the amount of audio stream data available
408 return m_NumDataAvailable;
409 }
410
411
412 BOOL
413 NTAPI
414 CIrpQueue::MinimumDataAvailable()
415 {
416 BOOL Result;
417
418 if (m_StartStream)
419 return TRUE;
420
421 if (m_MinimumDataThreshold < m_NumDataAvailable)
422 {
423 m_StartStream = TRUE;
424 Result = TRUE;
425 }
426 else
427 {
428 Result = FALSE;
429 }
430 return Result;
431 }
432
433 BOOL
434 NTAPI
435 CIrpQueue::CancelBuffers()
436 {
437
438 m_StartStream = FALSE;
439 return TRUE;
440 }
441
442 VOID
443 NTAPI
444 CIrpQueue::UpdateFormat(
445 PKSDATAFORMAT DataFormat)
446 {
447 m_DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
448 m_MinimumDataThreshold = m_DataFormat->WaveFormatEx.nAvgBytesPerSec / 3;
449 m_StartStream = FALSE;
450 m_NumDataAvailable = 0;
451 }
452
453 NTSTATUS
454 NTAPI
455 CIrpQueue::GetMappingWithTag(
456 IN PVOID Tag,
457 OUT PPHYSICAL_ADDRESS PhysicalAddress,
458 OUT PVOID *VirtualAddress,
459 OUT PULONG ByteCount,
460 OUT PULONG Flags)
461 {
462 PKSSTREAM_HEADER StreamHeader;
463 PIRP Irp;
464
465 *Flags = 0;
466 PC_ASSERT(Tag != NULL);
467
468 // get an irp from the queue
469 Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
470
471 // check if there is an irp
472 if (!Irp)
473 {
474 // no irp available
475 m_OutOfMapping = TRUE;
476 m_StartStream = FALSE;
477 return STATUS_UNSUCCESSFUL;
478 }
479
480 //FIXME support more than one stream header
481 PC_ASSERT(STREAMHEADER_COUNT(Irp) == 1);
482
483 // HACK get stream header
484 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
485
486 // store mapping in the free list
487 ExInterlockedInsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry, &m_IrpListLock);
488
489 // return mapping
490 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
491 *VirtualAddress = StreamHeader->Data;
492 *ByteCount = StreamHeader->DataUsed;
493
494 // decrement mapping count
495 InterlockedDecrement(&m_NumMappings);
496 // decrement num data available
497 m_NumDataAvailable -= StreamHeader->DataUsed;
498
499 // store tag in irp
500 Irp->Tail.Overlay.DriverContext[3] = Tag;
501
502 // done
503 return STATUS_SUCCESS;
504 }
505
506 NTSTATUS
507 NTAPI
508 CIrpQueue::ReleaseMappingWithTag(
509 IN PVOID Tag)
510 {
511 PIRP Irp;
512 PLIST_ENTRY CurEntry;
513 PKSSTREAM_HEADER StreamHeader;
514
515 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag);
516
517 // remove irp from used list
518 CurEntry = ExInterlockedRemoveHeadList(&m_FreeIrpList, &m_IrpListLock);
519 // sanity check
520 PC_ASSERT(CurEntry);
521
522 // get irp from list entry
523 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
524
525 // HACK get stream header
526 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
527
528 // driver must release items in the same order
529 PC_ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
530
531 // irp has been processed completly
532 Irp->IoStatus.Status = STATUS_SUCCESS;
533
534 // frame extend contains the original request size, DataUsed contains the real buffer size
535 // is different when kmixer performs channel conversion, upsampling etc
536
537 Irp->IoStatus.Information = StreamHeader->FrameExtent;
538
539 // free stream header, no tag as wdmaud.drv allocates it atm
540 ExFreePool(StreamHeader);
541
542 // complete the request
543 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
544
545 return STATUS_SUCCESS;
546 }
547
548 BOOL
549 NTAPI
550 CIrpQueue::HasLastMappingFailed()
551 {
552 return m_OutOfMapping;
553 }
554
555 VOID
556 NTAPI
557 CIrpQueue::PrintQueueStatus()
558 {
559
560 }
561
562 VOID
563 NTAPI
564 CIrpQueue::SetMinimumDataThreshold(
565 ULONG MinimumDataThreshold)
566 {
567
568 m_MinimumDataThreshold = MinimumDataThreshold;
569 }
570
571 ULONG
572 NTAPI
573 CIrpQueue::GetMinimumDataThreshold()
574 {
575 return m_MinimumDataThreshold;
576 }
577
578
579 NTSTATUS
580 NTAPI
581 NewIrpQueue(
582 IN IIrpQueue **Queue)
583 {
584 CIrpQueue *This = new(NonPagedPool, TAG_PORTCLASS)CIrpQueue(NULL);
585 if (!This)
586 return STATUS_INSUFFICIENT_RESOURCES;
587
588 This->AddRef();
589
590 *Queue = (IIrpQueue*)This;
591 return STATUS_SUCCESS;
592 }
593