Commit Graph

43 Commits

Author SHA1 Message Date
imp
db85f415fa while (0); -> while (0) in multi-line macros 2006-08-17 22:50:33 +00:00
wpaul
6daa19f5d4 The latest version of the Intel 2200BG/2915ABG driver (9.0.0.3-9) from
Intel's web site requires some minor tweaks to get it to work:

- The driver seems to have been released with full WMI tracing enabled,
  and makes references to some WMI APIs, namely IoWMIRegistrationControl(),
  WmiQueryTraceInformation() and WmiTraceMessage(). Only the first
  one is ever called (during intialization). These have been implemented
  as do-nothing stubs for now. Also added a definition for STATUS_NOT_FOUND
  to ntoskrnl_var.h, which is used as a return code for one of the WMI
  routines.

- The driver references KeRaiseIrqlToDpcLevel() and KeLowerIrql()
  (the latter as a function, which is unusual because normally
  KeLowerIrql() is a macro in the Windows DDK that calls KfLowewIrql()).
  I'm not sure why these are being called since they're not really
  part of WDM. Presumeably they're being used for backwards
  compatibility with old versions of Windows. These have been
  implemented in subr_hal.c. (Note that they're _stdcall routines
  instead of _fastcall.)

- When querying the OID_802_11_BSSID_LIST OID to get a BSSID list,
  you don't know ahead of time how many networks the NIC has found
  during scanning, so you're allowed to pass 0 as the list length.
  This should cause the driver to return an 'insufficient resources'
  error and set the length to indicate how many bytes are actually
  needed. However for some reason, the Intel driver does not honor
  this convention: if you give it a length of 0, it returns some
  other error and doesn't tell you how much space is really needed.
  To get around this, if using a length of 0 yields anything besides
  the expected error case, we arbitrarily assume a length of 64K.
  This is similar to the hack that wpa_supplicant uses when doing
  a BSSID list query.
2005-11-06 19:38:34 +00:00
wpaul
a160984738 Get rid of the timer tracking and reaping code in NdisMInitializeTimer()
and ndis_halt_nic(). It's been disabled for some time anyway, and
it turns out there's a possible deadlock in NdisMInitializeTimer() when
acquiring the miniport block lock to modify the timer list: it's
possible for a driver to call NdisMInitializeTimer() when the miniport
block lock has already been acquired by an earlier piece of code. You
can't acquire the same spinlock twice, so this can deadlock.

Also, implement MmMapIoSpace() and MmUnmapIoSpace(), and make
NdisMMapIoSpace() and NdisMUnmapIoSpace() use them. There are some
drivers that want MmMapIoSpace() and MmUnmapIoSpace() so that they can
map arbitrary register spaces not directly associated with their
device resources. For example, there's an Atheros driver for
a miniPci card (0x168C:0x1014) on the IBM Thinkpad x40 that wants
to map some I/O spaces at 0xF00000 and 0xE00000 which are held by
the acpi0 device. I don't know what it wants these ranges for,
but if it can't map and access them, the MiniportInitialize() method
fails.
2005-10-26 06:52:57 +00:00
wpaul
8be176b07b Correct the macro definition for KeRaiseIrql(). The official API
is KeRaiseIrql(newirql, &oldirql), not oldirql = KeRaiseIrql(newirql).
(The macro ultimately translates to KfRaiseIrql() which does use
the latter API, so this has no effect on generated code.)

Also, wait for thread termination the right way: kthread_exit()
will ultimately do a wakeup(td->td_proc). This is the event we
should wait on. Eliminate the previous synchronization machinery
for this since it was never guaranteed to work correctly.
2005-10-21 05:23:20 +00:00
wpaul
81737fff08 Another round of cleanups and fixes:
- Change ndis_return() from a DPC to a workitem so that it doesn't
  run at DISPATCH_LEVEL (with the dispatcher lock held).

- In if_ndis.c, submit packets to the stack via (*ifp->if_input)() in
  a workitem instead of doing it directly in ndis_rxeof(), because
  ndis_rxeof() runs in a DPC, and hence at DISPATCH_LEVEL. This
  implies that the 'dispatch level' mutex for the current CPU is
  being held, and we don't want to call if_input while holding
  any locks.

- Reimplement IoConnectInterrupt()/IoDisconnectInterrupt(). The original
  approach I used to track down the interrupt resource (by scanning
  the device tree starting at the nexus) is prone to problems when
  two devices share an interrupt. (E.g removing ndis1 might disable
  interrupts for ndis0.) The new approach is to multiplex all the
  NDIS interrupts through a common internal dispatcher (ntoskrnl_intr())
  and allow IoConnectInterrupt()/IoDisconnectInterrupt() to add or
  remove interrupts from the dispatch list.

- Implement KeAcquireInterruptSpinLock() and KeReleaseInterruptSpinLock().

