I have been thinking about using nullfunc frames in connection monitor
with hw which have IEEE80211_HW_REPORTS_TX_ACK_STATUS. Here are my
current patches based on the idea. They have received minimal testing
and there's still some work to do.
But I would like to get comments what people think about this. Might
this create some kind of IOP problems, for example?
I also added a new hw flag to be able to offload connection monitoring
to the hardware.
TODO:
o send qos nullfunc when using wmm
---
Kalle Valo (3):
mac80211: refactor nullfunc status handling to a separate function
mac80211: use nullfunc in connection monitor
mac80211: add support connection monitor in hardware
include/net/mac80211.h | 1 +
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mlme.c | 40 +++++++++++++++++----
net/mac80211/status.c | 84 +++++++++++++++++++++++++++++++++++++-------
4 files changed, 106 insertions(+), 20 deletions(-)
From: Kalle Valo <[email protected]>
wl1271 has a feature so that it handles the connection monitor logic
in hardware, basically sending periodically nullfunc frames and reporting
to the host if AP is lost.
Add support to mac80211 by adding a new flag IEEE80211_HW_CONNECTION_MONITOR
which prevents conn_mon_timer from triggering during idle periods.
Signed-off-by: Kalle Valo <[email protected]>
---
include/net/mac80211.h | 1 +
net/mac80211/mlme.c | 3 +++
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 80eb7cc..1d28fcc 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -975,6 +975,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
+ IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
};
/**
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 4b9596f..3fa8e6a 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -850,6 +850,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
if (is_multicast_ether_addr(hdr->addr1))
return;
+ if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+ return;
+
mod_timer(&sdata->u.mgd.conn_mon_timer,
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
}
From: Kalle Valo <[email protected]>
Create a new function for handling nullfunc status and make it easier
to add new functionality.
No functional changes.
Signed-off-by: Kalle Valo <[email protected]>
---
net/mac80211/status.c | 38 +++++++++++++++++++++++++-------------
1 files changed, 25 insertions(+), 13 deletions(-)
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 56d5b9a..8a17454 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -157,6 +157,29 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
}
}
+static void ieee80211_nullfunc_status(struct ieee80211_local *local,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ __le16 fc = hdr->frame_control;
+
+ if (ieee80211_has_pm(fc) &&
+ (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
+ !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+ local->ps_sdata && !(local->scanning)) {
+ if (info->flags & IEEE80211_TX_STAT_ACK) {
+ local->ps_sdata->u.mgd.flags |=
+ IEEE80211_STA_NULLFUNC_ACKED;
+ ieee80211_queue_work(&local->hw,
+ &local->dynamic_ps_enable_work);
+ } else
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(10));
+ }
+}
+
+
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct sk_buff *skb2;
@@ -274,19 +297,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11FailedCount++;
}
- if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
- (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
- !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
- local->ps_sdata && !(local->scanning)) {
- if (info->flags & IEEE80211_TX_STAT_ACK) {
- local->ps_sdata->u.mgd.flags |=
- IEEE80211_STA_NULLFUNC_ACKED;
- ieee80211_queue_work(&local->hw,
- &local->dynamic_ps_enable_work);
- } else
- mod_timer(&local->dynamic_ps_timer, jiffies +
- msecs_to_jiffies(10));
- }
+ if (ieee80211_is_nullfunc(fc))
+ ieee80211_nullfunc_status(local, skb);
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX)
cfg80211_action_tx_status(
From: Kalle Valo <[email protected]>
Currently mac80211 is periodically sending probe request to the AP
during idle periods to make sure that the connection to the AP works. But
on hardware which have IEEE80211_HW_REPORTS_TX_ACK_STATUS we can use
nullfunc frames. This is because we can check from the tx status if an
acknowledgement frame was received from the AP or not.
Major benefit from this is that we don't need to wakeup from power save
just for this and can save power. Another benefit is that we can faster notice
if the AP is lost and hopefully reduce the roaming time in that case.
(NB: these are not implemented yet)
In the first phase we just use nullfunc frames instead of the probe requsts
and do not change the mlme drastically, just to be on the safe side. Later
on the logic can be improved and we can get the benefits mentioned above.
Signed-off-by: Kalle Valo <[email protected]>
---
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mlme.c | 37 ++++++++++++++++++++++++------
net/mac80211/status.c | 54 +++++++++++++++++++++++++++++++++++++++++---
3 files changed, 81 insertions(+), 11 deletions(-)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 241533e..79e065b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -317,6 +317,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
+ IEEE80211_STA_CON_POLL_ACKED = BIT(9),
};
struct ieee80211_if_managed {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 41812a1..4b9596f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -857,11 +857,16 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = sdata->local;
const u8 *ssid;
- ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
- ieee80211_send_probe_req(sdata, ifmgd->associated->bssid,
- ssid + 2, ssid[1], NULL, 0);
+ if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
+ ieee80211_send_nullfunc(local, sdata, 0);
+ } else {
+ ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+ ieee80211_send_probe_req(sdata, ifmgd->associated->bssid,
+ ssid + 2, ssid[1], NULL, 0);
+ }
ifmgd->probe_send_count++;
ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT;
@@ -1579,9 +1584,27 @@ static void ieee80211_sta_work(struct work_struct *work)
/* then process the rest of the work */
mutex_lock(&ifmgd->mtx);
- if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
+ if ((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
+ (ifmgd->flags & IEEE80211_STA_CON_POLL_ACKED)) {
+ /* FIXME: refactor with rx_mgmg_probe_resp() */
+ ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
+ IEEE80211_STA_BEACON_POLL |
+ IEEE80211_STA_CON_POLL_ACKED);
+ mutex_lock(&sdata->local->iflist_mtx);
+ ieee80211_recalc_ps(sdata->local, -1);
+ mutex_unlock(&sdata->local->iflist_mtx);
+ /*
+ * We've received a probe response, but are not sure whether
+ * we have or will be receiving any beacons or data, so let's
+ * schedule the timers again, just in case.
+ */
+ mod_beacon_timer(sdata);
+ mod_timer(&ifmgd->conn_mon_timer,
+ round_jiffies_up(jiffies +
+ IEEE80211_CONNECTION_IDLE_TIME));
+ } else if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL) &&
- ifmgd->associated) {
+ ifmgd->associated) {
u8 bssid[ETH_ALEN];
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
@@ -1590,7 +1613,7 @@ static void ieee80211_sta_work(struct work_struct *work)
else if (ifmgd->probe_send_count < IEEE80211_MAX_PROBE_TRIES) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "No probe response from AP %pM"
+ printk(KERN_DEBUG "No response from AP %pM"
" after %dms, try %d\n", bssid,
(1000 * IEEE80211_PROBE_WAIT)/HZ,
ifmgd->probe_send_count);
@@ -1603,7 +1626,7 @@ static void ieee80211_sta_work(struct work_struct *work)
*/
ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
IEEE80211_STA_BEACON_POLL);
- printk(KERN_DEBUG "No probe response from AP %pM"
+ printk(KERN_DEBUG "No response from AP %pM"
" after %dms, disconnecting.\n",
bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
ieee80211_set_disassoc(sdata);
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 8a17454..5311977 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -162,12 +162,58 @@ static void ieee80211_nullfunc_status(struct ieee80211_local *local,
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_managed *ifmgd;
__le16 fc = hdr->frame_control;
- if (ieee80211_has_pm(fc) &&
- (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
- !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
- local->ps_sdata && !(local->scanning)) {
+ if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
+ return;
+
+ if (info->flags & IEEE80211_TX_CTL_INJECTED)
+ return;
+
+ if (local->scanning)
+ return;
+
+ /* FIXME: check association? */
+
+ /*
+ * only executed when either 1) power save is enabled or 2) idle
+ * connection monitor is enabled, so this is definitely not in fast
+ * path and can be slow
+ */
+
+ /* connection tracking */
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ continue;
+
+ ifmgd = &sdata->u.mgd;
+
+ if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
+ IEEE80211_STA_CONNECTION_POLL)) {
+ printk(KERN_DEBUG "nullfunc %s\n",
+ (info->flags & IEEE80211_TX_STAT_ACK) ?
+ "acked" : "nacked");
+
+ if (info->flags & IEEE80211_TX_STAT_ACK)
+ ifmgd->flags |= IEEE80211_STA_CON_POLL_ACKED;
+
+ /*
+ * FIXME: report negative failure somehow, that way
+ * there's no need to wait for the timer to
+ * trigger
+ */
+ ieee80211_queue_work(&local->hw, &ifmgd->work);
+ }
+
+ }
+ rcu_read_unlock();
+
+ /* trying to go into power save */
+ /* FIXME: add check for IEEE80211_HW_PS_NULLFUNC_STACK */
+ if (local->ps_sdata && ieee80211_has_pm(fc)) {
if (info->flags & IEEE80211_TX_STAT_ACK) {
local->ps_sdata->u.mgd.flags |=
IEEE80211_STA_NULLFUNC_ACKED;