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