summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ath/if_ath.c194
-rw-r--r--sys/dev/ath/if_athvar.h24
2 files changed, 170 insertions, 48 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 74e95b5..c2afb3c 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -93,6 +93,12 @@ __FBSDID("$FreeBSD$");
((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \
(((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
+enum {
+ ATH_LED_TX,
+ ATH_LED_RX,
+ ATH_LED_POLL,
+};
+
static void ath_init(void *);
static void ath_stop_locked(struct ifnet *);
static void ath_stop(struct ifnet *);
@@ -157,7 +163,7 @@ static void ath_newassoc(struct ieee80211com *,
struct ieee80211_node *, int);
static int ath_getchannels(struct ath_softc *, u_int cc,
HAL_BOOL outdoor, HAL_BOOL xchanmode);
-static void ath_update_led(struct ath_softc *);
+static void ath_led_event(struct ath_softc *, int);
static void ath_update_txpow(struct ath_softc *);
static int ath_rate_setup(struct ath_softc *, u_int mode);
@@ -215,6 +221,7 @@ enum {
ATH_DEBUG_KEYCACHE = 0x00020000, /* key cache management */
ATH_DEBUG_STATE = 0x00040000, /* 802.11 state transitions */
ATH_DEBUG_NODE = 0x00080000, /* node management */
+ ATH_DEBUG_LED = 0x00100000, /* led management */
ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */
ATH_DEBUG_ANY = 0xffffffff
};
@@ -440,7 +447,11 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
goto bad2;
}
+ sc->sc_blinking = 0;
sc->sc_ledstate = 1;
+ sc->sc_ledon = 0; /* low true */
+ sc->sc_ledidle = (2700*hz)/1000; /* 2.7sec */
+ callout_init(&sc->sc_ledtimer, CALLOUT_MPSAFE);
/*
* Auto-enable soft led processing for IBM cards and for
* 5211 minipci cards. Users can also manually enable/disable
@@ -449,7 +460,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID);
if (sc->sc_softled) {
ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
- ath_hal_gpioset(ah, sc->sc_ledpin, 0);
+ ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
}
ifp->if_softc = sc;
@@ -933,8 +944,12 @@ ath_stop_locked(struct ifnet *ifp)
ifp->if_flags &= ~IFF_RUNNING;
ifp->if_timer = 0;
if (!sc->sc_invalid) {
- if (sc->sc_softled)
- ath_hal_gpioset(ah, sc->sc_ledpin, 1);
+ if (sc->sc_softled) {
+ callout_stop(&sc->sc_ledtimer);
+ ath_hal_gpioset(ah, sc->sc_ledpin,
+ !sc->sc_ledon);
+ sc->sc_blinking = 0;
+ }
ath_hal_intrset(ah, 0);
}
ath_draintxq(sc);
@@ -2606,8 +2621,6 @@ rx_accept:
len = ds->ds_rxstat.rs_datalen;
m->m_pkthdr.len = m->m_len = len;
- if (sc->sc_softled)
- ath_update_led(sc);
sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++;
if (sc->sc_drvbpf) {
@@ -2627,8 +2640,8 @@ rx_accept:
goto rx_next;
}
rix = ds->ds_rxstat.rs_rate;
- sc->sc_rx_th.wr_flags = sc->sc_hwflags[rix];
- sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix];
+ sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].flags;
+ sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate;
sc->sc_rx_th.wr_antsignal = ds->ds_rxstat.rs_rssi;
sc->sc_rx_th.wr_antenna = ds->ds_rxstat.rs_antenna;
/* XXX TSF */
@@ -2676,7 +2689,7 @@ rx_accept:
if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
ieee80211_dump_pkt(mtod(m, caddr_t), len,
- sc->sc_hwmap[ds->ds_rxstat.rs_rate],
+ sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate,
ds->ds_rxstat.rs_rssi);
}
@@ -2715,6 +2728,21 @@ rx_accept:
ieee80211_input(ic, m, ni,
ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
+ if (sc->sc_softled) {
+ /*
+ * Blink for any data frame. Otherwise do a
+ * heartbeat-style blink when idle. The latter
+ * is mainly for station mode where we depend on
+ * periodic beacon frames to trigger the poll event.
+ */
+ if (sc->sc_ipackets != ifp->if_ipackets) {
+ sc->sc_ipackets = ifp->if_ipackets;
+ sc->sc_rxrate = ds->ds_rxstat.rs_rate;
+ ath_led_event(sc, ATH_LED_RX);
+ } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
+ ath_led_event(sc, ATH_LED_POLL);
+ }
+
/*
* Reclaim node reference.
*/
@@ -3094,6 +3122,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
*/
ath_rate_findrate(sc, an, shortPreamble, pktlen,
&rix, &try0, &txrate);
+ sc->sc_txrate = txrate; /* for LED blinking */
/*
* Default all non-QoS traffic to the background queue.
*/
@@ -3216,15 +3245,15 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len,
- sc->sc_hwmap[txrate], -1);
+ sc->sc_hwmap[txrate].ieeerate, -1);
if (ic->ic_rawbpf)
bpf_mtap(ic->ic_rawbpf, m0);
if (sc->sc_drvbpf) {
- sc->sc_tx_th.wt_flags = sc->sc_hwflags[txrate];
+ sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].flags;
if (iswep)
sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
- sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate];
+ sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate;
sc->sc_tx_th.wt_txpower = ni->ni_txpower;
sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
@@ -3339,9 +3368,6 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf
txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
ATH_TXQ_UNLOCK(txq);
- if (sc->sc_softled)
- ath_update_led(sc);
-
/*
* The CAB queue is started from the SWBA handler since
* frames only go out on DTIM and to avoid possible races.
@@ -3469,6 +3495,9 @@ ath_tx_proc_q0(void *arg, int npending)
ifp->if_flags &= ~IFF_OACTIVE;
sc->sc_tx_timer = 0;
+ if (sc->sc_softled)
+ ath_led_event(sc, ATH_LED_TX);
+
ath_start(ifp);
}
@@ -3494,6 +3523,9 @@ ath_tx_proc_q0123(void *arg, int npending)
ifp->if_flags &= ~IFF_OACTIVE;
sc->sc_tx_timer = 0;
+ if (sc->sc_softled)
+ ath_led_event(sc, ATH_LED_TX);
+
ath_start(ifp);
}
@@ -3518,6 +3550,9 @@ ath_tx_proc(void *arg, int npending)
ifp->if_flags &= ~IFF_OACTIVE;
sc->sc_tx_timer = 0;
+ if (sc->sc_softled)
+ ath_led_event(sc, ATH_LED_TX);
+
ath_start(ifp);
}
@@ -4001,26 +4036,59 @@ ath_getchannels(struct ath_softc *sc, u_int cc,
}
static void
-ath_update_led(struct ath_softc *sc)
+ath_led_done(void *arg)
{
- struct ieee80211com *ic = &sc->sc_ic;
- struct ath_hal *ah = sc->sc_ah;
- u_int32_t threshold;
+ struct ath_softc *sc = arg;
- /*
- * When not associated, flash LED on for 5s, off for 200ms.
- * XXX this assumes 100ms beacon interval.
- */
- if (ic->ic_state != IEEE80211_S_RUN) {
- threshold = 2 + sc->sc_ledstate * 48;
- } else {
- threshold = 2 + sc->sc_ledstate * 18;
- }
- if (ic->ic_stats.is_rx_beacon - sc->sc_beacons >= threshold) {
- ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
- ath_hal_gpioset(ah, sc->sc_ledpin, sc->sc_ledstate);
- sc->sc_ledstate ^= 1;
- sc->sc_beacons = ic->ic_stats.is_rx_beacon;
+ sc->sc_blinking = 0;
+}
+
+/*
+ * Turn the LED off: flip the pin and then set a timer so no
+ * update will happen for the specified duration.
+ */
+static void
+ath_led_off(void *arg)
+{
+ struct ath_softc *sc = arg;
+
+ ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
+ callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc);
+}
+
+/*
+ * Blink the LED according to the specified on/off times.
+ */
+static void
+ath_led_blink(struct ath_softc *sc, int on, int off)
+{
+ DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
+ ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon);
+ sc->sc_blinking = 1;
+ sc->sc_ledoff = off;
+ callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc);
+}
+
+static void
+ath_led_event(struct ath_softc *sc, int event)
+{
+
+ sc->sc_ledevent = ticks; /* time of last event */
+ if (sc->sc_blinking) /* don't interrupt active blink */
+ return;
+ switch (event) {
+ case ATH_LED_POLL:
+ ath_led_blink(sc, sc->sc_hwmap[0].ledon,
+ sc->sc_hwmap[0].ledoff);
+ break;
+ case ATH_LED_TX:
+ ath_led_blink(sc, sc->sc_hwmap[sc->sc_txrate].ledon,
+ sc->sc_hwmap[sc->sc_txrate].ledoff);
+ break;
+ case ATH_LED_RX:
+ ath_led_blink(sc, sc->sc_hwmap[sc->sc_rxrate].ledon,
+ sc->sc_hwmap[sc->sc_rxrate].ledoff);
+ break;
}
}
@@ -4094,8 +4162,30 @@ ath_rate_setup(struct ath_softc *sc, u_int mode)
static void
ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
{
+#define N(a) (sizeof(a)/sizeof(a[0]))
+ /* NB: on/off times from the Atheros NDIS driver, w/ permission */
+ static const struct {
+ u_int rate; /* tx/rx 802.11 rate */
+ u_int16_t timeOn; /* LED on time (ms) */
+ u_int16_t timeOff; /* LED off time (ms) */
+ } blinkrates[] = {
+ { 108, 40, 10 },
+ { 96, 44, 11 },
+ { 72, 50, 13 },
+ { 48, 57, 14 },
+ { 36, 67, 16 },
+ { 24, 80, 20 },
+ { 22, 100, 25 },
+ { 18, 133, 34 },
+ { 12, 160, 40 },
+ { 10, 200, 50 },
+ { 6, 240, 58 },
+ { 4, 267, 66 },
+ { 2, 400, 100 },
+ { 0, 500, 130 },
+ };
const HAL_RATE_TABLE *rt;
- int i;
+ int i, j;
memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
rt = sc->sc_rates[mode];
@@ -4103,15 +4193,26 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
for (i = 0; i < rt->rateCount; i++)
sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
- memset(sc->sc_hwflags, 0, sizeof(sc->sc_hwflags));
for (i = 0; i < 32; i++) {
u_int8_t ix = rt->rateCodeToIndex[i];
- if (ix == 0xff)
+ if (ix == 0xff) {
+ sc->sc_hwmap[i].ledon = (500 * hz) / 1000;
+ sc->sc_hwmap[i].ledoff = (130 * hz) / 1000;
continue;
- sc->sc_hwmap[i] = rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
+ }
+ sc->sc_hwmap[i].ieeerate =
+ rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
if (rt->info[ix].shortPreamble ||
rt->info[ix].phy == IEEE80211_T_OFDM)
- sc->sc_hwflags[i] |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ sc->sc_hwmap[i].flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ /* setup blink rate table to avoid per-packet lookup */
+ for (j = 0; j < N(blinkrates)-1; j++)
+ if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate)
+ break;
+ /* NB: this uses the last entry if the rate isn't found */
+ /* XXX beware of overlow */
+ sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000;
+ sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000;
}
sc->sc_currates = rt;
sc->sc_curmode = mode;
@@ -4122,6 +4223,7 @@ ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
*/
sc->sc_protrix = (mode == IEEE80211_MODE_11G ? 1 : 0);
/* NB: caller is responsible for reseting rate control state */
+#undef N
}
#ifdef AR_DEBUG
@@ -4368,12 +4470,14 @@ ath_sysctl_softled(SYSCTL_HANDLER_ARGS)
error = sysctl_handle_int(oidp, &softled, 0, req);
if (error || !req->newptr)
return error;
- if (softled > 1)
- softled = 1;
+ softled = (softled != 0);
if (softled != sc->sc_softled) {
- if (softled)
+ if (softled) {
+ /* NB: handle any sc_ledpin change */
ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
- ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !softled);
+ ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin,
+ !sc->sc_ledon);
+ }
sc->sc_softled = softled;
}
return 0;
@@ -4484,6 +4588,12 @@ ath_sysctlattach(struct ath_softc *sc)
"ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0,
"GPIO pin connected to LED");
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "ledon", CTLFLAG_RW, &sc->sc_ledon, 0,
+ "setting to turn LED on");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
+ "idle time for inactivity LED (ticks)");
+ SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"txantenna", CTLFLAG_RW, &sc->sc_txantenna, 0,
"tx antenna (0=auto)");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index 8f4e1e7..9b180d7 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -173,7 +173,9 @@ struct ath_softc {
sc_hasdiversity : 1,/* rx diversity available */
sc_diversity : 1,/* enable rx diversity */
sc_hasveol : 1, /* tx VEOL support */
- sc_hastpc : 1; /* per-packet TPC support */
+ sc_hastpc : 1, /* per-packet TPC support */
+ sc_ledstate: 1, /* LED on/off state */
+ sc_blinking: 1; /* LED blink operation active */
/* rate tables */
const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX];
const HAL_RATE_TABLE *sc_currates; /* current rate table */
@@ -181,17 +183,27 @@ struct ath_softc {
u_int16_t sc_curtxpow; /* current tx power limit */
HAL_CHANNEL sc_curchan; /* current h/w channel */
u_int8_t sc_rixmap[256]; /* IEEE to h/w rate table ix */
- u_int8_t sc_hwmap[32]; /* h/w rate ix to IEEE table */
- u_int8_t sc_hwflags[32]; /* " " " to radiotap flags */
+ struct {
+ u_int8_t ieeerate; /* IEEE rate */
+ u_int8_t flags; /* radiotap flags */
+ u_int16_t ledon; /* softled on time */
+ u_int16_t ledoff; /* softled off time */
+ } sc_hwmap[32]; /* h/w rate ix mappings */
u_int8_t sc_protrix; /* protection rate index */
u_int sc_txantenna; /* tx antenna (fixed or auto) */
HAL_INT sc_imask; /* interrupt mask copy */
u_int sc_keymax; /* size of key cache */
u_int8_t sc_keymap[16]; /* bit map of key cache use */
- u_int32_t sc_beacons; /* beacon count for LED mgmt */
- u_int16_t sc_ledstate; /* LED on/off state */
- u_int16_t sc_ledpin; /* GPIO pin for driving LED */
+ u_int sc_ledpin; /* GPIO pin for driving LED */
+ u_int sc_ledon; /* pin setting for LED on */
+ u_int sc_ledidle; /* idle polling interval */
+ u_int32_t sc_ipackets; /* last data packet count */
+ int sc_ledevent; /* time of last LED event */
+ u_int8_t sc_rxrate; /* current rx rate for LED */
+ u_int8_t sc_txrate; /* current tx rate for LED */
+ u_int16_t sc_ledoff; /* off time for current blink */
+ struct callout sc_ledtimer; /* led off timer */
struct bpf_if *sc_drvbpf;
union {
OpenPOWER on IntegriCloud