1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            drivers/wdm/audio/backpln/portcls/pin_dmus.cpp
5  * PURPOSE:         DMus IRP Audio Pin
6  * PROGRAMMER:      Johannes Anderwald
7  */
8 
9 #include "private.hpp"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 class CPortPinDMus : public CUnknownImpl<IPortPinDMus>
15 {
16 public:
17     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
18 
19     IMP_IPortPinDMus;
20     IMP_IServiceSink;
21     IMP_IMasterClock;
22     IMP_IAllocatorMXF;
23 
CPortPinDMus(IUnknown * OuterUnknown)24      CPortPinDMus(IUnknown * OuterUnknown){}
~CPortPinDMus()25      virtual ~CPortPinDMus(){}
26 
27 protected:
28     VOID TransferMidiDataToDMus();
29     VOID TransferMidiData();
30 
31     IPortDMus * m_Port;
32     IPortFilterDMus * m_Filter;
33     KSPIN_DESCRIPTOR * m_KsPinDescriptor;
34     PMINIPORTDMUS m_Miniport;
35 
36     PSERVICEGROUP m_ServiceGroup;
37 
38     PMXF m_Mxf;
39     ULONGLONG m_SchedulePreFetch;
40     NPAGED_LOOKASIDE_LIST m_LookAsideEvent;
41     NPAGED_LOOKASIDE_LIST m_LookAsideBuffer;
42 
43     PMINIPORTMIDI m_MidiMiniport;
44     PMINIPORTMIDISTREAM m_MidiStream;
45 
46     KSSTATE m_State;
47     PKSDATAFORMAT m_Format;
48     KSPIN_CONNECT * m_ConnectDetails;
49 
50     DMUS_STREAM_TYPE m_Capture;
51     PDEVICE_OBJECT m_DeviceObject;
52     IIrpQueue * m_IrpQueue;
53 
54     ULONG m_TotalPackets;
55     ULONG m_PreCompleted;
56     ULONG m_PostCompleted;
57 
58     ULONG m_LastTag;
59 };
60 
61 typedef struct
62 {
63     DMUS_KERNEL_EVENT Event;
64     PVOID Tag;
65 }DMUS_KERNEL_EVENT_WITH_TAG, *PDMUS_KERNEL_EVENT_WITH_TAG;
66 
67 typedef struct
68 {
69     CPortPinDMus *Pin;
70     PIO_WORKITEM WorkItem;
71     KSSTATE State;
72 }SETSTREAM_CONTEXT, *PSETSTREAM_CONTEXT;
73 
74 //==================================================================================================================================
75 NTSTATUS
76 NTAPI
GetTime(OUT REFERENCE_TIME * prtTime)77 CPortPinDMus::GetTime(OUT REFERENCE_TIME  *prtTime)
78 {
79     UNIMPLEMENTED;
80     return STATUS_SUCCESS;
81 }
82 
83 //==================================================================================================================================
84 NTSTATUS
85 NTAPI
GetMessage(OUT PDMUS_KERNEL_EVENT * ppDMKEvt)86 CPortPinDMus::GetMessage(
87     OUT PDMUS_KERNEL_EVENT * ppDMKEvt)
88 {
89     PVOID Buffer;
90 
91     Buffer = ExAllocateFromNPagedLookasideList(&m_LookAsideEvent);
92     if (!Buffer)
93         return STATUS_INSUFFICIENT_RESOURCES;
94 
95     *ppDMKEvt = (PDMUS_KERNEL_EVENT)Buffer;
96     RtlZeroMemory(Buffer, sizeof(DMUS_KERNEL_EVENT));
97     return STATUS_SUCCESS;
98 }
99 
100 USHORT
101 NTAPI
GetBufferSize()102 CPortPinDMus::GetBufferSize()
103 {
104     return PAGE_SIZE;
105 }
106 
107 NTSTATUS
108 NTAPI
GetBuffer(OUT PBYTE * ppBuffer)109 CPortPinDMus::GetBuffer(
110     OUT PBYTE * ppBuffer)
111 {
112     PVOID Buffer;
113 
114     Buffer = ExAllocateFromNPagedLookasideList(&m_LookAsideBuffer);
115     if (!Buffer)
116         return STATUS_INSUFFICIENT_RESOURCES;
117 
118     *ppBuffer = (PBYTE)Buffer;
119     RtlZeroMemory(Buffer, PAGE_SIZE);
120     return STATUS_SUCCESS;
121 }
122 
123 NTSTATUS
124 NTAPI
PutBuffer(IN PBYTE pBuffer)125 CPortPinDMus::PutBuffer(
126     IN PBYTE pBuffer)
127 {
128     PDMUS_KERNEL_EVENT_WITH_TAG Event = (PDMUS_KERNEL_EVENT_WITH_TAG)pBuffer;
129 
130     m_IrpQueue->ReleaseMappingWithTag(Event->Tag);
131 
132     ExFreeToNPagedLookasideList(&m_LookAsideBuffer, pBuffer);
133     return STATUS_SUCCESS;
134 }
135 
136 NTSTATUS
137 NTAPI
SetState(IN KSSTATE State)138 CPortPinDMus::SetState(
139     IN KSSTATE State)
140 {
141     UNIMPLEMENTED;
142     return STATUS_NOT_IMPLEMENTED;
143 }
144 
145 NTSTATUS
146 NTAPI
PutMessage(IN PDMUS_KERNEL_EVENT pDMKEvt)147 CPortPinDMus::PutMessage(
148     IN PDMUS_KERNEL_EVENT pDMKEvt)
149 {
150     ExFreeToNPagedLookasideList(&m_LookAsideEvent, pDMKEvt);
151     return STATUS_SUCCESS;
152 }
153 
154 NTSTATUS
155 NTAPI
ConnectOutput(IN PMXF sinkMXF)156 CPortPinDMus::ConnectOutput(
157     IN PMXF sinkMXF)
158 {
159     UNIMPLEMENTED;
160     return STATUS_NOT_IMPLEMENTED;
161 }
162 
163 NTSTATUS
164 NTAPI
DisconnectOutput(IN PMXF sinkMXF)165 CPortPinDMus::DisconnectOutput(
166     IN PMXF sinkMXF)
167 {
168     UNIMPLEMENTED;
169     return STATUS_NOT_IMPLEMENTED;
170 }
171 
172 //==================================================================================================================================
173 
174 VOID
TransferMidiData()175 CPortPinDMus::TransferMidiData()
176 {
177     NTSTATUS Status;
178     PUCHAR Buffer;
179     ULONG BufferSize;
180     ULONG BytesWritten;
181 
182     do
183     {
184         Status = m_IrpQueue->GetMapping(&Buffer, &BufferSize);
185         if (!NT_SUCCESS(Status))
186         {
187             return;
188         }
189 
190         if (m_Capture)
191         {
192             Status = m_MidiStream->Read(Buffer, BufferSize, &BytesWritten);
193             if (!NT_SUCCESS(Status))
194             {
195                 DPRINT("Read failed with %x\n", Status);
196                 return;
197             }
198         }
199         else
200         {
201             Status = m_MidiStream->Write(Buffer, BufferSize, &BytesWritten);
202             if (!NT_SUCCESS(Status))
203             {
204                 DPRINT("Write failed with %x\n", Status);
205                 return;
206             }
207         }
208 
209         if (!BytesWritten)
210         {
211             DPRINT("Device is busy retry later\n");
212             return;
213         }
214 
215         m_IrpQueue->UpdateMapping(BytesWritten);
216 
217     }while(TRUE);
218 
219 }
220 
221 VOID
TransferMidiDataToDMus()222 CPortPinDMus::TransferMidiDataToDMus()
223 {
224     NTSTATUS Status;
225     PHYSICAL_ADDRESS  PhysicalAddress;
226     ULONG BufferSize, Flags;
227     PVOID Buffer;
228     PDMUS_KERNEL_EVENT_WITH_TAG Event, LastEvent = NULL, Root = NULL;
229 
230     do
231     {
232         m_LastTag++;
233         Status = m_IrpQueue->GetMappingWithTag(UlongToPtr(m_LastTag), &PhysicalAddress, &Buffer, &BufferSize, &Flags);
234         if (!NT_SUCCESS(Status))
235         {
236             break;
237         }
238 
239         Status = GetMessage((PDMUS_KERNEL_EVENT*)&Event);
240         if (!NT_SUCCESS(Status))
241             break;
242 
243         //FIXME
244         //set up struct
245         //Event->Event.usFlags = DMUS_KEF_EVENT_COMPLETE;
246         Event->Event.cbStruct = sizeof(DMUS_KERNEL_EVENT);
247         Event->Event.cbEvent = (USHORT)BufferSize;
248         Event->Event.uData.pbData = (PBYTE)Buffer;
249 
250         if (!Root)
251             Root = Event;
252         else
253             LastEvent->Event.pNextEvt = (struct _DMUS_KERNEL_EVENT *)Event;
254 
255         LastEvent = Event;
256         LastEvent->Event.pNextEvt = NULL;
257         LastEvent->Tag = UlongToPtr(m_LastTag);
258 
259     }while(TRUE);
260 
261     if (!Root)
262     {
263         return;
264     }
265 
266     Status = m_Mxf->PutMessage((PDMUS_KERNEL_EVENT)Root);
267     DPRINT("Status %x\n", Status);
268 }
269 
270 VOID
271 NTAPI
RequestService()272 CPortPinDMus::RequestService()
273 {
274     PC_ASSERT_IRQL(DISPATCH_LEVEL);
275 
276     if (m_MidiStream)
277     {
278         TransferMidiData();
279     }
280     else if (m_Mxf)
281     {
282         TransferMidiDataToDMus();
283     }
284 }
285 
286 //==================================================================================================================================
287 
288 NTSTATUS
289 NTAPI
QueryInterface(IN REFIID refiid,OUT PVOID * Output)290 CPortPinDMus::QueryInterface(
291     IN  REFIID refiid,
292     OUT PVOID* Output)
293 {
294 
295     if (IsEqualGUIDAligned(refiid, IID_IIrpTarget) ||
296         IsEqualGUIDAligned(refiid, IID_IUnknown))
297     {
298         *Output = PVOID(PUNKNOWN(this));
299         PUNKNOWN(*Output)->AddRef();
300         return STATUS_SUCCESS;
301     }
302 
303     return STATUS_UNSUCCESSFUL;
304 }
305 
306 NTSTATUS
307 NTAPI
NewIrpTarget(OUT struct IIrpTarget ** OutTarget,IN PCWSTR Name,IN PUNKNOWN Unknown,IN POOL_TYPE PoolType,IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN KSOBJECT_CREATE * CreateObject)308 CPortPinDMus::NewIrpTarget(
309     OUT struct IIrpTarget **OutTarget,
310     IN PCWSTR Name,
311     IN PUNKNOWN Unknown,
312     IN POOL_TYPE PoolType,
313     IN PDEVICE_OBJECT DeviceObject,
314     IN PIRP Irp,
315     IN KSOBJECT_CREATE *CreateObject)
316 {
317     UNIMPLEMENTED;
318 
319     Irp->IoStatus.Information = 0;
320     Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
321     IoCompleteRequest(Irp, IO_NO_INCREMENT);
322 
323     return STATUS_UNSUCCESSFUL;
324 }
325 
326 NTSTATUS
327 NTAPI
DeviceIoControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)328 CPortPinDMus::DeviceIoControl(
329     IN PDEVICE_OBJECT DeviceObject,
330     IN PIRP Irp)
331 {
332     UNIMPLEMENTED;
333 
334     Irp->IoStatus.Information = 0;
335     Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
336     IoCompleteRequest(Irp, IO_NO_INCREMENT);
337 
338     return STATUS_UNSUCCESSFUL;
339 }
340 
341 NTSTATUS
342 NTAPI
Read(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)343 CPortPinDMus::Read(
344     IN PDEVICE_OBJECT DeviceObject,
345     IN PIRP Irp)
346 {
347     return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
348 }
349 
350 NTSTATUS
351 NTAPI
Write(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)352 CPortPinDMus::Write(
353     IN PDEVICE_OBJECT DeviceObject,
354     IN PIRP Irp)
355 {
356     return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
357 }
358 
359 NTSTATUS
360 NTAPI
Flush(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)361 CPortPinDMus::Flush(
362     IN PDEVICE_OBJECT DeviceObject,
363     IN PIRP Irp)
364 {
365     return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
366 }
367 
368 NTSTATUS
369 NTAPI
Close(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)370 CPortPinDMus::Close(
371     IN PDEVICE_OBJECT DeviceObject,
372     IN PIRP Irp)
373 {
374     NTSTATUS Status;
375     ISubdevice * SubDevice;
376     PSUBDEVICE_DESCRIPTOR Descriptor;
377 
378     if (m_ServiceGroup)
379     {
380         m_ServiceGroup->RemoveMember(PSERVICESINK(this));
381     }
382 
383     if (m_MidiStream)
384     {
385         if (m_State != KSSTATE_STOP)
386         {
387             m_MidiStream->SetState(KSSTATE_STOP);
388             m_State = KSSTATE_STOP;
389         }
390         DPRINT("Closing stream at Irql %u\n", KeGetCurrentIrql());
391         m_MidiStream->Release();
392     }
393 
394     Status = m_Port->QueryInterface(IID_ISubdevice, (PVOID*)&SubDevice);
395     if (NT_SUCCESS(Status))
396     {
397         Status = SubDevice->GetDescriptor(&Descriptor);
398         if (NT_SUCCESS(Status))
399         {
400             // release reference count
401             Descriptor->Factory.Instances[m_ConnectDetails->PinId].CurrentPinInstanceCount--;
402         }
403         SubDevice->Release();
404     }
405 
406     if (m_Format)
407     {
408         FreeItem(m_Format, TAG_PORTCLASS);
409         m_Format = NULL;
410     }
411 
412     // complete the irp
413     Irp->IoStatus.Information = 0;
414     Irp->IoStatus.Status = STATUS_SUCCESS;
415     IoCompleteRequest(Irp, IO_NO_INCREMENT);
416 
417     // destroy DMus pin
418     m_Filter->FreePin(PPORTPINDMUS(this));
419 
420     return STATUS_SUCCESS;
421 }
422 
423 NTSTATUS
424 NTAPI
QuerySecurity(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)425 CPortPinDMus::QuerySecurity(
426     IN PDEVICE_OBJECT DeviceObject,
427     IN PIRP Irp)
428 {
429     return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
430 }
431 
432 NTSTATUS
433 NTAPI
SetSecurity(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)434 CPortPinDMus::SetSecurity(
435     IN PDEVICE_OBJECT DeviceObject,
436     IN PIRP Irp)
437 {
438     return KsDispatchInvalidDeviceRequest(DeviceObject, Irp);
439 }
440 
441 BOOLEAN
442 NTAPI
FastDeviceIoControl(IN PFILE_OBJECT FileObject,IN BOOLEAN Wait,IN PVOID InputBuffer,IN ULONG InputBufferLength,OUT PVOID OutputBuffer,IN ULONG OutputBufferLength,IN ULONG IoControlCode,OUT PIO_STATUS_BLOCK StatusBlock,IN PDEVICE_OBJECT DeviceObject)443 CPortPinDMus::FastDeviceIoControl(
444     IN PFILE_OBJECT FileObject,
445     IN BOOLEAN Wait,
446     IN PVOID InputBuffer,
447     IN ULONG InputBufferLength,
448     OUT PVOID OutputBuffer,
449     IN ULONG OutputBufferLength,
450     IN ULONG IoControlCode,
451     OUT PIO_STATUS_BLOCK StatusBlock,
452     IN PDEVICE_OBJECT DeviceObject)
453 {
454     return FALSE;
455 }
456 
457 BOOLEAN
458 NTAPI
FastRead(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN ULONG LockKey,IN PVOID Buffer,OUT PIO_STATUS_BLOCK StatusBlock,IN PDEVICE_OBJECT DeviceObject)459 CPortPinDMus::FastRead(
460     IN PFILE_OBJECT FileObject,
461     IN PLARGE_INTEGER FileOffset,
462     IN ULONG Length,
463     IN BOOLEAN Wait,
464     IN ULONG LockKey,
465     IN PVOID Buffer,
466     OUT PIO_STATUS_BLOCK StatusBlock,
467     IN PDEVICE_OBJECT DeviceObject)
468 {
469     return FALSE;
470 }
471 
472 BOOLEAN
473 NTAPI
FastWrite(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER FileOffset,IN ULONG Length,IN BOOLEAN Wait,IN ULONG LockKey,IN PVOID Buffer,OUT PIO_STATUS_BLOCK StatusBlock,IN PDEVICE_OBJECT DeviceObject)474 CPortPinDMus::FastWrite(
475     IN PFILE_OBJECT FileObject,
476     IN PLARGE_INTEGER FileOffset,
477     IN ULONG Length,
478     IN BOOLEAN Wait,
479     IN ULONG LockKey,
480     IN PVOID Buffer,
481     OUT PIO_STATUS_BLOCK StatusBlock,
482     IN PDEVICE_OBJECT DeviceObject)
483 {
484     return FALSE;
485 }
486 
487 NTSTATUS
488 NTAPI
Init(IN PPORTDMUS Port,IN PPORTFILTERDMUS Filter,IN KSPIN_CONNECT * ConnectDetails,IN KSPIN_DESCRIPTOR * KsPinDescriptor,IN PDEVICE_OBJECT DeviceObject)489 CPortPinDMus::Init(
490     IN PPORTDMUS Port,
491     IN PPORTFILTERDMUS Filter,
492     IN KSPIN_CONNECT * ConnectDetails,
493     IN KSPIN_DESCRIPTOR * KsPinDescriptor,
494     IN PDEVICE_OBJECT DeviceObject)
495 {
496     NTSTATUS Status;
497     PKSDATAFORMAT DataFormat;
498     DMUS_STREAM_TYPE Type;
499 
500     Port->AddRef();
501     Filter->AddRef();
502 
503     m_Port = Port;
504     m_Filter = Filter;
505     m_KsPinDescriptor = KsPinDescriptor;
506     m_ConnectDetails = ConnectDetails;
507     m_DeviceObject = DeviceObject;
508 
509     GetDMusMiniport(Port, &m_Miniport, &m_MidiMiniport);
510 
511     DataFormat = (PKSDATAFORMAT)(ConnectDetails + 1);
512 
513     DPRINT("CPortPinDMus::Init entered\n");
514 
515     m_Format = (PKSDATAFORMAT)AllocateItem(NonPagedPool, DataFormat->FormatSize, TAG_PORTCLASS);
516     if (!m_Format)
517         return STATUS_INSUFFICIENT_RESOURCES;
518 
519     RtlMoveMemory(m_Format, DataFormat, DataFormat->FormatSize);
520 
521     if (KsPinDescriptor->Communication == KSPIN_COMMUNICATION_SINK && KsPinDescriptor->DataFlow == KSPIN_DATAFLOW_IN)
522     {
523         Type = DMUS_STREAM_MIDI_RENDER;
524     }
525     else if (KsPinDescriptor->Communication == KSPIN_COMMUNICATION_SINK && KsPinDescriptor->DataFlow == KSPIN_DATAFLOW_OUT)
526     {
527         Type = DMUS_STREAM_MIDI_CAPTURE;
528     }
529     else
530     {
531         DPRINT("Unexpected Communication %u DataFlow %u\n", KsPinDescriptor->Communication, KsPinDescriptor->DataFlow);
532         DbgBreakPoint();
533         while(TRUE);
534     }
535 
536     Status = NewIrpQueue(&m_IrpQueue);
537     if (!NT_SUCCESS(Status))
538     {
539         DPRINT("Failed to allocate IrpQueue with %x\n", Status);
540         return Status;
541     }
542 
543     if (m_MidiMiniport)
544     {
545         Status = m_MidiMiniport->NewStream(&m_MidiStream, NULL, NonPagedPool, ConnectDetails->PinId, Type, m_Format, &m_ServiceGroup);
546 
547         DPRINT("CPortPinDMus::Init Status %x\n", Status);
548 
549         if (!NT_SUCCESS(Status))
550             return Status;
551     }
552     else
553     {
554         Status = m_Miniport->NewStream(&m_Mxf, NULL, NonPagedPool, ConnectDetails->PinId, Type, m_Format, &m_ServiceGroup, PAllocatorMXF(this), PMASTERCLOCK(this),&m_SchedulePreFetch);
555 
556         DPRINT("CPortPinDMus::Init Status %x\n", Status);
557 
558         if (!NT_SUCCESS(Status))
559             return Status;
560 
561         if (Type == DMUS_STREAM_MIDI_CAPTURE)
562         {
563             Status = m_Mxf->ConnectOutput(PMXF(this));
564             if (!NT_SUCCESS(Status))
565             {
566                 DPRINT("IMXF_ConnectOutput failed with Status %x\n", Status);
567                 return Status;
568             }
569         }
570 
571         ExInitializeNPagedLookasideList(&m_LookAsideEvent, NULL, NULL, 0, sizeof(DMUS_KERNEL_EVENT_WITH_TAG), TAG_PORTCLASS, 0);
572         ExInitializeNPagedLookasideList(&m_LookAsideBuffer, NULL, NULL, 0, PAGE_SIZE, TAG_PORTCLASS, 0);
573     }
574 
575     if (m_ServiceGroup)
576     {
577         Status = m_ServiceGroup->AddMember(PSERVICESINK(this));
578         if (!NT_SUCCESS(Status))
579         {
580             DPRINT("Failed to add pin to service group\n");
581             return Status;
582         }
583     }
584 
585     Status = m_IrpQueue->Init(ConnectDetails, KsPinDescriptor, 0, 0, FALSE);
586     if (!NT_SUCCESS(Status))
587     {
588         DPRINT("IrpQueue_Init failed with %x\n", Status);
589         return Status;
590     }
591 
592     m_State = KSSTATE_STOP;
593     m_Capture = Type;
594 
595     return STATUS_SUCCESS;
596 }
597 
598 VOID
599 NTAPI
Notify()600 CPortPinDMus::Notify()
601 {
602     m_ServiceGroup->RequestService();
603 }
604 
605 NTSTATUS
NewPortPinDMus(OUT IPortPinDMus ** OutPin)606 NewPortPinDMus(
607     OUT IPortPinDMus ** OutPin)
608 {
609     CPortPinDMus * This;
610 
611     This = new (NonPagedPool, TAG_PORTCLASS)CPortPinDMus(NULL);
612     if (!This)
613         return STATUS_INSUFFICIENT_RESOURCES;
614 
615     This->AddRef();
616 
617     // store result
618     *OutPin = (IPortPinDMus*)This;
619 
620     return STATUS_SUCCESS;
621 }
622