[net80211] Migrate HT/legacy protection mode and preamble calculation to per-VAP flags

The later firmware devices (including iwn!) support multiple configuration
contexts for a lot of things, leaving it up to the firmware to decide
which channel and vap is active.  This allows for things like off-channel
p2p sta/ap operation and other weird things.

However, net80211 is still focused on a "net80211 drives all" when it comes to driving
the NIC, and as part of this history a lot of these options are global and not per-VAP.
This is fine when net80211 drives things and all VAPs share a single channel - these
parameters importantly really reflect the state of the channel! - but it will increasingly
be not fine when we start supporting more weird configurations and more recent NICs.
Yeah, recent like iwn/iwm.

Anyway - so, migrate all of the HT protection, legacy protection and preamble
stuff to be per-VAP.  The global flags are still there; they're now calculated
in a deferred taskqueue that mirrors the old behaviour.  Firmware based drivers
which have per-VAP configuration of these parameters can now just listen to the
per-VAP options.

What do I mean by per-channel? Well, the above configuration parameters really
are about interoperation with other devices on the same channel. Eg, HT protection
mode will flip to legacy/mixed if it hears ANY BSS that supports non-HT stations or
indicates it has non-HT stations associated.  So, these flags really should be
per-channel rather than per-VAP, and then for things like "do i need short preamble
or long preamble?" turn into a "do I need it for this current operating channel".
Then any VAP using it can query the channel that it's on, reflecting the real
required state.

This patch does none of the above paragraph just yet.

I'm also cheating a bit - I'm currently not using separate taskqueues for
the beacon updates and the per-VAP configuration updates.  I can always further
split it later if I need to but I didn't think it was SUPER important here.

So:

* Create vap taskqueue entries for ERP/protection, HT protection and short/long
  preamble;
* Migrate the HT station count, short/long slot station count, etc - into per-VAP
  variables rather than global;
* Fix a bug with my WME work from a while ago which made it per-VAP - do the WME
  beacon update /after/ the WME update taskqueue runs, not before;
* Any time the HT protmode configuration changes or the ERP protection mode
  config changes - schedule the task, which will call the driver without the
  net80211 lock held and all correctly serialised;
* Use the global flags for beacon IEs and VAP flags for probe responses and
  other IE situations.

The primary consumer of this is ath10k.  iwn could use it when sending RXON,
but we don't support IBSS or AP modes on it yet, and I'm not yet sure whether
it's required in STA mode (ie whether the firmware parses beacons to change
protection mode or whether we need to.)

Tested:

* AR9280, STA/AP
* AR9380, DWDS STA+STA/AP
* ath10k work, STA/AP
* Intel 6235, STA
* Various rtwn / run NICs, DWDS STA and STA configurations
This commit is contained in:
Adrian Chadd 2020-07-01 00:23:49 +00:00
parent 7290cb47fc
commit f1481c8d3b
12 changed files with 670 additions and 203 deletions

View File

