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