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