Changeset View
Changeset View
Standalone View
Standalone View
sys/net80211/ieee80211.c
Show First 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | static int ieee80211_media_setup(struct ieee80211com *ic, | ||||
struct ifmedia *media, int caps, int addsta, | struct ifmedia *media, int caps, int addsta, | ||||
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); | ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); | ||||
static int media_status(enum ieee80211_opmode, | static int media_status(enum ieee80211_opmode, | ||||
const struct ieee80211_channel *); | const struct ieee80211_channel *); | ||||
static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); | static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); | ||||
MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); | MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); | ||||
/* | |||||
* Default supported rates for 802.11 operation (in IEEE .5Mb units). | |||||
*/ | |||||
#define B(r) ((r) | IEEE80211_RATE_BASIC) | |||||
static const struct ieee80211_rateset ieee80211_rateset_11a = | |||||
{ 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; | |||||
static const struct ieee80211_rateset ieee80211_rateset_half = | |||||
{ 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; | |||||
static const struct ieee80211_rateset ieee80211_rateset_quarter = | |||||
{ 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; | |||||
static const struct ieee80211_rateset ieee80211_rateset_11b = | |||||
{ 4, { B(2), B(4), B(11), B(22) } }; | |||||
/* NB: OFDM rates are handled specially based on mode */ | |||||
static const struct ieee80211_rateset ieee80211_rateset_11g = | |||||
{ 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; | |||||
#undef B | |||||
static int set_vht_extchan(struct ieee80211_channel *c); | static int set_vht_extchan(struct ieee80211_channel *c); | ||||
static const struct ieee80211_rateset null_rateset; | |||||
/* | /* | ||||
* Fill in 802.11 available channel set, mark | * Fill in 802.11 available channel set, mark | ||||
* all available channels as active, and pick | * all available channels as active, and pick | ||||
* a default channel if not already specified. | * a default channel if not already specified. | ||||
*/ | */ | ||||
void | void | ||||
ieee80211_chan_init(struct ieee80211com *ic) | ieee80211_chan_init(struct ieee80211com *ic) | ||||
{ | { | ||||
#define DEFAULTRATES(m, def) do { \ | |||||
if (ic->ic_sup_rates[m].rs_nrates == 0) \ | |||||
ic->ic_sup_rates[m] = def; \ | |||||
} while (0) | |||||
struct ieee80211_channel *c; | struct ieee80211_channel *c; | ||||
int i; | int i; | ||||
KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, | KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, | ||||
("invalid number of channels specified: %u", ic->ic_nchans)); | ("invalid number of channels specified: %u", ic->ic_nchans)); | ||||
memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); | memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); | ||||
memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); | memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); | ||||
setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); | setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | ieee80211_chan_init(struct ieee80211com *ic) | ||||
ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); | ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); | ||||
/* invalidate any previous state */ | /* invalidate any previous state */ | ||||
ic->ic_bsschan = IEEE80211_CHAN_ANYC; | ic->ic_bsschan = IEEE80211_CHAN_ANYC; | ||||
ic->ic_prevchan = NULL; | ic->ic_prevchan = NULL; | ||||
ic->ic_csa_newchan = NULL; | ic->ic_csa_newchan = NULL; | ||||
/* arbitrarily pick the first channel */ | /* arbitrarily pick the first channel */ | ||||
ic->ic_curchan = &ic->ic_channels[0]; | ic->ic_curchan = &ic->ic_channels[0]; | ||||
ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); | ic->ic_rt = | ||||
ieee80211_get_ratetable(ieee80211_chan2mode(ic->ic_curchan)); | |||||
/* fillin well-known rate sets if driver has not specified */ | /* fillin well-known rate sets if driver has not specified */ | ||||
DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); | /* NB: skip AUTO mode */ | ||||
DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); | for (i = IEEE80211_MODE_AUTO + 1; i < IEEE80211_MODE_MAX; i++) { | ||||
DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); | if (ic->ic_sup_rates[i] != NULL) | ||||
DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); | continue; | ||||
DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); | |||||
DEFAULTRATES(IEEE80211_MODE_STURBO_A, ieee80211_rateset_11a); | |||||
DEFAULTRATES(IEEE80211_MODE_HALF, ieee80211_rateset_half); | |||||
DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter); | |||||
DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); | |||||
DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); | |||||
DEFAULTRATES(IEEE80211_MODE_VHT_2GHZ, ieee80211_rateset_11g); | |||||
DEFAULTRATES(IEEE80211_MODE_VHT_5GHZ, ieee80211_rateset_11a); | |||||
if (isset(ic->ic_modecaps, i)) | |||||
ic->ic_sup_rates[i] = ieee80211_get_def_rateset(i); | |||||
else | |||||
ic->ic_sup_rates[i] = &null_rateset; | |||||
} | |||||
/* | /* | ||||
* Setup required information to fill the mcsset field, if driver did | * Setup required information to fill the mcsset field, if driver did | ||||
* not. Assume a 2T2R setup for historic reasons. | * not. Assume a 2T2R setup for historic reasons. | ||||
*/ | */ | ||||
if (ic->ic_rxstream == 0) | if (ic->ic_rxstream == 0) | ||||
ic->ic_rxstream = 2; | ic->ic_rxstream = 2; | ||||
if (ic->ic_txstream == 0) | if (ic->ic_txstream == 0) | ||||
ic->ic_txstream = 2; | ic->ic_txstream = 2; | ||||
ieee80211_init_suphtrates(ic); | ieee80211_init_suphtrates(ic); | ||||
/* | /* | ||||
* Set auto mode to reset active channel state and any desired channel. | * Set auto mode to reset active channel state and any desired channel. | ||||
*/ | */ | ||||
(void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); | ieee80211_setmode(ic, IEEE80211_MODE_AUTO); | ||||
#undef DEFAULTRATES | |||||
} | } | ||||
static void | static void | ||||
null_update_mcast(struct ieee80211com *ic) | null_update_mcast(struct ieee80211com *ic) | ||||
{ | { | ||||
ic_printf(ic, "need multicast update callback\n"); | ic_printf(ic, "need multicast update callback\n"); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,538 Lines • ▼ Show 20 Lines | |||||
* Setup the media data structures according to the channel and | * Setup the media data structures according to the channel and | ||||
* rate tables. | * rate tables. | ||||
*/ | */ | ||||
static int | static int | ||||
ieee80211_media_setup(struct ieee80211com *ic, | ieee80211_media_setup(struct ieee80211com *ic, | ||||
struct ifmedia *media, int caps, int addsta, | struct ifmedia *media, int caps, int addsta, | ||||
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) | ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) | ||||
{ | { | ||||
int i, j, rate, maxrate, mword, r; | #define MAX_RATES 24 | ||||
enum ieee80211_phymode mode; | |||||
const struct ieee80211_rateset *rs; | const struct ieee80211_rateset *rs; | ||||
struct ieee80211_rateset allrates; | const struct ieee80211_rate_t *rate; | ||||
enum ieee80211_phymode mode; | |||||
uint16_t rate_idx; | |||||
uint8_t allrates[MAX_RATES]; /* NB: rateset may be too small */ | |||||
uint8_t rate_mask[IEEE80211_RATE_BITMAP_SIZE]; | |||||
uint8_t value; | |||||
int i, maxrate, mword, rates_count; | |||||
/* | /* | ||||
* Fill in media characteristics. | * Fill in media characteristics. | ||||
*/ | */ | ||||
ifmedia_init(media, 0, media_change, media_stat); | ifmedia_init(media, 0, media_change, media_stat); | ||||
maxrate = 0; | maxrate = 0; | ||||
rates_count = 0; | |||||
/* | /* | ||||
* Add media for legacy operating modes. | * Add media for legacy operating modes. | ||||
*/ | */ | ||||
memset(&allrates, 0, sizeof(allrates)); | memset(&allrates, 0, sizeof(allrates)); | ||||
memset(rate_mask, 0, sizeof(rate_mask)); | |||||
for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { | for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { | ||||
if (isclr(ic->ic_modecaps, mode)) | if (isclr(ic->ic_modecaps, mode)) | ||||
continue; | continue; | ||||
addmedia(media, caps, addsta, mode, IFM_AUTO); | addmedia(media, caps, addsta, mode, IFM_AUTO); | ||||
if (mode == IEEE80211_MODE_AUTO) | if (mode == IEEE80211_MODE_AUTO) | ||||
continue; | continue; | ||||
rs = &ic->ic_sup_rates[mode]; | rs = ic->ic_sup_rates[mode]; | ||||
for (i = 0; i < rs->rs_nrates; i++) { | for (i = 0; i < rs->rs_nrates; i++) { | ||||
rate = rs->rs_rates[i]; | rate_idx = rs->rates[i].rs_index; | ||||
rate = ieee80211_get_rate(rate_idx); | |||||
mword = ieee80211_rate2media(ic, rate, mode); | mword = ieee80211_rate2media(ic, rate, mode); | ||||
if (mword == 0) | if (mword == 0) | ||||
continue; | continue; | ||||
addmedia(media, caps, addsta, mode, mword); | addmedia(media, caps, addsta, mode, mword); | ||||
/* | /* | ||||
* Add legacy rate to the collection of all rates. | * Add legacy rate to the collection of all rates. | ||||
*/ | */ | ||||
r = rate & IEEE80211_RATE_VAL; | value = rate->value; | ||||
for (j = 0; j < allrates.rs_nrates; j++) | KASSERT(value < IEEE80211_RATE_VALUE_MAX, | ||||
if (allrates.rs_rates[j] == r) | ("increase RATE_VALUE_MAX constant")); | ||||
break; | if (!isset(rate_mask, value)) { | ||||
if (j == allrates.rs_nrates) { | |||||
/* unique, add to the set */ | /* unique, add to the set */ | ||||
allrates.rs_rates[j] = r; | setbit(rate_mask, value); | ||||
allrates.rs_nrates++; | allrates[rates_count++] = rate_idx; | ||||
} | } | ||||
rate = (rate & IEEE80211_RATE_VAL) / 2; | if (value > maxrate) | ||||
if (rate > maxrate) | maxrate = value; | ||||
maxrate = rate; | |||||
} | } | ||||
} | } | ||||
for (i = 0; i < allrates.rs_nrates; i++) { | for (i = 0; i < rates_count; i++) { | ||||
mword = ieee80211_rate2media(ic, allrates.rs_rates[i], | rate = ieee80211_get_rate(allrates[i]); | ||||
IEEE80211_MODE_AUTO); | mword = ieee80211_rate2media(ic, rate, IEEE80211_MODE_AUTO); | ||||
if (mword == 0) | if (mword == 0) | ||||
continue; | continue; | ||||
/* NB: remove media options from mword */ | /* NB: remove media options from mword */ | ||||
addmedia(media, caps, addsta, | addmedia(media, caps, addsta, | ||||
IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); | IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); | ||||
} | } | ||||
/* | /* | ||||
* Add HT/11n media. Note that we do not have enough | * Add HT/11n media. Note that we do not have enough | ||||
* bits in the media subtype to express the MCS so we | * bits in the media subtype to express the MCS so we | ||||
* use a "placeholder" media subtype and any fixed MCS | * use a "placeholder" media subtype and any fixed MCS | ||||
* must be specified with a different mechanism. | * must be specified with a different mechanism. | ||||
*/ | */ | ||||
for (; mode <= IEEE80211_MODE_11NG; mode++) { | for (; mode <= IEEE80211_MODE_11NG; mode++) { | ||||
if (isclr(ic->ic_modecaps, mode)) | if (isclr(ic->ic_modecaps, mode)) | ||||
continue; | continue; | ||||
addmedia(media, caps, addsta, mode, IFM_AUTO); | addmedia(media, caps, addsta, mode, IFM_AUTO); | ||||
addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); | addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); | ||||
} | } | ||||
if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || | if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || | ||||
isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { | isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { | ||||
uint32_t chan_flags; | |||||
int sgi; | |||||
addmedia(media, caps, addsta, | addmedia(media, caps, addsta, | ||||
IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); | IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); | ||||
i = ic->ic_txstream * 8 - 1; | |||||
if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && | rate_idx = IEEE80211_RATE_INDEX_HT( | ||||
(ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) | ic->ic_txstream * IEEE80211_HT_MCS_CHAIN - 1); | ||||
rate = ieee80211_htrates[i].ht40_rate_400ns; | |||||
else if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40)) | if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { | ||||
rate = ieee80211_htrates[i].ht40_rate_800ns; | chan_flags = IEEE80211_CHAN_HT40; | ||||
else if ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)) | sgi = (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) != 0; | ||||
rate = ieee80211_htrates[i].ht20_rate_400ns; | } else { | ||||
else | chan_flags = IEEE80211_CHAN_HT20; | ||||
rate = ieee80211_htrates[i].ht20_rate_800ns; | sgi = (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) != 0; | ||||
if (rate > maxrate) | |||||
maxrate = rate; | |||||
} | } | ||||
value = ieee80211_get_rateKbps_by_idx(rate_idx, | |||||
chan_flags, sgi); | |||||
value /= 500; | |||||
if (value > maxrate) | |||||
maxrate = value; | |||||
} | |||||
/* | /* | ||||
* Add VHT media. | * Add VHT media. | ||||
*/ | */ | ||||
for (; mode <= IEEE80211_MODE_VHT_5GHZ; mode++) { | for (; mode <= IEEE80211_MODE_VHT_5GHZ; mode++) { | ||||
if (isclr(ic->ic_modecaps, mode)) | if (isclr(ic->ic_modecaps, mode)) | ||||
continue; | continue; | ||||
addmedia(media, caps, addsta, mode, IFM_AUTO); | addmedia(media, caps, addsta, mode, IFM_AUTO); | ||||
addmedia(media, caps, addsta, mode, IFM_IEEE80211_VHT); | addmedia(media, caps, addsta, mode, IFM_IEEE80211_VHT); | ||||
/* XXX TODO: VHT maxrate */ | |||||
} | } | ||||
if (isset(ic->ic_modecaps, IEEE80211_MODE_VHT_2GHZ) || | |||||
isset(ic->ic_modecaps, IEEE80211_MODE_VHT_5GHZ)) { | |||||
uint32_t chan_flags; | |||||
uint8_t vht_chan_cap; | |||||
int sgi; | |||||
return maxrate; | addmedia(media, caps, addsta, | ||||
IEEE80211_MODE_AUTO, IFM_IEEE80211_VHT); | |||||
/* XXX assume only MCS7 for now */ | |||||
rate_idx = IEEE80211_RATE_INDEX_VHT(7, ic->ic_txstream); | |||||
/* XXX create / move to ieee80211_vht_get_max_mcs() */ | |||||
/* XXX should check vap caps instead */ | |||||
vht_chan_cap = | |||||
(ic->ic_vhtcaps & IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK); | |||||
vht_chan_cap >>= IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S; | |||||
if (vht_chan_cap >= IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ) { | |||||
chan_flags = IEEE80211_CHAN_VHT160; | |||||
sgi = (ic->ic_vhtcaps & IEEE80211_VHTCAP_SHORT_GI_160); | |||||
sgi >>= IEEE80211_VHTCAP_SHORT_GI_160_S; | |||||
} else if (vht_chan_cap == | |||||
IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80P80MHZ) { | |||||
chan_flags = IEEE80211_CHAN_VHT80_80; | |||||
sgi = (ic->ic_vhtcaps & IEEE80211_VHTCAP_SHORT_GI_80); | |||||
sgi >>= IEEE80211_VHTCAP_SHORT_GI_80_S; | |||||
} else { | |||||
chan_flags = IEEE80211_CHAN_VHT80; | |||||
/* XXX unknown (has no capability bits yet). */ | |||||
sgi = 0; | |||||
} | } | ||||
/* XXX inline or eliminate? */ | value = ieee80211_get_rateKbps_by_idx(rate_idx, | ||||
const struct ieee80211_rateset * | chan_flags, sgi); | ||||
ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) | value /= 500; | ||||
{ | if (value > maxrate) | ||||
/* XXX does this work for 11ng basic rates? */ | maxrate = value; | ||||
return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; | |||||
} | } | ||||
/* XXX inline or eliminate? */ | return maxrate; | ||||
const struct ieee80211_htrateset * | #undef MAX_RATES | ||||
ieee80211_get_suphtrates(struct ieee80211com *ic, | |||||
const struct ieee80211_channel *c) | |||||
{ | |||||
return &ic->ic_sup_htrates; | |||||
} | } | ||||
void | void | ||||
ieee80211_announce(struct ieee80211com *ic) | ieee80211_announce(struct ieee80211com *ic) | ||||
{ | { | ||||
int i, rate, mword; | |||||
enum ieee80211_phymode mode; | |||||
const struct ieee80211_rateset *rs; | const struct ieee80211_rateset *rs; | ||||
const struct ieee80211_rate_t *rate; | |||||
enum ieee80211_phymode mode; | |||||
uint32_t speed; | |||||
int i; | |||||
/* NB: skip AUTO since it has no rates */ | /* NB: skip AUTO since it has no rates */ | ||||
for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { | for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { | ||||
if (isclr(ic->ic_modecaps, mode)) | if (isclr(ic->ic_modecaps, mode)) | ||||
continue; | continue; | ||||
ic_printf(ic, "%s rates: ", ieee80211_phymode_name[mode]); | ic_printf(ic, "%s rates: ", ieee80211_phymode_name[mode]); | ||||
rs = &ic->ic_sup_rates[mode]; | rs = ic->ic_sup_rates[mode]; | ||||
for (i = 0; i < rs->rs_nrates; i++) { | for (i = 0; i < rs->rs_nrates; i++) { | ||||
mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); | rate = ieee80211_get_rate(rs->rates[i].rs_index); | ||||
if (mword == 0) | speed = ieee80211_get_rateKbps(rate, 0, 0); | ||||
continue; | speed /= 500; | ||||
rate = ieee80211_media2rate(mword); | |||||
printf("%s%d%sMbps", (i != 0 ? " " : ""), | printf("%s%d%sMbps", (i != 0 ? " " : ""), | ||||
rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); | speed / 2, ((speed & 0x1) != 0 ? ".5" : "")); | ||||
} | } | ||||
printf("\n"); | printf("\n"); | ||||
} | } | ||||
ieee80211_ht_announce(ic); | ieee80211_ht_announce(ic); | ||||
ieee80211_vht_announce(ic); | ieee80211_vht_announce(ic); | ||||
} | } | ||||
void | void | ||||
▲ Show 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | if (IEEE80211_IS_CHAN_HT40(chan)) | ||||
status |= IFM_IEEE80211_HT40; | status |= IFM_IEEE80211_HT40; | ||||
#endif | #endif | ||||
return status; | return status; | ||||
} | } | ||||
void | void | ||||
ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) | ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) | ||||
{ | { | ||||
const struct ieee80211_rate_t *rate; | |||||
struct ieee80211vap *vap = ifp->if_softc; | struct ieee80211vap *vap = ifp->if_softc; | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
enum ieee80211_phymode mode; | enum ieee80211_phymode mode; | ||||
imr->ifm_status = IFM_AVALID; | imr->ifm_status = IFM_AVALID; | ||||
/* | /* | ||||
* NB: use the current channel's mode to lock down a xmit | * NB: use the current channel's mode to lock down a xmit | ||||
* rate only when running; otherwise we may have a mismatch | * rate only when running; otherwise we may have a mismatch | ||||
* in which case the rate will not be convertible. | * in which case the rate will not be convertible. | ||||
*/ | */ | ||||
if (vap->iv_state == IEEE80211_S_RUN || | if (vap->iv_state == IEEE80211_S_RUN || | ||||
vap->iv_state == IEEE80211_S_SLEEP) { | vap->iv_state == IEEE80211_S_SLEEP) { | ||||
imr->ifm_status |= IFM_ACTIVE; | imr->ifm_status |= IFM_ACTIVE; | ||||
mode = ieee80211_chan2mode(ic->ic_curchan); | mode = ieee80211_chan2mode(ic->ic_curchan); | ||||
} else | } else | ||||
mode = IEEE80211_MODE_AUTO; | mode = IEEE80211_MODE_AUTO; | ||||
imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); | imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); | ||||
/* | /* | ||||
* Calculate a current rate if possible. | * Calculate a current rate if possible. | ||||
*/ | */ | ||||
if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { | if (vap->iv_txparms[mode].ucastrate != IEEE80211_RATE_NONEXISTENT) { | ||||
/* | /* | ||||
* A fixed rate is set, report that. | * A fixed rate is set, report that. | ||||
*/ | */ | ||||
imr->ifm_active |= ieee80211_rate2media(ic, | rate = ieee80211_get_rate(vap->iv_txparms[mode].ucastrate); | ||||
vap->iv_txparms[mode].ucastrate, mode); | imr->ifm_active |= ieee80211_rate2media(ic, rate, mode); | ||||
} else if (vap->iv_opmode == IEEE80211_M_STA) { | } else if (vap->iv_opmode == IEEE80211_M_STA) { | ||||
/* | /* | ||||
* In station mode report the current transmit rate. | * In station mode report the current transmit rate. | ||||
*/ | */ | ||||
imr->ifm_active |= ieee80211_rate2media(ic, | rate = ieee80211_get_rate(vap->iv_bss->ni_txrate); | ||||
vap->iv_bss->ni_txrate, mode); | imr->ifm_active |= ieee80211_rate2media(ic, rate, mode); | ||||
} else | } else | ||||
imr->ifm_active |= IFM_AUTO; | imr->ifm_active |= IFM_AUTO; | ||||
if (imr->ifm_status & IFM_ACTIVE) | if (imr->ifm_status & IFM_ACTIVE) | ||||
imr->ifm_current = imr->ifm_active; | imr->ifm_current = imr->ifm_active; | ||||
} | } | ||||
/* | /* | ||||
* Set the current phy mode and recalculate the active channel | * Set the current phy mode and recalculate the active channel | ||||
* set based on the available channels for this mode. Also | * set based on the available channels for this mode. Also | ||||
* select a new default/current channel if the current one is | * select a new default/current channel if the current one is | ||||
* inappropriate for this mode. | * inappropriate for this mode. | ||||
*/ | */ | ||||
int | void | ||||
ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) | ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) | ||||
{ | { | ||||
/* | |||||
* Adjust basic rates in 11b/11g supported rate set. | |||||
* Note that if operating on a hal/quarter rate channel | |||||
* this is a noop as those rates sets are different | |||||
* and used instead. | |||||
*/ | |||||
if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) | |||||
ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); | |||||
ic->ic_curmode = mode; | ic->ic_curmode = mode; | ||||
ieee80211_reset_erp(ic); /* reset ERP state */ | ieee80211_reset_erp(ic); /* reset ERP state */ | ||||
return 0; | |||||
} | } | ||||
/* | /* | ||||
* Return the phy mode for with the specified channel. | * Return the phy mode for with the specified channel. | ||||
*/ | */ | ||||
enum ieee80211_phymode | enum ieee80211_phymode | ||||
ieee80211_chan2mode(const struct ieee80211_channel *chan) | ieee80211_chan2mode(const struct ieee80211_channel *chan) | ||||
{ | { | ||||
Show All 26 Lines | else if (IEEE80211_IS_CHAN_FHSS(chan)) | ||||
return IEEE80211_MODE_FH; | return IEEE80211_MODE_FH; | ||||
/* NB: should not get here */ | /* NB: should not get here */ | ||||
printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", | printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", | ||||
__func__, chan->ic_freq, chan->ic_flags); | __func__, chan->ic_freq, chan->ic_flags); | ||||
return IEEE80211_MODE_11B; | return IEEE80211_MODE_11B; | ||||
} | } | ||||
struct ratemedia { | static const u_int rates_fh[] = { | ||||
u_int match; /* rate + mode */ | [2] = IFM_IEEE80211_FH1, | ||||
u_int media; /* if_media rate */ | [4] = IFM_IEEE80211_FH2 | ||||
}; | }; | ||||
static int | static const u_int rates_11b[] = { | ||||
findmedia(const struct ratemedia rates[], int n, u_int match) | [2] = IFM_IEEE80211_DS1, | ||||
[4] = IFM_IEEE80211_DS2, | |||||
[11] = IFM_IEEE80211_DS5, | |||||
[22] = IFM_IEEE80211_DS11, | |||||
[44] = IFM_IEEE80211_DS22 | |||||
}; | |||||
static const u_int rates_11a[] = { | |||||
[6] = IFM_IEEE80211_OFDM3, | |||||
[9] = IFM_IEEE80211_OFDM4, | |||||
[12] = IFM_IEEE80211_OFDM6, | |||||
[18] = IFM_IEEE80211_OFDM9, | |||||
[24] = IFM_IEEE80211_OFDM12, | |||||
[36] = IFM_IEEE80211_OFDM18, | |||||
[48] = IFM_IEEE80211_OFDM24, | |||||
[54] = IFM_IEEE80211_OFDM27, | |||||
[72] = IFM_IEEE80211_OFDM36, | |||||
[96] = IFM_IEEE80211_OFDM48, | |||||
[108] = IFM_IEEE80211_OFDM54, | |||||
/* NB: OFDM72 doesn't really exist so we don't handle it */ | |||||
}; | |||||
/* assumed throughout the file */ | |||||
_Static_assert(IFM_AUTO == 0, | |||||
"set initial values for ratemedia structures manually"); | |||||
static __inline int | |||||
findmedia(const u_int rates[], int n, uint8_t value) | |||||
{ | { | ||||
int i; | |||||
for (i = 0; i < n; i++) | if (__predict_false(value >= n)) | ||||
if (rates[i].match == match) | return (IFM_AUTO); | ||||
return rates[i].media; | |||||
return IFM_AUTO; | return (rates[value]); | ||||
} | } | ||||
/* | /* | ||||
* Convert IEEE80211 rate value to ifmedia subtype. | * Convert IEEE80211 rate value to ifmedia subtype. | ||||
* Rate is either a legacy rate in units of 0.5Mbps | |||||
* or an MCS index. | |||||
*/ | */ | ||||
int | int | ||||
ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) | ieee80211_rate2media(struct ieee80211com *ic, | ||||
const struct ieee80211_rate_t *rate, enum ieee80211_phymode mode) | |||||
{ | { | ||||
static const struct ratemedia rates[] = { | #define FINDMEDIA(_rates) \ | ||||
{ 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, | findmedia(_rates, nitems(_rates), rate->value) | ||||
{ 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, | |||||
{ 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, | |||||
{ 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, | |||||
{ 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, | |||||
{ 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, | |||||
{ 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, | |||||
{ 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, | |||||
{ 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, | |||||
{ 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, | |||||
{ 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, | |||||
{ 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, | |||||
{ 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, | |||||
{ 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, | |||||
{ 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, | |||||
{ 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, | |||||
{ 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, | |||||
{ 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, | |||||
{ 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, | |||||
{ 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, | |||||
{ 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, | |||||
{ 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, | |||||
{ 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, | |||||
{ 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, | |||||
{ 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, | |||||
{ 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, | |||||
{ 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, | |||||
{ 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, | |||||
{ 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, | |||||
{ 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, | |||||
/* NB: OFDM72 doesn't really exist so we don't handle it */ | |||||
}; | |||||
static const struct ratemedia htrates[] = { | |||||
{ 0, IFM_IEEE80211_MCS }, | |||||
{ 1, IFM_IEEE80211_MCS }, | |||||
{ 2, IFM_IEEE80211_MCS }, | |||||
{ 3, IFM_IEEE80211_MCS }, | |||||
{ 4, IFM_IEEE80211_MCS }, | |||||
{ 5, IFM_IEEE80211_MCS }, | |||||
{ 6, IFM_IEEE80211_MCS }, | |||||
{ 7, IFM_IEEE80211_MCS }, | |||||
{ 8, IFM_IEEE80211_MCS }, | |||||
{ 9, IFM_IEEE80211_MCS }, | |||||
{ 10, IFM_IEEE80211_MCS }, | |||||
{ 11, IFM_IEEE80211_MCS }, | |||||
{ 12, IFM_IEEE80211_MCS }, | |||||
{ 13, IFM_IEEE80211_MCS }, | |||||
{ 14, IFM_IEEE80211_MCS }, | |||||
{ 15, IFM_IEEE80211_MCS }, | |||||
{ 16, IFM_IEEE80211_MCS }, | |||||
{ 17, IFM_IEEE80211_MCS }, | |||||
{ 18, IFM_IEEE80211_MCS }, | |||||
{ 19, IFM_IEEE80211_MCS }, | |||||
{ 20, IFM_IEEE80211_MCS }, | |||||
{ 21, IFM_IEEE80211_MCS }, | |||||
{ 22, IFM_IEEE80211_MCS }, | |||||
{ 23, IFM_IEEE80211_MCS }, | |||||
{ 24, IFM_IEEE80211_MCS }, | |||||
{ 25, IFM_IEEE80211_MCS }, | |||||
{ 26, IFM_IEEE80211_MCS }, | |||||
{ 27, IFM_IEEE80211_MCS }, | |||||
{ 28, IFM_IEEE80211_MCS }, | |||||
{ 29, IFM_IEEE80211_MCS }, | |||||
{ 30, IFM_IEEE80211_MCS }, | |||||
{ 31, IFM_IEEE80211_MCS }, | |||||
{ 32, IFM_IEEE80211_MCS }, | |||||
{ 33, IFM_IEEE80211_MCS }, | |||||
{ 34, IFM_IEEE80211_MCS }, | |||||
{ 35, IFM_IEEE80211_MCS }, | |||||
{ 36, IFM_IEEE80211_MCS }, | |||||
{ 37, IFM_IEEE80211_MCS }, | |||||
{ 38, IFM_IEEE80211_MCS }, | |||||
{ 39, IFM_IEEE80211_MCS }, | |||||
{ 40, IFM_IEEE80211_MCS }, | |||||
{ 41, IFM_IEEE80211_MCS }, | |||||
{ 42, IFM_IEEE80211_MCS }, | |||||
{ 43, IFM_IEEE80211_MCS }, | |||||
{ 44, IFM_IEEE80211_MCS }, | |||||
{ 45, IFM_IEEE80211_MCS }, | |||||
{ 46, IFM_IEEE80211_MCS }, | |||||
{ 47, IFM_IEEE80211_MCS }, | |||||
{ 48, IFM_IEEE80211_MCS }, | |||||
{ 49, IFM_IEEE80211_MCS }, | |||||
{ 50, IFM_IEEE80211_MCS }, | |||||
{ 51, IFM_IEEE80211_MCS }, | |||||
{ 52, IFM_IEEE80211_MCS }, | |||||
{ 53, IFM_IEEE80211_MCS }, | |||||
{ 54, IFM_IEEE80211_MCS }, | |||||
{ 55, IFM_IEEE80211_MCS }, | |||||
{ 56, IFM_IEEE80211_MCS }, | |||||
{ 57, IFM_IEEE80211_MCS }, | |||||
{ 58, IFM_IEEE80211_MCS }, | |||||
{ 59, IFM_IEEE80211_MCS }, | |||||
{ 60, IFM_IEEE80211_MCS }, | |||||
{ 61, IFM_IEEE80211_MCS }, | |||||
{ 62, IFM_IEEE80211_MCS }, | |||||
{ 63, IFM_IEEE80211_MCS }, | |||||
{ 64, IFM_IEEE80211_MCS }, | |||||
{ 65, IFM_IEEE80211_MCS }, | |||||
{ 66, IFM_IEEE80211_MCS }, | |||||
{ 67, IFM_IEEE80211_MCS }, | |||||
{ 68, IFM_IEEE80211_MCS }, | |||||
{ 69, IFM_IEEE80211_MCS }, | |||||
{ 70, IFM_IEEE80211_MCS }, | |||||
{ 71, IFM_IEEE80211_MCS }, | |||||
{ 72, IFM_IEEE80211_MCS }, | |||||
{ 73, IFM_IEEE80211_MCS }, | |||||
{ 74, IFM_IEEE80211_MCS }, | |||||
{ 75, IFM_IEEE80211_MCS }, | |||||
{ 76, IFM_IEEE80211_MCS }, | |||||
}; | |||||
int m; | |||||
/* | /* NB: process modes that are not handled by get_ratetable() */ | ||||
* Check 11n rates first for match as an MCS. | switch (mode) { | ||||
*/ | case IEEE80211_MODE_FH: | ||||
if (mode == IEEE80211_MODE_11NA) { | if (rate->type == IEEE80211_T_FH) | ||||
if (rate & IEEE80211_RATE_MCS) { | return (FINDMEDIA(rates_fh)); | ||||
rate &= ~IEEE80211_RATE_MCS; | |||||
m = findmedia(htrates, nitems(htrates), rate); | return (IFM_AUTO); | ||||
if (m != IFM_AUTO) | case IEEE80211_MODE_AUTO: | ||||
return m | IFM_IEEE80211_11NA; | /* XXX an(4) has no ieee80211com structure */ | ||||
if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) { | |||||
if (rate->type == IEEE80211_T_FH) | |||||
return (FINDMEDIA(rates_fh)); | |||||
return (IFM_AUTO); | |||||
} | } | ||||
} else if (mode == IEEE80211_MODE_11NG) { | |||||
/* NB: 12 is ambiguous, it will be treated as an MCS */ | /* XXX how AUTO should be treated for VHT / HT rates? */ | ||||
if (rate & IEEE80211_RATE_MCS) { | switch (rate->type) { | ||||
rate &= ~IEEE80211_RATE_MCS; | case IEEE80211_T_OFDM: | ||||
m = findmedia(htrates, nitems(htrates), rate); | case IEEE80211_T_OFDM_HALF: | ||||
if (m != IFM_AUTO) | case IEEE80211_T_OFDM_QUARTER: | ||||
return m | IFM_IEEE80211_11NG; | case IEEE80211_T_TURBO: | ||||
return (FINDMEDIA(rates_11a)); | |||||
case IEEE80211_T_PBCC: | |||||
case IEEE80211_T_CCK: | |||||
return (FINDMEDIA(rates_11b)); | |||||
default: | |||||
return (IFM_AUTO); | |||||
} | } | ||||
default: | |||||
{ | |||||
const struct ieee80211_rate_table *rt; | |||||
rt = ieee80211_get_ratetable(mode); | |||||
if (!ieee80211_isratevalid(rt, rate)) | |||||
return (IFM_AUTO); | |||||
break; | |||||
} | } | ||||
rate &= IEEE80211_RATE_VAL; | } | ||||
/* NB: check VHT / HT rates separately */ | |||||
switch (rate->type) { | |||||
case IEEE80211_T_VHT: | |||||
switch (mode) { | switch (mode) { | ||||
case IEEE80211_MODE_11A: | case IEEE80211_MODE_VHT_2GHZ: | ||||
case IEEE80211_MODE_HALF: /* XXX good 'nuf */ | return (IFM_IEEE80211_VHT | IFM_IEEE80211_VHT2G); | ||||
case IEEE80211_MODE_QUARTER: | case IEEE80211_MODE_VHT_5GHZ: | ||||
return (IFM_IEEE80211_VHT | IFM_IEEE80211_VHT5G); | |||||
default: | |||||
/* NOTREACHED */ | |||||
KASSERT(0, ("unknown mode %u for VHT rate", mode)); | |||||
return (IFM_AUTO); | |||||
} | |||||
case IEEE80211_T_HT: | |||||
switch (mode) { | |||||
case IEEE80211_MODE_11NA: | case IEEE80211_MODE_11NA: | ||||
case IEEE80211_MODE_TURBO_A: | return (IFM_IEEE80211_MCS | IFM_IEEE80211_11NA); | ||||
case IEEE80211_MODE_STURBO_A: | case IEEE80211_MODE_11NG: | ||||
return findmedia(rates, nitems(rates), | return (IFM_IEEE80211_MCS | IFM_IEEE80211_11NG); | ||||
rate | IFM_IEEE80211_11A); | default: | ||||
/* NOTREACHED */ | |||||
KASSERT(0, ("unknown mode %u for HT rate", mode)); | |||||
return (IFM_AUTO); | |||||
} | |||||
default: | |||||
break; | |||||
} | |||||
switch (mode) { | |||||
case IEEE80211_MODE_11B: | case IEEE80211_MODE_11B: | ||||
return findmedia(rates, nitems(rates), | return (FINDMEDIA(rates_11b)); | ||||
rate | IFM_IEEE80211_11B); | |||||
case IEEE80211_MODE_FH: | |||||
return findmedia(rates, nitems(rates), | |||||
rate | IFM_IEEE80211_FH); | |||||
case IEEE80211_MODE_AUTO: | |||||
/* NB: ic may be NULL for some drivers */ | |||||
if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) | |||||
return findmedia(rates, nitems(rates), | |||||
rate | IFM_IEEE80211_FH); | |||||
/* NB: hack, 11g matches both 11b+11a rates */ | |||||
/* fall thru... */ | |||||
case IEEE80211_MODE_11G: | case IEEE80211_MODE_11G: | ||||
case IEEE80211_MODE_11NG: | case IEEE80211_MODE_11NG: | ||||
case IEEE80211_MODE_TURBO_G: | |||||
return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11G); | |||||
case IEEE80211_MODE_VHT_2GHZ: | case IEEE80211_MODE_VHT_2GHZ: | ||||
case IEEE80211_MODE_TURBO_G: | |||||
if (rate->type == IEEE80211_T_CCK || | |||||
rate->type == IEEE80211_T_PBCC) | |||||
return (FINDMEDIA(rates_11b)); | |||||
/* FALLTHROUGH */ | |||||
case IEEE80211_MODE_11A: | |||||
case IEEE80211_MODE_HALF: /* XXX good 'nuf */ | |||||
case IEEE80211_MODE_QUARTER: | |||||
case IEEE80211_MODE_11NA: | |||||
case IEEE80211_MODE_VHT_5GHZ: | case IEEE80211_MODE_VHT_5GHZ: | ||||
/* XXX TODO: need to figure out mapping for VHT rates */ | case IEEE80211_MODE_TURBO_A: | ||||
return IFM_AUTO; | case IEEE80211_MODE_STURBO_A: | ||||
return (FINDMEDIA(rates_11a)); | |||||
default: | |||||
/* NOTREACHED */ | |||||
KASSERT(0, ("unhandled mode %u", mode)); | |||||
break; | |||||
} | } | ||||
return IFM_AUTO; | return (IFM_AUTO); | ||||
#undef FINDMEDIA | |||||
} | } | ||||
int | int | ||||
ieee80211_media2rate(int mword) | ieee80211_media2rate(int mword) | ||||
{ | { | ||||
static const int ieeerates[] = { | static const int ieeerates[] = { | ||||
-1, /* IFM_AUTO */ | -1, /* IFM_AUTO */ | ||||
0, /* IFM_MANUAL */ | 0, /* IFM_MANUAL */ | ||||
▲ Show 20 Lines • Show All 86 Lines • Show Last 20 Lines |