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