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