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