@ -483,6 +483,17 @@ _db_show_vap(const struct ieee80211vap *vap, int showmesh, int showprocs)
if (vap->iv_tdma != NULL)
_db_show_tdma("\t", vap->iv_tdma, showprocs);
#endif /* IEEE80211_SUPPORT_TDMA */
db_printf("\tsta_assoc %u", vap->iv_sta_assoc);
db_printf(" ht_sta_assoc %u", vap->iv_ht_sta_assoc);
db_printf(" ht40_sta_assoc %u", vap->iv_ht40_sta_assoc);
db_printf("\n");
db_printf(" nonerpsta %u", vap->iv_nonerpsta);
db_printf(" longslotsta %u", vap->iv_longslotsta);
db_printf(" lastnonerp %d", vap->iv_lastnonerp);
db_printf(" lastnonht %d", vap->iv_lastnonht);
db_printf("\n");
if (showprocs) {
DB_PRINTSYM("\t", "iv_key_alloc", vap->iv_key_alloc);
DB_PRINTSYM("\t", "iv_key_delete", vap->iv_key_delete);
@ -608,17 +619,8 @@ _db_show_com(const struct ieee80211com *ic, int showvaps, int showsta,
_db_show_node_table("\t", &ic->ic_sta);
db_printf("\tprotmode %d", ic->ic_protmode);
db_printf(" nonerpsta %u", ic->ic_nonerpsta);
db_printf(" longslotsta %u", ic->ic_longslotsta);
db_printf(" lastnonerp %d", ic->ic_lastnonerp);
db_printf("\n");
db_printf("\tsta_assoc %u", ic->ic_sta_assoc);
db_printf(" ht_sta_assoc %u", ic->ic_ht_sta_assoc);
db_printf(" ht40_sta_assoc %u", ic->ic_ht40_sta_assoc);
db_printf("\n");
db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode);
db_printf(" htprotmode %d", ic->ic_htprotmode);
db_printf(" lastnonht %d", ic->ic_lastnonht);
db_printf("\n");
db_printf("\tsuperg %p\n", ic->ic_superg);

View File

@ -206,8 +206,9 @@ hostap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* state and the timeout routines check if the flag
* is set before doing anything so this is sufficient.
*/
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
vap->iv_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
vap->iv_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
/* XXX TODO: schedule deferred update? */
/* fall thru... */
case IEEE80211_S_CAC:
/*
@ -1812,10 +1813,13 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
scan.status == 0 && /* NB: on-channel */
((scan.erp & 0x100) == 0 || /* NB: no ERP, 11b sta*/
(scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) {
ic->ic_lastnonerp = ticks;
ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
if (ic->ic_protmode != IEEE80211_PROT_NONE &&
(ic->ic_flags & IEEE80211_F_USEPROT) == 0) {
vap->iv_lastnonerp = ticks;
vap->iv_flags_ext |= IEEE80211_FEXT_NONERP_PR;
/*
* XXX TODO: this may need to check all VAPs?
*/
if (vap->iv_protmode != IEEE80211_PROT_NONE &&
(vap->iv_flags & IEEE80211_F_USEPROT) == 0) {
IEEE80211_NOTE_FRAME(vap,
IEEE80211_MSG_ASSOC, wh,
"non-ERP present on channel %d "
@ -1823,8 +1827,8 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
"enable use of protection",
ic->ic_curchan->ic_ieee,
scan.erp, scan.chan);
ic->ic_flags |= IEEE80211_F_USEPROT;
ieee80211_notify_erp(ic);
vap->iv_flags |= IEEE80211_F_USEPROT;
ieee80211_vap_update_erp_protmode(vap);
}
}
/*
@ -1844,12 +1848,12 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
break;
}
if (scan.htinfo == NULL) {
ieee80211_htprot_update(ic,
ieee80211_htprot_update(vap,
IEEE80211_HTINFO_OPMODE_PROTOPT |
IEEE80211_HTINFO_NONHT_PRESENT);
} else if (ishtmixed(scan.htinfo)) {
/* XXX? take NONHT_PRESENT from beacon? */
ieee80211_htprot_update(ic,
ieee80211_htprot_update(vap,
IEEE80211_HTINFO_OPMODE_MIXED |
IEEE80211_HTINFO_NONHT_PRESENT);
}

View File

@ -270,6 +270,9 @@ ieee80211_ht_vattach(struct ieee80211vap *vap)
vap->iv_ampdu_mintraffic[WME_AC_VO] = 32;
vap->iv_ampdu_mintraffic[WME_AC_VI] = 32;
vap->iv_htprotmode = IEEE80211_PROT_RTSCTS;
vap->iv_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
if (vap->iv_htcaps & IEEE80211_HTC_HT) {
/*
* Device is HT capable; enable all HT-related
@ -1509,38 +1512,36 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
}
/*
* Notify hostap vaps of a change in the HTINFO ie.
* Notify a VAP of a change in the HTINFO ie if it's a hostap VAP.
*
* This is to be called from the deferred HT protection update
* task once the flags are updated.
*/
static void
htinfo_notify(struct ieee80211com *ic)
void
ieee80211_htinfo_notify(struct ieee80211vap *vap)
{
struct ieee80211vap *vap;
int first = 1;
IEEE80211_LOCK_ASSERT(ic);
IEEE80211_LOCK_ASSERT(vap->iv_ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap->iv_opmode != IEEE80211_M_HOSTAP)
continue;
if (vap->iv_state != IEEE80211_S_RUN ||
!IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan))
continue;
if (first) {
IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
vap->iv_bss,
"HT bss occupancy change: %d sta, %d ht, "
"%d ht40%s, HT protmode now 0x%x"
, ic->ic_sta_assoc
, ic->ic_ht_sta_assoc
, ic->ic_ht40_sta_assoc
, (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) ?
", non-HT sta present" : ""
, ic->ic_curhtprotmode);
first = 0;
}
ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
}
if (vap->iv_opmode != IEEE80211_M_HOSTAP)
return;
if (vap->iv_state != IEEE80211_S_RUN ||
!IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan))
return;
IEEE80211_NOTE(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
vap->iv_bss,
"HT bss occupancy change: %d sta, %d ht, "
"%d ht40%s, HT protmode now 0x%x"
, vap->iv_sta_assoc
, vap->iv_ht_sta_assoc
, vap->iv_ht40_sta_assoc
, (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) ?
", non-HT sta present" : ""
, vap->iv_curhtprotmode);
ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
}
/*
@ -1548,26 +1549,28 @@ htinfo_notify(struct ieee80211com *ic)
* state and handle updates.
*/
static void
htinfo_update(struct ieee80211com *ic)
htinfo_update(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
uint8_t protmode;
if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
if (vap->iv_sta_assoc != vap->iv_ht_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_MIXED
| IEEE80211_HTINFO_NONHT_PRESENT;
} else if (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) {
} else if (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) {
protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
| IEEE80211_HTINFO_NONHT_PRESENT;
} else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) &&
ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
vap->iv_sta_assoc != vap->iv_ht40_sta_assoc) {
protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
} else {
protmode = IEEE80211_HTINFO_OPMODE_PURE;
}
if (protmode != ic->ic_curhtprotmode) {
ic->ic_curhtprotmode = protmode;
htinfo_notify(ic);
if (protmode != vap->iv_curhtprotmode) {
vap->iv_curhtprotmode = protmode;
/* Update VAP with new protection mode */
ieee80211_vap_update_ht_protmode(vap);
}
}
@ -1577,16 +1580,16 @@ htinfo_update(struct ieee80211com *ic)
void
ieee80211_ht_node_join(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
IEEE80211_LOCK_ASSERT(ic);
IEEE80211_LOCK_ASSERT(vap->iv_ic);
if (ni->ni_flags & IEEE80211_NODE_HT) {
ic->ic_ht_sta_assoc++;
vap->iv_ht_sta_assoc++;
if (ni->ni_chw == 40)
ic->ic_ht40_sta_assoc++;
vap->iv_ht40_sta_assoc++;
}
htinfo_update(ic);
htinfo_update(vap);
}
/*
@ -1595,16 +1598,16 @@ ieee80211_ht_node_join(struct ieee80211_node *ni)
void
ieee80211_ht_node_leave(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
IEEE80211_LOCK_ASSERT(ic);
IEEE80211_LOCK_ASSERT(vap->iv_ic);
if (ni->ni_flags & IEEE80211_NODE_HT) {
ic->ic_ht_sta_assoc--;
vap->iv_ht_sta_assoc--;
if (ni->ni_chw == 40)
ic->ic_ht40_sta_assoc--;
vap->iv_ht40_sta_assoc--;
}
htinfo_update(ic);
htinfo_update(vap);
}
/*
@ -1618,25 +1621,27 @@ ieee80211_ht_node_leave(struct ieee80211_node *ni)
* a higher precedence than PROTOPT (i.e. we will not change
* change PROTOPT -> MIXED; only MIXED -> PROTOPT). This
* corresponds to how we handle things in htinfo_update.
*
*/
void
ieee80211_htprot_update(struct ieee80211com *ic, int protmode)
ieee80211_htprot_update(struct ieee80211vap *vap, int protmode)
{
struct ieee80211com *ic = vap->iv_ic;
#define OPMODE(x) SM(x, IEEE80211_HTINFO_OPMODE)
IEEE80211_LOCK(ic);
/* track non-HT station presence */
KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT,
("protmode 0x%x", protmode));
ic->ic_flags_ht |= IEEE80211_FHT_NONHT_PR;
ic->ic_lastnonht = ticks;
vap->iv_flags_ht |= IEEE80211_FHT_NONHT_PR;
vap->iv_lastnonht = ticks;
if (protmode != ic->ic_curhtprotmode &&
(OPMODE(ic->ic_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED ||
if (protmode != vap->iv_curhtprotmode &&
(OPMODE(vap->iv_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED ||
OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) {
/* push beacon update */
ic->ic_curhtprotmode = protmode;
htinfo_notify(ic);
vap->iv_curhtprotmode = protmode;
/* Update VAP with new protection mode */
ieee80211_vap_update_ht_protmode(vap);
}
IEEE80211_UNLOCK(ic);
#undef OPMODE
@ -1651,18 +1656,17 @@ ieee80211_htprot_update(struct ieee80211com *ic, int protmode)
* gone we time out this condition.
*/
void
ieee80211_ht_timeout(struct ieee80211com *ic)
ieee80211_ht_timeout(struct ieee80211vap *vap)
{
IEEE80211_LOCK_ASSERT(ic);
if ((ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) &&
ieee80211_time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
#if 0
IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
IEEE80211_LOCK_ASSERT(vap->iv_ic);
if ((vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) &&
ieee80211_time_after(ticks, vap->iv_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s", "time out non-HT STA present on channel");
#endif
ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
htinfo_update(ic);
vap->iv_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
htinfo_update(vap);
}
}
@ -3507,6 +3511,12 @@ ieee80211_ht_update_beacon(struct ieee80211vap *vap,
ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
/* protection mode */
/*
* XXX TODO: this uses the global flag, not the per-VAP flag.
* Eventually (once the protection modes are done per-channel
* rather than per-VAP) we can flip this over to be per-VAP but
* using the channel protection mode.
*/
ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
ieee80211_free_node(ni);
@ -3547,7 +3557,11 @@ ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni)
if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
frm[1] = ic->ic_curhtprotmode;
/*
* Add current protection mode. Unlike for beacons,
* this will respect the per-VAP flags.
*/
frm[1] = vap->iv_curhtprotmode;
frm += 5;

View File

@ -851,7 +851,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
ireq->i_val = vap->iv_rtsthreshold;
break;
case IEEE80211_IOC_PROTMODE:
ireq->i_val = ic->ic_protmode;
ireq->i_val = vap->iv_protmode;
break;
case IEEE80211_IOC_TXPOWER:
/*
@ -1097,7 +1097,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
error = ieee80211_ioctl_getdevcaps(ic, ireq);
break;
case IEEE80211_IOC_HTPROTMODE:
ireq->i_val = ic->ic_htprotmode;
ireq->i_val = vap->iv_htprotmode;
break;
case IEEE80211_IOC_HTCONF:
if (vap->iv_flags_ht & IEEE80211_FHT_HT) {
@ -2912,11 +2912,13 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
case IEEE80211_IOC_PROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
return EINVAL;
ic->ic_protmode = (enum ieee80211_protmode)ireq->i_val;
vap->iv_protmode = (enum ieee80211_protmode)ireq->i_val;
/* NB: if not operating in 11g this can wait */
if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
error = ERESTART;
/* driver callback for protection mode update */
ieee80211_vap_update_erp_protmode(vap);
break;
case IEEE80211_IOC_TXPOWER:
if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
@ -3384,11 +3386,13 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
case IEEE80211_IOC_HTPROTMODE:
if (ireq->i_val > IEEE80211_PROT_RTSCTS)
return EINVAL;
ic->ic_htprotmode = ireq->i_val ?
vap->iv_htprotmode = ireq->i_val ?
IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
/* NB: if not operating in 11n this can wait */
if (isvapht(vap))
error = ERESTART;
/* Notify driver layer of HT protmode changes */
ieee80211_vap_update_ht_protmode(vap);
break;
case IEEE80211_IOC_STA_VLAN:
error = ieee80211_ioctl_setstavlan(vap, ireq);

View File

@ -99,7 +99,7 @@ static void ieee80211_node_table_init(struct ieee80211com *ic,
static void ieee80211_node_table_reset(struct ieee80211_node_table *,
struct ieee80211vap *);
static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
static void ieee80211_erp_timeout(struct ieee80211com *);
static void ieee80211_vap_erp_timeout(struct ieee80211vap *);
MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie");
@ -674,7 +674,6 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
{
#ifdef IEEE80211_DEBUG
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
#endif
if (! ieee80211_ibss_merge_check(ni))
@ -683,9 +682,9 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s: new bssid %s: %s preamble, %s slot time%s\n", __func__,
ether_sprintf(ni->ni_bssid),
ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
vap->iv_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
vap->iv_flags&IEEE80211_F_SHSLOT ? "short" : "long",
ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : ""
vap->iv_flags&IEEE80211_F_USEPROT ? ", protection" : ""
);
return ieee80211_sta_join1(ieee80211_ref_node(ni));
}
@ -2507,6 +2506,20 @@ ieee80211_drain(struct ieee80211com *ic)
IEEE80211_NODE_UNLOCK(nt);
}
/*
* Per-ieee80211vap inactivity timer callback.
*/
static void
ieee80211_vap_timeout(struct ieee80211vap *vap)
{
IEEE80211_LOCK_ASSERT(vap->iv_ic);
ieee80211_vap_erp_timeout(vap);
ieee80211_ht_timeout(vap);
ieee80211_vht_timeout(vap);
}
/*
* Per-ieee80211com inactivity timer callback.
*/
@ -2514,6 +2527,7 @@ void
ieee80211_node_timeout(void *arg)
{
struct ieee80211com *ic = arg;
struct ieee80211vap *vap;
/*
* Defer timeout processing if a channel switch is pending.
@ -2530,9 +2544,8 @@ ieee80211_node_timeout(void *arg)
ieee80211_ageq_age(&ic->ic_stageq, IEEE80211_INACT_WAIT);
IEEE80211_LOCK(ic);
ieee80211_erp_timeout(ic);
ieee80211_ht_timeout(ic);
ieee80211_vht_timeout(ic);
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
ieee80211_vap_timeout(vap);
IEEE80211_UNLOCK(ic);
}
callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
@ -2645,7 +2658,12 @@ ieee80211_dump_nodes(struct ieee80211_node_table *nt)
(ieee80211_iter_func *) ieee80211_dump_node, nt);
}
static void
/*
* Iterate over the VAPs and update their ERP beacon IEs.
*
* Note this must be called from the deferred ERP update task paths.
*/
void
ieee80211_notify_erp_locked(struct ieee80211com *ic)
{
struct ieee80211vap *vap;
@ -2657,14 +2675,6 @@ ieee80211_notify_erp_locked(struct ieee80211com *ic)
ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP);
}
void
ieee80211_notify_erp(struct ieee80211com *ic)
{
IEEE80211_LOCK(ic);
ieee80211_notify_erp_locked(ic);
IEEE80211_UNLOCK(ic);
}
/*
* Handle a station joining an 11g network.
*/
@ -2684,10 +2694,13 @@ ieee80211_node_join_11g(struct ieee80211_node *ni)
* next beacon transmission (per sec. 7.3.1.4 of 11g).
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
ic->ic_longslotsta++;
vap->iv_longslotsta++;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station needs long slot time, count %d",
ic->ic_longslotsta);
vap->iv_longslotsta);
/*
* XXX TODO: this may need all VAPs checked!
*/
if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) {
/*
* Don't force slot time when switched to turbo
@ -2703,10 +2716,10 @@ ieee80211_node_join_11g(struct ieee80211_node *ni)
* if configured.
*/
if (!ieee80211_iserp_rateset(&ni->ni_rates)) {
ic->ic_nonerpsta++;
vap->iv_nonerpsta++;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"station is !ERP, %d non-ERP stations associated",
ic->ic_nonerpsta);
vap->iv_nonerpsta);
/*
* If station does not support short preamble
* then we must enable use of Barker preamble.
@ -2714,20 +2727,21 @@ ieee80211_node_join_11g(struct ieee80211_node *ni)
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"%s", "station needs long preamble");
ic->ic_flags |= IEEE80211_F_USEBARKER;
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
vap->iv_flags |= IEEE80211_F_USEBARKER;
vap->iv_flags &= ~IEEE80211_F_SHPREAMBLE;
ieee80211_vap_update_preamble(vap);
}
/*
* If protection is configured and this is the first
* indication we should use protection, enable it.
*/
if (ic->ic_protmode != IEEE80211_PROT_NONE &&
ic->ic_nonerpsta == 1 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
if (vap->iv_protmode != IEEE80211_PROT_NONE &&
vap->iv_nonerpsta == 1 &&
(vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: enable use of protection\n", __func__);
ic->ic_flags |= IEEE80211_F_USEPROT;
ieee80211_notify_erp_locked(ic);
vap->iv_flags |= IEEE80211_F_USEPROT;
ieee80211_vap_update_erp_protmode(vap);
}
} else
ni->ni_flags |= IEEE80211_NODE_ERP;
@ -2762,7 +2776,6 @@ ieee80211_node_join(struct ieee80211_node *ni, int resp)
IEEE80211_LOCK(ic);
IEEE80211_AID_SET(vap, ni->ni_associd);
vap->iv_sta_assoc++;
ic->ic_sta_assoc++;
if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
ieee80211_ht_node_join(ni);
@ -2783,9 +2796,9 @@ ieee80211_node_join(struct ieee80211_node *ni, int resp)
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
"station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s%s",
IEEE80211_NODE_AID(ni),
ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
vap->iv_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
vap->iv_flags & IEEE80211_F_SHSLOT ? "short" : "long",
ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "",
vap->iv_flags & IEEE80211_F_USEPROT ? ", protection" : "",
ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
/* XXX update for VHT string */
ni->ni_flags & IEEE80211_NODE_HT ?
@ -2815,20 +2828,23 @@ ieee80211_node_join(struct ieee80211_node *ni, int resp)
}
static void
disable_protection(struct ieee80211com *ic)
disable_protection(struct ieee80211vap *vap)
{
KASSERT(ic->ic_nonerpsta == 0 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0,
("%d non ERP stations, flags 0x%x", ic->ic_nonerpsta,
ic->ic_flags_ext));
struct ieee80211com *ic = vap->iv_ic;
ic->ic_flags &= ~IEEE80211_F_USEPROT;
KASSERT(vap->iv_nonerpsta == 0 &&
(vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0,
("%d non ERP stations, flags 0x%x", vap->iv_nonerpsta,
vap->iv_flags_ext));
vap->iv_flags &= ~IEEE80211_F_USEPROT;
/* XXX verify mode? */
if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
ic->ic_flags &= ~IEEE80211_F_USEBARKER;
vap->iv_flags |= IEEE80211_F_SHPREAMBLE;
vap->iv_flags &= ~IEEE80211_F_USEBARKER;
}
ieee80211_notify_erp_locked(ic);
ieee80211_vap_update_erp_protmode(vap);
ieee80211_vap_update_preamble(vap);
}
/*
@ -2850,13 +2866,16 @@ ieee80211_node_leave_11g(struct ieee80211_node *ni)
* If a long slot station do the slot time bookkeeping.
*/
if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
KASSERT(ic->ic_longslotsta > 0,
("bogus long slot station count %d", ic->ic_longslotsta));
ic->ic_longslotsta--;
KASSERT(vap->iv_longslotsta > 0,
("bogus long slot station count %d", vap->iv_longslotsta));
vap->iv_longslotsta--;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"long slot time station leaves, count now %d",
ic->ic_longslotsta);
if (ic->ic_longslotsta == 0) {
vap->iv_longslotsta);
/*
* XXX TODO: this may need all VAPs checked!
*/
if (vap->iv_longslotsta == 0) {
/*
* Re-enable use of short slot time if supported
* and not operating in IBSS mode (per spec).
@ -2875,18 +2894,18 @@ ieee80211_node_leave_11g(struct ieee80211_node *ni)
* If a non-ERP station do the protection-related bookkeeping.
*/
if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) {
KASSERT(ic->ic_nonerpsta > 0,
("bogus non-ERP station count %d", ic->ic_nonerpsta));
ic->ic_nonerpsta--;
KASSERT(vap->iv_nonerpsta > 0,
("bogus non-ERP station count %d", vap->iv_nonerpsta));
vap->iv_nonerpsta--;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
"non-ERP station leaves, count now %d%s", ic->ic_nonerpsta,
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
"non-ERP station leaves, count now %d%s", vap->iv_nonerpsta,
(vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
" (non-ERP sta present)" : "");
if (ic->ic_nonerpsta == 0 &&
(ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
if (vap->iv_nonerpsta == 0 &&
(vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
"%s: disable use of protection\n", __func__);
disable_protection(ic);
disable_protection(vap);
}
}
}
@ -2900,20 +2919,18 @@ ieee80211_node_leave_11g(struct ieee80211_node *ni)
* condition.
*/
static void
ieee80211_erp_timeout(struct ieee80211com *ic)
ieee80211_vap_erp_timeout(struct ieee80211vap *vap)
{
IEEE80211_LOCK_ASSERT(ic);
IEEE80211_LOCK_ASSERT(vap->iv_ic);
if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
ieee80211_time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
#if 0
IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
if ((vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
ieee80211_time_after(ticks, vap->iv_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
"%s", "age out non-ERP sta present on channel");
#endif
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
if (ic->ic_nonerpsta == 0)
disable_protection(ic);
vap->iv_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
if (vap->iv_nonerpsta == 0)
disable_protection(vap);
}
}
@ -2952,7 +2969,6 @@ ieee80211_node_leave(struct ieee80211_node *ni)
IEEE80211_LOCK(ic);
IEEE80211_AID_CLR(vap, ni->ni_associd);
vap->iv_sta_assoc--;
ic->ic_sta_assoc--;
if (IEEE80211_IS_CHAN_VHT(ic->ic_bsschan))
ieee80211_vht_node_leave(ni);

View File

@ -479,7 +479,7 @@ int ieee80211_iterate_nodes_vap(struct ieee80211_node_table *,
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
void ieee80211_notify_erp(struct ieee80211com *);
void ieee80211_notify_erp_locked(struct ieee80211com *);
void ieee80211_dump_node(struct ieee80211_node_table *,
struct ieee80211_node *);
void ieee80211_dump_nodes(struct ieee80211_node_table *);

View File

@ -2100,15 +2100,34 @@ ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
* Add an erp element to a frame.
*/
static uint8_t *
ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
ieee80211_add_erp(uint8_t *frm, struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
uint8_t erp;
*frm++ = IEEE80211_ELEMID_ERP;
*frm++ = 1;
erp = 0;
if (ic->ic_nonerpsta != 0)
/*
* TODO: This uses the global flags for now because
* the per-VAP flags are fine for per-VAP, but don't
* take into account which VAPs share the same channel
* and which are on different channels.
*
* ERP and HT/VHT protection mode is a function of
* how many stations are on a channel, not specifically
* the VAP or global. But, until we grow that status,
* the global flag will have to do.
*/
if (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR)
erp |= IEEE80211_ERP_NON_ERP_PRESENT;
/*
* TODO: same as above; these should be based not
* on the vap or ic flags, but instead on a combination
* of per-VAP and channels.
*/
if (ic->ic_flags & IEEE80211_F_USEPROT)
erp |= IEEE80211_ERP_USE_PROTECTION;
if (ic->ic_flags & IEEE80211_F_USEBARKER)
@ -2569,7 +2588,6 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
uint16_t
ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
struct ieee80211com *ic = vap->iv_ic;
uint16_t capinfo;
KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode"));
@ -2582,7 +2600,7 @@ ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
capinfo = 0;
if (vap->iv_flags & IEEE80211_F_PRIVACY)
capinfo |= IEEE80211_CAPINFO_PRIVACY;
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(chan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (vap->iv_flags & IEEE80211_F_SHSLOT)
@ -2761,7 +2779,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
* NB: Some 11a AP's reject the request when
* short preamble is set.
*/
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) &&
IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
@ -3098,7 +3116,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
}
}
if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan))
frm = ieee80211_add_erp(frm, ic);
frm = ieee80211_add_erp(frm, vap);
frm = ieee80211_add_xrates(frm, rs);
frm = ieee80211_add_rsn(frm, vap);
/*
@ -3268,6 +3286,7 @@ ieee80211_alloc_prot(struct ieee80211_node *ni, const struct mbuf *m,
uint8_t rate, int prot)
{
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211vap *vap = ni->ni_vap;
const struct ieee80211_frame *wh;
struct mbuf *mprot;
uint16_t dur;
@ -3279,7 +3298,7 @@ ieee80211_alloc_prot(struct ieee80211_node *ni, const struct mbuf *m,
wh = mtod(m, const struct ieee80211_frame *);
pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
isshort = (vap->iv_flags & IEEE80211_F_SHPREAMBLE) != 0;
dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort)
+ ieee80211_ack_duration(ic->ic_rt, rate, isshort);
@ -3288,7 +3307,7 @@ ieee80211_alloc_prot(struct ieee80211_node *ni, const struct mbuf *m,
dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort);
mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
} else
mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
mprot = ieee80211_alloc_cts(ic, vap->iv_myaddr, dur);
return (mprot);
}
@ -3496,7 +3515,7 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) {
bo->bo_erp = frm;
frm = ieee80211_add_erp(frm, ic);
frm = ieee80211_add_erp(frm, vap);
}
frm = ieee80211_add_xrates(frm, rs);
frm = ieee80211_add_rsn(frm, vap);
@ -3981,7 +4000,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast)
/*
* ERP element needs updating.
*/
(void) ieee80211_add_erp(bo->bo_erp, ic);
(void) ieee80211_add_erp(bo->bo_erp, vap);
clrbit(bo->bo_flags, IEEE80211_BEACON_ERP);
}
#ifdef IEEE80211_SUPPORT_SUPERG

View File

@ -335,7 +335,7 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m)
if (psq->psq_len >= psq->psq_maxlen) {
psq->psq_drops++;
IEEE80211_PSQ_UNLOCK(psq);
IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
"pwr save q overflow, drops %d (size %d)",
psq->psq_drops, psq->psq_len);
#ifdef IEEE80211_DEBUG

