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