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