diff options
Diffstat (limited to 'sys/dev/ath/if_ath.c')
-rw-r--r-- | sys/dev/ath/if_ath.c | 255 |
1 files changed, 235 insertions, 20 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 9269f5f..58687a7 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -117,6 +117,7 @@ static int ath_ioctl(struct ifnet *, u_long, caddr_t); static void ath_fatal_proc(void *, int); static void ath_rxorn_proc(void *, int); static void ath_bmiss_proc(void *, int); +static void ath_radar_proc(void *, int); static int ath_key_alloc(struct ieee80211com *, const struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); @@ -239,6 +240,8 @@ enum { ATH_DEBUG_STATE = 0x00040000, /* 802.11 state transitions */ ATH_DEBUG_NODE = 0x00080000, /* node management */ ATH_DEBUG_LED = 0x00100000, /* led management */ + ATH_DEBUG_FF = 0x00200000, /* fast frames */ + ATH_DEBUG_DFS = 0x00400000, /* DFS processing */ ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */ ATH_DEBUG_ANY = 0xffffffff }; @@ -380,6 +383,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) } callout_init(&sc->sc_scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0); callout_init(&sc->sc_cal_ch, CALLOUT_MPSAFE); + callout_init(&sc->sc_dfs_ch, CALLOUT_MPSAFE); ATH_TXBUF_LOCK_INIT(sc); @@ -392,7 +396,8 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) TASK_INIT(&sc->sc_rxorntask, 0, ath_rxorn_proc, sc); TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc); TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc); - TASK_INIT(&sc->sc_bstucktask, 0, ath_bstuck_proc, sc); + TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc); + TASK_INIT(&sc->sc_radartask, 0, ath_radar_proc, sc); /* * Allocate hardware transmit queues: one queue for @@ -858,6 +863,24 @@ ath_bmiss_proc(void *arg, int pending) } } +static void +ath_radar_proc(void *arg, int pending) +{ + struct ath_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ath_hal *ah = sc->sc_ah; + HAL_CHANNEL hchan; + + if (ath_hal_procdfs(ah, &hchan)) { + if_printf(ifp, "radar detected on channel %u/0x%x/0x%x\n", + hchan.channel, hchan.channelFlags, hchan.privFlags); + /* + * Initiate channel change. + */ + /* XXX not yet */ + } +} + static u_int ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan) { @@ -868,7 +891,7 @@ ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan) CHANNEL_B, /* IEEE80211_MODE_11B */ CHANNEL_PUREG, /* IEEE80211_MODE_11G */ 0, /* IEEE80211_MODE_FH */ - CHANNEL_T, /* IEEE80211_MODE_TURBO_A */ + CHANNEL_ST, /* IEEE80211_MODE_TURBO_A */ CHANNEL_108G /* IEEE80211_MODE_TURBO_G */ }; enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan); @@ -923,6 +946,8 @@ ath_init(void *arg) * state cached in the driver. */ sc->sc_diversity = ath_hal_getdiversity(ah); + sc->sc_calinterval = 1; + sc->sc_caltries = 0; /* * Setup the hardware after reset: the key cache @@ -1044,7 +1069,7 @@ ath_stop(struct ifnet *ifp) * (and system). This varies by chip and is mostly an * issue with newer parts that go to sleep more quickly. */ - ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP, 0); + ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); } ATH_UNLOCK(sc); } @@ -1082,14 +1107,16 @@ ath_reset(struct ifnet *ifp) __func__, status); ath_update_txpow(sc); /* update tx power state */ sc->sc_diversity = ath_hal_getdiversity(ah); - if (ath_startrecv(sc) != 0) /* restart recv */ - if_printf(ifp, "%s: unable to start recv logic\n", __func__); + sc->sc_calinterval = 1; + sc->sc_caltries = 0; /* * We may be doing a reset in response to an ioctl * that changes the channel so update any state that * might change as a result. */ ath_chan_change(sc, c); + if (ath_startrecv(sc) != 0) /* restart recv */ + if_printf(ifp, "%s: unable to start recv logic\n", __func__); if (ic->ic_state == IEEE80211_S_RUN) ath_beacon_config(sc); /* restart beacons */ ath_hal_intrset(ah, sc->sc_imask); @@ -1664,12 +1691,13 @@ ath_key_update_end(struct ieee80211com *ic) static u_int32_t ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state) { +#define RX_FILTER_PRESERVE (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR) struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = sc->sc_ah; struct ifnet *ifp = sc->sc_ifp; u_int32_t rfilt; - rfilt = (ath_hal_getrxfilter(ah) & HAL_RX_FILTER_PHYERR) + rfilt = (ath_hal_getrxfilter(ah) & RX_FILTER_PRESERVE) | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; if (ic->ic_opmode != IEEE80211_M_STA) rfilt |= HAL_RX_FILTER_PROBEREQ; @@ -1681,6 +1709,7 @@ ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state) state == IEEE80211_S_SCAN) rfilt |= HAL_RX_FILTER_BEACON; return rfilt; +#undef RX_FILTER_PRESERVE } static void @@ -1786,7 +1815,7 @@ ath_beaconq_setup(struct ath_hal *ah) qi.tqi_cwmin = HAL_TXQ_USEDEFAULT; qi.tqi_cwmax = HAL_TXQ_USEDEFAULT; /* NB: for dynamic turbo, don't enable any other interrupts */ - qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE; + qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE; return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi); } @@ -2600,6 +2629,7 @@ ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) ds = bf->bf_desc; ds->ds_link = bf->bf_daddr; /* link to self */ ds->ds_data = bf->bf_segs[0].ds_addr; + ds->ds_vdata = mtod(m, void *); /* for radar */ ath_hal_setuprxdesc(ah, ds , m->m_len /* buffer size */ , 0 @@ -2980,7 +3010,9 @@ rx_next: } while (ath_rxbuf_init(sc, bf) == 0); /* rx signal state monitoring */ - ath_hal_rxmonitor(ah, &sc->sc_halstats); + ath_hal_rxmonitor(ah, &sc->sc_halstats, &sc->sc_curchan); + if (ath_hal_radar_event(ah)) + taskqueue_enqueue(sc->sc_tq, &sc->sc_radartask); if (ngood) sc->sc_lastrx = tsf; @@ -3016,7 +3048,7 @@ ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) * up in which case the top half of the kernel may backup * due to a lack of tx descriptors. */ - qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE; + qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE; qnum = ath_hal_setuptxqueue(ah, qtype, &qi); if (qnum == -1) { /* @@ -4097,6 +4129,42 @@ ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) } /* + * Poll for a channel clear indication; this is required + * for channels requiring DFS and not previously visited + * and/or with a recent radar detection. + */ +static void +ath_dfswait(void *arg) +{ + struct ath_softc *sc = arg; + struct ath_hal *ah = sc->sc_ah; + HAL_CHANNEL hchan; + + ath_hal_radar_wait(ah, &hchan); + DPRINTF(sc, ATH_DEBUG_DFS, "%s: radar_wait %u/%x/%x\n", + __func__, hchan.channel, hchan.channelFlags, hchan.privFlags); + + if (hchan.privFlags & CHANNEL_INTERFERENCE) { + if_printf(sc->sc_ifp, + "channel %u/0x%x/0x%x has interference\n", + hchan.channel, hchan.channelFlags, hchan.privFlags); + return; + } + if ((hchan.privFlags & CHANNEL_DFS) == 0) { + /* XXX should not happen */ + return; + } + if (hchan.privFlags & CHANNEL_DFS_CLEAR) { + sc->sc_curchan.privFlags |= CHANNEL_DFS_CLEAR; + sc->sc_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + if_printf(sc->sc_ifp, + "channel %u/0x%x/0x%x marked clear\n", + hchan.channel, hchan.channelFlags, hchan.privFlags); + } else + callout_reset(&sc->sc_dfs_ch, 2 * hz, ath_dfswait, sc); +} + +/* * Set/change channels. If the channel is really being changed, * it's done by reseting the chip. To accomplish this we must * first cleanup any pending DMA, then restart stuff after a la @@ -4120,10 +4188,10 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz, hal flags 0x%x) -> %u (%u MHz, hal flags 0x%x)\n", __func__, - ath_hal_mhz2ieee(sc->sc_curchan.channel, + ath_hal_mhz2ieee(ah, sc->sc_curchan.channel, sc->sc_curchan.channelFlags), sc->sc_curchan.channel, sc->sc_curchan.channelFlags, - ath_hal_mhz2ieee(hchan.channel, hchan.channelFlags), + ath_hal_mhz2ieee(ah, hchan.channel, hchan.channelFlags), hchan.channel, hchan.channelFlags); if (hchan.channel != sc->sc_curchan.channel || hchan.channelFlags != sc->sc_curchan.channelFlags) { @@ -4148,6 +4216,8 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) sc->sc_curchan = hchan; ath_update_txpow(sc); /* update tx power state */ sc->sc_diversity = ath_hal_getdiversity(ah); + sc->sc_calinterval = 1; + sc->sc_caltries = 0; /* * Re-enable rx framework. @@ -4166,6 +4236,25 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) ath_chan_change(sc, chan); /* + * Handle DFS required waiting period to determine + * if channel is clear of radar traffic. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { +#define DFS_AND_NOT_CLEAR(_c) \ + (((_c)->privFlags & (CHANNEL_DFS | CHANNEL_DFS_CLEAR)) == CHANNEL_DFS) + if (DFS_AND_NOT_CLEAR(&sc->sc_curchan)) { + if_printf(sc->sc_ifp, + "wait for DFS clear channel signal\n"); + /* XXX stop sndq */ + sc->sc_ifp->if_drv_flags |= IFF_DRV_OACTIVE; + callout_reset(&sc->sc_dfs_ch, + 2 * hz, ath_dfswait, sc); + } else + callout_stop(&sc->sc_dfs_ch); +#undef DFS_NOT_CLEAR + } + + /* * Re-enable interrupts. */ ath_hal_intrset(ah, sc->sc_imask); @@ -4192,6 +4281,7 @@ ath_calibrate(void *arg) { struct ath_softc *sc = arg; struct ath_hal *ah = sc->sc_ah; + HAL_BOOL iqCalDone; sc->sc_stats.ast_per_cal++; @@ -4205,7 +4295,7 @@ ath_calibrate(void *arg) sc->sc_stats.ast_per_rfgain++; ath_reset(sc->sc_ifp); } - if (!ath_hal_calibrate(ah, &sc->sc_curchan)) { + if (!ath_hal_calibrate(ah, &sc->sc_curchan, &iqCalDone)) { DPRINTF(sc, ATH_DEBUG_ANY, "%s: calibration of channel %u failed\n", __func__, sc->sc_curchan.channel); @@ -4215,7 +4305,30 @@ ath_calibrate(void *arg) * Calibrate noise floor data again in case of change. */ ath_hal_process_noisefloor(ah); - callout_reset(&sc->sc_cal_ch, ath_calinterval * hz, ath_calibrate, sc); + /* + * Poll more frequently when the IQ calibration is in + * progress to speedup loading the final settings. + * We temper this aggressive polling with an exponential + * back off after 4 tries up to ath_calinterval. + */ + if (iqCalDone || sc->sc_calinterval >= ath_calinterval) { + sc->sc_caltries = 0; + sc->sc_calinterval = ath_calinterval; + } else if (sc->sc_caltries > 4) { + sc->sc_caltries = 0; + sc->sc_calinterval <<= 1; + if (sc->sc_calinterval > ath_calinterval) + sc->sc_calinterval = ath_calinterval; + } + KASSERT(0 < sc->sc_calinterval && sc->sc_calinterval <= ath_calinterval, + ("bad calibration interval %u", sc->sc_calinterval)); + + DPRINTF(sc, ATH_DEBUG_CALIBRATE, + "%s: next +%u (%siqCalDone tries %u)\n", __func__, + sc->sc_calinterval, iqCalDone ? "" : "!", sc->sc_caltries); + sc->sc_caltries++; + callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz, + ath_calibrate, sc); } static int @@ -4242,6 +4355,7 @@ ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) callout_stop(&sc->sc_scan_ch); callout_stop(&sc->sc_cal_ch); + callout_stop(&sc->sc_dfs_ch); ath_hal_setledstate(ah, leds[nstate]); /* set LED */ if (nstate == IEEE80211_S_INIT) { @@ -4372,7 +4486,7 @@ done: */ if (nstate == IEEE80211_S_RUN) { /* start periodic recalibration timer */ - callout_reset(&sc->sc_cal_ch, ath_calinterval * hz, + callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz, ath_calibrate, sc); } else if (nstate == IEEE80211_S_SCAN) { /* start ap/neighbor scan timer */ @@ -4439,6 +4553,7 @@ static int ath_getchannels(struct ath_softc *sc, u_int cc, HAL_BOOL outdoor, HAL_BOOL xchanmode) { +#define COMPAT (CHANNEL_ALL_NOTURBO|CHANNEL_PASSIVE) struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = sc->sc_ifp; struct ath_hal *ah = sc->sc_ah; @@ -4452,6 +4567,7 @@ ath_getchannels(struct ath_softc *sc, u_int cc, return ENOMEM; } if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan, + NULL, 0, NULL, cc, HAL_MODE_ALL, outdoor, xchanmode)) { u_int32_t rd; @@ -4468,23 +4584,42 @@ ath_getchannels(struct ath_softc *sc, u_int cc, */ for (i = 0; i < nchan; i++) { HAL_CHANNEL *c = &chans[i]; - ix = ath_hal_mhz2ieee(c->channel, c->channelFlags); + u_int16_t flags; + + ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags); if (ix > IEEE80211_CHAN_MAX) { - if_printf(ifp, "bad hal channel %u (%u/%x) ignored\n", + if_printf(ifp, "bad hal channel %d (%u/%x) ignored\n", ix, c->channel, c->channelFlags); continue; } - /* NB: flags are known to be compatible */ + if (ix < 0) { + /* XXX can't handle stuff <2400 right now */ + if (bootverbose) + if_printf(ifp, "hal channel %d (%u/%x) " + "cannot be handled; ignored\n", + ix, c->channel, c->channelFlags); + continue; + } + /* + * Calculate net80211 flags; most are compatible + * but some need massaging. Note the static turbo + * conversion can be removed once net80211 is updated + * to understand static vs. dynamic turbo. + */ + flags = c->channelFlags & COMPAT; + if (c->channelFlags & CHANNEL_STURBO) + flags |= IEEE80211_CHAN_TURBO; if (ic->ic_channels[ix].ic_freq == 0) { ic->ic_channels[ix].ic_freq = c->channel; - ic->ic_channels[ix].ic_flags = c->channelFlags; + ic->ic_channels[ix].ic_flags = flags; } else { /* channels overlap; e.g. 11g and 11b */ - ic->ic_channels[ix].ic_flags |= c->channelFlags; + ic->ic_channels[ix].ic_flags |= flags; } } free(chans, M_TEMP); return 0; +#undef COMPAT } static void @@ -5030,6 +5165,43 @@ ath_sysctl_tpc(SYSCTL_HANDLER_ARGS) } static int +ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS) +{ + struct ath_softc *sc = arg1; + struct ath_hal *ah = sc->sc_ah; + u_int rfkill = ath_hal_getrfkill(ah); + int error; + + error = sysctl_handle_int(oidp, &rfkill, 0, req); + if (error || !req->newptr) + return error; + if (rfkill == ath_hal_getrfkill(ah)) /* unchanged */ + return 0; + if (!ath_hal_setrfkill(ah, rfkill) || ath_reset(sc->sc_ifp) != 0) + return EINVAL; + else + return 0; +} + +static int +ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS) +{ + struct ath_softc *sc = arg1; + u_int rfsilent; + int error; + + ath_hal_getrfsilent(sc->sc_ah, &rfsilent); + error = sysctl_handle_int(oidp, &rfsilent, 0, req); + if (error || !req->newptr) + return error; + if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) + return EINVAL; + sc->sc_rfsilentpin = rfsilent & 0x1c; + sc->sc_rfsilentpol = (rfsilent & 0x2) != 0; + return 0; +} + +static int ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; @@ -5044,6 +5216,34 @@ ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS) return !ath_hal_setregdomain(sc->sc_ah, rd) ? EINVAL : 0; } +static int +ath_sysctl_tpack(SYSCTL_HANDLER_ARGS) +{ + struct ath_softc *sc = arg1; + u_int32_t tpack; + int error; + + ath_hal_gettpack(sc->sc_ah, &tpack); + error = sysctl_handle_int(oidp, &tpack, 0, req); + if (error || !req->newptr) + return error; + return !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0; +} + +static int +ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS) +{ + struct ath_softc *sc = arg1; + u_int32_t tpcts; + int error; + + ath_hal_gettpcts(sc->sc_ah, &tpcts); + error = sysctl_handle_int(oidp, &tpcts, 0, req); + if (error || !req->newptr) + return error; + return !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0; +} + static void ath_sysctlattach(struct ath_softc *sc) { @@ -5104,10 +5304,25 @@ ath_sysctlattach(struct ath_softc *sc) SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0, ath_sysctl_tpscale, "I", "tx power scaling"); - if (ath_hal_hastpc(ah)) + if (ath_hal_hastpc(ah)) { SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0, ath_sysctl_tpc, "I", "enable/disable per-packet TPC"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tpack", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + ath_sysctl_tpack, "I", "tx power for ack frames"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + ath_sysctl_tpcts, "I", "tx power for cts frames"); + } + if (ath_hal_hasrfsilent(ah)) { + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + ath_sysctl_rfsilent, "I", "h/w RF silent config"); + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "rfkill", CTLTYPE_INT | CTLFLAG_RW, sc, 0, + ath_sysctl_rfkill, "I", "enable/disable RF kill switch"); + } sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "monpass", CTLFLAG_RW, &sc->sc_monpass, 0, |