diff options
author | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2008-04-20 20:35:46 +0000 |
commit | 3569e353ca63336d80ab0143dd9669b0b9e6b123 (patch) | |
tree | bc7985c57e7ecfa1ac03e48c406a25430dba634b /sys/dev/ipw | |
parent | 682b4ae9be70192e298129ada878af3486683aaf (diff) | |
download | FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.zip FreeBSD-src-3569e353ca63336d80ab0143dd9669b0b9e6b123.tar.gz |
Multi-bss (aka vap) support for 802.11 devices.
Note this includes changes to all drivers and moves some device firmware
loading to use firmware(9) and a separate module (e.g. ral). Also there
no longer are separate wlan_scan* modules; this functionality is now
bundled into the wlan module.
Supported by: Hobnob and Marvell
Reviewed by: many
Obtained from: Atheros (some bits)
Diffstat (limited to 'sys/dev/ipw')
-rw-r--r-- | sys/dev/ipw/if_ipw.c | 1029 | ||||
-rw-r--r-- | sys/dev/ipw/if_ipwvar.h | 52 |
2 files changed, 507 insertions, 574 deletions
diff --git a/sys/dev/ipw/if_ipw.c b/sys/dev/ipw/if_ipw.c index 8a69a40..f21bc5b 100644 --- a/sys/dev/ipw/if_ipw.c +++ b/sys/dev/ipw/if_ipw.c @@ -107,13 +107,21 @@ static const struct ipw_ident ipw_ident_table[] = { { 0, 0, NULL } }; +static struct ieee80211vap *ipw_vap_create(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void ipw_vap_delete(struct ieee80211vap *); static int ipw_dma_alloc(struct ipw_softc *); static void ipw_release(struct ipw_softc *); -static int ipw_media_change(struct ifnet *); static void ipw_media_status(struct ifnet *, struct ifmediareq *); -static int ipw_newstate(struct ieee80211com *, enum ieee80211_state, int); +static int ipw_newstate(struct ieee80211vap *, enum ieee80211_state, int); static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t); static void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *); +static void ipw_assocsuccess(void *, int); +static void ipw_assocfailed(void *, int); +static void ipw_scandone(void *, int); +static void ipw_bmiss(void *, int); static void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *); static void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *, struct ipw_soft_bd *, struct ipw_soft_buf *); @@ -126,6 +134,8 @@ static const char * ipw_cmdname(int); static int ipw_cmd(struct ipw_softc *, uint32_t, void *, uint32_t); static int ipw_tx_start(struct ifnet *, struct mbuf *, struct ieee80211_node *); +static int ipw_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); static void ipw_start(struct ifnet *); static void ipw_start_locked(struct ifnet *); static void ipw_watchdog(void *); @@ -138,12 +148,10 @@ static int ipw_load_ucode(struct ipw_softc *, const char *, int); static int ipw_load_firmware(struct ipw_softc *, const char *, int); static int ipw_config(struct ipw_softc *); static void ipw_assoc_task(void *, int); -static int ipw_auth_and_assoc(struct ipw_softc *); static void ipw_disassoc_task(void *, int); -static int ipw_disassociate(struct ipw_softc *); static void ipw_init_task(void *, int); static void ipw_init(void *); -static void ipw_init_locked(struct ipw_softc *, int); +static void ipw_init_locked(struct ipw_softc *); static void ipw_stop(void *); static void ipw_stop_locked(struct ipw_softc *); static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS); @@ -163,8 +171,9 @@ static int ipw_scan(struct ipw_softc *); static void ipw_scan_start(struct ieee80211com *); static void ipw_scan_end(struct ieee80211com *); static void ipw_set_channel(struct ieee80211com *); -static void ipw_scan_curchan(struct ieee80211com *, unsigned long maxdwell); -static void ipw_scan_mindwell(struct ieee80211com *); +static void ipw_scan_curchan(struct ieee80211_scan_state *, + unsigned long maxdwell); +static void ipw_scan_mindwell(struct ieee80211_scan_state *); static int ipw_probe(device_t); static int ipw_attach(device_t); @@ -219,7 +228,7 @@ ipw_attach(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); struct ifnet *ifp; - struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211com *ic; struct ieee80211_channel *c; uint16_t val; int error, i; @@ -231,8 +240,7 @@ ipw_attach(device_t dev) TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc); TASK_INIT(&sc->sc_scan_task, 0, ipw_scan_task, sc); - TASK_INIT(&sc->sc_assoc_task, 0, ipw_assoc_task, sc); - TASK_INIT(&sc->sc_disassoc_task, 0, ipw_disassoc_task, sc); + TASK_INIT(&sc->sc_bmiss_task, 0, ipw_bmiss, sc); callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { @@ -262,24 +270,25 @@ ipw_attach(device_t dev) RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); - goto fail; + goto fail1; } if (ipw_reset(sc) != 0) { device_printf(dev, "could not reset adapter\n"); - goto fail; + goto fail2; } if (ipw_dma_alloc(sc) != 0) { device_printf(dev, "could not allocate DMA resources\n"); - goto fail; + goto fail2; } - ifp = sc->sc_ifp = if_alloc(IFT_ETHER); + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); - goto fail; + goto fail3; } + ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); @@ -292,9 +301,8 @@ ipw_attach(device_t dev) IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; - ic->ic_phytype = IEEE80211_T_DS; ic->ic_opmode = IEEE80211_M_STA; - ic->ic_state = IEEE80211_S_INIT; + ic->ic_phytype = IEEE80211_T_DS; /* set device capabilities */ ic->ic_caps = IEEE80211_C_IBSS /* IBSS mode supported */ @@ -333,20 +341,18 @@ ipw_attach(device_t dev) sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH; ieee80211_ifattach(ic); - /* override state transition machine */ - sc->sc_newstate = ic->ic_newstate; - ic->ic_newstate = ipw_newstate; - ieee80211_media_init(ic, ipw_media_change, ipw_media_status); - ic->ic_scan_start = ipw_scan_start; ic->ic_scan_end = ipw_scan_end; ic->ic_set_channel = ipw_set_channel; ic->ic_scan_curchan = ipw_scan_curchan; ic->ic_scan_mindwell = ipw_scan_mindwell; + ic->ic_raw_xmit = ipw_raw_xmit; + + ic->ic_vap_create = ipw_vap_create; + ic->ic_vap_delete = ipw_vap_delete; - bpfattach2(ifp, DLT_IEEE802_11_RADIO, - sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap), - &sc->sc_drvbpf); + bpfattach(ifp, DLT_IEEE802_11_RADIO, + sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap)); sc->sc_rxtap_len = sizeof sc->sc_rxtap; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); @@ -359,8 +365,6 @@ ipw_attach(device_t dev) /* * Add a few sysctl knobs. */ - sc->dwelltime = 100; - SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio", CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I", @@ -371,11 +375,6 @@ ipw_attach(device_t dev) CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S", "statistics"); - SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell", - CTLFLAG_RW, &sc->dwelltime, 0, - "channel dwell time (ms) for AP/station scanning"); - /* * Hook our interrupt after all initialization is complete. */ @@ -383,15 +382,23 @@ ipw_attach(device_t dev) NULL, ipw_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "could not set up interrupt\n"); - goto fail; + goto fail4; } if (bootverbose) ieee80211_announce(ic); return 0; - -fail: ipw_detach(dev); +fail4: + if_free(ifp); +fail3: + ipw_release(sc); +fail2: + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); +fail1: + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); +fail: + mtx_destroy(&sc->sc_mtx); return ENXIO; } @@ -399,33 +406,27 @@ static int ipw_detach(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; ipw_stop(sc); + + bpfdetach(ifp); + ieee80211_ifdetach(ic); + callout_drain(&sc->sc_wdtimer); taskqueue_drain(taskqueue_fast, &sc->sc_init_task); taskqueue_drain(taskqueue_fast, &sc->sc_scan_task); - taskqueue_drain(taskqueue_fast, &sc->sc_assoc_task); - taskqueue_drain(taskqueue_fast, &sc->sc_disassoc_task); - - if (ifp != NULL) { - bpfdetach(ifp); - ieee80211_ifdetach(ic); - } + taskqueue_drain(taskqueue_fast, &sc->sc_bmiss_task); ipw_release(sc); - if (sc->irq != NULL) { - bus_teardown_intr(dev, sc->irq, sc->sc_ih); - bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); - } + bus_teardown_intr(dev, sc->irq, sc->sc_ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); - if (sc->mem != NULL) - bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); - if (ifp != NULL) - if_free(ifp); + if_free(ifp); if (sc->sc_firmware != NULL) { firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); @@ -437,6 +438,103 @@ ipw_detach(device_t dev) return 0; } +static struct ieee80211vap * +ipw_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ifnet *ifp = ic->ic_ifp; + struct ipw_softc *sc = ifp->if_softc; + struct ipw_vap *ivp; + struct ieee80211vap *vap; + const struct firmware *fp; + const struct ipw_firmware_hdr *hdr; + const char *imagename; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + + switch (opmode) { + case IEEE80211_M_STA: + imagename = "ipw_bss"; + break; + case IEEE80211_M_IBSS: + imagename = "ipw_ibss"; + break; + case IEEE80211_M_MONITOR: + imagename = "ipw_monitor"; + break; + default: + return NULL; + } + + /* + * Load firmware image using the firmware(9) subsystem. Doing + * this unlocked is ok since we're single-threaded by the + * 802.11 layer. + */ + if (sc->sc_firmware == NULL || + strcmp(sc->sc_firmware->name, imagename) != 0) { + if (sc->sc_firmware != NULL) + firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); + sc->sc_firmware = firmware_get(imagename); + } + if (sc->sc_firmware == NULL) { + device_printf(sc->sc_dev, + "could not load firmware image '%s'\n", imagename); + return NULL; + } + fp = sc->sc_firmware; + if (fp->datasize < sizeof *hdr) { + device_printf(sc->sc_dev, + "firmware image too short %zu\n", fp->datasize); + firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); + sc->sc_firmware = NULL; + return NULL; + } + hdr = (const struct ipw_firmware_hdr *)fp->data; + if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) + + le32toh(hdr->ucodesz)) { + device_printf(sc->sc_dev, + "firmware image too short %zu\n", fp->datasize); + firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); + sc->sc_firmware = NULL; + return NULL; + } + + ivp = (struct ipw_vap *) malloc(sizeof(struct ipw_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (ivp == NULL) + return NULL; + vap = &ivp->vap; + + TASK_INIT(&ivp->assoc_task, 0, ipw_assoc_task, vap); + TASK_INIT(&ivp->disassoc_task, 0, ipw_disassoc_task, vap); + TASK_INIT(&ivp->assoc_success_task, 0, ipw_assocsuccess, vap); + TASK_INIT(&ivp->assoc_failed_task, 0, ipw_assocfailed, vap); + TASK_INIT(&ivp->scandone_task, 0, ipw_scandone, vap); + + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); + /* override with driver methods */ + ivp->newstate = vap->iv_newstate; + vap->iv_newstate = ipw_newstate; + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, ipw_media_status); + ic->ic_opmode = opmode; + return vap; +} + +static void +ipw_vap_delete(struct ieee80211vap *vap) +{ + struct ipw_vap *ivp = IPW_VAP(vap); + + ieee80211_vap_detach(vap); + free(ivp, M_80211_VAP); +} + static int ipw_dma_alloc(struct ipw_softc *sc) { @@ -748,45 +846,17 @@ static int ipw_resume(device_t dev) { struct ipw_softc *sc = device_get_softc(dev); - struct ifnet *ifp = sc->sc_ic.ic_ifp; - IPW_LOCK_DECL; - - IPW_LOCK(sc); + struct ifnet *ifp = sc->sc_ifp; pci_write_config(dev, 0x41, 0, 1); - if (ifp->if_flags & IFF_UP) { - ipw_init_locked(sc, 0); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ipw_start_locked(ifp); - } - - IPW_UNLOCK(sc); + if (ifp->if_flags & IFF_UP) + ipw_init(sc); return 0; } static int -ipw_media_change(struct ifnet *ifp) -{ - struct ipw_softc *sc = ifp->if_softc; - int error; - IPW_LOCK_DECL; - - IPW_LOCK(sc); - error = ieee80211_media_change(ifp); - if (error == ENETRESET) { - if ((ifp->if_flags & IFF_UP) && - (ifp->if_drv_flags & IFF_DRV_RUNNING)) - ipw_init_locked(sc, 0); - error = 0; - } - IPW_UNLOCK(sc); - - return (error); -} - -static int ipw_cvtrate(int ipwrate) { switch (ipwrate) { @@ -805,47 +875,26 @@ ipw_cvtrate(int ipwrate) static void ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr) { - struct ipw_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; - int rate; - - imr->ifm_status = IFM_AVALID; - imr->ifm_active = IFM_IEEE80211; - if (ic->ic_state == IEEE80211_S_RUN) - imr->ifm_status |= IFM_ACTIVE; + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + struct ipw_softc *sc = ic->ic_ifp->if_softc; /* read current transmission rate from adapter */ - rate = ipw_cvtrate(ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf); - imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B); - - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - break; - - case IEEE80211_M_IBSS: - imr->ifm_active |= IFM_IEEE80211_IBSS; - break; - - case IEEE80211_M_MONITOR: - imr->ifm_active |= IFM_IEEE80211_MONITOR; - break; - - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - case IEEE80211_M_WDS: - /* should not get there */ - break; - } + vap->iv_bss->ni_txrate = ipw_cvtrate( + ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf); + ieee80211_media_status(ifp, imr); } static int -ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +ipw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { + struct ipw_vap *ivp = IPW_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, - ieee80211_state_name[ic->ic_state], + ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate], sc->flags)); switch (nstate) { @@ -859,36 +908,40 @@ ipw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) * AUTH -> RUN transition and we want to do nothing. * This is all totally bogus and needs to be redone. */ - if (ic->ic_state == IEEE80211_S_SCAN) - taskqueue_enqueue_fast(taskqueue_fast, - &sc->sc_assoc_task); + if (vap->iv_state == IEEE80211_S_SCAN) { + taskqueue_enqueue(taskqueue_swi, + &IPW_VAP(vap)->assoc_task); + return EINPROGRESS; + } } break; case IEEE80211_S_INIT: if (sc->flags & IPW_FLAG_ASSOCIATED) - taskqueue_enqueue_fast(taskqueue_fast, - &sc->sc_disassoc_task); + taskqueue_enqueue(taskqueue_swi, + &IPW_VAP(vap)->disassoc_task); break; case IEEE80211_S_AUTH: - taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_assoc_task); - break; + taskqueue_enqueue(taskqueue_swi, &IPW_VAP(vap)->assoc_task); + return EINPROGRESS; case IEEE80211_S_ASSOC: /* * If we are not transitioning from AUTH the resend the * association request. */ - if (ic->ic_state != IEEE80211_S_AUTH) - taskqueue_enqueue_fast(taskqueue_fast, - &sc->sc_assoc_task); + if (vap->iv_state != IEEE80211_S_AUTH) { + taskqueue_enqueue(taskqueue_swi, + &IPW_VAP(vap)->assoc_task); + return EINPROGRESS; + } break; default: break; } - return (*sc->sc_newstate)(ic, nstate, arg); + return ivp->newstate(vap, nstate, arg); } /* @@ -965,10 +1018,44 @@ ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) } static void +ipw_assocsuccess(void *arg, int npending) +{ + struct ieee80211vap *vap = arg; + + ieee80211_new_state(vap, IEEE80211_S_RUN, -1); +} + +static void +ipw_assocfailed(void *arg, int npending) +{ + struct ieee80211vap *vap = arg; + + ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); +} + +static void +ipw_scandone(void *arg, int npending) +{ + struct ieee80211vap *vap = arg; + + ieee80211_scan_done(vap); +} + +static void +ipw_bmiss(void *arg, int npending) +{ + struct ieee80211com *ic = arg; + + ieee80211_beacon_miss(ic); +} + +static void ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) { -#define IEEESTATE(ic) ieee80211_state_name[ic->ic_state] - struct ieee80211com *ic = &sc->sc_ic; +#define IEEESTATE(vap) ieee80211_state_name[vap->iv_state] + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t state; bus_dmamap_sync(sc->rxbuf_dmat, sbuf->map, BUS_DMASYNC_POSTREAD); @@ -978,27 +1065,32 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) switch (state) { case IPW_STATE_ASSOCIATED: DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n", - IEEESTATE(ic), sc->flags)); - sc->flags |= IPW_FLAG_ASSOCIATED; + IEEESTATE(vap), sc->flags)); /* XXX suppress state change in case the fw auto-associates */ - if (ic->ic_state != IEEE80211_S_ASSOC) { - DPRINTF(("Unexpected association (state %u)\n", - ic->ic_state)); - } else - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + if ((sc->flags & IPW_FLAG_ASSOCIATING) == 0) { + DPRINTF(("Unexpected association (%s, flags 0x%x)\n", + IEEESTATE(vap), sc->flags)); + break; + } + sc->flags &= ~IPW_FLAG_ASSOCIATING; + sc->flags |= IPW_FLAG_ASSOCIATED; + taskqueue_enqueue(taskqueue_swi, + &IPW_VAP(vap)->assoc_success_task); break; case IPW_STATE_SCANNING: DPRINTFN(3, ("Scanning (%s flags 0x%x)\n", - IEEESTATE(ic), sc->flags)); + IEEESTATE(vap), sc->flags)); /* * NB: Check driver state for association on assoc * loss as the firmware will immediately start to * scan and we would treat it as a beacon miss if * we checked the 802.11 layer state. */ - if (sc->flags & IPW_FLAG_ASSOCIATED) - ieee80211_beacon_miss(ic); + if (sc->flags & IPW_FLAG_ASSOCIATED) { + /* XXX probably need to issue disassoc to fw */ + taskqueue_enqueue(taskqueue_swi, &sc->sc_bmiss_task); + } break; case IPW_STATE_SCAN_COMPLETE: @@ -1009,14 +1101,15 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) * around this by marking the HACK flag and skipping * the first scan complete event. */ + DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n", + IEEESTATE(vap), sc->flags)); if (sc->flags & IPW_FLAG_HACK) { sc->flags &= ~IPW_FLAG_HACK; break; } - DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n", - IEEESTATE(ic), sc->flags)); if (sc->flags & IPW_FLAG_SCANNING) { - ieee80211_scan_done(ic); + taskqueue_enqueue(taskqueue_swi, + &IPW_VAP(vap)->scandone_task); sc->flags &= ~IPW_FLAG_SCANNING; sc->sc_scan_timer = 0; } @@ -1024,27 +1117,31 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) case IPW_STATE_ASSOCIATION_LOST: DPRINTFN(2, ("Association lost (%s flags 0x%x)\n", - IEEESTATE(ic), sc->flags)); - sc->flags &= ~IPW_FLAG_ASSOCIATED; - if (ic->ic_state == IEEE80211_S_RUN) - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + IEEESTATE(vap), sc->flags)); + sc->flags &= ~(IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED); + if (vap->iv_state == IEEE80211_S_RUN) + taskqueue_enqueue(taskqueue_swi, + &IPW_VAP(vap)->assoc_failed_task); break; case IPW_STATE_DISABLED: + /* XXX? is this right? */ + sc->flags &= ~(IPW_FLAG_HACK | IPW_FLAG_SCANNING | + IPW_FLAG_ASSOCIATING | IPW_FLAG_ASSOCIATED); DPRINTFN(2, ("Firmware disabled (%s flags 0x%x)\n", - IEEESTATE(ic), sc->flags)); + IEEESTATE(vap), sc->flags)); break; case IPW_STATE_RADIO_DISABLED: - DPRINTFN(2, ("Radio off (%s flags 0x%x)\n", - IEEESTATE(ic), sc->flags)); - ic->ic_ifp->if_flags &= ~IFF_UP; + device_printf(sc->sc_dev, "radio turned off\n"); + ieee80211_notify_radio(ic, 0); ipw_stop_locked(sc); + /* XXX start polling thread to detect radio on */ break; default: DPRINTFN(2, ("%s: unhandled state %u %s flags 0x%x\n", - __func__, state, IEEESTATE(ic), sc->flags)); + __func__, state, IEEESTATE(vap), sc->flags)); break; } #undef IEEESTATE @@ -1056,7 +1153,8 @@ ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) static void ipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan) { - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; ic->ic_curchan = chan; sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = @@ -1072,7 +1170,8 @@ ipw_setcurchan(struct ipw_softc *sc, struct ieee80211_channel *chan) static void ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m) { - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_channel *c; struct ieee80211_frame *wh; uint8_t subtype; @@ -1089,6 +1188,7 @@ ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m) subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) return; + /* XXX use ieee80211_parse_beacon */ frm = (uint8_t *)(wh + 1); efrm = mtod(m, uint8_t *) + m->m_len; @@ -1116,10 +1216,9 @@ static void ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mnew, *m; - struct ieee80211_frame *wh; struct ieee80211_node *ni; bus_addr_t physaddr; int error; @@ -1177,7 +1276,7 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = le32toh(status->len); - if (bpf_peers_present(sc->sc_drvbpf)) { + if (bpf_peers_present(ifp->if_bpf)) { struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; @@ -1185,21 +1284,19 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); - bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } if (sc->flags & IPW_FLAG_SCANNING) ipw_fix_channel(sc, m); - wh = mtod(m, struct ieee80211_frame *); IPW_UNLOCK(sc); - ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); - - /* send the frame to the 802.11 layer */ - ieee80211_input(ic, m, ni, status->rssi, -95/*XXX*/, 0); - - /* node is no longer needed */ - ieee80211_free_node(ni); + ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); + if (ni != NULL) { + (void) ieee80211_input(ni, m, status->rssi, -95, 0); + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, status->rssi, -95, 0); IPW_LOCK(sc); bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); @@ -1208,7 +1305,6 @@ ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, static void ipw_rx_intr(struct ipw_softc *sc) { - struct ieee80211com *ic = &sc->sc_ic; struct ipw_status *status; struct ipw_soft_bd *sbd; struct ipw_soft_buf *sbuf; @@ -1243,11 +1339,7 @@ ipw_rx_intr(struct ipw_softc *sc) case IPW_STATUS_CODE_NOTIFICATION: DPRINTFN(2, ("notification status, len %u flags 0x%x\n", le32toh(status->len), status->flags)); - if (ic->ic_state == IEEE80211_S_AUTH) { - /* XXX assume auth notification */ - ieee80211_node_authorize(ic->ic_bss); - ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); - } + /* XXX maybe drive state machine AUTH->ASSOC? */ break; default: @@ -1311,7 +1403,7 @@ ipw_release_sbd(struct ipw_softc *sc, struct ipw_soft_bd *sbd) static void ipw_tx_intr(struct ipw_softc *sc) { - struct ifnet *ifp = sc->sc_ic.ic_ifp; + struct ifnet *ifp = sc->sc_ifp; struct ipw_soft_bd *sbd; uint32_t r, i; @@ -1359,7 +1451,7 @@ ipw_intr(void *arg) if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) { device_printf(sc->sc_dev, "firmware error\n"); - taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_init_task); + taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task); r = 0; /* don't process more interrupts */ } @@ -1447,6 +1539,8 @@ ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len) bus_addr_t physaddr; int error; + IPW_LOCK_ASSERT(sc); + if (sc->flags & IPW_FLAG_BUSY) { device_printf(sc->sc_dev, "%s: %s not sent, busy\n", __func__, ipw_cmdname(type)); @@ -1514,7 +1608,7 @@ static int ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) { struct ipw_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_frame *wh; struct ipw_soft_bd *sbd; struct ipw_soft_hdr *shdr; @@ -1528,24 +1622,23 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ic, ni, m0); + k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } - /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } - if (bpf_peers_present(sc->sc_drvbpf)) { + if (bpf_peers_present(ifp->if_bpf)) { struct ipw_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); - bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0); + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } shdr = SLIST_FIRST(&sc->free_shdr); @@ -1660,6 +1753,16 @@ ipw_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) return 0; } +static int +ipw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + /* no support; just discard */ + m_freem(m); + ieee80211_free_node(ni); + return 0; +} + static void ipw_start(struct ifnet *ifp) { @@ -1675,54 +1778,31 @@ static void ipw_start_locked(struct ifnet *ifp) { struct ipw_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; - struct mbuf *m0; - struct ether_header *eh; struct ieee80211_node *ni; + struct mbuf *m; IPW_LOCK_ASSERT(sc); - if (ic->ic_state != IEEE80211_S_RUN) - return; - for (;;) { - IFQ_DRV_DEQUEUE(&ifp->if_snd, m0); - if (m0 == NULL) + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) break; - if (sc->txfree < 1 + IPW_MAX_NSEG) { - IFQ_DRV_PREPEND(&ifp->if_snd, m0); + IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } - - if (m0->m_len < sizeof (struct ether_header) && - (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) - continue; - - eh = mtod(m0, struct ether_header *); - ni = ieee80211_find_txnode(ic, eh->ether_dhost); - if (ni == NULL) { - m_freem(m0); - continue; - } - BPF_MTAP(ifp, m0); - - m0 = ieee80211_encap(ic, m0, ni); - if (m0 == NULL) { + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + m = ieee80211_encap(ni, m); + if (m == NULL) { ieee80211_free_node(ni); continue; } - - if (bpf_peers_present(ic->ic_rawbpf)) - bpf_mtap(ic->ic_rawbpf, m0); - - if (ipw_tx_start(ifp, m0, ni) != 0) { + if (ipw_tx_start(ifp, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } - /* start watchdog timer */ sc->sc_tx_timer = 5; } @@ -1732,8 +1812,8 @@ static void ipw_watchdog(void *arg) { struct ipw_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; IPW_LOCK_ASSERT(sc); @@ -1741,8 +1821,7 @@ ipw_watchdog(void *arg) if (--sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); ifp->if_oerrors++; - taskqueue_enqueue_fast(taskqueue_fast, - &sc->sc_init_task); + taskqueue_enqueue(taskqueue_swi, &sc->sc_init_task); } } if (sc->sc_scan_timer > 0) { @@ -1750,7 +1829,7 @@ ipw_watchdog(void *arg) DPRINTFN(3, ("Scan timeout\n")); /* End the scan */ if (sc->flags & IPW_FLAG_SCANNING) { - ieee80211_scan_done(ic); + ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); sc->flags &= ~IPW_FLAG_SCANNING; } } @@ -1763,37 +1842,35 @@ static int ipw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ipw_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; - int error = 0; + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0, startall = 0; IPW_LOCK_DECL; IPW_LOCK(sc); - switch (cmd) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) - ipw_init_locked(sc, 0); + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + ipw_init_locked(sc); + startall = 1; + } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) ipw_stop_locked(sc); } break; - + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; default: - error = ieee80211_ioctl(ic, cmd, data); - } - - if (error == ENETRESET) { - if ((ifp->if_flags & IFF_UP) && - (ifp->if_drv_flags & IFF_DRV_RUNNING) && - (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)) - ipw_init_locked(sc, 0); - error = 0; + error = ether_ioctl(ifp, cmd, data); } - IPW_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); return error; } @@ -2010,13 +2087,15 @@ ipw_load_firmware(struct ipw_softc *sc, const char *fw, int size) static int ipw_setwepkeys(struct ipw_softc *sc) { - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ipw_wep_key wepkey; struct ieee80211_key *wk; int error, i; for (i = 0; i < IEEE80211_WEP_NKID; i++) { - wk = &ic->ic_crypto.cs_nw_keys[i]; + wk = &vap->iv_nw_keys[i]; if (wk->wk_cipher == NULL || wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) @@ -2166,7 +2245,8 @@ done: static int ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan) { - struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; uint32_t data; int error; @@ -2178,308 +2258,126 @@ ipw_setchannel(struct ipw_softc *sc, struct ieee80211_channel *chan) return error; } -static int -ipw_config(struct ipw_softc *sc) +static void +ipw_assoc_task(void *context, int pending) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = ic->ic_ifp; + struct ieee80211vap *vap = context; + struct ifnet *ifp = vap->iv_ic->ic_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ipw_softc *sc = ifp->if_softc; + struct ieee80211_node *ni = vap->iv_bss; struct ipw_security security; - struct ipw_configuration config; uint32_t data; int error; + IPW_LOCK_DECL; + IPW_LOCK(sc); error = ipw_disable(sc); if (error != 0) - return error; - - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - case IEEE80211_M_HOSTAP: - case IEEE80211_M_WDS: /* XXX */ - data = htole32(IPW_MODE_BSS); - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - data = htole32(IPW_MODE_IBSS); - break; - case IEEE80211_M_MONITOR: - data = htole32(IPW_MODE_MONITOR); - break; - } - DPRINTF(("Setting mode to %u\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data); - if (error != 0) - return error; - - if (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_MONITOR) { - error = ipw_setchannel(sc, ic->ic_curchan); - if (error != 0) - return error; - } - - if (ic->ic_opmode == IEEE80211_M_MONITOR) - return ipw_enable(sc); - - IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); - DPRINTF(("Setting MAC address to %6D\n", ic->ic_myaddr, ":")); - error = ipw_cmd(sc, IPW_CMD_SET_MAC_ADDRESS, ic->ic_myaddr, - IEEE80211_ADDR_LEN); - if (error != 0) - return error; - - config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK | - IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE); - if (ic->ic_opmode == IEEE80211_M_IBSS) - config.flags |= htole32(IPW_CFG_IBSS_AUTO_START); - if (ifp->if_flags & IFF_PROMISC) - config.flags |= htole32(IPW_CFG_PROMISCUOUS); - config.bss_chan = htole32(0x3fff); /* channels 1-14 */ - config.ibss_chan = htole32(0x7ff); /* channels 1-11 */ - DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags))); - error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config); - if (error != 0) - return error; - - data = htole32(0x3); /* 1, 2 */ - DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data); - if (error != 0) - return error; - - /* NB: use the same rate set */ - DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data); - if (error != 0) - return error; - - data = htole32(0xf); /* 1, 2, 5.5, 11 */ - DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data); - if (error != 0) - return error; - - data = htole32(IPW_POWER_MODE_CAM); - DPRINTF(("Setting power mode to %u\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data); - if (error != 0) - return error; - - if (ic->ic_opmode == IEEE80211_M_IBSS) { - data = htole32(32); /* default value */ - DPRINTF(("Setting tx power index to %u\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data, - sizeof data); - if (error != 0) - return error; - } - - data = htole32(ic->ic_rtsthreshold); - DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data); - if (error != 0) - return error; - - data = htole32(ic->ic_fragthreshold); - DPRINTF(("Setting frag threshold to %u\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); - if (error != 0) - return error; - - error = ipw_setssid(sc, ic->ic_des_ssid[0].ssid, ic->ic_des_ssid[0].len); - if (error != 0) - return error; - - error = ipw_setbssid(sc, NULL); - if (error != 0) - return error; - - if (ic->ic_flags & IEEE80211_F_DESBSSID) { - DPRINTF(("Setting desired BSSID to %6D\n", ic->ic_des_bssid, - ":")); - error = ipw_cmd(sc, IPW_CMD_SET_DESIRED_BSSID, - ic->ic_des_bssid, IEEE80211_ADDR_LEN); - if (error != 0) - return error; - } + goto done; memset(&security, 0, sizeof security); - security.authmode = (ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED) ? + security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ? IPW_AUTH_SHARED : IPW_AUTH_OPEN; security.ciphers = htole32(IPW_CIPHER_NONE); DPRINTF(("Setting authmode to %u\n", security.authmode)); error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security, sizeof security); if (error != 0) - return error; - - if (ic->ic_flags & IEEE80211_F_PRIVACY) { - error = ipw_setwepkeys(sc); - if (error != 0) - return error; - - if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE) { - data = htole32(ic->ic_crypto.cs_def_txkey); - DPRINTF(("Setting wep tx key index to %u\n", - le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data, - sizeof data); - if (error != 0) - return error; - } - } - - data = htole32((ic->ic_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0); - DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data); - if (error != 0) - return error; - - if (ic->ic_opt_ie != NULL) { - error = ipw_setwpaie(sc, ic->ic_opt_ie, ic->ic_opt_ie_len); - if (error != 0) - return error; - } - - if (ic->ic_opmode == IEEE80211_M_IBSS) { - data = htole32(ic->ic_bintval); - DPRINTF(("Setting beacon interval to %u\n", le32toh(data))); - error = ipw_cmd(sc, IPW_CMD_SET_BEACON_INTERVAL, &data, - sizeof data); - if (error != 0) - return error; - } - - error = ipw_setscanopts(sc, 0x3fff, 0); - if (error != 0) - return error; - - return (ipw_enable(sc)); -} - -/* - * Handler for sc_assoc_task. This is a simple wrapper around - * ipw_auth_and_assoc(). - */ -static void -ipw_assoc_task(void *context, int pending) -{ - struct ipw_softc *sc = context; - IPW_LOCK_DECL; - - IPW_LOCK(sc); - ipw_auth_and_assoc(sc); - IPW_UNLOCK(sc); -} - -static int -ipw_auth_and_assoc(struct ipw_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_node *ni = ic->ic_bss; - struct ipw_security security; - uint32_t data; - int error; + goto done; - error = ipw_disable(sc); + data = htole32(vap->iv_rtsthreshold); + DPRINTF(("Setting RTS threshold to %u\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_RTS_THRESHOLD, &data, sizeof data); if (error != 0) - return (error); + goto done; - memset(&security, 0, sizeof security); - security.authmode = (ni->ni_authmode == IEEE80211_AUTH_SHARED) ? - IPW_AUTH_SHARED : IPW_AUTH_OPEN; - security.ciphers = htole32(IPW_CIPHER_NONE); - DPRINTF(("Setting authmode to %u\n", security.authmode)); - error = ipw_cmd(sc, IPW_CMD_SET_SECURITY_INFO, &security, - sizeof security); + data = htole32(vap->iv_fragthreshold); + DPRINTF(("Setting frag threshold to %u\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_FRAG_THRESHOLD, &data, sizeof data); if (error != 0) - return (error); + goto done; - if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if (vap->iv_flags & IEEE80211_F_PRIVACY) { error = ipw_setwepkeys(sc); if (error != 0) - return error; + goto done; - if (ic->ic_crypto.cs_def_txkey != IEEE80211_KEYIX_NONE) { - data = htole32(ic->ic_crypto.cs_def_txkey); + if (vap->iv_def_txkey != IEEE80211_KEYIX_NONE) { + data = htole32(vap->iv_def_txkey); DPRINTF(("Setting wep tx key index to %u\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_WEP_KEY_INDEX, &data, sizeof data); if (error != 0) - return error; + goto done; } } - data = htole32((ic->ic_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0); + data = htole32((vap->iv_flags & IEEE80211_F_PRIVACY) ? IPW_WEPON : 0); DPRINTF(("Setting wep flags to 0x%x\n", le32toh(data))); error = ipw_cmd(sc, IPW_CMD_SET_WEP_FLAGS, &data, sizeof data); if (error != 0) - return error; + goto done; error = ipw_setssid(sc, ni->ni_essid, ni->ni_esslen); if (error != 0) - return (error); + goto done; error = ipw_setbssid(sc, ni->ni_bssid); if (error != 0) - return (error); + goto done; - if (ic->ic_opt_ie != NULL) { - error = ipw_setwpaie(sc, ic->ic_opt_ie, ic->ic_opt_ie_len); + if (vap->iv_appie_assocreq != NULL) { + struct ieee80211_appie *ie = vap->iv_appie_assocreq; + error = ipw_setwpaie(sc, ie->ie_data, ie->ie_len); if (error != 0) - return error; + goto done; } if (ic->ic_opmode == IEEE80211_M_IBSS) { error = ipw_setchannel(sc, ni->ni_chan); if (error != 0) - return (error); + goto done; } /* lock scan to ap's channel and enable associate */ error = ipw_setscanopts(sc, - 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0); + 1<<(ieee80211_chan2ieee(ic, ni->ni_chan)-1), 0); + if (error != 0) + goto done; - return ipw_enable(sc); /* finally, enable adapter */ + error = ipw_enable(sc); /* finally, enable adapter */ + if (error == 0) + sc->flags |= IPW_FLAG_ASSOCIATING; +done: + IPW_UNLOCK(sc); } -/* - * Handler for sc_disassoc_task. This is a simple wrapper around - * ipw_disassociate(). - */ static void ipw_disassoc_task(void *context, int pending) { - struct ipw_softc *sc = context; + struct ieee80211vap *vap = context; + struct ifnet *ifp = vap->iv_ic->ic_ifp; + struct ieee80211_node *ni = vap->iv_bss; + struct ipw_softc *sc = ifp->if_softc; IPW_LOCK_DECL; IPW_LOCK(sc); - ipw_disassociate(sc); - IPW_UNLOCK(sc); -} - -static int -ipw_disassociate(struct ipw_softc *sc) -{ - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211_node *ni = ic->ic_bss; - DPRINTF(("Disassociate from %6D\n", ni->ni_bssid, ":")); - /* * NB: don't try to do this if ipw_stop_master has * shutdown the firmware and disabled interrupts. */ - if (!(sc->flags & IPW_FLAG_FW_INITED)) - return (0); - - sc->flags &= ~IPW_FLAG_ASSOCIATED; - /* - * NB: firmware currently ignores bssid parameter, but - * supply it in case this changes (follow linux driver). - */ - return ipw_cmd(sc, IPW_CMD_DISASSOCIATE, - ni->ni_bssid, IEEE80211_ADDR_LEN); + if (sc->flags & IPW_FLAG_FW_INITED) { + sc->flags &= ~IPW_FLAG_ASSOCIATED; + /* + * NB: firmware currently ignores bssid parameter, but + * supply it in case this changes (follow linux driver). + */ + (void) ipw_cmd(sc, IPW_CMD_DISASSOCIATE, + ni->ni_bssid, IEEE80211_ADDR_LEN); + } + IPW_UNLOCK(sc); } /* @@ -2496,27 +2394,31 @@ static void ipw_init(void *priv) { struct ipw_softc *sc = priv; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; IPW_LOCK_DECL; IPW_LOCK(sc); - ipw_init_locked(sc, 0); + ipw_init_locked(sc); IPW_UNLOCK(sc); + + ieee80211_start_all(ic); } static void -ipw_init_locked(struct ipw_softc *sc, int force) +ipw_init_locked(struct ipw_softc *sc) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); const struct firmware *fp; const struct ipw_firmware_hdr *hdr; - const char *imagename, *fw; - IPW_LOCK_DECL; + const char *fw; IPW_LOCK_ASSERT(sc); DPRINTF(("%s: state %s flags 0x%x\n", __func__, - ieee80211_state_name[ic->ic_state], sc->flags)); + ieee80211_state_name[vap->iv_state], sc->flags)); /* * Avoid re-entrant calls. We need to release the mutex in ipw_init() @@ -2531,63 +2433,22 @@ ipw_init_locked(struct ipw_softc *sc, int force) if (ipw_reset(sc) != 0) { device_printf(sc->sc_dev, "could not reset adapter\n"); - goto fail1; - } - - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - imagename = "ipw_bss"; - break; - case IEEE80211_M_IBSS: - imagename = "ipw_ibss"; - break; - case IEEE80211_M_MONITOR: - imagename = "ipw_monitor"; - break; - default: - imagename = NULL; /* should not get there */ - } - - /* - * Load firmware image using the firmware(9) subsystem. We need to - * release the driver's lock first. - */ - if (sc->sc_firmware == NULL || strcmp(sc->sc_firmware->name, - imagename) != 0) { - IPW_UNLOCK(sc); - if (sc->sc_firmware != NULL) - firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); - sc->sc_firmware = firmware_get(imagename); - IPW_LOCK(sc); + goto fail; } if (sc->sc_firmware == NULL) { - device_printf(sc->sc_dev, - "could not load firmware image '%s'\n", imagename); - goto fail1; + device_printf(sc->sc_dev, "no firmware\n"); + goto fail; } - + /* NB: consistency already checked on load */ fp = sc->sc_firmware; - if (fp->datasize < sizeof *hdr) { - device_printf(sc->sc_dev, - "firmware image too short %zu\n", fp->datasize); - goto fail2; - } - hdr = (const struct ipw_firmware_hdr *)fp->data; - if (fp->datasize < sizeof *hdr + le32toh(hdr->mainsz) + - le32toh(hdr->ucodesz)) { - device_printf(sc->sc_dev, - "firmware image too short %zu\n", fp->datasize); - goto fail2; - } - - DPRINTF(("Loading firmware image '%s'\n", imagename)); + DPRINTF(("Loading firmware image '%s'\n", fp->name)); fw = (const char *)fp->data + sizeof *hdr + le32toh(hdr->mainsz); if (ipw_load_ucode(sc, fw, le32toh(hdr->ucodesz)) != 0) { device_printf(sc->sc_dev, "could not load microcode\n"); - goto fail2; + goto fail; } ipw_stop_master(sc); @@ -2615,7 +2476,7 @@ ipw_init_locked(struct ipw_softc *sc, int force) fw = (const char *)fp->data + sizeof *hdr; if (ipw_load_firmware(sc, fw, le32toh(hdr->mainsz)) != 0) { device_printf(sc->sc_dev, "could not load firmware\n"); - goto fail2; + goto fail; } sc->flags |= IPW_FLAG_FW_INITED; @@ -2628,21 +2489,9 @@ ipw_init_locked(struct ipw_softc *sc, int force) if (ipw_config(sc) != 0) { device_printf(sc->sc_dev, "device configuration failed\n"); - goto fail1; + goto fail; } - if (ic->ic_opmode != IEEE80211_M_MONITOR) { - /* - * NB: When restarting the adapter clock the state - * machine regardless of the roaming mode; otherwise - * we need to notify user apps so they can manually - * get us going again. - */ - if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL || force) - ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); - } else - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); - callout_reset(&sc->sc_wdtimer, hz, ipw_watchdog, sc); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; @@ -2650,13 +2499,102 @@ ipw_init_locked(struct ipw_softc *sc, int force) sc->flags &=~ IPW_FLAG_INIT_LOCKED; return; -fail2: firmware_put(fp, FIRMWARE_UNLOAD); - sc->sc_firmware = NULL; -fail1: ifp->if_flags &= ~IFF_UP; +fail: ipw_stop_locked(sc); sc->flags &=~ IPW_FLAG_INIT_LOCKED; } +static int +ipw_config(struct ipw_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ipw_configuration config; + uint32_t data; + int error; + + error = ipw_disable(sc); + if (error != 0) + return error; + + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_WDS: /* XXX */ + data = htole32(IPW_MODE_BSS); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + data = htole32(IPW_MODE_IBSS); + break; + case IEEE80211_M_MONITOR: + data = htole32(IPW_MODE_MONITOR); + break; + } + DPRINTF(("Setting mode to %u\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_MODE, &data, sizeof data); + if (error != 0) + return error; + + if (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_MONITOR) { + error = ipw_setchannel(sc, ic->ic_curchan); + if (error != 0) + return error; + } + + if (ic->ic_opmode == IEEE80211_M_MONITOR) + return ipw_enable(sc); + + config.flags = htole32(IPW_CFG_BSS_MASK | IPW_CFG_IBSS_MASK | + IPW_CFG_PREAMBLE_AUTO | IPW_CFG_802_1x_ENABLE); + if (ic->ic_opmode == IEEE80211_M_IBSS) + config.flags |= htole32(IPW_CFG_IBSS_AUTO_START); + if (ifp->if_flags & IFF_PROMISC) + config.flags |= htole32(IPW_CFG_PROMISCUOUS); + config.bss_chan = htole32(0x3fff); /* channels 1-14 */ + config.ibss_chan = htole32(0x7ff); /* channels 1-11 */ + DPRINTF(("Setting configuration to 0x%x\n", le32toh(config.flags))); + error = ipw_cmd(sc, IPW_CMD_SET_CONFIGURATION, &config, sizeof config); + if (error != 0) + return error; + + data = htole32(0x3); /* 1, 2 */ + DPRINTF(("Setting basic tx rates to 0x%x\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_BASIC_TX_RATES, &data, sizeof data); + if (error != 0) + return error; + + /* NB: use the same rate set */ + DPRINTF(("Setting msdu tx rates to 0x%x\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_MSDU_TX_RATES, &data, sizeof data); + if (error != 0) + return error; + + data = htole32(0xf); /* 1, 2, 5.5, 11 */ + DPRINTF(("Setting tx rates to 0x%x\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_TX_RATES, &data, sizeof data); + if (error != 0) + return error; + + data = htole32(IPW_POWER_MODE_CAM); + DPRINTF(("Setting power mode to %u\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_POWER_MODE, &data, sizeof data); + if (error != 0) + return error; + + if (ic->ic_opmode == IEEE80211_M_IBSS) { + data = htole32(32); /* default value */ + DPRINTF(("Setting tx power index to %u\n", le32toh(data))); + error = ipw_cmd(sc, IPW_CMD_SET_TX_POWER_INDEX, &data, + sizeof data); + if (error != 0) + return error; + } + + return 0; +} + static void ipw_stop(void *priv) { @@ -2671,14 +2609,11 @@ ipw_stop(void *priv) static void ipw_stop_locked(struct ipw_softc *sc) { - struct ieee80211com *ic = &sc->sc_ic; - struct ifnet *ifp = ic->ic_ifp; + struct ifnet *ifp = sc->sc_ifp; int i; IPW_LOCK_ASSERT(sc); - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); - callout_stop(&sc->sc_wdtimer); ipw_stop_master(sc); @@ -2795,7 +2730,7 @@ ipw_scan_start(struct ieee80211com *ic) IPW_LOCK(sc); if (!(sc->flags & IPW_FLAG_SCANNING)) - taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_scan_task); + taskqueue_enqueue(taskqueue_swi, &sc->sc_scan_task); IPW_UNLOCK(sc); } @@ -2816,13 +2751,13 @@ ipw_set_channel(struct ieee80211com *ic) } static void -ipw_scan_curchan(struct ieee80211com *ic, unsigned long maxdwell) +ipw_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { /* NB: all channels are scanned at once */ } static void -ipw_scan_mindwell(struct ieee80211com *ic) +ipw_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } diff --git a/sys/dev/ipw/if_ipwvar.h b/sys/dev/ipw/if_ipwvar.h index f6ec999..de35019 100644 --- a/sys/dev/ipw/if_ipwvar.h +++ b/sys/dev/ipw/if_ipwvar.h @@ -76,30 +76,40 @@ struct ipw_tx_radiotap_header { ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) +struct ipw_vap { + struct ieee80211vap vap; + struct task assoc_task; + struct task disassoc_task; + struct task assoc_success_task; + struct task assoc_failed_task; + struct task scandone_task; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define IPW_VAP(vap) ((struct ipw_vap *)(vap)) + struct ipw_softc { struct ifnet *sc_ifp; - struct ieee80211com sc_ic; - int (*sc_newstate)(struct ieee80211com *, - enum ieee80211_state, int); device_t sc_dev; struct mtx sc_mtx; struct task sc_init_task; struct task sc_scan_task; struct task sc_chan_task; - struct task sc_assoc_task; - struct task sc_disassoc_task; + struct task sc_bmiss_task; struct callout sc_wdtimer; /* watchdog timer */ uint32_t flags; -#define IPW_FLAG_FW_INITED (1 << 0) -#define IPW_FLAG_INIT_LOCKED (1 << 1) -#define IPW_FLAG_HAS_RADIO_SWITCH (1 << 2) -#define IPW_FLAG_HACK (1 << 3) -#define IPW_FLAG_SCANNING (1 << 4) -#define IPW_FLAG_ENABLED (1 << 5) -#define IPW_FLAG_BUSY (1 << 6) -#define IPW_FLAG_ASSOCIATED (1 << 7) +#define IPW_FLAG_FW_INITED 0x0001 +#define IPW_FLAG_INIT_LOCKED 0x0002 +#define IPW_FLAG_HAS_RADIO_SWITCH 0x0004 +#define IPW_FLAG_HACK 0x0008 +#define IPW_FLAG_SCANNING 0x0010 +#define IPW_FLAG_ENABLED 0x0020 +#define IPW_FLAG_BUSY 0x0040 +#define IPW_FLAG_ASSOCIATING 0x0080 +#define IPW_FLAG_ASSOCIATED 0x0100 int irq_rid; int mem_rid; @@ -152,22 +162,10 @@ struct ipw_softc { uint32_t rxcur; int txfree; - int dwelltime; - - struct bpf_if *sc_drvbpf; - - union { - struct ipw_rx_radiotap_header th; - uint8_t pad[64]; - } sc_rxtapu; -#define sc_rxtap sc_rxtapu.th + struct ipw_rx_radiotap_header sc_rxtap; int sc_rxtap_len; - union { - struct ipw_tx_radiotap_header th; - uint8_t pad[64]; - } sc_txtapu; -#define sc_txtap sc_txtapu.th + struct ipw_tx_radiotap_header sc_txtap; int sc_txtap_len; }; |