1 | /* |
2 | * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
18 | |
19 | #include <linux/module.h> |
20 | #include <linux/firmware.h> |
21 | #include <linux/platform_device.h> |
22 | #include <linux/of.h> |
23 | #include <linux/of_address.h> |
24 | #include <linux/rpmsg.h> |
25 | #include <linux/soc/qcom/smem_state.h> |
26 | #include <linux/soc/qcom/wcnss_ctrl.h> |
27 | #include <net/ipv6.h> |
28 | #include "wcn36xx.h" |
29 | #include "testmode.h" |
30 | #include "firmware.h" |
31 | |
32 | unsigned int wcn36xx_dbg_mask; |
33 | module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644); |
34 | MODULE_PARM_DESC(debug_mask, "Debugging mask" ); |
35 | |
36 | #define CHAN2G(_freq, _idx) { \ |
37 | .band = NL80211_BAND_2GHZ, \ |
38 | .center_freq = (_freq), \ |
39 | .hw_value = (_idx), \ |
40 | .max_power = 25, \ |
41 | } |
42 | |
43 | #define CHAN5G(_freq, _idx, _phy_val) { \ |
44 | .band = NL80211_BAND_5GHZ, \ |
45 | .center_freq = (_freq), \ |
46 | .hw_value = (_phy_val) << HW_VALUE_PHY_SHIFT | HW_VALUE_CHANNEL(_idx), \ |
47 | .max_power = 25, \ |
48 | } |
49 | |
50 | /* The wcn firmware expects channel values to matching |
51 | * their mnemonic values. So use these for .hw_value. */ |
52 | static struct ieee80211_channel wcn_2ghz_channels[] = { |
53 | CHAN2G(2412, 1), /* Channel 1 */ |
54 | CHAN2G(2417, 2), /* Channel 2 */ |
55 | CHAN2G(2422, 3), /* Channel 3 */ |
56 | CHAN2G(2427, 4), /* Channel 4 */ |
57 | CHAN2G(2432, 5), /* Channel 5 */ |
58 | CHAN2G(2437, 6), /* Channel 6 */ |
59 | CHAN2G(2442, 7), /* Channel 7 */ |
60 | CHAN2G(2447, 8), /* Channel 8 */ |
61 | CHAN2G(2452, 9), /* Channel 9 */ |
62 | CHAN2G(2457, 10), /* Channel 10 */ |
63 | CHAN2G(2462, 11), /* Channel 11 */ |
64 | CHAN2G(2467, 12), /* Channel 12 */ |
65 | CHAN2G(2472, 13), /* Channel 13 */ |
66 | CHAN2G(2484, 14) /* Channel 14 */ |
67 | |
68 | }; |
69 | |
70 | static struct ieee80211_channel wcn_5ghz_channels[] = { |
71 | CHAN5G(5180, 36, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), |
72 | CHAN5G(5200, 40, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), |
73 | CHAN5G(5220, 44, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), |
74 | CHAN5G(5240, 48, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), |
75 | CHAN5G(5260, 52, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), |
76 | CHAN5G(5280, 56, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), |
77 | CHAN5G(5300, 60, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), |
78 | CHAN5G(5320, 64, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), |
79 | CHAN5G(5500, 100, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), |
80 | CHAN5G(5520, 104, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), |
81 | CHAN5G(5540, 108, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), |
82 | CHAN5G(5560, 112, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), |
83 | CHAN5G(5580, 116, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), |
84 | CHAN5G(5600, 120, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), |
85 | CHAN5G(5620, 124, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), |
86 | CHAN5G(5640, 128, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), |
87 | CHAN5G(5660, 132, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), |
88 | CHAN5G(5680, 136, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), |
89 | CHAN5G(5700, 140, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), |
90 | CHAN5G(5720, 144, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), |
91 | CHAN5G(5745, 149, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW), |
92 | CHAN5G(5765, 153, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW), |
93 | CHAN5G(5785, 157, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH), |
94 | CHAN5G(5805, 161, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH), |
95 | CHAN5G(5825, 165, 0) |
96 | }; |
97 | |
98 | #define RATE(_bitrate, _hw_rate, _flags) { \ |
99 | .bitrate = (_bitrate), \ |
100 | .flags = (_flags), \ |
101 | .hw_value = (_hw_rate), \ |
102 | .hw_value_short = (_hw_rate) \ |
103 | } |
104 | |
105 | static struct ieee80211_rate wcn_2ghz_rates[] = { |
106 | RATE(10, HW_RATE_INDEX_1MBPS, 0), |
107 | RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE), |
108 | RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE), |
109 | RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE), |
110 | RATE(60, HW_RATE_INDEX_6MBPS, 0), |
111 | RATE(90, HW_RATE_INDEX_9MBPS, 0), |
112 | RATE(120, HW_RATE_INDEX_12MBPS, 0), |
113 | RATE(180, HW_RATE_INDEX_18MBPS, 0), |
114 | RATE(240, HW_RATE_INDEX_24MBPS, 0), |
115 | RATE(360, HW_RATE_INDEX_36MBPS, 0), |
116 | RATE(480, HW_RATE_INDEX_48MBPS, 0), |
117 | RATE(540, HW_RATE_INDEX_54MBPS, 0) |
118 | }; |
119 | |
120 | static struct ieee80211_rate wcn_5ghz_rates[] = { |
121 | RATE(60, HW_RATE_INDEX_6MBPS, 0), |
122 | RATE(90, HW_RATE_INDEX_9MBPS, 0), |
123 | RATE(120, HW_RATE_INDEX_12MBPS, 0), |
124 | RATE(180, HW_RATE_INDEX_18MBPS, 0), |
125 | RATE(240, HW_RATE_INDEX_24MBPS, 0), |
126 | RATE(360, HW_RATE_INDEX_36MBPS, 0), |
127 | RATE(480, HW_RATE_INDEX_48MBPS, 0), |
128 | RATE(540, HW_RATE_INDEX_54MBPS, 0) |
129 | }; |
130 | |
131 | static struct ieee80211_supported_band wcn_band_2ghz = { |
132 | .channels = wcn_2ghz_channels, |
133 | .n_channels = ARRAY_SIZE(wcn_2ghz_channels), |
134 | .bitrates = wcn_2ghz_rates, |
135 | .n_bitrates = ARRAY_SIZE(wcn_2ghz_rates), |
136 | .ht_cap = { |
137 | .cap = IEEE80211_HT_CAP_GRN_FLD | |
138 | IEEE80211_HT_CAP_SGI_20 | |
139 | IEEE80211_HT_CAP_DSSSCCK40 | |
140 | IEEE80211_HT_CAP_LSIG_TXOP_PROT | |
141 | IEEE80211_HT_CAP_SGI_40 | |
142 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, |
143 | .ht_supported = true, |
144 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, |
145 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, |
146 | .mcs = { |
147 | .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, |
148 | .rx_highest = cpu_to_le16(72), |
149 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, |
150 | } |
151 | } |
152 | }; |
153 | |
154 | static struct ieee80211_supported_band wcn_band_5ghz = { |
155 | .channels = wcn_5ghz_channels, |
156 | .n_channels = ARRAY_SIZE(wcn_5ghz_channels), |
157 | .bitrates = wcn_5ghz_rates, |
158 | .n_bitrates = ARRAY_SIZE(wcn_5ghz_rates), |
159 | .ht_cap = { |
160 | .cap = IEEE80211_HT_CAP_GRN_FLD | |
161 | IEEE80211_HT_CAP_SGI_20 | |
162 | IEEE80211_HT_CAP_DSSSCCK40 | |
163 | IEEE80211_HT_CAP_LSIG_TXOP_PROT | |
164 | IEEE80211_HT_CAP_SGI_40 | |
165 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, |
166 | .ht_supported = true, |
167 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, |
168 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, |
169 | .mcs = { |
170 | .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, |
171 | .rx_highest = cpu_to_le16(150), |
172 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, |
173 | } |
174 | } |
175 | }; |
176 | |
177 | #ifdef CONFIG_PM |
178 | |
179 | static const struct wiphy_wowlan_support wowlan_support = { |
180 | .flags = WIPHY_WOWLAN_ANY | |
181 | WIPHY_WOWLAN_MAGIC_PKT | |
182 | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
183 | }; |
184 | |
185 | #endif |
186 | |
187 | static inline u8 get_sta_index(struct ieee80211_vif *vif, |
188 | struct wcn36xx_sta *sta_priv) |
189 | { |
190 | return NL80211_IFTYPE_STATION == vif->type ? |
191 | sta_priv->bss_sta_index : |
192 | sta_priv->sta_index; |
193 | } |
194 | |
195 | static void wcn36xx_feat_caps_info(struct wcn36xx *wcn) |
196 | { |
197 | int i; |
198 | |
199 | for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) { |
200 | if (wcn36xx_firmware_get_feat_caps(bitmap: wcn->fw_feat_caps, cap: i)) { |
201 | wcn36xx_dbg(WCN36XX_DBG_MAC, "FW Cap %s\n" , |
202 | wcn36xx_firmware_get_cap_name(i)); |
203 | } |
204 | } |
205 | } |
206 | |
207 | static int wcn36xx_start(struct ieee80211_hw *hw) |
208 | { |
209 | struct wcn36xx *wcn = hw->priv; |
210 | int ret; |
211 | |
212 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n" ); |
213 | |
214 | /* SMD initialization */ |
215 | ret = wcn36xx_smd_open(wcn); |
216 | if (ret) { |
217 | wcn36xx_err("Failed to open smd channel: %d\n" , ret); |
218 | goto out_err; |
219 | } |
220 | |
221 | /* Allocate memory pools for Mgmt BD headers and Data BD headers */ |
222 | ret = wcn36xx_dxe_allocate_mem_pools(wcn); |
223 | if (ret) { |
224 | wcn36xx_err("Failed to alloc DXE mempool: %d\n" , ret); |
225 | goto out_smd_close; |
226 | } |
227 | |
228 | ret = wcn36xx_dxe_alloc_ctl_blks(wcn); |
229 | if (ret) { |
230 | wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n" , ret); |
231 | goto out_free_dxe_pool; |
232 | } |
233 | |
234 | ret = wcn36xx_smd_load_nv(wcn); |
235 | if (ret) { |
236 | wcn36xx_err("Failed to push NV to chip\n" ); |
237 | goto out_free_dxe_ctl; |
238 | } |
239 | |
240 | ret = wcn36xx_smd_start(wcn); |
241 | if (ret) { |
242 | wcn36xx_err("Failed to start chip\n" ); |
243 | goto out_free_dxe_ctl; |
244 | } |
245 | |
246 | if (!wcn36xx_is_fw_version(wcn, major: 1, minor: 2, version: 2, revision: 24)) { |
247 | ret = wcn36xx_smd_feature_caps_exchange(wcn); |
248 | if (ret) |
249 | wcn36xx_warn("Exchange feature caps failed\n" ); |
250 | else |
251 | wcn36xx_feat_caps_info(wcn); |
252 | } |
253 | |
254 | /* DMA channel initialization */ |
255 | ret = wcn36xx_dxe_init(wcn); |
256 | if (ret) { |
257 | wcn36xx_err("DXE init failed\n" ); |
258 | goto out_smd_stop; |
259 | } |
260 | |
261 | wcn36xx_debugfs_init(wcn); |
262 | |
263 | INIT_LIST_HEAD(list: &wcn->vif_list); |
264 | spin_lock_init(&wcn->dxe_lock); |
265 | spin_lock_init(&wcn->survey_lock); |
266 | |
267 | return 0; |
268 | |
269 | out_smd_stop: |
270 | wcn36xx_smd_stop(wcn); |
271 | out_free_dxe_ctl: |
272 | wcn36xx_dxe_free_ctl_blks(wcn); |
273 | out_free_dxe_pool: |
274 | wcn36xx_dxe_free_mem_pools(wcn); |
275 | out_smd_close: |
276 | wcn36xx_smd_close(wcn); |
277 | out_err: |
278 | return ret; |
279 | } |
280 | |
281 | static void wcn36xx_stop(struct ieee80211_hw *hw) |
282 | { |
283 | struct wcn36xx *wcn = hw->priv; |
284 | |
285 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n" ); |
286 | |
287 | mutex_lock(&wcn->scan_lock); |
288 | if (wcn->scan_req) { |
289 | struct cfg80211_scan_info scan_info = { |
290 | .aborted = true, |
291 | }; |
292 | |
293 | ieee80211_scan_completed(hw: wcn->hw, info: &scan_info); |
294 | } |
295 | wcn->scan_req = NULL; |
296 | mutex_unlock(lock: &wcn->scan_lock); |
297 | |
298 | wcn36xx_debugfs_exit(wcn); |
299 | wcn36xx_smd_stop(wcn); |
300 | wcn36xx_dxe_deinit(wcn); |
301 | wcn36xx_smd_close(wcn); |
302 | |
303 | wcn36xx_dxe_free_mem_pools(wcn); |
304 | wcn36xx_dxe_free_ctl_blks(wcn); |
305 | } |
306 | |
307 | static void wcn36xx_change_ps(struct wcn36xx *wcn, bool enable) |
308 | { |
309 | struct ieee80211_vif *vif = NULL; |
310 | struct wcn36xx_vif *tmp; |
311 | |
312 | list_for_each_entry(tmp, &wcn->vif_list, list) { |
313 | vif = wcn36xx_priv_to_vif(vif_priv: tmp); |
314 | if (enable && !wcn->sw_scan) { |
315 | if (vif->cfg.ps) /* ps allowed ? */ |
316 | wcn36xx_pmc_enter_bmps_state(wcn, vif); |
317 | } else { |
318 | wcn36xx_pmc_exit_bmps_state(wcn, vif); |
319 | } |
320 | } |
321 | } |
322 | |
323 | static void wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch) |
324 | { |
325 | struct ieee80211_vif *vif = NULL; |
326 | struct wcn36xx_vif *tmp; |
327 | struct ieee80211_supported_band *band; |
328 | struct ieee80211_channel *channel = NULL; |
329 | unsigned long flags; |
330 | int i, j; |
331 | |
332 | for (i = 0; i < ARRAY_SIZE(wcn->hw->wiphy->bands); i++) { |
333 | band = wcn->hw->wiphy->bands[i]; |
334 | if (!band) |
335 | break; |
336 | for (j = 0; j < band->n_channels; j++) { |
337 | if (HW_VALUE_CHANNEL(band->channels[j].hw_value) == ch) { |
338 | channel = &band->channels[j]; |
339 | break; |
340 | } |
341 | } |
342 | if (channel) |
343 | break; |
344 | } |
345 | |
346 | if (!channel) { |
347 | wcn36xx_err("Cannot tune to channel %d\n" , ch); |
348 | return; |
349 | } |
350 | |
351 | spin_lock_irqsave(&wcn->survey_lock, flags); |
352 | wcn->band = band; |
353 | wcn->channel = channel; |
354 | spin_unlock_irqrestore(lock: &wcn->survey_lock, flags); |
355 | |
356 | list_for_each_entry(tmp, &wcn->vif_list, list) { |
357 | vif = wcn36xx_priv_to_vif(vif_priv: tmp); |
358 | wcn36xx_smd_switch_channel(wcn, vif, ch); |
359 | } |
360 | |
361 | return; |
362 | } |
363 | |
364 | static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) |
365 | { |
366 | struct wcn36xx *wcn = hw->priv; |
367 | int ret; |
368 | |
369 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n" , changed); |
370 | |
371 | mutex_lock(&wcn->conf_mutex); |
372 | |
373 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { |
374 | int ch = WCN36XX_HW_CHANNEL(wcn); |
375 | wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n" , |
376 | ch); |
377 | |
378 | if (wcn->sw_scan_opchannel == ch && wcn->sw_scan_channel) { |
379 | /* If channel is the initial operating channel, we may |
380 | * want to receive/transmit regular data packets, then |
381 | * simply stop the scan session and exit PS mode. |
382 | */ |
383 | if (wcn->sw_scan_channel) |
384 | wcn36xx_smd_end_scan(wcn, scan_channel: wcn->sw_scan_channel); |
385 | if (wcn->sw_scan_init) { |
386 | wcn36xx_smd_finish_scan(wcn, mode: HAL_SYS_MODE_SCAN, |
387 | vif: wcn->sw_scan_vif); |
388 | } |
389 | } else if (wcn->sw_scan) { |
390 | /* A scan is ongoing, do not change the operating |
391 | * channel, but start a scan session on the channel. |
392 | */ |
393 | if (wcn->sw_scan_channel) |
394 | wcn36xx_smd_end_scan(wcn, scan_channel: wcn->sw_scan_channel); |
395 | if (!wcn->sw_scan_init) { |
396 | /* This can fail if we are unable to notify the |
397 | * operating channel. |
398 | */ |
399 | ret = wcn36xx_smd_init_scan(wcn, |
400 | mode: HAL_SYS_MODE_SCAN, |
401 | vif: wcn->sw_scan_vif); |
402 | if (ret) { |
403 | mutex_unlock(lock: &wcn->conf_mutex); |
404 | return -EIO; |
405 | } |
406 | } |
407 | wcn36xx_smd_start_scan(wcn, scan_channel: ch); |
408 | } else { |
409 | wcn36xx_change_opchannel(wcn, ch); |
410 | } |
411 | } |
412 | |
413 | if (changed & IEEE80211_CONF_CHANGE_PS) |
414 | wcn36xx_change_ps(wcn, enable: hw->conf.flags & IEEE80211_CONF_PS); |
415 | |
416 | if (changed & IEEE80211_CONF_CHANGE_IDLE) { |
417 | if (hw->conf.flags & IEEE80211_CONF_IDLE) |
418 | wcn36xx_smd_enter_imps(wcn); |
419 | else |
420 | wcn36xx_smd_exit_imps(wcn); |
421 | } |
422 | |
423 | mutex_unlock(lock: &wcn->conf_mutex); |
424 | |
425 | return 0; |
426 | } |
427 | |
428 | static void wcn36xx_configure_filter(struct ieee80211_hw *hw, |
429 | unsigned int changed, |
430 | unsigned int *total, u64 multicast) |
431 | { |
432 | struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp; |
433 | struct wcn36xx *wcn = hw->priv; |
434 | struct wcn36xx_vif *tmp; |
435 | struct ieee80211_vif *vif = NULL; |
436 | |
437 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n" ); |
438 | |
439 | mutex_lock(&wcn->conf_mutex); |
440 | |
441 | *total &= FIF_ALLMULTI; |
442 | |
443 | fp = (void *)(unsigned long)multicast; |
444 | list_for_each_entry(tmp, &wcn->vif_list, list) { |
445 | vif = wcn36xx_priv_to_vif(vif_priv: tmp); |
446 | |
447 | /* FW handles MC filtering only when connected as STA */ |
448 | if (*total & FIF_ALLMULTI) |
449 | wcn36xx_smd_set_mc_list(wcn, vif, NULL); |
450 | else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc) |
451 | wcn36xx_smd_set_mc_list(wcn, vif, fp); |
452 | } |
453 | |
454 | mutex_unlock(lock: &wcn->conf_mutex); |
455 | kfree(objp: fp); |
456 | } |
457 | |
458 | static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw, |
459 | struct netdev_hw_addr_list *mc_list) |
460 | { |
461 | struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp; |
462 | struct netdev_hw_addr *ha; |
463 | |
464 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n" ); |
465 | fp = kzalloc(size: sizeof(*fp), GFP_ATOMIC); |
466 | if (!fp) { |
467 | wcn36xx_err("Out of memory setting filters.\n" ); |
468 | return 0; |
469 | } |
470 | |
471 | fp->mc_addr_count = 0; |
472 | /* update multicast filtering parameters */ |
473 | if (netdev_hw_addr_list_count(mc_list) <= |
474 | WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) { |
475 | netdev_hw_addr_list_for_each(ha, mc_list) { |
476 | memcpy(fp->mc_addr[fp->mc_addr_count], |
477 | ha->addr, ETH_ALEN); |
478 | fp->mc_addr_count++; |
479 | } |
480 | } |
481 | |
482 | return (u64)(unsigned long)fp; |
483 | } |
484 | |
485 | static void wcn36xx_tx(struct ieee80211_hw *hw, |
486 | struct ieee80211_tx_control *control, |
487 | struct sk_buff *skb) |
488 | { |
489 | struct wcn36xx *wcn = hw->priv; |
490 | struct wcn36xx_sta *sta_priv = NULL; |
491 | |
492 | if (control->sta) |
493 | sta_priv = wcn36xx_sta_to_priv(sta: control->sta); |
494 | |
495 | if (wcn36xx_start_tx(wcn, sta_priv, skb)) |
496 | ieee80211_free_txskb(hw: wcn->hw, skb); |
497 | } |
498 | |
499 | static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, |
500 | struct ieee80211_vif *vif, |
501 | struct ieee80211_sta *sta, |
502 | struct ieee80211_key_conf *key_conf) |
503 | { |
504 | struct wcn36xx *wcn = hw->priv; |
505 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
506 | struct wcn36xx_sta *sta_priv = sta ? wcn36xx_sta_to_priv(sta) : NULL; |
507 | int ret = 0; |
508 | u8 key[WLAN_MAX_KEY_LEN]; |
509 | |
510 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n" ); |
511 | wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n" , |
512 | cmd, key_conf->cipher, key_conf->keyidx, |
513 | key_conf->keylen, key_conf->flags); |
514 | wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: " , |
515 | key_conf->key, |
516 | key_conf->keylen); |
517 | |
518 | mutex_lock(&wcn->conf_mutex); |
519 | |
520 | switch (key_conf->cipher) { |
521 | case WLAN_CIPHER_SUITE_WEP40: |
522 | vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; |
523 | break; |
524 | case WLAN_CIPHER_SUITE_WEP104: |
525 | vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP104; |
526 | break; |
527 | case WLAN_CIPHER_SUITE_CCMP: |
528 | vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP; |
529 | break; |
530 | case WLAN_CIPHER_SUITE_TKIP: |
531 | vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP; |
532 | break; |
533 | default: |
534 | wcn36xx_err("Unsupported key type 0x%x\n" , |
535 | key_conf->cipher); |
536 | ret = -EOPNOTSUPP; |
537 | goto out; |
538 | } |
539 | |
540 | switch (cmd) { |
541 | case SET_KEY: |
542 | if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) { |
543 | /* |
544 | * Supplicant is sending key in the wrong order: |
545 | * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) |
546 | * but HW expects it to be in the order as described in |
547 | * IEEE 802.11 spec (see chapter 11.7) like this: |
548 | * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b) |
549 | */ |
550 | memcpy(key, key_conf->key, 16); |
551 | memcpy(key + 16, key_conf->key + 24, 8); |
552 | memcpy(key + 24, key_conf->key + 16, 8); |
553 | } else { |
554 | memcpy(key, key_conf->key, key_conf->keylen); |
555 | } |
556 | |
557 | if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) { |
558 | sta_priv->is_data_encrypted = true; |
559 | /* Reconfigure bss with encrypt_type */ |
560 | if (NL80211_IFTYPE_STATION == vif->type) { |
561 | wcn36xx_smd_config_bss(wcn, |
562 | vif, |
563 | sta, |
564 | bssid: sta->addr, |
565 | update: true); |
566 | wcn36xx_smd_config_sta(wcn, vif, sta); |
567 | } |
568 | |
569 | wcn36xx_smd_set_stakey(wcn, |
570 | enc_type: vif_priv->encrypt_type, |
571 | keyidx: key_conf->keyidx, |
572 | keylen: key_conf->keylen, |
573 | key, |
574 | sta_index: get_sta_index(vif, sta_priv)); |
575 | } else { |
576 | wcn36xx_smd_set_bsskey(wcn, |
577 | enc_type: vif_priv->encrypt_type, |
578 | bssidx: vif_priv->bss_index, |
579 | keyidx: key_conf->keyidx, |
580 | keylen: key_conf->keylen, |
581 | key); |
582 | |
583 | if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) || |
584 | (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) { |
585 | list_for_each_entry(sta_priv, |
586 | &vif_priv->sta_list, list) { |
587 | sta_priv->is_data_encrypted = true; |
588 | wcn36xx_smd_set_stakey(wcn, |
589 | enc_type: vif_priv->encrypt_type, |
590 | keyidx: key_conf->keyidx, |
591 | keylen: key_conf->keylen, |
592 | key, |
593 | sta_index: get_sta_index(vif, sta_priv)); |
594 | } |
595 | } |
596 | } |
597 | break; |
598 | case DISABLE_KEY: |
599 | if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { |
600 | if (vif_priv->bss_index != WCN36XX_HAL_BSS_INVALID_IDX) |
601 | wcn36xx_smd_remove_bsskey(wcn, |
602 | enc_type: vif_priv->encrypt_type, |
603 | bssidx: vif_priv->bss_index, |
604 | keyidx: key_conf->keyidx); |
605 | |
606 | vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE; |
607 | } else { |
608 | sta_priv->is_data_encrypted = false; |
609 | /* do not remove key if disassociated */ |
610 | if (sta_priv->aid) |
611 | wcn36xx_smd_remove_stakey(wcn, |
612 | enc_type: vif_priv->encrypt_type, |
613 | keyidx: key_conf->keyidx, |
614 | sta_index: get_sta_index(vif, sta_priv)); |
615 | } |
616 | break; |
617 | default: |
618 | wcn36xx_err("Unsupported key cmd 0x%x\n" , cmd); |
619 | ret = -EOPNOTSUPP; |
620 | goto out; |
621 | } |
622 | |
623 | out: |
624 | mutex_unlock(lock: &wcn->conf_mutex); |
625 | |
626 | return ret; |
627 | } |
628 | |
629 | static int wcn36xx_hw_scan(struct ieee80211_hw *hw, |
630 | struct ieee80211_vif *vif, |
631 | struct ieee80211_scan_request *hw_req) |
632 | { |
633 | struct wcn36xx *wcn = hw->priv; |
634 | |
635 | if (!wcn36xx_firmware_get_feat_caps(bitmap: wcn->fw_feat_caps, cap: SCAN_OFFLOAD)) { |
636 | /* fallback to mac80211 software scan */ |
637 | return 1; |
638 | } |
639 | |
640 | /* Firmware scan offload is limited to 48 channels, fallback to |
641 | * software driven scanning otherwise. |
642 | */ |
643 | if (hw_req->req.n_channels > 48) { |
644 | wcn36xx_warn("Offload scan aborted, n_channels=%u" , |
645 | hw_req->req.n_channels); |
646 | return 1; |
647 | } |
648 | |
649 | mutex_lock(&wcn->scan_lock); |
650 | if (wcn->scan_req) { |
651 | mutex_unlock(lock: &wcn->scan_lock); |
652 | return -EBUSY; |
653 | } |
654 | |
655 | wcn->scan_aborted = false; |
656 | wcn->scan_req = &hw_req->req; |
657 | |
658 | mutex_unlock(lock: &wcn->scan_lock); |
659 | |
660 | wcn36xx_smd_update_channel_list(wcn, req: &hw_req->req); |
661 | return wcn36xx_smd_start_hw_scan(wcn, vif, req: &hw_req->req); |
662 | } |
663 | |
664 | static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw, |
665 | struct ieee80211_vif *vif) |
666 | { |
667 | struct wcn36xx *wcn = hw->priv; |
668 | |
669 | mutex_lock(&wcn->scan_lock); |
670 | wcn->scan_aborted = true; |
671 | mutex_unlock(lock: &wcn->scan_lock); |
672 | |
673 | if (wcn36xx_firmware_get_feat_caps(bitmap: wcn->fw_feat_caps, cap: SCAN_OFFLOAD)) { |
674 | /* ieee80211_scan_completed will be called on FW scan |
675 | * indication */ |
676 | wcn36xx_smd_stop_hw_scan(wcn); |
677 | } |
678 | } |
679 | |
680 | static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw, |
681 | struct ieee80211_vif *vif, |
682 | const u8 *mac_addr) |
683 | { |
684 | struct wcn36xx *wcn = hw->priv; |
685 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
686 | |
687 | wcn36xx_dbg(WCN36XX_DBG_MAC, "sw_scan_start" ); |
688 | |
689 | wcn->sw_scan = true; |
690 | wcn->sw_scan_vif = vif; |
691 | wcn->sw_scan_channel = 0; |
692 | if (vif_priv->sta_assoc) |
693 | wcn->sw_scan_opchannel = WCN36XX_HW_CHANNEL(wcn); |
694 | else |
695 | wcn->sw_scan_opchannel = 0; |
696 | } |
697 | |
698 | static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw, |
699 | struct ieee80211_vif *vif) |
700 | { |
701 | struct wcn36xx *wcn = hw->priv; |
702 | |
703 | wcn36xx_dbg(WCN36XX_DBG_MAC, "sw_scan_complete" ); |
704 | |
705 | /* ensure that any scan session is finished */ |
706 | if (wcn->sw_scan_channel) |
707 | wcn36xx_smd_end_scan(wcn, scan_channel: wcn->sw_scan_channel); |
708 | if (wcn->sw_scan_init) { |
709 | wcn36xx_smd_finish_scan(wcn, mode: HAL_SYS_MODE_SCAN, |
710 | vif: wcn->sw_scan_vif); |
711 | } |
712 | wcn->sw_scan = false; |
713 | wcn->sw_scan_opchannel = 0; |
714 | } |
715 | |
716 | static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, |
717 | enum nl80211_band band) |
718 | { |
719 | int i, size; |
720 | u16 *rates_table; |
721 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); |
722 | u32 rates = sta->deflink.supp_rates[band]; |
723 | |
724 | memset(&sta_priv->supported_rates, 0, |
725 | sizeof(sta_priv->supported_rates)); |
726 | sta_priv->supported_rates.op_rate_mode = STA_11n; |
727 | |
728 | size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates); |
729 | rates_table = sta_priv->supported_rates.dsss_rates; |
730 | if (band == NL80211_BAND_2GHZ) { |
731 | for (i = 0; i < size; i++) { |
732 | if (rates & 0x01) { |
733 | rates_table[i] = wcn_2ghz_rates[i].hw_value; |
734 | rates = rates >> 1; |
735 | } |
736 | } |
737 | } |
738 | |
739 | size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates); |
740 | rates_table = sta_priv->supported_rates.ofdm_rates; |
741 | for (i = 0; i < size; i++) { |
742 | if (rates & 0x01) { |
743 | rates_table[i] = wcn_5ghz_rates[i].hw_value; |
744 | rates = rates >> 1; |
745 | } |
746 | } |
747 | |
748 | if (sta->deflink.ht_cap.ht_supported) { |
749 | BUILD_BUG_ON(sizeof(sta->deflink.ht_cap.mcs.rx_mask) > |
750 | sizeof(sta_priv->supported_rates.supported_mcs_set)); |
751 | memcpy(sta_priv->supported_rates.supported_mcs_set, |
752 | sta->deflink.ht_cap.mcs.rx_mask, |
753 | sizeof(sta->deflink.ht_cap.mcs.rx_mask)); |
754 | } |
755 | |
756 | if (sta->deflink.vht_cap.vht_supported) { |
757 | sta_priv->supported_rates.op_rate_mode = STA_11ac; |
758 | sta_priv->supported_rates.vht_rx_mcs_map = |
759 | sta->deflink.vht_cap.vht_mcs.rx_mcs_map; |
760 | sta_priv->supported_rates.vht_tx_mcs_map = |
761 | sta->deflink.vht_cap.vht_mcs.tx_mcs_map; |
762 | } |
763 | } |
764 | |
765 | void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) |
766 | { |
767 | u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = { |
768 | HW_RATE_INDEX_6MBPS, |
769 | HW_RATE_INDEX_9MBPS, |
770 | HW_RATE_INDEX_12MBPS, |
771 | HW_RATE_INDEX_18MBPS, |
772 | HW_RATE_INDEX_24MBPS, |
773 | HW_RATE_INDEX_36MBPS, |
774 | HW_RATE_INDEX_48MBPS, |
775 | HW_RATE_INDEX_54MBPS |
776 | }; |
777 | u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = { |
778 | HW_RATE_INDEX_1MBPS, |
779 | HW_RATE_INDEX_2MBPS, |
780 | HW_RATE_INDEX_5_5MBPS, |
781 | HW_RATE_INDEX_11MBPS |
782 | }; |
783 | |
784 | rates->op_rate_mode = STA_11n; |
785 | memcpy(rates->dsss_rates, dsss_rates, |
786 | sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES); |
787 | memcpy(rates->ofdm_rates, ofdm_rates, |
788 | sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES); |
789 | rates->supported_mcs_set[0] = 0xFF; |
790 | } |
791 | |
792 | void wcn36xx_set_default_rates_v1(struct wcn36xx_hal_supported_rates_v1 *rates) |
793 | { |
794 | rates->op_rate_mode = STA_11ac; |
795 | rates->vht_rx_mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9; |
796 | rates->vht_tx_mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9; |
797 | } |
798 | |
799 | static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, |
800 | struct ieee80211_vif *vif, |
801 | struct ieee80211_bss_conf *bss_conf, |
802 | u64 changed) |
803 | { |
804 | struct wcn36xx *wcn = hw->priv; |
805 | struct sk_buff *skb = NULL; |
806 | u16 tim_off, tim_len; |
807 | enum wcn36xx_hal_link_state link_state; |
808 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
809 | |
810 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%llx\n" , |
811 | vif, changed); |
812 | |
813 | mutex_lock(&wcn->conf_mutex); |
814 | |
815 | if (changed & BSS_CHANGED_BEACON_INFO) { |
816 | wcn36xx_dbg(WCN36XX_DBG_MAC, |
817 | "mac bss changed dtim period %d\n" , |
818 | bss_conf->dtim_period); |
819 | |
820 | vif_priv->dtim_period = bss_conf->dtim_period; |
821 | } |
822 | |
823 | if (changed & BSS_CHANGED_BSSID) { |
824 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n" , |
825 | bss_conf->bssid); |
826 | |
827 | if (!is_zero_ether_addr(addr: bss_conf->bssid)) { |
828 | vif_priv->is_joining = true; |
829 | vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX; |
830 | wcn36xx_smd_set_link_st(wcn, bssid: bss_conf->bssid, sta_mac: vif->addr, |
831 | state: WCN36XX_HAL_LINK_PREASSOC_STATE); |
832 | wcn36xx_smd_join(wcn, bssid: bss_conf->bssid, |
833 | vif: vif->addr, WCN36XX_HW_CHANNEL(wcn)); |
834 | wcn36xx_smd_config_bss(wcn, vif, NULL, |
835 | bssid: bss_conf->bssid, update: false); |
836 | } else { |
837 | vif_priv->is_joining = false; |
838 | wcn36xx_smd_delete_bss(wcn, vif); |
839 | wcn36xx_smd_set_link_st(wcn, bssid: bss_conf->bssid, sta_mac: vif->addr, |
840 | state: WCN36XX_HAL_LINK_IDLE_STATE); |
841 | vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE; |
842 | } |
843 | } |
844 | |
845 | if (changed & BSS_CHANGED_SSID) { |
846 | wcn36xx_dbg(WCN36XX_DBG_MAC, |
847 | "mac bss changed ssid\n" ); |
848 | wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid " , |
849 | vif->cfg.ssid, vif->cfg.ssid_len); |
850 | |
851 | vif_priv->ssid.length = vif->cfg.ssid_len; |
852 | memcpy(&vif_priv->ssid.ssid, |
853 | vif->cfg.ssid, |
854 | vif->cfg.ssid_len); |
855 | } |
856 | |
857 | if (changed & BSS_CHANGED_ASSOC) { |
858 | vif_priv->is_joining = false; |
859 | if (vif->cfg.assoc) { |
860 | struct ieee80211_sta *sta; |
861 | struct wcn36xx_sta *sta_priv; |
862 | |
863 | wcn36xx_dbg(WCN36XX_DBG_MAC, |
864 | "mac assoc bss %pM vif %pM AID=%d\n" , |
865 | bss_conf->bssid, |
866 | vif->addr, |
867 | vif->cfg.aid); |
868 | |
869 | vif_priv->sta_assoc = true; |
870 | |
871 | /* |
872 | * Holding conf_mutex ensures mutal exclusion with |
873 | * wcn36xx_sta_remove() and as such ensures that sta |
874 | * won't be freed while we're operating on it. As such |
875 | * we do not need to hold the rcu_read_lock(). |
876 | */ |
877 | sta = ieee80211_find_sta(vif, addr: bss_conf->bssid); |
878 | if (!sta) { |
879 | wcn36xx_err("sta %pM is not found\n" , |
880 | bss_conf->bssid); |
881 | goto out; |
882 | } |
883 | sta_priv = wcn36xx_sta_to_priv(sta); |
884 | |
885 | wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); |
886 | |
887 | wcn36xx_smd_set_link_st(wcn, bssid: bss_conf->bssid, |
888 | sta_mac: vif->addr, |
889 | state: WCN36XX_HAL_LINK_POSTASSOC_STATE); |
890 | wcn36xx_smd_config_bss(wcn, vif, sta, |
891 | bssid: bss_conf->bssid, |
892 | update: true); |
893 | sta_priv->aid = vif->cfg.aid; |
894 | /* |
895 | * config_sta must be called from because this is the |
896 | * place where AID is available. |
897 | */ |
898 | wcn36xx_smd_config_sta(wcn, vif, sta); |
899 | if (vif->type == NL80211_IFTYPE_STATION) |
900 | wcn36xx_smd_add_beacon_filter(wcn, vif); |
901 | wcn36xx_enable_keep_alive_null_packet(wcn, vif); |
902 | } else { |
903 | wcn36xx_dbg(WCN36XX_DBG_MAC, |
904 | "disassociated bss %pM vif %pM AID=%d\n" , |
905 | bss_conf->bssid, |
906 | vif->addr, |
907 | vif->cfg.aid); |
908 | vif_priv->sta_assoc = false; |
909 | wcn36xx_smd_set_link_st(wcn, |
910 | bssid: bss_conf->bssid, |
911 | sta_mac: vif->addr, |
912 | state: WCN36XX_HAL_LINK_IDLE_STATE); |
913 | } |
914 | } |
915 | |
916 | if (changed & BSS_CHANGED_AP_PROBE_RESP) { |
917 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n" ); |
918 | skb = ieee80211_proberesp_get(hw, vif); |
919 | if (!skb) { |
920 | wcn36xx_err("failed to alloc probereq skb\n" ); |
921 | goto out; |
922 | } |
923 | |
924 | wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb); |
925 | dev_kfree_skb(skb); |
926 | } |
927 | |
928 | if (changed & BSS_CHANGED_BEACON_ENABLED || |
929 | changed & BSS_CHANGED_BEACON) { |
930 | wcn36xx_dbg(WCN36XX_DBG_MAC, |
931 | "mac bss changed beacon enabled %d\n" , |
932 | bss_conf->enable_beacon); |
933 | |
934 | if (bss_conf->enable_beacon) { |
935 | vif_priv->dtim_period = bss_conf->dtim_period; |
936 | vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX; |
937 | wcn36xx_smd_config_bss(wcn, vif, NULL, |
938 | bssid: vif->addr, update: false); |
939 | skb = ieee80211_beacon_get_tim(hw, vif, tim_offset: &tim_off, |
940 | tim_length: &tim_len, link_id: 0); |
941 | if (!skb) { |
942 | wcn36xx_err("failed to alloc beacon skb\n" ); |
943 | goto out; |
944 | } |
945 | wcn36xx_smd_send_beacon(wcn, vif, skb_beacon: skb, tim_off, p2p_off: 0); |
946 | dev_kfree_skb(skb); |
947 | |
948 | if (vif->type == NL80211_IFTYPE_ADHOC || |
949 | vif->type == NL80211_IFTYPE_MESH_POINT) |
950 | link_state = WCN36XX_HAL_LINK_IBSS_STATE; |
951 | else |
952 | link_state = WCN36XX_HAL_LINK_AP_STATE; |
953 | |
954 | wcn36xx_smd_set_link_st(wcn, bssid: vif->addr, sta_mac: vif->addr, |
955 | state: link_state); |
956 | } else { |
957 | wcn36xx_smd_delete_bss(wcn, vif); |
958 | wcn36xx_smd_set_link_st(wcn, bssid: vif->addr, sta_mac: vif->addr, |
959 | state: WCN36XX_HAL_LINK_IDLE_STATE); |
960 | } |
961 | } |
962 | out: |
963 | |
964 | mutex_unlock(lock: &wcn->conf_mutex); |
965 | } |
966 | |
967 | /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */ |
968 | static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) |
969 | { |
970 | struct wcn36xx *wcn = hw->priv; |
971 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n" , value); |
972 | |
973 | mutex_lock(&wcn->conf_mutex); |
974 | wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value); |
975 | mutex_unlock(lock: &wcn->conf_mutex); |
976 | |
977 | return 0; |
978 | } |
979 | |
980 | static void wcn36xx_remove_interface(struct ieee80211_hw *hw, |
981 | struct ieee80211_vif *vif) |
982 | { |
983 | struct wcn36xx *wcn = hw->priv; |
984 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
985 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n" , vif); |
986 | |
987 | mutex_lock(&wcn->conf_mutex); |
988 | |
989 | list_del(entry: &vif_priv->list); |
990 | wcn36xx_smd_delete_sta_self(wcn, addr: vif->addr); |
991 | |
992 | mutex_unlock(lock: &wcn->conf_mutex); |
993 | } |
994 | |
995 | static int wcn36xx_add_interface(struct ieee80211_hw *hw, |
996 | struct ieee80211_vif *vif) |
997 | { |
998 | struct wcn36xx *wcn = hw->priv; |
999 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
1000 | |
1001 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n" , |
1002 | vif, vif->type); |
1003 | |
1004 | if (!(NL80211_IFTYPE_STATION == vif->type || |
1005 | NL80211_IFTYPE_AP == vif->type || |
1006 | NL80211_IFTYPE_ADHOC == vif->type || |
1007 | NL80211_IFTYPE_MESH_POINT == vif->type)) { |
1008 | wcn36xx_warn("Unsupported interface type requested: %d\n" , |
1009 | vif->type); |
1010 | return -EOPNOTSUPP; |
1011 | } |
1012 | |
1013 | mutex_lock(&wcn->conf_mutex); |
1014 | |
1015 | vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX; |
1016 | INIT_LIST_HEAD(list: &vif_priv->sta_list); |
1017 | list_add(new: &vif_priv->list, head: &wcn->vif_list); |
1018 | wcn36xx_smd_add_sta_self(wcn, vif); |
1019 | |
1020 | mutex_unlock(lock: &wcn->conf_mutex); |
1021 | |
1022 | return 0; |
1023 | } |
1024 | |
1025 | static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
1026 | struct ieee80211_sta *sta) |
1027 | { |
1028 | struct wcn36xx *wcn = hw->priv; |
1029 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
1030 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); |
1031 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n" , |
1032 | vif, sta->addr); |
1033 | |
1034 | mutex_lock(&wcn->conf_mutex); |
1035 | |
1036 | spin_lock_init(&sta_priv->ampdu_lock); |
1037 | sta_priv->vif = vif_priv; |
1038 | list_add(new: &sta_priv->list, head: &vif_priv->sta_list); |
1039 | |
1040 | /* |
1041 | * For STA mode HW will be configured on BSS_CHANGED_ASSOC because |
1042 | * at this stage AID is not available yet. |
1043 | */ |
1044 | if (NL80211_IFTYPE_STATION != vif->type) { |
1045 | wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); |
1046 | sta_priv->aid = sta->aid; |
1047 | wcn36xx_smd_config_sta(wcn, vif, sta); |
1048 | } |
1049 | |
1050 | mutex_unlock(lock: &wcn->conf_mutex); |
1051 | |
1052 | return 0; |
1053 | } |
1054 | |
1055 | static int wcn36xx_sta_remove(struct ieee80211_hw *hw, |
1056 | struct ieee80211_vif *vif, |
1057 | struct ieee80211_sta *sta) |
1058 | { |
1059 | struct wcn36xx *wcn = hw->priv; |
1060 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); |
1061 | |
1062 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n" , |
1063 | vif, sta->addr, sta_priv->sta_index); |
1064 | |
1065 | mutex_lock(&wcn->conf_mutex); |
1066 | |
1067 | list_del(entry: &sta_priv->list); |
1068 | wcn36xx_smd_delete_sta(wcn, sta_index: sta_priv->sta_index); |
1069 | sta_priv->vif = NULL; |
1070 | |
1071 | mutex_unlock(lock: &wcn->conf_mutex); |
1072 | |
1073 | return 0; |
1074 | } |
1075 | |
1076 | #ifdef CONFIG_PM |
1077 | |
1078 | static struct ieee80211_vif *wcn36xx_get_first_assoc_vif(struct wcn36xx *wcn) |
1079 | { |
1080 | struct wcn36xx_vif *vif_priv = NULL; |
1081 | struct ieee80211_vif *vif = NULL; |
1082 | |
1083 | list_for_each_entry(vif_priv, &wcn->vif_list, list) { |
1084 | if (vif_priv->sta_assoc) { |
1085 | vif = wcn36xx_priv_to_vif(vif_priv); |
1086 | break; |
1087 | } |
1088 | } |
1089 | return vif; |
1090 | } |
1091 | |
1092 | static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) |
1093 | { |
1094 | struct wcn36xx *wcn = hw->priv; |
1095 | struct ieee80211_vif *vif = NULL; |
1096 | int ret = 0; |
1097 | |
1098 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n" ); |
1099 | |
1100 | mutex_lock(&wcn->conf_mutex); |
1101 | |
1102 | vif = wcn36xx_get_first_assoc_vif(wcn); |
1103 | if (vif) { |
1104 | ret = wcn36xx_smd_arp_offload(wcn, vif, enable: true); |
1105 | if (ret) |
1106 | goto out; |
1107 | ret = wcn36xx_smd_ipv6_ns_offload(wcn, vif, enable: true); |
1108 | if (ret) |
1109 | goto out; |
1110 | ret = wcn36xx_smd_gtk_offload(wcn, vif, enable: true); |
1111 | if (ret) |
1112 | goto out; |
1113 | ret = wcn36xx_smd_set_power_params(wcn, ignore_dtim: true); |
1114 | if (ret) |
1115 | goto out; |
1116 | ret = wcn36xx_smd_wlan_host_suspend_ind(wcn); |
1117 | } |
1118 | |
1119 | /* Disable IRQ, we don't want to handle any packet before mac80211 is |
1120 | * resumed and ready to receive packets. |
1121 | */ |
1122 | disable_irq(irq: wcn->tx_irq); |
1123 | disable_irq(irq: wcn->rx_irq); |
1124 | |
1125 | out: |
1126 | mutex_unlock(lock: &wcn->conf_mutex); |
1127 | return ret; |
1128 | } |
1129 | |
1130 | static int wcn36xx_resume(struct ieee80211_hw *hw) |
1131 | { |
1132 | struct wcn36xx *wcn = hw->priv; |
1133 | struct ieee80211_vif *vif = NULL; |
1134 | |
1135 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n" ); |
1136 | |
1137 | mutex_lock(&wcn->conf_mutex); |
1138 | vif = wcn36xx_get_first_assoc_vif(wcn); |
1139 | if (vif) { |
1140 | wcn36xx_smd_host_resume(wcn); |
1141 | wcn36xx_smd_set_power_params(wcn, ignore_dtim: false); |
1142 | wcn36xx_smd_gtk_offload_get_info(wcn, vif); |
1143 | wcn36xx_smd_gtk_offload(wcn, vif, enable: false); |
1144 | wcn36xx_smd_ipv6_ns_offload(wcn, vif, enable: false); |
1145 | wcn36xx_smd_arp_offload(wcn, vif, enable: false); |
1146 | } |
1147 | |
1148 | enable_irq(irq: wcn->tx_irq); |
1149 | enable_irq(irq: wcn->rx_irq); |
1150 | |
1151 | mutex_unlock(lock: &wcn->conf_mutex); |
1152 | |
1153 | return 0; |
1154 | } |
1155 | |
1156 | static void wcn36xx_set_rekey_data(struct ieee80211_hw *hw, |
1157 | struct ieee80211_vif *vif, |
1158 | struct cfg80211_gtk_rekey_data *data) |
1159 | { |
1160 | struct wcn36xx *wcn = hw->priv; |
1161 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
1162 | |
1163 | mutex_lock(&wcn->conf_mutex); |
1164 | |
1165 | memcpy(vif_priv->rekey_data.kek, data->kek, NL80211_KEK_LEN); |
1166 | memcpy(vif_priv->rekey_data.kck, data->kck, NL80211_KCK_LEN); |
1167 | vif_priv->rekey_data.replay_ctr = |
1168 | cpu_to_le64(be64_to_cpup((__be64 *)data->replay_ctr)); |
1169 | vif_priv->rekey_data.valid = true; |
1170 | |
1171 | mutex_unlock(lock: &wcn->conf_mutex); |
1172 | } |
1173 | |
1174 | #endif |
1175 | |
1176 | static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, |
1177 | struct ieee80211_vif *vif, |
1178 | struct ieee80211_ampdu_params *params) |
1179 | { |
1180 | struct wcn36xx *wcn = hw->priv; |
1181 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta: params->sta); |
1182 | struct ieee80211_sta *sta = params->sta; |
1183 | enum ieee80211_ampdu_mlme_action action = params->action; |
1184 | u16 tid = params->tid; |
1185 | u16 *ssn = ¶ms->ssn; |
1186 | int ret = 0; |
1187 | int session; |
1188 | |
1189 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n" , |
1190 | action, tid); |
1191 | |
1192 | mutex_lock(&wcn->conf_mutex); |
1193 | |
1194 | switch (action) { |
1195 | case IEEE80211_AMPDU_RX_START: |
1196 | sta_priv->tid = tid; |
1197 | session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, direction: 0, |
1198 | sta_index: get_sta_index(vif, sta_priv)); |
1199 | if (session < 0) { |
1200 | ret = session; |
1201 | goto out; |
1202 | } |
1203 | wcn36xx_smd_add_ba(wcn, session_id: session); |
1204 | break; |
1205 | case IEEE80211_AMPDU_RX_STOP: |
1206 | wcn36xx_smd_del_ba(wcn, tid, direction: 0, sta_index: get_sta_index(vif, sta_priv)); |
1207 | break; |
1208 | case IEEE80211_AMPDU_TX_START: |
1209 | spin_lock_bh(lock: &sta_priv->ampdu_lock); |
1210 | sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START; |
1211 | spin_unlock_bh(lock: &sta_priv->ampdu_lock); |
1212 | |
1213 | /* Replace the mac80211 ssn with the firmware one */ |
1214 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu ssn = %u\n" , *ssn); |
1215 | wcn36xx_smd_trigger_ba(wcn, sta_index: get_sta_index(vif, sta_priv), tid, ssn); |
1216 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu fw-ssn = %u\n" , *ssn); |
1217 | |
1218 | /* Start BA session */ |
1219 | session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, direction: 1, |
1220 | sta_index: get_sta_index(vif, sta_priv)); |
1221 | if (session < 0) { |
1222 | ret = session; |
1223 | goto out; |
1224 | } |
1225 | ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; |
1226 | break; |
1227 | case IEEE80211_AMPDU_TX_OPERATIONAL: |
1228 | spin_lock_bh(lock: &sta_priv->ampdu_lock); |
1229 | sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL; |
1230 | spin_unlock_bh(lock: &sta_priv->ampdu_lock); |
1231 | |
1232 | break; |
1233 | case IEEE80211_AMPDU_TX_STOP_FLUSH: |
1234 | case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: |
1235 | case IEEE80211_AMPDU_TX_STOP_CONT: |
1236 | spin_lock_bh(lock: &sta_priv->ampdu_lock); |
1237 | sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE; |
1238 | spin_unlock_bh(lock: &sta_priv->ampdu_lock); |
1239 | |
1240 | wcn36xx_smd_del_ba(wcn, tid, direction: 1, sta_index: get_sta_index(vif, sta_priv)); |
1241 | ieee80211_stop_tx_ba_cb_irqsafe(vif, ra: sta->addr, tid); |
1242 | break; |
1243 | default: |
1244 | wcn36xx_err("Unknown AMPDU action\n" ); |
1245 | } |
1246 | |
1247 | out: |
1248 | mutex_unlock(lock: &wcn->conf_mutex); |
1249 | |
1250 | return ret; |
1251 | } |
1252 | |
1253 | #if IS_ENABLED(CONFIG_IPV6) |
1254 | static void wcn36xx_ipv6_addr_change(struct ieee80211_hw *hw, |
1255 | struct ieee80211_vif *vif, |
1256 | struct inet6_dev *idev) |
1257 | { |
1258 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
1259 | struct inet6_ifaddr *ifa; |
1260 | int idx = 0; |
1261 | |
1262 | memset(vif_priv->tentative_addrs, 0, sizeof(vif_priv->tentative_addrs)); |
1263 | |
1264 | read_lock_bh(&idev->lock); |
1265 | list_for_each_entry(ifa, &idev->addr_list, if_list) { |
1266 | vif_priv->target_ipv6_addrs[idx] = ifa->addr; |
1267 | if (ifa->flags & IFA_F_TENTATIVE) |
1268 | __set_bit(idx, vif_priv->tentative_addrs); |
1269 | idx++; |
1270 | if (idx >= WCN36XX_HAL_IPV6_OFFLOAD_ADDR_MAX) |
1271 | break; |
1272 | wcn36xx_dbg(WCN36XX_DBG_MAC, "%pI6 %s\n" , &ifa->addr, |
1273 | (ifa->flags & IFA_F_TENTATIVE) ? "tentative" : NULL); |
1274 | } |
1275 | read_unlock_bh(&idev->lock); |
1276 | |
1277 | vif_priv->num_target_ipv6_addrs = idx; |
1278 | } |
1279 | #endif |
1280 | |
1281 | static void wcn36xx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
1282 | u32 queues, bool drop) |
1283 | { |
1284 | struct wcn36xx *wcn = hw->priv; |
1285 | |
1286 | if (wcn36xx_dxe_tx_flush(wcn)) { |
1287 | wcn36xx_err("Failed to flush hardware tx queues\n" ); |
1288 | } |
1289 | } |
1290 | |
1291 | static int wcn36xx_get_survey(struct ieee80211_hw *hw, int idx, |
1292 | struct survey_info *survey) |
1293 | { |
1294 | struct wcn36xx *wcn = hw->priv; |
1295 | struct ieee80211_supported_band *sband; |
1296 | struct wcn36xx_chan_survey *chan_survey; |
1297 | int band_idx; |
1298 | unsigned long flags; |
1299 | |
1300 | sband = wcn->hw->wiphy->bands[NL80211_BAND_2GHZ]; |
1301 | band_idx = idx; |
1302 | if (band_idx >= sband->n_channels) { |
1303 | band_idx -= sband->n_channels; |
1304 | sband = wcn->hw->wiphy->bands[NL80211_BAND_5GHZ]; |
1305 | } |
1306 | |
1307 | if (!sband || band_idx >= sband->n_channels) |
1308 | return -ENOENT; |
1309 | |
1310 | spin_lock_irqsave(&wcn->survey_lock, flags); |
1311 | |
1312 | chan_survey = &wcn->chan_survey[idx]; |
1313 | survey->channel = &sband->channels[band_idx]; |
1314 | survey->noise = chan_survey->rssi - chan_survey->snr; |
1315 | survey->filled = 0; |
1316 | |
1317 | if (chan_survey->rssi > -100 && chan_survey->rssi < 0) |
1318 | survey->filled |= SURVEY_INFO_NOISE_DBM; |
1319 | |
1320 | if (survey->channel == wcn->channel) |
1321 | survey->filled |= SURVEY_INFO_IN_USE; |
1322 | |
1323 | spin_unlock_irqrestore(lock: &wcn->survey_lock, flags); |
1324 | |
1325 | wcn36xx_dbg(WCN36XX_DBG_MAC, |
1326 | "ch %d rssi %d snr %d noise %d filled %x freq %d\n" , |
1327 | HW_VALUE_CHANNEL(survey->channel->hw_value), |
1328 | chan_survey->rssi, chan_survey->snr, survey->noise, |
1329 | survey->filled, survey->channel->center_freq); |
1330 | |
1331 | return 0; |
1332 | } |
1333 | |
1334 | static void wcn36xx_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
1335 | struct ieee80211_sta *sta, struct station_info *sinfo) |
1336 | { |
1337 | struct wcn36xx *wcn; |
1338 | u8 sta_index; |
1339 | int status; |
1340 | |
1341 | wcn = hw->priv; |
1342 | sta_index = get_sta_index(vif, sta_priv: wcn36xx_sta_to_priv(sta)); |
1343 | status = wcn36xx_smd_get_stats(wcn, sta_index, stats_mask: HAL_GLOBAL_CLASS_A_STATS_INFO, sinfo); |
1344 | |
1345 | if (status) |
1346 | wcn36xx_err("wcn36xx_smd_get_stats failed\n" ); |
1347 | } |
1348 | |
1349 | static const struct ieee80211_ops wcn36xx_ops = { |
1350 | .add_chanctx = ieee80211_emulate_add_chanctx, |
1351 | .remove_chanctx = ieee80211_emulate_remove_chanctx, |
1352 | .change_chanctx = ieee80211_emulate_change_chanctx, |
1353 | .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, |
1354 | .start = wcn36xx_start, |
1355 | .stop = wcn36xx_stop, |
1356 | .add_interface = wcn36xx_add_interface, |
1357 | .remove_interface = wcn36xx_remove_interface, |
1358 | #ifdef CONFIG_PM |
1359 | .suspend = wcn36xx_suspend, |
1360 | .resume = wcn36xx_resume, |
1361 | .set_rekey_data = wcn36xx_set_rekey_data, |
1362 | #endif |
1363 | .config = wcn36xx_config, |
1364 | .prepare_multicast = wcn36xx_prepare_multicast, |
1365 | .configure_filter = wcn36xx_configure_filter, |
1366 | .tx = wcn36xx_tx, |
1367 | .wake_tx_queue = ieee80211_handle_wake_tx_queue, |
1368 | .set_key = wcn36xx_set_key, |
1369 | .hw_scan = wcn36xx_hw_scan, |
1370 | .cancel_hw_scan = wcn36xx_cancel_hw_scan, |
1371 | .sw_scan_start = wcn36xx_sw_scan_start, |
1372 | .sw_scan_complete = wcn36xx_sw_scan_complete, |
1373 | .bss_info_changed = wcn36xx_bss_info_changed, |
1374 | .set_rts_threshold = wcn36xx_set_rts_threshold, |
1375 | .sta_add = wcn36xx_sta_add, |
1376 | .sta_remove = wcn36xx_sta_remove, |
1377 | .sta_statistics = wcn36xx_sta_statistics, |
1378 | .ampdu_action = wcn36xx_ampdu_action, |
1379 | #if IS_ENABLED(CONFIG_IPV6) |
1380 | .ipv6_addr_change = wcn36xx_ipv6_addr_change, |
1381 | #endif |
1382 | .flush = wcn36xx_flush, |
1383 | .get_survey = wcn36xx_get_survey, |
1384 | |
1385 | CFG80211_TESTMODE_CMD(wcn36xx_tm_cmd) |
1386 | }; |
1387 | |
1388 | static void |
1389 | wcn36xx_set_ieee80211_vht_caps(struct ieee80211_sta_vht_cap *vht_cap) |
1390 | { |
1391 | vht_cap->vht_supported = true; |
1392 | |
1393 | vht_cap->cap = (IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 | |
1394 | IEEE80211_VHT_CAP_SHORT_GI_80 | |
1395 | IEEE80211_VHT_CAP_RXSTBC_1 | |
1396 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | |
1397 | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | |
1398 | 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | |
1399 | 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT); |
1400 | |
1401 | vht_cap->vht_mcs.rx_mcs_map = |
1402 | cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 | |
1403 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | |
1404 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | |
1405 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | |
1406 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | |
1407 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | |
1408 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | |
1409 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); |
1410 | |
1411 | vht_cap->vht_mcs.rx_highest = cpu_to_le16(433); |
1412 | vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest; |
1413 | |
1414 | vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; |
1415 | } |
1416 | |
1417 | static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) |
1418 | { |
1419 | static const u32 cipher_suites[] = { |
1420 | WLAN_CIPHER_SUITE_WEP40, |
1421 | WLAN_CIPHER_SUITE_WEP104, |
1422 | WLAN_CIPHER_SUITE_TKIP, |
1423 | WLAN_CIPHER_SUITE_CCMP, |
1424 | }; |
1425 | |
1426 | ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY); |
1427 | ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION); |
1428 | ieee80211_hw_set(wcn->hw, SUPPORTS_PS); |
1429 | ieee80211_hw_set(wcn->hw, SIGNAL_DBM); |
1430 | ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); |
1431 | ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS); |
1432 | ieee80211_hw_set(wcn->hw, REPORTS_TX_ACK_STATUS); |
1433 | |
1434 | wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | |
1435 | BIT(NL80211_IFTYPE_AP) | |
1436 | BIT(NL80211_IFTYPE_ADHOC) | |
1437 | BIT(NL80211_IFTYPE_MESH_POINT); |
1438 | |
1439 | wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz; |
1440 | if (wcn->rf_id != RF_IRIS_WCN3620) |
1441 | wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz; |
1442 | |
1443 | if (wcn->rf_id == RF_IRIS_WCN3680) |
1444 | wcn36xx_set_ieee80211_vht_caps(vht_cap: &wcn_band_5ghz.vht_cap); |
1445 | |
1446 | wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS; |
1447 | wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN; |
1448 | |
1449 | wcn->hw->wiphy->cipher_suites = cipher_suites; |
1450 | wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); |
1451 | |
1452 | #ifdef CONFIG_PM |
1453 | wcn->hw->wiphy->wowlan = &wowlan_support; |
1454 | #endif |
1455 | |
1456 | wcn->hw->max_listen_interval = 200; |
1457 | |
1458 | wcn->hw->queues = 4; |
1459 | |
1460 | SET_IEEE80211_DEV(hw: wcn->hw, dev: wcn->dev); |
1461 | |
1462 | wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); |
1463 | wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); |
1464 | |
1465 | wiphy_ext_feature_set(wiphy: wcn->hw->wiphy, |
1466 | ftidx: NL80211_EXT_FEATURE_CQM_RSSI_LIST); |
1467 | |
1468 | return 0; |
1469 | } |
1470 | |
1471 | static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, |
1472 | struct platform_device *pdev) |
1473 | { |
1474 | struct device_node *mmio_node; |
1475 | struct device_node *iris_node; |
1476 | int index; |
1477 | int ret; |
1478 | |
1479 | /* Set TX IRQ */ |
1480 | ret = platform_get_irq_byname(pdev, "tx" ); |
1481 | if (ret < 0) |
1482 | return ret; |
1483 | wcn->tx_irq = ret; |
1484 | |
1485 | /* Set RX IRQ */ |
1486 | ret = platform_get_irq_byname(pdev, "rx" ); |
1487 | if (ret < 0) |
1488 | return ret; |
1489 | wcn->rx_irq = ret; |
1490 | |
1491 | /* Acquire SMSM tx enable handle */ |
1492 | wcn->tx_enable_state = qcom_smem_state_get(dev: &pdev->dev, |
1493 | con_id: "tx-enable" , bit: &wcn->tx_enable_state_bit); |
1494 | if (IS_ERR(ptr: wcn->tx_enable_state)) { |
1495 | wcn36xx_err("failed to get tx-enable state\n" ); |
1496 | return PTR_ERR(ptr: wcn->tx_enable_state); |
1497 | } |
1498 | |
1499 | /* Acquire SMSM tx rings empty handle */ |
1500 | wcn->tx_rings_empty_state = qcom_smem_state_get(dev: &pdev->dev, |
1501 | con_id: "tx-rings-empty" , bit: &wcn->tx_rings_empty_state_bit); |
1502 | if (IS_ERR(ptr: wcn->tx_rings_empty_state)) { |
1503 | wcn36xx_err("failed to get tx-rings-empty state\n" ); |
1504 | return PTR_ERR(ptr: wcn->tx_rings_empty_state); |
1505 | } |
1506 | |
1507 | mmio_node = of_parse_phandle(np: pdev->dev.parent->of_node, phandle_name: "qcom,mmio" , index: 0); |
1508 | if (!mmio_node) { |
1509 | wcn36xx_err("failed to acquire qcom,mmio reference\n" ); |
1510 | return -EINVAL; |
1511 | } |
1512 | |
1513 | wcn->is_pronto = !!of_device_is_compatible(device: mmio_node, "qcom,pronto" ); |
1514 | wcn->is_pronto_v3 = !!of_device_is_compatible(device: mmio_node, "qcom,pronto-v3-pil" ); |
1515 | |
1516 | /* Map the CCU memory */ |
1517 | index = of_property_match_string(np: mmio_node, propname: "reg-names" , string: "ccu" ); |
1518 | wcn->ccu_base = of_iomap(node: mmio_node, index); |
1519 | if (!wcn->ccu_base) { |
1520 | wcn36xx_err("failed to map ccu memory\n" ); |
1521 | ret = -ENOMEM; |
1522 | goto put_mmio_node; |
1523 | } |
1524 | |
1525 | /* Map the DXE memory */ |
1526 | index = of_property_match_string(np: mmio_node, propname: "reg-names" , string: "dxe" ); |
1527 | wcn->dxe_base = of_iomap(node: mmio_node, index); |
1528 | if (!wcn->dxe_base) { |
1529 | wcn36xx_err("failed to map dxe memory\n" ); |
1530 | ret = -ENOMEM; |
1531 | goto unmap_ccu; |
1532 | } |
1533 | |
1534 | /* External RF module */ |
1535 | iris_node = of_get_child_by_name(node: mmio_node, name: "iris" ); |
1536 | if (iris_node) { |
1537 | if (of_device_is_compatible(device: iris_node, "qcom,wcn3620" )) |
1538 | wcn->rf_id = RF_IRIS_WCN3620; |
1539 | if (of_device_is_compatible(device: iris_node, "qcom,wcn3660" ) || |
1540 | of_device_is_compatible(device: iris_node, "qcom,wcn3660b" )) |
1541 | wcn->rf_id = RF_IRIS_WCN3660; |
1542 | if (of_device_is_compatible(device: iris_node, "qcom,wcn3680" )) |
1543 | wcn->rf_id = RF_IRIS_WCN3680; |
1544 | of_node_put(node: iris_node); |
1545 | } |
1546 | |
1547 | of_node_put(node: mmio_node); |
1548 | return 0; |
1549 | |
1550 | unmap_ccu: |
1551 | iounmap(addr: wcn->ccu_base); |
1552 | put_mmio_node: |
1553 | of_node_put(node: mmio_node); |
1554 | return ret; |
1555 | } |
1556 | |
1557 | static int wcn36xx_probe(struct platform_device *pdev) |
1558 | { |
1559 | struct ieee80211_hw *hw; |
1560 | struct wcn36xx *wcn; |
1561 | void *wcnss; |
1562 | int ret; |
1563 | const u8 *addr; |
1564 | int n_channels; |
1565 | |
1566 | wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n" ); |
1567 | |
1568 | wcnss = dev_get_drvdata(dev: pdev->dev.parent); |
1569 | |
1570 | hw = ieee80211_alloc_hw(priv_data_len: sizeof(struct wcn36xx), ops: &wcn36xx_ops); |
1571 | if (!hw) { |
1572 | wcn36xx_err("failed to alloc hw\n" ); |
1573 | ret = -ENOMEM; |
1574 | goto out_err; |
1575 | } |
1576 | platform_set_drvdata(pdev, data: hw); |
1577 | wcn = hw->priv; |
1578 | wcn->hw = hw; |
1579 | wcn->dev = &pdev->dev; |
1580 | wcn->first_boot = true; |
1581 | mutex_init(&wcn->conf_mutex); |
1582 | mutex_init(&wcn->hal_mutex); |
1583 | mutex_init(&wcn->scan_lock); |
1584 | __skb_queue_head_init(list: &wcn->amsdu); |
1585 | |
1586 | wcn->hal_buf = devm_kmalloc(dev: wcn->dev, WCN36XX_HAL_BUF_SIZE, GFP_KERNEL); |
1587 | if (!wcn->hal_buf) { |
1588 | ret = -ENOMEM; |
1589 | goto out_wq; |
1590 | } |
1591 | |
1592 | n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels; |
1593 | wcn->chan_survey = devm_kmalloc(dev: wcn->dev, size: n_channels, GFP_KERNEL); |
1594 | if (!wcn->chan_survey) { |
1595 | ret = -ENOMEM; |
1596 | goto out_wq; |
1597 | } |
1598 | |
1599 | ret = dma_set_mask_and_coherent(dev: wcn->dev, DMA_BIT_MASK(32)); |
1600 | if (ret < 0) { |
1601 | wcn36xx_err("failed to set DMA mask: %d\n" , ret); |
1602 | goto out_wq; |
1603 | } |
1604 | |
1605 | wcn->nv_file = WLAN_NV_FILE; |
1606 | ret = of_property_read_string(np: wcn->dev->parent->of_node, propname: "firmware-name" , out_string: &wcn->nv_file); |
1607 | if (ret < 0 && ret != -EINVAL) { |
1608 | wcn36xx_err("failed to read \"firmware-name\" property: %d\n" , ret); |
1609 | goto out_wq; |
1610 | } |
1611 | |
1612 | wcn->smd_channel = qcom_wcnss_open_channel(wcnss, name: "WLAN_CTRL" , cb: wcn36xx_smd_rsp_process, priv: hw); |
1613 | if (IS_ERR(ptr: wcn->smd_channel)) { |
1614 | wcn36xx_err("failed to open WLAN_CTRL channel\n" ); |
1615 | ret = PTR_ERR(ptr: wcn->smd_channel); |
1616 | goto out_wq; |
1617 | } |
1618 | |
1619 | addr = of_get_property(node: pdev->dev.of_node, name: "local-mac-address" , lenp: &ret); |
1620 | if (addr && ret != ETH_ALEN) { |
1621 | wcn36xx_err("invalid local-mac-address\n" ); |
1622 | ret = -EINVAL; |
1623 | goto out_destroy_ept; |
1624 | } else if (addr) { |
1625 | wcn36xx_info("mac address: %pM\n" , addr); |
1626 | SET_IEEE80211_PERM_ADDR(hw: wcn->hw, addr); |
1627 | } |
1628 | |
1629 | ret = wcn36xx_platform_get_resources(wcn, pdev); |
1630 | if (ret) |
1631 | goto out_destroy_ept; |
1632 | |
1633 | wcn36xx_init_ieee80211(wcn); |
1634 | ret = ieee80211_register_hw(hw: wcn->hw); |
1635 | if (ret) |
1636 | goto out_unmap; |
1637 | |
1638 | return 0; |
1639 | |
1640 | out_unmap: |
1641 | iounmap(addr: wcn->ccu_base); |
1642 | iounmap(addr: wcn->dxe_base); |
1643 | out_destroy_ept: |
1644 | rpmsg_destroy_ept(wcn->smd_channel); |
1645 | out_wq: |
1646 | ieee80211_free_hw(hw); |
1647 | out_err: |
1648 | return ret; |
1649 | } |
1650 | |
1651 | static void wcn36xx_remove(struct platform_device *pdev) |
1652 | { |
1653 | struct ieee80211_hw *hw = platform_get_drvdata(pdev); |
1654 | struct wcn36xx *wcn = hw->priv; |
1655 | wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n" ); |
1656 | |
1657 | release_firmware(fw: wcn->nv); |
1658 | |
1659 | ieee80211_unregister_hw(hw); |
1660 | |
1661 | qcom_smem_state_put(wcn->tx_enable_state); |
1662 | qcom_smem_state_put(wcn->tx_rings_empty_state); |
1663 | |
1664 | rpmsg_destroy_ept(wcn->smd_channel); |
1665 | |
1666 | iounmap(addr: wcn->dxe_base); |
1667 | iounmap(addr: wcn->ccu_base); |
1668 | |
1669 | __skb_queue_purge(list: &wcn->amsdu); |
1670 | |
1671 | mutex_destroy(lock: &wcn->hal_mutex); |
1672 | ieee80211_free_hw(hw); |
1673 | } |
1674 | |
1675 | static const struct of_device_id wcn36xx_of_match[] = { |
1676 | { .compatible = "qcom,wcnss-wlan" }, |
1677 | {} |
1678 | }; |
1679 | MODULE_DEVICE_TABLE(of, wcn36xx_of_match); |
1680 | |
1681 | static struct platform_driver wcn36xx_driver = { |
1682 | .probe = wcn36xx_probe, |
1683 | .remove_new = wcn36xx_remove, |
1684 | .driver = { |
1685 | .name = "wcn36xx" , |
1686 | .of_match_table = wcn36xx_of_match, |
1687 | }, |
1688 | }; |
1689 | |
1690 | module_platform_driver(wcn36xx_driver); |
1691 | |
1692 | MODULE_DESCRIPTION("Qualcomm Atheros WCN3660/3680 wireless driver" ); |
1693 | MODULE_LICENSE("Dual BSD/GPL" ); |
1694 | MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com" ); |
1695 | MODULE_FIRMWARE(WLAN_NV_FILE); |
1696 | |