summaryrefslogtreecommitdiffstats
path: root/sys/dev/if_ndis
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2004-04-14 07:48:03 +0000
committerwpaul <wpaul@FreeBSD.org>2004-04-14 07:48:03 +0000
commit9765d24df650a8593a1d8dbd170e1f17b4bcb60f (patch)
tree2b4bcda838ff86f31726acf476842f624cb8bd4f /sys/dev/if_ndis
parent6ad9bc9a7779734ead6ac09fa9f5ef748368d800 (diff)
downloadFreeBSD-src-9765d24df650a8593a1d8dbd170e1f17b4bcb60f.zip
FreeBSD-src-9765d24df650a8593a1d8dbd170e1f17b4bcb60f.tar.gz
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.
Diffstat (limited to 'sys/dev/if_ndis')
-rw-r--r--sys/dev/if_ndis/if_ndis.c86
-rw-r--r--sys/dev/if_ndis/if_ndisvar.h11
2 files changed, 47 insertions, 50 deletions
diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c
index 901b313..f7467d9 100644
--- a/sys/dev/if_ndis/if_ndis.c
+++ b/sys/dev/if_ndis/if_ndis.c
@@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$");
#include <compat/ndis/pe_var.h>
#include <compat/ndis/resource_var.h>
+#include <compat/ndis/hal_var.h>
#include <compat/ndis/ntoskrnl_var.h>
#include <compat/ndis/ndis_var.h>
#include <compat/ndis/cfg_var.h>
@@ -371,8 +372,10 @@ ndis_attach(dev)
sc = device_get_softc(dev);
- sc->ndis_mtx = mtx_pool_alloc(ndis_mtxpool);
- sc->ndis_intrmtx = mtx_pool_alloc(ndis_mtxpool);
+ mtx_init(&sc->ndis_mtx, "ndis softc lock",
+ MTX_NETWORK_LOCK, MTX_DEF);
+ mtx_init(&sc->ndis_intrmtx,
+ "ndis irq lock", MTX_NETWORK_LOCK, MTX_DEF);
/*
* Hook interrupt early, since calling the driver's
@@ -492,7 +495,6 @@ ndis_attach(dev)
/* Do media setup */
if (sc->ndis_80211) {
struct ieee80211com *ic = (void *)ifp;
- ndis_80211_config config;
ndis_80211_rates_ex rates;
struct ndis_80211_nettype_list *ntl;
uint32_t arg;
@@ -650,27 +652,11 @@ nonettypes:
r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i);
if (r == 0)
ic->ic_caps |= IEEE80211_C_PMGT;
- i = sizeof(config);
- bzero((char *)&config, sizeof(config));
- config.nc_length = i;
- config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh);
- r = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &i);
- if (r == 0) {
- int chan;
- chan = ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0);
- if (chan < 0 || chan >= IEEE80211_CHAN_MAX) {
- ic->ic_ibss_chan = &ic->ic_channels[1];
- } else
- ic->ic_ibss_chan = &ic->ic_channels[chan];
- } else {
- device_printf(sc->ndis_dev, "couldn't retrieve "
- "channel info: %d\n", r);
- ic->ic_ibss_chan = &ic->ic_channels[1];
- }
bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr));
ieee80211_ifattach(ifp);
ieee80211_media_init(ifp, ieee80211_media_change,
ndis_media_status);
+ ic->ic_ibss_chan = IEEE80211_CHAN_ANYC;
ic->ic_bss->ni_chan = ic->ic_ibss_chan;
} else {
ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd,
@@ -757,6 +743,9 @@ ndis_detach(dev)
sysctl_ctx_free(&sc->ndis_ctx);
+ mtx_destroy(&sc->ndis_mtx);
+ mtx_destroy(&sc->ndis_intrmtx);
+
return(0);
}
@@ -1073,8 +1062,6 @@ ndis_linksts_done(adapter)
ndis_sched(ndis_starttask, ifp, NDIS_TASKQUEUE);
break;
case NDIS_STATUS_MEDIA_DISCONNECT:
- if (sc->ndis_80211)
- ndis_getstate_80211(sc);
if (sc->ndis_link)
ndis_sched(ndis_ticktask, sc, NDIS_TASKQUEUE);
break;
@@ -1091,14 +1078,17 @@ ndis_intrtask(arg)
{
struct ndis_softc *sc;
struct ifnet *ifp;
+ uint8_t irql;
sc = arg;
ifp = &sc->arpcom.ac_if;
+ irql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
ndis_intrhand(sc);
- mtx_pool_lock(ndis_mtxpool, sc->ndis_intrmtx);
+ FASTCALL1(hal_lower_irql, irql);
+ mtx_lock(&sc->ndis_intrmtx);
ndis_enable_intr(sc);
- mtx_pool_unlock(ndis_mtxpool, sc->ndis_intrmtx);
+ mtx_unlock(&sc->ndis_intrmtx);
return;
}
@@ -1118,14 +1108,14 @@ ndis_intr(arg)
if (sc->ndis_block.nmb_miniportadapterctx == NULL)
return;
- mtx_pool_lock(ndis_mtxpool, sc->ndis_intrmtx);
+ mtx_lock(&sc->ndis_intrmtx);
if (sc->ndis_block.nmb_interrupt->ni_isrreq == TRUE)
ndis_isr(sc, &is_our_intr, &call_isr);
else {
ndis_disable_intr(sc);
call_isr = 1;
}
- mtx_pool_unlock(ndis_mtxpool, sc->ndis_intrmtx);
+ mtx_unlock(&sc->ndis_intrmtx);
if ((is_our_intr || call_isr))
ndis_sched(ndis_intrtask, ifp, NDIS_SWI);
@@ -1617,32 +1607,22 @@ ndis_setstate_80211(sc)
device_printf (sc->ndis_dev, "set auth failed: %d\n", rval);
#endif
- /* Set SSID. */
-
- len = sizeof(ssid);
- bzero((char *)&ssid, len);
- ssid.ns_ssidlen = ic->ic_des_esslen;
- if (ssid.ns_ssidlen == 0) {
- ssid.ns_ssidlen = 1;
- } else
- bcopy(ic->ic_des_essid, ssid.ns_ssid, ssid.ns_ssidlen);
- rval = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len);
-
- if (rval)
- device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval);
-
len = sizeof(config);
bzero((char *)&config, len);
config.nc_length = len;
config.nc_fhconfig.ncf_length = sizeof(ndis_80211_config_fh);
- rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len);
- if (rval == 0) {
- int chan;
+ rval = ndis_get_info(sc, OID_802_11_CONFIGURATION, &config, &len);
- chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ if (rval == 0 && ic->ic_ibss_chan != IEEE80211_CHAN_ANYC) {
+ int chan, chanflag;
+
+ chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
+ chanflag = config.nc_dsconfig > 2500000 ? IEEE80211_CHAN_2GHZ :
+ IEEE80211_CHAN_5GHZ;
if (chan != ieee80211_mhz2ieee(config.nc_dsconfig / 1000, 0)) {
config.nc_dsconfig =
- ic->ic_bss->ni_chan->ic_freq * 1000;
+ ic->ic_ibss_chan->ic_freq * 1000;
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
len = sizeof(config);
config.nc_length = len;
config.nc_fhconfig.ncf_length =
@@ -1654,10 +1634,24 @@ ndis_setstate_80211(sc)
"DS config to %ukHz: %d\n",
config.nc_dsconfig, rval);
}
- } else
+ } else if (rval)
device_printf(sc->ndis_dev, "couldn't retrieve "
"channel info: %d\n", rval);
+ /* Set SSID -- always do this last. */
+
+ len = sizeof(ssid);
+ bzero((char *)&ssid, len);
+ ssid.ns_ssidlen = ic->ic_des_esslen;
+ if (ssid.ns_ssidlen == 0) {
+ ssid.ns_ssidlen = 1;
+ } else
+ bcopy(ic->ic_des_essid, ssid.ns_ssid, ssid.ns_ssidlen);
+ rval = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len);
+
+ if (rval)
+ device_printf (sc->ndis_dev, "set ssid failed: %d\n", rval);
+
return;
}
diff --git a/sys/dev/if_ndis/if_ndisvar.h b/sys/dev/if_ndis/if_ndisvar.h
index c2bc6b6..2b2f47f 100644
--- a/sys/dev/if_ndis/if_ndisvar.h
+++ b/sys/dev/if_ndis/if_ndisvar.h
@@ -32,6 +32,9 @@
* $FreeBSD$
*/
+#define NDIS_DEFAULT_NODENAME "FreeBSD NDIS node"
+#define NDIS_NODENAME_LEN 32
+
struct ndis_pci_type {
uint16_t ndis_vid;
uint16_t ndis_did;
@@ -87,8 +90,8 @@ struct ndis_softc {
struct resource *ndis_res_am; /* attribute mem (pccard) */
struct resource *ndis_res_cm; /* common mem (pccard) */
int ndis_rescnt;
- struct mtx *ndis_mtx;
- struct mtx *ndis_intrmtx;
+ struct mtx ndis_mtx;
+ struct mtx ndis_intrmtx;
device_t ndis_dev;
int ndis_unit;
ndis_miniport_block ndis_block;
@@ -124,6 +127,6 @@ struct ndis_softc {
int ndis_mmapcnt;
};
-#define NDIS_LOCK(_sc) mtx_pool_lock(ndis_mtxpool, (_sc)->ndis_mtx)
-#define NDIS_UNLOCK(_sc) mtx_pool_unlock(ndis_mtxpool, (_sc)->ndis_mtx)
+#define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx)
+#define NDIS_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx)
OpenPOWER on IntegriCloud