dbd146bde4103ccc69d4da2979a832b3879b7922
[reactos.git] / reactos / drivers / wdm / audio / backpln / portcls / pin_dmus.cpp
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