blob: a0cb265e5816bacb6e91a3c3c90591dcdef4a90b [file] [log] [blame]
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001/**
2 * This file contains functions used in USB interface module.
3 */
4#include <linux/delay.h>
Holger Schurig084708b2007-05-25 12:37:58 -04005#include <linux/moduleparam.h>
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02006#include <linux/firmware.h>
7#include <linux/netdevice.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09008#include <linux/slab.h>
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02009#include <linux/usb.h>
10
David Woodhouseb77ec4c2008-05-20 16:48:52 +010011#ifdef CONFIG_OLPC
12#include <asm/olpc.h>
13#endif
14
Holger Schurigec3eef22007-05-25 12:49:10 -040015#define DRV_NAME "usb8xxx"
16
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020017#include "host.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020018#include "decl.h"
19#include "defs.h"
20#include "dev.h"
Dan Williams14e865b2007-12-10 15:11:23 -050021#include "cmd.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020022#include "if_usb.h"
23
David Woodhouseeae86bf2007-12-14 00:47:05 -050024#define INSANEDEBUG 0
25#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0)
26
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020027#define MESSAGE_HEADER_LEN 4
28
Andres Salomon82209ad2007-11-20 17:43:32 -050029static char *lbs_fw_name = "usb8388.bin";
Holger Schurig10078322007-11-15 18:05:47 -050030module_param_named(fw_name, lbs_fw_name, charp, 0644);
Holger Schurig084708b2007-05-25 12:37:58 -040031
Ben Hutchingsa974a4b2009-11-07 22:00:03 +000032MODULE_FIRMWARE("usb8388.bin");
33
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020034static struct usb_device_id if_usb_table[] = {
35 /* Enter the device signature inside */
Holger Schurig66fcc552007-05-25 00:11:58 -040036 { USB_DEVICE(0x1286, 0x2001) },
37 { USB_DEVICE(0x05a3, 0x8388) },
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020038 {} /* Terminating entry */
39};
40
41MODULE_DEVICE_TABLE(usb, if_usb_table);
42
43static void if_usb_receive(struct urb *urb);
44static void if_usb_receive_fwload(struct urb *urb);
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -070045static int __if_usb_prog_firmware(struct if_usb_card *cardp,
46 const char *fwname, int cmd);
47static int if_usb_prog_firmware(struct if_usb_card *cardp,
48 const char *fwname, int cmd);
David Woodhouseeae86bf2007-12-14 00:47:05 -050049static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
50 uint8_t *payload, uint16_t nb);
David Woodhouseeae86bf2007-12-14 00:47:05 -050051static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
52 uint16_t nb);
53static void if_usb_free(struct if_usb_card *cardp);
54static int if_usb_submit_rx_urb(struct if_usb_card *cardp);
55static int if_usb_reset_device(struct if_usb_card *cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020056
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -070057/* sysfs hooks */
58
59/**
60 * Set function to write firmware to device's persistent memory
61 */
62static ssize_t if_usb_firmware_set(struct device *dev,
63 struct device_attribute *attr, const char *buf, size_t count)
64{
Kiran Divekarab65f642009-02-19 19:32:39 -050065 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -070066 struct if_usb_card *cardp = priv->card;
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -070067 int ret;
68
John W. Linvillecadeba32009-05-27 00:49:36 +020069 ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_FW);
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -070070 if (ret == 0)
71 return count;
72
73 return ret;
74}
75
76/**
77 * lbs_flash_fw attribute to be exported per ethX interface through sysfs
78 * (/sys/class/net/ethX/lbs_flash_fw). Use this like so to write firmware to
79 * the device's persistent memory:
80 * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_fw
81 */
82static DEVICE_ATTR(lbs_flash_fw, 0200, NULL, if_usb_firmware_set);
83
84/**
85 * Set function to write firmware to device's persistent memory
86 */
87static ssize_t if_usb_boot2_set(struct device *dev,
88 struct device_attribute *attr, const char *buf, size_t count)
89{
Kiran Divekarab65f642009-02-19 19:32:39 -050090 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -070091 struct if_usb_card *cardp = priv->card;
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -070092 int ret;
93
John W. Linvillecadeba32009-05-27 00:49:36 +020094 ret = if_usb_prog_firmware(cardp, buf, BOOT_CMD_UPDATE_BOOT2);
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -070095 if (ret == 0)
96 return count;
97
98 return ret;
99}
100
101/**
102 * lbs_flash_boot2 attribute to be exported per ethX interface through sysfs
103 * (/sys/class/net/ethX/lbs_flash_boot2). Use this like so to write firmware
104 * to the device's persistent memory:
105 * echo usb8388-5.126.0.p5.bin > /sys/class/net/ethX/lbs_flash_boot2
106 */
107static DEVICE_ATTR(lbs_flash_boot2, 0200, NULL, if_usb_boot2_set);
108
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200109/**
110 * @brief call back function to handle the status of the URB
111 * @param urb pointer to urb structure
112 * @return N/A
113 */
114static void if_usb_write_bulk_callback(struct urb *urb)
115{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500116 struct if_usb_card *cardp = (struct if_usb_card *) urb->context;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200117
118 /* handle the transmission complete validations */
119
Dan Williams954ee162007-08-20 11:43:25 -0400120 if (urb->status == 0) {
Holger Schurig69f90322007-11-23 15:43:44 +0100121 struct lbs_private *priv = cardp->priv;
Dan Williams954ee162007-08-20 11:43:25 -0400122
David Woodhouseeae86bf2007-12-14 00:47:05 -0500123 lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n");
124 lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n",
125 urb->actual_length);
Dan Williams954ee162007-08-20 11:43:25 -0400126
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700127 /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not
128 * passed up to the lbs level.
Dan Williams954ee162007-08-20 11:43:25 -0400129 */
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700130 if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT)
David Woodhousee775ed72007-12-06 14:36:11 +0000131 lbs_host_to_card_done(priv);
Dan Williams954ee162007-08-20 11:43:25 -0400132 } else {
133 /* print the failure status number for debug */
134 lbs_pr_info("URB in failure status: %d\n", urb->status);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200135 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200136}
137
138/**
139 * @brief free tx/rx urb, skb and rx buffer
David Woodhouseeae86bf2007-12-14 00:47:05 -0500140 * @param cardp pointer if_usb_card
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200141 * @return N/A
142 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500143static void if_usb_free(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200144{
Holger Schurig9012b282007-05-25 11:27:16 -0400145 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200146
147 /* Unlink tx & rx urb */
148 usb_kill_urb(cardp->tx_urb);
149 usb_kill_urb(cardp->rx_urb);
150
151 usb_free_urb(cardp->tx_urb);
152 cardp->tx_urb = NULL;
153
154 usb_free_urb(cardp->rx_urb);
155 cardp->rx_urb = NULL;
156
David Woodhouseeae86bf2007-12-14 00:47:05 -0500157 kfree(cardp->ep_out_buf);
158 cardp->ep_out_buf = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200159
Holger Schurig9012b282007-05-25 11:27:16 -0400160 lbs_deb_leave(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200161}
162
David Woodhouse46949612007-12-17 14:42:33 -0500163static void if_usb_setup_firmware(struct lbs_private *priv)
David Woodhouse04c80f12007-12-06 12:51:00 +0000164{
Holger Schurig43659292008-01-16 15:52:58 +0100165 struct if_usb_card *cardp = priv->card;
David Woodhouse04c80f12007-12-06 12:51:00 +0000166 struct cmd_ds_set_boot2_ver b2_cmd;
David Woodhouse46949612007-12-17 14:42:33 -0500167 struct cmd_ds_802_11_fw_wake_method wake_method;
David Woodhouse04c80f12007-12-06 12:51:00 +0000168
David Woodhouse9fae8992007-12-15 03:46:44 -0500169 b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd));
David Woodhouse04c80f12007-12-06 12:51:00 +0000170 b2_cmd.action = 0;
Holger Schurig43659292008-01-16 15:52:58 +0100171 b2_cmd.version = cardp->boot2_version;
David Woodhouse04c80f12007-12-06 12:51:00 +0000172
David Woodhouseb23b2062007-12-15 01:22:09 -0500173 if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd))
David Woodhouse04c80f12007-12-06 12:51:00 +0000174 lbs_deb_usb("Setting boot2 version failed\n");
David Woodhouse46949612007-12-17 14:42:33 -0500175
176 priv->wol_gpio = 2; /* Wake via GPIO2... */
177 priv->wol_gap = 20; /* ... after 20ms */
Anna Neal582c1b52008-10-20 16:46:56 -0700178 lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA,
179 (struct wol_config *) NULL);
David Woodhouse46949612007-12-17 14:42:33 -0500180
181 wake_method.hdr.size = cpu_to_le16(sizeof(wake_method));
182 wake_method.action = cpu_to_le16(CMD_ACT_GET);
183 if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) {
184 lbs_pr_info("Firmware does not seem to support PS mode\n");
Andrey Yurovskye0d61332009-06-16 13:20:01 -0700185 priv->fwcapinfo &= ~FW_CAPINFO_PS;
David Woodhouse46949612007-12-17 14:42:33 -0500186 } else {
187 if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) {
188 lbs_deb_usb("Firmware seems to support PS with wake-via-command\n");
David Woodhouse46949612007-12-17 14:42:33 -0500189 } else {
190 /* The versions which boot up this way don't seem to
191 work even if we set it to the command interrupt */
Andrey Yurovskye0d61332009-06-16 13:20:01 -0700192 priv->fwcapinfo &= ~FW_CAPINFO_PS;
David Woodhouse46949612007-12-17 14:42:33 -0500193 lbs_pr_info("Firmware doesn't wake via command interrupt; disabling PS mode\n");
194 }
195 }
David Woodhouse04c80f12007-12-06 12:51:00 +0000196}
197
David Woodhouse2fd6cfe2007-12-11 17:44:10 -0500198static void if_usb_fw_timeo(unsigned long priv)
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500199{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500200 struct if_usb_card *cardp = (void *)priv;
David Woodhouse04c80f12007-12-06 12:51:00 +0000201
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500202 if (cardp->fwdnldover) {
203 lbs_deb_usb("Download complete, no event. Assuming success\n");
204 } else {
205 lbs_pr_err("Download timed out\n");
206 cardp->surprise_removed = 1;
207 }
208 wake_up(&cardp->fw_wq);
209}
David Woodhouseeae86bf2007-12-14 00:47:05 -0500210
David Woodhouseb77ec4c2008-05-20 16:48:52 +0100211#ifdef CONFIG_OLPC
212static void if_usb_reset_olpc_card(struct lbs_private *priv)
213{
214 printk(KERN_CRIT "Resetting OLPC wireless via EC...\n");
215 olpc_ec_cmd(0x25, NULL, 0, NULL, 0);
216}
217#endif
218
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200219/**
220 * @brief sets the configuration values
221 * @param ifnum interface number
222 * @param id pointer to usb_device_id
223 * @return 0 on success, error code on failure
224 */
225static int if_usb_probe(struct usb_interface *intf,
226 const struct usb_device_id *id)
227{
228 struct usb_device *udev;
229 struct usb_host_interface *iface_desc;
230 struct usb_endpoint_descriptor *endpoint;
Holger Schurig69f90322007-11-23 15:43:44 +0100231 struct lbs_private *priv;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500232 struct if_usb_card *cardp;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200233 int i;
234
235 udev = interface_to_usbdev(intf);
236
David Woodhouseeae86bf2007-12-14 00:47:05 -0500237 cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL);
Holger Schurig435a1ac2007-05-25 12:41:52 -0400238 if (!cardp) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200239 lbs_pr_err("Out of memory allocating private data.\n");
240 goto error;
241 }
242
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500243 setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp);
244 init_waitqueue_head(&cardp->fw_wq);
David Woodhouse7e226272007-12-14 22:53:41 -0500245
Holger Schurig435a1ac2007-05-25 12:41:52 -0400246 cardp->udev = udev;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200247 iface_desc = intf->cur_altsetting;
248
Holger Schurig9012b282007-05-25 11:27:16 -0400249 lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
David Woodhouseeae86bf2007-12-14 00:47:05 -0500250 " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
David Woodhouse981f1872007-05-25 23:36:54 -0400251 le16_to_cpu(udev->descriptor.bcdUSB),
252 udev->descriptor.bDeviceClass,
253 udev->descriptor.bDeviceSubClass,
254 udev->descriptor.bDeviceProtocol);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200255
256 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
257 endpoint = &iface_desc->endpoint[i].desc;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500258 if (usb_endpoint_is_bulk_in(endpoint)) {
259 cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
260 cardp->ep_in = usb_endpoint_num(endpoint);
261
262 lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in);
263 lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size);
264
265 } else if (usb_endpoint_is_bulk_out(endpoint)) {
266 cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize);
267 cardp->ep_out = usb_endpoint_num(endpoint);
268
269 lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out);
270 lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200271 }
David Woodhouseeae86bf2007-12-14 00:47:05 -0500272 }
273 if (!cardp->ep_out_size || !cardp->ep_in_size) {
274 lbs_deb_usbd(&udev->dev, "Endpoints not found\n");
275 goto dealloc;
276 }
277 if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
278 lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
279 goto dealloc;
280 }
281 if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
282 lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
283 goto dealloc;
284 }
285 cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL);
286 if (!cardp->ep_out_buf) {
287 lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n");
288 goto dealloc;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200289 }
290
Dan Williams954ee162007-08-20 11:43:25 -0400291 /* Upload firmware */
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700292 if (__if_usb_prog_firmware(cardp, lbs_fw_name, BOOT_CMD_FW_BY_USB)) {
David Woodhouse23d36ee2007-12-12 00:14:21 -0500293 lbs_deb_usbd(&udev->dev, "FW upload failed\n");
Dan Williams954ee162007-08-20 11:43:25 -0400294 goto err_prog_firmware;
295 }
Holger Schurig3874d0f2007-05-25 12:01:42 -0400296
Holger Schurig10078322007-11-15 18:05:47 -0500297 if (!(priv = lbs_add_card(cardp, &udev->dev)))
Dan Williams954ee162007-08-20 11:43:25 -0400298 goto err_prog_firmware;
299
300 cardp->priv = priv;
David Woodhousec9cd6f92007-12-11 13:15:25 -0500301 cardp->priv->fw_ready = 1;
Luis Carlos Cobo965f8bbc2007-08-02 13:16:55 -0400302
Holger Schurig208fdd22007-05-25 12:17:06 -0400303 priv->hw_host_to_card = if_usb_host_to_card;
Amitkumar Karwar49125452009-09-30 20:04:38 -0700304 priv->enter_deep_sleep = NULL;
305 priv->exit_deep_sleep = NULL;
306 priv->reset_deep_sleep_wakeup = NULL;
David Woodhouseb77ec4c2008-05-20 16:48:52 +0100307#ifdef CONFIG_OLPC
308 if (machine_is_olpc())
309 priv->reset_card = if_usb_reset_olpc_card;
310#endif
311
Holger Schurig43659292008-01-16 15:52:58 +0100312 cardp->boot2_version = udev->descriptor.bcdDevice;
Holger Schurig208fdd22007-05-25 12:17:06 -0400313
Dan Williams954ee162007-08-20 11:43:25 -0400314 if_usb_submit_rx_urb(cardp);
Dan Williams954ee162007-08-20 11:43:25 -0400315
Holger Schurig10078322007-11-15 18:05:47 -0500316 if (lbs_start_card(priv))
Dan Williams954ee162007-08-20 11:43:25 -0400317 goto err_start_card;
Holger Schurig32a74b72007-05-25 12:04:31 -0400318
David Woodhouse46949612007-12-17 14:42:33 -0500319 if_usb_setup_firmware(priv);
David Woodhoused1f7a5b2007-12-12 17:40:56 -0500320
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200321 usb_get_dev(udev);
Holger Schurig435a1ac2007-05-25 12:41:52 -0400322 usb_set_intfdata(intf, cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200323
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -0700324 if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_fw))
325 lbs_pr_err("cannot register lbs_flash_fw attribute\n");
326
327 if (device_create_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2))
328 lbs_pr_err("cannot register lbs_flash_boot2 attribute\n");
329
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200330 return 0;
331
Dan Williams954ee162007-08-20 11:43:25 -0400332err_start_card:
Holger Schurig10078322007-11-15 18:05:47 -0500333 lbs_remove_card(priv);
Dan Williams954ee162007-08-20 11:43:25 -0400334err_prog_firmware:
335 if_usb_reset_device(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200336dealloc:
Holger Schurig435a1ac2007-05-25 12:41:52 -0400337 if_usb_free(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200338
339error:
340 return -ENOMEM;
341}
342
343/**
344 * @brief free resource and cleanup
Holger Schurig435a1ac2007-05-25 12:41:52 -0400345 * @param intf USB interface structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200346 * @return N/A
347 */
348static void if_usb_disconnect(struct usb_interface *intf)
349{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500350 struct if_usb_card *cardp = usb_get_intfdata(intf);
Holger Schurig69f90322007-11-23 15:43:44 +0100351 struct lbs_private *priv = (struct lbs_private *) cardp->priv;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200352
Dan Williams954ee162007-08-20 11:43:25 -0400353 lbs_deb_enter(LBS_DEB_MAIN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200354
Brian Cavagnolo1ff41eb2008-07-21 11:03:16 -0700355 device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_boot2);
356 device_remove_file(&priv->dev->dev, &dev_attr_lbs_flash_fw);
357
Dan Williams954ee162007-08-20 11:43:25 -0400358 cardp->surprise_removed = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200359
Dan Williams954ee162007-08-20 11:43:25 -0400360 if (priv) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000361 priv->surpriseremoved = 1;
Holger Schurig10078322007-11-15 18:05:47 -0500362 lbs_stop_card(priv);
Holger Schurig10078322007-11-15 18:05:47 -0500363 lbs_remove_card(priv);
Dan Williams954ee162007-08-20 11:43:25 -0400364 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200365
366 /* Unlink and free urb */
367 if_usb_free(cardp);
368
369 usb_set_intfdata(intf, NULL);
370 usb_put_dev(interface_to_usbdev(intf));
371
Dan Williams954ee162007-08-20 11:43:25 -0400372 lbs_deb_leave(LBS_DEB_MAIN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200373}
374
375/**
376 * @brief This function download FW
Holger Schurig69f90322007-11-23 15:43:44 +0100377 * @param priv pointer to struct lbs_private
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200378 * @return 0
379 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500380static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200381{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500382 struct fwdata *fwdata = cardp->ep_out_buf;
David Woodhouse6dfff892008-05-23 18:37:51 +0100383 const uint8_t *firmware = cardp->fw->data;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200384
David Woodhouseeae86bf2007-12-14 00:47:05 -0500385 /* If we got a CRC failure on the last block, back
386 up and retry it */
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200387 if (!cardp->CRC_OK) {
388 cardp->totalbytes = cardp->fwlastblksent;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500389 cardp->fwseqnum--;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200390 }
391
David Woodhouseeae86bf2007-12-14 00:47:05 -0500392 lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
393 cardp->totalbytes);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200394
David Woodhouseeae86bf2007-12-14 00:47:05 -0500395 /* struct fwdata (which we sent to the card) has an
396 extra __le32 field in between the header and the data,
397 which is not in the struct fwheader in the actual
398 firmware binary. Insert the seqnum in the middle... */
399 memcpy(&fwdata->hdr, &firmware[cardp->totalbytes],
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200400 sizeof(struct fwheader));
401
402 cardp->fwlastblksent = cardp->totalbytes;
403 cardp->totalbytes += sizeof(struct fwheader);
404
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200405 memcpy(fwdata->data, &firmware[cardp->totalbytes],
David Woodhouseeae86bf2007-12-14 00:47:05 -0500406 le32_to_cpu(fwdata->hdr.datalength));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200407
David Woodhouseeae86bf2007-12-14 00:47:05 -0500408 lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
409 le32_to_cpu(fwdata->hdr.datalength));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200410
David Woodhouseeae86bf2007-12-14 00:47:05 -0500411 fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
412 cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200413
David Woodhouseeae86bf2007-12-14 00:47:05 -0500414 usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
415 le32_to_cpu(fwdata->hdr.datalength));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200416
David Woodhouseeae86bf2007-12-14 00:47:05 -0500417 if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
418 lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
419 lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n",
420 cardp->fwseqnum, cardp->totalbytes);
421 } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
422 lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n");
423 lbs_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200424
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200425 cardp->fwfinalblk = 1;
426 }
427
David Woodhouseeae86bf2007-12-14 00:47:05 -0500428 lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
429 cardp->totalbytes);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200430
431 return 0;
432}
433
David Woodhouseeae86bf2007-12-14 00:47:05 -0500434static int if_usb_reset_device(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200435{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500436 struct cmd_ds_command *cmd = cardp->ep_out_buf + 4;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200437 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200438
Holger Schurig3874d0f2007-05-25 12:01:42 -0400439 lbs_deb_enter(LBS_DEB_USB);
440
David Woodhouseeae86bf2007-12-14 00:47:05 -0500441 *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000442
443 cmd->command = cpu_to_le16(CMD_802_11_RESET);
Dan Williamsf8e77ca2008-09-10 09:04:33 -0400444 cmd->size = cpu_to_le16(sizeof(struct cmd_header));
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000445 cmd->result = cpu_to_le16(0);
446 cmd->seqnum = cpu_to_le16(0x5a5a);
Dan Williamsf8e77ca2008-09-10 09:04:33 -0400447 usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header));
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000448
David Woodhousec8ba39d2007-12-10 18:53:34 -0500449 msleep(100);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200450 ret = usb_reset_device(cardp->udev);
David Woodhousec8ba39d2007-12-10 18:53:34 -0500451 msleep(100);
Holger Schurig3874d0f2007-05-25 12:01:42 -0400452
David Woodhouseb77ec4c2008-05-20 16:48:52 +0100453#ifdef CONFIG_OLPC
454 if (ret && machine_is_olpc())
455 if_usb_reset_olpc_card(NULL);
456#endif
457
Holger Schurig3874d0f2007-05-25 12:01:42 -0400458 lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret);
459
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200460 return ret;
461}
462
463/**
464 * @brief This function transfer the data to the device.
Holger Schurig69f90322007-11-23 15:43:44 +0100465 * @param priv pointer to struct lbs_private
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200466 * @param payload pointer to payload data
467 * @param nb data length
468 * @return 0 or -1
469 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500470static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200471{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200472 int ret = -1;
473
474 /* check if device is removed */
Dan Williams954ee162007-08-20 11:43:25 -0400475 if (cardp->surprise_removed) {
Holger Schurig9012b282007-05-25 11:27:16 -0400476 lbs_deb_usbd(&cardp->udev->dev, "Device removed\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200477 goto tx_ret;
478 }
479
480 usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
481 usb_sndbulkpipe(cardp->udev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500482 cardp->ep_out),
Dan Williams954ee162007-08-20 11:43:25 -0400483 payload, nb, if_usb_write_bulk_callback, cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200484
485 cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
486
487 if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
David Woodhouse0856e682007-12-07 15:12:26 +0000488 lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200489 ret = -1;
490 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500491 lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200492 ret = 0;
493 }
494
495tx_ret:
496 return ret;
497}
498
David Woodhouseeae86bf2007-12-14 00:47:05 -0500499static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
Dan Williams954ee162007-08-20 11:43:25 -0400500 void (*callbackfn)(struct urb *urb))
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200501{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200502 struct sk_buff *skb;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200503 int ret = -1;
504
505 if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) {
506 lbs_pr_err("No free skb\n");
507 goto rx_ret;
508 }
509
David Woodhouseeae86bf2007-12-14 00:47:05 -0500510 cardp->rx_skb = skb;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200511
512 /* Fill the receive configuration URB and initialise the Rx call back */
513 usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500514 usb_rcvbulkpipe(cardp->udev, cardp->ep_in),
David Woodhousee9024a02009-10-30 17:45:14 +0000515 skb->data + IPFIELD_ALIGN_OFFSET,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200516 MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500517 cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200518
519 cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
520
David Woodhouseeae86bf2007-12-14 00:47:05 -0500521 lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200522 if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) {
David Woodhouse0856e682007-12-07 15:12:26 +0000523 lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret);
David Woodhouse77d8cf22007-11-28 16:20:51 +0000524 kfree_skb(skb);
David Woodhouseeae86bf2007-12-14 00:47:05 -0500525 cardp->rx_skb = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200526 ret = -1;
527 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500528 lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200529 ret = 0;
530 }
531
532rx_ret:
533 return ret;
534}
535
David Woodhouseeae86bf2007-12-14 00:47:05 -0500536static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200537{
Dan Williams954ee162007-08-20 11:43:25 -0400538 return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200539}
540
David Woodhouseeae86bf2007-12-14 00:47:05 -0500541static int if_usb_submit_rx_urb(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200542{
Dan Williams954ee162007-08-20 11:43:25 -0400543 return __if_usb_submit_rx_urb(cardp, &if_usb_receive);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200544}
545
546static void if_usb_receive_fwload(struct urb *urb)
547{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500548 struct if_usb_card *cardp = urb->context;
549 struct sk_buff *skb = cardp->rx_skb;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200550 struct fwsyncheader *syncfwheader;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500551 struct bootcmdresp bootcmdresp;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200552
553 if (urb->status) {
Holger Schurig9012b282007-05-25 11:27:16 -0400554 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500555 "URB status is failed during fw load\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200556 kfree_skb(skb);
557 return;
558 }
559
David Woodhousec9cd6f92007-12-11 13:15:25 -0500560 if (cardp->fwdnldover) {
561 __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
562
563 if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
564 tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
565 lbs_pr_info("Firmware ready event received\n");
566 wake_up(&cardp->fw_wq);
567 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500568 lbs_deb_usb("Waiting for confirmation; got %x %x\n",
569 le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
David Woodhousec9cd6f92007-12-11 13:15:25 -0500570 if_usb_submit_rx_urb_fwload(cardp);
571 }
572 kfree_skb(skb);
573 return;
574 }
David Woodhousec8ba39d2007-12-10 18:53:34 -0500575 if (cardp->bootcmdresp <= 0) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200576 memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET,
577 sizeof(bootcmdresp));
David Woodhouseeae86bf2007-12-14 00:47:05 -0500578
David Woodhouse981f1872007-05-25 23:36:54 -0400579 if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200580 kfree_skb(skb);
Dan Williams954ee162007-08-20 11:43:25 -0400581 if_usb_submit_rx_urb_fwload(cardp);
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700582 cardp->bootcmdresp = BOOT_CMD_RESP_OK;
Holger Schurig9012b282007-05-25 11:27:16 -0400583 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500584 "Received valid boot command response\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200585 return;
586 }
David Woodhouseeae86bf2007-12-14 00:47:05 -0500587 if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
588 if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
589 bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
590 bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
David Woodhousec9cd6f92007-12-11 13:15:25 -0500591 if (!cardp->bootcmdresp)
592 lbs_pr_info("Firmware already seems alive; resetting\n");
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000593 cardp->bootcmdresp = -1;
594 } else {
595 lbs_pr_info("boot cmd response wrong magic number (0x%x)\n",
David Woodhouseeae86bf2007-12-14 00:47:05 -0500596 le32_to_cpu(bootcmdresp.magic));
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000597 }
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700598 } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) &&
599 (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) &&
600 (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500601 lbs_pr_info("boot cmd response cmd_tag error (%d)\n",
602 bootcmdresp.cmd);
603 } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) {
604 lbs_pr_info("boot cmd response result error (%d)\n",
605 bootcmdresp.result);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200606 } else {
607 cardp->bootcmdresp = 1;
Holger Schurig9012b282007-05-25 11:27:16 -0400608 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500609 "Received valid boot command response\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200610 }
611 kfree_skb(skb);
Dan Williams954ee162007-08-20 11:43:25 -0400612 if_usb_submit_rx_urb_fwload(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200613 return;
614 }
615
616 syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC);
617 if (!syncfwheader) {
Holger Schurig9012b282007-05-25 11:27:16 -0400618 lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200619 kfree_skb(skb);
620 return;
621 }
622
623 memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500624 sizeof(struct fwsyncheader));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200625
626 if (!syncfwheader->cmd) {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500627 lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n");
628 lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n",
629 le32_to_cpu(syncfwheader->seqnum));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200630 cardp->CRC_OK = 1;
631 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500632 lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200633 cardp->CRC_OK = 0;
634 }
635
636 kfree_skb(skb);
637
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700638 /* Give device 5s to either write firmware to its RAM or eeprom */
639 mod_timer(&cardp->fw_timeout, jiffies + (HZ*5));
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500640
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200641 if (cardp->fwfinalblk) {
642 cardp->fwdnldover = 1;
643 goto exit;
644 }
645
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500646 if_usb_send_fw_pkt(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200647
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500648 exit:
David Woodhousec9cd6f92007-12-11 13:15:25 -0500649 if_usb_submit_rx_urb_fwload(cardp);
650
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200651 kfree(syncfwheader);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200652}
653
654#define MRVDRV_MIN_PKT_LEN 30
655
656static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500657 struct if_usb_card *cardp,
Holger Schurig69f90322007-11-23 15:43:44 +0100658 struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200659{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500660 if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN
661 || recvlength < MRVDRV_MIN_PKT_LEN) {
662 lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200663 kfree_skb(skb);
664 return;
665 }
666
667 skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
668 skb_put(skb, recvlength);
669 skb_pull(skb, MESSAGE_HEADER_LEN);
David Woodhouseeae86bf2007-12-14 00:47:05 -0500670
Holger Schurig10078322007-11-15 18:05:47 -0500671 lbs_process_rxed_packet(priv, skb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200672}
673
David Woodhouseeae86bf2007-12-14 00:47:05 -0500674static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200675 struct sk_buff *skb,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500676 struct if_usb_card *cardp,
Holger Schurig69f90322007-11-23 15:43:44 +0100677 struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200678{
Holger Schurig7919b892008-04-01 14:50:43 +0200679 u8 i;
680
Dan Williamsddac4522007-12-11 13:49:39 -0500681 if (recvlength > LBS_CMD_BUFFER_SIZE) {
Holger Schurig9012b282007-05-25 11:27:16 -0400682 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500683 "The receive buffer is too large\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200684 kfree_skb(skb);
685 return;
686 }
687
Alexander Beregalov0ee904c2009-04-11 14:50:23 +0000688 BUG_ON(!in_interrupt());
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200689
David Woodhouseaa21c002007-12-08 20:04:36 +0000690 spin_lock(&priv->driver_lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200691
Holger Schurig7919b892008-04-01 14:50:43 +0200692 i = (priv->resp_idx == 0) ? 1 : 0;
693 BUG_ON(priv->resp_len[i]);
694 priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN);
695 memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN,
696 priv->resp_len[i]);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200697 kfree_skb(skb);
Holger Schurig7919b892008-04-01 14:50:43 +0200698 lbs_notify_command_response(priv, i);
699
David Woodhouseaa21c002007-12-08 20:04:36 +0000700 spin_unlock(&priv->driver_lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200701
Holger Schurig9012b282007-05-25 11:27:16 -0400702 lbs_deb_usbd(&cardp->udev->dev,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200703 "Wake up main thread to handle cmd response\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200704}
705
706/**
707 * @brief This function reads of the packet into the upload buff,
708 * wake up the main thread and initialise the Rx callack.
709 *
710 * @param urb pointer to struct urb
711 * @return N/A
712 */
713static void if_usb_receive(struct urb *urb)
714{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500715 struct if_usb_card *cardp = urb->context;
716 struct sk_buff *skb = cardp->rx_skb;
Holger Schurig69f90322007-11-23 15:43:44 +0100717 struct lbs_private *priv = cardp->priv;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200718 int recvlength = urb->actual_length;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500719 uint8_t *recvbuff = NULL;
720 uint32_t recvtype = 0;
721 __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
Holger Schurig7919b892008-04-01 14:50:43 +0200722 uint32_t event;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200723
Holger Schurig9012b282007-05-25 11:27:16 -0400724 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200725
726 if (recvlength) {
727 if (urb->status) {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500728 lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
729 urb->status);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200730 kfree_skb(skb);
731 goto setup_for_next;
732 }
733
734 recvbuff = skb->data + IPFIELD_ALIGN_OFFSET;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500735 recvtype = le32_to_cpu(pkt[0]);
Holger Schurig9012b282007-05-25 11:27:16 -0400736 lbs_deb_usbd(&cardp->udev->dev,
Dan Williams8362cd42007-08-03 09:40:55 -0400737 "Recv length = 0x%x, Recv type = 0x%X\n",
738 recvlength, recvtype);
David Woodhouse77d8cf22007-11-28 16:20:51 +0000739 } else if (urb->status) {
740 kfree_skb(skb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200741 goto rx_exit;
David Woodhouse77d8cf22007-11-28 16:20:51 +0000742 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200743
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200744 switch (recvtype) {
745 case CMD_TYPE_DATA:
746 process_cmdtypedata(recvlength, skb, cardp, priv);
747 break;
748
749 case CMD_TYPE_REQUEST:
750 process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
751 break;
752
753 case CMD_TYPE_INDICATION:
Holger Schurig7919b892008-04-01 14:50:43 +0200754 /* Event handling */
755 event = le32_to_cpu(pkt[1]);
756 lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event);
757 kfree_skb(skb);
David Woodhouseeae86bf2007-12-14 00:47:05 -0500758
759 /* Icky undocumented magic special case */
Holger Schurig7919b892008-04-01 14:50:43 +0200760 if (event & 0xffff0000) {
761 u32 trycount = (event & 0xffff0000) >> 16;
762
763 lbs_send_tx_feedback(priv, trycount);
764 } else
765 lbs_queue_event(priv, event & 0xFF);
766 break;
767
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200768 default:
Dan Williams8362cd42007-08-03 09:40:55 -0400769 lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n",
David Woodhouseeae86bf2007-12-14 00:47:05 -0500770 recvtype);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200771 kfree_skb(skb);
772 break;
773 }
774
775setup_for_next:
Dan Williams954ee162007-08-20 11:43:25 -0400776 if_usb_submit_rx_urb(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200777rx_exit:
Holger Schurig9012b282007-05-25 11:27:16 -0400778 lbs_deb_leave(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200779}
780
781/**
782 * @brief This function downloads data to FW
Holger Schurig69f90322007-11-23 15:43:44 +0100783 * @param priv pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200784 * @param type type of data
785 * @param buf pointer to data buffer
786 * @param len number of bytes
787 * @return 0 or -1
788 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500789static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
790 uint8_t *payload, uint16_t nb)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200791{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500792 struct if_usb_card *cardp = priv->card;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200793
Holger Schurig9012b282007-05-25 11:27:16 -0400794 lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type);
795 lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200796
797 if (type == MVMS_CMD) {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500798 *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
Holger Schurig634b8f42007-05-25 13:05:16 -0400799 priv->dnld_sent = DNLD_CMD_SENT;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200800 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500801 *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA);
Holger Schurig634b8f42007-05-25 13:05:16 -0400802 priv->dnld_sent = DNLD_DATA_SENT;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200803 }
804
David Woodhouseeae86bf2007-12-14 00:47:05 -0500805 memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200806
David Woodhouseeae86bf2007-12-14 00:47:05 -0500807 return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200808}
809
Dan Williams9e22cb62007-08-02 11:14:49 -0400810/**
811 * @brief This function issues Boot command to the Boot2 code
812 * @param ivalue 1:Boot from FW by USB-Download
813 * 2:Boot from FW in EEPROM
814 * @return 0
815 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500816static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
Dan Williams9e22cb62007-08-02 11:14:49 -0400817{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500818 struct bootcmd *bootcmd = cardp->ep_out_buf;
Dan Williams9e22cb62007-08-02 11:14:49 -0400819
820 /* Prepare command */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500821 bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER);
822 bootcmd->cmd = ivalue;
823 memset(bootcmd->pad, 0, sizeof(bootcmd->pad));
Dan Williams9e22cb62007-08-02 11:14:49 -0400824
825 /* Issue command */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500826 usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd));
Dan Williams9e22cb62007-08-02 11:14:49 -0400827
828 return 0;
829}
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200830
831
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400832/**
833 * @brief This function checks the validity of Boot2/FW image.
834 *
835 * @param data pointer to image
836 * len image length
837 * @return 0 or -1
838 */
David Woodhouse6dfff892008-05-23 18:37:51 +0100839static int check_fwfile_format(const uint8_t *data, uint32_t totlen)
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400840{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500841 uint32_t bincmd, exit;
842 uint32_t blksize, offset, len;
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400843 int ret;
844
845 ret = 1;
846 exit = len = 0;
847
848 do {
849 struct fwheader *fwh = (void *)data;
850
851 bincmd = le32_to_cpu(fwh->dnldcmd);
852 blksize = le32_to_cpu(fwh->datalength);
853 switch (bincmd) {
854 case FW_HAS_DATA_TO_RECV:
855 offset = sizeof(struct fwheader) + blksize;
856 data += offset;
857 len += offset;
858 if (len >= totlen)
859 exit = 1;
860 break;
861 case FW_HAS_LAST_BLOCK:
862 exit = 1;
863 ret = 0;
864 break;
865 default:
866 exit = 1;
867 break;
868 }
869 } while (!exit);
870
871 if (ret)
872 lbs_pr_err("firmware file format check FAIL\n");
873 else
874 lbs_deb_fw("firmware file format check PASS\n");
875
876 return ret;
877}
878
879
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700880/**
881* @brief This function programs the firmware subject to cmd
882*
883* @param cardp the if_usb_card descriptor
884* fwname firmware or boot2 image file name
885* cmd either BOOT_CMD_FW_BY_USB, BOOT_CMD_UPDATE_FW,
886* or BOOT_CMD_UPDATE_BOOT2.
887* @return 0 or error code
888*/
889static int if_usb_prog_firmware(struct if_usb_card *cardp,
890 const char *fwname, int cmd)
891{
892 struct lbs_private *priv = cardp->priv;
893 unsigned long flags, caps;
894 int ret;
895
896 caps = priv->fwcapinfo;
897 if (((cmd == BOOT_CMD_UPDATE_FW) && !(caps & FW_CAPINFO_FIRMWARE_UPGRADE)) ||
898 ((cmd == BOOT_CMD_UPDATE_BOOT2) && !(caps & FW_CAPINFO_BOOT2_UPGRADE)))
899 return -EOPNOTSUPP;
900
901 /* Ensure main thread is idle. */
902 spin_lock_irqsave(&priv->driver_lock, flags);
903 while (priv->cur_cmd != NULL || priv->dnld_sent != DNLD_RES_RECEIVED) {
904 spin_unlock_irqrestore(&priv->driver_lock, flags);
905 if (wait_event_interruptible(priv->waitq,
906 (priv->cur_cmd == NULL &&
907 priv->dnld_sent == DNLD_RES_RECEIVED))) {
908 return -ERESTARTSYS;
909 }
910 spin_lock_irqsave(&priv->driver_lock, flags);
911 }
912 priv->dnld_sent = DNLD_BOOTCMD_SENT;
913 spin_unlock_irqrestore(&priv->driver_lock, flags);
914
915 ret = __if_usb_prog_firmware(cardp, fwname, cmd);
916
917 spin_lock_irqsave(&priv->driver_lock, flags);
918 priv->dnld_sent = DNLD_RES_RECEIVED;
919 spin_unlock_irqrestore(&priv->driver_lock, flags);
920
921 wake_up_interruptible(&priv->waitq);
922
923 return ret;
924}
925
926static int __if_usb_prog_firmware(struct if_usb_card *cardp,
927 const char *fwname, int cmd)
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400928{
Dan Williams954ee162007-08-20 11:43:25 -0400929 int i = 0;
930 static int reset_count = 10;
931 int ret = 0;
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400932
Dan Williams954ee162007-08-20 11:43:25 -0400933 lbs_deb_enter(LBS_DEB_USB);
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400934
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700935 ret = request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
936 if (ret < 0) {
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400937 lbs_pr_err("request_firmware() failed with %#x\n", ret);
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700938 lbs_pr_err("firmware %s not found\n", fwname);
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400939 goto done;
940 }
941
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700942 if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
943 ret = -EINVAL;
Dan Williams954ee162007-08-20 11:43:25 -0400944 goto release_fw;
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700945 }
946
947 /* Cancel any pending usb business */
948 usb_kill_urb(cardp->rx_urb);
949 usb_kill_urb(cardp->tx_urb);
950
951 cardp->fwlastblksent = 0;
952 cardp->fwdnldover = 0;
953 cardp->totalbytes = 0;
954 cardp->fwfinalblk = 0;
955 cardp->bootcmdresp = 0;
Dan Williams954ee162007-08-20 11:43:25 -0400956
957restart:
958 if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
959 lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700960 ret = -EIO;
Dan Williams954ee162007-08-20 11:43:25 -0400961 goto release_fw;
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400962 }
963
Dan Williams954ee162007-08-20 11:43:25 -0400964 cardp->bootcmdresp = 0;
965 do {
966 int j = 0;
967 i++;
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700968 if_usb_issue_boot_command(cardp, cmd);
Dan Williams954ee162007-08-20 11:43:25 -0400969 /* wait for command response */
970 do {
971 j++;
972 msleep_interruptible(100);
973 } while (cardp->bootcmdresp == 0 && j < 10);
974 } while (cardp->bootcmdresp == 0 && i < 5);
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400975
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700976 if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) {
977 /* Return to normal operation */
978 ret = -EOPNOTSUPP;
979 usb_kill_urb(cardp->rx_urb);
980 usb_kill_urb(cardp->tx_urb);
981 if (if_usb_submit_rx_urb(cardp) < 0)
982 ret = -EIO;
983 goto release_fw;
984 } else if (cardp->bootcmdresp <= 0) {
Dan Williams954ee162007-08-20 11:43:25 -0400985 if (--reset_count >= 0) {
986 if_usb_reset_device(cardp);
987 goto restart;
988 }
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -0700989 ret = -EIO;
990 goto release_fw;
Dan Williams954ee162007-08-20 11:43:25 -0400991 }
992
993 i = 0;
994
995 cardp->totalbytes = 0;
996 cardp->fwlastblksent = 0;
997 cardp->CRC_OK = 1;
998 cardp->fwdnldover = 0;
999 cardp->fwseqnum = -1;
1000 cardp->totalbytes = 0;
1001 cardp->fwfinalblk = 0;
1002
David Woodhouse4f82f5c2007-12-11 00:07:58 -05001003 /* Send the first firmware packet... */
1004 if_usb_send_fw_pkt(cardp);
Dan Williams954ee162007-08-20 11:43:25 -04001005
David Woodhouse4f82f5c2007-12-11 00:07:58 -05001006 /* ... and wait for the process to complete */
1007 wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);
David Woodhouse7e226272007-12-14 22:53:41 -05001008
David Woodhouse4f82f5c2007-12-11 00:07:58 -05001009 del_timer_sync(&cardp->fw_timeout);
David Woodhousec9cd6f92007-12-11 13:15:25 -05001010 usb_kill_urb(cardp->rx_urb);
Dan Williams954ee162007-08-20 11:43:25 -04001011
1012 if (!cardp->fwdnldover) {
1013 lbs_pr_info("failed to load fw, resetting device!\n");
1014 if (--reset_count >= 0) {
1015 if_usb_reset_device(cardp);
1016 goto restart;
1017 }
1018
1019 lbs_pr_info("FW download failure, time = %d ms\n", i * 100);
Brian Cavagnolo1556c0f2008-07-21 11:02:46 -07001020 ret = -EIO;
Dan Williams954ee162007-08-20 11:43:25 -04001021 goto release_fw;
1022 }
1023
David Woodhouseeae86bf2007-12-14 00:47:05 -05001024 release_fw:
Dan Williams954ee162007-08-20 11:43:25 -04001025 release_firmware(cardp->fw);
1026 cardp->fw = NULL;
1027
David Woodhouseeae86bf2007-12-14 00:47:05 -05001028 done:
Dan Williams954ee162007-08-20 11:43:25 -04001029 lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret);
Holger Schurig1df4e8f2007-08-02 11:45:12 -04001030 return ret;
1031}
1032
1033
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001034#ifdef CONFIG_PM
1035static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
1036{
David Woodhouseeae86bf2007-12-14 00:47:05 -05001037 struct if_usb_card *cardp = usb_get_intfdata(intf);
Holger Schurig69f90322007-11-23 15:43:44 +01001038 struct lbs_private *priv = cardp->priv;
David Woodhoused1f7a5b2007-12-12 17:40:56 -05001039 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001040
Holger Schurig9012b282007-05-25 11:27:16 -04001041 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001042
David Woodhouseaa21c002007-12-08 20:04:36 +00001043 if (priv->psstate != PS_STATE_FULL_POWER)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001044 return -1;
1045
Amitkumar Karwar66fceb62010-05-19 03:24:38 -07001046 if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
1047 lbs_pr_info("Suspend attempt without "
1048 "configuring wake params!\n");
1049 return -ENOSYS;
1050 }
1051
David Woodhoused1f7a5b2007-12-12 17:40:56 -05001052 ret = lbs_suspend(priv);
1053 if (ret)
1054 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001055
1056 /* Unlink tx & rx urb */
1057 usb_kill_urb(cardp->tx_urb);
1058 usb_kill_urb(cardp->rx_urb);
1059
David Woodhoused1f7a5b2007-12-12 17:40:56 -05001060 out:
Holger Schurig9012b282007-05-25 11:27:16 -04001061 lbs_deb_leave(LBS_DEB_USB);
David Woodhoused1f7a5b2007-12-12 17:40:56 -05001062 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001063}
1064
1065static int if_usb_resume(struct usb_interface *intf)
1066{
David Woodhouseeae86bf2007-12-14 00:47:05 -05001067 struct if_usb_card *cardp = usb_get_intfdata(intf);
Holger Schurig69f90322007-11-23 15:43:44 +01001068 struct lbs_private *priv = cardp->priv;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001069
Holger Schurig9012b282007-05-25 11:27:16 -04001070 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001071
David Woodhouse6bc822b2007-12-11 12:53:43 -05001072 if_usb_submit_rx_urb(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001073
David Woodhoused1f7a5b2007-12-12 17:40:56 -05001074 lbs_resume(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001075
Holger Schurig9012b282007-05-25 11:27:16 -04001076 lbs_deb_leave(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001077 return 0;
1078}
1079#else
1080#define if_usb_suspend NULL
1081#define if_usb_resume NULL
1082#endif
1083
1084static struct usb_driver if_usb_driver = {
Andres Salomon798fbfe2007-11-20 17:44:04 -05001085 .name = DRV_NAME,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001086 .probe = if_usb_probe,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001087 .disconnect = if_usb_disconnect,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001088 .id_table = if_usb_table,
1089 .suspend = if_usb_suspend,
1090 .resume = if_usb_resume,
andrey@cozybit.com7b58ccf2008-07-01 11:43:53 -07001091 .reset_resume = if_usb_resume,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001092};
1093
Andres Salomon4fb910f2007-11-20 17:43:45 -05001094static int __init if_usb_init_module(void)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001095{
Holger Schurig084708b2007-05-25 12:37:58 -04001096 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001097
Holger Schurig084708b2007-05-25 12:37:58 -04001098 lbs_deb_enter(LBS_DEB_MAIN);
1099
Holger Schurig084708b2007-05-25 12:37:58 -04001100 ret = usb_register(&if_usb_driver);
1101
1102 lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
1103 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001104}
1105
Andres Salomon4fb910f2007-11-20 17:43:45 -05001106static void __exit if_usb_exit_module(void)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001107{
Holger Schurig084708b2007-05-25 12:37:58 -04001108 lbs_deb_enter(LBS_DEB_MAIN);
1109
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001110 usb_deregister(&if_usb_driver);
Holger Schurig084708b2007-05-25 12:37:58 -04001111
1112 lbs_deb_leave(LBS_DEB_MAIN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001113}
Holger Schurig084708b2007-05-25 12:37:58 -04001114
1115module_init(if_usb_init_module);
1116module_exit(if_usb_exit_module);
1117
1118MODULE_DESCRIPTION("8388 USB WLAN Driver");
David Woodhouseeae86bf2007-12-14 00:47:05 -05001119MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc.");
Holger Schurig084708b2007-05-25 12:37:58 -04001120MODULE_LICENSE("GPL");