]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
9bc6772e | 2 | #include "cmd.h" |
2f01a1f5 KV |
3 | |
4 | #include <linux/module.h> | |
5a0e3ad6 | 5 | #include <linux/slab.h> |
64322e28 | 6 | #include <linux/etherdevice.h> |
2f01a1f5 | 7 | |
13674118 | 8 | #include "wl1251.h" |
9bc6772e KV |
9 | #include "reg.h" |
10 | #include "io.h" | |
11 | #include "ps.h" | |
12 | #include "acx.h" | |
ff25839b KV |
13 | |
14 | /** | |
15 | * send command to firmware | |
16 | * | |
17 | * @wl: wl struct | |
18 | * @id: command id | |
19 | * @buf: buffer containing the command, must work with dma | |
20 | * @len: length of the buffer | |
21 | */ | |
80301cdc | 22 | int wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len) |
2f01a1f5 | 23 | { |
80301cdc | 24 | struct wl1251_cmd_header *cmd; |
2f01a1f5 | 25 | unsigned long timeout; |
2f01a1f5 KV |
26 | u32 intr; |
27 | int ret = 0; | |
28 | ||
ff25839b KV |
29 | cmd = buf; |
30 | cmd->id = id; | |
31 | cmd->status = 0; | |
32 | ||
33 | WARN_ON(len % 4 != 0); | |
2f01a1f5 | 34 | |
0764de64 | 35 | wl1251_mem_write(wl, wl->cmd_box_addr, buf, len); |
2f01a1f5 | 36 | |
80301cdc | 37 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); |
2f01a1f5 | 38 | |
80301cdc | 39 | timeout = jiffies + msecs_to_jiffies(WL1251_COMMAND_TIMEOUT); |
2f01a1f5 | 40 | |
80301cdc | 41 | intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); |
0e71bb08 | 42 | while (!(intr & WL1251_ACX_INTR_CMD_COMPLETE)) { |
2f01a1f5 | 43 | if (time_after(jiffies, timeout)) { |
80301cdc | 44 | wl1251_error("command complete timeout"); |
2f01a1f5 KV |
45 | ret = -ETIMEDOUT; |
46 | goto out; | |
47 | } | |
48 | ||
49 | msleep(1); | |
50 | ||
80301cdc | 51 | intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); |
2f01a1f5 KV |
52 | } |
53 | ||
80301cdc | 54 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, |
0e71bb08 | 55 | WL1251_ACX_INTR_CMD_COMPLETE); |
2f01a1f5 KV |
56 | |
57 | out: | |
2f01a1f5 KV |
58 | return ret; |
59 | } | |
60 | ||
ff25839b KV |
61 | /** |
62 | * send test command to firmware | |
63 | * | |
64 | * @wl: wl struct | |
6021b289 | 65 | * @buf: buffer containing the command, with all headers, must work with dma |
ff25839b KV |
66 | * @len: length of the buffer |
67 | * @answer: is answer needed | |
ff25839b | 68 | */ |
80301cdc | 69 | int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer) |
2f01a1f5 KV |
70 | { |
71 | int ret; | |
72 | ||
80301cdc | 73 | wl1251_debug(DEBUG_CMD, "cmd test"); |
2f01a1f5 | 74 | |
80301cdc | 75 | ret = wl1251_cmd_send(wl, CMD_TEST, buf, buf_len); |
ff25839b | 76 | |
2f01a1f5 | 77 | if (ret < 0) { |
80301cdc | 78 | wl1251_warning("TEST command failed"); |
6021b289 | 79 | return ret; |
2f01a1f5 KV |
80 | } |
81 | ||
82 | if (answer) { | |
80301cdc | 83 | struct wl1251_command *cmd_answer; |
6021b289 | 84 | |
2f01a1f5 KV |
85 | /* |
86 | * The test command got in, we can read the answer. | |
80301cdc | 87 | * The answer would be a wl1251_command, where the |
2f01a1f5 KV |
88 | * parameter array contains the actual answer. |
89 | */ | |
0764de64 | 90 | wl1251_mem_read(wl, wl->cmd_box_addr, buf, buf_len); |
2f01a1f5 | 91 | |
6021b289 AK |
92 | cmd_answer = buf; |
93 | ||
94 | if (cmd_answer->header.status != CMD_STATUS_SUCCESS) | |
80301cdc | 95 | wl1251_error("TEST command answer error: %d", |
6021b289 | 96 | cmd_answer->header.status); |
2f01a1f5 KV |
97 | } |
98 | ||
6021b289 | 99 | return 0; |
2f01a1f5 KV |
100 | } |
101 | ||
ff25839b KV |
102 | /** |
103 | * read acx from firmware | |
104 | * | |
105 | * @wl: wl struct | |
106 | * @id: acx id | |
107 | * @buf: buffer for the response, including all headers, must work with dma | |
25985edc | 108 | * @len: length of buf |
ff25839b | 109 | */ |
80301cdc | 110 | int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len) |
2f01a1f5 | 111 | { |
ff25839b | 112 | struct acx_header *acx = buf; |
2f01a1f5 KV |
113 | int ret; |
114 | ||
80301cdc | 115 | wl1251_debug(DEBUG_CMD, "cmd interrogate"); |
2f01a1f5 | 116 | |
ff25839b | 117 | acx->id = id; |
2f01a1f5 | 118 | |
ff25839b KV |
119 | /* payload length, does not include any headers */ |
120 | acx->len = len - sizeof(*acx); | |
121 | ||
80301cdc | 122 | ret = wl1251_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); |
2f01a1f5 | 123 | if (ret < 0) { |
80301cdc | 124 | wl1251_error("INTERROGATE command failed"); |
ff25839b | 125 | goto out; |
2f01a1f5 KV |
126 | } |
127 | ||
2f01a1f5 | 128 | /* the interrogate command got in, we can read the answer */ |
0764de64 | 129 | wl1251_mem_read(wl, wl->cmd_box_addr, buf, len); |
2f01a1f5 | 130 | |
ff25839b KV |
131 | acx = buf; |
132 | if (acx->cmd.status != CMD_STATUS_SUCCESS) | |
80301cdc | 133 | wl1251_error("INTERROGATE command error: %d", |
ff25839b | 134 | acx->cmd.status); |
2f01a1f5 | 135 | |
ff25839b KV |
136 | out: |
137 | return ret; | |
2f01a1f5 KV |
138 | } |
139 | ||
ff25839b KV |
140 | /** |
141 | * write acx value to firmware | |
142 | * | |
143 | * @wl: wl struct | |
144 | * @id: acx id | |
145 | * @buf: buffer containing acx, including all headers, must work with dma | |
146 | * @len: length of buf | |
147 | */ | |
80301cdc | 148 | int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len) |
2f01a1f5 | 149 | { |
ff25839b | 150 | struct acx_header *acx = buf; |
2f01a1f5 KV |
151 | int ret; |
152 | ||
80301cdc | 153 | wl1251_debug(DEBUG_CMD, "cmd configure"); |
2f01a1f5 | 154 | |
ff25839b KV |
155 | acx->id = id; |
156 | ||
157 | /* payload length, does not include any headers */ | |
158 | acx->len = len - sizeof(*acx); | |
159 | ||
80301cdc | 160 | ret = wl1251_cmd_send(wl, CMD_CONFIGURE, acx, len); |
2f01a1f5 | 161 | if (ret < 0) { |
80301cdc | 162 | wl1251_warning("CONFIGURE command NOK"); |
2f01a1f5 KV |
163 | return ret; |
164 | } | |
165 | ||
166 | return 0; | |
2f01a1f5 KV |
167 | } |
168 | ||
80301cdc | 169 | int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, |
2f01a1f5 KV |
170 | void *bitmap, u16 bitmap_len, u8 bitmap_control) |
171 | { | |
80301cdc | 172 | struct wl1251_cmd_vbm_update *vbm; |
2f01a1f5 KV |
173 | int ret; |
174 | ||
80301cdc | 175 | wl1251_debug(DEBUG_CMD, "cmd vbm"); |
2f01a1f5 | 176 | |
ff25839b KV |
177 | vbm = kzalloc(sizeof(*vbm), GFP_KERNEL); |
178 | if (!vbm) { | |
179 | ret = -ENOMEM; | |
180 | goto out; | |
181 | } | |
182 | ||
2f01a1f5 | 183 | /* Count and period will be filled by the target */ |
ff25839b | 184 | vbm->tim.bitmap_ctrl = bitmap_control; |
2f01a1f5 | 185 | if (bitmap_len > PARTIAL_VBM_MAX) { |
80301cdc | 186 | wl1251_warning("cmd vbm len is %d B, truncating to %d", |
2f01a1f5 KV |
187 | bitmap_len, PARTIAL_VBM_MAX); |
188 | bitmap_len = PARTIAL_VBM_MAX; | |
189 | } | |
ff25839b KV |
190 | memcpy(vbm->tim.pvb_field, bitmap, bitmap_len); |
191 | vbm->tim.identity = identity; | |
192 | vbm->tim.length = bitmap_len + 3; | |
2f01a1f5 | 193 | |
ff25839b | 194 | vbm->len = cpu_to_le16(bitmap_len + 5); |
2f01a1f5 | 195 | |
80301cdc | 196 | ret = wl1251_cmd_send(wl, CMD_VBM, vbm, sizeof(*vbm)); |
2f01a1f5 | 197 | if (ret < 0) { |
80301cdc | 198 | wl1251_error("VBM command failed"); |
ff25839b | 199 | goto out; |
2f01a1f5 KV |
200 | } |
201 | ||
ff25839b KV |
202 | out: |
203 | kfree(vbm); | |
9f19fa62 | 204 | return ret; |
2f01a1f5 KV |
205 | } |
206 | ||
9281691f | 207 | int wl1251_cmd_data_path_rx(struct wl1251 *wl, u8 channel, bool enable) |
2f01a1f5 | 208 | { |
ff25839b | 209 | struct cmd_enabledisable_path *cmd; |
2f01a1f5 | 210 | int ret; |
9281691f | 211 | u16 cmd_rx; |
2f01a1f5 | 212 | |
80301cdc | 213 | wl1251_debug(DEBUG_CMD, "cmd data path"); |
2f01a1f5 | 214 | |
ff25839b KV |
215 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); |
216 | if (!cmd) { | |
217 | ret = -ENOMEM; | |
218 | goto out; | |
219 | } | |
220 | ||
221 | cmd->channel = channel; | |
222 | ||
9281691f | 223 | if (enable) |
2f01a1f5 | 224 | cmd_rx = CMD_ENABLE_RX; |
9281691f | 225 | else |
2f01a1f5 | 226 | cmd_rx = CMD_DISABLE_RX; |
2f01a1f5 | 227 | |
80301cdc | 228 | ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); |
2f01a1f5 | 229 | if (ret < 0) { |
80301cdc | 230 | wl1251_error("rx %s cmd for channel %d failed", |
2f01a1f5 | 231 | enable ? "start" : "stop", channel); |
ff25839b | 232 | goto out; |
2f01a1f5 KV |
233 | } |
234 | ||
80301cdc | 235 | wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d", |
2f01a1f5 KV |
236 | enable ? "start" : "stop", channel); |
237 | ||
9281691f DG |
238 | out: |
239 | kfree(cmd); | |
240 | return ret; | |
241 | } | |
242 | ||
243 | int wl1251_cmd_data_path_tx(struct wl1251 *wl, u8 channel, bool enable) | |
244 | { | |
245 | struct cmd_enabledisable_path *cmd; | |
246 | int ret; | |
247 | u16 cmd_tx; | |
248 | ||
249 | wl1251_debug(DEBUG_CMD, "cmd data path"); | |
250 | ||
251 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
252 | if (!cmd) | |
253 | return -ENOMEM; | |
254 | ||
255 | cmd->channel = channel; | |
256 | ||
257 | if (enable) | |
258 | cmd_tx = CMD_ENABLE_TX; | |
259 | else | |
260 | cmd_tx = CMD_DISABLE_TX; | |
261 | ||
80301cdc | 262 | ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); |
9281691f | 263 | if (ret < 0) |
80301cdc | 264 | wl1251_error("tx %s cmd for channel %d failed", |
2f01a1f5 | 265 | enable ? "start" : "stop", channel); |
9281691f DG |
266 | else |
267 | wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d", | |
268 | enable ? "start" : "stop", channel); | |
2f01a1f5 | 269 | |
ff25839b KV |
270 | kfree(cmd); |
271 | return ret; | |
2f01a1f5 KV |
272 | } |
273 | ||
c88f8754 KV |
274 | int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, |
275 | u16 beacon_interval, u8 dtim_interval) | |
2f01a1f5 | 276 | { |
ff25839b | 277 | struct cmd_join *join; |
2f01a1f5 KV |
278 | int ret, i; |
279 | u8 *bssid; | |
280 | ||
ff25839b KV |
281 | join = kzalloc(sizeof(*join), GFP_KERNEL); |
282 | if (!join) { | |
283 | ret = -ENOMEM; | |
284 | goto out; | |
285 | } | |
286 | ||
c88f8754 | 287 | wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d", |
e2fd4611 | 288 | bss_type == BSS_TYPE_IBSS ? " ibss" : "", |
c88f8754 | 289 | channel, beacon_interval, dtim_interval); |
2f01a1f5 KV |
290 | |
291 | /* Reverse order BSSID */ | |
ff25839b | 292 | bssid = (u8 *) &join->bssid_lsb; |
2f01a1f5 KV |
293 | for (i = 0; i < ETH_ALEN; i++) |
294 | bssid[i] = wl->bssid[ETH_ALEN - i - 1]; | |
295 | ||
ff25839b KV |
296 | join->rx_config_options = wl->rx_config; |
297 | join->rx_filter_options = wl->rx_filter; | |
2f01a1f5 | 298 | |
ff25839b | 299 | join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | |
2f01a1f5 KV |
300 | RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; |
301 | ||
ff25839b KV |
302 | join->beacon_interval = beacon_interval; |
303 | join->dtim_interval = dtim_interval; | |
304 | join->bss_type = bss_type; | |
c88f8754 | 305 | join->channel = channel; |
ff25839b | 306 | join->ctrl = JOIN_CMD_CTRL_TX_FLUSH; |
2f01a1f5 | 307 | |
80301cdc | 308 | ret = wl1251_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); |
2f01a1f5 | 309 | if (ret < 0) { |
80301cdc | 310 | wl1251_error("failed to initiate cmd join"); |
ff25839b | 311 | goto out; |
2f01a1f5 KV |
312 | } |
313 | ||
ff25839b KV |
314 | out: |
315 | kfree(join); | |
316 | return ret; | |
2f01a1f5 KV |
317 | } |
318 | ||
80301cdc | 319 | int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode) |
2f01a1f5 | 320 | { |
80301cdc | 321 | struct wl1251_cmd_ps_params *ps_params = NULL; |
ff25839b | 322 | int ret = 0; |
2f01a1f5 | 323 | |
80301cdc | 324 | wl1251_debug(DEBUG_CMD, "cmd set ps mode"); |
2f01a1f5 | 325 | |
ff25839b KV |
326 | ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); |
327 | if (!ps_params) { | |
328 | ret = -ENOMEM; | |
329 | goto out; | |
330 | } | |
331 | ||
332 | ps_params->ps_mode = ps_mode; | |
333 | ps_params->send_null_data = 1; | |
334 | ps_params->retries = 5; | |
335 | ps_params->hang_over_period = 128; | |
336 | ps_params->null_data_rate = 1; /* 1 Mbps */ | |
2f01a1f5 | 337 | |
80301cdc | 338 | ret = wl1251_cmd_send(wl, CMD_SET_PS_MODE, ps_params, |
ff25839b | 339 | sizeof(*ps_params)); |
2f01a1f5 | 340 | if (ret < 0) { |
80301cdc | 341 | wl1251_error("cmd set_ps_mode failed"); |
ff25839b | 342 | goto out; |
2f01a1f5 KV |
343 | } |
344 | ||
ff25839b KV |
345 | out: |
346 | kfree(ps_params); | |
347 | return ret; | |
2f01a1f5 KV |
348 | } |
349 | ||
80301cdc | 350 | int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, |
ff25839b | 351 | size_t len) |
2f01a1f5 | 352 | { |
ff25839b KV |
353 | struct cmd_read_write_memory *cmd; |
354 | int ret = 0; | |
2f01a1f5 | 355 | |
80301cdc | 356 | wl1251_debug(DEBUG_CMD, "cmd read memory"); |
2f01a1f5 | 357 | |
ff25839b KV |
358 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); |
359 | if (!cmd) { | |
360 | ret = -ENOMEM; | |
361 | goto out; | |
362 | } | |
363 | ||
364 | WARN_ON(len > MAX_READ_SIZE); | |
365 | len = min_t(size_t, len, MAX_READ_SIZE); | |
366 | ||
367 | cmd->addr = addr; | |
368 | cmd->size = len; | |
2f01a1f5 | 369 | |
80301cdc | 370 | ret = wl1251_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); |
2f01a1f5 | 371 | if (ret < 0) { |
80301cdc | 372 | wl1251_error("read memory command failed: %d", ret); |
ff25839b | 373 | goto out; |
2f01a1f5 KV |
374 | } |
375 | ||
376 | /* the read command got in, we can now read the answer */ | |
0764de64 | 377 | wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); |
2f01a1f5 | 378 | |
ff25839b | 379 | if (cmd->header.status != CMD_STATUS_SUCCESS) |
80301cdc | 380 | wl1251_error("error in read command result: %d", |
ff25839b | 381 | cmd->header.status); |
2f01a1f5 | 382 | |
ff25839b | 383 | memcpy(answer, cmd->value, len); |
2f01a1f5 | 384 | |
ff25839b KV |
385 | out: |
386 | kfree(cmd); | |
387 | return ret; | |
2f01a1f5 KV |
388 | } |
389 | ||
80301cdc | 390 | int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, |
2f01a1f5 KV |
391 | void *buf, size_t buf_len) |
392 | { | |
80301cdc | 393 | struct wl1251_cmd_packet_template *cmd; |
ff25839b KV |
394 | size_t cmd_len; |
395 | int ret = 0; | |
2f01a1f5 | 396 | |
80301cdc | 397 | wl1251_debug(DEBUG_CMD, "cmd template %d", cmd_id); |
2f01a1f5 | 398 | |
80301cdc KV |
399 | WARN_ON(buf_len > WL1251_MAX_TEMPLATE_SIZE); |
400 | buf_len = min_t(size_t, buf_len, WL1251_MAX_TEMPLATE_SIZE); | |
ff25839b KV |
401 | cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4); |
402 | ||
403 | cmd = kzalloc(cmd_len, GFP_KERNEL); | |
404 | if (!cmd) { | |
405 | ret = -ENOMEM; | |
406 | goto out; | |
407 | } | |
408 | ||
409 | cmd->size = cpu_to_le16(buf_len); | |
2f01a1f5 KV |
410 | |
411 | if (buf) | |
ff25839b | 412 | memcpy(cmd->data, buf, buf_len); |
2f01a1f5 | 413 | |
80301cdc | 414 | ret = wl1251_cmd_send(wl, cmd_id, cmd, cmd_len); |
2f01a1f5 | 415 | if (ret < 0) { |
80301cdc | 416 | wl1251_warning("cmd set_template failed: %d", ret); |
ff25839b | 417 | goto out; |
2f01a1f5 KV |
418 | } |
419 | ||
ff25839b KV |
420 | out: |
421 | kfree(cmd); | |
422 | return ret; | |
2f01a1f5 | 423 | } |
3a98c30f KV |
424 | |
425 | int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, | |
dc52f0a8 | 426 | struct ieee80211_channel *channels[], |
3a98c30f KV |
427 | unsigned int n_channels, unsigned int n_probes) |
428 | { | |
429 | struct wl1251_cmd_scan *cmd; | |
430 | int i, ret = 0; | |
431 | ||
64322e28 DG |
432 | wl1251_debug(DEBUG_CMD, "cmd scan channels %d", n_channels); |
433 | ||
434 | WARN_ON(n_channels > SCAN_MAX_NUM_OF_CHANNELS); | |
3a98c30f KV |
435 | |
436 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
437 | if (!cmd) | |
438 | return -ENOMEM; | |
439 | ||
440 | cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); | |
441 | cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN | | |
442 | CFG_RX_MGMT_EN | | |
443 | CFG_RX_BCN_EN); | |
444 | cmd->params.scan_options = 0; | |
64322e28 DG |
445 | /* |
446 | * Use high priority scan when not associated to prevent fw issue | |
447 | * causing never-ending scans (sometimes 20+ minutes). | |
448 | * Note: This bug may be caused by the fw's DTIM handling. | |
449 | */ | |
450 | if (is_zero_ether_addr(wl->bssid)) | |
76be3434 | 451 | cmd->params.scan_options |= cpu_to_le16(WL1251_SCAN_OPT_PRIORITY_HIGH); |
3a98c30f KV |
452 | cmd->params.num_channels = n_channels; |
453 | cmd->params.num_probe_requests = n_probes; | |
454 | cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ | |
455 | cmd->params.tid_trigger = 0; | |
456 | ||
457 | for (i = 0; i < n_channels; i++) { | |
458 | cmd->channels[i].min_duration = | |
459 | cpu_to_le32(WL1251_SCAN_MIN_DURATION); | |
460 | cmd->channels[i].max_duration = | |
461 | cpu_to_le32(WL1251_SCAN_MAX_DURATION); | |
462 | memset(&cmd->channels[i].bssid_lsb, 0xff, 4); | |
463 | memset(&cmd->channels[i].bssid_msb, 0xff, 2); | |
464 | cmd->channels[i].early_termination = 0; | |
465 | cmd->channels[i].tx_power_att = 0; | |
dc52f0a8 | 466 | cmd->channels[i].channel = channels[i]->hw_value; |
3a98c30f KV |
467 | } |
468 | ||
469 | cmd->params.ssid_len = ssid_len; | |
470 | if (ssid) | |
471 | memcpy(cmd->params.ssid, ssid, ssid_len); | |
472 | ||
473 | ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); | |
474 | if (ret < 0) { | |
475 | wl1251_error("cmd scan failed: %d", ret); | |
476 | goto out; | |
477 | } | |
478 | ||
479 | wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); | |
480 | ||
481 | if (cmd->header.status != CMD_STATUS_SUCCESS) { | |
482 | wl1251_error("cmd scan status wasn't success: %d", | |
483 | cmd->header.status); | |
484 | ret = -EIO; | |
485 | goto out; | |
486 | } | |
487 | ||
488 | out: | |
489 | kfree(cmd); | |
490 | return ret; | |
491 | } | |
492 | ||
493 | int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout) | |
494 | { | |
495 | struct wl1251_cmd_trigger_scan_to *cmd; | |
496 | int ret; | |
497 | ||
498 | wl1251_debug(DEBUG_CMD, "cmd trigger scan to"); | |
499 | ||
500 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
501 | if (!cmd) | |
502 | return -ENOMEM; | |
503 | ||
504 | cmd->timeout = timeout; | |
505 | ||
fe0dbcc9 | 506 | ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, cmd, sizeof(*cmd)); |
3a98c30f KV |
507 | if (ret < 0) { |
508 | wl1251_error("cmd trigger scan to failed: %d", ret); | |
509 | goto out; | |
510 | } | |
511 | ||
512 | out: | |
513 | kfree(cmd); | |
514 | return ret; | |
515 | } |