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];
> 

Reply via email to