- Implementation of HalReadDmaCounter.
[reactos.git] / reactos / hal / halx86 / adapter.c
1 /* $Id: adapter.c,v 1.11 2004/07/22 18:49:18 navaraf Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: hal/x86/adapter.c (from ntoskrnl/io/adapter.c)
6 * PURPOSE: DMA handling
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
8 * Vizzini (vizzini@plasmic.com)
9 * UPDATE HISTORY:
10 * Created 22/05/98
11 * 18-Oct-2003 Vizzini DMA support modifications
12 */
13
14 /* INCLUDES *****************************************************************/
15
16 #include <ddk/ntddk.h>
17 #include <ddk/iotypes.h>
18 #include <internal/debug.h>
19 #include <hal.h>
20
21 /* FUNCTIONS *****************************************************************/
22
23 /* NOTE: IoAllocateAdapterChannel in NTOSKRNL.EXE */
24
25 \f
26 NTSTATUS STDCALL
27 HalAllocateAdapterChannel(
28 PADAPTER_OBJECT AdapterObject,
29 PWAIT_CONTEXT_BLOCK WaitContextBlock,
30 ULONG NumberOfMapRegisters,
31 PDRIVER_CONTROL ExecutionRoutine)
32 /*
33 * FUNCTION: Sets up an ADAPTER_OBJECT with map registers
34 * ARGUMENTS:
35 * - AdapterObject: pointer to an ADAPTER_OBJECT to set up
36 * - WaitContextBlock: Context block to be used with ExecutionRoutine
37 * - NumberOfMapRegisters: number of map registers requested
38 * - ExecutionRoutine: callback to call when map registers are allocated
39 * RETURNS:
40 * STATUS_INSUFFICIENT_RESOURCES if map registers cannot be allocated
41 * STATUS_SUCCESS in all other cases, including if the callbacak had
42 * to be queued for later delivery
43 * NOTES:
44 * - the ADAPTER_OBJECT struct is undocumented; please make copious
45 * notes in hal.h if anything is changed or improved since there is
46 * no other documentation for this data structure
47 * BUGS:
48 * - it's possible that some of this code is in the wrong place
49 * - there are many unhandled cases
50 */
51 {
52 LARGE_INTEGER MinAddress;
53 LARGE_INTEGER MaxAddress;
54 LARGE_INTEGER BoundryAddressMultiple;
55 IO_ALLOCATION_ACTION Retval;
56
57 assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
58
59 /*
60 FIXME: return STATUS_INSUFFICIENT_RESOURCES if the NumberOfMapRegisters
61 requested is larger than the value returned by IoGetDmaAdapter.
62 */
63
64 /* set up the wait context block in case we can't run right away */
65 WaitContextBlock->DeviceRoutine = ExecutionRoutine;
66 WaitContextBlock->NumberOfMapRegisters = NumberOfMapRegisters;
67
68 /* returns true if queued, else returns false and sets the queue to busy */
69 if(KeInsertDeviceQueue(&AdapterObject->DeviceQueue, &WaitContextBlock->WaitQueueEntry))
70 return STATUS_SUCCESS;
71
72 /* 24-bit max address due to 16-bit dma controllers */
73 MinAddress.QuadPart = 0x0000000;
74 MaxAddress.QuadPart = 0x1000000;
75 BoundryAddressMultiple.QuadPart = 0;
76
77 /* why 64K alignment? */
78 /*
79 * X86 lacks map registers, so for now, we allocate a contiguous
80 * block of physical memory <16MB and copy all DMA buffers into
81 * that. This can be optimized.
82 *
83 * FIXME: We propably shouldn't allocate the memory here for common
84 * buffer transfers. See a comment in IoMapTransfer about common buffer
85 * support.
86 */
87 AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory(
88 NumberOfMapRegisters * PAGE_SIZE,
89 MinAddress,
90 MaxAddress,
91 BoundryAddressMultiple,
92 MmCached,
93 0x10000 );
94
95 if(!AdapterObject->MapRegisterBase)
96 return STATUS_INSUFFICIENT_RESOURCES;
97
98 AdapterObject->AllocatedMapRegisters = NumberOfMapRegisters;
99
100 /* call the client's AdapterControl callback with its map registers and context */
101 Retval = ExecutionRoutine(WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
102 AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
103
104 /*
105 * KeepObject: don't free any resources; the ADAPTER_OBJECT is still in use
106 * and the caller will call IoFreeAdapterChannel later
107 *
108 * DeallocateObject: Deallocate the map registers and release the ADAPTER_OBJECT
109 * so someone else can use it
110 *
111 * DeallocateObjectKeepRegisters: release the ADAPTER_OBJECT but hang on to
112 * the map registers. The client will later call IoFreeMapRegisters.
113 *
114 * NOTE - IoFreeAdapterChannel runs the queue, so it must be called
115 * unless the adapter object is not to be freed.
116 */
117 if( Retval == DeallocateObject )
118 IoFreeAdapterChannel(AdapterObject);
119 else if(Retval == DeallocateObjectKeepRegisters)
120 {
121 /* don't free the allocated map registers - this is what IoFreeAdapterChannel checks */
122 AdapterObject->AllocatedMapRegisters = 0;
123 IoFreeAdapterChannel(AdapterObject);
124 }
125
126 /*
127 * if we don't call IoFreeAdapterChannel, the next device won't get de-queued,
128 * which is what we want.
129 */
130
131 return STATUS_SUCCESS;
132 }
133
134 \f
135 BOOLEAN STDCALL
136 IoFlushAdapterBuffers (
137 PADAPTER_OBJECT AdapterObject,
138 PMDL Mdl,
139 PVOID MapRegisterBase,
140 PVOID CurrentVa,
141 ULONG Length,
142 BOOLEAN WriteToDevice)
143 /*
144 * FUNCTION: flush any data remaining in the dma controller's memory into the host memory
145 * ARGUMENTS:
146 * AdapterObject: the adapter object to flush
147 * Mdl: original MDL to flush data into
148 * MapRegisterBase: map register base that was just used by IoMapTransfer, etc
149 * CurrentVa: offset into Mdl to be flushed into, same as was passed to IoMapTransfer
150 * Length: length of the buffer to be flushed into
151 * WriteToDevice: True if it's a write, False if it's a read
152 * RETURNS:
153 * TRUE in all cases
154 * NOTES:
155 * - This copies data from the map register-backed buffer to the user's target buffer.
156 * Data is not in the user buffer until this is called.
157 * - This is only meaningful on a read operation. Return immediately for a write.
158 */
159 {
160 assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
161
162 /* this can happen if the card supports scatter/gather */
163 if(!MapRegisterBase)
164 return TRUE;
165
166 /* mask out (disable) the dma channel */
167 if (AdapterObject->Channel < 4)
168 WRITE_PORT_UCHAR( (PVOID)0x0A, (UCHAR)(AdapterObject->Channel | 0x4) );
169 else
170 WRITE_PORT_UCHAR( (PVOID)0xD4, (UCHAR)((AdapterObject->Channel - 4) | 0x4) );
171
172 if(WriteToDevice)
173 return TRUE;
174
175 memcpy(
176 (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )),
177 MapRegisterBase, Length );
178
179 return TRUE;
180 }
181
182 \f
183 VOID STDCALL
184 IoFreeAdapterChannel (PADAPTER_OBJECT AdapterObject)
185 /*
186 * FUNCTION: frees DMA resources allocated by IoAllocateAdapterChannel
187 * ARGUMENTS:
188 * AdapterObject: Adapter object with resources to free
189 * NOTES:
190 * - This function releases the DMA adapter and optionally the map registers
191 * - After releasing the adapter, it checks the adapter's queue and runs
192 * each queued device object in series until the queue is empty
193 * - This is the only way the device queue is emptied.
194 */
195 {
196 LARGE_INTEGER MaxAddress;
197 LARGE_INTEGER MinAddress;
198 LARGE_INTEGER BoundryAddressMultiple;
199 PWAIT_CONTEXT_BLOCK WaitContextBlock;
200 IO_ALLOCATION_ACTION Retval;
201
202 while(1)
203 {
204 /* To keep map registers, call here with the following set to 0 */
205 if(AdapterObject->AllocatedMapRegisters)
206 IoFreeMapRegisters(AdapterObject, AdapterObject->MapRegisterBase, AdapterObject->AllocatedMapRegisters);
207
208 if(!(WaitContextBlock = (PWAIT_CONTEXT_BLOCK)KeRemoveDeviceQueue(&AdapterObject->DeviceQueue)))
209 break;
210
211 /*
212 * the following should really be done elsewhere since this
213 * function really can't return an error code. FIXME.
214 */
215
216 /* 24-bit max address due to 16-bit dma controllers */
217 MinAddress.QuadPart = 0x0000000;
218 MaxAddress.QuadPart = 0x1000000;
219 BoundryAddressMultiple.QuadPart = 0;
220
221 AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory(
222 WaitContextBlock->NumberOfMapRegisters * PAGE_SIZE,
223 MinAddress,
224 MaxAddress,
225 BoundryAddressMultiple,
226 MmCached,
227 0x10000 );
228
229 if(!AdapterObject->MapRegisterBase)
230 return;
231
232 /* call the adapter control routine */
233 Retval = ((PDRIVER_CONTROL)WaitContextBlock->DeviceRoutine)(WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
234 AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
235
236 if(Retval == KeepObject)
237 {
238 /* we're done until the caller manually calls IoFreeAdapterChannel */
239 break;
240 }
241 else if(Retval == DeallocateObjectKeepRegisters)
242 {
243 /* hide the map registers so they aren't deallocated next time around */
244 AdapterObject->AllocatedMapRegisters = 0;
245 }
246 }
247 }
248
249 \f
250 VOID STDCALL
251 IoFreeMapRegisters (
252 IN PADAPTER_OBJECT AdapterObject,
253 IN PVOID MapRegisterBase,
254 IN ULONG NumberOfMapRegisters)
255 /*
256 * FUNCTION: free map registers reserved by the system for a DMA
257 * ARGUMENTS:
258 * AdapterObject: dma adapter to free map registers on
259 * MapRegisterBase: hadle to map registers to free
260 * NumberOfRegisters: number of map registers to be freed
261 * NOTES:
262 * - XXX real windows has a funky interdependence between IoFreeMapRegisters
263 * and IoFreeAdapterChannel
264 * BUGS:
265 * - needs to be improved to use a real map register implementation
266 */
267 {
268 if( AdapterObject->AllocatedMapRegisters )
269 {
270 MmFreeContiguousMemory(AdapterObject->MapRegisterBase);
271 AdapterObject->MapRegisterBase = 0;
272 }
273 }
274
275 \f
276 PHYSICAL_ADDRESS STDCALL
277 IoMapTransfer (
278 IN PADAPTER_OBJECT AdapterObject,
279 IN PMDL Mdl,
280 IN PVOID MapRegisterBase,
281 IN PVOID CurrentVa,
282 IN OUT PULONG Length,
283 IN BOOLEAN WriteToDevice)
284 /*
285 * FUNCTION: map a dma for transfer and do the dma if it's a slave
286 * ARGUMENTS:
287 * AdapterObject: adapter object to do the dma on. busmaster may pass NULL.
288 * Mdl: locked-down user buffer to DMA in to or out of
289 * MapRegisterBase: handle to map registers to use for this dma. allways NULL
290 * when doing s/g.
291 * CurrentVa: index into Mdl to transfer into/out of
292 * Length: length of transfer in/out. Only modified on out when doing s/g.
293 * WriteToDevice: TRUE if it's an output dma, FALSE otherwise
294 * RETURNS:
295 * If a busmaster: A logical address that can be used to program a dma controller
296 * Otherwise: nothing meaningful
297 * NOTES:
298 * - This function does a copyover to contiguous memory <16MB
299 * - If it's a slave transfer, this function actually performs it.
300 * BUGS:
301 * - If the controller supports scatter/gather, the copyover should not happen
302 */
303 {
304 PHYSICAL_ADDRESS Address;
305 PVOID MaskReg, ClearReg, ModeReg;
306 UCHAR ModeMask, LengthShift;
307 KIRQL OldIrql;
308
309 #if defined(__GNUC__)
310 Address.QuadPart = 0ULL;
311 #else
312 Address.QuadPart = 0;
313 #endif
314
315 /* Isa System (slave) DMA? */
316 if (AdapterObject && AdapterObject->InterfaceType == Isa && !AdapterObject->Master)
317 {
318 #if 0
319 /* channel 0 is reserved for DRAM refresh */
320 assert(AdapterObject->Channel != 0);
321 /* channel 4 is reserved for cascade */
322 assert(AdapterObject->Channel != 4);
323 #endif
324
325 KeAcquireSpinLock(&AdapterObject->SpinLock, &OldIrql);
326
327 /*
328 * FIXME: Handle case when doing common-buffer System DMA. In this case,
329 * the buffer described by MDL is already phys. contiguous and below
330 * 16 mega. Driver makes a one-shot call to IoMapTransfer during init.
331 * to program controller with the common-buffer.
332 *
333 * UPDATE: Common buffer support is in place, but it's not done in a
334 * clean way. We use the buffer passed by the MDL in case that the
335 * adapter object is marked as auto initialize. I'm not sure if this
336 * is correct and if not, how to do it properly. Note that it's also
337 * possible to allocate the common buffer with different adapter object
338 * and IoMapTransfer must still work in this case. Eventually this should
339 * be cleaned up somehow or at least this comment modified to reflect
340 * the reality.
341 * -- Filip Navara, 19/07/2004
342 */
343
344 /* if it is a write to the device, copy the caller buffer to the low buffer */
345 if( WriteToDevice && !AdapterObject->AutoInitialize )
346 {
347 memcpy(MapRegisterBase,
348 (char*)MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
349 *Length );
350 }
351
352 // 16-bit DMA
353 if( AdapterObject->Channel >= 4 )
354 {
355 MaskReg = (PVOID)0xD4; ClearReg = (PVOID)0xD8; ModeReg = (PVOID)0xD6;
356 LengthShift = 1;
357 }
358 else
359 {
360 MaskReg = (PVOID)0x0A; ClearReg = (PVOID)0x0C; ModeReg = (PVOID)0x0B;
361 LengthShift = 0;
362 }
363
364 // calculate the mask we will later set to the mode register
365 ModeMask = (AdapterObject->Channel & 3) | ( WriteToDevice ? 0x8 : 0x4 );
366 // FIXME: if not demand mode, which mode to use? 0x40 for single mode
367 if (!AdapterObject->DemandMode)
368 ModeMask |= 0x40;
369 if (AdapterObject->AutoInitialize)
370 ModeMask |= 0x10;
371
372 // program up the dma controller, and return
373 if (!AdapterObject->AutoInitialize)
374 Address = MmGetPhysicalAddress( MapRegisterBase );
375 else
376 Address = MmGetPhysicalAddress( CurrentVa );
377 // disable and select the channel number
378 WRITE_PORT_UCHAR( MaskReg, (UCHAR)((AdapterObject->Channel & 3) | 0x4) );
379 // write zero to the reset register
380 WRITE_PORT_UCHAR( ClearReg, 0 );
381 // mode register, or channel with 0x4 for write memory, 0x8 for read memory, 0x10 for auto initialize
382 WRITE_PORT_UCHAR( ModeReg, ModeMask);
383 // set the 64k page register for the channel
384 WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(Address.u.LowPart >> 16) );
385 // low, then high address byte, which is always 0 for us, because we have a 64k alligned address
386 WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 );
387 WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 );
388 // count is 1 less than length, low then high
389 WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)((*Length >> LengthShift) - 1) );
390 WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)(((*Length >> LengthShift) - 1)>>8) );
391 // unmask the channel to let it rip
392 WRITE_PORT_UCHAR( MaskReg, AdapterObject->Channel & 3 );
393
394 KeReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
395
396 /*
397 NOTE: Return value should be ignored when doing System DMA.
398 Maybe return some more obvious invalid address here (thou returning
399 MapRegisterBase is also wrong;-)to catch invalid use?
400 */
401 Address.QuadPart = (ULONG)MapRegisterBase;
402 return Address;
403 }
404
405
406 /*
407 Busmaster with s/g support?
408 NOTE: old docs allowed busmasters to pass a NULL Adapter. In this case, MapRegisterBase
409 being NULL is used to detect a s/g busmaster.
410 */
411 if ((!AdapterObject && !MapRegisterBase) ||
412 (AdapterObject && AdapterObject->Master && AdapterObject->ScatterGather))
413 {
414 /*
415 Just return the passed VA's corresponding phys. address.
416 Update length to the number of phys. contiguous bytes found.
417 */
418
419 PULONG MdlPages;
420 ULONG MdlPageIndex, PhysContiguousLen;
421 ULONG PhysAddress;
422
423 MdlPages = (PULONG)(Mdl + 1);
424
425 /* Get VA's corresponding mdl phys. page index */
426 MdlPageIndex = ((ULONG)CurrentVa - (ULONG)Mdl->StartVa) / PAGE_SIZE;
427
428 /* Get phys. page containing the VA */
429 PhysAddress = MdlPages[MdlPageIndex];
430
431 PhysContiguousLen = PAGE_SIZE - BYTE_OFFSET(CurrentVa);
432
433 /* VA to map may span several contiguous phys. pages (unlikely) */
434 while (PhysContiguousLen < *Length &&
435 MdlPages[MdlPageIndex++] + PAGE_SIZE == MdlPages[MdlPageIndex])
436 {
437 /*
438 Note that allways adding PAGE_SIZE may make PhysContiguousLen greater
439 than Length if buffer doesn't end on page boundary. Take this
440 into consideration below.
441 */
442 PhysContiguousLen += PAGE_SIZE;
443 }
444
445 if (PhysContiguousLen < *Length)
446 {
447 *Length = PhysContiguousLen;
448 }
449
450 //add offset to phys. page address
451 Address.QuadPart = PhysAddress + BYTE_OFFSET(CurrentVa);
452 return Address;
453 }
454
455
456 /*
457 Busmaster without s/g support?
458 NOTE: old docs allowed busmasters to pass a NULL Adapter. In this case, MapRegisterBase
459 not being NULL is used to detect a non s/g busmaster.
460 */
461 if ((!AdapterObject && MapRegisterBase) ||
462 (AdapterObject && AdapterObject->Master && !AdapterObject->ScatterGather))
463 {
464 /*
465 NOTE: Busmasters doing common-buffer DMA shouldn't call IoMapTransfer, but I don't
466 know if it's illegal... Maybe figure out what to do in this case...
467 */
468
469 if( WriteToDevice )
470 {
471 memcpy(MapRegisterBase,
472 (char*)MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
473 *Length );
474 }
475
476 return MmGetPhysicalAddress(MapRegisterBase);
477 }
478
479 DPRINT1("IoMapTransfer: Unsupported operation\n");
480 KEBUGCHECK(0);
481 return Address;
482 }
483
484
485 /* EOF */
486
487
488
489