]>
Commit | Line | Data |
---|---|---|
8e84c258 EK |
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> | |
4bda7faf | 20 | #include <linux/firmware.h> |
8e84c258 | 21 | #include <linux/platform_device.h> |
05ddce49 BA |
22 | #include <linux/of_address.h> |
23 | #include <linux/of_device.h> | |
f303a931 | 24 | #include <linux/of_irq.h> |
5052de8d | 25 | #include <linux/rpmsg.h> |
f303a931 BA |
26 | #include <linux/soc/qcom/smem_state.h> |
27 | #include <linux/soc/qcom/wcnss_ctrl.h> | |
8e84c258 EK |
28 | #include "wcn36xx.h" |
29 | ||
30 | unsigned int wcn36xx_dbg_mask; | |
31 | module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644); | |
32 | MODULE_PARM_DESC(debug_mask, "Debugging mask"); | |
33 | ||
34 | #define CHAN2G(_freq, _idx) { \ | |
57fbcce3 | 35 | .band = NL80211_BAND_2GHZ, \ |
8e84c258 EK |
36 | .center_freq = (_freq), \ |
37 | .hw_value = (_idx), \ | |
38 | .max_power = 25, \ | |
39 | } | |
40 | ||
41 | #define CHAN5G(_freq, _idx) { \ | |
57fbcce3 | 42 | .band = NL80211_BAND_5GHZ, \ |
8e84c258 EK |
43 | .center_freq = (_freq), \ |
44 | .hw_value = (_idx), \ | |
45 | .max_power = 25, \ | |
46 | } | |
47 | ||
48 | /* The wcn firmware expects channel values to matching | |
49 | * their mnemonic values. So use these for .hw_value. */ | |
50 | static struct ieee80211_channel wcn_2ghz_channels[] = { | |
51 | CHAN2G(2412, 1), /* Channel 1 */ | |
52 | CHAN2G(2417, 2), /* Channel 2 */ | |
53 | CHAN2G(2422, 3), /* Channel 3 */ | |
54 | CHAN2G(2427, 4), /* Channel 4 */ | |
55 | CHAN2G(2432, 5), /* Channel 5 */ | |
56 | CHAN2G(2437, 6), /* Channel 6 */ | |
57 | CHAN2G(2442, 7), /* Channel 7 */ | |
58 | CHAN2G(2447, 8), /* Channel 8 */ | |
59 | CHAN2G(2452, 9), /* Channel 9 */ | |
60 | CHAN2G(2457, 10), /* Channel 10 */ | |
61 | CHAN2G(2462, 11), /* Channel 11 */ | |
62 | CHAN2G(2467, 12), /* Channel 12 */ | |
63 | CHAN2G(2472, 13), /* Channel 13 */ | |
64 | CHAN2G(2484, 14) /* Channel 14 */ | |
65 | ||
66 | }; | |
67 | ||
68 | static struct ieee80211_channel wcn_5ghz_channels[] = { | |
69 | CHAN5G(5180, 36), | |
70 | CHAN5G(5200, 40), | |
71 | CHAN5G(5220, 44), | |
72 | CHAN5G(5240, 48), | |
73 | CHAN5G(5260, 52), | |
74 | CHAN5G(5280, 56), | |
75 | CHAN5G(5300, 60), | |
76 | CHAN5G(5320, 64), | |
77 | CHAN5G(5500, 100), | |
78 | CHAN5G(5520, 104), | |
79 | CHAN5G(5540, 108), | |
80 | CHAN5G(5560, 112), | |
81 | CHAN5G(5580, 116), | |
82 | CHAN5G(5600, 120), | |
83 | CHAN5G(5620, 124), | |
84 | CHAN5G(5640, 128), | |
85 | CHAN5G(5660, 132), | |
86 | CHAN5G(5700, 140), | |
87 | CHAN5G(5745, 149), | |
88 | CHAN5G(5765, 153), | |
89 | CHAN5G(5785, 157), | |
90 | CHAN5G(5805, 161), | |
91 | CHAN5G(5825, 165) | |
92 | }; | |
93 | ||
94 | #define RATE(_bitrate, _hw_rate, _flags) { \ | |
95 | .bitrate = (_bitrate), \ | |
96 | .flags = (_flags), \ | |
97 | .hw_value = (_hw_rate), \ | |
98 | .hw_value_short = (_hw_rate) \ | |
99 | } | |
100 | ||
101 | static struct ieee80211_rate wcn_2ghz_rates[] = { | |
102 | RATE(10, HW_RATE_INDEX_1MBPS, 0), | |
103 | RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE), | |
104 | RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE), | |
105 | RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE), | |
106 | RATE(60, HW_RATE_INDEX_6MBPS, 0), | |
107 | RATE(90, HW_RATE_INDEX_9MBPS, 0), | |
108 | RATE(120, HW_RATE_INDEX_12MBPS, 0), | |
109 | RATE(180, HW_RATE_INDEX_18MBPS, 0), | |
110 | RATE(240, HW_RATE_INDEX_24MBPS, 0), | |
111 | RATE(360, HW_RATE_INDEX_36MBPS, 0), | |
112 | RATE(480, HW_RATE_INDEX_48MBPS, 0), | |
113 | RATE(540, HW_RATE_INDEX_54MBPS, 0) | |
114 | }; | |
115 | ||
116 | static struct ieee80211_rate wcn_5ghz_rates[] = { | |
117 | RATE(60, HW_RATE_INDEX_6MBPS, 0), | |
118 | RATE(90, HW_RATE_INDEX_9MBPS, 0), | |
119 | RATE(120, HW_RATE_INDEX_12MBPS, 0), | |
120 | RATE(180, HW_RATE_INDEX_18MBPS, 0), | |
121 | RATE(240, HW_RATE_INDEX_24MBPS, 0), | |
122 | RATE(360, HW_RATE_INDEX_36MBPS, 0), | |
123 | RATE(480, HW_RATE_INDEX_48MBPS, 0), | |
124 | RATE(540, HW_RATE_INDEX_54MBPS, 0) | |
125 | }; | |
126 | ||
127 | static struct ieee80211_supported_band wcn_band_2ghz = { | |
128 | .channels = wcn_2ghz_channels, | |
129 | .n_channels = ARRAY_SIZE(wcn_2ghz_channels), | |
130 | .bitrates = wcn_2ghz_rates, | |
131 | .n_bitrates = ARRAY_SIZE(wcn_2ghz_rates), | |
132 | .ht_cap = { | |
133 | .cap = IEEE80211_HT_CAP_GRN_FLD | | |
134 | IEEE80211_HT_CAP_SGI_20 | | |
135 | IEEE80211_HT_CAP_DSSSCCK40 | | |
136 | IEEE80211_HT_CAP_LSIG_TXOP_PROT, | |
137 | .ht_supported = true, | |
138 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, | |
139 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, | |
140 | .mcs = { | |
141 | .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, | |
142 | .rx_highest = cpu_to_le16(72), | |
143 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, | |
144 | } | |
145 | } | |
146 | }; | |
147 | ||
148 | static struct ieee80211_supported_band wcn_band_5ghz = { | |
149 | .channels = wcn_5ghz_channels, | |
150 | .n_channels = ARRAY_SIZE(wcn_5ghz_channels), | |
151 | .bitrates = wcn_5ghz_rates, | |
152 | .n_bitrates = ARRAY_SIZE(wcn_5ghz_rates), | |
153 | .ht_cap = { | |
154 | .cap = IEEE80211_HT_CAP_GRN_FLD | | |
155 | IEEE80211_HT_CAP_SGI_20 | | |
156 | IEEE80211_HT_CAP_DSSSCCK40 | | |
157 | IEEE80211_HT_CAP_LSIG_TXOP_PROT | | |
158 | IEEE80211_HT_CAP_SGI_40 | | |
159 | IEEE80211_HT_CAP_SUP_WIDTH_20_40, | |
160 | .ht_supported = true, | |
161 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, | |
162 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, | |
163 | .mcs = { | |
164 | .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, | |
165 | .rx_highest = cpu_to_le16(72), | |
166 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, | |
167 | } | |
168 | } | |
169 | }; | |
170 | ||
171 | #ifdef CONFIG_PM | |
172 | ||
173 | static const struct wiphy_wowlan_support wowlan_support = { | |
174 | .flags = WIPHY_WOWLAN_ANY | |
175 | }; | |
176 | ||
177 | #endif | |
178 | ||
179 | static inline u8 get_sta_index(struct ieee80211_vif *vif, | |
180 | struct wcn36xx_sta *sta_priv) | |
181 | { | |
182 | return NL80211_IFTYPE_STATION == vif->type ? | |
183 | sta_priv->bss_sta_index : | |
184 | sta_priv->sta_index; | |
185 | } | |
186 | ||
2be6636a PF |
187 | static const char * const wcn36xx_caps_names[] = { |
188 | "MCC", /* 0 */ | |
189 | "P2P", /* 1 */ | |
190 | "DOT11AC", /* 2 */ | |
191 | "SLM_SESSIONIZATION", /* 3 */ | |
192 | "DOT11AC_OPMODE", /* 4 */ | |
193 | "SAP32STA", /* 5 */ | |
194 | "TDLS", /* 6 */ | |
195 | "P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */ | |
196 | "WLANACTIVE_OFFLOAD", /* 8 */ | |
197 | "BEACON_OFFLOAD", /* 9 */ | |
198 | "SCAN_OFFLOAD", /* 10 */ | |
199 | "ROAM_OFFLOAD", /* 11 */ | |
200 | "BCN_MISS_OFFLOAD", /* 12 */ | |
201 | "STA_POWERSAVE", /* 13 */ | |
202 | "STA_ADVANCED_PWRSAVE", /* 14 */ | |
203 | "AP_UAPSD", /* 15 */ | |
204 | "AP_DFS", /* 16 */ | |
205 | "BLOCKACK", /* 17 */ | |
206 | "PHY_ERR", /* 18 */ | |
207 | "BCN_FILTER", /* 19 */ | |
208 | "RTT", /* 20 */ | |
209 | "RATECTRL", /* 21 */ | |
ffc03c33 BA |
210 | "WOW", /* 22 */ |
211 | "WLAN_ROAM_SCAN_OFFLOAD", /* 23 */ | |
212 | "SPECULATIVE_PS_POLL", /* 24 */ | |
213 | "SCAN_SCH", /* 25 */ | |
214 | "IBSS_HEARTBEAT_OFFLOAD", /* 26 */ | |
215 | "WLAN_SCAN_OFFLOAD", /* 27 */ | |
216 | "WLAN_PERIODIC_TX_PTRN", /* 28 */ | |
217 | "ADVANCE_TDLS", /* 29 */ | |
218 | "BATCH_SCAN", /* 30 */ | |
219 | "FW_IN_TX_PATH", /* 31 */ | |
220 | "EXTENDED_NSOFFLOAD_SLOT", /* 32 */ | |
221 | "CH_SWITCH_V1", /* 33 */ | |
222 | "HT40_OBSS_SCAN", /* 34 */ | |
223 | "UPDATE_CHANNEL_LIST", /* 35 */ | |
224 | "WLAN_MCADDR_FLT", /* 36 */ | |
225 | "WLAN_CH144", /* 37 */ | |
226 | "NAN", /* 38 */ | |
227 | "TDLS_SCAN_COEXISTENCE", /* 39 */ | |
228 | "LINK_LAYER_STATS_MEAS", /* 40 */ | |
229 | "MU_MIMO", /* 41 */ | |
230 | "EXTENDED_SCAN", /* 42 */ | |
231 | "DYNAMIC_WMM_PS", /* 43 */ | |
232 | "MAC_SPOOFED_SCAN", /* 44 */ | |
233 | "BMU_ERROR_GENERIC_RECOVERY", /* 45 */ | |
234 | "DISA", /* 46 */ | |
235 | "FW_STATS", /* 47 */ | |
236 | "WPS_PRBRSP_TMPL", /* 48 */ | |
237 | "BCN_IE_FLT_DELTA", /* 49 */ | |
238 | "TDLS_OFF_CHANNEL", /* 51 */ | |
239 | "RTT3", /* 52 */ | |
240 | "MGMT_FRAME_LOGGING", /* 53 */ | |
241 | "ENHANCED_TXBD_COMPLETION", /* 54 */ | |
242 | "LOGGING_ENHANCEMENT", /* 55 */ | |
243 | "EXT_SCAN_ENHANCED", /* 56 */ | |
244 | "MEMORY_DUMP_SUPPORTED", /* 57 */ | |
245 | "PER_PKT_STATS_SUPPORTED", /* 58 */ | |
246 | "EXT_LL_STAT", /* 60 */ | |
247 | "WIFI_CONFIG", /* 61 */ | |
248 | "ANTENNA_DIVERSITY_SELECTION", /* 62 */ | |
2be6636a PF |
249 | }; |
250 | ||
251 | static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x) | |
252 | { | |
253 | if (x >= ARRAY_SIZE(wcn36xx_caps_names)) | |
254 | return "UNKNOWN"; | |
255 | return wcn36xx_caps_names[x]; | |
256 | } | |
257 | ||
258 | static void wcn36xx_feat_caps_info(struct wcn36xx *wcn) | |
259 | { | |
260 | int i; | |
261 | ||
262 | for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) { | |
263 | if (get_feat_caps(wcn->fw_feat_caps, i)) | |
264 | wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i)); | |
265 | } | |
266 | } | |
267 | ||
8e84c258 EK |
268 | static int wcn36xx_start(struct ieee80211_hw *hw) |
269 | { | |
270 | struct wcn36xx *wcn = hw->priv; | |
271 | int ret; | |
272 | ||
273 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n"); | |
274 | ||
275 | /* SMD initialization */ | |
276 | ret = wcn36xx_smd_open(wcn); | |
277 | if (ret) { | |
278 | wcn36xx_err("Failed to open smd channel: %d\n", ret); | |
279 | goto out_err; | |
280 | } | |
281 | ||
282 | /* Allocate memory pools for Mgmt BD headers and Data BD headers */ | |
283 | ret = wcn36xx_dxe_allocate_mem_pools(wcn); | |
284 | if (ret) { | |
285 | wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret); | |
286 | goto out_smd_close; | |
287 | } | |
288 | ||
289 | ret = wcn36xx_dxe_alloc_ctl_blks(wcn); | |
290 | if (ret) { | |
291 | wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret); | |
292 | goto out_free_dxe_pool; | |
293 | } | |
294 | ||
295 | wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL); | |
296 | if (!wcn->hal_buf) { | |
297 | wcn36xx_err("Failed to allocate smd buf\n"); | |
298 | ret = -ENOMEM; | |
299 | goto out_free_dxe_ctl; | |
300 | } | |
301 | ||
302 | ret = wcn36xx_smd_load_nv(wcn); | |
303 | if (ret) { | |
304 | wcn36xx_err("Failed to push NV to chip\n"); | |
305 | goto out_free_smd_buf; | |
306 | } | |
307 | ||
308 | ret = wcn36xx_smd_start(wcn); | |
309 | if (ret) { | |
310 | wcn36xx_err("Failed to start chip\n"); | |
311 | goto out_free_smd_buf; | |
312 | } | |
313 | ||
f2ed5d24 PF |
314 | if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { |
315 | ret = wcn36xx_smd_feature_caps_exchange(wcn); | |
316 | if (ret) | |
317 | wcn36xx_warn("Exchange feature caps failed\n"); | |
318 | else | |
319 | wcn36xx_feat_caps_info(wcn); | |
320 | } | |
321 | ||
8e84c258 EK |
322 | /* DMA channel initialization */ |
323 | ret = wcn36xx_dxe_init(wcn); | |
324 | if (ret) { | |
325 | wcn36xx_err("DXE init failed\n"); | |
326 | goto out_smd_stop; | |
327 | } | |
328 | ||
329 | wcn36xx_debugfs_init(wcn); | |
330 | ||
8e84c258 | 331 | INIT_LIST_HEAD(&wcn->vif_list); |
4b12462a BC |
332 | spin_lock_init(&wcn->dxe_lock); |
333 | ||
8e84c258 EK |
334 | return 0; |
335 | ||
336 | out_smd_stop: | |
337 | wcn36xx_smd_stop(wcn); | |
338 | out_free_smd_buf: | |
339 | kfree(wcn->hal_buf); | |
8e84c258 EK |
340 | out_free_dxe_ctl: |
341 | wcn36xx_dxe_free_ctl_blks(wcn); | |
4aa2d31f CJ |
342 | out_free_dxe_pool: |
343 | wcn36xx_dxe_free_mem_pools(wcn); | |
8e84c258 EK |
344 | out_smd_close: |
345 | wcn36xx_smd_close(wcn); | |
346 | out_err: | |
347 | return ret; | |
348 | } | |
349 | ||
350 | static void wcn36xx_stop(struct ieee80211_hw *hw) | |
351 | { | |
352 | struct wcn36xx *wcn = hw->priv; | |
353 | ||
354 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n"); | |
355 | ||
356 | wcn36xx_debugfs_exit(wcn); | |
357 | wcn36xx_smd_stop(wcn); | |
358 | wcn36xx_dxe_deinit(wcn); | |
359 | wcn36xx_smd_close(wcn); | |
360 | ||
361 | wcn36xx_dxe_free_mem_pools(wcn); | |
362 | wcn36xx_dxe_free_ctl_blks(wcn); | |
363 | ||
364 | kfree(wcn->hal_buf); | |
365 | } | |
366 | ||
367 | static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) | |
368 | { | |
369 | struct wcn36xx *wcn = hw->priv; | |
370 | struct ieee80211_vif *vif = NULL; | |
371 | struct wcn36xx_vif *tmp; | |
372 | ||
373 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); | |
374 | ||
375 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { | |
376 | int ch = WCN36XX_HW_CHANNEL(wcn); | |
377 | wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", | |
378 | ch); | |
379 | list_for_each_entry(tmp, &wcn->vif_list, list) { | |
ce75877f | 380 | vif = wcn36xx_priv_to_vif(tmp); |
8e84c258 EK |
381 | wcn36xx_smd_switch_channel(wcn, vif, ch); |
382 | } | |
383 | } | |
384 | ||
385 | return 0; | |
386 | } | |
387 | ||
8e84c258 EK |
388 | static void wcn36xx_configure_filter(struct ieee80211_hw *hw, |
389 | unsigned int changed, | |
390 | unsigned int *total, u64 multicast) | |
391 | { | |
20a779ed PF |
392 | struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp; |
393 | struct wcn36xx *wcn = hw->priv; | |
394 | struct wcn36xx_vif *tmp; | |
395 | struct ieee80211_vif *vif = NULL; | |
396 | ||
8e84c258 EK |
397 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n"); |
398 | ||
20a779ed PF |
399 | *total &= FIF_ALLMULTI; |
400 | ||
401 | fp = (void *)(unsigned long)multicast; | |
402 | list_for_each_entry(tmp, &wcn->vif_list, list) { | |
403 | vif = wcn36xx_priv_to_vif(tmp); | |
404 | ||
405 | /* FW handles MC filtering only when connected as STA */ | |
406 | if (*total & FIF_ALLMULTI) | |
407 | wcn36xx_smd_set_mc_list(wcn, vif, NULL); | |
408 | else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc) | |
409 | wcn36xx_smd_set_mc_list(wcn, vif, fp); | |
410 | } | |
411 | kfree(fp); | |
412 | } | |
413 | ||
414 | static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw, | |
415 | struct netdev_hw_addr_list *mc_list) | |
416 | { | |
417 | struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp; | |
418 | struct netdev_hw_addr *ha; | |
419 | ||
420 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n"); | |
421 | fp = kzalloc(sizeof(*fp), GFP_ATOMIC); | |
422 | if (!fp) { | |
423 | wcn36xx_err("Out of memory setting filters.\n"); | |
424 | return 0; | |
425 | } | |
426 | ||
427 | fp->mc_addr_count = 0; | |
428 | /* update multicast filtering parameters */ | |
429 | if (netdev_hw_addr_list_count(mc_list) <= | |
430 | WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) { | |
431 | netdev_hw_addr_list_for_each(ha, mc_list) { | |
432 | memcpy(fp->mc_addr[fp->mc_addr_count], | |
433 | ha->addr, ETH_ALEN); | |
434 | fp->mc_addr_count++; | |
435 | } | |
436 | } | |
437 | ||
438 | return (u64)(unsigned long)fp; | |
8e84c258 EK |
439 | } |
440 | ||
441 | static void wcn36xx_tx(struct ieee80211_hw *hw, | |
442 | struct ieee80211_tx_control *control, | |
443 | struct sk_buff *skb) | |
444 | { | |
445 | struct wcn36xx *wcn = hw->priv; | |
446 | struct wcn36xx_sta *sta_priv = NULL; | |
447 | ||
448 | if (control->sta) | |
a92e4696 | 449 | sta_priv = wcn36xx_sta_to_priv(control->sta); |
8e84c258 EK |
450 | |
451 | if (wcn36xx_start_tx(wcn, sta_priv, skb)) | |
452 | ieee80211_free_txskb(wcn->hw, skb); | |
453 | } | |
454 | ||
455 | static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, | |
456 | struct ieee80211_vif *vif, | |
457 | struct ieee80211_sta *sta, | |
458 | struct ieee80211_key_conf *key_conf) | |
459 | { | |
460 | struct wcn36xx *wcn = hw->priv; | |
ce75877f | 461 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
81c69263 | 462 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); |
8e84c258 EK |
463 | int ret = 0; |
464 | u8 key[WLAN_MAX_KEY_LEN]; | |
465 | ||
466 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n"); | |
467 | wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n", | |
468 | cmd, key_conf->cipher, key_conf->keyidx, | |
469 | key_conf->keylen, key_conf->flags); | |
470 | wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ", | |
471 | key_conf->key, | |
472 | key_conf->keylen); | |
473 | ||
474 | switch (key_conf->cipher) { | |
475 | case WLAN_CIPHER_SUITE_WEP40: | |
476 | vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; | |
477 | break; | |
478 | case WLAN_CIPHER_SUITE_WEP104: | |
479 | vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; | |
480 | break; | |
481 | case WLAN_CIPHER_SUITE_CCMP: | |
482 | vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP; | |
483 | break; | |
484 | case WLAN_CIPHER_SUITE_TKIP: | |
485 | vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP; | |
486 | break; | |
487 | default: | |
488 | wcn36xx_err("Unsupported key type 0x%x\n", | |
489 | key_conf->cipher); | |
490 | ret = -EOPNOTSUPP; | |
491 | goto out; | |
492 | } | |
493 | ||
494 | switch (cmd) { | |
495 | case SET_KEY: | |
496 | if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) { | |
497 | /* | |
498 | * Supplicant is sending key in the wrong order: | |
499 | * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) | |
500 | * but HW expects it to be in the order as described in | |
501 | * IEEE 802.11 spec (see chapter 11.7) like this: | |
502 | * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b) | |
503 | */ | |
504 | memcpy(key, key_conf->key, 16); | |
505 | memcpy(key + 16, key_conf->key + 24, 8); | |
506 | memcpy(key + 24, key_conf->key + 16, 8); | |
507 | } else { | |
508 | memcpy(key, key_conf->key, key_conf->keylen); | |
509 | } | |
510 | ||
511 | if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) { | |
512 | sta_priv->is_data_encrypted = true; | |
513 | /* Reconfigure bss with encrypt_type */ | |
514 | if (NL80211_IFTYPE_STATION == vif->type) | |
515 | wcn36xx_smd_config_bss(wcn, | |
516 | vif, | |
517 | sta, | |
518 | sta->addr, | |
519 | true); | |
520 | ||
521 | wcn36xx_smd_set_stakey(wcn, | |
522 | vif_priv->encrypt_type, | |
523 | key_conf->keyidx, | |
524 | key_conf->keylen, | |
525 | key, | |
526 | get_sta_index(vif, sta_priv)); | |
527 | } else { | |
528 | wcn36xx_smd_set_bsskey(wcn, | |
529 | vif_priv->encrypt_type, | |
530 | key_conf->keyidx, | |
531 | key_conf->keylen, | |
532 | key); | |
533 | if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) || | |
534 | (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) { | |
535 | sta_priv->is_data_encrypted = true; | |
536 | wcn36xx_smd_set_stakey(wcn, | |
537 | vif_priv->encrypt_type, | |
538 | key_conf->keyidx, | |
539 | key_conf->keylen, | |
540 | key, | |
541 | get_sta_index(vif, sta_priv)); | |
542 | } | |
543 | } | |
544 | break; | |
545 | case DISABLE_KEY: | |
546 | if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { | |
2716a8ac | 547 | vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE; |
8e84c258 EK |
548 | wcn36xx_smd_remove_bsskey(wcn, |
549 | vif_priv->encrypt_type, | |
550 | key_conf->keyidx); | |
551 | } else { | |
552 | sta_priv->is_data_encrypted = false; | |
553 | /* do not remove key if disassociated */ | |
554 | if (sta_priv->aid) | |
555 | wcn36xx_smd_remove_stakey(wcn, | |
556 | vif_priv->encrypt_type, | |
557 | key_conf->keyidx, | |
558 | get_sta_index(vif, sta_priv)); | |
559 | } | |
560 | break; | |
561 | default: | |
562 | wcn36xx_err("Unsupported key cmd 0x%x\n", cmd); | |
563 | ret = -EOPNOTSUPP; | |
564 | goto out; | |
8e84c258 EK |
565 | } |
566 | ||
567 | out: | |
568 | return ret; | |
569 | } | |
570 | ||
88603903 | 571 | static void wcn36xx_hw_scan_worker(struct work_struct *work) |
8e84c258 | 572 | { |
88603903 BA |
573 | struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work); |
574 | struct cfg80211_scan_request *req = wcn->scan_req; | |
575 | u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX]; | |
576 | struct cfg80211_scan_info scan_info = {}; | |
03c95dbe | 577 | bool aborted = false; |
88603903 BA |
578 | int i; |
579 | ||
580 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels); | |
581 | ||
582 | for (i = 0; i < req->n_channels; i++) | |
583 | channels[i] = req->channels[i]->hw_value; | |
584 | ||
585 | wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels); | |
8e84c258 EK |
586 | |
587 | wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); | |
88603903 | 588 | for (i = 0; i < req->n_channels; i++) { |
03c95dbe BA |
589 | mutex_lock(&wcn->scan_lock); |
590 | aborted = wcn->scan_aborted; | |
591 | mutex_unlock(&wcn->scan_lock); | |
592 | ||
593 | if (aborted) | |
594 | break; | |
595 | ||
88603903 BA |
596 | wcn->scan_freq = req->channels[i]->center_freq; |
597 | wcn->scan_band = req->channels[i]->band; | |
598 | ||
599 | wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value); | |
600 | msleep(30); | |
601 | wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value); | |
602 | ||
603 | wcn->scan_freq = 0; | |
604 | } | |
605 | wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); | |
606 | ||
03c95dbe | 607 | scan_info.aborted = aborted; |
88603903 BA |
608 | ieee80211_scan_completed(wcn->hw, &scan_info); |
609 | ||
610 | mutex_lock(&wcn->scan_lock); | |
611 | wcn->scan_req = NULL; | |
612 | mutex_unlock(&wcn->scan_lock); | |
8e84c258 EK |
613 | } |
614 | ||
88603903 BA |
615 | static int wcn36xx_hw_scan(struct ieee80211_hw *hw, |
616 | struct ieee80211_vif *vif, | |
617 | struct ieee80211_scan_request *hw_req) | |
8e84c258 EK |
618 | { |
619 | struct wcn36xx *wcn = hw->priv; | |
620 | ||
88603903 BA |
621 | mutex_lock(&wcn->scan_lock); |
622 | if (wcn->scan_req) { | |
623 | mutex_unlock(&wcn->scan_lock); | |
624 | return -EBUSY; | |
625 | } | |
03c95dbe BA |
626 | |
627 | wcn->scan_aborted = false; | |
88603903 BA |
628 | wcn->scan_req = &hw_req->req; |
629 | mutex_unlock(&wcn->scan_lock); | |
630 | ||
631 | schedule_work(&wcn->scan_work); | |
632 | ||
633 | return 0; | |
8e84c258 EK |
634 | } |
635 | ||
03c95dbe BA |
636 | static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw, |
637 | struct ieee80211_vif *vif) | |
638 | { | |
639 | struct wcn36xx *wcn = hw->priv; | |
640 | ||
641 | mutex_lock(&wcn->scan_lock); | |
642 | wcn->scan_aborted = true; | |
643 | mutex_unlock(&wcn->scan_lock); | |
644 | ||
645 | cancel_work_sync(&wcn->scan_work); | |
646 | } | |
647 | ||
8e84c258 | 648 | static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, |
57fbcce3 | 649 | enum nl80211_band band) |
8e84c258 EK |
650 | { |
651 | int i, size; | |
652 | u16 *rates_table; | |
a92e4696 | 653 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); |
8e84c258 EK |
654 | u32 rates = sta->supp_rates[band]; |
655 | ||
656 | memset(&sta_priv->supported_rates, 0, | |
657 | sizeof(sta_priv->supported_rates)); | |
658 | sta_priv->supported_rates.op_rate_mode = STA_11n; | |
659 | ||
660 | size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates); | |
661 | rates_table = sta_priv->supported_rates.dsss_rates; | |
57fbcce3 | 662 | if (band == NL80211_BAND_2GHZ) { |
8e84c258 EK |
663 | for (i = 0; i < size; i++) { |
664 | if (rates & 0x01) { | |
665 | rates_table[i] = wcn_2ghz_rates[i].hw_value; | |
666 | rates = rates >> 1; | |
667 | } | |
668 | } | |
669 | } | |
670 | ||
671 | size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates); | |
672 | rates_table = sta_priv->supported_rates.ofdm_rates; | |
673 | for (i = 0; i < size; i++) { | |
674 | if (rates & 0x01) { | |
675 | rates_table[i] = wcn_5ghz_rates[i].hw_value; | |
676 | rates = rates >> 1; | |
677 | } | |
678 | } | |
679 | ||
680 | if (sta->ht_cap.ht_supported) { | |
681 | BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) > | |
682 | sizeof(sta_priv->supported_rates.supported_mcs_set)); | |
683 | memcpy(sta_priv->supported_rates.supported_mcs_set, | |
684 | sta->ht_cap.mcs.rx_mask, | |
685 | sizeof(sta->ht_cap.mcs.rx_mask)); | |
686 | } | |
687 | } | |
688 | void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) | |
689 | { | |
690 | u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = { | |
691 | HW_RATE_INDEX_6MBPS, | |
692 | HW_RATE_INDEX_9MBPS, | |
693 | HW_RATE_INDEX_12MBPS, | |
694 | HW_RATE_INDEX_18MBPS, | |
695 | HW_RATE_INDEX_24MBPS, | |
696 | HW_RATE_INDEX_36MBPS, | |
697 | HW_RATE_INDEX_48MBPS, | |
698 | HW_RATE_INDEX_54MBPS | |
699 | }; | |
700 | u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = { | |
701 | HW_RATE_INDEX_1MBPS, | |
702 | HW_RATE_INDEX_2MBPS, | |
703 | HW_RATE_INDEX_5_5MBPS, | |
704 | HW_RATE_INDEX_11MBPS | |
705 | }; | |
706 | ||
707 | rates->op_rate_mode = STA_11n; | |
708 | memcpy(rates->dsss_rates, dsss_rates, | |
709 | sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES); | |
710 | memcpy(rates->ofdm_rates, ofdm_rates, | |
711 | sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES); | |
712 | rates->supported_mcs_set[0] = 0xFF; | |
713 | } | |
714 | static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, | |
715 | struct ieee80211_vif *vif, | |
716 | struct ieee80211_bss_conf *bss_conf, | |
717 | u32 changed) | |
718 | { | |
719 | struct wcn36xx *wcn = hw->priv; | |
720 | struct sk_buff *skb = NULL; | |
721 | u16 tim_off, tim_len; | |
722 | enum wcn36xx_hal_link_state link_state; | |
ce75877f | 723 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
8e84c258 EK |
724 | |
725 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n", | |
726 | vif, changed); | |
727 | ||
728 | if (changed & BSS_CHANGED_BEACON_INFO) { | |
729 | wcn36xx_dbg(WCN36XX_DBG_MAC, | |
730 | "mac bss changed dtim period %d\n", | |
731 | bss_conf->dtim_period); | |
732 | ||
733 | vif_priv->dtim_period = bss_conf->dtim_period; | |
734 | } | |
735 | ||
736 | if (changed & BSS_CHANGED_PS) { | |
737 | wcn36xx_dbg(WCN36XX_DBG_MAC, | |
738 | "mac bss PS set %d\n", | |
739 | bss_conf->ps); | |
740 | if (bss_conf->ps) { | |
741 | wcn36xx_pmc_enter_bmps_state(wcn, vif); | |
742 | } else { | |
743 | wcn36xx_pmc_exit_bmps_state(wcn, vif); | |
744 | } | |
745 | } | |
746 | ||
747 | if (changed & BSS_CHANGED_BSSID) { | |
748 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n", | |
749 | bss_conf->bssid); | |
750 | ||
751 | if (!is_zero_ether_addr(bss_conf->bssid)) { | |
752 | vif_priv->is_joining = true; | |
90023c03 | 753 | vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX; |
8e84c258 EK |
754 | wcn36xx_smd_join(wcn, bss_conf->bssid, |
755 | vif->addr, WCN36XX_HW_CHANNEL(wcn)); | |
756 | wcn36xx_smd_config_bss(wcn, vif, NULL, | |
757 | bss_conf->bssid, false); | |
758 | } else { | |
759 | vif_priv->is_joining = false; | |
760 | wcn36xx_smd_delete_bss(wcn, vif); | |
2716a8ac | 761 | vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE; |
8e84c258 EK |
762 | } |
763 | } | |
764 | ||
765 | if (changed & BSS_CHANGED_SSID) { | |
766 | wcn36xx_dbg(WCN36XX_DBG_MAC, | |
767 | "mac bss changed ssid\n"); | |
768 | wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ", | |
769 | bss_conf->ssid, bss_conf->ssid_len); | |
770 | ||
771 | vif_priv->ssid.length = bss_conf->ssid_len; | |
772 | memcpy(&vif_priv->ssid.ssid, | |
773 | bss_conf->ssid, | |
774 | bss_conf->ssid_len); | |
775 | } | |
776 | ||
777 | if (changed & BSS_CHANGED_ASSOC) { | |
778 | vif_priv->is_joining = false; | |
779 | if (bss_conf->assoc) { | |
780 | struct ieee80211_sta *sta; | |
781 | struct wcn36xx_sta *sta_priv; | |
782 | ||
783 | wcn36xx_dbg(WCN36XX_DBG_MAC, | |
784 | "mac assoc bss %pM vif %pM AID=%d\n", | |
785 | bss_conf->bssid, | |
786 | vif->addr, | |
787 | bss_conf->aid); | |
788 | ||
043ce546 | 789 | vif_priv->sta_assoc = true; |
8e84c258 EK |
790 | rcu_read_lock(); |
791 | sta = ieee80211_find_sta(vif, bss_conf->bssid); | |
792 | if (!sta) { | |
793 | wcn36xx_err("sta %pM is not found\n", | |
794 | bss_conf->bssid); | |
795 | rcu_read_unlock(); | |
796 | goto out; | |
797 | } | |
a92e4696 | 798 | sta_priv = wcn36xx_sta_to_priv(sta); |
8e84c258 EK |
799 | |
800 | wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); | |
801 | ||
802 | wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, | |
803 | vif->addr, | |
804 | WCN36XX_HAL_LINK_POSTASSOC_STATE); | |
805 | wcn36xx_smd_config_bss(wcn, vif, sta, | |
806 | bss_conf->bssid, | |
807 | true); | |
808 | sta_priv->aid = bss_conf->aid; | |
809 | /* | |
810 | * config_sta must be called from because this is the | |
811 | * place where AID is available. | |
812 | */ | |
813 | wcn36xx_smd_config_sta(wcn, vif, sta); | |
814 | rcu_read_unlock(); | |
815 | } else { | |
816 | wcn36xx_dbg(WCN36XX_DBG_MAC, | |
817 | "disassociated bss %pM vif %pM AID=%d\n", | |
818 | bss_conf->bssid, | |
819 | vif->addr, | |
820 | bss_conf->aid); | |
043ce546 | 821 | vif_priv->sta_assoc = false; |
8e84c258 EK |
822 | wcn36xx_smd_set_link_st(wcn, |
823 | bss_conf->bssid, | |
824 | vif->addr, | |
825 | WCN36XX_HAL_LINK_IDLE_STATE); | |
826 | } | |
827 | } | |
828 | ||
829 | if (changed & BSS_CHANGED_AP_PROBE_RESP) { | |
830 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n"); | |
831 | skb = ieee80211_proberesp_get(hw, vif); | |
832 | if (!skb) { | |
833 | wcn36xx_err("failed to alloc probereq skb\n"); | |
834 | goto out; | |
835 | } | |
836 | ||
837 | wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb); | |
838 | dev_kfree_skb(skb); | |
839 | } | |
840 | ||
b3e3f871 CYY |
841 | if (changed & BSS_CHANGED_BEACON_ENABLED || |
842 | changed & BSS_CHANGED_BEACON) { | |
8e84c258 EK |
843 | wcn36xx_dbg(WCN36XX_DBG_MAC, |
844 | "mac bss changed beacon enabled %d\n", | |
845 | bss_conf->enable_beacon); | |
846 | ||
847 | if (bss_conf->enable_beacon) { | |
908628db | 848 | vif_priv->dtim_period = bss_conf->dtim_period; |
90023c03 | 849 | vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX; |
8e84c258 EK |
850 | wcn36xx_smd_config_bss(wcn, vif, NULL, |
851 | vif->addr, false); | |
852 | skb = ieee80211_beacon_get_tim(hw, vif, &tim_off, | |
853 | &tim_len); | |
854 | if (!skb) { | |
855 | wcn36xx_err("failed to alloc beacon skb\n"); | |
856 | goto out; | |
857 | } | |
858 | wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0); | |
859 | dev_kfree_skb(skb); | |
860 | ||
861 | if (vif->type == NL80211_IFTYPE_ADHOC || | |
862 | vif->type == NL80211_IFTYPE_MESH_POINT) | |
863 | link_state = WCN36XX_HAL_LINK_IBSS_STATE; | |
864 | else | |
865 | link_state = WCN36XX_HAL_LINK_AP_STATE; | |
866 | ||
867 | wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, | |
868 | link_state); | |
869 | } else { | |
5443918d | 870 | wcn36xx_smd_delete_bss(wcn, vif); |
8e84c258 EK |
871 | wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, |
872 | WCN36XX_HAL_LINK_IDLE_STATE); | |
8e84c258 EK |
873 | } |
874 | } | |
875 | out: | |
876 | return; | |
877 | } | |
878 | ||
879 | /* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */ | |
880 | static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) | |
881 | { | |
882 | struct wcn36xx *wcn = hw->priv; | |
883 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value); | |
884 | ||
885 | wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value); | |
886 | return 0; | |
887 | } | |
888 | ||
889 | static void wcn36xx_remove_interface(struct ieee80211_hw *hw, | |
890 | struct ieee80211_vif *vif) | |
891 | { | |
892 | struct wcn36xx *wcn = hw->priv; | |
ce75877f | 893 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
8e84c258 EK |
894 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif); |
895 | ||
896 | list_del(&vif_priv->list); | |
897 | wcn36xx_smd_delete_sta_self(wcn, vif->addr); | |
898 | } | |
899 | ||
900 | static int wcn36xx_add_interface(struct ieee80211_hw *hw, | |
901 | struct ieee80211_vif *vif) | |
902 | { | |
903 | struct wcn36xx *wcn = hw->priv; | |
ce75877f | 904 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
8e84c258 EK |
905 | |
906 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n", | |
907 | vif, vif->type); | |
908 | ||
909 | if (!(NL80211_IFTYPE_STATION == vif->type || | |
910 | NL80211_IFTYPE_AP == vif->type || | |
911 | NL80211_IFTYPE_ADHOC == vif->type || | |
912 | NL80211_IFTYPE_MESH_POINT == vif->type)) { | |
913 | wcn36xx_warn("Unsupported interface type requested: %d\n", | |
914 | vif->type); | |
915 | return -EOPNOTSUPP; | |
916 | } | |
917 | ||
918 | list_add(&vif_priv->list, &wcn->vif_list); | |
919 | wcn36xx_smd_add_sta_self(wcn, vif); | |
920 | ||
921 | return 0; | |
922 | } | |
923 | ||
924 | static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
925 | struct ieee80211_sta *sta) | |
926 | { | |
927 | struct wcn36xx *wcn = hw->priv; | |
ce75877f | 928 | struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif); |
a92e4696 | 929 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); |
8e84c258 EK |
930 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", |
931 | vif, sta->addr); | |
932 | ||
e26dc173 | 933 | spin_lock_init(&sta_priv->ampdu_lock); |
8e84c258 EK |
934 | sta_priv->vif = vif_priv; |
935 | /* | |
936 | * For STA mode HW will be configured on BSS_CHANGED_ASSOC because | |
937 | * at this stage AID is not available yet. | |
938 | */ | |
939 | if (NL80211_IFTYPE_STATION != vif->type) { | |
940 | wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); | |
941 | sta_priv->aid = sta->aid; | |
942 | wcn36xx_smd_config_sta(wcn, vif, sta); | |
943 | } | |
944 | return 0; | |
945 | } | |
946 | ||
947 | static int wcn36xx_sta_remove(struct ieee80211_hw *hw, | |
948 | struct ieee80211_vif *vif, | |
949 | struct ieee80211_sta *sta) | |
950 | { | |
951 | struct wcn36xx *wcn = hw->priv; | |
a92e4696 | 952 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta); |
8e84c258 EK |
953 | |
954 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n", | |
955 | vif, sta->addr, sta_priv->sta_index); | |
956 | ||
957 | wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index); | |
8e84c258 EK |
958 | sta_priv->vif = NULL; |
959 | return 0; | |
960 | } | |
961 | ||
962 | #ifdef CONFIG_PM | |
963 | ||
964 | static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) | |
965 | { | |
966 | struct wcn36xx *wcn = hw->priv; | |
967 | ||
968 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n"); | |
969 | ||
970 | flush_workqueue(wcn->hal_ind_wq); | |
971 | wcn36xx_smd_set_power_params(wcn, true); | |
972 | return 0; | |
973 | } | |
974 | ||
975 | static int wcn36xx_resume(struct ieee80211_hw *hw) | |
976 | { | |
977 | struct wcn36xx *wcn = hw->priv; | |
978 | ||
979 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n"); | |
980 | ||
981 | flush_workqueue(wcn->hal_ind_wq); | |
982 | wcn36xx_smd_set_power_params(wcn, false); | |
983 | return 0; | |
984 | } | |
985 | ||
986 | #endif | |
987 | ||
988 | static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, | |
989 | struct ieee80211_vif *vif, | |
50ea05ef | 990 | struct ieee80211_ampdu_params *params) |
8e84c258 EK |
991 | { |
992 | struct wcn36xx *wcn = hw->priv; | |
a92e4696 | 993 | struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta); |
50ea05ef SS |
994 | struct ieee80211_sta *sta = params->sta; |
995 | enum ieee80211_ampdu_mlme_action action = params->action; | |
996 | u16 tid = params->tid; | |
997 | u16 *ssn = ¶ms->ssn; | |
8e84c258 EK |
998 | |
999 | wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", | |
1000 | action, tid); | |
1001 | ||
8e84c258 EK |
1002 | switch (action) { |
1003 | case IEEE80211_AMPDU_RX_START: | |
1004 | sta_priv->tid = tid; | |
1005 | wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, | |
1006 | get_sta_index(vif, sta_priv)); | |
1007 | wcn36xx_smd_add_ba(wcn); | |
1008 | wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); | |
8e84c258 EK |
1009 | break; |
1010 | case IEEE80211_AMPDU_RX_STOP: | |
1011 | wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); | |
1012 | break; | |
1013 | case IEEE80211_AMPDU_TX_START: | |
e26dc173 BC |
1014 | spin_lock_bh(&sta_priv->ampdu_lock); |
1015 | sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START; | |
1016 | spin_unlock_bh(&sta_priv->ampdu_lock); | |
1017 | ||
8e84c258 EK |
1018 | ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); |
1019 | break; | |
1020 | case IEEE80211_AMPDU_TX_OPERATIONAL: | |
e26dc173 BC |
1021 | spin_lock_bh(&sta_priv->ampdu_lock); |
1022 | sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL; | |
1023 | spin_unlock_bh(&sta_priv->ampdu_lock); | |
1024 | ||
8e84c258 EK |
1025 | wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1, |
1026 | get_sta_index(vif, sta_priv)); | |
1027 | break; | |
1028 | case IEEE80211_AMPDU_TX_STOP_FLUSH: | |
1029 | case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: | |
1030 | case IEEE80211_AMPDU_TX_STOP_CONT: | |
e26dc173 BC |
1031 | spin_lock_bh(&sta_priv->ampdu_lock); |
1032 | sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE; | |
1033 | spin_unlock_bh(&sta_priv->ampdu_lock); | |
1034 | ||
8e84c258 EK |
1035 | ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); |
1036 | break; | |
1037 | default: | |
1038 | wcn36xx_err("Unknown AMPDU action\n"); | |
1039 | } | |
1040 | ||
1041 | return 0; | |
1042 | } | |
1043 | ||
1044 | static const struct ieee80211_ops wcn36xx_ops = { | |
1045 | .start = wcn36xx_start, | |
1046 | .stop = wcn36xx_stop, | |
1047 | .add_interface = wcn36xx_add_interface, | |
1048 | .remove_interface = wcn36xx_remove_interface, | |
1049 | #ifdef CONFIG_PM | |
1050 | .suspend = wcn36xx_suspend, | |
1051 | .resume = wcn36xx_resume, | |
1052 | #endif | |
1053 | .config = wcn36xx_config, | |
20a779ed | 1054 | .prepare_multicast = wcn36xx_prepare_multicast, |
8e84c258 EK |
1055 | .configure_filter = wcn36xx_configure_filter, |
1056 | .tx = wcn36xx_tx, | |
1057 | .set_key = wcn36xx_set_key, | |
88603903 | 1058 | .hw_scan = wcn36xx_hw_scan, |
03c95dbe | 1059 | .cancel_hw_scan = wcn36xx_cancel_hw_scan, |
8e84c258 EK |
1060 | .bss_info_changed = wcn36xx_bss_info_changed, |
1061 | .set_rts_threshold = wcn36xx_set_rts_threshold, | |
1062 | .sta_add = wcn36xx_sta_add, | |
1063 | .sta_remove = wcn36xx_sta_remove, | |
1064 | .ampdu_action = wcn36xx_ampdu_action, | |
1065 | }; | |
1066 | ||
1067 | static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) | |
1068 | { | |
1069 | int ret = 0; | |
1070 | ||
1071 | static const u32 cipher_suites[] = { | |
1072 | WLAN_CIPHER_SUITE_WEP40, | |
1073 | WLAN_CIPHER_SUITE_WEP104, | |
1074 | WLAN_CIPHER_SUITE_TKIP, | |
1075 | WLAN_CIPHER_SUITE_CCMP, | |
1076 | }; | |
1077 | ||
30686bf7 JB |
1078 | ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY); |
1079 | ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION); | |
1080 | ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR); | |
1081 | ieee80211_hw_set(wcn->hw, SUPPORTS_PS); | |
1082 | ieee80211_hw_set(wcn->hw, SIGNAL_DBM); | |
1083 | ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); | |
88603903 | 1084 | ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS); |
8e84c258 EK |
1085 | |
1086 | wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | |
1087 | BIT(NL80211_IFTYPE_AP) | | |
1088 | BIT(NL80211_IFTYPE_ADHOC) | | |
1089 | BIT(NL80211_IFTYPE_MESH_POINT); | |
1090 | ||
57fbcce3 JB |
1091 | wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz; |
1092 | wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz; | |
8e84c258 | 1093 | |
88603903 BA |
1094 | wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS; |
1095 | wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN; | |
1096 | ||
8e84c258 EK |
1097 | wcn->hw->wiphy->cipher_suites = cipher_suites; |
1098 | wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); | |
1099 | ||
1100 | wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; | |
1101 | ||
1102 | #ifdef CONFIG_PM | |
1103 | wcn->hw->wiphy->wowlan = &wowlan_support; | |
1104 | #endif | |
1105 | ||
1106 | wcn->hw->max_listen_interval = 200; | |
1107 | ||
1108 | wcn->hw->queues = 4; | |
1109 | ||
1110 | SET_IEEE80211_DEV(wcn->hw, wcn->dev); | |
1111 | ||
1112 | wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); | |
1113 | wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); | |
1114 | ||
ae44b502 AZ |
1115 | wiphy_ext_feature_set(wcn->hw->wiphy, |
1116 | NL80211_EXT_FEATURE_CQM_RSSI_LIST); | |
1117 | ||
8e84c258 EK |
1118 | return ret; |
1119 | } | |
1120 | ||
1121 | static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, | |
1122 | struct platform_device *pdev) | |
1123 | { | |
05ddce49 | 1124 | struct device_node *mmio_node; |
8e84c258 | 1125 | struct resource *res; |
05ddce49 BA |
1126 | int index; |
1127 | int ret; | |
1128 | ||
8e84c258 | 1129 | /* Set TX IRQ */ |
f303a931 | 1130 | res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx"); |
8e84c258 EK |
1131 | if (!res) { |
1132 | wcn36xx_err("failed to get tx_irq\n"); | |
1133 | return -ENOENT; | |
1134 | } | |
1135 | wcn->tx_irq = res->start; | |
1136 | ||
1137 | /* Set RX IRQ */ | |
f303a931 | 1138 | res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "rx"); |
8e84c258 EK |
1139 | if (!res) { |
1140 | wcn36xx_err("failed to get rx_irq\n"); | |
1141 | return -ENOENT; | |
1142 | } | |
1143 | wcn->rx_irq = res->start; | |
1144 | ||
f303a931 BA |
1145 | /* Acquire SMSM tx enable handle */ |
1146 | wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev, | |
1147 | "tx-enable", &wcn->tx_enable_state_bit); | |
1148 | if (IS_ERR(wcn->tx_enable_state)) { | |
1149 | wcn36xx_err("failed to get tx-enable state\n"); | |
1150 | return PTR_ERR(wcn->tx_enable_state); | |
1151 | } | |
1152 | ||
1153 | /* Acquire SMSM tx rings empty handle */ | |
1154 | wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev, | |
1155 | "tx-rings-empty", &wcn->tx_rings_empty_state_bit); | |
1156 | if (IS_ERR(wcn->tx_rings_empty_state)) { | |
1157 | wcn36xx_err("failed to get tx-rings-empty state\n"); | |
1158 | return PTR_ERR(wcn->tx_rings_empty_state); | |
1159 | } | |
1160 | ||
05ddce49 BA |
1161 | mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0); |
1162 | if (!mmio_node) { | |
1163 | wcn36xx_err("failed to acquire qcom,mmio reference\n"); | |
1164 | return -EINVAL; | |
1165 | } | |
1166 | ||
6f10b4e1 BA |
1167 | wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto"); |
1168 | ||
05ddce49 BA |
1169 | /* Map the CCU memory */ |
1170 | index = of_property_match_string(mmio_node, "reg-names", "ccu"); | |
1171 | wcn->ccu_base = of_iomap(mmio_node, index); | |
1172 | if (!wcn->ccu_base) { | |
1173 | wcn36xx_err("failed to map ccu memory\n"); | |
1174 | ret = -ENOMEM; | |
1175 | goto put_mmio_node; | |
8e84c258 | 1176 | } |
05ddce49 BA |
1177 | |
1178 | /* Map the DXE memory */ | |
1179 | index = of_property_match_string(mmio_node, "reg-names", "dxe"); | |
1180 | wcn->dxe_base = of_iomap(mmio_node, index); | |
1181 | if (!wcn->dxe_base) { | |
1182 | wcn36xx_err("failed to map dxe memory\n"); | |
1183 | ret = -ENOMEM; | |
1184 | goto unmap_ccu; | |
8e84c258 | 1185 | } |
05ddce49 BA |
1186 | |
1187 | of_node_put(mmio_node); | |
8e84c258 | 1188 | return 0; |
05ddce49 BA |
1189 | |
1190 | unmap_ccu: | |
1191 | iounmap(wcn->ccu_base); | |
1192 | put_mmio_node: | |
1193 | of_node_put(mmio_node); | |
1194 | return ret; | |
8e84c258 EK |
1195 | } |
1196 | ||
1197 | static int wcn36xx_probe(struct platform_device *pdev) | |
1198 | { | |
1199 | struct ieee80211_hw *hw; | |
1200 | struct wcn36xx *wcn; | |
f303a931 | 1201 | void *wcnss; |
8e84c258 | 1202 | int ret; |
f303a931 | 1203 | const u8 *addr; |
8e84c258 EK |
1204 | |
1205 | wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); | |
1206 | ||
f303a931 BA |
1207 | wcnss = dev_get_drvdata(pdev->dev.parent); |
1208 | ||
8e84c258 EK |
1209 | hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); |
1210 | if (!hw) { | |
1211 | wcn36xx_err("failed to alloc hw\n"); | |
1212 | ret = -ENOMEM; | |
1213 | goto out_err; | |
1214 | } | |
1215 | platform_set_drvdata(pdev, hw); | |
1216 | wcn = hw->priv; | |
1217 | wcn->hw = hw; | |
1218 | wcn->dev = &pdev->dev; | |
8e84c258 | 1219 | mutex_init(&wcn->hal_mutex); |
88603903 BA |
1220 | mutex_init(&wcn->scan_lock); |
1221 | ||
1222 | INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker); | |
8e84c258 | 1223 | |
5052de8d | 1224 | wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw); |
f303a931 BA |
1225 | if (IS_ERR(wcn->smd_channel)) { |
1226 | wcn36xx_err("failed to open WLAN_CTRL channel\n"); | |
1227 | ret = PTR_ERR(wcn->smd_channel); | |
1228 | goto out_wq; | |
1229 | } | |
1230 | ||
f303a931 BA |
1231 | addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret); |
1232 | if (addr && ret != ETH_ALEN) { | |
1233 | wcn36xx_err("invalid local-mac-address\n"); | |
1234 | ret = -EINVAL; | |
1235 | goto out_wq; | |
1236 | } else if (addr) { | |
8e84c258 EK |
1237 | wcn36xx_info("mac address: %pM\n", addr); |
1238 | SET_IEEE80211_PERM_ADDR(wcn->hw, addr); | |
1239 | } | |
1240 | ||
1241 | ret = wcn36xx_platform_get_resources(wcn, pdev); | |
1242 | if (ret) | |
1243 | goto out_wq; | |
1244 | ||
1245 | wcn36xx_init_ieee80211(wcn); | |
1246 | ret = ieee80211_register_hw(wcn->hw); | |
1247 | if (ret) | |
1248 | goto out_unmap; | |
1249 | ||
1250 | return 0; | |
1251 | ||
1252 | out_unmap: | |
05ddce49 BA |
1253 | iounmap(wcn->ccu_base); |
1254 | iounmap(wcn->dxe_base); | |
8e84c258 EK |
1255 | out_wq: |
1256 | ieee80211_free_hw(hw); | |
1257 | out_err: | |
1258 | return ret; | |
1259 | } | |
f303a931 | 1260 | |
8e84c258 EK |
1261 | static int wcn36xx_remove(struct platform_device *pdev) |
1262 | { | |
1263 | struct ieee80211_hw *hw = platform_get_drvdata(pdev); | |
1264 | struct wcn36xx *wcn = hw->priv; | |
1265 | wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n"); | |
1266 | ||
4bda7faf | 1267 | release_firmware(wcn->nv); |
8e84c258 EK |
1268 | |
1269 | ieee80211_unregister_hw(hw); | |
f303a931 BA |
1270 | |
1271 | qcom_smem_state_put(wcn->tx_enable_state); | |
1272 | qcom_smem_state_put(wcn->tx_rings_empty_state); | |
1273 | ||
efad8396 BA |
1274 | rpmsg_destroy_ept(wcn->smd_channel); |
1275 | ||
05ddce49 BA |
1276 | iounmap(wcn->dxe_base); |
1277 | iounmap(wcn->ccu_base); | |
d5362888 BA |
1278 | |
1279 | mutex_destroy(&wcn->hal_mutex); | |
8e84c258 EK |
1280 | ieee80211_free_hw(hw); |
1281 | ||
1282 | return 0; | |
1283 | } | |
f303a931 BA |
1284 | |
1285 | static const struct of_device_id wcn36xx_of_match[] = { | |
1286 | { .compatible = "qcom,wcnss-wlan" }, | |
8e84c258 EK |
1287 | {} |
1288 | }; | |
f303a931 | 1289 | MODULE_DEVICE_TABLE(of, wcn36xx_of_match); |
8e84c258 EK |
1290 | |
1291 | static struct platform_driver wcn36xx_driver = { | |
1292 | .probe = wcn36xx_probe, | |
1293 | .remove = wcn36xx_remove, | |
1294 | .driver = { | |
1295 | .name = "wcn36xx", | |
f303a931 | 1296 | .of_match_table = wcn36xx_of_match, |
8e84c258 | 1297 | }, |
8e84c258 EK |
1298 | }; |
1299 | ||
f303a931 | 1300 | module_platform_driver(wcn36xx_driver); |
8e84c258 EK |
1301 | |
1302 | MODULE_LICENSE("Dual BSD/GPL"); | |
1303 | MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); | |
1304 | MODULE_FIRMWARE(WLAN_NV_FILE); |