blob: 24783103a7ddb5bba1f34b48396040b38447774a [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>
8#include <linux/usb.h>
9
David Woodhouseb77ec4c2008-05-20 16:48:52 +010010#ifdef CONFIG_OLPC
11#include <asm/olpc.h>
12#endif
13
Holger Schurigec3eef22007-05-25 12:49:10 -040014#define DRV_NAME "usb8xxx"
15
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020016#include "host.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020017#include "decl.h"
18#include "defs.h"
19#include "dev.h"
Dan Williams14e865b2007-12-10 15:11:23 -050020#include "cmd.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020021#include "if_usb.h"
22
David Woodhouseeae86bf2007-12-14 00:47:05 -050023#define INSANEDEBUG 0
24#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0)
25
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020026#define MESSAGE_HEADER_LEN 4
27
Andres Salomon82209ad2007-11-20 17:43:32 -050028static char *lbs_fw_name = "usb8388.bin";
Holger Schurig10078322007-11-15 18:05:47 -050029module_param_named(fw_name, lbs_fw_name, charp, 0644);
Holger Schurig084708b2007-05-25 12:37:58 -040030
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020031static struct usb_device_id if_usb_table[] = {
32 /* Enter the device signature inside */
Holger Schurig66fcc552007-05-25 00:11:58 -040033 { USB_DEVICE(0x1286, 0x2001) },
34 { USB_DEVICE(0x05a3, 0x8388) },
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020035 {} /* Terminating entry */
36};
37
38MODULE_DEVICE_TABLE(usb, if_usb_table);
39
40static void if_usb_receive(struct urb *urb);
41static void if_usb_receive_fwload(struct urb *urb);
David Woodhouseeae86bf2007-12-14 00:47:05 -050042static int if_usb_prog_firmware(struct if_usb_card *cardp);
43static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
44 uint8_t *payload, uint16_t nb);
David Woodhouseeae86bf2007-12-14 00:47:05 -050045static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
46 uint16_t nb);
47static void if_usb_free(struct if_usb_card *cardp);
48static int if_usb_submit_rx_urb(struct if_usb_card *cardp);
49static int if_usb_reset_device(struct if_usb_card *cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020050
51/**
52 * @brief call back function to handle the status of the URB
53 * @param urb pointer to urb structure
54 * @return N/A
55 */
56static void if_usb_write_bulk_callback(struct urb *urb)
57{
David Woodhouseeae86bf2007-12-14 00:47:05 -050058 struct if_usb_card *cardp = (struct if_usb_card *) urb->context;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020059
60 /* handle the transmission complete validations */
61
Dan Williams954ee162007-08-20 11:43:25 -040062 if (urb->status == 0) {
Holger Schurig69f90322007-11-23 15:43:44 +010063 struct lbs_private *priv = cardp->priv;
Dan Williams954ee162007-08-20 11:43:25 -040064
David Woodhouseeae86bf2007-12-14 00:47:05 -050065 lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n");
66 lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n",
67 urb->actual_length);
Dan Williams954ee162007-08-20 11:43:25 -040068
69 /* Used for both firmware TX and regular TX. priv isn't
70 * valid at firmware load time.
71 */
David Woodhousee775ed72007-12-06 14:36:11 +000072 if (priv)
73 lbs_host_to_card_done(priv);
Dan Williams954ee162007-08-20 11:43:25 -040074 } else {
75 /* print the failure status number for debug */
76 lbs_pr_info("URB in failure status: %d\n", urb->status);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020077 }
78
79 return;
80}
81
82/**
83 * @brief free tx/rx urb, skb and rx buffer
David Woodhouseeae86bf2007-12-14 00:47:05 -050084 * @param cardp pointer if_usb_card
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020085 * @return N/A
86 */
David Woodhouseeae86bf2007-12-14 00:47:05 -050087static void if_usb_free(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020088{
Holger Schurig9012b282007-05-25 11:27:16 -040089 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020090
91 /* Unlink tx & rx urb */
92 usb_kill_urb(cardp->tx_urb);
93 usb_kill_urb(cardp->rx_urb);
94
95 usb_free_urb(cardp->tx_urb);
96 cardp->tx_urb = NULL;
97
98 usb_free_urb(cardp->rx_urb);
99 cardp->rx_urb = NULL;
100
David Woodhouseeae86bf2007-12-14 00:47:05 -0500101 kfree(cardp->ep_out_buf);
102 cardp->ep_out_buf = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200103
Holger Schurig9012b282007-05-25 11:27:16 -0400104 lbs_deb_leave(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200105}
106
David Woodhouse46949612007-12-17 14:42:33 -0500107static void if_usb_setup_firmware(struct lbs_private *priv)
David Woodhouse04c80f12007-12-06 12:51:00 +0000108{
Holger Schurig43659292008-01-16 15:52:58 +0100109 struct if_usb_card *cardp = priv->card;
David Woodhouse04c80f12007-12-06 12:51:00 +0000110 struct cmd_ds_set_boot2_ver b2_cmd;
David Woodhouse46949612007-12-17 14:42:33 -0500111 struct cmd_ds_802_11_fw_wake_method wake_method;
David Woodhouse04c80f12007-12-06 12:51:00 +0000112
David Woodhouse9fae8992007-12-15 03:46:44 -0500113 b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd));
David Woodhouse04c80f12007-12-06 12:51:00 +0000114 b2_cmd.action = 0;
Holger Schurig43659292008-01-16 15:52:58 +0100115 b2_cmd.version = cardp->boot2_version;
David Woodhouse04c80f12007-12-06 12:51:00 +0000116
David Woodhouseb23b2062007-12-15 01:22:09 -0500117 if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd))
David Woodhouse04c80f12007-12-06 12:51:00 +0000118 lbs_deb_usb("Setting boot2 version failed\n");
David Woodhouse46949612007-12-17 14:42:33 -0500119
120 priv->wol_gpio = 2; /* Wake via GPIO2... */
121 priv->wol_gap = 20; /* ... after 20ms */
122 lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA);
123
124 wake_method.hdr.size = cpu_to_le16(sizeof(wake_method));
125 wake_method.action = cpu_to_le16(CMD_ACT_GET);
126 if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) {
127 lbs_pr_info("Firmware does not seem to support PS mode\n");
128 } else {
129 if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) {
130 lbs_deb_usb("Firmware seems to support PS with wake-via-command\n");
131 priv->ps_supported = 1;
132 } else {
133 /* The versions which boot up this way don't seem to
134 work even if we set it to the command interrupt */
135 lbs_pr_info("Firmware doesn't wake via command interrupt; disabling PS mode\n");
136 }
137 }
David Woodhouse04c80f12007-12-06 12:51:00 +0000138}
139
David Woodhouse2fd6cfe2007-12-11 17:44:10 -0500140static void if_usb_fw_timeo(unsigned long priv)
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500141{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500142 struct if_usb_card *cardp = (void *)priv;
David Woodhouse04c80f12007-12-06 12:51:00 +0000143
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500144 if (cardp->fwdnldover) {
145 lbs_deb_usb("Download complete, no event. Assuming success\n");
146 } else {
147 lbs_pr_err("Download timed out\n");
148 cardp->surprise_removed = 1;
149 }
150 wake_up(&cardp->fw_wq);
151}
David Woodhouseeae86bf2007-12-14 00:47:05 -0500152
David Woodhouseb77ec4c2008-05-20 16:48:52 +0100153#ifdef CONFIG_OLPC
154static void if_usb_reset_olpc_card(struct lbs_private *priv)
155{
156 printk(KERN_CRIT "Resetting OLPC wireless via EC...\n");
157 olpc_ec_cmd(0x25, NULL, 0, NULL, 0);
158}
159#endif
160
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200161/**
162 * @brief sets the configuration values
163 * @param ifnum interface number
164 * @param id pointer to usb_device_id
165 * @return 0 on success, error code on failure
166 */
167static int if_usb_probe(struct usb_interface *intf,
168 const struct usb_device_id *id)
169{
170 struct usb_device *udev;
171 struct usb_host_interface *iface_desc;
172 struct usb_endpoint_descriptor *endpoint;
Holger Schurig69f90322007-11-23 15:43:44 +0100173 struct lbs_private *priv;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500174 struct if_usb_card *cardp;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200175 int i;
176
177 udev = interface_to_usbdev(intf);
178
David Woodhouseeae86bf2007-12-14 00:47:05 -0500179 cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL);
Holger Schurig435a1ac2007-05-25 12:41:52 -0400180 if (!cardp) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200181 lbs_pr_err("Out of memory allocating private data.\n");
182 goto error;
183 }
184
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500185 setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp);
186 init_waitqueue_head(&cardp->fw_wq);
David Woodhouse7e226272007-12-14 22:53:41 -0500187
Holger Schurig435a1ac2007-05-25 12:41:52 -0400188 cardp->udev = udev;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200189 iface_desc = intf->cur_altsetting;
190
Holger Schurig9012b282007-05-25 11:27:16 -0400191 lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
David Woodhouseeae86bf2007-12-14 00:47:05 -0500192 " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
David Woodhouse981f1872007-05-25 23:36:54 -0400193 le16_to_cpu(udev->descriptor.bcdUSB),
194 udev->descriptor.bDeviceClass,
195 udev->descriptor.bDeviceSubClass,
196 udev->descriptor.bDeviceProtocol);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200197
198 for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
199 endpoint = &iface_desc->endpoint[i].desc;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500200 if (usb_endpoint_is_bulk_in(endpoint)) {
201 cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
202 cardp->ep_in = usb_endpoint_num(endpoint);
203
204 lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in);
205 lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size);
206
207 } else if (usb_endpoint_is_bulk_out(endpoint)) {
208 cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize);
209 cardp->ep_out = usb_endpoint_num(endpoint);
210
211 lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out);
212 lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200213 }
David Woodhouseeae86bf2007-12-14 00:47:05 -0500214 }
215 if (!cardp->ep_out_size || !cardp->ep_in_size) {
216 lbs_deb_usbd(&udev->dev, "Endpoints not found\n");
217 goto dealloc;
218 }
219 if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
220 lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
221 goto dealloc;
222 }
223 if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
224 lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
225 goto dealloc;
226 }
227 cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL);
228 if (!cardp->ep_out_buf) {
229 lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n");
230 goto dealloc;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200231 }
232
Dan Williams954ee162007-08-20 11:43:25 -0400233 /* Upload firmware */
Dan Williams954ee162007-08-20 11:43:25 -0400234 if (if_usb_prog_firmware(cardp)) {
David Woodhouse23d36ee2007-12-12 00:14:21 -0500235 lbs_deb_usbd(&udev->dev, "FW upload failed\n");
Dan Williams954ee162007-08-20 11:43:25 -0400236 goto err_prog_firmware;
237 }
Holger Schurig3874d0f2007-05-25 12:01:42 -0400238
Holger Schurig10078322007-11-15 18:05:47 -0500239 if (!(priv = lbs_add_card(cardp, &udev->dev)))
Dan Williams954ee162007-08-20 11:43:25 -0400240 goto err_prog_firmware;
241
242 cardp->priv = priv;
David Woodhousec9cd6f92007-12-11 13:15:25 -0500243 cardp->priv->fw_ready = 1;
Luis Carlos Cobo965f8bbc2007-08-02 13:16:55 -0400244
Holger Schurig208fdd22007-05-25 12:17:06 -0400245 priv->hw_host_to_card = if_usb_host_to_card;
David Woodhouseb77ec4c2008-05-20 16:48:52 +0100246#ifdef CONFIG_OLPC
247 if (machine_is_olpc())
248 priv->reset_card = if_usb_reset_olpc_card;
249#endif
250
Holger Schurig43659292008-01-16 15:52:58 +0100251 cardp->boot2_version = udev->descriptor.bcdDevice;
Holger Schurig208fdd22007-05-25 12:17:06 -0400252
Dan Williams954ee162007-08-20 11:43:25 -0400253 if_usb_submit_rx_urb(cardp);
Dan Williams954ee162007-08-20 11:43:25 -0400254
Holger Schurig10078322007-11-15 18:05:47 -0500255 if (lbs_start_card(priv))
Dan Williams954ee162007-08-20 11:43:25 -0400256 goto err_start_card;
Holger Schurig32a74b72007-05-25 12:04:31 -0400257
David Woodhouse46949612007-12-17 14:42:33 -0500258 if_usb_setup_firmware(priv);
David Woodhoused1f7a5b2007-12-12 17:40:56 -0500259
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200260 usb_get_dev(udev);
Holger Schurig435a1ac2007-05-25 12:41:52 -0400261 usb_set_intfdata(intf, cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200262
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200263 return 0;
264
Dan Williams954ee162007-08-20 11:43:25 -0400265err_start_card:
Holger Schurig10078322007-11-15 18:05:47 -0500266 lbs_remove_card(priv);
Dan Williams954ee162007-08-20 11:43:25 -0400267err_prog_firmware:
268 if_usb_reset_device(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200269dealloc:
Holger Schurig435a1ac2007-05-25 12:41:52 -0400270 if_usb_free(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200271
272error:
273 return -ENOMEM;
274}
275
276/**
277 * @brief free resource and cleanup
Holger Schurig435a1ac2007-05-25 12:41:52 -0400278 * @param intf USB interface structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200279 * @return N/A
280 */
281static void if_usb_disconnect(struct usb_interface *intf)
282{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500283 struct if_usb_card *cardp = usb_get_intfdata(intf);
Holger Schurig69f90322007-11-23 15:43:44 +0100284 struct lbs_private *priv = (struct lbs_private *) cardp->priv;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200285
Dan Williams954ee162007-08-20 11:43:25 -0400286 lbs_deb_enter(LBS_DEB_MAIN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200287
Dan Williams954ee162007-08-20 11:43:25 -0400288 cardp->surprise_removed = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200289
Dan Williams954ee162007-08-20 11:43:25 -0400290 if (priv) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000291 priv->surpriseremoved = 1;
Holger Schurig10078322007-11-15 18:05:47 -0500292 lbs_stop_card(priv);
Holger Schurig10078322007-11-15 18:05:47 -0500293 lbs_remove_card(priv);
Dan Williams954ee162007-08-20 11:43:25 -0400294 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200295
296 /* Unlink and free urb */
297 if_usb_free(cardp);
298
299 usb_set_intfdata(intf, NULL);
300 usb_put_dev(interface_to_usbdev(intf));
301
Dan Williams954ee162007-08-20 11:43:25 -0400302 lbs_deb_leave(LBS_DEB_MAIN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200303}
304
305/**
306 * @brief This function download FW
Holger Schurig69f90322007-11-23 15:43:44 +0100307 * @param priv pointer to struct lbs_private
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200308 * @return 0
309 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500310static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200311{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500312 struct fwdata *fwdata = cardp->ep_out_buf;
313 uint8_t *firmware = cardp->fw->data;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200314
David Woodhouseeae86bf2007-12-14 00:47:05 -0500315 /* If we got a CRC failure on the last block, back
316 up and retry it */
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200317 if (!cardp->CRC_OK) {
318 cardp->totalbytes = cardp->fwlastblksent;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500319 cardp->fwseqnum--;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200320 }
321
David Woodhouseeae86bf2007-12-14 00:47:05 -0500322 lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
323 cardp->totalbytes);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200324
David Woodhouseeae86bf2007-12-14 00:47:05 -0500325 /* struct fwdata (which we sent to the card) has an
326 extra __le32 field in between the header and the data,
327 which is not in the struct fwheader in the actual
328 firmware binary. Insert the seqnum in the middle... */
329 memcpy(&fwdata->hdr, &firmware[cardp->totalbytes],
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200330 sizeof(struct fwheader));
331
332 cardp->fwlastblksent = cardp->totalbytes;
333 cardp->totalbytes += sizeof(struct fwheader);
334
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200335 memcpy(fwdata->data, &firmware[cardp->totalbytes],
David Woodhouseeae86bf2007-12-14 00:47:05 -0500336 le32_to_cpu(fwdata->hdr.datalength));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200337
David Woodhouseeae86bf2007-12-14 00:47:05 -0500338 lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
339 le32_to_cpu(fwdata->hdr.datalength));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200340
David Woodhouseeae86bf2007-12-14 00:47:05 -0500341 fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
342 cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200343
David Woodhouseeae86bf2007-12-14 00:47:05 -0500344 usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
345 le32_to_cpu(fwdata->hdr.datalength));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200346
David Woodhouseeae86bf2007-12-14 00:47:05 -0500347 if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
348 lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
349 lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n",
350 cardp->fwseqnum, cardp->totalbytes);
351 } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
352 lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n");
353 lbs_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200354
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200355 cardp->fwfinalblk = 1;
356 }
357
David Woodhouseeae86bf2007-12-14 00:47:05 -0500358 lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
359 cardp->totalbytes);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200360
361 return 0;
362}
363
David Woodhouseeae86bf2007-12-14 00:47:05 -0500364static int if_usb_reset_device(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200365{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500366 struct cmd_ds_command *cmd = cardp->ep_out_buf + 4;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200367 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200368
Holger Schurig3874d0f2007-05-25 12:01:42 -0400369 lbs_deb_enter(LBS_DEB_USB);
370
David Woodhouseeae86bf2007-12-14 00:47:05 -0500371 *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000372
373 cmd->command = cpu_to_le16(CMD_802_11_RESET);
374 cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN);
375 cmd->result = cpu_to_le16(0);
376 cmd->seqnum = cpu_to_le16(0x5a5a);
377 cmd->params.reset.action = cpu_to_le16(CMD_ACT_HALT);
David Woodhouseeae86bf2007-12-14 00:47:05 -0500378 usb_tx_block(cardp, cardp->ep_out_buf, 4 + S_DS_GEN + sizeof(struct cmd_ds_802_11_reset));
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000379
David Woodhousec8ba39d2007-12-10 18:53:34 -0500380 msleep(100);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200381 ret = usb_reset_device(cardp->udev);
David Woodhousec8ba39d2007-12-10 18:53:34 -0500382 msleep(100);
Holger Schurig3874d0f2007-05-25 12:01:42 -0400383
David Woodhouseb77ec4c2008-05-20 16:48:52 +0100384#ifdef CONFIG_OLPC
385 if (ret && machine_is_olpc())
386 if_usb_reset_olpc_card(NULL);
387#endif
388
Holger Schurig3874d0f2007-05-25 12:01:42 -0400389 lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret);
390
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200391 return ret;
392}
393
394/**
395 * @brief This function transfer the data to the device.
Holger Schurig69f90322007-11-23 15:43:44 +0100396 * @param priv pointer to struct lbs_private
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200397 * @param payload pointer to payload data
398 * @param nb data length
399 * @return 0 or -1
400 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500401static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200402{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200403 int ret = -1;
404
405 /* check if device is removed */
Dan Williams954ee162007-08-20 11:43:25 -0400406 if (cardp->surprise_removed) {
Holger Schurig9012b282007-05-25 11:27:16 -0400407 lbs_deb_usbd(&cardp->udev->dev, "Device removed\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200408 goto tx_ret;
409 }
410
411 usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
412 usb_sndbulkpipe(cardp->udev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500413 cardp->ep_out),
Dan Williams954ee162007-08-20 11:43:25 -0400414 payload, nb, if_usb_write_bulk_callback, cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200415
416 cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
417
418 if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
David Woodhouse0856e682007-12-07 15:12:26 +0000419 lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200420 ret = -1;
421 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500422 lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200423 ret = 0;
424 }
425
426tx_ret:
427 return ret;
428}
429
David Woodhouseeae86bf2007-12-14 00:47:05 -0500430static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
Dan Williams954ee162007-08-20 11:43:25 -0400431 void (*callbackfn)(struct urb *urb))
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200432{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200433 struct sk_buff *skb;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200434 int ret = -1;
435
436 if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) {
437 lbs_pr_err("No free skb\n");
438 goto rx_ret;
439 }
440
David Woodhouseeae86bf2007-12-14 00:47:05 -0500441 cardp->rx_skb = skb;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200442
443 /* Fill the receive configuration URB and initialise the Rx call back */
444 usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500445 usb_rcvbulkpipe(cardp->udev, cardp->ep_in),
Dan Williams4269e2a2007-05-10 23:10:18 -0400446 (void *) (skb->tail + (size_t) IPFIELD_ALIGN_OFFSET),
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200447 MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500448 cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200449
450 cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
451
David Woodhouseeae86bf2007-12-14 00:47:05 -0500452 lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200453 if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) {
David Woodhouse0856e682007-12-07 15:12:26 +0000454 lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret);
David Woodhouse77d8cf22007-11-28 16:20:51 +0000455 kfree_skb(skb);
David Woodhouseeae86bf2007-12-14 00:47:05 -0500456 cardp->rx_skb = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200457 ret = -1;
458 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500459 lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200460 ret = 0;
461 }
462
463rx_ret:
464 return ret;
465}
466
David Woodhouseeae86bf2007-12-14 00:47:05 -0500467static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200468{
Dan Williams954ee162007-08-20 11:43:25 -0400469 return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200470}
471
David Woodhouseeae86bf2007-12-14 00:47:05 -0500472static int if_usb_submit_rx_urb(struct if_usb_card *cardp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200473{
Dan Williams954ee162007-08-20 11:43:25 -0400474 return __if_usb_submit_rx_urb(cardp, &if_usb_receive);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200475}
476
477static void if_usb_receive_fwload(struct urb *urb)
478{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500479 struct if_usb_card *cardp = urb->context;
480 struct sk_buff *skb = cardp->rx_skb;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200481 struct fwsyncheader *syncfwheader;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500482 struct bootcmdresp bootcmdresp;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200483
484 if (urb->status) {
Holger Schurig9012b282007-05-25 11:27:16 -0400485 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500486 "URB status is failed during fw load\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200487 kfree_skb(skb);
488 return;
489 }
490
David Woodhousec9cd6f92007-12-11 13:15:25 -0500491 if (cardp->fwdnldover) {
492 __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
493
494 if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
495 tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
496 lbs_pr_info("Firmware ready event received\n");
497 wake_up(&cardp->fw_wq);
498 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500499 lbs_deb_usb("Waiting for confirmation; got %x %x\n",
500 le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
David Woodhousec9cd6f92007-12-11 13:15:25 -0500501 if_usb_submit_rx_urb_fwload(cardp);
502 }
503 kfree_skb(skb);
504 return;
505 }
David Woodhousec8ba39d2007-12-10 18:53:34 -0500506 if (cardp->bootcmdresp <= 0) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200507 memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET,
508 sizeof(bootcmdresp));
David Woodhouseeae86bf2007-12-14 00:47:05 -0500509
David Woodhouse981f1872007-05-25 23:36:54 -0400510 if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200511 kfree_skb(skb);
Dan Williams954ee162007-08-20 11:43:25 -0400512 if_usb_submit_rx_urb_fwload(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200513 cardp->bootcmdresp = 1;
Holger Schurig9012b282007-05-25 11:27:16 -0400514 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500515 "Received valid boot command response\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200516 return;
517 }
David Woodhouseeae86bf2007-12-14 00:47:05 -0500518 if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
519 if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
520 bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
521 bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
David Woodhousec9cd6f92007-12-11 13:15:25 -0500522 if (!cardp->bootcmdresp)
523 lbs_pr_info("Firmware already seems alive; resetting\n");
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000524 cardp->bootcmdresp = -1;
525 } else {
526 lbs_pr_info("boot cmd response wrong magic number (0x%x)\n",
David Woodhouseeae86bf2007-12-14 00:47:05 -0500527 le32_to_cpu(bootcmdresp.magic));
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000528 }
David Woodhouseeae86bf2007-12-14 00:47:05 -0500529 } else if (bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) {
530 lbs_pr_info("boot cmd response cmd_tag error (%d)\n",
531 bootcmdresp.cmd);
532 } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) {
533 lbs_pr_info("boot cmd response result error (%d)\n",
534 bootcmdresp.result);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200535 } else {
536 cardp->bootcmdresp = 1;
Holger Schurig9012b282007-05-25 11:27:16 -0400537 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500538 "Received valid boot command response\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200539 }
540 kfree_skb(skb);
Dan Williams954ee162007-08-20 11:43:25 -0400541 if_usb_submit_rx_urb_fwload(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200542 return;
543 }
544
545 syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC);
546 if (!syncfwheader) {
Holger Schurig9012b282007-05-25 11:27:16 -0400547 lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200548 kfree_skb(skb);
549 return;
550 }
551
552 memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500553 sizeof(struct fwsyncheader));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200554
555 if (!syncfwheader->cmd) {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500556 lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n");
557 lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n",
558 le32_to_cpu(syncfwheader->seqnum));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200559 cardp->CRC_OK = 1;
560 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500561 lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200562 cardp->CRC_OK = 0;
563 }
564
565 kfree_skb(skb);
566
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500567 /* reschedule timer for 200ms hence */
568 mod_timer(&cardp->fw_timeout, jiffies + (HZ/5));
569
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200570 if (cardp->fwfinalblk) {
571 cardp->fwdnldover = 1;
572 goto exit;
573 }
574
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500575 if_usb_send_fw_pkt(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200576
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500577 exit:
David Woodhousec9cd6f92007-12-11 13:15:25 -0500578 if_usb_submit_rx_urb_fwload(cardp);
579
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200580 kfree(syncfwheader);
581
582 return;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200583}
584
585#define MRVDRV_MIN_PKT_LEN 30
586
587static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500588 struct if_usb_card *cardp,
Holger Schurig69f90322007-11-23 15:43:44 +0100589 struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200590{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500591 if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN
592 || recvlength < MRVDRV_MIN_PKT_LEN) {
593 lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200594 kfree_skb(skb);
595 return;
596 }
597
598 skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
599 skb_put(skb, recvlength);
600 skb_pull(skb, MESSAGE_HEADER_LEN);
David Woodhouseeae86bf2007-12-14 00:47:05 -0500601
Holger Schurig10078322007-11-15 18:05:47 -0500602 lbs_process_rxed_packet(priv, skb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200603}
604
David Woodhouseeae86bf2007-12-14 00:47:05 -0500605static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200606 struct sk_buff *skb,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500607 struct if_usb_card *cardp,
Holger Schurig69f90322007-11-23 15:43:44 +0100608 struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200609{
Holger Schurig7919b892008-04-01 14:50:43 +0200610 u8 i;
611
Dan Williamsddac4522007-12-11 13:49:39 -0500612 if (recvlength > LBS_CMD_BUFFER_SIZE) {
Holger Schurig9012b282007-05-25 11:27:16 -0400613 lbs_deb_usbd(&cardp->udev->dev,
David Woodhouseeae86bf2007-12-14 00:47:05 -0500614 "The receive buffer is too large\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200615 kfree_skb(skb);
616 return;
617 }
618
619 if (!in_interrupt())
620 BUG();
621
David Woodhouseaa21c002007-12-08 20:04:36 +0000622 spin_lock(&priv->driver_lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200623
Holger Schurig7919b892008-04-01 14:50:43 +0200624 i = (priv->resp_idx == 0) ? 1 : 0;
625 BUG_ON(priv->resp_len[i]);
626 priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN);
627 memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN,
628 priv->resp_len[i]);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200629 kfree_skb(skb);
Holger Schurig7919b892008-04-01 14:50:43 +0200630 lbs_notify_command_response(priv, i);
631
David Woodhouseaa21c002007-12-08 20:04:36 +0000632 spin_unlock(&priv->driver_lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200633
Holger Schurig9012b282007-05-25 11:27:16 -0400634 lbs_deb_usbd(&cardp->udev->dev,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200635 "Wake up main thread to handle cmd response\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200636}
637
638/**
639 * @brief This function reads of the packet into the upload buff,
640 * wake up the main thread and initialise the Rx callack.
641 *
642 * @param urb pointer to struct urb
643 * @return N/A
644 */
645static void if_usb_receive(struct urb *urb)
646{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500647 struct if_usb_card *cardp = urb->context;
648 struct sk_buff *skb = cardp->rx_skb;
Holger Schurig69f90322007-11-23 15:43:44 +0100649 struct lbs_private *priv = cardp->priv;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200650 int recvlength = urb->actual_length;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500651 uint8_t *recvbuff = NULL;
652 uint32_t recvtype = 0;
653 __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
Holger Schurig7919b892008-04-01 14:50:43 +0200654 uint32_t event;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200655
Holger Schurig9012b282007-05-25 11:27:16 -0400656 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200657
658 if (recvlength) {
659 if (urb->status) {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500660 lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
661 urb->status);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200662 kfree_skb(skb);
663 goto setup_for_next;
664 }
665
666 recvbuff = skb->data + IPFIELD_ALIGN_OFFSET;
David Woodhouseeae86bf2007-12-14 00:47:05 -0500667 recvtype = le32_to_cpu(pkt[0]);
Holger Schurig9012b282007-05-25 11:27:16 -0400668 lbs_deb_usbd(&cardp->udev->dev,
Dan Williams8362cd42007-08-03 09:40:55 -0400669 "Recv length = 0x%x, Recv type = 0x%X\n",
670 recvlength, recvtype);
David Woodhouse77d8cf22007-11-28 16:20:51 +0000671 } else if (urb->status) {
672 kfree_skb(skb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200673 goto rx_exit;
David Woodhouse77d8cf22007-11-28 16:20:51 +0000674 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200675
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200676 switch (recvtype) {
677 case CMD_TYPE_DATA:
678 process_cmdtypedata(recvlength, skb, cardp, priv);
679 break;
680
681 case CMD_TYPE_REQUEST:
682 process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
683 break;
684
685 case CMD_TYPE_INDICATION:
Holger Schurig7919b892008-04-01 14:50:43 +0200686 /* Event handling */
687 event = le32_to_cpu(pkt[1]);
688 lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event);
689 kfree_skb(skb);
David Woodhouseeae86bf2007-12-14 00:47:05 -0500690
691 /* Icky undocumented magic special case */
Holger Schurig7919b892008-04-01 14:50:43 +0200692 if (event & 0xffff0000) {
693 u32 trycount = (event & 0xffff0000) >> 16;
694
695 lbs_send_tx_feedback(priv, trycount);
696 } else
697 lbs_queue_event(priv, event & 0xFF);
698 break;
699
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200700 default:
Dan Williams8362cd42007-08-03 09:40:55 -0400701 lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n",
David Woodhouseeae86bf2007-12-14 00:47:05 -0500702 recvtype);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200703 kfree_skb(skb);
704 break;
705 }
706
707setup_for_next:
Dan Williams954ee162007-08-20 11:43:25 -0400708 if_usb_submit_rx_urb(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200709rx_exit:
Holger Schurig9012b282007-05-25 11:27:16 -0400710 lbs_deb_leave(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200711}
712
713/**
714 * @brief This function downloads data to FW
Holger Schurig69f90322007-11-23 15:43:44 +0100715 * @param priv pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200716 * @param type type of data
717 * @param buf pointer to data buffer
718 * @param len number of bytes
719 * @return 0 or -1
720 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500721static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
722 uint8_t *payload, uint16_t nb)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200723{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500724 struct if_usb_card *cardp = priv->card;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200725
Holger Schurig9012b282007-05-25 11:27:16 -0400726 lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type);
727 lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200728
729 if (type == MVMS_CMD) {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500730 *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
Holger Schurig634b8f42007-05-25 13:05:16 -0400731 priv->dnld_sent = DNLD_CMD_SENT;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200732 } else {
David Woodhouseeae86bf2007-12-14 00:47:05 -0500733 *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA);
Holger Schurig634b8f42007-05-25 13:05:16 -0400734 priv->dnld_sent = DNLD_DATA_SENT;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200735 }
736
David Woodhouseeae86bf2007-12-14 00:47:05 -0500737 memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200738
David Woodhouseeae86bf2007-12-14 00:47:05 -0500739 return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200740}
741
Dan Williams9e22cb62007-08-02 11:14:49 -0400742/**
743 * @brief This function issues Boot command to the Boot2 code
744 * @param ivalue 1:Boot from FW by USB-Download
745 * 2:Boot from FW in EEPROM
746 * @return 0
747 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500748static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
Dan Williams9e22cb62007-08-02 11:14:49 -0400749{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500750 struct bootcmd *bootcmd = cardp->ep_out_buf;
Dan Williams9e22cb62007-08-02 11:14:49 -0400751
752 /* Prepare command */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500753 bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER);
754 bootcmd->cmd = ivalue;
755 memset(bootcmd->pad, 0, sizeof(bootcmd->pad));
Dan Williams9e22cb62007-08-02 11:14:49 -0400756
757 /* Issue command */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500758 usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd));
Dan Williams9e22cb62007-08-02 11:14:49 -0400759
760 return 0;
761}
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200762
763
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400764/**
765 * @brief This function checks the validity of Boot2/FW image.
766 *
767 * @param data pointer to image
768 * len image length
769 * @return 0 or -1
770 */
David Woodhouseeae86bf2007-12-14 00:47:05 -0500771static int check_fwfile_format(uint8_t *data, uint32_t totlen)
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400772{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500773 uint32_t bincmd, exit;
774 uint32_t blksize, offset, len;
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400775 int ret;
776
777 ret = 1;
778 exit = len = 0;
779
780 do {
781 struct fwheader *fwh = (void *)data;
782
783 bincmd = le32_to_cpu(fwh->dnldcmd);
784 blksize = le32_to_cpu(fwh->datalength);
785 switch (bincmd) {
786 case FW_HAS_DATA_TO_RECV:
787 offset = sizeof(struct fwheader) + blksize;
788 data += offset;
789 len += offset;
790 if (len >= totlen)
791 exit = 1;
792 break;
793 case FW_HAS_LAST_BLOCK:
794 exit = 1;
795 ret = 0;
796 break;
797 default:
798 exit = 1;
799 break;
800 }
801 } while (!exit);
802
803 if (ret)
804 lbs_pr_err("firmware file format check FAIL\n");
805 else
806 lbs_deb_fw("firmware file format check PASS\n");
807
808 return ret;
809}
810
811
David Woodhouseeae86bf2007-12-14 00:47:05 -0500812static int if_usb_prog_firmware(struct if_usb_card *cardp)
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400813{
Dan Williams954ee162007-08-20 11:43:25 -0400814 int i = 0;
815 static int reset_count = 10;
816 int ret = 0;
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400817
Dan Williams954ee162007-08-20 11:43:25 -0400818 lbs_deb_enter(LBS_DEB_USB);
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400819
Holger Schurig10078322007-11-15 18:05:47 -0500820 if ((ret = request_firmware(&cardp->fw, lbs_fw_name,
Dan Williams954ee162007-08-20 11:43:25 -0400821 &cardp->udev->dev)) < 0) {
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400822 lbs_pr_err("request_firmware() failed with %#x\n", ret);
Holger Schurig10078322007-11-15 18:05:47 -0500823 lbs_pr_err("firmware %s not found\n", lbs_fw_name);
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400824 goto done;
825 }
826
Dan Williams954ee162007-08-20 11:43:25 -0400827 if (check_fwfile_format(cardp->fw->data, cardp->fw->size))
828 goto release_fw;
829
830restart:
831 if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
832 lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
833 ret = -1;
834 goto release_fw;
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400835 }
836
Dan Williams954ee162007-08-20 11:43:25 -0400837 cardp->bootcmdresp = 0;
838 do {
839 int j = 0;
840 i++;
841 /* Issue Boot command = 1, Boot from Download-FW */
842 if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
843 /* wait for command response */
844 do {
845 j++;
846 msleep_interruptible(100);
847 } while (cardp->bootcmdresp == 0 && j < 10);
848 } while (cardp->bootcmdresp == 0 && i < 5);
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400849
David Woodhouse6d35fdf2007-12-08 23:49:06 +0000850 if (cardp->bootcmdresp <= 0) {
Dan Williams954ee162007-08-20 11:43:25 -0400851 if (--reset_count >= 0) {
852 if_usb_reset_device(cardp);
853 goto restart;
854 }
855 return -1;
856 }
857
858 i = 0;
859
860 cardp->totalbytes = 0;
861 cardp->fwlastblksent = 0;
862 cardp->CRC_OK = 1;
863 cardp->fwdnldover = 0;
864 cardp->fwseqnum = -1;
865 cardp->totalbytes = 0;
866 cardp->fwfinalblk = 0;
867
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500868 /* Send the first firmware packet... */
869 if_usb_send_fw_pkt(cardp);
Dan Williams954ee162007-08-20 11:43:25 -0400870
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500871 /* ... and wait for the process to complete */
872 wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);
David Woodhouse7e226272007-12-14 22:53:41 -0500873
David Woodhouse4f82f5c2007-12-11 00:07:58 -0500874 del_timer_sync(&cardp->fw_timeout);
David Woodhousec9cd6f92007-12-11 13:15:25 -0500875 usb_kill_urb(cardp->rx_urb);
Dan Williams954ee162007-08-20 11:43:25 -0400876
877 if (!cardp->fwdnldover) {
878 lbs_pr_info("failed to load fw, resetting device!\n");
879 if (--reset_count >= 0) {
880 if_usb_reset_device(cardp);
881 goto restart;
882 }
883
884 lbs_pr_info("FW download failure, time = %d ms\n", i * 100);
885 ret = -1;
886 goto release_fw;
887 }
888
David Woodhouseeae86bf2007-12-14 00:47:05 -0500889 release_fw:
Dan Williams954ee162007-08-20 11:43:25 -0400890 release_firmware(cardp->fw);
891 cardp->fw = NULL;
892
David Woodhouseeae86bf2007-12-14 00:47:05 -0500893 done:
Dan Williams954ee162007-08-20 11:43:25 -0400894 lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret);
Holger Schurig1df4e8f2007-08-02 11:45:12 -0400895 return ret;
896}
897
898
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200899#ifdef CONFIG_PM
900static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
901{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500902 struct if_usb_card *cardp = usb_get_intfdata(intf);
Holger Schurig69f90322007-11-23 15:43:44 +0100903 struct lbs_private *priv = cardp->priv;
David Woodhoused1f7a5b2007-12-12 17:40:56 -0500904 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200905
Holger Schurig9012b282007-05-25 11:27:16 -0400906 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200907
David Woodhouseaa21c002007-12-08 20:04:36 +0000908 if (priv->psstate != PS_STATE_FULL_POWER)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200909 return -1;
910
David Woodhoused1f7a5b2007-12-12 17:40:56 -0500911 ret = lbs_suspend(priv);
912 if (ret)
913 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200914
915 /* Unlink tx & rx urb */
916 usb_kill_urb(cardp->tx_urb);
917 usb_kill_urb(cardp->rx_urb);
918
David Woodhoused1f7a5b2007-12-12 17:40:56 -0500919 out:
Holger Schurig9012b282007-05-25 11:27:16 -0400920 lbs_deb_leave(LBS_DEB_USB);
David Woodhoused1f7a5b2007-12-12 17:40:56 -0500921 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200922}
923
924static int if_usb_resume(struct usb_interface *intf)
925{
David Woodhouseeae86bf2007-12-14 00:47:05 -0500926 struct if_usb_card *cardp = usb_get_intfdata(intf);
Holger Schurig69f90322007-11-23 15:43:44 +0100927 struct lbs_private *priv = cardp->priv;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200928
Holger Schurig9012b282007-05-25 11:27:16 -0400929 lbs_deb_enter(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200930
David Woodhouse6bc822b2007-12-11 12:53:43 -0500931 if_usb_submit_rx_urb(cardp);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200932
David Woodhoused1f7a5b2007-12-12 17:40:56 -0500933 lbs_resume(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200934
Holger Schurig9012b282007-05-25 11:27:16 -0400935 lbs_deb_leave(LBS_DEB_USB);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200936 return 0;
937}
938#else
939#define if_usb_suspend NULL
940#define if_usb_resume NULL
941#endif
942
943static struct usb_driver if_usb_driver = {
Andres Salomon798fbfe2007-11-20 17:44:04 -0500944 .name = DRV_NAME,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200945 .probe = if_usb_probe,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200946 .disconnect = if_usb_disconnect,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200947 .id_table = if_usb_table,
948 .suspend = if_usb_suspend,
949 .resume = if_usb_resume,
950};
951
Andres Salomon4fb910f2007-11-20 17:43:45 -0500952static int __init if_usb_init_module(void)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200953{
Holger Schurig084708b2007-05-25 12:37:58 -0400954 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200955
Holger Schurig084708b2007-05-25 12:37:58 -0400956 lbs_deb_enter(LBS_DEB_MAIN);
957
Holger Schurig084708b2007-05-25 12:37:58 -0400958 ret = usb_register(&if_usb_driver);
959
960 lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
961 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200962}
963
Andres Salomon4fb910f2007-11-20 17:43:45 -0500964static void __exit if_usb_exit_module(void)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200965{
Holger Schurig084708b2007-05-25 12:37:58 -0400966 lbs_deb_enter(LBS_DEB_MAIN);
967
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200968 usb_deregister(&if_usb_driver);
Holger Schurig084708b2007-05-25 12:37:58 -0400969
970 lbs_deb_leave(LBS_DEB_MAIN);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200971}
Holger Schurig084708b2007-05-25 12:37:58 -0400972
973module_init(if_usb_init_module);
974module_exit(if_usb_exit_module);
975
976MODULE_DESCRIPTION("8388 USB WLAN Driver");
David Woodhouseeae86bf2007-12-14 00:47:05 -0500977MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc.");
Holger Schurig084708b2007-05-25 12:37:58 -0400978MODULE_LICENSE("GPL");