Sync with trunk head (r49139)
[reactos.git] / 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 volatile ULONG m_CurrentOffset;
39 LONG m_NumMappings;
40 ULONG m_NumDataAvailable;
41 PKSPIN_CONNECT m_ConnectDetails;
42 KSPIN_LOCK m_IrpListLock;
43 LIST_ENTRY m_IrpList;
44 LIST_ENTRY m_FreeIrpList;
45 PIRP m_Irp;
46 PVOID m_SilenceBuffer;
47
48 ULONG m_OutOfMapping;
49 ULONG m_MaxFrameSize;
50 ULONG m_Alignment;
51
52 LONG m_Ref;
53
54 };
55
56 #define OFFSET_HEADERINDEX (0)
57 #define OFFSET_STREAMHEADER (2)
58 #define OFFSET_HEADERCOUNT (3)
59
60
61 #define STREAMHEADER_INDEX(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX]))
62 #define STREAMHEADER_COUNT(Irp) (PtrToUlong(Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT]))
63 #define STREAMHEADER_CURRENT(Irp) (Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER])
64
65
66 NTSTATUS
67 NTAPI
68 CIrpQueue::QueryInterface(
69 IN REFIID refiid,
70 OUT PVOID* Output)
71 {
72 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
73 {
74 *Output = PVOID(PUNKNOWN(this));
75 PUNKNOWN(*Output)->AddRef();
76 return STATUS_SUCCESS;
77 }
78
79 return STATUS_UNSUCCESSFUL;
80 }
81
82 NTSTATUS
83 NTAPI
84 CIrpQueue::Init(
85 IN KSPIN_CONNECT *ConnectDetails,
86 IN ULONG FrameSize,
87 IN ULONG Alignment,
88 IN PVOID SilenceBuffer)
89 {
90 m_ConnectDetails = ConnectDetails;
91 m_MaxFrameSize = FrameSize;
92 m_SilenceBuffer = SilenceBuffer;
93 m_Alignment = Alignment;
94
95 InitializeListHead(&m_IrpList);
96 InitializeListHead(&m_FreeIrpList);
97 KeInitializeSpinLock(&m_IrpListLock);
98
99 return STATUS_SUCCESS;
100 }
101
102 NTSTATUS
103 NTAPI
104 CIrpQueue::AddMapping(
105 IN PIRP Irp,
106 OUT PULONG Data)
107 {
108 PKSSTREAM_HEADER Header;
109 NTSTATUS Status = STATUS_SUCCESS;
110 PIO_STACK_LOCATION IoStack;
111 ULONG NumHeaders, NumData, Index;
112 PMDL Mdl;
113
114 PC_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
115
116 // get current irp stack location
117 IoStack = IoGetCurrentIrpStackLocation(Irp);
118
119 if (!Irp->MdlAddress)
120 {
121 // ioctl from KsStudio
122 // Wdmaud already probes buffers, therefore no need to probe it again
123 // probe the stream irp
124 if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_WRITE_STREAM)
125 Status = KsProbeStreamIrp(Irp, KSSTREAM_WRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
126 else if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_READ_STREAM)
127 Status = KsProbeStreamIrp(Irp, KSSTREAM_READ | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_SYSTEMADDRESS, 0);
128 else
129 PC_ASSERT(0);
130
131 // check for success
132 if (!NT_SUCCESS(Status))
133 {
134 DPRINT("KsProbeStreamIrp failed with %x\n", Status);
135 return Status;
136 }
137 }
138
139 // get first stream header
140
141 if (Irp->RequestorMode == UserMode)
142 Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
143 else
144 Header = (PKSSTREAM_HEADER)Irp->UserBuffer;
145
146 // sanity check
147 PC_ASSERT(Header);
148
149 // calculate num headers
150 NumHeaders = IoStack->Parameters.DeviceIoControl.OutputBufferLength / Header->Size;
151
152 // assume headers of same length
153 PC_ASSERT(IoStack->Parameters.DeviceIoControl.OutputBufferLength % Header->Size == 0);
154
155
156 // get first audio buffer
157 Mdl = Irp->MdlAddress;
158 // sanity check
159 PC_ASSERT(Mdl);
160
161 // store the current stream header
162 Irp->Tail.Overlay.DriverContext[OFFSET_STREAMHEADER] = (PVOID)Header;
163 // store header count
164 Irp->Tail.Overlay.DriverContext[OFFSET_HEADERCOUNT] = UlongToPtr(NumHeaders);
165
166 // store current header index
167 Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
168
169 NumData = 0;
170 // prepare all headers
171 for(Index = 0; Index < NumHeaders; Index++)
172 {
173 // sanity checks
174 PC_ASSERT(Header);
175 PC_ASSERT(Mdl);
176
177 if (Irp->RequestorMode == UserMode)
178 {
179 Header->Data = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
180 }
181
182 if (!Header->Data)
183 {
184 // insufficient resources
185 ExFreePool(Irp->AssociatedIrp.SystemBuffer);
186 Irp->AssociatedIrp.SystemBuffer = NULL;
187 // complete and forget request
188 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
189 Irp->IoStatus.Information = 0;
190
191 IoCompleteRequest(Irp, IO_NO_INCREMENT);
192 return STATUS_INSUFFICIENT_RESOURCES;
193 }
194
195 // increment num mappings
196 InterlockedIncrement(&m_NumMappings);
197
198 // increment available data
199 InterlockedExchangeAdd((PLONG)&m_NumDataAvailable,
200 (max(Header->DataUsed, Header->FrameExtent)));
201
202 NumData += max(Header->DataUsed, Header->FrameExtent);
203
204 // move to next header
205 Header = (PKSSTREAM_HEADER)((ULONG_PTR)Header + Header->Size);
206
207 // move to next mdl
208 Mdl = Mdl->Next;
209 }
210
211 DPRINT("StreamHeaders %u NumData %u FrameSize %u NumDataAvailable %u\n", NumHeaders, NumData, m_MaxFrameSize, m_NumDataAvailable);
212 *Data = NumData;
213
214 // mark irp as pending
215 IoMarkIrpPending(Irp);
216
217 // add irp to cancelable queue
218 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, Irp, KsListEntryTail, NULL);
219
220 // disable mapping failed status
221 m_OutOfMapping = FALSE;
222
223 // done
224 return Status;
225 }
226
227 NTSTATUS
228 NTAPI
229 CIrpQueue::GetMapping(
230 OUT PUCHAR * Buffer,
231 OUT PULONG BufferSize)
232 {
233 PIRP Irp;
234 ULONG Offset;
235 //PIO_STACK_LOCATION IoStack;
236 PKSSTREAM_HEADER StreamHeader;
237
238 // check if there is an irp in the partially processed
239 if (m_Irp)
240 {
241 // use last irp
242 if (m_Irp->Cancel == FALSE)
243 {
244 Irp = m_Irp;
245 Offset = m_CurrentOffset;
246 }
247 else
248 {
249 // irp has been cancelled
250 m_Irp->IoStatus.Status = STATUS_CANCELLED;
251 IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
252 m_Irp = Irp = NULL;
253 }
254 }
255 else
256 {
257 // get a fresh new irp from the queue
258 m_Irp = Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
259 m_CurrentOffset = Offset = 0;
260 }
261
262 if (!Irp && m_SilenceBuffer && m_MaxFrameSize)
263 {
264 DPRINT("NoIrp\n");
265 // no irp available, use silence buffer
266 *Buffer = (PUCHAR)m_SilenceBuffer;
267 *BufferSize = m_MaxFrameSize;
268 return STATUS_SUCCESS;
269 }
270
271 if (!Irp)
272 {
273 // no irp buffer available
274 return STATUS_UNSUCCESSFUL;
275 }
276
277
278 // get stream header
279 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
280
281 // sanity check
282 PC_ASSERT(StreamHeader);
283
284 // store buffersize
285 if (StreamHeader->DataUsed)
286 *BufferSize = StreamHeader->DataUsed - Offset;
287 else
288 *BufferSize = StreamHeader->FrameExtent - Offset;
289
290 PC_ASSERT(*BufferSize);
291
292 // store buffer
293 *Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
294
295 // unset flag that no irps are available
296 m_OutOfMapping = FALSE;
297
298 return STATUS_SUCCESS;
299 }
300
301 VOID
302 NTAPI
303 CIrpQueue::UpdateMapping(
304 IN ULONG BytesWritten)
305 {
306 PKSSTREAM_HEADER StreamHeader;
307 ULONG Size, NumData, Index;
308
309 if (!m_Irp)
310 {
311 // silence buffer was used
312 return;
313 }
314
315 // get stream header
316 StreamHeader = (PKSSTREAM_HEADER)STREAMHEADER_CURRENT(m_Irp);
317
318 // sanity check
319 // ASSERT(StreamHeader);
320
321 // add to current offset
322 InterlockedExchangeAdd((volatile PLONG)&m_CurrentOffset, (LONG)BytesWritten);
323
324 // decrement available data counter
325 m_NumDataAvailable -= BytesWritten;
326
327 if (StreamHeader->DataUsed)
328 Size = StreamHeader->DataUsed;
329 else
330 Size = StreamHeader->FrameExtent;
331
332 PC_ASSERT(Size);
333
334 if (m_CurrentOffset >= Size)
335 {
336 if (STREAMHEADER_INDEX(m_Irp) + 1 < STREAMHEADER_COUNT(m_Irp))
337 {
338 // the irp has at least one more stream header
339 m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(STREAMHEADER_INDEX(m_Irp) + 1);
340
341 // get next stream header
342 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
343
344 // store next stream header
345 STREAMHEADER_CURRENT(m_Irp) = (PVOID)StreamHeader;
346
347 // reset current offset
348 m_CurrentOffset = 0;
349
350 // done
351 return;
352 }
353
354 // irp has been processed completly
355
356 NumData = 0;
357 if (m_Irp->RequestorMode == KernelMode)
358 StreamHeader = (PKSSTREAM_HEADER)m_Irp->UserBuffer;
359 else
360 StreamHeader = (PKSSTREAM_HEADER)m_Irp->AssociatedIrp.SystemBuffer;
361
362 // loop all stream headers
363 for(Index = 0; Index < STREAMHEADER_COUNT(m_Irp); Index++)
364 {
365 PC_ASSERT(StreamHeader);
366
367 // add size of buffer
368 // depends on if the buffer is input / output
369 if (StreamHeader->DataUsed)
370 Size = StreamHeader->DataUsed;
371 else
372 Size = StreamHeader->FrameExtent;
373
374 // increment size
375 NumData += Size;
376
377 // get next stream header
378 StreamHeader = (PKSSTREAM_HEADER)((ULONG_PTR)StreamHeader + StreamHeader->Size);
379 }
380
381 if (m_ConnectDetails->Interface.Id == KSINTERFACE_STANDARD_LOOPED_STREAMING)
382 {
383 // looped streaming repeat the buffers untill
384 // the caller decides to stop the streams
385
386 // reset stream header index
387 m_Irp->Tail.Overlay.DriverContext[OFFSET_HEADERINDEX] = UlongToPtr(0);
388 // re-insert irp
389 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
390 // clear current irp
391 m_Irp = NULL;
392 // reset offset
393 m_CurrentOffset = 0;
394 // increment available data
395 InterlockedExchangeAdd((PLONG)&m_NumDataAvailable, NumData);
396 // done
397 return;
398 }
399
400 m_Irp->IoStatus.Status = STATUS_SUCCESS;
401 m_Irp->IoStatus.Information = NumData;
402
403 // complete the request
404 IoCompleteRequest(m_Irp, IO_SOUND_INCREMENT);
405 // remove irp as it is complete
406 m_Irp = NULL;
407 m_CurrentOffset = 0;
408 }
409 }
410
411 ULONG
412 NTAPI
413 CIrpQueue::NumData()
414 {
415 // returns the amount of audio stream data available
416 return m_NumDataAvailable;
417 }
418
419 BOOL
420 NTAPI
421 CIrpQueue::CancelBuffers()
422 {
423 // is there an active irp
424 if (m_Irp)
425 {
426 // re-insert it to cancelable queue
427 KsAddIrpToCancelableQueue(&m_IrpList, &m_IrpListLock, m_Irp, KsListEntryTail, NULL);
428 //set it to zero
429 m_Irp = NULL;
430 }
431
432 // cancel all irps
433 KsCancelIo(&m_IrpList, &m_IrpListLock);
434 // reset number of mappings
435 m_NumMappings = 0;
436 // reset number of data available
437 m_NumDataAvailable = 0;
438
439 // done
440 return TRUE;
441 }
442
443 NTSTATUS
444 NTAPI
445 CIrpQueue::GetMappingWithTag(
446 IN PVOID Tag,
447 OUT PPHYSICAL_ADDRESS PhysicalAddress,
448 OUT PVOID *VirtualAddress,
449 OUT PULONG ByteCount,
450 OUT PULONG Flags)
451 {
452 PKSSTREAM_HEADER StreamHeader;
453 PIRP Irp;
454
455 *Flags = 0;
456 PC_ASSERT(Tag != NULL);
457
458 // get an irp from the queue
459 Irp = KsRemoveIrpFromCancelableQueue(&m_IrpList, &m_IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
460
461 // check if there is an irp
462 if (!Irp)
463 {
464 // no irp available
465 m_OutOfMapping = TRUE;
466 return STATUS_NOT_FOUND;
467 }
468
469 //FIXME support more than one stream header
470 PC_ASSERT(STREAMHEADER_COUNT(Irp) == 1);
471
472 // HACK get stream header
473 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
474
475 // store mapping in the free list
476 ExInterlockedInsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry, &m_IrpListLock);
477
478 // return mapping
479 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
480 *VirtualAddress = StreamHeader->Data;
481 *ByteCount = StreamHeader->DataUsed;
482
483 // decrement mapping count
484 InterlockedDecrement(&m_NumMappings);
485 // decrement num data available
486 m_NumDataAvailable -= StreamHeader->DataUsed;
487
488 // store tag in irp
489 Irp->Tail.Overlay.DriverContext[3] = Tag;
490
491 // done
492 return STATUS_SUCCESS;
493 }
494
495 NTSTATUS
496 NTAPI
497 CIrpQueue::ReleaseMappingWithTag(
498 IN PVOID Tag)
499 {
500 PIRP Irp;
501 PLIST_ENTRY CurEntry;
502 PKSSTREAM_HEADER StreamHeader;
503
504 DPRINT("CIrpQueue::ReleaseMappingWithTag Tag %p\n", Tag);
505
506 // remove irp from used list
507 CurEntry = ExInterlockedRemoveHeadList(&m_FreeIrpList, &m_IrpListLock);
508 // sanity check
509 PC_ASSERT(CurEntry);
510
511 // get irp from list entry
512 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
513
514 // HACK get stream header
515 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
516
517 // driver must release items in the same order
518 PC_ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
519
520 // irp has been processed completly
521 Irp->IoStatus.Status = STATUS_SUCCESS;
522
523 // frame extend contains the original request size, DataUsed contains the real buffer size
524 // is different when kmixer performs channel conversion, upsampling etc
525
526 Irp->IoStatus.Information = StreamHeader->FrameExtent;
527
528 // complete the request
529 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
530
531 return STATUS_SUCCESS;
532 }
533
534 BOOLEAN
535 NTAPI
536 CIrpQueue::HasLastMappingFailed()
537 {
538 return m_OutOfMapping;
539 }
540
541 ULONG
542 NTAPI
543 CIrpQueue::GetCurrentIrpOffset()
544 {
545
546 return m_CurrentOffset;
547 }
548
549 BOOLEAN
550 NTAPI
551 CIrpQueue::GetAcquiredTagRange(
552 IN PVOID * FirstTag,
553 IN PVOID * LastTag)
554 {
555 KIRQL OldLevel;
556 BOOLEAN Ret = FALSE;
557 PIRP Irp;
558 PLIST_ENTRY CurEntry;
559
560 KeAcquireSpinLock(&m_IrpListLock, &OldLevel);
561
562 if (!IsListEmpty(&m_FreeIrpList))
563 {
564 // get first entry
565 CurEntry = RemoveHeadList(&m_FreeIrpList);
566 // get irp from list entry
567 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
568
569 // get tag of first acquired buffer
570 *FirstTag = Irp->Tail.Overlay.DriverContext[3];
571
572 // put back irp
573 InsertHeadList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
574
575 // get last entry
576 CurEntry = RemoveTailList(&m_FreeIrpList);
577 // get irp from list entry
578 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
579
580 // get tag of first acquired buffer
581 *LastTag = Irp->Tail.Overlay.DriverContext[3];
582
583 // put back irp
584 InsertTailList(&m_FreeIrpList, &Irp->Tail.Overlay.ListEntry);
585
586 // indicate success
587 Ret = TRUE;
588 }
589
590 // release lock
591 KeReleaseSpinLock(&m_IrpListLock, OldLevel);
592 // done
593 return Ret;
594 }
595
596 NTSTATUS
597 NTAPI
598 NewIrpQueue(
599 IN IIrpQueue **Queue)
600 {
601 CIrpQueue *This = new(NonPagedPool, TAG_PORTCLASS)CIrpQueue(NULL);
602 if (!This)
603 return STATUS_INSUFFICIENT_RESOURCES;
604
605 This->AddRef();
606
607 *Queue = (IIrpQueue*)This;
608 return STATUS_SUCCESS;
609 }
610