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