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