diff --git a/sys/dev/rtwn/if_rtwn_ridx.h b/sys/dev/rtwn/if_rtwn_ridx.h index dd93f2db4ccf..6193f9ee2cad 100644 --- a/sys/dev/rtwn/if_rtwn_ridx.h +++ b/sys/dev/rtwn/if_rtwn_ridx.h @@ -1,92 +1,117 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef IF_RTWN_RIDX_H #define IF_RTWN_RIDX_H /* HW rate indices. */ + +/* + * Note - these are also used as offsets into the TX power table + * array. + */ #define RTWN_RIDX_CCK1 0 #define RTWN_RIDX_CCK2 1 #define RTWN_RIDX_CCK55 2 #define RTWN_RIDX_CCK11 3 #define RTWN_RIDX_OFDM6 4 #define RTWN_RIDX_OFDM9 5 #define RTWN_RIDX_OFDM12 6 #define RTWN_RIDX_OFDM18 7 #define RTWN_RIDX_OFDM24 8 #define RTWN_RIDX_OFDM36 9 #define RTWN_RIDX_OFDM48 10 #define RTWN_RIDX_OFDM54 11 #define RTWN_RIDX_HT_MCS_SHIFT 12 #define RTWN_RIDX_HT_MCS(i) (RTWN_RIDX_HT_MCS_SHIFT + (i)) +#define RTWN_RIDX_TO_MCS(ridx) ((ridx) - RTWN_RIDX_HT_MCS_SHIFT) + +/* HT supports up to MCS31, so goes from 12 -> 43 */ + +#define RTWN_RIDX_LEGACY_HT_COUNT 44 + +/* + * VHT supports MCS0..9 for up to 4 spatial streams, so + * goes from 44 -> 83. + */ +#define RTWN_RIDX_VHT_MCS_SHIFT 44 +#define RTWN_RIDX_VHT_MCS(s, i) (RTWN_RIDX_VHT_MCS_SHIFT + ((10*s) + i)) + +/* + * The total amount of rate indexes, CCK, OFDM, HT MCS0..31, + * VHT MCS0..9 for 1-4 streams. + */ +#define RTWN_RIDX_COUNT 84 -#define RTWN_RIDX_COUNT 28 #define RTWN_RIDX_UNKNOWN (uint8_t)-1 -#define RTWN_RATE_IS_CCK(rate) ((rate) <= RTWN_RIDX_CCK11) +#define RTWN_RATE_IS_CCK(rate) ((rate) <= RTWN_RIDX_CCK11) #define RTWN_RATE_IS_OFDM(rate) \ - ((rate) >= RTWN_RIDX_OFDM6 && (rate) != RTWN_RIDX_UNKNOWN) + ((rate) >= RTWN_RIDX_OFDM6 && (rate) <= RTWN_RIDX_OFDM54) +#define RTWN_RATE_IS_HT(rate) \ + ((rate) >= RTWN_RIDX_HT_MCS_SHIFT && (rate) < RTWN_RIDX_VHT_MCS_SHIFT) +#define RTWN_RATE_IS_VHT(rate) \ + ((rate) >= RTWN_RIDX_VHT_MCS_SHIFT && (rate) <= RTWN_RIDX_COUNT) static const uint8_t ridx2rate[] = { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; static __inline uint8_t rate2ridx(uint8_t rate) { if (rate & IEEE80211_RATE_MCS) { return ((rate & 0xf) + RTWN_RIDX_HT_MCS_SHIFT); } switch (rate) { /* 11g */ case 12: return 4; case 18: return 5; case 24: return 6; case 36: return 7; case 48: return 8; case 72: return 9; case 96: return 10; case 108: return 11; /* 11b */ case 2: return 0; case 4: return 1; case 11: return 2; case 22: return 3; default: return RTWN_RIDX_UNKNOWN; } } /* XXX move to net80211 */ static __inline__ uint8_t rtwn_ctl_mcsrate(const struct ieee80211_rate_table *rt, uint8_t ridx) { uint8_t cix, rate; /* Check if we are using MCS rate. */ - KASSERT(ridx >= RTWN_RIDX_HT_MCS(0) && ridx != RTWN_RIDX_UNKNOWN, - ("bad mcs rate index %d", ridx)); + KASSERT(RTWN_RATE_IS_HT(ridx), ("bad mcs rate index %d", ridx)); rate = (ridx - RTWN_RIDX_HT_MCS(0)) | IEEE80211_RATE_MCS; cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex; KASSERT(cix != (uint8_t)-1, ("rate %d (%d) has no info", rate, ridx)); return rt->info[cix].dot11Rate; } #endif /* IF_RTWN_RIDX_H */ diff --git a/sys/dev/rtwn/rtl8188e/r88e_chan.c b/sys/dev/rtwn/rtl8188e/r88e_chan.c index c072b3554083..51474bc1b819 100644 --- a/sys/dev/rtwn/rtl8188e/r88e_chan.c +++ b/sys/dev/rtwn/rtl8188e/r88e_chan.c @@ -1,150 +1,150 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int r88e_get_power_group(struct rtwn_softc *sc, struct ieee80211_channel *c) { uint8_t chan; int group; chan = rtwn_chan2centieee(c); if (IEEE80211_IS_CHAN_2GHZ(c)) { if (chan <= 2) group = 0; else if (chan <= 5) group = 1; else if (chan <= 8) group = 2; else if (chan <= 11) group = 3; else if (chan <= 13) group = 4; else if (chan <= 14) group = 5; else { KASSERT(0, ("wrong 2GHz channel %d!\n", chan)); return (-1); } } else { KASSERT(0, ("wrong channel band (flags %08X)\n", c->ic_flags)); return (-1); } return (group); } void r88e_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c, uint8_t power[RTWN_RIDX_COUNT]) { struct r92c_softc *rs = sc->sc_priv; const struct rtwn_r88e_txpwr *rt = rs->rs_txpwr; uint8_t cckpow, ofdmpow, bw20pow, htpow = 0; int max_mcs, ridx, group; /* Determine channel group. */ group = r88e_get_power_group(sc, c); if (group == -1) { /* shouldn't happen */ device_printf(sc->sc_dev, "%s: incorrect channel\n", __func__); return; } /* XXX net80211 regulatory */ max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1); - KASSERT(max_mcs <= RTWN_RIDX_COUNT, ("increase ridx limit\n")); + KASSERT(max_mcs <= RTWN_RIDX_LEGACY_HT_COUNT, ("increase ridx limit\n")); /* Compute per-CCK rate Tx power. */ cckpow = rt->cck_tx_pwr[group]; for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) { power[ridx] = (ridx == RTWN_RIDX_CCK2) ? cckpow - 9 : cckpow; } if (group < 5) htpow = rt->ht40_tx_pwr[group]; /* Compute per-OFDM rate Tx power. */ ofdmpow = htpow + rt->ofdm_tx_pwr_diff; for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) power[ridx] = ofdmpow; bw20pow = htpow + rt->bw20_tx_pwr_diff; for (ridx = RTWN_RIDX_HT_MCS(0); ridx <= max_mcs; ridx++) power[ridx] = bw20pow; /* Apply max limit. */ for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) { if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } } void r88e_set_bw20(struct rtwn_softc *sc, uint8_t chan) { struct r92c_softc *rs = sc->sc_priv; rtwn_setbits_1(sc, R92C_BWOPMODE, 0, R92C_BWOPMODE_20MHZ); rtwn_bb_setbits(sc, R92C_FPGA0_RFMOD, R92C_RFMOD_40MHZ, 0); rtwn_bb_setbits(sc, R92C_FPGA1_RFMOD, R92C_RFMOD_40MHZ, 0); /* Select 20MHz bandwidth. */ rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, (rs->rf_chnlbw[0] & ~0xfff) | chan | R88E_RF_CHNLBW_BW20); } void r88e_set_gain(struct rtwn_softc *sc, uint8_t gain) { rtwn_bb_setbits(sc, R92C_OFDM0_AGCCORE1(0), R92C_OFDM0_AGCCORE1_GAIN_M, gain); } diff --git a/sys/dev/rtwn/rtl8188e/r88e_rx.c b/sys/dev/rtwn/rtl8188e/r88e_rx.c index 826076c8071c..287869885b86 100644 --- a/sys/dev/rtwn/rtl8188e/r88e_rx.c +++ b/sys/dev/rtwn/rtl8188e/r88e_rx.c @@ -1,235 +1,234 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int r88e_classify_intr(struct rtwn_softc *sc, void *buf, int len) { struct r92c_rx_stat *stat = buf; int report_sel = MS(le32toh(stat->rxdw3), R88E_RXDW3_RPT); switch (report_sel) { case R88E_RXDW3_RPT_RX: return (RTWN_RX_DATA); case R88E_RXDW3_RPT_TX1: /* per-packet Tx report */ return (RTWN_RX_TX_REPORT); case R88E_RXDW3_RPT_TX2: /* periodical Tx report */ return (RTWN_RX_TX_REPORT2); case R88E_RXDW3_RPT_HIS: return (RTWN_RX_OTHER); default: /* shut up the compiler */ return (RTWN_RX_DATA); } } void r88e_ratectl_tx_complete(struct rtwn_softc *sc, uint8_t *buf, int len) { struct ieee80211_ratectl_tx_status txs; struct r88e_tx_rpt_ccx *rpt; struct ieee80211_node *ni; uint8_t macid; int ntries; /* Skip Rx descriptor. */ buf += sizeof(struct r92c_rx_stat); len -= sizeof(struct r92c_rx_stat); rpt = (struct r88e_tx_rpt_ccx *)buf; if (len != sizeof(*rpt)) { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: wrong report size (%d, must be %zu)\n", __func__, len, sizeof(*rpt)); return; } RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: ccx report dump: 0: %02X, 1: %02X, 2: %02X, queue time: " "low %02X, high %02X, final ridx: %02X, 6: %02X, 7: %02X\n", __func__, rpt->rptb0, rpt->rptb1, rpt->rptb2, rpt->queue_time_low, rpt->queue_time_high, rpt->final_rate, rpt->rptb6, rpt->rptb7); macid = MS(rpt->rptb1, R88E_RPTB1_MACID); if (macid > sc->macid_limit) { device_printf(sc->sc_dev, "macid %u is too big; increase MACID_MAX limit\n", macid); return; } ntries = MS(rpt->rptb2, R88E_RPTB2_RETRY_CNT); ni = sc->node_list[macid]; if (ni != NULL) { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: frame for macid %u was" "%s sent (%d retries)\n", __func__, macid, (rpt->rptb1 & R88E_RPTB1_PKT_OK) ? "" : " not", ntries); txs.flags = IEEE80211_RATECTL_STATUS_LONG_RETRY | IEEE80211_RATECTL_STATUS_FINAL_RATE; txs.long_retries = ntries; - if (rpt->final_rate > RTWN_RIDX_OFDM54) { /* MCS */ - txs.final_rate = - rpt->final_rate - RTWN_RIDX_HT_MCS_SHIFT; + if (RTWN_RATE_IS_HT(rpt->final_rate)) { /* MCS */ + txs.final_rate = RTWN_RIDX_TO_MCS(rpt->final_rate); txs.final_rate |= IEEE80211_RATE_MCS; } else txs.final_rate = ridx2rate[rpt->final_rate]; if (rpt->rptb1 & R88E_RPTB1_PKT_OK) txs.status = IEEE80211_RATECTL_TX_SUCCESS; else if (rpt->rptb2 & R88E_RPTB2_RETRY_OVER) txs.status = IEEE80211_RATECTL_TX_FAIL_LONG; else if (rpt->rptb2 & R88E_RPTB2_LIFE_EXPIRE) txs.status = IEEE80211_RATECTL_TX_FAIL_EXPIRED; else txs.status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED; ieee80211_ratectl_tx_complete(ni, &txs); } else { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: macid %u, ni is NULL\n", __func__, macid); } } void r88e_handle_c2h_report(struct rtwn_softc *sc, uint8_t *buf, int len) { /* Skip Rx descriptor. */ buf += sizeof(struct r92c_rx_stat); len -= sizeof(struct r92c_rx_stat); if (len != R88E_INTR_MSG_LEN) { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: wrong interrupt message size (%d, must be %d)\n", __func__, len, R88E_INTR_MSG_LEN); return; } /* XXX TODO */ } int8_t r88e_get_rssi_cck(struct rtwn_softc *sc, void *physt) { struct r88e_rx_phystat *phy = (struct r88e_rx_phystat *)physt; int8_t lna_idx, vga_idx, rssi; lna_idx = (phy->agc_rpt & 0xe0) >> 5; vga_idx = (phy->agc_rpt & 0x1f); rssi = 6 - 2 * vga_idx; switch (lna_idx) { case 7: if (vga_idx > 27) rssi = -100 + 6; else rssi += -100 + 2 * 27; break; case 6: rssi += -48 + 2 * 2; break; case 5: rssi += -42 + 2 * 7; break; case 4: rssi += -36 + 2 * 7; break; case 3: rssi += -24 + 2 * 7; break; case 2: rssi += -6 + 2 * 5; if (sc->sc_flags & RTWN_FLAG_CCK_HIPWR) rssi -= 6; break; case 1: rssi += 8; break; case 0: rssi += 14; break; } return (rssi); } int8_t r88e_get_rssi_ofdm(struct rtwn_softc *sc, void *physt) { struct r88e_rx_phystat *phy = (struct r88e_rx_phystat *)physt; int rssi; /* Get average RSSI. */ rssi = ((phy->sig_qual >> 1) & 0x7f) - 110; return (rssi); } void r88e_get_rx_stats(struct rtwn_softc *sc, struct ieee80211_rx_stats *rxs, const void *desc, const void *physt_ptr) { const struct r88e_rx_phystat *physt = physt_ptr; r92c_get_rx_stats(sc, rxs, desc, physt_ptr); if (!sc->sc_ht40) { /* XXX center channel */ rxs->r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; rxs->r_flags |= IEEE80211_R_BAND; rxs->c_ieee = physt->chan; rxs->c_freq = ieee80211_ieee2mhz(rxs->c_ieee, IEEE80211_CHAN_2GHZ); rxs->c_band = IEEE80211_CHAN_2GHZ; } } diff --git a/sys/dev/rtwn/rtl8192c/r92c_chan.c b/sys/dev/rtwn/rtl8192c/r92c_chan.c index 0edb32a97b4d..55d73c934f1e 100644 --- a/sys/dev/rtwn/rtl8192c/r92c_chan.c +++ b/sys/dev/rtwn/rtl8192c/r92c_chan.c @@ -1,348 +1,348 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int r92c_get_power_group(struct rtwn_softc *sc, struct ieee80211_channel *c) { uint8_t chan; int group; chan = rtwn_chan2centieee(c); if (IEEE80211_IS_CHAN_2GHZ(c)) { if (chan <= 3) group = 0; else if (chan <= 9) group = 1; else if (chan <= 14) group = 2; else { KASSERT(0, ("wrong 2GHz channel %d!\n", chan)); return (-1); } } else { KASSERT(0, ("wrong channel band (flags %08X)\n", c->ic_flags)); return (-1); } return (group); } /* XXX recheck */ void r92c_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c, uint8_t power[RTWN_RIDX_COUNT]) { struct r92c_softc *rs = sc->sc_priv; struct rtwn_r92c_txpwr *rt = rs->rs_txpwr; const struct rtwn_r92c_txagc *base = rs->rs_txagc; uint8_t ofdmpow, htpow, diff, max; int max_mcs, ridx, group; /* Determine channel group. */ group = r92c_get_power_group(sc, c); if (group == -1) { /* shouldn't happen */ device_printf(sc->sc_dev, "%s: incorrect channel\n", __func__); return; } /* XXX net80211 regulatory */ max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1); - KASSERT(max_mcs <= RTWN_RIDX_COUNT, ("increase ridx limit\n")); + KASSERT(max_mcs <= RTWN_RIDX_LEGACY_HT_COUNT, ("increase ridx limit\n")); if (rs->regulatory == 0) { for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) power[ridx] = base[chain].pwr[0][ridx]; } - for (ridx = RTWN_RIDX_OFDM6; ridx < RTWN_RIDX_COUNT; ridx++) { + for (ridx = RTWN_RIDX_OFDM6; ridx < RTWN_RIDX_LEGACY_HT_COUNT; ridx++) { if (rs->regulatory == 3) { power[ridx] = base[chain].pwr[0][ridx]; /* Apply vendor limits. */ if (IEEE80211_IS_CHAN_HT40(c)) max = rt->ht40_max_pwr[chain][group]; else max = rt->ht20_max_pwr[chain][group]; if (power[ridx] > max) power[ridx] = max; } else if (rs->regulatory == 1) { if (!IEEE80211_IS_CHAN_HT40(c)) power[ridx] = base[chain].pwr[group][ridx]; } else if (rs->regulatory != 2) power[ridx] = base[chain].pwr[0][ridx]; } /* Compute per-CCK rate Tx power. */ for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) power[ridx] += rt->cck_tx_pwr[chain][group]; htpow = rt->ht40_1s_tx_pwr[chain][group]; if (sc->ntxchains > 1) { /* Apply reduction for 2 spatial streams. */ diff = rt->ht40_2s_tx_pwr_diff[chain][group]; htpow = (htpow > diff) ? htpow - diff : 0; } /* Compute per-OFDM rate Tx power. */ diff = rt->ofdm_tx_pwr_diff[chain][group]; ofdmpow = htpow + diff; /* HT->OFDM correction. */ for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) power[ridx] += ofdmpow; /* Compute per-MCS Tx power. */ if (!IEEE80211_IS_CHAN_HT40(c)) { diff = rt->ht20_tx_pwr_diff[chain][group]; htpow += diff; /* HT40->HT20 correction. */ } for (ridx = RTWN_RIDX_HT_MCS(0); ridx <= max_mcs; ridx++) power[ridx] += htpow; /* Apply max limit. */ for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) { if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } } void r92c_write_txpower(struct rtwn_softc *sc, int chain, uint8_t power[RTWN_RIDX_COUNT]) { uint32_t reg; /* Write per-CCK rate Tx power. */ if (chain == 0) { reg = rtwn_bb_read(sc, R92C_TXAGC_A_CCK1_MCS32); reg = RW(reg, R92C_TXAGC_A_CCK1, power[RTWN_RIDX_CCK1]); rtwn_bb_write(sc, R92C_TXAGC_A_CCK1_MCS32, reg); reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); reg = RW(reg, R92C_TXAGC_A_CCK2, power[RTWN_RIDX_CCK2]); reg = RW(reg, R92C_TXAGC_A_CCK55, power[RTWN_RIDX_CCK55]); reg = RW(reg, R92C_TXAGC_A_CCK11, power[RTWN_RIDX_CCK11]); rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); } else { reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK1_55_MCS32); reg = RW(reg, R92C_TXAGC_B_CCK1, power[RTWN_RIDX_CCK1]); reg = RW(reg, R92C_TXAGC_B_CCK2, power[RTWN_RIDX_CCK2]); reg = RW(reg, R92C_TXAGC_B_CCK55, power[RTWN_RIDX_CCK55]); rtwn_bb_write(sc, R92C_TXAGC_B_CCK1_55_MCS32, reg); reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); reg = RW(reg, R92C_TXAGC_B_CCK11, power[RTWN_RIDX_CCK11]); rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); } /* Write per-OFDM rate Tx power. */ rtwn_bb_write(sc, R92C_TXAGC_RATE18_06(chain), SM(R92C_TXAGC_RATE06, power[RTWN_RIDX_OFDM6]) | SM(R92C_TXAGC_RATE09, power[RTWN_RIDX_OFDM9]) | SM(R92C_TXAGC_RATE12, power[RTWN_RIDX_OFDM12]) | SM(R92C_TXAGC_RATE18, power[RTWN_RIDX_OFDM18])); rtwn_bb_write(sc, R92C_TXAGC_RATE54_24(chain), SM(R92C_TXAGC_RATE24, power[RTWN_RIDX_OFDM24]) | SM(R92C_TXAGC_RATE36, power[RTWN_RIDX_OFDM36]) | SM(R92C_TXAGC_RATE48, power[RTWN_RIDX_OFDM48]) | SM(R92C_TXAGC_RATE54, power[RTWN_RIDX_OFDM54])); /* Write per-MCS Tx power. */ rtwn_bb_write(sc, R92C_TXAGC_MCS03_MCS00(chain), SM(R92C_TXAGC_MCS00, power[RTWN_RIDX_HT_MCS(0)]) | SM(R92C_TXAGC_MCS01, power[RTWN_RIDX_HT_MCS(1)]) | SM(R92C_TXAGC_MCS02, power[RTWN_RIDX_HT_MCS(2)]) | SM(R92C_TXAGC_MCS03, power[RTWN_RIDX_HT_MCS(3)])); rtwn_bb_write(sc, R92C_TXAGC_MCS07_MCS04(chain), SM(R92C_TXAGC_MCS04, power[RTWN_RIDX_HT_MCS(4)]) | SM(R92C_TXAGC_MCS05, power[RTWN_RIDX_HT_MCS(5)]) | SM(R92C_TXAGC_MCS06, power[RTWN_RIDX_HT_MCS(6)]) | SM(R92C_TXAGC_MCS07, power[RTWN_RIDX_HT_MCS(7)])); if (sc->ntxchains >= 2) { rtwn_bb_write(sc, R92C_TXAGC_MCS11_MCS08(chain), SM(R92C_TXAGC_MCS08, power[RTWN_RIDX_HT_MCS(8)]) | SM(R92C_TXAGC_MCS09, power[RTWN_RIDX_HT_MCS(9)]) | SM(R92C_TXAGC_MCS10, power[RTWN_RIDX_HT_MCS(10)]) | SM(R92C_TXAGC_MCS11, power[RTWN_RIDX_HT_MCS(11)])); rtwn_bb_write(sc, R92C_TXAGC_MCS15_MCS12(chain), SM(R92C_TXAGC_MCS12, power[RTWN_RIDX_HT_MCS(12)]) | SM(R92C_TXAGC_MCS13, power[RTWN_RIDX_HT_MCS(13)]) | SM(R92C_TXAGC_MCS14, power[RTWN_RIDX_HT_MCS(14)]) | SM(R92C_TXAGC_MCS15, power[RTWN_RIDX_HT_MCS(15)])); } } static void r92c_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c) { uint8_t power[RTWN_RIDX_COUNT]; int i; for (i = 0; i < sc->ntxchains; i++) { memset(power, 0, sizeof(power)); /* Compute per-rate Tx power values. */ rtwn_r92c_get_txpower(sc, i, c, power); #ifdef RTWN_DEBUG if (sc->sc_debug & RTWN_DEBUG_TXPWR) { int max_mcs, ridx; max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1); /* Dump per-rate Tx power values. */ printf("Tx power for chain %d:\n", i); for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) printf("Rate %d = %u\n", ridx, power[ridx]); } #endif /* Write per-rate Tx power values to hardware. */ r92c_write_txpower(sc, i, power); } } static void r92c_set_bw40(struct rtwn_softc *sc, uint8_t chan, int prichlo) { struct r92c_softc *rs = sc->sc_priv; rtwn_setbits_1(sc, R92C_BWOPMODE, R92C_BWOPMODE_20MHZ, 0); rtwn_setbits_1(sc, R92C_RRSR + 2, 0x6f, (prichlo ? 1 : 2) << 5); rtwn_bb_setbits(sc, R92C_FPGA0_RFMOD, 0, R92C_RFMOD_40MHZ); rtwn_bb_setbits(sc, R92C_FPGA1_RFMOD, 0, R92C_RFMOD_40MHZ); /* Set CCK side band. */ rtwn_bb_setbits(sc, R92C_CCK0_SYSTEM, 0x10, (prichlo ? 0 : 1) << 4); rtwn_bb_setbits(sc, R92C_OFDM1_LSTF, 0x0c00, (prichlo ? 1 : 2) << 10); rtwn_bb_setbits(sc, R92C_FPGA0_ANAPARAM2, R92C_FPGA0_ANAPARAM2_CBW20, 0); rtwn_bb_setbits(sc, 0x818, 0x0c000000, (prichlo ? 2 : 1) << 26); /* Select 40MHz bandwidth. */ rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, (rs->rf_chnlbw[0] & ~0xfff) | chan); } void r92c_set_bw20(struct rtwn_softc *sc, uint8_t chan) { struct r92c_softc *rs = sc->sc_priv; rtwn_setbits_1(sc, R92C_BWOPMODE, 0, R92C_BWOPMODE_20MHZ); rtwn_bb_setbits(sc, R92C_FPGA0_RFMOD, R92C_RFMOD_40MHZ, 0); rtwn_bb_setbits(sc, R92C_FPGA1_RFMOD, R92C_RFMOD_40MHZ, 0); rtwn_bb_setbits(sc, R92C_FPGA0_ANAPARAM2, 0, R92C_FPGA0_ANAPARAM2_CBW20); /* Select 20MHz bandwidth. */ rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, (rs->rf_chnlbw[0] & ~0xfff) | chan | R92C_RF_CHNLBW_BW20); } void r92c_set_chan(struct rtwn_softc *sc, struct ieee80211_channel *c) { struct r92c_softc *rs = sc->sc_priv; u_int chan; int i; chan = rtwn_chan2centieee(c); /* Set Tx power for this new channel. */ r92c_set_txpower(sc, c); for (i = 0; i < sc->nrxchains; i++) { rtwn_rf_write(sc, i, R92C_RF_CHNLBW, RW(rs->rf_chnlbw[i], R92C_RF_CHNLBW_CHNL, chan)); } if (IEEE80211_IS_CHAN_HT40(c)) r92c_set_bw40(sc, chan, IEEE80211_IS_CHAN_HT40U(c)); else rtwn_r92c_set_bw20(sc, chan); } void r92c_set_gain(struct rtwn_softc *sc, uint8_t gain) { rtwn_bb_setbits(sc, R92C_OFDM0_AGCCORE1(0), R92C_OFDM0_AGCCORE1_GAIN_M, gain); rtwn_bb_setbits(sc, R92C_OFDM0_AGCCORE1(1), R92C_OFDM0_AGCCORE1_GAIN_M, gain); } void r92c_scan_start(struct ieee80211com *ic) { struct rtwn_softc *sc = ic->ic_softc; struct r92c_softc *rs = sc->sc_priv; RTWN_LOCK(sc); /* Set gain for scanning. */ rtwn_r92c_set_gain(sc, 0x20); RTWN_UNLOCK(sc); rs->rs_scan_start(ic); } void r92c_scan_end(struct ieee80211com *ic) { struct rtwn_softc *sc = ic->ic_softc; struct r92c_softc *rs = sc->sc_priv; RTWN_LOCK(sc); /* Set gain under link. */ rtwn_r92c_set_gain(sc, 0x32); RTWN_UNLOCK(sc); rs->rs_scan_end(ic); } diff --git a/sys/dev/rtwn/rtl8192c/r92c_fw.c b/sys/dev/rtwn/rtl8192c/r92c_fw.c index 5ab56a5f454e..e611e5addeb3 100644 --- a/sys/dev/rtwn/rtl8192c/r92c_fw.c +++ b/sys/dev/rtwn/rtl8192c/r92c_fw.c @@ -1,504 +1,504 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef RTWN_WITHOUT_UCODE static int r92c_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len) { struct r92c_fw_cmd cmd; int ntries, error; KASSERT(len <= sizeof(cmd.msg), ("%s: firmware command too long (%d > %zu)\n", __func__, len, sizeof(cmd.msg))); if (!(sc->sc_flags & RTWN_FW_LOADED)) { RTWN_DPRINTF(sc, RTWN_DEBUG_FIRMWARE, "%s: firmware " "was not loaded; command (id %u) will be discarded\n", __func__, id); return (0); } /* Wait for current FW box to be empty. */ for (ntries = 0; ntries < 100; ntries++) { if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur))) break; rtwn_delay(sc, 2000); } if (ntries == 100) { device_printf(sc->sc_dev, "could not send firmware command\n"); return (ETIMEDOUT); } memset(&cmd, 0, sizeof(cmd)); cmd.id = id; if (len > 3) { /* Ext command: [id : byte2 : byte3 : byte4 : byte0 : byte1] */ cmd.id |= R92C_CMD_FLAG_EXT; memcpy(cmd.msg, (const uint8_t *)buf + 2, len - 2); memcpy(cmd.msg + 3, buf, 2); } else memcpy(cmd.msg, buf, len); /* Write the first word last since that will trigger the FW. */ if (len > 3) { error = rtwn_write_2(sc, R92C_HMEBOX_EXT(sc->fwcur), *(uint16_t *)((uint8_t *)&cmd + 4)); if (error != 0) return (error); } error = rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *(uint32_t *)&cmd); if (error != 0) return (error); sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX; return (0); } void r92c_fw_reset(struct rtwn_softc *sc, int reason) { int ntries; if (reason == RTWN_FW_RESET_CHECKSUM) return; /* Tell 8051 to reset itself. */ rtwn_write_1(sc, R92C_HMETFR + 3, 0x20); /* Wait until 8051 resets by itself. */ for (ntries = 0; ntries < 100; ntries++) { if ((rtwn_read_2(sc, R92C_SYS_FUNC_EN) & R92C_SYS_FUNC_EN_CPUEN) == 0) return; rtwn_delay(sc, 50); } /* Force 8051 reset. */ rtwn_setbits_1_shift(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_CPUEN, 0, 1); } void r92c_fw_download_enable(struct rtwn_softc *sc, int enable) { if (enable) { /* 8051 enable. */ rtwn_setbits_1_shift(sc, R92C_SYS_FUNC_EN, 0, R92C_SYS_FUNC_EN_CPUEN, 1); /* MCU firmware download enable. */ rtwn_setbits_1(sc, R92C_MCUFWDL, 0, R92C_MCUFWDL_EN); /* 8051 reset. */ rtwn_setbits_1_shift(sc, R92C_MCUFWDL, R92C_MCUFWDL_ROM_DLEN, 0, 2); } else { /* MCU download disable. */ rtwn_setbits_1(sc, R92C_MCUFWDL, R92C_MCUFWDL_EN, 0); /* Reserved for f/w extension. */ rtwn_write_1(sc, R92C_MCUFWDL + 1, 0); } } #endif /* * Initialize firmware rate adaptation. */ #ifndef RTWN_WITHOUT_UCODE static int r92c_send_ra_cmd(struct rtwn_softc *sc, int macid, uint32_t rates, int maxrate) { struct r92c_fw_cmd_macid_cfg cmd; uint8_t mode; int error = 0; /* XXX should be called directly from iv_newstate() for MACID_BC */ /* XXX joinbss, not send_ra_cmd() */ #ifdef RTWN_TODO /* NB: group addressed frames are done at 11bg rates for now */ if (ic->ic_curmode == IEEE80211_MODE_11B) mode = R92C_RAID_11B; else mode = R92C_RAID_11BG; /* XXX misleading 'mode' value here for unicast frames */ RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: mode 0x%x, rates 0x%08x, basicrates 0x%08x\n", __func__, mode, rates, basicrates); /* Set rates mask for group addressed frames. */ cmd.macid = RTWN_MACID_BC | R92C_CMD_MACID_VALID; cmd.mask = htole32(mode << 28 | basicrates); error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); if (error != 0) { device_printf(sc->sc_dev, "could not set RA mask for broadcast station\n"); return (error); } #endif /* Set rates mask for unicast frames. */ - if (maxrate >= RTWN_RIDX_HT_MCS(0)) + if (RTWN_RATE_IS_HT(maxrate)) mode = R92C_RAID_11GN; - else if (maxrate >= RTWN_RIDX_OFDM6) + else if (RTWN_RATE_IS_OFDM(maxrate)) mode = R92C_RAID_11BG; else mode = R92C_RAID_11B; cmd.macid = macid | R92C_CMD_MACID_VALID; cmd.mask = htole32(mode << 28 | rates); error = r92c_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set RA mask for %d station\n", __func__, macid); return (error); } return (0); } #endif static void r92c_init_ra(struct rtwn_softc *sc, int macid) { struct ieee80211_htrateset *rs_ht; struct ieee80211_node *ni; uint32_t rates; int maxrate; RTWN_NT_LOCK(sc); if (sc->node_list[macid] == NULL) { RTWN_DPRINTF(sc, RTWN_DEBUG_RA, "%s: macid %d, ni is NULL\n", __func__, macid); RTWN_NT_UNLOCK(sc); return; } ni = ieee80211_ref_node(sc->node_list[macid]); if (ni->ni_flags & IEEE80211_NODE_HT) rs_ht = &ni->ni_htrates; else rs_ht = NULL; /* XXX MACID_BC */ rtwn_get_rates(sc, &ni->ni_rates, rs_ht, &rates, &maxrate, 0); RTWN_NT_UNLOCK(sc); #ifndef RTWN_WITHOUT_UCODE if (sc->sc_ratectl == RTWN_RATECTL_FW) { r92c_send_ra_cmd(sc, macid, rates, maxrate); } #endif rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(macid), maxrate); ieee80211_free_node(ni); } void r92c_joinbss_rpt(struct rtwn_softc *sc, int macid) { #ifndef RTWN_WITHOUT_UCODE struct r92c_softc *rs = sc->sc_priv; struct ieee80211vap *vap; struct r92c_fw_cmd_joinbss_rpt cmd; if (sc->vaps[0] == NULL) /* XXX fix */ goto end; vap = &sc->vaps[0]->vap; if ((vap->iv_state == IEEE80211_S_RUN) ^ !(rs->rs_flags & R92C_FLAG_ASSOCIATED)) goto end; if (rs->rs_flags & R92C_FLAG_ASSOCIATED) { cmd.mstatus = R92C_MSTATUS_DISASSOC; rs->rs_flags &= ~R92C_FLAG_ASSOCIATED; } else { cmd.mstatus = R92C_MSTATUS_ASSOC; rs->rs_flags |= R92C_FLAG_ASSOCIATED; } if (r92c_fw_cmd(sc, R92C_CMD_JOINBSS_RPT, &cmd, sizeof(cmd)) != 0) { device_printf(sc->sc_dev, "%s: cannot change media status!\n", __func__); } end: #endif /* TODO: init rates for RTWN_MACID_BC. */ if (macid & RTWN_MACID_VALID) r92c_init_ra(sc, macid & ~RTWN_MACID_VALID); } #ifndef RTWN_WITHOUT_UCODE int r92c_set_rsvd_page(struct rtwn_softc *sc, int probe_resp, int null, int qos_null) { struct r92c_fw_cmd_rsvdpage rsvd; rsvd.probe_resp = probe_resp; rsvd.ps_poll = 0; rsvd.null_data = null; return (r92c_fw_cmd(sc, R92C_CMD_RSVD_PAGE, &rsvd, sizeof(rsvd))); } int r92c_set_pwrmode(struct rtwn_softc *sc, struct ieee80211vap *vap, int off) { struct r92c_fw_cmd_pwrmode mode; int error; /* XXX dm_RF_saving */ if (off && vap->iv_state == IEEE80211_S_RUN && (vap->iv_flags & IEEE80211_F_PMGTON)) mode.mode = R92C_PWRMODE_MIN; else mode.mode = R92C_PWRMODE_CAM; mode.smart_ps = R92C_PWRMODE_SMARTPS_NULLDATA; mode.bcn_pass = 1; /* XXX */ error = r92c_fw_cmd(sc, R92C_CMD_SET_PWRMODE, &mode, sizeof(mode)); if (error != 0) { device_printf(sc->sc_dev, "%s: CMD_SET_PWRMODE was not sent, error %d\n", __func__, error); } return (error); } void r92c_set_rssi(struct rtwn_softc *sc) { struct ieee80211_node *ni; struct rtwn_node *rn; struct r92c_fw_cmd_rssi cmd; int i; cmd.reserved = 0; RTWN_NT_LOCK(sc); for (i = 0; i < sc->macid_limit; i++) { /* XXX optimize? */ ni = sc->node_list[i]; if (ni == NULL) continue; rn = RTWN_NODE(ni); cmd.macid = i; cmd.pwdb = rn->avg_pwdb; RTWN_DPRINTF(sc, RTWN_DEBUG_RSSI, "%s: sending RSSI command (macid %d, rssi %d)\n", __func__, i, rn->avg_pwdb); RTWN_NT_UNLOCK(sc); r92c_fw_cmd(sc, R92C_CMD_RSSI_SETTING, &cmd, sizeof(cmd)); RTWN_NT_LOCK(sc); } RTWN_NT_UNLOCK(sc); } static void r92c_ratectl_tx_complete(struct rtwn_softc *sc, uint8_t *buf, int len) { struct ieee80211_ratectl_tx_status txs; struct r92c_c2h_tx_rpt *rpt; struct ieee80211_node *ni; uint8_t macid; int ntries; if (sc->sc_ratectl != RTWN_RATECTL_NET80211) { /* shouldn't happen */ device_printf(sc->sc_dev, "%s called while ratectl = %d!\n", __func__, sc->sc_ratectl); return; } rpt = (struct r92c_c2h_tx_rpt *)buf; if (len != sizeof(*rpt)) { device_printf(sc->sc_dev, "%s: wrong report size (%d, must be %zu)\n", __func__, len, sizeof(*rpt)); return; } RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: ccx report dump: 0: %02X, 1: %02X, queue time: " "low %02X, high %02X, 4: %02X, 5: %02X, 6: %02X, 7: %02X\n", __func__, rpt->rptb0, rpt->rptb1, rpt->queue_time_low, rpt->queue_time_high, rpt->rptb4, rpt->rptb5, rpt->rptb6, rpt->rptb7); macid = MS(rpt->rptb5, R92C_RPTB5_MACID); if (macid > sc->macid_limit) { device_printf(sc->sc_dev, "macid %u is too big; increase MACID_MAX limit\n", macid); return; } ntries = MS(rpt->rptb0, R92C_RPTB0_RETRY_CNT); RTWN_NT_LOCK(sc); ni = sc->node_list[macid]; if (ni != NULL) { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: frame for macid %u was" "%s sent (%d retries)\n", __func__, macid, (rpt->rptb7 & R92C_RPTB7_PKT_OK) ? "" : " not", ntries); txs.flags = IEEE80211_RATECTL_STATUS_LONG_RETRY; txs.long_retries = ntries; if (rpt->rptb7 & R92C_RPTB7_PKT_OK) txs.status = IEEE80211_RATECTL_TX_SUCCESS; else if (rpt->rptb6 & R92C_RPTB6_RETRY_OVER) txs.status = IEEE80211_RATECTL_TX_FAIL_LONG; /* XXX */ else if (rpt->rptb6 & R92C_RPTB6_LIFE_EXPIRE) txs.status = IEEE80211_RATECTL_TX_FAIL_EXPIRED; else txs.status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED; ieee80211_ratectl_tx_complete(ni, &txs); } else { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: macid %u, ni is NULL\n", __func__, macid); } RTWN_NT_UNLOCK(sc); #ifdef IEEE80211_SUPPORT_SUPERG if (sc->sc_tx_n_active > 0 && --sc->sc_tx_n_active <= 1) rtwn_cmd_sleepable(sc, NULL, 0, rtwn_ff_flush_all); #endif } static void r92c_handle_c2h_task(struct rtwn_softc *sc, union sec_param *data) { const uint16_t off = R92C_C2H_EVT_MSG + sizeof(struct r92c_c2h_evt); struct r92c_softc *rs = sc->sc_priv; uint16_t buf[R92C_C2H_MSG_MAX_LEN / 2 + 1]; uint8_t id, len, status; int i; /* Do not reschedule the task if device is not running. */ if (!(sc->sc_flags & RTWN_RUNNING)) return; /* Read current status. */ status = rtwn_read_1(sc, R92C_C2H_EVT_CLEAR); if (status == R92C_C2H_EVT_HOST_CLOSE) goto end; /* nothing to do */ else if (status == R92C_C2H_EVT_FW_CLOSE) { len = rtwn_read_1(sc, R92C_C2H_EVT_MSG); id = MS(len, R92C_C2H_EVTB0_ID); len = MS(len, R92C_C2H_EVTB0_LEN); memset(buf, 0, sizeof(buf)); /* Try to optimize event reads. */ for (i = 0; i < len; i += 2) buf[i / 2] = rtwn_read_2(sc, off + i); KASSERT(i < sizeof(buf), ("%s: buffer overrun (%d >= %zu)!", __func__, i, sizeof(buf))); switch (id) { case R92C_C2H_EVT_TX_REPORT: r92c_ratectl_tx_complete(sc, (uint8_t *)buf, len); break; default: device_printf(sc->sc_dev, "%s: C2H report %u (len %u) was not handled\n", __func__, id, len); break; } } /* Prepare for next event. */ rtwn_write_1(sc, R92C_C2H_EVT_CLEAR, R92C_C2H_EVT_HOST_CLOSE); end: /* Adjust timeout for next call. */ if (rs->rs_c2h_pending != 0) { rs->rs_c2h_pending = 0; rs->rs_c2h_paused = 0; } else rs->rs_c2h_paused++; if (rs->rs_c2h_paused > R92C_TX_PAUSED_THRESHOLD) rs->rs_c2h_timeout = hz; else rs->rs_c2h_timeout = MAX(hz / 100, 1); /* Reschedule the task. */ callout_reset(&rs->rs_c2h_report, rs->rs_c2h_timeout, r92c_handle_c2h_report, sc); } void r92c_handle_c2h_report(void *arg) { struct rtwn_softc *sc = arg; rtwn_cmd_sleepable(sc, NULL, 0, r92c_handle_c2h_task); } #endif /* RTWN_WITHOUT_UCODE */ diff --git a/sys/dev/rtwn/rtl8192c/r92c_rx.c b/sys/dev/rtwn/rtl8192c/r92c_rx.c index 72f726a24550..9af3b1ebc2c9 100644 --- a/sys/dev/rtwn/rtl8192c/r92c_rx.c +++ b/sys/dev/rtwn/rtl8192c/r92c_rx.c @@ -1,151 +1,151 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int r92c_classify_intr(struct rtwn_softc *sc, void *buf, int len) { /* NB: reports are fetched from C2H_MSG register. */ return (RTWN_RX_DATA); } int8_t r92c_get_rssi_cck(struct rtwn_softc *sc, void *physt) { static const int8_t cckoff[] = { 16, -12, -26, -46 }; struct r92c_rx_cck *cck = (struct r92c_rx_cck *)physt; uint8_t rpt; int8_t rssi; if (sc->sc_flags & RTWN_FLAG_CCK_HIPWR) { rpt = (cck->agc_rpt >> 5) & 0x03; rssi = (cck->agc_rpt & 0x1f) << 1; } else { rpt = (cck->agc_rpt >> 6) & 0x03; rssi = cck->agc_rpt & 0x3e; } rssi = cckoff[rpt] - rssi; return (rssi); } int8_t r92c_get_rssi_ofdm(struct rtwn_softc *sc, void *physt) { struct r92c_rx_phystat *phy = (struct r92c_rx_phystat *)physt; int rssi; /* Get average RSSI. */ rssi = ((phy->pwdb_all >> 1) & 0x7f) - 110; return (rssi); } uint8_t r92c_rx_radiotap_flags(const void *buf) { const struct r92c_rx_stat *stat = buf; uint8_t flags, rate; if (!(stat->rxdw3 & htole32(R92C_RXDW3_SPLCP))) return (0); rate = MS(le32toh(stat->rxdw3), R92C_RXDW3_RATE); if (RTWN_RATE_IS_CCK(rate)) flags = IEEE80211_RADIOTAP_F_SHORTPRE; else flags = IEEE80211_RADIOTAP_F_SHORTGI; return (flags); } void r92c_get_rx_stats(struct rtwn_softc *sc, struct ieee80211_rx_stats *rxs, const void *desc, const void *physt_ptr) { const struct r92c_rx_stat *stat = desc; uint32_t rxdw1, rxdw3; uint8_t rate; rxdw1 = le32toh(stat->rxdw1); rxdw3 = le32toh(stat->rxdw3); rate = MS(rxdw3, R92C_RXDW3_RATE); if (rxdw1 & R92C_RXDW1_AMPDU) rxs->c_pktflags |= IEEE80211_RX_F_AMPDU; else if (rxdw1 & R92C_RXDW1_AMPDU_MORE) rxs->c_pktflags |= IEEE80211_RX_F_AMPDU_MORE; - if ((rxdw3 & R92C_RXDW3_SPLCP) && rate >= RTWN_RIDX_HT_MCS(0)) + if ((rxdw3 & R92C_RXDW3_SPLCP) && RTWN_RATE_IS_HT(rate)) rxs->c_pktflags |= IEEE80211_RX_F_SHORTGI; if (rxdw3 & R92C_RXDW3_HT40) rxs->c_width = IEEE80211_RX_FW_40MHZ; else rxs->c_width = IEEE80211_RX_FW_20MHZ; if (RTWN_RATE_IS_CCK(rate)) rxs->c_phytype = IEEE80211_RX_FP_11B; - else if (rate < RTWN_RIDX_HT_MCS(0)) + else if (RTWN_RATE_IS_OFDM(rate)) rxs->c_phytype = IEEE80211_RX_FP_11G; else rxs->c_phytype = IEEE80211_RX_FP_11NG; /* Map HW rate index to 802.11 rate. */ - if (rate < RTWN_RIDX_HT_MCS(0)) { + if (RTWN_RATE_IS_CCK(rate) || RTWN_RATE_IS_OFDM(rate)) { rxs->c_rate = ridx2rate[rate]; if (RTWN_RATE_IS_CCK(rate)) rxs->c_pktflags |= IEEE80211_RX_F_CCK; else rxs->c_pktflags |= IEEE80211_RX_F_OFDM; } else { /* MCS0~15. */ rxs->c_rate = - IEEE80211_RATE_MCS | (rate - RTWN_RIDX_HT_MCS_SHIFT); + IEEE80211_RATE_MCS | (RTWN_RIDX_TO_MCS(rate)); rxs->c_pktflags |= IEEE80211_RX_F_HT; } } diff --git a/sys/dev/rtwn/rtl8192c/r92c_tx.c b/sys/dev/rtwn/rtl8192c/r92c_tx.c index 8a4c6581832b..9583a7e1119e 100644 --- a/sys/dev/rtwn/rtl8192c/r92c_tx.c +++ b/sys/dev/rtwn/rtl8192c/r92c_tx.c @@ -1,463 +1,463 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int r92c_tx_get_sco(struct rtwn_softc *sc, struct ieee80211_channel *c) { if (IEEE80211_IS_CHAN_HT40U(c)) return (R92C_TXDW4_SCO_SCA); else return (R92C_TXDW4_SCO_SCB); } static void r92c_tx_set_ht40(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) { struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; if (ieee80211_ht_check_tx_ht40(ni)) { int extc_offset; extc_offset = r92c_tx_get_sco(sc, ni->ni_chan); txd->txdw4 |= htole32(R92C_TXDW4_DATA_BW40); txd->txdw4 |= htole32(SM(R92C_TXDW4_DATA_SCO, extc_offset)); } } static void r92c_tx_protection(struct rtwn_softc *sc, struct r92c_tx_desc *txd, enum ieee80211_protmode mode, uint8_t ridx) { struct ieee80211com *ic = &sc->sc_ic; uint8_t rate; switch (mode) { case IEEE80211_PROT_CTSONLY: txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF); break; case IEEE80211_PROT_RTSCTS: txd->txdw4 |= htole32(R92C_TXDW4_RTSEN); break; default: break; } if (mode == IEEE80211_PROT_CTSONLY || mode == IEEE80211_PROT_RTSCTS) { - if (ridx >= RTWN_RIDX_HT_MCS(0)) + if (RTWN_RATE_IS_HT(ridx)) rate = rtwn_ctl_mcsrate(ic->ic_rt, ridx); else rate = ieee80211_ctl_rate(ic->ic_rt, ridx2rate[ridx]); ridx = rate2ridx(IEEE80211_RV(rate)); txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, ridx)); /* RTS rate fallback limit (max). */ txd->txdw5 |= htole32(SM(R92C_TXDW5_RTSRATE_FB_LMT, 0xf)); if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) txd->txdw4 |= htole32(R92C_TXDW4_RTS_SHORT); } } static void r92c_tx_raid(struct rtwn_softc *sc, struct r92c_tx_desc *txd, struct ieee80211_node *ni, int ismcast) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_channel *chan; enum ieee80211_phymode mode; uint8_t raid; chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? ni->ni_chan : ic->ic_curchan; mode = ieee80211_chan2mode(chan); /* NB: group addressed frames are done at 11bg rates for now */ if (ismcast || !(ni->ni_flags & IEEE80211_NODE_HT)) { switch (mode) { case IEEE80211_MODE_11B: case IEEE80211_MODE_11G: break; case IEEE80211_MODE_11NG: mode = IEEE80211_MODE_11G; break; default: device_printf(sc->sc_dev, "unknown mode(1) %d!\n", ic->ic_curmode); return; } } switch (mode) { case IEEE80211_MODE_11B: raid = R92C_RAID_11B; break; case IEEE80211_MODE_11G: if (vap->iv_flags & IEEE80211_F_PUREG) raid = R92C_RAID_11G; else raid = R92C_RAID_11BG; break; case IEEE80211_MODE_11NG: if (vap->iv_flags_ht & IEEE80211_FHT_PUREN) raid = R92C_RAID_11N; else raid = R92C_RAID_11BGN; break; default: device_printf(sc->sc_dev, "unknown mode(2) %d!\n", mode); return; } txd->txdw1 |= htole32(SM(R92C_TXDW1_RAID, raid)); } /* XXX move to device-independent layer */ static void r92c_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) { struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; /* * Only enable short-GI if we're transmitting in that * width to that node. * * Specifically, do not enable shortgi for 20MHz if * we're attempting to transmit at 40MHz. */ if (ieee80211_ht_check_tx_ht40(ni)) { if (ieee80211_ht_check_tx_shortgi_40(ni)) txd->txdw5 |= htole32(R92C_TXDW5_SGI); } else if (ieee80211_ht_check_tx_ht(ni)) { if (ieee80211_ht_check_tx_shortgi_20(ni)) txd->txdw5 |= htole32(R92C_TXDW5_SGI); } } void r92c_tx_enable_ampdu(void *buf, int enable) { struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; if (enable) txd->txdw1 |= htole32(R92C_TXDW1_AGGEN); else txd->txdw1 |= htole32(R92C_TXDW1_AGGBK); } void r92c_tx_setup_hwseq(void *buf) { struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ_EN); } void r92c_tx_setup_macid(void *buf, int id) { struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, id)); /* XXX does not belong here */ /* XXX temporary (I hope) */ /* Force CCK1 for RTS / CTS frames (driver bug) */ txd->txdw4 &= ~htole32(SM(R92C_TXDW4_RTSRATE, R92C_TXDW4_RTSRATE_M)); txd->txdw4 &= ~htole32(R92C_TXDW4_RTS_SHORT); } static int r92c_calculate_tx_agg_window(struct rtwn_softc *sc, const struct ieee80211_node *ni, int tid) { const struct ieee80211_tx_ampdu *tap; int wnd; tap = &ni->ni_tx_ampdu[tid]; /* * BAW is (MAX_AGG * 2) + 1, hence the /2 here. * Ensure we don't send 0 or more than 64. */ wnd = tap->txa_wnd / 2; if (wnd == 0) wnd = 1; else if (wnd > 0x1f) wnd = 0x1f; return (wnd); } void r92c_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, void *buf, uint8_t ridx, int maxretry) { #ifndef RTWN_WITHOUT_UCODE struct r92c_softc *rs = sc->sc_priv; #endif struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct rtwn_vap *uvp = RTWN_VAP(vap); struct ieee80211_frame *wh; struct r92c_tx_desc *txd; enum ieee80211_protmode prot; uint8_t type, tid, qos, qsel; int hasqos, ismcast, macid; wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; hasqos = IEEE80211_QOS_HAS_SEQ(wh); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Select TX ring for this frame. */ if (hasqos) { qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; } else { qos = 0; tid = 0; } /* Fill Tx descriptor. */ txd = (struct r92c_tx_desc *)buf; txd->flags0 |= R92C_FLAGS0_LSG | R92C_FLAGS0_FSG; if (ismcast) txd->flags0 |= R92C_FLAGS0_BMCAST; if (!ismcast) { /* Unicast frame, check if an ACK is expected. */ if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK) { txd->txdw5 |= htole32(R92C_TXDW5_RTY_LMT_ENA); txd->txdw5 |= htole32(SM(R92C_TXDW5_RTY_LMT, maxretry)); } struct rtwn_node *un = RTWN_NODE(ni); macid = un->id; if (type == IEEE80211_FC0_TYPE_DATA) { qsel = tid % RTWN_MAX_TID; rtwn_r92c_tx_enable_ampdu(sc, buf, (m->m_flags & M_AMPDU_MPDU) != 0); if (m->m_flags & M_AMPDU_MPDU) { txd->txdw2 |= htole32(SM(R92C_TXDW2_AMPDU_DEN, ieee80211_ht_get_node_ampdu_density(ni))); txd->txdw6 |= htole32(SM(R92C_TXDW6_MAX_AGG, r92c_calculate_tx_agg_window(sc, ni, tid))); } if (sc->sc_ratectl == RTWN_RATECTL_NET80211) { txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT); sc->sc_tx_n_active++; #ifndef RTWN_WITHOUT_UCODE rs->rs_c2h_pending++; #endif } if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) txd->txdw4 |= htole32(R92C_TXDW4_DATA_SHPRE); prot = IEEE80211_PROT_NONE; - if (ridx >= RTWN_RIDX_HT_MCS(0)) { + if (RTWN_RATE_IS_HT(ridx)) { r92c_tx_set_ht40(sc, txd, ni); r92c_tx_set_sgi(sc, txd, ni); prot = ic->ic_htprotmode; } else if (ic->ic_flags & IEEE80211_F_USEPROT) prot = ic->ic_protmode; /* XXX fix last comparison for A-MSDU (in net80211) */ /* XXX A-MPDU? */ if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold && vap->iv_rtsthreshold != IEEE80211_RTS_MAX) prot = IEEE80211_PROT_RTSCTS; /* NB: checks for ht40 / short bits (set above). */ if (prot != IEEE80211_PROT_NONE) r92c_tx_protection(sc, txd, prot, ridx); } else /* IEEE80211_FC0_TYPE_MGT */ qsel = R92C_TXDW1_QSEL_MGNT; } else { macid = RTWN_MACID_BC; qsel = R92C_TXDW1_QSEL_MGNT; } txd->txdw1 |= htole32(SM(R92C_TXDW1_QSEL, qsel)); rtwn_r92c_tx_setup_macid(sc, txd, macid); txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, ridx)); /* Data rate fallback limit (max). */ txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FB_LMT, 0x1f)); txd->txdw4 |= htole32(SM(R92C_TXDW4_PORT_ID, uvp->id)); r92c_tx_raid(sc, txd, ni, ismcast); /* Force this rate if needed. */ if (sc->sc_ratectl != RTWN_RATECTL_FW) txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); if (!hasqos) { /* Use HW sequence numbering for non-QoS frames. */ rtwn_r92c_tx_setup_hwseq(sc, txd); txd->txdw4 |= htole32(SM(R92C_TXDW4_SEQ_SEL, uvp->id)); } else { uint16_t seqno; if (m->m_flags & M_AMPDU_MPDU) { seqno = ni->ni_txseqs[tid] % IEEE80211_SEQ_RANGE; ni->ni_txseqs[tid]++; } else seqno = M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE; /* Set sequence number. */ txd->txdseq = htole16(seqno); } } void r92c_fill_tx_desc_raw(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, void *buf, const struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; struct rtwn_vap *uvp = RTWN_VAP(vap); struct ieee80211_frame *wh; struct r92c_tx_desc *txd; uint8_t ridx; int ismcast; /* XXX TODO: 11n checks, matching r92c_fill_tx_desc() */ wh = mtod(m, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); ridx = rate2ridx(params->ibp_rate0); /* Fill Tx descriptor. */ txd = (struct r92c_tx_desc *)buf; txd->flags0 |= R92C_FLAGS0_LSG | R92C_FLAGS0_FSG; if (ismcast) txd->flags0 |= R92C_FLAGS0_BMCAST; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { txd->txdw5 |= htole32(R92C_TXDW5_RTY_LMT_ENA); txd->txdw5 |= htole32(SM(R92C_TXDW5_RTY_LMT, params->ibp_try0)); } if (params->ibp_flags & IEEE80211_BPF_RTS) r92c_tx_protection(sc, txd, IEEE80211_PROT_RTSCTS, ridx); if (params->ibp_flags & IEEE80211_BPF_CTS) r92c_tx_protection(sc, txd, IEEE80211_PROT_CTSONLY, ridx); rtwn_r92c_tx_setup_macid(sc, txd, RTWN_MACID_BC); txd->txdw1 |= htole32(SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT)); /* Set TX rate index. */ txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, ridx)); txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FB_LMT, 0x1f)); txd->txdw4 |= htole32(SM(R92C_TXDW4_PORT_ID, uvp->id)); txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); r92c_tx_raid(sc, txd, ni, ismcast); if (!IEEE80211_QOS_HAS_SEQ(wh)) { /* Use HW sequence numbering for non-QoS frames. */ rtwn_r92c_tx_setup_hwseq(sc, txd); txd->txdw4 |= htole32(SM(R92C_TXDW4_SEQ_SEL, uvp->id)); } else { /* Set sequence number. */ txd->txdseq |= htole16(M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE); } } void r92c_fill_tx_desc_null(struct rtwn_softc *sc, void *buf, int is11b, int qos, int id) { struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; txd->flags0 = R92C_FLAGS0_FSG | R92C_FLAGS0_LSG | R92C_FLAGS0_OWN; txd->txdw1 = htole32( SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT)); txd->txdw4 = htole32(R92C_TXDW4_DRVRATE); txd->txdw4 |= htole32(SM(R92C_TXDW4_PORT_ID, id)); if (is11b) { txd->txdw5 = htole32(SM(R92C_TXDW5_DATARATE, RTWN_RIDX_CCK1)); } else { txd->txdw5 = htole32(SM(R92C_TXDW5_DATARATE, RTWN_RIDX_OFDM6)); } if (!qos) { rtwn_r92c_tx_setup_hwseq(sc, txd); txd->txdw4 |= htole32(SM(R92C_TXDW4_SEQ_SEL, id)); } } uint8_t r92c_tx_radiotap_flags(const void *buf) { const struct r92c_tx_desc *txd = buf; uint8_t flags; flags = 0; if (txd->txdw4 & htole32(R92C_TXDW4_DATA_SHPRE)) flags |= IEEE80211_RADIOTAP_F_SHORTPRE; if (txd->txdw5 & htole32(R92C_TXDW5_SGI)) flags |= IEEE80211_RADIOTAP_F_SHORTGI; return (flags); } diff --git a/sys/dev/rtwn/rtl8192e/r92e_chan.c b/sys/dev/rtwn/rtl8192e/r92e_chan.c index b28462873c09..c6e911309cd2 100644 --- a/sys/dev/rtwn/rtl8192e/r92e_chan.c +++ b/sys/dev/rtwn/rtl8192e/r92e_chan.c @@ -1,230 +1,230 @@ /*- * Copyright (c) 2017 Kevin Lo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int r92e_get_power_group(struct rtwn_softc *sc, struct ieee80211_channel *c) { uint8_t chan; int group; chan = rtwn_chan2centieee(c); if (IEEE80211_IS_CHAN_2GHZ(c)) { if (chan <= 2) group = 0; else if (chan <= 5) group = 1; else if (chan <= 8) group = 2; else if (chan <= 11) group = 3; else if (chan <= 14) group = 4; else { KASSERT(0, ("wrong 2GHz channel %d!\n", chan)); return (-1); } } else { KASSERT(0, ("wrong channel band (flags %08X)\n", c->ic_flags)); return (-1); } return (group); } static void r92e_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c, uint8_t power[RTWN_RIDX_COUNT]) { struct r92e_softc *rs = sc->sc_priv; int i, ridx, group, max_mcs; /* Determine channel group. */ group = r92e_get_power_group(sc, c); if (group == -1) { /* shouldn't happen */ device_printf(sc->sc_dev, "%s: incorrect channel\n", __func__); return; } max_mcs = RTWN_RIDX_HT_MCS(sc->ntxchains * 8 - 1); /* XXX regulatory */ /* XXX net80211 regulatory */ for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) power[ridx] = rs->cck_tx_pwr[chain][group]; for (ridx = RTWN_RIDX_OFDM6; ridx <= max_mcs; ridx++) power[ridx] = rs->ht40_tx_pwr_2g[chain][group]; for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) power[ridx] += rs->ofdm_tx_pwr_diff_2g[chain][0]; for (i = 0; i < sc->ntxchains; i++) { uint8_t min_mcs; uint8_t pwr_diff; if (IEEE80211_IS_CHAN_HT40(c)) pwr_diff = rs->bw40_tx_pwr_diff_2g[chain][i]; else pwr_diff = rs->bw20_tx_pwr_diff_2g[chain][i]; min_mcs = RTWN_RIDX_HT_MCS(i * 8); for (ridx = min_mcs; ridx <= max_mcs; ridx++) power[ridx] += pwr_diff; } /* Apply max limit. */ for (ridx = RTWN_RIDX_CCK1; ridx <= max_mcs; ridx++) { if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } #ifdef RTWN_DEBUG if (sc->sc_debug & RTWN_DEBUG_TXPWR) { /* Dump per-rate Tx power values. */ printf("Tx power for chain %d:\n", chain); - for (ridx = RTWN_RIDX_CCK1; ridx < RTWN_RIDX_COUNT; ridx++) + for (ridx = RTWN_RIDX_CCK1; ridx < RTWN_RIDX_LEGACY_HT_COUNT; ridx++) printf("Rate %d = %u\n", ridx, power[ridx]); } #endif } static void r92e_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c) { uint8_t power[RTWN_RIDX_COUNT]; int i; for (i = 0; i < sc->ntxchains; i++) { memset(power, 0, sizeof(power)); /* Compute per-rate Tx power values. */ r92e_get_txpower(sc, i, c, power); /* Write per-rate Tx power values to hardware. */ r92c_write_txpower(sc, i, power); } } static void r92e_set_bw40(struct rtwn_softc *sc, uint8_t chan, int prichlo) { int i; rtwn_setbits_2(sc, R92C_WMAC_TRXPTCL_CTL, 0x100, 0x80); rtwn_write_1(sc, R12A_DATA_SEC, prichlo ? R12A_DATA_SEC_PRIM_DOWN_20 : R12A_DATA_SEC_PRIM_UP_20); rtwn_bb_setbits(sc, R92C_FPGA0_RFMOD, 0, R92C_RFMOD_40MHZ); rtwn_bb_setbits(sc, R92C_FPGA1_RFMOD, 0, R92C_RFMOD_40MHZ); /* Select 40MHz bandwidth. */ for (i = 0; i < sc->nrxchains; i++) rtwn_rf_setbits(sc, i, R92C_RF_CHNLBW, R88E_RF_CHNLBW_BW20, 0x400); /* Set CCK side band. */ rtwn_bb_setbits(sc, R92C_CCK0_SYSTEM, R92C_CCK0_SYSTEM_CCK_SIDEBAND, (prichlo ? 0 : 1) << 4); rtwn_bb_setbits(sc, R92C_OFDM1_LSTF, 0x0c00, (prichlo ? 1 : 2) << 10); rtwn_bb_setbits(sc, R92C_FPGA0_ANAPARAM2, R92C_FPGA0_ANAPARAM2_CBW20, 0); rtwn_bb_setbits(sc, 0x818, 0x0c000000, (prichlo ? 2 : 1) << 26); } static void r92e_set_bw20(struct rtwn_softc *sc, uint8_t chan) { int i; rtwn_setbits_2(sc, R92C_WMAC_TRXPTCL_CTL, 0x180, 0); rtwn_write_1(sc, R12A_DATA_SEC, R12A_DATA_SEC_NO_EXT); rtwn_bb_setbits(sc, R92C_FPGA0_RFMOD, R92C_RFMOD_40MHZ, 0); rtwn_bb_setbits(sc, R92C_FPGA1_RFMOD, R92C_RFMOD_40MHZ, 0); /* Select 20MHz bandwidth. */ for (i = 0; i < sc->nrxchains; i++) rtwn_rf_setbits(sc, i, R92C_RF_CHNLBW, R88E_RF_CHNLBW_BW20, 0xc00); rtwn_bb_setbits(sc, R92C_OFDM0_TXPSEUDONOISEWGT, 0xc0000000, 0); } void r92e_set_chan(struct rtwn_softc *sc, struct ieee80211_channel *c) { struct r92e_softc *rs = sc->sc_priv; u_int chan; int i; chan = rtwn_chan2centieee(c); for (i = 0; i < sc->nrxchains; i++) { rtwn_rf_write(sc, i, R92C_RF_CHNLBW, RW(rs->rf_chnlbw[0], R92C_RF_CHNLBW_CHNL, chan)); } if (IEEE80211_IS_CHAN_HT40(c)) r92e_set_bw40(sc, chan, IEEE80211_IS_CHAN_HT40U(c)); else r92e_set_bw20(sc, chan); /* Set Tx power for this new channel. */ r92e_set_txpower(sc, c); } diff --git a/sys/dev/rtwn/rtl8812a/r12a_rx.c b/sys/dev/rtwn/rtl8812a/r12a_rx.c index 763397636ac9..482e4ee38c81 100644 --- a/sys/dev/rtwn/rtl8812a/r12a_rx.c +++ b/sys/dev/rtwn/rtl8812a/r12a_rx.c @@ -1,315 +1,314 @@ /*- * Copyright (c) 2016 Andriy Voskoboinyk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef RTWN_WITHOUT_UCODE void r12a_ratectl_tx_complete(struct rtwn_softc *sc, uint8_t *buf, int len) { struct ieee80211_ratectl_tx_status txs; struct r12a_c2h_tx_rpt *rpt; struct ieee80211_node *ni; int ntries; /* Skip Rx descriptor / report id / sequence fields. */ buf += sizeof(struct r92c_rx_stat) + 2; len -= sizeof(struct r92c_rx_stat) + 2; rpt = (struct r12a_c2h_tx_rpt *)buf; if (len != sizeof(*rpt)) { device_printf(sc->sc_dev, "%s: wrong report size (%d, must be %zu)\n", __func__, len, sizeof(*rpt)); return; } RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: ccx report dump: 0: %02X, id: %02X, 2: %02X, queue time: " "low %02X, high %02X, final ridx: %02X, rsvd: %04X\n", __func__, rpt->txrptb0, rpt->macid, rpt->txrptb2, rpt->queue_time_low, rpt->queue_time_high, rpt->final_rate, rpt->reserved); if (rpt->macid > sc->macid_limit) { device_printf(sc->sc_dev, "macid %u is too big; increase MACID_MAX limit\n", rpt->macid); return; } ntries = MS(rpt->txrptb2, R12A_TXRPTB2_RETRY_CNT); ni = sc->node_list[rpt->macid]; if (ni != NULL) { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: frame for macid %u was" "%s sent (%d retries)\n", __func__, rpt->macid, (rpt->txrptb0 & (R12A_TXRPTB0_RETRY_OVER | R12A_TXRPTB0_LIFE_EXPIRE)) ? " not" : "", ntries); txs.flags = IEEE80211_RATECTL_STATUS_LONG_RETRY | IEEE80211_RATECTL_STATUS_FINAL_RATE; txs.long_retries = ntries; - if (rpt->final_rate > RTWN_RIDX_OFDM54) { /* MCS */ - txs.final_rate = - rpt->final_rate - RTWN_RIDX_HT_MCS_SHIFT; + if (RTWN_RATE_IS_HT(rpt->final_rate)) { /* MCS */ + txs.final_rate = RTWN_RIDX_TO_MCS(rpt->final_rate); txs.final_rate |= IEEE80211_RATE_MCS; } else txs.final_rate = ridx2rate[rpt->final_rate]; if (rpt->txrptb0 & R12A_TXRPTB0_RETRY_OVER) txs.status = IEEE80211_RATECTL_TX_FAIL_LONG; else if (rpt->txrptb0 & R12A_TXRPTB0_LIFE_EXPIRE) txs.status = IEEE80211_RATECTL_TX_FAIL_EXPIRED; else txs.status = IEEE80211_RATECTL_TX_SUCCESS; ieee80211_ratectl_tx_complete(ni, &txs); } else { RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: macid %u, ni is NULL\n", __func__, rpt->macid); } } void r12a_handle_c2h_report(struct rtwn_softc *sc, uint8_t *buf, int len) { struct r12a_softc *rs = sc->sc_priv; /* Skip Rx descriptor. */ buf += sizeof(struct r92c_rx_stat); len -= sizeof(struct r92c_rx_stat); if (len < 2) { device_printf(sc->sc_dev, "C2H report too short (len %d)\n", len); return; } len -= 2; switch (buf[0]) { /* command id */ case R12A_C2H_TX_REPORT: /* NOTREACHED */ KASSERT(0, ("use handle_tx_report() instead of %s\n", __func__)); break; case R12A_C2H_IQK_FINISHED: RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "FW IQ calibration finished\n"); rs->rs_flags &= ~R12A_IQK_RUNNING; break; default: device_printf(sc->sc_dev, "%s: C2H report %u was not handled\n", __func__, buf[0]); } } #else void r12a_ratectl_tx_complete(struct rtwn_softc *sc, uint8_t *buf, int len) { /* Should not happen. */ device_printf(sc->sc_dev, "%s: called\n", __func__); } void r12a_handle_c2h_report(struct rtwn_softc *sc, uint8_t *buf, int len) { /* Should not happen. */ device_printf(sc->sc_dev, "%s: called\n", __func__); } #endif int r12a_check_frame_checksum(struct rtwn_softc *sc, struct mbuf *m) { struct r12a_softc *rs = sc->sc_priv; struct r92c_rx_stat *stat; uint32_t rxdw1; stat = mtod(m, struct r92c_rx_stat *); rxdw1 = le32toh(stat->rxdw1); if (rxdw1 & R12A_RXDW1_CKSUM) { RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: %s/%s checksum is %s\n", __func__, (rxdw1 & R12A_RXDW1_UDP) ? "UDP" : "TCP", (rxdw1 & R12A_RXDW1_IPV6) ? "IPv6" : "IP", (rxdw1 & R12A_RXDW1_CKSUM_ERR) ? "invalid" : "valid"); if (rxdw1 & R12A_RXDW1_CKSUM_ERR) return (-1); if ((rxdw1 & R12A_RXDW1_IPV6) ? (rs->rs_flags & R12A_RXCKSUM6_EN) : (rs->rs_flags & R12A_RXCKSUM_EN)) { m->m_pkthdr.csum_flags = CSUM_IP_CHECKED | CSUM_IP_VALID | CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } return (0); } uint8_t r12a_rx_radiotap_flags(const void *buf) { const struct r92c_rx_stat *stat = buf; uint8_t flags, rate; if (!(stat->rxdw4 & htole32(R12A_RXDW4_SPLCP))) return (0); rate = MS(le32toh(stat->rxdw3), R12A_RXDW3_RATE); if (RTWN_RATE_IS_CCK(rate)) flags = IEEE80211_RADIOTAP_F_SHORTPRE; else flags = IEEE80211_RADIOTAP_F_SHORTGI; return (flags); } void r12a_get_rx_stats(struct rtwn_softc *sc, struct ieee80211_rx_stats *rxs, const void *desc, const void *physt_ptr) { const struct r92c_rx_stat *stat = desc; const struct r12a_rx_phystat *physt = physt_ptr; uint32_t rxdw0, rxdw1, rxdw3, rxdw4; uint8_t rate; rxdw0 = le32toh(stat->rxdw0); rxdw1 = le32toh(stat->rxdw1); rxdw3 = le32toh(stat->rxdw3); rxdw4 = le32toh(stat->rxdw4); rate = MS(rxdw3, R12A_RXDW3_RATE); /* TODO: STBC */ if (rxdw4 & R12A_RXDW4_LDPC) rxs->c_pktflags |= IEEE80211_RX_F_LDPC; if (rxdw1 & R12A_RXDW1_AMPDU) { if (rxdw0 & R92C_RXDW0_PHYST) rxs->c_pktflags |= IEEE80211_RX_F_AMPDU; else rxs->c_pktflags |= IEEE80211_RX_F_AMPDU_MORE; } - if ((rxdw4 & R12A_RXDW4_SPLCP) && rate >= RTWN_RIDX_HT_MCS(0)) + if ((rxdw4 & R12A_RXDW4_SPLCP) && RTWN_RATE_IS_HT(rate)) rxs->c_pktflags |= IEEE80211_RX_F_SHORTGI; switch (MS(rxdw4, R12A_RXDW4_BW)) { case R12A_RXDW4_BW20: rxs->c_width = IEEE80211_RX_FW_20MHZ; break; case R12A_RXDW4_BW40: rxs->c_width = IEEE80211_RX_FW_40MHZ; break; case R12A_RXDW4_BW80: rxs->c_width = IEEE80211_RX_FW_80MHZ; break; default: break; } if (RTWN_RATE_IS_CCK(rate)) rxs->c_phytype = IEEE80211_RX_FP_11B; else { int is5ghz; /* XXX magic */ /* XXX check with RTL8812AU */ is5ghz = (physt->cfosho[2] != 0x01); - if (rate < RTWN_RIDX_HT_MCS(0)) { + if (RTWN_RATE_IS_CCK(rate) || RTWN_RATE_IS_OFDM(rate)) { if (is5ghz) rxs->c_phytype = IEEE80211_RX_FP_11A; else rxs->c_phytype = IEEE80211_RX_FP_11G; } else { if (is5ghz) rxs->c_phytype = IEEE80211_RX_FP_11NA; else rxs->c_phytype = IEEE80211_RX_FP_11NG; } } /* Map HW rate index to 802.11 rate. */ - if (rate < RTWN_RIDX_HT_MCS(0)) { + if (RTWN_RATE_IS_CCK(rate) || RTWN_RATE_IS_OFDM(rate)) { rxs->c_rate = ridx2rate[rate]; if (RTWN_RATE_IS_CCK(rate)) rxs->c_pktflags |= IEEE80211_RX_F_CCK; else rxs->c_pktflags |= IEEE80211_RX_F_OFDM; } else { /* MCS0~15. */ /* TODO: VHT rates */ rxs->c_rate = - IEEE80211_RATE_MCS | (rate - RTWN_RIDX_HT_MCS_SHIFT); + IEEE80211_RATE_MCS | RTWN_RIDX_TO_MCS(rate); rxs->c_pktflags |= IEEE80211_RX_F_HT; } /* * XXX always zero for RTL8821AU * (vendor driver does not check this field) */ #if 0 rxs->r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; rxs->r_flags |= IEEE80211_R_BAND; rxs->c_ieee = MS(le16toh(physt->phyw1), R12A_PHYW1_CHAN); rxs->c_freq = ieee80211_ieee2mhz(rxs->c_ieee, (rxs->c_ieee < 36) ? IEEE80211_CHAN_2GHZ : IEEE80211_CHAN_5GHZ); rxs->c_band = (rxs->c_ieee < 36) ? IEEE80211_CHAN_2GHZ : IEEE80211_CHAN_5GHZ; #endif } diff --git a/sys/dev/rtwn/rtl8812a/r12a_tx.c b/sys/dev/rtwn/rtl8812a/r12a_tx.c index 8b16be17f8eb..822416a09618 100644 --- a/sys/dev/rtwn/rtl8812a/r12a_tx.c +++ b/sys/dev/rtwn/rtl8812a/r12a_tx.c @@ -1,478 +1,480 @@ /*- * Copyright (c) 2016 Andriy Voskoboinyk * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int r12a_get_primary_channel(struct rtwn_softc *sc, struct ieee80211_channel *c) { /* XXX VHT80; VHT40 */ if (IEEE80211_IS_CHAN_HT40U(c)) return (R12A_TXDW5_PRIM_CHAN_20_80_2); else return (R12A_TXDW5_PRIM_CHAN_20_80_3); } static void r12a_tx_set_ht40(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) { struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; /* XXX VHT80; VHT40; VHT20 */ if (ieee80211_ht_check_tx_ht40(ni)) { int prim_chan; prim_chan = r12a_get_primary_channel(sc, ni->ni_chan); txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_BW, R12A_TXDW5_DATA_BW40)); txd->txdw5 |= htole32(SM(R12A_TXDW5_DATA_PRIM_CHAN, prim_chan)); } } static void r12a_tx_protection(struct rtwn_softc *sc, struct r12a_tx_desc *txd, enum ieee80211_protmode mode, uint8_t ridx) { struct ieee80211com *ic = &sc->sc_ic; uint8_t rate; switch (mode) { case IEEE80211_PROT_CTSONLY: txd->txdw3 |= htole32(R12A_TXDW3_CTS2SELF); break; case IEEE80211_PROT_RTSCTS: txd->txdw3 |= htole32(R12A_TXDW3_RTSEN); break; default: break; } if (mode == IEEE80211_PROT_CTSONLY || mode == IEEE80211_PROT_RTSCTS) { - if (ridx >= RTWN_RIDX_HT_MCS(0)) + /* TODO: VHT */ + if (RTWN_RATE_IS_HT(ridx)) rate = rtwn_ctl_mcsrate(ic->ic_rt, ridx); else rate = ieee80211_ctl_rate(ic->ic_rt, ridx2rate[ridx]); ridx = rate2ridx(IEEE80211_RV(rate)); txd->txdw4 |= htole32(SM(R12A_TXDW4_RTSRATE, ridx)); /* RTS rate fallback limit (max). */ txd->txdw4 |= htole32(SM(R12A_TXDW4_RTSRATE_FB_LMT, 0xf)); if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) txd->txdw5 |= htole32(R12A_TXDW5_RTS_SHORT); } } static void r12a_tx_raid(struct rtwn_softc *sc, struct r12a_tx_desc *txd, struct ieee80211_node *ni, int ismcast) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_channel *chan; enum ieee80211_phymode mode; uint8_t raid; chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? ni->ni_chan : ic->ic_curchan; mode = ieee80211_chan2mode(chan); /* NB: group addressed frames are done at 11bg rates for now */ if (ismcast || !(ni->ni_flags & IEEE80211_NODE_HT)) { switch (mode) { case IEEE80211_MODE_11A: case IEEE80211_MODE_11B: case IEEE80211_MODE_11G: break; case IEEE80211_MODE_11NA: mode = IEEE80211_MODE_11A; break; case IEEE80211_MODE_11NG: mode = IEEE80211_MODE_11G; break; case IEEE80211_MODE_VHT_5GHZ: mode = IEEE80211_MODE_VHT_5GHZ; break; default: device_printf(sc->sc_dev, "unknown mode(1) %d!\n", ic->ic_curmode); return; } } switch (mode) { case IEEE80211_MODE_11A: raid = R12A_RAID_11G; break; case IEEE80211_MODE_11B: raid = R12A_RAID_11B; break; case IEEE80211_MODE_11G: if (vap->iv_flags & IEEE80211_F_PUREG) raid = R12A_RAID_11G; else raid = R12A_RAID_11BG; break; case IEEE80211_MODE_11NA: if (sc->ntxchains == 1) raid = R12A_RAID_11GN_1; else raid = R12A_RAID_11GN_2; break; case IEEE80211_MODE_11NG: if (sc->ntxchains == 1) { if (IEEE80211_IS_CHAN_HT40(chan)) raid = R12A_RAID_11BGN_1_40; else raid = R12A_RAID_11BGN_1; } else { if (IEEE80211_IS_CHAN_HT40(chan)) raid = R12A_RAID_11BGN_2_40; else raid = R12A_RAID_11BGN_2; } break; case IEEE80211_MODE_VHT_5GHZ: if (sc->ntxchains == 1) raid = R12A_RAID_11AC_1; else raid = R12A_RAID_11AC_2; break; default: device_printf(sc->sc_dev, "unknown mode(2) %d!\n", mode); return; } txd->txdw1 |= htole32(SM(R12A_TXDW1_RAID, raid)); } static void r12a_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) { struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; /* TODO: VHT 20/40/80 checks */ /* * Only enable short-GI if we're transmitting in that * width to that node. * * Specifically, do not enable shortgi for 20MHz if * we're attempting to transmit at 40MHz. */ if (ieee80211_ht_check_tx_ht40(ni)) { if (ieee80211_ht_check_tx_shortgi_40(ni)) txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); } else if (ieee80211_ht_check_tx_ht(ni)) { if (ieee80211_ht_check_tx_shortgi_20(ni)) txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); } } static void r12a_tx_set_ldpc(struct rtwn_softc *sc, struct r12a_tx_desc *txd, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; if ((vap->iv_flags_ht & IEEE80211_FHT_LDPC_TX) && (ni->ni_htcap & IEEE80211_HTCAP_LDPC)) txd->txdw5 |= htole32(R12A_TXDW5_DATA_LDPC); } static int r12a_calculate_tx_agg_window(struct rtwn_softc *sc, const struct ieee80211_node *ni, int tid) { const struct ieee80211_tx_ampdu *tap; int wnd; tap = &ni->ni_tx_ampdu[tid]; /* * BAW is (MAX_AGG * 2) + 1, hence the /2 here. * Ensure we don't send 0 or more than 64. */ wnd = tap->txa_wnd / 2; if (wnd == 0) wnd = 1; else if (wnd > 0x1f) wnd = 0x1f; return (wnd); } void r12a_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, void *buf, uint8_t ridx, int maxretry) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct rtwn_vap *uvp = RTWN_VAP(vap); struct ieee80211_frame *wh; struct r12a_tx_desc *txd; enum ieee80211_protmode prot; uint8_t type, tid, qos, qsel; int hasqos, ismcast, macid; wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; hasqos = IEEE80211_QOS_HAS_SEQ(wh); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Select TX ring for this frame. */ if (hasqos) { qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; } else { qos = 0; tid = 0; } /* Fill Tx descriptor. */ txd = (struct r12a_tx_desc *)buf; txd->flags0 |= R12A_FLAGS0_LSG | R12A_FLAGS0_FSG; if (ismcast) txd->flags0 |= R12A_FLAGS0_BMCAST; if (!ismcast) { /* Unicast frame, check if an ACK is expected. */ if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK) { txd->txdw4 = htole32(R12A_TXDW4_RETRY_LMT_ENA); txd->txdw4 |= htole32(SM(R12A_TXDW4_RETRY_LMT, maxretry)); } struct rtwn_node *un = RTWN_NODE(ni); macid = un->id; if (type == IEEE80211_FC0_TYPE_DATA) { qsel = tid % RTWN_MAX_TID; if (m->m_flags & M_AMPDU_MPDU) { txd->txdw2 |= htole32(R12A_TXDW2_AGGEN); txd->txdw2 |= htole32(SM(R12A_TXDW2_AMPDU_DEN, ieee80211_ht_get_node_ampdu_density(ni))); txd->txdw3 |= htole32(SM(R12A_TXDW3_MAX_AGG, r12a_calculate_tx_agg_window(sc, ni, tid))); } else txd->txdw2 |= htole32(R12A_TXDW2_AGGBK); if (sc->sc_ratectl == RTWN_RATECTL_NET80211) { txd->txdw2 |= htole32(R12A_TXDW2_SPE_RPT); sc->sc_tx_n_active++; } if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); prot = IEEE80211_PROT_NONE; - if (ridx >= RTWN_RIDX_HT_MCS(0)) { + /* TODO: VHT */ + if (RTWN_RATE_IS_HT(ridx)) { r12a_tx_set_ht40(sc, txd, ni); r12a_tx_set_sgi(sc, txd, ni); r12a_tx_set_ldpc(sc, txd, ni); prot = ic->ic_htprotmode; } else if (ic->ic_flags & IEEE80211_F_USEPROT) prot = ic->ic_protmode; /* XXX fix last comparison for A-MSDU (in net80211) */ /* XXX A-MPDU? */ if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold && vap->iv_rtsthreshold != IEEE80211_RTS_MAX) prot = IEEE80211_PROT_RTSCTS; if (prot != IEEE80211_PROT_NONE) r12a_tx_protection(sc, txd, prot, ridx); } else /* IEEE80211_FC0_TYPE_MGT */ qsel = R12A_TXDW1_QSEL_MGNT; } else { macid = RTWN_MACID_BC; qsel = R12A_TXDW1_QSEL_MGNT; } txd->txdw1 |= htole32(SM(R12A_TXDW1_QSEL, qsel)); txd->txdw1 |= htole32(SM(R12A_TXDW1_MACID, macid)); txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE, ridx)); /* Data rate fallback limit (max). */ txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE_FB_LMT, 0x1f)); /* XXX recheck for non-21au */ txd->txdw6 |= htole32(SM(R21A_TXDW6_MBSSID, uvp->id)); r12a_tx_raid(sc, txd, ni, ismcast); /* Force this rate if needed. */ if (sc->sc_ratectl != RTWN_RATECTL_FW) txd->txdw3 |= htole32(R12A_TXDW3_DRVRATE); if (!hasqos) { /* Use HW sequence numbering for non-QoS frames. */ txd->txdw8 |= htole32(R12A_TXDW8_HWSEQ_EN); txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, uvp->id)); } else { uint16_t seqno; if (m->m_flags & M_AMPDU_MPDU) { seqno = ni->ni_txseqs[tid]; ni->ni_txseqs[tid]++; } else seqno = M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE; /* Set sequence number. */ txd->txdw9 |= htole32(SM(R12A_TXDW9_SEQ, seqno)); } } void r12a_fill_tx_desc_raw(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, void *buf, const struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; struct rtwn_vap *uvp = RTWN_VAP(vap); struct ieee80211_frame *wh; struct r12a_tx_desc *txd; uint8_t ridx; int ismcast; /* XXX TODO: 11n checks, matching rtwn_fill_tx_desc() */ wh = mtod(m, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); ridx = rate2ridx(params->ibp_rate0); /* Fill Tx descriptor. */ txd = (struct r12a_tx_desc *)buf; txd->flags0 |= R12A_FLAGS0_LSG | R12A_FLAGS0_FSG; if (ismcast) txd->flags0 |= R12A_FLAGS0_BMCAST; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { txd->txdw4 = htole32(R12A_TXDW4_RETRY_LMT_ENA); txd->txdw4 |= htole32(SM(R12A_TXDW4_RETRY_LMT, params->ibp_try0)); } if (params->ibp_flags & IEEE80211_BPF_RTS) r12a_tx_protection(sc, txd, IEEE80211_PROT_RTSCTS, ridx); if (params->ibp_flags & IEEE80211_BPF_CTS) r12a_tx_protection(sc, txd, IEEE80211_PROT_CTSONLY, ridx); txd->txdw1 |= htole32(SM(R12A_TXDW1_MACID, RTWN_MACID_BC)); txd->txdw1 |= htole32(SM(R12A_TXDW1_QSEL, R12A_TXDW1_QSEL_MGNT)); /* Set TX rate index. */ txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE, ridx)); txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE_FB_LMT, 0x1f)); txd->txdw6 |= htole32(SM(R21A_TXDW6_MBSSID, uvp->id)); txd->txdw3 |= htole32(R12A_TXDW3_DRVRATE); r12a_tx_raid(sc, txd, ni, ismcast); if (!IEEE80211_QOS_HAS_SEQ(wh)) { /* Use HW sequence numbering for non-QoS frames. */ txd->txdw8 |= htole32(R12A_TXDW8_HWSEQ_EN); txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, uvp->id)); } else { /* Set sequence number. */ txd->txdw9 |= htole32(SM(R12A_TXDW9_SEQ, M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE)); } } void r12a_fill_tx_desc_null(struct rtwn_softc *sc, void *buf, int is11b, int qos, int id) { struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; txd->flags0 = R12A_FLAGS0_FSG | R12A_FLAGS0_LSG | R12A_FLAGS0_OWN; txd->txdw1 = htole32( SM(R12A_TXDW1_QSEL, R12A_TXDW1_QSEL_MGNT)); txd->txdw3 = htole32(R12A_TXDW3_DRVRATE); txd->txdw6 = htole32(SM(R21A_TXDW6_MBSSID, id)); if (is11b) { txd->txdw4 = htole32(SM(R12A_TXDW4_DATARATE, RTWN_RIDX_CCK1)); } else { txd->txdw4 = htole32(SM(R12A_TXDW4_DATARATE, RTWN_RIDX_OFDM6)); } if (!qos) { txd->txdw8 = htole32(R12A_TXDW8_HWSEQ_EN); txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, id)); } } uint8_t r12a_tx_radiotap_flags(const void *buf) { const struct r12a_tx_desc *txd = buf; uint8_t flags, rate; if (!(txd->txdw5 & htole32(R12A_TXDW5_DATA_SHORT))) return (0); rate = MS(le32toh(txd->txdw4), R12A_TXDW4_DATARATE); if (RTWN_RATE_IS_CCK(rate)) flags = IEEE80211_RADIOTAP_F_SHORTPRE; else flags = IEEE80211_RADIOTAP_F_SHORTGI; return (flags); }