View File

@ -246,6 +246,9 @@ static void update_chw(void *, int);
static void vap_update_wme(void *, int);
static void vap_update_slot(void *, int);
static void restart_vaps(void *, int);
static void vap_update_erp_protmode(void *, int);
static void vap_update_preamble(void *, int);
static void vap_update_ht_protmode(void *, int);
static void ieee80211_newstate_cb(void *, int);
static int
@ -275,7 +278,7 @@ ieee80211_proto_attach(struct ieee80211com *ic)
max_hdr = max_linkhdr + max_protohdr;
max_datalen = MHLEN - max_hdr;
}
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
//ic->ic_protmode = IEEE80211_PROT_CTSONLY;
TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ic);
TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic);
@ -342,6 +345,9 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
TASK_INIT(&vap->iv_wme_task, 0, vap_update_wme, vap);
TASK_INIT(&vap->iv_slot_task, 0, vap_update_slot, vap);
TASK_INIT(&vap->iv_erp_protmode_task, 0, vap_update_erp_protmode, vap);
TASK_INIT(&vap->iv_ht_protmode_task, 0, vap_update_ht_protmode, vap);
TASK_INIT(&vap->iv_preamble_task, 0, vap_update_preamble, vap);
/*
* Install default tx rate handling: no fixed rate, lowest
* supported rate for mgmt and multicast frames. Default
@ -388,6 +394,7 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
vap->iv_update_beacon = null_update_beacon;
vap->iv_deliver_data = ieee80211_deliver_data;
vap->iv_protmode = IEEE80211_PROT_CTSONLY;
/* attach support for operating mode */
ic->ic_vattach[vap->iv_opmode](vap);
@ -763,6 +770,22 @@ ieee80211_vap_reset_erp(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
vap->iv_nonerpsta = 0;
vap->iv_longslotsta = 0;
vap->iv_flags &= ~IEEE80211_F_USEPROT;
/*
* Set short preamble and ERP barker-preamble flags.
*/
if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(vap->iv_caps & IEEE80211_C_SHPREAMBLE)) {
vap->iv_flags |= IEEE80211_F_SHPREAMBLE;
vap->iv_flags &= ~IEEE80211_F_USEBARKER;
} else {
vap->iv_flags &= ~IEEE80211_F_SHPREAMBLE;
vap->iv_flags |= IEEE80211_F_USEBARKER;
}
/*
* Short slot time is enabled only when operating in 11g
* and not in an IBSS. We must also honor whether or not
@ -778,13 +801,15 @@ ieee80211_vap_reset_erp(struct ieee80211vap *vap)
/*
* Reset 11g-related state.
*
* Note this resets the global state and a caller should schedule
* a re-check of all the VAPs after setup to update said state.
*/
void
ieee80211_reset_erp(struct ieee80211com *ic)
{
#if 0
ic->ic_flags &= ~IEEE80211_F_USEPROT;
ic->ic_nonerpsta = 0;
ic->ic_longslotsta = 0;
/*
* Set short preamble and ERP barker-preamble flags.
*/
@ -796,6 +821,8 @@ ieee80211_reset_erp(struct ieee80211com *ic)
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
ic->ic_flags |= IEEE80211_F_USEBARKER;
}
#endif
/* XXX TODO: schedule a new per-VAP ERP calculation */
}
/*
@ -812,6 +839,9 @@ ieee80211_reset_erp(struct ieee80211com *ic)
* If the per-VAP method is not called then the global flags will be
* flipped into sync with the VAPs; ic_flags IEEE80211_F_SHSLOT will
* be set only if all of the vaps will have it set.
*
* Look at the comments for vap_update_erp_protmode() for more
* background; this assumes all VAPs are on the same channel.
*/
static void
vap_update_slot(void *arg, int npending)
@ -848,7 +878,6 @@ vap_update_slot(void *arg, int npending)
else
num_lgslot++;
}
IEEE80211_UNLOCK(ic);
/*
* It looks backwards but - if the number of short slot VAPs
@ -860,6 +889,7 @@ vap_update_slot(void *arg, int npending)
ic->ic_flags &= ~IEEE80211_F_SHSLOT;
else if (num_lgslot == 0)
ic->ic_flags |= IEEE80211_F_SHSLOT;
IEEE80211_UNLOCK(ic);
/*
* Call the driver with our new global slot time flags.
@ -868,6 +898,293 @@ vap_update_slot(void *arg, int npending)
ic->ic_updateslot(ic);
}
/*
* Deferred ERP protmode update.
*
* This currently calculates the global ERP protection mode flag
* based on each of the VAPs. Any VAP with it enabled is enough
* for the global flag to be enabled. All VAPs with it disabled
* is enough for it to be disabled.
*
* This may make sense right now for the supported hardware where
* net80211 is controlling the single channel configuration, but
* offload firmware that's doing channel changes (eg off-channel
* TDLS, off-channel STA, off-channel P2P STA/AP) may get some
* silly looking flag updates.
*
* Ideally the protection mode calculation is done based on the
* channel, and all VAPs using that channel will inherit it.
* But until that's what net80211 does, this wil have to do.
*/
static void
vap_update_erp_protmode(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *iv;
int enable_protmode = 0;
int non_erp_present = 0;
/*
* Iterate over all of the VAPs to calculate the overlapping
* ERP protection mode configuration and ERP present math.
*
* For now we assume that if a driver can handle this per-VAP
* then it'll ignore the ic->ic_protmode variant and instead
* will look at the vap related flags.
*/
IEEE80211_LOCK(ic);
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
if (iv->iv_flags & IEEE80211_F_USEPROT)
enable_protmode = 1;
if (iv->iv_flags_ext & IEEE80211_FEXT_NONERP_PR)
non_erp_present = 1;
}
if (enable_protmode)
ic->ic_flags |= IEEE80211_F_USEPROT;
else
ic->ic_flags &= ~IEEE80211_F_USEPROT;
if (non_erp_present)
ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
else
ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
/* Beacon update on all VAPs */
ieee80211_notify_erp_locked(ic);
IEEE80211_UNLOCK(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called; enable_protmode=%d, non_erp_present=%d\n",
__func__, enable_protmode, non_erp_present);
/*
* Now that the global configuration flags are calculated,
* notify the VAP about its configuration.
*
* The global flags will be used when assembling ERP IEs
* for multi-VAP operation, even if it's on a different
* channel. Yes, that's going to need fixing in the
* future.
*/
if (vap->iv_erp_protmode_update != NULL)
vap->iv_erp_protmode_update(vap);
}
/*
* Deferred ERP short preamble/barker update.
*
* All VAPs need to use short preamble for it to be globally
* enabled or not.
*
* Look at the comments for vap_update_erp_protmode() for more
* background; this assumes all VAPs are on the same channel.
*/
static void
vap_update_preamble(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *iv;
int barker_count = 0, short_preamble_count = 0, count = 0;
/*
* Iterate over all of the VAPs to calculate the overlapping
* short or long preamble configuration.
*
* For now we assume that if a driver can handle this per-VAP
* then it'll ignore the ic->ic_flags variant and instead
* will look at the vap related flags.
*/
IEEE80211_LOCK(ic);
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
if (iv->iv_flags & IEEE80211_F_USEBARKER)
barker_count++;
if (iv->iv_flags & IEEE80211_F_SHPREAMBLE)
short_preamble_count++;
count++;
}
/*
* As with vap_update_erp_protmode(), the global flags are
* currently used for beacon IEs.
*/
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called; barker_count=%d, short_preamble_count=%d\n",
__func__, barker_count, short_preamble_count);
/*
* Only flip on short preamble if all of the VAPs support
* it.
*/
if (barker_count == 0 && short_preamble_count == count) {
ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
ic->ic_flags &= ~IEEE80211_F_USEBARKER;
} else {
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
ic->ic_flags |= IEEE80211_F_USEBARKER;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: global barker=%d preamble=%d\n",
__func__,
!! (ic->ic_flags & IEEE80211_F_USEBARKER),
!! (ic->ic_flags & IEEE80211_F_SHPREAMBLE));
/* Beacon update on all VAPs */
ieee80211_notify_erp_locked(ic);
IEEE80211_UNLOCK(ic);
/* Driver notification */
if (vap->iv_erp_protmode_update != NULL)
vap->iv_preamble_update(vap);
}
/*
* Deferred HT protmode update and beacon update.
*
* Look at the comments for vap_update_erp_protmode() for more
* background; this assumes all VAPs are on the same channel.
*/
static void
vap_update_ht_protmode(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211vap *iv;
struct ieee80211com *ic = vap->iv_ic;
int num_vaps = 0, num_pure = 0, num_mixed = 0;
int num_optional = 0, num_ht2040 = 0, num_nonht = 0;
int num_ht_sta = 0, num_ht40_sta = 0, num_sta = 0;
int num_nonhtpr = 0;
/*
* Iterate over all of the VAPs to calculate everything.
*
* There are a few different flags to calculate:
*
* + whether there's HT only or HT+legacy stations;
* + whether there's HT20, HT40, or HT20+HT40 stations;
* + whether the desired protection mode is mixed, pure or
* one of the two above.
*
* For now we assume that if a driver can handle this per-VAP
* then it'll ignore the ic->ic_htprotmode / ic->ic_curhtprotmode
* variant and instead will look at the vap related variables.
*
* XXX TODO: non-greenfield STAs present (IEEE80211_HTINFO_NONGF_PRESENT) !
*/
IEEE80211_LOCK(ic);
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
num_vaps++;
/* overlapping BSSes advertising non-HT status present */
if (iv->iv_flags_ht & IEEE80211_FHT_NONHT_PR)
num_nonht++;
/* Operating mode flags */
if (iv->iv_curhtprotmode & IEEE80211_HTINFO_NONHT_PRESENT)
num_nonhtpr++;
switch (iv->iv_curhtprotmode & IEEE80211_HTINFO_OPMODE) {
case IEEE80211_HTINFO_OPMODE_PURE:
num_pure++;
break;
case IEEE80211_HTINFO_OPMODE_PROTOPT:
num_optional++;
break;
case IEEE80211_HTINFO_OPMODE_HT20PR:
num_ht2040++;
break;
case IEEE80211_HTINFO_OPMODE_MIXED:
num_mixed++;
break;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s: vap %s: nonht_pr=%d, curhtprotmode=0x%02x\n",
__func__,
ieee80211_get_vap_ifname(iv),
!! (iv->iv_flags_ht & IEEE80211_FHT_NONHT_PR),
iv->iv_curhtprotmode);
num_ht_sta += iv->iv_ht_sta_assoc;
num_ht40_sta += iv->iv_ht40_sta_assoc;
num_sta += iv->iv_sta_assoc;
}
/*
* Step 1 - if any VAPs indicate NONHT_PR set (overlapping BSS
* non-HT present), set it here. This shouldn't be used by
* anything but the old overlapping BSS logic so if any drivers
* consume it, it's up to date.
*/
if (num_nonht > 0)
ic->ic_flags_ht |= IEEE80211_FHT_NONHT_PR;
else
ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
/*
* Step 2 - default HT protection mode to MIXED (802.11-2016 10.26.3.1.)
*
* + If all VAPs are PURE, we can stay PURE.
* + If all VAPs are PROTOPT, we can go to PROTOPT.
* + If any VAP has HT20PR then it sees at least a HT40+HT20 station.
* Note that we may have a VAP with one HT20 and a VAP with one HT40;
* So we look at the sum ht and sum ht40 sta counts; if we have a
* HT station and the HT20 != HT40 count, we have to do HT20PR here.
* Note all stations need to be HT for this to be an option.
* + The fall-through is MIXED, because it means we have some odd
* non HT40-involved combination of opmode and this is the most
* sensible default.
*/
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_MIXED;
if (num_pure == num_vaps)
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
if (num_optional == num_vaps)
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PROTOPT;
/*
* Note: we need /a/ HT40 station somewhere for this to
* be a possibility.
*/
if ((num_ht2040 > 0) ||
((num_ht_sta > 0) && (num_ht40_sta > 0) &&
(num_ht_sta != num_ht40_sta)))
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_HT20PR;
/*
* Step 3 - if any of the stations across the VAPs are
* non-HT then this needs to be flipped back to MIXED.
*/
if (num_ht_sta != num_sta)
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_MIXED;
/*
* Step 4 - If we see any overlapping BSS non-HT stations
* via beacons then flip on NONHT_PRESENT.
*/
if (num_nonhtpr > 0)
ic->ic_curhtprotmode |= IEEE80211_HTINFO_NONHT_PRESENT;
/* Notify all VAPs to potentially update their beacons */
TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next)
ieee80211_htinfo_notify(iv);
IEEE80211_UNLOCK(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
"%s: global: nonht_pr=%d ht_opmode=0x%02x\n",
__func__,
!! (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR),
ic->ic_curhtprotmode);
/* Driver update */
if (vap->iv_erp_protmode_update != NULL)
vap->iv_ht_protmode_update(vap);
}
/*
* Set the short slot time state and notify the driver.
*
@ -878,6 +1195,8 @@ ieee80211_vap_set_shortslottime(struct ieee80211vap *vap, int onoff)
{
struct ieee80211com *ic = vap->iv_ic;
/* XXX lock? */
/*
* Only modify the per-VAP slot time.
*/
@ -886,10 +1205,68 @@ ieee80211_vap_set_shortslottime(struct ieee80211vap *vap, int onoff)
else
vap->iv_flags &= ~IEEE80211_F_SHSLOT;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called; onoff=%d\n", __func__, onoff);
/* schedule the deferred slot flag update and update */
ieee80211_runtask(ic, &vap->iv_slot_task);
}
/*
* Update the VAP short /long / barker preamble state and
* update beacon state if needed.
*
* For now it simply copies the global flags into the per-vap
* flags and schedules the callback. Later this will support
* both global and per-VAP flags, especially useful for
* and STA+STA multi-channel operation (eg p2p).
*/
void
ieee80211_vap_update_preamble(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
/* XXX lock? */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called\n", __func__);
/* schedule the deferred slot flag update and update */
ieee80211_runtask(ic, &vap->iv_preamble_task);
}
/*
* Update the VAP 11g protection mode and update beacon state
* if needed.
*/
void
ieee80211_vap_update_erp_protmode(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
/* XXX lock? */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called\n", __func__);
/* schedule the deferred slot flag update and update */
ieee80211_runtask(ic, &vap->iv_erp_protmode_task);
}
/*
* Update the VAP 11n protection mode and update beacon state
* if needed.
*/
void
ieee80211_vap_update_ht_protmode(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
/* XXX lock? */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG,
"%s: called\n", __func__);
/* schedule the deferred protmode update */
ieee80211_runtask(ic, &vap->iv_ht_protmode_task);
}
/*
* Check if the specified rate set supports ERP.
* NB: the rate set is assumed to be sorted.
@ -1344,7 +1721,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
* further.
*/
if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) {
vap->iv_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) {
static const uint8_t logCwMin[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = 3,
[IEEE80211_MODE_11A] = 3,
@ -1370,22 +1747,6 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
ieee80211_wme_acnames[WME_AC_BE], chanp->wmep_logcwmin);
}
/*
* Arrange for the beacon update.
*
* XXX what about MBSS, WDS?
*/
if (vap->iv_opmode == IEEE80211_M_HOSTAP
|| vap->iv_opmode == IEEE80211_M_IBSS) {
/*
* Arrange for a beacon update and bump the parameter
* set number so associated stations load the new values.
*/
wme->wme_bssChanParams.cap_info =
(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
}
/* schedule the deferred WME update */
ieee80211_runtask(ic, &vap->iv_wme_task);
@ -1499,7 +1860,7 @@ update_chw(void *arg, int npending)
}
/*
* Deferred WME update.
* Deferred WME parameter and beacon update.
*
* In preparation for per-VAP WME configuration, call the VAP
* method if the VAP requires it. Otherwise, just call the
@ -1511,12 +1872,32 @@ vap_update_wme(void *arg, int npending)
{
struct ieee80211vap *vap = arg;
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
/* Driver update */
if (vap->iv_wme_update != NULL)
vap->iv_wme_update(vap,
ic->ic_wme.wme_chanParams.cap_wmeParams);
else
ic->ic_wme.wme_update(ic);
IEEE80211_LOCK(ic);
/*
* Arrange for the beacon update.
*
* XXX what about MBSS, WDS?
*/
if (vap->iv_opmode == IEEE80211_M_HOSTAP
|| vap->iv_opmode == IEEE80211_M_IBSS) {
/*
* Arrange for a beacon update and bump the parameter
* set number so associated stations load the new values.
*/
wme->wme_bssChanParams.cap_info =
(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME);
}
IEEE80211_UNLOCK(ic);
}
static void

