diff options
author | Marcelo Tosatti <marcelo@kvack.org> | 2007-02-10 09:25:27 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-04-28 11:00:54 -0400 |
commit | 876c9d3aeb989cf1961f2c228d309ba5dcfb1172 (patch) | |
tree | 239e9db92d13abc799c1ffc5304d8ec1503dbc61 /drivers/net/wireless/libertas/if_usb.c | |
parent | 35c3404efa7407811b706453f83d39b2539dcbd0 (diff) |
[PATCH] Marvell Libertas 8388 802.11b/g USB driver
Add the Marvell Libertas 8388 802.11 USB driver.
Signed-off-by: Marcelo Tosatti <marcelo@kvack.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/if_usb.c')
-rw-r--r-- | drivers/net/wireless/libertas/if_usb.c | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c new file mode 100644 index 00000000000..695fb6a66ff --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.c | |||
@@ -0,0 +1,952 @@ | |||
1 | /** | ||
2 | * This file contains functions used in USB interface module. | ||
3 | */ | ||
4 | #include <linux/delay.h> | ||
5 | #include <linux/firmware.h> | ||
6 | #include <linux/netdevice.h> | ||
7 | #include <linux/usb.h> | ||
8 | |||
9 | #include "host.h" | ||
10 | #include "sbi.h" | ||
11 | #include "decl.h" | ||
12 | #include "defs.h" | ||
13 | #include "dev.h" | ||
14 | #include "if_usb.h" | ||
15 | |||
16 | #define MESSAGE_HEADER_LEN 4 | ||
17 | |||
18 | static const char usbdriver_name[] = "usb8xxx"; | ||
19 | |||
20 | static struct usb_device_id if_usb_table[] = { | ||
21 | /* Enter the device signature inside */ | ||
22 | { | ||
23 | USB_DEVICE(USB8388_VID_1, USB8388_PID_1), | ||
24 | }, | ||
25 | { | ||
26 | USB_DEVICE(USB8388_VID_2, USB8388_PID_2), | ||
27 | }, | ||
28 | {} /* Terminating entry */ | ||
29 | }; | ||
30 | |||
31 | MODULE_DEVICE_TABLE(usb, if_usb_table); | ||
32 | |||
33 | static void if_usb_receive(struct urb *urb); | ||
34 | static void if_usb_receive_fwload(struct urb *urb); | ||
35 | |||
36 | /** | ||
37 | * @brief call back function to handle the status of the URB | ||
38 | * @param urb pointer to urb structure | ||
39 | * @return N/A | ||
40 | */ | ||
41 | static void if_usb_write_bulk_callback(struct urb *urb) | ||
42 | { | ||
43 | wlan_private *priv = (wlan_private *) (urb->context); | ||
44 | wlan_adapter *adapter = priv->adapter; | ||
45 | struct net_device *dev = priv->wlan_dev.netdev; | ||
46 | |||
47 | /* handle the transmission complete validations */ | ||
48 | |||
49 | if (urb->status != 0) { | ||
50 | /* print the failure status number for debug */ | ||
51 | lbs_pr_info("URB in failure status\n"); | ||
52 | } else { | ||
53 | lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n"); | ||
54 | lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n", | ||
55 | urb->actual_length); | ||
56 | priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; | ||
57 | /* Wake main thread if commands are pending */ | ||
58 | if (!adapter->cur_cmd) | ||
59 | wake_up_interruptible(&priv->mainthread.waitq); | ||
60 | if ((adapter->connect_status == libertas_connected)) | ||
61 | netif_wake_queue(dev); | ||
62 | } | ||
63 | |||
64 | return; | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * @brief free tx/rx urb, skb and rx buffer | ||
69 | * @param cardp pointer usb_card_rec | ||
70 | * @return N/A | ||
71 | */ | ||
72 | void if_usb_free(struct usb_card_rec *cardp) | ||
73 | { | ||
74 | ENTER(); | ||
75 | |||
76 | /* Unlink tx & rx urb */ | ||
77 | usb_kill_urb(cardp->tx_urb); | ||
78 | usb_kill_urb(cardp->rx_urb); | ||
79 | |||
80 | usb_free_urb(cardp->tx_urb); | ||
81 | cardp->tx_urb = NULL; | ||
82 | |||
83 | usb_free_urb(cardp->rx_urb); | ||
84 | cardp->rx_urb = NULL; | ||
85 | |||
86 | kfree(cardp->bulk_out_buffer); | ||
87 | cardp->bulk_out_buffer = NULL; | ||
88 | |||
89 | LEAVE(); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * @brief sets the configuration values | ||
95 | * @param ifnum interface number | ||
96 | * @param id pointer to usb_device_id | ||
97 | * @return 0 on success, error code on failure | ||
98 | */ | ||
99 | static int if_usb_probe(struct usb_interface *intf, | ||
100 | const struct usb_device_id *id) | ||
101 | { | ||
102 | struct usb_device *udev; | ||
103 | struct usb_host_interface *iface_desc; | ||
104 | struct usb_endpoint_descriptor *endpoint; | ||
105 | wlan_private *pwlanpriv; | ||
106 | struct usb_card_rec *usb_cardp; | ||
107 | int i; | ||
108 | |||
109 | udev = interface_to_usbdev(intf); | ||
110 | |||
111 | usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); | ||
112 | if (!usb_cardp) { | ||
113 | lbs_pr_err("Out of memory allocating private data.\n"); | ||
114 | goto error; | ||
115 | } | ||
116 | |||
117 | usb_cardp->udev = udev; | ||
118 | iface_desc = intf->cur_altsetting; | ||
119 | |||
120 | lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" | ||
121 | " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", | ||
122 | udev->descriptor.bcdUSB, | ||
123 | udev->descriptor.bDeviceClass, | ||
124 | udev->descriptor.bDeviceSubClass, | ||
125 | udev->descriptor.bDeviceProtocol); | ||
126 | |||
127 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
128 | endpoint = &iface_desc->endpoint[i].desc; | ||
129 | if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) | ||
130 | && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | ||
131 | USB_ENDPOINT_XFER_BULK)) { | ||
132 | /* we found a bulk in endpoint */ | ||
133 | lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n", | ||
134 | endpoint->wMaxPacketSize); | ||
135 | if (! | ||
136 | (usb_cardp->rx_urb = | ||
137 | usb_alloc_urb(0, GFP_KERNEL))) { | ||
138 | lbs_dev_dbg(1, &udev->dev, | ||
139 | "Rx URB allocation failed\n"); | ||
140 | goto dealloc; | ||
141 | } | ||
142 | usb_cardp->rx_urb_recall = 0; | ||
143 | |||
144 | usb_cardp->bulk_in_size = | ||
145 | endpoint->wMaxPacketSize; | ||
146 | usb_cardp->bulk_in_endpointAddr = | ||
147 | (endpoint-> | ||
148 | bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | ||
149 | lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n", | ||
150 | endpoint->bEndpointAddress); | ||
151 | } | ||
152 | |||
153 | if (((endpoint-> | ||
154 | bEndpointAddress & USB_ENDPOINT_DIR_MASK) == | ||
155 | USB_DIR_OUT) | ||
156 | && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == | ||
157 | USB_ENDPOINT_XFER_BULK)) { | ||
158 | /* We found bulk out endpoint */ | ||
159 | if (! | ||
160 | (usb_cardp->tx_urb = | ||
161 | usb_alloc_urb(0, GFP_KERNEL))) { | ||
162 | lbs_dev_dbg(1,&udev->dev, | ||
163 | "Tx URB allocation failed\n"); | ||
164 | goto dealloc; | ||
165 | } | ||
166 | |||
167 | usb_cardp->bulk_out_size = | ||
168 | endpoint->wMaxPacketSize; | ||
169 | lbs_dev_dbg(1, &udev->dev, | ||
170 | "Bulk out size is %d\n", | ||
171 | endpoint->wMaxPacketSize); | ||
172 | usb_cardp->bulk_out_endpointAddr = | ||
173 | endpoint->bEndpointAddress; | ||
174 | lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n", | ||
175 | endpoint->bEndpointAddress); | ||
176 | usb_cardp->bulk_out_buffer = | ||
177 | kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, | ||
178 | GFP_KERNEL); | ||
179 | |||
180 | if (!usb_cardp->bulk_out_buffer) { | ||
181 | lbs_dev_dbg(1, &udev->dev, | ||
182 | "Could not allocate buffer\n"); | ||
183 | goto dealloc; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | |||
189 | /* At this point wlan_add_card() will be called. Don't worry | ||
190 | * about keeping pwlanpriv around since it will be set on our | ||
191 | * usb device data in -> add() -> libertas_sbi_register_dev(). | ||
192 | */ | ||
193 | if (!(pwlanpriv = wlan_add_card(usb_cardp))) | ||
194 | goto dealloc; | ||
195 | |||
196 | usb_get_dev(udev); | ||
197 | usb_set_intfdata(intf, usb_cardp); | ||
198 | |||
199 | /* | ||
200 | * return card structure, which can be got back in the | ||
201 | * diconnect function as the ptr | ||
202 | * argument. | ||
203 | */ | ||
204 | return 0; | ||
205 | |||
206 | dealloc: | ||
207 | if_usb_free(usb_cardp); | ||
208 | |||
209 | error: | ||
210 | return -ENOMEM; | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * @brief free resource and cleanup | ||
215 | * @param udev pointer to usb_device | ||
216 | * @param ptr pointer to usb_cardp | ||
217 | * @return N/A | ||
218 | */ | ||
219 | static void if_usb_disconnect(struct usb_interface *intf) | ||
220 | { | ||
221 | struct usb_card_rec *cardp = usb_get_intfdata(intf); | ||
222 | wlan_private *priv = (wlan_private *) cardp->priv; | ||
223 | wlan_adapter *adapter = NULL; | ||
224 | |||
225 | adapter = priv->adapter; | ||
226 | |||
227 | /* | ||
228 | * Update Surprise removed to TRUE | ||
229 | */ | ||
230 | adapter->surpriseremoved = 1; | ||
231 | |||
232 | /* card is removed and we can call wlan_remove_card */ | ||
233 | lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n"); | ||
234 | wlan_remove_card(cardp); | ||
235 | |||
236 | /* Unlink and free urb */ | ||
237 | if_usb_free(cardp); | ||
238 | |||
239 | usb_set_intfdata(intf, NULL); | ||
240 | usb_put_dev(interface_to_usbdev(intf)); | ||
241 | |||
242 | return; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * @brief This function download FW | ||
247 | * @param priv pointer to wlan_private | ||
248 | * @return 0 | ||
249 | */ | ||
250 | static int if_prog_firmware(wlan_private * priv) | ||
251 | { | ||
252 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
253 | struct FWData *fwdata; | ||
254 | struct fwheader *fwheader; | ||
255 | u8 *firmware = priv->firmware->data; | ||
256 | |||
257 | fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC); | ||
258 | |||
259 | if (!fwdata) | ||
260 | return -1; | ||
261 | |||
262 | fwheader = &fwdata->fwheader; | ||
263 | |||
264 | if (!cardp->CRC_OK) { | ||
265 | cardp->totalbytes = cardp->fwlastblksent; | ||
266 | cardp->fwseqnum = cardp->lastseqnum - 1; | ||
267 | } | ||
268 | |||
269 | lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n", | ||
270 | cardp->totalbytes); | ||
271 | |||
272 | memcpy(fwheader, &firmware[cardp->totalbytes], | ||
273 | sizeof(struct fwheader)); | ||
274 | |||
275 | cardp->fwlastblksent = cardp->totalbytes; | ||
276 | cardp->totalbytes += sizeof(struct fwheader); | ||
277 | |||
278 | lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n"); | ||
279 | memcpy(fwdata->data, &firmware[cardp->totalbytes], | ||
280 | fwdata->fwheader.datalength); | ||
281 | |||
282 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
283 | "Data length = %d\n", fwdata->fwheader.datalength); | ||
284 | |||
285 | cardp->fwseqnum = cardp->fwseqnum + 1; | ||
286 | |||
287 | fwdata->seqnum = cardp->fwseqnum; | ||
288 | cardp->lastseqnum = fwdata->seqnum; | ||
289 | cardp->totalbytes += fwdata->fwheader.datalength; | ||
290 | |||
291 | if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) { | ||
292 | lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n"); | ||
293 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
294 | "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, | ||
295 | cardp->totalbytes); | ||
296 | memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); | ||
297 | usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); | ||
298 | |||
299 | } else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) { | ||
300 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
301 | "Host has finished FW downloading\n"); | ||
302 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
303 | "Donwloading FW JUMP BLOCK\n"); | ||
304 | memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); | ||
305 | usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); | ||
306 | cardp->fwfinalblk = 1; | ||
307 | } | ||
308 | |||
309 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
310 | "The firmware download is done size is %d\n", | ||
311 | cardp->totalbytes); | ||
312 | |||
313 | kfree(fwdata); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static int libertas_do_reset(wlan_private *priv) | ||
319 | { | ||
320 | int ret; | ||
321 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
322 | |||
323 | ret = usb_reset_device(cardp->udev); | ||
324 | if (!ret) { | ||
325 | msleep(10); | ||
326 | reset_device(priv); | ||
327 | msleep(10); | ||
328 | } | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * @brief This function transfer the data to the device. | ||
334 | * @param priv pointer to wlan_private | ||
335 | * @param payload pointer to payload data | ||
336 | * @param nb data length | ||
337 | * @return 0 or -1 | ||
338 | */ | ||
339 | int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb) | ||
340 | { | ||
341 | /* pointer to card structure */ | ||
342 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
343 | int ret = -1; | ||
344 | |||
345 | /* check if device is removed */ | ||
346 | if (priv->adapter->surpriseremoved) { | ||
347 | lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n"); | ||
348 | goto tx_ret; | ||
349 | } | ||
350 | |||
351 | usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, | ||
352 | usb_sndbulkpipe(cardp->udev, | ||
353 | cardp->bulk_out_endpointAddr), | ||
354 | payload, nb, if_usb_write_bulk_callback, priv); | ||
355 | |||
356 | cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; | ||
357 | |||
358 | if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { | ||
359 | /* transfer failed */ | ||
360 | lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n"); | ||
361 | ret = -1; | ||
362 | } else { | ||
363 | lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n"); | ||
364 | ret = 0; | ||
365 | } | ||
366 | |||
367 | tx_ret: | ||
368 | return ret; | ||
369 | } | ||
370 | |||
371 | static int __if_usb_submit_rx_urb(wlan_private * priv, | ||
372 | void (*callbackfn) | ||
373 | (struct urb *urb)) | ||
374 | { | ||
375 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
376 | struct sk_buff *skb; | ||
377 | struct read_cb_info *rinfo = &cardp->rinfo; | ||
378 | int ret = -1; | ||
379 | |||
380 | if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { | ||
381 | lbs_pr_err("No free skb\n"); | ||
382 | goto rx_ret; | ||
383 | } | ||
384 | |||
385 | rinfo->skb = skb; | ||
386 | |||
387 | /* Fill the receive configuration URB and initialise the Rx call back */ | ||
388 | usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, | ||
389 | usb_rcvbulkpipe(cardp->udev, | ||
390 | cardp->bulk_in_endpointAddr), | ||
391 | skb->tail + IPFIELD_ALIGN_OFFSET, | ||
392 | MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, | ||
393 | rinfo); | ||
394 | |||
395 | cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; | ||
396 | |||
397 | lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); | ||
398 | if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { | ||
399 | /* handle failure conditions */ | ||
400 | lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n"); | ||
401 | ret = -1; | ||
402 | } else { | ||
403 | lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n"); | ||
404 | ret = 0; | ||
405 | } | ||
406 | |||
407 | rx_ret: | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv) | ||
412 | { | ||
413 | return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload); | ||
414 | } | ||
415 | |||
416 | static inline int if_usb_submit_rx_urb(wlan_private * priv) | ||
417 | { | ||
418 | return __if_usb_submit_rx_urb(priv, &if_usb_receive); | ||
419 | } | ||
420 | |||
421 | static void if_usb_receive_fwload(struct urb *urb) | ||
422 | { | ||
423 | struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; | ||
424 | wlan_private *priv = rinfo->priv; | ||
425 | struct sk_buff *skb = rinfo->skb; | ||
426 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
427 | struct fwsyncheader *syncfwheader; | ||
428 | struct bootcmdrespStr bootcmdresp; | ||
429 | |||
430 | if (urb->status) { | ||
431 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
432 | "URB status is failed during fw load\n"); | ||
433 | kfree_skb(skb); | ||
434 | return; | ||
435 | } | ||
436 | |||
437 | if (cardp->bootcmdresp == 0) { | ||
438 | memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, | ||
439 | sizeof(bootcmdresp)); | ||
440 | if (cardp->udev->descriptor.bcdDevice < 0x3106) { | ||
441 | kfree_skb(skb); | ||
442 | if_usb_submit_rx_urb_fwload(priv); | ||
443 | cardp->bootcmdresp = 1; | ||
444 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
445 | "Received valid boot command response\n"); | ||
446 | return; | ||
447 | } | ||
448 | if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) { | ||
449 | lbs_pr_info( | ||
450 | "boot cmd response wrong magic number (0x%x)\n", | ||
451 | bootcmdresp.u32magicnumber); | ||
452 | } else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) { | ||
453 | lbs_pr_info( | ||
454 | "boot cmd response cmd_tag error (%d)\n", | ||
455 | bootcmdresp.u8cmd_tag); | ||
456 | } else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) { | ||
457 | lbs_pr_info( | ||
458 | "boot cmd response result error (%d)\n", | ||
459 | bootcmdresp.u8result); | ||
460 | } else { | ||
461 | cardp->bootcmdresp = 1; | ||
462 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
463 | "Received valid boot command response\n"); | ||
464 | } | ||
465 | kfree_skb(skb); | ||
466 | if_usb_submit_rx_urb_fwload(priv); | ||
467 | return; | ||
468 | } | ||
469 | |||
470 | syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); | ||
471 | if (!syncfwheader) { | ||
472 | lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n"); | ||
473 | kfree_skb(skb); | ||
474 | return; | ||
475 | } | ||
476 | |||
477 | memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET, | ||
478 | sizeof(struct fwsyncheader)); | ||
479 | |||
480 | if (!syncfwheader->cmd) { | ||
481 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
482 | "FW received Blk with correct CRC\n"); | ||
483 | lbs_dev_dbg(2, &cardp->udev->dev, | ||
484 | "FW received Blk seqnum = %d\n", | ||
485 | syncfwheader->seqnum); | ||
486 | cardp->CRC_OK = 1; | ||
487 | } else { | ||
488 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
489 | "FW received Blk with CRC error\n"); | ||
490 | cardp->CRC_OK = 0; | ||
491 | } | ||
492 | |||
493 | kfree_skb(skb); | ||
494 | |||
495 | if (cardp->fwfinalblk) { | ||
496 | cardp->fwdnldover = 1; | ||
497 | goto exit; | ||
498 | } | ||
499 | |||
500 | if_prog_firmware(priv); | ||
501 | |||
502 | if_usb_submit_rx_urb_fwload(priv); | ||
503 | exit: | ||
504 | kfree(syncfwheader); | ||
505 | |||
506 | return; | ||
507 | |||
508 | } | ||
509 | |||
510 | #define MRVDRV_MIN_PKT_LEN 30 | ||
511 | |||
512 | static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, | ||
513 | struct usb_card_rec *cardp, | ||
514 | wlan_private *priv) | ||
515 | { | ||
516 | if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + | ||
517 | MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { | ||
518 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
519 | "Packet length is Invalid\n"); | ||
520 | kfree_skb(skb); | ||
521 | return; | ||
522 | } | ||
523 | |||
524 | skb_reserve(skb, IPFIELD_ALIGN_OFFSET); | ||
525 | skb_put(skb, recvlength); | ||
526 | skb_pull(skb, MESSAGE_HEADER_LEN); | ||
527 | libertas_process_rxed_packet(priv, skb); | ||
528 | priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); | ||
529 | } | ||
530 | |||
531 | static inline void process_cmdrequest(int recvlength, u8 *recvbuff, | ||
532 | struct sk_buff *skb, | ||
533 | struct usb_card_rec *cardp, | ||
534 | wlan_private *priv) | ||
535 | { | ||
536 | u8 *cmdbuf; | ||
537 | if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) { | ||
538 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
539 | "The receive buffer is too large\n"); | ||
540 | kfree_skb(skb); | ||
541 | return; | ||
542 | } | ||
543 | |||
544 | if (!in_interrupt()) | ||
545 | BUG(); | ||
546 | |||
547 | spin_lock(&priv->adapter->driver_lock); | ||
548 | /* take care of cur_cmd = NULL case by reading the | ||
549 | * data to clear the interrupt */ | ||
550 | if (!priv->adapter->cur_cmd) { | ||
551 | cmdbuf = priv->wlan_dev.upld_buf; | ||
552 | priv->adapter->hisregcpy &= ~his_cmdupldrdy; | ||
553 | } else | ||
554 | cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr; | ||
555 | |||
556 | cardp->usb_int_cause |= his_cmdupldrdy; | ||
557 | priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); | ||
558 | memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN, | ||
559 | priv->wlan_dev.upld_len); | ||
560 | |||
561 | kfree_skb(skb); | ||
562 | libertas_interrupt(priv->wlan_dev.netdev); | ||
563 | spin_unlock(&priv->adapter->driver_lock); | ||
564 | |||
565 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
566 | "Wake up main thread to handle cmd response\n"); | ||
567 | |||
568 | return; | ||
569 | } | ||
570 | |||
571 | /** | ||
572 | * @brief This function reads of the packet into the upload buff, | ||
573 | * wake up the main thread and initialise the Rx callack. | ||
574 | * | ||
575 | * @param urb pointer to struct urb | ||
576 | * @return N/A | ||
577 | */ | ||
578 | static void if_usb_receive(struct urb *urb) | ||
579 | { | ||
580 | struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; | ||
581 | wlan_private *priv = rinfo->priv; | ||
582 | struct sk_buff *skb = rinfo->skb; | ||
583 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
584 | |||
585 | int recvlength = urb->actual_length; | ||
586 | u8 *recvbuff = NULL; | ||
587 | u32 recvtype; | ||
588 | |||
589 | ENTER(); | ||
590 | |||
591 | if (recvlength) { | ||
592 | if (urb->status) { | ||
593 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
594 | "URB status is failed\n"); | ||
595 | kfree_skb(skb); | ||
596 | goto setup_for_next; | ||
597 | } | ||
598 | |||
599 | recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; | ||
600 | memcpy(&recvtype, recvbuff, sizeof(u32)); | ||
601 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
602 | "Recv length = 0x%x\n", recvlength); | ||
603 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
604 | "Receive type = 0x%X\n", recvtype); | ||
605 | recvtype = le32_to_cpu(recvtype); | ||
606 | lbs_dev_dbg(1, &cardp->udev->dev, | ||
607 | "Receive type after = 0x%X\n", recvtype); | ||
608 | } else if (urb->status) | ||
609 | goto rx_exit; | ||
610 | |||
611 | |||
612 | switch (recvtype) { | ||
613 | case CMD_TYPE_DATA: | ||
614 | process_cmdtypedata(recvlength, skb, cardp, priv); | ||
615 | break; | ||
616 | |||
617 | case CMD_TYPE_REQUEST: | ||
618 | process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); | ||
619 | break; | ||
620 | |||
621 | case CMD_TYPE_INDICATION: | ||
622 | /* Event cause handling */ | ||
623 | spin_lock(&priv->adapter->driver_lock); | ||
624 | cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN); | ||
625 | lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n", | ||
626 | cardp->usb_event_cause); | ||
627 | if (cardp->usb_event_cause & 0xffff0000) { | ||
628 | libertas_send_tx_feedback(priv); | ||
629 | break; | ||
630 | } | ||
631 | cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3; | ||
632 | cardp->usb_int_cause |= his_cardevent; | ||
633 | kfree_skb(skb); | ||
634 | libertas_interrupt(priv->wlan_dev.netdev); | ||
635 | spin_unlock(&priv->adapter->driver_lock); | ||
636 | goto rx_exit; | ||
637 | default: | ||
638 | kfree_skb(skb); | ||
639 | break; | ||
640 | } | ||
641 | |||
642 | setup_for_next: | ||
643 | if_usb_submit_rx_urb(priv); | ||
644 | rx_exit: | ||
645 | LEAVE(); | ||
646 | return; | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * @brief This function downloads data to FW | ||
651 | * @param priv pointer to wlan_private structure | ||
652 | * @param type type of data | ||
653 | * @param buf pointer to data buffer | ||
654 | * @param len number of bytes | ||
655 | * @return 0 or -1 | ||
656 | */ | ||
657 | int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb) | ||
658 | { | ||
659 | int ret = -1; | ||
660 | u32 tmp; | ||
661 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
662 | |||
663 | lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type); | ||
664 | lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb); | ||
665 | |||
666 | if (type == MVMS_CMD) { | ||
667 | tmp = cpu_to_le32(CMD_TYPE_REQUEST); | ||
668 | priv->wlan_dev.dnld_sent = DNLD_CMD_SENT; | ||
669 | memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, | ||
670 | MESSAGE_HEADER_LEN); | ||
671 | |||
672 | } else { | ||
673 | tmp = cpu_to_le32(CMD_TYPE_DATA); | ||
674 | priv->wlan_dev.dnld_sent = DNLD_DATA_SENT; | ||
675 | memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, | ||
676 | MESSAGE_HEADER_LEN); | ||
677 | } | ||
678 | |||
679 | memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb); | ||
680 | |||
681 | ret = | ||
682 | usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN); | ||
683 | |||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | /* called with adapter->driver_lock held */ | ||
688 | int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg) | ||
689 | { | ||
690 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
691 | |||
692 | *ireg = cardp->usb_int_cause; | ||
693 | cardp->usb_int_cause = 0; | ||
694 | |||
695 | lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg); | ||
696 | |||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | int libertas_sbi_read_event_cause(wlan_private * priv) | ||
701 | { | ||
702 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
703 | priv->adapter->eventcause = cardp->usb_event_cause; | ||
704 | /* Re-submit rx urb here to avoid event lost issue */ | ||
705 | if_usb_submit_rx_urb(priv); | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | int reset_device(wlan_private *priv) | ||
710 | { | ||
711 | int ret; | ||
712 | |||
713 | ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset, | ||
714 | cmd_act_halt, 0, 0, NULL); | ||
715 | msleep_interruptible(10); | ||
716 | |||
717 | return ret; | ||
718 | } | ||
719 | |||
720 | int libertas_sbi_unregister_dev(wlan_private * priv) | ||
721 | { | ||
722 | int ret = 0; | ||
723 | |||
724 | /* Need to send a Reset command to device before USB resources freed | ||
725 | * and wlan_remove_card() called, then device can handle FW download | ||
726 | * again. | ||
727 | */ | ||
728 | if (priv) | ||
729 | reset_device(priv); | ||
730 | |||
731 | return ret; | ||
732 | } | ||
733 | |||
734 | |||
735 | /** | ||
736 | * @brief This function register usb device and initialize parameter | ||
737 | * @param priv pointer to wlan_private | ||
738 | * @return 0 or -1 | ||
739 | */ | ||
740 | int libertas_sbi_register_dev(wlan_private * priv) | ||
741 | { | ||
742 | |||
743 | struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; | ||
744 | ENTER(); | ||
745 | |||
746 | cardp->priv = priv; | ||
747 | cardp->eth_dev = priv->wlan_dev.netdev; | ||
748 | priv->hotplug_device = &(cardp->udev->dev); | ||
749 | |||
750 | SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev)); | ||
751 | |||
752 | lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n", | ||
753 | cardp->udev); | ||
754 | |||
755 | LEAVE(); | ||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | |||
760 | |||
761 | int libertas_sbi_prog_firmware(wlan_private * priv) | ||
762 | { | ||
763 | struct usb_card_rec *cardp = priv->wlan_dev.card; | ||
764 | int i = 0; | ||
765 | static int reset_count = 10; | ||
766 | |||
767 | ENTER(); | ||
768 | |||
769 | cardp->rinfo.priv = priv; | ||
770 | |||
771 | restart: | ||
772 | if (if_usb_submit_rx_urb_fwload(priv) < 0) { | ||
773 | lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n"); | ||
774 | LEAVE(); | ||
775 | return -1; | ||
776 | } | ||
777 | |||
778 | #ifdef SUPPORT_BOOT_COMMAND | ||
779 | cardp->bootcmdresp = 0; | ||
780 | do { | ||
781 | int j = 0; | ||
782 | i++; | ||
783 | /* Issue Boot command = 1, Boot from Download-FW */ | ||
784 | if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB); | ||
785 | /* wait for command response */ | ||
786 | do { | ||
787 | j++; | ||
788 | msleep_interruptible(100); | ||
789 | } while (cardp->bootcmdresp == 0 && j < 10); | ||
790 | } while (cardp->bootcmdresp == 0 && i < 5); | ||
791 | |||
792 | if (cardp->bootcmdresp == 0) { | ||
793 | if (--reset_count >= 0) { | ||
794 | libertas_do_reset(priv); | ||
795 | goto restart; | ||
796 | } | ||
797 | return -1; | ||
798 | } | ||
799 | #endif | ||
800 | |||
801 | i = 0; | ||
802 | priv->adapter->fw_ready = 0; | ||
803 | |||
804 | cardp->totalbytes = 0; | ||
805 | cardp->fwlastblksent = 0; | ||
806 | cardp->CRC_OK = 1; | ||
807 | cardp->fwdnldover = 0; | ||
808 | cardp->fwseqnum = -1; | ||
809 | cardp->totalbytes = 0; | ||
810 | cardp->fwfinalblk = 0; | ||
811 | |||
812 | if_prog_firmware(priv); | ||
813 | |||
814 | do { | ||
815 | lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n"); | ||
816 | i++; | ||
817 | msleep_interruptible(100); | ||
818 | if (priv->adapter->surpriseremoved || i >= 20) | ||
819 | break; | ||
820 | } while (!cardp->fwdnldover); | ||
821 | |||
822 | if (!cardp->fwdnldover) { | ||
823 | lbs_pr_info("failed to load fw, resetting device!\n"); | ||
824 | if (--reset_count >= 0) { | ||
825 | libertas_do_reset(priv); | ||
826 | goto restart; | ||
827 | } | ||
828 | |||
829 | lbs_pr_info("FW download failure, time = %d ms\n", i * 100); | ||
830 | LEAVE(); | ||
831 | return -1; | ||
832 | } | ||
833 | |||
834 | if_usb_submit_rx_urb(priv); | ||
835 | |||
836 | /* Delay 200 ms to waiting for the FW ready */ | ||
837 | msleep_interruptible(200); | ||
838 | |||
839 | priv->adapter->fw_ready = 1; | ||
840 | |||
841 | LEAVE(); | ||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | /** | ||
846 | * @brief Given a usb_card_rec return its wlan_private | ||
847 | * @param card pointer to a usb_card_rec | ||
848 | * @return pointer to wlan_private | ||
849 | */ | ||
850 | wlan_private *libertas_sbi_get_priv(void *card) | ||
851 | { | ||
852 | struct usb_card_rec *cardp = card; | ||
853 | return cardp->priv; | ||
854 | } | ||
855 | |||
856 | #ifdef ENABLE_PM | ||
857 | int libertas_sbi_suspend(wlan_private * priv) | ||
858 | { | ||
859 | return 0; | ||
860 | } | ||
861 | |||
862 | int libertas_sbi_resume(wlan_private * priv) | ||
863 | { | ||
864 | return 0; | ||
865 | } | ||
866 | #endif | ||
867 | |||
868 | #ifdef CONFIG_PM | ||
869 | static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) | ||
870 | { | ||
871 | struct usb_card_rec *cardp = usb_get_intfdata(intf); | ||
872 | wlan_private *priv = cardp->priv; | ||
873 | |||
874 | ENTER(); | ||
875 | |||
876 | if (priv->adapter->psstate != PS_STATE_FULL_POWER) | ||
877 | return -1; | ||
878 | |||
879 | netif_device_detach(cardp->eth_dev); | ||
880 | |||
881 | /* Unlink tx & rx urb */ | ||
882 | usb_kill_urb(cardp->tx_urb); | ||
883 | usb_kill_urb(cardp->rx_urb); | ||
884 | |||
885 | cardp->rx_urb_recall = 1; | ||
886 | |||
887 | LEAVE(); | ||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | static int if_usb_resume(struct usb_interface *intf) | ||
892 | { | ||
893 | struct usb_card_rec *cardp = usb_get_intfdata(intf); | ||
894 | |||
895 | ENTER(); | ||
896 | |||
897 | cardp->rx_urb_recall = 0; | ||
898 | |||
899 | if_usb_submit_rx_urb(cardp->priv); | ||
900 | |||
901 | netif_device_attach(cardp->eth_dev); | ||
902 | |||
903 | LEAVE(); | ||
904 | return 0; | ||
905 | } | ||
906 | #else | ||
907 | #define if_usb_suspend NULL | ||
908 | #define if_usb_resume NULL | ||
909 | #endif | ||
910 | |||
911 | static struct usb_driver if_usb_driver = { | ||
912 | /* driver name */ | ||
913 | .name = usbdriver_name, | ||
914 | /* probe function name */ | ||
915 | .probe = if_usb_probe, | ||
916 | /* disconnect function name */ | ||
917 | .disconnect = if_usb_disconnect, | ||
918 | /* device signature table */ | ||
919 | .id_table = if_usb_table, | ||
920 | .suspend = if_usb_suspend, | ||
921 | .resume = if_usb_resume, | ||
922 | }; | ||
923 | |||
924 | /** | ||
925 | * @brief This function registers driver. | ||
926 | * @param add pointer to add_card callback function | ||
927 | * @param remove pointer to remove card callback function | ||
928 | * @param arg pointer to call back function parameter | ||
929 | * @return dummy success variable | ||
930 | */ | ||
931 | int libertas_sbi_register(void) | ||
932 | { | ||
933 | /* | ||
934 | * API registers the Marvell USB driver | ||
935 | * to the USB system | ||
936 | */ | ||
937 | usb_register(&if_usb_driver); | ||
938 | |||
939 | /* Return success to wlan layer */ | ||
940 | return 0; | ||
941 | } | ||
942 | |||
943 | /** | ||
944 | * @brief This function removes usb driver. | ||
945 | * @return N/A | ||
946 | */ | ||
947 | void libertas_sbi_unregister(void) | ||
948 | { | ||
949 | /* API unregisters the driver from USB subsystem */ | ||
950 | usb_deregister(&if_usb_driver); | ||
951 | return; | ||
952 | } | ||