- Change the DPC and workitem threads to use the KeXXXSpinLock
  API instead of mtx_lock_spin()/mtx_unlock_spin().

- Simplify the NdisXXXPacket routines by creating an actual
  packet pool structure and using the InterlockedSList routines
  to manage the packet queue.

- Only honor the value returned by OID_GEN_MAXIMUM_SEND_PACKETS
  for serialized drivers. For deserialized drivers, we now create
  a packet array of 64 entries. (The Microsoft DDK documentation
  says that for deserialized miniports, OID_GEN_MAXIMUM_SEND_PACKETS
  is ignored, and the driver for the Marvell 8335 chip, which is
  a deserialized miniport, returns 1 when queried.)

- Clean up timer handling in subr_ntoskrnl.

- Add the following conditional debugging code:
	NTOSKRNL_DEBUG_TIMERS - add debugging and stats for timers
	NDIS_DEBUG_PACKETS - add extra sanity checking for NdisXXXPacket API
	NTOSKRNL_DEBUG_SPINLOCKS - add test for spinning too long

- In kern_ndis.c, always start the HAL first and shut it down last,
  since Windows spinlocks depend on it. Ntoskrnl should similarly be
  started second and shut down next to last.
2005-10-18 19:52:15 +00:00
wpaul
0ce580e541 Convert ndis_set_info() and ndis_get_info() from using msleep()
to KeSetEvent()/KeWaitForSingleObject(). Also make object argument
of KeWaitForSingleObject() a void * like it's supposed to be.
2005-10-12 03:02:50 +00:00
wpaul
ef07dbe57f This commit makes a big round of updates and fixes many, many things.
First and most importantly, I threw out the thread priority-twiddling
implementation of KeRaiseIrql()/KeLowerIrq()/KeGetCurrentIrql() in
favor of a new scheme that uses sleep mutexes. The old scheme was
really very naughty and sought to provide the same behavior as
Windows spinlocks (i.e. blocking pre-emption) but in a way that
wouldn't raise the ire of WITNESS. The new scheme represents
'DISPATCH_LEVEL' as the acquisition of a per-cpu sleep mutex. If
a thread on cpu0 acquires the 'dispatcher mutex,' it will block
any other thread on the same processor that tries to acquire it,
in effect only allowing one thread on the processor to be at
'DISPATCH_LEVEL' at any given time. It can then do the 'atomic sit
and spin' routine on the spinlock variable itself. If a thread on
cpu1 wants to acquire the same spinlock, it acquires the 'dispatcher
mutex' for cpu1 and then it too does an atomic sit and spin to try
acquiring the spinlock.

Unlike real spinlocks, this does not disable pre-emption of all
threads on the CPU, but it does put any threads involved with
the NDISulator to sleep, which is just as good for our purposes.

This means I can now play nice with WITNESS, and I can safely do
things like call malloc() when I'm at 'DISPATCH_LEVEL,' which
you're allowed to do in Windows.

