54d200f0eee21ef852d3205bd1296c339927631d
[reactos.git] / reactos / drivers / wdm / audio / backpln / portcls / irpstream.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/backpln/portcls/irpstream.c
5 * PURPOSE: IRP Stream handling
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9 #include "private.h"
10
11
12 typedef struct
13 {
14 IIrpQueueVtbl *lpVtbl;
15
16 LONG ref;
17
18 ULONG CurrentOffset;
19 LONG NumMappings;
20 ULONG NumDataAvailable;
21 BOOL StartStream;
22 KSPIN_CONNECT *ConnectDetails;
23 PKSDATAFORMAT_WAVEFORMATEX DataFormat;
24
25 KSPIN_LOCK IrpListLock;
26 LIST_ENTRY IrpList;
27 LIST_ENTRY FreeIrpList;
28 PIRP Irp;
29 PVOID SilenceBuffer;
30
31 ULONG OutOfMapping;
32 ULONG MaxFrameSize;
33 ULONG Alignment;
34 ULONG MinimumDataThreshold;
35
36 }IIrpQueueImpl;
37
38 NTSTATUS
39 NTAPI
40 IIrpQueue_fnQueryInterface(
41 IIrpQueue* iface,
42 IN REFIID refiid,
43 OUT PVOID* Output)
44 {
45 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
46
47 if (IsEqualGUIDAligned(refiid, &IID_IUnknown))
48 {
49 *Output = &This->lpVtbl;
50 _InterlockedIncrement(&This->ref);
51 return STATUS_SUCCESS;
52 }
53 return STATUS_UNSUCCESSFUL;
54 }
55
56 ULONG
57 NTAPI
58 IIrpQueue_fnAddRef(
59 IIrpQueue* iface)
60 {
61 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
62
63 return _InterlockedIncrement(&This->ref);
64 }
65
66 ULONG
67 NTAPI
68 IIrpQueue_fnRelease(
69 IIrpQueue* iface)
70 {
71 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
72
73 _InterlockedDecrement(&This->ref);
74
75 if (This->ref == 0)
76 {
77 FreeItem(This, TAG_PORTCLASS);
78 return 0;
79 }
80 /* Return new reference count */
81 return This->ref;
82 }
83
84
85 NTSTATUS
86 NTAPI
87 IIrpQueue_fnInit(
88 IN IIrpQueue *iface,
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 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
97
98 This->ConnectDetails = ConnectDetails;
99 This->DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
100 This->MaxFrameSize = FrameSize;
101 This->SilenceBuffer = SilenceBuffer;
102 This->Alignment = Alignment;
103 This->MinimumDataThreshold = ((PKSDATAFORMAT_WAVEFORMATEX)DataFormat)->WaveFormatEx.nAvgBytesPerSec / 3;
104
105 InitializeListHead(&This->IrpList);
106 InitializeListHead(&This->FreeIrpList);
107 KeInitializeSpinLock(&This->IrpListLock);
108
109 return STATUS_SUCCESS;
110 }
111
112 NTSTATUS
113 NTAPI
114 IIrpQueue_fnAddMapping(
115 IN IIrpQueue *iface,
116 IN PUCHAR Buffer,
117 IN ULONG BufferSize,
118 IN PIRP Irp)
119 {
120 PKSSTREAM_HEADER Header;
121 //PIO_STACK_LOCATION IoStack;
122 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
123
124 #if 0
125 /* get current irp stack location */
126 IoStack = IoGetCurrentIrpStackLocation(Irp);
127
128 ASSERT(IoStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(KSSTREAM_HEADER));
129
130 /* get stream header */
131 Header = (KSSTREAM_HEADER*)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
132 #else
133 /* HACK get stream header */
134 Header = (KSSTREAM_HEADER*)Buffer;
135
136 /* HACK untill stream probing is ready */
137 Irp->Tail.Overlay.DriverContext[2] = (PVOID)Header;
138 #endif
139
140 /* sanity check */
141 ASSERT(Header);
142
143 /* dont exceed max frame size */
144 //ASSERT(This->MaxFrameSize >= Header->DataUsed);
145
146 /* increment num mappings */
147 InterlockedIncrement(&This->NumMappings);
148
149 /* increment num data available */
150 This->NumDataAvailable += Header->DataUsed;
151
152 /* mark irp as pending */
153 IoMarkIrpPending(Irp);
154
155 /* add irp to cancelable queue */
156 KsAddIrpToCancelableQueue(&This->IrpList, &This->IrpListLock, Irp, KsListEntryTail, NULL);
157
158 /* done */
159 return STATUS_SUCCESS;
160 }
161
162 NTSTATUS
163 NTAPI
164 IIrpQueue_fnGetMapping(
165 IN IIrpQueue *iface,
166 OUT PUCHAR * Buffer,
167 OUT PULONG BufferSize)
168 {
169 PIRP Irp;
170 ULONG Offset;
171 //PIO_STACK_LOCATION IoStack;
172 PKSSTREAM_HEADER StreamHeader;
173 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
174
175 /* check if there is an irp in the partially processed */
176 if (This->Irp)
177 {
178 /* use last irp */
179 if (This->Irp->Cancel == FALSE)
180 {
181 Irp = This->Irp;
182 Offset = This->CurrentOffset;
183 }
184 else
185 {
186 /* irp has been cancelled */
187 This->Irp->IoStatus.Status = STATUS_CANCELLED;
188 IoCompleteRequest(This->Irp, IO_NO_INCREMENT);
189 This->Irp = Irp = NULL;
190 }
191 }
192 else
193 {
194 /* get a fresh new irp from the queue */
195 This->Irp = Irp = KsRemoveIrpFromCancelableQueue(&This->IrpList, &This->IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
196 This->CurrentOffset = Offset = 0;
197 }
198
199 if (!Irp)
200 {
201 /* no irp available, use silence buffer */
202 *Buffer = This->SilenceBuffer;
203 *BufferSize = This->MaxFrameSize;
204 /* flag for port wave pci driver */
205 This->OutOfMapping = TRUE;
206 /* indicate flag to restart fast buffering */
207 This->StartStream = FALSE;
208 return STATUS_SUCCESS;
209 }
210
211 #if 0
212 /* get current irp stack location */
213 IoStack = IoGetCurrentIrpStackLocation(Irp);
214
215 /* get stream header */
216 StreamHeader = (PKSSTREAM_HEADER)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
217 #else
218 /* HACK get stream header */
219 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
220 #endif
221
222 /* sanity check */
223 ASSERT(StreamHeader);
224
225 /* store buffersize */
226 *BufferSize = StreamHeader->DataUsed - Offset;
227
228 /* store buffer */
229 *Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
230
231 /* unset flag that no irps are available */
232 This->OutOfMapping = FALSE;
233
234 return STATUS_SUCCESS;
235 }
236
237 VOID
238 NTAPI
239 IIrpQueue_fnUpdateMapping(
240 IN IIrpQueue *iface,
241 IN ULONG BytesWritten)
242 {
243 //PIO_STACK_LOCATION IoStack;
244 PKSSTREAM_HEADER StreamHeader;
245 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
246
247 if (!This->Irp)
248 {
249 /* silence buffer was used */
250 return;
251 }
252
253 #if 0
254 /* get current irp stack location */
255 IoStack = IoGetCurrentIrpStackLocation(This->Irp);
256
257 /* get stream header */
258 StreamHeader = (PKSSTREAM_HEADER)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
259 #else
260 /* HACK get stream header */
261 StreamHeader = (PKSSTREAM_HEADER)This->Irp->Tail.Overlay.DriverContext[2];
262 #endif
263
264 /* sanity check */
265 ASSERT(StreamHeader);
266
267 /* add to current offset */
268 This->CurrentOffset += BytesWritten;
269
270 /* decrement available data counter */
271 This->NumDataAvailable -= BytesWritten;
272
273 if (This->CurrentOffset >= StreamHeader->DataUsed)
274 {
275 /* irp has been processed completly */
276 This->Irp->IoStatus.Status = STATUS_SUCCESS;
277
278 /* frame extend contains the original request size, DataUsed contains the real buffer size
279 * is different when kmixer performs channel conversion, upsampling etc
280 */
281 This->Irp->IoStatus.Information = StreamHeader->FrameExtent;
282
283 /* free stream data, no tag as wdmaud.drv does it atm */
284 ExFreePool(StreamHeader->Data);
285
286 /* free stream header, no tag as wdmaud.drv allocates it atm */
287 ExFreePool(StreamHeader);
288
289 /* complete the request */
290 IoCompleteRequest(This->Irp, IO_SOUND_INCREMENT);
291 /* remove irp as it is complete */
292 This->Irp = NULL;
293 This->CurrentOffset = 0;
294 }
295 }
296
297 ULONG
298 NTAPI
299 IIrpQueue_fnNumMappings(
300 IN IIrpQueue *iface)
301 {
302 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
303
304 /* returns the amount of mappings available */
305 return This->NumMappings;
306 }
307
308 ULONG
309 NTAPI
310 IIrpQueue_fnNumData(
311 IN IIrpQueue *iface)
312 {
313 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
314 /* returns the amount of audio stream data available */
315 return This->NumDataAvailable;
316 }
317
318
319 BOOL
320 NTAPI
321 IIrpQueue_fnMinimumDataAvailable(
322 IN IIrpQueue *iface)
323 {
324 BOOL Result;
325 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
326
327 if (This->StartStream)
328 return TRUE;
329
330 if (This->MinimumDataThreshold < This->NumDataAvailable)
331 {
332 This->StartStream = TRUE;
333 Result = TRUE;
334 }
335 else
336 {
337 Result = FALSE;
338 }
339 return Result;
340 }
341
342 BOOL
343 NTAPI
344 IIrpQueue_fnCancelBuffers(
345 IN IIrpQueue *iface)
346 {
347 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
348
349 This->StartStream = FALSE;
350 return TRUE;
351 }
352
353 VOID
354 NTAPI
355 IIrpQueue_fnUpdateFormat(
356 IN IIrpQueue *iface,
357 PKSDATAFORMAT DataFormat)
358 {
359 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
360 This->DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
361 This->MinimumDataThreshold = This->DataFormat->WaveFormatEx.nAvgBytesPerSec / 3;
362 This->StartStream = FALSE;
363 This->NumDataAvailable = 0;
364 }
365
366 NTSTATUS
367 NTAPI
368 IIrpQueue_fnGetMappingWithTag(
369 IN IIrpQueue *iface,
370 IN PVOID Tag,
371 OUT PPHYSICAL_ADDRESS PhysicalAddress,
372 OUT PVOID *VirtualAddress,
373 OUT PULONG ByteCount,
374 OUT PULONG Flags)
375 {
376 PKSSTREAM_HEADER StreamHeader;
377 PIRP Irp;
378 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
379
380 *Flags = 0;
381 ASSERT(Tag != NULL);
382
383 /* get an irp from the queue */
384 Irp = KsRemoveIrpFromCancelableQueue(&This->IrpList, &This->IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
385
386 /* check if there is an irp */
387 if (!Irp)
388 {
389 /* no irp available */
390 This->OutOfMapping = TRUE;
391 This->StartStream = FALSE;
392 return STATUS_UNSUCCESSFUL;
393 }
394
395 /* HACK get stream header */
396 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
397
398 /* store mapping in the free list */
399 ExInterlockedInsertTailList(&This->FreeIrpList, &Irp->Tail.Overlay.ListEntry, &This->IrpListLock);
400
401 /* return mapping */
402 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
403 *VirtualAddress = StreamHeader->Data;
404 *ByteCount = StreamHeader->DataUsed;
405
406 /* decrement mapping count */
407 InterlockedDecrement(&This->NumMappings);
408 /* decrement num data available */
409 This->NumDataAvailable -= StreamHeader->DataUsed;
410
411 /* store tag in irp */
412 Irp->Tail.Overlay.DriverContext[3] = Tag;
413
414 /* done */
415 return STATUS_SUCCESS;
416 }
417
418 NTSTATUS
419 NTAPI
420 IIrpQueue_fnReleaseMappingWithTag(
421 IN IIrpQueue *iface,
422 IN PVOID Tag)
423 {
424 PIRP Irp;
425 PLIST_ENTRY CurEntry;
426 PKSSTREAM_HEADER StreamHeader;
427 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
428
429 DPRINT("IIrpQueue_fnReleaseMappingWithTag Tag %p\n", Tag);
430
431 /* remove irp from used list */
432 CurEntry = ExInterlockedRemoveHeadList(&This->FreeIrpList, &This->IrpListLock);
433 /* sanity check */
434 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 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 data, no tag as wdmaud.drv does it atm */
454 ExFreePool(StreamHeader->Data);
455
456 /* free stream header, no tag as wdmaud.drv allocates it atm */
457 ExFreePool(StreamHeader);
458
459 /* complete the request */
460 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
461
462 return STATUS_SUCCESS;
463 }
464
465 BOOL
466 NTAPI
467 IIrpQueue_fnHasLastMappingFailed(
468 IN IIrpQueue *iface)
469 {
470 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
471 return This->OutOfMapping;
472 }
473
474 VOID
475 NTAPI
476 IIrpQueue_fnPrintQueueStatus(
477 IN IIrpQueue *iface)
478 {
479
480 }
481
482 VOID
483 NTAPI
484 IIrpQueue_fnSetMinimumDataThreshold(
485 IN IIrpQueue *iface,
486 ULONG MinimumDataThreshold)
487 {
488 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
489
490 This->MinimumDataThreshold = MinimumDataThreshold;
491 }
492
493 ULONG
494 NTAPI
495 IIrpQueue_fnGetMinimumDataThreshold(
496 IN IIrpQueue *iface)
497 {
498 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
499
500 return This->MinimumDataThreshold;
501 }
502
503
504 static IIrpQueueVtbl vt_IIrpQueue =
505 {
506 IIrpQueue_fnQueryInterface,
507 IIrpQueue_fnAddRef,
508 IIrpQueue_fnRelease,
509 IIrpQueue_fnInit,
510 IIrpQueue_fnAddMapping,
511 IIrpQueue_fnGetMapping,
512 IIrpQueue_fnUpdateMapping,
513 IIrpQueue_fnNumMappings,
514 IIrpQueue_fnNumData,
515 IIrpQueue_fnMinimumDataAvailable,
516 IIrpQueue_fnCancelBuffers,
517 IIrpQueue_fnUpdateFormat,
518 IIrpQueue_fnGetMappingWithTag,
519 IIrpQueue_fnReleaseMappingWithTag,
520 IIrpQueue_fnHasLastMappingFailed,
521 IIrpQueue_fnPrintQueueStatus,
522 IIrpQueue_fnSetMinimumDataThreshold,
523 IIrpQueue_fnGetMinimumDataThreshold
524 };
525
526 NTSTATUS
527 NTAPI
528 NewIrpQueue(
529 IN IIrpQueue **Queue)
530 {
531 IIrpQueueImpl *This = AllocateItem(NonPagedPool, sizeof(IIrpQueueImpl), TAG_PORTCLASS);
532 if (!This)
533 return STATUS_INSUFFICIENT_RESOURCES;
534
535 This->ref = 1;
536 This->lpVtbl = &vt_IIrpQueue;
537
538 *Queue = (IIrpQueue*)This;
539 return STATUS_SUCCESS;
540
541 }
542