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