Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi...
[linux-2.6] / net / mac80211 / pm.c
1 #include <net/mac80211.h>
2 #include <net/rtnetlink.h>
3
4 #include "ieee80211_i.h"
5 #include "led.h"
6
7 int __ieee80211_suspend(struct ieee80211_hw *hw)
8 {
9         struct ieee80211_local *local = hw_to_local(hw);
10         struct ieee80211_sub_if_data *sdata;
11         struct ieee80211_if_init_conf conf;
12         struct sta_info *sta;
13         unsigned long flags;
14
15         ieee80211_stop_queues_by_reason(hw,
16                         IEEE80211_QUEUE_STOP_REASON_SUSPEND);
17
18         flush_workqueue(local->hw.workqueue);
19
20         /* disable keys */
21         list_for_each_entry(sdata, &local->interfaces, list)
22                 ieee80211_disable_keys(sdata);
23
24         /* Tear down aggregation sessions */
25
26         rcu_read_lock();
27
28         if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
29                 list_for_each_entry_rcu(sta, &local->sta_list, list) {
30                         set_sta_flags(sta, WLAN_STA_SUSPEND);
31                         ieee80211_sta_tear_down_BA_sessions(sta);
32                 }
33         }
34
35         rcu_read_unlock();
36
37         /* remove STAs */
38         if (local->ops->sta_notify) {
39                 spin_lock_irqsave(&local->sta_lock, flags);
40                 list_for_each_entry(sta, &local->sta_list, list) {
41                         if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
42                                 sdata = container_of(sdata->bss,
43                                              struct ieee80211_sub_if_data,
44                                              u.ap);
45
46                         local->ops->sta_notify(hw, &sdata->vif,
47                                 STA_NOTIFY_REMOVE, &sta->sta);
48                 }
49                 spin_unlock_irqrestore(&local->sta_lock, flags);
50         }
51
52         /* remove all interfaces */
53         list_for_each_entry(sdata, &local->interfaces, list) {
54                 if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
55                     sdata->vif.type != NL80211_IFTYPE_MONITOR &&
56                     netif_running(sdata->dev)) {
57                         conf.vif = &sdata->vif;
58                         conf.type = sdata->vif.type;
59                         conf.mac_addr = sdata->dev->dev_addr;
60                         local->ops->remove_interface(hw, &conf);
61                 }
62         }
63
64         /* flush again, in case driver queued work */
65         flush_workqueue(local->hw.workqueue);
66
67         /* stop hardware */
68         if (local->open_count) {
69                 ieee80211_led_radio(local, false);
70                 local->ops->stop(hw);
71         }
72         return 0;
73 }
74
75 int __ieee80211_resume(struct ieee80211_hw *hw)
76 {
77         struct ieee80211_local *local = hw_to_local(hw);
78         struct ieee80211_sub_if_data *sdata;
79         struct ieee80211_if_init_conf conf;
80         struct sta_info *sta;
81         unsigned long flags;
82         int res;
83
84         /* restart hardware */
85         if (local->open_count) {
86                 res = local->ops->start(hw);
87
88                 ieee80211_led_radio(local, hw->conf.radio_enabled);
89         }
90
91         /* add interfaces */
92         list_for_each_entry(sdata, &local->interfaces, list) {
93                 if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
94                     sdata->vif.type != NL80211_IFTYPE_MONITOR &&
95                     netif_running(sdata->dev)) {
96                         conf.vif = &sdata->vif;
97                         conf.type = sdata->vif.type;
98                         conf.mac_addr = sdata->dev->dev_addr;
99                         res = local->ops->add_interface(hw, &conf);
100                 }
101         }
102
103         /* add STAs back */
104         if (local->ops->sta_notify) {
105                 spin_lock_irqsave(&local->sta_lock, flags);
106                 list_for_each_entry(sta, &local->sta_list, list) {
107                         if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
108                                 sdata = container_of(sdata->bss,
109                                              struct ieee80211_sub_if_data,
110                                              u.ap);
111
112                         local->ops->sta_notify(hw, &sdata->vif,
113                                 STA_NOTIFY_ADD, &sta->sta);
114                 }
115                 spin_unlock_irqrestore(&local->sta_lock, flags);
116         }
117
118         /* Clear Suspend state so that ADDBA requests can be processed */
119
120         rcu_read_lock();
121
122         if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
123                 list_for_each_entry_rcu(sta, &local->sta_list, list) {
124                         clear_sta_flags(sta, WLAN_STA_SUSPEND);
125                 }
126         }
127
128         rcu_read_unlock();
129
130         /* add back keys */
131         list_for_each_entry(sdata, &local->interfaces, list)
132                 if (netif_running(sdata->dev))
133                         ieee80211_enable_keys(sdata);
134
135         /* setup RTS threshold */
136         if (local->ops->set_rts_threshold)
137                 local->ops->set_rts_threshold(hw, local->rts_threshold);
138
139         /* reconfigure hardware */
140         ieee80211_hw_config(local, ~0);
141
142         netif_addr_lock_bh(local->mdev);
143         ieee80211_configure_filter(local);
144         netif_addr_unlock_bh(local->mdev);
145
146         /* Finally also reconfigure all the BSS information */
147         list_for_each_entry(sdata, &local->interfaces, list) {
148                 u32 changed = ~0;
149                 if (!netif_running(sdata->dev))
150                         continue;
151                 switch (sdata->vif.type) {
152                 case NL80211_IFTYPE_STATION:
153                         /* disable beacon change bits */
154                         changed &= ~IEEE80211_IFCC_BEACON;
155                         /* fall through */
156                 case NL80211_IFTYPE_ADHOC:
157                 case NL80211_IFTYPE_AP:
158                 case NL80211_IFTYPE_MESH_POINT:
159                         /*
160                          * Driver's config_interface can fail if rfkill is
161                          * enabled. Accommodate this return code.
162                          * FIXME: When mac80211 has knowledge of rfkill
163                          * state the code below can change back to:
164                          *   WARN(ieee80211_if_config(sdata, changed));
165                          *   ieee80211_bss_info_change_notify(sdata, ~0);
166                          */
167                         if (ieee80211_if_config(sdata, changed))
168                                 printk(KERN_DEBUG "%s: failed to configure interface during resume\n",
169                                        sdata->dev->name);
170                         else
171                                 ieee80211_bss_info_change_notify(sdata, ~0);
172                         break;
173                 case NL80211_IFTYPE_WDS:
174                         break;
175                 case NL80211_IFTYPE_AP_VLAN:
176                 case NL80211_IFTYPE_MONITOR:
177                         /* ignore virtual */
178                         break;
179                 case NL80211_IFTYPE_UNSPECIFIED:
180                 case __NL80211_IFTYPE_AFTER_LAST:
181                         WARN_ON(1);
182                         break;
183                 }
184         }
185
186         ieee80211_wake_queues_by_reason(hw,
187                         IEEE80211_QUEUE_STOP_REASON_SUSPEND);
188
189         return 0;
190 }