ace2baad8b
arch/i386/conf/GENERIC, arch/i386/conf/files.i386 and dev/pci/files.pci which can simply be uncommented to compile NDIS into the kernel. I'll write some documentation on this soon. Note that NDIS is still somewhat experimental. It is currently tested and functions relatively well on on two cards: 1. Dell (Broadcom) TrueMobile 1400 Dual Band WLAN Mini-PCI 2. Intel EtherExpress Pro/100
461 lines
12 KiB
C
461 lines
12 KiB
C
/*-
|
|
* Copyright (c) 2003
|
|
* Bill Paul <wpaul@windriver.com>. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Bill Paul.
|
|
* 4. Neither the name of the author nor the names of any co-contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifdef __FreeBSD__
|
|
__FBSDID("$FreeBSD: src/sys/compat/ndis/subr_hal.c,v 1.13.2.3 2005/03/31 04:24:35 wpaul Exp $");
|
|
#endif
|
|
#ifdef __NetBSD__
|
|
__KERNEL_RCSID(0, "$NetBSD: subr_hal.c,v 1.3 2006/03/31 00:03:57 rittera Exp $");
|
|
#endif
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/callout.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/lock.h>
|
|
#ifdef __FreeBSD__
|
|
#include <sys/mutex.h>
|
|
#endif
|
|
#include <sys/proc.h>
|
|
#include <sys/sched.h>
|
|
#ifdef __FreeBSD__
|
|
#include <sys/module.h>
|
|
#endif
|
|
|
|
#include <sys/systm.h>
|
|
#ifdef __NetBSD__
|
|
#include <sys/lkm.h>
|
|
#endif
|
|
#ifdef __FreeBSD__
|
|
#include <machine/clock.h>
|
|
#include <machine/bus_memio.h>
|
|
#include <machine/bus_pio.h>
|
|
#endif
|
|
#include <machine/bus.h>
|
|
|
|
#ifdef __FreeBSD__
|
|
#include <sys/bus.h>
|
|
#include <sys/rman.h>
|
|
#endif
|
|
|
|
#include <compat/ndis/pe_var.h>
|
|
#include <compat/ndis/ntoskrnl_var.h>
|
|
#include <compat/ndis/hal_var.h>
|
|
|
|
__stdcall static void KeStallExecutionProcessor(uint32_t);
|
|
__stdcall static void WRITE_PORT_BUFFER_ULONG(uint32_t *,
|
|
uint32_t *, uint32_t);
|
|
__stdcall static void WRITE_PORT_BUFFER_USHORT(uint16_t *,
|
|
uint16_t *, uint32_t);
|
|
__stdcall static void WRITE_PORT_BUFFER_UCHAR(uint8_t *,
|
|
uint8_t *, uint32_t);
|
|
__stdcall static void WRITE_PORT_ULONG(uint32_t *, uint32_t);
|
|
__stdcall static void WRITE_PORT_USHORT(uint16_t *, uint16_t);
|
|
__stdcall static void WRITE_PORT_UCHAR(uint8_t *, uint8_t);
|
|
__stdcall static uint32_t READ_PORT_ULONG(uint32_t *);
|
|
__stdcall static uint16_t READ_PORT_USHORT(uint16_t *);
|
|
__stdcall static uint8_t READ_PORT_UCHAR(uint8_t *);
|
|
__stdcall static void READ_PORT_BUFFER_ULONG(uint32_t *,
|
|
uint32_t *, uint32_t);
|
|
__stdcall static void READ_PORT_BUFFER_USHORT(uint16_t *,
|
|
uint16_t *, uint32_t);
|
|
__stdcall static void READ_PORT_BUFFER_UCHAR(uint8_t *,
|
|
uint8_t *, uint32_t);
|
|
__stdcall static uint64_t KeQueryPerformanceCounter(uint64_t *);
|
|
__stdcall static void dummy (void);
|
|
|
|
extern struct mtx_pool *ndis_mtxpool;
|
|
|
|
int
|
|
hal_libinit()
|
|
{
|
|
image_patch_table *patch;
|
|
|
|
patch = hal_functbl;
|
|
while (patch->ipt_func != NULL) {
|
|
windrv_wrap((funcptr)patch->ipt_func,
|
|
(funcptr *)&patch->ipt_wrap);
|
|
patch++;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
hal_libfini()
|
|
{
|
|
image_patch_table *patch;
|
|
|
|
patch = hal_functbl;
|
|
while (patch->ipt_func != NULL) {
|
|
windrv_unwrap(patch->ipt_wrap);
|
|
patch++;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
__stdcall static void
|
|
KeStallExecutionProcessor(usecs)
|
|
uint32_t usecs;
|
|
{
|
|
DELAY(usecs);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
WRITE_PORT_ULONG(port, val)
|
|
uint32_t *port;
|
|
uint32_t val;
|
|
{
|
|
bus_space_write_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
WRITE_PORT_USHORT(port, val)
|
|
uint16_t *port;
|
|
uint16_t val;
|
|
{
|
|
bus_space_write_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
WRITE_PORT_UCHAR(port, val)
|
|
uint8_t *port;
|
|
uint8_t val;
|
|
{
|
|
bus_space_write_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
WRITE_PORT_BUFFER_ULONG(port, val, cnt)
|
|
uint32_t *port;
|
|
uint32_t *val;
|
|
uint32_t cnt;
|
|
{
|
|
bus_space_write_multi_4(NDIS_BUS_SPACE_IO, 0x0,
|
|
(bus_size_t)port, val, cnt);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
WRITE_PORT_BUFFER_USHORT(port, val, cnt)
|
|
uint16_t *port;
|
|
uint16_t *val;
|
|
uint32_t cnt;
|
|
{
|
|
bus_space_write_multi_2(NDIS_BUS_SPACE_IO, 0x0,
|
|
(bus_size_t)port, val, cnt);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
WRITE_PORT_BUFFER_UCHAR(port, val, cnt)
|
|
uint8_t *port;
|
|
uint8_t *val;
|
|
uint32_t cnt;
|
|
{
|
|
bus_space_write_multi_1(NDIS_BUS_SPACE_IO, 0x0,
|
|
(bus_size_t)port, val, cnt);
|
|
return;
|
|
}
|
|
|
|
__stdcall static uint16_t
|
|
READ_PORT_USHORT(port)
|
|
uint16_t *port;
|
|
{
|
|
return(bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
|
|
}
|
|
|
|
__stdcall static uint32_t
|
|
READ_PORT_ULONG(port)
|
|
uint32_t *port;
|
|
{
|
|
return(bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
|
|
}
|
|
|
|
__stdcall static uint8_t
|
|
READ_PORT_UCHAR(port)
|
|
uint8_t *port;
|
|
{
|
|
return(bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
|
|
}
|
|
|
|
__stdcall static void
|
|
READ_PORT_BUFFER_ULONG(port, val, cnt)
|
|
uint32_t *port;
|
|
uint32_t *val;
|
|
uint32_t cnt;
|
|
{
|
|
bus_space_read_multi_4(NDIS_BUS_SPACE_IO, 0x0,
|
|
(bus_size_t)port, val, cnt);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
READ_PORT_BUFFER_USHORT(port, val, cnt)
|
|
uint16_t *port;
|
|
uint16_t *val;
|
|
uint32_t cnt;
|
|
{
|
|
bus_space_read_multi_2(NDIS_BUS_SPACE_IO, 0x0,
|
|
(bus_size_t)port, val, cnt);
|
|
return;
|
|
}
|
|
|
|
__stdcall static void
|
|
READ_PORT_BUFFER_UCHAR(port, val, cnt)
|
|
uint8_t *port;
|
|
uint8_t *val;
|
|
uint32_t cnt;
|
|
{
|
|
bus_space_read_multi_1(NDIS_BUS_SPACE_IO, 0x0,
|
|
(bus_size_t)port, val, cnt);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The spinlock implementation in Windows differs from that of FreeBSD.
|
|
* The basic operation of spinlocks involves two steps: 1) spin in a
|
|
* tight loop while trying to acquire a lock, 2) after obtaining the
|
|
* lock, disable preemption. (Note that on uniprocessor systems, you're
|
|
* allowed to skip the first step and just lock out pre-emption, since
|
|
* it's not possible for you to be in contention with another running
|
|
* thread.) Later, you release the lock then re-enable preemption.
|
|
* The difference between Windows and FreeBSD lies in how preemption
|
|
* is disabled. In FreeBSD, it's done using critical_enter(), which on
|
|
* the x86 arch translates to a cli instruction. This masks off all
|
|
* interrupts, and effectively stops the scheduler from ever running
|
|
* so _nothing_ can execute except the current thread. In Windows,
|
|
* preemption is disabled by raising the processor IRQL to DISPATCH_LEVEL.
|
|
* This stops other threads from running, but does _not_ block device
|
|
* interrupts. This means ISRs can still run, and they can make other
|
|
* threads runable, but those other threads won't be able to execute
|
|
* until the current thread lowers the IRQL to something less than
|
|
* DISPATCH_LEVEL.
|
|
*
|
|
* There's another commonly used IRQL in Windows, which is APC_LEVEL.
|
|
* An APC is an Asynchronous Procedure Call, which differs from a DPC
|
|
* (Defered Procedure Call) in that a DPC is queued up to run in
|
|
* another thread, while an APC runs in the thread that scheduled
|
|
* it (similar to a signal handler in a UNIX process). We don't
|
|
* actually support the notion of APCs in FreeBSD, so for now, the
|
|
* only IRQLs we're interested in are DISPATCH_LEVEL and PASSIVE_LEVEL.
|
|
*
|
|
* To simulate DISPATCH_LEVEL, we raise the current thread's priority
|
|
* to PI_REALTIME, which is the highest we can give it. This should,
|
|
* if I understand things correctly, prevent anything except for an
|
|
* interrupt thread from preempting us. PASSIVE_LEVEL is basically
|
|
* everything else.
|
|
*
|
|
* Be aware that, at least on the x86 arch, the Windows spinlock
|
|
* functions are divided up in peculiar ways. The actual spinlock
|
|
* functions are KfAcquireSpinLock() and KfReleaseSpinLock(), and
|
|
* they live in HAL.dll. Meanwhile, KeInitializeSpinLock(),
|
|
* KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
|
|
* live in ntoskrnl.exe. Most Windows source code will call
|
|
* KeAcquireSpinLock() and KeReleaseSpinLock(), but these are just
|
|
* macros that call KfAcquireSpinLock() and KfReleaseSpinLock().
|
|
* KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
|
|
* perform the lock aquisition/release functions without doing the
|
|
* IRQL manipulation, and are used when one is already running at
|
|
* DISPATCH_LEVEL. Make sense? Good.
|
|
*
|
|
* According to the Microsoft documentation, any thread that calls
|
|
* KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If
|
|
* we detect someone trying to acquire a spinlock from DEVICE_LEVEL
|
|
* or HIGH_LEVEL, we panic.
|
|
*/
|
|
|
|
__fastcall uint8_t
|
|
KfAcquireSpinLock(REGARGS1(kspin_lock *lock))
|
|
{
|
|
uint8_t oldirql;
|
|
|
|
/* I am so going to hell for this. */
|
|
if (KeGetCurrentIrql() > DISPATCH_LEVEL)
|
|
panic("IRQL_NOT_LESS_THAN_OR_EQUAL");
|
|
|
|
oldirql = KeRaiseIrql(DISPATCH_LEVEL);
|
|
KeAcquireSpinLockAtDpcLevel(lock);
|
|
|
|
return(oldirql);
|
|
}
|
|
|
|
__fastcall void
|
|
KfReleaseSpinLock(REGARGS2(kspin_lock *lock, uint8_t newirql))
|
|
{
|
|
KeReleaseSpinLockFromDpcLevel(lock);
|
|
KeLowerIrql(newirql);
|
|
|
|
return;
|
|
}
|
|
|
|
__stdcall uint8_t
|
|
KeGetCurrentIrql(void)
|
|
{
|
|
if (AT_DISPATCH_LEVEL(curthread))
|
|
return(DISPATCH_LEVEL);
|
|
return(PASSIVE_LEVEL);
|
|
}
|
|
|
|
__stdcall static uint64_t
|
|
KeQueryPerformanceCounter(freq)
|
|
uint64_t *freq;
|
|
{
|
|
if (freq != NULL)
|
|
*freq = hz;
|
|
|
|
return((uint64_t)ticks);
|
|
}
|
|
|
|
|
|
static int old_ipl;
|
|
static int ipl_raised = FALSE;
|
|
|
|
__fastcall uint8_t
|
|
KfRaiseIrql(REGARGS1(uint8_t irql))
|
|
{
|
|
uint8_t oldirql = 0;
|
|
//#ifdef __NetBSD__
|
|
// uint8_t s;
|
|
//#endif
|
|
|
|
if (irql < KeGetCurrentIrql())
|
|
panic("IRQL_NOT_LESS_THAN");
|
|
|
|
if (KeGetCurrentIrql() == DISPATCH_LEVEL)
|
|
return(DISPATCH_LEVEL);
|
|
#ifdef __NetBSD__
|
|
if(irql >= DISPATCH_LEVEL && !ipl_raised) {
|
|
old_ipl = splsoftclock();
|
|
ipl_raised = TRUE;
|
|
oldirql = win_irql;
|
|
win_irql = irql;
|
|
}
|
|
#else /* __FreeBSD__ */
|
|
mtx_lock_spin(&sched_lock);
|
|
oldirql = curthread->td_base_pri;
|
|
sched_prio(curthread, PI_REALTIME);
|
|
#if __FreeBSD_version < 600000
|
|
curthread->td_base_pri = PI_REALTIME;
|
|
#endif
|
|
mtx_unlock_spin(&sched_lock);
|
|
#endif /* __FreeBSD__ */
|
|
|
|
return(oldirql);
|
|
}
|
|
|
|
__fastcall void
|
|
KfLowerIrql(REGARGS1(uint8_t oldirql))
|
|
{
|
|
//#ifdef __NetBSD__
|
|
// uint8_t s;
|
|
//#endif
|
|
|
|
if (oldirql == DISPATCH_LEVEL)
|
|
return;
|
|
|
|
#ifdef __FreeBSD__
|
|
if (KeGetCurrentIrql() != DISPATCH_LEVEL)
|
|
panic("IRQL_NOT_GREATER_THAN");
|
|
#else /* __NetBSD__ */
|
|
if (KeGetCurrentIrql() < oldirql)
|
|
panic("IRQL_NOT_GREATER_THAN");
|
|
#endif
|
|
|
|
#ifdef __NetBSD__
|
|
if(oldirql < DISPATCH_LEVEL && ipl_raised) {
|
|
splx(old_ipl);
|
|
ipl_raised = FALSE;
|
|
win_irql = oldirql;
|
|
}
|
|
#else
|
|
mtx_lock_spin(&sched_lock);
|
|
#if __FreeBSD_version < 600000
|
|
curthread->td_base_pri = oldirql;
|
|
#endif
|
|
sched_prio(curthread, oldirql);
|
|
mtx_unlock_spin(&sched_lock);
|
|
#endif /* __NetBSD__ */
|
|
|
|
return;
|
|
}
|
|
|
|
__stdcall
|
|
static void dummy()
|
|
{
|
|
printf ("hal dummy called...\n");
|
|
return;
|
|
}
|
|
|
|
image_patch_table hal_functbl[] = {
|
|
IMPORT_FUNC(KeStallExecutionProcessor),
|
|
IMPORT_FUNC(WRITE_PORT_ULONG),
|
|
IMPORT_FUNC(WRITE_PORT_USHORT),
|
|
IMPORT_FUNC(WRITE_PORT_UCHAR),
|
|
IMPORT_FUNC(WRITE_PORT_BUFFER_ULONG),
|
|
IMPORT_FUNC(WRITE_PORT_BUFFER_USHORT),
|
|
IMPORT_FUNC(WRITE_PORT_BUFFER_UCHAR),
|
|
IMPORT_FUNC(READ_PORT_ULONG),
|
|
IMPORT_FUNC(READ_PORT_USHORT),
|
|
IMPORT_FUNC(READ_PORT_UCHAR),
|
|
IMPORT_FUNC(READ_PORT_BUFFER_ULONG),
|
|
IMPORT_FUNC(READ_PORT_BUFFER_USHORT),
|
|
IMPORT_FUNC(READ_PORT_BUFFER_UCHAR),
|
|
IMPORT_FUNC(KfAcquireSpinLock),
|
|
IMPORT_FUNC(KfReleaseSpinLock),
|
|
IMPORT_FUNC(KeGetCurrentIrql),
|
|
IMPORT_FUNC(KeQueryPerformanceCounter),
|
|
IMPORT_FUNC(KfLowerIrql),
|
|
IMPORT_FUNC(KfRaiseIrql),
|
|
|
|
/*
|
|
* This last entry is a catch-all for any function we haven't
|
|
* implemented yet. The PE import list patching routine will
|
|
* use it for any function that doesn't have an explicit match
|
|
* in this table.
|
|
*/
|
|
|
|
{ NULL, (FUNC)dummy, NULL },
|
|
|
|
/* End of list. */
|
|
|
|
{ NULL, NULL, NULL }
|
|
};
|