Next, I completely re-wrote most of the event/timer/mutex handling
and wait code. KeWaitForSingleObject() and KeWaitForMultipleObjects()
have been re-written to use condition variables instead of msleep().
This allows us to use the Windows convention whereby thread A can
tell thread B "wake up with a boosted priority." (With msleep(), you
instead have thread B saying "when I get woken up, I'll use this
priority here," and thread A can't tell it to do otherwise.) The
new KeWaitForMultipleObjects() has been better tested and better
duplicates the semantics of its Windows counterpart.

I also overhauled the IoQueueWorkItem() API and underlying code.
Like KeInsertQueueDpc(), IoQueueWorkItem() must insure that the
same work item isn't put on the queue twice. ExQueueWorkItem(),
which in my implementation is built on top of IoQueueWorkItem(),
was also modified to perform a similar test.

I renamed the doubly-linked list macros to give them the same names
as their Windows counterparts and fixed RemoveListTail() and
RemoveListHead() so they properly return the removed item.

I also corrected the list handling code in ntoskrnl_dpc_thread()
and ntoskrnl_workitem_thread(). I realized that the original logic
did not correctly handle the case where a DPC callout tries to
queue up another DPC. It works correctly now.

I implemented IoConnectInterrupt() and IoDisconnectInterrupt() and
modified NdisMRegisterInterrupt() and NdisMDisconnectInterrupt() to
use them. I also tried to duplicate the interrupt handling scheme
used in Windows. The interrupt handling is now internal to ndis.ko,
and the ndis_intr() function has been removed from if_ndis.c. (In
the USB case, interrupt handling isn't needed in if_ndis.c anyway.)

NdisMSleep() has been rewritten to use a KeWaitForSingleObject()
and a KeTimer, which is how it works in Windows. (This is mainly
to insure that the NDISulator uses the KeTimer API so I can spot
any problems with it that may arise.)

KeCancelTimer() has been changed so that it only cancels timers, and
does not attempt to cancel a DPC if the timer managed to fire and
queue one up before KeCancelTimer() was called. The Windows DDK
documentation seems to imply that KeCantelTimer() will also call
KeRemoveQueueDpc() if necessary, but it really doesn't.

The KeTimer implementation has been rewritten to use the callout API
directly instead of timeout()/untimeout(). I still cheat a little in
that I have to manage my own small callout timer wheel, but the timer
code works more smoothly now. I discovered a race condition using
timeout()/untimeout() with periodic timers where untimeout() fails
to actually cancel a timer. I don't quite understand where the race
is, using callout_init()/callout_reset()/callout_stop() directly
seems to fix it.

I also discovered and fixed a bug in winx32_wrap.S related to
translating _stdcall calls. There are a couple of routines
(i.e. the 64-bit arithmetic intrinsics in subr_ntoskrnl) that
return 64-bit quantities. On the x86 arch, 64-bit values are
returned in the %eax and %edx registers. However, it happens
that the ctxsw_utow() routine uses %edx as a scratch register,
and x86_stdcall_wrap() and x86_stdcall_call() were only preserving
%eax before branching to ctxsw_utow(). This means %edx was getting
clobbered in some cases. Curiously, the most noticeable effect of this
bug is that the driver for the TI AXC110 chipset would constantly drop
and reacquire its link for no apparent reason. Both %eax and %edx
are preserved on the stack now. The _fastcall and _regparm
wrappers already handled everything correctly.

I changed if_ndis to use IoAllocateWorkItem() and IoQueueWorkItem()
instead of the NdisScheduleWorkItem() API. This is to avoid possible
deadlocks with any drivers that use NdisScheduleWorkItem() themselves.

The unicode/ansi conversion handling code has been cleaned up. The
internal routines have been moved to subr_ntoskrnl and the
RtlXXX routines have been exported so that subr_ndis can call them.
This removes the incestuous relationship between the two modules
regarding this code and fixes the implementation so that it honors
the 'maxlen' fields correctly. (Previously it was possible for
NdisUnicodeStringToAnsiString() to possibly clobber memory it didn't
own, which was causing many mysterious crashes in the Marvell 8335
driver.)

The registry handling code (NdisOpen/Close/ReadConfiguration()) has
been fixed to allocate memory for all the parameters it hands out to
callers and delete whem when NdisCloseConfiguration() is called.
(Previously, it would secretly use a single static buffer.)

I also substantially updated if_ndis so that the source can now be
built on FreeBSD 7, 6 and 5 without any changes. On FreeBSD 5, only
WEP support is enabled. On FreeBSD 6 and 7, WPA-PSK support is enabled.

The original WPA code has been updated to fit in more cleanly with
the net80211 API, and to eleminate the use of magic numbers. The
ndis_80211_setstate() routine now sets a default authmode of OPEN
and initializes the RTS threshold and fragmentation threshold.
The WPA routines were changed so that the authentication mode is
always set first, followed by the cipher. Some drivers depend on
the operations being performed in this order.

I also added passthrough ioctls that allow application code to
directly call the MiniportSetInformation()/MiniportQueryInformation()
methods via ndis_set_info() and ndis_get_info(). The ndis_linksts()
routine also caches the last 4 events signalled by the driver via
NdisMIndicateStatus(), and they can be queried by an application via
a separate ioctl. This is done to allow wpa_supplicant to directly
program the various crypto and key management options in the driver,
allowing things like WPA2 support to work.

Whew.
2005-10-10 16:46:39 +00:00
wpaul
8e4107ff8f Fix some of the things I broke so that the SMC2602W (AMD Am1772) driver
works again.

This driver uses NdisScheduleWorkItem(), and we have to take special steps
to insure that its workitems don't collide with any of the other workitems
used by the NDISulator. In particular, if one of the driver's work jobs
blocks, it can prevent NdisMAllocateSharedMemoryAsync() from completing
when expected.

The original hack to fix this was to have NdisMAllocateSharedMemoryAsync()
defer its work to the DPC queue instead of the general task queue. To
fix it now, I decided to add some additional workitem threads. (There's
supposed to be a pool of worker threads in Windows anyway.) Currently,
there are 4. There should be at least 2. One is reserved for the legacy
ExQueueWorkItem() API, while the others are used in round-robin by the
IoQueueWorkItem() API. NdisMAllocateSharedMemoryAsync() uses the latter
API while NdisScheduleWorkItem() uses the former, so the deadlock is
avoided.

Fixed NdisMRegisterDevice()/NdisMDeregisterDevice() to work a little
more sensibly with the new driver_object/device_object framework. It
doesn't really register a working user-mode interface, but the existing
code was completely wrong for the new framework.

Fixed a couple of bugs dealing with the cancellation of events and
DPCs. When cancelling an event that's still on the timer queue (i.e.
hasn't expired yet), reset dh_inserted in its dispatch header to FALSE.
Previously, it was left set to TRUE, which would make a cancelled
timer appear to have not been cancelled. Also, when removing a DPC
from a queue, reset its list pointers, otherwise a cancelled DPC
might mistakenly be treated as still pending.

Lastly, fix the behavior of ntoskrnl_wakeup() when dealing with
objects that have nobody waiting on them: sync event objects get
their signalled state reset to FALSE, but notification objects
should still be set to TRUE.
2005-05-19 04:44:26 +00:00
wpaul
51b4d0ab71 More fixes for multibus drivers. When calling out to the match
function in if_ndis_pci.c and if_ndis_pccard.c, provide the bustype
too so the stubs can ignore devlists that don't concern them.
2005-05-08 23:19:20 +00:00
wpaul
e9bace5ba1 This commit makes a bunch of changes, some big, some not so big.
- Remove the old task threads from kern_ndis.c and reimplement them in
  subr_ntoskrnl.c, in order to more properly emulate the Windows DPC
  API. Each CPU gets its own DPC queue/thread, and each queue can
  have low, medium and high importance DPCs. New APIs implemented:
  KeSetTargetProcessorDpc(), KeSetImportanceDpc() and KeFlushQueuedDpcs().
  (This is the biggest change.)

- Fix a bug in NdisMInitializeTimer(): the k_dpc pointer in the
  nmt_timer embedded in the ndis_miniport_timer struct must be set
  to point to the DPC, also embedded in the struct. Failing to do
  this breaks dequeueing of DPCs submitted via timers, and in turn
  breaks cancelling timers.

- Fix a bug in KeCancelTimer(): if the timer is interted in the timer
  queue (i.e. the timeout callback is still pending), we have to both
  untimeout() the timer _and_ call KeRemoveQueueDpc() to nuke the DPC
  that might be pending. Failing to do this breaks cancellation of
  periodic timers, which always appear to be inserted in the timer queue.

- Make use of the nmt_nexttimer field in ndis_miniport_timer: keep a
  queue of pending timers and cancel them all in ndis_halt_nic(), prior
  to calling MiniportHalt(). Also call KeFlushQueuedDpcs() to make sure
  any DPCs queued by the timers have expired.

- Modify NdisMAllocateSharedMemory() and NdisMFreeSharedMemory() to keep
  track of both the virtual and physical addresses of the shared memory
  buffers that get handed out. The AirGo MIMO driver appears to have a bug
  in it: for one of the segments is allocates, it returns the wrong
  virtual address. This would confuse NdisMFreeSharedMemory() and cause
  a crash. Why it doesn't crash Windows too I have no idea (from reading
  the documentation for NdisMFreeSharedMemory(), it appears to be a violation
  of the API).

- Implement strstr(), strchr() and MmIsAddressValid().

- Implement IoAllocateWorkItem(), IoFreeWorkItem(), IoQueueWorkItem() and
  ExQueueWorkItem(). (This is the second biggest change.)

- Make NdisScheduleWorkItem() call ExQueueWorkItem(). (Note that the
  ExQueueWorkItem() API is deprecated by Microsoft, but NDIS still uses
  it, since NdisScheduleWorkItem() is incompatible with the IoXXXWorkItem()
  API.)

- Change if_ndis.c to use the NdisScheduleWorkItem() interface for scheduling
  tasks.

With all these changes and fixes, the AirGo MIMO driver for the Belkin
F5D8010 Pre-N card now works. Special thanks to Paul Robinson
(paul dawt robinson at pwermedia dawt net) for the loan of a card
for testing.
2005-05-05 03:56:09 +00:00
wpaul
b493dd59e2 Throw the switch on the new driver generation/loading mechanism. From
here on in, if_ndis.ko will be pre-built as a module, and can be built
into a static kernel (though it's not part of GENERIC). Drivers are
created using the new ndisgen(8) script, which uses ndiscvt(8) under
the covers, along with a few other tools. The result is a driver module
that can be kldloaded into the kernel.

A driver with foo.inf and foo.sys files will be converted into
foo_sys.ko (and foo_sys.o, for those who want/need to make static
kernels). This module contains all of the necessary info from the
.INF file and the driver binary image, converted into an ELF module.
You can kldload this module (or add it to /boot/loader.conf) to have
it loaded automatically. Any required firmware files can be bundled
into the module as well (or converted/loaded separately).

Also, add a workaround for a problem in NdisMSleep(). During system
bootstrap (cold == 1), msleep() always returns 0 without actually
sleeping. The Intel 2200BG driver uses NdisMSleep() to wait for
the NIC's firmware to come to life, and fails to load if NdisMSleep()
doesn't actually delay. As a workaround, if msleep() (and hence
ndis_thsuspend()) returns 0, use a hard DELAY() to sleep instead).
This is not really the right thing to do, but we can't really do much
else. At the very least, this makes the Intel driver happy.

