xref: /openbmc/linux/net/mac80211/pm.c (revision 82ced6fd)
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 }
191