summaryrefslogtreecommitdiff
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2018-02-05 08:48:58 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2018-02-05 08:48:58 +0000
commitbf71019078ea0497d107bea49eb4e117cc3aa093 (patch)
tree6dbf845497c88bb4f79556712d20868b9eb5aed4 /sys/dev/usb
parent1add52af22cdb43d3505d022d7d10839f7c15617 (diff)
Move athn(4) USB devices to the open source atk9k HTC firmware.
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@
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/if_athn_usb.c428
-rw-r--r--sys/dev/usb/if_athn_usb.h176
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 *);
};