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