Move athn(4) USB devices to the open source atk9k HTC firmware.
authorstsp <stsp@openbsd.org>
Mon, 5 Feb 2018 08:48:57 +0000 (08:48 +0000)
committerstsp <stsp@openbsd.org>
Mon, 5 Feb 2018 08:48:57 +0000 (08:48 +0000)
Also fix support for AR7010 devices, fix mode hostap by properly
managing the firmware station table, and fix Tx rate reporting.

Tested on AR7010 and AR9271 devices.

ok kevlo@

sys/dev/usb/if_athn_usb.c
sys/dev/usb/if_athn_usb.h

index 45a160e..ad5b388 100644 (file)
@@ -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);
index 6f579be..bc1d10b 100644 (file)
@@ -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>
 /* 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 *);
 };