55de908ab2
Instead of operating on a single channel only, use the new channel context infrastructure in all mac80211 code. This enables drivers that want to use the new channel context infrastructure to use multiple channels, while nothing should change for all the other drivers that don't support it. Right now this disables both TX power settings and spatial multiplexing powersave. Both need to be re-enabled on a channel context basis. Additionally, when channel contexts are used drop the connection when channel switch is received rather than trying to handle it. This will have to be improved later. [With fixes from Eliad and Emmanuel incorporated] Signed-off-by: Eliad Peller <eliad@wizery.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
165 lines
3.9 KiB
C
165 lines
3.9 KiB
C
#include <net/mac80211.h>
|
|
#include <net/rtnetlink.h>
|
|
|
|
#include "ieee80211_i.h"
|
|
#include "mesh.h"
|
|
#include "driver-ops.h"
|
|
#include "led.h"
|
|
|
|
/* return value indicates whether the driver should be further notified */
|
|
static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
|
|
{
|
|
switch (sdata->vif.type) {
|
|
case NL80211_IFTYPE_STATION:
|
|
ieee80211_sta_quiesce(sdata);
|
|
return true;
|
|
case NL80211_IFTYPE_ADHOC:
|
|
ieee80211_ibss_quiesce(sdata);
|
|
return true;
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
ieee80211_mesh_quiesce(sdata);
|
|
return true;
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
case NL80211_IFTYPE_MONITOR:
|
|
/* don't tell driver about this */
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
|
{
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct sta_info *sta;
|
|
|
|
if (!local->open_count)
|
|
goto suspend;
|
|
|
|
ieee80211_scan_cancel(local);
|
|
|
|
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
|
|
mutex_lock(&local->sta_mtx);
|
|
list_for_each_entry(sta, &local->sta_list, list) {
|
|
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
|
ieee80211_sta_tear_down_BA_sessions(sta, true);
|
|
}
|
|
mutex_unlock(&local->sta_mtx);
|
|
}
|
|
|
|
ieee80211_stop_queues_by_reason(hw,
|
|
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
|
|
|
/* flush out all packets */
|
|
synchronize_net();
|
|
|
|
drv_flush(local, false);
|
|
|
|
local->quiescing = true;
|
|
/* make quiescing visible to timers everywhere */
|
|
mb();
|
|
|
|
flush_workqueue(local->workqueue);
|
|
|
|
/* Don't try to run timers while suspended. */
|
|
del_timer_sync(&local->sta_cleanup);
|
|
|
|
/*
|
|
* Note that this particular timer doesn't need to be
|
|
* restarted at resume.
|
|
*/
|
|
cancel_work_sync(&local->dynamic_ps_enable_work);
|
|
del_timer_sync(&local->dynamic_ps_timer);
|
|
|
|
local->wowlan = wowlan && local->open_count;
|
|
if (local->wowlan) {
|
|
int err = drv_suspend(local, wowlan);
|
|
if (err < 0) {
|
|
local->quiescing = false;
|
|
local->wowlan = false;
|
|
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
|
|
mutex_lock(&local->sta_mtx);
|
|
list_for_each_entry(sta,
|
|
&local->sta_list, list) {
|
|
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
|
}
|
|
mutex_unlock(&local->sta_mtx);
|
|
}
|
|
ieee80211_wake_queues_by_reason(hw,
|
|
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
|
return err;
|
|
} else if (err > 0) {
|
|
WARN_ON(err != 1);
|
|
local->wowlan = false;
|
|
} else {
|
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
|
cancel_work_sync(&sdata->work);
|
|
ieee80211_quiesce(sdata);
|
|
}
|
|
goto suspend;
|
|
}
|
|
}
|
|
|
|
/* disable keys */
|
|
list_for_each_entry(sdata, &local->interfaces, list)
|
|
ieee80211_disable_keys(sdata);
|
|
|
|
/* tear down aggregation sessions and remove STAs */
|
|
mutex_lock(&local->sta_mtx);
|
|
list_for_each_entry(sta, &local->sta_list, list) {
|
|
if (sta->uploaded) {
|
|
enum ieee80211_sta_state state;
|
|
|
|
state = sta->sta_state;
|
|
for (; state > IEEE80211_STA_NOTEXIST; state--)
|
|
WARN_ON(drv_sta_state(local, sta->sdata, sta,
|
|
state, state - 1));
|
|
}
|
|
|
|
mesh_plink_quiesce(sta);
|
|
}
|
|
mutex_unlock(&local->sta_mtx);
|
|
|
|
/* remove all interfaces */
|
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
|
cancel_work_sync(&sdata->work);
|
|
|
|
if (!ieee80211_quiesce(sdata))
|
|
continue;
|
|
|
|
if (!ieee80211_sdata_running(sdata))
|
|
continue;
|
|
|
|
/* disable beaconing */
|
|
ieee80211_bss_info_change_notify(sdata,
|
|
BSS_CHANGED_BEACON_ENABLED);
|
|
|
|
/* the interface is leaving the channel and is removed */
|
|
ieee80211_vif_release_channel(sdata);
|
|
drv_remove_interface(local, sdata);
|
|
}
|
|
|
|
sdata = rtnl_dereference(local->monitor_sdata);
|
|
if (sdata)
|
|
drv_remove_interface(local, sdata);
|
|
|
|
/* stop hardware - this must stop RX */
|
|
if (local->open_count)
|
|
ieee80211_stop_device(local);
|
|
|
|
suspend:
|
|
local->suspended = true;
|
|
/* need suspended to be visible before quiescing is false */
|
|
barrier();
|
|
local->quiescing = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* __ieee80211_resume() is a static inline which just calls
|
|
* ieee80211_reconfig(), which is also needed for hardware
|
|
* hang/firmware failure/etc. recovery.
|
|
*/
|