summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstsp <stsp@openbsd.org>2016-01-25 11:27:11 +0000
committerstsp <stsp@openbsd.org>2016-01-25 11:27:11 +0000
commitcb2109bf4c69a23d22df6f7ea603dfae22198109 (patch)
treea2726e5970c912da55d90b970df5eed2f96a4632
parentRevert the minimum number of Rx ring slots back to 32 (diff)
downloadwireguard-openbsd-cb2109bf4c69a23d22df6f7ea603dfae22198109.tar.xz
wireguard-openbsd-cb2109bf4c69a23d22df6f7ea603dfae22198109.zip
Keep track of HT protection settings in beacons and have 11n-capable
drivers update hardware configuration accordingly. tested by myself, tb@, deraadt@, abieber@ ok mpi@
-rw-r--r--sys/dev/pci/if_iwm.c35
-rw-r--r--sys/dev/pci/if_iwmvar.h5
-rw-r--r--sys/dev/pci/if_iwn.c88
-rw-r--r--sys/net80211/ieee80211_input.c30
-rw-r--r--sys/net80211/ieee80211_var.h4
5 files changed, 151 insertions, 11 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index 295a510edf2..8cd422dbaa0 100644
--- a/sys/dev/pci/if_iwm.c
+++ b/sys/dev/pci/if_iwm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwm.c,v 1.75 2016/01/07 23:08:38 stsp Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.76 2016/01/25 11:27:11 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -294,6 +294,8 @@ int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
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, struct ieee80211_node *ni,
}
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 device *self, void *aux)
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
diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h
index de4d2a715a4..95688e67c8f 100644
--- a/sys/dev/pci/if_iwmvar.h
+++ b/sys/dev/pci/if_iwmvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwmvar.h,v 1.15 2016/01/05 18:41:15 stsp Exp $ */
+/* $OpenBSD: if_iwmvar.h,v 1.16 2016/01/25 11:27:11 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -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;
diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c
index 03e69451e20..18e60135ba8 100644
--- a/sys/dev/pci/if_iwn.c
+++ b/sys/dev/pci/if_iwn.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwn.c,v 1.157 2016/01/13 14:39:35 stsp Exp $ */
+/* $OpenBSD: if_iwn.c,v 1.158 2016/01/25 11:27:11 stsp Exp $ */
/*-
* Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -226,6 +226,8 @@ int iwn_set_key(struct ieee80211com *, struct ieee80211_node *,
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 device *self, void *aux)
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
@@ -5012,6 +5015,89 @@ iwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
}
/*
+ * 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);
+}
+
+/*
* This function is called by upper layer when an ADDBA request is received
* from another STA and before the ADDBA response is sent.
*/
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 17b57390438..a1811563a0f 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_input.c,v 1.151 2016/01/07 23:22:31 stsp Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.152 2016/01/25 11:27:11 stsp Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
@@ -1577,10 +1577,14 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, struct mbuf *m,
} 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 ieee80211com *ic, struct mbuf *m,
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 ieee80211com *ic, struct mbuf *m,
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) {
/*
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index ea672da5658..ab20a704046 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_var.h,v 1.70 2016/01/12 09:28:09 stsp Exp $ */
+/* $OpenBSD: ieee80211_var.h,v 1.71 2016/01/25 11:27:11 stsp Exp $ */
/* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */
/*-
@@ -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];