]>
Commit | Line | Data |
---|---|---|
2b27bdcc | 1 | // SPDX-License-Identifier: GPL-2.0-only |
f5fc0f86 LC |
2 | /* |
3 | * This file is part of wl1271 | |
4 | * | |
5 | * Copyright (C) 2009 Nokia Corporation | |
6 | * | |
7 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | |
f5fc0f86 LC |
8 | */ |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
5a0e3ad6 | 12 | #include <linux/slab.h> |
f5fc0f86 | 13 | |
0f4e3122 | 14 | #include "debug.h" |
00d20100 | 15 | #include "init.h" |
f5fc0f86 | 16 | #include "wl12xx_80211.h" |
00d20100 SL |
17 | #include "acx.h" |
18 | #include "cmd.h" | |
e0fe371b | 19 | #include "tx.h" |
48a61477 | 20 | #include "io.h" |
8a9affc0 | 21 | #include "hw_ops.h" |
f5fc0f86 | 22 | |
92c77c73 | 23 | int wl1271_init_templates_config(struct wl1271 *wl) |
f5fc0f86 | 24 | { |
bfb24c9e | 25 | int ret, i; |
5ec8a448 | 26 | size_t max_size; |
f5fc0f86 LC |
27 | |
28 | /* send empty templates for fw memory reservation */ | |
cdaac628 | 29 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
78e28062 | 30 | wl->scan_templ_id_2_4, NULL, |
c08e371a | 31 | WL1271_CMD_TEMPL_MAX_SIZE, |
606c1487 | 32 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
33 | if (ret < 0) |
34 | return ret; | |
35 | ||
cdaac628 | 36 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
78e28062 | 37 | wl->scan_templ_id_5, |
c08e371a | 38 | NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, |
11eb5429 JO |
39 | WL1271_RATE_AUTOMATIC); |
40 | if (ret < 0) | |
41 | return ret; | |
abb0b3bf | 42 | |
3df74f46 YD |
43 | if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) { |
44 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, | |
78e28062 EP |
45 | wl->sched_scan_templ_id_2_4, |
46 | NULL, | |
3df74f46 YD |
47 | WL1271_CMD_TEMPL_MAX_SIZE, |
48 | 0, WL1271_RATE_AUTOMATIC); | |
49 | if (ret < 0) | |
50 | return ret; | |
51 | ||
52 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, | |
78e28062 EP |
53 | wl->sched_scan_templ_id_5, |
54 | NULL, | |
3df74f46 YD |
55 | WL1271_CMD_TEMPL_MAX_SIZE, |
56 | 0, WL1271_RATE_AUTOMATIC); | |
57 | if (ret < 0) | |
58 | return ret; | |
59 | } | |
60 | ||
cdaac628 EP |
61 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
62 | CMD_TEMPL_NULL_DATA, NULL, | |
bfb24c9e | 63 | sizeof(struct wl12xx_null_data_template), |
606c1487 | 64 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
65 | if (ret < 0) |
66 | return ret; | |
67 | ||
cdaac628 EP |
68 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
69 | CMD_TEMPL_PS_POLL, NULL, | |
bfb24c9e | 70 | sizeof(struct wl12xx_ps_poll_template), |
606c1487 | 71 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
72 | if (ret < 0) |
73 | return ret; | |
74 | ||
cdaac628 EP |
75 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
76 | CMD_TEMPL_QOS_NULL_DATA, NULL, | |
f5fc0f86 | 77 | sizeof |
97127e67 | 78 | (struct ieee80211_qos_hdr), |
606c1487 | 79 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
80 | if (ret < 0) |
81 | return ret; | |
82 | ||
cdaac628 EP |
83 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
84 | CMD_TEMPL_PROBE_RESPONSE, NULL, | |
154037d1 | 85 | WL1271_CMD_TEMPL_DFLT_SIZE, |
606c1487 | 86 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
87 | if (ret < 0) |
88 | return ret; | |
89 | ||
cdaac628 EP |
90 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
91 | CMD_TEMPL_BEACON, NULL, | |
154037d1 | 92 | WL1271_CMD_TEMPL_DFLT_SIZE, |
606c1487 | 93 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
94 | if (ret < 0) |
95 | return ret; | |
96 | ||
5ec8a448 EP |
97 | max_size = sizeof(struct wl12xx_arp_rsp_template) + |
98 | WL1271_EXTRA_SPACE_MAX; | |
cdaac628 EP |
99 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
100 | CMD_TEMPL_ARP_RSP, NULL, | |
5ec8a448 | 101 | max_size, |
c5312772 EP |
102 | 0, WL1271_RATE_AUTOMATIC); |
103 | if (ret < 0) | |
104 | return ret; | |
105 | ||
92c77c73 EP |
106 | /* |
107 | * Put very large empty placeholders for all templates. These | |
108 | * reserve memory for later. | |
109 | */ | |
cdaac628 EP |
110 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
111 | CMD_TEMPL_AP_PROBE_RESPONSE, NULL, | |
92c77c73 EP |
112 | WL1271_CMD_TEMPL_MAX_SIZE, |
113 | 0, WL1271_RATE_AUTOMATIC); | |
114 | if (ret < 0) | |
115 | return ret; | |
116 | ||
cdaac628 EP |
117 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
118 | CMD_TEMPL_AP_BEACON, NULL, | |
92c77c73 EP |
119 | WL1271_CMD_TEMPL_MAX_SIZE, |
120 | 0, WL1271_RATE_AUTOMATIC); | |
121 | if (ret < 0) | |
122 | return ret; | |
123 | ||
cdaac628 EP |
124 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
125 | CMD_TEMPL_DEAUTH_AP, NULL, | |
92c77c73 EP |
126 | sizeof |
127 | (struct wl12xx_disconn_template), | |
128 | 0, WL1271_RATE_AUTOMATIC); | |
129 | if (ret < 0) | |
130 | return ret; | |
131 | ||
001e39a8 | 132 | for (i = 0; i < WLCORE_MAX_KLV_TEMPLATES; i++) { |
cdaac628 EP |
133 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
134 | CMD_TEMPL_KLV, NULL, | |
97127e67 EP |
135 | sizeof(struct ieee80211_qos_hdr), |
136 | i, WL1271_RATE_AUTOMATIC); | |
bfb24c9e JO |
137 | if (ret < 0) |
138 | return ret; | |
139 | } | |
140 | ||
f5fc0f86 LC |
141 | return 0; |
142 | } | |
143 | ||
87fbcb0f EP |
144 | static int wl1271_ap_init_deauth_template(struct wl1271 *wl, |
145 | struct wl12xx_vif *wlvif) | |
e0fe371b AN |
146 | { |
147 | struct wl12xx_disconn_template *tmpl; | |
148 | int ret; | |
af7fbb28 | 149 | u32 rate; |
e0fe371b AN |
150 | |
151 | tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL); | |
152 | if (!tmpl) { | |
153 | ret = -ENOMEM; | |
154 | goto out; | |
155 | } | |
156 | ||
157 | tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | | |
158 | IEEE80211_STYPE_DEAUTH); | |
159 | ||
87fbcb0f | 160 | rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
cdaac628 EP |
161 | ret = wl1271_cmd_template_set(wl, wlvif->role_id, |
162 | CMD_TEMPL_DEAUTH_AP, | |
af7fbb28 | 163 | tmpl, sizeof(*tmpl), 0, rate); |
e0fe371b AN |
164 | |
165 | out: | |
166 | kfree(tmpl); | |
167 | return ret; | |
168 | } | |
169 | ||
784f694d EP |
170 | static int wl1271_ap_init_null_template(struct wl1271 *wl, |
171 | struct ieee80211_vif *vif) | |
e0fe371b | 172 | { |
87fbcb0f | 173 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
174 | struct ieee80211_hdr_3addr *nullfunc; |
175 | int ret; | |
af7fbb28 | 176 | u32 rate; |
e0fe371b AN |
177 | |
178 | nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL); | |
179 | if (!nullfunc) { | |
180 | ret = -ENOMEM; | |
181 | goto out; | |
182 | } | |
183 | ||
184 | nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | |
185 | IEEE80211_STYPE_NULLFUNC | | |
186 | IEEE80211_FCTL_FROMDS); | |
187 | ||
188 | /* nullfunc->addr1 is filled by FW */ | |
189 | ||
784f694d EP |
190 | memcpy(nullfunc->addr2, vif->addr, ETH_ALEN); |
191 | memcpy(nullfunc->addr3, vif->addr, ETH_ALEN); | |
e0fe371b | 192 | |
87fbcb0f | 193 | rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
cdaac628 EP |
194 | ret = wl1271_cmd_template_set(wl, wlvif->role_id, |
195 | CMD_TEMPL_NULL_DATA, nullfunc, | |
af7fbb28 | 196 | sizeof(*nullfunc), 0, rate); |
e0fe371b AN |
197 | |
198 | out: | |
199 | kfree(nullfunc); | |
200 | return ret; | |
201 | } | |
202 | ||
784f694d EP |
203 | static int wl1271_ap_init_qos_null_template(struct wl1271 *wl, |
204 | struct ieee80211_vif *vif) | |
e0fe371b | 205 | { |
87fbcb0f | 206 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
207 | struct ieee80211_qos_hdr *qosnull; |
208 | int ret; | |
af7fbb28 | 209 | u32 rate; |
e0fe371b AN |
210 | |
211 | qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL); | |
212 | if (!qosnull) { | |
213 | ret = -ENOMEM; | |
214 | goto out; | |
215 | } | |
216 | ||
217 | qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | |
218 | IEEE80211_STYPE_QOS_NULLFUNC | | |
219 | IEEE80211_FCTL_FROMDS); | |
220 | ||
221 | /* qosnull->addr1 is filled by FW */ | |
222 | ||
784f694d EP |
223 | memcpy(qosnull->addr2, vif->addr, ETH_ALEN); |
224 | memcpy(qosnull->addr3, vif->addr, ETH_ALEN); | |
e0fe371b | 225 | |
87fbcb0f | 226 | rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
cdaac628 EP |
227 | ret = wl1271_cmd_template_set(wl, wlvif->role_id, |
228 | CMD_TEMPL_QOS_NULL_DATA, qosnull, | |
af7fbb28 | 229 | sizeof(*qosnull), 0, rate); |
e0fe371b AN |
230 | |
231 | out: | |
232 | kfree(qosnull); | |
233 | return ret; | |
234 | } | |
235 | ||
92c77c73 | 236 | static int wl12xx_init_rx_config(struct wl1271 *wl) |
e0fe371b AN |
237 | { |
238 | int ret; | |
239 | ||
92c77c73 | 240 | ret = wl1271_acx_rx_msdu_life_time(wl); |
e0fe371b AN |
241 | if (ret < 0) |
242 | return ret; | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
0603d891 EP |
247 | static int wl12xx_init_phy_vif_config(struct wl1271 *wl, |
248 | struct wl12xx_vif *wlvif) | |
f5fc0f86 LC |
249 | { |
250 | int ret; | |
251 | ||
0603d891 | 252 | ret = wl1271_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME); |
f5fc0f86 LC |
253 | if (ret < 0) |
254 | return ret; | |
255 | ||
0603d891 | 256 | ret = wl1271_acx_service_period_timeout(wl, wlvif); |
f5fc0f86 LC |
257 | if (ret < 0) |
258 | return ret; | |
259 | ||
0603d891 | 260 | ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold); |
f5fc0f86 LC |
261 | if (ret < 0) |
262 | return ret; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
a693534b AN |
267 | static int wl1271_init_sta_beacon_filter(struct wl1271 *wl, |
268 | struct wl12xx_vif *wlvif) | |
f5fc0f86 LC |
269 | { |
270 | int ret; | |
271 | ||
a693534b | 272 | ret = wl1271_acx_beacon_filter_table(wl, wlvif); |
f5fc0f86 LC |
273 | if (ret < 0) |
274 | return ret; | |
275 | ||
d881fa2c EP |
276 | /* disable beacon filtering until we get the first beacon */ |
277 | ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); | |
f5fc0f86 LC |
278 | if (ret < 0) |
279 | return ret; | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
12419cce | 284 | int wl1271_init_pta(struct wl1271 *wl) |
f5fc0f86 LC |
285 | { |
286 | int ret; | |
287 | ||
3be4112c | 288 | ret = wl12xx_acx_sg_cfg(wl); |
f5fc0f86 LC |
289 | if (ret < 0) |
290 | return ret; | |
291 | ||
7fc3a864 | 292 | ret = wl1271_acx_sg_enable(wl, wl->sg_enabled); |
f5fc0f86 LC |
293 | if (ret < 0) |
294 | return ret; | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
12419cce | 299 | int wl1271_init_energy_detection(struct wl1271 *wl) |
f5fc0f86 LC |
300 | { |
301 | int ret; | |
302 | ||
303 | ret = wl1271_acx_cca_threshold(wl); | |
304 | if (ret < 0) | |
305 | return ret; | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
0603d891 EP |
310 | static int wl1271_init_beacon_broadcast(struct wl1271 *wl, |
311 | struct wl12xx_vif *wlvif) | |
f5fc0f86 LC |
312 | { |
313 | int ret; | |
314 | ||
0603d891 | 315 | ret = wl1271_acx_bcn_dtim_options(wl, wlvif); |
f5fc0f86 LC |
316 | if (ret < 0) |
317 | return ret; | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
95dac04f IY |
322 | static int wl12xx_init_fwlog(struct wl1271 *wl) |
323 | { | |
324 | int ret; | |
325 | ||
6f7dd16c | 326 | if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) |
95dac04f IY |
327 | return 0; |
328 | ||
329 | ret = wl12xx_cmd_config_fwlog(wl); | |
330 | if (ret < 0) | |
331 | return ret; | |
332 | ||
333 | return 0; | |
334 | } | |
335 | ||
92c77c73 | 336 | /* generic sta initialization (non vif-specific) */ |
7845af35 | 337 | int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
e0fe371b AN |
338 | { |
339 | int ret; | |
340 | ||
c8bde243 | 341 | /* PS config */ |
d2d66c56 | 342 | ret = wl12xx_acx_config_ps(wl, wlvif); |
c8bde243 EP |
343 | if (ret < 0) |
344 | return ret; | |
345 | ||
ff86843d SL |
346 | /* FM WLAN coexistence */ |
347 | ret = wl1271_acx_fm_coex(wl); | |
348 | if (ret < 0) | |
349 | return ret; | |
350 | ||
30d0c8fd | 351 | ret = wl1271_acx_sta_rate_policies(wl, wlvif); |
e0fe371b AN |
352 | if (ret < 0) |
353 | return ret; | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
0603d891 EP |
358 | static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl, |
359 | struct ieee80211_vif *vif) | |
e0fe371b | 360 | { |
0603d891 | 361 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
001e39a8 | 362 | int ret; |
e0fe371b AN |
363 | |
364 | /* disable the keep-alive feature */ | |
0603d891 | 365 | ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); |
e0fe371b AN |
366 | if (ret < 0) |
367 | return ret; | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
92c77c73 | 372 | /* generic ap initialization (non vif-specific) */ |
87fbcb0f | 373 | static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
e0fe371b | 374 | { |
70f47424 | 375 | int ret; |
e0fe371b | 376 | |
87fbcb0f | 377 | ret = wl1271_init_ap_rates(wl, wlvif); |
e0fe371b AN |
378 | if (ret < 0) |
379 | return ret; | |
380 | ||
e2f1e50f K |
381 | /* configure AP sleep, if enabled */ |
382 | ret = wlcore_hw_ap_sleep(wl); | |
383 | if (ret < 0) | |
384 | return ret; | |
385 | ||
e0fe371b AN |
386 | return 0; |
387 | } | |
388 | ||
784f694d | 389 | int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif) |
e0fe371b | 390 | { |
87fbcb0f | 391 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
392 | int ret; |
393 | ||
87fbcb0f | 394 | ret = wl1271_ap_init_deauth_template(wl, wlvif); |
e0fe371b AN |
395 | if (ret < 0) |
396 | return ret; | |
397 | ||
784f694d | 398 | ret = wl1271_ap_init_null_template(wl, vif); |
e0fe371b AN |
399 | if (ret < 0) |
400 | return ret; | |
401 | ||
784f694d | 402 | ret = wl1271_ap_init_qos_null_template(wl, vif); |
e0fe371b AN |
403 | if (ret < 0) |
404 | return ret; | |
405 | ||
521a4a23 AN |
406 | /* |
407 | * when operating as AP we want to receive external beacons for | |
408 | * configuring ERP protection. | |
409 | */ | |
0603d891 | 410 | ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); |
521a4a23 AN |
411 | if (ret < 0) |
412 | return ret; | |
413 | ||
e0fe371b AN |
414 | return 0; |
415 | } | |
416 | ||
784f694d EP |
417 | static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl, |
418 | struct ieee80211_vif *vif) | |
c45a85b5 | 419 | { |
784f694d | 420 | return wl1271_ap_init_templates(wl, vif); |
c45a85b5 AN |
421 | } |
422 | ||
87fbcb0f | 423 | int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
70f47424 AN |
424 | { |
425 | int i, ret; | |
426 | struct conf_tx_rate_class rc; | |
427 | u32 supported_rates; | |
428 | ||
87fbcb0f EP |
429 | wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", |
430 | wlvif->basic_rate_set); | |
70f47424 | 431 | |
87fbcb0f | 432 | if (wlvif->basic_rate_set == 0) |
70f47424 AN |
433 | return -EINVAL; |
434 | ||
87fbcb0f | 435 | rc.enabled_rates = wlvif->basic_rate_set; |
70f47424 AN |
436 | rc.long_retry_limit = 10; |
437 | rc.short_retry_limit = 10; | |
438 | rc.aflags = 0; | |
e5a359f8 | 439 | ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx); |
70f47424 AN |
440 | if (ret < 0) |
441 | return ret; | |
442 | ||
443 | /* use the min basic rate for AP broadcast/multicast */ | |
87fbcb0f | 444 | rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
70f47424 AN |
445 | rc.short_retry_limit = 10; |
446 | rc.long_retry_limit = 10; | |
447 | rc.aflags = 0; | |
e5a359f8 | 448 | ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx); |
70f47424 AN |
449 | if (ret < 0) |
450 | return ret; | |
451 | ||
452 | /* | |
453 | * If the basic rates contain OFDM rates, use OFDM only | |
454 | * rates for unicast TX as well. Else use all supported rates. | |
455 | */ | |
bc566f92 | 456 | if (wl->ofdm_only_ap && (wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) |
70f47424 AN |
457 | supported_rates = CONF_TX_OFDM_RATES; |
458 | else | |
42ec1f82 | 459 | supported_rates = CONF_TX_ENABLED_RATES; |
70f47424 | 460 | |
1a8adb67 AN |
461 | /* unconditionally enable HT rates */ |
462 | supported_rates |= CONF_TX_MCS_RATES; | |
463 | ||
ebc7e57d AN |
464 | /* get extra MIMO or wide-chan rates where the HW supports it */ |
465 | supported_rates |= wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); | |
466 | ||
70f47424 AN |
467 | /* configure unicast TX rate classes */ |
468 | for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { | |
469 | rc.enabled_rates = supported_rates; | |
470 | rc.short_retry_limit = 10; | |
471 | rc.long_retry_limit = 10; | |
472 | rc.aflags = 0; | |
e5a359f8 EP |
473 | ret = wl1271_acx_ap_rate_policy(wl, &rc, |
474 | wlvif->ap.ucast_rate_idx[i]); | |
70f47424 AN |
475 | if (ret < 0) |
476 | return ret; | |
477 | } | |
478 | ||
479 | return 0; | |
480 | } | |
481 | ||
536129c8 | 482 | static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
4b7fac77 | 483 | { |
4b7fac77 | 484 | /* Reset the BA RX indicators */ |
d0802abd | 485 | wlvif->ba_allowed = true; |
0f9c8250 | 486 | wl->ba_rx_session_count = 0; |
4b7fac77 | 487 | |
0f9c8250 | 488 | /* BA is supported in STA/AP modes */ |
536129c8 EP |
489 | if (wlvif->bss_type != BSS_TYPE_AP_BSS && |
490 | wlvif->bss_type != BSS_TYPE_STA_BSS) { | |
d0802abd | 491 | wlvif->ba_support = false; |
0f9c8250 AN |
492 | return 0; |
493 | } | |
4b7fac77 | 494 | |
d0802abd | 495 | wlvif->ba_support = true; |
4b7fac77 | 496 | |
0f9c8250 | 497 | /* 802.11n initiator BA session setting */ |
0603d891 | 498 | return wl12xx_acx_set_ba_initiator_policy(wl, wlvif); |
4b7fac77 LS |
499 | } |
500 | ||
92c77c73 | 501 | /* vif-specifc initialization */ |
0603d891 | 502 | static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
92c77c73 EP |
503 | { |
504 | int ret; | |
505 | ||
0603d891 | 506 | ret = wl1271_acx_group_address_tbl(wl, wlvif, true, NULL, 0); |
92c77c73 EP |
507 | if (ret < 0) |
508 | return ret; | |
509 | ||
510 | /* Initialize connection monitoring thresholds */ | |
0603d891 | 511 | ret = wl1271_acx_conn_monit_params(wl, wlvif, false); |
92c77c73 EP |
512 | if (ret < 0) |
513 | return ret; | |
514 | ||
515 | /* Beacon filtering */ | |
a693534b | 516 | ret = wl1271_init_sta_beacon_filter(wl, wlvif); |
92c77c73 EP |
517 | if (ret < 0) |
518 | return ret; | |
519 | ||
520 | /* Beacons and broadcast settings */ | |
0603d891 | 521 | ret = wl1271_init_beacon_broadcast(wl, wlvif); |
92c77c73 EP |
522 | if (ret < 0) |
523 | return ret; | |
48a61477 | 524 | |
92c77c73 | 525 | /* Configure rssi/snr averaging weights */ |
0603d891 | 526 | ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif); |
92c77c73 EP |
527 | if (ret < 0) |
528 | return ret; | |
529 | ||
530 | return 0; | |
531 | } | |
532 | ||
183b8021 | 533 | /* vif-specific initialization */ |
0603d891 | 534 | static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
92c77c73 EP |
535 | { |
536 | int ret; | |
537 | ||
0603d891 | 538 | ret = wl1271_acx_ap_max_tx_retry(wl, wlvif); |
92c77c73 EP |
539 | if (ret < 0) |
540 | return ret; | |
541 | ||
542 | /* initialize Tx power */ | |
6bd65029 | 543 | ret = wl1271_acx_tx_power(wl, wlvif, wlvif->power_level); |
92c77c73 EP |
544 | if (ret < 0) |
545 | return ret; | |
546 | ||
8cf77e17 EP |
547 | if (wl->radar_debug_mode) |
548 | wlcore_cmd_generic_cfg(wl, wlvif, | |
549 | WLCORE_CFG_FEATURE_RADAR_DEBUG, | |
550 | wl->radar_debug_mode, 0); | |
551 | ||
92c77c73 EP |
552 | return 0; |
553 | } | |
554 | ||
555 | int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) | |
f5fc0f86 | 556 | { |
87fbcb0f | 557 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
243eeb51 | 558 | struct conf_tx_ac_category *conf_ac; |
f2054df5 | 559 | struct conf_tx_tid *conf_tid; |
536129c8 | 560 | bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); |
92c77c73 EP |
561 | int ret, i; |
562 | ||
2f18cf7c AN |
563 | /* consider all existing roles before configuring psm. */ |
564 | ||
565 | if (wl->ap_count == 0 && is_ap) { /* first AP */ | |
e2f1e50f | 566 | ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); |
2f18cf7c AN |
567 | if (ret < 0) |
568 | return ret; | |
71e996be EP |
569 | |
570 | /* unmask ap events */ | |
571 | wl->event_mask |= wl->ap_event_mask; | |
572 | ret = wl1271_event_unmask(wl); | |
573 | if (ret < 0) | |
574 | return ret; | |
2f18cf7c AN |
575 | /* first STA, no APs */ |
576 | } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) { | |
577 | u8 sta_auth = wl->conf.conn.sta_sleep_auth; | |
578 | /* Configure for power according to debugfs */ | |
579 | if (sta_auth != WL1271_PSM_ILLEGAL) | |
580 | ret = wl1271_acx_sleep_auth(wl, sta_auth); | |
2f18cf7c AN |
581 | /* Configure for ELP power saving */ |
582 | else | |
583 | ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); | |
584 | ||
585 | if (ret < 0) | |
586 | return ret; | |
a4e4130d EP |
587 | } |
588 | ||
92c77c73 EP |
589 | /* Mode specific init */ |
590 | if (is_ap) { | |
87fbcb0f | 591 | ret = wl1271_ap_hw_init(wl, wlvif); |
92c77c73 EP |
592 | if (ret < 0) |
593 | return ret; | |
594 | ||
0603d891 | 595 | ret = wl12xx_init_ap_role(wl, wlvif); |
92c77c73 EP |
596 | if (ret < 0) |
597 | return ret; | |
598 | } else { | |
30d0c8fd | 599 | ret = wl1271_sta_hw_init(wl, wlvif); |
92c77c73 EP |
600 | if (ret < 0) |
601 | return ret; | |
602 | ||
0603d891 | 603 | ret = wl12xx_init_sta_role(wl, wlvif); |
92c77c73 EP |
604 | if (ret < 0) |
605 | return ret; | |
606 | } | |
607 | ||
0603d891 | 608 | wl12xx_init_phy_vif_config(wl, wlvif); |
92c77c73 EP |
609 | |
610 | /* Default TID/AC configuration */ | |
611 | BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count); | |
612 | for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { | |
613 | conf_ac = &wl->conf.tx.ac_conf[i]; | |
0603d891 EP |
614 | ret = wl1271_acx_ac_cfg(wl, wlvif, conf_ac->ac, |
615 | conf_ac->cw_min, conf_ac->cw_max, | |
616 | conf_ac->aifsn, conf_ac->tx_op_limit); | |
92c77c73 EP |
617 | if (ret < 0) |
618 | return ret; | |
619 | ||
620 | conf_tid = &wl->conf.tx.tid_conf[i]; | |
0603d891 | 621 | ret = wl1271_acx_tid_cfg(wl, wlvif, |
92c77c73 EP |
622 | conf_tid->queue_id, |
623 | conf_tid->channel_type, | |
624 | conf_tid->tsid, | |
625 | conf_tid->ps_scheme, | |
626 | conf_tid->ack_policy, | |
627 | conf_tid->apsd_conf[0], | |
628 | conf_tid->apsd_conf[1]); | |
629 | if (ret < 0) | |
630 | return ret; | |
631 | } | |
632 | ||
633 | /* Configure HW encryption */ | |
0603d891 | 634 | ret = wl1271_acx_feature_cfg(wl, wlvif); |
92c77c73 EP |
635 | if (ret < 0) |
636 | return ret; | |
637 | ||
638 | /* Mode specific init - post mem init */ | |
639 | if (is_ap) | |
640 | ret = wl1271_ap_hw_init_post_mem(wl, vif); | |
641 | else | |
0603d891 | 642 | ret = wl1271_sta_hw_init_post_mem(wl, vif); |
92c77c73 EP |
643 | |
644 | if (ret < 0) | |
645 | return ret; | |
646 | ||
647 | /* Configure initiator BA sessions policies */ | |
536129c8 | 648 | ret = wl1271_set_ba_policies(wl, wlvif); |
92c77c73 EP |
649 | if (ret < 0) |
650 | return ret; | |
651 | ||
8a9affc0 AN |
652 | ret = wlcore_hw_init_vif(wl, wlvif); |
653 | if (ret < 0) | |
654 | return ret; | |
655 | ||
92c77c73 EP |
656 | return 0; |
657 | } | |
658 | ||
659 | int wl1271_hw_init(struct wl1271 *wl) | |
660 | { | |
661 | int ret; | |
662 | ||
9d68d1ee LC |
663 | /* Chip-specific hw init */ |
664 | ret = wl->ops->hw_init(wl); | |
48a61477 SL |
665 | if (ret < 0) |
666 | return ret; | |
667 | ||
92c77c73 EP |
668 | /* Init templates */ |
669 | ret = wl1271_init_templates_config(wl); | |
670 | if (ret < 0) | |
671 | return ret; | |
644a4860 | 672 | |
92c77c73 EP |
673 | ret = wl12xx_acx_mem_cfg(wl); |
674 | if (ret < 0) | |
675 | return ret; | |
676 | ||
677 | /* Configure the FW logger */ | |
678 | ret = wl12xx_init_fwlog(wl); | |
f5fc0f86 LC |
679 | if (ret < 0) |
680 | return ret; | |
681 | ||
6b70e7eb VG |
682 | ret = wlcore_cmd_regdomain_config_locked(wl); |
683 | if (ret < 0) | |
684 | return ret; | |
685 | ||
801f870b AN |
686 | /* Bluetooth WLAN coexistence */ |
687 | ret = wl1271_init_pta(wl); | |
688 | if (ret < 0) | |
689 | return ret; | |
690 | ||
f5fc0f86 LC |
691 | /* Default memory configuration */ |
692 | ret = wl1271_acx_init_mem_config(wl); | |
693 | if (ret < 0) | |
694 | return ret; | |
695 | ||
696 | /* RX config */ | |
08c1d1c7 | 697 | ret = wl12xx_init_rx_config(wl); |
f5fc0f86 LC |
698 | if (ret < 0) |
699 | goto out_free_memmap; | |
700 | ||
6e92b416 LC |
701 | ret = wl1271_acx_dco_itrim_params(wl); |
702 | if (ret < 0) | |
703 | goto out_free_memmap; | |
704 | ||
f5fc0f86 LC |
705 | /* Configure TX patch complete interrupt behavior */ |
706 | ret = wl1271_acx_tx_config_options(wl); | |
707 | if (ret < 0) | |
708 | goto out_free_memmap; | |
709 | ||
710 | /* RX complete interrupt pacing */ | |
711 | ret = wl1271_acx_init_rx_interrupt(wl); | |
712 | if (ret < 0) | |
713 | goto out_free_memmap; | |
714 | ||
f5fc0f86 LC |
715 | /* Energy detection */ |
716 | ret = wl1271_init_energy_detection(wl); | |
717 | if (ret < 0) | |
718 | goto out_free_memmap; | |
719 | ||
f5fc0f86 | 720 | /* Default fragmentation threshold */ |
5f704d18 | 721 | ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold); |
f5fc0f86 LC |
722 | if (ret < 0) |
723 | goto out_free_memmap; | |
724 | ||
f5fc0f86 | 725 | /* Enable data path */ |
94210897 | 726 | ret = wl1271_cmd_data_path(wl, 1); |
f5fc0f86 LC |
727 | if (ret < 0) |
728 | goto out_free_memmap; | |
729 | ||
38ad2d87 JO |
730 | /* configure PM */ |
731 | ret = wl1271_acx_pm_config(wl); | |
732 | if (ret < 0) | |
733 | goto out_free_memmap; | |
734 | ||
fa6ad9f0 EP |
735 | ret = wl12xx_acx_set_rate_mgmt_params(wl); |
736 | if (ret < 0) | |
737 | goto out_free_memmap; | |
738 | ||
9487775c EP |
739 | /* configure hangover */ |
740 | ret = wl12xx_acx_config_hangover(wl); | |
741 | if (ret < 0) | |
742 | goto out_free_memmap; | |
743 | ||
f5fc0f86 LC |
744 | return 0; |
745 | ||
746 | out_free_memmap: | |
747 | kfree(wl->target_mem_map); | |
34415236 | 748 | wl->target_mem_map = NULL; |
f5fc0f86 LC |
749 | |
750 | return ret; | |
751 | } |