On Thu, Jan 21, 2016 at 01:57:28AM +0100, Stefan Sperling wrote: > On Wed, Jan 20, 2016 at 10:16:53PM +0100, Stefan Sperling wrote: > > On Wed, Jan 20, 2016 at 10:04:11PM +0100, Stefan Sperling wrote: > > > This diff makes us keep track of changes in the network's HT protection > > > settings. These settings are advertised in beacons and change dynamically > > > based on the nature of clients associated to an AP at a given moment. > > > > > > Tracking these changes is rather important. > > > If a non-11n client associates to an AP which previously had 11n clients > > > only, we must update our wireless device's configuration accordingly or > > > the new client might damage frames we send out. > > > > This diff still has issues on iwn(4). Don't test there yet, please... > > This diff works fine for me with both iwm(4) and iwn(4). > > I couldn't figure out how to make proper use of iwn's RXON_ASSOC command. > Linux uses this command to avoid having to restore a lot of state in > firmware when changing RXON flags. In my case sending RXON_ASSOC always > broke Tx. I'm now using an implementation which uses RXON but works.
I know a few people have been testing this. Anyone who wants to review? My plan is to commit this soon unless I hear objections. > Index: dev/pci/if_iwm.c > =================================================================== > RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v > retrieving revision 1.75 > diff -u -p -r1.75 if_iwm.c > --- dev/pci/if_iwm.c 7 Jan 2016 23:08:38 -0000 1.75 > +++ dev/pci/if_iwm.c 21 Jan 2016 00:31:38 -0000 > @@ -294,6 +294,8 @@ int iwm_nvm_read_section(struct iwm_soft > uint16_t *); > void iwm_init_channel_map(struct iwm_softc *, const uint16_t * const); > void iwm_setup_ht_rates(struct iwm_softc *); > +void iwm_htprot_task(void *); > +void iwm_update_htprot(struct ieee80211com *, struct ieee80211_node *); > int iwm_ampdu_rx_start(struct ieee80211com *, > struct ieee80211_node *, uint8_t); > void iwm_ampdu_rx_stop(struct ieee80211com *, > @@ -2602,6 +2604,34 @@ iwm_mvm_sta_rx_agg(struct iwm_softc *sc, > } > > void > +iwm_htprot_task(void *arg) > +{ > + struct iwm_softc *sc = arg; > + struct ieee80211com *ic = &sc->sc_ic; > + struct iwm_node *in = (void *)ic->ic_bss; > + int error; > + > + /* This call updates HT protection based on in->in_ni.ni_htop1. */ > + error = iwm_mvm_mac_ctxt_changed(sc, in); > + if (error != 0) > + printf("%s: could not change HT protection: error %d\n", > + DEVNAME(sc), error); > +} > + > +/* > + * This function is called by upper layer when HT protection settings in > + * beacons have changed. > + */ > +void > +iwm_update_htprot(struct ieee80211com *ic, struct ieee80211_node *ni) > +{ > + struct iwm_softc *sc = ic->ic_softc; > + > + /* assumes that ni == ic->ic_bss */ > + task_add(systq, &sc->htprot_task); > +} > + > +void > iwm_ba_task(void *arg) > { > struct iwm_softc *sc = arg; > @@ -5878,6 +5908,7 @@ iwm_stop(struct ifnet *ifp, int disable) > task_del(sc->sc_eswq, &sc->sc_eswk); > task_del(systq, &sc->setrates_task); > task_del(systq, &sc->ba_task); > + task_del(systq, &sc->htprot_task); > > sc->sc_newstate(ic, IEEE80211_S_INIT, -1); > > @@ -6586,6 +6617,7 @@ iwm_preinit(struct iwm_softc *sc) > /* Override 802.11 state transition machine. */ > sc->sc_newstate = ic->ic_newstate; > ic->ic_newstate = iwm_newstate; > + ic->ic_update_htprot = iwm_update_htprot; > ic->ic_ampdu_rx_start = iwm_ampdu_rx_start; > ic->ic_ampdu_rx_stop = iwm_ampdu_rx_stop; > #ifdef notyet > @@ -6822,6 +6854,7 @@ iwm_attach(struct device *parent, struct > task_set(&sc->newstate_task, iwm_newstate_task, sc); > task_set(&sc->setrates_task, iwm_setrates_task, sc); > task_set(&sc->ba_task, iwm_ba_task, sc); > + task_set(&sc->htprot_task, iwm_htprot_task, sc); > > /* > * We cannot read the MAC address without loading the > Index: dev/pci/if_iwmvar.h > =================================================================== > RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v > retrieving revision 1.15 > diff -u -p -r1.15 if_iwmvar.h > --- dev/pci/if_iwmvar.h 5 Jan 2016 18:41:15 -0000 1.15 > +++ dev/pci/if_iwmvar.h 20 Jan 2016 17:37:06 -0000 > @@ -376,6 +376,9 @@ struct iwm_softc { > int ba_tid; > uint16_t ba_ssn; > > + /* Task for HT protection updates. */ > + struct task htprot_task; > + > bus_space_tag_t sc_st; > bus_space_handle_t sc_sh; > bus_size_t sc_sz; > Index: dev/pci/if_iwn.c > =================================================================== > RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v > retrieving revision 1.157 > diff -u -p -r1.157 if_iwn.c > --- dev/pci/if_iwn.c 13 Jan 2016 14:39:35 -0000 1.157 > +++ dev/pci/if_iwn.c 21 Jan 2016 00:42:59 -0000 > @@ -226,6 +226,8 @@ int iwn_set_key(struct ieee80211com *, > struct ieee80211_key *); > void iwn_delete_key(struct ieee80211com *, struct ieee80211_node *, > struct ieee80211_key *); > +void iwn_update_htprot(struct ieee80211com *, > + struct ieee80211_node *); > int iwn_ampdu_rx_start(struct ieee80211com *, > struct ieee80211_node *, uint8_t); > void iwn_ampdu_rx_stop(struct ieee80211com *, > @@ -515,6 +517,7 @@ iwn_attach(struct device *parent, struct > ic->ic_updateedca = iwn_updateedca; > ic->ic_set_key = iwn_set_key; > ic->ic_delete_key = iwn_delete_key; > + ic->ic_update_htprot = iwn_update_htprot; > ic->ic_ampdu_rx_start = iwn_ampdu_rx_start; > ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop; > #ifdef notyet > @@ -5009,6 +5012,89 @@ iwn_delete_key(struct ieee80211com *ic, > node.kid = 0xff; > DPRINTF(("delete keys for node %d\n", node.id)); > (void)ops->add_node(sc, &node, 1); > +} > + > +/* > + * This function is called by upper layer when HT protection settings in > + * beacons have changed. > + */ > +void > +iwn_update_htprot(struct ieee80211com *ic, struct ieee80211_node *ni) > +{ > + struct iwn_softc *sc = ic->ic_softc; > + struct iwn_ops *ops = &sc->ops; > + enum ieee80211_htprot htprot; > + struct iwn_node_info node; > + int error, ridx; > + > + timeout_del(&sc->calib_to); > + > + /* Fake a "disassociation" so we can change RXON configuration. */ > + sc->rxon.filter &= ~htole32(IWN_FILTER_BSS); > + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); > + if (error != 0) { > + printf("%s: RXON command failed\n", sc->sc_dev.dv_xname); > + return; > + } > + > + /* Update HT protection mode setting. */ > + htprot = (ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK) >> > + IEEE80211_HTOP1_PROT_SHIFT; > + sc->rxon.flags &= ~htole32(IWN_RXON_HT_PROTMODE(3)); > + sc->rxon.flags |= htole32(IWN_RXON_HT_PROTMODE(htprot)); > + sc->rxon.filter |= htole32(IWN_FILTER_BSS); > + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1); > + if (error != 0) { > + printf("%s: RXON command failed\n", sc->sc_dev.dv_xname); > + return; > + } > + > + /* > + * The firmware loses TX power table, node table, LQ table, > + * and sensitivity calibration after an RXON command. > + */ > + > + if ((error = ops->set_txpower(sc, 1)) != 0) { > + printf("%s: could not set TX power\n", sc->sc_dev.dv_xname); > + return; > + } > + > + ridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? > + IWN_RIDX_OFDM6 : IWN_RIDX_CCK1; > + if ((error = iwn_add_broadcast_node(sc, 1, ridx)) != 0) { > + printf("%s: could not add broadcast node\n", > + sc->sc_dev.dv_xname); > + return; > + } > + > + memset(&node, 0, sizeof node); > + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); > + node.id = IWN_ID_BSS; > +#ifdef notyet > + node.htflags = htole32(IWN_AMDPU_SIZE_FACTOR(3) | > + IWN_AMDPU_DENSITY(5)); /* 2us */ > +#endif > + error = ops->add_node(sc, &node, 1); > + if (error != 0) { > + printf("%s: could not add BSS node\n", sc->sc_dev.dv_xname); > + return; > + } > + > + if ((error = iwn_set_link_quality(sc, ni)) != 0) { > + printf("%s: could not setup link quality for node %d\n", > + sc->sc_dev.dv_xname, node.id); > + return; > + } > + > + if ((error = iwn_init_sensitivity(sc)) != 0) { > + printf("%s: could not set sensitivity\n", > + sc->sc_dev.dv_xname); > + return; > + } > + > + sc->calib.state = IWN_CALIB_STATE_ASSOC; > + sc->calib_cnt = 0; > + timeout_add_msec(&sc->calib_to, 500); > } > > /* > Index: net80211/ieee80211_input.c > =================================================================== > RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v > retrieving revision 1.151 > diff -u -p -r1.151 ieee80211_input.c > --- net80211/ieee80211_input.c 7 Jan 2016 23:22:31 -0000 1.151 > +++ net80211/ieee80211_input.c 21 Jan 2016 00:32:32 -0000 > @@ -1577,10 +1577,14 @@ ieee80211_recv_probe_resp(struct ieee802 > } else > is_new = 0; > > + if (htcaps) > + ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]); > + if (htop) > + ieee80211_setup_htop(ni, htop + 2, htop[1]); > + > /* > * When operating in station mode, check for state updates > - * while we're associated. We consider only 11g stuff right > - * now. > + * while we're associated. > */ > if (ic->ic_opmode == IEEE80211_M_STA && > ic->ic_state == IEEE80211_S_RUN && > @@ -1599,6 +1603,22 @@ ieee80211_recv_probe_resp(struct ieee802 > ic->ic_flags &= ~IEEE80211_F_USEPROT; > ic->ic_bss->ni_erp = erp; > } > + if (ic->ic_bss->ni_flags & IEEE80211_NODE_HT) { > + enum ieee80211_htprot htprot_last, htprot; > + htprot_last = > + ((ic->ic_bss->ni_htop1 & IEEE80211_HTOP1_PROT_MASK) > + >> IEEE80211_HTOP1_PROT_SHIFT); > + htprot = ((ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK) >> > + IEEE80211_HTOP1_PROT_SHIFT); > + if (htprot_last != htprot) { > + DPRINTF(("[%s] htprot change: was %d, now %d\n", > + ether_sprintf((u_int8_t *)wh->i_addr2), > + htprot_last, htprot)); > + ic->ic_bss->ni_htop1 = ni->ni_htop1; > + ic->ic_update_htprot(ic, ic->ic_bss); > + } > + } > + > /* > * Check if AP short slot time setting has changed > * since last beacon and give the driver a chance to > @@ -1679,10 +1699,6 @@ ieee80211_recv_probe_resp(struct ieee802 > ni->ni_erp = erp; > /* NB: must be after ni_chan is setup */ > ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); > - if (htcaps) > - ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]); > - if (htop) > - ieee80211_setup_htop(ni, htop + 2, htop[1]); > #ifndef IEEE80211_STA_ONLY > if (ic->ic_opmode == IEEE80211_M_IBSS && is_new && isprobe) { > /* > Index: net80211/ieee80211_var.h > =================================================================== > RCS file: /cvs/src/sys/net80211/ieee80211_var.h,v > retrieving revision 1.70 > diff -u -p -r1.70 ieee80211_var.h > --- net80211/ieee80211_var.h 12 Jan 2016 09:28:09 -0000 1.70 > +++ net80211/ieee80211_var.h 21 Jan 2016 00:31:54 -0000 > @@ -213,6 +213,8 @@ struct ieee80211com { > struct ieee80211_node *, u_int8_t); > void (*ic_ampdu_rx_stop)(struct ieee80211com *, > struct ieee80211_node *, u_int8_t); > + void (*ic_update_htprot)(struct ieee80211com *, > + struct ieee80211_node *); > u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; > struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; > struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; >