aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/usb/if_rum.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/if_rum.c')
-rw-r--r--sys/dev/usb/if_rum.c272
1 files changed, 199 insertions, 73 deletions
diff --git a/sys/dev/usb/if_rum.c b/sys/dev/usb/if_rum.c
index 2f22117d4e76..16ed1adbcced 100644
--- a/sys/dev/usb/if_rum.c
+++ b/sys/dev/usb/if_rum.c
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_amrr.h>
#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_regdomain.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
@@ -128,8 +129,8 @@ static void rum_free_tx_list(struct rum_softc *);
static int rum_alloc_rx_list(struct rum_softc *);
static void rum_free_rx_list(struct rum_softc *);
static int rum_media_change(struct ifnet *);
-static void rum_next_scan(void *);
static void rum_task(void *);
+static void rum_scantask(void *);
static int rum_newstate(struct ieee80211com *,
enum ieee80211_state, int);
static void rum_txeof(usbd_xfer_handle, usbd_private_handle,
@@ -187,11 +188,15 @@ static int rum_load_microcode(struct rum_softc *, const u_char *,
static int rum_prepare_beacon(struct rum_softc *);
static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
+static void rum_scan_start(struct ieee80211com *);
+static void rum_scan_end(struct ieee80211com *);
+static void rum_set_channel(struct ieee80211com *);
+static int rum_get_rssi(struct rum_softc *, uint8_t);
static void rum_amrr_start(struct rum_softc *,
struct ieee80211_node *);
static void rum_amrr_timeout(void *);
static void rum_amrr_update(usbd_xfer_handle, usbd_private_handle,
- usbd_status status);
+ usbd_status);
static const struct {
uint32_t reg;
@@ -374,7 +379,7 @@ USB_ATTACH(rum)
usb_interface_descriptor_t *id;
usb_endpoint_descriptor_t *ed;
usbd_status error;
- int i, ntries, size;
+ int i, ntries, size, bands;
uint32_t tmp;
sc->sc_udev = uaa->device;
@@ -426,9 +431,8 @@ USB_ATTACH(rum)
MTX_DEF | MTX_RECURSE);
usb_init_task(&sc->sc_task, rum_task, sc);
- callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0);
- callout_init(&sc->scan_ch, debug_mpsafenet ? CALLOUT_MPSAFE : 0);
-
+ usb_init_task(&sc->sc_scantask, rum_scantask, sc);
+ callout_init(&sc->watchdog_ch, 0);
callout_init(&sc->amrr_ch, 0);
/* retrieve RT2573 rev. no */
@@ -490,42 +494,49 @@ USB_ATTACH(rum)
IEEE80211_C_TXPMGT | /* tx power management */
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_BGSCAN | /* bg scanning supported */
IEEE80211_C_WPA; /* 802.11i */
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, 0, CTRY_DEFAULT, bands, 0, 1);
+
if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) {
+ struct ieee80211_channel *c;
+
/* set supported .11a channels */
for (i = 34; i <= 46; i += 4) {
- ic->ic_channels[i].ic_freq =
- ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+ c = &ic->ic_channels[ic->ic_nchans++];
+ c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+ c->ic_flags = IEEE80211_CHAN_A;
+ c->ic_ieee = i;
}
for (i = 36; i <= 64; i += 4) {
- ic->ic_channels[i].ic_freq =
- ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+ c = &ic->ic_channels[ic->ic_nchans++];
+ c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+ c->ic_flags = IEEE80211_CHAN_A;
+ c->ic_ieee = i;
}
for (i = 100; i <= 140; i += 4) {
- ic->ic_channels[i].ic_freq =
- ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+ c = &ic->ic_channels[ic->ic_nchans++];
+ c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+ c->ic_flags = IEEE80211_CHAN_A;
+ c->ic_ieee = i;
}
for (i = 149; i <= 165; i += 4) {
- ic->ic_channels[i].ic_freq =
- ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
- ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+ c = &ic->ic_channels[ic->ic_nchans++];
+ c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+ c->ic_flags = IEEE80211_CHAN_A;
+ c->ic_ieee = i;
}
}
- /* set supported .11b and .11g channels (1 through 14) */
- for (i = 1; i <= 14; i++) {
- ic->ic_channels[i].ic_freq =
- ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
- ic->ic_channels[i].ic_flags =
- IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
- IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
- }
-
ieee80211_ifattach(ic);
+ ic->ic_scan_start = rum_scan_start;
+ ic->ic_scan_end = rum_scan_end;
+ ic->ic_set_channel = rum_set_channel;
+
/* enable s/w bmiss handling in sta mode */
ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
@@ -535,7 +546,9 @@ USB_ATTACH(rum)
ic->ic_raw_xmit = rum_raw_xmit;
ieee80211_media_init(ic, rum_media_change, ieee80211_media_status);
- ieee80211_amrr_init(&sc->amrr, ic, 1, 10);
+ ieee80211_amrr_init(&sc->amrr, ic,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD);
bpfattach2(ifp, DLT_IEEE802_11_RADIO,
sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN,
@@ -563,8 +576,8 @@ USB_DETACH(rum)
rum_stop(sc);
usb_rem_task(sc->sc_udev, &sc->sc_task);
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
callout_stop(&sc->watchdog_ch);
- callout_stop(&sc->scan_ch);
callout_stop(&sc->amrr_ch);
if (sc->amrr_xfer != NULL) {
@@ -737,20 +750,6 @@ rum_media_change(struct ifnet *ifp)
return 0;
}
-/*
- * This function is called periodically (every 200ms) during scanning to
- * switch from one channel to another.
- */
-static void
-rum_next_scan(void *arg)
-{
- struct rum_softc *sc = arg;
- struct ieee80211com *ic = &sc->sc_ic;
-
- if (ic->ic_state == IEEE80211_S_SCAN)
- ieee80211_next_scan(ic);
-}
-
static void
rum_task(void *arg)
{
@@ -773,22 +772,7 @@ rum_task(void *arg)
}
break;
- case IEEE80211_S_SCAN:
- rum_set_chan(sc, ic->ic_curchan);
- callout_reset(&sc->scan_ch, hz / 5, rum_next_scan, sc);
- break;
-
- case IEEE80211_S_AUTH:
- rum_set_chan(sc, ic->ic_curchan);
- break;
-
- case IEEE80211_S_ASSOC:
- rum_set_chan(sc, ic->ic_curchan);
- break;
-
case IEEE80211_S_RUN:
- rum_set_chan(sc, ic->ic_curchan);
-
ni = ic->ic_bss;
if (ic->ic_opmode != IEEE80211_M_MONITOR) {
@@ -811,6 +795,8 @@ rum_task(void *arg)
ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
rum_amrr_start(sc, ni);
break;
+ default:
+ break;
}
RUM_UNLOCK(sc);
@@ -823,7 +809,6 @@ rum_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
struct rum_softc *sc = ic->ic_ifp->if_softc;
- callout_stop(&sc->scan_ch);
callout_stop(&sc->amrr_ch);
/* do it in a process context */
@@ -851,6 +836,10 @@ rum_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
struct rum_softc *sc = data->sc;
struct ifnet *ifp = sc->sc_ic.ic_ifp;
+ if (data->m->m_flags & M_TXCB)
+ ieee80211_process_callback(data->ni, data->m,
+ status == USBD_NORMAL_COMPLETION ? 0 : ETIMEDOUT);
+
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
return;
@@ -891,7 +880,7 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
struct ieee80211_frame *wh;
struct ieee80211_node *ni;
struct mbuf *mnew, *m;
- int len;
+ int len, rssi;
if (status != USBD_NORMAL_COMPLETION) {
if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
@@ -938,6 +927,15 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
m->m_data = (caddr_t)(desc + 1);
m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff;
+ rssi = rum_get_rssi(sc, desc->rssi);
+
+ wh = mtod(m, struct ieee80211_frame *);
+ ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
+
+ /* Error happened during RSSI conversion. */
+ if (rssi < 0)
+ rssi = ni->ni_rssi;
+
if (bpf_peers_present(sc->sc_drvbpf)) {
struct rum_rx_radiotap_header *tap = &sc->sc_rxtap;
@@ -946,16 +944,13 @@ rum_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
tap->wr_antenna = sc->rx_ant;
- tap->wr_antsignal = desc->rssi;
+ tap->wr_antsignal = rssi;
bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m);
}
- wh = mtod(m, struct ieee80211_frame *);
- ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
-
/* send the frame to the 802.11 layer */
- ieee80211_input(ic, m, ni, desc->rssi, 0);
+ ieee80211_input(ic, m, ni, rssi, RT2573_NOISE_FLOOR, 0);
/* node is no longer needed */
ieee80211_free_node(ni);
@@ -1293,7 +1288,7 @@ rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
wh = mtod(m0, struct ieee80211_frame *);
if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE)
- rate = ic->ic_bss->ni_rates.rs_rates[ic->ic_fixed_rate];
+ rate = ic->ic_fixed_rate;
else
rate = ni->ni_rates.rs_rates[ni->ni_txrate];
@@ -1378,8 +1373,6 @@ rum_start(struct ifnet *ifp)
struct mbuf *m0;
struct ether_header *eh;
- RUM_LOCK(sc);
-
for (;;) {
IF_POLL(&ic->ic_mgtq, m0);
if (m0 != NULL) {
@@ -1442,27 +1435,27 @@ rum_start(struct ifnet *ifp)
sc->sc_tx_timer = 5;
callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc);
}
-
- RUM_UNLOCK(sc);
}
static void
rum_watchdog(void *arg)
{
- struct rum_softc *sc = (struct rum_softc *)arg;
- struct ieee80211com *ic = &sc->sc_ic;
+ struct rum_softc *sc = arg;
+
+ RUM_LOCK(sc);
if (sc->sc_tx_timer > 0) {
if (--sc->sc_tx_timer == 0) {
device_printf(sc->sc_dev, "device timeout\n");
/*rum_init(ifp); XXX needs a process context! */
sc->sc_ifp->if_oerrors++;
+ RUM_UNLOCK(sc);
return;
}
callout_reset(&sc->watchdog_ch, hz, rum_watchdog, sc);
}
- ieee80211_watchdog(ic);
+ RUM_UNLOCK(sc);
}
static int
@@ -1984,11 +1977,24 @@ rum_read_eeprom(struct rum_softc *sc)
if ((val & 0xff) != 0xff)
sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10)
+ sc->rssi_2ghz_corr = 0;
+
rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2);
val = le16toh(val);
if ((val & 0xff) != 0xff)
sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10)
+ sc->rssi_5ghz_corr = 0;
+
+ if (sc->ext_2ghz_lna)
+ sc->rssi_2ghz_corr -= 14;
+ if (sc->ext_5ghz_lna)
+ sc->rssi_5ghz_corr -= 14;
+
DPRINTF(("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
sc->rssi_2ghz_corr, sc->rssi_5ghz_corr));
@@ -2407,4 +2413,124 @@ rum_amrr_update(usbd_xfer_handle xfer, usbd_private_handle priv,
callout_reset(&sc->amrr_ch, hz, rum_amrr_timeout, sc);
}
+static void
+rum_scan_start(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SCAN_START;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+rum_scan_end(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SCAN_END;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+rum_set_channel(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_scantask);
+
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SET_CHANNEL;
+ usb_add_task(sc->sc_udev, &sc->sc_scantask, USB_TASKQ_DRIVER);
+}
+
+static void
+rum_scantask(void *arg)
+{
+ struct rum_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ uint32_t tmp;
+
+ RUM_LOCK(sc);
+
+ switch (sc->sc_scan_action) {
+ case RUM_SCAN_START:
+ /* abort TSF synchronization */
+ tmp = rum_read(sc, RT2573_TXRX_CSR9);
+ rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
+ rum_set_bssid(sc, ifp->if_broadcastaddr);
+ break;
+
+ case RUM_SCAN_END:
+ rum_enable_tsf_sync(sc);
+ /* XXX keep local copy */
+ rum_set_bssid(sc, ic->ic_bss->ni_bssid);
+ break;
+
+ case RUM_SET_CHANNEL:
+ mtx_lock(&Giant);
+ rum_set_chan(sc, ic->ic_curchan);
+ mtx_unlock(&Giant);
+ break;
+
+ default:
+ panic("unknown scan action %d\n", sc->sc_scan_action);
+ /* NEVER REACHED */
+ break;
+ }
+
+ RUM_UNLOCK(sc);
+}
+
+static int
+rum_get_rssi(struct rum_softc *sc, uint8_t raw)
+{
+ int lna, agc, rssi;
+
+ lna = (raw >> 5) & 0x3;
+ agc = raw & 0x1f;
+
+ if (lna == 0) {
+ /*
+ * No RSSI mapping
+ *
+ * NB: Since RSSI is relative to noise floor, -1 is
+ * adequate for caller to know error happened.
+ */
+ return -1;
+ }
+
+ rssi = (2 * agc) - RT2573_NOISE_FLOOR;
+
+ if (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan)) {
+ rssi += sc->rssi_2ghz_corr;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 74;
+ else if (lna == 3)
+ rssi -= 90;
+ } else {
+ rssi += sc->rssi_5ghz_corr;
+
+ if (!sc->ext_5ghz_lna && lna != 1)
+ rssi += 4;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 86;
+ else if (lna == 3)
+ rssi -= 100;
+ }
+ return rssi;
+}
+
DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, usbd_driver_load, 0);