Discussion:
keep track of HT protection in 11n mode
Stefan Sperling
2016-01-20 21:04:11 UTC
Permalink
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.

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 20 Jan 2016 20:42:23 -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 *, const 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, const 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 20 Jan 2016 20:45:40 -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 *,
+ const 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,44 @@ 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, const struct ieee80211_node *ni)
+{
+ struct iwn_softc *sc = ic->ic_softc;
+ struct iwn_rxon_assoc cmd;
+ enum ieee80211_htprot htprot;
+ int error;
+
+ memset(&cmd, 0, sizeof cmd);
+
+ /* Copy settings from RXON configuration. */
+ cmd.flags = sc->rxon.flags;
+ cmd.filter = sc->rxon.filter;
+ cmd.cck_mask = sc->rxon.cck_mask;
+ cmd.ofdm_mask = sc->rxon.ofdm_mask;
+ cmd.ht_single_mask = sc->rxon.ht_single_mask;
+ cmd.ht_dual_mask = sc->rxon.ht_dual_mask;
+ cmd.ht_triple_mask = sc->rxon.ht_triple_mask;
+ cmd.rxchain = sc->rxon.rxchain;
+ cmd.acquisition = sc->rxon.acquisition;
+
+ /* Update HT protection setting. */
+ htprot = ((ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK) >>
+ IEEE80211_HTOP1_PROT_SHIFT);
+ cmd.flags &= ~htole32(IWN_RXON_HT_PROTMODE(3));
+ cmd.flags |= htole32(IWN_RXON_HT_PROTMODE(htprot));
+
+ /* Use the RXON_ASSOC command since we're already associated. */
+ error = iwn_cmd(sc, IWN_CMD_RXON_ASSOC, &cmd, sizeof cmd, 1);
+ if (error != 0)
+ printf("%s: could not change HT protection: error %d\n",
+ sc->sc_dev.dv_xname, error);
}

/*
Index: dev/pci/if_iwnreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwnreg.h,v
retrieving revision 1.51
diff -u -p -r1.51 if_iwnreg.h
--- dev/pci/if_iwnreg.h 7 Jan 2016 23:08:38 -0000 1.51
+++ dev/pci/if_iwnreg.h 20 Jan 2016 20:09:13 -0000
@@ -529,6 +529,22 @@ struct iwn_rxon {
#define IWN4965_RXONSZ (sizeof (struct iwn_rxon) - 6)
#define IWN5000_RXONSZ (sizeof (struct iwn_rxon))

+/* Structure for command IWN_CMD_RXON_ASSOC. */
+struct iwn_rxon_assoc {
+ uint32_t flags;
+ uint32_t filter;
+ uint8_t cck_mask;
+ uint8_t ofdm_mask;
+ uint16_t reserved1;
+ uint8_t ht_single_mask;
+ uint8_t ht_dual_mask;
+ uint8_t ht_triple_mask;
+ uint8_t reserved2;
+ uint16_t rxchain;
+ uint16_t acquisition;
+ uint32_t reserved3;
+} __packed;
+
/* Structure for command IWN_CMD_ASSOCIATE. */
struct iwn_assoc {
uint32_t flags;
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 20 Jan 2016 20:52:08 -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 20 Jan 2016 19:18:40 -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 *,
+ const 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];
Stefan Sperling
2016-01-20 21:16:53 UTC
Permalink
Post by Stefan Sperling
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...
Stefan Sperling
2016-01-21 00:57:28 UTC
Permalink
Post by Stefan Sperling
Post by Stefan Sperling
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.

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];
Stefan Sperling
2016-01-24 09:09:36 UTC
Permalink
Post by Stefan Sperling
Post by Stefan Sperling
Post by Stefan Sperling
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.
Post by Stefan Sperling
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];
Loading...