summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/wlan
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2010-05-13 00:19:03 +0000
committerthompsa <thompsa@FreeBSD.org>2010-05-13 00:19:03 +0000
commitc260948bc7bd3fe9d1a27d8be8bddc7e5261f980 (patch)
treecc3a880dc85be5ec7798aebfdd3945963f8efac6 /sys/dev/usb/wlan
parentd57aea636907a33a4d849915b2c88264122fbdcf (diff)
downloadFreeBSD-src-c260948bc7bd3fe9d1a27d8be8bddc7e5261f980.zip
FreeBSD-src-c260948bc7bd3fe9d1a27d8be8bddc7e5261f980.tar.gz
Sync run(4) driver from author's site.
Submitted by: Akinori Furukoshi Obtained from: git://gitorious.org/run/run.git
Diffstat (limited to 'sys/dev/usb/wlan')
-rw-r--r--sys/dev/usb/wlan/if_run.c1151
-rw-r--r--sys/dev/usb/wlan/if_runreg.h22
-rw-r--r--sys/dev/usb/wlan/if_runvar.h61
3 files changed, 789 insertions, 445 deletions
diff --git a/sys/dev/usb/wlan/if_run.c b/sys/dev/usb/wlan/if_run.c
index c798c32..c570664 100644
--- a/sys/dev/usb/wlan/if_run.c
+++ b/sys/dev/usb/wlan/if_run.c
@@ -1,5 +1,3 @@
-/* $FreeBSD$ */
-
/*-
* Copyright (c) 2008,2010 Damien Bergamini <damien.bergamini@free.fr>
* ported to FreeBSD by Akinori Furukoshi <moonlightakkiy@yahoo.ca>
@@ -39,7 +37,6 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
-#include <sys/systm.h>
#include <sys/linker.h>
#include <sys/firmware.h>
#include <sys/kdb.h>
@@ -74,7 +71,7 @@ __FBSDID("$FreeBSD$");
#define USB_DEBUG_VAR run_debug
#include <dev/usb/usb_debug.h>
-#include "if_runreg.h" /* shared with ral(4) */
+#include "if_runreg.h"
#include "if_runvar.h"
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
@@ -93,6 +90,12 @@ SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RW, &run_debug, 0,
#define IEEE80211_HAS_ADDR4(wh) \
(((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+/*
+ * Because of LOR in run_key_delete(), use atomic instead.
+ * '& RUN_CMDQ_MASQ' is to loop cmdq[].
+ */
+#define RUN_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & RUN_CMDQ_MASQ)
+
static const struct usb_device_id run_devs[] = {
{ USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2770) },
{ USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2870) },
@@ -312,6 +315,7 @@ static struct ieee80211vap *run_vap_create(struct ieee80211com *,
const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t
mac[IEEE80211_ADDR_LEN]);
static void run_vap_delete(struct ieee80211vap *);
+static void run_cmdq_cb(void *, int);
static void run_setup_tx_list(struct run_softc *,
struct run_endpoint_queue *);
static void run_unsetup_tx_list(struct run_softc *,
@@ -342,23 +346,24 @@ static struct ieee80211_node *run_node_alloc(struct ieee80211vap *,
static int run_media_change(struct ifnet *);
static int run_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static int run_wme_update(struct ieee80211com *);
-static void run_wme_update_cb(void *, int);
+static void run_wme_update_cb(void *);
static void run_key_update_begin(struct ieee80211vap *);
static void run_key_update_end(struct ieee80211vap *);
-static int run_key_set(struct ieee80211vap *, const struct ieee80211_key *,
+static void run_key_set_cb(void *);
+static int run_key_set(struct ieee80211vap *, struct ieee80211_key *,
const uint8_t mac[IEEE80211_ADDR_LEN]);
-static int run_key_delete(struct ieee80211vap *,
- const struct ieee80211_key *);
-static void run_ratectl_start(struct run_softc *, struct ieee80211_node *);
+static void run_key_delete_cb(void *);
+static int run_key_delete(struct ieee80211vap *, struct ieee80211_key *);
static void run_ratectl_to(void *);
static void run_ratectl_cb(void *, int);
+static void run_drain_fifo(void *);
static void run_iter_func(void *, struct ieee80211_node *);
+static void run_newassoc_cb(void *);
static void run_newassoc(struct ieee80211_node *, int);
static void run_rx_frame(struct run_softc *, struct mbuf *, uint32_t);
static void run_tx_free(struct run_endpoint_queue *pq,
struct run_tx_data *, int);
-static void run_set_tx_desc(struct run_softc *, struct run_tx_data *,
- uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
+static void run_set_tx_desc(struct run_softc *, struct run_tx_data *);
static int run_tx(struct run_softc *, struct mbuf *,
struct ieee80211_node *);
static int run_tx_mgt(struct run_softc *, struct mbuf *,
@@ -382,11 +387,10 @@ static int run_set_chan(struct run_softc *, struct ieee80211_channel *);
static void run_set_channel(struct ieee80211com *);
static void run_scan_start(struct ieee80211com *);
static void run_scan_end(struct ieee80211com *);
-static uint8_t run_rate2mcs(uint8_t);
static void run_update_beacon(struct ieee80211vap *, int);
-static void run_update_beacon_locked(struct ieee80211vap *, int);
+static void run_update_beacon_cb(void *);
static void run_updateprot(struct ieee80211com *);
-static void run_usb_timeout_cb(void *, int);
+static void run_usb_timeout_cb(void *);
static void run_reset_livelock(struct run_softc *);
static void run_enable_tsf_sync(struct run_softc *);
static void run_enable_mrr(struct run_softc *);
@@ -396,6 +400,7 @@ static void run_set_leds(struct run_softc *, uint16_t);
static void run_set_bssid(struct run_softc *, const uint8_t *);
static void run_set_macaddr(struct run_softc *, const uint8_t *);
static void run_updateslot(struct ifnet *);
+static void run_update_mcast(struct ifnet *);
static int8_t run_rssi2dbm(struct run_softc *, uint8_t, uint8_t);
static void run_update_promisc_locked(struct ifnet *);
static void run_update_promisc(struct ifnet *);
@@ -411,7 +416,7 @@ static void run_stop(void *);
static void run_delay(struct run_softc *, unsigned int);
static const struct {
- uint32_t reg;
+ uint16_t reg;
uint32_t val;
} rt2870_def_mac[] = {
RT2870_DEF_MAC
@@ -551,6 +556,7 @@ run_attach(device_t self)
MTX_NETWORK_LOCK, MTX_DEF);
iface_index = RT2860_IFACE_INDEX;
+
error = usbd_transfer_setup(uaa->device, &iface_index,
sc->sc_xfer, run_config, RUN_N_XFER, sc, &sc->sc_mtx);
if (error) {
@@ -616,15 +622,15 @@ run_attach(device_t self)
ic->ic_ifp = ifp;
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
-#if 0
- ic->ic_state = IEEE80211_S_INIT;
-#endif
+
/* set device capabilities */
ic->ic_caps =
IEEE80211_C_STA | /* station mode supported */
IEEE80211_C_MONITOR | /* monitor mode supported */
IEEE80211_C_IBSS |
IEEE80211_C_HOSTAP |
+ IEEE80211_C_WDS | /* 4-address traffic works */
+ IEEE80211_C_MBSS |
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
IEEE80211_C_SHSLOT | /* short slot time supported */
IEEE80211_C_WME | /* WME */
@@ -671,6 +677,7 @@ run_attach(device_t self)
ic->ic_node_alloc = run_node_alloc;
ic->ic_newassoc = run_newassoc;
//ic->ic_updateslot = run_updateslot;
+ ic->ic_update_mcast = run_update_mcast;
ic->ic_wme.wme_update = run_wme_update;
ic->ic_raw_xmit = run_raw_xmit;
ic->ic_update_promisc = run_update_promisc;
@@ -684,6 +691,10 @@ run_attach(device_t self)
&sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
RUN_RX_RADIOTAP_PRESENT);
+ TASK_INIT(&sc->cmdq_task, 0, run_cmdq_cb, sc);
+ TASK_INIT(&sc->ratectl_task, 0, run_ratectl_cb, sc);
+ callout_init((struct callout *)&sc->ratectl_ch, 1);
+
if (bootverbose)
ieee80211_announce(ic);
@@ -713,6 +724,10 @@ run_detach(device_t self)
if (ifp) {
ic = ifp->if_l2com;
+ /* drain tasks */
+ usb_callout_drain(&sc->ratectl_ch);
+ ieee80211_draintask(ic, &sc->cmdq_task);
+ ieee80211_draintask(ic, &sc->ratectl_task);
ieee80211_ifdetach(ic);
if_free(ifp);
}
@@ -728,41 +743,92 @@ run_vap_create(struct ieee80211com *ic,
const uint8_t bssid[IEEE80211_ADDR_LEN],
const uint8_t mac[IEEE80211_ADDR_LEN])
{
- struct run_softc *sc = ic->ic_ifp->if_softc;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct run_softc *sc = ifp->if_softc;
struct run_vap *rvp;
struct ieee80211vap *vap;
+ int i;
- if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ if(sc->rvp_cnt >= RUN_VAP_MAX){
+ if_printf(ifp, "number of VAPs maxed out\n");
return NULL;
- sc->sc_rvp = rvp = (struct run_vap *) malloc(sizeof(struct run_vap),
+ }
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ /* enable s/w bmiss handling for sta mode */
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ /* fall though */
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ /* other than WDS vaps, only one at a time */
+ if (!TAILQ_EMPTY(&ic->ic_vaps))
+ return NULL;
+ break;
+ case IEEE80211_M_WDS:
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next){
+ if(vap->iv_opmode != IEEE80211_M_HOSTAP)
+ continue;
+ /* WDS vap's always share the local mac address. */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ break;
+ }
+ if(vap == NULL){
+ if_printf(ifp, "wds only supported in ap mode\n");
+ return NULL;
+ }
+ break;
+ default:
+ if_printf(ifp, "unknown opmode %d\n", opmode);
+ return NULL;
+ }
+
+ rvp = (struct run_vap *) malloc(sizeof(struct run_vap),
M_80211_VAP, M_NOWAIT | M_ZERO);
if (rvp == NULL)
return NULL;
vap = &rvp->vap;
- /* enable s/w bmiss handling for sta mode */
- ieee80211_vap_setup(ic, vap, name, unit, opmode,
- flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac);
vap->iv_key_update_begin = run_key_update_begin;
vap->iv_key_update_end = run_key_update_end;
- vap->iv_key_delete = run_key_delete;
- vap->iv_key_set = run_key_set;
vap->iv_update_beacon = run_update_beacon;
+ vap->iv_max_aid = RT2870_WCID_MAX;
+ /*
+ * To delete the right key from h/w, we need wcid.
+ * Luckily, there is unused space in ieee80211_key{}, wk_pad,
+ * and matching wcid will be written into there. So, cast
+ * some spells to remove 'const' from ieee80211_key{}
+ */
+ vap->iv_key_delete = (void *)run_key_delete;
+ vap->iv_key_set = (void *)run_key_set;
/* override state transition machine */
rvp->newstate = vap->iv_newstate;
vap->iv_newstate = run_newstate;
- TASK_INIT(&rvp->ratectl_task, 0, run_ratectl_cb, rvp);
- TASK_INIT(&sc->wme_task, 0, run_wme_update_cb, ic);
- TASK_INIT(&sc->usb_timeout_task, 0, run_usb_timeout_cb, sc);
- callout_init((struct callout *)&rvp->ratectl_ch, 1);
ieee80211_ratectl_init(vap);
ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
/* complete setup */
ieee80211_vap_attach(vap, run_media_change, ieee80211_media_status);
- ic->ic_opmode = opmode;
+
+ /* make sure id is always unique */
+ for(i = 0; i < RUN_VAP_MAX; i++){
+ if((sc->rvp_bmap & 1 << i) == 0){
+ sc->rvp_bmap |= 1 << i;
+ rvp->rvp_id = i;
+ break;
+ }
+ }
+ if(sc->rvp_cnt++ == 0)
+ ic->ic_opmode = opmode;
+
+ DPRINTF("rvp_id=%d bmap=%x rvp_cnt=%d\n",
+ rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt);
+
return vap;
}
@@ -773,6 +839,7 @@ run_vap_delete(struct ieee80211vap *vap)
struct ifnet *ifp;
struct ieee80211com *ic;
struct run_softc *sc;
+ uint8_t rvp_id;
if(vap == NULL)
return;
@@ -783,19 +850,59 @@ run_vap_delete(struct ieee80211vap *vap)
sc = ifp->if_softc;
RUN_LOCK(sc);
- sc->sc_rvp->ratectl_run = RUN_RATECTL_OFF;
- RUN_UNLOCK(sc);
- /* drain them all */
- usb_callout_drain(&sc->sc_rvp->ratectl_ch);
- ieee80211_draintask(ic, &sc->sc_rvp->ratectl_task);
- ieee80211_draintask(ic, &sc->wme_task);
- ieee80211_draintask(ic, &sc->usb_timeout_task);
+ rvp_id = rvp->rvp_id;
+ sc->ratectl_run &= ~(1 << rvp_id);
+ sc->rvp_bmap &= ~(1 << rvp_id);
+ run_set_region_4(sc, RT2860_SKEY(rvp_id, 0), 0, 128);
+ run_set_region_4(sc, RT2860_BCN_BASE(rvp_id), 0, 512);
+ --sc->rvp_cnt;
+
+ DPRINTF("vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n",
+ vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt);
+
+ RUN_UNLOCK(sc);
ieee80211_ratectl_deinit(vap);
ieee80211_vap_detach(vap);
free(rvp, M_80211_VAP);
- sc->sc_rvp = NULL;
+}
+
+/*
+ * There are numbers of functions need to be called in context thread.
+ * Rather than creating taskqueue event for each of those functions,
+ * here is all-for-one taskqueue callback function. This function
+ * gurantees deferred functions are executed in the same order they
+ * were enqueued.
+ * '& RUN_CMDQ_MASQ' is to loop cmdq[].
+ */
+static void
+run_cmdq_cb(void *arg, int pending)
+{
+ struct run_softc *sc = arg;
+ uint8_t i;
+
+ /* call cmdq[].func locked */
+ RUN_LOCK(sc);
+ for(i = sc->cmdq_exec; sc->cmdq[i].func && pending;
+ i = sc->cmdq_exec, pending--){
+ DPRINTFN(6, "cmdq_exec=%d pending=%d\n", i, pending);
+ if(sc->cmdq_run == RUN_CMDQ_GO){
+ /*
+ * If arg0 is NULL, callback func needs more
+ * than one arg. So, pass ptr to cmdq struct.
+ */
+ if(sc->cmdq[i].arg0)
+ sc->cmdq[i].func(sc->cmdq[i].arg0);
+ else
+ sc->cmdq[i].func(&sc->cmdq[i]);
+ }
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].func = NULL;
+ sc->cmdq_exec++;
+ sc->cmdq_exec &= RUN_CMDQ_MASQ;
+ }
+ RUN_UNLOCK(sc);
}
static void
@@ -1415,6 +1522,7 @@ run_read_eeprom(struct run_softc *sc)
DPRINTF("EEPROM RF rev=0x%02x chains=%dT%dR\n",
sc->rf_rev, sc->ntxchains, sc->nrxchains);
+ /* check if RF supports automatic Tx access gain control */
run_srom_read(sc, RT2860_EEPROM_CONFIG, &val);
DPRINTF("EEPROM CFG 0x%04x\n", val);
/* check if driver should patch the DAC issue */
@@ -1489,10 +1597,10 @@ run_read_eeprom(struct run_softc *sc)
for (ridx = 0; ridx < 5; ridx++) {
uint32_t reg;
- run_srom_read(sc, RT2860_EEPROM_RPWR + ridx, &val);
- reg = (uint32_t)val << 16;
- run_srom_read(sc, RT2860_EEPROM_RPWR + ridx + 1, &val);
- reg |= val;
+ run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2, &val);
+ reg = val;
+ run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2 + 1, &val);
+ reg |= (uint32_t)val << 16;
sc->txpow20mhz[ridx] = reg;
sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
@@ -1575,19 +1683,21 @@ run_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
static int
run_media_change(struct ifnet *ifp)
{
+ struct ieee80211vap *vap = ifp->if_softc;
+ struct ieee80211com *ic = vap->iv_ic;
const struct ieee80211_txparam *tp;
- struct run_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = sc->sc_ifp->if_l2com;
- struct ieee80211vap *vap = &sc->sc_rvp->vap;
+ struct run_softc *sc = ic->ic_ifp->if_softc;
+ struct run_node *rn = (void *)vap->iv_bss;
uint8_t rate, ridx;
int error;
RUN_LOCK(sc);
error = ieee80211_media_change(ifp);
- if (error != ENETRESET)
+ if (error != ENETRESET){
RUN_UNLOCK(sc);
return error;
+ }
tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
@@ -1596,13 +1706,16 @@ run_media_change(struct ifnet *ifp)
for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
if (rt2860_rates[ridx].rate == rate)
break;
- sc->fixed_ridx = ridx;
+ rn->fix_ridx = ridx;
+ DPRINTF("rate=%d, fix_ridx=%d\n", rate, rn->fix_ridx);
}
+#if 0
if ((ifp->if_flags & IFF_UP) &&
(ifp->if_drv_flags & IFF_DRV_RUNNING)){
run_init_locked(sc);
}
+#endif
RUN_UNLOCK(sc);
@@ -1618,8 +1731,11 @@ run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
struct run_vap *rvp = RUN_VAP(vap);
enum ieee80211_state ostate;
struct ieee80211_node *ni;
+ uint32_t sta[3];
uint32_t tmp;
- uint8_t wcid;
+ uint8_t ratectl;
+ uint8_t restart_ratectl = 0;
+ uint8_t bid = 1 << rvp->rvp_id;
ostate = vap->iv_state;
DPRINTF("%s -> %s\n",
@@ -1629,8 +1745,9 @@ run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
IEEE80211_UNLOCK(ic);
RUN_LOCK(sc);
- sc->sc_rvp->ratectl_run = RUN_RATECTL_OFF;
- usb_callout_stop(&rvp->ratectl_ch);
+ ratectl = sc->ratectl_run; /* remember current state */
+ sc->ratectl_run = RUN_RATECTL_OFF;
+ usb_callout_stop(&sc->ratectl_ch);
if (ostate == IEEE80211_S_RUN) {
/* turn link LED off */
@@ -1639,8 +1756,16 @@ run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
switch (nstate) {
case IEEE80211_S_INIT:
- if (ostate == IEEE80211_S_RUN) {
- /* abort TSF synchronization */
+ restart_ratectl = 1;
+
+ if (ostate != IEEE80211_S_RUN)
+ break;
+
+ ratectl &= ~bid;
+ sc->runbmap &= ~bid;
+
+ /* abort TSF synchronization if there is no vap running */
+ if(--sc->running == 0){
run_read(sc, RT2860_BCN_TIME_CFG, &tmp);
run_write(sc, RT2860_BCN_TIME_CFG,
tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN |
@@ -1648,8 +1773,42 @@ run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
}
break;
+
case IEEE80211_S_RUN:
ni = vap->iv_bss;
+ if(!(sc->runbmap & bid)){
+ if(sc->running++)
+ restart_ratectl = 1;
+ sc->runbmap |= bid;
+ }
+
+ switch(vap->iv_opmode){
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ sc->ap_running |= bid;
+ ic->ic_opmode = vap->iv_opmode;
+ run_update_beacon_cb(vap);
+ break;
+ case IEEE80211_M_IBSS:
+ sc->adhoc_running |= bid;
+ if(!sc->ap_running)
+ ic->ic_opmode = vap->iv_opmode;
+ run_update_beacon_cb(vap);
+ break;
+ case IEEE80211_M_STA:
+ sc->sta_running |= bid;
+ if(!sc->ap_running && !sc->adhoc_running)
+ ic->ic_opmode = vap->iv_opmode;
+
+ /* read statistic counters (clear on read) */
+ run_read_region_1(sc, RT2860_TX_STA_CNT0,
+ (uint8_t *)sta, sizeof sta);
+
+ break;
+ default:
+ ic->ic_opmode = vap->iv_opmode;
+ break;
+ }
if (vap->iv_opmode != IEEE80211_M_MONITOR) {
run_updateslot(ic->ic_ifp);
@@ -1658,31 +1817,17 @@ run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
run_set_basicrates(sc);
IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
run_set_bssid(sc, ni->ni_bssid);
- }
-
- if (vap->iv_opmode == IEEE80211_M_STA) {
- /* add BSS entry to the WCID table */
- wcid = RUN_AID2WCID(ni->ni_associd);
- run_write_region_1(sc, RT2860_WCID_ENTRY(wcid),
- ni->ni_macaddr, IEEE80211_ADDR_LEN);
- }
-
- if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
- vap->iv_opmode == IEEE80211_M_IBSS)
- run_update_beacon_locked(vap, 0);
-
- if (vap->iv_opmode != IEEE80211_M_MONITOR) {
run_enable_tsf_sync(sc);
- } /* else tsf */
- /* enable automatic rate adaptation */
- tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
- if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
- run_ratectl_start(sc, ni);
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ ratectl |= bid;
+ }
/* turn link LED on */
run_set_leds(sc, RT2860_LED_RADIO |
- (IEEE80211_IS_CHAN_2GHZ(vap->iv_bss->ni_chan) ?
+ (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ?
RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ));
break;
@@ -1691,34 +1836,26 @@ run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
break;
}
+ /* restart amrr for running VAPs */
+ if((sc->ratectl_run = ratectl) && restart_ratectl)
+ usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
+
RUN_UNLOCK(sc);
IEEE80211_LOCK(ic);
return(rvp->newstate(vap, nstate, arg));
}
-/* another taskqueue, so usbd_do_request() can go sleep */
-static int
-run_wme_update(struct ieee80211com *ic)
-{
- struct run_softc *sc = ic->ic_ifp->if_softc;
-
- ieee80211_runtask(ic, &sc->wme_task);
-
- /* return whatever, upper layer desn't care anyway */
- return 0;
-}
-
/* ARGSUSED */
static void
-run_wme_update_cb(void *arg, int pending)
+run_wme_update_cb(void *arg)
{
struct ieee80211com *ic = arg;
struct run_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211_wme_state *wmesp = &ic->ic_wme;
int aci, error = 0;
- RUN_LOCK(sc);
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
/* update MAC TX configuration registers */
for (aci = 0; aci < WME_NUM_AC; aci++) {
@@ -1761,19 +1898,39 @@ err:
if(error)
DPRINTF("WME update failed\n");
- RUN_UNLOCK(sc);
return;
}
+static int
+run_wme_update(struct ieee80211com *ic)
+{
+ struct run_softc *sc = ic->ic_ifp->if_softc;
+
+ /* sometime called wothout lock */
+ if(mtx_owned(&ic->ic_comlock.mtx)){
+ uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store);
+ DPRINTF("cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_wme_update_cb;
+ sc->cmdq[i].arg0 = ic;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ return (0);
+ }
+
+ RUN_LOCK(sc);
+ run_wme_update_cb(ic);
+ RUN_UNLOCK(sc);
+
+ /* return whatever, upper layer desn't care anyway */
+ return (0);
+}
+
static void
run_key_update_begin(struct ieee80211vap *vap)
{
/*
- * Because run_key_delete() needs special attention
- * on lock related operation, lock handling is being done
- * differently in run_key_set and _delete.
- *
- * So, we don't use key_update_begin and _end.
+ * To avoid out-of-order events, both run_key_set() and
+ * _delete() are deferred and handled by run_cmdq_cb().
+ * So, there is nothing we need to do here.
*/
}
@@ -1783,37 +1940,31 @@ run_key_update_end(struct ieee80211vap *vap)
/* null */
}
-/*
- * return 0 on error
- */
-static int
-run_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
- const uint8_t mac[IEEE80211_ADDR_LEN])
+static void
+run_key_set_cb(void *arg)
{
+ struct run_cmdq *cmdq = arg;
+ struct ieee80211vap *vap = cmdq->arg1;
+ struct ieee80211_key *k = cmdq->k;
struct ieee80211com *ic = vap->iv_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct run_softc *sc = ifp->if_softc;
+ struct run_softc *sc = ic->ic_ifp->if_softc;
struct ieee80211_node *ni;
uint32_t attr;
uint16_t base, associd;
uint8_t mode, wcid, txmic, rxmic, iv[8];
- int error = 0;
- RUN_LOCK(sc);
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
if(vap->iv_opmode == IEEE80211_M_HOSTAP){
- ni = ieee80211_find_vap_node(&ic->ic_sta, vap, mac);
- associd = (ni != NULL) ? ni->ni_associd : 0;
- if(ni != NULL)
- ieee80211_free_node(ni);
+ ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac);
txmic = 24;
rxmic = 16;
} else {
ni = vap->iv_bss;
- associd = (ni != NULL) ? ni->ni_associd : 0;
txmic = 16;
rxmic = 24;
}
+ associd = (ni != NULL) ? ni->ni_associd : 0;
/* map net80211 cipher to RT2860 security mode */
switch (k->wk_cipher->ic_cipher) {
@@ -1831,16 +1982,18 @@ run_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
break;
default:
DPRINTF("undefined case\n");
- goto fail;
+ return;
}
- DPRINTFN(1, "associd=%x, keyix=%d, mode=%x, type=%s\n",
+ DPRINTFN(1, "associd=%x, keyix=%d, mode=%x, type=%s, tx=%s, rx=%s\n",
associd, k->wk_keyix, mode,
- (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise");
+ (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise",
+ (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off",
+ (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off");
if (k->wk_flags & IEEE80211_KEY_GROUP) {
wcid = 0; /* NB: update WCID0 for group keys */
- base = RT2860_SKEY(0, k->wk_keyix);
+ base = RT2860_SKEY(RUN_VAP(vap)->rvp_id, k->wk_keyix);
} else {
wcid = RUN_AID2WCID(associd);
base = RT2860_PKEY(wcid);
@@ -1848,15 +2001,15 @@ run_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) {
if(run_write_region_1(sc, base, k->wk_key, 16))
- goto fail;
+ return;
if(run_write_region_1(sc, base + 16, &k->wk_key[txmic], 8)) /* wk_txmic */
- goto fail;
+ return;
if(run_write_region_1(sc, base + 24, &k->wk_key[rxmic], 8)) /* wk_rxmic */
- goto fail;
+ return;
} else {
/* roundup len to 16-bit: XXX fix write_region_1() instead */
if(run_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1))
- goto fail;
+ return;
}
if (!(k->wk_flags & IEEE80211_KEY_GROUP) ||
@@ -1864,7 +2017,7 @@ run_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
/* set initial packet number in IV+EIV */
if (k->wk_cipher == IEEE80211_CIPHER_WEP){
memset(iv, 0, sizeof iv);
- iv[3] = sc->sc_rvp->vap.iv_def_txkey << 6;
+ iv[3] = vap->iv_def_txkey << 6;
} else {
if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) {
iv[0] = k->wk_keytsc >> 8;
@@ -1882,111 +2035,134 @@ run_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
iv[7] = k->wk_keytsc >> 40;
}
if(run_write_region_1(sc, RT2860_IVEIV(wcid), iv, 8))
- goto fail;
+ return;
}
if (k->wk_flags & IEEE80211_KEY_GROUP) {
/* install group key */
if(run_read(sc, RT2860_SKEY_MODE_0_7, &attr))
- goto fail;
+ return;
attr &= ~(0xf << (k->wk_keyix * 4));
attr |= mode << (k->wk_keyix * 4);
if(run_write(sc, RT2860_SKEY_MODE_0_7, attr))
- goto fail;
+ return;
} else {
/* install pairwise key */
if(run_read(sc, RT2860_WCID_ATTR(wcid), &attr))
- goto fail;
+ return;
attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN;
if(run_write(sc, RT2860_WCID_ATTR(wcid), attr))
- goto fail;
+ return;
}
/* TODO create a pass-thru key entry? */
-fail:
- RUN_UNLOCK(sc);
- return (error? 0 : 1);
+ /* need wcid to delete the right key later */
+ k->wk_pad = wcid;
}
/*
+ * Don't have to be deferred, but in order to keep order of
+ * execution, i.e. with run_key_delete(), defer this and let
+ * run_cmdq_cb() maintain the order.
+ *
* return 0 on error
*/
static int
-run_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
+run_key_set(struct ieee80211vap *vap, struct ieee80211_key *k,
+ const uint8_t mac[IEEE80211_ADDR_LEN])
{
struct ieee80211com *ic = vap->iv_ic;
struct run_softc *sc = ic->ic_ifp->if_softc;
- struct ieee80211_node *ni = vap->iv_bss;
- struct ieee80211_node_table *nt = &ic->ic_sta;
+ uint32_t i;
+
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ DPRINTF("cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_key_set_cb;
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].arg1 = vap;
+ sc->cmdq[i].k = k;
+ IEEE80211_ADDR_COPY(sc->cmdq[i].mac, mac);
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ return(1);
+}
+
+/*
+ * If wlan is destroyed without being brought down i.e. without
+ * wlan down or wpa_cli terminate, this function is called after
+ * vap is gone. Don't refer it.
+ */
+static void
+run_key_delete_cb(void *arg)
+{
+ struct run_cmdq *cmdq = arg;
+ struct run_softc *sc = cmdq->arg1;
+ struct ieee80211_key *k = &cmdq->key;
uint32_t attr;
uint8_t wcid;
- int error = 0;
- uint8_t nislocked, cislocked;
- if((nislocked = IEEE80211_NODE_IS_LOCKED(nt)))
- IEEE80211_NODE_UNLOCK(nt);
- if((cislocked = mtx_owned(&ic->ic_comlock.mtx)))
- IEEE80211_UNLOCK(ic);
- RUN_LOCK(sc);
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
if (k->wk_flags & IEEE80211_KEY_GROUP) {
/* remove group key */
- if(run_read(sc, RT2860_SKEY_MODE_0_7, &attr))
- goto fail;
+ DPRINTF("removing group key\n");
+ run_read(sc, RT2860_SKEY_MODE_0_7, &attr);
attr &= ~(0xf << (k->wk_keyix * 4));
- if(run_write(sc, RT2860_SKEY_MODE_0_7, attr))
- goto fail;
+ run_write(sc, RT2860_SKEY_MODE_0_7, attr);
} else {
/* remove pairwise key */
- wcid = RUN_AID2WCID((ni != NULL) ? ni->ni_associd : 0);
- if(run_read(sc, RT2860_WCID_ATTR(wcid), &attr))
- goto fail;
+ DPRINTF("removing key for wcid %x\n", k->wk_pad);
+ /* matching wcid was written to wk_pad in run_key_set() */
+ wcid = k->wk_pad;
+ run_read(sc, RT2860_WCID_ATTR(wcid), &attr);
attr &= ~0xf;
- if(run_write(sc, RT2860_WCID_ATTR(wcid), attr))
- goto fail;
+ run_write(sc, RT2860_WCID_ATTR(wcid), attr);
+ run_set_region_4(sc, RT2860_WCID_ENTRY(wcid), 0, 8);
}
-fail:
- RUN_UNLOCK(sc);
- if(cislocked)
- IEEE80211_LOCK(ic);
- if(nislocked)
- IEEE80211_NODE_LOCK(nt);
-
- return (error? 0 : 1);
+ k->wk_pad = 0;
}
-static void
-run_ratectl_start(struct run_softc *sc, struct ieee80211_node *ni)
+/*
+ * return 0 on error
+ */
+static int
+run_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k)
{
- struct ieee80211vap *vap = ni->ni_vap;
- struct run_vap *rvp = RUN_VAP(vap);
- uint32_t sta[3];
-
- RUN_LOCK_ASSERT(sc, MA_OWNED);
-
- /* read statistic counters (clear on read) and update AMRR state */
- run_read_region_1(sc, RT2860_TX_STA_CNT0,
- (uint8_t *)sta, sizeof sta);
-
- ieee80211_ratectl_node_init(ni);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_ifp->if_softc;
+ struct ieee80211_key *k0;
+ uint32_t i;
- /* start at lowest available bit-rate, AMRR will raise */
- ni->ni_txrate = 2;
+ /*
+ * When called back, key might be gone. So, make a copy
+ * of some values need to delete keys before deferring.
+ * But, because of LOR with node lock, cannot use lock here.
+ * So, use atomic instead.
+ */
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ DPRINTF("cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_key_delete_cb;
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].arg1 = sc;
+ k0 = &sc->cmdq[i].key;
+ k0->wk_flags = k->wk_flags;
+ k0->wk_keyix = k->wk_keyix;
+ /* matching wcid was written to wk_pad in run_key_set() */
+ k0->wk_pad = k->wk_pad;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ return (1); /* return fake success */
- /* start calibration timer */
- rvp->ratectl_run = RUN_RATECTL_ON;
- usb_callout_reset(&rvp->ratectl_ch, hz, run_ratectl_to, rvp);
}
static void
run_ratectl_to(void *arg)
{
- struct run_vap *rvp = arg;
+ struct run_softc *sc = arg;
/* do it in a process context, so it can go sleep */
- ieee80211_runtask(rvp->vap.iv_ic, &rvp->ratectl_task);
+ ieee80211_runtask(sc->sc_ifp->if_l2com, &sc->ratectl_task);
/* next timeout will be rescheduled in the callback task */
}
@@ -1994,13 +2170,15 @@ run_ratectl_to(void *arg)
static void
run_ratectl_cb(void *arg, int pending)
{
- struct run_vap *rvp = arg;
- struct ieee80211vap *vap = &rvp->vap;
- struct ieee80211com *ic = vap->iv_ic;
- struct run_softc *sc = ic->ic_ifp->if_softc;
+ struct run_softc *sc = arg;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
- if (ic->ic_opmode == IEEE80211_M_STA)
- run_iter_func(rvp, vap->iv_bss);
+ if(vap == NULL)
+ return;
+
+ if(sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA)
+ run_iter_func(sc, vap->iv_bss);
else {
/*
* run_reset_livelock() doesn't do anything with AMRR,
@@ -2011,77 +2189,95 @@ run_ratectl_cb(void *arg, int pending)
*/
RUN_LOCK(sc);
run_reset_livelock(sc);
+ /* just in case, there are some stats to drain */
+ run_drain_fifo(sc);
RUN_UNLOCK(sc);
- ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, rvp);
+ ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, sc);
}
- if(rvp->ratectl_run == RUN_RATECTL_ON)
- usb_callout_reset(&rvp->ratectl_ch, hz, run_ratectl_to, rvp);
+ if(sc->ratectl_run != RUN_RATECTL_OFF)
+ usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
}
-
static void
-run_iter_func(void *arg, struct ieee80211_node *ni)
+run_drain_fifo(void *arg)
{
- struct run_vap *rvp = arg;
- struct ieee80211com *ic = rvp->vap.iv_ic;
- struct ifnet *ifp = ic->ic_ifp;
- struct run_softc *sc = ifp->if_softc;
- struct ieee80211_node_table *nt = &ic->ic_sta;
- uint32_t sta[3], stat;
- int error;
+ struct run_softc *sc = arg;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211_node *ni = sc->sc_ni[0]; /* make compiler happy */
+ uint32_t stat;
+ int retrycnt = 0;
uint8_t wcid, mcs, pid;
- struct ieee80211vap *vap = ni->ni_vap;
- int txcnt = 0, success = 0, retrycnt = 0;
-
- if(ic->ic_opmode != IEEE80211_M_STA)
- IEEE80211_NODE_ITERATE_UNLOCK(nt);
- RUN_LOCK(sc);
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
- if(ic->ic_opmode != IEEE80211_M_STA){
+ for (;;) {
/* drain Tx status FIFO (maxsize = 16) */
run_read(sc, RT2860_TX_STAT_FIFO, &stat);
- while (stat & RT2860_TXQ_VLD) {
- DPRINTFN(4, "tx stat 0x%08x\n", stat);
+ DPRINTFN(4, "tx stat 0x%08x\n", stat);
+ if(!(stat & RT2860_TXQ_VLD))
+ break;
- wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff;
+ wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff;
- /* if no ACK was requested, no feedback is available */
- if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff)
- continue;
+ /* if no ACK was requested, no feedback is available */
+ if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_WCID_MAX ||
+ wcid == 0)
+ continue;
- /* update per-STA AMRR stats */
- if (stat & RT2860_TXQ_OK) {
- /*
- * Check if there were retries, ie if the Tx
- * success rate is different from the requested
- * rate. Note that it works only because we do
- * not allow rate fallback from OFDM to CCK.
- */
- mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f;
- pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf;
- if (mcs + 1 != pid)
- retrycnt = 1;
- ieee80211_ratectl_tx_complete(vap, ni,
- IEEE80211_RATECTL_TX_SUCCESS,
- &retrycnt, NULL);
- } else {
+ ni = sc->sc_ni[wcid];
+ if(ni->ni_rctls == NULL)
+ continue;
+
+ /* update per-STA AMRR stats */
+ if (stat & RT2860_TXQ_OK) {
+ /*
+ * Check if there were retries, ie if the Tx
+ * success rate is different from the requested
+ * rate. Note that it works only because we do
+ * not allow rate fallback from OFDM to CCK.
+ */
+ mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f;
+ pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf;
+ if (mcs + 1 != pid)
retrycnt = 1;
- ieee80211_ratectl_tx_complete(vap, ni,
- IEEE80211_RATECTL_TX_SUCCESS,
- &retrycnt, NULL);
- ifp->if_oerrors++;
- }
- run_read_region_1(sc, RT2860_TX_STAT_FIFO,
- (uint8_t *)&stat, sizeof stat);
+ ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
+ IEEE80211_RATECTL_TX_SUCCESS,
+ &retrycnt, NULL);
+ } else {
+ retrycnt = 1;
+ ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
+ IEEE80211_RATECTL_TX_FAILURE,
+ &retrycnt, NULL);
+ ifp->if_oerrors++;
}
- } else {
+ }
+ DPRINTFN(3, "count=%d\n", sc->fifo_cnt);
+
+ sc->fifo_cnt = 0;
+}
+
+static void
+run_iter_func(void *arg, struct ieee80211_node *ni)
+{
+ struct run_softc *sc = arg;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct run_node *rn = (void *)ni;
+ uint32_t sta[3];
+ int txcnt = 0, success = 0, retrycnt = 0;
+ int error;
+
+ if(sc->rvp_cnt <= 1 && (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_STA)){
+ RUN_LOCK(sc);
+
/* read statistic counters (clear on read) and update AMRR state */
error = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta,
sizeof sta);
if (error != 0)
- goto skip;
+ return;
DPRINTFN(3, "retrycnt=%d txcnt=%d failcnt=%d\n",
le32toh(sta[1]) >> 16, le32toh(sta[1]) & 0xffff,
@@ -2101,17 +2297,29 @@ run_iter_func(void *arg, struct ieee80211_node *ni)
success =
(le32toh(sta[1]) >> 16) +
(le32toh(sta[1]) & 0xffff);
+
ieee80211_ratectl_tx_update(vap, ni, &txcnt, &success,
&retrycnt);
+
+ RUN_UNLOCK(sc);
}
- ieee80211_ratectl_rate(ni, NULL, 0);
+ rn->amrr_ridx = ieee80211_ratectl_rate(ni, NULL, 0);
+ DPRINTFN(3, "ridx=%d\n", rn->amrr_ridx);
+}
-skip:;
- RUN_UNLOCK(sc);
+static void
+run_newassoc_cb(void *arg)
+{
+ struct run_cmdq *cmdq = arg;
+ struct ieee80211_node *ni = cmdq->arg1;
+ struct run_softc *sc = ni->ni_vap->iv_ic->ic_ifp->if_softc;
+ uint8_t wcid = cmdq->wcid;
- if(ic->ic_opmode != IEEE80211_M_STA)
- IEEE80211_NODE_ITERATE_LOCK(nt);
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
+ run_write_region_1(sc, RT2860_WCID_ENTRY(wcid),
+ ni->ni_macaddr, IEEE80211_ADDR_LEN);
}
static void
@@ -2119,11 +2327,40 @@ run_newassoc(struct ieee80211_node *ni, int isnew)
{
struct run_node *rn = (void *)ni;
struct ieee80211_rateset *rs = &ni->ni_rates;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_ifp->if_softc;
uint8_t rate;
- int ridx, i, j;
+ uint8_t ridx;
+ uint8_t wcid = RUN_AID2WCID(ni->ni_associd);
+ int i, j;
+
+ if(wcid > RT2870_WCID_MAX){
+ device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid);
+ return;
+ }
- DPRINTF("new assoc isnew=%d addr=%s\n",
- isnew, ether_sprintf(ni->ni_macaddr));
+ /* only interested in true associations */
+ if (isnew && ni->ni_associd != 0){
+
+ /*
+ * This function could is called though timeout function.
+ * Need to defer.
+ */
+ uint32_t cnt = RUN_CMDQ_GET(&sc->cmdq_store);
+ DPRINTF("cmdq_store=%d\n", cnt);
+ sc->cmdq[cnt].func = run_newassoc_cb;
+ sc->cmdq[cnt].arg0 = NULL;
+ sc->cmdq[cnt].arg1 = ni;
+ sc->cmdq[cnt].wcid = wcid;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+
+ DPRINTF("new assoc isnew=%d associd=%x addr=%s\n",
+ isnew, ni->ni_associd, ether_sprintf(ni->ni_macaddr));
+
+ ieee80211_ratectl_node_init(ni);
+ sc->sc_ni[wcid] = ni;
for (i = 0; i < rs->rs_nrates; i++) {
rate = rs->rs_rates[i] & IEEE80211_RATE_VAL;
@@ -2148,6 +2385,14 @@ run_newassoc(struct ieee80211_node *ni, int isnew)
DPRINTF("rate=0x%02x ridx=%d ctl_ridx=%d\n",
rs->rs_rates[i], rn->ridx[i], rn->ctl_ridx[i]);
}
+ rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
+ for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ rn->mgt_ridx = ridx;
+ DPRINTF("rate=%d, mgmt_ridx=%d\n", rate, rn->mgt_ridx);
+
+ usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
}
/*
@@ -2172,7 +2417,6 @@ static void
run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen)
{
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211vap *vap = &sc->sc_rvp->vap;
struct ieee80211com *ic = ifp->if_l2com;
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
@@ -2217,9 +2461,13 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen)
len += 2;
}
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+
if (__predict_false(flags & RT2860_RX_MICERR)) {
/* report MIC failures to net80211 for TKIP */
- ieee80211_notify_michael_failure(vap, wh, rxwi->keyidx);
+ if(ni != NULL)
+ ieee80211_notify_michael_failure(ni->ni_vap, wh, rxwi->keyidx);
m_freem(m);
ifp->if_ierrors++;
DPRINTF("MIC error. Someone is lying.\n");
@@ -2233,8 +2481,6 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen)
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = len;
- ni = ieee80211_find_rxnode(ic,
- mtod(m, struct ieee80211_frame_min *));
if (ni != NULL) {
(void)ieee80211_input(ni, m, rssi, nf);
ieee80211_free_node(ni);
@@ -2431,6 +2677,7 @@ run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, unsigned int ind
{
struct run_softc *sc = usbd_xfer_softc(xfer);
struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
struct run_tx_data *data;
struct ieee80211vap *vap = NULL;
struct usb_page_cache *pc;
@@ -2486,20 +2733,22 @@ tr_setup:
vap = data->ni->ni_vap;
if (ieee80211_radiotap_active_vap(vap)) {
struct run_tx_radiotap_header *tap = &sc->sc_txtap;
+ struct rt2860_txwi *txwi =
+ (struct rt2860_txwi *)(&data->desc + sizeof(struct rt2870_txd));
tap->wt_flags = 0;
tap->wt_rate = rt2860_rates[data->ridx].rate;
tap->wt_chan_freq = htole16(vap->iv_bss->ni_chan->ic_freq);
tap->wt_chan_flags = htole16(vap->iv_bss->ni_chan->ic_flags);
tap->wt_hwqueue = index;
- if (data->mcs & RT2860_PHY_SHPRE)
+ if (le16toh(txwi->phy) & RT2860_PHY_SHPRE)
tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
ieee80211_radiotap_tx(vap, m);
}
/* align end on a 4-bytes boundary */
- len = (size + m->m_pkthdr.len + 3) & ~3;
+ len = (size + IEEE80211_CRC_LEN + m->m_pkthdr.len + 3) & ~3;
DPRINTFN(11, "sending frame len=%u xferlen=%u @ index %d\n",
m->m_pkthdr.len, len, index);
@@ -2524,14 +2773,22 @@ tr_setup:
ifp->if_oerrors++;
if (data != NULL) {
+ if(data->ni != NULL)
+ vap = data->ni->ni_vap;
run_tx_free(pq, data, error);
usbd_xfer_set_priv(xfer, NULL);
}
+ if(vap == NULL)
+ vap = TAILQ_FIRST(&ic->ic_vaps);
if (error != USB_ERR_CANCELLED) {
if (error == USB_ERR_TIMEOUT) {
device_printf(sc->sc_dev, "device timeout\n");
- ieee80211_runtask(ifp->if_l2com, &sc->usb_timeout_task);
+ uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store);
+ DPRINTF("cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_usb_timeout_cb;
+ sc->cmdq[i].arg0 = vap;
+ ieee80211_runtask(ic, &sc->cmdq_task);
}
/*
@@ -2583,22 +2840,21 @@ run_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error)
}
static void
-run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data,
- uint8_t wflags, uint8_t xflags, uint8_t opflags, uint8_t dflags,
- uint8_t type, uint8_t pad)
+run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data)
{
struct mbuf *m = data->m;
struct ieee80211com *ic = sc->sc_ifp->if_l2com;
- struct ieee80211vap *vap = &sc->sc_rvp->vap;
+ struct ieee80211vap *vap = data->ni->ni_vap;
struct ieee80211_frame *wh;
struct rt2870_txd *txd;
struct rt2860_txwi *txwi;
- int xferlen;
- uint8_t mcs;
+ uint16_t xferlen;
+ uint16_t mcs;
uint8_t ridx = data->ridx;
+ uint8_t pad;
/* get MCS code from rate index */
- data->mcs = mcs = rt2860_rates[ridx].mcs;
+ mcs = rt2860_rates[ridx].mcs;
xferlen = sizeof(*txwi) + m->m_pkthdr.len;
@@ -2606,15 +2862,21 @@ run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data,
xferlen = (xferlen + 3) & ~3;
txd = (struct rt2870_txd *)&data->desc;
- txd->flags = dflags;
txd->len = htole16(xferlen);
+ wh = mtod(m, struct ieee80211_frame *);
+
+ /*
+ * Ether both are true or both are false, the header
+ * are nicely aligned to 32-bit. So, no L2 padding.
+ */
+ if(IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh))
+ pad = 0;
+ else
+ pad = 2;
+
/* setup TX Wireless Information */
txwi = (struct rt2860_txwi *)(txd + 1);
- txwi->flags = wflags;
- txwi->xflags = xflags;
- txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ?
- RUN_AID2WCID(data->ni->ni_associd) : 0xff;
txwi->len = htole16(m->m_pkthdr.len - pad);
if (rt2860_rates[ridx].phy == IEEE80211_T_DS) {
txwi->phy = htole16(RT2860_PHY_CCK);
@@ -2625,16 +2887,14 @@ run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data,
txwi->phy = htole16(RT2860_PHY_OFDM);
txwi->phy |= htole16(mcs);
- wh = mtod(m, struct ieee80211_frame *);
-
/* check if RTS/CTS or CTS-to-self protection is required */
if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
(m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold ||
((ic->ic_flags & IEEE80211_F_USEPROT) &&
rt2860_rates[ridx].phy == IEEE80211_T_OFDM)))
- txwi->txop = RT2860_TX_TXOP_HT | opflags;
+ txwi->txop |= RT2860_TX_TXOP_HT;
else
- txwi->txop = RT2860_TX_TXOP_BACKOFF | opflags;
+ txwi->txop |= RT2860_TX_TXOP_BACKOFF;
}
/* This function must be called locked */
@@ -2642,21 +2902,24 @@ static int
run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
{
struct ieee80211com *ic = sc->sc_ifp->if_l2com;
- struct ieee80211vap *vap = &sc->sc_rvp->vap;
+ struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_frame *wh;
+ struct ieee80211_channel *chan;
const struct ieee80211_txparam *tp;
+ struct run_node *rn = (void *)ni;
struct run_tx_data *data;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
uint16_t qos;
uint16_t dur;
+ uint16_t qid;
uint8_t type;
uint8_t tid;
- uint8_t qid;
+ uint8_t ridx;
+ uint8_t ctl_ridx;
uint8_t qflags;
- uint8_t pad;
uint8_t xflags = 0;
int hasqos;
- int ridx;
- int ctl_ridx;
RUN_LOCK_ASSERT(sc, MA_OWNED);
@@ -2681,19 +2944,18 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
qos = le16toh(*(const uint16_t *)frm);
tid = qos & IEEE80211_QOS_TID;
qid = TID_TO_WME_AC(tid);
- pad = 2;
} else {
qos = 0;
tid = 0;
qid = WME_AC_BE;
- pad = 0;
}
qflags = (qid < 4) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_HCCA;
DPRINTFN(8, "qos %d\tqid %d\ttid %d\tqflags %x\n",
qos, qid, tid, qflags);
- tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ chan = (ni->ni_chan != IEEE80211_CHAN_ANYC)?ni->ni_chan:ic->ic_curchan;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(chan)];
/* pickup a rate index */
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
@@ -2701,25 +2963,22 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1;
ctl_ridx = rt2860_rates[ridx].ctl_ridx;
- } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
- ridx = sc->fixed_ridx;
- ctl_ridx = rt2860_rates[ridx].ctl_ridx;
} else {
- for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++){
- if (rt2860_rates[ridx].rate == ni->ni_txrate)
- break;
- }
+ if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ ridx = rn->fix_ridx;
+ else
+ ridx = rn->amrr_ridx;
ctl_ridx = rt2860_rates[ridx].ctl_ridx;
}
if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
(!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) !=
IEEE80211_QOS_ACKPOLICY_NOACK)) {
- xflags |= RT2860_TX_ACK;
+ xflags = RT2860_TX_ACK;
if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
- dur = rt2860_rates[ridx].sp_ack_dur;
+ dur = rt2860_rates[ctl_ridx].sp_ack_dur;
else
- dur = rt2860_rates[ridx].lp_ack_dur;
+ dur = rt2860_rates[ctl_ridx].lp_ack_dur;
*(uint16_t *)wh->i_dur = htole16(dur);
}
@@ -2733,11 +2992,64 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next);
sc->sc_epq[qid].tx_nfree--;
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = qflags;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->xflags = xflags;
+ txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ?
+ RUN_AID2WCID(ni->ni_associd) : 0xff;
+ /* clear leftover garbage bits */
+ txwi->flags = 0;
+ txwi->txop = 0;
+
data->m = m;
data->ni = ni;
data->ridx = ridx;
- run_set_tx_desc(sc, data, 0, xflags, 0, qflags, type, pad);
+ run_set_tx_desc(sc, data);
+
+ /*
+ * The chip keeps track of 2 kind of Tx stats,
+ * * TX_STAT_FIFO, for per WCID stats, and
+ * * TX_STA_CNT0 for all-TX-in-one stats.
+ *
+ * To use FIFO stats, we need to store MCS into the driver-private
+ * PacketID field. So that, we can tell whose stats when we read them.
+ * We add 1 to the MCS because setting the PacketID field to 0 means
+ * that we don't want feedback in TX_STAT_FIFO.
+ * And, that's what we want for STA mode, since TX_STA_CNT0 does the job.
+ *
+ * FIFO stats doesn't count Tx with WCID 0xff, so we do this in run_tx().
+ */
+ if(sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS){
+ uint16_t pid = (rt2860_rates[ridx].mcs + 1) & 0xf;
+ txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT);
+
+ /*
+ * Unlike PCI based devices, we don't get any interrupt from
+ * USB devices, so we simulate FIFO-is-full interrupt here.
+ * Ralink recomends to drain FIFO stats every 100 ms, but 16 slots
+ * quickly get fulled. To prevent overflow, increment a counter on
+ * every FIFO stat request, so we know how many slots are left.
+ * We do this only in HOSTAP or multiple vap mode since FIFO stats
+ * are used only in those modes.
+ * We just drain stats. AMRR gets updated every 1 sec by
+ * run_ratectl_cb() via callout.
+ * Call it early. Otherwise overflow.
+ */
+ if(sc->fifo_cnt++ == 10){
+ /*
+ * With multiple vaps or if_bridge, if_start() is called
+ * with a non-sleepable lock, tcpinp. So, need to defer.
+ */
+ uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store);
+ DPRINTFN(6, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_drain_fifo;
+ sc->cmdq[i].arg0 = sc;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+ }
STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next);
@@ -2753,36 +3065,36 @@ run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
static int
run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
{
- const struct ieee80211_txparam *tp;
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ifp->if_l2com;
+ struct run_node *rn = (void *)ni;
struct run_tx_data *data;
struct ieee80211_frame *wh;
- int ridx;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
uint16_t dur;
+ uint8_t ridx = rn->mgt_ridx;
uint8_t type;
uint8_t xflags = 0;
+ uint8_t wflags = 0;
RUN_LOCK_ASSERT(sc, MA_OWNED);
wh = mtod(m, struct ieee80211_frame *);
type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
- tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
- if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
+ wflags |= RT2860_TX_TS;
+ else if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
xflags |= RT2860_TX_ACK;
- dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate,
+ dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate,
ic->ic_flags & IEEE80211_F_SHPREAMBLE);
*(uint16_t *)wh->i_dur = htole16(dur);
-
- /* tell hardware to add timestamp for probe responses */
- if ((wh->i_fc[0] &
- (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
- (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
- xflags |= RT2860_TX_TS;
}
if (sc->sc_epq[0].tx_nfree == 0) {
@@ -2794,19 +3106,23 @@ run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
sc->sc_epq[0].tx_nfree--;
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = RT2860_TX_QSEL_EDCA;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->flags = wflags;
+ txwi->xflags = xflags;
+ txwi->txop = 0; /* clear leftover garbage bits */
+
data->m = m;
data->ni = ni;
- for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
- if (rt2860_rates[ridx].rate == tp->mgmtrate)
- break;
data->ridx = ridx;
- run_set_tx_desc(sc, data, 0, xflags, 0, RT2860_TX_QSEL_MGMT,
- wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, 0);
+ run_set_tx_desc(sc, data);
DPRINTFN(10, "sending mgt frame len=%d rate=%d\n", m->m_pkthdr.len +
(int)(sizeof (struct rt2870_txd) + sizeof (struct rt2860_rxwi)),
- tp->mgmtrate);
+ rt2860_rates[ridx].rate);
STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
@@ -2822,6 +3138,8 @@ run_sendprot(struct run_softc *sc,
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
struct run_tx_data *data;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
struct mbuf *mprot;
int ridx;
int protrate;
@@ -2830,8 +3148,8 @@ run_sendprot(struct run_softc *sc,
int isshort;
uint16_t dur;
uint8_t type;
- uint8_t wflags;
- uint8_t txflags = 0;
+ uint8_t wflags = 0;
+ uint8_t xflags = 0;
RUN_LOCK_ASSERT(sc, MA_OWNED);
@@ -2860,7 +3178,7 @@ run_sendprot(struct run_softc *sc,
if (prot == IEEE80211_PROT_RTSCTS) {
/* NB: CTS is the same size as an ACK */
dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort);
- txflags |= RT2860_TX_ACK;
+ xflags |= RT2860_TX_ACK;
mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
} else {
mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
@@ -2875,6 +3193,14 @@ run_sendprot(struct run_softc *sc,
STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
sc->sc_epq[0].tx_nfree--;
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = RT2860_TX_QSEL_EDCA;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->flags = wflags;
+ txwi->xflags = xflags;
+ txwi->txop = 0; /* clear leftover garbage bits */
+
data->m = mprot;
data->ni = ieee80211_ref_node(ni);
@@ -2883,8 +3209,7 @@ run_sendprot(struct run_softc *sc,
break;
data->ridx = ridx;
- run_set_tx_desc(sc, data, wflags, txflags, 0,
- RT2860_TX_QSEL_EDCA, type, 0);
+ run_set_tx_desc(sc, data);
DPRINTFN(1, "sending prot len=%u rate=%u\n",
m->m_pkthdr.len, rate);
@@ -2903,11 +3228,13 @@ run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
struct run_tx_data *data;
+ struct rt2870_txd *txd;
+ struct rt2860_txwi *txwi;
uint8_t type;
- uint8_t opflags;
- uint8_t txflags;
- int ridx;
- int rate;
+ uint8_t ridx;
+ uint8_t rate;
+ uint8_t opflags = 0;
+ uint8_t xflags = 0;
int error;
RUN_LOCK_ASSERT(sc, MA_OWNED);
@@ -2923,10 +3250,8 @@ run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
return (EINVAL);
}
- opflags = 0;
- txflags = 0;
if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
- txflags |= RT2860_TX_ACK;
+ xflags |= RT2860_TX_ACK;
if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
error = run_sendprot(sc, m, ni,
params->ibp_flags & IEEE80211_BPF_RTS ?
@@ -2949,6 +3274,14 @@ run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
sc->sc_epq[0].tx_nfree--;
+ txd = (struct rt2870_txd *)&data->desc;
+ txd->flags = RT2860_TX_QSEL_EDCA;
+ txwi = (struct rt2860_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->xflags = xflags;
+ txwi->txop = opflags;
+ txwi->flags = 0; /* clear leftover garbage bits */
+
data->m = m;
data->ni = ni;
for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
@@ -2956,8 +3289,7 @@ run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
break;
data->ridx = ridx;
- run_set_tx_desc(sc, data, 0, txflags, opflags,
- RT2860_TX_QSEL_EDCA, type, 0);
+ run_set_tx_desc(sc, data);
DPRINTFN(10, "sending raw frame len=%u rate=%u\n",
m->m_pkthdr.len, rate);
@@ -2975,14 +3307,14 @@ run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
{
struct ifnet *ifp = ni->ni_ic->ic_ifp;
struct run_softc *sc = ifp->if_softc;
- int error;
-
+ int error = 0;
+
RUN_LOCK(sc);
/* prevent management frames from being sent if we're not ready */
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
error = ENETDOWN;
- goto bad;
+ goto done;
}
if (params == NULL) {
@@ -2990,28 +3322,27 @@ run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
if ((error = run_tx_mgt(sc, m, ni)) != 0){
ifp->if_oerrors++;
DPRINTF("mgt tx failed\n");
- goto bad;
+ goto done;
}
} else {
/* tx raw packet with param */
if ((error = run_tx_param(sc, m, ni, params)) != 0){
ifp->if_oerrors++;
DPRINTF("tx with param failed\n");
- goto bad;
+ goto done;
}
}
ifp->if_opackets++;
+done:
RUN_UNLOCK(sc);
- return (0);
-
-bad:
- RUN_UNLOCK(sc);
- if(m != NULL)
- m_freem(m);
- ieee80211_free_node(ni);
+ if(error != 0){
+ if(m != NULL)
+ m_freem(m);
+ ieee80211_free_node(ni);
+ }
return (error);
}
@@ -3053,24 +3384,27 @@ run_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
struct run_softc *sc = ifp->if_softc;
struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ifreq *ifr = (struct ifreq *) data;
- int error = 0, startall = 0;
+ int startall = 0;
+ int error = 0;
switch (cmd) {
case SIOCSIFFLAGS:
RUN_LOCK(sc);
if (ifp->if_flags & IFF_UP) {
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)){
- run_init_locked(sc);
startall = 1;
+ run_init_locked(sc);
} else
run_update_promisc_locked(ifp);
} else {
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- run_stop(sc);
+ if(ifp->if_drv_flags & IFF_DRV_RUNNING &&
+ (ic->ic_nrunning == 0 || sc->rvp_cnt <= 1)){
+ run_stop(sc);
+ }
}
RUN_UNLOCK(sc);
if(startall)
- ieee80211_start_all(ic);
+ ieee80211_start_all(ic);
break;
case SIOCGIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
@@ -3138,19 +3472,17 @@ run_select_chan_group(struct run_softc *sc, int group)
run_write(sc, RT2860_TX_BAND_CFG, tmp);
/* enable appropriate Power Amplifiers and Low Noise Amplifiers */
- tmp = RT2860_RFTR_EN | RT2860_TRSW_EN;
+ tmp = RT2860_RFTR_EN | RT2860_TRSW_EN | RT2860_LNA_PE0_EN;
+ if (sc->nrxchains > 1)
+ tmp |= RT2860_LNA_PE1_EN;
if (group == 0) { /* 2GHz */
- tmp |= RT2860_PA_PE_G0_EN | RT2860_LNA_PE_G0_EN;
+ tmp |= RT2860_PA_PE_G0_EN;
if (sc->ntxchains > 1)
tmp |= RT2860_PA_PE_G1_EN;
- if (sc->nrxchains > 1)
- tmp |= RT2860_LNA_PE_G1_EN;
} else { /* 5GHz */
- tmp |= RT2860_PA_PE_A0_EN | RT2860_LNA_PE_A0_EN;
+ tmp |= RT2860_PA_PE_A0_EN;
if (sc->ntxchains > 1)
tmp |= RT2860_PA_PE_A1_EN;
- if (sc->nrxchains > 1)
- tmp |= RT2860_LNA_PE_A1_EN;
}
if (sc->mac_ver == 0x3572) {
run_rt3070_rf_write(sc, 8, 0x00);
@@ -3198,13 +3530,13 @@ run_rt2870_set_chan(struct run_softc *sc, uint32_t chan)
txpow2 = sc->txpow2[i];
if (chan > 14) {
if (txpow1 >= 0)
- txpow1 = txpow1 << 1;
+ txpow1 = txpow1 << 1 | 1;
else
- txpow1 = (7 + txpow1) << 1 | 1;
+ txpow1 = (7 + txpow1) << 1;
if (txpow2 >= 0)
- txpow2 = txpow2 << 1;
+ txpow2 = txpow2 << 1 | 1;
else
- txpow2 = (7 + txpow2) << 1 | 1;
+ txpow2 = (7 + txpow2) << 1;
}
r3 = rfprog[i].r3 | txpow1 << 7;
r4 = rfprog[i].r4 | sc->freq << 13 | txpow2 << 4;
@@ -3541,36 +3873,38 @@ run_scan_end(struct ieee80211com *ic)
return;
}
-static uint8_t
-run_rate2mcs(uint8_t rate)
+/*
+ * Could be called from ieee80211_node_timeout()
+ * (non-sleepable thread)
+ */
+static void
+run_update_beacon(struct ieee80211vap *vap, int item)
{
- switch (rate) {
- /* CCK rates */
- case 2: return 0;
- case 4: return 1;
- case 11: return 2;
- case 22: return 3;
- /* OFDM rates */
- case 12: return 0;
- case 18: return 1;
- case 24: return 2;
- case 36: return 3;
- case 48: return 4;
- case 72: return 5;
- case 96: return 6;
- case 108: return 7;
- }
- return 0; /* shouldn't get here */
+ struct ieee80211com *ic = vap->iv_ic;
+ struct run_softc *sc = ic->ic_ifp->if_softc;
+ uint32_t i;
+
+ i = RUN_CMDQ_GET(&sc->cmdq_store);
+ DPRINTF("cmdq_store=%d\n", i);
+ sc->cmdq[i].func = run_update_beacon_cb;
+ sc->cmdq[i].arg0 = vap;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ return;
}
static void
-run_update_beacon_locked(struct ieee80211vap *vap, int item)
+run_update_beacon_cb(void *arg)
{
+ struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct run_softc *sc = ic->ic_ifp->if_softc;
struct rt2860_txwi txwi;
struct mbuf *m;
- int rate;
+ uint8_t ridx;
+
+ if(vap->iv_bss->ni_chan == IEEE80211_CHAN_ANYC)
+ return;
if ((m = ieee80211_beacon_alloc(vap->iv_bss, &RUN_VAP(vap)->bo)) == NULL)
return;
@@ -3579,16 +3913,17 @@ run_update_beacon_locked(struct ieee80211vap *vap, int item)
txwi.wcid = 0xff;
txwi.len = htole16(m->m_pkthdr.len);
/* send beacons at the lowest available rate */
- rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? 12 : 2;
- txwi.phy = htole16(run_rate2mcs(rate));
- if (rate == 12)
+ ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
+ RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1;
+ txwi.phy = htole16(rt2860_rates[ridx].mcs);
+ if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM)
txwi.phy |= htole16(RT2860_PHY_OFDM);
txwi.txop = RT2860_TX_TXOP_HT;
txwi.flags = RT2860_TX_TS;
- run_write_region_1(sc, RT2860_BCN_BASE(0),
- (u_int8_t *)&txwi, sizeof txwi);
- run_write_region_1(sc, RT2860_BCN_BASE(0) + sizeof txwi,
+ run_write_region_1(sc, RT2860_BCN_BASE(RUN_VAP(vap)->rvp_id),
+ (uint8_t *)&txwi, sizeof txwi);
+ run_write_region_1(sc, RT2860_BCN_BASE(RUN_VAP(vap)->rvp_id) + sizeof txwi,
mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1); /* roundup len */
m_freem(m);
@@ -3597,21 +3932,6 @@ run_update_beacon_locked(struct ieee80211vap *vap, int item)
}
static void
-run_update_beacon(struct ieee80211vap *vap, int item)
-{
- struct ieee80211com *ic = vap->iv_ic;
- struct run_softc *sc = ic->ic_ifp->if_softc;
-
- IEEE80211_UNLOCK(ic);
- RUN_LOCK(sc);
- run_update_beacon_locked(vap, item);
- RUN_UNLOCK(sc);
- IEEE80211_LOCK(ic);
-
- return;
-}
-
-static void
run_updateprot(struct ieee80211com *ic)
{
struct run_softc *sc = ic->ic_ifp->if_softc;
@@ -3635,12 +3955,12 @@ run_updateprot(struct ieee80211com *ic)
}
static void
-run_usb_timeout_cb(void *arg, int pending)
+run_usb_timeout_cb(void *arg)
{
- struct run_softc *sc = arg;
- struct ieee80211vap *vap = &sc->sc_rvp->vap;
+ struct ieee80211vap *vap = arg;
+ struct run_softc *sc = vap->iv_ic->ic_ifp->if_softc;
- RUN_LOCK(sc);
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
if(vap->iv_state == IEEE80211_S_RUN &&
vap->iv_opmode != IEEE80211_M_STA)
@@ -3651,8 +3971,6 @@ run_usb_timeout_cb(void *arg, int pending)
ieee80211_cancel_scan(vap);
} else
DPRINTF("timeout by unknown cause\n");
-
- RUN_UNLOCK(sc);
}
static void
@@ -3660,12 +3978,15 @@ run_reset_livelock(struct run_softc *sc)
{
uint32_t tmp;
+ RUN_LOCK_ASSERT(sc, MA_OWNED);
+
/*
* In IBSS or HostAP modes (when the hardware sends beacons), the MAC
* can run into a livelock and start sending CTS-to-self frames like
* crazy if protection is enabled. Reset MAC/BBP for a while
*/
run_read(sc, RT2860_DEBUG, &tmp);
+ DPRINTFN(3, "debug reg %08x\n", tmp);
if((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))){
DPRINTF("CTS-to-self livelock detected\n");
run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST);
@@ -3709,35 +4030,39 @@ run_update_promisc(struct ifnet *ifp)
static void
run_enable_tsf_sync(struct run_softc *sc)
{
- struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211com *ic = sc->sc_ifp->if_l2com;
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
uint32_t tmp;
+ DPRINTF("rvp_id=%d ic_opmode=%d\n", RUN_VAP(vap)->rvp_id, ic->ic_opmode);
+
run_read(sc, RT2860_BCN_TIME_CFG, &tmp);
tmp &= ~0x1fffff;
tmp |= vap->iv_bss->ni_intval * 16;
tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN;
- if (vap->iv_opmode == IEEE80211_M_STA) {
+ if (ic->ic_opmode == IEEE80211_M_STA) {
/*
* Local TSF is always updated with remote TSF on beacon
* reception.
*/
tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT;
- } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
+ } else if (ic->ic_opmode == IEEE80211_M_IBSS) {
tmp |= RT2860_BCN_TX_EN;
/*
* Local TSF is updated with remote TSF on beacon reception
* only if the remote TSF is greater than local TSF.
*/
tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT;
- } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ } else if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_MBSS) {
tmp |= RT2860_BCN_TX_EN;
/* SYNC with nobody */
tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT;
- } else
+ } else {
DPRINTF("Enabling TSF failed. undefined opmode\n");
+ return;
+ }
run_write(sc, RT2860_BCN_TIME_CFG, tmp);
}
@@ -3833,6 +4158,13 @@ run_updateslot(struct ifnet *ifp)
run_write(sc, RT2860_BKOFF_SLOT_CFG, tmp);
}
+static void
+run_update_mcast(struct ifnet *ifp)
+{
+ /* h/w filter supports getting everything or nothing */
+ ifp->if_flags |= IFF_ALLMULTI;
+}
+
static int8_t
run_rssi2dbm(struct run_softc *sc, uint8_t rssi, uint8_t rxchain)
{
@@ -3942,23 +4274,18 @@ run_rt3070_rf_init(struct run_softc *sc)
/* patch LNA_PE_G1 */
run_read(sc, RT3070_GPIO_SWITCH, &tmp);
run_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20);
+
} else if(sc->mac_ver == 0x3572){
run_rt3070_rf_read(sc, 6, &rf);
run_rt3070_rf_write(sc, 6, rf | 0x40);
- if (sc->mac_rev < 0x0211){
- /* increase voltage from 1.2V to 1.35V */
- run_read(sc, RT3070_LDO_CFG0, &tmp);
- tmp = (tmp & ~0x0f000000) | 0x0d000000;
- run_write(sc, RT3070_LDO_CFG0, tmp);
- } else {
- /* increase voltage from 1.2V to 1.35V */
- run_read(sc, RT3070_LDO_CFG0, &tmp);
- tmp = (tmp & ~0x1f000000) | 0x0d000000;
- run_write(sc, RT3070_LDO_CFG0, tmp);
+ /* increase voltage from 1.2V to 1.35V */
+ run_read(sc, RT3070_LDO_CFG0, &tmp);
+ tmp = (tmp & ~0x1f000000) | 0x0d000000;
+ run_write(sc, RT3070_LDO_CFG0, tmp);
+ if (sc->mac_rev < 0x0211 || !sc->patch_dac){
run_delay(sc, 1); /* wait for 1msec */
-
/* decrease voltage back to 1.2V */
tmp = (tmp & ~0x1f000000) | 0x01000000;
run_write(sc, RT3070_LDO_CFG0, tmp);
@@ -4240,6 +4567,9 @@ run_init_locked(struct run_softc *sc)
int ridx;
int ntries;
+ if(ic->ic_nrunning > 1)
+ return;
+
run_stop(sc);
for (ntries = 0; ntries < 100; ntries++) {
@@ -4353,7 +4683,7 @@ run_init_locked(struct run_softc *sc)
run_write(sc, RT2860_WMM_TXOP1_CFG, 48 << 16 | 96);
/* write vendor-specific BBP values (from EEPROM) */
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 10; i++) {
if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff)
continue;
run_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val);
@@ -4400,6 +4730,7 @@ run_init_locked(struct run_softc *sc)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ sc->cmdq_run = RUN_CMDQ_GO;
for(i = 0; i != RUN_N_XFER; i++)
usbd_xfer_set_stall(sc->sc_xfer[i]);
@@ -4427,7 +4758,7 @@ run_init(void *arg)
RUN_UNLOCK(sc);
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
- ieee80211_start_all(ic);
+ ieee80211_start_all(ic);
}
static void
@@ -4435,24 +4766,20 @@ run_stop(void *arg)
{
struct run_softc *sc = (struct run_softc *)arg;
struct ifnet *ifp = sc->sc_ifp;
- struct ieee80211com *ic = ifp->if_l2com;
uint32_t tmp;
int i;
int ntries;
RUN_LOCK_ASSERT(sc, MA_OWNED);
- if(sc->sc_rvp != NULL){
- sc->sc_rvp->ratectl_run = RUN_RATECTL_OFF;
- if (ic->ic_flags & IEEE80211_F_SCAN)
- ieee80211_cancel_scan(&sc->sc_rvp->vap);
- }
-
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
run_set_leds(sc, 0); /* turn all LEDs off */
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ sc->ratectl_run = RUN_RATECTL_OFF;
+ sc->cmdq_run = RUN_CMDQ_ABORT;
+
RUN_UNLOCK(sc);
for(i = 0; i < RUN_N_XFER; i++)
diff --git a/sys/dev/usb/wlan/if_runreg.h b/sys/dev/usb/wlan/if_runreg.h
index f872fad..33f3b94 100644
--- a/sys/dev/usb/wlan/if_runreg.h
+++ b/sys/dev/usb/wlan/if_runreg.h
@@ -517,8 +517,10 @@
#define RT2860_LNA_PE_A0_POL (1 << 12)
#define RT2860_LNA_PE_G1_EN (1 << 11)
#define RT2860_LNA_PE_A1_EN (1 << 10)
+#define RT2860_LNA_PE1_EN (RT2860_LNA_PE_A1_EN | RT2860_LNA_PE_G1_EN)
#define RT2860_LNA_PE_G0_EN (1 << 9)
#define RT2860_LNA_PE_A0_EN (1 << 8)
+#define RT2860_LNA_PE0_EN (RT2860_LNA_PE_A0_EN | RT2860_LNA_PE_G0_EN)
#define RT2860_PA_PE_G1_POL (1 << 7)
#define RT2860_PA_PE_A1_POL (1 << 6)
#define RT2860_PA_PE_G0_POL (1 << 5)
@@ -896,7 +898,7 @@ struct rt2860_rxwi {
#define RT2860_RIDX_CCK1 0
#define RT2860_RIDX_CCK11 3
#define RT2860_RIDX_OFDM6 4
-#define RT2860_RIDX_MAX 11
+#define RT2860_RIDX_MAX 12
static const struct rt2860_rate {
uint8_t rate;
uint8_t mcs;
@@ -1097,24 +1099,6 @@ static const struct rt2860_rate {
{ 171, 0x100bb1, 0x1300f5, 0x05e014, 0x001401 }, \
{ 173, 0x100bb1, 0x1300f5, 0x05e014, 0x001403 }
-#if 0
-#define RT3070_RF3020 \
- { 241, 2, 2 }, \
- { 241, 2, 7 }, \
- { 242, 2, 2 }, \
- { 242, 2, 7 }, \
- { 243, 2, 2 }, \
- { 243, 2, 7 }, \
- { 244, 2, 2 }, \
- { 244, 2, 7 }, \
- { 245, 2, 2 }, \
- { 245, 2, 7 }, \
- { 246, 2, 2 }, \
- { 246, 2, 7 }, \
- { 247, 2, 2 }, \
- { 248, 2, 4 }
-#endif
-
#define RT3070_RF3052 \
{ 0xf1, 2, 2 }, \
{ 0xf1, 2, 7 }, \
diff --git a/sys/dev/usb/wlan/if_runvar.h b/sys/dev/usb/wlan/if_runvar.h
index a7f8f1f..7349eaf 100644
--- a/sys/dev/usb/wlan/if_runvar.h
+++ b/sys/dev/usb/wlan/if_runvar.h
@@ -44,16 +44,18 @@
#define RUN_TX_RING_COUNT 32
#define RUN_RX_RING_COUNT 1
-#define RT2870_WCID_MAX 253
+#define RT2870_WCID_MAX 64
#define RUN_AID2WCID(aid) ((aid) & 0xff)
+#define RUN_VAP_MAX 8
+
struct run_rx_radiotap_header {
struct ieee80211_radiotap_header wr_ihdr;
uint8_t wr_flags;
uint8_t wr_rate;
uint16_t wr_chan_freq;
uint16_t wr_chan_flags;
- uint8_t wr_dbm_antsignal;
+ int8_t wr_dbm_antsignal;
uint8_t wr_antenna;
uint8_t wr_antsignal;
} __packed;
@@ -93,8 +95,7 @@ struct run_tx_data {
uint32_t align[0]; /* dummy field */
uint8_t desc[sizeof(struct rt2870_txd) +
sizeof(struct rt2860_txwi)];
- int ridx;
- uint8_t mcs;
+ uint8_t ridx;
};
STAILQ_HEAD(run_tx_data_head, run_tx_data);
@@ -102,19 +103,29 @@ struct run_node {
struct ieee80211_node ni;
uint8_t ridx[IEEE80211_RATE_MAXSIZE];
uint8_t ctl_ridx[IEEE80211_RATE_MAXSIZE];
+ uint8_t amrr_ridx;
+ uint8_t mgt_ridx;
+ uint8_t fix_ridx;
+};
+
+struct run_cmdq {
+ void *arg0;
+ void *arg1;
+ void (*func)(void *);
+ struct ieee80211_key *k;
+ struct ieee80211_key key;
+ uint8_t mac[IEEE80211_ADDR_LEN];
+ uint8_t wcid;
};
struct run_vap {
struct ieee80211vap vap;
struct ieee80211_beacon_offsets bo;
- struct usb_callout ratectl_ch;
- struct task ratectl_task;
- uint8_t ratectl_run;
-#define RUN_RATECTL_ON 1
-#define RUN_RATECTL_OFF 0
int (*newstate)(struct ieee80211vap *,
enum ieee80211_state, int);
+
+ uint8_t rvp_id;
};
#define RUN_VAP(vap) ((struct run_vap *)(vap))
@@ -148,7 +159,7 @@ struct run_softc {
device_t sc_dev;
struct usb_device *sc_udev;
struct ifnet *sc_ifp;
- struct run_vap *sc_rvp;
+ struct ieee80211_node *sc_ni[RT2870_WCID_MAX + 1];
int (*sc_srom_read)(struct run_softc *,
uint16_t, uint16_t *);
@@ -159,7 +170,6 @@ struct run_softc {
uint8_t freq;
uint8_t ntxchains;
uint8_t nrxchains;
- int fixed_ridx;
uint8_t bbp25;
uint8_t bbp26;
@@ -182,7 +192,7 @@ struct run_softc {
struct {
uint8_t reg;
uint8_t val;
- } bbp[8], rf[10];
+ } bbp[10], rf[10];
uint8_t leds;
uint16_t led[3];
uint32_t txpow20mhz[5];
@@ -195,13 +205,36 @@ struct run_softc {
struct run_endpoint_queue sc_epq[RUN_EP_QUEUES];
- struct task wme_task;
- struct task usb_timeout_task;
+ struct task ratectl_task;
+ struct usb_callout ratectl_ch;
+ uint8_t ratectl_run;
+#define RUN_RATECTL_OFF 0
+
+/* need to be power of 2, otherwise RUN_CMDQ_GET fails */
+#define RUN_CMDQ_MAX 16
+#define RUN_CMDQ_MASQ (RUN_CMDQ_MAX - 1)
+ struct run_cmdq cmdq[RUN_CMDQ_MAX];
+ struct task cmdq_task;
+ uint32_t cmdq_store;
+ uint8_t cmdq_exec;
+ uint8_t cmdq_run;
+#define RUN_CMDQ_ABORT 0
+#define RUN_CMDQ_GO 1
struct usb_xfer *sc_xfer[RUN_N_XFER];
struct mbuf *rx_m;
+ uint8_t fifo_cnt;
+
+ uint8_t running;
+ uint8_t runbmap;
+ uint8_t ap_running;
+ uint8_t adhoc_running;
+ uint8_t sta_running;
+ uint8_t rvp_cnt;
+ uint8_t rvp_bmap;
+
union {
struct run_rx_radiotap_header th;
uint8_t pad[64];
OpenPOWER on IntegriCloud