There are probably other drivers that fail in this way during bootstrap.
Unfortunately, the only workaround for those is to avoid pre-loading
them and kldload them once the system is running instead.
2005-04-24 20:21:22 +00:00
wpaul
a3b2d3191d Create new i386 windows/bsd thunking layer, similar to the amd64 thunking
layer, but with a twist.

The twist has to do with the fact that Microsoft supports structured
exception handling in kernel mode. On the i386 arch, exception handling
is implemented by hanging an exception registration list off the
Thread Environment Block (TEB), and the TEB is accessed via the %fs
register. The problem is, we use %fs as a pointer to the pcpu stucture,
which means any driver that tries to write through %fs:0 will overwrite
the curthread pointer and make a serious mess of things.

To get around this, Project Evil now creates a special entry in
the GDT on each processor. When we call into Windows code, a context
switch routine will fix up %fs so it points to our new descriptor,
which in turn points to a fake TEB. When the Windows code returns,
or calls out to an external routine, we swap %fs back again. Currently,
Project Evil makes use of GDT slot 7, which is all 0s by default.
I fully expect someone to jump up and say I can't do that, but I
couldn't find any code that makes use of this entry anywhere. Sadly,
this was the only method I could come up with that worked on both
UP and SMP. (Modifying the LDT works on UP, but becomes incredibly
complicated on SMP.) If necessary, the context switching stuff can
be yanked out while preserving the convention calling wrappers.

