Commit | Line | Data |
---|---|---|
e16e7f07 JP |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Implementation of mac80211 API. | |
4 | * | |
5 | * Copyright (c) 2017-2019, Silicon Laboratories, Inc. | |
6 | * Copyright (c) 2010, ST-Ericsson | |
7 | */ | |
8 | #include <net/mac80211.h> | |
9 | ||
10 | #include "sta.h" | |
11 | #include "wfx.h" | |
40115bbc JP |
12 | #include "fwio.h" |
13 | #include "bh.h" | |
fb2490f6 | 14 | #include "key.h" |
1a61af0f | 15 | #include "scan.h" |
40115bbc JP |
16 | #include "debug.h" |
17 | #include "hif_tx.h" | |
1a61af0f | 18 | #include "hif_tx_mib.h" |
e16e7f07 | 19 | |
40115bbc JP |
20 | #define HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES 2 |
21 | ||
c360f1cc | 22 | u32 wfx_rate_mask_to_hw(struct wfx_dev *wdev, u32 rates) |
40115bbc JP |
23 | { |
24 | int i; | |
25 | u32 ret = 0; | |
26 | // WFx only support 2GHz | |
27 | struct ieee80211_supported_band *sband = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; | |
28 | ||
29 | for (i = 0; i < sband->n_bitrates; i++) { | |
30 | if (rates & BIT(i)) { | |
31 | if (i >= sband->n_bitrates) | |
32 | dev_warn(wdev->dev, "unsupported basic rate\n"); | |
33 | else | |
34 | ret |= BIT(sband->bitrates[i].hw_value); | |
35 | } | |
36 | } | |
37 | return ret; | |
38 | } | |
39 | ||
40 | static void __wfx_free_event_queue(struct list_head *list) | |
41 | { | |
42 | struct wfx_hif_event *event, *tmp; | |
43 | ||
44 | list_for_each_entry_safe(event, tmp, list, link) { | |
45 | list_del(&event->link); | |
46 | kfree(event); | |
47 | } | |
48 | } | |
49 | ||
50 | static void wfx_free_event_queue(struct wfx_vif *wvif) | |
51 | { | |
52 | LIST_HEAD(list); | |
53 | ||
54 | spin_lock(&wvif->event_queue_lock); | |
55 | list_splice_init(&wvif->event_queue, &list); | |
56 | spin_unlock(&wvif->event_queue_lock); | |
57 | ||
58 | __wfx_free_event_queue(&list); | |
59 | } | |
60 | ||
61 | void wfx_cqm_bssloss_sm(struct wfx_vif *wvif, int init, int good, int bad) | |
62 | { | |
63 | int tx = 0; | |
64 | ||
65 | mutex_lock(&wvif->bss_loss_lock); | |
40115bbc JP |
66 | cancel_work_sync(&wvif->bss_params_work); |
67 | ||
40115bbc JP |
68 | if (init) { |
69 | schedule_delayed_work(&wvif->bss_loss_work, HZ); | |
70 | wvif->bss_loss_state = 0; | |
71 | ||
72 | if (!atomic_read(&wvif->wdev->tx_lock)) | |
73 | tx = 1; | |
74 | } else if (good) { | |
75 | cancel_delayed_work_sync(&wvif->bss_loss_work); | |
76 | wvif->bss_loss_state = 0; | |
77 | schedule_work(&wvif->bss_params_work); | |
78 | } else if (bad) { | |
79 | /* FIXME Should we just keep going until we time out? */ | |
80 | if (wvif->bss_loss_state < 3) | |
81 | tx = 1; | |
82 | } else { | |
83 | cancel_delayed_work_sync(&wvif->bss_loss_work); | |
84 | wvif->bss_loss_state = 0; | |
85 | } | |
86 | ||
87 | /* Spit out a NULL packet to our AP if necessary */ | |
88 | // FIXME: call ieee80211_beacon_loss/ieee80211_connection_loss instead | |
89 | if (tx) { | |
90 | struct sk_buff *skb; | |
76b5c2ce JP |
91 | struct ieee80211_hdr *hdr; |
92 | struct ieee80211_tx_control control = { }; | |
40115bbc JP |
93 | |
94 | wvif->bss_loss_state++; | |
95 | ||
96 | skb = ieee80211_nullfunc_get(wvif->wdev->hw, wvif->vif, false); | |
97 | if (!skb) | |
98 | goto end; | |
76b5c2ce | 99 | hdr = (struct ieee80211_hdr *)skb->data; |
8c7128c4 JI |
100 | memset(IEEE80211_SKB_CB(skb), 0, |
101 | sizeof(*IEEE80211_SKB_CB(skb))); | |
40115bbc JP |
102 | IEEE80211_SKB_CB(skb)->control.vif = wvif->vif; |
103 | IEEE80211_SKB_CB(skb)->driver_rates[0].idx = 0; | |
104 | IEEE80211_SKB_CB(skb)->driver_rates[0].count = 1; | |
105 | IEEE80211_SKB_CB(skb)->driver_rates[1].idx = -1; | |
76b5c2ce JP |
106 | rcu_read_lock(); // protect control.sta |
107 | control.sta = ieee80211_find_sta(wvif->vif, hdr->addr1); | |
108 | wfx_tx(wvif->wdev->hw, &control, skb); | |
109 | rcu_read_unlock(); | |
40115bbc JP |
110 | } |
111 | end: | |
112 | mutex_unlock(&wvif->bss_loss_lock); | |
113 | } | |
114 | ||
1a61af0f JP |
115 | int wfx_fwd_probe_req(struct wfx_vif *wvif, bool enable) |
116 | { | |
117 | wvif->fwd_probe_req = enable; | |
118 | return hif_set_rx_filter(wvif, wvif->filter_bssid, | |
119 | wvif->fwd_probe_req); | |
120 | } | |
121 | ||
40115bbc JP |
122 | static int wfx_set_mcast_filter(struct wfx_vif *wvif, |
123 | struct wfx_grp_addr_table *fp) | |
124 | { | |
0b58486e | 125 | int i; |
40115bbc JP |
126 | |
127 | // Temporary workaround for filters | |
c47b70e2 | 128 | return hif_set_data_filtering(wvif, false, true); |
40115bbc | 129 | |
c47b70e2 JP |
130 | if (!fp->enable) |
131 | return hif_set_data_filtering(wvif, false, true); | |
40115bbc | 132 | |
0b58486e JP |
133 | for (i = 0; i < fp->num_addresses; i++) |
134 | hif_set_mac_addr_condition(wvif, i, fp->address_list[i]); | |
135 | hif_set_uc_mc_bc_condition(wvif, 0, | |
136 | HIF_FILTER_UNICAST | HIF_FILTER_BROADCAST); | |
137 | hif_set_config_data_filter(wvif, true, 0, BIT(1), | |
138 | BIT(fp->num_addresses) - 1); | |
139 | hif_set_data_filtering(wvif, true, true); | |
40115bbc | 140 | |
0b58486e | 141 | return 0; |
40115bbc JP |
142 | } |
143 | ||
144 | void wfx_update_filtering(struct wfx_vif *wvif) | |
145 | { | |
146 | int ret; | |
fd5d78bd JP |
147 | int bf_enable; |
148 | int bf_count; | |
149 | int n_filter_ies; | |
c54f9f0e | 150 | struct hif_ie_table_entry filter_ies[] = { |
40115bbc JP |
151 | { |
152 | .ie_id = WLAN_EID_VENDOR_SPECIFIC, | |
153 | .has_changed = 1, | |
154 | .no_longer = 1, | |
155 | .has_appeared = 1, | |
c54f9f0e | 156 | .oui = { 0x50, 0x6F, 0x9A }, |
40115bbc JP |
157 | }, { |
158 | .ie_id = WLAN_EID_HT_OPERATION, | |
159 | .has_changed = 1, | |
160 | .no_longer = 1, | |
161 | .has_appeared = 1, | |
162 | }, { | |
163 | .ie_id = WLAN_EID_ERP_INFO, | |
164 | .has_changed = 1, | |
165 | .no_longer = 1, | |
166 | .has_appeared = 1, | |
167 | } | |
168 | }; | |
169 | ||
170 | if (wvif->state == WFX_STATE_PASSIVE) | |
171 | return; | |
172 | ||
40115bbc | 173 | if (wvif->disable_beacon_filter) { |
fd5d78bd JP |
174 | bf_enable = 0; |
175 | bf_count = 1; | |
c54f9f0e | 176 | n_filter_ies = 0; |
fd5d78bd JP |
177 | } else if (wvif->vif->type != NL80211_IFTYPE_STATION) { |
178 | bf_enable = HIF_BEACON_FILTER_ENABLE | HIF_BEACON_FILTER_AUTO_ERP; | |
179 | bf_count = 0; | |
c54f9f0e | 180 | n_filter_ies = 2; |
40115bbc | 181 | } else { |
fd5d78bd JP |
182 | bf_enable = HIF_BEACON_FILTER_ENABLE; |
183 | bf_count = 0; | |
c54f9f0e | 184 | n_filter_ies = 3; |
40115bbc JP |
185 | } |
186 | ||
fd5d78bd | 187 | ret = hif_set_rx_filter(wvif, wvif->filter_bssid, wvif->fwd_probe_req); |
40115bbc | 188 | if (!ret) |
fd5d78bd | 189 | ret = hif_set_beacon_filter_table(wvif, n_filter_ies, filter_ies); |
40115bbc | 190 | if (!ret) |
fd5d78bd | 191 | ret = hif_beacon_filter_control(wvif, bf_enable, bf_count); |
40115bbc JP |
192 | if (!ret) |
193 | ret = wfx_set_mcast_filter(wvif, &wvif->mcast_filter); | |
194 | if (ret) | |
195 | dev_err(wvif->wdev->dev, "update filtering failed: %d\n", ret); | |
40115bbc JP |
196 | } |
197 | ||
8fd1fe82 | 198 | static void wfx_update_filtering_work(struct work_struct *work) |
40115bbc | 199 | { |
8c7128c4 JI |
200 | struct wfx_vif *wvif = container_of(work, struct wfx_vif, |
201 | update_filtering_work); | |
40115bbc JP |
202 | |
203 | wfx_update_filtering(wvif); | |
204 | } | |
205 | ||
8c7128c4 JI |
206 | u64 wfx_prepare_multicast(struct ieee80211_hw *hw, |
207 | struct netdev_hw_addr_list *mc_list) | |
40115bbc JP |
208 | { |
209 | int i; | |
210 | struct netdev_hw_addr *ha; | |
211 | struct wfx_vif *wvif = NULL; | |
212 | struct wfx_dev *wdev = hw->priv; | |
213 | int count = netdev_hw_addr_list_count(mc_list); | |
214 | ||
215 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { | |
216 | memset(&wvif->mcast_filter, 0x00, sizeof(wvif->mcast_filter)); | |
8c7128c4 JI |
217 | if (!count || |
218 | count > ARRAY_SIZE(wvif->mcast_filter.address_list)) | |
40115bbc JP |
219 | continue; |
220 | ||
221 | i = 0; | |
222 | netdev_hw_addr_list_for_each(ha, mc_list) { | |
8c7128c4 JI |
223 | ether_addr_copy(wvif->mcast_filter.address_list[i], |
224 | ha->addr); | |
40115bbc JP |
225 | i++; |
226 | } | |
168c7d76 | 227 | wvif->mcast_filter.enable = true; |
40115bbc JP |
228 | wvif->mcast_filter.num_addresses = count; |
229 | } | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | void wfx_configure_filter(struct ieee80211_hw *hw, | |
235 | unsigned int changed_flags, | |
236 | unsigned int *total_flags, | |
237 | u64 unused) | |
238 | { | |
239 | struct wfx_vif *wvif = NULL; | |
240 | struct wfx_dev *wdev = hw->priv; | |
241 | ||
242 | *total_flags &= FIF_OTHER_BSS | FIF_FCSFAIL | FIF_PROBE_REQ; | |
243 | ||
244 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { | |
d1c015b4 | 245 | mutex_lock(&wvif->scan_lock); |
8c7128c4 JI |
246 | wvif->filter_bssid = (*total_flags & |
247 | (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? 0 : 1; | |
40115bbc JP |
248 | wvif->disable_beacon_filter = !(*total_flags & FIF_PROBE_REQ); |
249 | wfx_fwd_probe_req(wvif, true); | |
250 | wfx_update_filtering(wvif); | |
d1c015b4 | 251 | mutex_unlock(&wvif->scan_lock); |
40115bbc JP |
252 | } |
253 | } | |
254 | ||
8dd5bb66 | 255 | static int wfx_update_pm(struct wfx_vif *wvif) |
97e587bd | 256 | { |
50ad848c | 257 | struct ieee80211_conf *conf = &wvif->wdev->hw->conf; |
adc90758 JP |
258 | bool ps = conf->flags & IEEE80211_CONF_PS; |
259 | int ps_timeout = conf->dynamic_ps_timeout; | |
f98138a1 | 260 | struct ieee80211_channel *chan0 = NULL, *chan1 = NULL; |
97e587bd | 261 | |
adc90758 | 262 | WARN_ON(conf->dynamic_ps_timeout < 0); |
97e587bd JP |
263 | if (wvif->state != WFX_STATE_STA || !wvif->bss_params.aid) |
264 | return 0; | |
adc90758 JP |
265 | if (!ps) |
266 | ps_timeout = 0; | |
7e2b32f5 | 267 | if (wvif->uapsd_mask) |
adc90758 | 268 | ps_timeout = 0; |
97e587bd | 269 | |
f98138a1 JP |
270 | // Kernel disable powersave when an AP is in use. In contrary, it is |
271 | // absolutely necessary to enable legacy powersave for WF200 if channels | |
272 | // are differents. | |
273 | if (wdev_to_wvif(wvif->wdev, 0)) | |
274 | chan0 = wdev_to_wvif(wvif->wdev, 0)->vif->bss_conf.chandef.chan; | |
275 | if (wdev_to_wvif(wvif->wdev, 1)) | |
276 | chan1 = wdev_to_wvif(wvif->wdev, 1)->vif->bss_conf.chandef.chan; | |
277 | if (chan0 && chan1 && chan0->hw_value != chan1->hw_value && | |
278 | wvif->vif->type != NL80211_IFTYPE_AP) { | |
adc90758 JP |
279 | ps = true; |
280 | ps_timeout = 0; | |
97e587bd JP |
281 | } |
282 | ||
283 | if (!wait_for_completion_timeout(&wvif->set_pm_mode_complete, | |
8a274dfb | 284 | TU_TO_JIFFIES(512))) |
97e587bd JP |
285 | dev_warn(wvif->wdev->dev, |
286 | "timeout while waiting of set_pm_mode_complete\n"); | |
adc90758 | 287 | return hif_set_pm(wvif, ps, ps_timeout); |
97e587bd JP |
288 | } |
289 | ||
40115bbc JP |
290 | int wfx_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
291 | u16 queue, const struct ieee80211_tx_queue_params *params) | |
292 | { | |
293 | struct wfx_dev *wdev = hw->priv; | |
294 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; | |
9b90910f | 295 | int old_uapsd = wvif->uapsd_mask; |
40115bbc | 296 | |
c91ba8c8 | 297 | WARN_ON(queue >= hw->queues); |
40115bbc | 298 | |
c91ba8c8 | 299 | mutex_lock(&wdev->conf_mutex); |
7e2b32f5 | 300 | assign_bit(queue, &wvif->uapsd_mask, params->uapsd); |
871341db JP |
301 | memcpy(&wvif->edca_params[queue], params, sizeof(*params)); |
302 | hif_set_edca_queue_params(wvif, queue, params); | |
9b90910f JP |
303 | if (wvif->vif->type == NL80211_IFTYPE_STATION && |
304 | old_uapsd != wvif->uapsd_mask) { | |
7e2b32f5 | 305 | hif_set_uapsd_info(wvif, wvif->uapsd_mask); |
963aff57 | 306 | wfx_update_pm(wvif); |
40115bbc | 307 | } |
40115bbc | 308 | mutex_unlock(&wdev->conf_mutex); |
02a33f8e | 309 | return 0; |
40115bbc JP |
310 | } |
311 | ||
40115bbc JP |
312 | int wfx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) |
313 | { | |
314 | struct wfx_dev *wdev = hw->priv; | |
315 | struct wfx_vif *wvif = NULL; | |
316 | ||
317 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) | |
318 | hif_rts_threshold(wvif, value); | |
319 | return 0; | |
320 | } | |
321 | ||
40115bbc JP |
322 | static int __wfx_flush(struct wfx_dev *wdev, bool drop) |
323 | { | |
40115bbc | 324 | for (;;) { |
044df863 | 325 | if (drop) |
40115bbc | 326 | wfx_tx_queues_clear(wdev); |
044df863 JP |
327 | if (wait_event_timeout(wdev->tx_queue_stats.wait_link_id_empty, |
328 | wfx_tx_queues_is_empty(wdev), | |
329 | 2 * HZ) <= 0) | |
330 | return -ETIMEDOUT; | |
331 | wfx_tx_flush(wdev); | |
332 | if (wfx_tx_queues_is_empty(wdev)) | |
333 | return 0; | |
334 | dev_warn(wdev->dev, "frames queued while flushing tx queues"); | |
40115bbc | 335 | } |
40115bbc JP |
336 | } |
337 | ||
338 | void wfx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
339 | u32 queues, bool drop) | |
340 | { | |
50a4fb47 | 341 | // FIXME: only flush requested vif and queues |
044df863 | 342 | __wfx_flush(hw->priv, drop); |
40115bbc JP |
343 | } |
344 | ||
345 | /* WSM callbacks */ | |
346 | ||
5cd382b2 | 347 | static void wfx_event_report_rssi(struct wfx_vif *wvif, u8 raw_rcpi_rssi) |
40115bbc JP |
348 | { |
349 | /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 | |
350 | * RSSI = RCPI / 2 - 110 | |
351 | */ | |
352 | int rcpi_rssi; | |
353 | int cqm_evt; | |
354 | ||
355 | rcpi_rssi = raw_rcpi_rssi / 2 - 110; | |
ba366b92 | 356 | if (rcpi_rssi <= wvif->vif->bss_conf.cqm_rssi_thold) |
40115bbc JP |
357 | cqm_evt = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; |
358 | else | |
359 | cqm_evt = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; | |
360 | ieee80211_cqm_rssi_notify(wvif->vif, cqm_evt, rcpi_rssi, GFP_KERNEL); | |
361 | } | |
362 | ||
8fd1fe82 | 363 | static void wfx_event_handler_work(struct work_struct *work) |
40115bbc JP |
364 | { |
365 | struct wfx_vif *wvif = | |
366 | container_of(work, struct wfx_vif, event_handler_work); | |
367 | struct wfx_hif_event *event; | |
368 | ||
369 | LIST_HEAD(list); | |
370 | ||
371 | spin_lock(&wvif->event_queue_lock); | |
372 | list_splice_init(&wvif->event_queue, &list); | |
373 | spin_unlock(&wvif->event_queue_lock); | |
374 | ||
375 | list_for_each_entry(event, &list, link) { | |
376 | switch (event->evt.event_id) { | |
377 | case HIF_EVENT_IND_BSSLOST: | |
378 | cancel_work_sync(&wvif->unjoin_work); | |
3dc67854 JP |
379 | mutex_lock(&wvif->scan_lock); |
380 | wfx_cqm_bssloss_sm(wvif, 1, 0, 0); | |
381 | mutex_unlock(&wvif->scan_lock); | |
40115bbc JP |
382 | break; |
383 | case HIF_EVENT_IND_BSSREGAINED: | |
384 | wfx_cqm_bssloss_sm(wvif, 0, 0, 0); | |
385 | cancel_work_sync(&wvif->unjoin_work); | |
386 | break; | |
387 | case HIF_EVENT_IND_RCPI_RSSI: | |
8c7128c4 JI |
388 | wfx_event_report_rssi(wvif, |
389 | event->evt.event_data.rcpi_rssi); | |
40115bbc JP |
390 | break; |
391 | case HIF_EVENT_IND_PS_MODE_ERROR: | |
8c7128c4 JI |
392 | dev_warn(wvif->wdev->dev, |
393 | "error while processing power save request\n"); | |
40115bbc JP |
394 | break; |
395 | default: | |
8c7128c4 JI |
396 | dev_warn(wvif->wdev->dev, |
397 | "unhandled event indication: %.2x\n", | |
398 | event->evt.event_id); | |
40115bbc JP |
399 | break; |
400 | } | |
401 | } | |
402 | __wfx_free_event_queue(&list); | |
403 | } | |
404 | ||
8fd1fe82 | 405 | static void wfx_bss_loss_work(struct work_struct *work) |
40115bbc | 406 | { |
8c7128c4 JI |
407 | struct wfx_vif *wvif = container_of(work, struct wfx_vif, |
408 | bss_loss_work.work); | |
40115bbc JP |
409 | |
410 | ieee80211_connection_loss(wvif->vif); | |
411 | } | |
412 | ||
8fd1fe82 | 413 | static void wfx_bss_params_work(struct work_struct *work) |
40115bbc | 414 | { |
8c7128c4 JI |
415 | struct wfx_vif *wvif = container_of(work, struct wfx_vif, |
416 | bss_params_work); | |
40115bbc JP |
417 | |
418 | mutex_lock(&wvif->wdev->conf_mutex); | |
419 | wvif->bss_params.bss_flags.lost_count_only = 1; | |
420 | hif_set_bss_params(wvif, &wvif->bss_params); | |
421 | wvif->bss_params.bss_flags.lost_count_only = 0; | |
422 | mutex_unlock(&wvif->wdev->conf_mutex); | |
423 | } | |
424 | ||
40115bbc JP |
425 | static void wfx_do_unjoin(struct wfx_vif *wvif) |
426 | { | |
427 | mutex_lock(&wvif->wdev->conf_mutex); | |
428 | ||
40115bbc JP |
429 | if (!wvif->state) |
430 | goto done; | |
431 | ||
432 | if (wvif->state == WFX_STATE_AP) | |
433 | goto done; | |
434 | ||
435 | cancel_work_sync(&wvif->update_filtering_work); | |
40115bbc JP |
436 | wvif->state = WFX_STATE_PASSIVE; |
437 | ||
438 | /* Unjoin is a reset. */ | |
439 | wfx_tx_flush(wvif->wdev); | |
440 | hif_keep_alive_period(wvif, 0); | |
441 | hif_reset(wvif, false); | |
99879121 | 442 | wfx_tx_policy_init(wvif); |
40115bbc JP |
443 | hif_set_macaddr(wvif, wvif->vif->addr); |
444 | wfx_free_event_queue(wvif); | |
445 | cancel_work_sync(&wvif->event_handler_work); | |
446 | wfx_cqm_bssloss_sm(wvif, 0, 0, 0); | |
447 | ||
448 | /* Disable Block ACKs */ | |
449 | hif_set_block_ack_policy(wvif, 0, 0); | |
450 | ||
451 | wvif->disable_beacon_filter = false; | |
452 | wfx_update_filtering(wvif); | |
453 | memset(&wvif->bss_params, 0, sizeof(wvif->bss_params)); | |
40115bbc JP |
454 | |
455 | done: | |
456 | mutex_unlock(&wvif->wdev->conf_mutex); | |
457 | } | |
458 | ||
8c7128c4 JI |
459 | static void wfx_set_mfp(struct wfx_vif *wvif, |
460 | struct cfg80211_bss *bss) | |
40115bbc | 461 | { |
5cd382b2 JI |
462 | const int pairwise_cipher_suite_count_offset = 8 / sizeof(u16); |
463 | const int pairwise_cipher_suite_size = 4 / sizeof(u16); | |
464 | const int akm_suite_size = 4 / sizeof(u16); | |
465 | const u16 *ptr = NULL; | |
40115bbc JP |
466 | bool mfpc = false; |
467 | bool mfpr = false; | |
468 | ||
469 | /* 802.11w protected mgmt frames */ | |
470 | ||
471 | /* retrieve MFPC and MFPR flags from beacon or PBRSP */ | |
472 | ||
473 | rcu_read_lock(); | |
474 | if (bss) | |
5cd382b2 JI |
475 | ptr = (const u16 *) ieee80211_bss_get_ie(bss, |
476 | WLAN_EID_RSN); | |
40115bbc JP |
477 | |
478 | if (ptr) { | |
479 | ptr += pairwise_cipher_suite_count_offset; | |
480 | ptr += 1 + pairwise_cipher_suite_size * *ptr; | |
481 | ptr += 1 + akm_suite_size * *ptr; | |
482 | mfpr = *ptr & BIT(6); | |
483 | mfpc = *ptr & BIT(7); | |
484 | } | |
485 | rcu_read_unlock(); | |
486 | ||
487 | hif_set_mfp(wvif, mfpc, mfpr); | |
488 | } | |
489 | ||
40115bbc JP |
490 | static void wfx_do_join(struct wfx_vif *wvif) |
491 | { | |
9ced9b59 | 492 | int ret; |
40115bbc JP |
493 | struct ieee80211_bss_conf *conf = &wvif->vif->bss_conf; |
494 | struct cfg80211_bss *bss = NULL; | |
ac42c12d JP |
495 | u8 ssid[IEEE80211_MAX_SSID_LEN]; |
496 | const u8 *ssidie = NULL; | |
497 | int ssidlen = 0; | |
40115bbc | 498 | |
536607c0 JP |
499 | wfx_tx_lock_flush(wvif->wdev); |
500 | ||
40115bbc JP |
501 | if (wvif->state) |
502 | wfx_do_unjoin(wvif); | |
503 | ||
8c7128c4 | 504 | bss = cfg80211_get_bss(wvif->wdev->hw->wiphy, wvif->channel, |
9ced9b59 | 505 | conf->bssid, NULL, 0, |
40115bbc | 506 | IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); |
40115bbc JP |
507 | if (!bss && !conf->ibss_joined) { |
508 | wfx_tx_unlock(wvif->wdev); | |
509 | return; | |
510 | } | |
511 | ||
512 | mutex_lock(&wvif->wdev->conf_mutex); | |
513 | ||
40115bbc JP |
514 | /* Sanity check beacon interval */ |
515 | if (!wvif->beacon_int) | |
516 | wvif->beacon_int = 1; | |
517 | ||
ac42c12d | 518 | rcu_read_lock(); // protect ssidie |
9ced9b59 | 519 | if (!conf->ibss_joined) |
40115bbc | 520 | ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); |
ac42c12d JP |
521 | if (ssidie) { |
522 | ssidlen = ssidie[1]; | |
523 | memcpy(ssid, &ssidie[2], ssidie[1]); | |
524 | } | |
525 | rcu_read_unlock(); | |
40115bbc JP |
526 | |
527 | wfx_tx_flush(wvif->wdev); | |
528 | ||
529 | if (wvif_count(wvif->wdev) <= 1) | |
530 | hif_set_block_ack_policy(wvif, 0xFF, 0xFF); | |
531 | ||
532 | wfx_set_mfp(wvif, bss); | |
533 | ||
40115bbc | 534 | wvif->wdev->tx_burst_idx = -1; |
ac42c12d | 535 | ret = hif_join(wvif, conf, wvif->channel, ssid, ssidlen); |
9ced9b59 | 536 | if (ret) { |
40115bbc JP |
537 | ieee80211_connection_loss(wvif->vif); |
538 | wvif->join_complete_status = -1; | |
539 | /* Tx lock still held, unjoin will clear it. */ | |
540 | if (!schedule_work(&wvif->unjoin_work)) | |
541 | wfx_tx_unlock(wvif->wdev); | |
542 | } else { | |
543 | wvif->join_complete_status = 0; | |
544 | if (wvif->vif->type == NL80211_IFTYPE_ADHOC) | |
545 | wvif->state = WFX_STATE_IBSS; | |
546 | else | |
547 | wvif->state = WFX_STATE_PRE_STA; | |
548 | wfx_tx_unlock(wvif->wdev); | |
549 | ||
550 | /* Upload keys */ | |
551 | wfx_upload_keys(wvif); | |
552 | ||
553 | /* Due to beacon filtering it is possible that the | |
554 | * AP's beacon is not known for the mac80211 stack. | |
555 | * Disable filtering temporary to make sure the stack | |
556 | * receives at least one | |
557 | */ | |
558 | wvif->disable_beacon_filter = true; | |
559 | } | |
560 | wfx_update_filtering(wvif); | |
561 | ||
40115bbc JP |
562 | mutex_unlock(&wvif->wdev->conf_mutex); |
563 | if (bss) | |
564 | cfg80211_put_bss(wvif->wdev->hw->wiphy, bss); | |
565 | } | |
566 | ||
8fd1fe82 | 567 | static void wfx_unjoin_work(struct work_struct *work) |
40115bbc JP |
568 | { |
569 | struct wfx_vif *wvif = container_of(work, struct wfx_vif, unjoin_work); | |
570 | ||
571 | wfx_do_unjoin(wvif); | |
572 | wfx_tx_unlock(wvif->wdev); | |
573 | } | |
574 | ||
575 | int wfx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
576 | struct ieee80211_sta *sta) | |
577 | { | |
40115bbc JP |
578 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; |
579 | struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; | |
40115bbc | 580 | |
7d2d2bfd | 581 | spin_lock_init(&sta_priv->lock); |
40115bbc | 582 | sta_priv->vif_id = wvif->id; |
40115bbc | 583 | |
d6aeba57 JP |
584 | // FIXME: in station mode, the current API interprets new link-id as a |
585 | // tdls peer. | |
586 | if (vif->type == NL80211_IFTYPE_STATION) | |
587 | return 0; | |
588 | sta_priv->link_id = ffz(wvif->link_id_map); | |
589 | wvif->link_id_map |= BIT(sta_priv->link_id); | |
590 | WARN_ON(!sta_priv->link_id); | |
591 | WARN_ON(sta_priv->link_id >= WFX_MAX_STA_IN_AP_MODE); | |
592 | hif_map_link(wvif, sta->addr, 0, sta_priv->link_id); | |
593 | ||
40115bbc JP |
594 | spin_lock_bh(&wvif->ps_state_lock); |
595 | if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == | |
596 | IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) | |
597 | wvif->sta_asleep_mask |= BIT(sta_priv->link_id); | |
40115bbc JP |
598 | spin_unlock_bh(&wvif->ps_state_lock); |
599 | return 0; | |
600 | } | |
601 | ||
602 | int wfx_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
603 | struct ieee80211_sta *sta) | |
604 | { | |
40115bbc JP |
605 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; |
606 | struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; | |
98511a91 | 607 | int i; |
40115bbc | 608 | |
df3519a3 | 609 | for (i = 0; i < ARRAY_SIZE(sta_priv->buffered); i++) |
4bbc6a3e JP |
610 | if (sta_priv->buffered[i]) |
611 | dev_warn(wvif->wdev->dev, "release station while %d pending frame on queue %d", | |
612 | sta_priv->buffered[i], i); | |
d6aeba57 JP |
613 | // FIXME: see note in wfx_sta_add() |
614 | if (vif->type == NL80211_IFTYPE_STATION) | |
40115bbc | 615 | return 0; |
d6aeba57 JP |
616 | // FIXME add a mutex? |
617 | hif_map_link(wvif, sta->addr, 1, sta_priv->link_id); | |
618 | wvif->link_id_map &= ~BIT(sta_priv->link_id); | |
40115bbc JP |
619 | return 0; |
620 | } | |
621 | ||
40115bbc JP |
622 | static int wfx_start_ap(struct wfx_vif *wvif) |
623 | { | |
624 | int ret; | |
40115bbc | 625 | |
09779276 | 626 | wvif->beacon_int = wvif->vif->bss_conf.beacon_int; |
40115bbc | 627 | wvif->wdev->tx_burst_idx = -1; |
09779276 JP |
628 | ret = hif_start(wvif, &wvif->vif->bss_conf, wvif->channel); |
629 | if (ret) | |
630 | return ret; | |
631 | ret = wfx_upload_keys(wvif); | |
632 | if (ret) | |
633 | return ret; | |
634 | if (wvif_count(wvif->wdev) <= 1) | |
635 | hif_set_block_ack_policy(wvif, 0xFF, 0xFF); | |
636 | wvif->state = WFX_STATE_AP; | |
637 | wfx_update_filtering(wvif); | |
638 | return 0; | |
40115bbc JP |
639 | } |
640 | ||
641 | static int wfx_update_beaconing(struct wfx_vif *wvif) | |
642 | { | |
0b2b0595 JP |
643 | if (wvif->vif->type != NL80211_IFTYPE_AP) |
644 | return 0; | |
645 | if (wvif->state == WFX_STATE_AP && | |
646 | wvif->beacon_int == wvif->vif->bss_conf.beacon_int) | |
647 | return 0; | |
648 | wfx_tx_lock_flush(wvif->wdev); | |
649 | hif_reset(wvif, false); | |
650 | wfx_tx_policy_init(wvif); | |
651 | wvif->state = WFX_STATE_PASSIVE; | |
652 | wfx_start_ap(wvif); | |
653 | wfx_tx_unlock(wvif->wdev); | |
40115bbc JP |
654 | return 0; |
655 | } | |
656 | ||
d8a92d91 | 657 | static int wfx_upload_ap_templates(struct wfx_vif *wvif) |
40115bbc | 658 | { |
094ecec9 | 659 | struct sk_buff *skb; |
40115bbc JP |
660 | |
661 | if (wvif->vif->type == NL80211_IFTYPE_STATION || | |
662 | wvif->vif->type == NL80211_IFTYPE_MONITOR || | |
663 | wvif->vif->type == NL80211_IFTYPE_UNSPECIFIED) | |
094ecec9 | 664 | return 0; |
40115bbc JP |
665 | |
666 | skb = ieee80211_beacon_get(wvif->wdev->hw, wvif->vif); | |
40115bbc JP |
667 | if (!skb) |
668 | return -ENOMEM; | |
094ecec9 JP |
669 | hif_set_template_frame(wvif, skb, HIF_TMPLT_BCN, |
670 | API_RATE_INDEX_B_1MBPS); | |
305f7109 | 671 | dev_kfree_skb(skb); |
40115bbc | 672 | |
305f7109 JP |
673 | skb = ieee80211_proberesp_get(wvif->wdev->hw, wvif->vif); |
674 | if (!skb) | |
675 | return -ENOMEM; | |
094ecec9 JP |
676 | hif_set_template_frame(wvif, skb, HIF_TMPLT_PRBRES, |
677 | API_RATE_INDEX_B_1MBPS); | |
1878c5b9 | 678 | dev_kfree_skb(skb); |
094ecec9 | 679 | return 0; |
40115bbc JP |
680 | } |
681 | ||
8c7128c4 JI |
682 | static void wfx_join_finalize(struct wfx_vif *wvif, |
683 | struct ieee80211_bss_conf *info) | |
40115bbc JP |
684 | { |
685 | struct ieee80211_sta *sta = NULL; | |
40115bbc | 686 | |
40115bbc | 687 | wvif->beacon_int = info->beacon_int; |
d0014901 | 688 | rcu_read_lock(); // protect sta |
40115bbc JP |
689 | if (info->bssid && !info->ibss_joined) |
690 | sta = ieee80211_find_sta(wvif->vif, info->bssid); | |
811ed3e2 | 691 | if (sta) |
40115bbc JP |
692 | wvif->bss_params.operational_rate_set = |
693 | wfx_rate_mask_to_hw(wvif->wdev, sta->supp_rates[wvif->channel->band]); | |
811ed3e2 | 694 | else |
40115bbc | 695 | wvif->bss_params.operational_rate_set = -1; |
046cc2ef | 696 | rcu_read_unlock(); |
08dced7f JP |
697 | if (sta && |
698 | info->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) | |
40115bbc JP |
699 | hif_dual_cts_protection(wvif, true); |
700 | else | |
701 | hif_dual_cts_protection(wvif, false); | |
702 | ||
40115bbc JP |
703 | wfx_cqm_bssloss_sm(wvif, 0, 0, 0); |
704 | cancel_work_sync(&wvif->unjoin_work); | |
705 | ||
706 | wvif->bss_params.beacon_lost_count = 20; | |
707 | wvif->bss_params.aid = info->aid; | |
708 | ||
046cc2ef | 709 | hif_set_association_mode(wvif, info); |
40115bbc JP |
710 | |
711 | if (!info->ibss_joined) { | |
712 | hif_keep_alive_period(wvif, 30 /* sec */); | |
713 | hif_set_bss_params(wvif, &wvif->bss_params); | |
b0674e06 JP |
714 | hif_set_beacon_wakeup_period(wvif, info->dtim_period, |
715 | info->dtim_period); | |
8dd5bb66 | 716 | wfx_update_pm(wvif); |
40115bbc JP |
717 | } |
718 | } | |
719 | ||
720 | void wfx_bss_info_changed(struct ieee80211_hw *hw, | |
721 | struct ieee80211_vif *vif, | |
722 | struct ieee80211_bss_conf *info, | |
723 | u32 changed) | |
724 | { | |
725 | struct wfx_dev *wdev = hw->priv; | |
726 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; | |
727 | bool do_join = false; | |
728 | int i; | |
40115bbc JP |
729 | |
730 | mutex_lock(&wdev->conf_mutex); | |
731 | ||
732 | /* TODO: BSS_CHANGED_QOS */ | |
733 | if (changed & BSS_CHANGED_ARP_FILTER) { | |
40115bbc | 734 | for (i = 0; i < HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES; i++) { |
e52e68ee JP |
735 | __be32 *arp_addr = &info->arp_addr_list[i]; |
736 | ||
737 | if (info->arp_addr_cnt > HIF_MAX_ARP_IP_ADDRTABLE_ENTRIES) | |
738 | arp_addr = NULL; | |
739 | if (i >= info->arp_addr_cnt) | |
740 | arp_addr = NULL; | |
741 | hif_set_arp_ipv4_filter(wvif, i, arp_addr); | |
40115bbc JP |
742 | } |
743 | } | |
744 | ||
eddd8585 JP |
745 | if (changed & BSS_CHANGED_BEACON || |
746 | changed & BSS_CHANGED_AP_PROBE_RESP || | |
747 | changed & BSS_CHANGED_BSSID || | |
748 | changed & BSS_CHANGED_SSID || | |
749 | changed & BSS_CHANGED_IBSS) { | |
40115bbc JP |
750 | wvif->beacon_int = info->beacon_int; |
751 | wfx_update_beaconing(wvif); | |
d8a92d91 JP |
752 | wfx_upload_ap_templates(wvif); |
753 | wfx_fwd_probe_req(wvif, false); | |
40115bbc JP |
754 | } |
755 | ||
8c7128c4 | 756 | if (changed & BSS_CHANGED_BEACON_ENABLED && |
9d443ffc JP |
757 | wvif->state != WFX_STATE_IBSS) |
758 | hif_beacon_transmit(wvif, info->enable_beacon); | |
40115bbc | 759 | |
b0674e06 JP |
760 | if (changed & BSS_CHANGED_BEACON_INFO) |
761 | hif_set_beacon_wakeup_period(wvif, info->dtim_period, | |
762 | info->dtim_period); | |
763 | ||
40115bbc JP |
764 | /* assoc/disassoc, or maybe AID changed */ |
765 | if (changed & BSS_CHANGED_ASSOC) { | |
766 | wfx_tx_lock_flush(wdev); | |
767 | wvif->wep_default_key_id = -1; | |
768 | wfx_tx_unlock(wdev); | |
769 | } | |
770 | ||
771 | if (changed & BSS_CHANGED_ASSOC && !info->assoc && | |
772 | (wvif->state == WFX_STATE_STA || wvif->state == WFX_STATE_IBSS)) { | |
773 | /* Shedule unjoin work */ | |
774 | wfx_tx_lock(wdev); | |
775 | if (!schedule_work(&wvif->unjoin_work)) | |
776 | wfx_tx_unlock(wdev); | |
777 | } else { | |
778 | if (changed & BSS_CHANGED_BEACON_INT) { | |
779 | if (info->ibss_joined) | |
780 | do_join = true; | |
781 | else if (wvif->state == WFX_STATE_AP) | |
782 | wfx_update_beaconing(wvif); | |
783 | } | |
784 | ||
785 | if (changed & BSS_CHANGED_BSSID) | |
786 | do_join = true; | |
787 | ||
eddd8585 JP |
788 | if (changed & BSS_CHANGED_ASSOC || |
789 | changed & BSS_CHANGED_BSSID || | |
790 | changed & BSS_CHANGED_IBSS || | |
791 | changed & BSS_CHANGED_BASIC_RATES || | |
792 | changed & BSS_CHANGED_HT) { | |
40115bbc JP |
793 | if (info->assoc) { |
794 | if (wvif->state < WFX_STATE_PRE_STA) { | |
795 | ieee80211_connection_loss(vif); | |
796 | mutex_unlock(&wdev->conf_mutex); | |
797 | return; | |
798 | } else if (wvif->state == WFX_STATE_PRE_STA) { | |
799 | wvif->state = WFX_STATE_STA; | |
800 | } | |
801 | } else { | |
802 | do_join = true; | |
803 | } | |
804 | ||
805 | if (info->assoc || info->ibss_joined) | |
806 | wfx_join_finalize(wvif, info); | |
807 | else | |
8c7128c4 JI |
808 | memset(&wvif->bss_params, 0, |
809 | sizeof(wvif->bss_params)); | |
40115bbc JP |
810 | } |
811 | } | |
812 | ||
eddd8585 JP |
813 | if (changed & BSS_CHANGED_ASSOC || |
814 | changed & BSS_CHANGED_ERP_CTS_PROT || | |
815 | changed & BSS_CHANGED_ERP_PREAMBLE) { | |
deb7734e | 816 | u8 erp_ie[3] = { WLAN_EID_ERP_INFO, 1, 0 }; |
40115bbc | 817 | |
deb7734e | 818 | hif_erp_use_protection(wvif, info->use_cts_prot); |
40115bbc | 819 | if (info->use_cts_prot) |
deb7734e | 820 | erp_ie[2] |= WLAN_ERP_USE_PROTECTION; |
40115bbc | 821 | if (info->use_short_preamble) |
deb7734e JP |
822 | erp_ie[2] |= WLAN_ERP_BARKER_PREAMBLE; |
823 | if (wvif->vif->type != NL80211_IFTYPE_STATION) | |
a09343fc | 824 | hif_update_ie_beacon(wvif, erp_ie, sizeof(erp_ie)); |
40115bbc JP |
825 | } |
826 | ||
eddd8585 | 827 | if (changed & BSS_CHANGED_ASSOC || changed & BSS_CHANGED_ERP_SLOT) |
40115bbc JP |
828 | hif_slot_time(wvif, info->use_short_slot ? 9 : 20); |
829 | ||
ba366b92 | 830 | if (changed & BSS_CHANGED_ASSOC || changed & BSS_CHANGED_CQM) |
9ed8b0d0 JP |
831 | hif_set_rcpi_rssi_threshold(wvif, info->cqm_rssi_thold, |
832 | info->cqm_rssi_hyst); | |
40115bbc | 833 | |
f050f3da JP |
834 | if (changed & BSS_CHANGED_TXPOWER) |
835 | hif_set_output_power(wvif, info->txpower); | |
8d97a12f JP |
836 | |
837 | if (changed & BSS_CHANGED_PS) | |
838 | wfx_update_pm(wvif); | |
839 | ||
40115bbc JP |
840 | mutex_unlock(&wdev->conf_mutex); |
841 | ||
536607c0 JP |
842 | if (do_join) |
843 | wfx_do_join(wvif); | |
40115bbc JP |
844 | } |
845 | ||
6537adc3 JP |
846 | static void wfx_ps_notify_sta(struct wfx_vif *wvif, |
847 | enum sta_notify_cmd notify_cmd, int link_id) | |
40115bbc | 848 | { |
40115bbc | 849 | spin_lock_bh(&wvif->ps_state_lock); |
ded6ca11 JP |
850 | if (notify_cmd == STA_NOTIFY_SLEEP) |
851 | wvif->sta_asleep_mask |= BIT(link_id); | |
852 | else // notify_cmd == STA_NOTIFY_AWAKE | |
853 | wvif->sta_asleep_mask &= ~BIT(link_id); | |
40115bbc | 854 | spin_unlock_bh(&wvif->ps_state_lock); |
ded6ca11 JP |
855 | if (notify_cmd == STA_NOTIFY_AWAKE) |
856 | wfx_bh_request_tx(wvif->wdev); | |
40115bbc JP |
857 | } |
858 | ||
859 | void wfx_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
860 | enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) | |
861 | { | |
862 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; | |
863 | struct wfx_sta_priv *sta_priv = (struct wfx_sta_priv *) &sta->drv_priv; | |
864 | ||
6537adc3 | 865 | wfx_ps_notify_sta(wvif, notify_cmd, sta_priv->link_id); |
40115bbc JP |
866 | } |
867 | ||
36cbb5d2 | 868 | static int wfx_update_tim(struct wfx_vif *wvif) |
9bca45f3 JP |
869 | { |
870 | struct sk_buff *skb; | |
9bca45f3 JP |
871 | u16 tim_offset, tim_length; |
872 | u8 *tim_ptr; | |
873 | ||
874 | skb = ieee80211_beacon_get_tim(wvif->wdev->hw, wvif->vif, | |
875 | &tim_offset, &tim_length); | |
40115bbc | 876 | if (!skb) { |
044df863 | 877 | __wfx_flush(wvif->wdev, true); |
9bca45f3 | 878 | return -ENOENT; |
40115bbc | 879 | } |
9bca45f3 JP |
880 | tim_ptr = skb->data + tim_offset; |
881 | ||
882 | if (tim_offset && tim_length >= 6) { | |
883 | /* Ignore DTIM count from mac80211: | |
884 | * firmware handles DTIM internally. | |
885 | */ | |
886 | tim_ptr[2] = 0; | |
887 | ||
888 | /* Set/reset aid0 bit */ | |
a3c529a8 | 889 | if (wfx_tx_queues_get_after_dtim(wvif)) |
9bca45f3 JP |
890 | tim_ptr[4] |= 1; |
891 | else | |
892 | tim_ptr[4] &= ~1; | |
893 | } | |
894 | ||
a09343fc | 895 | hif_update_ie_beacon(wvif, tim_ptr, tim_length); |
9bca45f3 JP |
896 | dev_kfree_skb(skb); |
897 | ||
898 | return 0; | |
899 | } | |
900 | ||
36cbb5d2 | 901 | static void wfx_update_tim_work(struct work_struct *work) |
40115bbc | 902 | { |
36cbb5d2 | 903 | struct wfx_vif *wvif = container_of(work, struct wfx_vif, update_tim_work); |
40115bbc | 904 | |
36cbb5d2 | 905 | wfx_update_tim(wvif); |
40115bbc JP |
906 | } |
907 | ||
908 | int wfx_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) | |
909 | { | |
910 | struct wfx_dev *wdev = hw->priv; | |
76498b49 | 911 | struct wfx_sta_priv *sta_dev = (struct wfx_sta_priv *)&sta->drv_priv; |
40115bbc JP |
912 | struct wfx_vif *wvif = wdev_to_wvif(wdev, sta_dev->vif_id); |
913 | ||
36cbb5d2 | 914 | schedule_work(&wvif->update_tim_work); |
40115bbc JP |
915 | return 0; |
916 | } | |
917 | ||
a3c529a8 | 918 | void wfx_suspend_resume_mc(struct wfx_vif *wvif, enum sta_notify_cmd notify_cmd) |
9bca45f3 | 919 | { |
a3c529a8 JP |
920 | WARN(!wfx_tx_queues_get_after_dtim(wvif), "incorrect sequence"); |
921 | WARN(wvif->after_dtim_tx_allowed, "incorrect sequence"); | |
922 | wvif->after_dtim_tx_allowed = true; | |
923 | wfx_bh_request_tx(wvif->wdev); | |
9bca45f3 JP |
924 | } |
925 | ||
40115bbc JP |
926 | int wfx_ampdu_action(struct ieee80211_hw *hw, |
927 | struct ieee80211_vif *vif, | |
928 | struct ieee80211_ampdu_params *params) | |
929 | { | |
930 | /* Aggregation is implemented fully in firmware, | |
931 | * including block ack negotiation. Do not allow | |
932 | * mac80211 stack to do anything: it interferes with | |
933 | * the firmware. | |
934 | */ | |
935 | ||
936 | /* Note that we still need this function stubbed. */ | |
937 | ||
938 | return -ENOTSUPP; | |
939 | } | |
940 | ||
40115bbc JP |
941 | int wfx_add_chanctx(struct ieee80211_hw *hw, |
942 | struct ieee80211_chanctx_conf *conf) | |
943 | { | |
944 | return 0; | |
945 | } | |
946 | ||
947 | void wfx_remove_chanctx(struct ieee80211_hw *hw, | |
948 | struct ieee80211_chanctx_conf *conf) | |
949 | { | |
950 | } | |
951 | ||
952 | void wfx_change_chanctx(struct ieee80211_hw *hw, | |
953 | struct ieee80211_chanctx_conf *conf, | |
954 | u32 changed) | |
955 | { | |
956 | } | |
957 | ||
958 | int wfx_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
959 | struct ieee80211_chanctx_conf *conf) | |
960 | { | |
961 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; | |
962 | struct ieee80211_channel *ch = conf->def.chan; | |
963 | ||
964 | WARN(wvif->channel, "channel overwrite"); | |
965 | wvif->channel = ch; | |
40115bbc JP |
966 | |
967 | return 0; | |
968 | } | |
969 | ||
8c7128c4 JI |
970 | void wfx_unassign_vif_chanctx(struct ieee80211_hw *hw, |
971 | struct ieee80211_vif *vif, | |
40115bbc JP |
972 | struct ieee80211_chanctx_conf *conf) |
973 | { | |
974 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; | |
975 | struct ieee80211_channel *ch = conf->def.chan; | |
976 | ||
977 | WARN(wvif->channel != ch, "channel mismatch"); | |
978 | wvif->channel = NULL; | |
979 | } | |
980 | ||
981 | int wfx_config(struct ieee80211_hw *hw, u32 changed) | |
982 | { | |
8d97a12f | 983 | return 0; |
40115bbc JP |
984 | } |
985 | ||
e16e7f07 JP |
986 | int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) |
987 | { | |
0469fc6a | 988 | int i, ret = 0; |
e16e7f07 JP |
989 | struct wfx_dev *wdev = hw->priv; |
990 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; | |
991 | ||
40115bbc JP |
992 | vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | |
993 | IEEE80211_VIF_SUPPORTS_UAPSD | | |
994 | IEEE80211_VIF_SUPPORTS_CQM_RSSI; | |
995 | ||
1a61af0f JP |
996 | mutex_lock(&wdev->conf_mutex); |
997 | ||
40115bbc JP |
998 | switch (vif->type) { |
999 | case NL80211_IFTYPE_STATION: | |
1000 | case NL80211_IFTYPE_ADHOC: | |
1001 | case NL80211_IFTYPE_AP: | |
1002 | break; | |
1003 | default: | |
1004 | mutex_unlock(&wdev->conf_mutex); | |
1005 | return -EOPNOTSUPP; | |
1006 | } | |
1007 | ||
e16e7f07 JP |
1008 | for (i = 0; i < ARRAY_SIZE(wdev->vif); i++) { |
1009 | if (!wdev->vif[i]) { | |
1010 | wdev->vif[i] = vif; | |
1011 | wvif->id = i; | |
1012 | break; | |
1013 | } | |
1014 | } | |
1a61af0f JP |
1015 | if (i == ARRAY_SIZE(wdev->vif)) { |
1016 | mutex_unlock(&wdev->conf_mutex); | |
e16e7f07 | 1017 | return -EOPNOTSUPP; |
1a61af0f | 1018 | } |
40115bbc | 1019 | // FIXME: prefer use of container_of() to get vif |
e16e7f07 JP |
1020 | wvif->vif = vif; |
1021 | wvif->wdev = wdev; | |
1022 | ||
d6aeba57 | 1023 | wvif->link_id_map = 1; // link-id 0 is reserved for multicast |
9bca45f3 | 1024 | spin_lock_init(&wvif->ps_state_lock); |
36cbb5d2 | 1025 | INIT_WORK(&wvif->update_tim_work, wfx_update_tim_work); |
9bca45f3 | 1026 | |
963aff57 JP |
1027 | memset(&wvif->bss_params, 0, sizeof(wvif->bss_params)); |
1028 | ||
40115bbc JP |
1029 | mutex_init(&wvif->bss_loss_lock); |
1030 | INIT_DELAYED_WORK(&wvif->bss_loss_work, wfx_bss_loss_work); | |
1031 | ||
fb2490f6 JP |
1032 | wvif->wep_default_key_id = -1; |
1033 | INIT_WORK(&wvif->wep_key_work, wfx_wep_key_work); | |
1a61af0f | 1034 | |
40115bbc JP |
1035 | spin_lock_init(&wvif->event_queue_lock); |
1036 | INIT_LIST_HEAD(&wvif->event_queue); | |
1037 | INIT_WORK(&wvif->event_handler_work, wfx_event_handler_work); | |
1038 | ||
1039 | init_completion(&wvif->set_pm_mode_complete); | |
1040 | complete(&wvif->set_pm_mode_complete); | |
40115bbc JP |
1041 | INIT_WORK(&wvif->update_filtering_work, wfx_update_filtering_work); |
1042 | INIT_WORK(&wvif->bss_params_work, wfx_bss_params_work); | |
40115bbc | 1043 | INIT_WORK(&wvif->unjoin_work, wfx_unjoin_work); |
99879121 | 1044 | INIT_WORK(&wvif->tx_policy_upload_work, wfx_tx_policy_upload_work); |
d1c015b4 JP |
1045 | |
1046 | mutex_init(&wvif->scan_lock); | |
1047 | init_completion(&wvif->scan_complete); | |
3827e33d | 1048 | INIT_WORK(&wvif->scan_work, wfx_hw_scan_work); |
d1c015b4 | 1049 | |
b0ac999e | 1050 | INIT_WORK(&wvif->tx_policy_upload_work, wfx_tx_policy_upload_work); |
1a61af0f | 1051 | mutex_unlock(&wdev->conf_mutex); |
40115bbc JP |
1052 | |
1053 | hif_set_macaddr(wvif, vif->addr); | |
40115bbc | 1054 | |
fcd6c0f9 | 1055 | wfx_tx_policy_init(wvif); |
40115bbc JP |
1056 | wvif = NULL; |
1057 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { | |
1058 | // Combo mode does not support Block Acks. We can re-enable them | |
1059 | if (wvif_count(wdev) == 1) | |
1060 | hif_set_block_ack_policy(wvif, 0xFF, 0xFF); | |
1061 | else | |
1062 | hif_set_block_ack_policy(wvif, 0x00, 0x00); | |
1063 | // Combo force powersave mode. We can re-enable it now | |
0469fc6a | 1064 | ret = wfx_update_pm(wvif); |
40115bbc | 1065 | } |
0469fc6a | 1066 | return ret; |
e16e7f07 JP |
1067 | } |
1068 | ||
1069 | void wfx_remove_interface(struct ieee80211_hw *hw, | |
1070 | struct ieee80211_vif *vif) | |
1071 | { | |
40115bbc | 1072 | struct wfx_dev *wdev = hw->priv; |
9bca45f3 | 1073 | struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv; |
40115bbc | 1074 | |
40115bbc | 1075 | wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300)); |
9bca45f3 | 1076 | |
40115bbc | 1077 | mutex_lock(&wdev->conf_mutex); |
d6aeba57 | 1078 | WARN(wvif->link_id_map != 1, "corrupted state"); |
40115bbc JP |
1079 | switch (wvif->state) { |
1080 | case WFX_STATE_PRE_STA: | |
1081 | case WFX_STATE_STA: | |
1082 | case WFX_STATE_IBSS: | |
1083 | wfx_tx_lock_flush(wdev); | |
1084 | if (!schedule_work(&wvif->unjoin_work)) | |
1085 | wfx_tx_unlock(wdev); | |
1086 | break; | |
1087 | case WFX_STATE_AP: | |
40115bbc | 1088 | wvif->sta_asleep_mask = 0; |
40115bbc JP |
1089 | /* reset.link_id = 0; */ |
1090 | hif_reset(wvif, false); | |
1091 | break; | |
1092 | default: | |
1093 | break; | |
1094 | } | |
1095 | ||
1096 | wvif->state = WFX_STATE_PASSIVE; | |
9bca45f3 | 1097 | wfx_tx_queues_wait_empty_vif(wvif); |
40115bbc JP |
1098 | wfx_tx_unlock(wdev); |
1099 | ||
1100 | /* FIXME: In add to reset MAC address, try to reset interface */ | |
1101 | hif_set_macaddr(wvif, NULL); | |
1102 | ||
40115bbc JP |
1103 | wfx_cqm_bssloss_sm(wvif, 0, 0, 0); |
1104 | cancel_work_sync(&wvif->unjoin_work); | |
40115bbc JP |
1105 | wfx_free_event_queue(wvif); |
1106 | ||
1107 | wdev->vif[wvif->id] = NULL; | |
1108 | wvif->vif = NULL; | |
1109 | ||
1110 | mutex_unlock(&wdev->conf_mutex); | |
1111 | wvif = NULL; | |
1112 | while ((wvif = wvif_iterate(wdev, wvif)) != NULL) { | |
1113 | // Combo mode does not support Block Acks. We can re-enable them | |
1114 | if (wvif_count(wdev) == 1) | |
1115 | hif_set_block_ack_policy(wvif, 0xFF, 0xFF); | |
1116 | else | |
1117 | hif_set_block_ack_policy(wvif, 0x00, 0x00); | |
1118 | // Combo force powersave mode. We can re-enable it now | |
8dd5bb66 | 1119 | wfx_update_pm(wvif); |
40115bbc | 1120 | } |
e16e7f07 JP |
1121 | } |
1122 | ||
1123 | int wfx_start(struct ieee80211_hw *hw) | |
1124 | { | |
1125 | return 0; | |
1126 | } | |
1127 | ||
1128 | void wfx_stop(struct ieee80211_hw *hw) | |
1129 | { | |
9bca45f3 JP |
1130 | struct wfx_dev *wdev = hw->priv; |
1131 | ||
1132 | wfx_tx_lock_flush(wdev); | |
1a61af0f | 1133 | mutex_lock(&wdev->conf_mutex); |
9bca45f3 | 1134 | wfx_tx_queues_clear(wdev); |
1a61af0f | 1135 | mutex_unlock(&wdev->conf_mutex); |
9bca45f3 JP |
1136 | wfx_tx_unlock(wdev); |
1137 | WARN(atomic_read(&wdev->tx_lock), "tx_lock is locked"); | |
e16e7f07 | 1138 | } |