Commit | Line | Data |
---|---|---|
f5fc0f86 LC |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2008-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 "wl1271_acx.h" | |
25 | ||
26 | #include <linux/module.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <linux/crc7.h> | |
29 | #include <linux/spi/spi.h> | |
5a0e3ad6 | 30 | #include <linux/slab.h> |
f5fc0f86 LC |
31 | |
32 | #include "wl1271.h" | |
33 | #include "wl12xx_80211.h" | |
34 | #include "wl1271_reg.h" | |
35 | #include "wl1271_spi.h" | |
36 | #include "wl1271_ps.h" | |
37 | ||
51f2be24 | 38 | int wl1271_acx_wake_up_conditions(struct wl1271 *wl) |
f5fc0f86 LC |
39 | { |
40 | struct acx_wake_up_condition *wake_up; | |
41 | int ret; | |
42 | ||
43 | wl1271_debug(DEBUG_ACX, "acx wake up conditions"); | |
44 | ||
45 | wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); | |
46 | if (!wake_up) { | |
47 | ret = -ENOMEM; | |
48 | goto out; | |
49 | } | |
50 | ||
51f2be24 JO |
51 | wake_up->wake_up_event = wl->conf.conn.wake_up_event; |
52 | wake_up->listen_interval = wl->conf.conn.listen_interval; | |
f5fc0f86 LC |
53 | |
54 | ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, | |
55 | wake_up, sizeof(*wake_up)); | |
56 | if (ret < 0) { | |
57 | wl1271_warning("could not set wake up conditions: %d", ret); | |
58 | goto out; | |
59 | } | |
60 | ||
61 | out: | |
62 | kfree(wake_up); | |
63 | return ret; | |
64 | } | |
65 | ||
66 | int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth) | |
67 | { | |
68 | struct acx_sleep_auth *auth; | |
69 | int ret; | |
70 | ||
71 | wl1271_debug(DEBUG_ACX, "acx sleep auth"); | |
72 | ||
73 | auth = kzalloc(sizeof(*auth), GFP_KERNEL); | |
74 | if (!auth) { | |
75 | ret = -ENOMEM; | |
76 | goto out; | |
77 | } | |
78 | ||
79 | auth->sleep_auth = sleep_auth; | |
80 | ||
81 | ret = wl1271_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); | |
82 | if (ret < 0) | |
83 | return ret; | |
84 | ||
85 | out: | |
86 | kfree(auth); | |
87 | return ret; | |
88 | } | |
89 | ||
90 | int wl1271_acx_fw_version(struct wl1271 *wl, char *buf, size_t len) | |
91 | { | |
92 | struct acx_revision *rev; | |
93 | int ret; | |
94 | ||
95 | wl1271_debug(DEBUG_ACX, "acx fw rev"); | |
96 | ||
97 | rev = kzalloc(sizeof(*rev), GFP_KERNEL); | |
98 | if (!rev) { | |
99 | ret = -ENOMEM; | |
100 | goto out; | |
101 | } | |
102 | ||
103 | ret = wl1271_cmd_interrogate(wl, ACX_FW_REV, rev, sizeof(*rev)); | |
104 | if (ret < 0) { | |
105 | wl1271_warning("ACX_FW_REV interrogate failed"); | |
106 | goto out; | |
107 | } | |
108 | ||
109 | /* be careful with the buffer sizes */ | |
110 | strncpy(buf, rev->fw_version, min(len, sizeof(rev->fw_version))); | |
111 | ||
112 | /* | |
113 | * if the firmware version string is exactly | |
114 | * sizeof(rev->fw_version) long or fw_len is less than | |
115 | * sizeof(rev->fw_version) it won't be null terminated | |
116 | */ | |
117 | buf[min(len, sizeof(rev->fw_version)) - 1] = '\0'; | |
118 | ||
119 | out: | |
120 | kfree(rev); | |
121 | return ret; | |
122 | } | |
123 | ||
124 | int wl1271_acx_tx_power(struct wl1271 *wl, int power) | |
125 | { | |
126 | struct acx_current_tx_power *acx; | |
127 | int ret; | |
128 | ||
129 | wl1271_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr"); | |
130 | ||
131 | if (power < 0 || power > 25) | |
132 | return -EINVAL; | |
133 | ||
134 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
135 | if (!acx) { | |
136 | ret = -ENOMEM; | |
137 | goto out; | |
138 | } | |
139 | ||
ac78403e LC |
140 | /* |
141 | * FIXME: This is a workaround needed while we don't the correct | |
142 | * calibration, to avoid distortions | |
143 | */ | |
144 | /* acx->current_tx_power = power * 10; */ | |
605351e2 | 145 | acx->current_tx_power = 120; |
f5fc0f86 LC |
146 | |
147 | ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx)); | |
148 | if (ret < 0) { | |
149 | wl1271_warning("configure of tx power failed: %d", ret); | |
150 | goto out; | |
151 | } | |
152 | ||
153 | out: | |
154 | kfree(acx); | |
155 | return ret; | |
156 | } | |
157 | ||
158 | int wl1271_acx_feature_cfg(struct wl1271 *wl) | |
159 | { | |
160 | struct acx_feature_config *feature; | |
161 | int ret; | |
162 | ||
163 | wl1271_debug(DEBUG_ACX, "acx feature cfg"); | |
164 | ||
165 | feature = kzalloc(sizeof(*feature), GFP_KERNEL); | |
166 | if (!feature) { | |
167 | ret = -ENOMEM; | |
168 | goto out; | |
169 | } | |
170 | ||
171 | /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ | |
172 | feature->data_flow_options = 0; | |
173 | feature->options = 0; | |
174 | ||
175 | ret = wl1271_cmd_configure(wl, ACX_FEATURE_CFG, | |
176 | feature, sizeof(*feature)); | |
177 | if (ret < 0) { | |
178 | wl1271_error("Couldnt set HW encryption"); | |
179 | goto out; | |
180 | } | |
181 | ||
182 | out: | |
183 | kfree(feature); | |
184 | return ret; | |
185 | } | |
186 | ||
187 | int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, | |
188 | size_t len) | |
189 | { | |
190 | int ret; | |
191 | ||
192 | wl1271_debug(DEBUG_ACX, "acx mem map"); | |
193 | ||
194 | ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len); | |
195 | if (ret < 0) | |
196 | return ret; | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
8793f9bb | 201 | int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl) |
f5fc0f86 LC |
202 | { |
203 | struct acx_rx_msdu_lifetime *acx; | |
204 | int ret; | |
205 | ||
206 | wl1271_debug(DEBUG_ACX, "acx rx msdu life time"); | |
207 | ||
208 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
209 | if (!acx) { | |
210 | ret = -ENOMEM; | |
211 | goto out; | |
212 | } | |
213 | ||
d0f63b20 | 214 | acx->lifetime = cpu_to_le32(wl->conf.rx.rx_msdu_life_time); |
f5fc0f86 LC |
215 | ret = wl1271_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, |
216 | acx, sizeof(*acx)); | |
217 | if (ret < 0) { | |
218 | wl1271_warning("failed to set rx msdu life time: %d", ret); | |
219 | goto out; | |
220 | } | |
221 | ||
222 | out: | |
223 | kfree(acx); | |
224 | return ret; | |
225 | } | |
226 | ||
227 | int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter) | |
228 | { | |
229 | struct acx_rx_config *rx_config; | |
230 | int ret; | |
231 | ||
232 | wl1271_debug(DEBUG_ACX, "acx rx config"); | |
233 | ||
234 | rx_config = kzalloc(sizeof(*rx_config), GFP_KERNEL); | |
235 | if (!rx_config) { | |
236 | ret = -ENOMEM; | |
237 | goto out; | |
238 | } | |
239 | ||
d0f63b20 LC |
240 | rx_config->config_options = cpu_to_le32(config); |
241 | rx_config->filter_options = cpu_to_le32(filter); | |
f5fc0f86 LC |
242 | |
243 | ret = wl1271_cmd_configure(wl, ACX_RX_CFG, | |
244 | rx_config, sizeof(*rx_config)); | |
245 | if (ret < 0) { | |
246 | wl1271_warning("failed to set rx config: %d", ret); | |
247 | goto out; | |
248 | } | |
249 | ||
250 | out: | |
251 | kfree(rx_config); | |
252 | return ret; | |
253 | } | |
254 | ||
255 | int wl1271_acx_pd_threshold(struct wl1271 *wl) | |
256 | { | |
257 | struct acx_packet_detection *pd; | |
258 | int ret; | |
259 | ||
260 | wl1271_debug(DEBUG_ACX, "acx data pd threshold"); | |
261 | ||
262 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | |
263 | if (!pd) { | |
264 | ret = -ENOMEM; | |
265 | goto out; | |
266 | } | |
267 | ||
d0f63b20 | 268 | pd->threshold = cpu_to_le32(wl->conf.rx.packet_detection_threshold); |
f5fc0f86 LC |
269 | |
270 | ret = wl1271_cmd_configure(wl, ACX_PD_THRESHOLD, pd, sizeof(*pd)); | |
271 | if (ret < 0) { | |
272 | wl1271_warning("failed to set pd threshold: %d", ret); | |
273 | goto out; | |
274 | } | |
275 | ||
276 | out: | |
277 | kfree(pd); | |
278 | return 0; | |
279 | } | |
280 | ||
281 | int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time) | |
282 | { | |
283 | struct acx_slot *slot; | |
284 | int ret; | |
285 | ||
286 | wl1271_debug(DEBUG_ACX, "acx slot"); | |
287 | ||
288 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | |
289 | if (!slot) { | |
290 | ret = -ENOMEM; | |
291 | goto out; | |
292 | } | |
293 | ||
294 | slot->wone_index = STATION_WONE_INDEX; | |
295 | slot->slot_time = slot_time; | |
296 | ||
297 | ret = wl1271_cmd_configure(wl, ACX_SLOT, slot, sizeof(*slot)); | |
298 | if (ret < 0) { | |
299 | wl1271_warning("failed to set slot time: %d", ret); | |
300 | goto out; | |
301 | } | |
302 | ||
303 | out: | |
304 | kfree(slot); | |
305 | return ret; | |
306 | } | |
307 | ||
c87dec9f JO |
308 | int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, |
309 | void *mc_list, u32 mc_list_len) | |
f5fc0f86 LC |
310 | { |
311 | struct acx_dot11_grp_addr_tbl *acx; | |
312 | int ret; | |
313 | ||
314 | wl1271_debug(DEBUG_ACX, "acx group address tbl"); | |
315 | ||
316 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
317 | if (!acx) { | |
318 | ret = -ENOMEM; | |
319 | goto out; | |
320 | } | |
321 | ||
322 | /* MAC filtering */ | |
c87dec9f JO |
323 | acx->enabled = enable; |
324 | acx->num_groups = mc_list_len; | |
325 | memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN); | |
f5fc0f86 LC |
326 | |
327 | ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, | |
328 | acx, sizeof(*acx)); | |
329 | if (ret < 0) { | |
330 | wl1271_warning("failed to set group addr table: %d", ret); | |
331 | goto out; | |
332 | } | |
333 | ||
334 | out: | |
335 | kfree(acx); | |
336 | return ret; | |
337 | } | |
338 | ||
339 | int wl1271_acx_service_period_timeout(struct wl1271 *wl) | |
340 | { | |
341 | struct acx_rx_timeout *rx_timeout; | |
342 | int ret; | |
343 | ||
344 | rx_timeout = kzalloc(sizeof(*rx_timeout), GFP_KERNEL); | |
345 | if (!rx_timeout) { | |
346 | ret = -ENOMEM; | |
347 | goto out; | |
348 | } | |
349 | ||
350 | wl1271_debug(DEBUG_ACX, "acx service period timeout"); | |
351 | ||
d0f63b20 LC |
352 | rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout); |
353 | rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout); | |
f5fc0f86 LC |
354 | |
355 | ret = wl1271_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT, | |
356 | rx_timeout, sizeof(*rx_timeout)); | |
357 | if (ret < 0) { | |
358 | wl1271_warning("failed to set service period timeout: %d", | |
359 | ret); | |
360 | goto out; | |
361 | } | |
362 | ||
363 | out: | |
364 | kfree(rx_timeout); | |
365 | return ret; | |
366 | } | |
367 | ||
368 | int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) | |
369 | { | |
370 | struct acx_rts_threshold *rts; | |
371 | int ret; | |
372 | ||
373 | wl1271_debug(DEBUG_ACX, "acx rts threshold"); | |
374 | ||
375 | rts = kzalloc(sizeof(*rts), GFP_KERNEL); | |
376 | if (!rts) { | |
377 | ret = -ENOMEM; | |
378 | goto out; | |
379 | } | |
380 | ||
d0f63b20 | 381 | rts->threshold = cpu_to_le16(rts_threshold); |
f5fc0f86 LC |
382 | |
383 | ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); | |
384 | if (ret < 0) { | |
385 | wl1271_warning("failed to set rts threshold: %d", ret); | |
386 | goto out; | |
387 | } | |
388 | ||
389 | out: | |
390 | kfree(rts); | |
391 | return ret; | |
392 | } | |
393 | ||
6e92b416 LC |
394 | int wl1271_acx_dco_itrim_params(struct wl1271 *wl) |
395 | { | |
396 | struct acx_dco_itrim_params *dco; | |
397 | struct conf_itrim_settings *c = &wl->conf.itrim; | |
398 | int ret; | |
399 | ||
400 | wl1271_debug(DEBUG_ACX, "acx dco itrim parameters"); | |
401 | ||
402 | dco = kzalloc(sizeof(*dco), GFP_KERNEL); | |
403 | if (!dco) { | |
404 | ret = -ENOMEM; | |
405 | goto out; | |
406 | } | |
407 | ||
408 | dco->enable = c->enable; | |
409 | dco->timeout = cpu_to_le32(c->timeout); | |
410 | ||
411 | ret = wl1271_cmd_configure(wl, ACX_SET_DCO_ITRIM_PARAMS, | |
412 | dco, sizeof(*dco)); | |
413 | if (ret < 0) { | |
414 | wl1271_warning("failed to set dco itrim parameters: %d", ret); | |
415 | goto out; | |
416 | } | |
417 | ||
418 | out: | |
419 | kfree(dco); | |
420 | return ret; | |
421 | } | |
422 | ||
1922167b | 423 | int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter) |
f5fc0f86 | 424 | { |
51f2be24 JO |
425 | struct acx_beacon_filter_option *beacon_filter = NULL; |
426 | int ret = 0; | |
f5fc0f86 LC |
427 | |
428 | wl1271_debug(DEBUG_ACX, "acx beacon filter opt"); | |
429 | ||
51f2be24 JO |
430 | if (enable_filter && |
431 | wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED) | |
432 | goto out; | |
433 | ||
f5fc0f86 LC |
434 | beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); |
435 | if (!beacon_filter) { | |
436 | ret = -ENOMEM; | |
437 | goto out; | |
438 | } | |
439 | ||
1922167b | 440 | beacon_filter->enable = enable_filter; |
51f2be24 JO |
441 | |
442 | /* | |
443 | * When set to zero, and the filter is enabled, beacons | |
444 | * without the unicast TIM bit set are dropped. | |
445 | */ | |
f5fc0f86 LC |
446 | beacon_filter->max_num_beacons = 0; |
447 | ||
448 | ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_OPT, | |
449 | beacon_filter, sizeof(*beacon_filter)); | |
450 | if (ret < 0) { | |
451 | wl1271_warning("failed to set beacon filter opt: %d", ret); | |
452 | goto out; | |
453 | } | |
454 | ||
455 | out: | |
456 | kfree(beacon_filter); | |
457 | return ret; | |
458 | } | |
459 | ||
460 | int wl1271_acx_beacon_filter_table(struct wl1271 *wl) | |
461 | { | |
462 | struct acx_beacon_filter_ie_table *ie_table; | |
51f2be24 | 463 | int i, idx = 0; |
f5fc0f86 | 464 | int ret; |
51f2be24 | 465 | bool vendor_spec = false; |
f5fc0f86 LC |
466 | |
467 | wl1271_debug(DEBUG_ACX, "acx beacon filter table"); | |
468 | ||
469 | ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL); | |
470 | if (!ie_table) { | |
471 | ret = -ENOMEM; | |
472 | goto out; | |
473 | } | |
474 | ||
1922167b | 475 | /* configure default beacon pass-through rules */ |
51f2be24 JO |
476 | ie_table->num_ie = 0; |
477 | for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) { | |
478 | struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]); | |
479 | ie_table->table[idx++] = r->ie; | |
480 | ie_table->table[idx++] = r->rule; | |
481 | ||
482 | if (r->ie == WLAN_EID_VENDOR_SPECIFIC) { | |
483 | /* only one vendor specific ie allowed */ | |
484 | if (vendor_spec) | |
485 | continue; | |
486 | ||
487 | /* for vendor specific rules configure the | |
488 | additional fields */ | |
489 | memcpy(&(ie_table->table[idx]), r->oui, | |
490 | CONF_BCN_IE_OUI_LEN); | |
491 | idx += CONF_BCN_IE_OUI_LEN; | |
492 | ie_table->table[idx++] = r->type; | |
493 | memcpy(&(ie_table->table[idx]), r->version, | |
494 | CONF_BCN_IE_VER_LEN); | |
495 | idx += CONF_BCN_IE_VER_LEN; | |
496 | vendor_spec = true; | |
497 | } | |
498 | ||
499 | ie_table->num_ie++; | |
500 | } | |
f5fc0f86 LC |
501 | |
502 | ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, | |
503 | ie_table, sizeof(*ie_table)); | |
504 | if (ret < 0) { | |
505 | wl1271_warning("failed to set beacon filter table: %d", ret); | |
506 | goto out; | |
507 | } | |
508 | ||
509 | out: | |
510 | kfree(ie_table); | |
511 | return ret; | |
512 | } | |
513 | ||
34415236 JO |
514 | int wl1271_acx_conn_monit_params(struct wl1271 *wl) |
515 | { | |
516 | struct acx_conn_monit_params *acx; | |
517 | int ret; | |
518 | ||
519 | wl1271_debug(DEBUG_ACX, "acx connection monitor parameters"); | |
520 | ||
521 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
522 | if (!acx) { | |
523 | ret = -ENOMEM; | |
524 | goto out; | |
525 | } | |
526 | ||
d0f63b20 LC |
527 | acx->synch_fail_thold = cpu_to_le32(wl->conf.conn.synch_fail_thold); |
528 | acx->bss_lose_timeout = cpu_to_le32(wl->conf.conn.bss_lose_timeout); | |
34415236 JO |
529 | |
530 | ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS, | |
531 | acx, sizeof(*acx)); | |
532 | if (ret < 0) { | |
533 | wl1271_warning("failed to set connection monitor " | |
534 | "parameters: %d", ret); | |
535 | goto out; | |
536 | } | |
537 | ||
538 | out: | |
539 | kfree(acx); | |
540 | return ret; | |
541 | } | |
542 | ||
543 | ||
f5fc0f86 LC |
544 | int wl1271_acx_sg_enable(struct wl1271 *wl) |
545 | { | |
546 | struct acx_bt_wlan_coex *pta; | |
547 | int ret; | |
548 | ||
549 | wl1271_debug(DEBUG_ACX, "acx sg enable"); | |
550 | ||
551 | pta = kzalloc(sizeof(*pta), GFP_KERNEL); | |
552 | if (!pta) { | |
553 | ret = -ENOMEM; | |
554 | goto out; | |
555 | } | |
556 | ||
557 | pta->enable = SG_ENABLE; | |
558 | ||
559 | ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); | |
560 | if (ret < 0) { | |
561 | wl1271_warning("failed to set softgemini enable: %d", ret); | |
562 | goto out; | |
563 | } | |
564 | ||
565 | out: | |
566 | kfree(pta); | |
567 | return ret; | |
568 | } | |
569 | ||
570 | int wl1271_acx_sg_cfg(struct wl1271 *wl) | |
571 | { | |
572 | struct acx_bt_wlan_coex_param *param; | |
2b60100b | 573 | struct conf_sg_settings *c = &wl->conf.sg; |
f5fc0f86 LC |
574 | int ret; |
575 | ||
576 | wl1271_debug(DEBUG_ACX, "acx sg cfg"); | |
577 | ||
578 | param = kzalloc(sizeof(*param), GFP_KERNEL); | |
579 | if (!param) { | |
580 | ret = -ENOMEM; | |
581 | goto out; | |
582 | } | |
583 | ||
584 | /* BT-WLAN coext parameters */ | |
d0f63b20 LC |
585 | param->per_threshold = cpu_to_le32(c->per_threshold); |
586 | param->max_scan_compensation_time = | |
587 | cpu_to_le32(c->max_scan_compensation_time); | |
588 | param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval); | |
2b60100b JO |
589 | param->load_ratio = c->load_ratio; |
590 | param->auto_ps_mode = c->auto_ps_mode; | |
591 | param->probe_req_compensation = c->probe_req_compensation; | |
592 | param->scan_window_compensation = c->scan_window_compensation; | |
593 | param->antenna_config = c->antenna_config; | |
594 | param->beacon_miss_threshold = c->beacon_miss_threshold; | |
d0f63b20 LC |
595 | param->rate_adaptation_threshold = |
596 | cpu_to_le32(c->rate_adaptation_threshold); | |
2b60100b | 597 | param->rate_adaptation_snr = c->rate_adaptation_snr; |
f5fc0f86 LC |
598 | |
599 | ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); | |
600 | if (ret < 0) { | |
601 | wl1271_warning("failed to set sg config: %d", ret); | |
602 | goto out; | |
603 | } | |
604 | ||
605 | out: | |
606 | kfree(param); | |
607 | return ret; | |
608 | } | |
609 | ||
610 | int wl1271_acx_cca_threshold(struct wl1271 *wl) | |
611 | { | |
612 | struct acx_energy_detection *detection; | |
613 | int ret; | |
614 | ||
615 | wl1271_debug(DEBUG_ACX, "acx cca threshold"); | |
616 | ||
617 | detection = kzalloc(sizeof(*detection), GFP_KERNEL); | |
618 | if (!detection) { | |
619 | ret = -ENOMEM; | |
620 | goto out; | |
621 | } | |
622 | ||
d0f63b20 | 623 | detection->rx_cca_threshold = cpu_to_le16(wl->conf.rx.rx_cca_threshold); |
45b531a8 | 624 | detection->tx_energy_detection = wl->conf.tx.tx_energy_detection; |
f5fc0f86 LC |
625 | |
626 | ret = wl1271_cmd_configure(wl, ACX_CCA_THRESHOLD, | |
627 | detection, sizeof(*detection)); | |
628 | if (ret < 0) { | |
629 | wl1271_warning("failed to set cca threshold: %d", ret); | |
630 | return ret; | |
631 | } | |
632 | ||
633 | out: | |
634 | kfree(detection); | |
635 | return ret; | |
636 | } | |
637 | ||
638 | int wl1271_acx_bcn_dtim_options(struct wl1271 *wl) | |
639 | { | |
640 | struct acx_beacon_broadcast *bb; | |
641 | int ret; | |
642 | ||
643 | wl1271_debug(DEBUG_ACX, "acx bcn dtim options"); | |
644 | ||
645 | bb = kzalloc(sizeof(*bb), GFP_KERNEL); | |
646 | if (!bb) { | |
647 | ret = -ENOMEM; | |
648 | goto out; | |
649 | } | |
650 | ||
d0f63b20 LC |
651 | bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout); |
652 | bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout); | |
51f2be24 JO |
653 | bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps; |
654 | bb->ps_poll_threshold = wl->conf.conn.ps_poll_threshold; | |
f5fc0f86 LC |
655 | |
656 | ret = wl1271_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb)); | |
657 | if (ret < 0) { | |
658 | wl1271_warning("failed to set rx config: %d", ret); | |
659 | goto out; | |
660 | } | |
661 | ||
662 | out: | |
663 | kfree(bb); | |
664 | return ret; | |
665 | } | |
666 | ||
667 | int wl1271_acx_aid(struct wl1271 *wl, u16 aid) | |
668 | { | |
669 | struct acx_aid *acx_aid; | |
670 | int ret; | |
671 | ||
672 | wl1271_debug(DEBUG_ACX, "acx aid"); | |
673 | ||
674 | acx_aid = kzalloc(sizeof(*acx_aid), GFP_KERNEL); | |
675 | if (!acx_aid) { | |
676 | ret = -ENOMEM; | |
677 | goto out; | |
678 | } | |
679 | ||
d0f63b20 | 680 | acx_aid->aid = cpu_to_le16(aid); |
f5fc0f86 LC |
681 | |
682 | ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid)); | |
683 | if (ret < 0) { | |
684 | wl1271_warning("failed to set aid: %d", ret); | |
685 | goto out; | |
686 | } | |
687 | ||
688 | out: | |
689 | kfree(acx_aid); | |
690 | return ret; | |
691 | } | |
692 | ||
693 | int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask) | |
694 | { | |
695 | struct acx_event_mask *mask; | |
696 | int ret; | |
697 | ||
698 | wl1271_debug(DEBUG_ACX, "acx event mbox mask"); | |
699 | ||
700 | mask = kzalloc(sizeof(*mask), GFP_KERNEL); | |
701 | if (!mask) { | |
702 | ret = -ENOMEM; | |
703 | goto out; | |
704 | } | |
705 | ||
706 | /* high event mask is unused */ | |
d0f63b20 LC |
707 | mask->high_event_mask = cpu_to_le32(0xffffffff); |
708 | mask->event_mask = cpu_to_le32(event_mask); | |
f5fc0f86 LC |
709 | |
710 | ret = wl1271_cmd_configure(wl, ACX_EVENT_MBOX_MASK, | |
711 | mask, sizeof(*mask)); | |
712 | if (ret < 0) { | |
713 | wl1271_warning("failed to set acx_event_mbox_mask: %d", ret); | |
714 | goto out; | |
715 | } | |
716 | ||
717 | out: | |
718 | kfree(mask); | |
719 | return ret; | |
720 | } | |
721 | ||
722 | int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble) | |
723 | { | |
724 | struct acx_preamble *acx; | |
725 | int ret; | |
726 | ||
727 | wl1271_debug(DEBUG_ACX, "acx_set_preamble"); | |
728 | ||
729 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
730 | if (!acx) { | |
731 | ret = -ENOMEM; | |
732 | goto out; | |
733 | } | |
734 | ||
735 | acx->preamble = preamble; | |
736 | ||
737 | ret = wl1271_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx)); | |
738 | if (ret < 0) { | |
739 | wl1271_warning("Setting of preamble failed: %d", ret); | |
740 | goto out; | |
741 | } | |
742 | ||
743 | out: | |
744 | kfree(acx); | |
745 | return ret; | |
746 | } | |
747 | ||
748 | int wl1271_acx_cts_protect(struct wl1271 *wl, | |
749 | enum acx_ctsprotect_type ctsprotect) | |
750 | { | |
751 | struct acx_ctsprotect *acx; | |
752 | int ret; | |
753 | ||
754 | wl1271_debug(DEBUG_ACX, "acx_set_ctsprotect"); | |
755 | ||
756 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
757 | if (!acx) { | |
758 | ret = -ENOMEM; | |
759 | goto out; | |
760 | } | |
761 | ||
762 | acx->ctsprotect = ctsprotect; | |
763 | ||
764 | ret = wl1271_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx)); | |
765 | if (ret < 0) { | |
766 | wl1271_warning("Setting of ctsprotect failed: %d", ret); | |
767 | goto out; | |
768 | } | |
769 | ||
770 | out: | |
771 | kfree(acx); | |
772 | return ret; | |
773 | } | |
774 | ||
775 | int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) | |
776 | { | |
777 | int ret; | |
778 | ||
779 | wl1271_debug(DEBUG_ACX, "acx statistics"); | |
780 | ||
781 | ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats, | |
782 | sizeof(*stats)); | |
783 | if (ret < 0) { | |
784 | wl1271_warning("acx statistics failed: %d", ret); | |
785 | return -ENOMEM; | |
786 | } | |
787 | ||
788 | return 0; | |
789 | } | |
790 | ||
830fb67b | 791 | int wl1271_acx_rate_policies(struct wl1271 *wl) |
f5fc0f86 LC |
792 | { |
793 | struct acx_rate_policy *acx; | |
45b531a8 | 794 | struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf; |
830fb67b | 795 | int idx = 0; |
f5fc0f86 LC |
796 | int ret = 0; |
797 | ||
798 | wl1271_debug(DEBUG_ACX, "acx rate policies"); | |
799 | ||
800 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
801 | ||
802 | if (!acx) { | |
803 | ret = -ENOMEM; | |
804 | goto out; | |
805 | } | |
806 | ||
830fb67b JO |
807 | /* configure one basic rate class */ |
808 | idx = ACX_TX_BASIC_RATE; | |
809 | acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->basic_rate_set); | |
810 | acx->rate_class[idx].short_retry_limit = c->short_retry_limit; | |
811 | acx->rate_class[idx].long_retry_limit = c->long_retry_limit; | |
812 | acx->rate_class[idx].aflags = c->aflags; | |
813 | ||
814 | /* configure one AP supported rate class */ | |
815 | idx = ACX_TX_AP_FULL_RATE; | |
816 | acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->rate_set); | |
817 | acx->rate_class[idx].short_retry_limit = c->short_retry_limit; | |
818 | acx->rate_class[idx].long_retry_limit = c->long_retry_limit; | |
819 | acx->rate_class[idx].aflags = c->aflags; | |
820 | ||
821 | acx->rate_class_cnt = cpu_to_le32(ACX_TX_RATE_POLICY_CNT); | |
f5fc0f86 LC |
822 | |
823 | ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); | |
824 | if (ret < 0) { | |
825 | wl1271_warning("Setting of rate policies failed: %d", ret); | |
826 | goto out; | |
827 | } | |
828 | ||
829 | out: | |
830 | kfree(acx); | |
831 | return ret; | |
832 | } | |
833 | ||
243eeb51 KV |
834 | int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, |
835 | u8 aifsn, u16 txop) | |
f5fc0f86 LC |
836 | { |
837 | struct acx_ac_cfg *acx; | |
243eeb51 | 838 | int ret = 0; |
f5fc0f86 | 839 | |
243eeb51 KV |
840 | wl1271_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d " |
841 | "aifs %d txop %d", ac, cw_min, cw_max, aifsn, txop); | |
f5fc0f86 LC |
842 | |
843 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
844 | ||
845 | if (!acx) { | |
846 | ret = -ENOMEM; | |
847 | goto out; | |
848 | } | |
849 | ||
243eeb51 KV |
850 | acx->ac = ac; |
851 | acx->cw_min = cw_min; | |
852 | acx->cw_max = cpu_to_le16(cw_max); | |
853 | acx->aifsn = aifsn; | |
854 | acx->tx_op_limit = cpu_to_le16(txop); | |
f5fc0f86 | 855 | |
243eeb51 KV |
856 | ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); |
857 | if (ret < 0) { | |
858 | wl1271_warning("acx ac cfg failed: %d", ret); | |
859 | goto out; | |
f5fc0f86 LC |
860 | } |
861 | ||
862 | out: | |
863 | kfree(acx); | |
864 | return ret; | |
865 | } | |
866 | ||
f2054df5 KV |
867 | int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, |
868 | u8 tsid, u8 ps_scheme, u8 ack_policy, | |
869 | u32 apsd_conf0, u32 apsd_conf1) | |
f5fc0f86 LC |
870 | { |
871 | struct acx_tid_config *acx; | |
f2054df5 | 872 | int ret = 0; |
f5fc0f86 LC |
873 | |
874 | wl1271_debug(DEBUG_ACX, "acx tid config"); | |
875 | ||
876 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
877 | ||
878 | if (!acx) { | |
879 | ret = -ENOMEM; | |
880 | goto out; | |
881 | } | |
882 | ||
f2054df5 KV |
883 | acx->queue_id = queue_id; |
884 | acx->channel_type = channel_type; | |
885 | acx->tsid = tsid; | |
886 | acx->ps_scheme = ps_scheme; | |
887 | acx->ack_policy = ack_policy; | |
888 | acx->apsd_conf[0] = cpu_to_le32(apsd_conf0); | |
889 | acx->apsd_conf[1] = cpu_to_le32(apsd_conf1); | |
f5fc0f86 | 890 | |
f2054df5 KV |
891 | ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); |
892 | if (ret < 0) { | |
893 | wl1271_warning("Setting of tid config failed: %d", ret); | |
894 | goto out; | |
f5fc0f86 LC |
895 | } |
896 | ||
897 | out: | |
898 | kfree(acx); | |
899 | return ret; | |
900 | } | |
901 | ||
902 | int wl1271_acx_frag_threshold(struct wl1271 *wl) | |
903 | { | |
904 | struct acx_frag_threshold *acx; | |
905 | int ret = 0; | |
906 | ||
907 | wl1271_debug(DEBUG_ACX, "acx frag threshold"); | |
908 | ||
909 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
910 | ||
911 | if (!acx) { | |
912 | ret = -ENOMEM; | |
913 | goto out; | |
914 | } | |
915 | ||
d0f63b20 | 916 | acx->frag_threshold = cpu_to_le16(wl->conf.tx.frag_threshold); |
f5fc0f86 LC |
917 | ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx)); |
918 | if (ret < 0) { | |
919 | wl1271_warning("Setting of frag threshold failed: %d", ret); | |
920 | goto out; | |
921 | } | |
922 | ||
923 | out: | |
924 | kfree(acx); | |
925 | return ret; | |
926 | } | |
927 | ||
928 | int wl1271_acx_tx_config_options(struct wl1271 *wl) | |
929 | { | |
930 | struct acx_tx_config_options *acx; | |
931 | int ret = 0; | |
932 | ||
933 | wl1271_debug(DEBUG_ACX, "acx tx config options"); | |
934 | ||
935 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
936 | ||
937 | if (!acx) { | |
938 | ret = -ENOMEM; | |
939 | goto out; | |
940 | } | |
941 | ||
d0f63b20 LC |
942 | acx->tx_compl_timeout = cpu_to_le16(wl->conf.tx.tx_compl_timeout); |
943 | acx->tx_compl_threshold = cpu_to_le16(wl->conf.tx.tx_compl_threshold); | |
f5fc0f86 LC |
944 | ret = wl1271_cmd_configure(wl, ACX_TX_CONFIG_OPT, acx, sizeof(*acx)); |
945 | if (ret < 0) { | |
946 | wl1271_warning("Setting of tx options failed: %d", ret); | |
947 | goto out; | |
948 | } | |
949 | ||
950 | out: | |
951 | kfree(acx); | |
952 | return ret; | |
953 | } | |
954 | ||
955 | int wl1271_acx_mem_cfg(struct wl1271 *wl) | |
956 | { | |
957 | struct wl1271_acx_config_memory *mem_conf; | |
958 | int ret; | |
959 | ||
960 | wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); | |
961 | ||
962 | mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL); | |
963 | if (!mem_conf) { | |
964 | ret = -ENOMEM; | |
965 | goto out; | |
966 | } | |
967 | ||
968 | /* memory config */ | |
d0f63b20 | 969 | mem_conf->num_stations = DEFAULT_NUM_STATIONS; |
f5fc0f86 LC |
970 | mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS; |
971 | mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS; | |
972 | mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES; | |
d0f63b20 | 973 | mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); |
f5fc0f86 LC |
974 | |
975 | ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, | |
976 | sizeof(*mem_conf)); | |
977 | if (ret < 0) { | |
978 | wl1271_warning("wl1271 mem config failed: %d", ret); | |
979 | goto out; | |
980 | } | |
981 | ||
982 | out: | |
983 | kfree(mem_conf); | |
984 | return ret; | |
985 | } | |
986 | ||
987 | int wl1271_acx_init_mem_config(struct wl1271 *wl) | |
988 | { | |
989 | int ret; | |
990 | ||
991 | ret = wl1271_acx_mem_cfg(wl); | |
992 | if (ret < 0) | |
993 | return ret; | |
994 | ||
995 | wl->target_mem_map = kzalloc(sizeof(struct wl1271_acx_mem_map), | |
45b531a8 | 996 | GFP_KERNEL); |
f5fc0f86 LC |
997 | if (!wl->target_mem_map) { |
998 | wl1271_error("couldn't allocate target memory map"); | |
999 | return -ENOMEM; | |
1000 | } | |
1001 | ||
1002 | /* we now ask for the firmware built memory map */ | |
1003 | ret = wl1271_acx_mem_map(wl, (void *)wl->target_mem_map, | |
1004 | sizeof(struct wl1271_acx_mem_map)); | |
1005 | if (ret < 0) { | |
1006 | wl1271_error("couldn't retrieve firmware memory map"); | |
1007 | kfree(wl->target_mem_map); | |
1008 | wl->target_mem_map = NULL; | |
1009 | return ret; | |
1010 | } | |
1011 | ||
1012 | /* initialize TX block book keeping */ | |
d0f63b20 LC |
1013 | wl->tx_blocks_available = |
1014 | le32_to_cpu(wl->target_mem_map->num_tx_mem_blocks); | |
f5fc0f86 LC |
1015 | wl1271_debug(DEBUG_TX, "available tx blocks: %d", |
1016 | wl->tx_blocks_available); | |
1017 | ||
1018 | return 0; | |
1019 | } | |
1020 | ||
1021 | int wl1271_acx_init_rx_interrupt(struct wl1271 *wl) | |
1022 | { | |
1023 | struct wl1271_acx_rx_config_opt *rx_conf; | |
1024 | int ret; | |
1025 | ||
1026 | wl1271_debug(DEBUG_ACX, "wl1271 rx interrupt config"); | |
1027 | ||
1028 | rx_conf = kzalloc(sizeof(*rx_conf), GFP_KERNEL); | |
1029 | if (!rx_conf) { | |
1030 | ret = -ENOMEM; | |
1031 | goto out; | |
1032 | } | |
1033 | ||
d0f63b20 LC |
1034 | rx_conf->threshold = cpu_to_le16(wl->conf.rx.irq_pkt_threshold); |
1035 | rx_conf->timeout = cpu_to_le16(wl->conf.rx.irq_timeout); | |
1036 | rx_conf->mblk_threshold = cpu_to_le16(wl->conf.rx.irq_blk_threshold); | |
8793f9bb | 1037 | rx_conf->queue_type = wl->conf.rx.queue_type; |
f5fc0f86 LC |
1038 | |
1039 | ret = wl1271_cmd_configure(wl, ACX_RX_CONFIG_OPT, rx_conf, | |
1040 | sizeof(*rx_conf)); | |
1041 | if (ret < 0) { | |
1042 | wl1271_warning("wl1271 rx config opt failed: %d", ret); | |
1043 | goto out; | |
1044 | } | |
1045 | ||
1046 | out: | |
1047 | kfree(rx_conf); | |
1048 | return ret; | |
1049 | } | |
3cfd6cf9 | 1050 | |
11f70f97 JO |
1051 | int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable) |
1052 | { | |
1053 | struct wl1271_acx_bet_enable *acx = NULL; | |
1054 | int ret = 0; | |
1055 | ||
1056 | wl1271_debug(DEBUG_ACX, "acx bet enable"); | |
1057 | ||
1058 | if (enable && wl->conf.conn.bet_enable == CONF_BET_MODE_DISABLE) | |
1059 | goto out; | |
1060 | ||
1061 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
1062 | if (!acx) { | |
1063 | ret = -ENOMEM; | |
1064 | goto out; | |
1065 | } | |
1066 | ||
1067 | acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE; | |
1068 | acx->max_consecutive = wl->conf.conn.bet_max_consecutive; | |
1069 | ||
1070 | ret = wl1271_cmd_configure(wl, ACX_BET_ENABLE, acx, sizeof(*acx)); | |
1071 | if (ret < 0) { | |
1072 | wl1271_warning("acx bet enable failed: %d", ret); | |
1073 | goto out; | |
1074 | } | |
1075 | ||
1076 | out: | |
1077 | kfree(acx); | |
1078 | return ret; | |
1079 | } | |
01c09162 JO |
1080 | |
1081 | int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, | |
1082 | u8 version) | |
1083 | { | |
1084 | struct wl1271_acx_arp_filter *acx; | |
1085 | int ret; | |
1086 | ||
1087 | wl1271_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable); | |
1088 | ||
1089 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
1090 | if (!acx) { | |
1091 | ret = -ENOMEM; | |
1092 | goto out; | |
1093 | } | |
1094 | ||
1095 | acx->version = version; | |
1096 | acx->enable = enable; | |
1097 | ||
1098 | if (enable == true) { | |
1099 | if (version == ACX_IPV4_VERSION) | |
1100 | memcpy(acx->address, address, ACX_IPV4_ADDR_SIZE); | |
1101 | else if (version == ACX_IPV6_VERSION) | |
1102 | memcpy(acx->address, address, sizeof(acx->address)); | |
1103 | else | |
1104 | wl1271_error("Invalid IP version"); | |
1105 | } | |
1106 | ||
1107 | ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER, | |
1108 | acx, sizeof(*acx)); | |
1109 | if (ret < 0) { | |
1110 | wl1271_warning("failed to set arp ip filter: %d", ret); | |
1111 | goto out; | |
1112 | } | |
1113 | ||
1114 | out: | |
1115 | kfree(acx); | |
1116 | return ret; | |
1117 | } | |
38ad2d87 JO |
1118 | |
1119 | int wl1271_acx_pm_config(struct wl1271 *wl) | |
1120 | { | |
1121 | struct wl1271_acx_pm_config *acx = NULL; | |
1122 | struct conf_pm_config_settings *c = &wl->conf.pm_config; | |
1123 | int ret = 0; | |
1124 | ||
1125 | wl1271_debug(DEBUG_ACX, "acx pm config"); | |
1126 | ||
1127 | acx = kzalloc(sizeof(*acx), GFP_KERNEL); | |
1128 | if (!acx) { | |
1129 | ret = -ENOMEM; | |
1130 | goto out; | |
1131 | } | |
1132 | ||
1133 | acx->host_clk_settling_time = cpu_to_le32(c->host_clk_settling_time); | |
1134 | acx->host_fast_wakeup_support = c->host_fast_wakeup_support; | |
1135 | ||
1136 | ret = wl1271_cmd_configure(wl, ACX_PM_CONFIG, acx, sizeof(*acx)); | |
1137 | if (ret < 0) { | |
1138 | wl1271_warning("acx pm config failed: %d", ret); | |
1139 | goto out; | |
1140 | } | |
1141 | ||
1142 | out: | |
1143 | kfree(acx); | |
1144 | return ret; | |
1145 | } |