diff options
-rw-r--r-- | sys/compat/ndis/kern_ndis.c | 142 | ||||
-rw-r--r-- | sys/compat/ndis/kern_windrv.c | 56 | ||||
-rw-r--r-- | sys/compat/ndis/ndis_var.h | 119 | ||||
-rw-r--r-- | sys/compat/ndis/ntoskrnl_var.h | 142 | ||||
-rw-r--r-- | sys/compat/ndis/subr_hal.c | 108 | ||||
-rw-r--r-- | sys/compat/ndis/subr_ndis.c | 555 | ||||
-rw-r--r-- | sys/compat/ndis/subr_ntoskrnl.c | 1286 | ||||
-rw-r--r-- | sys/compat/ndis/winx32_wrap.S | 24 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndis.c | 1244 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndisvar.h | 88 |
10 files changed, 2537 insertions, 1227 deletions
diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c index 573d78f..97f63f7 100644 --- a/sys/compat/ndis/kern_ndis.c +++ b/sys/compat/ndis/kern_ndis.c @@ -82,7 +82,7 @@ static void ndis_setdone_func(ndis_handle, ndis_status); static void ndis_getdone_func(ndis_handle, ndis_status); static void ndis_resetdone_func(ndis_handle, ndis_status, uint8_t); static void ndis_sendrsrcavail_func(ndis_handle); -static void ndis_intrhand(kdpc *, device_object *, +static void ndis_intrsetup(kdpc *, device_object *, irp *, struct ndis_softc *); static void ndis_return(kdpc *, void *, void *, void *); @@ -93,7 +93,7 @@ static image_patch_table kernndis_functbl[] = { IMPORT_SFUNC(ndis_getdone_func, 2), IMPORT_SFUNC(ndis_resetdone_func, 3), IMPORT_SFUNC(ndis_sendrsrcavail_func, 1), - IMPORT_SFUNC(ndis_intrhand, 4), + IMPORT_SFUNC(ndis_intrsetup, 4), IMPORT_SFUNC(ndis_return, 1), { NULL, NULL, NULL } @@ -183,34 +183,6 @@ ndis_modevent(module_t mod, int cmd, void *arg) DEV_MODULE(ndisapi, ndis_modevent, NULL); MODULE_VERSION(ndisapi, 1); -int -ndis_thsuspend(p, m, timo) - struct proc *p; - struct mtx *m; - int timo; -{ - int error; - - if (m != NULL) { - error = msleep(&p->p_siglist, m, - curthread->td_priority, "ndissp", timo); - } else { - PROC_LOCK(p); - error = msleep(&p->p_siglist, &p->p_mtx, - curthread->td_priority|PDROP, "ndissp", timo); - } - - return(error); -} - -void -ndis_thresume(p) - struct proc *p; -{ - wakeup(&p->p_siglist); - return; -} - static void ndis_sendrsrcavail_func(adapter) ndis_handle adapter; @@ -328,7 +300,6 @@ ndis_create_sysctls(arg) #endif /* Add the driver-specific registry keys. */ - vals = sc->ndis_regvals; while(1) { if (vals->nc_cfgkey == NULL) break; @@ -358,17 +329,8 @@ ndis_create_sysctls(arg) continue; } -#if __FreeBSD_version < 502113 - SYSCTL_ADD_STRING(&sc->ndis_ctx, - SYSCTL_CHILDREN(sc->ndis_tree), -#else - SYSCTL_ADD_STRING(device_get_sysctl_ctx(sc->ndis_dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ndis_dev)), -#endif - OID_AUTO, vals->nc_cfgkey, - CTLFLAG_RW, vals->nc_val, - sizeof(vals->nc_val), - vals->nc_cfgdesc); + ndis_add_sysctl(sc, vals->nc_cfgkey, vals->nc_cfgdesc, + vals->nc_val, CTLFLAG_RW); vals++; } @@ -420,8 +382,10 @@ ndis_add_sysctl(arg, key, desc, val, flag) cfg = malloc(sizeof(struct ndis_cfglist), M_DEVBUF, M_NOWAIT|M_ZERO); - if (cfg == NULL) + if (cfg == NULL) { + printf("failed for %s\n", key); return(ENOMEM); + } cfg->ndis_cfg.nc_cfgkey = strdup(key, M_DEVBUF); if (desc == NULL) { @@ -433,6 +397,7 @@ ndis_add_sysctl(arg, key, desc, val, flag) TAILQ_INSERT_TAIL(&sc->ndis_cfglist_head, cfg, link); + cfg->ndis_oid = #if __FreeBSD_version < 502113 SYSCTL_ADD_STRING(&sc->ndis_ctx, SYSCTL_CHILDREN(sc->ndis_tree), #else @@ -446,18 +411,34 @@ ndis_add_sysctl(arg, key, desc, val, flag) return(0); } +/* + * Somewhere, somebody decided "hey, let's automatically create + * a sysctl tree for each device instance as it's created -- it'll + * make life so much easier!" Lies. Why must they turn the kernel + * into a house of lies? + */ + int ndis_flush_sysctls(arg) void *arg; { struct ndis_softc *sc; struct ndis_cfglist *cfg; + struct sysctl_ctx_list *clist; sc = arg; +#if __FreeBSD_version < 502113 + clist = &sc->ndis_ctx; +#else + clist = device_get_sysctl_ctx(sc->ndis_dev); +#endif + while (!TAILQ_EMPTY(&sc->ndis_cfglist_head)) { cfg = TAILQ_FIRST(&sc->ndis_cfglist_head); TAILQ_REMOVE(&sc->ndis_cfglist_head, cfg, link); + sysctl_ctx_entry_del(clist, cfg->ndis_oid); + sysctl_remove_oid(cfg->ndis_oid, 1, 0); free(cfg->ndis_cfg.nc_cfgkey, M_DEVBUF); free(cfg->ndis_cfg.nc_cfgdesc, M_DEVBUF); free(cfg, M_DEVBUF); @@ -488,9 +469,11 @@ ndis_return(dpc, arg, sysarg1, sysarg2) returnfunc = sc->ndis_chars->nmc_return_packet_func; - KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + if (NDIS_SERIALIZED(sc->ndis_block)) + KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); MSCALL2(returnfunc, adapter, p); - KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + if (NDIS_SERIALIZED(sc->ndis_block)) + KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); return; } @@ -691,10 +674,13 @@ ndis_ptom(m0, p) struct mbuf **m0; ndis_packet *p; { - struct mbuf *m, *prev = NULL; + struct mbuf *m = NULL, *prev = NULL; ndis_buffer *buf; ndis_packet_private *priv; uint32_t totlen = 0; + struct ifnet *ifp; + struct ether_header *eh; + int diff; if (p == NULL || m0 == NULL) return(EINVAL); @@ -718,6 +704,7 @@ ndis_ptom(m0, p) MEXTADD(m, m->m_data, m->m_len, ndis_return_packet, p, 0, EXT_NDIS); p->np_refcnt++; + totlen += m->m_len; if (m->m_flags & M_PKTHDR) *m0 = m; @@ -726,6 +713,24 @@ ndis_ptom(m0, p) prev = m; } + /* + * This is a hack to deal with the Marvell 8335 driver + * which, when associated with an AP in WPA-PSK mode, + * seems to overpad its frames by 8 bytes. I don't know + * that the extra 8 bytes are for, and they're not there + * in open mode, so for now clamp the frame size at 1514 + * until I can figure out how to deal with this properly, + * otherwise if_ethersubr() will spank us by discarding + * the 'oversize' frames. + */ + + eh = mtod((*m0), struct ether_header *); + ifp = ((struct ndis_softc *)p->np_softc)->ifp; + if (totlen > ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE)) { + diff = totlen - ETHER_MAX_FRAME(ifp, eh->ether_type, FALSE); + totlen -= diff; + m->m_len -= diff; + } (*m0)->m_pkthdr.len = totlen; return(0); @@ -770,6 +775,7 @@ ndis_mtop(m0, p) *p = NULL; return(ENOMEM); } + MmBuildMdlForNonPagedPool(buf); if (priv->npp_head == NULL) priv->npp_head = buf; @@ -875,7 +881,6 @@ ndis_set_info(arg, oid, buf, buflen) rval = sc->ndis_block->nmb_setstat; } - if (byteswritten) *buflen = byteswritten; if (bytesneeded) @@ -1080,7 +1085,7 @@ ndis_reset_nic(arg) return(0); } -#define NDIS_REAP_TIMERS +#undef NDIS_REAP_TIMERS int ndis_halt_nic(arg) @@ -1112,9 +1117,9 @@ ndis_halt_nic(arg) n->nmt_nexttimer = NULL; } sc->ndis_block->nmb_timerlist = NULL; +#endif if (!cold) KeFlushQueuedDpcs(); -#endif NDIS_LOCK(sc); adapter = sc->ndis_block->nmb_miniportadapterctx; @@ -1123,7 +1128,6 @@ ndis_halt_nic(arg) return(EIO); } - sc->ndis_block->nmb_miniportadapterctx = NULL; sc->ndis_block->nmb_devicectx = NULL; /* @@ -1137,6 +1141,10 @@ ndis_halt_nic(arg) MSCALL1(haltfunc, adapter); + NDIS_LOCK(sc); + sc->ndis_block->nmb_miniportadapterctx = NULL; + NDIS_UNLOCK(sc); + return(0); } @@ -1216,7 +1224,7 @@ ndis_init_nic(arg) * expects them to fire before the halt is called. */ - ndis_thsuspend(curthread->td_proc, NULL, hz); + tsleep(curthread->td_proc, PWAIT, "ndwait", hz); NDIS_LOCK(sc); sc->ndis_block->nmb_devicectx = sc; @@ -1292,33 +1300,26 @@ ndis_isr(arg, ourintr, callhandler) } static void -ndis_intrhand(dpc, dobj, ip, sc) +ndis_intrsetup(dpc, dobj, ip, sc) kdpc *dpc; device_object *dobj; irp *ip; struct ndis_softc *sc; { - ndis_handle adapter; - ndis_interrupt_handler intrfunc; - uint8_t irql; - - adapter = sc->ndis_block->nmb_miniportadapterctx; - intrfunc = sc->ndis_chars->nmc_interrupt_func; - - if (adapter == NULL || intrfunc == NULL) - return; - - if (NDIS_SERIALIZED(sc->ndis_block)) - KeAcquireSpinLock(&sc->ndis_block->nmb_lock, &irql); + ndis_miniport_interrupt *intr; - MSCALL1(intrfunc, adapter); + intr = sc->ndis_block->nmb_interrupt; - /* If there's a MiniportEnableInterrupt() routine, call it. */ + /* Sanity check. */ - ndis_enable_intr(sc); + if (intr == NULL) + return; - if (NDIS_SERIALIZED(sc->ndis_block)) - KeReleaseSpinLock(&sc->ndis_block->nmb_lock, irql); + KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); + KeResetEvent(&intr->ni_dpcevt); + if (KeInsertQueueDpc(&intr->ni_dpc, NULL, NULL) == TRUE) + intr->ni_dpccnt++; + KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); return; } @@ -1424,6 +1425,7 @@ NdisAddDevice(drv, pdo) block->nmb_physdeviceobj = pdo; block->nmb_nextdeviceobj = IoAttachDeviceToDeviceStack(fdo, pdo); KeInitializeSpinLock(&block->nmb_lock); + InitializeListHead(&block->nmb_parmlist); /* * Stash pointers to the miniport block and miniport @@ -1447,7 +1449,7 @@ NdisAddDevice(drv, pdo) IoDeleteDevice(fdo); return(status); } - INIT_LIST_HEAD((&block->nmb_packetlist)); + InitializeListHead((&block->nmb_packetlist)); } /* Give interrupt handling priority over timers. */ diff --git a/sys/compat/ndis/kern_windrv.c b/sys/compat/ndis/kern_windrv.c index 90c977b..9202911 100644 --- a/sys/compat/ndis/kern_windrv.c +++ b/sys/compat/ndis/kern_windrv.c @@ -137,8 +137,8 @@ windrv_libfini(void) } mtx_unlock(&drvdb_mtx); - free(fake_pci_driver.dro_drivername.us_buf, M_DEVBUF); - free(fake_pccard_driver.dro_drivername.us_buf, M_DEVBUF); + RtlFreeUnicodeString(&fake_pci_driver.dro_drivername); + RtlFreeUnicodeString(&fake_pccard_driver.dro_drivername); mtx_destroy(&drvdb_mtx); @@ -161,16 +161,16 @@ windrv_lookup(img, name) { struct drvdb_ent *d; unicode_string us; + ansi_string as; bzero((char *)&us, sizeof(us)); /* Damn unicode. */ if (name != NULL) { - us.us_len = strlen(name) * 2; - us.us_maxlen = strlen(name) * 2; - us.us_buf = NULL; - ndis_ascii_to_unicode(name, &us.us_buf); + RtlInitAnsiString(&as, name); + if (RtlAnsiStringToUnicodeString(&us, &as, TRUE)) + return(NULL); } mtx_lock(&drvdb_mtx); @@ -187,7 +187,7 @@ windrv_lookup(img, name) mtx_unlock(&drvdb_mtx); if (name != NULL) - ExFreePool(us.us_buf); + RtlFreeUnicodeString(&us); return(NULL); } @@ -230,7 +230,7 @@ windrv_unload(mod, img, len) driver_object *drv; device_object *d, *pdo; device_t dev; - list_entry *e, *c; + list_entry *e; drv = windrv_lookup(img, NULL); @@ -267,7 +267,7 @@ windrv_unload(mod, img, len) mtx_lock(&drvdb_mtx); } } - + STAILQ_FOREACH(db, &drvdb_head, link) { if (db->windrv_object->dro_driverstart == (void *)img) { r = db; @@ -280,23 +280,23 @@ windrv_unload(mod, img, len) if (r == NULL) return (ENOENT); + if (drv == NULL) + return(ENOENT); + /* * Destroy any custom extensions that may have been added. */ drv = r->windrv_object; - e = drv->dro_driverext->dre_usrext.nle_flink; - while (e != &drv->dro_driverext->dre_usrext) { - c = e->nle_flink; - REMOVE_LIST_ENTRY(e); + while (!IsListEmpty(&drv->dro_driverext->dre_usrext)) { + e = RemoveHeadList(&drv->dro_driverext->dre_usrext); ExFreePool(e); - e = c; } /* Free the driver extension */ free(drv->dro_driverext, M_DEVBUF); /* Free the driver name */ - free(drv->dro_drivername.us_buf, M_DEVBUF); + RtlFreeUnicodeString(&drv->dro_drivername); /* Free driver object */ free(drv, M_DEVBUF); @@ -330,6 +330,7 @@ windrv_load(mod, img, len, bustype, devlist, regvals) struct driver_object *drv; int status; uint32_t *ptr; + ansi_string as; /* * First step: try to relocate and dynalink the executable @@ -396,16 +397,17 @@ skipreloc: return(ENOMEM); } - INIT_LIST_HEAD((&drv->dro_driverext->dre_usrext)); + InitializeListHead((&drv->dro_driverext->dre_usrext)); drv->dro_driverstart = (void *)img; drv->dro_driversize = len; - drv->dro_drivername.us_len = strlen(DUMMY_REGISTRY_PATH) * 2; - drv->dro_drivername.us_maxlen = strlen(DUMMY_REGISTRY_PATH) * 2; - drv->dro_drivername.us_buf = NULL; - ndis_ascii_to_unicode(DUMMY_REGISTRY_PATH, - &drv->dro_drivername.us_buf); + RtlInitAnsiString(&as, DUMMY_REGISTRY_PATH); + if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) { + free(new, M_DEVBUF); + free(drv, M_DEVBUF); + return(ENOMEM); + } new->windrv_object = drv; new->windrv_regvals = regvals; @@ -417,7 +419,7 @@ skipreloc: status = MSCALL2(entry, drv, &drv->dro_drivername); if (status != STATUS_SUCCESS) { - free(drv->dro_drivername.us_buf, M_DEVBUF); + RtlFreeUnicodeString(&drv->dro_drivername); free(drv, M_DEVBUF); free(new, M_DEVBUF); return(ENODEV); @@ -517,15 +519,15 @@ windrv_bus_attach(drv, name) char *name; { struct drvdb_ent *new; + ansi_string as; new = malloc(sizeof(struct drvdb_ent), M_DEVBUF, M_NOWAIT|M_ZERO); if (new == NULL) return (ENOMEM); - drv->dro_drivername.us_len = strlen(name) * 2; - drv->dro_drivername.us_maxlen = strlen(name) * 2; - drv->dro_drivername.us_buf = NULL; - ndis_ascii_to_unicode(name, &drv->dro_drivername.us_buf); + RtlInitAnsiString(&as, name); + if (RtlAnsiStringToUnicodeString(&drv->dro_drivername, &as, TRUE)) + return(ENOMEM); /* * Set up a fake image pointer to avoid false matches @@ -674,7 +676,7 @@ ctxsw_utow(void) * hasn't, we need to do it right now or else things will * explode. */ - + if (t->tid_self != t) x86_newldt(NULL); diff --git a/sys/compat/ndis/ndis_var.h b/sys/compat/ndis/ndis_var.h index 742fae2..4fd7106 100644 --- a/sys/compat/ndis/ndis_var.h +++ b/sys/compat/ndis/ndis_var.h @@ -268,6 +268,31 @@ typedef uint8_t ndis_kirql; #define OID_PNP_WAKE_UP_PATTERN_LIST 0xFD010105 #define OID_PNP_ENABLE_WAKE_UP 0xFD010106 +/* + * These are the possible power states for + * OID_PNP_SET_POWER and OID_PNP_QUERY_POWER. + */ +#define NDIS_POWERSTATE_UNSPEC 0 +#define NDIS_POWERSTATE_D0 1 +#define NDIS_POWERSTATE_D1 2 +#define NDIS_POWERSTATE_D2 3 +#define NDIS_POWERSTATE_D3 4 + +/* + * These are used with the MiniportPnpEventNotify() method. + */ + +#define NDIS_POWERPROFILE_BATTERY 0 +#define NDIS_POWERPROFILE_ACONLINE 1 + +#define NDIS_PNP_EVENT_QUERY_REMOVED 0 +#define NDIS_PNP_EVENT_REMOVED 1 +#define NDIS_PNP_EVENT_SURPRISE_REMOVED 2 +#define NDIS_PNP_EVENT_QUERY_STOPPED 3 +#define NDIS_PNP_EVENT_STOPPED 4 +#define NDIS_PNP_EVENT_PROFILECHANGED 5 + + /* PnP/PM Statistics (Optional). */ #define OID_PNP_WAKE_UP_OK 0xFD020200 #define OID_PNP_WAKE_UP_ERROR 0xFD020201 @@ -310,6 +335,8 @@ typedef uint8_t ndis_kirql; #define OID_802_11_REMOVE_KEY 0x0D01011E #define OID_802_11_ASSOCIATION_INFORMATION 0x0D01011F #define OID_802_11_TEST 0x0D010120 +#define OID_802_11_CAPABILITY 0x0D010122 +#define OID_802_11_PMKID 0x0D010123 /* structures/definitions for 802.11 */ #define NDIS_80211_NETTYPE_11FH 0x00000000 @@ -390,6 +417,8 @@ typedef struct ndis_80211_wep ndis_80211_wep; #define NDIS_80211_AUTHMODE_WPA 0x00000003 #define NDIS_80211_AUTHMODE_WPAPSK 0x00000004 #define NDIS_80211_AUTHMODE_WPANONE 0x00000005 +#define NDIS_80211_AUTHMODE_WPA2 0x00000006 +#define NDIS_80211_AUTHMODE_WPA2PSK 0x00000007 typedef uint8_t ndis_80211_rates[8]; typedef uint8_t ndis_80211_rates_ex[16]; @@ -483,6 +512,7 @@ typedef uint32_t ndis_80211_antenna; #define NDIS_80211_RELOADDEFAULT_WEP 0x00000000 #define NDIS_80211_STATUSTYPE_AUTH 0x00000000 +#define NDIS_80211_STATUSTYPE_PMKIDLIST 0x00000001 struct ndis_80211_status_indication { uint32_t nsi_type; @@ -490,6 +520,11 @@ struct ndis_80211_status_indication { typedef struct ndis_80211_status_indication ndis_80211_status_indication; +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + struct ndis_80211_auth_request { uint32_t nar_len; ndis_80211_macaddr nar_bssid; @@ -503,8 +538,9 @@ struct ndis_80211_key { uint32_t nk_keyidx; uint32_t nk_keylen; ndis_80211_macaddr nk_bssid; + uint8_t nk_pad[6]; uint64_t nk_keyrsc; - uint8_t nk_keydata[256]; + uint8_t nk_keydata[32]; }; typedef struct ndis_80211_key ndis_80211_key; @@ -573,6 +609,61 @@ struct ndis_80211_test { typedef struct ndis_80211_test ndis_80211_test; +struct ndis_80211_auth_encrypt { + uint32_t ne_authmode; + uint32_t ne_cryptstat; +}; + +typedef struct ndis_80211_auth_encrypt ndis_80211_auth_encrypt; + +struct ndis_80211_caps { + uint32_t nc_len; + uint32_t nc_ver; + uint32_t nc_numpmkids; + ndis_80211_auth_encrypt nc_authencs[1]; +}; + +typedef struct ndis_80211_caps ndis_80211_caps; + +struct ndis_80211_bssidinfo { + ndis_80211_macaddr nb_bssid; + uint8_t nb_pmkid[16]; +}; + +typedef struct ndis_80211_bssidinfo ndis_80211_bssidinfo; + +struct ndis_80211_pmkid { + uint32_t np_len; + uint32_t np_bssidcnt; + ndis_80211_bssidinfo np_bssidinfo[1]; +}; + +typedef struct ndis_80211_pmkid ndis_80211_pmkid; + +struct ndis_80211_pmkid_cand { + ndis_80211_macaddr npc_bssid; + uint32_t npc_flags; +}; + +typedef struct ndis_80211_pmkid_cand ndis_80211_pmkid_cand; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED (0x01) + +struct ndis_80211_pmkid_candidate_list { + uint32_t npcl_version; + uint32_t npcl_numcandidates; + ndis_80211_pmkid_cand npcl_candidatelist[1]; +}; + +typedef struct ndis_80211_pmkid_candidate_list ndis_80211_pmkid_candidate_list; + +struct ndis_80211_enc_indication { + uint32_t nei_statustype; + ndis_80211_pmkid_candidate_list nei_pmkidlist; +}; + +typedef struct ndis_80211_enc_indication ndis_80211_enc_indication; + /* TCP OIDs. */ #define OID_TCP_TASK_OFFLOAD 0xFC010201 @@ -682,6 +773,7 @@ typedef struct ndis_task_ipsec ndis_task_ipsec; * all attributes. */ +#define NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT 0x00000001 #define NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT 0x00000002 #define NDIS_ATTRIBUTE_IGNORE_TOKEN_RING_ERRORS 0x00000004 #define NDIS_ATTRIBUTE_BUS_MASTER 0x00000008 @@ -805,8 +897,20 @@ struct ndis_config_parm { } ncp_parmdata; }; +/* + * Not part of Windows NDIS spec; we uses this to keep a + * list of ndis_config_parm structures that we've allocated. + */ + typedef struct ndis_config_parm ndis_config_parm; +struct ndis_parmlist_entry { + list_entry np_list; + ndis_config_parm np_parm; +}; + +typedef struct ndis_parmlist_entry ndis_parmlist_entry; + #ifdef notdef struct ndis_list_entry { struct ndis_list_entry *nle_flink; @@ -952,16 +1056,16 @@ typedef struct ndis_request ndis_request; * Filler, not used. */ struct ndis_miniport_interrupt { - void *ni_introbj; + kinterrupt *ni_introbj; ndis_kspin_lock ni_dpccountlock; void *ni_rsvd; void *ni_isrfunc; void *ni_dpcfunc; - struct ndis_kdpc ni_dpc; + kdpc ni_dpc; ndis_miniport_block *ni_block; uint8_t ni_dpccnt; uint8_t ni_filler1; - struct ndis_kevent ni_dpcsdoneevent; + struct nt_kevent ni_dpcevt; uint8_t ni_shared; uint8_t ni_isrreq; }; @@ -1506,8 +1610,7 @@ struct ndis_miniport_block { * End of windows-specific portion of miniport block. Everything * below is BSD-specific. */ - uint8_t nmb_dummybuf[128]; - ndis_config_parm nmb_replyparm; + list_entry nmb_parmlist; ndis_resource_list *nmb_rlist; ndis_status nmb_getstat; ndis_status nmb_setstat; @@ -1600,8 +1703,6 @@ extern image_patch_table ndis_functbl[]; __BEGIN_DECLS extern int ndis_libinit(void); extern int ndis_libfini(void); -extern int ndis_ascii_to_unicode(char *, uint16_t **); -extern int ndis_unicode_to_ascii(uint16_t *, int, char **); extern int ndis_load_driver(vm_offset_t, void *); extern int ndis_unload_driver(void *); extern int ndis_mtop(struct mbuf *, ndis_packet **); @@ -1629,8 +1730,6 @@ extern int ndis_destroy_dma(void *); extern int ndis_create_sysctls(void *); extern int ndis_add_sysctl(void *, char *, char *, char *, int); extern int ndis_flush_sysctls(void *); -extern int ndis_thsuspend(struct proc *, struct mtx *, int); -extern void ndis_thresume(struct proc *); extern int ndis_strcasecmp(const char *, const char *); extern int ndis_strncasecmp(const char *, const char *, size_t); diff --git a/sys/compat/ndis/ntoskrnl_var.h b/sys/compat/ndis/ntoskrnl_var.h index 622a387..7a1f3af 100644 --- a/sys/compat/ndis/ntoskrnl_var.h +++ b/sys/compat/ndis/ntoskrnl_var.h @@ -48,6 +48,14 @@ struct unicode_string { typedef struct unicode_string unicode_string; +struct ansi_string { + uint16_t as_len; + uint16_t as_maxlen; + char *as_buf; +}; + +typedef struct ansi_string ansi_string; + /* * Windows memory descriptor list. In Windows, it's possible for * buffers to be passed between user and kernel contexts without @@ -197,43 +205,54 @@ struct list_entry { typedef struct list_entry list_entry; -#define INIT_LIST_HEAD(l) \ +#define InitializeListHead(l) \ (l)->nle_flink = (l)->nle_blink = (l) -#define REMOVE_LIST_ENTRY(e) \ +#define IsListEmpty(h) \ + ((h)->nle_flink == (h)) + +#define RemoveEntryList(e) \ do { \ list_entry *b; \ list_entry *f; \ \ - f = e->nle_flink; \ - b = e->nle_blink; \ + f = (e)->nle_flink; \ + b = (e)->nle_blink; \ b->nle_flink = f; \ f->nle_blink = b; \ } while (0) -#define REMOVE_LIST_HEAD(l) \ - do { \ - list_entry *f; \ - list_entry *e; \ - \ - e = l->nle_flink; \ - f = e->nle_flink; \ - l->nle_flink = f; \ - f->nle_blink = l; \ - } while (0) +/* These two have to be inlined since they return things. */ -#define REMOVE_LIST_TAIL(l) \ - do { \ - list_entry *b; \ - list_entry *e; \ - \ - e = l->nle_blink; \ - b = e->nle_blink; \ - l->nle_blink = b; \ - b->nle_flink = l; \ - } while (0) +static __inline__ list_entry * +RemoveHeadList(list_entry *l) +{ + list_entry *f; + list_entry *e; + + e = l->nle_flink; + f = e->nle_flink; + l->nle_flink = f; + f->nle_blink = l; + + return (e); +} + +static __inline__ list_entry * +RemoveTailList(list_entry *l) +{ + list_entry *b; + list_entry *e; + + e = l->nle_blink; + b = e->nle_blink; + l->nle_blink = b; + b->nle_flink = l; -#define INSERT_LIST_TAIL(l, e) \ + return (e); +} + +#define InsertTailList(l, e) \ do { \ list_entry *b; \ \ @@ -244,7 +263,7 @@ typedef struct list_entry list_entry; l->nle_blink = (e); \ } while (0) -#define INSERT_LIST_HEAD(l, e) \ +#define InsertHeadList(l, e) \ do { \ list_entry *f; \ \ @@ -263,12 +282,24 @@ struct nt_dispatch_header { uint8_t dh_abs; uint8_t dh_size; uint8_t dh_inserted; - uint32_t dh_sigstate; + int32_t dh_sigstate; list_entry dh_waitlisthead; }; typedef struct nt_dispatch_header nt_dispatch_header; +/* Dispatcher object types */ + +#define DISP_TYPE_NOTIFICATION_EVENT 0 /* KEVENT */ +#define DISP_TYPE_SYNCHRONIZATION_EVENT 1 /* KEVENT */ +#define DISP_TYPE_MUTANT 2 /* KMUTANT/KMUTEX */ +#define DISP_TYPE_PROCESS 3 /* KPROCESS */ +#define DISP_TYPE_QUEUE 4 /* KQUEUE */ +#define DISP_TYPE_SEMAPHORE 5 /* KSEMAPHORE */ +#define DISP_TYPE_THREAD 6 /* KTHREAD */ +#define DISP_TYPE_NOTIFICATION_TIMER 8 /* KTIMER */ +#define DISP_TYPE_SYNCHRONIZATION_TIMER 9 /* KTIMER */ + #define OTYPE_EVENT 0 #define OTYPE_MUTEX 1 #define OTYPE_THREAD 2 @@ -334,14 +365,14 @@ struct ktimer { uint64_t k_duetime; union { list_entry k_timerlistentry; - struct callout_handle k_handle; + struct callout *k_callout; } u; void *k_dpc; uint32_t k_period; }; #define k_timerlistentry u.k_timerlistentry -#define k_handle u.k_handle +#define k_callout u.k_callout typedef struct ktimer ktimer; @@ -389,18 +420,12 @@ typedef struct kdpc kdpc; */ struct kmutant { nt_dispatch_header km_header; - union { - list_entry km_listentry; - uint32_t km_acquirecnt; - } u; + list_entry km_listentry; void *km_ownerthread; uint8_t km_abandoned; uint8_t km_apcdisable; }; -#define km_listentry u.km_listentry -#define km_acquirecnt u.km_acquirecnt - typedef struct kmutant kmutant; #define LOOKASIDE_DEPTH 256 @@ -485,18 +510,28 @@ struct wait_block { void *wb_kthread; nt_dispatch_header *wb_object; struct wait_block *wb_next; +#ifdef notdef uint16_t wb_waitkey; uint16_t wb_waittype; +#endif + uint8_t wb_waitkey; + uint8_t wb_waittype; + uint8_t wb_awakened; + uint8_t wb_oldpri; }; typedef struct wait_block wait_block; +#define wb_ext wb_kthread + #define THREAD_WAIT_OBJECTS 3 #define MAX_WAIT_OBJECTS 64 #define WAITTYPE_ALL 0 #define WAITTYPE_ANY 1 +#define WAITKEY_VALID 0x8000 + struct thread_context { void *tc_thrctx; void *tc_thrfunc; @@ -533,6 +568,23 @@ struct custom_extension { typedef struct custom_extension custom_extension; /* + * The KINTERRUPT structure in Windows is opaque to drivers. + * We define our own custom version with things we need. + */ + +struct kinterrupt { + device_t ki_dev; + void *ki_cookie; + struct resource *ki_irq; + kspin_lock ki_lock_priv; + kspin_lock *ki_lock; + void *ki_svcfunc; + void *ki_svcctx; +}; + +typedef struct kinterrupt kinterrupt; + +/* * In Windows, there are Physical Device Objects (PDOs) and * Functional Device Objects (FDOs). Physical Device Objects are * created and maintained by bus drivers. For example, the PCI @@ -1199,7 +1251,7 @@ typedef struct work_queue_item work_queue_item; do { \ (w)->wqi_func = (func); \ (w)->wqi_ctx = (ctx); \ - INIT_LIST_HEAD(&((w)->wqi_entry)); \ + InitializeListHead(&((w)->wqi_entry)); \ } while (0); \ @@ -1251,6 +1303,16 @@ extern void ctxsw_wtou(void); extern int ntoskrnl_libinit(void); extern int ntoskrnl_libfini(void); + +extern uint32_t RtlUnicodeStringToAnsiString(ansi_string *, + unicode_string *, uint8_t); +extern uint32_t RtlAnsiStringToUnicodeString(unicode_string *, + ansi_string *, uint8_t); +extern void RtlInitAnsiString(ansi_string *, char *); +extern void RtlInitUnicodeString(unicode_string *, + uint16_t *); +extern void RtlFreeUnicodeString(unicode_string *); +extern void RtlFreeAnsiString(ansi_string *); extern void KeInitializeDpc(kdpc *, void *, void *); extern uint8_t KeInsertQueueDpc(kdpc *, void *, void *); extern uint8_t KeRemoveQueueDpc(kdpc *); @@ -1280,10 +1342,16 @@ extern void KeAcquireSpinLockAtDpcLevel(kspin_lock *); extern void KeReleaseSpinLockFromDpcLevel(kspin_lock *); #endif extern void KeInitializeSpinLock(kspin_lock *); +extern uint8_t KeSynchronizeExecution(kinterrupt *, void *, void *); extern uintptr_t InterlockedExchange(volatile uint32_t *, uintptr_t); extern void *ExAllocatePoolWithTag(uint32_t, size_t, uint32_t); extern void ExFreePool(void *); +extern uint32_t IoConnectInterrupt(kinterrupt **, void *, void *, + kspin_lock *, uint32_t, uint8_t, uint8_t, uint8_t, uint8_t, + uint32_t, uint8_t); +extern void MmBuildMdlForNonPagedPool(mdl *); +extern void IoDisconnectInterrupt(kinterrupt *); extern uint32_t IoAllocateDriverObjectExtension(driver_object *, void *, uint32_t, void **); extern void *IoGetDriverObjectExtension(driver_object *, void *); diff --git a/sys/compat/ndis/subr_hal.c b/sys/compat/ndis/subr_hal.c index ca8f009..fa376bf 100644 --- a/sys/compat/ndis/subr_hal.c +++ b/sys/compat/ndis/subr_hal.c @@ -80,12 +80,18 @@ static void READ_PORT_BUFFER_UCHAR(uint8_t *, static uint64_t KeQueryPerformanceCounter(uint64_t *); static void dummy (void); -extern struct mtx_pool *ndis_mtxpool; +#define NDIS_MAXCPUS 64 +static struct mtx disp_lock[NDIS_MAXCPUS]; int hal_libinit() { image_patch_table *patch; + int i; + + for (i = 0; i < NDIS_MAXCPUS; i++) + mtx_init(&disp_lock[i], "HAL preemption lock", + "HAL lock", MTX_RECURSE|MTX_DEF); patch = hal_functbl; while (patch->ipt_func != NULL) { @@ -95,6 +101,7 @@ hal_libinit() patch++; } + return(0); } @@ -102,6 +109,10 @@ int hal_libfini() { image_patch_table *patch; + int i; + + for (i = 0; i < NDIS_MAXCPUS; i++) + mtx_destroy(&disp_lock[i]); patch = hal_functbl; while (patch->ipt_func != NULL) { @@ -285,6 +296,72 @@ READ_PORT_BUFFER_UCHAR(port, val, cnt) * KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If * we detect someone trying to acquire a spinlock from DEVICE_LEVEL * or HIGH_LEVEL, we panic. + * + * Alternate sleep-lock-based spinlock implementation + * -------------------------------------------------- + * + * The earlier spinlock implementation was arguably a bit of a hack + * and presented several problems. It was basically designed to provide + * the functionality of spinlocks without incurring the wrath of + * WITNESS. We could get away with using both our spinlock implementation + * and FreeBSD sleep locks at the same time, but if WITNESS knew what + * we were really up to, it would have spanked us rather severely. + * + * There's another method we can use based entirely on sleep locks. + * First, it's important to realize that everything we're locking + * resides inside Project Evil itself: any critical data being locked + * by drivers belongs to the drivers, and should not be referenced + * by any other OS code outside of the NDISulator. The priority-based + * locking scheme has system-wide effects, just like real spinlocks + * (blocking preemption affects the whole CPU), but since we keep all + * our critical data private, we can use a simpler mechanism that + * affects only code/threads directly related to Project Evil. + * + * The idea is to create a sleep lock mutex for each CPU in the system. + * When a CPU running in the NDISulator wants to acquire a spinlock, it + * does the following: + * - Pin ourselves to the current CPU + * - Acquire the mutex for the current CPU + * - Spin on the spinlock variable using atomic test and set, just like + * a real spinlock. + * - Once we have the lock, we execute our critical code + * + * To give up the lock, we do: + * - Clear the spinlock variable with an atomic op + * - Release the per-CPU mutex + * - Unpin ourselves from the current CPU. + * + * On a uniprocessor system, this means all threads that access protected + * data are serialized through the per-CPU mutex. After one thread + * acquires the 'spinlock,' any other thread that uses a spinlock on the + * current CPU will block on the per-CPU mutex, which has the same general + * effect of blocking pre-emption, but _only_ for those threads that are + * running NDISulator code. + * + * On a multiprocessor system, threads on different CPUs all block on + * their respective per-CPU mutex, and the atomic test/set operation + * on the spinlock variable provides inter-CPU synchronization, though + * only for threads running NDISulator code. + * + * This method solves an important problem. In Windows, you're allowed + * to do an ExAllocatePoolWithTag() with a spinlock held, provided you + * allocate from NonPagedPool. This implies an atomic heap allocation + * that will not cause the current thread to sleep. (You can't sleep + * while holding real spinlock: clowns will eat you.) But in FreeBSD, + * malloc(9) _always_ triggers the acquisition of a sleep lock, even + * when you use M_NOWAIT. This is not a problem for FreeBSD native + * code: you're allowed to sleep in things like interrupt threads. But + * it is a problem with the old priority-based spinlock implementation: + * even though we get away with it most of the time, we really can't + * do a malloc(9) after doing a KeAcquireSpinLock() or KeRaiseIrql(). + * With the new implementation, it's not a problem: you're allowed to + * acquire more than one sleep lock (as long as you avoid lock order + * reversals). + * + * The one drawback to this approach is that now we have a lot of + * contention on one per-CPU mutex within the NDISulator code. Whether + * or not this is preferable to the expected Windows spinlock behavior + * of blocking pre-emption is debatable. */ uint8_t @@ -314,10 +391,10 @@ KfReleaseSpinLock(lock, newirql) return; } - uint8_t +uint8_t KeGetCurrentIrql() { - if (AT_DISPATCH_LEVEL(curthread)) + if (mtx_owned(&disp_lock[curthread->td_oncpu])) return(DISPATCH_LEVEL); return(PASSIVE_LEVEL); } @@ -338,19 +415,14 @@ KfRaiseIrql(irql) { uint8_t oldirql; - if (irql < KeGetCurrentIrql()) + oldirql = KeGetCurrentIrql(); + if (irql < oldirql) panic("IRQL_NOT_LESS_THAN"); - if (KeGetCurrentIrql() == DISPATCH_LEVEL) - return(DISPATCH_LEVEL); - - mtx_lock_spin(&sched_lock); - oldirql = curthread->td_base_pri; - sched_prio(curthread, PI_REALTIME); -#if __FreeBSD_version < 600000 - curthread->td_base_pri = PI_REALTIME; -#endif - mtx_unlock_spin(&sched_lock); + if (oldirql != DISPATCH_LEVEL) { + sched_pin(); + mtx_lock(&disp_lock[curthread->td_oncpu]); + } return(oldirql); } @@ -365,12 +437,8 @@ KfLowerIrql(oldirql) if (KeGetCurrentIrql() != DISPATCH_LEVEL) panic("IRQL_NOT_GREATER_THAN"); - mtx_lock_spin(&sched_lock); -#if __FreeBSD_version < 600000 - curthread->td_base_pri = oldirql; -#endif - sched_prio(curthread, oldirql); - mtx_unlock_spin(&sched_lock); + mtx_unlock(&disp_lock[curthread->td_oncpu]); + sched_unpin(); return; } diff --git a/sys/compat/ndis/subr_ndis.c b/sys/compat/ndis/subr_ndis.c index 5dfe8e5..7813515 100644 --- a/sys/compat/ndis/subr_ndis.c +++ b/sys/compat/ndis/subr_ndis.c @@ -104,6 +104,13 @@ __FBSDID("$FreeBSD$"); #include <compat/ndis/ndis_var.h> #include <dev/if_ndis/if_ndisvar.h> +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> +#include <vm/uma.h> +#include <vm/vm_kern.h> +#include <vm/vm_map.h> + static char ndis_filepath[MAXPATHLEN]; extern struct nd_head ndis_devhead; @@ -124,17 +131,17 @@ static ndis_status NdisMSetAttributesEx(ndis_handle, ndis_handle, static void NdisOpenConfiguration(ndis_status *, ndis_handle *, ndis_handle); static void NdisOpenConfigurationKeyByIndex(ndis_status *, - ndis_handle, uint32_t, ndis_unicode_string *, ndis_handle *); + ndis_handle, uint32_t, unicode_string *, ndis_handle *); static void NdisOpenConfigurationKeyByName(ndis_status *, - ndis_handle, ndis_unicode_string *, ndis_handle *); + ndis_handle, unicode_string *, ndis_handle *); static ndis_status ndis_encode_parm(ndis_miniport_block *, struct sysctl_oid *, ndis_parm_type, ndis_config_parm **); static ndis_status ndis_decode_parm(ndis_miniport_block *, ndis_config_parm *, char *); static void NdisReadConfiguration(ndis_status *, ndis_config_parm **, - ndis_handle, ndis_unicode_string *, ndis_parm_type); + ndis_handle, unicode_string *, ndis_parm_type); static void NdisWriteConfiguration(ndis_status *, ndis_handle, - ndis_unicode_string *, ndis_config_parm *); + unicode_string *, ndis_config_parm *); static void NdisCloseConfiguration(ndis_handle); static void NdisAllocateSpinLock(ndis_spin_lock *); static void NdisFreeSpinLock(ndis_spin_lock *); @@ -213,11 +220,10 @@ static void NdisInitializeEvent(ndis_event *); static void NdisSetEvent(ndis_event *); static void NdisResetEvent(ndis_event *); static uint8_t NdisWaitEvent(ndis_event *, uint32_t); -static ndis_status NdisUnicodeStringToAnsiString(ndis_ansi_string *, - ndis_unicode_string *); +static ndis_status NdisUnicodeStringToAnsiString(ansi_string *, + unicode_string *); static ndis_status - NdisAnsiStringToUnicodeString(ndis_unicode_string *, - ndis_ansi_string *); + NdisAnsiStringToUnicodeString(unicode_string *, ansi_string *); static ndis_status NdisMPciAssignResources(ndis_handle, uint32_t, ndis_resource_list **); static ndis_status NdisMRegisterInterrupt(ndis_miniport_interrupt *, @@ -248,11 +254,10 @@ static uint8_t void *, void *); static void NdisGetCurrentSystemTime(uint64_t *); static void NdisGetSystemUpTime(uint32_t *); -static void NdisInitializeString(ndis_unicode_string *, char *); -static void NdisInitAnsiString(ndis_ansi_string *, char *); -static void NdisInitUnicodeString(ndis_unicode_string *, - uint16_t *); -static void NdisFreeString(ndis_unicode_string *); +static void NdisInitializeString(unicode_string *, char *); +static void NdisInitAnsiString(ansi_string *, char *); +static void NdisInitUnicodeString(unicode_string *, uint16_t *); +static void NdisFreeString(unicode_string *); static ndis_status NdisMRemoveMiniport(ndis_handle *); static void NdisTerminateWrapper(ndis_handle, void *); static void NdisMGetDeviceProperty(ndis_handle, device_object **, @@ -264,7 +269,7 @@ static void NdisGetFirstBufferFromPacketSafe(ndis_packet *, ndis_buffer **, void **, uint32_t *, uint32_t *, uint32_t); static int ndis_find_sym(linker_file_t, char *, char *, caddr_t *); static void NdisOpenFile(ndis_status *, ndis_handle *, uint32_t *, - ndis_unicode_string *, ndis_physaddr); + unicode_string *, ndis_physaddr); static void NdisMapFile(ndis_status *, void **, ndis_handle); static void NdisUnmapFile(ndis_handle); static void NdisCloseFile(ndis_handle); @@ -272,18 +277,19 @@ static uint8_t NdisSystemProcessorCount(void); static void NdisMIndicateStatusComplete(ndis_handle); static void NdisMIndicateStatus(ndis_handle, ndis_status, void *, uint32_t); +static void ndis_intr(void *); +static void ndis_intrhand(kdpc *, ndis_miniport_interrupt *, void *, void *); static funcptr ndis_findwrap(funcptr); static void NdisCopyFromPacketToPacket(ndis_packet *, uint32_t, uint32_t, ndis_packet *, uint32_t, uint32_t *); static void NdisCopyFromPacketToPacketSafe(ndis_packet *, uint32_t, uint32_t, ndis_packet *, uint32_t, uint32_t *, uint32_t); static ndis_status NdisMRegisterDevice(ndis_handle, - ndis_unicode_string *, ndis_unicode_string *, driver_dispatch **, + unicode_string *, unicode_string *, driver_dispatch **, void **, ndis_handle *); static ndis_status NdisMDeregisterDevice(ndis_handle); static ndis_status - NdisMQueryAdapterInstanceName(ndis_unicode_string *, - ndis_handle); + NdisMQueryAdapterInstanceName(unicode_string *, ndis_handle); static void NdisMRegisterUnloadHandler(ndis_handle, void *); static void dummy(void); @@ -346,57 +352,6 @@ ndis_findwrap(func) } /* - * NDIS deals with strings in unicode format, so we have - * do deal with them that way too. For now, we only handle - * conversion between unicode and ASCII since that's all - * that device drivers care about. - */ - -int -ndis_ascii_to_unicode(ascii, unicode) - char *ascii; - uint16_t **unicode; -{ - uint16_t *ustr; - int i; - - if (*unicode == NULL) - *unicode = malloc(strlen(ascii) * 2, M_DEVBUF, M_NOWAIT); - - if (*unicode == NULL) - return(ENOMEM); - ustr = *unicode; - for (i = 0; i < strlen(ascii); i++) { - *ustr = (uint16_t)ascii[i]; - ustr++; - } - - return(0); -} - -int -ndis_unicode_to_ascii(unicode, ulen, ascii) - uint16_t *unicode; - int ulen; - char **ascii; -{ - uint8_t *astr; - int i; - - if (*ascii == NULL) - *ascii = malloc((ulen / 2) + 1, M_DEVBUF, M_NOWAIT|M_ZERO); - if (*ascii == NULL) - return(ENOMEM); - astr = *ascii; - for (i = 0; i < ulen / 2; i++) { - *astr = (uint8_t)unicode[i]; - astr++; - } - - return(0); -} - -/* * This routine does the messy Windows Driver Model device attachment * stuff on behalf of NDIS drivers. We register our own AddDevice * routine here @@ -465,8 +420,10 @@ NdisMRegisterMiniport(handle, characteristics, len) if (IoAllocateDriverObjectExtension(drv, (void *)1, sizeof(ndis_miniport_characteristics), (void **)&ch) != - STATUS_SUCCESS) + STATUS_SUCCESS) { + printf("register error\n"); return(NDIS_STATUS_RESOURCES); + } bzero((char *)ch, sizeof(ndis_miniport_characteristics)); @@ -489,10 +446,10 @@ NdisAllocateMemoryWithTag(vaddr, len, tag) { void *mem; - mem = ExAllocatePoolWithTag(NonPagedPool, len, tag); - if (mem == NULL) + if (mem == NULL) { return(NDIS_STATUS_RESOURCES); + } *vaddr = mem; return(NDIS_STATUS_SUCCESS); @@ -568,7 +525,7 @@ static void NdisOpenConfigurationKeyByName(status, cfg, subkey, subhandle) ndis_status *status; ndis_handle cfg; - ndis_unicode_string *subkey; + unicode_string *subkey; ndis_handle *subhandle; { *subhandle = cfg; @@ -582,7 +539,7 @@ NdisOpenConfigurationKeyByIndex(status, cfg, idx, subkey, subhandle) ndis_status *status; ndis_handle cfg; uint32_t idx; - ndis_unicode_string *subkey; + unicode_string *subkey; ndis_handle *subhandle; { *status = NDIS_STATUS_FAILURE; @@ -597,36 +554,58 @@ ndis_encode_parm(block, oid, type, parm) ndis_parm_type type; ndis_config_parm **parm; { - uint16_t *unicode; - ndis_unicode_string *ustr; + ndis_config_parm *p; + ndis_parmlist_entry *np; + unicode_string *us; + ansi_string as; int base = 0; + uint32_t val; + char tmp[32]; - unicode = (uint16_t *)&block->nmb_dummybuf; + np = ExAllocatePoolWithTag(NonPagedPool, + sizeof(ndis_parmlist_entry), 0); + if (np == NULL) + return(NDIS_STATUS_RESOURCES); + InsertHeadList((&block->nmb_parmlist), (&np->np_list)); + *parm = p = &np->np_parm; switch(type) { case ndis_parm_string: - ndis_ascii_to_unicode((char *)oid->oid_arg1, &unicode); - (*parm)->ncp_type = ndis_parm_string; - ustr = &(*parm)->ncp_parmdata.ncp_stringdata; - ustr->us_len = strlen((char *)oid->oid_arg1) * 2; - ustr->us_buf = unicode; + /* See if this might be a number. */ + val = strtoul((char *)oid->oid_arg1, NULL, 10); + us = &p->ncp_parmdata.ncp_stringdata; + p->ncp_type = ndis_parm_string; + if (val) { + snprintf(tmp, 32, "%x", val); + RtlInitAnsiString(&as, tmp); + } else { + RtlInitAnsiString(&as, (char *)oid->oid_arg1); + } + + if (RtlAnsiStringToUnicodeString(us, &as, TRUE)) { + ExFreePool(np); + return(NDIS_STATUS_RESOURCES); + } break; case ndis_parm_int: if (strncmp((char *)oid->oid_arg1, "0x", 2) == 0) base = 16; else base = 10; - (*parm)->ncp_type = ndis_parm_int; - (*parm)->ncp_parmdata.ncp_intdata = + p->ncp_type = ndis_parm_int; + p->ncp_parmdata.ncp_intdata = strtol((char *)oid->oid_arg1, NULL, base); break; case ndis_parm_hexint: +#ifdef notdef if (strncmp((char *)oid->oid_arg1, "0x", 2) == 0) base = 16; else base = 10; - (*parm)->ncp_type = ndis_parm_hexint; - (*parm)->ncp_parmdata.ncp_intdata = +#endif + base = 16; + p->ncp_type = ndis_parm_hexint; + p->ncp_parmdata.ncp_intdata = strtoul((char *)oid->oid_arg1, NULL, base); break; default: @@ -689,15 +668,15 @@ NdisReadConfiguration(status, parm, cfg, key, type) ndis_status *status; ndis_config_parm **parm; ndis_handle cfg; - ndis_unicode_string *key; + unicode_string *key; ndis_parm_type type; { char *keystr = NULL; - uint16_t *unicode; ndis_miniport_block *block; struct ndis_softc *sc; struct sysctl_oid *oidp; struct sysctl_ctx_entry *e; + ansi_string as; block = (ndis_miniport_block *)cfg; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); @@ -707,10 +686,12 @@ NdisReadConfiguration(status, parm, cfg, key, type) return; } - ndis_unicode_to_ascii(key->us_buf, key->us_len, &keystr); - *parm = &block->nmb_replyparm; - bzero((char *)&block->nmb_replyparm, sizeof(ndis_config_parm)); - unicode = (uint16_t *)&block->nmb_dummybuf; + if (RtlUnicodeStringToAnsiString(&as, key, TRUE)) { + *status = NDIS_STATUS_RESOURCES; + return; + } + + keystr = as.as_buf; /* * See if registry key is already in a list of known keys @@ -724,12 +705,13 @@ NdisReadConfiguration(status, parm, cfg, key, type) oidp = e->entry; if (ndis_strcasecmp(oidp->oid_name, keystr) == 0) { if (strcmp((char *)oidp->oid_arg1, "UNSET") == 0) { - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; return; } + *status = ndis_encode_parm(block, oidp, type, parm); - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } } @@ -753,7 +735,7 @@ NdisReadConfiguration(status, parm, cfg, key, type) ndis_add_sysctl(sc, keystr, "(dynamic string key)", "UNSET", CTLFLAG_RW); - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_FAILURE; return; @@ -765,15 +747,16 @@ ndis_decode_parm(block, parm, val) ndis_config_parm *parm; char *val; { - ndis_unicode_string *ustr; - char *astr = NULL; + unicode_string *ustr; + ansi_string as; switch(parm->ncp_type) { case ndis_parm_string: ustr = &parm->ncp_parmdata.ncp_stringdata; - ndis_unicode_to_ascii(ustr->us_buf, ustr->us_len, &astr); - bcopy(astr, val, 254); - free(astr, M_DEVBUF); + if (RtlUnicodeStringToAnsiString(&as, ustr, TRUE)) + return(NDIS_STATUS_RESOURCES); + bcopy(as.as_buf, val, as.as_len); + RtlFreeAnsiString(&as); break; case ndis_parm_int: sprintf(val, "%d", parm->ncp_parmdata.ncp_intdata); @@ -792,9 +775,10 @@ static void NdisWriteConfiguration(status, cfg, key, parm) ndis_status *status; ndis_handle cfg; - ndis_unicode_string *key; + unicode_string *key; ndis_config_parm *parm; { + ansi_string as; char *keystr = NULL; ndis_miniport_block *block; struct ndis_softc *sc; @@ -805,13 +789,18 @@ NdisWriteConfiguration(status, cfg, key, parm) block = (ndis_miniport_block *)cfg; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - ndis_unicode_to_ascii(key->us_buf, key->us_len, &keystr); + if (RtlUnicodeStringToAnsiString(&as, key, TRUE)) { + *status = NDIS_STATUS_RESOURCES; + return; + } + + keystr = as.as_buf; /* Decode the parameter into a string. */ bzero(val, sizeof(val)); *status = ndis_decode_parm(block, parm, val); if (*status != NDIS_STATUS_SUCCESS) { - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } @@ -826,7 +815,7 @@ NdisWriteConfiguration(status, cfg, key, parm) if (ndis_strcasecmp(oidp->oid_name, keystr) == 0) { /* Found it, set the value. */ strcpy((char *)oidp->oid_arg1, val); - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); return; } } @@ -835,7 +824,7 @@ NdisWriteConfiguration(status, cfg, key, parm) ndis_add_sysctl(sc, keystr, "(dynamically set key)", val, CTLFLAG_RW); - free(keystr, M_DEVBUF); + RtlFreeAnsiString(&as); *status = NDIS_STATUS_SUCCESS; return; } @@ -844,6 +833,22 @@ static void NdisCloseConfiguration(cfg) ndis_handle cfg; { + list_entry *e; + ndis_parmlist_entry *pe; + ndis_miniport_block *block; + ndis_config_parm *p; + + block = (ndis_miniport_block *)cfg; + + while (!IsListEmpty(&block->nmb_parmlist)) { + e = RemoveHeadList(&block->nmb_parmlist); + pe = CONTAINING_RECORD(e, ndis_parmlist_entry, np_list); + p = &pe->np_parm; + if (p->ncp_type == ndis_parm_string) + RtlFreeUnicodeString(&p->ncp_parmdata.ncp_stringdata); + ExFreePool(e); + } + return; } @@ -1038,6 +1043,7 @@ NdisWritePciSlotInformation(adapter, slot, offset, buf, len) * The errorlog routine uses a variable argument list, so we * have to declare it this way. */ + #define ERRMSGLEN 512 static void NdisWriteErrorLogEntry(ndis_handle adapter, ndis_error_code code, @@ -1046,13 +1052,15 @@ NdisWriteErrorLogEntry(ndis_handle adapter, ndis_error_code code, ndis_miniport_block *block; va_list ap; int i, error; - char *str = NULL, *ustr = NULL; + char *str = NULL; uint16_t flags; char msgbuf[ERRMSGLEN]; device_t dev; driver_object *drv; struct ndis_softc *sc; struct ifnet *ifp; + unicode_string us; + ansi_string as; block = (ndis_miniport_block *)adapter; dev = block->nmb_physdeviceobj->do_devext; @@ -1064,10 +1072,10 @@ NdisWriteErrorLogEntry(ndis_handle adapter, ndis_error_code code, code, &str, &i, &flags); if (error == 0 && flags & MESSAGE_RESOURCE_UNICODE && ifp->if_flags & IFF_DEBUG) { - ustr = msgbuf; - ndis_unicode_to_ascii((uint16_t *)str, - ((i / 2)) > (ERRMSGLEN - 1) ? ERRMSGLEN : i, &ustr); - str = ustr; + RtlInitUnicodeString(&us, (uint16_t *)msgbuf); + if (RtlUnicodeStringToAnsiString(&as, &us, TRUE)) + return; + str = as.as_buf; } device_printf (dev, "NDIS ERROR: %x (%s)\n", code, @@ -1082,6 +1090,9 @@ NdisWriteErrorLogEntry(ndis_handle adapter, ndis_error_code code, va_end(ap); } + if (str != NULL) + RtlFreeAnsiString(&as); + return; } @@ -1404,10 +1415,18 @@ NdisReadNetworkAddress(status, addr, addrlen, adapter) block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); +#ifdef IFP2ENADDR if (bcmp(IFP2ENADDR(sc->ifp), empty, ETHER_ADDR_LEN) == 0) +#else + if (bcmp(sc->arpcom.ac_enaddr, empty, ETHER_ADDR_LEN) == 0) +#endif *status = NDIS_STATUS_FAILURE; else { +#ifdef IFP2ENADDR *addr = IFP2ENADDR(sc->ifp); +#else + *addr = sc->arpcom.ac_enaddr; +#endif *addrlen = ETHER_ADDR_LEN; *status = NDIS_STATUS_SUCCESS; } @@ -1530,6 +1549,8 @@ NdisMAllocateSharedMemory(adapter, len, cached, vaddr, paddr) if (sh == NULL) return; + InitializeListHead(&sh->ndis_list); + /* * When performing shared memory allocations, create a tag * with a lowaddr limit that restricts physical memory mappings @@ -1582,10 +1603,11 @@ NdisMAllocateSharedMemory(adapter, len, cached, vaddr, paddr) * searching based on the virtual address fails. */ + NDIS_LOCK(sc); sh->ndis_paddr.np_quad = paddr->np_quad; sh->ndis_saddr = *vaddr; - sh->ndis_next = sc->ndis_shlist; - sc->ndis_shlist = sh; + InsertHeadList((&sc->ndis_shlist), (&sh->ndis_list)); + NDIS_UNLOCK(sc); return; } @@ -1674,21 +1696,24 @@ NdisMFreeSharedMemory(adapter, len, cached, vaddr, paddr) { ndis_miniport_block *block; struct ndis_softc *sc; - struct ndis_shmem *sh, *prev; + struct ndis_shmem *sh = NULL; + list_entry *l; if (vaddr == NULL || adapter == NULL) return; block = (ndis_miniport_block *)adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); - sh = prev = sc->ndis_shlist; /* Sanity check: is list empty? */ - if (sh == NULL) + if (IsListEmpty(&sc->ndis_shlist)) return; - while (sh) { + NDIS_LOCK(sc); + l = sc->ndis_shlist.nle_flink; + while (l != &sc->ndis_shlist) { + sh = CONTAINING_RECORD(l, struct ndis_shmem, ndis_list); if (sh->ndis_saddr == vaddr) break; /* @@ -1697,26 +1722,25 @@ NdisMFreeSharedMemory(adapter, len, cached, vaddr, paddr) */ if (sh->ndis_paddr.np_quad == paddr.np_quad) break; - prev = sh; - sh = sh->ndis_next; + l = l->nle_flink; } if (sh == NULL) { + NDIS_UNLOCK(sc); printf("NDIS: buggy driver tried to free " "invalid shared memory: vaddr: %p paddr: 0x%jx\n", vaddr, (uintmax_t)paddr.np_quad); return; } + RemoveEntryList(&sh->ndis_list); + + NDIS_UNLOCK(sc); + bus_dmamap_unload(sh->ndis_stag, sh->ndis_smap); bus_dmamem_free(sh->ndis_stag, sh->ndis_saddr, sh->ndis_smap); bus_dma_tag_destroy(sh->ndis_stag); - if (sh == sc->ndis_shlist) - sc->ndis_shlist = sh->ndis_next; - else - prev->ndis_next = sh->ndis_next; - free(sh, M_DEVBUF); return; @@ -2077,6 +2101,7 @@ NdisAllocateBufferPool(status, pool, descnum) ndis_handle *pool; uint32_t descnum; { + /* * The only thing we can really do here is verify that descnum * is a reasonable value, but I really don't know what to check @@ -2111,6 +2136,8 @@ NdisAllocateBuffer(status, buffer, pool, vaddr, len) return; } + MmBuildMdlForNonPagedPool(buf); + *buffer = buf; *status = NDIS_STATUS_SUCCESS; @@ -2260,37 +2287,35 @@ NdisWaitEvent(event, msecs) static ndis_status NdisUnicodeStringToAnsiString(dstr, sstr) - ndis_ansi_string *dstr; - ndis_unicode_string *sstr; + ansi_string *dstr; + unicode_string *sstr; { - if (dstr == NULL || sstr == NULL) - return(NDIS_STATUS_FAILURE); - if (ndis_unicode_to_ascii(sstr->us_buf, - sstr->us_len, &dstr->nas_buf)) + uint32_t rval; + + rval = RtlUnicodeStringToAnsiString(dstr, sstr, FALSE); + + if (rval == STATUS_INSUFFICIENT_RESOURCES) + return(NDIS_STATUS_RESOURCES); + if (rval) return(NDIS_STATUS_FAILURE); - dstr->nas_len = dstr->nas_maxlen = strlen(dstr->nas_buf); + return (NDIS_STATUS_SUCCESS); } static ndis_status NdisAnsiStringToUnicodeString(dstr, sstr) - ndis_unicode_string *dstr; - ndis_ansi_string *sstr; + unicode_string *dstr; + ansi_string *sstr; { - char *str; - if (dstr == NULL || sstr == NULL) - return(NDIS_STATUS_FAILURE); - str = malloc(sstr->nas_len + 1, M_DEVBUF, M_NOWAIT); - if (str == NULL) - return(NDIS_STATUS_FAILURE); - strncpy(str, sstr->nas_buf, sstr->nas_len); - *(str + sstr->nas_len) = '\0'; - if (ndis_ascii_to_unicode(str, &dstr->us_buf)) { - free(str, M_DEVBUF); + uint32_t rval; + + rval = RtlAnsiStringToUnicodeString(dstr, sstr, FALSE); + + if (rval == STATUS_INSUFFICIENT_RESOURCES) + return(NDIS_STATUS_RESOURCES); + if (rval) return(NDIS_STATUS_FAILURE); - } - dstr->us_len = dstr->us_maxlen = sstr->nas_len * 2; - free(str, M_DEVBUF); + return (NDIS_STATUS_SUCCESS); } @@ -2311,6 +2336,77 @@ NdisMPciAssignResources(adapter, slot, list) return (NDIS_STATUS_SUCCESS); } +static void +ndis_intr(arg) + void *arg; +{ + struct ndis_softc *sc; + struct ifnet *ifp; + int is_our_intr = 0; + int call_isr = 0; + ndis_miniport_interrupt *intr; + + sc = arg; + ifp = sc->ifp; + intr = sc->ndis_block->nmb_interrupt; + + if (intr == NULL || sc->ndis_block->nmb_miniportadapterctx == NULL) + return; + + 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; + } + + if ((is_our_intr || call_isr)) + IoRequestDpc(sc->ndis_block->nmb_deviceobj, NULL, sc); + + return; +} + +static void +ndis_intrhand(dpc, intr, sysarg1, sysarg2) + kdpc *dpc; + ndis_miniport_interrupt *intr; + void *sysarg1; + void *sysarg2; +{ + struct ndis_softc *sc; + ndis_miniport_block *block; + ndis_handle adapter; + + block = intr->ni_block; + adapter = block->nmb_miniportadapterctx; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); + + MSCALL1(intr->ni_isrfunc, adapter); + + /* If there's a MiniportEnableInterrupt() routine, call it. */ + + ndis_enable_intr(sc); + + if (NDIS_SERIALIZED(sc->ndis_block)) + KeReleaseSpinLockFromDpcLevel(&block->nmb_lock); + + /* + * Set the completion event if we've drained all + * pending interrupts. + */ + + KeAcquireSpinLockAtDpcLevel(&intr->ni_dpccountlock); + intr->ni_dpccnt--; + if (intr->ni_dpccnt == 0) + KeSetEvent(&intr->ni_dpcevt, IO_NO_INCREMENT, FALSE); + KeReleaseSpinLockFromDpcLevel(&intr->ni_dpccountlock); + + return; +} + static ndis_status NdisMRegisterInterrupt(intr, adapter, ivec, ilevel, reqisr, shared, imode) ndis_miniport_interrupt *intr; @@ -2322,15 +2418,40 @@ NdisMRegisterInterrupt(intr, adapter, ivec, ilevel, reqisr, shared, imode) ndis_interrupt_mode imode; { ndis_miniport_block *block; + ndis_miniport_characteristics *ch; + struct ndis_softc *sc; + int error; block = adapter; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + ch = IoGetDriverObjectExtension(block->nmb_deviceobj->do_drvobj, + (void *)1); + + intr->ni_rsvd = ExAllocatePoolWithTag(NonPagedPool, + sizeof(struct mtx), 0); + if (intr->ni_rsvd == NULL) + return(NDIS_STATUS_RESOURCES); intr->ni_block = adapter; intr->ni_isrreq = reqisr; intr->ni_shared = shared; - block->nmb_interrupt = intr; + intr->ni_dpccnt = 0; + intr->ni_isrfunc = ch->nmc_interrupt_func; + intr->ni_dpcfunc = ch->nmc_isr_func; + + KeInitializeEvent(&intr->ni_dpcevt, EVENT_TYPE_NOTIFY, TRUE); + KeInitializeDpc(&intr->ni_dpc, + ndis_findwrap((funcptr)ndis_intrhand), intr); + KeSetImportanceDpc(&intr->ni_dpc, KDPC_IMPORTANCE_LOW); - KeInitializeSpinLock(&intr->ni_dpccountlock); + error = IoConnectInterrupt(&intr->ni_introbj, + ndis_findwrap((funcptr)ndis_intr), sc, NULL, + ivec, ilevel, 0, imode, shared, 0, FALSE); + + if (error != STATUS_SUCCESS) + return(NDIS_STATUS_FAILURE); + + block->nmb_interrupt = intr; return(NDIS_STATUS_SUCCESS); } @@ -2339,6 +2460,29 @@ static void NdisMDeregisterInterrupt(intr) ndis_miniport_interrupt *intr; { + ndis_miniport_block *block; + struct ndis_softc *sc; + uint8_t irql; + + block = intr->ni_block; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + + /* Should really be KeSynchronizeExecution() */ + + KeAcquireSpinLock(intr->ni_introbj->ki_lock, &irql); + block->nmb_interrupt = NULL; + KeReleaseSpinLock(intr->ni_introbj->ki_lock, irql); +/* + KeFlushQueuedDpcs(); +*/ + /* Disconnect our ISR */ + + IoDisconnectInterrupt(intr->ni_introbj); + + KeWaitForSingleObject((nt_dispatch_header *)&intr->ni_dpcevt, + 0, 0, FALSE, NULL); + KeResetEvent(&intr->ni_dpcevt); + return; } @@ -2429,23 +2573,22 @@ static void NdisMSleep(usecs) uint32_t usecs; { - struct timeval tv; + ktimer timer; /* * During system bootstrap, (i.e. cold == 1), we aren't - * allowed to msleep(), so calling ndis_thsuspend() here - * will return 0, and we won't actually have delayed. This - * is a problem because some drivers expect NdisMSleep() - * to always wait, and might fail if the expected delay - * period does not in fact elapse. As a workaround, if the - * attempt to sleep delay fails, we do a hard DELAY() instead. + * allowed to sleep, so we have to do a hard DELAY() + * instead. */ - tv.tv_sec = 0; - tv.tv_usec = usecs; - if (ndis_thsuspend(curthread->td_proc, NULL, tvtohz(&tv)) == 0) + if (cold) DELAY(usecs); - + else { + KeInitializeTimer(&timer); + KeSetTimer(&timer, ((int64_t)usecs * -10), NULL); + KeWaitForSingleObject((nt_dispatch_header *)&timer, + 0, 0, FALSE, NULL); + } return; } @@ -2571,19 +2714,7 @@ NdisMSynchronizeWithInterrupt(intr, syncfunc, syncctx) void *syncfunc; void *syncctx; { - uint8_t (*sync)(void *); - uint8_t rval; - uint8_t irql; - - if (syncfunc == NULL || syncctx == NULL) - return(0); - - sync = syncfunc; - KeAcquireSpinLock(&intr->ni_dpccountlock, &irql); - rval = MSCALL1(sync, syncctx); - KeReleaseSpinLock(&intr->ni_dpccountlock, irql); - - return(rval); + return(KeSynchronizeExecution(intr->ni_introbj, syncfunc, syncctx)); } /* @@ -2620,28 +2751,20 @@ NdisGetSystemUpTime(tval) static void NdisInitializeString(dst, src) - ndis_unicode_string *dst; + unicode_string *dst; char *src; { - ndis_unicode_string *u; - - u = dst; - u->us_buf = NULL; - if (ndis_ascii_to_unicode(src, &u->us_buf)) - return; - u->us_len = u->us_maxlen = strlen(src) * 2; + ansi_string as; + RtlInitAnsiString(&as, src); + RtlAnsiStringToUnicodeString(dst, &as, TRUE); return; } static void NdisFreeString(str) - ndis_unicode_string *str; + unicode_string *str; { - if (str == NULL) - return; - if (str->us_buf != NULL) - free(str->us_buf, M_DEVBUF); - free(str, M_DEVBUF); + RtlFreeUnicodeString(str); return; } @@ -2654,47 +2777,19 @@ NdisMRemoveMiniport(adapter) static void NdisInitAnsiString(dst, src) - ndis_ansi_string *dst; + ansi_string *dst; char *src; { - ndis_ansi_string *a; - - a = dst; - if (a == NULL) - return; - if (src == NULL) { - a->nas_len = a->nas_maxlen = 0; - a->nas_buf = NULL; - } else { - a->nas_buf = src; - a->nas_len = a->nas_maxlen = strlen(src); - } - + RtlInitAnsiString(dst, src); return; } static void NdisInitUnicodeString(dst, src) - ndis_unicode_string *dst; + unicode_string *dst; uint16_t *src; { - ndis_unicode_string *u; - int i; - - u = dst; - if (u == NULL) - return; - if (src == NULL) { - u->us_len = u->us_maxlen = 0; - u->us_buf = NULL; - } else { - i = 0; - while(src[i] != 0) - i++; - u->us_buf = src; - u->us_len = u->us_maxlen = i * 2; - } - + RtlInitUnicodeString(dst, src); return; } @@ -2806,9 +2901,10 @@ NdisOpenFile(status, filehandle, filelength, filename, highestaddr) ndis_status *status; ndis_handle *filehandle; uint32_t *filelength; - ndis_unicode_string *filename; + unicode_string *filename; ndis_physaddr highestaddr; { + ansi_string as; char *afilename = NULL; struct thread *td = curthread; struct nameidata nd; @@ -2820,8 +2916,13 @@ NdisOpenFile(status, filehandle, filelength, filename, highestaddr) linker_file_t head, lf; caddr_t kldstart, kldend; - ndis_unicode_to_ascii(filename->us_buf, - filename->us_len, &afilename); + if (RtlUnicodeStringToAnsiString(&as, filename, TRUE)) { + *status = NDIS_STATUS_RESOURCES; + return; + } + + afilename = strdup(as.as_buf, M_DEVBUF); + RtlFreeAnsiString(&as); fh = ExAllocatePoolWithTag(NonPagedPool, sizeof(ndis_fh), 0); if (fh == NULL) { @@ -3120,6 +3221,7 @@ NdisScheduleWorkItem(work) ExInitializeWorkItem(wqi, (work_item_func)work->nwi_func, work->nwi_ctx); ExQueueWorkItem(wqi, WORKQUEUE_DELAYED); + return(NDIS_STATUS_SUCCESS); } @@ -3230,8 +3332,8 @@ NdisCopyFromPacketToPacketSafe(dpkt, doff, reqlen, spkt, soff, cpylen, prio) static ndis_status NdisMRegisterDevice(handle, devname, symname, majorfuncs, devobj, devhandle) ndis_handle handle; - ndis_unicode_string *devname; - ndis_unicode_string *symname; + unicode_string *devname; + unicode_string *symname; driver_dispatch *majorfuncs[]; void **devobj; ndis_handle *devhandle; @@ -3260,18 +3362,19 @@ NdisMDeregisterDevice(handle) static ndis_status NdisMQueryAdapterInstanceName(name, handle) - ndis_unicode_string *name; + unicode_string *name; ndis_handle handle; { ndis_miniport_block *block; device_t dev; + ansi_string as; block = (ndis_miniport_block *)handle; dev = block->nmb_physdeviceobj->do_devext; - ndis_ascii_to_unicode(__DECONST(char *, - device_get_nameunit(dev)), &name->us_buf); - name->us_len = strlen(device_get_nameunit(dev)) * 2; + RtlInitAnsiString(&as, __DECONST(char *, device_get_nameunit(dev))); + if (RtlAnsiStringToUnicodeString(name, &as, TRUE)) + return(NDIS_STATUS_RESOURCES); return(NDIS_STATUS_SUCCESS); } @@ -3422,6 +3525,8 @@ image_patch_table ndis_functbl[] = { IMPORT_SFUNC(NdisMRegisterUnloadHandler, 2), IMPORT_SFUNC(ndis_timercall, 4), IMPORT_SFUNC(ndis_asyncmem_complete, 2), + IMPORT_SFUNC(ndis_intr, 1), + IMPORT_SFUNC(ndis_intrhand, 4), /* * This last entry is a catch-all for any function we haven't diff --git a/sys/compat/ndis/subr_ntoskrnl.c b/sys/compat/ndis/subr_ntoskrnl.c index 6ff0689..fd3955c 100644 --- a/sys/compat/ndis/subr_ntoskrnl.c +++ b/sys/compat/ndis/subr_ntoskrnl.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #endif #include <sys/kernel.h> #include <sys/proc.h> +#include <sys/condvar.h> #include <sys/kthread.h> #include <sys/module.h> #include <sys/smp.h> @@ -58,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include <machine/clock.h> #include <machine/bus.h> #include <machine/stdarg.h> +#include <machine/resource.h> #include <sys/bus.h> #include <sys/rman.h> @@ -77,11 +79,8 @@ __FBSDID("$FreeBSD$"); #include <compat/ndis/ndis_var.h> struct kdpc_queue { - list_entry kq_high; - list_entry kq_low; - list_entry kq_med; + list_entry kq_disp; struct thread *kq_td; - int kq_state; int kq_cpu; int kq_exit; struct mtx kq_lock; @@ -92,14 +91,22 @@ struct kdpc_queue { typedef struct kdpc_queue kdpc_queue; -static uint8_t RtlEqualUnicodeString(ndis_unicode_string *, - ndis_unicode_string *, uint8_t); -static void RtlCopyUnicodeString(ndis_unicode_string *, - ndis_unicode_string *); -static ndis_status RtlUnicodeStringToAnsiString(ndis_ansi_string *, - ndis_unicode_string *, uint8_t); -static ndis_status RtlAnsiStringToUnicodeString(ndis_unicode_string *, - ndis_ansi_string *, uint8_t); +struct wb_ext { + struct cv we_cv; + struct thread *we_td; +}; + +typedef struct wb_ext wb_ext; + +#define NTOSKRNL_TIMEOUTS 256 +struct callout ntoskrnl_callout[NTOSKRNL_TIMEOUTS]; +int ntoskrnl_callidx; +#define CALLOUT_INC(i) (i) = ((i) + 1) % NTOSKRNL_TIMEOUTS + +static uint8_t RtlEqualUnicodeString(unicode_string *, + unicode_string *, uint8_t); +static void RtlCopyUnicodeString(unicode_string *, + unicode_string *); static irp *IoBuildSynchronousFsdRequest(uint32_t, device_object *, void *, uint32_t, uint64_t *, nt_kevent *, io_status_block *); static irp *IoBuildAsynchronousFsdRequest(uint32_t, @@ -115,7 +122,10 @@ static irp *IoMakeAssociatedIrp(irp *, uint8_t); static uint32_t KeWaitForMultipleObjects(uint32_t, nt_dispatch_header **, uint32_t, uint32_t, uint32_t, uint8_t, int64_t *, wait_block *); -static void ntoskrnl_wakeup(void *); +static void ntoskrnl_waittest(nt_dispatch_header *, uint32_t); +static void ntoskrnl_satisfy_wait(nt_dispatch_header *, struct thread *); +static void ntoskrnl_satisfy_multiple_waits(wait_block *); +static int ntoskrnl_is_signalled(nt_dispatch_header *, struct thread *); static void ntoskrnl_timercall(void *); static void ntoskrnl_run_dpc(void *); static void ntoskrnl_dpc_thread(void *); @@ -123,7 +133,12 @@ static void ntoskrnl_destroy_dpc_threads(void); static void ntoskrnl_destroy_workitem_threads(void); static void ntoskrnl_workitem_thread(void *); static void ntoskrnl_workitem(device_object *, void *); +static void ntoskrnl_unicode_to_ascii(uint16_t *, char *, int); +static void ntoskrnl_ascii_to_unicode(char *, uint16_t *, int); static uint8_t ntoskrnl_insert_dpc(list_entry *, kdpc *); +static device_t ntoskrnl_finddev(device_t, uint32_t, + uint8_t, uint8_t, struct resource **); +static void ntoskrnl_intr(void *); static void WRITE_REGISTER_USHORT(uint16_t *, uint16_t); static uint16_t READ_REGISTER_USHORT(uint16_t *); static void WRITE_REGISTER_ULONG(uint32_t *, uint32_t); @@ -163,19 +178,13 @@ static uint32_t InterlockedIncrement(volatile uint32_t *); static uint32_t InterlockedDecrement(volatile uint32_t *); static void ExInterlockedAddLargeStatistic(uint64_t *, uint32_t); static uint32_t MmSizeOfMdl(void *, size_t); -static void MmBuildMdlForNonPagedPool(mdl *); static void *MmMapLockedPages(mdl *, uint8_t); static void *MmMapLockedPagesSpecifyCache(mdl *, uint8_t, uint32_t, void *, uint32_t, uint32_t); static void MmUnmapLockedPages(void *, mdl *); static uint8_t MmIsAddressValid(void *); static size_t RtlCompareMemory(const void *, const void *, size_t); -static void RtlInitAnsiString(ndis_ansi_string *, char *); -static void RtlInitUnicodeString(ndis_unicode_string *, - uint16_t *); -static void RtlFreeUnicodeString(ndis_unicode_string *); -static void RtlFreeAnsiString(ndis_ansi_string *); -static ndis_status RtlUnicodeStringToInteger(ndis_unicode_string *, +static ndis_status RtlUnicodeStringToInteger(unicode_string *, uint32_t, uint32_t *); static int atoi (const char *); static long atol (const char *); @@ -198,6 +207,8 @@ static void ObfDereferenceObject(void *); static uint32_t ZwClose(ndis_handle); static void *ntoskrnl_memset(void *, int, size_t); static char *ntoskrnl_strstr(char *, char *); +static int ntoskrnl_toupper(int); +static int ntoskrnl_tolower(int); static funcptr ntoskrnl_findwrap(funcptr); static uint32_t DbgPrint(char *, ...); static void DbgBreakPoint(void); @@ -280,6 +291,9 @@ ntoskrnl_libinit() patch++; } + for (i = 0; i < NTOSKRNL_TIMEOUTS; i++) + callout_init(&ntoskrnl_callout[i], CALLOUT_MPSAFE); + /* * MDLs are supposed to be variable size (they describe * buffers containing some number of pages, but we don't @@ -313,10 +327,10 @@ ntoskrnl_libfini() patch++; } - /* Stop the DPC queues. */ - ntoskrnl_destroy_dpc_threads(); /* Stop the workitem queues. */ ntoskrnl_destroy_workitem_threads(); + /* Stop the DPC queues. */ + ntoskrnl_destroy_dpc_threads(); ExFreePool(kq_queues); ExFreePool(wq_queues); @@ -362,11 +376,24 @@ ntoskrnl_strstr(s, find) return ((char *)s); } +static int +ntoskrnl_toupper(c) + int c; +{ + return(toupper(c)); +} + +static int +ntoskrnl_tolower(c) + int c; +{ + return(tolower(c)); +} static uint8_t RtlEqualUnicodeString(str1, str2, caseinsensitive) - ndis_unicode_string *str1; - ndis_unicode_string *str2; + unicode_string *str1; + unicode_string *str2; uint8_t caseinsensitive; { int i; @@ -390,8 +417,8 @@ RtlEqualUnicodeString(str1, str2, caseinsensitive) static void RtlCopyUnicodeString(dest, src) - ndis_unicode_string *dest; - ndis_unicode_string *src; + unicode_string *dest; + unicode_string *src; { if (dest->us_maxlen >= src->us_len) @@ -402,55 +429,99 @@ RtlCopyUnicodeString(dest, src) return; } -static ndis_status +static void +ntoskrnl_ascii_to_unicode(ascii, unicode, len) + char *ascii; + uint16_t *unicode; + int len; +{ + int i; + uint16_t *ustr; + + ustr = unicode; + for (i = 0; i < len; i++) { + *ustr = (uint16_t)ascii[i]; + ustr++; + } + + return; +} + +static void +ntoskrnl_unicode_to_ascii(unicode, ascii, len) + uint16_t *unicode; + char *ascii; + int len; +{ + int i; + uint8_t *astr; + + astr = ascii; + for (i = 0; i < len / 2; i++) { + *astr = (uint8_t)unicode[i]; + astr++; + } + + return; +} + +uint32_t RtlUnicodeStringToAnsiString(dest, src, allocate) - ndis_ansi_string *dest; - ndis_unicode_string *src; + ansi_string *dest; + unicode_string *src; uint8_t allocate; { - char *astr = NULL; - if (dest == NULL || src == NULL) return(NDIS_STATUS_FAILURE); + + dest->as_len = src->us_len / 2; + if (dest->as_maxlen < dest->as_len) + dest->as_len = dest->as_maxlen; + if (allocate == TRUE) { - if (ndis_unicode_to_ascii(src->us_buf, src->us_len, &astr)) - return(NDIS_STATUS_FAILURE); - dest->nas_buf = astr; - dest->nas_len = dest->nas_maxlen = strlen(astr); + dest->as_buf = ExAllocatePoolWithTag(NonPagedPool, + (src->us_len / 2) + 1, 0); + if (dest->as_buf == NULL) + return(STATUS_INSUFFICIENT_RESOURCES); + dest->as_len = dest->as_maxlen = src->us_len / 2; } else { - dest->nas_len = src->us_len / 2; /* XXX */ - if (dest->nas_maxlen < dest->nas_len) - dest->nas_len = dest->nas_maxlen; - ndis_unicode_to_ascii(src->us_buf, dest->nas_len * 2, - &dest->nas_buf); + dest->as_len = src->us_len / 2; /* XXX */ + if (dest->as_maxlen < dest->as_len) + dest->as_len = dest->as_maxlen; } - return (NDIS_STATUS_SUCCESS); + + ntoskrnl_unicode_to_ascii(src->us_buf, dest->as_buf, + dest->as_len * 2); + + return (STATUS_SUCCESS); } -static ndis_status +uint32_t RtlAnsiStringToUnicodeString(dest, src, allocate) - ndis_unicode_string *dest; - ndis_ansi_string *src; + unicode_string *dest; + ansi_string *src; uint8_t allocate; { - uint16_t *ustr = NULL; - if (dest == NULL || src == NULL) return(NDIS_STATUS_FAILURE); if (allocate == TRUE) { - if (ndis_ascii_to_unicode(src->nas_buf, &ustr)) - return(NDIS_STATUS_FAILURE); - dest->us_buf = ustr; - dest->us_len = dest->us_maxlen = strlen(src->nas_buf) * 2; + dest->us_buf = ExAllocatePoolWithTag(NonPagedPool, + src->as_len * 2, 0); + if (dest->us_buf == NULL) + return(STATUS_INSUFFICIENT_RESOURCES); + dest->us_len = dest->us_maxlen = strlen(src->as_buf) * 2; } else { - dest->us_len = src->nas_len * 2; /* XXX */ + dest->us_len = src->as_len * 2; /* XXX */ if (dest->us_maxlen < dest->us_len) dest->us_len = dest->us_maxlen; - ndis_ascii_to_unicode(src->nas_buf, &dest->us_buf); } - return (NDIS_STATUS_SUCCESS); + + ntoskrnl_ascii_to_unicode(src->as_buf, dest->us_buf, + dest->us_len / 2); + + return (STATUS_SUCCESS); } void * @@ -461,9 +532,10 @@ ExAllocatePoolWithTag(pooltype, len, tag) { void *buf; - buf = malloc(len, M_DEVBUF, M_NOWAIT); + buf = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); if (buf == NULL) return(NULL); + return(buf); } @@ -491,7 +563,7 @@ IoAllocateDriverObjectExtension(drv, clid, extlen, ext) return(STATUS_INSUFFICIENT_RESOURCES); ce->ce_clid = clid; - INSERT_LIST_TAIL((&drv->dro_driverext->dre_usrext), (&ce->ce_list)); + InsertTailList((&drv->dro_driverext->dre_usrext), (&ce->ce_list)); *ext = (void *)(ce + 1); @@ -902,7 +974,7 @@ IoInitializeIrp(io, psize, ssize) io->irp_size = psize; io->irp_stackcnt = ssize; io->irp_currentstackloc = ssize; - INIT_LIST_HEAD(&io->irp_thlist); + InitializeListHead(&io->irp_thlist); io->irp_tail.irp_overlay.irp_csl = (io_stack_location *)(io + 1) + ssize; @@ -1060,6 +1132,202 @@ IofCompleteRequest(ip, prioboost) return; } +static device_t +ntoskrnl_finddev(dev, vector, irql, shared, res) + device_t dev; + uint32_t vector; + uint8_t irql; + uint8_t shared; + struct resource **res; +{ + device_t *children; + device_t matching_dev; + int childcnt; + struct resource *r; + struct resource_list *rl; + struct resource_list_entry *rle; + uint32_t flags; + int i; + + /* We only want devices that have been successfully probed. */ + + if (device_is_alive(dev) == FALSE) + return(NULL); + + device_get_children(dev, &children, &childcnt); + + /* + * If this device has no children, it's a leaf: we can + * examine its resources. If the interrupt resource we + * want isn't here, we return NULL, otherwise we return + * the device to terminate the recursive search. + */ + + if (childcnt == 0) { + rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); + if (rl == NULL) + return(NULL); +#if __FreeBSD_version < 600022 + SLIST_FOREACH(rle, rl, link) { +#else + STAILQ_FOREACH(rle, rl, link) { +#endif + r = rle->res; + + if (r == NULL) + continue; + + flags = rman_get_flags(r); + + if (!(flags & RF_ACTIVE)) + continue; + + if (shared == TRUE && !(flags & RF_SHAREABLE)) + continue; + + if (rle->type == SYS_RES_IRQ && + rman_get_start(r) == irql) { + *res = r; + return(dev); + } + } + /* No match. */ + return (NULL); + } + + /* + * If this device has children, do another + * level of recursion to inspect them. + */ + + for (i = 0; i < childcnt; i++) { + matching_dev = ntoskrnl_finddev(children[i], + vector, irql, shared, res); + if (matching_dev != NULL) { + free(children, M_TEMP); + return(matching_dev); + } + } + + free(children, M_TEMP); + return(NULL); +} + +static void +ntoskrnl_intr(arg) + void *arg; +{ + kinterrupt *iobj; + uint8_t irql; + + iobj = arg; + + KeAcquireSpinLock(iobj->ki_lock, &irql); + MSCALL1(iobj->ki_svcfunc, iobj->ki_svcctx); + KeReleaseSpinLock(iobj->ki_lock, irql); + + return; +} + +uint8_t +KeSynchronizeExecution(iobj, syncfunc, syncctx) + kinterrupt *iobj; + void *syncfunc; + void *syncctx; +{ + uint8_t irql; + + KeAcquireSpinLock(iobj->ki_lock, &irql); + MSCALL1(syncfunc, syncctx); + KeReleaseSpinLock(iobj->ki_lock, irql); + + return(TRUE); +} + +/* + * This routine is a pain because the only thing we get passed + * here is the interrupt request level and vector, but bus_setup_intr() + * needs the device too. We can hack around this for now, but it's + * awkward. + */ + +uint32_t +IoConnectInterrupt(iobj, svcfunc, svcctx, lock, vector, irql, + syncirql, imode, shared, affinity, savefloat) + kinterrupt **iobj; + void *svcfunc; + void *svcctx; + uint32_t vector; + kspin_lock *lock; + uint8_t irql; + uint8_t syncirql; + uint8_t imode; + uint8_t shared; + uint32_t affinity; + uint8_t savefloat; +{ + devclass_t nexus_class; + device_t *nexus_devs, devp; + int nexus_count = 0; + device_t matching_dev = NULL; + struct resource *res; + int i, error; + + nexus_class = devclass_find("nexus"); + devclass_get_devices(nexus_class, &nexus_devs, &nexus_count); + + for (i = 0; i < nexus_count; i++) { + devp = nexus_devs[i]; + matching_dev = ntoskrnl_finddev(devp, vector, + irql, shared, &res); + if (matching_dev) + break; + } + + free(nexus_devs, M_TEMP); + + if (matching_dev == NULL) + return(STATUS_INVALID_PARAMETER); + + *iobj = ExAllocatePoolWithTag(NonPagedPool, sizeof(kinterrupt), 0); + if (*iobj == NULL) + return(STATUS_INSUFFICIENT_RESOURCES); + + (*iobj)->ki_dev = matching_dev; + (*iobj)->ki_irq = res; + (*iobj)->ki_svcfunc = svcfunc; + (*iobj)->ki_svcctx = svcctx; + + if (lock == NULL) { + KeInitializeSpinLock(&(*iobj)->ki_lock_priv); + (*iobj)->ki_lock = &(*iobj)->ki_lock_priv; + } else + (*iobj)->ki_lock = lock; + + error = bus_setup_intr(matching_dev, res, INTR_TYPE_NET | INTR_MPSAFE, + ntoskrnl_intr, *iobj, &(*iobj)->ki_cookie); + + if (error) { + ExFreePool(iobj); + return (STATUS_INVALID_PARAMETER); + } + + return(STATUS_SUCCESS); +} + +void +IoDisconnectInterrupt(iobj) + kinterrupt *iobj; +{ + if (iobj == NULL) + return; + + bus_teardown_intr(iobj->ki_dev, iobj->ki_irq, iobj->ki_cookie); + ExFreePool(iobj); + + return; +} + device_object * IoAttachDeviceToDeviceStack(src, dst) device_object *src; @@ -1094,7 +1362,7 @@ IoDetachDevice(topdev) topdev->do_attacheddev = tail->do_attacheddev; topdev->do_refcnt--; - /* Now reduce the stacksize count for the tail objects. */ + /* Now reduce the stacksize count for the takm_il objects. */ tail = topdev->do_attacheddev; while (tail != NULL) { @@ -1107,48 +1375,168 @@ IoDetachDevice(topdev) return; } -/* Always called with dispatcher lock held. */ -static void -ntoskrnl_wakeup(arg) - void *arg; +/* + * For the most part, an object is considered signalled if + * dh_sigstate == TRUE. The exception is for mutant objects + * (mutexes), where the logic works like this: + * + * - If the thread already owns the object and sigstate is + * less than or equal to 0, then the object is considered + * signalled (recursive acquisition). + * - If dh_sigstate == 1, the object is also considered + * signalled. + */ + +static int +ntoskrnl_is_signalled(obj, td) + nt_dispatch_header *obj; + struct thread *td; { + kmutant *km; + + if (obj->dh_type == DISP_TYPE_MUTANT) { + km = (kmutant *)obj; + if ((obj->dh_sigstate <= 0 && km->km_ownerthread == td) || + obj->dh_sigstate == 1) + return(TRUE); + return(FALSE); + } + + if (obj->dh_sigstate > 0) + return(TRUE); + return(FALSE); +} + +static void +ntoskrnl_satisfy_wait(obj, td) nt_dispatch_header *obj; - wait_block *w; - list_entry *e; struct thread *td; +{ + kmutant *km; - obj = arg; + switch (obj->dh_type) { + case DISP_TYPE_MUTANT: + km = (struct kmutant *)obj; + obj->dh_sigstate--; + /* + * If sigstate reaches 0, the mutex is now + * non-signalled (the new thread owns it). + */ + if (obj->dh_sigstate == 0) { + km->km_ownerthread = td; + if (km->km_abandoned == TRUE) + km->km_abandoned = FALSE; + } + break; + /* Synchronization objects get reset to unsignalled. */ + case DISP_TYPE_SYNCHRONIZATION_EVENT: + case DISP_TYPE_SYNCHRONIZATION_TIMER: + obj->dh_sigstate = 0; + break; + case DISP_TYPE_SEMAPHORE: + obj->dh_sigstate--; + break; + default: + break; + } - e = obj->dh_waitlisthead.nle_flink; + return; +} + +static void +ntoskrnl_satisfy_multiple_waits(wb) + wait_block *wb; +{ + wait_block *cur; + struct thread *td; + + cur = wb; + td = wb->wb_kthread; - obj->dh_sigstate = TRUE; + do { + ntoskrnl_satisfy_wait(wb->wb_object, td); + cur->wb_awakened = TRUE; + cur = cur->wb_next; + } while (cur != wb); + + return; +} + +/* Always called with dispatcher lock held. */ +static void +ntoskrnl_waittest(obj, increment) + nt_dispatch_header *obj; + uint32_t increment; +{ + wait_block *w, *next; + list_entry *e; + struct thread *td; + wb_ext *we; + int satisfied; /* - * What happens if someone tells us to wake up - * threads waiting on an object, but nobody's - * waiting on it at the moment? For sync events, - * the signal state is supposed to be automatically - * reset, but this only happens in the KeWaitXXX() - * functions. If nobody is waiting, the state never - * gets cleared. + * Once an object has been signalled, we walk its list of + * wait blocks. If a wait block can be awakened, then satisfy + * waits as necessary and wake the thread. + * + * The rules work like this: + * + * If a wait block is marked as WAITTYPE_ANY, then + * we can satisfy the wait conditions on the current + * object and wake the thread right away. Satisfying + * the wait also has the effect of breaking us out + * of the search loop. + * + * If the object is marked as WAITTYLE_ALL, then the + * wait block will be part of a circularly linked + * list of wait blocks belonging to a waiting thread + * that's sleeping in KeWaitForMultipleObjects(). In + * order to wake the thread, all the objects in the + * wait list must be in the signalled state. If they + * are, we then satisfy all of them and wake the + * thread. + * */ - if (e == &obj->dh_waitlisthead) { - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - return; - } + e = obj->dh_waitlisthead.nle_flink; - while (e != &obj->dh_waitlisthead) { - w = (wait_block *)e; - td = w->wb_kthread; - ndis_thresume(td->td_proc); - /* - * For synchronization objects, only wake up - * the first waiter. - */ - if (obj->dh_type == EVENT_TYPE_SYNC) - break; + while (e != &obj->dh_waitlisthead && obj->dh_sigstate > 0) { + w = CONTAINING_RECORD(e, wait_block, wb_waitlist); + we = w->wb_ext; + td = we->we_td; + satisfied = FALSE; + if (w->wb_waittype == WAITTYPE_ANY) { + /* + * Thread can be awakened if + * any wait is satisfied. + */ + ntoskrnl_satisfy_wait(obj, td); + satisfied = TRUE; + w->wb_awakened = TRUE; + } else { + /* + * Thread can only be woken up + * if all waits are satisfied. + * If the thread is waiting on multiple + * objects, they should all be linked + * through the wb_next pointers in the + * wait blocks. + */ + satisfied = TRUE; + next = w->wb_next; + while (next != w) { + if (ntoskrnl_is_signalled(obj, td) == FALSE) { + satisfied = FALSE; + break; + } + next = next->wb_next; + } + ntoskrnl_satisfy_multiple_waits(w); + } + + if (satisfied == TRUE) + cv_broadcastpri(&we->we_cv, w->wb_oldpri - + (increment * 4)); e = e->nle_flink; } @@ -1206,7 +1594,7 @@ ntoskrnl_time(tval) * * - For mutexes, we try to acquire the mutex and if we can't, we wait * on the mutex until it's available and then grab it. When a mutex is - * released, it enters the signaled state, which wakes up one of the + * released, it enters the signalled state, which wakes up one of the * threads waiting to acquire it. Mutexes are always synchronization * events. * @@ -1228,49 +1616,54 @@ KeWaitForSingleObject(obj, reason, mode, alertable, duetime) uint8_t alertable; int64_t *duetime; { - struct thread *td = curthread; - kmutant *km; wait_block w; + struct thread *td = curthread; struct timeval tv; int error = 0; uint64_t curtime; + wb_ext we; if (obj == NULL) return(STATUS_INVALID_PARAMETER); mtx_lock(&ntoskrnl_dispatchlock); + cv_init(&we.we_cv, "KeWFS"); + we.we_td = td; + /* - * See if the object is a mutex. If so, and we already own - * it, then just increment the acquisition count and return. - * - * For any other kind of object, see if it's already in the - * signalled state, and if it is, just return. If the object - * is marked as a synchronization event, reset the state to - * unsignalled. + * Check to see if this object is already signalled, + * and just return without waiting if it is. */ - - if (obj->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL || - km->km_ownerthread == curthread->td_proc) { - obj->dh_sigstate = FALSE; - km->km_acquirecnt++; - km->km_ownerthread = curthread->td_proc; + if (ntoskrnl_is_signalled(obj, td) == TRUE) { + /* Sanity check the signal state value. */ + if (obj->dh_sigstate != INT32_MIN) { + ntoskrnl_satisfy_wait(obj, curthread); mtx_unlock(&ntoskrnl_dispatchlock); return (STATUS_SUCCESS); + } else { + /* + * There's a limit to how many times we can + * recursively acquire a mutant. If we hit + * the limit, something is very wrong. + */ + if (obj->dh_type == DISP_TYPE_MUTANT) { + mtx_unlock(&ntoskrnl_dispatchlock); + panic("mutant limit exceeded"); + } } - } else if (obj->dh_sigstate == TRUE) { - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - mtx_unlock(&ntoskrnl_dispatchlock); - return (STATUS_SUCCESS); } + bzero((char *)&w, sizeof(wait_block)); w.wb_object = obj; - w.wb_kthread = td; + w.wb_ext = &we; + w.wb_waittype = WAITTYPE_ANY; + w.wb_next = &w; + w.wb_waitkey = 0; + w.wb_awakened = FALSE; + w.wb_oldpri = td->td_priority; - INSERT_LIST_TAIL((&obj->dh_waitlisthead), (&w.wb_waitlist)); + InsertTailList((&obj->dh_waitlisthead), (&w.wb_waitlist)); /* * The timeout value is specified in 100 nanosecond units @@ -1298,41 +1691,30 @@ KeWaitForSingleObject(obj, reason, mode, alertable, duetime) } } - error = ndis_thsuspend(td->td_proc, &ntoskrnl_dispatchlock, - duetime == NULL ? 0 : tvtohz(&tv)); + if (duetime == NULL) + cv_wait(&we.we_cv, &ntoskrnl_dispatchlock); + else + error = cv_timedwait(&we.we_cv, + &ntoskrnl_dispatchlock, tvtohz(&tv)); + + RemoveEntryList(&w.wb_waitlist); + + cv_destroy(&we.we_cv); /* We timed out. Leave the object alone and return status. */ if (error == EWOULDBLOCK) { - REMOVE_LIST_ENTRY((&w.wb_waitlist)); - INIT_LIST_HEAD((&w.wb_waitlist)); mtx_unlock(&ntoskrnl_dispatchlock); return(STATUS_TIMEOUT); } - /* - * Mutexes are always synchronization objects, which means - * if several threads are waiting to acquire it, only one will - * be woken up. If that one is us, and the mutex is up for grabs, - * grab it. - */ - - if (obj->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL) { - km->km_ownerthread = curthread->td_proc; - km->km_acquirecnt++; - } - } - - if (obj->dh_type == EVENT_TYPE_SYNC) - obj->dh_sigstate = FALSE; - REMOVE_LIST_ENTRY((&w.wb_waitlist)); - INIT_LIST_HEAD((&w.wb_waitlist)); - mtx_unlock(&ntoskrnl_dispatchlock); return(STATUS_SUCCESS); +/* + return(KeWaitForMultipleObjects(1, &obj, WAITTYPE_ALL, reason, + mode, alertable, duetime, &w)); +*/ } static uint32_t @@ -1348,13 +1730,15 @@ KeWaitForMultipleObjects(cnt, obj, wtype, reason, mode, wait_block *wb_array; { struct thread *td = curthread; - kmutant *km; - wait_block _wb_array[THREAD_WAIT_OBJECTS]; - wait_block *w; + wait_block *whead, *w; + wait_block _wb_array[MAX_WAIT_OBJECTS]; + nt_dispatch_header *cur; struct timeval tv; - int i, wcnt = 0, widx = 0, error = 0; + int i, wcnt = 0, error = 0; uint64_t curtime; struct timespec t1, t2; + uint32_t status = STATUS_SUCCESS; + wb_ext we; if (cnt > MAX_WAIT_OBJECTS) return(STATUS_INVALID_PARAMETER); @@ -1363,51 +1747,88 @@ KeWaitForMultipleObjects(cnt, obj, wtype, reason, mode, mtx_lock(&ntoskrnl_dispatchlock); + cv_init(&we.we_cv, "KeWFM"); + we.we_td = td; + if (wb_array == NULL) - w = &_wb_array[0]; + whead = _wb_array; else - w = wb_array; + whead = wb_array; + + bzero((char *)whead, sizeof(wait_block) * cnt); /* First pass: see if we can satisfy any waits immediately. */ + wcnt = 0; + w = whead; + for (i = 0; i < cnt; i++) { - if (obj[i]->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj[i]; - if (km->km_ownerthread == NULL || - km->km_ownerthread == curthread->td_proc) { - obj[i]->dh_sigstate = FALSE; - km->km_acquirecnt++; - km->km_ownerthread = curthread->td_proc; - if (wtype == WAITTYPE_ANY) { - mtx_unlock(&ntoskrnl_dispatchlock); - return (STATUS_WAIT_0 + i); - } + InsertTailList((&obj[i]->dh_waitlisthead), + (&w->wb_waitlist)); + w->wb_ext = &we; + w->wb_object = obj[i]; + w->wb_waittype = wtype; + w->wb_waitkey = i; + w->wb_awakened = FALSE; + w->wb_oldpri = td->td_priority; + w->wb_next = w + 1; + w++; + wcnt++; + if (ntoskrnl_is_signalled(obj[i], td)) { + /* + * There's a limit to how many times + * we can recursively acquire a mutant. + * If we hit the limit, something + * is very wrong. + */ + if (obj[i]->dh_sigstate == INT32_MIN && + obj[i]->dh_type == DISP_TYPE_MUTANT) { + mtx_unlock(&ntoskrnl_dispatchlock); + panic("mutant limit exceeded"); } - } else if (obj[i]->dh_sigstate == TRUE) { - if (obj[i]->dh_type == EVENT_TYPE_SYNC) - obj[i]->dh_sigstate = FALSE; + + /* + * If this is a WAITTYPE_ANY wait, then + * satisfy the waited object and exit + * right now. + */ + if (wtype == WAITTYPE_ANY) { - mtx_unlock(&ntoskrnl_dispatchlock); - return (STATUS_WAIT_0 + i); + ntoskrnl_satisfy_wait(obj[i], td); + status = STATUS_WAIT_0 + i; + goto wait_done; + } else { + w--; + wcnt--; + w->wb_object = NULL; + RemoveEntryList(&w->wb_waitlist); } } } /* - * Second pass: set up wait for anything we can't - * satisfy immediately. + * If this is a WAITTYPE_ALL wait and all objects are + * already signalled, satisfy the waits and exit now. */ - for (i = 0; i < cnt; i++) { - if (obj[i]->dh_sigstate == TRUE) - continue; - INSERT_LIST_TAIL((&obj[i]->dh_waitlisthead), - (&w[i].wb_waitlist)); - w[i].wb_kthread = td; - w[i].wb_object = obj[i]; - wcnt++; + if (wtype == WAITTYPE_ALL && wcnt == 0) { + for (i = 0; i < cnt; i++) + ntoskrnl_satisfy_wait(obj[i], td); + status = STATUS_SUCCESS; + goto wait_done; } + /* + * Create a circular waitblock list. The waitcount + * must always be non-zero when we get here. + */ + + (w - 1)->wb_next = whead; + + /* Wait on any objects that aren't yet signalled. */ + + /* Calculate timeout, if any. */ + if (duetime != NULL) { if (*duetime < 0) { tv.tv_sec = - (*duetime) / 10000000; @@ -1428,32 +1849,62 @@ KeWaitForMultipleObjects(cnt, obj, wtype, reason, mode, while (wcnt) { nanotime(&t1); - error = ndis_thsuspend(td->td_proc, &ntoskrnl_dispatchlock, - duetime == NULL ? 0 : tvtohz(&tv)); + if (duetime == NULL) + cv_wait(&we.we_cv, &ntoskrnl_dispatchlock); + else + error = cv_timedwait(&we.we_cv, + &ntoskrnl_dispatchlock, tvtohz(&tv)); + + /* Wait with timeout expired. */ + + if (error) { + status = STATUS_TIMEOUT; + goto wait_done; + } nanotime(&t2); - for (i = 0; i < cnt; i++) { - if (obj[i]->dh_size == OTYPE_MUTEX) { - km = (kmutant *)obj; - if (km->km_ownerthread == NULL) { - km->km_ownerthread = - curthread->td_proc; - km->km_acquirecnt++; + /* See what's been signalled. */ + + w = whead; + do { + cur = w->wb_object; + if (ntoskrnl_is_signalled(cur, td) == TRUE || + w->wb_awakened == TRUE) { + /* Sanity check the signal state value. */ + if (cur->dh_sigstate == INT32_MIN && + cur->dh_type == DISP_TYPE_MUTANT) { + mtx_unlock(&ntoskrnl_dispatchlock); + panic("mutant limit exceeded"); } - } - if (obj[i]->dh_sigstate == TRUE) { - widx = i; - if (obj[i]->dh_type == EVENT_TYPE_SYNC) - obj[i]->dh_sigstate = FALSE; - REMOVE_LIST_ENTRY((&w[i].wb_waitlist)); - INIT_LIST_HEAD((&w[i].wb_waitlist)); wcnt--; + if (wtype == WAITTYPE_ANY) { + status = w->wb_waitkey & + STATUS_WAIT_0; + goto wait_done; + } } + w = w->wb_next; + } while (w != whead); + + /* + * If all objects have been signalled, or if this + * is a WAITTYPE_ANY wait and we were woke up by + * someone, we can bail. + */ + + if (wcnt == 0) { + status = STATUS_SUCCESS; + goto wait_done; } - if (error || wtype == WAITTYPE_ANY) - break; + /* + * If this is WAITTYPE_ALL wait, and there's still + * objects that haven't been signalled, deduct the + * time that's elapsed so far from the timeout and + * wait again (or continue waiting indefinitely if + * there's no timeout). + */ if (duetime != NULL) { tv.tv_sec -= (t2.tv_sec - t1.tv_sec); @@ -1461,26 +1912,19 @@ KeWaitForMultipleObjects(cnt, obj, wtype, reason, mode, } } - if (wcnt) { - for (i = 0; i < cnt; i++) { - REMOVE_LIST_ENTRY((&w[i].wb_waitlist)); - INIT_LIST_HEAD((&w[i].wb_waitlist)); - } - } - if (error == EWOULDBLOCK) { - mtx_unlock(&ntoskrnl_dispatchlock); - return(STATUS_TIMEOUT); - } +wait_done: - if (wtype == WAITTYPE_ANY && wcnt) { - mtx_unlock(&ntoskrnl_dispatchlock); - return(STATUS_WAIT_0 + widx); - } + cv_destroy(&we.we_cv); + for (i = 0; i < cnt; i++) { + if (whead[i].wb_object != NULL) + RemoveEntryList(&whead[i].wb_waitlist); + + } mtx_unlock(&ntoskrnl_dispatchlock); - return(STATUS_SUCCESS); + return(status); } static void @@ -2047,7 +2491,7 @@ MmSizeOfMdl(vaddr, len) * Instead, we just fill in the page array with the kernel virtual * addresses of the buffers. */ -static void +void MmBuildMdlForNonPagedPool(m) mdl *m; { @@ -2135,16 +2579,14 @@ ntoskrnl_workitem_thread(arg) kq = arg; - INIT_LIST_HEAD(&kq->kq_med); + InitializeListHead(&kq->kq_disp); kq->kq_td = curthread; kq->kq_exit = 0; - kq->kq_state = NDIS_PSTATE_SLEEPING; mtx_init(&kq->kq_lock, "NDIS thread lock", NULL, MTX_SPIN); KeInitializeEvent(&kq->kq_proc, EVENT_TYPE_SYNC, FALSE); KeInitializeEvent(&kq->kq_dead, EVENT_TYPE_SYNC, FALSE); while (1) { - KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_proc, 0, 0, TRUE, NULL); @@ -2152,34 +2594,27 @@ ntoskrnl_workitem_thread(arg) if (kq->kq_exit) { mtx_unlock_spin(&kq->kq_lock); - KeSetEvent(&kq->kq_dead, 0, FALSE); + KeSetEvent(&kq->kq_dead, IO_NO_INCREMENT, FALSE); break; } - kq->kq_state = NDIS_PSTATE_RUNNING; - - l = kq->kq_med.nle_flink; - while (l != & kq->kq_med) { + while (!IsListEmpty(&kq->kq_disp)) { + l = RemoveHeadList(&kq->kq_disp); iw = CONTAINING_RECORD(l, io_workitem, iw_listentry); - REMOVE_LIST_HEAD((&kq->kq_med)); - INIT_LIST_HEAD(l); - if (iw->iw_func == NULL) { - l = kq->kq_med.nle_flink; + InitializeListHead((&iw->iw_listentry)); + if (iw->iw_func == NULL) continue; - } mtx_unlock_spin(&kq->kq_lock); MSCALL2(iw->iw_func, iw->iw_dobj, iw->iw_ctx); mtx_lock_spin(&kq->kq_lock); - l = kq->kq_med.nle_flink; } - kq->kq_state = NDIS_PSTATE_SLEEPING; - mtx_unlock_spin(&kq->kq_lock); } mtx_destroy(&kq->kq_lock); + #if __FreeBSD_version < 502113 mtx_lock(&Giant); #endif @@ -2195,9 +2630,8 @@ ntoskrnl_destroy_workitem_threads(void) for (i = 0; i < WORKITEM_THREADS; i++) { kq = wq_queues + i; - kq->kq_exit = 1; - KeSetEvent(&kq->kq_proc, 0, FALSE); + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_dead, 0, 0, TRUE, NULL); } @@ -2215,7 +2649,7 @@ IoAllocateWorkItem(dobj) if (iw == NULL) return(NULL); - INIT_LIST_HEAD(&iw->iw_listentry); + InitializeListHead(&iw->iw_listentry); iw->iw_dobj = dobj; mtx_lock(&ntoskrnl_dispatchlock); @@ -2241,21 +2675,38 @@ IoQueueWorkItem(iw, iw_func, qtype, ctx) uint32_t qtype; void *ctx; { - int state; kdpc_queue *kq; + list_entry *l; + io_workitem *cur; iw->iw_func = iw_func; iw->iw_ctx = ctx; - kq = wq_queues + iw->iw_idx; mtx_lock_spin(&kq->kq_lock); - INSERT_LIST_TAIL((&kq->kq_med), (&iw->iw_listentry)); - state = kq->kq_state; + + /* + * Traverse the list and make sure this workitem hasn't + * already been inserted. Queuing the same workitem + * twice will hose the list but good. + */ + + l = kq->kq_disp.nle_flink; + while (l != &kq->kq_disp) { + cur = CONTAINING_RECORD(l, io_workitem, iw_listentry); + if (cur == iw) { + /* Already queued -- do nothing. */ + mtx_unlock_spin(&kq->kq_lock); + return; + } + l = l->nle_flink; + } + + InsertTailList((&kq->kq_disp), (&iw->iw_listentry)); mtx_unlock_spin(&kq->kq_lock); - if (state == NDIS_PSTATE_SLEEPING) - KeSetEvent(&kq->kq_proc, 0, FALSE); + + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); return; } @@ -2278,6 +2729,31 @@ ntoskrnl_workitem(dobj, arg) return; } +/* + * The ExQueueWorkItem() API is deprecated in Windows XP. Microsoft + * warns that it's unsafe and to use IoQueueWorkItem() instead. The + * problem with ExQueueWorkItem() is that it can't guard against + * the condition where a driver submits a job to the work queue and + * is then unloaded before the job is able to run. IoQueueWorkItem() + * acquires a reference to the device's device_object via the + * object manager and retains it until after the job has completed, + * which prevents the driver from being unloaded before the job + * runs. (We don't currently support this behavior, though hopefully + * that will change once the object manager API is fleshed out a bit.) + * + * Having said all that, the ExQueueWorkItem() API remains, because + * there are still other parts of Windows that use it, including + * NDIS itself: NdisScheduleWorkItem() calls ExQueueWorkItem(). + * We fake up the ExQueueWorkItem() API on top of our implementation + * of IoQueueWorkItem(). Workitem thread #3 is reserved exclusively + * for ExQueueWorkItem() jobs, and we pass a pointer to the work + * queue item (provided by the caller) in to IoAllocateWorkItem() + * instead of the device_object. We need to save this pointer so + * we can apply a sanity check: as with the DPC queue and other + * workitem queues, we can't allow the same work queue item to + * be queued twice. If it's already pending, we silently return + */ + void ExQueueWorkItem(w, qtype) work_queue_item *w; @@ -2285,6 +2761,33 @@ ExQueueWorkItem(w, qtype) { io_workitem *iw; io_workitem_func iwf; + kdpc_queue *kq; + list_entry *l; + io_workitem *cur; + + + /* + * We need to do a special sanity test to make sure + * the ExQueueWorkItem() API isn't used to queue + * the same workitem twice. Rather than checking the + * io_workitem pointer itself, we test the attached + * device object, which is really a pointer to the + * legacy work queue item structure. + */ + + kq = wq_queues + WORKITEM_LEGACY_THREAD; + mtx_lock_spin(&kq->kq_lock); + l = kq->kq_disp.nle_flink; + while (l != &kq->kq_disp) { + cur = CONTAINING_RECORD(l, io_workitem, iw_listentry); + if (cur->iw_dobj == (device_object *)w) { + /* Already queued -- do nothing. */ + mtx_unlock_spin(&kq->kq_lock); + return; + } + l = l->nle_flink; + } + mtx_unlock_spin(&kq->kq_lock); iw = IoAllocateWorkItem((device_object *)w); if (iw == NULL) @@ -2316,33 +2819,33 @@ RtlCompareMemory(s1, s2, len) return(total); } -static void +void RtlInitAnsiString(dst, src) - ndis_ansi_string *dst; + ansi_string *dst; char *src; { - ndis_ansi_string *a; + ansi_string *a; a = dst; if (a == NULL) return; if (src == NULL) { - a->nas_len = a->nas_maxlen = 0; - a->nas_buf = NULL; + a->as_len = a->as_maxlen = 0; + a->as_buf = NULL; } else { - a->nas_buf = src; - a->nas_len = a->nas_maxlen = strlen(src); + a->as_buf = src; + a->as_len = a->as_maxlen = strlen(src); } return; } -static void +void RtlInitUnicodeString(dst, src) - ndis_unicode_string *dst; + unicode_string *dst; uint16_t *src; { - ndis_unicode_string *u; + unicode_string *u; int i; u = dst; @@ -2364,7 +2867,7 @@ RtlInitUnicodeString(dst, src) ndis_status RtlUnicodeStringToInteger(ustr, base, val) - ndis_unicode_string *ustr; + unicode_string *ustr; uint32_t base; uint32_t *val; { @@ -2410,31 +2913,31 @@ RtlUnicodeStringToInteger(ustr, base, val) astr++; } - ndis_unicode_to_ascii(uchr, len, &astr); + ntoskrnl_unicode_to_ascii(uchr, astr, len); *val = strtoul(abuf, NULL, base); return(NDIS_STATUS_SUCCESS); } -static void +void RtlFreeUnicodeString(ustr) - ndis_unicode_string *ustr; + unicode_string *ustr; { if (ustr->us_buf == NULL) return; - free(ustr->us_buf, M_DEVBUF); + ExFreePool(ustr->us_buf); ustr->us_buf = NULL; return; } -static void +void RtlFreeAnsiString(astr) - ndis_ansi_string *astr; + ansi_string *astr; { - if (astr->nas_buf == NULL) + if (astr->as_buf == NULL) return; - free(astr->nas_buf, M_DEVBUF); - astr->nas_buf = NULL; + ExFreePool(astr->as_buf); + astr->as_buf = NULL; return; } @@ -2512,13 +3015,12 @@ KeInitializeMutex(kmutex, level) kmutant *kmutex; uint32_t level; { - INIT_LIST_HEAD((&kmutex->km_header.dh_waitlisthead)); + InitializeListHead((&kmutex->km_header.dh_waitlisthead)); kmutex->km_abandoned = FALSE; kmutex->km_apcdisable = 1; - kmutex->km_header.dh_sigstate = TRUE; - kmutex->km_header.dh_type = EVENT_TYPE_SYNC; - kmutex->km_header.dh_size = OTYPE_MUTEX; - kmutex->km_acquirecnt = 0; + kmutex->km_header.dh_sigstate = 1; + kmutex->km_header.dh_type = DISP_TYPE_MUTANT; + kmutex->km_header.dh_size = sizeof(kmutant) / sizeof(uint32_t); kmutex->km_ownerthread = NULL; return; } @@ -2528,19 +3030,26 @@ KeReleaseMutex(kmutex, kwait) kmutant *kmutex; uint8_t kwait; { + uint32_t prevstate; + mtx_lock(&ntoskrnl_dispatchlock); - if (kmutex->km_ownerthread != curthread->td_proc) { + prevstate = kmutex->km_header.dh_sigstate; + if (kmutex->km_ownerthread != curthread) { mtx_unlock(&ntoskrnl_dispatchlock); return(STATUS_MUTANT_NOT_OWNED); } - kmutex->km_acquirecnt--; - if (kmutex->km_acquirecnt == 0) { + + kmutex->km_header.dh_sigstate++; + kmutex->km_abandoned = FALSE; + + if (kmutex->km_header.dh_sigstate == 1) { kmutex->km_ownerthread = NULL; - ntoskrnl_wakeup(&kmutex->km_header); + ntoskrnl_waittest(&kmutex->km_header, IO_NO_INCREMENT); } + mtx_unlock(&ntoskrnl_dispatchlock); - return(kmutex->km_acquirecnt); + return(prevstate); } static uint32_t @@ -2556,10 +3065,13 @@ KeInitializeEvent(kevent, type, state) uint32_t type; uint8_t state; { - INIT_LIST_HEAD((&kevent->k_header.dh_waitlisthead)); + InitializeListHead((&kevent->k_header.dh_waitlisthead)); kevent->k_header.dh_sigstate = state; - kevent->k_header.dh_type = type; - kevent->k_header.dh_size = OTYPE_EVENT; + if (type == EVENT_TYPE_NOTIFY) + kevent->k_header.dh_type = DISP_TYPE_NOTIFICATION_EVENT; + else + kevent->k_header.dh_type = DISP_TYPE_SYNCHRONIZATION_EVENT; + kevent->k_header.dh_size = sizeof(nt_kevent) / sizeof(uint32_t); return; } @@ -2584,10 +3096,49 @@ KeSetEvent(kevent, increment, kwait) uint8_t kwait; { uint32_t prevstate; + wait_block *w; + nt_dispatch_header *dh; + struct thread *td; + wb_ext *we; mtx_lock(&ntoskrnl_dispatchlock); prevstate = kevent->k_header.dh_sigstate; - ntoskrnl_wakeup(&kevent->k_header); + dh = &kevent->k_header; + + if (IsListEmpty(&dh->dh_waitlisthead)) + /* + * If there's nobody in the waitlist, just set + * the state to signalled. + */ + dh->dh_sigstate = 1; + else { + /* + * Get the first waiter. If this is a synchronization + * event, just wake up that one thread (don't bother + * setting the state to signalled since we're supposed + * to automatically clear synchronization events anyway). + * + * If it's a notification event, or the the first + * waiter is doing a WAITTYPE_ALL wait, go through + * the full wait satisfaction process. + */ + w = CONTAINING_RECORD(dh->dh_waitlisthead.nle_flink, + wait_block, wb_waitlist); + we = w->wb_ext; + td = we->we_td; + if (kevent->k_header.dh_type == DISP_TYPE_NOTIFICATION_EVENT || + w->wb_waittype == WAITTYPE_ALL) { + if (prevstate == 0) { + dh->dh_sigstate = 1; + ntoskrnl_waittest(dh, increment); + } + } else { + w->wb_awakened |= TRUE; + cv_broadcastpri(&we->we_cv, w->wb_oldpri - + (increment * 4)); + } + } + mtx_unlock(&ntoskrnl_dispatchlock); return(prevstate); @@ -2608,6 +3159,40 @@ KeReadStateEvent(kevent) return(kevent->k_header.dh_sigstate); } +/* + * The object manager in Windows is responsible for managing + * references and access to various types of objects, including + * device_objects, events, threads, timers and so on. However, + * there's a difference in the way objects are handled in user + * mode versus kernel mode. + * + * In user mode (i.e. Win32 applications), all objects are + * managed by the object manager. For example, when you create + * a timer or event object, you actually end up with an + * object_header (for the object manager's bookkeeping + * purposes) and an object body (which contains the actual object + * structure, e.g. ktimer, kevent, etc...). This allows Windows + * to manage resource quotas and to enforce access restrictions + * on basically every kind of system object handled by the kernel. + * + * However, in kernel mode, you only end up using the object + * manager some of the time. For example, in a driver, you create + * a timer object by simply allocating the memory for a ktimer + * structure and initializing it with KeInitializeTimer(). Hence, + * the timer has no object_header and no reference counting or + * security/resource checks are done on it. The assumption in + * this case is that if you're running in kernel mode, you know + * what you're doing, and you're already at an elevated privilege + * anyway. + * + * There are some exceptions to this. The two most important ones + * for our purposes are device_objects and threads. We need to use + * the object manager to do reference counting on device_objects, + * and for threads, you can only get a pointer to a thread's + * dispatch header by using ObReferenceObjectByHandle() on the + * handle returned by PsCreateSystemThread(). + */ + static ndis_status ObReferenceObjectByHandle(handle, reqaccess, otype, accessmode, object, handleinfo) @@ -2624,9 +3209,12 @@ ObReferenceObjectByHandle(handle, reqaccess, otype, if (nr == NULL) return(NDIS_STATUS_FAILURE); - INIT_LIST_HEAD((&nr->no_dh.dh_waitlisthead)); + InitializeListHead((&nr->no_dh.dh_waitlisthead)); nr->no_obj = handle; - nr->no_dh.dh_size = OTYPE_THREAD; + nr->no_dh.dh_type = DISP_TYPE_THREAD; + nr->no_dh.dh_sigstate = 0; + nr->no_dh.dh_size = (uint8_t)(sizeof(struct thread) / + sizeof(uint32_t)); TAILQ_INSERT_TAIL(&ntoskrnl_reflist, nr, link); *object = nr; @@ -2728,7 +3316,8 @@ PsTerminateSystemThread(status) TAILQ_FOREACH(nr, &ntoskrnl_reflist, link) { if (nr->no_obj != curthread->td_proc) continue; - ntoskrnl_wakeup(&nr->no_dh); + nr->no_dh.dh_sigstate = 1; + ntoskrnl_waittest(&nr->no_dh, IO_NO_INCREMENT); break; } mtx_unlock(&ntoskrnl_dispatchlock); @@ -2773,14 +3362,21 @@ ntoskrnl_timercall(arg) ktimer *timer; struct timeval tv; - mtx_unlock(&Giant); - mtx_lock(&ntoskrnl_dispatchlock); timer = arg; + callout_init(timer->k_callout, CALLOUT_MPSAFE); + + /* Mark the timer as no longer being on the timer queue. */ + timer->k_header.dh_inserted = FALSE; + /* Now signal the object and satisfy any waits on it. */ + + timer->k_header.dh_sigstate = 1; + ntoskrnl_waittest(&timer->k_header, IO_NO_INCREMENT); + /* * If this is a periodic timer, re-arm it * so it will fire again. We do this before @@ -2794,18 +3390,19 @@ ntoskrnl_timercall(arg) tv.tv_sec = 0; tv.tv_usec = timer->k_period * 1000; timer->k_header.dh_inserted = TRUE; - timer->k_handle = timeout(ntoskrnl_timercall, - timer, tvtohz(&tv)); + timer->k_callout = &ntoskrnl_callout[ntoskrnl_callidx]; + CALLOUT_INC(ntoskrnl_callidx); + callout_reset(timer->k_callout, tvtohz(&tv), + ntoskrnl_timercall, timer); } + /* If there's a DPC associated with the timer, queue it up. */ + if (timer->k_dpc != NULL) KeInsertQueueDpc(timer->k_dpc, NULL, NULL); - ntoskrnl_wakeup(&timer->k_header); mtx_unlock(&ntoskrnl_dispatchlock); - mtx_lock(&Giant); - return; } @@ -2830,12 +3427,14 @@ KeInitializeTimerEx(timer, type) return; bzero((char *)timer, sizeof(ktimer)); - INIT_LIST_HEAD((&timer->k_header.dh_waitlisthead)); + InitializeListHead((&timer->k_header.dh_waitlisthead)); timer->k_header.dh_sigstate = FALSE; timer->k_header.dh_inserted = FALSE; - timer->k_header.dh_type = type; - timer->k_header.dh_size = OTYPE_TIMER; - callout_handle_init(&timer->k_handle); + if (type == EVENT_TYPE_NOTIFY) + timer->k_header.dh_type = DISP_TYPE_NOTIFICATION_TIMER; + else + timer->k_header.dh_type = DISP_TYPE_SYNCHRONIZATION_TIMER; + timer->k_header.dh_size = sizeof(ktimer) / sizeof(uint32_t); return; } @@ -2868,18 +3467,27 @@ ntoskrnl_dpc_thread(arg) kq = arg; - INIT_LIST_HEAD(&kq->kq_high); - INIT_LIST_HEAD(&kq->kq_low); - INIT_LIST_HEAD(&kq->kq_med); + InitializeListHead(&kq->kq_disp); kq->kq_td = curthread; kq->kq_exit = 0; - kq->kq_state = NDIS_PSTATE_SLEEPING; mtx_init(&kq->kq_lock, "NDIS thread lock", NULL, MTX_SPIN); KeInitializeEvent(&kq->kq_proc, EVENT_TYPE_SYNC, FALSE); KeInitializeEvent(&kq->kq_done, EVENT_TYPE_SYNC, FALSE); KeInitializeEvent(&kq->kq_dead, EVENT_TYPE_SYNC, FALSE); + /* + * Elevate our priority. DPCs are used to run interrupt + * handlers, and they should trigger as soon as possible + * once scheduled by an ISR. + */ + + mtx_lock_spin(&sched_lock); sched_pin(); + sched_prio(curthread, PRI_MIN_KERN); +#if __FreeBSD_version < 600000 + curthread->td_base_pri = PRI_MIN_KERN; +#endif + mtx_unlock_spin(&sched_lock); while (1) { KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_proc, @@ -2889,59 +3497,23 @@ ntoskrnl_dpc_thread(arg) if (kq->kq_exit) { mtx_unlock_spin(&kq->kq_lock); - KeSetEvent(&kq->kq_dead, 0, FALSE); + KeSetEvent(&kq->kq_dead, IO_NO_INCREMENT, FALSE); break; } - kq->kq_state = NDIS_PSTATE_RUNNING; - - /* Process high importance list first. */ - - l = kq->kq_high.nle_flink; - while (l != &kq->kq_high) { - d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); - REMOVE_LIST_ENTRY((&d->k_dpclistentry)); - INIT_LIST_HEAD((&d->k_dpclistentry)); - d->k_lock = NULL; - mtx_unlock_spin(&kq->kq_lock); - ntoskrnl_run_dpc(d); - mtx_lock_spin(&kq->kq_lock); - l = kq->kq_high.nle_flink; - } - - /* Now the medium importance list. */ - - l = kq->kq_med.nle_flink; - while (l != &kq->kq_med) { - d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); - REMOVE_LIST_ENTRY((&d->k_dpclistentry)); - INIT_LIST_HEAD((&d->k_dpclistentry)); - d->k_lock = NULL; - mtx_unlock_spin(&kq->kq_lock); - ntoskrnl_run_dpc(d); - mtx_lock_spin(&kq->kq_lock); - l = kq->kq_med.nle_flink; - } - - /* And finally the low importance list. */ - - l = kq->kq_low.nle_flink; - while (l != &kq->kq_low) { + while (!IsListEmpty(&kq->kq_disp)) { + l = RemoveHeadList((&kq->kq_disp)); d = CONTAINING_RECORD(l, kdpc, k_dpclistentry); - REMOVE_LIST_ENTRY((&d->k_dpclistentry)); - INIT_LIST_HEAD((&d->k_dpclistentry)); + InitializeListHead((&d->k_dpclistentry)); d->k_lock = NULL; mtx_unlock_spin(&kq->kq_lock); ntoskrnl_run_dpc(d); mtx_lock_spin(&kq->kq_lock); - l = kq->kq_low.nle_flink; } - kq->kq_state = NDIS_PSTATE_SLEEPING; - mtx_unlock_spin(&kq->kq_lock); - KeSetEvent(&kq->kq_done, 0, FALSE); + KeSetEvent(&kq->kq_done, IO_NO_INCREMENT, FALSE); } @@ -2995,7 +3567,6 @@ ntoskrnl_destroy_dpc_threads(void) KeInitializeDpc(&dpc, NULL, NULL); KeSetTargetProcessorDpc(&dpc, i); KeInsertQueueDpc(&dpc, NULL, NULL); - KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_dead, 0, 0, TRUE, NULL); } @@ -3019,7 +3590,11 @@ ntoskrnl_insert_dpc(head, dpc) l = l->nle_flink; } - INSERT_LIST_TAIL((head), (&dpc->k_dpclistentry)); + if (dpc->k_importance == KDPC_IMPORTANCE_HIGH) + InsertHeadList((head), (&dpc->k_dpclistentry)); + else + InsertTailList((head), (&dpc->k_dpclistentry)); + return (TRUE); } @@ -3037,13 +3612,12 @@ KeInitializeDpc(dpc, dpcfunc, dpcctx) dpc->k_deferredctx = dpcctx; dpc->k_num = KDPC_CPU_DEFAULT; dpc->k_importance = KDPC_IMPORTANCE_MEDIUM; - dpc->k_num = KeGetCurrentProcessorNumber(); /* * In case someone tries to dequeue a DPC that * hasn't been queued yet. */ - dpc->k_lock = NULL /*&ntoskrnl_dispatchlock*/; - INIT_LIST_HEAD((&dpc->k_dpclistentry)); + dpc->k_lock = NULL; + InitializeListHead((&dpc->k_dpclistentry)); return; } @@ -3056,7 +3630,6 @@ KeInsertQueueDpc(dpc, sysarg1, sysarg2) { kdpc_queue *kq; uint8_t r; - int state; if (dpc == NULL) return(FALSE); @@ -3081,21 +3654,15 @@ KeInsertQueueDpc(dpc, sysarg1, sysarg2) */ mtx_lock_spin(&kq->kq_lock); - if (dpc->k_importance == KDPC_IMPORTANCE_HIGH) - r = ntoskrnl_insert_dpc(&kq->kq_high, dpc); - else if (dpc->k_importance == KDPC_IMPORTANCE_LOW) - r = ntoskrnl_insert_dpc(&kq->kq_low, dpc); - else - r = ntoskrnl_insert_dpc(&kq->kq_med, dpc); + r = ntoskrnl_insert_dpc(&kq->kq_disp, dpc); if (r == TRUE) dpc->k_lock = &kq->kq_lock; - state = kq->kq_state; mtx_unlock_spin(&kq->kq_lock); + if (r == FALSE) return(r); - if (state == NDIS_PSTATE_SLEEPING) - KeSetEvent(&kq->kq_proc, 0, FALSE); + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); return(r); } @@ -3104,21 +3671,26 @@ uint8_t KeRemoveQueueDpc(dpc) kdpc *dpc; { + struct mtx *lock; + if (dpc == NULL) return(FALSE); - if (dpc->k_lock == NULL) + lock = dpc->k_lock; + + if (lock == NULL) return(FALSE); - mtx_lock_spin((struct mtx*)(dpc->k_lock)); + mtx_lock_spin(lock); + dpc->k_lock = NULL; if (dpc->k_dpclistentry.nle_flink == &dpc->k_dpclistentry) { - mtx_unlock_spin((struct mtx*)(dpc->k_lock)); + mtx_unlock_spin(lock); return(FALSE); } - REMOVE_LIST_ENTRY((&dpc->k_dpclistentry)); - INIT_LIST_HEAD((&dpc->k_dpclistentry)); - mtx_unlock_spin((struct mtx*)(dpc->k_lock)); + RemoveEntryList((&dpc->k_dpclistentry)); + InitializeListHead((&dpc->k_dpclistentry)); + mtx_unlock_spin(lock); return(TRUE); } @@ -3162,7 +3734,7 @@ KeFlushQueuedDpcs(void) for (i = 0; i < mp_ncpus; i++) { kq = kq_queues + i; - KeSetEvent(&kq->kq_proc, 0, FALSE); + KeSetEvent(&kq->kq_proc, IO_NO_INCREMENT, FALSE); KeWaitForSingleObject((nt_dispatch_header *)&kq->kq_done, 0, 0, TRUE, NULL); } @@ -3193,7 +3765,7 @@ KeSetTimerEx(timer, duetime, period, dpc) mtx_lock(&ntoskrnl_dispatchlock); if (timer->k_header.dh_inserted == TRUE) { - untimeout(ntoskrnl_timercall, timer, timer->k_handle); + callout_stop(timer->k_callout); timer->k_header.dh_inserted = FALSE; pending = TRUE; } else @@ -3220,7 +3792,10 @@ KeSetTimerEx(timer, duetime, period, dpc) } timer->k_header.dh_inserted = TRUE; - timer->k_handle = timeout(ntoskrnl_timercall, timer, tvtohz(&tv)); + timer->k_callout = &ntoskrnl_callout[ntoskrnl_callidx]; + CALLOUT_INC(ntoskrnl_callidx); + callout_reset(timer->k_callout, tvtohz(&tv), + ntoskrnl_timercall, timer); mtx_unlock(&ntoskrnl_dispatchlock); @@ -3236,6 +3811,12 @@ KeSetTimer(timer, duetime, dpc) return (KeSetTimerEx(timer, duetime, 0, dpc)); } +/* + * The Windows DDK documentation seems to say that cancelling + * a timer that has a DPC will result in the DPC also being + * cancelled, but this isn't really the case. + */ + uint8_t KeCancelTimer(timer) ktimer *timer; @@ -3247,14 +3828,12 @@ KeCancelTimer(timer) mtx_lock(&ntoskrnl_dispatchlock); + pending = timer->k_header.dh_inserted; + if (timer->k_header.dh_inserted == TRUE) { - untimeout(ntoskrnl_timercall, timer, timer->k_handle); - if (timer->k_dpc != NULL) - KeRemoveQueueDpc(timer->k_dpc); timer->k_header.dh_inserted = FALSE; - pending = TRUE; - } else - pending = KeRemoveQueueDpc(timer->k_dpc); + callout_stop(timer->k_callout); + } mtx_unlock(&ntoskrnl_dispatchlock); @@ -3299,6 +3878,8 @@ image_patch_table ntoskrnl_functbl[] = { IMPORT_CFUNC(strncpy, 0), IMPORT_CFUNC(strcpy, 0), IMPORT_CFUNC(strlen, 0), + IMPORT_CFUNC_MAP(toupper, ntoskrnl_toupper, 0), + IMPORT_CFUNC_MAP(tolower, ntoskrnl_tolower, 0), IMPORT_CFUNC_MAP(strstr, ntoskrnl_strstr, 0), IMPORT_CFUNC_MAP(strchr, index, 0), IMPORT_CFUNC(memcpy, 0), @@ -3311,6 +3892,8 @@ image_patch_table ntoskrnl_functbl[] = { IMPORT_SFUNC(IoAcquireCancelSpinLock, 1), IMPORT_SFUNC(IoReleaseCancelSpinLock, 1), IMPORT_SFUNC(IoCancelIrp, 1), + IMPORT_SFUNC(IoConnectInterrupt, 11), + IMPORT_SFUNC(IoDisconnectInterrupt, 1), IMPORT_SFUNC(IoCreateDevice, 7), IMPORT_SFUNC(IoDeleteDevice, 1), IMPORT_SFUNC(IoGetAttachedDevice, 1), @@ -3324,6 +3907,7 @@ image_patch_table ntoskrnl_functbl[] = { IMPORT_SFUNC(IoMakeAssociatedIrp, 2), IMPORT_SFUNC(IoFreeIrp, 1), IMPORT_SFUNC(IoInitializeIrp, 3), + IMPORT_SFUNC(KeSynchronizeExecution, 3), IMPORT_SFUNC(KeWaitForSingleObject, 5), IMPORT_SFUNC(KeWaitForMultipleObjects, 8), IMPORT_SFUNC(_allmul, 4), diff --git a/sys/compat/ndis/winx32_wrap.S b/sys/compat/ndis/winx32_wrap.S index 4fadfb5..f5d24ba 100644 --- a/sys/compat/ndis/winx32_wrap.S +++ b/sys/compat/ndis/winx32_wrap.S @@ -82,6 +82,13 @@ * we only know the absolute addresses of the thunk and unthunk * routines. So we need to make sure the templates have enough * room in them for the full address. + * + * Also note that when we call the a thunk/unthunk routine after + * invoking a wrapped function, we have to make sure to preserve + * the value returned from that function. Most functions return + * a 32-bit value in %eax, however some routines return 64-bit + * values, which span both %eax and %edx. Consequently, we have + * to preserve both registers. */ /* @@ -114,13 +121,16 @@ ENTRY(x86_stdcall_wrap) x86_stdcall_wrap_call: movl $0,%eax call *%eax # jump to routine - mov %eax,%esi # preserve return val + push %eax # preserve return val + push %edx movl $ctxsw_utow, %eax call *%eax # thunk + pop %edx + pop %eax # restore return val + add $64,%esp # clean the stack - mov %esi,%eax # restore return val pop %edi pop %esi x86_stdcall_wrap_arg: @@ -151,12 +161,14 @@ ENTRY(x86_stdcall_call) call ctxsw_utow # thunk call *12(%edi) # branch to stdcall routine - mov %eax,%esi # preserve return val + push %eax # preserve return val + push %edx call ctxsw_wtou # unthunk + pop %edx + pop %eax # restore return val mov %edi,%esp # restore stack - mov %esi,%eax # restore return val pop %edi # restore %edi pop %esi # and %esi ret @@ -188,10 +200,12 @@ x86_fastcall_wrap_call: mov $0,%eax call *%eax # branch to fastcall routine push %eax # preserve return val + push %edx movl $ctxsw_utow, %eax call *%eax # thunk + pop %edx pop %eax # restore return val add $12,%esp # clean the stack x86_fastcall_wrap_arg: @@ -215,9 +229,11 @@ ENTRY(x86_fastcall_call) mov 16(%esp),%edx call *8(%esp) # branch to fastcall routine push %eax # preserve return val + push %edx call ctxsw_wtou # unthunk + pop %edx pop %eax # restore return val add $4,%esp # clean the stack ret diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c index 02a3dd6..3e67c52 100644 --- a/sys/dev/if_ndis/if_ndis.c +++ b/sys/dev/if_ndis/if_ndis.c @@ -29,7 +29,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * WPA support added by Arvind Srinivasan <arvind@celar.us> + * WPA support originally contributed by Arvind Srinivasan <arvind@celar.us> + * then hacked upon mercilessly by my. */ #include <sys/cdefs.h> @@ -49,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #endif + #include <net/if.h> #include <net/if_arp.h> #include <net/ethernet.h> @@ -119,12 +121,11 @@ static funcptr ndis_ticktask_wrap; static funcptr ndis_starttask_wrap; static funcptr ndis_resettask_wrap; -static void ndis_intr (void *); static void ndis_tick (void *); -static void ndis_ticktask (ndis_work_item *, void *); +static void ndis_ticktask (device_object *, void *); static void ndis_start (struct ifnet *); -static void ndis_starttask (ndis_work_item *, void *); -static void ndis_resettask (ndis_work_item *, void *); +static void ndis_starttask (device_object *, void *); +static void ndis_resettask (device_object *, void *); static int ndis_ioctl (struct ifnet *, u_long, caddr_t); static int ndis_wi_ioctl_get (struct ifnet *, u_long, caddr_t); static int ndis_wi_ioctl_set (struct ifnet *, u_long, caddr_t); @@ -140,8 +141,14 @@ static int ndis_probe_offload (struct ndis_softc *); static int ndis_set_offload (struct ndis_softc *); static void ndis_getstate_80211 (struct ndis_softc *); static void ndis_setstate_80211 (struct ndis_softc *); -static int ndis_add_key (struct ndis_softc *, - struct ieee80211req_key *, int16_t); +#ifdef IEEE80211_F_WPA +static int ndis_set_cipher (struct ndis_softc *, int); +static int ndis_set_wpa (struct ndis_softc *); +static int ndis_add_key (struct ieee80211com *, + const struct ieee80211_key *, const u_int8_t []); +static int ndis_del_key (struct ieee80211com *, + const struct ieee80211_key *); +#endif static void ndis_media_status (struct ifnet *, struct ifmediareq *); static void ndis_setmulti (struct ndis_softc *); @@ -243,7 +250,7 @@ ndis_setmulti(sc) &sc->ndis_filter, &len); if (error) device_printf (sc->ndis_dev, - "set filter failed: %d\n", error); + "set allmulti failed: %d\n", error); return; } @@ -294,7 +301,7 @@ out: error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, &sc->ndis_filter, &len); if (error) - device_printf (sc->ndis_dev, "set filter failed: %d\n", error); + device_printf (sc->ndis_dev, "set multi failed: %d\n", error); return; } @@ -468,33 +475,20 @@ ndis_attach(dev) int i; sc = device_get_softc(dev); +#if __FreeBSD_version < 600000 + sc->ifp = &sc->arpcom.ac_if; + ifp = sc->ifp; +#else ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { error = ENOSPC; goto fail; } +#endif ifp->if_softc = sc; - mtx_init(&sc->ndis_mtx, "ndis softc lock", - MTX_NETWORK_LOCK, MTX_DEF); - - /* - * Hook interrupt early, since calling the driver's - * init routine may trigger an interrupt. Note that - * we don't need to do any explicit interrupt setup - * for USB. - */ - - if (sc->ndis_iftype == PCMCIABus || sc->ndis_iftype == PCIBus) { - error = bus_setup_intr(dev, sc->ndis_irq, - INTR_TYPE_NET | INTR_MPSAFE, - ndis_intr, sc, &sc->ndis_intrhand); - - if (error) { - device_printf(dev, "couldn't set up irq\n"); - goto fail; - } - } + KeInitializeSpinLock(&sc->ndis_spinlock); + InitializeListHead(&sc->ndis_shlist); if (sc->ndis_iftype == PCMCIABus) { error = ndis_alloc_amem(sc); @@ -566,19 +560,22 @@ ndis_attach(dev) ndis_get_info(sc, OID_802_3_CURRENT_ADDRESS, &eaddr, &len); /* - * Figure out if we're allowed to use multipacket sends - * with this driver, and if so, how many. + * Figure out how big to make the TX buffer pool. */ - if (sc->ndis_chars->nmc_sendsingle_func && - sc->ndis_chars->nmc_sendmulti_func == NULL) { - sc->ndis_maxpkts = 1; - } else { - len = sizeof(sc->ndis_maxpkts); - ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS, - &sc->ndis_maxpkts, &len); + len = sizeof(sc->ndis_maxpkts); + if (ndis_get_info(sc, OID_GEN_MAXIMUM_SEND_PACKETS, + &sc->ndis_maxpkts, &len)) { + device_printf (dev, "failed to get max TX packets\n"); + error = ENXIO; + goto fail; } + /* Enforce some sanity, just in case. */ + + if (sc->ndis_maxpkts == 0) + sc->ndis_maxpkts = 10; + sc->ndis_txarray = malloc(sizeof(ndis_packet *) * sc->ndis_maxpkts, M_DEVBUF, M_NOWAIT|M_ZERO); @@ -645,7 +642,9 @@ ndis_attach(dev) uint32_t arg; int r; +#if __FreeBSD_version >= 600000 ic->ic_ifp = ifp; +#endif ic->ic_phytype = IEEE80211_T_DS; ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_IBSS; @@ -656,7 +655,7 @@ ndis_attach(dev) NULL, &len); if (r != ENOSPC) goto nonettypes; - ntl = malloc(len, M_DEVBUF, M_WAITOK|M_ZERO); + ntl = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); r = ndis_get_info(sc, OID_802_11_NETWORK_TYPES_SUPPORTED, ntl, &len); if (r != 0) { @@ -801,20 +800,73 @@ nonettypes: ic->ic_channels[i].ic_flags = chanflag; } + /* + * To test for WPA support, we need to see if we can + * set AUTHENTICATION_MODE to WPA and read it back + * successfully. + */ +#ifdef IEEE80211_F_WPA + i = sizeof(arg); + arg = NDIS_80211_AUTHMODE_WPA; + r = ndis_set_info(sc, + OID_802_11_AUTHENTICATION_MODE, &arg, &i); + if (r == 0) { + r = ndis_get_info(sc, + OID_802_11_AUTHENTICATION_MODE, &arg, &i); + if (r == 0 && arg == NDIS_80211_AUTHMODE_WPA) + ic->ic_caps |= IEEE80211_C_WPA; + } + + /* + * To test for supported ciphers, we set each + * available encryption type in descending order. + * If ENC3 works, then we have WEP, TKIP and AES. + * If only ENC2 works, then we have WEP and TKIP. + * If only ENC1 works, then we have just WEP. + */ i = sizeof(arg); - r = ndis_get_info(sc, OID_802_11_WEP_STATUS, &arg, &i); - if (arg != NDIS_80211_WEPSTAT_NOTSUPPORTED) + arg = NDIS_80211_WEPSTAT_ENC3ENABLED; + r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); + if (r == 0) { + ic->ic_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP| + IEEE80211_C_AES_CCM; + goto got_crypto; + } + arg = NDIS_80211_WEPSTAT_ENC2ENABLED; + r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); + if (r == 0) { + ic->ic_caps |= IEEE80211_C_WEP|IEEE80211_C_TKIP; + goto got_crypto; + } +#endif + arg = NDIS_80211_WEPSTAT_ENC1ENABLED; + r = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &i); + if (r == 0) ic->ic_caps |= IEEE80211_C_WEP; +#ifdef IEEE80211_F_WPA +got_crypto: +#endif i = sizeof(arg); r = ndis_get_info(sc, OID_802_11_POWER_MODE, &arg, &i); if (r == 0) ic->ic_caps |= IEEE80211_C_PMGT; bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr)); +#if __FreeBSD_version < 600000 + ieee80211_ifattach(ifp); + ieee80211_media_init(ifp, ieee80211_media_change, + ndis_media_status); +#else ieee80211_ifattach(ic); ieee80211_media_init(ic, ieee80211_media_change, ndis_media_status); +#endif ic->ic_ibss_chan = IEEE80211_CHAN_ANYC; ic->ic_bss->ni_chan = ic->ic_ibss_chan; +#ifdef IEEE80211_F_WPA + /* install key handing routines */ + ic->ic_crypto.cs_key_set = ndis_add_key; + ic->ic_crypto.cs_key_delete = ndis_del_key; +#endif } else { ifmedia_init(&sc->ifmedia, IFM_IMASK, ndis_ifmedia_upd, ndis_ifmedia_sts); @@ -833,12 +885,9 @@ nonettypes: sc->ndis_block->nmb_statusdone_func = ndis_linksts_done_wrap; /* Set up work item handlers. */ - NdisInitializeWorkItem(&sc->ndis_tickitem, - (ndis_proc)ndis_ticktask_wrap, sc); - NdisInitializeWorkItem(&sc->ndis_startitem, - (ndis_proc)ndis_starttask_wrap, ifp); - NdisInitializeWorkItem(&sc->ndis_resetitem, - (ndis_proc)ndis_resettask_wrap, sc); + sc->ndis_tickitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_startitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndis_resetitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); @@ -878,16 +927,25 @@ ndis_detach(dev) NDIS_UNLOCK(sc); ndis_stop(sc); if (sc->ndis_80211) +#if __FreeBSD_version < 600000 + ieee80211_ifdetach(ifp); +#else ieee80211_ifdetach(&sc->ic); +#endif else ether_ifdetach(ifp); } else NDIS_UNLOCK(sc); + if (sc->ndis_tickitem != NULL) + IoFreeWorkItem(sc->ndis_tickitem); + if (sc->ndis_startitem != NULL) + IoFreeWorkItem(sc->ndis_startitem); + if (sc->ndis_resetitem != NULL) + IoFreeWorkItem(sc->ndis_resetitem); + bus_generic_detach(dev); - if (sc->ndis_intrhand) - bus_teardown_intr(dev, sc->ndis_irq, sc->ndis_intrhand); if (sc->ndis_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ndis_irq); if (sc->ndis_res_io) @@ -900,8 +958,10 @@ ndis_detach(dev) bus_release_resource(dev, SYS_RES_MEMORY, sc->ndis_altmem_rid, sc->ndis_res_altmem); +#if __FreeBSD_version >= 600000 if (ifp != NULL) if_free(ifp); +#endif if (sc->ndis_iftype == PCMCIABus) ndis_free_amem(sc); @@ -939,8 +999,6 @@ ndis_detach(dev) sysctl_ctx_free(&sc->ndis_ctx); #endif - mtx_destroy(&sc->ndis_mtx); - return(0); } @@ -1047,7 +1105,7 @@ ndis_rxeof_eth(adapter, ctx, addr, hdr, hdrlen, lookahead, lookaheadlen, pktlen) KeAcquireSpinLock(&block->nmb_lock, &irql); - INSERT_LIST_TAIL((&block->nmb_packetlist), + InsertTailList((&block->nmb_packetlist), ((list_entry *)&p->u.np_clrsvd.np_miniport_rsvd)); KeReleaseSpinLock(&block->nmb_lock, irql); @@ -1099,8 +1157,8 @@ ndis_rxeof_xfr(dpc, adapter, sysarg1, sysarg2) KeAcquireSpinLockAtDpcLevel(&block->nmb_lock); l = block->nmb_packetlist.nle_flink; - while(l != &block->nmb_packetlist) { - REMOVE_LIST_HEAD((&block->nmb_packetlist)); + while(!IsListEmpty(&block->nmb_packetlist)) { + l = RemoveHeadList((&block->nmb_packetlist)); p = CONTAINING_RECORD(l, ndis_packet, u.np_clrsvd.np_miniport_rsvd); @@ -1223,6 +1281,7 @@ ndis_rxeof(adapter, packets, pktcnt) if (p->np_oob.npo_status == NDIS_STATUS_SUCCESS) ndis_return_packet(sc, p); } else { +#ifdef notdef if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) { m = m_dup(m0, M_DONTWAIT); /* @@ -1241,6 +1300,18 @@ ndis_rxeof(adapter, packets, pktcnt) m0 = m; } else p->np_oob.npo_status = NDIS_STATUS_PENDING; +#endif + m = m_dup(m0, M_DONTWAIT); + if (p->np_oob.npo_status == NDIS_STATUS_RESOURCES) + p->np_refcnt++; + else + p->np_oob.npo_status = NDIS_STATUS_PENDING; + m_freem(m0); + if (m == NULL) { + ifp->if_ierrors++; + continue; + } + m0 = m; m0->m_pkthdr.rcvif = ifp; ifp->if_ipackets++; @@ -1308,12 +1379,15 @@ ndis_txeof(adapter, packet, status) ifp->if_opackets++; else ifp->if_oerrors++; + ifp->if_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; NDIS_UNLOCK(sc); - NdisScheduleWorkItem(&sc->ndis_startitem); + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, ifp); return; } @@ -1330,8 +1404,32 @@ ndis_linksts(adapter, status, sbuf, slen) block = adapter; sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + sc->ndis_sts = status; + + /* Event list is all full up, drop this one. */ - block->nmb_getstat = status; + NDIS_LOCK(sc); + if (sc->ndis_evt[sc->ndis_evtpidx].ne_sts) { + NDIS_UNLOCK(sc); + return; + } + + /* Cache the event. */ + + if (slen) { + sc->ndis_evt[sc->ndis_evtpidx].ne_buf = malloc(slen, + M_TEMP, M_NOWAIT); + if (sc->ndis_evt[sc->ndis_evtpidx].ne_buf == NULL) { + NDIS_UNLOCK(sc); + return; + } + bcopy((char *)sbuf, + sc->ndis_evt[sc->ndis_evtpidx].ne_buf, slen); + } + sc->ndis_evt[sc->ndis_evtpidx].ne_sts = status; + sc->ndis_evt[sc->ndis_evtpidx].ne_len = slen; + NDIS_EVTINC(sc->ndis_evtpidx); + NDIS_UNLOCK(sc); return; } @@ -1351,51 +1449,28 @@ ndis_linksts_done(adapter) if (!NDIS_INITIALIZED(sc)) return; - switch (block->nmb_getstat) { + switch (sc->ndis_sts) { case NDIS_STATUS_MEDIA_CONNECT: - NdisScheduleWorkItem(&sc->ndis_tickitem); - NdisScheduleWorkItem(&sc->ndis_startitem); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, ifp); break; case NDIS_STATUS_MEDIA_DISCONNECT: if (sc->ndis_link) - NdisScheduleWorkItem(&sc->ndis_tickitem); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); break; default: break; } - return; -} - -static void -ndis_intr(arg) - void *arg; -{ - struct ndis_softc *sc; - struct ifnet *ifp; - int is_our_intr = 0; - int call_isr = 0; - uint8_t irql; - ndis_miniport_interrupt *intr; - - sc = arg; - ifp = sc->ifp; - intr = sc->ndis_block->nmb_interrupt; + /* Notify possible listners of interface change. */ - if (intr == NULL || sc->ndis_block->nmb_miniportadapterctx == NULL) - return; - - KeAcquireSpinLock(&intr->ni_dpccountlock, &irql); - 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; - } - KeReleaseSpinLock(&intr->ni_dpccountlock, irql); - - if ((is_our_intr || call_isr)) - IoRequestDpc(sc->ndis_block->nmb_deviceobj, NULL, sc); + rt_ifmsg(ifp); return; } @@ -1410,7 +1485,9 @@ ndis_tick(xsc) sc = xsc; - NdisScheduleWorkItem(&sc->ndis_tickitem); + IoQueueWorkItem(sc->ndis_tickitem, + (io_workitem_func)ndis_ticktask_wrap, + WORKQUEUE_CRITICAL, sc); sc->ndis_stat_ch = timeout(ndis_tick, sc, hz * sc->ndis_block->nmb_checkforhangsecs); @@ -1420,17 +1497,17 @@ ndis_tick(xsc) } static void -ndis_ticktask(w, xsc) - ndis_work_item *w; +ndis_ticktask(d, xsc) + device_object *d; void *xsc; { struct ndis_softc *sc; + struct ieee80211com *ic; ndis_checkforhang_handler hangfunc; uint8_t rval; - ndis_media_state linkstate; - int error, len; sc = xsc; + ic = &sc->ic; hangfunc = sc->ndis_chars->nmc_checkhang_func; @@ -1443,31 +1520,28 @@ ndis_ticktask(w, xsc) } } - len = sizeof(linkstate); - error = ndis_get_info(sc, OID_GEN_MEDIA_CONNECT_STATUS, - (void *)&linkstate, &len); - NDIS_LOCK(sc); - - if (sc->ndis_link == 0 && linkstate == nmc_connected) { - device_printf(sc->ndis_dev, "link up\n"); + if (sc->ndis_link == 0 && + sc->ndis_sts == NDIS_STATUS_MEDIA_CONNECT) { sc->ndis_link = 1; NDIS_UNLOCK(sc); - if (sc->ndis_80211) + if (sc->ndis_80211) { ndis_getstate_80211(sc); + ic->ic_state = IEEE80211_S_RUN; + } NDIS_LOCK(sc); #ifdef LINK_STATE_UP - sc->ifp->if_link_state = LINK_STATE_UP; - rt_ifmsg(sc->ifp); + if_link_state_change(sc->ifp, LINK_STATE_UP); #endif /* LINK_STATE_UP */ } - if (sc->ndis_link == 1 && linkstate == nmc_disconnected) { - device_printf(sc->ndis_dev, "link down\n"); + if (sc->ndis_link == 1 && + sc->ndis_sts == NDIS_STATUS_MEDIA_DISCONNECT) { sc->ndis_link = 0; + if (sc->ndis_80211) + ic->ic_state = IEEE80211_S_ASSOC; #ifdef LINK_STATE_DOWN - sc->ifp->if_link_state = LINK_STATE_DOWN; - rt_ifmsg(sc->ifp); + if_link_state_change(sc->ifp, LINK_STATE_DOWN); #endif /* LINK_STATE_DOWN */ } @@ -1504,8 +1578,8 @@ ndis_map_sclist(arg, segs, nseg, mapsize, error) } static void -ndis_starttask(w, arg) - ndis_work_item *w; +ndis_starttask(d, arg) + device_object *d; void *arg; { struct ifnet *ifp; @@ -1659,10 +1733,17 @@ ndis_start(ifp) NDIS_UNLOCK(sc); - if (sc->ndis_maxpkts == 1) - ndis_send_packet(sc, p); - else + /* + * According to NDIS documentation, if a driver exports + * a MiniportSendPackets() routine, we prefer that over + * a MiniportSend() routine (which sends just a single + * packet). + */ + + if (sc->ndis_chars->nmc_sendmulti_func != NULL) ndis_send_packets(sc, p0, pcnt); + else + ndis_send_packet(sc, p); return; } @@ -1673,7 +1754,7 @@ ndis_init(xsc) { struct ndis_softc *sc = xsc; struct ifnet *ifp = sc->ifp; - int i, error; + int i, len, error; /* * Avoid reintializing the link unnecessarily. @@ -1688,6 +1769,7 @@ ndis_init(xsc) * Cancel pending I/O and free all RX/TX buffers. */ ndis_stop(sc); + if (ndis_init_nic(sc)) return; @@ -1703,15 +1785,23 @@ ndis_init(xsc) if (ifp->if_flags & IFF_PROMISC) sc->ndis_filter |= NDIS_PACKET_TYPE_PROMISCUOUS; - i = sizeof(sc->ndis_filter); + len = sizeof(sc->ndis_filter); error = ndis_set_info(sc, OID_GEN_CURRENT_PACKET_FILTER, - &sc->ndis_filter, &i); + &sc->ndis_filter, &len); if (error) device_printf (sc->ndis_dev, "set filter failed: %d\n", error); /* + * Set lookahead. + */ + + i = ifp->if_mtu; + len = sizeof(i); + ndis_set_info(sc, OID_GEN_CURRENT_LOOKAHEAD, &i, &len); + + /* * Program the multicast filter, if necessary. */ ndis_setmulti(sc); @@ -1731,6 +1821,10 @@ ndis_init(xsc) sc->ndis_txpending = sc->ndis_maxpkts; sc->ndis_link = 0; +#ifdef LINK_STATE_UNKNOWN + if_link_state_change(sc->ifp, LINK_STATE_UNKNOWN); +#endif /* LINK_STATE_UNKNOWN */ + ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; @@ -1818,12 +1912,148 @@ ndis_ifmedia_sts(ifp, ifmr) return; } +#ifdef IEEE80211_F_WPA +static int +ndis_set_cipher(sc, cipher) + struct ndis_softc *sc; + int cipher; +{ + struct ieee80211com *ic; + int rval = 0, len; + uint32_t arg, save; + + ic = &sc->ic; + + len = sizeof(arg); + + if (cipher == WPA_CSE_WEP40 || WPA_CSE_WEP104) { + if (!(ic->ic_caps & IEEE80211_C_WEP)) + return(ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC1ENABLED; + } + + if (cipher == WPA_CSE_TKIP) { + if (!(ic->ic_caps & IEEE80211_C_TKIP)) + return(ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC2ENABLED; + } + + if (cipher == WPA_CSE_CCMP) { + if (!(ic->ic_caps & IEEE80211_C_AES_CCM)) + return(ENOTSUP); + arg = NDIS_80211_WEPSTAT_ENC3ENABLED; + } + + save = arg; + rval = ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); + + if (rval) + return(rval); + + /* Check that the cipher was set correctly. */ + + len = sizeof(save); + rval = ndis_get_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); + + if (rval != 0 || arg != save) + return(ENODEV); + + return(0); +} + +/* + * WPA is hairy to set up. Do the work in a separate routine + * so we don't clutter the setstate function too much. + * Important yet undocumented fact: first we have to set the + * authentication mode, _then_ we enable the ciphers. If one + * of the WPA authentication modes isn't enabled, the driver + * might not permit the TKIP or AES ciphers to be selected. + */ + +static int +ndis_set_wpa(sc) + struct ndis_softc *sc; +{ + struct ieee80211com *ic; + struct ieee80211_ie_wpa *w; + struct ndis_ie *n; + char *pos; + uint32_t arg; + int i; + + ic = &sc->ic; + + /* + * Apparently, the only way for us to know what ciphers + * and key management/authentication mode to use is for + * us to inspect the optional information element (IE) + * stored in the 802.11 state machine. This IE should be + * supplied by the WPA supplicant. + */ + + w = (struct ieee80211_ie_wpa *)ic->ic_opt_ie; + + /* Check for the right kind of IE. */ + if (w->wpa_id != IEEE80211_ELEMID_VENDOR) + return(EINVAL); + + /* Skip over the ucast cipher OIDs. */ + pos = (char *)&w->wpa_uciphers[0]; + pos += w->wpa_uciphercnt * sizeof(struct ndis_ie); + + /* Skip over the authmode count. */ + pos += sizeof(u_int16_t); + + /* + * Check for the authentication modes. I'm + * pretty sure there's only supposed to be one. + */ + + n = (struct ndis_ie *)pos; + if (n->ni_val == WPA_ASE_NONE) + arg = NDIS_80211_AUTHMODE_WPANONE; + + if (n->ni_val == WPA_ASE_8021X_UNSPEC) + arg = NDIS_80211_AUTHMODE_WPA; + + if (n->ni_val == WPA_ASE_8021X_PSK) + arg = NDIS_80211_AUTHMODE_WPAPSK; + + i = sizeof(arg); + if (ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i)) + return(ENOTSUP); + i = sizeof(arg); + ndis_get_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &i); + + /* Now configure the desired ciphers. */ + + /* First, set up the multicast group cipher. */ + n = (struct ndis_ie *)&w->wpa_mcipher[0]; + + if (ndis_set_cipher(sc, n->ni_val)) + return(ENOTSUP); + + /* Now start looking around for the unicast ciphers. */ + pos = (char *)&w->wpa_uciphers[0]; + n = (struct ndis_ie *)pos; + + for (i = 0; i < w->wpa_uciphercnt; i++) { + if (ndis_set_cipher(sc, n->ni_val)) + return(ENOTSUP); + n++; + } + + return(0); +} +#endif + static void ndis_setstate_80211(sc) struct ndis_softc *sc; { struct ieee80211com *ic; ndis_80211_ssid ssid; + ndis_80211_macaddr bssid; ndis_80211_config config; ndis_80211_wep wep; int i, rval = 0, len; @@ -1836,6 +2066,12 @@ ndis_setstate_80211(sc) if (!NDIS_INITIALIZED(sc)) return; + /* Disassociate and turn off radio. */ + + len = sizeof(arg); + arg = 1; + ndis_set_info(sc, OID_802_11_DISASSOCIATE, &arg, &len); + /* Set network infrastructure mode. */ len = sizeof(arg); @@ -1849,25 +2085,83 @@ ndis_setstate_80211(sc) if (rval) device_printf (sc->ndis_dev, "set infra failed: %d\n", rval); + /* Set RTS threshold */ + + len = sizeof(arg); + arg = ic->ic_rtsthreshold; + ndis_set_info(sc, OID_802_11_RTS_THRESHOLD, &arg, &len); + + /* Set fragmentation threshold */ + + len = sizeof(arg); + arg = ic->ic_fragthreshold; + ndis_set_info(sc, OID_802_11_FRAGMENTATION_THRESHOLD, &arg, &len); + + /* Set power management */ + + len = sizeof(arg); + if (ic->ic_flags & IEEE80211_F_PMGTON) + arg = NDIS_80211_POWERMODE_FAST_PSP; + else + arg = NDIS_80211_POWERMODE_CAM; + ndis_set_info(sc, OID_802_11_POWER_MODE, &arg, &len); + + /* + * Default encryption mode to off, authentication + * to open and privacy to 'accept everything.' + */ + + len = sizeof(arg); + arg = NDIS_80211_WEPSTAT_DISABLED; + ndis_set_info(sc, OID_802_11_ENCRYPTION_STATUS, &arg, &len); + + len = sizeof(arg); + arg = NDIS_80211_AUTHMODE_OPEN; + ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); + + /* + * Note that OID_80211_PRIVACY_FILTER is optional: + * not all drivers implement it. + */ + len = sizeof(arg); + arg = NDIS_80211_PRIVFILT_8021XWEP; + ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); + /* Set WEP */ -#ifdef IEEE80211_F_PRIVACY - if (ic->ic_flags & IEEE80211_F_PRIVACY) { +#if __FreeBSD_version < 600000 + if (ic->ic_flags & IEEE80211_F_WEPON) { #else - if (ic->ic_wep_mode >= IEEE80211_WEP_ON) { + if (ic->ic_flags & IEEE80211_F_PRIVACY && + !(ic->ic_flags & IEEE80211_F_WPA)) { #endif + int keys_set = 0; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { if (ic->ic_nw_keys[i].wk_keylen) { +#if __FreeBSD_version >= 600000 + if (ic->ic_nw_keys[i].wk_cipher->ic_cipher != + IEEE80211_CIPHER_WEP) + continue; +#endif bzero((char *)&wep, sizeof(wep)); wep.nw_keylen = ic->ic_nw_keys[i].wk_keylen; -#ifdef notdef - /* 5 and 13 are the only valid key lengths */ + + /* + * 5, 13 and 16 are the only valid + * only valid key lengths. Anything + * in between will be zero padded out to + * the next highest boundary. + */ if (ic->ic_nw_keys[i].wk_keylen < 5) wep.nw_keylen = 5; else if (ic->ic_nw_keys[i].wk_keylen > 5 && ic->ic_nw_keys[i].wk_keylen < 13) wep.nw_keylen = 13; -#endif + else if (ic->ic_nw_keys[i].wk_keylen > 13 && + ic->ic_nw_keys[i].wk_keylen < 16) + wep.nw_keylen = 16; + wep.nw_keyidx = i; wep.nw_length = (sizeof(uint32_t) * 3) + wep.nw_keylen; @@ -1881,48 +2175,37 @@ ndis_setstate_80211(sc) if (rval) device_printf(sc->ndis_dev, "set wepkey failed: %d\n", rval); + keys_set++; } } - arg = NDIS_80211_WEPSTAT_ENABLED; - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len); - if (rval) - device_printf(sc->ndis_dev, - "enable WEP failed: %d\n", rval); -#ifndef IEEE80211_F_WEPON -#if 0 - if (ic->ic_wep_mode != IEEE80211_WEP_8021X && - ic->ic_wep_mode != IEEE80211_WEP_ON) - arg = NDIS_80211_PRIVFILT_ACCEPTALL; - else -#endif -#endif - arg = NDIS_80211_PRIVFILT_8021XWEP; - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_PRIVACY_FILTER, &arg, &len); -#ifdef IEEE80211_WEP_8021X /*IEEE80211_F_WEPON*/ - /* Accept that we only have "shared" and 802.1x modes. */ - if (rval == 0) { - if (arg == NDIS_80211_PRIVFILT_ACCEPTALL) - ic->ic_wep_mode = IEEE80211_WEP_MIXED; + if (keys_set) { + arg = NDIS_80211_WEPSTAT_ENABLED; + len = sizeof(arg); + rval = ndis_set_info(sc, + OID_802_11_WEP_STATUS, &arg, &len); + if (rval) + device_printf(sc->ndis_dev, + "enable WEP failed: %d\n", rval); +#ifdef IEEE80211_F_DROPUNENC + if (ic->ic_flags & IEEE80211_F_DROPUNENC) + arg = NDIS_80211_PRIVFILT_8021XWEP; else - ic->ic_wep_mode = IEEE80211_WEP_8021X; - } + arg = NDIS_80211_PRIVFILT_ACCEPTALL; + + len = sizeof(arg); + ndis_set_info(sc, + OID_802_11_PRIVACY_FILTER, &arg, &len); #endif - arg = NDIS_80211_AUTHMODE_OPEN; - } else { - arg = NDIS_80211_WEPSTAT_DISABLED; - len = sizeof(arg); - ndis_set_info(sc, OID_802_11_WEP_STATUS, &arg, &len); - arg = NDIS_80211_AUTHMODE_OPEN; - } + } + } - len = sizeof(arg); - rval = ndis_set_info(sc, OID_802_11_AUTHENTICATION_MODE, &arg, &len); +#ifdef IEEE80211_F_WPA + /* Set up WPA. */ -#ifdef notyet - if (rval) - device_printf (sc->ndis_dev, "set auth failed: %d\n", rval); + if (ic->ic_flags & IEEE80211_F_WPA1 && ic->ic_opt_ie_len && + ic->ic_caps & IEEE80211_C_WPA) + if (ndis_set_wpa(sc)) + device_printf(sc->ndis_dev, "WPA setup failed\n"); #endif #ifdef notyet @@ -1965,13 +2248,13 @@ ndis_setstate_80211(sc) * Some drivers expect us to initialize these values, so * provide some defaults. */ + if (config.nc_beaconperiod == 0) config.nc_beaconperiod = 100; if (config.nc_atimwin == 0) config.nc_atimwin = 100; if (config.nc_fhconfig.ncf_dwelltime == 0) config.nc_fhconfig.ncf_dwelltime = 200; - if (rval == 0 && ic->ic_ibss_chan != IEEE80211_CHAN_ANYC) { int chan, chanflag; @@ -1997,6 +2280,32 @@ ndis_setstate_80211(sc) device_printf(sc->ndis_dev, "couldn't retrieve " "channel info: %d\n", rval); + /* + * If the user selected a specific BSSID, try + * to use that one. This is useful in the case where + * there are several APs in range with the same network + * name. To delete the BSSID, we use the broadcast + * address as the BSSID. + * Note that some drivers seem to allow setting a BSSID + * in ad-hoc mode, which has the effect of forcing the + * NIC to create an ad-hoc cell with a specific BSSID, + * instead of a randomly chosen one. However, the net80211 + * code makes the assumtion that the BSSID setting is invalid + * when you're in ad-hoc mode, so we don't allow that here. + */ + + len = IEEE80211_ADDR_LEN; + if (ic->ic_flags & IEEE80211_F_DESBSSID && + ic->ic_opmode != IEEE80211_M_IBSS) + bcopy(ic->ic_des_bssid, bssid, len); + else + bcopy(ifp->if_broadcastaddr, bssid, len); + + rval = ndis_set_info(sc, OID_802_11_BSSID, &bssid, &len); + if (rval) + device_printf(sc->ndis_dev, + "setting BSSID failed: %d\n", rval); + /* Set SSID -- always do this last. */ len = sizeof(ssid); @@ -2006,11 +2315,14 @@ ndis_setstate_80211(sc) 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); + ic->ic_state = IEEE80211_S_ASSOC; + return; } @@ -2059,9 +2371,11 @@ ndis_media_status(struct ifnet *ifp, struct ifmediareq *imr) case IEEE80211_MODE_11G: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G); break; +#ifdef IEEE80211_MODE_TURBO_A case IEEE80211_MODE_TURBO_A: imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A) | IFM_IEEE80211_TURBO; +#endif break; } } @@ -2266,6 +2580,9 @@ ndis_ioctl(ifp, command, data) { struct ndis_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; + struct ndis_oid_data oid; + struct ndis_evt evt; + void *oidbuf; int i, error = 0; /*NDIS_LOCK(sc);*/ @@ -2308,7 +2625,11 @@ ndis_ioctl(ifp, command, data) case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (sc->ndis_80211) { +#if __FreeBSD_version < 600000 + error = ieee80211_ioctl(ifp, command, data); +#else error = ieee80211_ioctl(&sc->ic, command, data); +#endif if (error == ENETRESET) { ndis_setstate_80211(sc); /*ndis_init(sc);*/ @@ -2341,6 +2662,101 @@ ndis_ioctl(ifp, command, data) else error = ENOTTY; break; + case SIOCGDRVSPEC: + if ((error = suser(curthread))) + break; + error = copyin(ifr->ifr_data, &oid, sizeof(oid)); + if (error) + break; + oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); + if (oidbuf == NULL) { + error = ENOMEM; + break; + } + error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = ndis_get_info(sc, oid.oid, oidbuf, &oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(&oid, ifr->ifr_data, sizeof(oid)); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); + free(oidbuf, M_TEMP); + break; + case SIOCSDRVSPEC: + if ((error = suser(curthread))) + break; + error = copyin(ifr->ifr_data, &oid, sizeof(oid)); + if (error) + break; + oidbuf = malloc(oid.len, M_TEMP, M_NOWAIT|M_ZERO); + if (oidbuf == NULL) { + error = ENOMEM; + break; + } + error = copyin(ifr->ifr_data + sizeof(oid), oidbuf, oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = ndis_set_info(sc, oid.oid, oidbuf, &oid.len); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(&oid, ifr->ifr_data, sizeof(oid)); + if (error) { + free(oidbuf, M_TEMP); + break; + } + error = copyout(oidbuf, ifr->ifr_data + sizeof(oid), oid.len); + free(oidbuf, M_TEMP); + break; + case SIOCGPRIVATE_0: + if ((error = suser(curthread))) + break; + NDIS_LOCK(sc); + if (sc->ndis_evt[sc->ndis_evtcidx].ne_sts == 0) { + error = ENOENT; + NDIS_UNLOCK(sc); + break; + } + error = copyin(ifr->ifr_data, &evt, sizeof(evt)); + if (error) + break; + if (evt.ne_len < sc->ndis_evt[sc->ndis_evtcidx].ne_len) { + error = ENOSPC; + NDIS_UNLOCK(sc); + break; + } + error = copyout(&sc->ndis_evt[sc->ndis_evtcidx], + ifr->ifr_data, sizeof(uint32_t) * 2); + if (error) { + NDIS_UNLOCK(sc); + break; + } + if (sc->ndis_evt[sc->ndis_evtcidx].ne_len) { + error = copyout(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, + ifr->ifr_data + (sizeof(uint32_t) * 2), + sc->ndis_evt[sc->ndis_evtcidx].ne_len); + if (error) + break; + free(sc->ndis_evt[sc->ndis_evtcidx].ne_buf, M_TEMP); + sc->ndis_evt[sc->ndis_evtcidx].ne_buf = NULL; + } + sc->ndis_evt[sc->ndis_evtcidx].ne_len = 0; + sc->ndis_evt[sc->ndis_evtcidx].ne_sts = 0; + NDIS_EVTINC(sc->ndis_evtcidx); + NDIS_UNLOCK(sc); + break; case SIOCGIFGENERIC: case SIOCSIFGENERIC: if (sc->ndis_80211 && NDIS_INITIALIZED(sc)) { @@ -2356,7 +2772,11 @@ ndis_ioctl(ifp, command, data) do_80211: sc->ndis_skip = 1; if (sc->ndis_80211) { +#if __FreeBSD_version < 600000 + error = ieee80211_ioctl(ifp, command, data); +#else error = ieee80211_ioctl(&sc->ic, command, data); +#endif if (error == ENETRESET) { ndis_setstate_80211(sc); error = 0; @@ -2403,7 +2823,7 @@ ndis_wi_ioctl_get(ifp, command, data) error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len); if (error != ENOSPC) break; - bl = malloc(len, M_DEVBUF, M_WAITOK|M_ZERO); + bl = malloc(len, M_DEVBUF, M_NOWAIT|M_ZERO); error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); if (error) { free(bl, M_DEVBUF); @@ -2495,68 +2915,61 @@ ndis_80211_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data) { struct ndis_softc *sc; struct ieee80211req *ireq; +#ifdef RTM_IEEE80211_SCAN ndis_80211_bssid_list_ex *bl; - ndis_80211_ssid ssid; - ndis_80211_macaddr bssid; ndis_wlan_bssid_ex *wb; struct ieee80211req_scan_result *sr, *bsr; - int error, len, i, j; + int i, j; char *cp; - uint8_t nodename[IEEE80211_NWID_LEN]; +#endif + int error, len; uint16_t nodename_u[IEEE80211_NWID_LEN + 1]; - char *acode; + unicode_string us; + ansi_string as; sc = ifp->if_softc; ireq = (struct ieee80211req *) data; - + switch (ireq->i_type) { +#ifdef IEEE80211_IOC_MLME case IEEE80211_IOC_MLME: error = 0; break; - case IEEE80211_IOC_BSSID: - len = sizeof(bssid); - bzero((char*)&bssid, len); - error = ndis_get_info(sc, OID_802_11_BSSID, &bssid, &len); - if (error) { - device_printf(sc->ndis_dev, "failed to get bssid\n"); - return(error); - } - ireq->i_len = len; - error = copyout(&bssid, ireq->i_data, len); - break; - case IEEE80211_IOC_SSID: - len = sizeof(ssid); - bzero((char*)&ssid, len); - error = ndis_get_info(sc, OID_802_11_SSID, &ssid, &len); - if (error) { - device_printf(sc->ndis_dev, "failed to get ssid: %d\n", error); - return(error); - } - ireq->i_len = ssid.ns_ssidlen; - error = copyout(&ssid.ns_ssid, ireq->i_data, ssid.ns_ssidlen); - break; +#endif +#ifdef RTM_IEEE80211_SCAN case IEEE80211_IOC_SCAN_RESULTS: len = 0; error = ndis_get_info(sc, OID_802_11_BSSID_LIST, NULL, &len); if (error != ENOSPC) break; - bl = malloc(len, M_DEVBUF, M_WAITOK | M_ZERO); + bl = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); error = ndis_get_info(sc, OID_802_11_BSSID_LIST, bl, &len); if (error) { free(bl, M_DEVBUF); break; } - sr = bsr = malloc(ireq->i_len, M_DEVBUF, M_WAITOK | M_ZERO); - wb = bl->nblx_bssid; + sr = bsr = malloc(ireq->i_len, M_DEVBUF, M_NOWAIT | M_ZERO); + wb = &bl->nblx_bssid[0]; len = 0; for (i = 0; i < bl->nblx_items; i++) { /* - * Check if we have enough space left for this ap + * Check if we have enough space left for this ap. + * Note that the size of the SSID list structure + * can vary: the extended structure with info + * elements is only supported by NDIS 5.1 drivers. */ - j = roundup(sizeof(*sr) + wb->nwbx_ssid.ns_ssidlen - + wb->nwbx_ielen - - sizeof(struct ndis_80211_fixed_ies), - sizeof(uint32_t)); + if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { + j = roundup(sizeof(*sr) + + wb->nwbx_ssid.ns_ssidlen + + wb->nwbx_ielen - + sizeof(struct ndis_80211_fixed_ies), + sizeof(uint32_t)); + } else { + j = roundup(sizeof(*sr) + + wb->nwbx_ssid.ns_ssidlen + + sizeof(struct ndis_80211_fixed_ies), + sizeof(uint32_t)); + } if (len + j > ireq->i_len) break; bcopy(&wb->nwbx_macaddr, &sr->isr_bssid, @@ -2586,11 +2999,13 @@ ndis_80211_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data) cp = (char *)sr + sizeof(*sr); bcopy(&wb->nwbx_ssid.ns_ssid, cp, sr->isr_ssid_len); cp += sr->isr_ssid_len; - sr->isr_ie_len = wb->nwbx_ielen - - sizeof(struct ndis_80211_fixed_ies); - bcopy((char *)wb->nwbx_ies + - sizeof(struct ndis_80211_fixed_ies), - cp, sr->isr_ie_len); + if (wb->nwbx_len > sizeof(ndis_wlan_bssid)) { + sr->isr_ie_len = wb->nwbx_ielen + - sizeof(struct ndis_80211_fixed_ies); + bcopy((char *)wb->nwbx_ies + + sizeof(struct ndis_80211_fixed_ies), + cp, sr->isr_ie_len); + } sr->isr_len = roundup(sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len, sizeof(uint32_t)); len += sr->isr_len; @@ -2603,318 +3018,172 @@ ndis_80211_ioctl_get(struct ifnet *ifp, u_long command, caddr_t data) free(bl, M_DEVBUF); free(bsr, M_DEVBUF); break; +#endif case IEEE80211_IOC_STATIONNAME: error = ndis_get_info(sc, OID_GEN_MACHINE_NAME, &nodename_u, &len); if (error) break; - acode = nodename; - bzero((char *)nodename, IEEE80211_NWID_LEN); - ndis_unicode_to_ascii(nodename_u, len, &acode); - ireq->i_len = len / 2 + 1; - error = copyout(acode, ireq->i_data, ireq->i_len); + us.us_len = us.us_maxlen = len; + us.us_buf = nodename_u; + if (RtlUnicodeStringToAnsiString(&as, &us, TRUE)) { + error = ENOMEM; + break; + } + ireq->i_len = as.as_len; + error = copyout(as.as_buf, ireq->i_data, ireq->i_len); + RtlFreeAnsiString(&as); break; default: +#if __FreeBSD_version < 600000 + error = ieee80211_ioctl(ifp, command, data); +#else error = ieee80211_ioctl(&sc->ic, command, data); +#endif + break; } return(error); } -static int -ndis_add_key(sc, wk, i_len) - struct ndis_softc *sc; - struct ieee80211req_key *wk; - int16_t i_len; +#ifdef IEEE80211_F_WPA +int +ndis_del_key(ic, key) + struct ieee80211com *ic; + const struct ieee80211_key *key; { - ndis_80211_key *rkey; - ndis_80211_wep *wep; - int len, error; - uint32_t arg; + struct ndis_softc *sc; + ndis_80211_key rkey; + int len, error = 0; - /* infrastructure mode only supported for now */ - len = sizeof(arg); - arg = NDIS_80211_NET_INFRA_BSS; - error = ndis_set_info(sc, OID_802_11_INFRASTRUCTURE_MODE, &arg, &len); - if (error) { - device_printf(sc->ndis_dev, - "setting infrastructure mode failed\n"); - return(error); - } + sc = ic->ic_ifp->if_softc; - switch(wk->ik_type) { - case IEEE80211_CIPHER_WEP: - len = 12 + wk->ik_keylen; - wep = malloc(len, M_TEMP, M_WAITOK | M_ZERO); - if(!wep) - return(ENOSPC); - wep->nw_length = len; - wep->nw_keyidx = wk->ik_keyix; - wep->nw_keylen = wk->ik_keylen; - if(wk->ik_flags & IEEE80211_KEY_XMIT) - wep->nw_keyidx |= 1 << 31; - device_printf(sc->ndis_dev, "setting wep key\n"); - error = copyin(wk->ik_keydata, wep->nw_keydata, wk->ik_keylen); - if(error) { - device_printf(sc->ndis_dev, - "copyin of wep key to kernel space failed\n"); - free(wep, M_TEMP); - break; - } - error = ndis_set_info(sc, OID_802_11_ADD_WEP, wep, &len); - if(error) { - device_printf(sc->ndis_dev, - "setting wep key failed\n"); - break; - } - free(wep, M_TEMP); + bzero((char *)&rkey, sizeof(rkey)); + len = sizeof(rkey); - /* set the authentication mode */ + rkey.nk_len = len; + rkey.nk_keyidx = key->wk_keyix; - arg = NDIS_80211_AUTHMODE_OPEN; - error = ndis_set_info(sc, - OID_802_11_AUTHENTICATION_MODE, &arg, &len); - if(error) { - device_printf(sc->ndis_dev, - "setting authentication mode failed\n"); - } + bcopy(ic->ic_ifp->if_broadcastaddr, + rkey.nk_bssid, IEEE80211_ADDR_LEN); - /* set the encryption */ + error = ndis_set_info(sc, OID_802_11_REMOVE_KEY, &rkey, &len); - len = sizeof(arg); - arg = NDIS_80211_WEPSTAT_ENABLED; - error = ndis_set_info(sc, - OID_802_11_ENCRYPTION_STATUS, &arg, &len); - if(error) { - device_printf(sc->ndis_dev, - "setting encryption status failed\n"); - return(error); - } - break; + if (error) + return(0); + + return(1); +} + +/* + * In theory this could be called for any key, but we'll + * only use it for WPA TKIP or AES keys. These need to be + * set after initial authentication with the AP. + */ + +static int +ndis_add_key(ic, key, mac) + struct ieee80211com *ic; + const struct ieee80211_key *key; + const uint8_t mac[IEEE80211_ADDR_LEN]; +{ + struct ndis_softc *sc; + ndis_80211_key rkey; + int len, error = 0; + + sc = ic->ic_ifp->if_softc; + + switch (key->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_TKIP: - len = 12 + 6 + 6 + 8 + wk->ik_keylen; - rkey = malloc(len, M_TEMP, M_WAITOK | M_ZERO); - if(!rkey) - return(ENOSPC); - rkey->nk_len = len; - error = copyin(wk->ik_macaddr, - rkey->nk_bssid, IEEE80211_ADDR_LEN); - if(error) { - device_printf(sc->ndis_dev, - "copyin of bssid to kernel space failed\n"); - free(rkey, M_TEMP); - break; - } - /* keyrsc needs to be fixed: need to do some shifting */ - error = copyin(&(wk->ik_keyrsc), - &(rkey->nk_keyrsc), sizeof(rkey->nk_keyrsc)); - if(error) { - device_printf(sc->ndis_dev, - "copyin of keyrsc to kernel space failed\n"); - free(rkey, M_TEMP); - break; - } + len = sizeof(ndis_80211_key); + bzero((char *)&rkey, sizeof(rkey)); + + rkey.nk_len = len; + rkey.nk_keylen = key->wk_keylen; + + if (key->wk_flags & IEEE80211_KEY_SWMIC) + rkey.nk_keylen += 16; /* key index - gets weird in NDIS */ - rkey->nk_keyidx = wk->ik_keyix; - if(wk->ik_flags & IEEE80211_KEY_XMIT) - rkey->nk_keyidx |= 1 << 31; - if((bcmp(rkey->nk_bssid, "\xff\xff\xff\xff\xff\xff", - IEEE80211_ADDR_LEN) == 0) || - (bcmp(rkey->nk_bssid, "\x0\x0\x0\x0\x0\x0", - IEEE80211_ADDR_LEN) == 0)) { - /* group key - nothing to do in ndis */ + if (key->wk_keyix != IEEE80211_KEYIX_NONE) + rkey.nk_keyidx = key->wk_keyix; + else + rkey.nk_keyidx = 0; + + if (key->wk_flags & IEEE80211_KEY_XMIT) + rkey.nk_keyidx |= 1 << 31; + + if (key->wk_flags & IEEE80211_KEY_GROUP) { + bcopy(ic->ic_ifp->if_broadcastaddr, + rkey.nk_bssid, IEEE80211_ADDR_LEN); } else { + bcopy(ic->ic_bss->ni_bssid, + rkey.nk_bssid, IEEE80211_ADDR_LEN); /* pairwise key */ - rkey->nk_keyidx |= 1 << 30; + rkey.nk_keyidx |= 1 << 30; } /* need to set bit 29 based on keyrsc */ + rkey.nk_keyrsc = key->wk_keyrsc; - rkey->nk_keylen = wk->ik_keylen; - if (wk->ik_type == IEEE80211_CIPHER_TKIP && - wk->ik_keylen == 32) { - /* - * key data needs to be offset by 4 due - * to mismatch between NDIS spec and BSD?? - */ - error = copyin(wk->ik_keydata, - rkey->nk_keydata + 4, 16); - if(error) { - device_printf(sc->ndis_dev, "copyin of " - "keydata(0) to kernel space failed\n"); - free(rkey, M_TEMP); - break; - } - error = copyin(wk->ik_keydata + 24, - rkey->nk_keydata + 20, 8); - if(error) { - device_printf(sc->ndis_dev, "copyin of " - "keydata(1) to kernel space failed\n"); - free(rkey, M_TEMP); - break; - } - error = copyin(wk->ik_keydata + 16, - rkey->nk_keydata + 28, 8); - if(error) { - device_printf(sc->ndis_dev, "copyin of " - "keydata(2) to kernel space failed\n"); - free(rkey, M_TEMP); - break; - } - } else { - error = copyin(wk->ik_keydata, - rkey->nk_keydata + 4, wk->ik_keylen); - if(error) { - device_printf(sc->ndis_dev, "copyin of " - "keydata(CCMP) to kernel space failed\n"); - free(rkey, M_TEMP); - break; - } - } - error = ndis_set_info(sc, OID_802_11_ADD_KEY, rkey, &len); + if (rkey.nk_keyrsc) + rkey.nk_keyidx |= 1 << 29; + + if (key->wk_flags & IEEE80211_KEY_SWMIC) { + bcopy(key->wk_key, rkey.nk_keydata, 16); + bcopy(key->wk_key + 24, rkey.nk_keydata + 16, 8); + bcopy(key->wk_key + 16, rkey.nk_keydata + 24, 8); + } else + bcopy(key->wk_key, rkey.nk_keydata, key->wk_keylen); + + error = ndis_set_info(sc, OID_802_11_ADD_KEY, &rkey, &len); + break; + case IEEE80211_CIPHER_WEP: + error = 0; break; + /* + * I don't know how to set up keys for the AES + * cipher yet. Is it the same as TKIP? + */ case IEEE80211_CIPHER_AES_CCM: - return(ENOTTY); default: - return(ENOTTY); + error = ENOTTY; + break; } - return(error); + + /* We need to return 1 for success, 0 for failure. */ + + if (error) + return(0); + + return (1); } +#endif static int ndis_80211_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data) { struct ndis_softc *sc; struct ieee80211req *ireq; - int error, len, arg, ucnt; - uint8_t nodename[IEEE80211_NWID_LEN]; - uint16_t nodename_u[IEEE80211_NWID_LEN + 1]; - uint16_t *ucode; - struct ieee80211req_del_key *rk; - struct ieee80211req_key *wk; - unsigned char *wpa_ie; - ndis_80211_ssid ssid; - ndis_80211_remove_key rkey; + int error = EINVAL, len; + ansi_string as; + unicode_string us; sc = ifp->if_softc; ireq = (struct ieee80211req *) data; - + switch (ireq->i_type) { +#ifdef IEEE80211_IOC_MLME case IEEE80211_IOC_MLME: case IEEE80211_IOC_ROAMING: case IEEE80211_IOC_COUNTERMEASURES: case IEEE80211_IOC_DROPUNENCRYPTED: error = 0; break; - case IEEE80211_IOC_PRIVACY: - len = sizeof(arg); - arg = NDIS_80211_PRIVFILT_8021XWEP; - error = ndis_set_info(sc, - OID_802_11_PRIVACY_FILTER, &arg, &len); - if (error) { - device_printf(sc->ndis_dev, - "setting wep privacy filter failed\n"); - error = 0; - } - break; - case IEEE80211_IOC_WPA: - /* nothing to do */ - error = 0; - break; - case IEEE80211_IOC_OPTIE: - wpa_ie = (char*)ireq->i_data; - if (ireq->i_len < 14 || !wpa_ie) { - /* cannot figure out anything */ - arg = NDIS_80211_AUTHMODE_OPEN; - error = ndis_set_info(sc, - OID_802_11_AUTHENTICATION_MODE, &arg, &len); - return(error); - } - if (wpa_ie[0] == IEEE80211_ELEMID_RSN) { - error = ENOTTY; - break; - } else if (wpa_ie[0] == IEEE80211_ELEMID_VENDOR) { - - /* set the encryption based on multicast cipher */ - - if (!memcmp(wpa_ie + 8, "\x00\x50\xf2\x02", 4)) { - len = sizeof(arg); - arg = NDIS_80211_WEPSTAT_ENC2ENABLED; - error = ndis_set_info(sc, - OID_802_11_ENCRYPTION_STATUS, &arg, &len); - if (error) { - device_printf(sc->ndis_dev, "setting " - "encryption status to " - "ENC2 failed\n"); - /* continue anyway */ - } - } - } - - /* set the authentication mode */ - - ucnt = wpa_ie[12] + 256* wpa_ie[13]; - - /* 4 bytes per unicast cipher */ - - ucnt = 14 + 4*ucnt + 2; /* account for number of authsels */ - - if (ireq->i_len < ucnt) { - arg = NDIS_80211_AUTHMODE_WPANONE; - } else { - if (!memcmp((void*)(&wpa_ie[ucnt]), - "\x00\x50\xf2\x02", 4)) { - arg = NDIS_80211_AUTHMODE_WPAPSK; - } else if (!memcmp((void*)(&wpa_ie[ucnt]), - "\x00\x50\xf2\x01", 4)) { - arg = NDIS_80211_AUTHMODE_WPA; - } else { - arg = NDIS_80211_AUTHMODE_WPANONE; - } - } - len = sizeof(arg); - error = ndis_set_info(sc, - OID_802_11_AUTHENTICATION_MODE, &arg, &len); - if (error) { - device_printf(sc->ndis_dev, - "setting authentication mode to WPA-PSK failed\n"); - break; - } - break; - case IEEE80211_IOC_SSID: - len = sizeof(ssid); - bzero((char*)&ssid, len); - ssid.ns_ssidlen = ireq->i_len; - error = copyin(ireq->i_data, &(ssid.ns_ssid), ireq->i_len); - if (error) - break; - error = ndis_set_info(sc, OID_802_11_SSID, &ssid, &len); - if (error) { - device_printf(sc->ndis_dev, - "setting SSID to %s\n", ssid.ns_ssid); - } - break; - case IEEE80211_IOC_DELKEY: - len = sizeof(rkey); - bzero((char*)&rkey, len); - rk = (struct ieee80211req_del_key*)ireq->i_data; - rkey.nk_len = len; - rkey.nk_keyidx = rk->idk_keyix; - error = copyin(rk->idk_macaddr, - &(rkey.nk_bssid), sizeof(ndis_80211_macaddr)); - if (error) - break; - error = ndis_set_info(sc, OID_802_11_REMOVE_KEY, &rkey, &len); - if (error) - device_printf(sc->ndis_dev, "deleting key\n"); - break; - case IEEE80211_IOC_WPAKEY: - wk = (struct ieee80211req_key*)ireq->i_data; - error = ndis_add_key(sc, wk, ireq->i_len); - break; +#endif +#ifdef RTM_IEEE80211_SCAN case IEEE80211_IOC_SCAN_REQ: len = 0; error = ndis_set_info(sc, OID_802_11_BSSID_LIST_SCAN, @@ -2922,6 +3191,7 @@ ndis_80211_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data) tsleep(&error, PPAUSE|PCATCH, "ssidscan", hz * 2); rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0); break; +#endif case IEEE80211_IOC_STATIONNAME: error = suser(curthread); if (error) @@ -2931,18 +3201,23 @@ ndis_80211_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data) error = EINVAL; break; } - bzero((char *)nodename, IEEE80211_NWID_LEN); - error = copyin(ireq->i_data, nodename, ireq->i_len); - if (error) + as.as_len = as.as_maxlen = ireq->i_len; + as.as_buf = ireq->i_data; + if (RtlAnsiStringToUnicodeString(&us, &as, TRUE)) { + error = ENOMEM; break; - ucode = nodename_u; - ndis_ascii_to_unicode((char *)nodename, &ucode); - len = ireq->i_len * 2; + } + len = us.us_len; error = ndis_set_info(sc, OID_GEN_MACHINE_NAME, - &nodename_u, &len); + us.us_buf, &len); + RtlFreeUnicodeString(&us); break; default: +#if __FreeBSD_version < 600000 + error = ieee80211_ioctl(ifp, command, data); +#else error = ieee80211_ioctl(&sc->ic, command, data); +#endif if (error == ENETRESET) { ndis_setstate_80211(sc); error = 0; @@ -2953,8 +3228,8 @@ ndis_80211_ioctl_set(struct ifnet *ifp, u_long command, caddr_t data) } static void -ndis_resettask(w, arg) - ndis_work_item *w; +ndis_resettask(d, arg) + device_object *d; void *arg; { struct ndis_softc *sc; @@ -2977,8 +3252,12 @@ ndis_watchdog(ifp) device_printf(sc->ndis_dev, "watchdog timeout\n"); NDIS_UNLOCK(sc); - NdisScheduleWorkItem(&sc->ndis_resetitem); - NdisScheduleWorkItem(&sc->ndis_startitem); + IoQueueWorkItem(sc->ndis_resetitem, + (io_workitem_func)ndis_resettask_wrap, + WORKQUEUE_CRITICAL, sc); + IoQueueWorkItem(sc->ndis_startitem, + (io_workitem_func)ndis_starttask_wrap, + WORKQUEUE_CRITICAL, ifp); return; } @@ -2992,6 +3271,12 @@ ndis_stop(sc) struct ndis_softc *sc; { struct ifnet *ifp; + struct ieee80211com *ic; + int i; + + ic = &sc->ic; + if (sc->ndis_80211) + ic->ic_state = IEEE80211_S_INIT; ifp = sc->ifp; untimeout(ndis_tick, sc, sc->ndis_stat_ch); @@ -3004,6 +3289,17 @@ ndis_stop(sc) ndis_halt_nic(sc); + NDIS_LOCK(sc); + for (i = 0; i < NDIS_EVENTS; i++) { + if (sc->ndis_evt[i].ne_sts && sc->ndis_evt[i].ne_buf != NULL) + free(sc->ndis_evt[i].ne_buf, M_TEMP); + sc->ndis_evt[i].ne_sts = 0; + sc->ndis_evt[i].ne_len = 0; + } + sc->ndis_evtcidx = 0; + sc->ndis_evtpidx = 0; + NDIS_UNLOCK(sc); + return; } diff --git a/sys/dev/if_ndis/if_ndisvar.h b/sys/dev/if_ndis/if_ndisvar.h index 5e28b7d..b595dd7 100644 --- a/sys/dev/if_ndis/if_ndisvar.h +++ b/sys/dev/if_ndis/if_ndisvar.h @@ -35,6 +35,16 @@ #define NDIS_DEFAULT_NODENAME "FreeBSD NDIS node" #define NDIS_NODENAME_LEN 32 +/* For setting/getting OIDs from userspace. */ + +struct ndis_oid_data { + uint32_t oid; + uint32_t len; +#ifdef notdef + uint8_t data[1]; +#endif +}; + struct ndis_pci_type { uint16_t ndis_vid; uint16_t ndis_did; @@ -49,28 +59,51 @@ struct ndis_pccard_type { }; struct ndis_shmem { + list_entry ndis_list; bus_dma_tag_t ndis_stag; bus_dmamap_t ndis_smap; void *ndis_saddr; ndis_physaddr ndis_paddr; - struct ndis_shmem *ndis_next; }; struct ndis_cfglist { ndis_cfg ndis_cfg; + struct sysctl_oid *ndis_oid; TAILQ_ENTRY(ndis_cfglist) link; }; +/* + * Helper struct to make parsing information + * elements easier. + */ +struct ndis_ie { + uint8_t ni_oui[3]; + uint8_t ni_val; +}; + TAILQ_HEAD(nch, ndis_cfglist); -#define NDIS_INITIALIZED(sc) (sc->ndis_block->nmb_miniportadapterctx != NULL) +#define NDIS_INITIALIZED(sc) (sc->ndis_block->nmb_devicectx != NULL) #define NDIS_INC(x) \ (x)->ndis_txidx = ((x)->ndis_txidx + 1) % (x)->ndis_maxpkts +#if __FreeBSD_version < 600000 +#define arpcom ic.ic_ac +#endif + +#define NDIS_EVENTS 4 +#define NDIS_EVTINC(x) (x) = ((x) + 1) % NDIS_EVENTS + +struct ndis_evt { + uint32_t ne_sts; + uint32_t ne_len; + char *ne_buf; +}; + struct ndis_softc { - struct ifnet *ifp; struct ieee80211com ic; /* interface info */ + struct ifnet *ifp; struct ifmedia ifmedia; /* media info */ u_long ndis_hwassist; uint32_t ndis_v4tx; @@ -91,7 +124,8 @@ struct ndis_softc { struct resource *ndis_res_cm; /* common mem (pccard) */ struct resource_list ndis_rl; int ndis_rescnt; - struct mtx ndis_mtx; + kspin_lock ndis_spinlock; + uint8_t ndis_irql; device_t ndis_dev; int ndis_unit; ndis_miniport_block *ndis_block; @@ -110,6 +144,7 @@ struct ndis_softc { struct nch ndis_cfglist_head; int ndis_80211; int ndis_link; + uint32_t ndis_sts; uint32_t ndis_filter; int ndis_if_flags; int ndis_skip; @@ -121,18 +156,53 @@ struct ndis_softc { int ndis_devidx; interface_type ndis_iftype; driver_object *ndis_dobj; - ndis_work_item ndis_tickitem; - ndis_work_item ndis_startitem; - ndis_work_item ndis_resetitem; + io_workitem *ndis_tickitem; + io_workitem *ndis_startitem; + io_workitem *ndis_resetitem; kdpc ndis_rxdpc; bus_dma_tag_t ndis_parent_tag; +/* struct ndis_shmem *ndis_shlist; +*/ + list_entry ndis_shlist; bus_dma_tag_t ndis_mtag; bus_dma_tag_t ndis_ttag; bus_dmamap_t *ndis_mmaps; bus_dmamap_t *ndis_tmaps; int ndis_mmapcnt; + struct ndis_evt ndis_evt[NDIS_EVENTS]; + int ndis_evtpidx; + int ndis_evtcidx; }; -#define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx) -#define NDIS_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx) +#define NDIS_LOCK(_sc) KeAcquireSpinLock(&(_sc)->ndis_spinlock, \ + &(_sc)->ndis_irql); +#define NDIS_UNLOCK(_sc) KeReleaseSpinLock(&(_sc)->ndis_spinlock, \ + (_sc)->ndis_irql); + +/* + * Backwards compatibility defines. + */ + +#ifndef IF_ADDR_LOCK +#define IF_ADDR_LOCK(x) +#define IF_ADDR_UNLOCK(x) +#endif + +#ifndef IFF_DRV_OACTIVE +#define IFF_DRV_OACTIVE IFF_OACTIVE +#define IFF_DRV_RUNNING IFF_RUNNING +#define if_drv_flags if_flags +#endif + +#ifndef ic_def_txkey +#define ic_def_txkey ic_wep_txkey +#define wk_keylen wk_len +#endif + +#ifndef SIOCGDRVSPEC +#define SIOCSDRVSPEC _IOW('i', 123, struct ifreq) /* set driver-specific + parameters */ +#define SIOCGDRVSPEC _IOWR('i', 123, struct ifreq) /* get driver-specific + parameters */ +#endif |