diff options
Diffstat (limited to 'sys/dev/iwn/if_iwn.c')
-rw-r--r-- | sys/dev/iwn/if_iwn.c | 326 |
1 files changed, 103 insertions, 223 deletions
diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c index 2928c0f..f32bdaa 100644 --- a/sys/dev/iwn/if_iwn.c +++ b/sys/dev/iwn/if_iwn.c @@ -129,7 +129,6 @@ void iwn_rx_intr(struct iwn_softc *, struct iwn_rx_desc *, void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *); void iwn_tx_intr(struct iwn_softc *, struct iwn_rx_desc *); void iwn_cmd_intr(struct iwn_softc *, struct iwn_rx_desc *); -static void iwn_bmiss(void *, int); void iwn_notif_intr(struct iwn_softc *); void iwn_intr(void *); void iwn_read_eeprom(struct iwn_softc *, @@ -166,8 +165,8 @@ void iwn_compute_differential_gain(struct iwn_softc *, void iwn_tune_sensitivity(struct iwn_softc *, const struct iwn_rx_stats *); int iwn_send_sensitivity(struct iwn_softc *); -int iwn_auth(struct iwn_softc *); -int iwn_run(struct iwn_softc *); +int iwn_auth(struct iwn_softc *, struct ieee80211vap *); +int iwn_run(struct iwn_softc *, struct ieee80211vap *); int iwn_scan(struct iwn_softc *); int iwn_config(struct iwn_softc *); void iwn_post_alive(struct iwn_softc *); @@ -183,8 +182,9 @@ static void iwn_scan_end(struct ieee80211com *); static void iwn_set_channel(struct ieee80211com *); static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void iwn_scan_mindwell(struct ieee80211_scan_state *); -static void iwn_ops(void *, int); -static int iwn_queue_cmd( struct iwn_softc *, int, int, int); +static void iwn_hwreset(void *, int); +static void iwn_radioon(void *, int); +static void iwn_radiooff(void *, int); static void iwn_bpfattach(struct iwn_softc *); static void iwn_sysctlattach(struct iwn_softc *); @@ -213,7 +213,6 @@ enum { printf(fmt, __VA_ARGS__); \ } while (0) -static const char *iwn_ops_str(int); static const char *iwn_intr_str(uint8_t); #else #define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) @@ -296,20 +295,10 @@ iwn_attach(device_t dev) } IWN_LOCK_INIT(sc); - IWN_CMD_LOCK_INIT(sc); callout_init_mtx(&sc->sc_timer_to, &sc->sc_mtx, 0); - - /* - * Create the taskqueues used by the driver. Primarily - * sc_tq handles most the task - */ - sc->sc_tq = taskqueue_create("iwn_taskq", M_NOWAIT | M_ZERO, - taskqueue_thread_enqueue, &sc->sc_tq); - taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", - device_get_nameunit(dev)); - - TASK_INIT(&sc->sc_ops_task, 0, iwn_ops, sc ); - TASK_INIT(&sc->sc_bmiss_task, 0, iwn_bmiss, sc); + TASK_INIT(&sc->sc_reinit_task, 0, iwn_hwreset, sc ); + TASK_INIT(&sc->sc_radioon_task, 0, iwn_radioon, sc ); + TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radiooff, sc ); /* * Put adapter into a known state. @@ -475,6 +464,10 @@ iwn_cleanup(device_t dev) struct ieee80211com *ic = ifp->if_l2com; int i; + ieee80211_draintask(ic, &sc->sc_reinit_task); + ieee80211_draintask(ic, &sc->sc_radioon_task); + ieee80211_draintask(ic, &sc->sc_radiooff_task); + if (ifp != NULL) { iwn_stop(sc); callout_drain(&sc->sc_timer_to); @@ -499,8 +492,6 @@ iwn_cleanup(device_t dev) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); if (ifp != NULL) if_free(ifp); - taskqueue_free(sc->sc_tq); - IWN_CMD_LOCK_DESTROY(sc); IWN_LOCK_DESTROY(sc); return 0; } @@ -983,20 +974,13 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); + IEEE80211_UNLOCK(ic); IWN_LOCK(sc); callout_stop(&sc->sc_timer_to); - IWN_UNLOCK(sc); - /* - * Some state transitions require issuing a configure request - * to the adapter. This must be done in a blocking context - * so we toss control to the task q thread where the state - * change will be finished after the command completes. - */ if (nstate == IEEE80211_S_AUTH && vap->iv_state != IEEE80211_S_AUTH) { /* !AUTH -> AUTH requires adapter config */ - error = iwn_queue_cmd(sc, IWN_AUTH, arg, IWN_QUEUE_NORMAL); - return (error != 0 ? error : EINPROGRESS); + error = iwn_auth(sc, vap); } if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { /* @@ -1004,8 +988,7 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * which is done with a firmware cmd. We also defer * starting the timers until that work is done. */ - error = iwn_queue_cmd(sc, IWN_RUN, arg, IWN_QUEUE_NORMAL); - return (error != 0 ? error : EINPROGRESS); + error = iwn_run(sc, vap); } if (nstate == IEEE80211_S_RUN) { /* @@ -1013,6 +996,8 @@ iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) */ iwn_calib_reset(sc); } + IWN_UNLOCK(sc); + IEEE80211_LOCK(ic); return ivp->iv_newstate(vap, nstate, arg); } @@ -1657,15 +1642,6 @@ iwn_cmd_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc) wakeup(&ring->cmd[desc->idx]); } -static void -iwn_bmiss(void *arg, int npending) -{ - struct iwn_softc *sc = arg; - struct ieee80211com *ic = sc->sc_ifp->if_l2com; - - ieee80211_beacon_miss(ic); -} - void iwn_notif_intr(struct iwn_softc *sc) { @@ -1726,8 +1702,7 @@ iwn_notif_intr(struct iwn_softc *sc) if (vap->iv_state == IEEE80211_S_RUN && misses > 5) (void) iwn_init_sensitivity(sc); if (misses >= vap->iv_bmissthreshold) - taskqueue_enqueue(taskqueue_swi, - &sc->sc_bmiss_task); + ieee80211_beacon_miss(ic); break; } case IWN_UC_READY: { @@ -1780,7 +1755,7 @@ iwn_notif_intr(struct iwn_softc *sc) "scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan); - iwn_queue_cmd(sc, IWN_SCAN_NEXT, 0, IWN_QUEUE_NORMAL); + ieee80211_scan_next(vap); break; } } @@ -1792,6 +1767,36 @@ iwn_notif_intr(struct iwn_softc *sc) IWN_WRITE(sc, IWN_RX_WIDX, hw & ~7); } +static void +iwn_rftoggle_intr(struct iwn_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); + + IWN_LOCK_ASSERT(sc); + + device_printf(sc->sc_dev, "RF switch: radio %s\n", + (tmp & IWN_GPIO_RF_ENABLED) ? "enabled" : "disabled"); + if (tmp & IWN_GPIO_RF_ENABLED) + ieee80211_runtask(ic, &sc->sc_radioon_task); + else + ieee80211_runtask(ic, &sc->sc_radiooff_task); +} + +static void +iwn_error_intr(struct iwn_softc *sc, uint32_t r1, uint32_t r2) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + IWN_LOCK_ASSERT(sc); + + device_printf(sc->sc_dev, "error, INTR=%b STATUS=0x%x\n", + r1, IWN_INTR_BITS, r2); + ieee80211_runtask(ic, &sc->sc_reinit_task); +} + void iwn_intr(void *arg) { @@ -1820,21 +1825,12 @@ iwn_intr(void *arg) DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2); - if (r1 & IWN_RF_TOGGLED) { - uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); - device_printf(sc->sc_dev, "RF switch: radio %s\n", - (tmp & IWN_GPIO_RF_ENABLED) ? "enabled" : "disabled"); - if (tmp & IWN_GPIO_RF_ENABLED) - iwn_queue_cmd(sc, IWN_RADIO_ENABLE, 0, IWN_QUEUE_CLEAR); - else - iwn_queue_cmd(sc, IWN_RADIO_DISABLE, 0, IWN_QUEUE_CLEAR); - } + if (r1 & IWN_RF_TOGGLED) + iwn_rftoggle_intr(sc); if (r1 & IWN_CT_REACHED) device_printf(sc->sc_dev, "critical temperature reached!\n"); if (r1 & (IWN_SW_ERROR | IWN_HW_ERROR)) { - device_printf(sc->sc_dev, "error, INTR=%b STATUS=0x%x\n", - r1, IWN_INTR_BITS, r2); - iwn_queue_cmd(sc, IWN_REINIT, 0, IWN_QUEUE_CLEAR); + iwn_error_intr(sc, r1, r2); goto done; } if ((r1 & (IWN_RX_INTR | IWN_SW_RX_INTR)) || (r2 & IWN_RX_STATUS_INTR)) @@ -2361,9 +2357,10 @@ iwn_watchdog(struct iwn_softc *sc) { if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; if_printf(ifp, "device timeout\n"); - iwn_queue_cmd(sc, IWN_REINIT, 0, IWN_QUEUE_CLEAR); + ieee80211_runtask(ic, &sc->sc_reinit_task); } } @@ -3446,11 +3443,10 @@ iwn_send_sensitivity(struct iwn_softc *sc) } int -iwn_auth(struct iwn_softc *sc) +iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); /*XXX*/ struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; int error; @@ -3539,12 +3535,11 @@ iwn_auth(struct iwn_softc *sc) * Configure the adapter for associated state. */ int -iwn_run(struct iwn_softc *sc) +iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) { #define MS(v,x) (((v) & x) >> x##_S) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); /*XXX*/ struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; int error, maxrxampdu, ampdudensity; @@ -4270,9 +4265,6 @@ iwn_stop_locked(struct iwn_softc *sc) IWN_WRITE(sc, IWN_INTR, 0xffffffff); IWN_WRITE(sc, IWN_INTR_STATUS, 0xffffffff); - /* Clear any commands left in the taskq command buffer */ - memset(sc->sc_cmd, 0, sizeof(sc->sc_cmd)); - /* reset all Tx rings */ for (i = 0; i < IWN_NTXQUEUES; i++) iwn_reset_tx_ring(sc, &sc->txq[i]); @@ -4308,7 +4300,10 @@ iwn_scan_start(struct ieee80211com *ic) struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; - iwn_queue_cmd(sc, IWN_SCAN_START, 0, IWN_QUEUE_NORMAL); + IWN_LOCK(sc); + /* make the link LED blink while we're scanning */ + iwn_set_led(sc, IWN_LED_LINK, 20, 2); + IWN_UNLOCK(sc); } /* @@ -4317,10 +4312,7 @@ iwn_scan_start(struct ieee80211com *ic) static void iwn_scan_end(struct ieee80211com *ic) { - struct ifnet *ifp = ic->ic_ifp; - struct iwn_softc *sc = ifp->if_softc; - - iwn_queue_cmd(sc, IWN_SCAN_STOP, 0, IWN_QUEUE_NORMAL); + /* ignore */ } /* @@ -4331,15 +4323,29 @@ iwn_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; + struct ieee80211vap *vap; const struct ieee80211_channel *c = ic->ic_curchan; + int error; + + vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ + IWN_LOCK(sc); if (c != sc->sc_curchan) { sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); - iwn_queue_cmd(sc, IWN_SET_CHAN, 0, IWN_QUEUE_NORMAL); + + error = iwn_config(sc); + if (error != 0) { + DPRINTF(sc, IWN_DEBUG_STATE, + "%s: set chan failed, cancel scan\n", + __func__); + //XXX Handle failed scan correctly + ieee80211_cancel_scan(vap); + } } + IWN_UNLOCK(sc); } /* @@ -4350,8 +4356,13 @@ iwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc; + int error; - iwn_queue_cmd(sc, IWN_SCAN_CURCHAN, 0, IWN_QUEUE_NORMAL); + IWN_LOCK(sc); + error = iwn_scan(sc); + IWN_UNLOCK(sc); + if (error != 0) + ieee80211_cancel_scan(vap); } /* @@ -4365,149 +4376,36 @@ iwn_scan_mindwell(struct ieee80211_scan_state *ss) /* NB: don't try to abort scan; wait for firmware to finish */ } -/* - * Carry out work in the taskq context. - */ static void -iwn_ops(void *arg0, int pending) +iwn_hwreset(void *arg0, int pending) { struct iwn_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap; - int cmd, arg, error; - enum ieee80211_state nstate; - for (;;) { - IWN_CMD_LOCK(sc); - cmd = sc->sc_cmd[sc->sc_cmd_cur]; - if (cmd == 0) { - /* No more commands to process */ - IWN_CMD_UNLOCK(sc); - return; - } - if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 && - cmd != IWN_RADIO_ENABLE ) { - IWN_CMD_UNLOCK(sc); - return; - } - arg = sc->sc_cmd_arg[sc->sc_cmd_cur]; - sc->sc_cmd[sc->sc_cmd_cur] = 0; /* free the slot */ - sc->sc_cmd_cur = (sc->sc_cmd_cur + 1) % IWN_CMD_MAXOPS; - IWN_CMD_UNLOCK(sc); - - IWN_LOCK(sc); /* NB: sync debug printfs on smp */ - DPRINTF(sc, IWN_DEBUG_OPS, "%s: %s (cmd 0x%x)\n", - __func__, iwn_ops_str(cmd), cmd); - - vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ - switch (cmd) { - case IWN_SCAN_START: - /* make the link LED blink while we're scanning */ - iwn_set_led(sc, IWN_LED_LINK, 20, 2); - break; - case IWN_SCAN_STOP: - break; - case IWN_SCAN_NEXT: - ieee80211_scan_next(vap); - break; - case IWN_SCAN_CURCHAN: - error = iwn_scan(sc); - if (error != 0) { - IWN_UNLOCK(sc); - ieee80211_cancel_scan(vap); - IWN_LOCK(sc); - return; - } - break; - case IWN_SET_CHAN: - error = iwn_config(sc); - if (error != 0) { - DPRINTF(sc, IWN_DEBUG_STATE, - "%s: set chan failed, cancel scan\n", - __func__); - IWN_UNLOCK(sc); - //XXX Handle failed scan correctly - ieee80211_cancel_scan(vap); - return; - } - break; - case IWN_AUTH: - case IWN_RUN: - if (cmd == IWN_AUTH) { - error = iwn_auth(sc); - nstate = IEEE80211_S_AUTH; - } else { - error = iwn_run(sc); - nstate = IEEE80211_S_RUN; - } - if (error == 0) { - IWN_UNLOCK(sc); - IEEE80211_LOCK(ic); - IWN_VAP(vap)->iv_newstate(vap, nstate, arg); - if (vap->iv_newstate_cb != NULL) - vap->iv_newstate_cb(vap, nstate, arg); - IEEE80211_UNLOCK(ic); - IWN_LOCK(sc); - } else { - device_printf(sc->sc_dev, - "%s: %s state change failed, error %d\n", - __func__, ieee80211_state_name[nstate], - error); - } - break; - case IWN_REINIT: - IWN_UNLOCK(sc); - iwn_init(sc); - IWN_LOCK(sc); - ieee80211_notify_radio(ic, 1); - break; - case IWN_RADIO_ENABLE: - KASSERT(sc->fw_fp != NULL, - ("Fware Not Loaded, can't load from tq")); - IWN_UNLOCK(sc); - iwn_init(sc); - IWN_LOCK(sc); - break; - case IWN_RADIO_DISABLE: - ieee80211_notify_radio(ic, 0); - iwn_stop_locked(sc); - break; - } - IWN_UNLOCK(sc); - } + iwn_init(sc); + ieee80211_notify_radio(ic, 1); } -/* - * Queue a command for execution in the taskq thread. - * This is needed as the net80211 callbacks do not allow - * sleeping, since we need to sleep to confirm commands have - * been processed by the firmware, we must defer execution to - * a sleep enabled thread. - */ -static int -iwn_queue_cmd(struct iwn_softc *sc, int cmd, int arg, int clear) +static void +iwn_radioon(void *arg0, int pending) { - IWN_CMD_LOCK(sc); - if (clear) { - sc->sc_cmd[0] = cmd; - sc->sc_cmd_arg[0] = arg; - sc->sc_cmd_cur = 0; - sc->sc_cmd_next = 1; - } else { - if (sc->sc_cmd[sc->sc_cmd_next] != 0) { - IWN_CMD_UNLOCK(sc); - DPRINTF(sc, IWN_DEBUG_ANY, "%s: command %d dropped\n", - __func__, cmd); - return EBUSY; - } - sc->sc_cmd[sc->sc_cmd_next] = cmd; - sc->sc_cmd_arg[sc->sc_cmd_next] = arg; - sc->sc_cmd_next = (sc->sc_cmd_next + 1) % IWN_CMD_MAXOPS; - } - taskqueue_enqueue(sc->sc_tq, &sc->sc_ops_task); - IWN_CMD_UNLOCK(sc); - return 0; + struct iwn_softc *sc = arg0; + + iwn_init(sc); +} + +static void +iwn_radiooff(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + IWN_LOCK(sc); + ieee80211_notify_radio(ic, 0); + iwn_stop_locked(sc); + IWN_UNLOCK(sc); } static void @@ -4542,24 +4440,6 @@ iwn_sysctlattach(struct iwn_softc *sc) #ifdef IWN_DEBUG static const char * -iwn_ops_str(int cmd) -{ - switch (cmd) { - case IWN_SCAN_START: return "SCAN_START"; - case IWN_SCAN_CURCHAN: return "SCAN_CURCHAN"; - case IWN_SCAN_STOP: return "SCAN_STOP"; - case IWN_SET_CHAN: return "SET_CHAN"; - case IWN_AUTH: return "AUTH"; - case IWN_SCAN_NEXT: return "SCAN_NEXT"; - case IWN_RUN: return "RUN"; - case IWN_RADIO_ENABLE: return "RADIO_ENABLE"; - case IWN_RADIO_DISABLE: return "RADIO_DISABLE"; - case IWN_REINIT: return "REINIT"; - } - return "UNKNOWN COMMAND"; -} - -static const char * iwn_intr_str(uint8_t cmd) { switch (cmd) { |