[otus] enable 802.11n for 2GHz and 5GHz.
This flips on basic 11n for 2GHz/5GHz station operation. * It flips on HT20 and MCS rates; * It enables A-MPDU decap - the payload format is a bit different; * It does do some basic checks for HT40 but I haven't yet flipped on HT40 support; * It enables software A-MSDU transmit; I honestly don't want to make A-MPDU TX work and there are apparently issues with QoS and A-MPDU TX. So I totally am ignoring A-MPDU TX; * MCS rate transmit is fine. I haven't: * A-MPDU TX, as I said above; * made radiotap work fully; * HT40; * short-GI support; * lots of other stuff that honestly no-one is likely to use. But! Hey, this is another ye olde 11n USB NIC that now works pretty OK in 11n rates. A-MPDU receive seems fine enough given it's a draft-n device from before 2010. Tested: * Ye olde UB82 Test NIC (AR9170 + AR9104) - 2GHz/5GHz
This commit is contained in:
parent
ae84ff9c47
commit
53652fb94e
@ -91,6 +91,7 @@ SYSCTL_INT(_hw_usb_otus, OID_AUTO, debug, CTLFLAG_RWTUN, &otus_debug, 0,
|
||||
#define OTUS_DEBUG_REGIO 0x00000200
|
||||
#define OTUS_DEBUG_IRQ 0x00000400
|
||||
#define OTUS_DEBUG_TXCOMP 0x00000800
|
||||
#define OTUS_DEBUG_RX_BUFFER 0x00001000
|
||||
#define OTUS_DEBUG_ANY 0xffffffff
|
||||
|
||||
#define OTUS_DPRINTF(sc, dm, ...) \
|
||||
@ -131,7 +132,6 @@ static device_attach_t otus_attach;
|
||||
static device_detach_t otus_detach;
|
||||
|
||||
static int otus_attachhook(struct otus_softc *);
|
||||
void otus_get_chanlist(struct otus_softc *);
|
||||
static void otus_getradiocaps(struct ieee80211com *, int, int *,
|
||||
struct ieee80211_channel[]);
|
||||
int otus_load_firmware(struct otus_softc *, const char *,
|
||||
@ -395,9 +395,8 @@ otus_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
|
||||
uvp->newstate = vap->iv_newstate;
|
||||
vap->iv_newstate = otus_newstate;
|
||||
|
||||
/* XXX TODO: double-check */
|
||||
vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16;
|
||||
vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K;
|
||||
vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8;
|
||||
vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
|
||||
|
||||
ieee80211_ratectl_init(vap);
|
||||
|
||||
@ -699,6 +698,16 @@ otus_attachhook(struct otus_softc *sc)
|
||||
IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->eeprom.baseEepHeader.macAddr);
|
||||
sc->sc_led_newstate = otus_led_newstate_type3; /* XXX */
|
||||
|
||||
if (sc->txmask == 0x5)
|
||||
ic->ic_txstream = 2;
|
||||
else
|
||||
ic->ic_txstream = 1;
|
||||
|
||||
if (sc->rxmask == 0x5)
|
||||
ic->ic_rxstream = 2;
|
||||
else
|
||||
ic->ic_rxstream = 1;
|
||||
|
||||
device_printf(sc->sc_dev,
|
||||
"MAC/BBP AR9170, RF AR%X, MIMO %dT%dR, address %s\n",
|
||||
(sc->capflags & AR5416_OPFLAGS_11A) ?
|
||||
@ -721,33 +730,21 @@ otus_attachhook(struct otus_softc *sc)
|
||||
IEEE80211_C_WME | /* WME/QoS */
|
||||
IEEE80211_C_SHSLOT | /* Short slot time supported. */
|
||||
IEEE80211_C_FF | /* Atheros fast-frames supported. */
|
||||
IEEE80211_C_MONITOR |
|
||||
IEEE80211_C_MONITOR | /* Enable monitor mode */
|
||||
IEEE80211_C_SWAMSDUTX | /* Do software A-MSDU TX */
|
||||
IEEE80211_C_WPA; /* WPA/RSN. */
|
||||
|
||||
/* XXX TODO: 11n */
|
||||
|
||||
ic->ic_htcaps =
|
||||
IEEE80211_HTC_HT |
|
||||
#if 0
|
||||
if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
|
||||
/* Set supported .11b and .11g rates. */
|
||||
ic->ic_sup_rates[IEEE80211_MODE_11B] =
|
||||
ieee80211_std_rateset_11b;
|
||||
ic->ic_sup_rates[IEEE80211_MODE_11G] =
|
||||
ieee80211_std_rateset_11g;
|
||||
}
|
||||
if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
|
||||
/* Set supported .11a rates. */
|
||||
ic->ic_sup_rates[IEEE80211_MODE_11A] =
|
||||
ieee80211_std_rateset_11a;
|
||||
}
|
||||
IEEE80211_HTC_AMPDU |
|
||||
#endif
|
||||
IEEE80211_HTC_AMSDU |
|
||||
IEEE80211_HTCAP_MAXAMSDU_3839 |
|
||||
IEEE80211_HTCAP_SMPS_OFF;
|
||||
|
||||
#if 0
|
||||
/* Build the list of supported channels. */
|
||||
otus_get_chanlist(sc);
|
||||
#else
|
||||
otus_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
|
||||
ic->ic_channels);
|
||||
#endif
|
||||
|
||||
ieee80211_ifattach(ic);
|
||||
ic->ic_raw_xmit = otus_raw_xmit;
|
||||
@ -780,38 +777,6 @@ otus_attachhook(struct otus_softc *sc)
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
otus_get_chanlist(struct otus_softc *sc)
|
||||
{
|
||||
struct ieee80211com *ic = &sc->sc_ic;
|
||||
uint16_t domain;
|
||||
uint8_t chan;
|
||||
int i;
|
||||
|
||||
/* XXX regulatory domain. */
|
||||
domain = le16toh(sc->eeprom.baseEepHeader.regDmn[0]);
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "regdomain=0x%04x\n", domain);
|
||||
|
||||
if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
|
||||
for (i = 0; i < 14; i++) {
|
||||
chan = ar_chans[i];
|
||||
ic->ic_channels[chan].ic_freq =
|
||||
ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
|
||||
ic->ic_channels[chan].ic_flags =
|
||||
IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
|
||||
IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
|
||||
}
|
||||
}
|
||||
if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
|
||||
for (i = 14; i < nitems(ar_chans); i++) {
|
||||
chan = ar_chans[i];
|
||||
ic->ic_channels[chan].ic_freq =
|
||||
ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
|
||||
ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
otus_getradiocaps(struct ieee80211com *ic,
|
||||
int maxchans, int *nchans, struct ieee80211_channel chans[])
|
||||
@ -824,15 +789,13 @@ otus_getradiocaps(struct ieee80211com *ic,
|
||||
if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
|
||||
setbit(bands, IEEE80211_MODE_11B);
|
||||
setbit(bands, IEEE80211_MODE_11G);
|
||||
#if 0
|
||||
if (sc->sc_ht)
|
||||
setbit(bands, IEEE80211_MODE_11NG);
|
||||
#endif
|
||||
ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
|
||||
ar_chans, 14, bands, 0);
|
||||
}
|
||||
if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
|
||||
setbit(bands, IEEE80211_MODE_11A);
|
||||
setbit(bands, IEEE80211_MODE_11NA);
|
||||
ieee80211_add_channel_list_5ghz(chans, maxchans, nchans,
|
||||
&ar_chans[14], nitems(ar_chans) - 14, bands, 0);
|
||||
}
|
||||
@ -1588,6 +1551,12 @@ otus_cmd_rxeof(struct otus_softc *sc, uint8_t *buf, int len)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a single MPDU.
|
||||
*
|
||||
* This may be a single MPDU, or it may be a sub-frame from an A-MPDU.
|
||||
* In the latter case some of the header details need to be adjusted.
|
||||
*/
|
||||
void
|
||||
otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len, struct mbufq *rxq)
|
||||
{
|
||||
@ -1596,41 +1565,126 @@ otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len, struct mbufq *rxq)
|
||||
#if 0
|
||||
struct ieee80211_node *ni;
|
||||
#endif
|
||||
struct ar_rx_tail *tail;
|
||||
struct ar_rx_macstatus *mac_status = NULL;
|
||||
struct ar_rx_phystatus *phy_status = NULL;
|
||||
struct ieee80211_frame *wh;
|
||||
struct mbuf *m;
|
||||
uint8_t *plcp;
|
||||
// int s;
|
||||
int mlen;
|
||||
|
||||
if (__predict_false(len < AR_PLCP_HDR_LEN)) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"sub-xfer too short %d\n", len);
|
||||
return;
|
||||
|
||||
if (otus_debug & OTUS_DEBUG_RX_BUFFER) {
|
||||
device_printf(sc->sc_dev, "%s: %*D\n",
|
||||
__func__, len, buf, "-");
|
||||
}
|
||||
plcp = buf;
|
||||
|
||||
/* All bits in the PLCP header are set to 1 for non-MPDU. */
|
||||
if (memcmp(plcp, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) {
|
||||
otus_cmd_rxeof(sc, plcp + AR_PLCP_HDR_LEN,
|
||||
/*
|
||||
* Before any data path stuff - check to see if this is a command
|
||||
* response.
|
||||
*
|
||||
* All bits in the PLCP header are set to 1 for non-MPDU.
|
||||
*/
|
||||
if ((len >= AR_PLCP_HDR_LEN) &&
|
||||
memcmp(buf, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) {
|
||||
otus_cmd_rxeof(sc, buf + AR_PLCP_HDR_LEN,
|
||||
len - AR_PLCP_HDR_LEN);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Received MPDU. */
|
||||
if (__predict_false(len < AR_PLCP_HDR_LEN + sizeof (*tail))) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "MPDU too short %d\n", len);
|
||||
/*
|
||||
* First step - get the status for the given frame.
|
||||
* This will tell us whether it's a single MPDU or
|
||||
* an A-MPDU subframe.
|
||||
*/
|
||||
if (len < sizeof(*mac_status)) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"%s: sub-xfer too short (no mac_status) (len %d)\n",
|
||||
__func__, len);
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
return;
|
||||
}
|
||||
tail = (struct ar_rx_tail *)(plcp + len - sizeof (*tail));
|
||||
/*
|
||||
* Remove the mac_status from the payload length.
|
||||
*
|
||||
* Note: cheating, don't reallocate the buffer!
|
||||
*/
|
||||
mac_status = (struct ar_rx_macstatus *)(buf + len - sizeof(*mac_status));
|
||||
len -= sizeof(*mac_status);
|
||||
|
||||
/* Discard error frames; don't discard BAD_RA (eg monitor mode); let net80211 do that */
|
||||
if (__predict_false((tail->error & ~AR_RX_ERROR_BAD_RA) != 0)) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "error frame 0x%02x\n", tail->error);
|
||||
if (tail->error & AR_RX_ERROR_FCS) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: mac status=0x%x\n",
|
||||
__func__, mac_status->status);
|
||||
|
||||
/*
|
||||
* Next - check the MAC status before doing anything else.
|
||||
* Extract out the PLCP header for single and first frames;
|
||||
* since there's a single RX path we can shove PLCP headers
|
||||
* from both into sc->ar_last_rx_plcp[] so it can be reused.
|
||||
*/
|
||||
if (((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_SINGLE) ||
|
||||
((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_FIRST)) {
|
||||
/*
|
||||
* Ok, we need to at least have a PLCP header at
|
||||
* this point.
|
||||
*/
|
||||
if (len < AR_PLCP_HDR_LEN) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"%s sub-xfer too short (no mac+plcp) (len %d\n)",
|
||||
__func__, len);
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
return;
|
||||
}
|
||||
memcpy(sc->ar_last_rx_plcp, buf, AR_PLCP_HDR_LEN);
|
||||
|
||||
/*
|
||||
* At this point we can just consume the PLCP header.
|
||||
* The beginning of the frame should thus be data.
|
||||
*/
|
||||
buf += AR_PLCP_HDR_LEN;
|
||||
len -= AR_PLCP_HDR_LEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Next - see if we have a PHY status.
|
||||
*
|
||||
* The PHY status is at the end of the final A-MPDU subframe
|
||||
* or a single MPDU frame.
|
||||
*
|
||||
* We'll use this to tag frames with noise floor / RSSI
|
||||
* if they have valid information.
|
||||
*/
|
||||
if (((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_SINGLE) ||
|
||||
((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_LAST)) {
|
||||
if (len < sizeof(*phy_status)) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"%s sub-xfer too short (no phy status) (len %d\n)",
|
||||
__func__, len);
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Take a pointer to the phy status and remove the length
|
||||
* from the end of the buffer.
|
||||
*
|
||||
* Note: we're cheating here; don't reallocate the buffer!
|
||||
*/
|
||||
phy_status = (struct ar_rx_phystatus *)
|
||||
(buf + len - sizeof(*phy_status));
|
||||
len -= sizeof(*phy_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Middle frames just have a MAC status (stripped above.)
|
||||
* No PHY status, and PLCP is from ar_last_rx_plcp.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Discard error frames; don't discard BAD_RA (eg monitor mode);
|
||||
* let net80211 do that
|
||||
*/
|
||||
if (__predict_false((mac_status->error & ~AR_RX_ERROR_BAD_RA) != 0)) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "error frame 0x%02x\n", mac_status->error);
|
||||
if (mac_status->error & AR_RX_ERROR_FCS) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "bad FCS\n");
|
||||
} else if (tail->error & AR_RX_ERROR_MMIC) {
|
||||
} else if (mac_status->error & AR_RX_ERROR_MMIC) {
|
||||
/* Report Michael MIC failures to net80211. */
|
||||
#if 0
|
||||
ieee80211_notify_michael_failure(ni->ni_vap, wh, keyidx);
|
||||
@ -1640,77 +1694,75 @@ otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len, struct mbufq *rxq)
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
return;
|
||||
}
|
||||
/* Compute MPDU's length. */
|
||||
mlen = len - AR_PLCP_HDR_LEN - sizeof (*tail);
|
||||
/* Make sure there's room for an 802.11 header + FCS. */
|
||||
if (__predict_false(mlen < IEEE80211_MIN_LEN)) {
|
||||
|
||||
/*
|
||||
* Make sure there's room for an 802.11 header + FCS.
|
||||
*
|
||||
* Note: a CTS/ACK is 14 bytes (FC, DUR, RA, FCS).
|
||||
* Making it IEEE80211_MIN_LEN misses CTS/ACKs.
|
||||
*
|
||||
* This won't be tossed at this point; eventually once
|
||||
* rx radiotap is implemented this will allow for
|
||||
* CTS/ACK frames. Passing them up to net80211 will
|
||||
* currently make it angry (too short packets.)
|
||||
*/
|
||||
if (len < 2 + 2 + IEEE80211_ADDR_LEN + IEEE80211_CRC_LEN) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"%s: too short for 802.11 (len %d)\n",
|
||||
__func__, len);
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
return;
|
||||
}
|
||||
mlen -= IEEE80211_CRC_LEN; /* strip 802.11 FCS */
|
||||
|
||||
wh = (struct ieee80211_frame *)(plcp + AR_PLCP_HDR_LEN);
|
||||
len -= IEEE80211_CRC_LEN; /* strip 802.11 FCS */
|
||||
wh = (struct ieee80211_frame *) buf;
|
||||
|
||||
/*
|
||||
* TODO: I see > 2KiB buffers in this path; is it A-MSDU or something?
|
||||
* The firmware does seem to spit out a bunch of frames
|
||||
* with invalid frame control values here. Just toss them
|
||||
* rather than letting net80211 get angry and log.
|
||||
*/
|
||||
m = m_get2(mlen, M_NOWAIT, MT_DATA, M_PKTHDR);
|
||||
if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
|
||||
IEEE80211_FC0_VERSION_0) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"%s: invalid 802.11 fc version (firmware bug?)\n",
|
||||
__func__);
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
m = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR);
|
||||
if (m == NULL) {
|
||||
device_printf(sc->sc_dev, "%s: failed m_get2() (mlen=%d)\n", __func__, mlen);
|
||||
device_printf(sc->sc_dev, "%s: failed m_get2() (len=%d)\n",
|
||||
__func__, len);
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Finalize mbuf. */
|
||||
memcpy(mtod(m, uint8_t *), wh, mlen);
|
||||
m->m_pkthdr.len = m->m_len = mlen;
|
||||
memcpy(mtod(m, uint8_t *), wh, len);
|
||||
m->m_pkthdr.len = m->m_len = len;
|
||||
|
||||
#if 0
|
||||
if (__predict_false(sc->sc_drvbpf != NULL)) {
|
||||
struct otus_rx_radiotap_header *tap = &sc->sc_rxtap;
|
||||
struct mbuf mb;
|
||||
/* XXX TODO: add setting rx radiotap fields here */
|
||||
|
||||
tap->wr_flags = 0;
|
||||
tap->wr_antsignal = tail->rssi;
|
||||
tap->wr_rate = 2; /* In case it can't be found below. */
|
||||
switch (tail->status & AR_RX_STATUS_MT_MASK) {
|
||||
case AR_RX_STATUS_MT_CCK:
|
||||
switch (plcp[0]) {
|
||||
case 10: tap->wr_rate = 2; break;
|
||||
case 20: tap->wr_rate = 4; break;
|
||||
case 55: tap->wr_rate = 11; break;
|
||||
case 110: tap->wr_rate = 22; break;
|
||||
/*
|
||||
* Ok, check the frame length and toss if it's too short
|
||||
* for net80211. This will toss ACK/CTS.
|
||||
*/
|
||||
if (m->m_len < IEEE80211_MIN_LEN) {
|
||||
/* XXX TODO: add radiotap receive here */
|
||||
m_free(m); m = NULL;
|
||||
return;
|
||||
}
|
||||
if (tail->status & AR_RX_STATUS_SHPREAMBLE)
|
||||
tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
|
||||
break;
|
||||
case AR_RX_STATUS_MT_OFDM:
|
||||
switch (plcp[0] & 0xf) {
|
||||
case 0xb: tap->wr_rate = 12; break;
|
||||
case 0xf: tap->wr_rate = 18; break;
|
||||
case 0xa: tap->wr_rate = 24; break;
|
||||
case 0xe: tap->wr_rate = 36; break;
|
||||
case 0x9: tap->wr_rate = 48; break;
|
||||
case 0xd: tap->wr_rate = 72; break;
|
||||
case 0x8: tap->wr_rate = 96; break;
|
||||
case 0xc: tap->wr_rate = 108; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
mb.m_data = (caddr_t)tap;
|
||||
mb.m_next = m;
|
||||
mb.m_nextpkt = NULL;
|
||||
mb.m_type = 0;
|
||||
mb.m_flags = 0;
|
||||
bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Add RSSI/NF to this mbuf */
|
||||
/* Add RSSI to this mbuf if we have a PHY header */
|
||||
bzero(&rxs, sizeof(rxs));
|
||||
rxs.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
|
||||
rxs.r_flags = IEEE80211_R_NF;
|
||||
rxs.c_nf = sc->sc_nf[0]; /* XXX chain 0 != combined rssi/nf */
|
||||
rxs.c_rssi = tail->rssi;
|
||||
if (phy_status != NULL) {
|
||||
rxs.r_flags |= IEEE80211_R_RSSI;
|
||||
rxs.c_rssi = phy_status->rssi;
|
||||
}
|
||||
/* XXX TODO: add MIMO RSSI/NF as well */
|
||||
if (ieee80211_add_rx_params(m, &rxs) == 0) {
|
||||
counter_u64_add(ic->ic_ierrors, 1);
|
||||
@ -1741,10 +1793,18 @@ otus_rxeof(struct usb_xfer *xfer, struct otus_data *data, struct mbufq *rxq)
|
||||
caddr_t buf = data->buf;
|
||||
struct ar_rx_head *head;
|
||||
uint16_t hlen;
|
||||
int len;
|
||||
int len, offset = 0;
|
||||
|
||||
usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
|
||||
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"%s: transfer completed; len=%d\n",
|
||||
__func__, len);
|
||||
if (otus_debug & OTUS_DEBUG_RX_BUFFER) {
|
||||
device_printf(sc->sc_dev, "%s: %*D\n",
|
||||
__func__, len, buf, "-");
|
||||
}
|
||||
|
||||
while (len >= sizeof (*head)) {
|
||||
head = (struct ar_rx_head *)buf;
|
||||
if (__predict_false(head->tag != htole16(AR_RX_HEAD_TAG))) {
|
||||
@ -1753,19 +1813,26 @@ otus_rxeof(struct usb_xfer *xfer, struct otus_data *data, struct mbufq *rxq)
|
||||
break;
|
||||
}
|
||||
hlen = le16toh(head->len);
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: hlen=%d\n",
|
||||
__func__, hlen);
|
||||
if (__predict_false(sizeof (*head) + hlen > len)) {
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"xfer too short %d/%d\n", len, hlen);
|
||||
break;
|
||||
}
|
||||
/* Process sub-xfer. */
|
||||
otus_sub_rxeof(sc, (uint8_t *)&head[1], hlen, rxq);
|
||||
otus_sub_rxeof(sc, (uint8_t *) (((uint8_t *) buf) + 4), hlen, rxq);
|
||||
|
||||
/* Next sub-xfer is aligned on a 32-bit boundary. */
|
||||
hlen = (sizeof (*head) + hlen + 3) & ~3;
|
||||
offset += hlen;
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
|
||||
"%s: rounded size is %d, next packet starts at %d\n",
|
||||
__func__, hlen, offset);
|
||||
buf += hlen;
|
||||
len -= hlen;
|
||||
}
|
||||
OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: done!\n", __func__);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2094,6 +2161,11 @@ otus_rate_to_hw_rate(struct otus_softc *sc, uint8_t rate)
|
||||
|
||||
is_2ghz = !! (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan));
|
||||
|
||||
/* MCS check */
|
||||
if (rate & 0x80) {
|
||||
return rate;
|
||||
}
|
||||
|
||||
switch (rate) {
|
||||
/* CCK */
|
||||
case 2:
|
||||
@ -2129,11 +2201,16 @@ otus_rate_to_hw_rate(struct otus_softc *sc, uint8_t rate)
|
||||
return (0x0); /* 1MB CCK */
|
||||
else
|
||||
return (0xb); /* 6MB OFDM */
|
||||
|
||||
/* XXX TODO: HT */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
otus_hw_rate_is_ht(struct otus_softc *sc, uint8_t hw_rate)
|
||||
{
|
||||
|
||||
return !! (hw_rate & 0x80);
|
||||
}
|
||||
|
||||
static int
|
||||
otus_hw_rate_is_ofdm(struct otus_softc *sc, uint8_t hw_rate)
|
||||
{
|
||||
@ -2262,7 +2339,10 @@ otus_tx(struct otus_softc *sc, struct ieee80211_node *ni, struct mbuf *m,
|
||||
if (!ismcast) {
|
||||
if (m->m_pkthdr.len + IEEE80211_CRC_LEN >= vap->iv_rtsthreshold)
|
||||
macctl |= AR_TX_MAC_RTS;
|
||||
else if (ic->ic_flags & IEEE80211_F_USEPROT) {
|
||||
else if (otus_hw_rate_is_ht(sc, rate)) {
|
||||
if (ic->ic_htprotmode == IEEE80211_PROT_RTSCTS)
|
||||
macctl |= AR_TX_MAC_RTS;
|
||||
} else if (ic->ic_flags & IEEE80211_F_USEPROT) {
|
||||
if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
|
||||
macctl |= AR_TX_MAC_CTS;
|
||||
else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
|
||||
@ -2270,8 +2350,15 @@ otus_tx(struct otus_softc *sc, struct ieee80211_node *ni, struct mbuf *m,
|
||||
}
|
||||
}
|
||||
|
||||
phyctl |= AR_TX_PHY_MCS(rate);
|
||||
if (otus_hw_rate_is_ofdm(sc, rate)) {
|
||||
phyctl |= AR_TX_PHY_MCS(rate & 0x7f); /* Note: MCS rates are 0x80 and above */
|
||||
if (otus_hw_rate_is_ht(sc, rate)) {
|
||||
phyctl |= AR_TX_PHY_MT_HT;
|
||||
/* Always use all tx antennas for now, just to be safe */
|
||||
phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
|
||||
|
||||
/* Heavy clip */
|
||||
phyctl |= (rate & 0x7) << AR_TX_PHY_TX_HEAVY_CLIP_SHIFT;
|
||||
} else if (otus_hw_rate_is_ofdm(sc, rate)) {
|
||||
phyctl |= AR_TX_PHY_MT_OFDM;
|
||||
/* Always use all tx antennas for now, just to be safe */
|
||||
phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
|
||||
@ -2287,7 +2374,6 @@ otus_tx(struct otus_softc *sc, struct ieee80211_node *ni, struct mbuf *m,
|
||||
if (!(macctl & AR_TX_MAC_NOACK))
|
||||
OTUS_NODE(ni)->tx_done++;
|
||||
|
||||
|
||||
/* Fill Tx descriptor. */
|
||||
head = (struct ar_tx_head *)data->buf;
|
||||
head->len = htole16(m->m_pkthdr.len + IEEE80211_CRC_LEN);
|
||||
@ -2463,6 +2549,22 @@ otus_updateslot(struct otus_softc *sc)
|
||||
(void)otus_write_barrier(sc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Things to do based on 2GHz or 5GHz:
|
||||
*
|
||||
* + slottime
|
||||
* + dyn_sifs_ack
|
||||
* + rts_cts_rate
|
||||
* + slot time
|
||||
* + mac_rates
|
||||
* + mac_tpc
|
||||
*
|
||||
* And in the transmit path
|
||||
* + tpc: carl9170_tx_rate_tpc_chains
|
||||
* + carl9170_tx_physet()
|
||||
* + disable short premable tx
|
||||
*/
|
||||
|
||||
int
|
||||
otus_init_mac(struct otus_softc *sc)
|
||||
{
|
||||
@ -2641,10 +2743,17 @@ otus_program_phy(struct otus_softc *sc, struct ieee80211_channel *c)
|
||||
int error, i;
|
||||
|
||||
/* Select PHY programming based on band and bandwidth. */
|
||||
if (IEEE80211_IS_CHAN_2GHZ(c))
|
||||
if (IEEE80211_IS_CHAN_2GHZ(c)) {
|
||||
if (IEEE80211_IS_CHAN_HT40(c))
|
||||
vals = ar5416_phy_vals_2ghz_40mhz;
|
||||
else
|
||||
vals = ar5416_phy_vals_2ghz_20mhz;
|
||||
} else {
|
||||
if (IEEE80211_IS_CHAN_HT40(c))
|
||||
vals = ar5416_phy_vals_5ghz_40mhz;
|
||||
else
|
||||
vals = ar5416_phy_vals_5ghz_20mhz;
|
||||
}
|
||||
for (i = 0; i < nitems(ar5416_phy_regs); i++)
|
||||
otus_write(sc, AR_PHY(ar5416_phy_regs[i]), vals[i]);
|
||||
sc->phy_vals = vals;
|
||||
|
@ -201,12 +201,17 @@ struct ar_tx_head {
|
||||
|
||||
uint32_t phyctl;
|
||||
/* Modulation type. */
|
||||
#define AR_TX_PHY_MT_SHIFT 0 /* 0:1 - PHY mode */
|
||||
#define AR_TX_PHY_MT_CCK 0
|
||||
#define AR_TX_PHY_MT_OFDM 1
|
||||
#define AR_TX_PHY_MT_HT 2
|
||||
#define AR_TX_PHY_GF (1 << 2)
|
||||
#define AR_TX_PHY_BW_SHIFT 3
|
||||
#define AR_TX_PHY_TPC_SHIFT 9
|
||||
#define AR_TX_PHY_GF (1 << 2) /* 2 - greenfield */
|
||||
#define AR_TX_PHY_BW_SHIFT 3 /* 4:3 - bandwidth */
|
||||
#define AR_TX_PHY_BW_20MHZ 0
|
||||
#define AR_TX_PHY_BW_40MHZ 2
|
||||
#define AR_TX_PHY_BW_40MHZ_DUP 3
|
||||
#define AR_TX_PHY_TX_HEAVY_CLIP_SHIFT 6 /* 9:6 - heavy clip */
|
||||
#define AR_TX_PHY_TPC_SHIFT 9 /* 14:9 - TX power */
|
||||
#define AR_TX_PHY_ANTMSK(msk) ((msk) << 15)
|
||||
#define AR_TX_PHY_MCS(mcs) ((mcs) << 18)
|
||||
#define AR_TX_PHY_SHGI (1U << 31)
|
||||
@ -220,12 +225,8 @@ struct ar_rx_head {
|
||||
} __packed;
|
||||
|
||||
/* Rx descriptor. */
|
||||
struct ar_rx_tail {
|
||||
uint8_t rssi_ant[3];
|
||||
uint8_t rssi_ant_ext[3];
|
||||
uint8_t rssi; /* Combined RSSI. */
|
||||
uint8_t evm[2][6]; /* Error Vector Magnitude. */
|
||||
uint8_t phy_err;
|
||||
|
||||
struct ar_rx_macstatus {
|
||||
uint8_t sa_idx;
|
||||
uint8_t da_idx;
|
||||
uint8_t error;
|
||||
@ -236,7 +237,6 @@ struct ar_rx_tail {
|
||||
#define AR_RX_ERROR_BAD_RA (1 << 4)
|
||||
#define AR_RX_ERROR_PLCP (1 << 5)
|
||||
#define AR_RX_ERROR_MMIC (1 << 6)
|
||||
|
||||
uint8_t status;
|
||||
/* Modulation type (same as AR_TX_PHY_MT). */
|
||||
#define AR_RX_STATUS_MT_MASK 0x3
|
||||
@ -244,6 +244,19 @@ struct ar_rx_tail {
|
||||
#define AR_RX_STATUS_MT_OFDM 1
|
||||
#define AR_RX_STATUS_MT_HT 2
|
||||
#define AR_RX_STATUS_SHPREAMBLE (1 << 3)
|
||||
#define AR_RX_STATUS_MPDU_MASK 0x30
|
||||
#define AR_RX_STATUS_MPDU_SINGLE 0x00
|
||||
#define AR_RX_STATUS_MPDU_LAST 0x10
|
||||
#define AR_RX_STATUS_MPDU_FIRST 0x20
|
||||
#define AR_RX_STATUS_MPDU_MIDDLE 0x30
|
||||
} __packed;
|
||||
|
||||
struct ar_rx_phystatus {
|
||||
uint8_t rssi_ant[3];
|
||||
uint8_t rssi_ant_ext[3];
|
||||
uint8_t rssi; /* Combined RSSI. */
|
||||
uint8_t evm[2][6]; /* Error Vector Magnitude. */
|
||||
uint8_t phy_err;
|
||||
} __packed;
|
||||
|
||||
#define AR_PLCP_HDR_LEN 12
|
||||
@ -468,7 +481,6 @@ static const uint32_t ar5416_phy_vals_5ghz_20mhz[] = {
|
||||
0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0
|
||||
};
|
||||
|
||||
#ifdef notyet
|
||||
static const uint32_t ar5416_phy_vals_5ghz_40mhz[] = {
|
||||
0x00000007, 0x000003c4, 0x00000000, 0xad848e19, 0x7d14e000,
|
||||
0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e,
|
||||
@ -537,9 +549,7 @@ static const uint32_t ar5416_phy_vals_5ghz_40mhz[] = {
|
||||
0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803,
|
||||
0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef notyet
|
||||
static const uint32_t ar5416_phy_vals_2ghz_40mhz[] = {
|
||||
0x00000007, 0x000003c4, 0x00000000, 0xad848e19, 0x7d14e000,
|
||||
0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e,
|
||||
@ -608,7 +618,6 @@ static const uint32_t ar5416_phy_vals_2ghz_40mhz[] = {
|
||||
0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803,
|
||||
0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0
|
||||
};
|
||||
#endif
|
||||
|
||||
static const uint32_t ar5416_phy_vals_2ghz_20mhz[] = {
|
||||
0x00000007, 0x00000300, 0x00000000, 0xad848e19, 0x7d14e000,
|
||||
@ -1054,6 +1063,9 @@ struct otus_softc {
|
||||
|
||||
struct usb_xfer *sc_xfer[OTUS_N_XFER];
|
||||
|
||||
/* Last seen PLCP header; for A-MPDU decap */
|
||||
uint8_t ar_last_rx_plcp[AR_PLCP_HDR_LEN];
|
||||
|
||||
STAILQ_HEAD(, otus_data) sc_rx_active;
|
||||
STAILQ_HEAD(, otus_data) sc_rx_inactive;
|
||||
STAILQ_HEAD(, otus_data) sc_tx_active[OTUS_N_XFER];
|
||||
|
Loading…
Reference in New Issue
Block a user