1 /* $Id: adapter.c,v 1.11 2004/07/22 18:49:18 navaraf Exp $
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)
11 * 18-Oct-2003 Vizzini DMA support modifications
14 /* INCLUDES *****************************************************************/
16 #include <ddk/ntddk.h>
17 #include <ddk/iotypes.h>
18 #include <internal/debug.h>
21 /* FUNCTIONS *****************************************************************/
23 /* NOTE: IoAllocateAdapterChannel in NTOSKRNL.EXE */
27 HalAllocateAdapterChannel(
28 PADAPTER_OBJECT AdapterObject
,
29 PWAIT_CONTEXT_BLOCK WaitContextBlock
,
30 ULONG NumberOfMapRegisters
,
31 PDRIVER_CONTROL ExecutionRoutine
)
33 * FUNCTION: Sets up an ADAPTER_OBJECT with map registers
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
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
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
48 * - it's possible that some of this code is in the wrong place
49 * - there are many unhandled cases
52 LARGE_INTEGER MinAddress
;
53 LARGE_INTEGER MaxAddress
;
54 LARGE_INTEGER BoundryAddressMultiple
;
55 IO_ALLOCATION_ACTION Retval
;
57 assert(KeGetCurrentIrql() == DISPATCH_LEVEL
);
60 FIXME: return STATUS_INSUFFICIENT_RESOURCES if the NumberOfMapRegisters
61 requested is larger than the value returned by IoGetDmaAdapter.
64 /* set up the wait context block in case we can't run right away */
65 WaitContextBlock
->DeviceRoutine
= ExecutionRoutine
;
66 WaitContextBlock
->NumberOfMapRegisters
= NumberOfMapRegisters
;
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
;
72 /* 24-bit max address due to 16-bit dma controllers */
73 MinAddress
.QuadPart
= 0x0000000;
74 MaxAddress
.QuadPart
= 0x1000000;
75 BoundryAddressMultiple
.QuadPart
= 0;
77 /* why 64K alignment? */
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.
83 * FIXME: We propably shouldn't allocate the memory here for common
84 * buffer transfers. See a comment in IoMapTransfer about common buffer
87 AdapterObject
->MapRegisterBase
= MmAllocateContiguousAlignedMemory(
88 NumberOfMapRegisters
* PAGE_SIZE
,
91 BoundryAddressMultiple
,
95 if(!AdapterObject
->MapRegisterBase
)
96 return STATUS_INSUFFICIENT_RESOURCES
;
98 AdapterObject
->AllocatedMapRegisters
= NumberOfMapRegisters
;
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
);
105 * KeepObject: don't free any resources; the ADAPTER_OBJECT is still in use
106 * and the caller will call IoFreeAdapterChannel later
108 * DeallocateObject: Deallocate the map registers and release the ADAPTER_OBJECT
109 * so someone else can use it
111 * DeallocateObjectKeepRegisters: release the ADAPTER_OBJECT but hang on to
112 * the map registers. The client will later call IoFreeMapRegisters.
114 * NOTE - IoFreeAdapterChannel runs the queue, so it must be called
115 * unless the adapter object is not to be freed.
117 if( Retval
== DeallocateObject
)
118 IoFreeAdapterChannel(AdapterObject
);
119 else if(Retval
== DeallocateObjectKeepRegisters
)
121 /* don't free the allocated map registers - this is what IoFreeAdapterChannel checks */
122 AdapterObject
->AllocatedMapRegisters
= 0;
123 IoFreeAdapterChannel(AdapterObject
);
127 * if we don't call IoFreeAdapterChannel, the next device won't get de-queued,
128 * which is what we want.
131 return STATUS_SUCCESS
;
136 IoFlushAdapterBuffers (
137 PADAPTER_OBJECT AdapterObject
,
139 PVOID MapRegisterBase
,
142 BOOLEAN WriteToDevice
)
144 * FUNCTION: flush any data remaining in the dma controller's memory into the host memory
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
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.
160 assert(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
162 /* this can happen if the card supports scatter/gather */
166 /* mask out (disable) the dma channel */
167 if (AdapterObject
->Channel
< 4)
168 WRITE_PORT_UCHAR( (PVOID
)0x0A, (UCHAR
)(AdapterObject
->Channel
| 0x4) );
170 WRITE_PORT_UCHAR( (PVOID
)0xD4, (UCHAR
)((AdapterObject
->Channel
- 4) | 0x4) );
176 (PVOID
)((DWORD
)MmGetSystemAddressForMdl( Mdl
) + (DWORD
)CurrentVa
- (DWORD
)MmGetMdlVirtualAddress( Mdl
)),
177 MapRegisterBase
, Length
);
184 IoFreeAdapterChannel (PADAPTER_OBJECT AdapterObject
)
186 * FUNCTION: frees DMA resources allocated by IoAllocateAdapterChannel
188 * AdapterObject: Adapter object with resources to free
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.
196 LARGE_INTEGER MaxAddress
;
197 LARGE_INTEGER MinAddress
;
198 LARGE_INTEGER BoundryAddressMultiple
;
199 PWAIT_CONTEXT_BLOCK WaitContextBlock
;
200 IO_ALLOCATION_ACTION Retval
;
204 /* To keep map registers, call here with the following set to 0 */
205 if(AdapterObject
->AllocatedMapRegisters
)
206 IoFreeMapRegisters(AdapterObject
, AdapterObject
->MapRegisterBase
, AdapterObject
->AllocatedMapRegisters
);
208 if(!(WaitContextBlock
= (PWAIT_CONTEXT_BLOCK
)KeRemoveDeviceQueue(&AdapterObject
->DeviceQueue
)))
212 * the following should really be done elsewhere since this
213 * function really can't return an error code. FIXME.
216 /* 24-bit max address due to 16-bit dma controllers */
217 MinAddress
.QuadPart
= 0x0000000;
218 MaxAddress
.QuadPart
= 0x1000000;
219 BoundryAddressMultiple
.QuadPart
= 0;
221 AdapterObject
->MapRegisterBase
= MmAllocateContiguousAlignedMemory(
222 WaitContextBlock
->NumberOfMapRegisters
* PAGE_SIZE
,
225 BoundryAddressMultiple
,
229 if(!AdapterObject
->MapRegisterBase
)
232 /* call the adapter control routine */
233 Retval
= ((PDRIVER_CONTROL
)WaitContextBlock
->DeviceRoutine
)(WaitContextBlock
->DeviceObject
, WaitContextBlock
->CurrentIrp
,
234 AdapterObject
->MapRegisterBase
, WaitContextBlock
->DeviceContext
);
236 if(Retval
== KeepObject
)
238 /* we're done until the caller manually calls IoFreeAdapterChannel */
241 else if(Retval
== DeallocateObjectKeepRegisters
)
243 /* hide the map registers so they aren't deallocated next time around */
244 AdapterObject
->AllocatedMapRegisters
= 0;
252 IN PADAPTER_OBJECT AdapterObject
,
253 IN PVOID MapRegisterBase
,
254 IN ULONG NumberOfMapRegisters
)
256 * FUNCTION: free map registers reserved by the system for a DMA
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
262 * - XXX real windows has a funky interdependence between IoFreeMapRegisters
263 * and IoFreeAdapterChannel
265 * - needs to be improved to use a real map register implementation
268 if( AdapterObject
->AllocatedMapRegisters
)
270 MmFreeContiguousMemory(AdapterObject
->MapRegisterBase
);
271 AdapterObject
->MapRegisterBase
= 0;
276 PHYSICAL_ADDRESS STDCALL
278 IN PADAPTER_OBJECT AdapterObject
,
280 IN PVOID MapRegisterBase
,
282 IN OUT PULONG Length
,
283 IN BOOLEAN WriteToDevice
)
285 * FUNCTION: map a dma for transfer and do the dma if it's a slave
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
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
295 * If a busmaster: A logical address that can be used to program a dma controller
296 * Otherwise: nothing meaningful
298 * - This function does a copyover to contiguous memory <16MB
299 * - If it's a slave transfer, this function actually performs it.
301 * - If the controller supports scatter/gather, the copyover should not happen
304 PHYSICAL_ADDRESS Address
;
305 PVOID MaskReg
, ClearReg
, ModeReg
;
306 UCHAR ModeMask
, LengthShift
;
309 #if defined(__GNUC__)
310 Address
.QuadPart
= 0ULL;
312 Address
.QuadPart
= 0;
315 /* Isa System (slave) DMA? */
316 if (AdapterObject
&& AdapterObject
->InterfaceType
== Isa
&& !AdapterObject
->Master
)
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);
325 KeAcquireSpinLock(&AdapterObject
->SpinLock
, &OldIrql
);
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.
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
341 * -- Filip Navara, 19/07/2004
344 /* if it is a write to the device, copy the caller buffer to the low buffer */
345 if( WriteToDevice
&& !AdapterObject
->AutoInitialize
)
347 memcpy(MapRegisterBase
,
348 (char*)MmGetSystemAddressForMdl(Mdl
) + ((ULONG
)CurrentVa
- (ULONG
)MmGetMdlVirtualAddress(Mdl
)),
353 if( AdapterObject
->Channel
>= 4 )
355 MaskReg
= (PVOID
)0xD4; ClearReg
= (PVOID
)0xD8; ModeReg
= (PVOID
)0xD6;
360 MaskReg
= (PVOID
)0x0A; ClearReg
= (PVOID
)0x0C; ModeReg
= (PVOID
)0x0B;
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
)
369 if (AdapterObject
->AutoInitialize
)
372 // program up the dma controller, and return
373 if (!AdapterObject
->AutoInitialize
)
374 Address
= MmGetPhysicalAddress( MapRegisterBase
);
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 );
394 KeReleaseSpinLock(&AdapterObject
->SpinLock
, OldIrql
);
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?
401 Address
.QuadPart
= (ULONG
)MapRegisterBase
;
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.
411 if ((!AdapterObject
&& !MapRegisterBase
) ||
412 (AdapterObject
&& AdapterObject
->Master
&& AdapterObject
->ScatterGather
))
415 Just return the passed VA's corresponding phys. address.
416 Update length to the number of phys. contiguous bytes found.
420 ULONG MdlPageIndex
, PhysContiguousLen
;
423 MdlPages
= (PULONG
)(Mdl
+ 1);
425 /* Get VA's corresponding mdl phys. page index */
426 MdlPageIndex
= ((ULONG
)CurrentVa
- (ULONG
)Mdl
->StartVa
) / PAGE_SIZE
;
428 /* Get phys. page containing the VA */
429 PhysAddress
= MdlPages
[MdlPageIndex
];
431 PhysContiguousLen
= PAGE_SIZE
- BYTE_OFFSET(CurrentVa
);
433 /* VA to map may span several contiguous phys. pages (unlikely) */
434 while (PhysContiguousLen
< *Length
&&
435 MdlPages
[MdlPageIndex
++] + PAGE_SIZE
== MdlPages
[MdlPageIndex
])
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.
442 PhysContiguousLen
+= PAGE_SIZE
;
445 if (PhysContiguousLen
< *Length
)
447 *Length
= PhysContiguousLen
;
450 //add offset to phys. page address
451 Address
.QuadPart
= PhysAddress
+ BYTE_OFFSET(CurrentVa
);
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.
461 if ((!AdapterObject
&& MapRegisterBase
) ||
462 (AdapterObject
&& AdapterObject
->Master
&& !AdapterObject
->ScatterGather
))
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...
471 memcpy(MapRegisterBase
,
472 (char*)MmGetSystemAddressForMdl(Mdl
) + ((ULONG
)CurrentVa
- (ULONG
)MmGetMdlVirtualAddress(Mdl
)),
476 return MmGetPhysicalAddress(MapRegisterBase
);
479 DPRINT1("IoMapTransfer: Unsupported operation\n");