1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            drivers/wdm/audio/backpln/portcls/dma_init.c
5  * PURPOSE:         portcls dma support object
6  * PROGRAMMER:      Johannes Anderwald
7  */
8 
9 #include "private.hpp"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 class CDmaChannelInit : public CUnknownImpl<IDmaChannelInit>
15 {
16 public:
17     inline
18     PVOID
19     operator new(
20         size_t Size,
21         POOL_TYPE PoolType,
22         ULONG Tag)
23     {
24         return ExAllocatePoolWithTag(PoolType, Size, Tag);
25     }
26 
27     STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
28 
29     IMP_IDmaChannelInit;
30     CDmaChannelInit(IUnknown * OuterUnknown) :
31         m_pDeviceObject(nullptr),
32         m_pAdapter(nullptr),
33         m_DmaStarted(FALSE),
34         m_MapSize(0),
35         m_MapRegisterBase(nullptr),
36         m_LastTransferCount(0),
37         m_MaximumBufferSize(0),
38         m_MaxMapRegisters(0),
39         m_AllocatedBufferSize(0),
40         m_BufferSize(0),
41         m_Address({0}),
42         m_Buffer(nullptr),
43         m_Mdl(nullptr),
44         m_WriteToDevice(FALSE)
45     {
46     }
47     virtual ~CDmaChannelInit(){}
48 
49 protected:
50 
51     PDEVICE_OBJECT m_pDeviceObject;
52     PDMA_ADAPTER m_pAdapter;
53 
54     BOOL m_DmaStarted;
55 
56     ULONG m_MapSize;
57     PVOID m_MapRegisterBase;
58 
59     ULONG m_LastTransferCount;
60 
61     ULONG m_MaximumBufferSize;
62     ULONG m_MaxMapRegisters;
63     ULONG m_AllocatedBufferSize;
64     ULONG m_BufferSize;
65 
66     PHYSICAL_ADDRESS m_Address;
67     PVOID m_Buffer;
68     PMDL m_Mdl;
69     BOOLEAN m_WriteToDevice;
70 
71     friend IO_ALLOCATION_ACTION NTAPI AdapterControl(IN PDEVICE_OBJECT  DeviceObject, IN PIRP  Irp, IN PVOID  MapRegisterBase, IN PVOID  Context);
72 };
73 
74 //---------------------------------------------------------------
75 // IUnknown methods
76 //
77 
78 extern GUID IID_IDmaChannelSlave;
79 
80 NTSTATUS
81 NTAPI
82 CDmaChannelInit::QueryInterface(
83     IN  REFIID refiid,
84     OUT PVOID* Output)
85 {
86     if (IsEqualGUIDAligned(refiid, IID_IUnknown) ||
87         IsEqualGUIDAligned(refiid, IID_IDmaChannel))
88         //IsEqualGUIDAligned(refiid, IID_IDmaChannelSlave)) // HACK
89     {
90         *Output = PVOID(PUNKNOWN(this));
91         PUNKNOWN(*Output)->AddRef();
92         return STATUS_SUCCESS;
93     }
94     DPRINT("No interface!!!\n");
95     return STATUS_UNSUCCESSFUL;
96 }
97 
98 //---------------------------------------------------------------
99 // IDmaChannel methods
100 //
101 
102 NTSTATUS
103 NTAPI
104 CDmaChannelInit::AllocateBuffer(
105     IN ULONG BufferSize,
106     IN PPHYSICAL_ADDRESS  PhysicalAddressConstraint OPTIONAL)
107 {
108     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
109 
110     // Did the caller already allocate a buffer ?*/
111     if (m_Buffer)
112     {
113         DPRINT("CDmaChannelInit_AllocateBuffer free common buffer first \n");
114         return STATUS_UNSUCCESSFUL;
115     }
116 
117     m_Buffer = m_pAdapter->DmaOperations->AllocateCommonBuffer(m_pAdapter, BufferSize, &m_Address, FALSE);
118     if (!m_Buffer)
119     {
120         DPRINT("CDmaChannelInit_AllocateBuffer fAllocateCommonBuffer failed \n");
121         return STATUS_UNSUCCESSFUL;
122     }
123 
124     m_BufferSize = BufferSize;
125     m_AllocatedBufferSize = BufferSize;
126     DPRINT("CDmaChannelInit::AllocateBuffer Success Buffer %p BufferSize %u Address %x\n", m_Buffer, BufferSize, m_Address);
127 
128     return STATUS_SUCCESS;
129 }
130 
131 ULONG
132 NTAPI
133 CDmaChannelInit::AllocatedBufferSize()
134 {
135     DPRINT("CDmaChannelInit_AllocatedBufferSize: this %p BufferSize %u\n", this, m_BufferSize);
136     return m_AllocatedBufferSize;
137 }
138 
139 VOID
140 NTAPI
141 CDmaChannelInit::CopyFrom(
142     IN PVOID  Destination,
143     IN PVOID  Source,
144     IN ULONG  ByteCount
145     )
146 {
147     DPRINT("CDmaChannelInit_CopyFrom: this %p Destination %p Source %p ByteCount %u\n", this, Destination, Source, ByteCount);
148 
149     CopyTo(Destination, Source, ByteCount);
150 }
151 
152 VOID
153 NTAPI
154 CDmaChannelInit::CopyTo(
155     IN PVOID  Destination,
156     IN PVOID  Source,
157     IN ULONG  ByteCount
158     )
159 {
160     DPRINT("CDmaChannelInit_CopyTo: this %p Destination %p Source %p ByteCount %u\n", this, Destination, Source, ByteCount);
161     RtlCopyMemory(Destination, Source, ByteCount);
162 }
163 
164 VOID
165 NTAPI
166 CDmaChannelInit::FreeBuffer()
167 {
168     DPRINT("CDmaChannelInit_FreeBuffer: this %p\n", this);
169 
170     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
171 
172     if (!m_Buffer)
173     {
174         DPRINT("CDmaChannelInit_FreeBuffer allocate common buffer first \n");
175         return;
176     }
177 
178     m_pAdapter->DmaOperations->FreeCommonBuffer(m_pAdapter, m_AllocatedBufferSize, m_Address, m_Buffer, FALSE);
179     m_Buffer = NULL;
180     m_AllocatedBufferSize = 0;
181     m_Address.QuadPart = 0LL;
182 
183     if (m_Mdl)
184     {
185         IoFreeMdl(m_Mdl);
186         m_Mdl = NULL;
187     }
188 }
189 
190 PADAPTER_OBJECT
191 NTAPI
192 CDmaChannelInit::GetAdapterObject()
193 {
194     DPRINT("CDmaChannelInit_GetAdapterObject: this %p\n", this);
195     return (PADAPTER_OBJECT)m_pAdapter;
196 }
197 
198 ULONG
199 NTAPI
200 CDmaChannelInit::MaximumBufferSize()
201 {
202     DPRINT("CDmaChannelInit_MaximumBufferSize: this %p\n", this);
203     return m_MaximumBufferSize;
204 }
205 
206 #ifdef _MSC_VER
207 
208 PHYSICAL_ADDRESS
209 NTAPI
210 CDmaChannelInit::PhysicalAddress()
211 {
212     DPRINT("CDmaChannelInit_PhysicalAddress: this %p Virtual %p Physical High %x Low %x%\n", this, m_Buffer, m_Address.HighPart, m_Address.LowPart);
213 
214     return m_Address;
215 }
216 
217 #else
218 
219 PHYSICAL_ADDRESS
220 NTAPI
221 CDmaChannelInit::PhysicalAddress(
222     PPHYSICAL_ADDRESS Address)
223 {
224     DPRINT("CDmaChannelInit_PhysicalAddress: this %p Virtual %p Physical High %x Low %x%\n", this, m_Buffer, m_Address.HighPart, m_Address.LowPart);
225 
226     PHYSICAL_ADDRESS Result;
227 
228     Address->QuadPart = m_Address.QuadPart;
229     Result.QuadPart = (ULONG_PTR)Address;
230     return Result;
231 }
232 
233 #endif
234 
235 VOID
236 NTAPI
237 CDmaChannelInit::SetBufferSize(
238     IN ULONG BufferSize)
239 {
240     DPRINT("CDmaChannelInit_SetBufferSize: this %p\n", this);
241     m_BufferSize = BufferSize;
242 
243 }
244 
245 ULONG
246 NTAPI
247 CDmaChannelInit::BufferSize()
248 {
249     DPRINT("BufferSize %u\n", m_BufferSize);
250     PC_ASSERT(m_BufferSize);
251     return m_BufferSize;
252 }
253 
254 PVOID
255 NTAPI
256 CDmaChannelInit::SystemAddress()
257 {
258     DPRINT("CDmaChannelInit_SystemAddress: this %p\n", this);
259     return m_Buffer;
260 }
261 
262 ULONG
263 NTAPI
264 CDmaChannelInit::TransferCount()
265 {
266     DPRINT("CDmaChannelInit_TransferCount: this %p\n", this);
267     return m_LastTransferCount;
268 }
269 
270 ULONG
271 NTAPI
272 CDmaChannelInit::ReadCounter()
273 {
274     ULONG Counter;
275 
276     PC_ASSERT_IRQL(DISPATCH_LEVEL);
277 
278     Counter = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter);
279 
280     if (!m_DmaStarted || Counter >= m_LastTransferCount)
281         Counter = 0;
282 
283     DPRINT("ReadCounter %u\n", Counter);
284 
285     return Counter;
286 }
287 
288 IO_ALLOCATION_ACTION
289 NTAPI
290 AdapterControl(
291     IN PDEVICE_OBJECT  DeviceObject,
292     IN PIRP  Irp,
293     IN PVOID  MapRegisterBase,
294     IN PVOID  Context)
295 {
296     ULONG Length;
297     CDmaChannelInit * This = (CDmaChannelInit*)Context;
298 
299     Length = This->m_MapSize;
300     This->m_MapRegisterBase = MapRegisterBase;
301 
302     This->m_pAdapter->DmaOperations->MapTransfer(This->m_pAdapter,
303                                                 This->m_Mdl,
304                                                 MapRegisterBase,
305                                                 (PVOID)((ULONG_PTR)This->m_Mdl->StartVa + This->m_Mdl->ByteOffset),
306                                                 &Length,
307                                                 This->m_WriteToDevice);
308 
309     if (Length == This->m_BufferSize)
310     {
311         This->m_DmaStarted = TRUE;
312     }
313 
314    return KeepObject;
315 }
316 
317 NTSTATUS
318 NTAPI
319 CDmaChannelInit::Start(
320     ULONG  MapSize,
321     BOOLEAN WriteToDevice)
322 {
323     NTSTATUS Status;
324     ULONG MapRegisters;
325     KIRQL OldIrql;
326 
327     DPRINT("CDmaChannelInit_Start: this %p\n", this);
328 
329     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
330 
331     if (m_DmaStarted)
332         return STATUS_UNSUCCESSFUL;
333 
334     if (!m_Mdl)
335     {
336         m_Mdl = IoAllocateMdl(m_Buffer, m_MaximumBufferSize, FALSE, FALSE, NULL);
337         if (!m_Mdl)
338         {
339             return STATUS_INSUFFICIENT_RESOURCES;
340         }
341         MmBuildMdlForNonPagedPool(m_Mdl);
342     }
343 
344     m_MapSize = MapSize;
345     m_WriteToDevice = WriteToDevice;
346     m_LastTransferCount = MapSize;
347 
348     //FIXME
349     // synchronize access
350     //
351     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
352 
353     MapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(m_Buffer, MapSize);
354     Status = m_pAdapter->DmaOperations->AllocateAdapterChannel(m_pAdapter, m_pDeviceObject, MapRegisters, AdapterControl, (PVOID)this);
355     KeLowerIrql(OldIrql);
356 
357     if(!NT_SUCCESS(Status))
358         m_LastTransferCount = 0;
359 
360     return Status;
361 }
362 
363 NTSTATUS
364 NTAPI
365 CDmaChannelInit::Stop()
366 {
367     KIRQL OldIrql;
368 
369     DPRINT("CDmaChannelInit::Stop: this %p\n", this);
370     PC_ASSERT_IRQL(DISPATCH_LEVEL);
371 
372     if (!m_DmaStarted)
373         return STATUS_SUCCESS;
374 
375     m_pAdapter->DmaOperations->FlushAdapterBuffers(m_pAdapter,
376                                                        m_Mdl,
377                                                        m_MapRegisterBase,
378                                                        (PVOID)((ULONG_PTR)m_Mdl->StartVa + m_Mdl->ByteOffset),
379                                                        m_MapSize,
380                                                        m_WriteToDevice);
381 
382     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
383 
384     m_pAdapter->DmaOperations->FreeAdapterChannel(m_pAdapter);
385 
386     KeLowerIrql(OldIrql);
387 
388     m_DmaStarted = FALSE;
389 
390     IoFreeMdl(m_Mdl);
391     m_Mdl = NULL;
392 
393     return STATUS_SUCCESS;
394 }
395 
396 NTSTATUS
397 NTAPI
398 CDmaChannelInit::WaitForTC(
399     ULONG  Timeout)
400 {
401     ULONG RetryCount;
402     ULONG BytesRemaining;
403     ULONG PrevBytesRemaining;
404 
405     PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
406 
407     BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter);
408     if (!BytesRemaining)
409     {
410         return STATUS_SUCCESS;
411     }
412 
413     RetryCount = Timeout / 10;
414     PrevBytesRemaining = 0xFFFFFFFF;
415     do
416     {
417         BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter);
418 
419         if (!BytesRemaining)
420             break;
421 
422         if (PrevBytesRemaining == BytesRemaining)
423             break;
424 
425         KeStallExecutionProcessor(10);
426         PrevBytesRemaining = BytesRemaining;
427 
428     }while(RetryCount-- >= 1);
429 
430     if (BytesRemaining)
431     {
432         return STATUS_UNSUCCESSFUL;
433     }
434 
435     return STATUS_SUCCESS;
436 
437 }
438 
439 NTSTATUS
440 NTAPI
441 CDmaChannelInit::Init(
442     IN PDEVICE_DESCRIPTION DeviceDescription,
443     IN PDEVICE_OBJECT DeviceObject)
444 {
445     INTERFACE_TYPE BusType;
446     NTSTATUS Status;
447     PDMA_ADAPTER Adapter;
448     PPCLASS_DEVICE_EXTENSION DeviceExt;
449     ULONG MapRegisters;
450     ULONG ResultLength;
451 
452     // Get bus type
453     Status = IoGetDeviceProperty(DeviceObject, DevicePropertyLegacyBusType, sizeof(BusType), (PVOID)&BusType, &ResultLength);
454     if (NT_SUCCESS(Status))
455     {
456         DeviceDescription->InterfaceType = BusType;
457     }
458     // Fetch device extension
459     DeviceExt = (PPCLASS_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
460     // Acquire dma adapter
461     Adapter = IoGetDmaAdapter(DeviceExt->PhysicalDeviceObject, DeviceDescription, &MapRegisters);
462     if (!Adapter)
463     {
464         FreeItem(this, TAG_PORTCLASS);
465         return STATUS_DEVICE_CONFIGURATION_ERROR;
466     }
467 
468     // initialize object
469     m_pAdapter = Adapter;
470     m_pDeviceObject = DeviceObject;
471     m_MaximumBufferSize = DeviceDescription->MaximumLength;
472     m_MaxMapRegisters = MapRegisters;
473 
474     return STATUS_SUCCESS;
475 }
476 
477 NTSTATUS
478 NTAPI
479 PcNewDmaChannel(
480     OUT PDMACHANNEL* OutDmaChannel,
481     IN  PUNKNOWN OuterUnknown OPTIONAL,
482     IN  POOL_TYPE PoolType,
483     IN  PDEVICE_DESCRIPTION DeviceDescription,
484     IN  PDEVICE_OBJECT DeviceObject)
485 {
486     NTSTATUS Status;
487     CDmaChannelInit * This;
488 
489     DPRINT("OutDmaChannel %p OuterUnknown %p PoolType %p DeviceDescription %p DeviceObject %p\n",
490             OutDmaChannel, OuterUnknown, PoolType, DeviceDescription, DeviceObject);
491 
492     This = new(PoolType, TAG_PORTCLASS)CDmaChannelInit(OuterUnknown);
493     if (!This)
494         return STATUS_INSUFFICIENT_RESOURCES;
495 
496     Status = This->QueryInterface(IID_IDmaChannel, (PVOID*)OutDmaChannel);
497 
498     if (!NT_SUCCESS(Status))
499     {
500         delete This;
501         return Status;
502     }
503 
504     Status = This->Init(DeviceDescription, DeviceObject);
505 
506     if (!NT_SUCCESS(Status))
507     {
508         delete This;
509         return Status;
510     }
511 
512     return Status;
513 }
514