Merge 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 DPRINT("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 DPRINT("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 DPRINT("CDmaChannelInit_AllocateBuffer fAllocateCommonBuffer failed \n");
112 return STATUS_UNSUCCESSFUL;
113 }
114
115 m_BufferSize = BufferSize;
116 m_AllocatedBufferSize = BufferSize;
117 DPRINT("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 DPRINT("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 DPRINT("BufferSize %u\n", m_BufferSize);
226 PC_ASSERT(m_BufferSize);
227 return m_BufferSize;
228 }
229
230
231 PVOID
232 NTAPI
233 CDmaChannelInit::SystemAddress()
234 {
235 DPRINT("CDmaChannelInit_SystemAddress: this %p\n", this);
236 return m_Buffer;
237 }
238
239 ULONG
240 NTAPI
241 CDmaChannelInit::TransferCount()
242 {
243 DPRINT("CDmaChannelInit_TransferCount: this %p\n", this);
244 return m_LastTransferCount;
245 }
246
247 ULONG
248 NTAPI
249 CDmaChannelInit::ReadCounter()
250 {
251 ULONG Counter;
252
253 PC_ASSERT_IRQL(DISPATCH_LEVEL);
254
255 Counter = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter);
256
257 if (!m_DmaStarted || Counter >= m_LastTransferCount)
258 Counter = 0;
259
260 DPRINT("ReadCounter %u\n", Counter);
261
262 return Counter;
263 }
264
265 IO_ALLOCATION_ACTION
266 NTAPI
267 AdapterControl(
268 IN PDEVICE_OBJECT DeviceObject,
269 IN PIRP Irp,
270 IN PVOID MapRegisterBase,
271 IN PVOID Context)
272 {
273 ULONG Length;
274 CDmaChannelInit * This = (CDmaChannelInit*)Context;
275
276 Length = This->m_MapSize;
277 This->m_MapRegisterBase = MapRegisterBase;
278
279 This->m_pAdapter->DmaOperations->MapTransfer(This->m_pAdapter,
280 This->m_Mdl,
281 MapRegisterBase,
282 (PVOID)((ULONG_PTR)This->m_Mdl->StartVa + This->m_Mdl->ByteOffset),
283 &Length,
284 This->m_WriteToDevice);
285
286 if (Length == This->m_BufferSize)
287 {
288 This->m_DmaStarted = TRUE;
289 }
290
291 return KeepObject;
292 }
293
294 NTSTATUS
295 NTAPI
296 CDmaChannelInit::Start(
297 ULONG MapSize,
298 BOOLEAN WriteToDevice)
299 {
300 NTSTATUS Status;
301 ULONG MapRegisters;
302 KIRQL OldIrql;
303
304 DPRINT("CDmaChannelInit_Start: this %p\n", this);
305
306 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
307
308 if (m_DmaStarted)
309 return STATUS_UNSUCCESSFUL;
310
311 if (!m_Mdl)
312 {
313 m_Mdl = IoAllocateMdl(m_Buffer, m_MaximumBufferSize, FALSE, FALSE, NULL);
314 if (!m_Mdl)
315 {
316 return STATUS_INSUFFICIENT_RESOURCES;
317 }
318 MmBuildMdlForNonPagedPool(m_Mdl);
319 }
320
321 m_MapSize = MapSize;
322 m_WriteToDevice = WriteToDevice;
323 m_LastTransferCount = MapSize;
324
325 //FIXME
326 // synchronize access
327 //
328 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
329
330 MapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(m_Buffer, MapSize);
331 Status = m_pAdapter->DmaOperations->AllocateAdapterChannel(m_pAdapter, m_pDeviceObject, MapRegisters, AdapterControl, (PVOID)this);
332 KeLowerIrql(OldIrql);
333
334 if(!NT_SUCCESS(Status))
335 m_LastTransferCount = 0;
336
337 return Status;
338 }
339
340 NTSTATUS
341 NTAPI
342 CDmaChannelInit::Stop()
343 {
344 KIRQL OldIrql;
345
346 DPRINT("CDmaChannelInit::Stop: this %p\n", this);
347 PC_ASSERT_IRQL(DISPATCH_LEVEL);
348
349 if (!m_DmaStarted)
350 return STATUS_SUCCESS;
351
352 m_pAdapter->DmaOperations->FlushAdapterBuffers(m_pAdapter,
353 m_Mdl,
354 m_MapRegisterBase,
355 (PVOID)((ULONG_PTR)m_Mdl->StartVa + m_Mdl->ByteOffset),
356 m_MapSize,
357 m_WriteToDevice);
358
359 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
360
361 m_pAdapter->DmaOperations->FreeAdapterChannel(m_pAdapter);
362
363 KeLowerIrql(OldIrql);
364
365 m_DmaStarted = FALSE;
366
367 IoFreeMdl(m_Mdl);
368 m_Mdl = NULL;
369
370 return STATUS_SUCCESS;
371 }
372
373 NTSTATUS
374 NTAPI
375 CDmaChannelInit::WaitForTC(
376 ULONG Timeout)
377 {
378 ULONG RetryCount;
379 ULONG BytesRemaining;
380 ULONG PrevBytesRemaining;
381
382 PC_ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);
383
384 BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter);
385 if (!BytesRemaining)
386 {
387 return STATUS_SUCCESS;
388 }
389
390 RetryCount = Timeout / 10;
391 PrevBytesRemaining = 0xFFFFFFFF;
392 do
393 {
394 BytesRemaining = m_pAdapter->DmaOperations->ReadDmaCounter(m_pAdapter);
395
396 if (!BytesRemaining)
397 break;
398
399 if (PrevBytesRemaining == BytesRemaining)
400 break;
401
402 KeStallExecutionProcessor(10);
403 PrevBytesRemaining = BytesRemaining;
404
405 }while(RetryCount-- >= 1);
406
407 if (BytesRemaining)
408 {
409 return STATUS_UNSUCCESSFUL;
410 }
411
412 return STATUS_SUCCESS;
413
414 }
415
416 NTSTATUS
417 NTAPI
418 CDmaChannelInit::Init(
419 IN PDEVICE_DESCRIPTION DeviceDescription,
420 IN PDEVICE_OBJECT DeviceObject)
421 {
422 INTERFACE_TYPE BusType;
423 NTSTATUS Status;
424 PDMA_ADAPTER Adapter;
425 PPCLASS_DEVICE_EXTENSION DeviceExt;
426 ULONG MapRegisters;
427 ULONG ResultLength;
428
429 // Get bus type
430 Status = IoGetDeviceProperty(DeviceObject, DevicePropertyLegacyBusType, sizeof(BusType), (PVOID)&BusType, &ResultLength);
431 if (NT_SUCCESS(Status))
432 {
433 DeviceDescription->InterfaceType = BusType;
434 }
435 // Fetch device extension
436 DeviceExt = (PPCLASS_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
437 // Acquire dma adapter
438 Adapter = IoGetDmaAdapter(DeviceExt->PhysicalDeviceObject, DeviceDescription, &MapRegisters);
439 if (!Adapter)
440 {
441 FreeItem(this, TAG_PORTCLASS);
442 return STATUS_DEVICE_CONFIGURATION_ERROR;
443 }
444
445 // initialize object
446 m_pAdapter = Adapter;
447 m_pDeviceObject = DeviceObject;
448 m_MaximumBufferSize = DeviceDescription->MaximumLength;
449 m_MaxMapRegisters = MapRegisters;
450
451 return STATUS_SUCCESS;
452 }
453
454 NTSTATUS
455 NTAPI
456 PcNewDmaChannel(
457 OUT PDMACHANNEL* OutDmaChannel,
458 IN PUNKNOWN OuterUnknown OPTIONAL,
459 IN POOL_TYPE PoolType,
460 IN PDEVICE_DESCRIPTION DeviceDescription,
461 IN PDEVICE_OBJECT DeviceObject)
462 {
463 NTSTATUS Status;
464 CDmaChannelInit * This;
465
466 DPRINT("OutDmaChannel %p OuterUnknown %p PoolType %p DeviceDescription %p DeviceObject %p\n",
467 OutDmaChannel, OuterUnknown, PoolType, DeviceDescription, DeviceObject);
468
469 This = new(PoolType, TAG_PORTCLASS)CDmaChannelInit(OuterUnknown);
470 if (!This)
471 return STATUS_INSUFFICIENT_RESOURCES;
472
473 Status = This->QueryInterface(IID_IDmaChannel, (PVOID*)OutDmaChannel);
474
475 if (!NT_SUCCESS(Status))
476 {
477 delete This;
478 return Status;
479 }
480
481 Status = This->Init(DeviceDescription, DeviceObject);
482
483 if (!NT_SUCCESS(Status))
484 {
485 delete This;
486 return Status;
487 }
488
489 return Status;
490 }