(Fortunately, it looks like Microsoft uses some special epilog/prolog
code on amd64 to implement exception handling, so the same nastiness
won't be necessary on that arch.)

The advantages are:

- Any driver that uses %fs as though it were a TEB pointer won't
  clobber pcpu.
- All the __stdcall/__fastcall/__regparm stuff that's specific to
  gcc goes away.

Also, while I'm here, switch NdisGetSystemUpTime() back to using
nanouptime() again. It turns out nanouptime() is way more accurate
than just using ticks(). On slower machines, the Atheros drivers
I tested seem to take a long time to associate due to the loss
in accuracy.
2005-04-11 02:02:35 +00:00
wpaul
12c3eeb4fa Tweak to hopefully make lookaside lists work on amd64: in Windows, the
nll_obsoletelock field in the lookaside list structure is only defined
for the i386 arch. For amd64, the field is gone, and different list
update routines are used which do their locking internally. Apparently
the Inprocomm amd64 driver uses lookaside lists. I'm not positive this
will make it work yet since I don't have an Inprocomm NIC to test, but
this needs to be fixed anyway.
2005-03-28 17:36:06 +00:00
wpaul
e41bbf9219 Check in ntoskrnl_var.h, which should have been included in the
previous commit.
2005-03-27 10:16:45 +00:00
wpaul
15a925bf93 MDLs are supposed to be variable size (they include an array of pages
that describe a buffer of variable size). The problem is, allocating
MDLs off the heap is slow, and it can happen that drivers will allocate
lots and lots of lots of MDLs as they run.

As a compromise, we now do the following: we pre-allocate a zone for
MDLs big enough to describe any buffer with 16 or less pages. If
IoAllocateMdl() needs a MDL for a buffer with 16 or less pages, we'll
allocate it from the zone. Otherwise, we allocate it from the heap.
MDLs allocate from the zone have a flag set in their mdl_flags field.
When the MDL is released, IoMdlFree() will uma_zfree() the MDL if
it has the MDL_ZONE_ALLOCED flag set, otherwise it will release it
to the heap.

The assumption is that 16 pages is a "big number" and we will rarely
need MDLs larger than that.

- Moved the ndis_buffer zone to subr_ntoskrnl.c from kern_ndis.c
  and named it mdl_zone.

- Modified IoAllocateMdl() and IoFreeMdl() to use uma_zalloc() and
  uma_zfree() if necessary.

- Made ndis_mtop() use IoAllocateMdl() instead of calling uma_zalloc()
  directly.

Inspired by: discussion with Giridhar Pemmasani
2005-02-26 00:22:16 +00:00
wpaul
371673aec8 Add macros to construct Windows IOCTL codes, and to extract function
codes from an IOCTL. (The USB module will need them later.)
2005-02-25 18:25:48 +00:00
wpaul
64968e6acf Fix a couple of callback instances that should have been wrapped with
MSCALLx().

Add definition for STATUS_PENDING error code.
2005-02-25 08:34:32 +00:00
wpaul
efb3e8caac - Correct one aspect of the driver_object/device_object/IRP framework:
when we create a PDO, the driver_object associated with it is that
  of the parent driver, not the driver we're trying to attach. For
  example, if we attach a PCI device, the PDO we pass to the NdisAddDevice()
  function should contain a pointer to fake_pci_driver, not to the NDIS
  driver itself. For PCI or PCMCIA devices this doesn't matter because
  the child never needs to talk to the parent bus driver, but for USB,
  the child needs to be able to send IRPs to the parent USB bus driver, and
  for that to work the parent USB bus driver has to be hung off the PDO.

  This involves modifying windrv_lookup() so that we can search for
  bus drivers by name, if necessary. Our fake bus drivers attach themselves
  as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them
  using those names.

  The individual attachment stubs now create and attach PDOs to the
  parent bus drivers instead of hanging them off the NDIS driver's
  object, and in if_ndis.c, we now search for the correct driver
  object depending on the bus type, and use that to find the correct PDO.

  With this fix, I can get my sample USB ethernet driver to deliver
  an IRP to my fake parent USB bus driver's dispatch routines.

- Add stub modules for USB support: subr_usbd.c, usbd_var.h and
  if_ndis_usb.c. The subr_usbd.c module is hooked up the build
  but currently doesn't do very much. It provides the stub USB
  parent driver object and a dispatch routine for
  IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at
  the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub
  compiles, but is not hooked up to the build yet. I'm putting
  these here so I can keep them under source code control as I
  flesh them out.
2005-02-24 21:49:14 +00:00
wpaul
954c02c21f Implement IoCancelIrp(), IoAcquireCancelSpinLock(), IoReleaseCancelSpinLock()
and a machine-independent though inefficient InterlockedExchange().
In Windows, InterlockedExchange() appears to be implemented in header
files via inline assembly. I would prefer using an atomic.h macro for
this, but there doesn't seem to be one that just does a plain old
atomic exchange (as opposed to compare and exchange). Also implement
IoSetCancelRoutine(), which is just a macro that uses InterlockedExchange().

Fill in IoBuildSynchronousFsdRequest(), IoBuildAsynchronousFsdRequest()
and IoBuildDeviceIoControlRequest() so that they do something useful,
and add a bunch of #defines to ntoskrnl_var.h to help make these work.
These may require some tweaks later.
2005-02-23 16:44:33 +00:00
wpaul
a372ba85ce KeAcquireSpinLockRaiseToDpc() and KeReleaseSpinLock() are (at least
for now) exactly the same as KfAcquireSpinLock() and KfReleaseSpinLock().
I implemented the former as small routines in subr_ntoskrnl.c that just
turned around and invoked the latter. But I don't really need the wrapper
routines: I can just create an entries in the ntoskrnl func table that
map KeAcquireSpinLockRaiseToDpc() and KeReleaseSpinLock() to
KfAcquireSpinLock() and KfReleaseSpinLock() directly. This means
the stubs can go away.
2005-02-16 18:18:30 +00:00
wpaul
07b632956a Add support for Windows/x86-64 binaries to Project Evil.
Ville-Pertti Keinonen (will at exomi dot comohmygodnospampleasekthx)
deserves a big thanks for submitting initial patches to make it
work. I have mangled his contributions appropriately.

The main gotcha with Windows/x86-64 is that Microsoft uses a different
calling convention than everyone else. The standard ABI requires using
6 registers for argument passing, with other arguments on the stack.
Microsoft uses only 4 registers, and requires the caller to leave room
on the stack for the register arguments incase the callee needs to
spill them. Unlike x86, where Microsoft uses a mix of _cdecl, _stdcall
and _fastcall, all routines on Windows/x86-64 uses the same convention.
This unfortunately means that all the functions we export to the
driver require an intermediate translation wrapper. Similarly, we have
to wrap all calls back into the driver binary itself.

The original patches provided macros to wrap every single routine at
compile time, providing a secondary jump table with a customized
wrapper for each exported routine. I decided to use a different approach:
the call wrapper for each function is created from a template at
runtime, and the routine to jump to is patched into the wrapper as
it is created. The subr_pe module has been modified to patch in the
wrapped function instead of the original. (On x86, the wrapping
routine is a no-op.)

There are some minor API differences that had to be accounted for:

- KeAcquireSpinLock() is a real function on amd64, not a macro wrapper
  around KfAcquireSpinLock()
- NdisFreeBuffer() is actually IoFreeMdl(). I had to change the whole
  NDIS_BUFFER API a bit to accomodate this.

Bugs fixed along the way:
- IoAllocateMdl() always returned NULL
- kern_windrv.c:windrv_unload() wasn't releasing private driver object
  extensions correctly (found thanks to memguard)

This has only been tested with the driver for the Broadcom 802.11g
chipset, which was the only Windows/x86-64 driver I could find.
2005-02-16 05:41:18 +00:00
wpaul
df89b62698 Next step on the road to IRPs: create and use an imitation of the
Windows DRIVER_OBJECT and DEVICE_OBJECT mechanism so that we can
simulate driver stacking.

In Windows, each loaded driver image is attached to a DRIVER_OBJECT
structure. Windows uses the registry to match up a given vendor/device
ID combination with a corresponding DRIVER_OBJECT. When a driver image
is first loaded, its DriverEntry() routine is invoked, which sets up
the AddDevice() function pointer in the DRIVER_OBJECT and creates
a dispatch table (based on IRP major codes). When a Windows bus driver
detects a new device, it creates a Physical Device Object (PDO) for
it. This is a DEVICE_OBJECT structure, with semantics analagous to
that of a device_t in FreeBSD. The Windows PNP manager will invoke
the driver's AddDevice() function and pass it pointers to the DRIVER_OBJECT
and the PDO.

The AddDevice() function then creates a new DRIVER_OBJECT structure of
its own. This is known as the Functional Device Object (FDO) and
corresponds roughly to a private softc instance. The driver uses
IoAttachDeviceToDeviceStack() to add this device object to the
driver stack for this PDO. Subsequent drivers (called filter drivers
in Windows-speak) can be loaded which add themselves to the stack.
When someone issues an IRP to a device, it travel along the stack
passing through several possible filter drivers until it reaches
the functional driver (which actually knows how to talk to the hardware)
at which point it will be completed. This is how Windows achieves
driver layering.

Project Evil now simulates most of this. if_ndis now has a modevent
handler which will use MOD_LOAD and MOD_UNLOAD events to drive the
creation and destruction of DRIVER_OBJECTs. (The load event also
does the relocation/dynalinking of the image.) We don't have a registry,
so the DRIVER_OBJECTS are stored in a linked list for now. Eventually,
the list entry will contain the vendor/device ID list extracted from
the .INF file. When ndis_probe() is called and detectes a supported
device, it will create a PDO for the device instance and attach it
to the DRIVER_OBJECT just as in Windows. ndis_attach() will then call
our NdisAddDevice() handler to create the FDO. The NDIS miniport block
is now a device extension hung off the FDO, just as it is in Windows.
The miniport characteristics table is now an extension hung off the
DRIVER_OBJECT as well (the characteristics are the same for all devices
handled by a given driver, so they don't need to be per-instance.)
We also do an IoAttachDeviceToDeviceStack() to put the FDO on the
stack for the PDO. There are a couple of fake bus drivers created
for the PCI and pccard buses. Eventually, there will be one for USB,
which will actually accept USB IRP.s

Things should still work just as before, only now we do things in
the proper order and maintain the correct framework to support passing
IRPs between drivers.

Various changes:

- corrected the comments about IRQL handling in subr_hal.c to more
  accurately reflect reality
- update ndiscvt to make the drv_data symbol in ndis_driver_data.h a
  global so that if_ndis_pci.o and/or if_ndis_pccard.o can see it.
- Obtain the softc pointer from the miniport block by referencing
  the PDO rather than a private pointer of our own (nmb_ifp is no
  longer used)
- implement IoAttachDeviceToDeviceStack(), IoDetachDevice(),
  IoGetAttachedDevice(), IoAllocateDriverObjectExtension(),
  IoGetDriverObjectExtension(), IoCreateDevice(), IoDeleteDevice(),
  IoAllocateIrp(), IoReuseIrp(), IoMakeAssociatedIrp(), IoFreeIrp(),
  IoInitializeIrp()
- fix a few mistakes in the driver_object and device_object definitions
- add a new module, kern_windrv.c, to handle the driver registration
  and relocation/dynalinkign duties (which don't really belong in
  kern_ndis.c).
- made ndis_block and ndis_chars in the ndis_softc stucture pointers
  and modified all references to it
- fixed NdisMRegisterMiniport() and NdisInitializeWrapper() so they
  work correctly with the new driver_object mechanism
- changed ndis_attach() to call NdisAddDevice() instead of ndis_load_driver()
  (which is now deprecated)
- used ExAllocatePoolWithTag()/ExFreePool() in lookaside list routines
  instead of kludged up alloc/free routines
- added kern_windrv.c to sys/modules/ndis/Makefile and files.i386.
2005-02-08 17:23:25 +00:00
wpaul
e00c1df907 Apparently, the Intel icc compiler doesn't like it when you use
attributes in casts (i.e. foo = (__stdcall sometype)bar). This only
happens in two places where we need to set up function pointers, so
work around the problem with some void pointer magic.
2005-01-25 17:00:54 +00:00
wpaul
361515a412 Begin the first phase of trying to add IRP support (and ultimately
USB device support):

- Convert all of my locally chosen function names to their actual
  Windows equivalents, where applicable. This is a big no-op change
  since it doesn't affect functionality, but it helps avoid a bit
  of confusion (it's now a lot easier to see which functions are
  emulated Windows API routines and which are just locally defined).

- Turn ndis_buffer into an mdl, like it should have been. The structure
  is the same, but now it belongs to the subr_ntoskrnl module.

- Implement a bunch of MDL handling macros from Windows and use them where
  applicable.

- Correct the implementation of IoFreeMdl().

- Properly implement IoAllocateMdl() and MmBuildMdlForNonPagedPool().

- Add the definitions for struct irp and struct driver_object.

- Add IMPORT_FUNC() and IMPORT_FUNC_MAP() macros to make formatting
  the module function tables a little cleaner. (Should also help
  with AMD64 support later on.)

- Fix if_ndis.c to use KeRaiseIrql() and KeLowerIrql() instead of
  the previous calls to hal_raise_irql() and hal_lower_irql() which
  have been renamed.

The function renaming generated a lot of churn here, but there should
be very little operational effect.
2005-01-24 18:18:12 +00:00
wpaul
2fe7b09cb1 Fix a problem reported by Pierre Beyssac. Sometinmes when ndis_get_info()
calls MiniportQueryInformation(), it will return NDIS_STATUS_PENDING.
When this happens, ndis_get_info() will sleep waiting for a completion
event. If two threads call ndis_get_info() and both end up having to
sleep, they will both end up waiting on the same wait channel, which
can cause a panic in sleepq_add() if INVARIANTS are turned on.

Fix this by having ndis_get_info() use a common mutex rather than
using the process mutex with PROC_LOCK(). Also do the same for
ndis_set_info(). Note that Pierre's original patch also made ndis_thsuspend()
use the new mutex, but ndis_thsuspend() shouldn't need this since
it will make each thread that calls it sleep on a unique wait channel.

Also, it occured to me that we probably don't want to enter
MiniportQueryInformation() or MiniportSetInformation() from more
than one thread at any given time, so now we acquire a Windows
spinlock before calling either of them. The Microsoft documentation
says that MiniportQueryInformation() and MiniportSetInformation()
are called at DISPATCH_LEVEL, and previously we would call
KeRaiseIrql() to set the IRQL to DISPATCH_LEVEL before entering
either routine, but this only guarantees mutual exclusion on
uniprocessor machines. To make it SMP safe, we need to use a real
spinlock. For now, I'm abusing the spinlock embedded in the
NDIS_MINIPORT_BLOCK structure for this purpose. (This may need to be
applied to some of the other routines in kern_ndis.c at a later date.)

Export ntoskrnl_init_lock() (KeInitializeSpinlock()) from subr_ntoskrnl.c
since we need to use in in kern_ndis.c, and since it's technically part
of the Windows kernel DDK API along with the other spinlock routines. Use
it in subr_ndis.c too rather than frobbing the spinlock directly.
2005-01-14 22:39:44 +00:00
imp
362fcfc1e2 Start each of the license/copyright comments with /*- 2005-01-05 22:34:37 +00:00
wpaul
f7237cd696 More minor cleanups and one small bug fix:
- 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().
2004-08-04 18:22:50 +00:00
wpaul
b9b3caf965 Big mess 'o changes:
- 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.
2004-08-01 20:04:31 +00:00
wpaul
d1e72fc336 Correct the AT_DISPATCH_LEVEL() macro to match earlier changes. 2004-04-20 02:27:38 +00:00
wpaul
1ea56deba6 - Use memory barrier with atomic operations in ntoskrnl_lock_dpc() and
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.
2004-04-16 00:04:28 +00:00
wpaul
9765d24df6 Continue my efforts to imitate Windows as closely as possible by
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.
2004-04-14 07:48:03 +00:00
wpaul
163b236504 Add missing cprd_flags member to partial resource structure in
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.
2004-03-29 02:15:29 +00:00
wpaul
e7b058478d The Intel 2200BG NDIS driver does an alloca() of about 5000 bytes
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.
2004-03-22 00:41:41 +00:00
wpaul
8feaa1f450 - Rewrite the timer and event API routines in subr_ndis.c so that they
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.
2004-03-20 23:39:43 +00:00
wpaul
f7976fadc0 - Some older Atheros drivers want KeInitializeTimer(), so implement it,
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.
2004-03-04 23:04:02 +00:00
wpaul
1e2ef501f0 More cleanups/fixes for the AMD Am1771 driver:
- 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.
2004-02-16 02:50:03 +00:00
wpaul
fe7c8eefc3 Add a whole bunch of new stuff to make the driver for the AMD Am1771/Am1772
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.
2004-02-07 06:44:13 +00:00
wpaul
545deb598a Add WDM major/minor #defines. 2004-01-19 20:45:27 +00:00
obrien
a1df3ddca2 The ndis_kspin_lock type is called KSPIN_LOCK in MS-Windows.
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.
2004-01-16 02:07:04 +00:00
wpaul
a0d9582fee Re-do the handling of ndis_buffers. The NDIS_BUFFER structure is
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.
2003-12-23 04:08:22 +00:00
wpaul
34759c3ef9 Correct the implementation of NDIS_BUFFER_TO_SPAN_PAGES(). 2003-12-13 09:07:35 +00:00
wpaul
1883e811f7 subr_ndis.c:
- 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.
2003-12-13 07:41:12 +00:00
wpaul
7e1ac58149 Commit the first cut of Project Evil, also known as the NDISulator.
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
2003-12-11 22:34:37 +00:00