Changes to support the 3Com 3c90x ndis5 driver and other bugfixes:
[reactos.git] / reactos / hal / halx86 / adapter.c
1 /* $Id: adapter.c,v 1.6 2003/10/20 06:03:28 vizzini 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 NTSTATUS STDCALL
26 HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject,
27 PWAIT_CONTEXT_BLOCK WaitContextBlock,
28 ULONG NumberOfMapRegisters,
29 PDRIVER_CONTROL ExecutionRoutine)
30 /*
31 * FUNCTION: Sets up an ADAPTER_OBJECT with map registers
32 * ARGUMENTS:
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
37 * RETURNS:
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
41 * NOTES:
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)
52 * BUGS:
53 * - This routine should check whether or not map registers are needed
54 * (rather than assuming they're not) and allocate them on platforms
55 * that support them.
56 */
57 {
58 #if 0
59 KIRQL OldIrql;
60 PVOID Buffer;
61 int ret;
62 LARGE_INTEGER MaxAddress;
63
64 MaxAddress.QuadPart = 0x1000000;
65
66 /* why 64K alignment? */
67 Buffer = MmAllocateContiguousAlignedMemory( NumberOfMapRegisters * PAGE_SIZE,
68 MaxAddress,
69 0x10000 );
70 if( !Buffer )
71 return STATUS_INSUFFICIENT_RESOURCES;
72 KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql );
73 if( AdapterObject->Inuse )
74 {
75 // someone is already using it, we need to wait
76 // create a wait block, and add it to the chain
77 UNIMPLEMENTED;
78 }
79 else {
80 AdapterObject->Inuse = TRUE;
81 KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql );
82 ret = ExecutionRoutine( DeviceObject,
83 NULL,
84 Buffer,
85 WaitContextBlock->DriverContext );
86 KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql );
87 if( ret == DeallocateObject )
88 {
89 MmFreeContiguousMemory( Buffer );
90 AdapterObject->Inuse = FALSE;
91 }
92 else AdapterObject->Buffer = Buffer;
93 }
94 KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql );
95 #endif
96
97 AdapterObject->MapRegisterBase = 0;
98 AdapterObject->AllocatedMapRegisters = 0;
99
100 IO_ALLOCATION_ACTION Retval = ExecutionRoutine(WaitContextBlock->DeviceObject,
101 WaitContextBlock->CurrentIrp, 0, WaitContextBlock->DeviceContext);
102
103 if(Retval == DeallocateObject)
104 IoFreeAdapterChannel(AdapterObject);
105 else if(Retval == DeallocateObjectKeepRegisters)
106 AdapterObject->AllocatedMapRegisters = 0;
107
108 return STATUS_SUCCESS;
109 }
110
111
112 BOOLEAN STDCALL
113 IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject,
114 PMDL Mdl,
115 PVOID MapRegisterBase,
116 PVOID CurrentVa,
117 ULONG Length,
118 BOOLEAN WriteToDevice)
119 {
120 // if this was a read from device, copy data back to caller buffer, otherwise, do nothing
121 if( !WriteToDevice )
122 memcpy( (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )), MapRegisterBase, Length );
123 return TRUE;
124 }
125
126
127 VOID STDCALL
128 IoFreeAdapterChannel (PADAPTER_OBJECT AdapterObject)
129 {
130 KIRQL OldIrql;
131
132 KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql );
133 if( AdapterObject->Inuse == FALSE )
134 {
135 DbgPrint( "Attempting to IoFreeAdapterChannel on a channel not in use\n" );
136 KEBUGCHECK(0);
137 }
138 AdapterObject->Inuse = FALSE;
139 if( AdapterObject->Buffer )
140 {
141 MmFreeContiguousMemory( AdapterObject->Buffer );
142 AdapterObject->Buffer = 0;
143 }
144 KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql );
145 }
146
147
148 VOID STDCALL
149 IoFreeMapRegisters (PADAPTER_OBJECT AdapterObject,
150 PVOID MapRegisterBase,
151 ULONG NumberOfMapRegisters)
152 {
153 UNIMPLEMENTED;
154 }
155
156
157 PHYSICAL_ADDRESS STDCALL
158 IoMapTransfer (PADAPTER_OBJECT AdapterObject,
159 PMDL Mdl,
160 PVOID MapRegisterBase,
161 PVOID CurrentVa,
162 PULONG Length,
163 BOOLEAN WriteToDevice)
164 {
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
168 if( WriteToDevice )
169 memcpy( MapRegisterBase,
170 MmGetSystemAddressForMdl( Mdl ) + ( (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl ) ),
171 *Length );
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;
190 return Address;
191 }
192
193
194 /* EOF */
195
196
197
198