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