linux/net/mac80211/pm.c
<<
>>
Prefs
   1#include <net/mac80211.h>
   2#include <net/rtnetlink.h>
   3
   4#include "ieee80211_i.h"
   5#include "mesh.h"
   6#include "driver-ops.h"
   7#include "led.h"
   8
   9/* return value indicates whether the driver should be further notified */
  10static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata)
  11{
  12        switch (sdata->vif.type) {
  13        case NL80211_IFTYPE_STATION:
  14                ieee80211_sta_quiesce(sdata);
  15                return true;
  16        case NL80211_IFTYPE_ADHOC:
  17                ieee80211_ibss_quiesce(sdata);
  18                return true;
  19        case NL80211_IFTYPE_MESH_POINT:
  20                ieee80211_mesh_quiesce(sdata);
  21                return true;
  22        case NL80211_IFTYPE_AP_VLAN:
  23        case NL80211_IFTYPE_MONITOR:
  24                /* don't tell driver about this */
  25                return false;
  26        default:
  27                return true;
  28        }
  29}
  30
  31int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
  32{
  33        struct ieee80211_local *local = hw_to_local(hw);
  34        struct ieee80211_sub_if_data *sdata;
  35        struct sta_info *sta;
  36
  37        if (!local->open_count)
  38                goto suspend;
  39
  40        ieee80211_scan_cancel(local);
  41
  42        if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
  43                mutex_lock(&local->sta_mtx);
  44                list_for_each_entry(sta, &local->sta_list, list) {
  45                        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
  46                        ieee80211_sta_tear_down_BA_sessions(sta, true);
  47                }
  48                mutex_unlock(&local->sta_mtx);
  49        }
  50
  51        ieee80211_stop_queues_by_reason(hw,
  52                        IEEE80211_QUEUE_STOP_REASON_SUSPEND);
  53
  54        /* flush out all packets */
  55        synchronize_net();
  56
  57        drv_flush(local, false);
  58
  59        local->quiescing = true;
  60        /* make quiescing visible to timers everywhere */
  61        mb();
  62
  63        flush_workqueue(local->workqueue);
  64
  65        /* Don't try to run timers while suspended. */
  66        del_timer_sync(&local->sta_cleanup);
  67
  68         /*
  69         * Note that this particular timer doesn't need to be
  70         * restarted at resume.
  71         */
  72        cancel_work_sync(&local->dynamic_ps_enable_work);
  73        del_timer_sync(&local->dynamic_ps_timer);
  74
  75        local->wowlan = wowlan && local->open_count;
  76        if (local->wowlan) {
  77                int err = drv_suspend(local, wowlan);
  78                if (err < 0) {
  79                        local->quiescing = false;
  80                        return err;
  81                } else if (err > 0) {
  82                        WARN_ON(err != 1);
  83                        local->wowlan = false;
  84                } else {
  85                        list_for_each_entry(sdata, &local->interfaces, list) {
  86                                cancel_work_sync(&sdata->work);
  87                                ieee80211_quiesce(sdata);
  88                        }
  89                        goto suspend;
  90                }
  91        }
  92
  93        /* disable keys */
  94        list_for_each_entry(sdata, &local->interfaces, list)
  95                ieee80211_disable_keys(sdata);
  96
  97        /* tear down aggregation sessions and remove STAs */
  98        mutex_lock(&local->sta_mtx);
  99        list_for_each_entry(sta, &local->sta_list, list) {
 100                if (sta->uploaded) {
 101                        sdata = sta->sdata;
 102                        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 103                                sdata = container_of(sdata->bss,
 104                                             struct ieee80211_sub_if_data,
 105                                             u.ap);
 106
 107                        drv_sta_remove(local, sdata, &sta->sta);
 108                }
 109
 110                mesh_plink_quiesce(sta);
 111        }
 112        mutex_unlock(&local->sta_mtx);
 113
 114        /* remove all interfaces */
 115        list_for_each_entry(sdata, &local->interfaces, list) {
 116                cancel_work_sync(&sdata->work);
 117
 118                if (!ieee80211_quiesce(sdata))
 119                        continue;
 120
 121                if (!ieee80211_sdata_running(sdata))
 122                        continue;
 123
 124                /* disable beaconing */
 125                ieee80211_bss_info_change_notify(sdata,
 126                        BSS_CHANGED_BEACON_ENABLED);
 127
 128                drv_remove_interface(local, sdata);
 129        }
 130
 131        /* stop hardware - this must stop RX */
 132        if (local->open_count)
 133                ieee80211_stop_device(local);
 134
 135 suspend:
 136        local->suspended = true;
 137        /* need suspended to be visible before quiescing is false */
 138        barrier();
 139        local->quiescing = false;
 140
 141        return 0;
 142}
 143
 144/*
 145 * __ieee80211_resume() is a static inline which just calls
 146 * ieee80211_reconfig(), which is also needed for hardware
 147 * hang/firmware failure/etc. recovery.
 148 */
 149
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.