View File

@ -303,6 +303,9 @@ void ieee80211_wme_vap_getparams(struct ieee80211vap *vap,
void ieee80211_wme_ic_getparams(struct ieee80211com *ic,
struct chanAccParams *);
int ieee80211_wme_vap_ac_is_noack(struct ieee80211vap *vap, int ac);
void ieee80211_vap_update_preamble(struct ieee80211vap *vap);
void ieee80211_vap_update_erp_protmode(struct ieee80211vap *vap);
void ieee80211_vap_update_ht_protmode(struct ieee80211vap *vap);
/*
* Return pointer to the QoS field from a Qos frame.

View File

@ -1460,12 +1460,13 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
ni->ni_erp, scan.erp);
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
(ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
ic->ic_flags |= IEEE80211_F_USEPROT;
vap->iv_flags |= IEEE80211_F_USEPROT;
else
ic->ic_flags &= ~IEEE80211_F_USEPROT;
vap->iv_flags &= ~IEEE80211_F_USEPROT;
ni->ni_erp = scan.erp;
/* XXX statistic */
/* XXX driver notification */
/* driver notification */
ieee80211_vap_update_erp_protmode(vap);
}
if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
@ -1891,15 +1892,16 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
*/
if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
ic->ic_flags &= ~IEEE80211_F_USEBARKER;
vap->iv_flags |= IEEE80211_F_SHPREAMBLE;
vap->iv_flags &= ~IEEE80211_F_USEBARKER;
} else {
ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
ic->ic_flags |= IEEE80211_F_USEBARKER;
vap->iv_flags &= ~IEEE80211_F_SHPREAMBLE;
vap->iv_flags |= IEEE80211_F_USEBARKER;
}
ieee80211_vap_set_shortslottime(vap,
IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
ieee80211_vap_update_preamble(vap);
/*
* Honor ERP protection.
*
@ -1907,17 +1909,18 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
*/
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
(ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
ic->ic_flags |= IEEE80211_F_USEPROT;
vap->iv_flags |= IEEE80211_F_USEPROT;
else
ic->ic_flags &= ~IEEE80211_F_USEPROT;
vap->iv_flags &= ~IEEE80211_F_USEPROT;
ieee80211_vap_update_erp_protmode(vap);
IEEE80211_NOTE_MAC(vap,
IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, wh->i_addr2,
"%sassoc success at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s%s",
ISREASSOC(subtype) ? "re" : "",
IEEE80211_NODE_AID(ni),
ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
vap->iv_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
vap->iv_flags&IEEE80211_F_SHSLOT ? "short" : "long",
ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
vap->iv_flags&IEEE80211_F_USEPROT ? ", protection" : "",
ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
ni->ni_flags & IEEE80211_NODE_HT ?
(ni->ni_chw == 40 ? ", HT40" : ", HT20") : "",

View File

@ -233,17 +233,11 @@ struct ieee80211com {
/* XXX multi-bss: split out common/vap parts */
struct ieee80211_wme_state ic_wme; /* WME/WMM state */
/* XXX multi-bss: can per-vap be done/make sense? */
/* Protection mode for net80211 driven channel NICs */
enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */
uint16_t ic_nonerpsta; /* # non-ERP stations */
uint16_t ic_longslotsta; /* # long slot time stations */
uint16_t ic_sta_assoc; /* stations associated */
uint16_t ic_ht_sta_assoc;/* HT stations associated */
uint16_t ic_ht40_sta_assoc;/* HT40 stations associated */
uint8_t ic_curhtprotmode;/* HTINFO bss state */
enum ieee80211_protmode ic_htprotmode; /* HT protection mode */
int ic_lastnonerp; /* last time non-ERP sta noted*/
int ic_lastnonht; /* last time non-HT sta noted */
uint8_t ic_curhtprotmode;/* HTINFO bss state */
uint8_t ic_rxstream; /* # RX streams */
uint8_t ic_txstream; /* # TX streams */
@ -391,6 +385,8 @@ struct ieee80211_aclator;
struct ieee80211_tdma_state;
struct ieee80211_mesh_state;
struct ieee80211_hwmp_state;
struct ieee80211_rx_histogram;
struct ieee80211_tx_histogram;
struct ieee80211vap {
struct ifmedia iv_media; /* interface media config */
@ -577,13 +573,38 @@ struct ieee80211vap {
const struct wmeParams *wme_params);
struct task iv_wme_task; /* deferred VAP WME update */
/* associated state; protection mode */
enum ieee80211_protmode iv_protmode; /* 802.11g protection mode */
enum ieee80211_protmode iv_htprotmode; /* HT protection mode */
uint8_t iv_curhtprotmode;/* HTINFO bss state */
uint16_t iv_nonerpsta; /* # non-ERP stations */
uint16_t iv_longslotsta; /* # long slot time stations */
uint16_t iv_ht_sta_assoc;/* HT stations associated */
uint16_t iv_ht40_sta_assoc;/* HT40 stations associated */
int iv_lastnonerp; /* last time non-ERP sta noted*/
int iv_lastnonht; /* last time non-HT sta noted */
/* update device state for 802.11 slot time change */
void (*iv_updateslot)(struct ieee80211vap *);
struct task iv_slot_task; /* deferred slot time update */
struct task iv_erp_protmode_task; /* deferred ERP protmode update */
void (*iv_erp_protmode_update)(struct ieee80211vap *);
struct task iv_preamble_task; /* deferred short/barker preamble update */
void (*iv_preamble_update)(struct ieee80211vap *);
struct task iv_ht_protmode_task; /* deferred HT protmode update */
void (*iv_ht_protmode_update)(struct ieee80211vap *);
/* per-vap U-APSD state */
uint8_t iv_uapsdinfo; /* sta mode QoS Info flags */
/* Optional transmit/receive histogram statistics */
struct ieee80211_rx_histogram *rx_histogram;
struct ieee80211_tx_histogram *tx_histogram;
uint64_t iv_spare[6];
};
MALLOC_DECLARE(M_80211_VAP);