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