diff options
-rw-r--r-- | sys/dev/usb/if_athn_usb.c | 428 | ||||
-rw-r--r-- | sys/dev/usb/if_athn_usb.h | 176 |
2 files changed, 458 insertions, 146 deletions
diff --git a/sys/dev/usb/if_athn_usb.c b/sys/dev/usb/if_athn_usb.c index 45a160eea26..ad5b3885fa3 100644 --- a/sys/dev/usb/if_athn_usb.c +++ b/sys/dev/usb/if_athn_usb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_athn_usb.c,v 1.48 2017/10/26 15:00:28 mpi Exp $ */ +/* $OpenBSD: if_athn_usb.c,v 1.49 2018/02/05 08:48:57 stsp Exp $ */ /*- * Copyright (c) 2011 Damien Bergamini <damien.bergamini@free.fr> @@ -130,17 +130,27 @@ void athn_usb_newstate_cb(struct athn_usb_softc *, void *); void athn_usb_newassoc(struct ieee80211com *, struct ieee80211_node *, int); void athn_usb_newassoc_cb(struct athn_usb_softc *, void *); -void athn_usb_node_leave(struct ieee80211com *, +struct ieee80211_node *athn_usb_node_alloc(struct ieee80211com *); +void athn_usb_count_active_sta(void *, struct ieee80211_node *); +void athn_usb_newauth_cb(struct athn_usb_softc *, void *); +int athn_usb_newauth(struct ieee80211com *, + struct ieee80211_node *, int, uint16_t); +void athn_usb_node_free(struct ieee80211com *, struct ieee80211_node *); -void athn_usb_node_leave_cb(struct athn_usb_softc *, void *); +void athn_usb_node_free_cb(struct athn_usb_softc *, void *); int athn_usb_ampdu_tx_start(struct ieee80211com *, struct ieee80211_node *, uint8_t); void athn_usb_ampdu_tx_start_cb(struct athn_usb_softc *, void *); void athn_usb_ampdu_tx_stop(struct ieee80211com *, struct ieee80211_node *, uint8_t); void athn_usb_ampdu_tx_stop_cb(struct athn_usb_softc *, void *); +void athn_usb_clean_nodes(void *, struct ieee80211_node *); int athn_usb_create_node(struct athn_usb_softc *, struct ieee80211_node *); +int athn_usb_node_set_rates(struct athn_usb_softc *, + struct ieee80211_node *); +int athn_usb_remove_node(struct athn_usb_softc *, + struct ieee80211_node *); void athn_usb_rx_enable(struct athn_softc *); int athn_set_chan(struct athn_softc *, struct ieee80211_channel *, struct ieee80211_channel *); @@ -159,6 +169,7 @@ void athn_usb_delete_key_cb(struct athn_usb_softc *, void *); void athn_usb_bcneof(struct usbd_xfer *, void *, usbd_status); void athn_usb_swba(struct athn_usb_softc *); +void athn_usb_tx_status(void *, struct ieee80211_node *); void athn_usb_rx_wmi_ctrl(struct athn_usb_softc *, uint8_t *, int); void athn_usb_intr(struct usbd_xfer *, void *, usbd_status); @@ -319,8 +330,13 @@ athn_usb_attachhook(struct device *self) ifp->if_ioctl = athn_usb_ioctl; ifp->if_start = athn_usb_start; ifp->if_watchdog = athn_usb_watchdog; + ic->ic_node_alloc = athn_usb_node_alloc; + ic->ic_newauth = athn_usb_newauth; ic->ic_newassoc = athn_usb_newassoc; - ic->ic_node_leave = athn_usb_node_leave; +#ifndef IEEE80211_STA_ONLY + usc->sc_node_free = ic->ic_node_free; + ic->ic_node_free = athn_usb_node_free; +#endif ic->ic_updateslot = athn_usb_updateslot; ic->ic_updateedca = athn_usb_updateedca; #ifdef notyet @@ -629,12 +645,9 @@ athn_usb_load_firmware(struct athn_usb_softc *usc) /* Determine which firmware image to load. */ if (usc->flags & ATHN_USB_FLAG_AR7010) { dd = usbd_get_device_descriptor(usc->sc_udev); - if (UGETW(dd->bcdDevice) == 0x0202) - name = "athn-ar7010-11"; - else - name = "athn-ar7010"; + name = "athn-open-ar7010"; } else - name = "athn-ar9271"; + name = "athn-open-ar9271"; /* Read firmware image from the filesystem. */ if ((error = loadfirmware(name, &fw, &fwsize)) != 0) { printf("%s: failed loadfirmware of file %s (error %d)\n", @@ -1026,20 +1039,15 @@ athn_usb_newstate_cb(struct athn_usb_softc *usc, void *arg) struct ieee80211com *ic = &sc->sc_ic; enum ieee80211_state ostate; uint32_t reg, imask; - uint8_t sta_index; int s, error; timeout_del(&sc->calib_to); s = splnet(); ostate = ic->ic_state; - DPRINTF(("newstate %d -> %d\n", ostate, cmd->state)); - if (ostate == IEEE80211_S_RUN) { - sta_index = ((struct athn_node *)ic->ic_bss)->sta_index; - (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, - &sta_index, sizeof(sta_index), NULL); - usc->nnodes--; + if (ostate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA) { + athn_usb_remove_node(usc, ic->ic_bss); reg = AR_READ(sc, AR_RX_FILTER); reg = (reg & ~AR_RX_FILTER_MYBEACON) | AR_RX_FILTER_BEACON; @@ -1053,13 +1061,21 @@ athn_usb_newstate_cb(struct athn_usb_softc *usc, void *arg) case IEEE80211_S_SCAN: /* Make the LED blink while scanning. */ athn_set_led(sc, !sc->led_state); - (void)athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL); + error = athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL); + if (error) + printf("%s: could not switch to channel %d\n", + usc->usb_dev.dv_xname, + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); if (!usbd_is_dying(usc->sc_udev)) timeout_add_msec(&sc->scan_to, 200); break; case IEEE80211_S_AUTH: athn_set_led(sc, 0); error = athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL); + if (error) + printf("%s: could not switch to channel %d\n", + usc->usb_dev.dv_xname, + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); break; case IEEE80211_S_ASSOC: break; @@ -1069,13 +1085,18 @@ athn_usb_newstate_cb(struct athn_usb_softc *usc, void *arg) if (ic->ic_opmode == IEEE80211_M_MONITOR) break; - /* Create node entry for our BSS */ - error = athn_usb_create_node(usc, ic->ic_bss); - + if (ic->ic_opmode == IEEE80211_M_STA) { + /* Create node entry for our BSS */ + error = athn_usb_create_node(usc, ic->ic_bss); + if (error) + printf("%s: could not update firmware station " + "table\n", usc->usb_dev.dv_xname); + } athn_set_bss(sc, ic->ic_bss); athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR); #ifndef IEEE80211_STA_ONLY if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL); athn_set_hostap_timers(sc); /* Enable software beacon alert interrupts. */ imask = htobe32(AR_IMR_SWBA); @@ -1108,50 +1129,180 @@ athn_usb_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, #ifndef IEEE80211_STA_ONLY struct athn_usb_softc *usc = ic->ic_softc; - if (ic->ic_opmode != IEEE80211_M_HOSTAP || !isnew) + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_state != IEEE80211_S_RUN) return; - /* Do it in a process context. */ + + /* Update the node's supported rates in a process context. */ ieee80211_ref_node(ni); athn_usb_do_async(usc, athn_usb_newassoc_cb, &ni, sizeof(ni)); +#endif } +#ifndef IEEE80211_STA_ONLY void athn_usb_newassoc_cb(struct athn_usb_softc *usc, void *arg) { - struct ieee80211com *ic = &usc->sc_sc.sc_ic; + struct ieee80211com *ic = &usc->sc_sc.sc_ic; struct ieee80211_node *ni = *(void **)arg; + struct athn_node *an = (struct athn_node *)ni; int s; - s = splnet(); + if (ic->ic_state != IEEE80211_S_RUN) + return; + + s = splnet(); /* NB: Node may have left before we got scheduled. */ - if (ni->ni_associd != 0 && ni->ni_state == IEEE80211_STA_ASSOC) - (void)athn_usb_create_node(usc, ni); + if (an->sta_index != 0) + (void)athn_usb_node_set_rates(usc, ni); ieee80211_release_node(ic, ni); splx(s); +} +#endif + +struct ieee80211_node * +athn_usb_node_alloc(struct ieee80211com *ic) +{ + struct athn_node *an; + + an = malloc(sizeof(struct athn_node), M_DEVBUF, M_NOWAIT | M_ZERO); + return (struct ieee80211_node *)an; +} + + +#ifndef IEEE80211_STA_ONLY +void +athn_usb_count_active_sta(void *arg, struct ieee80211_node *ni) +{ + int *nsta = arg; + struct athn_node *an = (struct athn_node *)ni; + + if (an->sta_index == 0) + return; + + if ((ni->ni_state == IEEE80211_STA_AUTH || + ni->ni_state == IEEE80211_STA_ASSOC) && + ni->ni_inact < IEEE80211_INACT_MAX) + (*nsta)++; +} + +struct athn_usb_newauth_cb_arg { + struct ieee80211_node *ni; + uint16_t seq; +}; + +void +athn_usb_newauth_cb(struct athn_usb_softc *usc, void *arg) +{ + struct ieee80211com *ic = &usc->sc_sc.sc_ic; + struct athn_usb_newauth_cb_arg *a = arg; + struct ieee80211_node *ni = a->ni; + uint16_t seq = a->seq; + struct athn_node *an = (struct athn_node *)ni; + int s, error = 0; + + free(arg, M_DEVBUF, sizeof(*arg)); + + if (ic->ic_state != IEEE80211_S_RUN) + return; + + s = splnet(); + if (an->sta_index == 0) { + error = athn_usb_create_node(usc, ni); + if (error) + printf("%s: could not add station %s to firmware " + "table\n", usc->usb_dev.dv_xname, + ether_sprintf(ni->ni_macaddr)); + } + if (error == 0) + ieee80211_auth_open_confirm(ic, ni, seq); + ieee80211_unref_node(&ni); + splx(s); +} #endif + +int +athn_usb_newauth(struct ieee80211com *ic, struct ieee80211_node *ni, + int isnew, uint16_t seq) +{ +#ifndef IEEE80211_STA_ONLY + struct athn_usb_softc *usc = ic->ic_softc; + struct ifnet *ifp = &ic->ic_if; + struct athn_node *an = (struct athn_node *)ni; + int nsta; + struct athn_usb_newauth_cb_arg *arg; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + return 0; + + if (!isnew && an->sta_index != 0) /* already in firmware table */ + return 0; + + /* Check if we have room in the firmware table. */ + nsta = 1; /* Account for default node. */ + ieee80211_iterate_nodes(ic, athn_usb_count_active_sta, &nsta); + if (nsta >= AR_USB_MAX_STA) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: cannot authenticate station %s: firmware " + "table is full\n", usc->usb_dev.dv_xname, + ether_sprintf(ni->ni_macaddr)); + return ENOSPC; + } + + /* + * In a process context, try to add this node to the + * firmware table and confirm the AUTH request. + */ + arg = malloc(sizeof(*arg), M_DEVBUF, M_NOWAIT); + if (arg == NULL) + return ENOMEM; + arg->ni = ieee80211_ref_node(ni); + arg->seq = seq; + athn_usb_do_async(usc, athn_usb_newauth_cb, arg, sizeof(*arg)); + return EBUSY; +#else + return 0; +#endif /* IEEE80211_STA_ONLY */ } +#ifndef IEEE80211_STA_ONLY void -athn_usb_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +athn_usb_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) { struct athn_usb_softc *usc = ic->ic_softc; - uint8_t sta_index; + struct athn_node *an = (struct athn_node *)ni; - /* Do it in a process context. */ - sta_index = ((struct athn_node *)ni)->sta_index; - athn_usb_do_async(usc, athn_usb_node_leave_cb, - &sta_index, sizeof(sta_index)); + /* + * Remove the node from the firmware table in a process context. + * Pass an index rather than the pointer which we will free. + */ + if (an->sta_index != 0) + athn_usb_do_async(usc, athn_usb_node_free_cb, + &an->sta_index, sizeof(an->sta_index)); + usc->sc_node_free(ic, ni); } void -athn_usb_node_leave_cb(struct athn_usb_softc *usc, void *arg) +athn_usb_node_free_cb(struct athn_usb_softc *usc, void *arg) { + struct ieee80211com *ic = &usc->sc_sc.sc_ic; + struct ifnet *ifp = &ic->ic_if; uint8_t sta_index = *(uint8_t *)arg; + int error; - (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, + error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, &sta_index, sizeof(sta_index), NULL); - usc->nnodes--; + if (error) { + printf("%s: could not remove station %u from firmware table\n", + usc->usb_dev.dv_xname, sta_index); + return; + } + usc->free_node_slots |= (1 << sta_index); + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %u removed from firmware table\n", + usc->usb_dev.dv_xname, sta_index); } +#endif /* IEEE80211_STA_ONLY */ int athn_usb_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, @@ -1210,27 +1361,67 @@ athn_usb_ampdu_tx_stop_cb(struct athn_usb_softc *usc, void *arg) &aggr, sizeof(aggr), NULL); } +#ifndef IEEE80211_STA_ONLY +/* Try to find a node we can evict to make room in the firmware table. */ +void +athn_usb_clean_nodes(void *arg, struct ieee80211_node *ni) +{ + struct athn_usb_softc *usc = arg; + struct ieee80211com *ic = &usc->sc_sc.sc_ic; + struct athn_node *an = (struct athn_node *)ni; + + /* + * Don't remove the default node (used for management frames). + * Nodes which are not in the firmware table also have index zero. + */ + if (an->sta_index == 0) + return; + + /* Remove non-associated nodes. */ + if (ni->ni_state != IEEE80211_STA_AUTH && + ni->ni_state != IEEE80211_STA_ASSOC) { + athn_usb_remove_node(usc, ni); + return; + } + + /* + * Kick off inactive associated nodes. This won't help + * immediately but will help if the new STA retries later. + */ + if (ni->ni_inact >= IEEE80211_INACT_MAX) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + ieee80211_node_leave(ic, ni); + } +} +#endif + int athn_usb_create_node(struct athn_usb_softc *usc, struct ieee80211_node *ni) { struct athn_node *an = (struct athn_node *)ni; struct ar_htc_target_sta sta; - struct ar_htc_target_rate rate; - int error, i, j; + int error, sta_index; +#ifndef IEEE80211_STA_ONLY + struct ieee80211com *ic = &usc->sc_sc.sc_ic; + struct ifnet *ifp = &ic->ic_if; - /* Firmware cannot handle more than 8 STAs. */ - if (usc->nnodes > AR_USB_MAX_STA) + /* Firmware cannot handle more than 8 STAs. Try to make room first. */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + ieee80211_iterate_nodes(ic, athn_usb_clean_nodes, usc); +#endif + if (usc->free_node_slots == 0x00) return ENOBUFS; - an->sta_index = IEEE80211_AID(ni->ni_associd); + sta_index = ffs(usc->free_node_slots) - 1; + if (sta_index < 0 || sta_index >= AR_USB_MAX_STA) + return ENOSPC; /* Create node entry on target. */ memset(&sta, 0, sizeof(sta)); IEEE80211_ADDR_COPY(sta.macaddr, ni->ni_macaddr); IEEE80211_ADDR_COPY(sta.bssid, ni->ni_bssid); - sta.associd = htobe16(ni->ni_associd); - sta.valid = 1; - sta.sta_index = an->sta_index; + sta.sta_index = sta_index; sta.maxampdu = 0xffff; if (ni->ni_flags & IEEE80211_NODE_HT) sta.flags |= htobe16(AR_HTC_STA_HT); @@ -1238,11 +1429,28 @@ athn_usb_create_node(struct athn_usb_softc *usc, struct ieee80211_node *ni) &sta, sizeof(sta), NULL); if (error != 0) return (error); - usc->nnodes++; + an->sta_index = sta_index; + usc->free_node_slots &= ~(1 << an->sta_index); + +#ifndef IEEE80211_STA_ONLY + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %u (%s) added to firmware table\n", + usc->usb_dev.dv_xname, sta_index, + ether_sprintf(ni->ni_macaddr)); +#endif + return athn_usb_node_set_rates(usc, ni); +} + +int +athn_usb_node_set_rates(struct athn_usb_softc *usc, struct ieee80211_node *ni) +{ + struct athn_node *an = (struct athn_node *)ni; + struct ar_htc_target_rate rate; + int i, j; /* Setup supported rates. */ memset(&rate, 0, sizeof(rate)); - rate.sta_index = sta.sta_index; + rate.sta_index = an->sta_index; rate.isnew = 1; rate.lg_rates.rs_nrates = ni->ni_rates.rs_nrates; memcpy(rate.lg_rates.rs_rates, ni->ni_rates.rs_rates, @@ -1270,9 +1478,40 @@ athn_usb_create_node(struct athn_usb_softc *usc, struct ieee80211_node *ni) rate.capflags |= htobe32(AR_RC_SGI_FLAG); #endif } - error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_RC_RATE_UPDATE, + + return athn_usb_wmi_xcmd(usc, AR_WMI_CMD_RC_RATE_UPDATE, &rate, sizeof(rate), NULL); - return (error); +} + +int +athn_usb_remove_node(struct athn_usb_softc *usc, struct ieee80211_node *ni) +{ + struct athn_node *an = (struct athn_node *)ni; + int error; +#ifndef IEEE80211_STA_ONLY + struct ieee80211com *ic = &usc->sc_sc.sc_ic; + struct ifnet *ifp = &ic->ic_if; +#endif + + error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, + &an->sta_index, sizeof(an->sta_index), NULL); + if (error) { + printf("%s: could not remove station %u (%s) from " + "firmware table\n", usc->usb_dev.dv_xname, an->sta_index, + ether_sprintf(ni->ni_macaddr)); + return error; + } + +#ifndef IEEE80211_STA_ONLY + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %u (%s) removed from firmware table\n", + usc->usb_dev.dv_xname, an->sta_index, + ether_sprintf(ni->ni_macaddr)); +#endif + + usc->free_node_slots |= (1 << an->sta_index); + an->sta_index = 0; + return 0; } void @@ -1319,8 +1558,16 @@ athn_usb_switch_chan(struct athn_softc *sc, struct ieee80211_channel *c, error = athn_hw_reset(sc, c, extc, 0); if (error != 0) /* Hopeless case. */ return (error); + + error = athn_set_chan(sc, c, extc); + if (AR_SREV_9271(sc) && error == 0) + ar9271_load_ani(sc); + if (error != 0) + return (error); } + sc->ops.set_txpower(sc, c, extc); + error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV); if (error != 0) return (error); @@ -1518,11 +1765,28 @@ athn_usb_swba(struct athn_usb_softc *usc) } #endif +/* Update current transmit rate for a node based on firmware Tx status. */ +void +athn_usb_tx_status(void *arg, struct ieee80211_node *ni) +{ + struct ar_wmi_evt_txstatus *ts = arg; + struct athn_node *an = (struct athn_node *)ni; + uint8_t rate_index = (ts->rate & AR_HTC_TXSTAT_RATE); + + if (an->sta_index != ts->cookie) /* Tx report for a different node */ + return; + + if (ts->flags & AR_HTC_TXSTAT_MCS) { + if (isset(ni->ni_rxmcs, rate_index)) + ni->ni_txmcs = rate_index; + } else if (rate_index < ni->ni_rates.rs_nrates) + ni->ni_txrate = rate_index; +} + void athn_usb_rx_wmi_ctrl(struct athn_usb_softc *usc, uint8_t *buf, int len) { struct ar_wmi_cmd_hdr *wmi; - struct ar_wmi_evt_txrate *txrate; uint16_t cmd_id; if (__predict_false(len < sizeof(*wmi))) @@ -1547,10 +1811,38 @@ athn_usb_rx_wmi_ctrl(struct athn_usb_softc *usc, uint8_t *buf, int len) athn_usb_swba(usc); break; #endif - case AR_WMI_EVT_TXRATE: - txrate = (struct ar_wmi_evt_txrate *)&wmi[1]; - DPRINTF(("txrate=%d\n", betoh32(txrate->txrate))); + case AR_WMI_EVT_TXSTATUS: { + struct ar_wmi_evt_txstatus_list *tsl; + int i; + + tsl = (struct ar_wmi_evt_txstatus_list *)&wmi[1]; + for (i = 0; i < tsl->count && i < nitems(tsl->ts); i++) { + struct ieee80211com *ic = &usc->sc_sc.sc_ic; + struct athn_node *an = (struct athn_node *)ic->ic_bss; + struct ar_wmi_evt_txstatus *ts = &tsl->ts[i]; + uint8_t qid; + + /* Skip the node we use to send management frames. */ + if (ts->cookie == 0) + continue; + + /* Skip Tx reports for non-data frame endpoints. */ + qid = (ts->rate & AR_HTC_TXSTAT_EPID) >> + AR_HTC_TXSTAT_EPID_SHIFT; + if (qid != usc->ep_data[EDCA_AC_BE] && + qid != usc->ep_data[EDCA_AC_BK] && + qid != usc->ep_data[EDCA_AC_VI] && + qid != usc->ep_data[EDCA_AC_VO]) + continue; + + if (ts->cookie == an->sta_index) + athn_usb_tx_status(ts, ic->ic_bss); + else + ieee80211_iterate_nodes(ic, athn_usb_tx_status, + ts); + } break; + } case AR_WMI_EVT_FATAL: printf("%s: fatal firmware error\n", usc->usb_dev.dv_xname); break; @@ -1944,7 +2236,7 @@ athn_usb_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) struct ar_tx_mgmt *txm; uint8_t *frm; uint16_t qos; - uint8_t sta_index, qid, tid = 0; + uint8_t qid, tid = 0; int hasqos, xferlen, error; wh = mtod(m, struct ieee80211_frame *); @@ -1983,7 +2275,6 @@ athn_usb_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); } #endif - sta_index = an->sta_index; /* NB: We don't take advantage of USB Tx stream mode for now. */ hdr = (struct ar_stream_hdr *)data->buf; @@ -1998,7 +2289,7 @@ athn_usb_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) txf = (struct ar_tx_frame *)&htc[1]; memset(txf, 0, sizeof(*txf)); txf->data_type = AR_HTC_NORMAL; - txf->node_idx = sta_index; + txf->node_idx = an->sta_index; txf->vif_idx = 0; txf->tid = tid; if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) @@ -2010,15 +2301,17 @@ athn_usb_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) txf->flags |= htobe32(AR_HTC_TX_RTSCTS); } txf->key_idx = 0xff; + txf->cookie = an->sta_index; frm = (uint8_t *)&txf[1]; } else { htc->endpoint_id = usc->ep_mgmt; txm = (struct ar_tx_mgmt *)&htc[1]; memset(txm, 0, sizeof(*txm)); - txm->node_idx = sta_index; + txm->node_idx = an->sta_index; txm->vif_idx = 0; txm->key_idx = 0xff; + txm->cookie = an->sta_index; frm = (uint8_t *)&txm[1]; } /* Copy payload. */ @@ -2282,17 +2575,13 @@ athn_usb_init(struct ifnet *ifp) &sta, sizeof(sta), NULL); if (error != 0) goto fail; - usc->nnodes++; + usc->free_node_slots = ~(1 << sta.sta_index); /* Update target capabilities. */ memset(&hic, 0, sizeof(hic)); - hic.flags = htobe32(0x400c2400); - hic.flags_ext = htobe32(0x00106080); hic.ampdu_limit = htobe32(0x0000ffff); hic.ampdu_subframes = 20; - hic.protmode = 1; /* XXX */ - hic.lg_txchainmask = sc->txchainmask; - hic.ht_txchainmask = sc->txchainmask; + hic.txchainmask = sc->txchainmask; DPRINTF(("updating target configuration\n")); error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_TARGET_IC_UPDATE, &hic, sizeof(hic), NULL); @@ -2356,17 +2645,22 @@ athn_usb_stop(struct ifnet *ifp) timeout_del(&sc->scan_to); timeout_del(&sc->calib_to); - /* Remove main interface. */ + /* Remove all non-default nodes. */ + for (sta_index = 1; sta_index < AR_USB_MAX_STA; sta_index++) { + if (usc->free_node_slots & (1 << sta_index)) + continue; + (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, + &sta_index, sizeof(sta_index), NULL); + } + + /* Remove main interface. This also invalidates our default node. */ memset(&hvif, 0, sizeof(hvif)); hvif.index = 0; IEEE80211_ADDR_COPY(hvif.myaddr, ic->ic_myaddr); (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_VAP_REMOVE, &hvif, sizeof(hvif), NULL); - /* Remove default node. */ - sta_index = 0; - (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, - &sta_index, sizeof(sta_index), NULL); - usc->nnodes--; + + usc->free_node_slots = 0xff; (void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR); (void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL); diff --git a/sys/dev/usb/if_athn_usb.h b/sys/dev/usb/if_athn_usb.h index 6f579be6432..bc1d10b2722 100644 --- a/sys/dev/usb/if_athn_usb.h +++ b/sys/dev/usb/if_athn_usb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_athn_usb.h,v 1.8 2017/04/08 02:57:25 deraadt Exp $ */ +/* $OpenBSD: if_athn_usb.h,v 1.9 2018/02/05 08:48:57 stsp Exp $ */ /*- * Copyright (c) 2011 Damien Bergamini <damien.bergamini@free.fr> @@ -34,47 +34,45 @@ /* Wireless module interface commands. */ #define AR_WMI_CMD_ECHO 0x001 #define AR_WMI_CMD_ACCESS_MEMORY 0x002 -#define AR_WMI_CMD_DISABLE_INTR 0x003 -#define AR_WMI_CMD_ENABLE_INTR 0x004 -#define AR_WMI_CMD_RX_LINK 0x005 +#define AR_WMI_GET_FW_VERSION 0x003 +#define AR_WMI_CMD_DISABLE_INTR 0x004 +#define AR_WMI_CMD_ENABLE_INTR 0x005 #define AR_WMI_CMD_ATH_INIT 0x006 #define AR_WMI_CMD_ABORT_TXQ 0x007 #define AR_WMI_CMD_STOP_TX_DMA 0x008 -#define AR_WMI_CMD_STOP_DMA_RECV 0x009 -#define AR_WMI_CMD_ABORT_TX_DMA 0x00a -#define AR_WMI_CMD_DRAIN_TXQ 0x00b -#define AR_WMI_CMD_DRAIN_TXQ_ALL 0x00c -#define AR_WMI_CMD_START_RECV 0x00d -#define AR_WMI_CMD_STOP_RECV 0x00e -#define AR_WMI_CMD_FLUSH_RECV 0x00f -#define AR_WMI_CMD_SET_MODE 0x010 -#define AR_WMI_CMD_RESET 0x011 -#define AR_WMI_CMD_NODE_CREATE 0x012 -#define AR_WMI_CMD_NODE_REMOVE 0x013 -#define AR_WMI_CMD_VAP_REMOVE 0x014 -#define AR_WMI_CMD_VAP_CREATE 0x015 -#define AR_WMI_CMD_BEACON_UPDATE 0x016 -#define AR_WMI_CMD_REG_READ 0x017 -#define AR_WMI_CMD_REG_WRITE 0x018 -#define AR_WMI_CMD_RC_STATE_CHANGE 0x019 -#define AR_WMI_CMD_RC_RATE_UPDATE 0x01a -#define AR_WMI_CMD_DEBUG_INFO 0x01b -#define AR_WMI_CMD_HOST_ATTACH 0x01c -#define AR_WMI_CMD_TARGET_IC_UPDATE 0x01d -#define AR_WMI_CMD_TGT_STATS 0x01e -#define AR_WMI_CMD_TX_AGGR_ENABLE 0x01f +#define AR_WMI_CMD_ABORT_TX_DMA 0x009 +#define AR_WMI_CMD_DRAIN_TXQ 0x00a +#define AR_WMI_CMD_DRAIN_TXQ_ALL 0x00b +#define AR_WMI_CMD_START_RECV 0x00c +#define AR_WMI_CMD_STOP_RECV 0x00d +#define AR_WMI_CMD_FLUSH_RECV 0x00e +#define AR_WMI_CMD_SET_MODE 0x00f +#define AR_WMI_CMD_NODE_CREATE 0x010 +#define AR_WMI_CMD_NODE_REMOVE 0x011 +#define AR_WMI_CMD_VAP_REMOVE 0x012 +#define AR_WMI_CMD_VAP_CREATE 0x013 +#define AR_WMI_CMD_REG_READ 0x014 +#define AR_WMI_CMD_REG_WRITE 0x015 +#define AR_WMI_CMD_RC_STATE_CHANGE 0x016 +#define AR_WMI_CMD_RC_RATE_UPDATE 0x017 +#define AR_WMI_CMD_TARGET_IC_UPDATE 0x018 +#define AR_WMI_CMD_TX_AGGR_ENABLE 0x019 #define AR_WMI_CMD_TGT_DETACH 0x020 -#define AR_WMI_CMD_TGT_TXQ_ENABLE 0x021 -#define AR_WMI_CMD_AGGR_LIMIT 0x026 +#define AR_WMI_CMD_NODE_UPDATE 0x021 +#define AR_WMI_CMD_INT_STATS 0x022 +#define AR_WMI_CMD_TX_STATS 0x023 +#define AR_WMI_CMD_RX_STATS 0x024 +#define AR_WMI_CMD_BITRATE_MASK 0x025 +#define AR_WMI_CMD_REG_RMW 0x026 + /* Wireless module interface events. */ #define AR_WMI_EVT_TGT_RDY 0x001 #define AR_WMI_EVT_SWBA 0x002 #define AR_WMI_EVT_FATAL 0x003 #define AR_WMI_EVT_TXTO 0x004 #define AR_WMI_EVT_BMISS 0x005 -#define AR_WMI_EVT_WLAN_TXCOMP 0x006 -#define AR_WMI_EVT_DELBA 0x007 -#define AR_WMI_EVT_TXRATE 0x008 +#define AR_WMI_EVT_DELBA 0x006 +#define AR_WMI_EVT_TXSTATUS 0x007 /* Structure for service AR_SVC_WMI_CONTROL. */ struct ar_wmi_cmd_hdr { @@ -85,15 +83,8 @@ struct ar_wmi_cmd_hdr { } __packed; /* Values for AR_WMI_CMD_SET_MODE. */ -#define AR_HTC_MODE_AUTO 0 -#define AR_HTC_MODE_11A 1 -#define AR_HTC_MODE_11B 2 -#define AR_HTC_MODE_11G 3 -#define AR_HTC_MODE_FH 4 -#define AR_HTC_MODE_TURBO_A 5 -#define AR_HTC_MODE_TURBO_G 6 -#define AR_HTC_MODE_11NA 7 -#define AR_HTC_MODE_11NG 8 +#define AR_HTC_MODE_11NA 0 +#define AR_HTC_MODE_11NG 1 #define AR_MAX_WRITE_COUNT 32 /* Structure for command AR_WMI_CMD_REG_WRITE. */ @@ -104,14 +95,11 @@ struct ar_wmi_cmd_reg_write { /* Structure for command AR_WMI_CMD_NODE_{CREATE,REMOVE}. */ struct ar_htc_target_sta { - uint16_t associd; - uint16_t txpower; - uint32_t pariwisekey; uint8_t macaddr[IEEE80211_ADDR_LEN]; uint8_t bssid[IEEE80211_ADDR_LEN]; uint8_t sta_index; uint8_t vif_index; - uint8_t vif_sta; + uint8_t is_vif_sta; uint16_t flags; #define AR_HTC_STA_AUTH 0x0001 #define AR_HTC_STA_QOS 0x0002 @@ -119,14 +107,14 @@ struct ar_htc_target_sta { #define AR_HTC_STA_HT 0x0008 uint16_t htcap; - uint8_t valid; - uint16_t capinfo; - uint32_t reserved[2]; - uint16_t txseqmgmt; - uint8_t is_vif_sta; uint16_t maxampdu; + uint8_t pad; + + /* Internal state. */ + uint16_t txseqmgmt; uint16_t iv16; uint32_t iv32; + void *ni_vap; } __packed; /* Structures for command AR_WMI_CMD_RC_RATE_UPDATE. */ @@ -139,12 +127,14 @@ struct ar_htc_rateset { struct ar_htc_target_rate { uint8_t sta_index; uint8_t isnew; + uint8_t pad[2]; uint32_t capflags; -#define AR_RC_DS_FLAG 0x00000001 -#define AR_RC_40_FLAG 0x00000002 -#define AR_RC_SGI_FLAG 0x00000004 -#define AR_RC_HT_FLAG 0x00000008 -#define AR_RC_STBC_FLAG 0x00000020 +#define AR_RC_DS_FLAG 0x00000001 +#define AR_RC_40_FLAG 0x00000002 +#define AR_RC_SGI_FLAG 0x00000004 +#define AR_RC_HT_FLAG 0x00000008 +#define AR_RC_STBC_FLAG 0x00000030 /* 2 bits */ +#define AR_RC_WEP_TKIP_FLAG 0x00000100 struct ar_htc_rateset lg_rates; struct ar_htc_rateset ht_rates; @@ -161,7 +151,6 @@ struct ar_htc_target_aggr { /* Structure for command AR_WMI_CMD_VAP_CREATE. */ struct ar_htc_target_vif { uint8_t index; - uint8_t des_bssid[IEEE80211_ADDR_LEN]; uint32_t opmode; #define AR_HTC_M_IBSS 0 #define AR_HTC_M_STA 1 @@ -169,41 +158,60 @@ struct ar_htc_target_vif { #define AR_HTC_M_AHDEMO 3 #define AR_HTC_M_HOSTAP 6 #define AR_HTC_M_MONITOR 8 - uint8_t myaddr[IEEE80211_ADDR_LEN]; - uint8_t bssid[IEEE80211_ADDR_LEN]; - uint32_t flags; - uint32_t flags_ext; - uint16_t ps_sta; - uint16_t rtsthreshold; uint8_t ath_cap; - int8_t mcast_rate; + uint16_t rtsthreshold; + uint8_t pad; + + /* Internal state. */ + int8_t nodeindex; + void *iv_bss; } __packed; /* Structure for command AM_WMI_CMD_TARGET_IC_UPDATE. */ struct ar_htc_cap_target { - uint32_t flags; - uint32_t flags_ext; uint32_t ampdu_limit; uint8_t ampdu_subframes; - uint8_t ht_txchainmask; - uint8_t lg_txchainmask; - uint8_t rtscts_ratecode; - uint8_t protmode; + uint8_t enable_coex; + uint8_t txchainmask; + uint8_t pad; } __packed; -/* Structure for event AR_WMI_EVT_TXRATE. */ -struct ar_wmi_evt_txrate { - uint32_t txrate; - uint8_t rssi_thresh; - uint8_t per; +struct ar_wmi_evt_txstatus { + uint8_t cookie; + + /* + * Legacy rates are indicated as rate array indices. + * HT rates are indicated as MCS indices. + */ + uint8_t rate; +#define AR_HTC_TXSTAT_RATE 0x0f +#define AR_HTC_TXSTAT_EPID 0xf0 +#define AR_HTC_TXSTAT_EPID_SHIFT 4 + + uint8_t flags; +#define AR_HTC_TXSTAT_ACK 0x01 +#define AR_HTC_TXSTAT_FILT 0x02 +#define AR_HTC_TXSTAT_RTC_CTS 0x04 +#define AR_HTC_TXSTAT_MCS 0x08 +#define AR_HTC_TXSTAT_CW40 0x10 +#define AR_HTC_TXSTAT_SGI 0x20 +} __packed; + +/* Structure for event AR_WMI_EVT_TXSTATUS. */ +#define AR_HTC_MAX_TX_STATUS 12 +struct ar_wmi_evt_txstatus_list { + uint8_t count; + struct ar_wmi_evt_txstatus ts[AR_HTC_MAX_TX_STATUS]; } __packed; /* HTC header. */ struct ar_htc_frame_hdr { uint8_t endpoint_id; uint8_t flags; -#define AR_HTC_FLAG_TRAILER 0x02 +#define AR_HTC_FLAG_NEED_CREDIT_UPDATE 0x01 +#define AR_HTC_FLAG_TRAILER 0x02 +#define AR_HTC_FLAG_CREDIT_REDISTRIBUTION 0x03 uint16_t payload_len; uint8_t control[4]; @@ -236,7 +244,8 @@ struct ar_tx_frame { uint8_t key_type; uint8_t key_idx; - uint8_t reserved[26]; + uint8_t cookie; + uint8_t pad; } __packed; /* Structure for service AR_SVC_WMI_MGMT. */ @@ -247,7 +256,8 @@ struct ar_tx_mgmt { uint8_t flags; uint8_t key_type; uint8_t key_idx; - uint16_t reserved; + uint8_t cookie; + uint8_t pad; } __packed; /* Structure for service AR_SVC_WMI_BEACON. */ @@ -459,6 +469,14 @@ struct athn_usb_softc { uint8_t ep_mgmt; uint8_t ep_data[EDCA_NUM_AC]; - /* Firmware cannot handle more than 8 STAs. */ - uint8_t nnodes; + /* + * Firmware cannot handle more than 8 STAs. + * We use a bitmask to keep track of available slots in the firmware's + * node array. A 1 bit at index N, as determined by ffs(3), means the + * slot at this index is available. + */ + uint8_t free_node_slots; + + void (*sc_node_free)(struct ieee80211com *, + struct ieee80211_node *); }; |