- Fix debugging flood #2
[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 NTSTATUS
61 NTAPI
62 CIrpQueue::QueryInterface(
63 IN REFIID refiid,
64 OUT PVOID* Output)
65 {
66 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
67 {
68 *Output = PVOID(PUNKNOWN(this));
69 PUNKNOWN(*Output)->AddRef();
70 return STATUS_SUCCESS;
71 }
72
73 return STATUS_UNSUCCESSFUL;
74 }
75
76 NTSTATUS
77 NTAPI
78 CIrpQueue::Init(
79 IN KSPIN_CONNECT *ConnectDetails,
80 IN PKSDATAFORMAT DataFormat,
81 IN PDEVICE_OBJECT DeviceObject,
82 IN ULONG FrameSize,
83 IN ULONG Alignment,
84 IN PVOID SilenceBuffer)
85 {
86 m_ConnectDetails = ConnectDetails;
87 m_DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
88 m_MaxFrameSize = FrameSize;
89 m_SilenceBuffer = SilenceBuffer;
90 m_Alignment = Alignment;
91 m_MinimumDataThreshold = ((PKSDATAFORMAT_WAVEFORMATEX)DataFormat)->WaveFormatEx.nAvgBytesPerSec / 3;
92
93 InitializeListHead(&m_IrpList);
94 InitializeListHead(&m_FreeIrpList);
95 KeInitializeSpinLock(&m_IrpListLock);
96
97 return STATUS_SUCCESS;
98 }
99
100 NTSTATUS
101 NTAPI
102 CIrpQueue::AddMapping(
103 IN PUCHAR Buffer,
104 IN ULONG BufferSize,
105 IN PIRP Irp)
106 {
107 PKSSTREAM_HEADER Header;
108 NTSTATUS Status = STATUS_SUCCESS;
109 PIO_STACK_LOCATION IoStack;
110
111 // get current irp stack location
112 IoStack = IoGetCurrentIrpStackLocation(Irp);
113
114 PC_ASSERT(!Buffer);
115
116 if (!Irp->MdlAddress)
117 {
118 // ioctl from KsStudio
119 // Wdmaud already probes buffers, therefore no need to probe it again
120 // probe the stream irp
121 Status = KsProbeStreamIrp(Irp, KSSTREAM_READ | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_ALLOWFORMATCHANGE | KSPROBE_SYSTEMADDRESS, 0);
122
123 // check for success
124 if (!NT_SUCCESS(Status))
125 {
126 DPRINT1("KsProbeStreamIrp failed with %x\n", Status);
127 return Status;
128 }
129 }
130 // get the stream header
131 Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
132 PC_ASSERT(Header);
133 PC_ASSERT(Irp->MdlAddress);
134
135 DPRINT1("Size %u DataUsed %u FrameExtent %u SizeHeader %u NumDataAvailable %u OutputLength %u\n", Header->Size, Header->DataUsed, Header->FrameExtent, sizeof(KSSTREAM_HEADER), m_NumDataAvailable, IoStack->Parameters.DeviceIoControl.OutputBufferLength);
136
137 Header->Data = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
138 PC_ASSERT(Header->Data);
139 //PC_ASSERT(Header->Size == IoStack->Parameters.DeviceIoControl.OutputBufferLength);
140
141 // HACK
142 Irp->Tail.Overlay.DriverContext[2] = (PVOID)Header;
143
144 // sanity check
145 PC_ASSERT(Header);
146
147 // dont exceed max frame size
148 //PC_ASSERT(m_MaxFrameSize >= Header->DataUsed);
149
150 // increment num mappings
151 InterlockedIncrement(&m_NumMappings);
152
153 // increment num data available
154 m_NumDataAvailable += Header->DataUsed;
155
156 // mark irp as pending
157 IoMarkIrpPending(Irp);
158
159 // add irp to cancelable queue
160 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
161
162 // done
163 return Status;
164 }
165
166 NTSTATUS
167 NTAPI
168 CIrpQueue::GetMapping(
169 OUT PUCHAR * Buffer,
170 OUT PULONG BufferSize)
171 {
172 PIRP Irp;
173 ULONG Offset;
174 //PIO_STACK_LOCATION IoStack;
175 PKSSTREAM_HEADER StreamHeader;
176
177 // check if there is an irp in the partially processed
178 if (m_Irp)
179 {
180 // use last irp
181 if (m_Irp->Cancel == FALSE)
182 {
183 Irp = m_Irp;
184 Offset = m_CurrentOffset;
185 }
186 else
187 {
188 // irp has been cancelled
189 m_Irp->IoStatus.Status = STATUS_CANCELLED;
190 IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
191 m_Irp = Irp = NULL;
192 }
193 }
194 else
195 {
196 // get a fresh new irp from the queue
197 m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
198 m_CurrentOffset = Offset = 0;
199 }
200
201 if (!Irp)
202 {
203 DPRINT1("NoIrp\n");
204 return STATUS_UNSUCCESSFUL;
205 // no irp available, use silence buffer
206 *Buffer = (PUCHAR)m_SilenceBuffer;
207 *BufferSize = m_MaxFrameSize;
208 // flag for port wave pci driver
209 m_OutOfMapping = TRUE;
210 // indicate flag to restart fast buffering
211 m_StartStream = FALSE;
212 return STATUS_SUCCESS;
213 }
214
215 #if 0
216 // get current irp stack location
217 IoStack = IoGetCurrentIrpStackLocation(Irp);
218
219 // get stream header
220 StreamHeader = (PKSSTREAM_HEADER)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
221 #else
222 // HACK get stream header
223 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
224 #endif
225
226 // sanity check
227 PC_ASSERT(StreamHeader);
228
229 // store buffersize
230 if (StreamHeader->DataUsed)
231 *BufferSize = StreamHeader->DataUsed - Offset;
232 else
233 *BufferSize = StreamHeader->FrameExtent - Offset;
234
235 PC_ASSERT(*BufferSize);
236
237 // store buffer
238 *Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
239
240 // unset flag that no irps are available
241 m_OutOfMapping = FALSE;
242
243 return STATUS_SUCCESS;
244 }
245
246 VOID
247 NTAPI
248 CIrpQueue::UpdateMapping(
249 IN ULONG BytesWritten)
250 {
251 PKSSTREAM_HEADER StreamHeader;
252 ULONG Size;
253
254 if (!m_Irp)
255 {
256 // silence buffer was used
257 return;
258 }
259
260 // get stream header
261 StreamHeader = (PKSSTREAM_HEADER)m_Irp->Tail.Overlay.DriverContext[2];
262
263 // sanity check
264 // ASSERT(StreamHeader);
265
266 // add to current offset
267 m_CurrentOffset += BytesWritten;
268
269 // decrement available data counter
270 m_NumDataAvailable -= BytesWritten;
271
272 if (StreamHeader->DataUsed)
273 Size = StreamHeader->DataUsed;
274 else
275 Size = StreamHeader->FrameExtent;
276
277 PC_ASSERT(Size);
278
279 if (m_CurrentOffset >= Size)
280 {
281 // irp has been processed completly
282 m_Irp->IoStatus.Status = STATUS_SUCCESS;
283
284 // frame extend contains the original request size, DataUsed contains the real buffer size
285 //is different when kmixer performs channel conversion, upsampling etc
286
287 m_Irp->IoStatus.Information = Size;
288
289 PC_ASSERT_IRQL(DISPATCH_LEVEL);
290 MmUnlockPages(m_Irp->MdlAddress);
291 IoFreeMdl(m_Irp->MdlAddress);
292 m_Irp->MdlAddress = NULL;
293 ExFreePool(m_Irp->AssociatedIrp.SystemBuffer);
294 m_Irp->AssociatedIrp.SystemBuffer = NULL;
295
296 // complete the request
297 IoCompleteRequest(m_Irp, IO_SOUND_INCREMENT);
298 // remove irp as it is complete
299 m_Irp = NULL;
300 m_CurrentOffset = 0;
301 }
302 }
303
304 ULONG
305 NTAPI
306 CIrpQueue::NumMappings()
307 {
308
309 // returns the amount of mappings available
310 return m_NumMappings;
311 }
312
313 ULONG
314 NTAPI
315 CIrpQueue::NumData()
316 {
317 // returns the amount of audio stream data available
318 return m_NumDataAvailable;
319 }
320
321
322 BOOL
323 NTAPI
324 CIrpQueue::MinimumDataAvailable()
325 {
326 BOOL Result;
327
328 if (m_StartStream)
329 return TRUE;
330
331 if (m_MinimumDataThreshold < m_NumDataAvailable)
332 {
333 m_StartStream = TRUE;
334 Result = TRUE;
335 }
336 else
337 {
338 Result = FALSE;
339 }
340 return Result;
341 }
342
343 BOOL
344 NTAPI
345 CIrpQueue::CancelBuffers()
346 {
347
348 m_StartStream = FALSE;
349 return TRUE;
350 }
351
352 VOID
353 NTAPI
354 CIrpQueue::UpdateFormat(
355 PKSDATAFORMAT DataFormat)
356 {
357 m_DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
358 m_MinimumDataThreshold = m_DataFormat->WaveFormatEx.nAvgBytesPerSec / 3;
359 m_StartStream = FALSE;
360 m_NumDataAvailable = 0;
361 }
362
363 NTSTATUS
364 NTAPI
365 CIrpQueue::GetMappingWithTag(
366 IN PVOID Tag,
367 OUT PPHYSICAL_ADDRESS PhysicalAddress,
368 OUT PVOID *VirtualAddress,
369 OUT PULONG ByteCount,
370 OUT PULONG Flags)
371 {
372 PKSSTREAM_HEADER StreamHeader;
373 PIRP Irp;
374
375 *Flags = 0;
376 PC_ASSERT(Tag != NULL);
377
378 // get an irp from the queue
379 Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
380
381 // check if there is an irp
382 if (!Irp)
383 {
384 // no irp available
385 m_OutOfMapping = TRUE;
386 m_StartStream = FALSE;
387 return STATUS_UNSUCCESSFUL;
388 }
389
390 // HACK get stream header
391 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
392
393 // store mapping in the free list
394 ExInterlockedInsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry, &m_IrpListLock);
395
396 // return mapping
397 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
398 *VirtualAddress = StreamHeader->Data;
399 *ByteCount = StreamHeader->DataUsed;
400
401 // decrement mapping count
402 InterlockedDecrement(&m_NumMappings);
403 // decrement num data available
404 m_NumDataAvailable -= StreamHeader->DataUsed;
405
406 // store tag in irp
407 Irp->Tail.Overlay.DriverContext[3] = Tag;
408
409 // done
410 return STATUS_SUCCESS;
411 }
412
413 NTSTATUS
414 NTAPI
415 CIrpQueue::ReleaseMappingWithTag(
416 IN PVOID Tag)
417 {
418 PIRP Irp;
419 PLIST_ENTRY CurEntry;
420 PKSSTREAM_HEADER StreamHeader;
421
422 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag);
423
424 // remove irp from used list
425 CurEntry = ExInterlockedRemoveHeadList(&m_FreeIrpList, &m_IrpListLock);
426 // sanity check
427 PC_ASSERT(CurEntry);
428
429 // get irp from list entry
430 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
431
432 // HACK get stream header
433 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
434
435 // driver must release items in the same order
436 PC_ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
437
438 // irp has been processed completly
439 Irp->IoStatus.Status = STATUS_SUCCESS;
440
441 // frame extend contains the original request size, DataUsed contains the real buffer size
442 // is different when kmixer performs channel conversion, upsampling etc
443
444 Irp->IoStatus.Information = StreamHeader->FrameExtent;
445
446 // free stream header, no tag as wdmaud.drv allocates it atm
447 ExFreePool(StreamHeader);
448
449 // complete the request
450 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
451
452 return STATUS_SUCCESS;
453 }
454
455 BOOL
456 NTAPI
457 CIrpQueue::HasLastMappingFailed()
458 {
459 return m_OutOfMapping;
460 }
461
462 VOID
463 NTAPI
464 CIrpQueue::PrintQueueStatus()
465 {
466
467 }
468
469 VOID
470 NTAPI
471 CIrpQueue::SetMinimumDataThreshold(
472 ULONG MinimumDataThreshold)
473 {
474
475 m_MinimumDataThreshold = MinimumDataThreshold;
476 }
477
478 ULONG
479 NTAPI
480 CIrpQueue::GetMinimumDataThreshold()
481 {
482 return m_MinimumDataThreshold;
483 }
484
485
486 NTSTATUS
487 NTAPI
488 NewIrpQueue(
489 IN IIrpQueue **Queue)
490 {
491 CIrpQueue *This = new(NonPagedPool, TAG_PORTCLASS)CIrpQueue(NULL);
492 if (!This)
493 return STATUS_INSUFFICIENT_RESOURCES;
494
495 This->AddRef();
496
497 *Queue = (IIrpQueue*)This;
498 return STATUS_SUCCESS;
499 }
500