summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/ieee80211.h9
-rw-r--r--net/mac80211/ieee80211_i.h5
-rw-r--r--net/mac80211/iface.c4
-rw-r--r--net/mac80211/main.c31
-rw-r--r--net/mac80211/mlme.c36
-rw-r--r--net/mac80211/wext.c2
6 files changed, 72 insertions, 15 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 4b501b48ce86..53563d53b5ad 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1383,4 +1383,13 @@ static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq)
return -1;
}
+/**
+ * ieee80211_tu_to_usec - convert time units (TU) to microseconds
+ * @tu: the TUs
+ */
+static inline unsigned long ieee80211_tu_to_usec(unsigned long tu)
+{
+ return 1024 * tu;
+}
+
#endif /* LINUX_IEEE80211_H */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index ff40dd7b523a..b1d18d967d8c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -750,6 +750,7 @@ struct ieee80211_local {
struct work_struct dynamic_ps_enable_work;
struct work_struct dynamic_ps_disable_work;
struct timer_list dynamic_ps_timer;
+ struct notifier_block network_latency_notifier;
int user_power_level; /* in dBm */
int power_constr_level; /* in dBm */
@@ -938,7 +939,9 @@ int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason
int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
void ieee80211_send_pspoll(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
-void ieee80211_recalc_ps(struct ieee80211_local *local);
+void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
+int ieee80211_max_network_latency(struct notifier_block *nb,
+ unsigned long data, void *dummy);
/* IBSS code */
int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 6240f76e2a43..5d60deb219d3 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -317,7 +317,7 @@ static int ieee80211_open(struct net_device *dev)
ieee80211_set_wmm_default(sdata);
}
- ieee80211_recalc_ps(local);
+ ieee80211_recalc_ps(local, -1);
/*
* ieee80211_sta_work is disabled while network interface
@@ -574,7 +574,7 @@ static int ieee80211_stop(struct net_device *dev)
hw_reconf_flags = 0;
}
- ieee80211_recalc_ps(local);
+ ieee80211_recalc_ps(local, -1);
/* do after stop to avoid reconfiguring when we stop anyway */
if (hw_reconf_flags)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 80c0e28bf549..049ce8639806 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -21,6 +21,7 @@
#include <linux/wireless.h>
#include <linux/rtnetlink.h>
#include <linux/bitmap.h>
+#include <linux/pm_qos_params.h>
#include <net/net_namespace.h>
#include <net/cfg80211.h>
@@ -1038,25 +1039,38 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
}
}
+ local->network_latency_notifier.notifier_call =
+ ieee80211_max_network_latency;
+ result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
+ &local->network_latency_notifier);
+
+ if (result) {
+ rtnl_lock();
+ goto fail_pm_qos;
+ }
+
return 0;
-fail_wep:
+ fail_pm_qos:
+ ieee80211_led_exit(local);
+ ieee80211_remove_interfaces(local);
+ fail_wep:
rate_control_deinitialize(local);
-fail_rate:
+ fail_rate:
unregister_netdevice(local->mdev);
local->mdev = NULL;
-fail_dev:
+ fail_dev:
rtnl_unlock();
sta_info_stop(local);
-fail_sta_info:
+ fail_sta_info:
debugfs_hw_del(local);
destroy_workqueue(local->hw.workqueue);
-fail_workqueue:
+ fail_workqueue:
if (local->mdev)
free_netdev(local->mdev);
-fail_mdev_alloc:
+ fail_mdev_alloc:
wiphy_unregister(local->hw.wiphy);
-fail_wiphy_register:
+ fail_wiphy_register:
kfree(local->int_scan_req.channels);
return result;
}
@@ -1069,6 +1083,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
tasklet_kill(&local->tx_pending_tasklet);
tasklet_kill(&local->tasklet);
+ pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
+ &local->network_latency_notifier);
+
rtnl_lock();
/*
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 06d9a1d23252..c39a214e7ad0 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -17,6 +17,7 @@
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
+#include <linux/pm_qos_params.h>
#include <net/mac80211.h>
#include <asm/unaligned.h>
@@ -515,7 +516,7 @@ static void ieee80211_change_ps(struct ieee80211_local *local)
}
/* need to hold RTNL or interface lock */
-void ieee80211_recalc_ps(struct ieee80211_local *local)
+void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
{
struct ieee80211_sub_if_data *sdata, *found = NULL;
int count = 0;
@@ -534,10 +535,22 @@ void ieee80211_recalc_ps(struct ieee80211_local *local)
count++;
}
- if (count == 1 && found->u.mgd.powersave)
- local->ps_sdata = found;
- else
+ if (count == 1 && found->u.mgd.powersave) {
+ s32 beaconint_us;
+
+ if (latency < 0)
+ latency = pm_qos_requirement(PM_QOS_NETWORK_LATENCY);
+
+ beaconint_us = ieee80211_tu_to_usec(
+ found->vif.bss_conf.beacon_int);
+
+ if (beaconint_us > latency)
+ local->ps_sdata = NULL;
+ else
+ local->ps_sdata = found;
+ } else {
local->ps_sdata = NULL;
+ }
ieee80211_change_ps(local);
}
@@ -2324,3 +2337,18 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
ieee80211_restart_sta_timer(sdata);
rcu_read_unlock();
}
+
+int ieee80211_max_network_latency(struct notifier_block *nb,
+ unsigned long data, void *dummy)
+{
+ s32 latency_usec = (s32) data;
+ struct ieee80211_local *local =
+ container_of(nb, struct ieee80211_local,
+ network_latency_notifier);
+
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_recalc_ps(local, latency_usec);
+ mutex_unlock(&local->iflist_mtx);
+
+ return 0;
+}
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index 81f63e57027f..1c4664b8b1a0 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -789,7 +789,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
ieee80211_hw_config(local,
IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
- ieee80211_recalc_ps(local);
+ ieee80211_recalc_ps(local, -1);
return 0;
}