diff options
Diffstat (limited to 'drivers/net/wireless/libertas_tf')
-rw-r--r-- | drivers/net/wireless/libertas_tf/Makefile | 6 | ||||
-rw-r--r-- | drivers/net/wireless/libertas_tf/cmd.c | 669 | ||||
-rw-r--r-- | drivers/net/wireless/libertas_tf/if_usb.c | 766 | ||||
-rw-r--r-- | drivers/net/wireless/libertas_tf/if_usb.h | 98 | ||||
-rw-r--r-- | drivers/net/wireless/libertas_tf/libertas_tf.h | 514 | ||||
-rw-r--r-- | drivers/net/wireless/libertas_tf/main.c | 662 |
6 files changed, 2715 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas_tf/Makefile b/drivers/net/wireless/libertas_tf/Makefile new file mode 100644 index 000000000000..ff5544d6ac9d --- /dev/null +++ b/drivers/net/wireless/libertas_tf/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | libertas_tf-objs := main.o cmd.o | ||
2 | |||
3 | libertas_tf_usb-objs += if_usb.o | ||
4 | |||
5 | obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf.o | ||
6 | obj-$(CONFIG_LIBERTAS_THINFIRM_USB) += libertas_tf_usb.o | ||
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c new file mode 100644 index 000000000000..fdbcf8ba3e8a --- /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 | } | ||
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c new file mode 100644 index 000000000000..1cc03a8dd67a --- /dev/null +++ b/drivers/net/wireless/libertas_tf/if_usb.c | |||
@@ -0,0 +1,766 @@ | |||
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 <linux/delay.h> | ||
11 | #include <linux/moduleparam.h> | ||
12 | #include <linux/firmware.h> | ||
13 | #include <linux/netdevice.h> | ||
14 | #include <linux/usb.h> | ||
15 | |||
16 | #define DRV_NAME "lbtf_usb" | ||
17 | |||
18 | #include "libertas_tf.h" | ||
19 | #include "if_usb.h" | ||
20 | |||
21 | #define MESSAGE_HEADER_LEN 4 | ||
22 | |||
23 | static char *lbtf_fw_name = "lbtf_usb.bin"; | ||
24 | module_param_named(fw_name, lbtf_fw_name, charp, 0644); | ||
25 | |||
26 | static struct usb_device_id if_usb_table[] = { | ||
27 | /* Enter the device signature inside */ | ||
28 | { USB_DEVICE(0x1286, 0x2001) }, | ||
29 | { USB_DEVICE(0x05a3, 0x8388) }, | ||
30 | {} /* Terminating entry */ | ||
31 | }; | ||
32 | |||
33 | MODULE_DEVICE_TABLE(usb, if_usb_table); | ||
34 | |||
35 | static void if_usb_receive(struct urb *urb); | ||
36 | static void if_usb_receive_fwload(struct urb *urb); | ||
37 | static int if_usb_prog_firmware(struct if_usb_card *cardp); | ||
38 | static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, | ||
39 | uint8_t *payload, uint16_t nb); | ||
40 | static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, | ||
41 | uint16_t nb, u8 data); | ||
42 | static void if_usb_free(struct if_usb_card *cardp); | ||
43 | static int if_usb_submit_rx_urb(struct if_usb_card *cardp); | ||
44 | static int if_usb_reset_device(struct if_usb_card *cardp); | ||
45 | |||
46 | /** | ||
47 | * if_usb_wrike_bulk_callback - call back to handle URB status | ||
48 | * | ||
49 | * @param urb pointer to urb structure | ||
50 | */ | ||
51 | static void if_usb_write_bulk_callback(struct urb *urb) | ||
52 | { | ||
53 | if (urb->status != 0) | ||
54 | printk(KERN_INFO "libertastf: URB in failure status: %d\n", | ||
55 | urb->status); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * if_usb_free - free tx/rx urb, skb and rx buffer | ||
60 | * | ||
61 | * @param cardp pointer if_usb_card | ||
62 | */ | ||
63 | static void if_usb_free(struct if_usb_card *cardp) | ||
64 | { | ||
65 | /* Unlink tx & rx urb */ | ||
66 | usb_kill_urb(cardp->tx_urb); | ||
67 | usb_kill_urb(cardp->rx_urb); | ||
68 | usb_kill_urb(cardp->cmd_urb); | ||
69 | |||
70 | usb_free_urb(cardp->tx_urb); | ||
71 | cardp->tx_urb = NULL; | ||
72 | |||
73 | usb_free_urb(cardp->rx_urb); | ||
74 | cardp->rx_urb = NULL; | ||
75 | |||
76 | usb_free_urb(cardp->cmd_urb); | ||
77 | cardp->cmd_urb = NULL; | ||
78 | |||
79 | kfree(cardp->ep_out_buf); | ||
80 | cardp->ep_out_buf = NULL; | ||
81 | } | ||
82 | |||
83 | static void if_usb_setup_firmware(struct lbtf_private *priv) | ||
84 | { | ||
85 | struct if_usb_card *cardp = priv->card; | ||
86 | struct cmd_ds_set_boot2_ver b2_cmd; | ||
87 | |||
88 | if_usb_submit_rx_urb(cardp); | ||
89 | b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); | ||
90 | b2_cmd.action = 0; | ||
91 | b2_cmd.version = cardp->boot2_version; | ||
92 | |||
93 | if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) | ||
94 | printk(KERN_INFO "libertastf: setting boot2 version failed\n"); | ||
95 | } | ||
96 | |||
97 | static void if_usb_fw_timeo(unsigned long priv) | ||
98 | { | ||
99 | struct if_usb_card *cardp = (void *)priv; | ||
100 | |||
101 | if (!cardp->fwdnldover) | ||
102 | /* Download timed out */ | ||
103 | cardp->priv->surpriseremoved = 1; | ||
104 | wake_up(&cardp->fw_wq); | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * if_usb_probe - sets the configuration values | ||
109 | * | ||
110 | * @ifnum interface number | ||
111 | * @id pointer to usb_device_id | ||
112 | * | ||
113 | * Returns: 0 on success, error code on failure | ||
114 | */ | ||
115 | static int if_usb_probe(struct usb_interface *intf, | ||
116 | const struct usb_device_id *id) | ||
117 | { | ||
118 | struct usb_device *udev; | ||
119 | struct usb_host_interface *iface_desc; | ||
120 | struct usb_endpoint_descriptor *endpoint; | ||
121 | struct lbtf_private *priv; | ||
122 | struct if_usb_card *cardp; | ||
123 | int i; | ||
124 | |||
125 | udev = interface_to_usbdev(intf); | ||
126 | |||
127 | cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); | ||
128 | if (!cardp) | ||
129 | goto error; | ||
130 | |||
131 | setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); | ||
132 | init_waitqueue_head(&cardp->fw_wq); | ||
133 | |||
134 | cardp->udev = udev; | ||
135 | iface_desc = intf->cur_altsetting; | ||
136 | |||
137 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
138 | endpoint = &iface_desc->endpoint[i].desc; | ||
139 | if (usb_endpoint_is_bulk_in(endpoint)) { | ||
140 | cardp->ep_in_size = | ||
141 | le16_to_cpu(endpoint->wMaxPacketSize); | ||
142 | cardp->ep_in = usb_endpoint_num(endpoint); | ||
143 | } else if (usb_endpoint_is_bulk_out(endpoint)) { | ||
144 | cardp->ep_out_size = | ||
145 | le16_to_cpu(endpoint->wMaxPacketSize); | ||
146 | cardp->ep_out = usb_endpoint_num(endpoint); | ||
147 | } | ||
148 | } | ||
149 | if (!cardp->ep_out_size || !cardp->ep_in_size) | ||
150 | /* Endpoints not found */ | ||
151 | goto dealloc; | ||
152 | |||
153 | cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
154 | if (!cardp->rx_urb) | ||
155 | goto dealloc; | ||
156 | |||
157 | cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
158 | if (!cardp->tx_urb) | ||
159 | goto dealloc; | ||
160 | |||
161 | cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
162 | if (!cardp->cmd_urb) | ||
163 | goto dealloc; | ||
164 | |||
165 | cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, | ||
166 | GFP_KERNEL); | ||
167 | if (!cardp->ep_out_buf) | ||
168 | goto dealloc; | ||
169 | |||
170 | priv = lbtf_add_card(cardp, &udev->dev); | ||
171 | if (!priv) | ||
172 | goto dealloc; | ||
173 | |||
174 | cardp->priv = priv; | ||
175 | |||
176 | priv->hw_host_to_card = if_usb_host_to_card; | ||
177 | priv->hw_prog_firmware = if_usb_prog_firmware; | ||
178 | priv->hw_reset_device = if_usb_reset_device; | ||
179 | cardp->boot2_version = udev->descriptor.bcdDevice; | ||
180 | |||
181 | usb_get_dev(udev); | ||
182 | usb_set_intfdata(intf, cardp); | ||
183 | |||
184 | return 0; | ||
185 | |||
186 | dealloc: | ||
187 | if_usb_free(cardp); | ||
188 | error: | ||
189 | return -ENOMEM; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * if_usb_disconnect - free resource and cleanup | ||
194 | * | ||
195 | * @intf USB interface structure | ||
196 | */ | ||
197 | static void if_usb_disconnect(struct usb_interface *intf) | ||
198 | { | ||
199 | struct if_usb_card *cardp = usb_get_intfdata(intf); | ||
200 | struct lbtf_private *priv = (struct lbtf_private *) cardp->priv; | ||
201 | |||
202 | if_usb_reset_device(cardp); | ||
203 | |||
204 | if (priv) | ||
205 | lbtf_remove_card(priv); | ||
206 | |||
207 | /* Unlink and free urb */ | ||
208 | if_usb_free(cardp); | ||
209 | |||
210 | usb_set_intfdata(intf, NULL); | ||
211 | usb_put_dev(interface_to_usbdev(intf)); | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * if_usb_send_fw_pkt - This function downloads the FW | ||
216 | * | ||
217 | * @priv pointer to struct lbtf_private | ||
218 | * | ||
219 | * Returns: 0 | ||
220 | */ | ||
221 | static int if_usb_send_fw_pkt(struct if_usb_card *cardp) | ||
222 | { | ||
223 | struct fwdata *fwdata = cardp->ep_out_buf; | ||
224 | u8 *firmware = (u8 *) cardp->fw->data; | ||
225 | |||
226 | /* If we got a CRC failure on the last block, back | ||
227 | up and retry it */ | ||
228 | if (!cardp->CRC_OK) { | ||
229 | cardp->totalbytes = cardp->fwlastblksent; | ||
230 | cardp->fwseqnum--; | ||
231 | } | ||
232 | |||
233 | /* struct fwdata (which we sent to the card) has an | ||
234 | extra __le32 field in between the header and the data, | ||
235 | which is not in the struct fwheader in the actual | ||
236 | firmware binary. Insert the seqnum in the middle... */ | ||
237 | memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], | ||
238 | sizeof(struct fwheader)); | ||
239 | |||
240 | cardp->fwlastblksent = cardp->totalbytes; | ||
241 | cardp->totalbytes += sizeof(struct fwheader); | ||
242 | |||
243 | memcpy(fwdata->data, &firmware[cardp->totalbytes], | ||
244 | le32_to_cpu(fwdata->hdr.datalength)); | ||
245 | |||
246 | fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); | ||
247 | cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); | ||
248 | |||
249 | usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + | ||
250 | le32_to_cpu(fwdata->hdr.datalength), 0); | ||
251 | |||
252 | if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) | ||
253 | /* Host has finished FW downloading | ||
254 | * Donwloading FW JUMP BLOCK | ||
255 | */ | ||
256 | cardp->fwfinalblk = 1; | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int if_usb_reset_device(struct if_usb_card *cardp) | ||
262 | { | ||
263 | struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4; | ||
264 | int ret; | ||
265 | |||
266 | *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); | ||
267 | |||
268 | cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET); | ||
269 | cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset)); | ||
270 | cmd->hdr.result = cpu_to_le16(0); | ||
271 | cmd->hdr.seqnum = cpu_to_le16(0x5a5a); | ||
272 | cmd->action = cpu_to_le16(CMD_ACT_HALT); | ||
273 | usb_tx_block(cardp, cardp->ep_out_buf, | ||
274 | 4 + sizeof(struct cmd_ds_802_11_reset), 0); | ||
275 | |||
276 | msleep(100); | ||
277 | ret = usb_reset_device(cardp->udev); | ||
278 | msleep(100); | ||
279 | |||
280 | return ret; | ||
281 | } | ||
282 | EXPORT_SYMBOL_GPL(if_usb_reset_device); | ||
283 | |||
284 | /** | ||
285 | * usb_tx_block - transfer data to the device | ||
286 | * | ||
287 | * @priv pointer to struct lbtf_private | ||
288 | * @payload pointer to payload data | ||
289 | * @nb data length | ||
290 | * @data non-zero for data, zero for commands | ||
291 | * | ||
292 | * Returns: 0 on success, nonzero otherwise. | ||
293 | */ | ||
294 | static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, | ||
295 | uint16_t nb, u8 data) | ||
296 | { | ||
297 | struct urb *urb; | ||
298 | |||
299 | /* check if device is removed */ | ||
300 | if (cardp->priv->surpriseremoved) | ||
301 | return -1; | ||
302 | |||
303 | if (data) | ||
304 | urb = cardp->tx_urb; | ||
305 | else | ||
306 | urb = cardp->cmd_urb; | ||
307 | |||
308 | usb_fill_bulk_urb(urb, cardp->udev, | ||
309 | usb_sndbulkpipe(cardp->udev, | ||
310 | cardp->ep_out), | ||
311 | payload, nb, if_usb_write_bulk_callback, cardp); | ||
312 | |||
313 | urb->transfer_flags |= URB_ZERO_PACKET; | ||
314 | |||
315 | if (usb_submit_urb(urb, GFP_ATOMIC)) | ||
316 | return -1; | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, | ||
321 | void (*callbackfn)(struct urb *urb)) | ||
322 | { | ||
323 | struct sk_buff *skb; | ||
324 | |||
325 | skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); | ||
326 | if (!skb) | ||
327 | return -1; | ||
328 | |||
329 | cardp->rx_skb = skb; | ||
330 | |||
331 | /* Fill the receive configuration URB and initialise the Rx call back */ | ||
332 | usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, | ||
333 | usb_rcvbulkpipe(cardp->udev, cardp->ep_in), | ||
334 | (void *) (skb->tail), | ||
335 | MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); | ||
336 | |||
337 | cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; | ||
338 | |||
339 | if (usb_submit_urb(cardp->rx_urb, GFP_ATOMIC)) { | ||
340 | kfree_skb(skb); | ||
341 | cardp->rx_skb = NULL; | ||
342 | return -1; | ||
343 | } else | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) | ||
348 | { | ||
349 | return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); | ||
350 | } | ||
351 | |||
352 | static int if_usb_submit_rx_urb(struct if_usb_card *cardp) | ||
353 | { | ||
354 | return __if_usb_submit_rx_urb(cardp, &if_usb_receive); | ||
355 | } | ||
356 | |||
357 | static void if_usb_receive_fwload(struct urb *urb) | ||
358 | { | ||
359 | struct if_usb_card *cardp = urb->context; | ||
360 | struct sk_buff *skb = cardp->rx_skb; | ||
361 | struct fwsyncheader *syncfwheader; | ||
362 | struct bootcmdresp bcmdresp; | ||
363 | |||
364 | if (urb->status) { | ||
365 | kfree_skb(skb); | ||
366 | return; | ||
367 | } | ||
368 | |||
369 | if (cardp->fwdnldover) { | ||
370 | __le32 *tmp = (__le32 *)(skb->data); | ||
371 | |||
372 | if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && | ||
373 | tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) | ||
374 | /* Firmware ready event received */ | ||
375 | wake_up(&cardp->fw_wq); | ||
376 | else | ||
377 | if_usb_submit_rx_urb_fwload(cardp); | ||
378 | kfree_skb(skb); | ||
379 | return; | ||
380 | } | ||
381 | if (cardp->bootcmdresp <= 0) { | ||
382 | memcpy(&bcmdresp, skb->data, sizeof(bcmdresp)); | ||
383 | |||
384 | if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { | ||
385 | kfree_skb(skb); | ||
386 | if_usb_submit_rx_urb_fwload(cardp); | ||
387 | cardp->bootcmdresp = 1; | ||
388 | /* Received valid boot command response */ | ||
389 | return; | ||
390 | } | ||
391 | if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { | ||
392 | if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || | ||
393 | bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || | ||
394 | bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) | ||
395 | cardp->bootcmdresp = -1; | ||
396 | } else if (bcmdresp.cmd == BOOT_CMD_FW_BY_USB && | ||
397 | bcmdresp.result == BOOT_CMD_RESP_OK) | ||
398 | cardp->bootcmdresp = 1; | ||
399 | |||
400 | kfree_skb(skb); | ||
401 | if_usb_submit_rx_urb_fwload(cardp); | ||
402 | return; | ||
403 | } | ||
404 | |||
405 | syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); | ||
406 | if (!syncfwheader) { | ||
407 | kfree_skb(skb); | ||
408 | return; | ||
409 | } | ||
410 | |||
411 | memcpy(syncfwheader, skb->data, sizeof(struct fwsyncheader)); | ||
412 | |||
413 | if (!syncfwheader->cmd) | ||
414 | cardp->CRC_OK = 1; | ||
415 | else | ||
416 | cardp->CRC_OK = 0; | ||
417 | kfree_skb(skb); | ||
418 | |||
419 | /* reschedule timer for 200ms hence */ | ||
420 | mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); | ||
421 | |||
422 | if (cardp->fwfinalblk) { | ||
423 | cardp->fwdnldover = 1; | ||
424 | goto exit; | ||
425 | } | ||
426 | |||
427 | if_usb_send_fw_pkt(cardp); | ||
428 | |||
429 | exit: | ||
430 | if_usb_submit_rx_urb_fwload(cardp); | ||
431 | |||
432 | kfree(syncfwheader); | ||
433 | |||
434 | return; | ||
435 | } | ||
436 | |||
437 | #define MRVDRV_MIN_PKT_LEN 30 | ||
438 | |||
439 | static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, | ||
440 | struct if_usb_card *cardp, | ||
441 | struct lbtf_private *priv) | ||
442 | { | ||
443 | if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN | ||
444 | || recvlength < MRVDRV_MIN_PKT_LEN) { | ||
445 | kfree_skb(skb); | ||
446 | return; | ||
447 | } | ||
448 | |||
449 | skb_put(skb, recvlength); | ||
450 | skb_pull(skb, MESSAGE_HEADER_LEN); | ||
451 | lbtf_rx(priv, skb); | ||
452 | } | ||
453 | |||
454 | static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, | ||
455 | struct sk_buff *skb, | ||
456 | struct if_usb_card *cardp, | ||
457 | struct lbtf_private *priv) | ||
458 | { | ||
459 | if (recvlength > LBS_CMD_BUFFER_SIZE) { | ||
460 | kfree_skb(skb); | ||
461 | return; | ||
462 | } | ||
463 | |||
464 | if (!in_interrupt()) | ||
465 | BUG(); | ||
466 | |||
467 | spin_lock(&priv->driver_lock); | ||
468 | memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN, | ||
469 | recvlength - MESSAGE_HEADER_LEN); | ||
470 | kfree_skb(skb); | ||
471 | lbtf_cmd_response_rx(priv); | ||
472 | spin_unlock(&priv->driver_lock); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * if_usb_receive - read data received from the device. | ||
477 | * | ||
478 | * @urb pointer to struct urb | ||
479 | */ | ||
480 | static void if_usb_receive(struct urb *urb) | ||
481 | { | ||
482 | struct if_usb_card *cardp = urb->context; | ||
483 | struct sk_buff *skb = cardp->rx_skb; | ||
484 | struct lbtf_private *priv = cardp->priv; | ||
485 | int recvlength = urb->actual_length; | ||
486 | uint8_t *recvbuff = NULL; | ||
487 | uint32_t recvtype = 0; | ||
488 | __le32 *pkt = (__le32 *) skb->data; | ||
489 | |||
490 | if (recvlength) { | ||
491 | if (urb->status) { | ||
492 | kfree_skb(skb); | ||
493 | goto setup_for_next; | ||
494 | } | ||
495 | |||
496 | recvbuff = skb->data; | ||
497 | recvtype = le32_to_cpu(pkt[0]); | ||
498 | } else if (urb->status) { | ||
499 | kfree_skb(skb); | ||
500 | return; | ||
501 | } | ||
502 | |||
503 | switch (recvtype) { | ||
504 | case CMD_TYPE_DATA: | ||
505 | process_cmdtypedata(recvlength, skb, cardp, priv); | ||
506 | break; | ||
507 | |||
508 | case CMD_TYPE_REQUEST: | ||
509 | process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); | ||
510 | break; | ||
511 | |||
512 | case CMD_TYPE_INDICATION: | ||
513 | { | ||
514 | /* Event cause handling */ | ||
515 | u32 event_cause = le32_to_cpu(pkt[1]); | ||
516 | |||
517 | /* Icky undocumented magic special case */ | ||
518 | if (event_cause & 0xffff0000) { | ||
519 | u16 tmp; | ||
520 | u8 retrycnt; | ||
521 | u8 failure; | ||
522 | |||
523 | tmp = event_cause >> 16; | ||
524 | retrycnt = tmp & 0x00ff; | ||
525 | failure = (tmp & 0xff00) >> 8; | ||
526 | lbtf_send_tx_feedback(priv, retrycnt, failure); | ||
527 | } else if (event_cause == LBTF_EVENT_BCN_SENT) | ||
528 | lbtf_bcn_sent(priv); | ||
529 | else | ||
530 | printk(KERN_DEBUG | ||
531 | "Unsupported notification %d received\n", | ||
532 | event_cause); | ||
533 | kfree_skb(skb); | ||
534 | break; | ||
535 | } | ||
536 | default: | ||
537 | printk(KERN_DEBUG "libertastf: unknown command type 0x%X\n", | ||
538 | recvtype); | ||
539 | kfree_skb(skb); | ||
540 | break; | ||
541 | } | ||
542 | |||
543 | setup_for_next: | ||
544 | if_usb_submit_rx_urb(cardp); | ||
545 | } | ||
546 | |||
547 | /** | ||
548 | * if_usb_host_to_card - Download data to the device | ||
549 | * | ||
550 | * @priv pointer to struct lbtf_private structure | ||
551 | * @type type of data | ||
552 | * @buf pointer to data buffer | ||
553 | * @len number of bytes | ||
554 | * | ||
555 | * Returns: 0 on success, nonzero otherwise | ||
556 | */ | ||
557 | static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, | ||
558 | uint8_t *payload, uint16_t nb) | ||
559 | { | ||
560 | struct if_usb_card *cardp = priv->card; | ||
561 | u8 data = 0; | ||
562 | |||
563 | if (type == MVMS_CMD) { | ||
564 | *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); | ||
565 | } else { | ||
566 | *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); | ||
567 | data = 1; | ||
568 | } | ||
569 | |||
570 | memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); | ||
571 | |||
572 | return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN, | ||
573 | data); | ||
574 | } | ||
575 | |||
576 | /** | ||
577 | * if_usb_issue_boot_command - Issue boot command to Boot2. | ||
578 | * | ||
579 | * @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM. | ||
580 | * | ||
581 | * Returns: 0 | ||
582 | */ | ||
583 | static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) | ||
584 | { | ||
585 | struct bootcmd *bootcmd = cardp->ep_out_buf; | ||
586 | |||
587 | /* Prepare command */ | ||
588 | bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); | ||
589 | bootcmd->cmd = ivalue; | ||
590 | memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); | ||
591 | |||
592 | /* Issue command */ | ||
593 | usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0); | ||
594 | |||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | |||
599 | /** | ||
600 | * check_fwfile_format - Check the validity of Boot2/FW image. | ||
601 | * | ||
602 | * @data pointer to image | ||
603 | * @totlen image length | ||
604 | * | ||
605 | * Returns: 0 if the image is valid, nonzero otherwise. | ||
606 | */ | ||
607 | static int check_fwfile_format(const u8 *data, u32 totlen) | ||
608 | { | ||
609 | u32 bincmd, exit; | ||
610 | u32 blksize, offset, len; | ||
611 | int ret; | ||
612 | |||
613 | ret = 1; | ||
614 | exit = len = 0; | ||
615 | |||
616 | do { | ||
617 | struct fwheader *fwh = (void *) data; | ||
618 | |||
619 | bincmd = le32_to_cpu(fwh->dnldcmd); | ||
620 | blksize = le32_to_cpu(fwh->datalength); | ||
621 | switch (bincmd) { | ||
622 | case FW_HAS_DATA_TO_RECV: | ||
623 | offset = sizeof(struct fwheader) + blksize; | ||
624 | data += offset; | ||
625 | len += offset; | ||
626 | if (len >= totlen) | ||
627 | exit = 1; | ||
628 | break; | ||
629 | case FW_HAS_LAST_BLOCK: | ||
630 | exit = 1; | ||
631 | ret = 0; | ||
632 | break; | ||
633 | default: | ||
634 | exit = 1; | ||
635 | break; | ||
636 | } | ||
637 | } while (!exit); | ||
638 | |||
639 | if (ret) | ||
640 | printk(KERN_INFO | ||
641 | "libertastf: firmware file format check failed\n"); | ||
642 | return ret; | ||
643 | } | ||
644 | |||
645 | |||
646 | static int if_usb_prog_firmware(struct if_usb_card *cardp) | ||
647 | { | ||
648 | int i = 0; | ||
649 | static int reset_count = 10; | ||
650 | int ret = 0; | ||
651 | |||
652 | ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev); | ||
653 | if (ret < 0) { | ||
654 | printk(KERN_INFO "libertastf: firmware %s not found\n", | ||
655 | lbtf_fw_name); | ||
656 | goto done; | ||
657 | } | ||
658 | |||
659 | if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) | ||
660 | goto release_fw; | ||
661 | |||
662 | restart: | ||
663 | if (if_usb_submit_rx_urb_fwload(cardp) < 0) { | ||
664 | ret = -1; | ||
665 | goto release_fw; | ||
666 | } | ||
667 | |||
668 | cardp->bootcmdresp = 0; | ||
669 | do { | ||
670 | int j = 0; | ||
671 | i++; | ||
672 | /* Issue Boot command = 1, Boot from Download-FW */ | ||
673 | if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); | ||
674 | /* wait for command response */ | ||
675 | do { | ||
676 | j++; | ||
677 | msleep_interruptible(100); | ||
678 | } while (cardp->bootcmdresp == 0 && j < 10); | ||
679 | } while (cardp->bootcmdresp == 0 && i < 5); | ||
680 | |||
681 | if (cardp->bootcmdresp <= 0) { | ||
682 | if (--reset_count >= 0) { | ||
683 | if_usb_reset_device(cardp); | ||
684 | goto restart; | ||
685 | } | ||
686 | return -1; | ||
687 | } | ||
688 | |||
689 | i = 0; | ||
690 | |||
691 | cardp->totalbytes = 0; | ||
692 | cardp->fwlastblksent = 0; | ||
693 | cardp->CRC_OK = 1; | ||
694 | cardp->fwdnldover = 0; | ||
695 | cardp->fwseqnum = -1; | ||
696 | cardp->totalbytes = 0; | ||
697 | cardp->fwfinalblk = 0; | ||
698 | |||
699 | /* Send the first firmware packet... */ | ||
700 | if_usb_send_fw_pkt(cardp); | ||
701 | |||
702 | /* ... and wait for the process to complete */ | ||
703 | wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved || | ||
704 | cardp->fwdnldover); | ||
705 | |||
706 | del_timer_sync(&cardp->fw_timeout); | ||
707 | usb_kill_urb(cardp->rx_urb); | ||
708 | |||
709 | if (!cardp->fwdnldover) { | ||
710 | printk(KERN_INFO "libertastf: failed to load fw," | ||
711 | " resetting device!\n"); | ||
712 | if (--reset_count >= 0) { | ||
713 | if_usb_reset_device(cardp); | ||
714 | goto restart; | ||
715 | } | ||
716 | |||
717 | printk(KERN_INFO "libertastf: fw download failure\n"); | ||
718 | ret = -1; | ||
719 | goto release_fw; | ||
720 | } | ||
721 | |||
722 | cardp->priv->fw_ready = 1; | ||
723 | |||
724 | release_fw: | ||
725 | release_firmware(cardp->fw); | ||
726 | cardp->fw = NULL; | ||
727 | |||
728 | if_usb_setup_firmware(cardp->priv); | ||
729 | |||
730 | done: | ||
731 | return ret; | ||
732 | } | ||
733 | EXPORT_SYMBOL_GPL(if_usb_prog_firmware); | ||
734 | |||
735 | |||
736 | #define if_usb_suspend NULL | ||
737 | #define if_usb_resume NULL | ||
738 | |||
739 | static struct usb_driver if_usb_driver = { | ||
740 | .name = DRV_NAME, | ||
741 | .probe = if_usb_probe, | ||
742 | .disconnect = if_usb_disconnect, | ||
743 | .id_table = if_usb_table, | ||
744 | .suspend = if_usb_suspend, | ||
745 | .resume = if_usb_resume, | ||
746 | }; | ||
747 | |||
748 | static int __init if_usb_init_module(void) | ||
749 | { | ||
750 | int ret = 0; | ||
751 | |||
752 | ret = usb_register(&if_usb_driver); | ||
753 | return ret; | ||
754 | } | ||
755 | |||
756 | static void __exit if_usb_exit_module(void) | ||
757 | { | ||
758 | usb_deregister(&if_usb_driver); | ||
759 | } | ||
760 | |||
761 | module_init(if_usb_init_module); | ||
762 | module_exit(if_usb_exit_module); | ||
763 | |||
764 | MODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver"); | ||
765 | MODULE_AUTHOR("Cozybit Inc."); | ||
766 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/net/wireless/libertas_tf/if_usb.h b/drivers/net/wireless/libertas_tf/if_usb.h new file mode 100644 index 000000000000..6fa5b3f59efe --- /dev/null +++ b/drivers/net/wireless/libertas_tf/if_usb.h | |||
@@ -0,0 +1,98 @@ | |||
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 <linux/wait.h> | ||
11 | #include <linux/timer.h> | ||
12 | |||
13 | struct lbtf_private; | ||
14 | |||
15 | /** | ||
16 | * This file contains definition for USB interface. | ||
17 | */ | ||
18 | #define CMD_TYPE_REQUEST 0xF00DFACE | ||
19 | #define CMD_TYPE_DATA 0xBEADC0DE | ||
20 | #define CMD_TYPE_INDICATION 0xBEEFFACE | ||
21 | |||
22 | #define BOOT_CMD_FW_BY_USB 0x01 | ||
23 | #define BOOT_CMD_FW_IN_EEPROM 0x02 | ||
24 | #define BOOT_CMD_UPDATE_BOOT2 0x03 | ||
25 | #define BOOT_CMD_UPDATE_FW 0x04 | ||
26 | #define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ | ||
27 | |||
28 | struct bootcmd { | ||
29 | __le32 magic; | ||
30 | uint8_t cmd; | ||
31 | uint8_t pad[11]; | ||
32 | }; | ||
33 | |||
34 | #define BOOT_CMD_RESP_OK 0x0001 | ||
35 | #define BOOT_CMD_RESP_FAIL 0x0000 | ||
36 | |||
37 | struct bootcmdresp { | ||
38 | __le32 magic; | ||
39 | uint8_t cmd; | ||
40 | uint8_t result; | ||
41 | uint8_t pad[2]; | ||
42 | }; | ||
43 | |||
44 | /** USB card description structure*/ | ||
45 | struct if_usb_card { | ||
46 | struct usb_device *udev; | ||
47 | struct urb *rx_urb, *tx_urb, *cmd_urb; | ||
48 | struct lbtf_private *priv; | ||
49 | |||
50 | struct sk_buff *rx_skb; | ||
51 | |||
52 | uint8_t ep_in; | ||
53 | uint8_t ep_out; | ||
54 | |||
55 | int8_t bootcmdresp; | ||
56 | |||
57 | int ep_in_size; | ||
58 | |||
59 | void *ep_out_buf; | ||
60 | int ep_out_size; | ||
61 | |||
62 | const struct firmware *fw; | ||
63 | struct timer_list fw_timeout; | ||
64 | wait_queue_head_t fw_wq; | ||
65 | uint32_t fwseqnum; | ||
66 | uint32_t totalbytes; | ||
67 | uint32_t fwlastblksent; | ||
68 | uint8_t CRC_OK; | ||
69 | uint8_t fwdnldover; | ||
70 | uint8_t fwfinalblk; | ||
71 | |||
72 | __le16 boot2_version; | ||
73 | }; | ||
74 | |||
75 | /** fwheader */ | ||
76 | struct fwheader { | ||
77 | __le32 dnldcmd; | ||
78 | __le32 baseaddr; | ||
79 | __le32 datalength; | ||
80 | __le32 CRC; | ||
81 | }; | ||
82 | |||
83 | #define FW_MAX_DATA_BLK_SIZE 600 | ||
84 | /** FWData */ | ||
85 | struct fwdata { | ||
86 | struct fwheader hdr; | ||
87 | __le32 seqnum; | ||
88 | uint8_t data[0]; | ||
89 | }; | ||
90 | |||
91 | /** fwsyncheader */ | ||
92 | struct fwsyncheader { | ||
93 | __le32 cmd; | ||
94 | __le32 seqnum; | ||
95 | }; | ||
96 | |||
97 | #define FW_HAS_DATA_TO_RECV 0x00000001 | ||
98 | #define FW_HAS_LAST_BLOCK 0x00000004 | ||
diff --git a/drivers/net/wireless/libertas_tf/libertas_tf.h b/drivers/net/wireless/libertas_tf/libertas_tf.h new file mode 100644 index 000000000000..8995cd7c29bf --- /dev/null +++ b/drivers/net/wireless/libertas_tf/libertas_tf.h | |||
@@ -0,0 +1,514 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008, cozybit Inc. | ||
3 | * Copyright (C) 2007, Red Hat, Inc. | ||
4 | * Copyright (C) 2003-2006, Marvell International Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or (at | ||
9 | * your option) any later version. | ||
10 | */ | ||
11 | #include <linux/spinlock.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/kthread.h> | ||
14 | #include <net/mac80211.h> | ||
15 | |||
16 | #ifndef DRV_NAME | ||
17 | #define DRV_NAME "libertas_tf" | ||
18 | #endif | ||
19 | |||
20 | #define MRVL_DEFAULT_RETRIES 9 | ||
21 | #define MRVL_PER_PACKET_RATE 0x10 | ||
22 | #define MRVL_MAX_BCN_SIZE 440 | ||
23 | #define CMD_OPTION_WAITFORRSP 0x0002 | ||
24 | |||
25 | /* Return command are almost always the same as the host command, but with | ||
26 | * bit 15 set high. There are a few exceptions, though... | ||
27 | */ | ||
28 | #define CMD_RET(cmd) (0x8000 | cmd) | ||
29 | |||
30 | /* Command codes */ | ||
31 | #define CMD_GET_HW_SPEC 0x0003 | ||
32 | #define CMD_802_11_RESET 0x0005 | ||
33 | #define CMD_MAC_MULTICAST_ADR 0x0010 | ||
34 | #define CMD_802_11_RADIO_CONTROL 0x001c | ||
35 | #define CMD_802_11_RF_CHANNEL 0x001d | ||
36 | #define CMD_802_11_RF_TX_POWER 0x001e | ||
37 | #define CMD_MAC_CONTROL 0x0028 | ||
38 | #define CMD_802_11_MAC_ADDRESS 0x004d | ||
39 | #define CMD_SET_BOOT2_VER 0x00a5 | ||
40 | #define CMD_802_11_BEACON_CTRL 0x00b0 | ||
41 | #define CMD_802_11_BEACON_SET 0x00cb | ||
42 | #define CMD_802_11_SET_MODE 0x00cc | ||
43 | #define CMD_802_11_SET_BSSID 0x00cd | ||
44 | |||
45 | #define CMD_ACT_GET 0x0000 | ||
46 | #define CMD_ACT_SET 0x0001 | ||
47 | |||
48 | /* Define action or option for CMD_802_11_RESET */ | ||
49 | #define CMD_ACT_HALT 0x0003 | ||
50 | |||
51 | /* Define action or option for CMD_MAC_CONTROL */ | ||
52 | #define CMD_ACT_MAC_RX_ON 0x0001 | ||
53 | #define CMD_ACT_MAC_TX_ON 0x0002 | ||
54 | #define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 | ||
55 | #define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 | ||
56 | #define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 | ||
57 | #define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 | ||
58 | |||
59 | /* Define action or option for CMD_802_11_RADIO_CONTROL */ | ||
60 | #define CMD_TYPE_AUTO_PREAMBLE 0x0001 | ||
61 | #define CMD_TYPE_SHORT_PREAMBLE 0x0002 | ||
62 | #define CMD_TYPE_LONG_PREAMBLE 0x0003 | ||
63 | |||
64 | #define TURN_ON_RF 0x01 | ||
65 | #define RADIO_ON 0x01 | ||
66 | #define RADIO_OFF 0x00 | ||
67 | |||
68 | #define SET_AUTO_PREAMBLE 0x05 | ||
69 | #define SET_SHORT_PREAMBLE 0x03 | ||
70 | #define SET_LONG_PREAMBLE 0x01 | ||
71 | |||
72 | /* Define action or option for CMD_802_11_RF_CHANNEL */ | ||
73 | #define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 | ||
74 | #define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 | ||
75 | |||
76 | /* Codes for CMD_802_11_SET_MODE */ | ||
77 | enum lbtf_mode { | ||
78 | LBTF_PASSIVE_MODE, | ||
79 | LBTF_STA_MODE, | ||
80 | LBTF_AP_MODE, | ||
81 | }; | ||
82 | |||
83 | /** Card Event definition */ | ||
84 | #define MACREG_INT_CODE_FIRMWARE_READY 48 | ||
85 | /** Buffer Constants */ | ||
86 | |||
87 | /* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical | ||
88 | * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas | ||
89 | * driver has more local TxPDs. Each TxPD on the host memory is associated | ||
90 | * with a Tx control node. The driver maintains 8 RxPD descriptors for | ||
91 | * station firmware to store Rx packet information. | ||
92 | * | ||
93 | * Current version of MAC has a 32x6 multicast address buffer. | ||
94 | * | ||
95 | * 802.11b can have up to 14 channels, the driver keeps the | ||
96 | * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. | ||
97 | */ | ||
98 | |||
99 | #define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 | ||
100 | #define LBS_NUM_CMD_BUFFERS 10 | ||
101 | #define LBS_CMD_BUFFER_SIZE (2 * 1024) | ||
102 | #define MRVDRV_MAX_CHANNEL_SIZE 14 | ||
103 | #define MRVDRV_SNAP_HEADER_LEN 8 | ||
104 | |||
105 | #define LBS_UPLD_SIZE 2312 | ||
106 | #define DEV_NAME_LEN 32 | ||
107 | |||
108 | /** Misc constants */ | ||
109 | /* This section defines 802.11 specific contants */ | ||
110 | |||
111 | #define MRVDRV_MAX_REGION_CODE 6 | ||
112 | /** | ||
113 | * the table to keep region code | ||
114 | */ | ||
115 | #define LBTF_REGDOMAIN_US 0x10 | ||
116 | #define LBTF_REGDOMAIN_CA 0x20 | ||
117 | #define LBTF_REGDOMAIN_EU 0x30 | ||
118 | #define LBTF_REGDOMAIN_SP 0x31 | ||
119 | #define LBTF_REGDOMAIN_FR 0x32 | ||
120 | #define LBTF_REGDOMAIN_JP 0x40 | ||
121 | |||
122 | #define SBI_EVENT_CAUSE_SHIFT 3 | ||
123 | |||
124 | /** RxPD status */ | ||
125 | |||
126 | #define MRVDRV_RXPD_STATUS_OK 0x0001 | ||
127 | |||
128 | |||
129 | /* This is for firmware specific length */ | ||
130 | #define EXTRA_LEN 36 | ||
131 | |||
132 | #define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ | ||
133 | (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) | ||
134 | |||
135 | #define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ | ||
136 | (ETH_FRAME_LEN + sizeof(struct rxpd) \ | ||
137 | + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) | ||
138 | |||
139 | #define CMD_F_HOSTCMD (1 << 0) | ||
140 | #define FW_CAPINFO_WPA (1 << 0) | ||
141 | |||
142 | #define RF_ANTENNA_1 0x1 | ||
143 | #define RF_ANTENNA_2 0x2 | ||
144 | #define RF_ANTENNA_AUTO 0xFFFF | ||
145 | |||
146 | #define LBTF_EVENT_BCN_SENT 55 | ||
147 | |||
148 | /** Global Variable Declaration */ | ||
149 | /** mv_ms_type */ | ||
150 | enum mv_ms_type { | ||
151 | MVMS_DAT = 0, | ||
152 | MVMS_CMD = 1, | ||
153 | MVMS_TXDONE = 2, | ||
154 | MVMS_EVENT | ||
155 | }; | ||
156 | |||
157 | extern struct workqueue_struct *lbtf_wq; | ||
158 | |||
159 | struct lbtf_private; | ||
160 | |||
161 | struct lbtf_offset_value { | ||
162 | u32 offset; | ||
163 | u32 value; | ||
164 | }; | ||
165 | |||
166 | struct channel_range { | ||
167 | u8 regdomain; | ||
168 | u8 start; | ||
169 | u8 end; /* exclusive (channel must be less than end) */ | ||
170 | }; | ||
171 | |||
172 | struct if_usb_card; | ||
173 | |||
174 | /** Private structure for the MV device */ | ||
175 | struct lbtf_private { | ||
176 | void *card; | ||
177 | struct ieee80211_hw *hw; | ||
178 | |||
179 | /* Command response buffer */ | ||
180 | u8 cmd_resp_buff[LBS_UPLD_SIZE]; | ||
181 | /* Download sent: | ||
182 | bit0 1/0=data_sent/data_tx_done, | ||
183 | bit1 1/0=cmd_sent/cmd_tx_done, | ||
184 | all other bits reserved 0 */ | ||
185 | struct ieee80211_vif *vif; | ||
186 | |||
187 | struct work_struct cmd_work; | ||
188 | struct work_struct tx_work; | ||
189 | /** Hardware access */ | ||
190 | int (*hw_host_to_card) (struct lbtf_private *priv, u8 type, u8 *payload, u16 nb); | ||
191 | int (*hw_prog_firmware) (struct if_usb_card *cardp); | ||
192 | int (*hw_reset_device) (struct if_usb_card *cardp); | ||
193 | |||
194 | |||
195 | /** Wlan adapter data structure*/ | ||
196 | /** STATUS variables */ | ||
197 | u32 fwrelease; | ||
198 | u32 fwcapinfo; | ||
199 | /* protected with big lock */ | ||
200 | |||
201 | struct mutex lock; | ||
202 | |||
203 | /** command-related variables */ | ||
204 | u16 seqnum; | ||
205 | /* protected by big lock */ | ||
206 | |||
207 | struct cmd_ctrl_node *cmd_array; | ||
208 | /** Current command */ | ||
209 | struct cmd_ctrl_node *cur_cmd; | ||
210 | /** command Queues */ | ||
211 | /** Free command buffers */ | ||
212 | struct list_head cmdfreeq; | ||
213 | /** Pending command buffers */ | ||
214 | struct list_head cmdpendingq; | ||
215 | |||
216 | /** spin locks */ | ||
217 | spinlock_t driver_lock; | ||
218 | |||
219 | /** Timers */ | ||
220 | struct timer_list command_timer; | ||
221 | int nr_retries; | ||
222 | int cmd_timed_out; | ||
223 | |||
224 | u8 cmd_response_rxed; | ||
225 | |||
226 | /** capability Info used in Association, start, join */ | ||
227 | u16 capability; | ||
228 | |||
229 | /** MAC address information */ | ||
230 | u8 current_addr[ETH_ALEN]; | ||
231 | u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; | ||
232 | u32 nr_of_multicastmacaddr; | ||
233 | int cur_freq; | ||
234 | |||
235 | struct sk_buff *skb_to_tx; | ||
236 | struct sk_buff *tx_skb; | ||
237 | |||
238 | /** NIC Operation characteristics */ | ||
239 | u16 mac_control; | ||
240 | u16 regioncode; | ||
241 | struct channel_range range; | ||
242 | |||
243 | u8 radioon; | ||
244 | u32 preamble; | ||
245 | |||
246 | struct ieee80211_channel channels[14]; | ||
247 | struct ieee80211_rate rates[12]; | ||
248 | struct ieee80211_supported_band band; | ||
249 | struct lbtf_offset_value offsetvalue; | ||
250 | |||
251 | u8 fw_ready; | ||
252 | u8 surpriseremoved; | ||
253 | struct sk_buff_head bc_ps_buf; | ||
254 | }; | ||
255 | |||
256 | /* 802.11-related definitions */ | ||
257 | |||
258 | /* TxPD descriptor */ | ||
259 | struct txpd { | ||
260 | /* Current Tx packet status */ | ||
261 | __le32 tx_status; | ||
262 | /* Tx control */ | ||
263 | __le32 tx_control; | ||
264 | __le32 tx_packet_location; | ||
265 | /* Tx packet length */ | ||
266 | __le16 tx_packet_length; | ||
267 | /* First 2 byte of destination MAC address */ | ||
268 | u8 tx_dest_addr_high[2]; | ||
269 | /* Last 4 byte of destination MAC address */ | ||
270 | u8 tx_dest_addr_low[4]; | ||
271 | /* Pkt Priority */ | ||
272 | u8 priority; | ||
273 | /* Pkt Trasnit Power control */ | ||
274 | u8 powermgmt; | ||
275 | /* Time the packet has been queued in the driver (units = 2ms) */ | ||
276 | u8 pktdelay_2ms; | ||
277 | /* reserved */ | ||
278 | u8 reserved1; | ||
279 | }; | ||
280 | |||
281 | /* RxPD Descriptor */ | ||
282 | struct rxpd { | ||
283 | /* Current Rx packet status */ | ||
284 | __le16 status; | ||
285 | |||
286 | /* SNR */ | ||
287 | u8 snr; | ||
288 | |||
289 | /* Tx control */ | ||
290 | u8 rx_control; | ||
291 | |||
292 | /* Pkt length */ | ||
293 | __le16 pkt_len; | ||
294 | |||
295 | /* Noise Floor */ | ||
296 | u8 nf; | ||
297 | |||
298 | /* Rx Packet Rate */ | ||
299 | u8 rx_rate; | ||
300 | |||
301 | /* Pkt addr */ | ||
302 | __le32 pkt_ptr; | ||
303 | |||
304 | /* Next Rx RxPD addr */ | ||
305 | __le32 next_rxpd_ptr; | ||
306 | |||
307 | /* Pkt Priority */ | ||
308 | u8 priority; | ||
309 | u8 reserved[3]; | ||
310 | }; | ||
311 | |||
312 | struct cmd_header { | ||
313 | __le16 command; | ||
314 | __le16 size; | ||
315 | __le16 seqnum; | ||
316 | __le16 result; | ||
317 | } __attribute__ ((packed)); | ||
318 | |||
319 | struct cmd_ctrl_node { | ||
320 | struct list_head list; | ||
321 | int result; | ||
322 | /* command response */ | ||
323 | int (*callback)(struct lbtf_private *, | ||
324 | unsigned long, struct cmd_header *); | ||
325 | unsigned long callback_arg; | ||
326 | /* command data */ | ||
327 | struct cmd_header *cmdbuf; | ||
328 | /* wait queue */ | ||
329 | u16 cmdwaitqwoken; | ||
330 | wait_queue_head_t cmdwait_q; | ||
331 | }; | ||
332 | |||
333 | /* | ||
334 | * Define data structure for CMD_GET_HW_SPEC | ||
335 | * This structure defines the response for the GET_HW_SPEC command | ||
336 | */ | ||
337 | struct cmd_ds_get_hw_spec { | ||
338 | struct cmd_header hdr; | ||
339 | |||
340 | /* HW Interface version number */ | ||
341 | __le16 hwifversion; | ||
342 | /* HW version number */ | ||
343 | __le16 version; | ||
344 | /* Max number of TxPD FW can handle */ | ||
345 | __le16 nr_txpd; | ||
346 | /* Max no of Multicast address */ | ||
347 | __le16 nr_mcast_adr; | ||
348 | /* MAC address */ | ||
349 | u8 permanentaddr[6]; | ||
350 | |||
351 | /* region Code */ | ||
352 | __le16 regioncode; | ||
353 | |||
354 | /* Number of antenna used */ | ||
355 | __le16 nr_antenna; | ||
356 | |||
357 | /* FW release number, example 0x01030304 = 2.3.4p1 */ | ||
358 | __le32 fwrelease; | ||
359 | |||
360 | /* Base Address of TxPD queue */ | ||
361 | __le32 wcb_base; | ||
362 | /* Read Pointer of RxPd queue */ | ||
363 | __le32 rxpd_rdptr; | ||
364 | |||
365 | /* Write Pointer of RxPd queue */ | ||
366 | __le32 rxpd_wrptr; | ||
367 | |||
368 | /*FW/HW capability */ | ||
369 | __le32 fwcapinfo; | ||
370 | } __attribute__ ((packed)); | ||
371 | |||
372 | struct cmd_ds_mac_control { | ||
373 | struct cmd_header hdr; | ||
374 | __le16 action; | ||
375 | u16 reserved; | ||
376 | }; | ||
377 | |||
378 | struct cmd_ds_802_11_mac_address { | ||
379 | struct cmd_header hdr; | ||
380 | |||
381 | __le16 action; | ||
382 | uint8_t macadd[ETH_ALEN]; | ||
383 | }; | ||
384 | |||
385 | struct cmd_ds_mac_multicast_addr { | ||
386 | struct cmd_header hdr; | ||
387 | |||
388 | __le16 action; | ||
389 | __le16 nr_of_adrs; | ||
390 | u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; | ||
391 | }; | ||
392 | |||
393 | struct cmd_ds_set_mode { | ||
394 | struct cmd_header hdr; | ||
395 | |||
396 | __le16 mode; | ||
397 | }; | ||
398 | |||
399 | struct cmd_ds_set_bssid { | ||
400 | struct cmd_header hdr; | ||
401 | |||
402 | u8 bssid[6]; | ||
403 | u8 activate; | ||
404 | }; | ||
405 | |||
406 | struct cmd_ds_802_11_radio_control { | ||
407 | struct cmd_header hdr; | ||
408 | |||
409 | __le16 action; | ||
410 | __le16 control; | ||
411 | }; | ||
412 | |||
413 | |||
414 | struct cmd_ds_802_11_rf_channel { | ||
415 | struct cmd_header hdr; | ||
416 | |||
417 | __le16 action; | ||
418 | __le16 channel; | ||
419 | __le16 rftype; /* unused */ | ||
420 | __le16 reserved; /* unused */ | ||
421 | u8 channellist[32]; /* unused */ | ||
422 | }; | ||
423 | |||
424 | struct cmd_ds_set_boot2_ver { | ||
425 | struct cmd_header hdr; | ||
426 | |||
427 | __le16 action; | ||
428 | __le16 version; | ||
429 | }; | ||
430 | |||
431 | struct cmd_ds_802_11_reset { | ||
432 | struct cmd_header hdr; | ||
433 | |||
434 | __le16 action; | ||
435 | }; | ||
436 | |||
437 | struct cmd_ds_802_11_beacon_control { | ||
438 | struct cmd_header hdr; | ||
439 | |||
440 | __le16 action; | ||
441 | __le16 beacon_enable; | ||
442 | __le16 beacon_period; | ||
443 | }; | ||
444 | |||
445 | struct cmd_ds_802_11_beacon_set { | ||
446 | struct cmd_header hdr; | ||
447 | |||
448 | __le16 len; | ||
449 | u8 beacon[MRVL_MAX_BCN_SIZE]; | ||
450 | }; | ||
451 | |||
452 | struct lbtf_private; | ||
453 | struct cmd_ctrl_node; | ||
454 | |||
455 | /** Function Prototype Declaration */ | ||
456 | void lbtf_set_mac_control(struct lbtf_private *priv); | ||
457 | |||
458 | int lbtf_free_cmd_buffer(struct lbtf_private *priv); | ||
459 | |||
460 | int lbtf_allocate_cmd_buffer(struct lbtf_private *priv); | ||
461 | int lbtf_execute_next_command(struct lbtf_private *priv); | ||
462 | int lbtf_set_radio_control(struct lbtf_private *priv); | ||
463 | int lbtf_update_hw_spec(struct lbtf_private *priv); | ||
464 | int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv); | ||
465 | void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode); | ||
466 | void lbtf_set_bssid(struct lbtf_private *priv, bool activate, u8 *bssid); | ||
467 | int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr); | ||
468 | |||
469 | int lbtf_set_channel(struct lbtf_private *priv, u8 channel); | ||
470 | |||
471 | int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon); | ||
472 | int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, | ||
473 | int beacon_int); | ||
474 | |||
475 | |||
476 | int lbtf_process_rx_command(struct lbtf_private *priv); | ||
477 | void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, | ||
478 | int result); | ||
479 | void lbtf_cmd_response_rx(struct lbtf_private *priv); | ||
480 | |||
481 | /* main.c */ | ||
482 | struct chan_freq_power *lbtf_get_region_cfp_table(u8 region, | ||
483 | int *cfp_no); | ||
484 | struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev); | ||
485 | int lbtf_remove_card(struct lbtf_private *priv); | ||
486 | int lbtf_start_card(struct lbtf_private *priv); | ||
487 | int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb); | ||
488 | void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail); | ||
489 | void lbtf_bcn_sent(struct lbtf_private *priv); | ||
490 | |||
491 | /* support functions for cmd.c */ | ||
492 | /* lbtf_cmd() infers the size of the buffer to copy data back into, from | ||
493 | the size of the target of the pointer. Since the command to be sent | ||
494 | may often be smaller, that size is set in cmd->size by the caller.*/ | ||
495 | #define lbtf_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ | ||
496 | uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ | ||
497 | (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ | ||
498 | __lbtf_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ | ||
499 | }) | ||
500 | |||
501 | #define lbtf_cmd_with_response(priv, cmdnr, cmd) \ | ||
502 | lbtf_cmd(priv, cmdnr, cmd, lbtf_cmd_copyback, (unsigned long) (cmd)) | ||
503 | |||
504 | void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, | ||
505 | struct cmd_header *in_cmd, int in_cmd_size); | ||
506 | |||
507 | int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, | ||
508 | struct cmd_header *in_cmd, int in_cmd_size, | ||
509 | int (*callback)(struct lbtf_private *, unsigned long, | ||
510 | struct cmd_header *), | ||
511 | unsigned long callback_arg); | ||
512 | |||
513 | int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, | ||
514 | struct cmd_header *resp); | ||
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c new file mode 100644 index 000000000000..feff945ad856 --- /dev/null +++ b/drivers/net/wireless/libertas_tf/main.c | |||
@@ -0,0 +1,662 @@ | |||
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 | #include "linux/etherdevice.h" | ||
12 | |||
13 | #define DRIVER_RELEASE_VERSION "004.p0" | ||
14 | /* thinfirm version: 5.132.X.pX */ | ||
15 | #define LBTF_FW_VER_MIN 0x05840300 | ||
16 | #define LBTF_FW_VER_MAX 0x0584ffff | ||
17 | #define QOS_CONTROL_LEN 2 | ||
18 | |||
19 | static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION; | ||
20 | struct workqueue_struct *lbtf_wq; | ||
21 | |||
22 | static const struct ieee80211_channel lbtf_channels[] = { | ||
23 | { .center_freq = 2412, .hw_value = 1 }, | ||
24 | { .center_freq = 2417, .hw_value = 2 }, | ||
25 | { .center_freq = 2422, .hw_value = 3 }, | ||
26 | { .center_freq = 2427, .hw_value = 4 }, | ||
27 | { .center_freq = 2432, .hw_value = 5 }, | ||
28 | { .center_freq = 2437, .hw_value = 6 }, | ||
29 | { .center_freq = 2442, .hw_value = 7 }, | ||
30 | { .center_freq = 2447, .hw_value = 8 }, | ||
31 | { .center_freq = 2452, .hw_value = 9 }, | ||
32 | { .center_freq = 2457, .hw_value = 10 }, | ||
33 | { .center_freq = 2462, .hw_value = 11 }, | ||
34 | { .center_freq = 2467, .hw_value = 12 }, | ||
35 | { .center_freq = 2472, .hw_value = 13 }, | ||
36 | { .center_freq = 2484, .hw_value = 14 }, | ||
37 | }; | ||
38 | |||
39 | /* This table contains the hardware specific values for the modulation rates. */ | ||
40 | static const struct ieee80211_rate lbtf_rates[] = { | ||
41 | { .bitrate = 10, | ||
42 | .hw_value = 0, }, | ||
43 | { .bitrate = 20, | ||
44 | .hw_value = 1, | ||
45 | .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||
46 | { .bitrate = 55, | ||
47 | .hw_value = 2, | ||
48 | .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||
49 | { .bitrate = 110, | ||
50 | .hw_value = 3, | ||
51 | .flags = IEEE80211_RATE_SHORT_PREAMBLE }, | ||
52 | { .bitrate = 60, | ||
53 | .hw_value = 5, | ||
54 | .flags = 0 }, | ||
55 | { .bitrate = 90, | ||
56 | .hw_value = 6, | ||
57 | .flags = 0 }, | ||
58 | { .bitrate = 120, | ||
59 | .hw_value = 7, | ||
60 | .flags = 0 }, | ||
61 | { .bitrate = 180, | ||
62 | .hw_value = 8, | ||
63 | .flags = 0 }, | ||
64 | { .bitrate = 240, | ||
65 | .hw_value = 9, | ||
66 | .flags = 0 }, | ||
67 | { .bitrate = 360, | ||
68 | .hw_value = 10, | ||
69 | .flags = 0 }, | ||
70 | { .bitrate = 480, | ||
71 | .hw_value = 11, | ||
72 | .flags = 0 }, | ||
73 | { .bitrate = 540, | ||
74 | .hw_value = 12, | ||
75 | .flags = 0 }, | ||
76 | }; | ||
77 | |||
78 | static void lbtf_cmd_work(struct work_struct *work) | ||
79 | { | ||
80 | struct lbtf_private *priv = container_of(work, struct lbtf_private, | ||
81 | cmd_work); | ||
82 | spin_lock_irq(&priv->driver_lock); | ||
83 | /* command response? */ | ||
84 | if (priv->cmd_response_rxed) { | ||
85 | priv->cmd_response_rxed = 0; | ||
86 | spin_unlock_irq(&priv->driver_lock); | ||
87 | lbtf_process_rx_command(priv); | ||
88 | spin_lock_irq(&priv->driver_lock); | ||
89 | } | ||
90 | |||
91 | if (priv->cmd_timed_out && priv->cur_cmd) { | ||
92 | struct cmd_ctrl_node *cmdnode = priv->cur_cmd; | ||
93 | |||
94 | if (++priv->nr_retries > 10) { | ||
95 | lbtf_complete_command(priv, cmdnode, | ||
96 | -ETIMEDOUT); | ||
97 | priv->nr_retries = 0; | ||
98 | } else { | ||
99 | priv->cur_cmd = NULL; | ||
100 | |||
101 | /* Stick it back at the _top_ of the pending | ||
102 | * queue for immediate resubmission */ | ||
103 | list_add(&cmdnode->list, &priv->cmdpendingq); | ||
104 | } | ||
105 | } | ||
106 | priv->cmd_timed_out = 0; | ||
107 | spin_unlock_irq(&priv->driver_lock); | ||
108 | |||
109 | if (!priv->fw_ready) | ||
110 | return; | ||
111 | /* Execute the next command */ | ||
112 | if (!priv->cur_cmd) | ||
113 | lbtf_execute_next_command(priv); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * lbtf_setup_firmware: initialize firmware. | ||
118 | * | ||
119 | * @priv A pointer to struct lbtf_private structure | ||
120 | * | ||
121 | * Returns: 0 on success. | ||
122 | */ | ||
123 | static int lbtf_setup_firmware(struct lbtf_private *priv) | ||
124 | { | ||
125 | int ret = -1; | ||
126 | |||
127 | /* | ||
128 | * Read priv address from HW | ||
129 | */ | ||
130 | memset(priv->current_addr, 0xff, ETH_ALEN); | ||
131 | ret = lbtf_update_hw_spec(priv); | ||
132 | if (ret) { | ||
133 | ret = -1; | ||
134 | goto done; | ||
135 | } | ||
136 | |||
137 | lbtf_set_mac_control(priv); | ||
138 | lbtf_set_radio_control(priv); | ||
139 | |||
140 | ret = 0; | ||
141 | done: | ||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * This function handles the timeout of command sending. | ||
147 | * It will re-send the same command again. | ||
148 | */ | ||
149 | static void command_timer_fn(unsigned long data) | ||
150 | { | ||
151 | struct lbtf_private *priv = (struct lbtf_private *)data; | ||
152 | unsigned long flags; | ||
153 | |||
154 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
155 | |||
156 | if (!priv->cur_cmd) { | ||
157 | printk(KERN_DEBUG "libertastf: command timer expired; " | ||
158 | "no pending command\n"); | ||
159 | goto out; | ||
160 | } | ||
161 | |||
162 | printk(KERN_DEBUG "libertas: command %x timed out\n", | ||
163 | le16_to_cpu(priv->cur_cmd->cmdbuf->command)); | ||
164 | |||
165 | priv->cmd_timed_out = 1; | ||
166 | queue_work(lbtf_wq, &priv->cmd_work); | ||
167 | out: | ||
168 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
169 | } | ||
170 | |||
171 | static int lbtf_init_adapter(struct lbtf_private *priv) | ||
172 | { | ||
173 | memset(priv->current_addr, 0xff, ETH_ALEN); | ||
174 | mutex_init(&priv->lock); | ||
175 | |||
176 | priv->vif = NULL; | ||
177 | setup_timer(&priv->command_timer, command_timer_fn, | ||
178 | (unsigned long)priv); | ||
179 | |||
180 | INIT_LIST_HEAD(&priv->cmdfreeq); | ||
181 | INIT_LIST_HEAD(&priv->cmdpendingq); | ||
182 | |||
183 | spin_lock_init(&priv->driver_lock); | ||
184 | |||
185 | /* Allocate the command buffers */ | ||
186 | if (lbtf_allocate_cmd_buffer(priv)) | ||
187 | return -1; | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static void lbtf_free_adapter(struct lbtf_private *priv) | ||
193 | { | ||
194 | lbtf_free_cmd_buffer(priv); | ||
195 | del_timer(&priv->command_timer); | ||
196 | } | ||
197 | |||
198 | static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | ||
199 | { | ||
200 | struct lbtf_private *priv = hw->priv; | ||
201 | |||
202 | priv->skb_to_tx = skb; | ||
203 | queue_work(lbtf_wq, &priv->tx_work); | ||
204 | /* | ||
205 | * queue will be restarted when we receive transmission feedback if | ||
206 | * there are no buffered multicast frames to send | ||
207 | */ | ||
208 | ieee80211_stop_queues(priv->hw); | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void lbtf_tx_work(struct work_struct *work) | ||
213 | { | ||
214 | struct lbtf_private *priv = container_of(work, struct lbtf_private, | ||
215 | tx_work); | ||
216 | unsigned int len; | ||
217 | struct ieee80211_tx_info *info; | ||
218 | struct txpd *txpd; | ||
219 | struct sk_buff *skb = NULL; | ||
220 | int err; | ||
221 | |||
222 | if ((priv->vif->type == NL80211_IFTYPE_AP) && | ||
223 | (!skb_queue_empty(&priv->bc_ps_buf))) | ||
224 | skb = skb_dequeue(&priv->bc_ps_buf); | ||
225 | else if (priv->skb_to_tx) { | ||
226 | skb = priv->skb_to_tx; | ||
227 | priv->skb_to_tx = NULL; | ||
228 | } else | ||
229 | return; | ||
230 | |||
231 | len = skb->len; | ||
232 | info = IEEE80211_SKB_CB(skb); | ||
233 | txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd)); | ||
234 | |||
235 | if (priv->surpriseremoved) { | ||
236 | dev_kfree_skb_any(skb); | ||
237 | return; | ||
238 | } | ||
239 | |||
240 | memset(txpd, 0, sizeof(struct txpd)); | ||
241 | /* Activate per-packet rate selection */ | ||
242 | txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE | | ||
243 | ieee80211_get_tx_rate(priv->hw, info)->hw_value); | ||
244 | |||
245 | /* copy destination address from 802.11 header */ | ||
246 | memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4, | ||
247 | ETH_ALEN); | ||
248 | txpd->tx_packet_length = cpu_to_le16(len); | ||
249 | txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); | ||
250 | BUG_ON(priv->tx_skb); | ||
251 | spin_lock_irq(&priv->driver_lock); | ||
252 | priv->tx_skb = skb; | ||
253 | err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len); | ||
254 | spin_unlock_irq(&priv->driver_lock); | ||
255 | if (err) { | ||
256 | dev_kfree_skb_any(skb); | ||
257 | priv->tx_skb = NULL; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | static int lbtf_op_start(struct ieee80211_hw *hw) | ||
262 | { | ||
263 | struct lbtf_private *priv = hw->priv; | ||
264 | void *card = priv->card; | ||
265 | int ret = -1; | ||
266 | |||
267 | if (!priv->fw_ready) | ||
268 | /* Upload firmware */ | ||
269 | if (priv->hw_prog_firmware(card)) | ||
270 | goto err_prog_firmware; | ||
271 | |||
272 | /* poke the firmware */ | ||
273 | priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; | ||
274 | priv->radioon = RADIO_ON; | ||
275 | priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; | ||
276 | ret = lbtf_setup_firmware(priv); | ||
277 | if (ret) | ||
278 | goto err_prog_firmware; | ||
279 | |||
280 | if ((priv->fwrelease < LBTF_FW_VER_MIN) || | ||
281 | (priv->fwrelease > LBTF_FW_VER_MAX)) { | ||
282 | ret = -1; | ||
283 | goto err_prog_firmware; | ||
284 | } | ||
285 | |||
286 | printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n"); | ||
287 | return 0; | ||
288 | |||
289 | err_prog_firmware: | ||
290 | priv->hw_reset_device(card); | ||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | static void lbtf_op_stop(struct ieee80211_hw *hw) | ||
295 | { | ||
296 | struct lbtf_private *priv = hw->priv; | ||
297 | unsigned long flags; | ||
298 | struct sk_buff *skb; | ||
299 | |||
300 | struct cmd_ctrl_node *cmdnode; | ||
301 | /* Flush pending command nodes */ | ||
302 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
303 | list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { | ||
304 | cmdnode->result = -ENOENT; | ||
305 | cmdnode->cmdwaitqwoken = 1; | ||
306 | wake_up_interruptible(&cmdnode->cmdwait_q); | ||
307 | } | ||
308 | |||
309 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
310 | cancel_work_sync(&priv->cmd_work); | ||
311 | cancel_work_sync(&priv->tx_work); | ||
312 | while ((skb = skb_dequeue(&priv->bc_ps_buf))) | ||
313 | dev_kfree_skb_any(skb); | ||
314 | priv->radioon = RADIO_OFF; | ||
315 | lbtf_set_radio_control(priv); | ||
316 | |||
317 | return; | ||
318 | } | ||
319 | |||
320 | static int lbtf_op_add_interface(struct ieee80211_hw *hw, | ||
321 | struct ieee80211_if_init_conf *conf) | ||
322 | { | ||
323 | struct lbtf_private *priv = hw->priv; | ||
324 | if (priv->vif != NULL) | ||
325 | return -EOPNOTSUPP; | ||
326 | |||
327 | priv->vif = conf->vif; | ||
328 | switch (conf->type) { | ||
329 | case NL80211_IFTYPE_MESH_POINT: | ||
330 | case NL80211_IFTYPE_AP: | ||
331 | lbtf_set_mode(priv, LBTF_AP_MODE); | ||
332 | break; | ||
333 | case NL80211_IFTYPE_STATION: | ||
334 | lbtf_set_mode(priv, LBTF_STA_MODE); | ||
335 | break; | ||
336 | default: | ||
337 | priv->vif = NULL; | ||
338 | return -EOPNOTSUPP; | ||
339 | } | ||
340 | lbtf_set_mac_address(priv, (u8 *) conf->mac_addr); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static void lbtf_op_remove_interface(struct ieee80211_hw *hw, | ||
345 | struct ieee80211_if_init_conf *conf) | ||
346 | { | ||
347 | struct lbtf_private *priv = hw->priv; | ||
348 | |||
349 | if (priv->vif->type == NL80211_IFTYPE_AP || | ||
350 | priv->vif->type == NL80211_IFTYPE_MESH_POINT) | ||
351 | lbtf_beacon_ctrl(priv, 0, 0); | ||
352 | lbtf_set_mode(priv, LBTF_PASSIVE_MODE); | ||
353 | lbtf_set_bssid(priv, 0, NULL); | ||
354 | priv->vif = NULL; | ||
355 | } | ||
356 | |||
357 | static int lbtf_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) | ||
358 | { | ||
359 | struct lbtf_private *priv = hw->priv; | ||
360 | if (conf->channel->center_freq != priv->cur_freq) { | ||
361 | priv->cur_freq = conf->channel->center_freq; | ||
362 | lbtf_set_channel(priv, conf->channel->hw_value); | ||
363 | } | ||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static int lbtf_op_config_interface(struct ieee80211_hw *hw, | ||
368 | struct ieee80211_vif *vif, | ||
369 | struct ieee80211_if_conf *conf) | ||
370 | { | ||
371 | struct lbtf_private *priv = hw->priv; | ||
372 | struct sk_buff *beacon; | ||
373 | |||
374 | switch (priv->vif->type) { | ||
375 | case NL80211_IFTYPE_AP: | ||
376 | case NL80211_IFTYPE_MESH_POINT: | ||
377 | beacon = ieee80211_beacon_get(hw, vif); | ||
378 | if (beacon) { | ||
379 | lbtf_beacon_set(priv, beacon); | ||
380 | kfree_skb(beacon); | ||
381 | lbtf_beacon_ctrl(priv, 1, hw->conf.beacon_int); | ||
382 | } | ||
383 | break; | ||
384 | default: | ||
385 | break; | ||
386 | } | ||
387 | |||
388 | if (conf->bssid) { | ||
389 | u8 null_bssid[ETH_ALEN] = {0}; | ||
390 | bool activate = compare_ether_addr(conf->bssid, null_bssid); | ||
391 | lbtf_set_bssid(priv, activate, conf->bssid); | ||
392 | } | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | #define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) | ||
398 | static void lbtf_op_configure_filter(struct ieee80211_hw *hw, | ||
399 | unsigned int changed_flags, | ||
400 | unsigned int *new_flags, | ||
401 | int mc_count, struct dev_mc_list *mclist) | ||
402 | { | ||
403 | struct lbtf_private *priv = hw->priv; | ||
404 | int old_mac_control = priv->mac_control; | ||
405 | int i; | ||
406 | changed_flags &= SUPPORTED_FIF_FLAGS; | ||
407 | *new_flags &= SUPPORTED_FIF_FLAGS; | ||
408 | |||
409 | if (!changed_flags) | ||
410 | return; | ||
411 | |||
412 | if (*new_flags & (FIF_PROMISC_IN_BSS)) | ||
413 | priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; | ||
414 | else | ||
415 | priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; | ||
416 | if (*new_flags & (FIF_ALLMULTI) || | ||
417 | mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) { | ||
418 | priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
419 | priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; | ||
420 | } else if (mc_count) { | ||
421 | priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; | ||
422 | priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; | ||
423 | priv->nr_of_multicastmacaddr = mc_count; | ||
424 | for (i = 0; i < mc_count; i++) { | ||
425 | if (!mclist) | ||
426 | break; | ||
427 | memcpy(&priv->multicastlist[i], mclist->da_addr, | ||
428 | ETH_ALEN); | ||
429 | mclist = mclist->next; | ||
430 | } | ||
431 | lbtf_cmd_set_mac_multicast_addr(priv); | ||
432 | } else { | ||
433 | priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | | ||
434 | CMD_ACT_MAC_ALL_MULTICAST_ENABLE); | ||
435 | if (priv->nr_of_multicastmacaddr) { | ||
436 | priv->nr_of_multicastmacaddr = 0; | ||
437 | lbtf_cmd_set_mac_multicast_addr(priv); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | |||
442 | if (priv->mac_control != old_mac_control) | ||
443 | lbtf_set_mac_control(priv); | ||
444 | } | ||
445 | |||
446 | static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, | ||
447 | struct ieee80211_vif *vif, | ||
448 | struct ieee80211_bss_conf *bss_conf, | ||
449 | u32 changes) | ||
450 | { | ||
451 | struct lbtf_private *priv = hw->priv; | ||
452 | |||
453 | if (changes & BSS_CHANGED_ERP_PREAMBLE) { | ||
454 | if (bss_conf->use_short_preamble) | ||
455 | priv->preamble = CMD_TYPE_SHORT_PREAMBLE; | ||
456 | else | ||
457 | priv->preamble = CMD_TYPE_LONG_PREAMBLE; | ||
458 | lbtf_set_radio_control(priv); | ||
459 | } | ||
460 | |||
461 | return; | ||
462 | } | ||
463 | |||
464 | static const struct ieee80211_ops lbtf_ops = { | ||
465 | .tx = lbtf_op_tx, | ||
466 | .start = lbtf_op_start, | ||
467 | .stop = lbtf_op_stop, | ||
468 | .add_interface = lbtf_op_add_interface, | ||
469 | .remove_interface = lbtf_op_remove_interface, | ||
470 | .config = lbtf_op_config, | ||
471 | .config_interface = lbtf_op_config_interface, | ||
472 | .configure_filter = lbtf_op_configure_filter, | ||
473 | .bss_info_changed = lbtf_op_bss_info_changed, | ||
474 | }; | ||
475 | |||
476 | int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) | ||
477 | { | ||
478 | struct ieee80211_rx_status stats; | ||
479 | struct rxpd *prxpd; | ||
480 | int need_padding; | ||
481 | unsigned int flags; | ||
482 | struct ieee80211_hdr *hdr; | ||
483 | |||
484 | prxpd = (struct rxpd *) skb->data; | ||
485 | |||
486 | stats.flag = 0; | ||
487 | if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) | ||
488 | stats.flag |= RX_FLAG_FAILED_FCS_CRC; | ||
489 | stats.freq = priv->cur_freq; | ||
490 | stats.band = IEEE80211_BAND_2GHZ; | ||
491 | stats.signal = prxpd->snr; | ||
492 | stats.noise = prxpd->nf; | ||
493 | stats.qual = prxpd->snr - prxpd->nf; | ||
494 | /* Marvell rate index has a hole at value 4 */ | ||
495 | if (prxpd->rx_rate > 4) | ||
496 | --prxpd->rx_rate; | ||
497 | stats.rate_idx = prxpd->rx_rate; | ||
498 | skb_pull(skb, sizeof(struct rxpd)); | ||
499 | |||
500 | hdr = (struct ieee80211_hdr *)skb->data; | ||
501 | flags = le32_to_cpu(*(__le32 *)(skb->data + 4)); | ||
502 | |||
503 | need_padding = ieee80211_is_data_qos(hdr->frame_control); | ||
504 | need_padding ^= ieee80211_has_a4(hdr->frame_control); | ||
505 | need_padding ^= ieee80211_is_data_qos(hdr->frame_control) && | ||
506 | (*ieee80211_get_qos_ctl(hdr) & | ||
507 | IEEE80211_QOS_CONTROL_A_MSDU_PRESENT); | ||
508 | |||
509 | if (need_padding) { | ||
510 | memmove(skb->data + 2, skb->data, skb->len); | ||
511 | skb_reserve(skb, 2); | ||
512 | } | ||
513 | |||
514 | ieee80211_rx_irqsafe(priv->hw, skb, &stats); | ||
515 | return 0; | ||
516 | } | ||
517 | EXPORT_SYMBOL_GPL(lbtf_rx); | ||
518 | |||
519 | /** | ||
520 | * lbtf_add_card: Add and initialize the card, no fw upload yet. | ||
521 | * | ||
522 | * @card A pointer to card | ||
523 | * | ||
524 | * Returns: pointer to struct lbtf_priv. | ||
525 | */ | ||
526 | struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) | ||
527 | { | ||
528 | struct ieee80211_hw *hw; | ||
529 | struct lbtf_private *priv = NULL; | ||
530 | |||
531 | hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops); | ||
532 | if (!hw) | ||
533 | goto done; | ||
534 | |||
535 | priv = hw->priv; | ||
536 | if (lbtf_init_adapter(priv)) | ||
537 | goto err_init_adapter; | ||
538 | |||
539 | priv->hw = hw; | ||
540 | priv->card = card; | ||
541 | priv->tx_skb = NULL; | ||
542 | |||
543 | hw->queues = 1; | ||
544 | hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; | ||
545 | hw->extra_tx_headroom = sizeof(struct txpd); | ||
546 | memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels)); | ||
547 | memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates)); | ||
548 | priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates); | ||
549 | priv->band.bitrates = priv->rates; | ||
550 | priv->band.n_channels = ARRAY_SIZE(lbtf_channels); | ||
551 | priv->band.channels = priv->channels; | ||
552 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; | ||
553 | skb_queue_head_init(&priv->bc_ps_buf); | ||
554 | |||
555 | SET_IEEE80211_DEV(hw, dmdev); | ||
556 | |||
557 | INIT_WORK(&priv->cmd_work, lbtf_cmd_work); | ||
558 | INIT_WORK(&priv->tx_work, lbtf_tx_work); | ||
559 | if (ieee80211_register_hw(hw)) | ||
560 | goto err_init_adapter; | ||
561 | |||
562 | goto done; | ||
563 | |||
564 | err_init_adapter: | ||
565 | lbtf_free_adapter(priv); | ||
566 | ieee80211_free_hw(hw); | ||
567 | priv = NULL; | ||
568 | |||
569 | done: | ||
570 | return priv; | ||
571 | } | ||
572 | EXPORT_SYMBOL_GPL(lbtf_add_card); | ||
573 | |||
574 | |||
575 | int lbtf_remove_card(struct lbtf_private *priv) | ||
576 | { | ||
577 | struct ieee80211_hw *hw = priv->hw; | ||
578 | |||
579 | priv->surpriseremoved = 1; | ||
580 | del_timer(&priv->command_timer); | ||
581 | lbtf_free_adapter(priv); | ||
582 | priv->hw = NULL; | ||
583 | ieee80211_unregister_hw(hw); | ||
584 | ieee80211_free_hw(hw); | ||
585 | |||
586 | return 0; | ||
587 | } | ||
588 | EXPORT_SYMBOL_GPL(lbtf_remove_card); | ||
589 | |||
590 | void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail) | ||
591 | { | ||
592 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); | ||
593 | memset(&info->status, 0, sizeof(info->status)); | ||
594 | /* | ||
595 | * Commented out, otherwise we never go beyond 1Mbit/s using mac80211 | ||
596 | * default pid rc algorithm. | ||
597 | * | ||
598 | * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt; | ||
599 | */ | ||
600 | info->status.excessive_retries = fail ? 1 : 0; | ||
601 | if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail) | ||
602 | info->flags |= IEEE80211_TX_STAT_ACK; | ||
603 | skb_pull(priv->tx_skb, sizeof(struct txpd)); | ||
604 | ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); | ||
605 | priv->tx_skb = NULL; | ||
606 | if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf)) | ||
607 | ieee80211_wake_queues(priv->hw); | ||
608 | else | ||
609 | queue_work(lbtf_wq, &priv->tx_work); | ||
610 | } | ||
611 | EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback); | ||
612 | |||
613 | void lbtf_bcn_sent(struct lbtf_private *priv) | ||
614 | { | ||
615 | struct sk_buff *skb = NULL; | ||
616 | |||
617 | if (priv->vif->type != NL80211_IFTYPE_AP) | ||
618 | return; | ||
619 | |||
620 | if (skb_queue_empty(&priv->bc_ps_buf)) { | ||
621 | bool tx_buff_bc = 0; | ||
622 | |||
623 | while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) { | ||
624 | skb_queue_tail(&priv->bc_ps_buf, skb); | ||
625 | tx_buff_bc = 1; | ||
626 | } | ||
627 | if (tx_buff_bc) { | ||
628 | ieee80211_stop_queues(priv->hw); | ||
629 | queue_work(lbtf_wq, &priv->tx_work); | ||
630 | } | ||
631 | } | ||
632 | |||
633 | skb = ieee80211_beacon_get(priv->hw, priv->vif); | ||
634 | |||
635 | if (skb) { | ||
636 | lbtf_beacon_set(priv, skb); | ||
637 | kfree_skb(skb); | ||
638 | } | ||
639 | } | ||
640 | EXPORT_SYMBOL_GPL(lbtf_bcn_sent); | ||
641 | |||
642 | static int __init lbtf_init_module(void) | ||
643 | { | ||
644 | lbtf_wq = create_workqueue("libertastf"); | ||
645 | if (lbtf_wq == NULL) { | ||
646 | printk(KERN_ERR "libertastf: couldn't create workqueue\n"); | ||
647 | return -ENOMEM; | ||
648 | } | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static void __exit lbtf_exit_module(void) | ||
653 | { | ||
654 | destroy_workqueue(lbtf_wq); | ||
655 | } | ||
656 | |||
657 | module_init(lbtf_init_module); | ||
658 | module_exit(lbtf_exit_module); | ||
659 | |||
660 | MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library"); | ||
661 | MODULE_AUTHOR("Cozybit Inc."); | ||
662 | MODULE_LICENSE("GPL"); | ||