diff options
author | Luis Carlos Cobo <luisca@cozybit.com> | 2008-08-14 13:41:06 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-08-22 16:29:56 -0400 |
commit | c305a19a0d0a47ac59a58865a4a63be65b1bf7c8 (patch) | |
tree | aef2eb72b258547aab40b1ebbdd413fca5d451e2 /drivers/net/wireless/libertas_tf | |
parent | 691cdb49388b808bfbfacaea93afb5c2807db45e (diff) |
libertas_tf: usb specific functions
The libertas thin firmware only supports usb devices, but the usb functions have
been kept separate to ease future support for other devices.
Signed-off-by: Luis Carlos Cobo <luisca@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas_tf')
-rw-r--r-- | drivers/net/wireless/libertas_tf/if_usb.c | 766 | ||||
-rw-r--r-- | drivers/net/wireless/libertas_tf/if_usb.h | 98 |
2 files changed, 864 insertions, 0 deletions
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 | ||