1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * This file is part of wl1271 |
4 | * |
5 | * Copyright (C) 2009 Nokia Corporation |
6 | * |
7 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include "debug.h" |
15 | #include "init.h" |
16 | #include "wl12xx_80211.h" |
17 | #include "acx.h" |
18 | #include "cmd.h" |
19 | #include "tx.h" |
20 | #include "io.h" |
21 | #include "hw_ops.h" |
22 | |
23 | int wl1271_init_templates_config(struct wl1271 *wl) |
24 | { |
25 | int ret, i; |
26 | size_t max_size; |
27 | |
28 | /* send empty templates for fw memory reservation */ |
29 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
30 | template_id: wl->scan_templ_id_2_4, NULL, |
31 | WL1271_CMD_TEMPL_MAX_SIZE, |
32 | index: 0, WL1271_RATE_AUTOMATIC); |
33 | if (ret < 0) |
34 | return ret; |
35 | |
36 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
37 | template_id: wl->scan_templ_id_5, |
38 | NULL, WL1271_CMD_TEMPL_MAX_SIZE, index: 0, |
39 | WL1271_RATE_AUTOMATIC); |
40 | if (ret < 0) |
41 | return ret; |
42 | |
43 | if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) { |
44 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
45 | template_id: wl->sched_scan_templ_id_2_4, |
46 | NULL, |
47 | WL1271_CMD_TEMPL_MAX_SIZE, |
48 | index: 0, WL1271_RATE_AUTOMATIC); |
49 | if (ret < 0) |
50 | return ret; |
51 | |
52 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
53 | template_id: wl->sched_scan_templ_id_5, |
54 | NULL, |
55 | WL1271_CMD_TEMPL_MAX_SIZE, |
56 | index: 0, WL1271_RATE_AUTOMATIC); |
57 | if (ret < 0) |
58 | return ret; |
59 | } |
60 | |
61 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
62 | template_id: CMD_TEMPL_NULL_DATA, NULL, |
63 | buf_len: sizeof(struct wl12xx_null_data_template), |
64 | index: 0, WL1271_RATE_AUTOMATIC); |
65 | if (ret < 0) |
66 | return ret; |
67 | |
68 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
69 | template_id: CMD_TEMPL_PS_POLL, NULL, |
70 | buf_len: sizeof(struct wl12xx_ps_poll_template), |
71 | index: 0, WL1271_RATE_AUTOMATIC); |
72 | if (ret < 0) |
73 | return ret; |
74 | |
75 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
76 | template_id: CMD_TEMPL_QOS_NULL_DATA, NULL, |
77 | buf_len: sizeof |
78 | (struct ieee80211_qos_hdr), |
79 | index: 0, WL1271_RATE_AUTOMATIC); |
80 | if (ret < 0) |
81 | return ret; |
82 | |
83 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
84 | template_id: CMD_TEMPL_PROBE_RESPONSE, NULL, |
85 | WL1271_CMD_TEMPL_DFLT_SIZE, |
86 | index: 0, WL1271_RATE_AUTOMATIC); |
87 | if (ret < 0) |
88 | return ret; |
89 | |
90 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
91 | template_id: CMD_TEMPL_BEACON, NULL, |
92 | WL1271_CMD_TEMPL_DFLT_SIZE, |
93 | index: 0, WL1271_RATE_AUTOMATIC); |
94 | if (ret < 0) |
95 | return ret; |
96 | |
97 | max_size = sizeof(struct wl12xx_arp_rsp_template) + |
98 | WL1271_EXTRA_SPACE_MAX; |
99 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
100 | template_id: CMD_TEMPL_ARP_RSP, NULL, |
101 | buf_len: max_size, |
102 | index: 0, WL1271_RATE_AUTOMATIC); |
103 | if (ret < 0) |
104 | return ret; |
105 | |
106 | /* |
107 | * Put very large empty placeholders for all templates. These |
108 | * reserve memory for later. |
109 | */ |
110 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
111 | template_id: CMD_TEMPL_AP_PROBE_RESPONSE, NULL, |
112 | WL1271_CMD_TEMPL_MAX_SIZE, |
113 | index: 0, WL1271_RATE_AUTOMATIC); |
114 | if (ret < 0) |
115 | return ret; |
116 | |
117 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
118 | template_id: CMD_TEMPL_AP_BEACON, NULL, |
119 | WL1271_CMD_TEMPL_MAX_SIZE, |
120 | index: 0, WL1271_RATE_AUTOMATIC); |
121 | if (ret < 0) |
122 | return ret; |
123 | |
124 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
125 | template_id: CMD_TEMPL_DEAUTH_AP, NULL, |
126 | buf_len: sizeof |
127 | (struct wl12xx_disconn_template), |
128 | index: 0, WL1271_RATE_AUTOMATIC); |
129 | if (ret < 0) |
130 | return ret; |
131 | |
132 | for (i = 0; i < WLCORE_MAX_KLV_TEMPLATES; i++) { |
133 | ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, |
134 | template_id: CMD_TEMPL_KLV, NULL, |
135 | buf_len: sizeof(struct ieee80211_qos_hdr), |
136 | index: i, WL1271_RATE_AUTOMATIC); |
137 | if (ret < 0) |
138 | return ret; |
139 | } |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | static int wl1271_ap_init_deauth_template(struct wl1271 *wl, |
145 | struct wl12xx_vif *wlvif) |
146 | { |
147 | struct wl12xx_disconn_template *tmpl; |
148 | int ret; |
149 | u32 rate; |
150 | |
151 | tmpl = kzalloc(size: 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 | |
160 | rate = wl1271_tx_min_rate_get(wl, rate_set: wlvif->basic_rate_set); |
161 | ret = wl1271_cmd_template_set(wl, role_id: wlvif->role_id, |
162 | template_id: CMD_TEMPL_DEAUTH_AP, |
163 | buf: tmpl, buf_len: sizeof(*tmpl), index: 0, rates: rate); |
164 | |
165 | out: |
166 | kfree(objp: tmpl); |
167 | return ret; |
168 | } |
169 | |
170 | static int wl1271_ap_init_null_template(struct wl1271 *wl, |
171 | struct ieee80211_vif *vif) |
172 | { |
173 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
174 | struct ieee80211_hdr_3addr *nullfunc; |
175 | int ret; |
176 | u32 rate; |
177 | |
178 | nullfunc = kzalloc(size: 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 | |
190 | memcpy(nullfunc->addr2, vif->addr, ETH_ALEN); |
191 | memcpy(nullfunc->addr3, vif->addr, ETH_ALEN); |
192 | |
193 | rate = wl1271_tx_min_rate_get(wl, rate_set: wlvif->basic_rate_set); |
194 | ret = wl1271_cmd_template_set(wl, role_id: wlvif->role_id, |
195 | template_id: CMD_TEMPL_NULL_DATA, buf: nullfunc, |
196 | buf_len: sizeof(*nullfunc), index: 0, rates: rate); |
197 | |
198 | out: |
199 | kfree(objp: nullfunc); |
200 | return ret; |
201 | } |
202 | |
203 | static int wl1271_ap_init_qos_null_template(struct wl1271 *wl, |
204 | struct ieee80211_vif *vif) |
205 | { |
206 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
207 | struct ieee80211_qos_hdr *qosnull; |
208 | int ret; |
209 | u32 rate; |
210 | |
211 | qosnull = kzalloc(size: 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 | |
223 | memcpy(qosnull->addr2, vif->addr, ETH_ALEN); |
224 | memcpy(qosnull->addr3, vif->addr, ETH_ALEN); |
225 | |
226 | rate = wl1271_tx_min_rate_get(wl, rate_set: wlvif->basic_rate_set); |
227 | ret = wl1271_cmd_template_set(wl, role_id: wlvif->role_id, |
228 | template_id: CMD_TEMPL_QOS_NULL_DATA, buf: qosnull, |
229 | buf_len: sizeof(*qosnull), index: 0, rates: rate); |
230 | |
231 | out: |
232 | kfree(objp: qosnull); |
233 | return ret; |
234 | } |
235 | |
236 | static int wl12xx_init_rx_config(struct wl1271 *wl) |
237 | { |
238 | int ret; |
239 | |
240 | ret = wl1271_acx_rx_msdu_life_time(wl); |
241 | if (ret < 0) |
242 | return ret; |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | static int wl12xx_init_phy_vif_config(struct wl1271 *wl, |
248 | struct wl12xx_vif *wlvif) |
249 | { |
250 | int ret; |
251 | |
252 | ret = wl1271_acx_slot(wl, wlvif, slot_time: DEFAULT_SLOT_TIME); |
253 | if (ret < 0) |
254 | return ret; |
255 | |
256 | ret = wl1271_acx_service_period_timeout(wl, wlvif); |
257 | if (ret < 0) |
258 | return ret; |
259 | |
260 | ret = wl1271_acx_rts_threshold(wl, wlvif, rts_threshold: wl->hw->wiphy->rts_threshold); |
261 | if (ret < 0) |
262 | return ret; |
263 | |
264 | return 0; |
265 | } |
266 | |
267 | static int wl1271_init_sta_beacon_filter(struct wl1271 *wl, |
268 | struct wl12xx_vif *wlvif) |
269 | { |
270 | int ret; |
271 | |
272 | ret = wl1271_acx_beacon_filter_table(wl, wlvif); |
273 | if (ret < 0) |
274 | return ret; |
275 | |
276 | /* disable beacon filtering until we get the first beacon */ |
277 | ret = wl1271_acx_beacon_filter_opt(wl, wlvif, enable_filter: false); |
278 | if (ret < 0) |
279 | return ret; |
280 | |
281 | return 0; |
282 | } |
283 | |
284 | int wl1271_init_pta(struct wl1271 *wl) |
285 | { |
286 | int ret; |
287 | |
288 | ret = wl12xx_acx_sg_cfg(wl); |
289 | if (ret < 0) |
290 | return ret; |
291 | |
292 | ret = wl1271_acx_sg_enable(wl, enable: wl->sg_enabled); |
293 | if (ret < 0) |
294 | return ret; |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | int wl1271_init_energy_detection(struct wl1271 *wl) |
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 | |
310 | static int wl1271_init_beacon_broadcast(struct wl1271 *wl, |
311 | struct wl12xx_vif *wlvif) |
312 | { |
313 | int ret; |
314 | |
315 | ret = wl1271_acx_bcn_dtim_options(wl, wlvif); |
316 | if (ret < 0) |
317 | return ret; |
318 | |
319 | return 0; |
320 | } |
321 | |
322 | static int wl12xx_init_fwlog(struct wl1271 *wl) |
323 | { |
324 | int ret; |
325 | |
326 | if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) |
327 | return 0; |
328 | |
329 | ret = wl12xx_cmd_config_fwlog(wl); |
330 | if (ret < 0) |
331 | return ret; |
332 | |
333 | return 0; |
334 | } |
335 | |
336 | /* generic sta initialization (non vif-specific) */ |
337 | int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
338 | { |
339 | int ret; |
340 | |
341 | /* PS config */ |
342 | ret = wl12xx_acx_config_ps(wl, wlvif); |
343 | if (ret < 0) |
344 | return ret; |
345 | |
346 | /* FM WLAN coexistence */ |
347 | ret = wl1271_acx_fm_coex(wl); |
348 | if (ret < 0) |
349 | return ret; |
350 | |
351 | ret = wl1271_acx_sta_rate_policies(wl, wlvif); |
352 | if (ret < 0) |
353 | return ret; |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl, |
359 | struct ieee80211_vif *vif) |
360 | { |
361 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
362 | int ret; |
363 | |
364 | /* disable the keep-alive feature */ |
365 | ret = wl1271_acx_keep_alive_mode(wl, vif: wlvif, enable: false); |
366 | if (ret < 0) |
367 | return ret; |
368 | |
369 | return 0; |
370 | } |
371 | |
372 | /* generic ap initialization (non vif-specific) */ |
373 | static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
374 | { |
375 | int ret; |
376 | |
377 | ret = wl1271_init_ap_rates(wl, wlvif); |
378 | if (ret < 0) |
379 | return ret; |
380 | |
381 | /* configure AP sleep, if enabled */ |
382 | ret = wlcore_hw_ap_sleep(wl); |
383 | if (ret < 0) |
384 | return ret; |
385 | |
386 | return 0; |
387 | } |
388 | |
389 | int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif) |
390 | { |
391 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
392 | int ret; |
393 | |
394 | ret = wl1271_ap_init_deauth_template(wl, wlvif); |
395 | if (ret < 0) |
396 | return ret; |
397 | |
398 | ret = wl1271_ap_init_null_template(wl, vif); |
399 | if (ret < 0) |
400 | return ret; |
401 | |
402 | ret = wl1271_ap_init_qos_null_template(wl, vif); |
403 | if (ret < 0) |
404 | return ret; |
405 | |
406 | /* |
407 | * when operating as AP we want to receive external beacons for |
408 | * configuring ERP protection. |
409 | */ |
410 | ret = wl1271_acx_beacon_filter_opt(wl, wlvif, enable_filter: false); |
411 | if (ret < 0) |
412 | return ret; |
413 | |
414 | return 0; |
415 | } |
416 | |
417 | static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl, |
418 | struct ieee80211_vif *vif) |
419 | { |
420 | return wl1271_ap_init_templates(wl, vif); |
421 | } |
422 | |
423 | int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
424 | { |
425 | int i, ret; |
426 | struct conf_tx_rate_class rc; |
427 | u32 supported_rates; |
428 | |
429 | wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x" , |
430 | wlvif->basic_rate_set); |
431 | |
432 | if (wlvif->basic_rate_set == 0) |
433 | return -EINVAL; |
434 | |
435 | rc.enabled_rates = wlvif->basic_rate_set; |
436 | rc.long_retry_limit = 10; |
437 | rc.short_retry_limit = 10; |
438 | rc.aflags = 0; |
439 | ret = wl1271_acx_ap_rate_policy(wl, c: &rc, idx: wlvif->ap.mgmt_rate_idx); |
440 | if (ret < 0) |
441 | return ret; |
442 | |
443 | /* use the min basic rate for AP broadcast/multicast */ |
444 | rc.enabled_rates = wl1271_tx_min_rate_get(wl, rate_set: wlvif->basic_rate_set); |
445 | rc.short_retry_limit = 10; |
446 | rc.long_retry_limit = 10; |
447 | rc.aflags = 0; |
448 | ret = wl1271_acx_ap_rate_policy(wl, c: &rc, idx: wlvif->ap.bcast_rate_idx); |
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 | */ |
456 | if (wl->ofdm_only_ap && (wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) |
457 | supported_rates = CONF_TX_OFDM_RATES; |
458 | else |
459 | supported_rates = CONF_TX_ENABLED_RATES; |
460 | |
461 | /* unconditionally enable HT rates */ |
462 | supported_rates |= CONF_TX_MCS_RATES; |
463 | |
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 | |
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; |
473 | ret = wl1271_acx_ap_rate_policy(wl, c: &rc, |
474 | idx: wlvif->ap.ucast_rate_idx[i]); |
475 | if (ret < 0) |
476 | return ret; |
477 | } |
478 | |
479 | return 0; |
480 | } |
481 | |
482 | static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
483 | { |
484 | /* Reset the BA RX indicators */ |
485 | wlvif->ba_allowed = true; |
486 | wl->ba_rx_session_count = 0; |
487 | |
488 | /* BA is supported in STA/AP modes */ |
489 | if (wlvif->bss_type != BSS_TYPE_AP_BSS && |
490 | wlvif->bss_type != BSS_TYPE_STA_BSS) { |
491 | wlvif->ba_support = false; |
492 | return 0; |
493 | } |
494 | |
495 | wlvif->ba_support = true; |
496 | |
497 | /* 802.11n initiator BA session setting */ |
498 | return wl12xx_acx_set_ba_initiator_policy(wl, wlvif); |
499 | } |
500 | |
501 | /* vif-specifc initialization */ |
502 | static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
503 | { |
504 | int ret; |
505 | |
506 | ret = wl1271_acx_group_address_tbl(wl, wlvif, enable: true, NULL, mc_list_len: 0); |
507 | if (ret < 0) |
508 | return ret; |
509 | |
510 | /* Initialize connection monitoring thresholds */ |
511 | ret = wl1271_acx_conn_monit_params(wl, wlvif, enable: false); |
512 | if (ret < 0) |
513 | return ret; |
514 | |
515 | /* Beacon filtering */ |
516 | ret = wl1271_init_sta_beacon_filter(wl, wlvif); |
517 | if (ret < 0) |
518 | return ret; |
519 | |
520 | /* Beacons and broadcast settings */ |
521 | ret = wl1271_init_beacon_broadcast(wl, wlvif); |
522 | if (ret < 0) |
523 | return ret; |
524 | |
525 | /* Configure rssi/snr averaging weights */ |
526 | ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif); |
527 | if (ret < 0) |
528 | return ret; |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | /* vif-specific initialization */ |
534 | static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
535 | { |
536 | int ret; |
537 | |
538 | ret = wl1271_acx_ap_max_tx_retry(wl, wlvif); |
539 | if (ret < 0) |
540 | return ret; |
541 | |
542 | /* initialize Tx power */ |
543 | ret = wl1271_acx_tx_power(wl, wlvif, power: wlvif->power_level); |
544 | if (ret < 0) |
545 | return ret; |
546 | |
547 | if (wl->radar_debug_mode) |
548 | wlcore_cmd_generic_cfg(wl, wlvif, |
549 | feature: WLCORE_CFG_FEATURE_RADAR_DEBUG, |
550 | enable: wl->radar_debug_mode, value: 0); |
551 | |
552 | return 0; |
553 | } |
554 | |
555 | int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) |
556 | { |
557 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
558 | struct conf_tx_ac_category *conf_ac; |
559 | struct conf_tx_tid *conf_tid; |
560 | bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); |
561 | int ret, i; |
562 | |
563 | /* consider all existing roles before configuring psm. */ |
564 | |
565 | if (wl->ap_count == 0 && is_ap) { /* first AP */ |
566 | ret = wl1271_acx_sleep_auth(wl, sleep_auth: WL1271_PSM_ELP); |
567 | if (ret < 0) |
568 | return ret; |
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; |
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, sleep_auth: sta_auth); |
581 | /* Configure for ELP power saving */ |
582 | else |
583 | ret = wl1271_acx_sleep_auth(wl, sleep_auth: WL1271_PSM_ELP); |
584 | |
585 | if (ret < 0) |
586 | return ret; |
587 | } |
588 | |
589 | /* Mode specific init */ |
590 | if (is_ap) { |
591 | ret = wl1271_ap_hw_init(wl, wlvif); |
592 | if (ret < 0) |
593 | return ret; |
594 | |
595 | ret = wl12xx_init_ap_role(wl, wlvif); |
596 | if (ret < 0) |
597 | return ret; |
598 | } else { |
599 | ret = wl1271_sta_hw_init(wl, wlvif); |
600 | if (ret < 0) |
601 | return ret; |
602 | |
603 | ret = wl12xx_init_sta_role(wl, wlvif); |
604 | if (ret < 0) |
605 | return ret; |
606 | } |
607 | |
608 | wl12xx_init_phy_vif_config(wl, wlvif); |
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]; |
614 | ret = wl1271_acx_ac_cfg(wl, wlvif, ac: conf_ac->ac, |
615 | cw_min: conf_ac->cw_min, cw_max: conf_ac->cw_max, |
616 | aifsn: conf_ac->aifsn, txop: conf_ac->tx_op_limit); |
617 | if (ret < 0) |
618 | return ret; |
619 | |
620 | conf_tid = &wl->conf.tx.tid_conf[i]; |
621 | ret = wl1271_acx_tid_cfg(wl, wlvif, |
622 | queue_id: conf_tid->queue_id, |
623 | channel_type: conf_tid->channel_type, |
624 | tsid: conf_tid->tsid, |
625 | ps_scheme: conf_tid->ps_scheme, |
626 | ack_policy: conf_tid->ack_policy, |
627 | apsd_conf0: conf_tid->apsd_conf[0], |
628 | apsd_conf1: conf_tid->apsd_conf[1]); |
629 | if (ret < 0) |
630 | return ret; |
631 | } |
632 | |
633 | /* Configure HW encryption */ |
634 | ret = wl1271_acx_feature_cfg(wl, wlvif); |
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 |
642 | ret = wl1271_sta_hw_init_post_mem(wl, vif); |
643 | |
644 | if (ret < 0) |
645 | return ret; |
646 | |
647 | /* Configure initiator BA sessions policies */ |
648 | ret = wl1271_set_ba_policies(wl, wlvif); |
649 | if (ret < 0) |
650 | return ret; |
651 | |
652 | ret = wlcore_hw_init_vif(wl, wlvif); |
653 | if (ret < 0) |
654 | return ret; |
655 | |
656 | return 0; |
657 | } |
658 | |
659 | int wl1271_hw_init(struct wl1271 *wl) |
660 | { |
661 | int ret; |
662 | |
663 | /* Chip-specific hw init */ |
664 | ret = wl->ops->hw_init(wl); |
665 | if (ret < 0) |
666 | return ret; |
667 | |
668 | /* Init templates */ |
669 | ret = wl1271_init_templates_config(wl); |
670 | if (ret < 0) |
671 | return ret; |
672 | |
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); |
679 | if (ret < 0) |
680 | return ret; |
681 | |
682 | ret = wlcore_cmd_regdomain_config_locked(wl); |
683 | if (ret < 0) |
684 | return ret; |
685 | |
686 | /* Bluetooth WLAN coexistence */ |
687 | ret = wl1271_init_pta(wl); |
688 | if (ret < 0) |
689 | return ret; |
690 | |
691 | /* Default memory configuration */ |
692 | ret = wl1271_acx_init_mem_config(wl); |
693 | if (ret < 0) |
694 | return ret; |
695 | |
696 | /* RX config */ |
697 | ret = wl12xx_init_rx_config(wl); |
698 | if (ret < 0) |
699 | goto out_free_memmap; |
700 | |
701 | ret = wl1271_acx_dco_itrim_params(wl); |
702 | if (ret < 0) |
703 | goto out_free_memmap; |
704 | |
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 | |
715 | /* Energy detection */ |
716 | ret = wl1271_init_energy_detection(wl); |
717 | if (ret < 0) |
718 | goto out_free_memmap; |
719 | |
720 | /* Default fragmentation threshold */ |
721 | ret = wl1271_acx_frag_threshold(wl, frag_threshold: wl->hw->wiphy->frag_threshold); |
722 | if (ret < 0) |
723 | goto out_free_memmap; |
724 | |
725 | /* Enable data path */ |
726 | ret = wl1271_cmd_data_path(wl, enable: 1); |
727 | if (ret < 0) |
728 | goto out_free_memmap; |
729 | |
730 | /* configure PM */ |
731 | ret = wl1271_acx_pm_config(wl); |
732 | if (ret < 0) |
733 | goto out_free_memmap; |
734 | |
735 | ret = wl12xx_acx_set_rate_mgmt_params(wl); |
736 | if (ret < 0) |
737 | goto out_free_memmap; |
738 | |
739 | /* configure hangover */ |
740 | ret = wl12xx_acx_config_hangover(wl); |
741 | if (ret < 0) |
742 | goto out_free_memmap; |
743 | |
744 | return 0; |
745 | |
746 | out_free_memmap: |
747 | kfree(objp: wl->target_mem_map); |
748 | wl->target_mem_map = NULL; |
749 | |
750 | return ret; |
751 | } |
752 | |