1 /* $Id: adapter.c,v 1.6 2003/10/20 06:03:28 vizzini 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 */
26 HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject
,
27 PWAIT_CONTEXT_BLOCK WaitContextBlock
,
28 ULONG NumberOfMapRegisters
,
29 PDRIVER_CONTROL ExecutionRoutine
)
31 * FUNCTION: Sets up an ADAPTER_OBJECT with map registers
33 * - AdapterObject: pointer to an ADAPTER_OBJECT to set up
34 * - WaitContextBlock: Context block to be used with ExecutionRoutine
35 * - NumberOfMapRegisters: number of map registers requested
36 * - ExecutionRoutine: callback to call when map registers are allocated
38 * STATUS_INSUFFICIENT_RESOURCES if map registers cannot be allocated
39 * STATUS_SUCCESS in all other cases, including if the callbacak had
40 * to be queued for later delivery
42 * - Map registers don't exist on X86 so we can just call the callback
43 * with a map register base of 0
44 * - the ADAPTER_OBJECT struct is undocumented; please make copious
45 * notes here if anything is changed or improved since there is
46 * no other documentation for this routine or its data structures
47 * - The original implementation of this function allocated a contiguous
48 * physical buffer the size of NumberOfMapRegisters * PAGE_SIZE, which
49 * is unnecessary and very expensive (contiguous memory is rare). It
50 * also leaked in some circumstances (drivers allocate and sometimes
51 * don't free map registers)
53 * - This routine should check whether or not map registers are needed
54 * (rather than assuming they're not) and allocate them on platforms
62 LARGE_INTEGER MaxAddress
;
64 MaxAddress
.QuadPart
= 0x1000000;
66 /* why 64K alignment? */
67 Buffer
= MmAllocateContiguousAlignedMemory( NumberOfMapRegisters
* PAGE_SIZE
,
71 return STATUS_INSUFFICIENT_RESOURCES
;
72 KeAcquireSpinLock( &AdapterObject
->SpinLock
, &OldIrql
);
73 if( AdapterObject
->Inuse
)
75 // someone is already using it, we need to wait
76 // create a wait block, and add it to the chain
80 AdapterObject
->Inuse
= TRUE
;
81 KeReleaseSpinLock( &AdapterObject
->SpinLock
, OldIrql
);
82 ret
= ExecutionRoutine( DeviceObject
,
85 WaitContextBlock
->DriverContext
);
86 KeAcquireSpinLock( &AdapterObject
->SpinLock
, &OldIrql
);
87 if( ret
== DeallocateObject
)
89 MmFreeContiguousMemory( Buffer
);
90 AdapterObject
->Inuse
= FALSE
;
92 else AdapterObject
->Buffer
= Buffer
;
94 KeReleaseSpinLock( &AdapterObject
->SpinLock
, OldIrql
);
97 AdapterObject
->MapRegisterBase
= 0;
98 AdapterObject
->AllocatedMapRegisters
= 0;
100 IO_ALLOCATION_ACTION Retval
= ExecutionRoutine(WaitContextBlock
->DeviceObject
,
101 WaitContextBlock
->CurrentIrp
, 0, WaitContextBlock
->DeviceContext
);
103 if(Retval
== DeallocateObject
)
104 IoFreeAdapterChannel(AdapterObject
);
105 else if(Retval
== DeallocateObjectKeepRegisters
)
106 AdapterObject
->AllocatedMapRegisters
= 0;
108 return STATUS_SUCCESS
;
113 IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject
,
115 PVOID MapRegisterBase
,
118 BOOLEAN WriteToDevice
)
120 // if this was a read from device, copy data back to caller buffer, otherwise, do nothing
122 memcpy( (PVOID
)((DWORD
)MmGetSystemAddressForMdl( Mdl
) + (DWORD
)CurrentVa
- (DWORD
)MmGetMdlVirtualAddress( Mdl
)), MapRegisterBase
, Length
);
128 IoFreeAdapterChannel (PADAPTER_OBJECT AdapterObject
)
132 KeAcquireSpinLock( &AdapterObject
->SpinLock
, &OldIrql
);
133 if( AdapterObject
->Inuse
== FALSE
)
135 DbgPrint( "Attempting to IoFreeAdapterChannel on a channel not in use\n" );
138 AdapterObject
->Inuse
= FALSE
;
139 if( AdapterObject
->Buffer
)
141 MmFreeContiguousMemory( AdapterObject
->Buffer
);
142 AdapterObject
->Buffer
= 0;
144 KeReleaseSpinLock( &AdapterObject
->SpinLock
, OldIrql
);
149 IoFreeMapRegisters (PADAPTER_OBJECT AdapterObject
,
150 PVOID MapRegisterBase
,
151 ULONG NumberOfMapRegisters
)
157 PHYSICAL_ADDRESS STDCALL
158 IoMapTransfer (PADAPTER_OBJECT AdapterObject
,
160 PVOID MapRegisterBase
,
163 BOOLEAN WriteToDevice
)
165 PHYSICAL_ADDRESS Address
;
166 // program up the dma controller, and return
167 // if it is a write to the device, copy the caller buffer to the low buffer
169 memcpy( MapRegisterBase
,
170 MmGetSystemAddressForMdl( Mdl
) + ( (DWORD
)CurrentVa
- (DWORD
)MmGetMdlVirtualAddress( Mdl
) ),
172 Address
= MmGetPhysicalAddress( MapRegisterBase
);
173 // port 0xA is the dma mask register, or a 0x10 on to the channel number to mask it
174 WRITE_PORT_UCHAR( (PVOID
)0x0A, AdapterObject
->Channel
| 0x10 );
175 // write zero to the reset register
176 WRITE_PORT_UCHAR( (PVOID
)0x0C, 0 );
177 // mode register, or channel with 0x4 for write memory, 0x8 for read memory, 0x10 for non auto initialize
178 WRITE_PORT_UCHAR( (PVOID
)0x0B, AdapterObject
->Channel
| ( WriteToDevice
? 0x8 : 0x4 ) );
179 // set the 64k page register for the channel
180 WRITE_PORT_UCHAR( AdapterObject
->PagePort
, (UCHAR
)(((ULONG
)Address
.QuadPart
)>>16) );
181 // low, then high address byte, which is always 0 for us, because we have a 64k alligned address
182 WRITE_PORT_UCHAR( AdapterObject
->OffsetPort
, 0 );
183 WRITE_PORT_UCHAR( AdapterObject
->OffsetPort
, 0 );
184 // count is 1 less than length, low then high
185 WRITE_PORT_UCHAR( AdapterObject
->CountPort
, (UCHAR
)(*Length
- 1) );
186 WRITE_PORT_UCHAR( AdapterObject
->CountPort
, (UCHAR
)((*Length
- 1)>>8) );
187 // unmask the channel to let it rip
188 WRITE_PORT_UCHAR( (PVOID
)0x0A, AdapterObject
->Channel
);
189 Address
.QuadPart
= (DWORD
)MapRegisterBase
;