From: Juuso Oikarinen <[email protected]>
The wl1271 has hardware connection monitoring, i.e. the hardware chipset
internally handles periodic null-funcs and probe-req transmitting in case of
non-acked periodic null-func or beacon loss.
To be able to transmit a valid probe-request to the associated to AP, the
hardware needs a template for it. Currently, there is no way for the hardware
to have this template, as the SSID and supported rate IE's are not passed down
by the mac80211.
These patches propose adding a function to mac80211, which the driver can use
to get a template for the currently associated-to AP, filled with the basic
needed IE's. The wl1271 patch uses this function, and configures it to the
hardware.
Any thoughts?
Juuso Oikarinen (2):
mac80211: Add function to get probe request template for current AP
wl1271: Fix setting of the hardware connection monitoring probe-req
template
drivers/net/wireless/wl12xx/wl1271.h | 3 ++
drivers/net/wireless/wl12xx/wl1271_cmd.c | 28 +++++++++++++++++++++++++
drivers/net/wireless/wl12xx/wl1271_cmd.h | 2 +
drivers/net/wireless/wl12xx/wl1271_main.c | 32 ++++++++++++++++++----------
drivers/net/wireless/wl12xx/wl1271_scan.c | 4 +++
include/net/mac80211.h | 12 ++++++++++
net/mac80211/ieee80211_i.h | 4 +++
net/mac80211/mlme.c | 24 +++++++++++++++++++++
net/mac80211/util.c | 23 ++++++++++++++++----
9 files changed, 115 insertions(+), 17 deletions(-)
From: Juuso Oikarinen <[email protected]>
The probe-request template used in the hardware connection monitoring feature
thus far has been an empty one, without the SSID IE and without supported rate
IEs. This causes problems with some AP's.
Additionally, after connected scans, the template for connection maintenance
would remain to be the one last used for scanning - potentially incorrect.
Fix these by getting a pre-filled directed probe-request template for the
associated-to AP from mac80211.
Signed-off-by: Juuso Oikarinen <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 3 ++
drivers/net/wireless/wl12xx/wl1271_cmd.c | 28 +++++++++++++++++++++++++
drivers/net/wireless/wl12xx/wl1271_cmd.h | 2 +
drivers/net/wireless/wl12xx/wl1271_main.c | 32 ++++++++++++++++++----------
drivers/net/wireless/wl12xx/wl1271_scan.c | 4 +++
5 files changed, 57 insertions(+), 12 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 8a4cd76..a6b519a 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -429,6 +429,9 @@ struct wl1271 {
struct wl1271_scan scan;
struct delayed_work scan_complete_work;
+ /* probe-req template for the current AP */
+ struct sk_buff *probereq;
+
/* Our association ID */
u16 aid;
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 5d3e848..bde1b80 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -611,6 +611,34 @@ out:
return ret;
}
+struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
+ struct sk_buff *skb)
+{
+ int ret;
+
+ if (!skb)
+ skb = ieee80211_ap_probereq_get(wl->hw, wl->vif);
+ if (!skb)
+ goto out;
+
+ wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len);
+
+ if (wl->band == IEEE80211_BAND_2GHZ)
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+ skb->data, skb->len, 0,
+ wl->conf.tx.basic_rate);
+ else
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+ skb->data, skb->len, 0,
+ wl->conf.tx.basic_rate_5);
+
+ if (ret < 0)
+ wl1271_error("Unable to set ap probe request template.");
+
+out:
+ return skb;
+}
+
int wl1271_build_qos_null_data(struct wl1271 *wl)
{
struct ieee80211_qos_hdr template;
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h
index a0caf4f..0552dea 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.h
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h
@@ -49,6 +49,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
int wl1271_cmd_build_probe_req(struct wl1271 *wl,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, u8 band);
+struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
+ struct sk_buff *skb);
int wl1271_build_qos_null_data(struct wl1271 *wl);
int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 48a4b99..4787c52 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1685,13 +1685,13 @@ out:
return ret;
}
-static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
+static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
+ int offset)
{
- u8 *ptr = beacon->data +
- offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ u8 *ptr = skb->data + offset;
/* find the location of the ssid in the beacon */
- while (ptr < beacon->data + beacon->len) {
+ while (ptr < skb->data + skb->len) {
if (ptr[0] == WLAN_EID_SSID) {
wl->ssid_len = ptr[1];
memcpy(wl->ssid, ptr+2, wl->ssid_len);
@@ -1699,7 +1699,7 @@ static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
}
ptr += ptr[1];
}
- wl1271_error("ad-hoc beacon template has no SSID!\n");
+ wl1271_error("No SSID in IEs!\n");
}
static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
@@ -1738,8 +1738,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (beacon) {
struct ieee80211_hdr *hdr;
+ int ieoffset = offsetof(struct ieee80211_mgmt,
+ u.beacon.variable);
+
+ wl1271_ssid_set(wl, beacon, ieoffset);
- wl1271_ssid_set(wl, beacon);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
beacon->data,
beacon->len, 0,
@@ -1819,6 +1822,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
u32 rates;
+ int ieoffset;
wl->aid = bss_conf->aid;
set_assoc = true;
@@ -1847,13 +1851,13 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
goto out_sleep;
/*
- * The SSID is intentionally set to NULL here - the
- * firmware will set the probe request with a
- * broadcast SSID regardless of what we set in the
- * template.
+ * Get a template for hardware connection maintenance
*/
- ret = wl1271_cmd_build_probe_req(wl, NULL, 0,
- NULL, 0, wl->band);
+ dev_kfree_skb(wl->probereq);
+ wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL);
+ ieoffset = offsetof(struct ieee80211_mgmt,
+ u.probe_req.variable);
+ wl1271_ssid_set(wl, wl->probereq, ieoffset);
/* enable the connection monitoring feature */
ret = wl1271_acx_conn_monit_params(wl, true);
@@ -1876,6 +1880,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
wl->aid = 0;
+ /* free probe-request template */
+ dev_kfree_skb(wl->probereq);
+ wl->probereq = NULL;
+
/* re-enable dynamic ps - just in case */
ieee80211_enable_dyn_ps(wl->vif);
diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.c b/drivers/net/wireless/wl12xx/wl1271_scan.c
index 909bb47..9deb9bf 100644
--- a/drivers/net/wireless/wl12xx/wl1271_scan.c
+++ b/drivers/net/wireless/wl12xx/wl1271_scan.c
@@ -52,6 +52,10 @@ void wl1271_scan_complete_work(struct work_struct *work)
ieee80211_scan_completed(wl->hw, false);
+ /* restore hardware connection monitoring template */
+ if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+ wl1271_cmd_build_ap_probe_req(wl, wl->probereq);
+
if (wl->scan.failed) {
wl1271_info("Scan completed due to error.");
ieee80211_queue_work(wl->hw, &wl->recovery_work);
--
1.7.1
On Fri, 2010-10-29 at 14:27 +0300, [email protected] wrote:
> From: Juuso Oikarinen <[email protected]>
>
> The wl1271 has hardware connection monitoring, i.e. the hardware chipset
> internally handles periodic null-funcs and probe-req transmitting in case of
> non-acked periodic null-func or beacon loss.
I'm wondering why it wants to do probe-req instead of nullfunc? I mean,
we're trying to make mac80211 use nullfunc+ACK instead of probereq
+proberesp ...
johannes
On Fri, 2010-10-29 at 13:35 +0200, ext Johannes Berg wrote:
> On Fri, 2010-10-29 at 14:27 +0300, [email protected] wrote:
> > From: Juuso Oikarinen <[email protected]>
> >
> > The wl1271 has hardware connection monitoring, i.e. the hardware chipset
> > internally handles periodic null-funcs and probe-req transmitting in case of
> > non-acked periodic null-func or beacon loss.
>
> I'm wondering why it wants to do probe-req instead of nullfunc? I mean,
> we're trying to make mac80211 use nullfunc+ACK instead of probereq
> +proberesp ...
The wl1271 actually uses null-funcs for the periodic stuff.
Only when the null-func fails, or it misses a specific number of
consecutive beacons, it will transmit a probe-request to the AP in a
final attempt to re-sync before indicating connection loss to the host.
The thing with the wl1271 is, that we cannot disable the probe-req. If
we don't configure a template for it, it will transmit garbage upon
losing connection with the AP.
Why it uses a probe-request as the final means to resync, I'm not sure.
-Juuso
From: Juuso Oikarinen <[email protected]>
Chipsets with hardware based connection monitoring need to autonomically
send directed probe-request frames to the AP (in the event of beacon loss,
for example.)
For the hardware to be able to do this, it requires a template for the frame
to transmit to the AP, filled in with the BSSID and SSID of the AP, but also
the supported rate IE's.
This patch adds a function to mac80211, which allows the hardware driver to
fetch this template after association, so it can be configured to the hardware.
Signed-off-by: Juuso Oikarinen <[email protected]>
---
include/net/mac80211.h | 12 ++++++++++++
net/mac80211/ieee80211_i.h | 4 ++++
net/mac80211/mlme.c | 24 ++++++++++++++++++++++++
net/mac80211/util.c | 23 ++++++++++++++++++-----
4 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9fdf982..8f212e9 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2501,6 +2501,18 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
struct ieee80211_sta *pubsta, bool block);
/**
+ * ieee80211_ap_probereq_get - retrieve a Probe Request template
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * Creates a Probe Request template which can, for example, be uploaded to
+ * hardware. The template is filled with bssid, ssid and supported rate
+ * information.
+ */
+struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+
+/**
* ieee80211_beacon_loss - inform hardware does not receive beacons
*
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b80c386..59a1d38 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1287,6 +1287,10 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
u8 channel);
+struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
+ u8 *dst,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *ie, size_t ie_len);
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index a3a9421..dfc4a31 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1108,6 +1108,30 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&ifmgd->mtx);
}
+struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct sk_buff *skb;
+ const u8 *ssid;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+ return NULL;
+
+ ASSERT_MGD_MTX(ifmgd);
+
+ if (!ifmgd->associated)
+ return NULL;
+
+ ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+ skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
+ ssid + 2, ssid[1], NULL, 0);
+
+ return skb;
+}
+EXPORT_SYMBOL(ieee80211_ap_probereq_get);
+
static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0b6fc92..885713d 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1011,9 +1011,10 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
return pos - buffer;
}
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
- const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len)
+struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
+ u8 *dst,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *ie, size_t ie_len)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -1027,7 +1028,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
if (!buf) {
printk(KERN_DEBUG "%s: failed to allocate temporary IE "
"buffer\n", sdata->name);
- return;
+ return NULL;
}
chan = ieee80211_frequency_to_channel(
@@ -1050,8 +1051,20 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
}
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
- ieee80211_tx_skb(sdata, skb);
kfree(buf);
+
+ return skb;
+}
+
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *ie, size_t ie_len)
+{
+ struct sk_buff *skb;
+
+ skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len);
+ if (skb)
+ ieee80211_tx_skb(sdata, skb);
}
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
--
1.7.1