diff options
author | Luis Carlos Cobo <luisca@cozybit.com> | 2008-08-14 13:41:01 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-08-22 16:29:55 -0400 |
commit | 691cdb49388b808bfbfacaea93afb5c2807db45e (patch) | |
tree | afde32b56dfd8a04556b1c7e8299ad37215fc197 | |
parent | 06b16ae5319251c26377afcb401e46056d5673f4 (diff) |
libertas_tf: command helper functions for libertas_tf
Signed-off-by: Luis Carlos Cobo <luisca@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/libertas_tf/cmd.c | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c new file mode 100644 index 00000000000..fdbcf8ba3e8 --- /dev/null +++ b/drivers/net/wireless/libertas_tf/cmd.c | |||
@@ -0,0 +1,669 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008, cozybit Inc. | ||
3 | * Copyright (C) 2003-2006, Marvell International Ltd. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or (at | ||
8 | * your option) any later version. | ||
9 | */ | ||
10 | #include "libertas_tf.h" | ||
11 | |||
12 | static const struct channel_range channel_ranges[] = { | ||
13 | { LBTF_REGDOMAIN_US, 1, 12 }, | ||
14 | { LBTF_REGDOMAIN_CA, 1, 12 }, | ||
15 | { LBTF_REGDOMAIN_EU, 1, 14 }, | ||
16 | { LBTF_REGDOMAIN_JP, 1, 14 }, | ||
17 | { LBTF_REGDOMAIN_SP, 1, 14 }, | ||
18 | { LBTF_REGDOMAIN_FR, 1, 14 }, | ||
19 | }; | ||
20 | |||
21 | static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] = | ||
22 | { | ||
23 | LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU, | ||
24 | LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP, | ||
25 | }; | ||
26 | |||
27 | static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv); | ||
28 | |||
29 | |||
30 | /** | ||
31 | * lbtf_cmd_copyback - Simple callback that copies response back into command | ||
32 | * | ||
33 | * @priv A pointer to struct lbtf_private structure | ||
34 | * @extra A pointer to the original command structure for which | ||
35 | * 'resp' is a response | ||
36 | * @resp A pointer to the command response | ||
37 | * | ||
38 | * Returns: 0 on success, error on failure | ||
39 | */ | ||
40 | int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, | ||
41 | struct cmd_header *resp) | ||
42 | { | ||
43 | struct cmd_header *buf = (void *)extra; | ||
44 | uint16_t copy_len; | ||
45 | |||
46 | copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); | ||
47 | memcpy(buf, resp, copy_len); | ||
48 | return 0; | ||
49 | } | ||
50 | EXPORT_SYMBOL_GPL(lbtf_cmd_copyback); | ||
51 | |||
52 | #define CHAN_TO_IDX(chan) ((chan) - 1) | ||
53 | |||
54 | static void lbtf_geo_init(struct lbtf_private *priv) | ||
55 | { | ||
56 | const struct channel_range *range = channel_ranges; | ||
57 | u8 ch; | ||
58 | int i; | ||
59 | |||
60 | for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) | ||
61 | if (channel_ranges[i].regdomain == priv->regioncode) { | ||
62 | range = &channel_ranges[i]; | ||
63 | break; | ||
64 | } | ||
65 | |||
66 | for (ch = priv->range.start; ch < priv->range.end; ch++) | ||
67 | priv->channels[CHAN_TO_IDX(ch)].flags = 0; | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * lbtf_update_hw_spec: Updates the hardware details. | ||
72 | * | ||
73 | * @priv A pointer to struct lbtf_private structure | ||
74 | * | ||
75 | * Returns: 0 on success, error on failure | ||
76 | */ | ||
77 | int lbtf_update_hw_spec(struct lbtf_private *priv) | ||
78 | { | ||
79 | struct cmd_ds_get_hw_spec cmd; | ||
80 | int ret = -1; | ||
81 | u32 i; | ||
82 | DECLARE_MAC_BUF(mac); | ||
83 | |||
84 | memset(&cmd, 0, sizeof(cmd)); | ||
85 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
86 | memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); | ||
87 | ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); | ||
88 | if (ret) | ||
89 | goto out; | ||
90 | |||
91 | priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); | ||
92 | |||
93 | /* The firmware release is in an interesting format: the patch | ||
94 | * level is in the most significant nibble ... so fix that: */ | ||
95 | priv->fwrelease = le32_to_cpu(cmd.fwrelease); | ||
96 | priv->fwrelease = (priv->fwrelease << 8) | | ||
97 | (priv->fwrelease >> 24 & 0xff); | ||
98 | |||
99 | printk(KERN_INFO "libertastf: %s, fw %u.%u.%up%u, cap 0x%08x\n", | ||
100 | print_mac(mac, cmd.permanentaddr), | ||
101 | priv->fwrelease >> 24 & 0xff, | ||
102 | priv->fwrelease >> 16 & 0xff, | ||
103 | priv->fwrelease >> 8 & 0xff, | ||
104 | priv->fwrelease & 0xff, | ||
105 | priv->fwcapinfo); | ||
106 | |||
107 | /* Clamp region code to 8-bit since FW spec indicates that it should | ||
108 | * only ever be 8-bit, even though the field size is 16-bit. Some | ||
109 | * firmware returns non-zero high 8 bits here. | ||
110 | */ | ||
111 | priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; | ||
112 | |||
113 | for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { | ||
114 | /* use the region code to search for the index */ | ||
115 | if (priv->regioncode == lbtf_region_code_to_index[i]) | ||
116 | break; | ||
117 | } | ||
118 | |||
119 | /* if it's unidentified region code, use the default (USA) */ | ||
120 | if (i >= MRVDRV_MAX_REGION_CODE) | ||
121 | priv->regioncode = 0x10; | ||
122 | |||
123 | if (priv->current_addr[0] == 0xff) | ||
124 | memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); | ||
125 | |||
126 | SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr); | ||
127 | |||
128 | lbtf_geo_init(priv); | ||
129 | out: | ||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * lbtf_set_channel: Set the radio channel | ||
135 | * | ||
136 | * @priv A pointer to struct lbtf_private structure | ||
137 | * @channel The desired channel, or 0 to clear a locked channel | ||
138 | * | ||
139 | * Returns: 0 on success, error on failure | ||
140 | */ | ||
141 | int lbtf_set_channel(struct lbtf_private *priv, u8 channel) | ||
142 | { | ||
143 | struct cmd_ds_802_11_rf_channel cmd; | ||
144 | |||
145 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
146 | cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); | ||
147 | cmd.channel = cpu_to_le16(channel); | ||
148 | |||
149 | return lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); | ||
150 | } | ||
151 | |||
152 | int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon) | ||
153 | { | ||
154 | struct cmd_ds_802_11_beacon_set cmd; | ||
155 | int size; | ||
156 | |||
157 | if (beacon->len > MRVL_MAX_BCN_SIZE) | ||
158 | return -1; | ||
159 | size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len; | ||
160 | cmd.hdr.size = cpu_to_le16(size); | ||
161 | cmd.len = cpu_to_le16(beacon->len); | ||
162 | memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len); | ||
163 | |||
164 | lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size); | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, | ||
169 | int beacon_int) { | ||
170 | struct cmd_ds_802_11_beacon_control cmd; | ||
171 | |||
172 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
173 | cmd.action = cpu_to_le16(CMD_ACT_SET); | ||
174 | cmd.beacon_enable = cpu_to_le16(beacon_enable); | ||
175 | cmd.beacon_period = cpu_to_le16(beacon_int); | ||
176 | |||
177 | lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd)); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static void lbtf_queue_cmd(struct lbtf_private *priv, | ||
182 | struct cmd_ctrl_node *cmdnode) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | |||
186 | if (!cmdnode) | ||
187 | return; | ||
188 | |||
189 | if (!cmdnode->cmdbuf->size) | ||
190 | return; | ||
191 | |||
192 | cmdnode->result = 0; | ||
193 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
194 | list_add_tail(&cmdnode->list, &priv->cmdpendingq); | ||
195 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
196 | } | ||
197 | |||
198 | static void lbtf_submit_command(struct lbtf_private *priv, | ||
199 | struct cmd_ctrl_node *cmdnode) | ||
200 | { | ||
201 | unsigned long flags; | ||
202 | struct cmd_header *cmd; | ||
203 | uint16_t cmdsize; | ||
204 | uint16_t command; | ||
205 | int timeo = 5 * HZ; | ||
206 | int ret; | ||
207 | |||
208 | cmd = cmdnode->cmdbuf; | ||
209 | |||
210 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
211 | priv->cur_cmd = cmdnode; | ||
212 | cmdsize = le16_to_cpu(cmd->size); | ||
213 | command = le16_to_cpu(cmd->command); | ||
214 | ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); | ||
215 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
216 | |||
217 | if (ret) | ||
218 | /* Let the timer kick in and retry, and potentially reset | ||
219 | the whole thing if the condition persists */ | ||
220 | timeo = HZ; | ||
221 | |||
222 | /* Setup the timer after transmit command */ | ||
223 | mod_timer(&priv->command_timer, jiffies + timeo); | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * This function inserts command node to cmdfreeq | ||
228 | * after cleans it. Requires priv->driver_lock held. | ||
229 | */ | ||
230 | static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, | ||
231 | struct cmd_ctrl_node *cmdnode) | ||
232 | { | ||
233 | if (!cmdnode) | ||
234 | return; | ||
235 | |||
236 | cmdnode->callback = NULL; | ||
237 | cmdnode->callback_arg = 0; | ||
238 | |||
239 | memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); | ||
240 | |||
241 | list_add_tail(&cmdnode->list, &priv->cmdfreeq); | ||
242 | } | ||
243 | |||
244 | static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, | ||
245 | struct cmd_ctrl_node *ptempcmd) | ||
246 | { | ||
247 | unsigned long flags; | ||
248 | |||
249 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
250 | __lbtf_cleanup_and_insert_cmd(priv, ptempcmd); | ||
251 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
252 | } | ||
253 | |||
254 | void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, | ||
255 | int result) | ||
256 | { | ||
257 | cmd->result = result; | ||
258 | cmd->cmdwaitqwoken = 1; | ||
259 | wake_up_interruptible(&cmd->cmdwait_q); | ||
260 | |||
261 | if (!cmd->callback) | ||
262 | __lbtf_cleanup_and_insert_cmd(priv, cmd); | ||
263 | priv->cur_cmd = NULL; | ||
264 | } | ||
265 | |||
266 | int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv) | ||
267 | { | ||
268 | struct cmd_ds_mac_multicast_addr cmd; | ||
269 | |||
270 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
271 | cmd.action = cpu_to_le16(CMD_ACT_SET); | ||
272 | |||
273 | cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr); | ||
274 | memcpy(cmd.maclist, priv->multicastlist, | ||
275 | priv->nr_of_multicastmacaddr * ETH_ALEN); | ||
276 | |||
277 | lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd)); | ||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) | ||
282 | { | ||
283 | struct cmd_ds_set_mode cmd; | ||
284 | |||
285 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
286 | cmd.mode = cpu_to_le16(mode); | ||
287 | lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); | ||
288 | } | ||
289 | |||
290 | void lbtf_set_bssid(struct lbtf_private *priv, bool activate, u8 *bssid) | ||
291 | { | ||
292 | struct cmd_ds_set_bssid cmd; | ||
293 | |||
294 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
295 | cmd.activate = activate ? 1 : 0; | ||
296 | if (activate) | ||
297 | memcpy(cmd.bssid, bssid, ETH_ALEN); | ||
298 | |||
299 | lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd)); | ||
300 | } | ||
301 | |||
302 | int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr) | ||
303 | { | ||
304 | struct cmd_ds_802_11_mac_address cmd; | ||
305 | |||
306 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
307 | cmd.action = cpu_to_le16(CMD_ACT_SET); | ||
308 | |||
309 | memcpy(cmd.macadd, mac_addr, ETH_ALEN); | ||
310 | |||
311 | lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd)); | ||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | int lbtf_set_radio_control(struct lbtf_private *priv) | ||
316 | { | ||
317 | int ret = 0; | ||
318 | struct cmd_ds_802_11_radio_control cmd; | ||
319 | |||
320 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
321 | cmd.action = cpu_to_le16(CMD_ACT_SET); | ||
322 | |||
323 | switch (priv->preamble) { | ||
324 | case CMD_TYPE_SHORT_PREAMBLE: | ||
325 | cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE); | ||
326 | break; | ||
327 | |||
328 | case CMD_TYPE_LONG_PREAMBLE: | ||
329 | cmd.control = cpu_to_le16(SET_LONG_PREAMBLE); | ||
330 | break; | ||
331 | |||
332 | case CMD_TYPE_AUTO_PREAMBLE: | ||
333 | default: | ||
334 | cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE); | ||
335 | break; | ||
336 | } | ||
337 | |||
338 | if (priv->radioon) | ||
339 | cmd.control |= cpu_to_le16(TURN_ON_RF); | ||
340 | else | ||
341 | cmd.control &= cpu_to_le16(~TURN_ON_RF); | ||
342 | |||
343 | ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | void lbtf_set_mac_control(struct lbtf_private *priv) | ||
348 | { | ||
349 | struct cmd_ds_mac_control cmd; | ||
350 | cmd.hdr.size = cpu_to_le16(sizeof(cmd)); | ||
351 | cmd.action = cpu_to_le16(priv->mac_control); | ||
352 | cmd.reserved = 0; | ||
353 | |||
354 | lbtf_cmd_async(priv, CMD_MAC_CONTROL, | ||
355 | &cmd.hdr, sizeof(cmd)); | ||
356 | } | ||
357 | |||
358 | /** | ||
359 | * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue | ||
360 | * | ||
361 | * @priv A pointer to struct lbtf_private structure | ||
362 | * | ||
363 | * Returns: 0 on success. | ||
364 | */ | ||
365 | int lbtf_allocate_cmd_buffer(struct lbtf_private *priv) | ||
366 | { | ||
367 | u32 bufsize; | ||
368 | u32 i; | ||
369 | struct cmd_ctrl_node *cmdarray; | ||
370 | |||
371 | /* Allocate and initialize the command array */ | ||
372 | bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; | ||
373 | cmdarray = kzalloc(bufsize, GFP_KERNEL); | ||
374 | if (!cmdarray) | ||
375 | return -1; | ||
376 | priv->cmd_array = cmdarray; | ||
377 | |||
378 | /* Allocate and initialize each command buffer in the command array */ | ||
379 | for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { | ||
380 | cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); | ||
381 | if (!cmdarray[i].cmdbuf) | ||
382 | return -1; | ||
383 | } | ||
384 | |||
385 | for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { | ||
386 | init_waitqueue_head(&cmdarray[i].cmdwait_q); | ||
387 | lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]); | ||
388 | } | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | /** | ||
393 | * lbtf_free_cmd_buffer - Frees the cmd buffer. | ||
394 | * | ||
395 | * @priv A pointer to struct lbtf_private structure | ||
396 | * | ||
397 | * Returns: 0 | ||
398 | */ | ||
399 | int lbtf_free_cmd_buffer(struct lbtf_private *priv) | ||
400 | { | ||
401 | struct cmd_ctrl_node *cmdarray; | ||
402 | unsigned int i; | ||
403 | |||
404 | /* need to check if cmd array is allocated or not */ | ||
405 | if (priv->cmd_array == NULL) | ||
406 | return 0; | ||
407 | |||
408 | cmdarray = priv->cmd_array; | ||
409 | |||
410 | /* Release shared memory buffers */ | ||
411 | for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { | ||
412 | kfree(cmdarray[i].cmdbuf); | ||
413 | cmdarray[i].cmdbuf = NULL; | ||
414 | } | ||
415 | |||
416 | /* Release cmd_ctrl_node */ | ||
417 | kfree(priv->cmd_array); | ||
418 | priv->cmd_array = NULL; | ||
419 | |||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | /** | ||
424 | * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue. | ||
425 | * | ||
426 | * @priv A pointer to struct lbtf_private structure | ||
427 | * | ||
428 | * Returns: pointer to a struct cmd_ctrl_node or NULL if none available. | ||
429 | */ | ||
430 | static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv) | ||
431 | { | ||
432 | struct cmd_ctrl_node *tempnode; | ||
433 | unsigned long flags; | ||
434 | |||
435 | if (!priv) | ||
436 | return NULL; | ||
437 | |||
438 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
439 | |||
440 | if (!list_empty(&priv->cmdfreeq)) { | ||
441 | tempnode = list_first_entry(&priv->cmdfreeq, | ||
442 | struct cmd_ctrl_node, list); | ||
443 | list_del(&tempnode->list); | ||
444 | } else | ||
445 | tempnode = NULL; | ||
446 | |||
447 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
448 | |||
449 | return tempnode; | ||
450 | } | ||
451 | |||
452 | /** | ||
453 | * lbtf_execute_next_command: execute next command in cmd pending queue. | ||
454 | * | ||
455 | * @priv A pointer to struct lbtf_private structure | ||
456 | * | ||
457 | * Returns: 0 on success. | ||
458 | */ | ||
459 | int lbtf_execute_next_command(struct lbtf_private *priv) | ||
460 | { | ||
461 | struct cmd_ctrl_node *cmdnode = NULL; | ||
462 | struct cmd_header *cmd; | ||
463 | unsigned long flags; | ||
464 | |||
465 | /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the | ||
466 | * only caller to us is lbtf_thread() and we get even when a | ||
467 | * data packet is received */ | ||
468 | |||
469 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
470 | |||
471 | if (priv->cur_cmd) { | ||
472 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
473 | return -1; | ||
474 | } | ||
475 | |||
476 | if (!list_empty(&priv->cmdpendingq)) { | ||
477 | cmdnode = list_first_entry(&priv->cmdpendingq, | ||
478 | struct cmd_ctrl_node, list); | ||
479 | } | ||
480 | |||
481 | if (cmdnode) { | ||
482 | cmd = cmdnode->cmdbuf; | ||
483 | |||
484 | list_del(&cmdnode->list); | ||
485 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
486 | lbtf_submit_command(priv, cmdnode); | ||
487 | } else | ||
488 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv, | ||
493 | uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, | ||
494 | int (*callback)(struct lbtf_private *, unsigned long, | ||
495 | struct cmd_header *), | ||
496 | unsigned long callback_arg) | ||
497 | { | ||
498 | struct cmd_ctrl_node *cmdnode; | ||
499 | |||
500 | if (priv->surpriseremoved) | ||
501 | return ERR_PTR(-ENOENT); | ||
502 | |||
503 | cmdnode = lbtf_get_cmd_ctrl_node(priv); | ||
504 | if (cmdnode == NULL) { | ||
505 | /* Wake up main thread to execute next command */ | ||
506 | queue_work(lbtf_wq, &priv->cmd_work); | ||
507 | return ERR_PTR(-ENOBUFS); | ||
508 | } | ||
509 | |||
510 | cmdnode->callback = callback; | ||
511 | cmdnode->callback_arg = callback_arg; | ||
512 | |||
513 | /* Copy the incoming command to the buffer */ | ||
514 | memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); | ||
515 | |||
516 | /* Set sequence number, clean result, move to buffer */ | ||
517 | priv->seqnum++; | ||
518 | cmdnode->cmdbuf->command = cpu_to_le16(command); | ||
519 | cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); | ||
520 | cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum); | ||
521 | cmdnode->cmdbuf->result = 0; | ||
522 | cmdnode->cmdwaitqwoken = 0; | ||
523 | lbtf_queue_cmd(priv, cmdnode); | ||
524 | queue_work(lbtf_wq, &priv->cmd_work); | ||
525 | |||
526 | return cmdnode; | ||
527 | } | ||
528 | |||
529 | void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, | ||
530 | struct cmd_header *in_cmd, int in_cmd_size) | ||
531 | { | ||
532 | __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0); | ||
533 | } | ||
534 | |||
535 | int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, | ||
536 | struct cmd_header *in_cmd, int in_cmd_size, | ||
537 | int (*callback)(struct lbtf_private *, | ||
538 | unsigned long, struct cmd_header *), | ||
539 | unsigned long callback_arg) | ||
540 | { | ||
541 | struct cmd_ctrl_node *cmdnode; | ||
542 | unsigned long flags; | ||
543 | int ret = 0; | ||
544 | |||
545 | cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, | ||
546 | callback, callback_arg); | ||
547 | if (IS_ERR(cmdnode)) | ||
548 | return PTR_ERR(cmdnode); | ||
549 | |||
550 | might_sleep(); | ||
551 | ret = wait_event_interruptible(cmdnode->cmdwait_q, | ||
552 | cmdnode->cmdwaitqwoken); | ||
553 | if (ret) { | ||
554 | printk(KERN_DEBUG | ||
555 | "libertastf: command 0x%04x interrupted by signal", | ||
556 | command); | ||
557 | return ret; | ||
558 | } | ||
559 | |||
560 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
561 | ret = cmdnode->result; | ||
562 | if (ret) | ||
563 | printk(KERN_DEBUG "libertastf: command 0x%04x failed: %d\n", | ||
564 | command, ret); | ||
565 | |||
566 | __lbtf_cleanup_and_insert_cmd(priv, cmdnode); | ||
567 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
568 | |||
569 | return ret; | ||
570 | } | ||
571 | EXPORT_SYMBOL_GPL(__lbtf_cmd); | ||
572 | |||
573 | /* Call holding driver_lock */ | ||
574 | void lbtf_cmd_response_rx(struct lbtf_private *priv) | ||
575 | { | ||
576 | priv->cmd_response_rxed = 1; | ||
577 | queue_work(lbtf_wq, &priv->cmd_work); | ||
578 | } | ||
579 | EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx); | ||
580 | |||
581 | int lbtf_process_rx_command(struct lbtf_private *priv) | ||
582 | { | ||
583 | uint16_t respcmd, curcmd; | ||
584 | struct cmd_header *resp; | ||
585 | int ret = 0; | ||
586 | unsigned long flags; | ||
587 | uint16_t result; | ||
588 | |||
589 | mutex_lock(&priv->lock); | ||
590 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
591 | |||
592 | if (!priv->cur_cmd) { | ||
593 | ret = -1; | ||
594 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
595 | goto done; | ||
596 | } | ||
597 | |||
598 | resp = (void *)priv->cmd_resp_buff; | ||
599 | curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); | ||
600 | respcmd = le16_to_cpu(resp->command); | ||
601 | result = le16_to_cpu(resp->result); | ||
602 | |||
603 | if (net_ratelimit()) | ||
604 | printk(KERN_DEBUG "libertastf: cmd response 0x%04x, seq %d, size %d\n", | ||
605 | respcmd, le16_to_cpu(resp->seqnum), | ||
606 | le16_to_cpu(resp->size)); | ||
607 | |||
608 | if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { | ||
609 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
610 | ret = -1; | ||
611 | goto done; | ||
612 | } | ||
613 | if (respcmd != CMD_RET(curcmd)) { | ||
614 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
615 | ret = -1; | ||
616 | goto done; | ||
617 | } | ||
618 | |||
619 | if (resp->result == cpu_to_le16(0x0004)) { | ||
620 | /* 0x0004 means -EAGAIN. Drop the response, let it time out | ||
621 | and be resubmitted */ | ||
622 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
623 | ret = -1; | ||
624 | goto done; | ||
625 | } | ||
626 | |||
627 | /* Now we got response from FW, cancel the command timer */ | ||
628 | del_timer(&priv->command_timer); | ||
629 | priv->cmd_timed_out = 0; | ||
630 | if (priv->nr_retries) | ||
631 | priv->nr_retries = 0; | ||
632 | |||
633 | /* If the command is not successful, cleanup and return failure */ | ||
634 | if ((result != 0 || !(respcmd & 0x8000))) { | ||
635 | /* | ||
636 | * Handling errors here | ||
637 | */ | ||
638 | switch (respcmd) { | ||
639 | case CMD_RET(CMD_GET_HW_SPEC): | ||
640 | case CMD_RET(CMD_802_11_RESET): | ||
641 | printk(KERN_DEBUG "libertastf: reset failed\n"); | ||
642 | break; | ||
643 | |||
644 | } | ||
645 | lbtf_complete_command(priv, priv->cur_cmd, result); | ||
646 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
647 | |||
648 | ret = -1; | ||
649 | goto done; | ||
650 | } | ||
651 | |||
652 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
653 | |||
654 | if (priv->cur_cmd && priv->cur_cmd->callback) { | ||
655 | ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, | ||
656 | resp); | ||
657 | } | ||
658 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
659 | |||
660 | if (priv->cur_cmd) { | ||
661 | /* Clean up and Put current command back to cmdfreeq */ | ||
662 | lbtf_complete_command(priv, priv->cur_cmd, result); | ||
663 | } | ||
664 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
665 | |||
666 | done: | ||
667 | mutex_unlock(&priv->lock); | ||
668 | return ret; | ||
669 | } | ||