These normally only manifest if the ndis compat module is statically
compiled into a kernel image by way of 'options NDISAPI'.
Submitted by: Dmitri Nikulin
Approved by: wpaul
PR: kern/71449
MFC after: 1 week
This was tested with a Netgear WG311v2 802.11b/g PCI card. Things
that were fixed:
- This chip has two memory mapped regions, one at PCIR_BAR(0) and the
other at PCIR_BAR(1). This is a little different from the other
chips I've seen with two PCI shared memory regions, since they tend
to have the second BAR ad PCIR_BAR(2). if_ndis_pci.c tests explicitly
for PCIR_BAR(2). This has been changed to simply fill in ndis_res_mem
first and ndis_res_altmem second, if a second shared memory range
exists. Given that NDIS drivers seem to scan for BARs in ascending
order, I think this should be ok.
- Fixed the code that tries to process firmware images that have been
loaded as .ko files. To save a step, I was setting up the address
mapping in ndis_open_file(), but ndis_map_file() flags pre-existing
mappings as an error (to avoid duplicate mappings). Changed this so
that the mapping is now donw in ndis_map_file() as expected.
- Made the typedef for 'driver_entry' explicitly include __stdcall
to silence gcc warning in ndis_load_driver().
NOTE: the Texas Instruments ACX111 driver needs firmware. With my
card, there were 3 .bin files shipped with the driver. You must
either put these files in /compat/ndis or convert them with
ndiscvt -f and kldload them so the driver can use them. Without
the firmware image, the NIC won't work.
- In ntoskrnl_var.h, I had defined compat macros for
ntoskrnl_acquire_spinlock() and ntoskrnl_release_spinlock() but
never used them. This is fortunate since they were stale. Fix them
to work properly. (In Windows/x86 KeAcquireSpinLock() is a macro that
calls KefAcquireSpinLock(), which lives in HAL.dll. To imitate this,
ntoskrnl_acquire_spinlock() is just a macro that calls hal_lock(),
which lives in subr_hal.o.)
- Add macros for ntoskrnl_raise_irql() and ntoskrnl_lower_irql() that
call hal_raise_irql() and hal_lower_irql().
- Use these macros in kern_ndis.c, subr_ndis.c and subr_ntoskrnl.c.
- Along the way, I realised subr_ndis.c:ndis_lock() was not calling
hal_lock() correctly (it was using the FASTCALL2() wrapper when
in reality this routine is FASTCALL1()). Using the
ntoskrnl_acquire_spinlock() fixes this. Not sure if this actually
caused any bugs since hal_lock() would have just ignored what
was in %edx, but it was still bogus.
This hides many of the uses of the FASTCALLx() macros which makes the
code a little cleaner. Should not have any effect on generated object
code, other than the one fix in ndis_lock().
- Give ndiscvt(8) the ability to process a .SYS file directly into
a .o file so that we don't have to emit big messy char arrays into
the ndis_driver_data.h file. This behavior is currently optional, but
may become the default some day.
- Give ndiscvt(8) the ability to turn arbitrary files into .ko files
so that they can be pre-loaded or kldloaded. (Both this and the
previous change involve using objcopy(1)).
- Give NdisOpenFile() the ability to 'read' files out of kernel memory
that have been kldloaded or pre-loaded, and disallow the use of
the normal vn_open() file opening method during bootstrap (when no
filesystems have been mounted yet). Some people have reported that
kldloading if_ndis.ko works fine when the system is running multiuser
but causes a panic when the modile is pre-loaded by /boot/loader. This
happens with drivers that need to use NdisOpenFile() to access
external files (i.e. firmware images). NdisOpenFile() won't work
during kernel bootstrapping because no filesystems have been mounted.
To get around this, you can now do the following:
o Say you have a firmware file called firmware.img
o Do: ndiscvt -f firmware.img -- this creates firmware.img.ko
o Put the firmware.img.ko in /boot/kernel
o add firmware.img_load="YES" in /boot/loader.conf
o add if_ndis_load="YES" and ndis_load="YES" as well
Now the loader will suck the additional file into memory as a .ko. The
phony .ko has two symbols in it: filename_start and filename_end, which
are generated by objcopy(1). ndis_open_file() will traverse each module
in the module list looking for these symbols and, if it finds them, it'll
use them to generate the file mapping address and length values that
the caller of NdisOpenFile() wants.
As a bonus, this will even work if the file has been statically linked
into the kernel itself, since the "kernel" module is searched too.
(ndiscvt(8) will generate both filename.o and filename.ko for you).
- Modify the mechanism used to provide make-pretend FASTCALL support.
Rather than using inline assembly to yank the first two arguments
out of %ecx and %edx, we now use the __regparm__(3) attribute (and
the __stdcall__ attribute) and use some macro magic to re-order
the arguments and provide dummy arguments as needed so that the
arguments passed in registers end up in the right place. Change
taken from DragonflyBSD version of the NDISulator.
actually work.
Make the PCI and PCCARD attachments provide a bus_get_resource_list()
method so that resource listing for PCCARD works. PCCARD does not
have a bus_get_resource_list() method (yet), so I faked up the
resource list management in if_ndis_pccard.c, and added
bus_get_resource_list() methods to both if_ndis_pccard.c and if_ndis_pci.c.
The one in the PCI attechment just hands off to the PCI bus code.
The difference is transparent to the NDIS resource handler code.
Fixed ndis_open_file() so that opening files which live on NFS
filesystems work: pass an actual ucred structure to VOP_GETATTR()
(NFS explodes if the ucred structure is NOCRED).
Make NdisMMapIoSpace() handle mapping of PCMCIA attribute memory
resources correctly.
Turn subr_ndis.c:my_strcasecmp() into ndis_strcasecmp() and export
it so that if_ndis_pccard.c can use it, and junk the other copy
of my_strcasecmp() from if_ndis_pccard.c.
- In subr_ndis.c:ndis_allocate_sharemem(), create the busdma tags
used for shared memory allocations with a lowaddr of 0x3E7FFFFF.
This forces the buffers to be mapped to physical/bus addresses within
the first 1GB of physical memory. It seems that at least one card
(Linksys Instant Wireless PCI V2.7) depends on this behavior. I
don't know if this is a hardware restriction, or if the NDIS
driver for this card is truncating the addresses itself, but using
physical/bus addresses beyong the 1GB limit causes initialization
failures.
- Create am NDIS_INITIALIZED() macro in if_ndisvar.h and use it in
if_ndis.c to test whether the device has been initialized rather
than checking for the presence of the IFF_UP flag in if_flags.
While debugging the previous problem, I noticed that bringing
up the device would always produce failures from ndis_setmulti().
It turns out that the following steps now occur during device
initialization:
- IFF_UP flag is set in if_flags
- ifp->if_ioctl() called with SIOCSIFADDR (which we don't handle)
- ifp->if_ioctl() called with SIOCADDMULTI
- ifp->if_ioctl() called with SIOCADDMULTI (again)
- ifp->if_ioctl() called with SIOCADDMULTI (yet again)
- ifp->if_ioctl() called with SIOCSIFFLAGS
Setting the receive filter and multicast filters can only be done
when the underlying NDIS driver has been initialized, which is done
by ifp->if_init(). However, we don't call ifp->if_init() until
ifp->if_ioctl() is called with SIOCSIFFLAGS and IFF_UP has been
set. It appears that now, the network stack tries to add multicast
addresses to interface's filter before those steps occur. Normally,
ndis_setmulti() would trap this condition by checking for the IFF_UP
flag, but the network code has in fact set this flag already, so
ndis_setmulti() is fooled into thinking the interface has been
initialized when it really hasn't.
It turns out this is usually harmless because the ifp->if_init()
routine (in this case ndis_init()) will set up the multicast
filter when it initializes the hardware anyway, and the underlying
routines (ndis_get_info()/ndis_set_info()) know that the driver/NIC
haven't been initialized yet, but you end up spurious error messages
on the console all the time.
Something tells me this new behavior isn't really correct. I think
the intention was to fix it so that ifp->if_init() is only called
once when we ifconfig an interface up, but the end result seems a
little bogus: the change of the IFF_UP flag should be propagated
down to the driver before calling any other ioctl() that might actually
require the hardware to be up and running.
make the key name matching case-insensitive. There are some drivers
and .inf files that have mismatched cases, e.g. the driver will look
for "AdhocBand" whereas the .inf file specifies a registry key to be
created called "AdHocBand." The mismatch is probably a typo that went
undetected (so much for QA), but since Windows seems to be case-insensitive,
we should be too.
In if_ndis.c, initialize rates and channels correctly so that specify
frequences correctly when trying to set channels in the 5Ghz band, and
so that 802.11b rates show up for some a/b/g cards (which otherwise
appear to have no 802.11b modes).
Also, when setting OID_802_11_CONFIGURATION in ndis_80211_setstate(),
provide default values for the beacon interval, ATIM window and dwelltime.
The Atheros "Aries" driver will crash if you try to select ad-hoc mode
and leave the beacon interval set to 0: it blindly uses this value and
does a division by 0 in the interrupt handler, causing an integer
divide trap.
- Use the dh_inserted member of the dispatch header in the Windows
timer structure to indicate that the timer has been "inserted into
the timer queue" (i.e. armed via timeout()). Use this as the value
to return to the caller in KeCancelTimer(). Previously, I was using
callout_pending(), but you can't use that with timeout()/untimeout()
without creating a potential race condition.
- Make ntoskrnl_init_timer() just a wrapper around ntoskrnl_init_timer_ex()
(reduces some code duplication).
- Drop Giant when entering if_ndis.c:ndis_tick() and
subr_ntorkrnl.c:ntoskrnl_timercall(). At the moment, I'm forced to
use system callwheel via timeout()/untimeout() to handle timers rather
than the callout API (struct callout is too big to fit inside the
Windows struct KTIMER, so I'm kind of hosed). Unfortunately, all
the callouts in the callwhere are not marked as MPSAFE, so when
one of them fires, it implicitly acquires Giant before invoking the
callback routine (and releases it when it returns). I don't need to
hold Giant, but there's no way to stop the callout code from acquiring
it as long as I'm using timeout()/untimeout(), so for now we cheat
by just dropping Giant right away (and re-acquiring it right before
the routine returns so keep the callout code happy). At some point,
I will need to solve this better, but for now this should be a suitable
workaround.
(I hope.)
My original instinct to make ndis_return_packet() asynchronous was correct.
Making ndis_rxeof() submit packets to the stack asynchronously fixes
one recursive spinlock acquisition, but it's also possible for it to
happen via the ndis_txeof() path too. So:
- In if_ndis.c, revert ndis_rxeof() to its old behavior (and don't bother
putting ndis_rxeof_serial() back since we don't need it anymore).
- In kern_ndis.c, make ndis_return_packet() submit the call to the
MiniportReturnPacket() function to the "ndis swi" thread so that
it always happens in another context no matter who calls it.
ntoskrnl_unlocl_dpc().
- hal_raise_irql(), hal_lower_irql() and hal_irql() didn't work right
on SMP (priority inheritance makes things... interesting). For now,
use only two states: DISPATCH_LEVEL (PI_REALTIME) and PASSIVE_LEVEL
(everything else). Tested on a dual PIII box.
- Use ndis_thsuspend() in ndis_sleep() instead of tsleep(). (I added
ndis_thsuspend() and ndis_thresume() to replace kthread_suspend()
and kthread_resume(); the former will preserve a thread's priority
when it wakes up, the latter will not.)
- Change use of tsleep() in ndis_stop_thread() to prevent priority
change on wakeup.
attempting to duplicate Windows spinlocks. Windows spinlocks differ
from FreeBSD spinlocks in the way they block preemption. FreeBSD
spinlocks use critical_enter(), which masks off _all_ interrupts.
This prevents any other threads from being scheduled, but it also
prevents ISRs from running. In Windows, preemption is achieved by
raising the processor IRQL to DISPATCH_LEVEL, which prevents other
threads from preempting you, but does _not_ prevent device ISRs
from running. (This is essentially what Solaris calls dispatcher
locks.) The Windows spinlock itself (kspin_lock) is just an integer
value which is atomically set when you acquire the lock and atomically
cleared when you release it.
FreeBSD doesn't have IRQ levels, so we have to cheat a little by
using thread priorities: normal thread priority is PASSIVE_LEVEL,
lowest interrupt thread priority is DISPATCH_LEVEL, highest thread
priority is DEVICE_LEVEL (PI_REALTIME) and critical_enter() is
HIGH_LEVEL. In practice, only PASSIVE_LEVEL and DISPATCH_LEVEL
matter to us. The immediate benefit of all this is that I no
longer have to rely on a mutex pool.
Now, I'm sure many people will be seized by the urge to criticize
me for doing an end run around our own spinlock implementation, but
it makes more sense to do it this way. Well, it does to me anyway.
Overview of the changes:
- Properly implement hal_lock(), hal_unlock(), hal_irql(),
hal_raise_irql() and hal_lower_irql() so that they more closely
resemble their Windows counterparts. The IRQL is determined by
thread priority.
- Make ntoskrnl_lock_dpc() and ntoskrnl_unlock_dpc() do what they do
in Windows, which is to atomically set/clear the lock value. These
routines are designed to be called from DISPATCH_LEVEL, and are
actually half of the work involved in acquiring/releasing spinlocks.
- Add FASTCALL1(), FASTCALL2() and FASTCALL3() macros/wrappers
that allow us to call a _fastcall function in spite of the fact
that our version of gcc doesn't support __attribute__((__fastcall__))
yet. The macros take 1, 2 or 3 arguments, respectively. We need
to call hal_lock(), hal_unlock() etc... ourselves, but can't really
invoke the function directly. I could have just made the underlying
functions native routines and put _fastcall wrappers around them for
the benefit of Windows binaries, but that would create needless bloat.
- Remove ndis_mtxpool and all references to it. We don't need it
anymore.
- Re-implement the NdisSpinLock routines so that they use hal_lock()
and friends like they do in Windows.
- Use the new spinlock methods for handling lookaside lists and
linked list updates in place of the mutex locks that were there
before.
- Remove mutex locking from ndis_isr() and ndis_intrhand() since they're
already called with ndis_intrmtx held in if_ndis.c.
- Put ndis_destroy_lock() code under explicit #ifdef notdef/#endif.
It turns out there are some drivers which stupidly free the memory
in which their spinlocks reside before calling ndis_destroy_lock()
on them (touch-after-free bug). The ADMtek wireless driver
is guilty of this faux pas. (Why this doesn't clobber Windows I
have no idea.)
- Make NdisDprAcquireSpinLock() and NdisDprReleaseSpinLock() into
real functions instead of aliasing them to NdisAcaquireSpinLock()
and NdisReleaseSpinLock(). The Dpr routines use
KeAcquireSpinLockAtDpcLevel() level and KeReleaseSpinLockFromDpcLevel(),
which acquires the lock without twiddling the IRQL.
- In ndis_linksts_done(), do _not_ call ndis_80211_getstate(). Some
drivers may call the status/status done callbacks as the result of
setting an OID: ndis_80211_getstate() gets OIDs, which means we
might cause the driver to recursively access some of its internal
structures unexpectedly. The ndis_ticktask() routine will call
ndis_80211_getstate() for us eventually anyway.
- Fix the channel setting code a little in ndis_80211_setstate(),
and initialize the channel to IEEE80211_CHAN_ANYC. (The Microsoft
spec says you're not supposed to twiddle the channel in BSS mode;
I may need to enforce this later.) This fixes the problems I was
having with the ADMtek adm8211 driver: we were setting the channel
to a non-standard default, which would cause it to fail to associate
in BSS mode.
- Use hal_raise_irql() to raise our IRQL to DISPATCH_LEVEL when
calling certain miniport routines, per the Microsoft documentation.
I think that's everything. Hopefully, other than fixing the ADMtek
driver, there should be no apparent change in behavior.
before calling BUS_GET_RESOURCE_LIST(). Previously, the list head would
only be initialized if BUS_GET_RESOURCE_LIST() succeeded; it needs to
be initialized unconditionally so that the list cleanup code won't
trip over potential stack garbage.
case we should wait for the resetdone handler to be called before
returning.
- When providing resources via ndis_query_resources(), uses the
computed rsclen when using bcopy() to copy out the resource data
rather than the caller-supplied buffer length.
- Avoid using ndis_reset_nic() in if_ndis.c unless we really need
to reset the NIC because of a problem.
- Allow interrupts to be fielded during ndis_attach(), at least
as far as allowing ndis_isr() and ndis_intrhand() to run.
- Use ndis_80211_rates_ex when probing for supported rates. Technically,
this isn't supposed to work since, although Microsoft added the extended
rate structure with the NDIS 5.1 update, the spec still says that
the OID_802_11_SUPPORTED_RATES OID uses ndis_80211_rates. In spite of
this, it appears some drivers use it anyway.
- When adding in our guessed rates, check to see if they already exist
so that we avoid any duplicates.
- Add a printf() to ndis_open_file() that alerts the user when a
driver attempts to open a file under /compat/ndis.
With these changes, I can get the driver for the SMC 2802W 54g PCI
card to load and run. This board uses a Prism54G chip. Note that in
order for this driver to work, you must place the supplied smc2802w.arm
firmware image under /compat/ndis. (The firmware is not resident on
the device.)
Note that this should also allow the 3Com 3CRWE154G72 card to work
as well; as far as I can tell, these cards also use a Prism54G chip.
resource_var.h.
In kern_ndis.c:ndis_convert_res(), fill in the cprd_flags and
cprd_sharedisp fields as best we can.
In if_ndis.c:ndis_setmulti(), don't bother updating the multicast
filter if our multicast address list is empty.
Add some missing updates to ndis_var.h and ntoskrnl_var.h that I
forgot to check in when I added the KeDpc stuff.
objects rather than synchronization objects. When a sync object is
signaled, only the first thread waiting on it is woken up, and then
it's automatically reset to the not-signaled state. When a
notification object is signaled, all threads waiting on it will
be woken up, and it remains in the signaled state until someone
resets it manually. We want the latter behavior for NDIS events.
- In kern_ndis.c:ndis_convert_res(), we have to create a temporary
copy of the list returned by BUS_GET_RESOURCE_LIST(). When the PCI
bus code probes resources for a given device, it enters them into
a singly linked list, head first. The result is that traversing
this list gives you the resources in reverse order. This means when
we create the Windows resource list, it will be in reverse order too.
Unfortunately, this can hose drivers for devices with multiple I/O
ranges of the same type, like, say, two memory mapped I/O regions (one
for registers, one to map the NVRAM/bootrom/whatever). Some drivers
test the range size to figure out which region is which, but others
just assume that the resources will be listed in ascending order from
lowest numbered BAR to highest. Reversing the order means such drivers
will choose the wrong resource as their I/O register range.
Since we can't traverse the resource SLIST backwards, we have to
make a temporary copy of the list in the right order and then build
the Windows resource list from that. I suppose we could just fix
the PCI bus code to use a TAILQ instead, but then I'd have to track
down all the consumers of the BUS_GET_RESOURCE_LIST() and fix them
too.
which pulls a job off a thread work queue (assuming it hasn't run yet).
This is needed for KeRemoveQueueDpc().
- In subr_ntoskrnl.c, implement KeInsertQueueDpc() and KeRemoveQueueDpc(),
to go with KeInitializeDpc() to round out the API. Also change the
KeTimer implementation to use this API instead of the private
timer callout scheduler. Functionality of the timer API remains
unchanged, but we get a couple new Windows kernel API routines and
more closely imitate the way thing works in Windows. (As of yet
I haven't encountered any drivers that use KeInsertQueueDpc() or
KeRemoveQueueDpc(), but it doesn't hurt to have them.)
when it associates with a net. Because FreeBSD's kstack size is only
2 pages by default, this blows the stack and causes a double fault.
To deal with this, we now create all our kthreads with 8 stack pages.
Also, we now run all timer callouts in the ndis swi thread (since
they would otherwise run in the clock ithread, whose stack is too
small). It happens that the alloca() in this case was occuring within
the interrupt handler, which was already running in the ndis swi
thread, but I want to deal with the callouts too just to be extra
safe.
NOTE: this will only work if you update vm_machdep.c with the change
I just committed. If you don't include this fix, setting the number
of stack pages with kthread_create() has essentially no effect.
are actually layered on top of the KeTimer API in subr_ntoskrnl.c, just
as it is in Windows. This reduces code duplication and more closely
imitates the way things are done in Windows.
- Modify ndis_encode_parm() to deal with the case where we have
a registry key expressed as a hex value ("0x1") which is being
read via NdisReadConfiguration() as an int. Previously, we tried
to decode things like "0x1" with strtol() using a base of 10, which
would always yield 0. This is what was causing problems with the
Intel 2200BG Centrino 802.11g driver: the .inf file that comes
with it has a key called RadioEnable with a value of 0x1. We
incorrectly decoded this value to '0' when it was queried, hence
the driver thought we wanted the radio turned off.
- In if_ndis.c, most drivers don't accept NDIS_80211_AUTHMODE_AUTO,
but NDIS_80211_AUTHMODE_SHARED may not be right in some cases,
so for now always use NDIS_80211_AUTHMODE_OPEN.
NOTE: There is still one problem with the Intel 2200BG driver: it
happens that the kernel stack in Windows is larger than the kernel
stack in FreeBSD. The 2200BG driver sometimes eats up more than 2
pages of stack space, which can lead to a double fault panic.
For the moment, I got things to work by adding the following to
my kernel config file:
options KSTACK_PAGES=8
I'm pretty sure 8 is too big; I just picked this value out of a hat
as a test, and it happened to work, so I left it. 4 pages might be
enough. Unfortunately, I don't think you can dynamically give a
thread a larger stack, so I'm not sure how to handle this short of
putting a note in the man page about it and dealing with the flood
of mail from people who never read man pages.
snprintf() and vsnprintf() in FreeBSD kernel land).
This is needed by the Intel Centrino 2200BG driver. Unfortunately, this
driver still doesn't work right with Project Evil even with this tweak,
but I'm unable to diagnose the problem since I don't have access to a
sample card.
for Windows are deserialized miniports. Such drivers maintain their own
queues and do their own locking. This particular driver is not deserialized
though, and we need special support to handle it correctly.
Typically, in the ndis_rxeof() handler, we pass all incoming packets
directly to (*ifp->if_input)(). This in turn may cause another thread
to run and preempt us, and the packet may actually be processed and
then released before we even exit the ndis_rxeof() routine. The
problem with this is that releasing a packet calls the ndis_return_packet()
function, which hands the packet and its buffers back to the driver.
Calling ndis_return_packet() before ndis_rxeof() returns will screw
up the driver's internal queues since, not being deserialized,
it does no locking.
To avoid this problem, if we detect a serialized driver (by checking
the attribute flags passed to NdisSetAttributesEx(), we use an alternate
ndis_rxeof() handler, ndis_rxeof_serial(), which puts the call to
(*ifp->if_input)() on the NDIS SWI work queue. This guarantees the
packet won't be processed until after ndis_rxeof_serial() returns.
Note that another approach is to always copy the packet data into
another mbuf and just let the driver retain ownership of the ndis_packet
structure (ndis_return_packet() never needs to be called in this
case). I'm not sure which method is faster.
that I added recently:
- When a periodic timer fires, it's automatically re-armed. We must
make sure to re-arm the timer _before_ invoking any caller-supplied
defered procedure call: the DPC may choose to call KeCancelTimer(),
and re-arming the timer after the DPC un-does the effect of the
cancel.
- Fix similar issue with periodic timers in subr_ndis.c.
- When calling KeSetTimer() or KeSetTimerEx(), if the timer is
already pending, untimeout() it first before timeout()ing
it again.
- The old Atheros driver for the 5211 seems to use KeSetTimerEx()
incorrectly, or at the very least in a very strange way that
doesn't quite follow the Microsoft documentation. In one case,
it calls KeSetTimerEx() with a duetime of 0 and a period of 5000.
The Microsoft documentation says that negative duetime values
are relative to the current time and positive values are absolute.
But it doesn't say what's supposed to happen with positive values
that less than the current time, i.e. absolute values that are
in the past.
Lacking any further information, I have decided that timers with
positive duetimes that are in the past should fire right away (or
in our case, after only 1 tick). This also takes care of the other
strange usage in the Atheros driver, where the duetime is
specified as 500000 and the period is 50. I think someone may
have meant to use -500000 and misinterpreted the documentation.
- Also modified KeWaitForSingleObject() and KeWaitForMultipleObjects()
to make the same duetime adjustment, since they have the same rules
regarding timeout values.
- Cosmetic: change name of 'timeout' variable in KeWaitForSingleObject()
and KeWaitForMultipleObjects() to 'duetime' to avoid senseless
(though harmless) overlap with timeout() function name.
With these fixes, I can get the 5211 card to associate properly with
my adhoc net using driver AR5211.SYS version 2.4.1.6.
if_ndis.c has been split into if_ndis_pci.c and if_ndis_pccard.c.
The ndiscvt(8) utility should be able to parse device info for PCMCIA
devices now. The ndis_alloc_amem() has moved from kern_ndis.c to
if_ndis_pccard.c so that kern_ndis.c no longer depends on pccard.
NOTE: this stuff is not guaranteed to work 100% correctly yet. So
far I have been able to load/init my PCMCIA Cisco Aironet 340 card,
but it crashes in the interrupt handler. The existing support for
PCI/cardbus devices should still work as before.
along with KeInitializeTimerEx(), KeSetTimer(), KeSetTimerEx(),
KeCancelTimer(), KeReadStateTimer() and KeInitializeDpc(). I don't
know for certain that these will make the Atheros driver happy since
I don't have the card/driver combo needed to test it, but these are
fairly independent so they shouldn't break anything else.
- Debugger() is present even in kernels without options DDB, so no
conditional compilation is necessary (pointed out by bde).
- Remove the extra km_acquirecnt member that I added to struct kmutant
and embed it within an unused portion of the structure instead, so that
we don't make the structure larger than it's defined to be in Windows.
I don't know what crack I was smoking when I decided it was ok to do
this, but it's worn off now.
routines to guard against problems caused by (possibly) buggy drivers.
The RealTek 8180 wireless driver calls NdisFreeBuffer() to release
some of its buffers _after_ it's already called NdisFreeBufferPool()
to destroy the pool to which the buffers belong. In our implementation,
this error causes NdisFreeBuffer() to touch stale heap memory.
If you are running a release kernel, and hence have INVARIANTS et al
turned off, it turns out nothing happens. But if you're using a
development kernel config with INVARIANTS on, the malloc()/free()
sanity checks will scribble over the pool memory with 0xdeadc0de
once it's released so that any attempts to touch it will cause a
trap, and indeed this is what happens. It happens that I run 5.2-RELEASE
on my laptop, so when I tested the rtl8180.sys driver, it worked fine
for me, but people trying to run it with development systems checked
out or cvsupped from -current would get a page fault on driver load.
I can't find any reason why the NDISulator would cause the RealTek
driver to do the NdisFreeBufferPool() prematurely, and the same driver
obviously works with Windows -- or at least, it doesn't cause a crash:
the Microsoft documentation for NdisFreeBufferPool() says that failing
to return all buffers to the pool before calling NdisFreeBufferPool()
causes a memory leak.
I've written to my contacts at RealTek asking them to check if this
is indeed a bug in their driver. In the meantime, these new sanity checks
will catch this problem and issue a warning rather than causing a trap.
The trick is to keep a count of outstanding buffers for each buffer pool,
and if the driver tries to call NdisFreeBufferPool() while there are still
buffers outstanding, we mark the pool for deletion and then defer
destroying it until after the last buffer has been reclaimed.
is set, since some drivers with debug info can be very chatty.
Also implement DbgBreakPoint(), which is the Windows equivalent of
Debugger(). Unfortunately, this forces subr_ntoskrnl.c to include
opt_ddb.h.
- When adding new waiting threads to the waitlist for an object,
use INSERT_LIST_TAIL() instead of INSERT_LIST_HEAD() so that new
waiters go at the end of the list instead of the beginning. When we
wake up a synchronization object, only the first waiter is awakened,
and this needs to be the first thread that actually waited on the object.
- Correct missing semicolon in INSERT_LIST_TAIL() macro.
- Implement lookaside lists correctly. Note that the Am1771 driver
uses lookaside lists to manage shared memory (i.e. DMAable) buffers
by specifying its own alloc and free routines. The Microsoft documentation
says you should avoid doing this, but apparently this did not deter
the developers at AMD from doing it anyway.
With these changes (which are the result of two straight days of almost
non-stop debugging), I think I finally have the object/thread handling
semantics implemented correctly. The Am1771 driver no longer crashes
unexpectedly during association or bringing the interface up.
The Am1771 driver will sometimes do the following:
- Some thread-> NdisScheduleWorkItem(some work)
- Worker thread -> do some work, KeWaitForSingleObject(some event)
- Some other thread -> NdisScheduleWorkItem(some other work)
When the second call to NdisScheduleWorkItem() occurs, the NDIS worker
thread (in our case ndis taskqueue) is suspended in KeWaitForSingleObject()
and waiting for an event to be signaled. This is different from when
the worker thread is idle and waiting on NdisScheduleWorkItem() to
send it more jobs. However, the ndis_sched() function in kern_ndis.c
always calls kthread_resume() when queueing a new job. Normally this
would be ok, but here this causes KeWaitForSingleObject() to return
prematurely, which is not what we want.
To fix this, the NDIS threads created by kern_ndis.c maintain a state
variable to indicate whether they are running (scanning the job list
and executing jobs) or sleeping (blocked on kthread_suspend() in
ndis_runq()), and ndis_sched() will only call kthread_resume() if
the thread is in the sleeping state.
Note that we can't just check to see if the thread is on the run queue:
in both cases, the thread is sleeping, but it's sleeping for different
reasons.
This stops the Am1771 driver from emitting various "NDIS ERROR" messages
and fixes some cases where it crashes.
routines to do anything except return error if the miniport adapter context
is not set (meaning we either having init'ed the driver yet, or the
initialization failed).
Also, be sure to NULL out the adapter context along with the
miniport characteristics pointers if calling the MiniportInitialize()
method fails.
802.11b chipset work. This chip is present on the SMC2602W version 3
NIC, which is what was used for testing. This driver creates kernel
threads (12 of them!) for various purposes, and required the following
routines:
PsCreateSystemThread()
PsTerminateSystemThread()
KeInitializeEvent()
KeSetEvent()
KeResetEvent()
KeInitializeMutex()
KeReleaseMutex()
KeWaitForSingleObject()
KeWaitForMultipleObjects()
IoGetDeviceProperty()
and several more. Also, this driver abuses the fact that NDIS events
and timers are actually Windows events and timers, and uses NDIS events
with KeWaitForSingleObject(). The NDIS event routines have been rewritten
to interface with the ntoskrnl module. Many routines with incorrect
prototypes have been cleaned up.
Also, this driver puts jobs on the NDIS taskqueue (via NdisScheduleWorkItem())
which block on events, and this interferes with the operation of
NdisMAllocateSharedMemoryAsync(), which was also being put on the
NDIS taskqueue. To avoid the deadlock, NdisMAllocateSharedMemoryAsync()
is now performed in the NDIS SWI thread instead.
There's still room for some cleanups here, and I really should implement
KeInitializeTimer() and friends.
Since we have a worker thread now, we can actually do the allocation
asynchronously in that thread's context. Also, we need to return a
status value: if we're unable to queue up the async allocation, we
return NDIS_STATUS_FAILURE, otherwise we return NDIS_STATUS_PENDING
to indicate the allocation has been queued and will occur later.
This replaces the kludge where we just invoked the callback routine
right away in the current context.
that Asus provides on its CDs has both a MiniportSend() routine
and a MiniportSendPackets() function. The Microsoft NDIS docs say
that if a driver has both, only the MiniportSendPackets() routine
will be used. Although I think I implemented the support correctly,
calling the MiniportSend() routine seems to result in no packets going
out on the air, even though no error status is returned. The
MiniportSendPackets() function does work though, so at least in
this case it doesn't matter.
In if_ndis.c:ndis_getstate_80211(), if ndis_get_assoc() returns
an error, don't bother trying to obtain any other state since the
calls may fail, or worse cause the underlying driver to crash.
(The above two changes make the Asus-supplied Centrino work.)
Also, when calling the OID_802_11_CONFIGURATION OID, remember
to initialize the structure lengths correctly.
In subr_ndis.c:ndis_open_file(), set the current working directory
to rootvnode if we're in a thread that doesn't have a current
working directory set.
and NdisCancelTimer(). NdisInitializeTimer() doesn't accept an NDIS
miniport context argument, so we have to derive it from the timer
function context (which is supposed to be the adapter private context).
NdisCancelTimer is now an alias for NdisMCancelTimer().
Also add stubs for NdisMRegisterDevice() and NdisMDeregisterDevice().
These are no-ops for now, but will likely get fleshed in once I start
working on the Am1771/Am1772 wireless driver.
attached when shutting down, kill our kthreads, but don't destroy
the mutex pool and uma zone resources since the driver shutdown
routine may need them later.
problem with using taskqueue_swi is that some of the things we defer
into threads might block for up to several seconds. This is an unfriendly
thing to do to taskqueue_swi, since it is assumed the taskqueue threads
will execute fairly quickly once a task is submitted. Reorganized the
locking in if_ndis.c in the process.
Cleaned up ndis_write_cfg() and ndis_decode_parm() a little.
According to the Windows DDK header files, KSPIN_LOCK is defined like this:
typedef ULONG_PTR KSPIN_LOCK;
From basetsd.h (SDK, Feb. 2003):
typedef [public] unsigned __int3264 ULONG_PTR, *PULONG_PTR;
typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
typedef _W64 unsigned long ULONG_PTR, *PULONG_PTR;
The keyword __int3264 specifies an integral type that has the following
properties:
+ It is 32-bit on 32-bit platforms
+ It is 64-bit on 64-bit platforms
+ It is 32-bit on the wire for backward compatibility.
It gets truncated on the sending side and extended appropriately
(signed or unsigned) on the receiving side.
Thus register_t seems the proper mapping onto FreeBSD for spin locks.
the definitions for NDIS_BUS_SPACE_IO and NDIS_BUS_SPACE_MEM logically
belong in hal_var.h. At least, that's my story, and I'm sticking to it.
Also, remove definition of __stdcall from if_ndis.c now that it's pulled
in from pe_var.h.
map ranges that are smaller than what our resource manager code knows
is available, rather than requiring that they match exactly. This
fixes a problem with the Intel PRO/1000 gigE driver: it wants to map
a range of 32 I/O ports, even though some chips appear set up to
decode a range of 64. With this fix, it loads and runs correctly.
unexpected interrupts. If an interrupt is triggered and we're not
finished initializing yet, bail. If we have finished initializing,
but IFF_UP isn't set yet, drain the interrupt with ndis_intr() or
ndis_disable_intr() as appropriate, then return _without_ scheduling
ndis_intrtask().
In kern_ndis.c:ndis_load_driver() only relocate/dynalink a given driver
image once. Trying to relocate an image that's already been relocated
will trash the image. We poison a part of the image header that we
don't otherwise need with a magic value to indicate it's already been
fixed up. This fixes the case where there are multiple units of the
same kind of device.
these add support for listing BSSIDs via wicontrol -l. I added code
to call OID_802_11_BSSID_LIST_SCAN to allow scanning for any nearby
wirelsss nets.
Convert from using individual mutexes to a mutex pool, created in
subr_ndis.c. This deals with the problem of drivers creating locks
in their DriverEntry() routines which might get trashed later.
Put some messages under IFF_DEBUG.
which has two important flags in it: the 'allocated by NDIS' flag
and the 'media specific info present' flag. There are two Windows macros
for getting/setting media specific info fields within the ndis_packet
structure which can behave improperly if these flags are not initialized
correctly when a packet is allocated. It seems the correct thing
to do is always set the NDIS_PACKET_ALLOCATED_BY_NDIS flag on
all newly allocated packets.
This fixes the crashes with the Intel Centrino wireless driver.
My sample card now seems to work correctly.
Also, fix a potential LOR involving ndis_txeof() in if_ndis.c.
By default, we search for files in /compat/ndis. This can be changed with
a systcl. These routines are used by some drivers which need to download
firmware or microcode into their respective devices during initialization.
Also, remove extraneous newlines from the 'built-in' sysctl/registry
variables.
the ni_dpccountlock member is an ndis_kspin_lock, not an
ndis_spin_lock (the latter is too big).
Run if_ndis.c:ndis_tick() via taskqueue_schedule(). Also run
ndis_start() via taskqueue in certain circumstances.
Using these tweaks, I can now get the Broadcom BCM5701 NDIS
driver to load and run. Unfortunately, the version I have seems
to suffer from the same bug as the SMC 83820 driver, which is
that it creates a spinlock during its DriverEntry() routine.
I'm still debating the right way to deal with this.
held. However, if we need to translate a unicode message table message,
ndis_unicode_to_ascii() might malloc() some memory, which causes
a warning from witness. Avoid this by using some stack space to hold
the translated message. (Also bounds check to make sure we don't
overrun the stack buffer.)
in subr_ndis and subr_ntoskrnl. This is faster and avoids potential
LOR whinage from witness (an LOR couldn't happen with the old code
since the interlocked inc/dec routines could not sleep with a lock
held, but this will keep witness happy and it's more efficient
anyway. I think.)
so we increment the right thing. (All work and not enough parens
make Bill something something...) This makes the RealTek 8139C+
driver work correctly.
Also fix some mtx_lock_spin()s and mtx_unlock_spin()s that should
have been just plain mtx_lock()s and mtx_unlock()s.
In kern_ndis.c: remove duplicate code from ndis_send_packets() and
just call the senddone handler (ndis_txeof()).
flag so that it can see if the message string is unicode or not and
do the conversion itself rather than doing it in subr_pe.c. This
prevents subr_pe.c from being dependent on subr_ndis.c.
the RT_MESSAGETABLE resources that some driver binaries have.
This allows us to print error messages in ndis_syslog().
- Correct the implementation of InterlockedIncrement() and
InterlockedDecrement() -- they return uint32_t, not void.
- Correct the declarations of the 64-bit arithmetic shift
routines in subr_ntoskrnl.c (_allshr, allshl, etc...). These
do not follow the _stdcall convention: instead, they appear
to be __attribute__((regparm(3)).
- Change the implementation of KeInitializeSpinLock(). There is
no complementary KeFreeSpinLock() function, so creating a new
mutex on each call to KeInitializeSpinLock() leaks resources
when a driver is unloaded. For now, KeInitializeSpinLock()
returns a handle to the ntoskrnl interlock mutex.
- Use a driver's MiniportDisableInterrupt() and MiniportEnableInterrupt()
routines if they exist. I'm not sure if I'm doing this right
yet, but at the very least this shouldn't break any currently
working drivers, and it makes the Intel PRO/1000 driver work.
- In ndis_register_intr(), save some state that might be needed
later, and save a pointer to the driver's interrupt structure
in the ndis_miniport_block.
- Save a pointer to the driver image for use by ndis_syslog()
when it calls pe_get_message().
and MiniportHandleInterrupt() is fired off later via a task queue in
ndis_intrtask(). This more accurately follows the NDIS interrupt handling
model, where the ISR does a minimal amount of work in interrupt context
and the handler is defered and run at a lower priority.
Create a separate ndis_intrmtx mutex just for the guarding the ISR.
Modify NdisSynchronizeWithInterrupt() to aquire the ndis_intrmtx
mutex before invoking the synchronized procedure. (The purpose of
this function is to provide mutual exclusion for code that shares
variables with the ISR.)
Modify NdisMRegisterInterrupt() to save a pointer to the miniport
block in the ndis_miniport_interrupt structure so that
NdisSynchronizeWithInterrupt() can grab it later and derive
ndis_intrmtx from it.
driver was compiled with.
Remove debug printf from ndis_assicn_pcirsc(). It doesn't serve
much purpose.
Implement NdisMIndicateStatus() and NdisMIndicateStatusComplete()
as functions in subr_ndis.c. In NDIS 4.0, they were functions. In
NDIS 5.0 and later, they're just macros.
Allocate a few extra packets/buffers beyond what the driver asks
for since sometimes it seems they can lie about how many they really
need, and some extra stupid ones don't check to see if NdisAllocatePacket()
and/or NdisAllocateBuffer() actually succeed.
calling the haltfunc. If an interrupt is triggered by the init
or halt func, the IFF_UP flag must be set in order for us to be able
to service it.
In kern_ndis.c: implement a handler for NdisMSendResourcesAvailable()
(currently does nothing since we don't really need it).
In subr_ndis.c:
- Correct ndis_init_string() and ndis_unicode_to_ansi(),
which were both horribly broken.
- Implement NdisImmediateReadPciSlotInformation() and
NdisImmediateWritePciSlotInformation().
- Implement NdisBufferLength().
- Work around my first confirmed NDIS driver bug.
The SMC 9462 gigE driver (natsemi 83820-based copper)
incorrectly creates a spinlock in its DriverEntry()
routine and then destroys it in its MiniportHalt()
handler. This is wrong: spinlocks should be created
in MiniportInit(). In a Windows environment, this is
often not a problem because DriverEntry()/MiniportInit()
are called once when the system boots and MiniportHalt()
or the shutdown handler is called when the system halts.
With this stuff in place, this driver now seems to work:
ndis0: <SMC EZ Card 1000> port 0xe000-0xe0ff mem 0xda000000-0xda000fff irq 10 at device 9.0 on pci0
ndis0: assign PCI resources...
ndis_open_file("FLASH9.hex", 18446744073709551615)
ndis0: Ethernet address: 00:04:e2:0e:d3:f0
subr_ndis.c: implement NdisDprAllocatePacket() and NdisDprFreePacket()
(which are aliased to NdisAllocatePacket() and NdisFreePacket()), and
bump the value we return in ndis_mapreg_cnt() to something ridiculously
large, since some drivers apparently expect to be able to allocate
way more than just 64.
These changes allow the Level 1 1000baseSX driver to work for
the following card:
ndis0: <SMC TigerCard 1000 Adapter> port 0xe000-0xe0ff mem 0xda004000-0xda0043ff irq 10 at device 9.0 on pci0
ndis0: Ethernet address: 00:e0:29:6f:cc:04
This is already supported by the lge(4) driver, but I decided
to take a try at making the Windows driver that came with it work too,
since I still had the floppy diskette for it lying around.
the NTx86 section decoration).
subr_ndis.c: correct the behavior of ndis_query_resources(): if the
caller doesn't provide enough space to return the resources, tell it
how much it needs to provide and return an error.
subr_hal.c & subr_ntoskrnl.c: implement/stub a bunch of new routines;
ntoskrnl:
KefAcquireSpinLockAtDpcLevel
KefReleaseSpinLockFromDpcLevel
MmMapLockedPages
InterlockedDecrement
InterlockedIncrement
IoFreeMdl
KeInitializeSpinLock
HAL:
KfReleaseSpinLock
KeGetCurrentIrql
KfAcquireSpinLock
Lastly, correct spelling of "_aullshr" in the ntoskrnl functable.
copyrights to the inf parser files.
Add a -n flag to ndiscvt to allow the user to override the default
device name of NDIS devices. Instead of "ndis0, ndis1, etc..."
you can have "foo0, foo1, etc..." This allows you to have more than
one kind of NDIS device in the kernel at the same time.
Convert from printf() to device_printf() in if_ndis.c, kern_ndis.c
and subr_ndis.c.
Create UMA zones for ndis_packet and ndis_buffer structs allocated
on transmit. The zones are created and destroyed in the modevent
handler in kern_ndis.c.
printf() and UMA changes submitted by green@freebsd.org
peter and jhb: use __volatile__ to prevent gcc from possibly reordering
code, use a null inline instruction instead of a no-op movl (I would
have done this myself if I knew it was allowed) and combine two register
assignments into a single asm statement.
- if_ndis.c: set the NDIS_STATUS_PENDING flag on all outgoing packets
in ndis_start(), make the resource allocation code a little smarter
about how it selects the altmem range, correct a lock order reversal
in ndis_tick().
ndis_var.h
- In kern_ndis.c:ndis_send_packets(), avoid dereferencing NULL pointers
created when the driver's send routine immediately calls the txeof
handler (which releases the packets for us anyway).
- In if_ndis.c:ndis_80211_setstate(), implement WEP support.
method with something a little more intelligent: use BUS_GET_RESOURCE_LIST()
to run through all resources allocated to us and map them as needed. This
way we know exactly what resources need to be mapped and what their RIDs
are without having to guess. This simplifies both ndis_attach() and
ndis_convert_res(), and eliminates the unfriendly "ndisX: couldn't map
<foo>" messages that are sometimes emitted during driver load.
nb_size field in an ndis_buffer is meant to represent, but it does not
represent the original allocation size, so the sanity check doesn't
make any sense now that we're using the Windows-mandated initialization
method.
Among other things, this makes the following card work with the
NDISulator:
ndis0: <NETGEAR PA301 Phoneline10X PCI Adapter> mem 0xda004000-0xda004fff irq 10 at device 9.0 on pci0
This is that notoriously undocumented 10Mbps HomePNA Broadcom chipset
that people wanted support for many moons ago. Sadly, the only other
HomePNA NIC I have handy is a 1Mbps device, so I can't actually do
any 10Mbps performance tests, but it talks to my 1Mbps ADMtek card
just fine.
For received packets, an status of NDIS_STATUS_RESOURCES means we need
to copy the packet data and return the ndis_packet to the driver immediatel.
NDIS_STATUS_SUCCESS means we get to hold onto the packet, but we have
to set the status to NDIS_STATUS_PENDING so the driver knows we're
going to hang onto it for a while.
For transmit packets, NDIS_STATUS_PENDING means the driver will
asynchronously return the packet to us via the ndis_txeof() routine,
and NDIS_STATUS_SUCCESS means the driver sent the frame, and NDIS
(i.e. the OS) retains ownership of the packet and can free it
right away.
evaluate them. Whatever they're meant to do, they're doing it wrong.
Also:
- Clean up last bits of NULL fallout in subr_pe
- Don't let ndis_ifmedia_sts() do anything if the IFF_UP flag isn't set
- Implement NdisSystemProcessorCount() and NdisQueryMapRegisterCount().
packet being freed has NDIS_STATUS_PENDING in the status field of
the OOB data. Finish implementing the "alternative" packet-releasing
function so it doesn't crash.
For those that are curious about ndis0: <ORiNOCO 802.11abg ComboCard Gold>:
1123 packets transmitted, 1120 packets received, 0% packet loss
round-trip min/avg/max/stddev = 3.837/6.146/13.919/1.925 ms
Not bad!
mbuf<->packet housekeeping. Instead, add a couple of extra fields
to the end of ndis_packet. These should be invisible to the Windows
driver module.
This also lets me get rid of a little bit of evil from ndis_ptom()
(frobbing of the ext_buf field instead of relying on the MEXTADD()
macro).
- Fix ndis_time().
- Implement NdisGetSystemUpTime().
- Implement RtlCopyUnicodeString() and RtlUnicodeStringToAnsiString().
- In ndis_getstate_80211(), use sc->ndis_link to determine connect
status.
Submitted by: Brian Feldman <green@freebsd.org>
- Add explicit cardbus attachment in if_ndis.c
- Clean up after moving bus_setup_intr() in ndis_attach().
- When setting an ssid, program an empty ssid as a 1-byte string
with a single 0 byte. The Microsoft documentation says this is
how you're supposed to tell the NIC to attach to 'any' ssid.
- Keep trace of callout handles for timers externally from the
ndis_miniport_timer structs, and run through and clobber them
all after invoking the haltfunc just in case the driver left one
running. (We need to make sure all timers are cancelled on driver
unload.)
- Handle the 'cancelled' argument in ndis_cancel_timer() correctly.
NDIS_80211_NET_INFRA_BSS: I accidentally reversed them during
transcription from the Microsoft headers. Note that the
driver will default to BSS mode, and you need to specify
'mediaopt adhoc' to get it into IBSS mode.
supposed to be opaque to the driver, however it is exposed through
several macros which expect certain behavior. In my original
implementation, I used the mappedsystemva member of the structure
to hold a pointer to the buffer and bytecount to hold the length.
It turns out you must use the startva pointer to point to the
page containing the start of the buffer and set byteoffset to
the offset within the page where the buffer starts. So, for a buffer
with address 'baseva,' startva is baseva & ~(PAGE_SIZE -1) and
byteoffset is baseva & (PAGE_SIZE -1). We have to maintain this
convention everywhere that ndis_buffers are used.
Fortunately, Microsoft defines some macros for initializing and
manipulating NDIS_BUFFER structures in ntddk.h. I adapted some
of them for use here and used them where appropriate.
This fixes the discrepancy I observed between how RX'ed packet sizes
were being reported in the Broadcom wireless driver and the sample
ethernet drivers that I've tested. This should also help the
Intel Centrino wireless driver work.
Also try to properly initialize the 802.11 BSS and IBSS channels.
(Sadly, the channel value is meaningless since there's no way
in the existing NDIS API to get/set the channel, but this should
take care of any 'invalid channel (NULL)' messages printed on
the console.
In NdisQueryBuffer() and NdisQueryBufferSafe(), the vaddr argument is
optional, so test it before trying to dereference it.
Also correct NdisGetFirstBufferFromPacket()/NdisGetFirstBufferFromPacketSafe():
we need to use nb_mappedsystemva from the buffer, not nb_systemva.
routines: NdisUnchainBufferAtBack(), NdisGetFirstBufferFromPacketSafe()
and NdisGetFirstBufferFromPacket(). This should bring us a little
closer to getting the Intel centrino wireless NIC to work.
Note: I have not actually tested these additions since I don't
have a driver that calls them, however they're pretty simple, and
one of them is taken pretty much directly from the Windows ndis.h
header file, so I'm fairly confident they work, but disclaimers
apply.
- Make ndis_get_info()/ndis_set_info() sleep on the setdone/getdone
routines if they get back NDIS_STATUS_PENDING.
- Add a bunch of net80211 support so that 802.11 cards can be twiddled
with ifconfig. This still needs more work and is not guaranteed to
work for everyone. It works on my 802.11b/g card anyway.
The problem here is Microsoft doesn't provide a good way to a) learn
all the rates that a card supports (if it has more than 8, you're
kinda hosed) and b) doesn't provide a good way to distinguish between
802.11b, 802.11b/g an 802.11a/b/g cards, so you sort of have to guess.
Setting the SSID and switching between infrastructure/adhoc modes
should work. WEP still needs to be implemented. I can't find any API
for getting/setting the channel other than the registry/sysctl keys.
definitions for more than one device (usually differentiated by
the PCI subvendor/subdevice ID). Each device also has its own tree
of registry keys. In some cases, each device has the same keys, but
sometimes each device has a unique tree but with overlap. Originally,
I just had ndiscvt(8) dump out all the keys it could find, and we
would try to apply them to every device we could find. Now, each key
has an index number that matches it to a device in the device ID list.
This lets us create just the keys that apply to a particular device.
I also added an extra field to the device list to hold the subvendor
and subdevice ID.
Some devices are generic, i.e. there is no subsystem definition. If
we have a device that doesn't match a specific subsystem value and
we have a generic entry, we use the generic entry.
make it more robust. This should fix problems with crashes under
heavy traffic loads that have been reported. Also add a 'query done'
callback handler to satisfy the e100bex.sys sample Intel driver.
NdisAnsiStringToUnicodeString(), NdisWriteConfiguration().
Also add stubs for NdisMGetDeviceProperty(), NdisTerminateWrapper(),
NdisOpenConfigurationKeyByName(), NdisOpenConfigurationKeyByIndex()
and NdisMGetDeviceProperty().
- fix ndis_time() so that it returns a time based on the proper
epoch (wacky though it may be)
- implement NdisInitializeString() and NdisFreeString(), and add
stub for NdisMRemoveMiniport()
ntoskrnl_var.h:
- add missing member to the general_lookaside struct (gl_listentry)
subr_ntoskrnl.c:
- Fix arguments to the interlocked push/pop routines: 'head' is an
slist_header *, not an slist_entry *
- Kludge up _fastcall support for the push/pop routines. The _fastcall
convention is similar to _stdcall, except the first two available
DWORD-sized arguments are passed in %ecx and %edx, respectively.
One kludge for this __attribute__ ((regparm(3))), however this
isn't entirely right, as it assumes %eax, %ecx and %edx will be
used (regparm(2) assumes %eax and %edx). Another kludge is to
declare the two fastcall-ed args as local register variables and
explicitly assign them to %ecx and %edx, but experimentation showed
that gcc would not guard %ecx and %edx against being clobbered.
Thus, I came up with a 3rd kludge, which is to use some inline
assembly of the form:
void *arg1;
void *arg2;
__asm__("movl %%ecx, %%ecx" : "=c" (arg1));
__asm__("movl %%edx, %%edx" : "=d" (arg2));
This lets gcc know that we're going to reference %ecx and %edx and
that it should make an effort not to let it get trampled. This wastes
an instruction (movl %reg, %reg is a no-op) but insures proper
behavior. It's possible there's a better way to do this though:
this is the first time I've used inline assembler in this fashion.
The above fixes to ntoskrnl_var.h an subr_ntoskrnl.c make lookaside
lists work for the two drivers I have that use them, one of which
is an NDIS 5.0 miniport and another which is 5.1.
subr_ndis.c: NdisGetCurrentSystemTime() which, according to the
Microsoft documentation returns "the number of 100 nanosecond
intervals since January 1, 1601." I have no idea what's so special
about that epoch or why they chose 100 nanosecond ticks. I don't
know the proper offset to convert nanotime() from the UNIX epoch
to January 1, 1601, so for now I'm just doing the unit convertion
to 100s of nanoseconds.
subr_ntoskrnl.c: memcpy(), memset(), ExInterlockedPopEntrySList(),
ExInterlockedPushEntrySList().
The latter two are different from InterlockedPopEntrySList()
and InterlockedPushEntrySList() in that they accept a spinlock to
hold while executing, whereas the non-Ex routines use a lock
internal to ntoskrnl. I also modified ExInitializePagedLookasideList()
and ExInitializeNPagedLookasideList() to initialize mutex locks
within the lookaside structures. It seems that in NDIS 5.0,
the lookaside allocate/free routines ExInterlockedPopEntrySList()
and ExInterlockedPushEntrySList(), which require the use of the
per-lookaside spinlock, whereas in NDIS 5.1, the per-lookaside
spinlock is deprecated. We need to support both cases.
Note that I appear to be doing something wrong with
ExInterlockedPopEntrySList() and ExInterlockedPushEntrySList():
they don't appear to obtain proper pointers to their arguments,
so I'm probably doing something wrong in terms of their calling
convention (they're declared to be FASTCALL in Widnows, and I'm
not sure what that means for gcc). It happens that in my stub
lookaside implementation, they don't need to do any work anyway,
so for now I've hacked them to always return NULL, which avoids
corrupting the stack. I need to do this right though.
it's an error to set the buffer bytecount to anything larger than
the buffer's original allocation size, but anything less than that
is ok.
Also, in ndis_ptom(), use the same logic: if the bytecount is
larger than the allocation size, consider the bytecount invalid
and the allocation size as the packet fragment length (m_len)
instead of the bytecount.
This corrects a consistency problem between the Broadcom wireless
driver and some of the ethernet drivers I've tested: the ethernet
drivers all report the packet frag sizes in buf->nb_bytecount, but
the Broadcom wireless driver reports them in buf->nb_size. This
seems like a bug to me, but it clearly must work in Windows, so
we have to deal with it here too.
is provided to NDIS via the the miniport characteristics structure
supplied in the call to NdisMRegisterMiniport(). But in NDIS 5.0
and earlier, you had to call NdisMRegisterAdapterShutdownHandler()
and supply both a function pointer and context pointer.
We try to handle both cases in ndis_shutdown_nic(). If the
driver registered a shutdown routine and a context,then used
that context, otherwise pass it the adapter context from
NdisMSetAttributesEx().
This fixes a panic on shutdown with the sample Intel 82559 e100bex.sys
driver from the Windows DDK.
function pointer
Yes, it's what you think it is. Yes, you should run away now.
This is a special compatibility module for allowing Windows NDIS
miniport network drivers to be used with FreeBSD/x86. This provides
_binary_ NDIS compatibility (not source): you can run NDIS driver
code, but you can't build it. There are three main parts:
sys/compat/ndis: the NDIS compat API, which provides binary
compatibility functions for many routines in NDIS.SYS, HAL.dll
and ntoskrnl.exe in Windows (these are the three modules that
most NDIS miniport drivers use). The compat module also contains
a small PE relocator/dynalinker which relocates the Windows .SYS
image and then patches in our native routines.
sys/dev/if_ndis: the if_ndis driver wrapper. This module makes
use of the ndis compat API and can be compiled with a specially
prepared binary image file (ndis_driver_data.h) containing the
Windows .SYS image and registry key information parsed out of the
accompanying .INF file. Once if_ndis.ko is built, it can be loaded
and unloaded just like a native FreeBSD kenrel module.
usr.sbin/ndiscvt: a special utility that converts foo.sys and foo.inf
into an ndis_driver_data.h file that can be compiled into if_ndis.o.
Contains an .inf file parser graciously provided by Matt Dodd (and
mercilessly hacked upon by me) that strips out device ID info and
registry key info from a .INF file and packages it up with a binary
image array. The ndiscvt(8) utility also does some manipulation of
the segments within the .sys file to make life easier for the kernel
loader. (Doing the manipulation here saves the kernel code from having
to move things around later, which would waste memory.)
ndiscvt is only built for the i386 arch. Only files.i386 has been
updated, and none of this is turned on in GENERIC. It should probably
work on pc98. I have no idea about amd64 or ia64 at this point.
This is still a work in progress. I estimate it's about %85 done, but
I want it under CVS control so I can track subsequent changes. It has
been tested with exactly three drivers: the LinkSys LNE100TX v4 driver
(Lne100v4.sys), the sample Intel 82559 driver from the Windows DDK
(e100bex.sys) and the Broadcom BCM43xx wireless driver (bcmwl5.sys). It
still needs to have a net80211 stuff added to it. To use it, you would
do something like this:
# cd /sys/modules/ndis
# make; make load
# cd /sys/modules/if_ndis
# ndiscvt -i /path/to/foo.inf -s /path/to/foo.sys -o ndis_driver_data.h
# make; make load
# sysctl -a | grep ndis
All registry keys are mapped to sysctl nodes. Sometimes drivers refer
to registry keys that aren't mentioned in foo.inf. If this happens,
the NDIS API module creates sysctl nodes for these keys on the fly so
you can tweak them.
An example usage of the Broadcom wireless driver would be:
# sysctl hw.ndis0.EnableAutoConnect=1
# sysctl hw.ndis0.SSID="MY_SSID"
# sysctl hw.ndis0.NetworkType=0 (0 for bss, 1 for adhoc)
# ifconfig ndis0 <my ipaddr> netmask 0xffffff00 up
Things to be done:
- get rid of debug messages
- add in ndis80211 support
- defer transmissions until after a status update with
NDIS_STATUS_CONNECTED occurs
- Create smarter lookaside list support
- Split off if_ndis_pci.c and if_ndis_pccard.c attachments
- Make sure PCMCIA support works
- Fix ndiscvt to properly parse PCMCIA device IDs from INF files
- write ndisapi.9 man page