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