Merge trunk head (r49270)
[reactos.git] / 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 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);
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