162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This file contains functions used in USB interface module.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/firmware.h>
1162306a36Sopenharmony_ci#include <linux/netdevice.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/usb.h>
1462306a36Sopenharmony_ci#include <linux/olpc-ec.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#ifdef CONFIG_OLPC
1762306a36Sopenharmony_ci#include <asm/olpc.h>
1862306a36Sopenharmony_ci#endif
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define DRV_NAME "usb8xxx"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "host.h"
2362306a36Sopenharmony_ci#include "decl.h"
2462306a36Sopenharmony_ci#include "defs.h"
2562306a36Sopenharmony_ci#include "dev.h"
2662306a36Sopenharmony_ci#include "cmd.h"
2762306a36Sopenharmony_ci#include "if_usb.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define INSANEDEBUG	0
3062306a36Sopenharmony_ci#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define MESSAGE_HEADER_LEN	4
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388_v9.bin");
3562306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388_v5.bin");
3662306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388.bin");
3762306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8682.bin");
3862306a36Sopenharmony_ciMODULE_FIRMWARE("usb8388.bin");
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cienum {
4162306a36Sopenharmony_ci	MODEL_UNKNOWN = 0x0,
4262306a36Sopenharmony_ci	MODEL_8388 = 0x1,
4362306a36Sopenharmony_ci	MODEL_8682 = 0x2
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* table of firmware file names */
4762306a36Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = {
4862306a36Sopenharmony_ci	{ MODEL_8388, "libertas/usb8388_olpc.bin", NULL },
4962306a36Sopenharmony_ci	{ MODEL_8388, "libertas/usb8388_v9.bin", NULL },
5062306a36Sopenharmony_ci	{ MODEL_8388, "libertas/usb8388_v5.bin", NULL },
5162306a36Sopenharmony_ci	{ MODEL_8388, "libertas/usb8388.bin", NULL },
5262306a36Sopenharmony_ci	{ MODEL_8388, "usb8388.bin", NULL },
5362306a36Sopenharmony_ci	{ MODEL_8682, "libertas/usb8682.bin", NULL },
5462306a36Sopenharmony_ci	{ 0, NULL, NULL }
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct usb_device_id if_usb_table[] = {
5862306a36Sopenharmony_ci	/* Enter the device signature inside */
5962306a36Sopenharmony_ci	{ USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 },
6062306a36Sopenharmony_ci	{ USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 },
6162306a36Sopenharmony_ci	{}	/* Terminating entry */
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, if_usb_table);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void if_usb_receive(struct urb *urb);
6762306a36Sopenharmony_cistatic void if_usb_receive_fwload(struct urb *urb);
6862306a36Sopenharmony_cistatic void if_usb_prog_firmware(struct lbs_private *priv, int ret,
6962306a36Sopenharmony_ci				 const struct firmware *fw,
7062306a36Sopenharmony_ci				 const struct firmware *unused);
7162306a36Sopenharmony_cistatic int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
7262306a36Sopenharmony_ci			       uint8_t *payload, uint16_t nb);
7362306a36Sopenharmony_cistatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
7462306a36Sopenharmony_ci			uint16_t nb);
7562306a36Sopenharmony_cistatic void if_usb_free(struct if_usb_card *cardp);
7662306a36Sopenharmony_cistatic int if_usb_submit_rx_urb(struct if_usb_card *cardp);
7762306a36Sopenharmony_cistatic int if_usb_reset_device(struct if_usb_card *cardp);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/**
8062306a36Sopenharmony_ci * if_usb_write_bulk_callback - callback function to handle the status
8162306a36Sopenharmony_ci * of the URB
8262306a36Sopenharmony_ci * @urb:	pointer to &urb structure
8362306a36Sopenharmony_ci * returns:	N/A
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistatic void if_usb_write_bulk_callback(struct urb *urb)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct if_usb_card *cardp = (struct if_usb_card *) urb->context;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* handle the transmission complete validations */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (urb->status == 0) {
9262306a36Sopenharmony_ci		struct lbs_private *priv = cardp->priv;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n");
9562306a36Sopenharmony_ci		lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n",
9662306a36Sopenharmony_ci			     urb->actual_length);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		/* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not
9962306a36Sopenharmony_ci		 * passed up to the lbs level.
10062306a36Sopenharmony_ci		 */
10162306a36Sopenharmony_ci		if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT)
10262306a36Sopenharmony_ci			lbs_host_to_card_done(priv);
10362306a36Sopenharmony_ci	} else {
10462306a36Sopenharmony_ci		/* print the failure status number for debug */
10562306a36Sopenharmony_ci		pr_info("URB in failure status: %d\n", urb->status);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * if_usb_free - free tx/rx urb, skb and rx buffer
11162306a36Sopenharmony_ci * @cardp:	pointer to &if_usb_card
11262306a36Sopenharmony_ci * returns:	N/A
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cistatic void if_usb_free(struct if_usb_card *cardp)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	/* Unlink tx & rx urb */
11762306a36Sopenharmony_ci	usb_kill_urb(cardp->tx_urb);
11862306a36Sopenharmony_ci	usb_kill_urb(cardp->rx_urb);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	usb_free_urb(cardp->tx_urb);
12162306a36Sopenharmony_ci	cardp->tx_urb = NULL;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	usb_free_urb(cardp->rx_urb);
12462306a36Sopenharmony_ci	cardp->rx_urb = NULL;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	kfree(cardp->ep_out_buf);
12762306a36Sopenharmony_ci	cardp->ep_out_buf = NULL;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void if_usb_setup_firmware(struct lbs_private *priv)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct if_usb_card *cardp = priv->card;
13362306a36Sopenharmony_ci	struct cmd_ds_set_boot2_ver b2_cmd;
13462306a36Sopenharmony_ci	struct cmd_ds_802_11_fw_wake_method wake_method;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd));
13762306a36Sopenharmony_ci	b2_cmd.action = 0;
13862306a36Sopenharmony_ci	b2_cmd.version = cardp->boot2_version;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd))
14162306a36Sopenharmony_ci		lbs_deb_usb("Setting boot2 version failed\n");
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	priv->wol_gpio = 2; /* Wake via GPIO2... */
14462306a36Sopenharmony_ci	priv->wol_gap = 20; /* ... after 20ms    */
14562306a36Sopenharmony_ci	lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA,
14662306a36Sopenharmony_ci			(struct wol_config *) NULL);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	wake_method.hdr.size = cpu_to_le16(sizeof(wake_method));
14962306a36Sopenharmony_ci	wake_method.action = cpu_to_le16(CMD_ACT_GET);
15062306a36Sopenharmony_ci	if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) {
15162306a36Sopenharmony_ci		netdev_info(priv->dev, "Firmware does not seem to support PS mode\n");
15262306a36Sopenharmony_ci		priv->fwcapinfo &= ~FW_CAPINFO_PS;
15362306a36Sopenharmony_ci	} else {
15462306a36Sopenharmony_ci		if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) {
15562306a36Sopenharmony_ci			lbs_deb_usb("Firmware seems to support PS with wake-via-command\n");
15662306a36Sopenharmony_ci		} else {
15762306a36Sopenharmony_ci			/* The versions which boot up this way don't seem to
15862306a36Sopenharmony_ci			   work even if we set it to the command interrupt */
15962306a36Sopenharmony_ci			priv->fwcapinfo &= ~FW_CAPINFO_PS;
16062306a36Sopenharmony_ci			netdev_info(priv->dev,
16162306a36Sopenharmony_ci				    "Firmware doesn't wake via command interrupt; disabling PS mode\n");
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void if_usb_fw_timeo(struct timer_list *t)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct if_usb_card *cardp = from_timer(cardp, t, fw_timeout);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (cardp->fwdnldover) {
17162306a36Sopenharmony_ci		lbs_deb_usb("Download complete, no event. Assuming success\n");
17262306a36Sopenharmony_ci	} else {
17362306a36Sopenharmony_ci		pr_err("Download timed out\n");
17462306a36Sopenharmony_ci		cardp->surprise_removed = 1;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	wake_up(&cardp->fw_wq);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci#ifdef CONFIG_OLPC
18062306a36Sopenharmony_cistatic void if_usb_reset_olpc_card(struct lbs_private *priv)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	printk(KERN_CRIT "Resetting OLPC wireless via EC...\n");
18362306a36Sopenharmony_ci	olpc_ec_cmd(0x25, NULL, 0, NULL, 0);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci#endif
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/**
18862306a36Sopenharmony_ci * if_usb_probe - sets the configuration values
18962306a36Sopenharmony_ci * @intf:	&usb_interface pointer
19062306a36Sopenharmony_ci * @id:	pointer to usb_device_id
19162306a36Sopenharmony_ci * returns:	0 on success, error code on failure
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic int if_usb_probe(struct usb_interface *intf,
19462306a36Sopenharmony_ci			const struct usb_device_id *id)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct usb_device *udev;
19762306a36Sopenharmony_ci	struct usb_host_interface *iface_desc;
19862306a36Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
19962306a36Sopenharmony_ci	struct lbs_private *priv;
20062306a36Sopenharmony_ci	struct if_usb_card *cardp;
20162306a36Sopenharmony_ci	int r = -ENOMEM;
20262306a36Sopenharmony_ci	int i;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	udev = interface_to_usbdev(intf);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL);
20762306a36Sopenharmony_ci	if (!cardp)
20862306a36Sopenharmony_ci		goto error;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	timer_setup(&cardp->fw_timeout, if_usb_fw_timeo, 0);
21162306a36Sopenharmony_ci	init_waitqueue_head(&cardp->fw_wq);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	cardp->udev = udev;
21462306a36Sopenharmony_ci	cardp->model = (uint32_t) id->driver_info;
21562306a36Sopenharmony_ci	iface_desc = intf->cur_altsetting;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
21862306a36Sopenharmony_ci		     " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
21962306a36Sopenharmony_ci		     le16_to_cpu(udev->descriptor.bcdUSB),
22062306a36Sopenharmony_ci		     udev->descriptor.bDeviceClass,
22162306a36Sopenharmony_ci		     udev->descriptor.bDeviceSubClass,
22262306a36Sopenharmony_ci		     udev->descriptor.bDeviceProtocol);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
22562306a36Sopenharmony_ci		endpoint = &iface_desc->endpoint[i].desc;
22662306a36Sopenharmony_ci		if (usb_endpoint_is_bulk_in(endpoint)) {
22762306a36Sopenharmony_ci			cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize);
22862306a36Sopenharmony_ci			cardp->ep_in = usb_endpoint_num(endpoint);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci			lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in);
23162306a36Sopenharmony_ci			lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		} else if (usb_endpoint_is_bulk_out(endpoint)) {
23462306a36Sopenharmony_ci			cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize);
23562306a36Sopenharmony_ci			cardp->ep_out = usb_endpoint_num(endpoint);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci			lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out);
23862306a36Sopenharmony_ci			lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size);
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	if (!cardp->ep_out_size || !cardp->ep_in_size) {
24262306a36Sopenharmony_ci		lbs_deb_usbd(&udev->dev, "Endpoints not found\n");
24362306a36Sopenharmony_ci		goto dealloc;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci	if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
24662306a36Sopenharmony_ci		lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
24762306a36Sopenharmony_ci		goto dealloc;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
25062306a36Sopenharmony_ci		lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
25162306a36Sopenharmony_ci		goto dealloc;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL);
25462306a36Sopenharmony_ci	if (!cardp->ep_out_buf) {
25562306a36Sopenharmony_ci		lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n");
25662306a36Sopenharmony_ci		goto dealloc;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	priv = lbs_add_card(cardp, &intf->dev);
26062306a36Sopenharmony_ci	if (IS_ERR(priv)) {
26162306a36Sopenharmony_ci		r = PTR_ERR(priv);
26262306a36Sopenharmony_ci		goto err_add_card;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	cardp->priv = priv;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	priv->hw_host_to_card = if_usb_host_to_card;
26862306a36Sopenharmony_ci	priv->enter_deep_sleep = NULL;
26962306a36Sopenharmony_ci	priv->exit_deep_sleep = NULL;
27062306a36Sopenharmony_ci	priv->reset_deep_sleep_wakeup = NULL;
27162306a36Sopenharmony_ci	priv->is_polling = false;
27262306a36Sopenharmony_ci#ifdef CONFIG_OLPC
27362306a36Sopenharmony_ci	if (machine_is_olpc())
27462306a36Sopenharmony_ci		priv->reset_card = if_usb_reset_olpc_card;
27562306a36Sopenharmony_ci#endif
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	cardp->boot2_version = udev->descriptor.bcdDevice;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	usb_get_dev(udev);
28062306a36Sopenharmony_ci	usb_set_intfdata(intf, cardp);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	r = lbs_get_firmware_async(priv, &udev->dev, cardp->model,
28362306a36Sopenharmony_ci				   fw_table, if_usb_prog_firmware);
28462306a36Sopenharmony_ci	if (r)
28562306a36Sopenharmony_ci		goto err_get_fw;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cierr_get_fw:
29062306a36Sopenharmony_ci	usb_put_dev(udev);
29162306a36Sopenharmony_ci	lbs_remove_card(priv);
29262306a36Sopenharmony_cierr_add_card:
29362306a36Sopenharmony_ci	if_usb_reset_device(cardp);
29462306a36Sopenharmony_cidealloc:
29562306a36Sopenharmony_ci	if_usb_free(cardp);
29662306a36Sopenharmony_ci	kfree(cardp);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cierror:
29962306a36Sopenharmony_ci	return r;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/**
30362306a36Sopenharmony_ci * if_usb_disconnect - free resource and cleanup
30462306a36Sopenharmony_ci * @intf:	USB interface structure
30562306a36Sopenharmony_ci * returns:	N/A
30662306a36Sopenharmony_ci */
30762306a36Sopenharmony_cistatic void if_usb_disconnect(struct usb_interface *intf)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct if_usb_card *cardp = usb_get_intfdata(intf);
31062306a36Sopenharmony_ci	struct lbs_private *priv = cardp->priv;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	cardp->surprise_removed = 1;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (priv) {
31562306a36Sopenharmony_ci		lbs_stop_card(priv);
31662306a36Sopenharmony_ci		lbs_remove_card(priv);
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Unlink and free urb */
32062306a36Sopenharmony_ci	if_usb_free(cardp);
32162306a36Sopenharmony_ci	kfree(cardp);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
32462306a36Sopenharmony_ci	usb_put_dev(interface_to_usbdev(intf));
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/**
32862306a36Sopenharmony_ci * if_usb_send_fw_pkt - download FW
32962306a36Sopenharmony_ci * @cardp:	pointer to &struct if_usb_card
33062306a36Sopenharmony_ci * returns:	0
33162306a36Sopenharmony_ci */
33262306a36Sopenharmony_cistatic int if_usb_send_fw_pkt(struct if_usb_card *cardp)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct fwdata *fwdata = cardp->ep_out_buf;
33562306a36Sopenharmony_ci	const uint8_t *firmware = cardp->fw->data;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* If we got a CRC failure on the last block, back
33862306a36Sopenharmony_ci	   up and retry it */
33962306a36Sopenharmony_ci	if (!cardp->CRC_OK) {
34062306a36Sopenharmony_ci		cardp->totalbytes = cardp->fwlastblksent;
34162306a36Sopenharmony_ci		cardp->fwseqnum--;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
34562306a36Sopenharmony_ci		     cardp->totalbytes);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/* struct fwdata (which we sent to the card) has an
34862306a36Sopenharmony_ci	   extra __le32 field in between the header and the data,
34962306a36Sopenharmony_ci	   which is not in the struct fwheader in the actual
35062306a36Sopenharmony_ci	   firmware binary. Insert the seqnum in the middle... */
35162306a36Sopenharmony_ci	memcpy(&fwdata->hdr, &firmware[cardp->totalbytes],
35262306a36Sopenharmony_ci	       sizeof(struct fwheader));
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	cardp->fwlastblksent = cardp->totalbytes;
35562306a36Sopenharmony_ci	cardp->totalbytes += sizeof(struct fwheader);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	memcpy(fwdata->data, &firmware[cardp->totalbytes],
35862306a36Sopenharmony_ci	       le32_to_cpu(fwdata->hdr.datalength));
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
36162306a36Sopenharmony_ci		     le32_to_cpu(fwdata->hdr.datalength));
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
36462306a36Sopenharmony_ci	cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
36762306a36Sopenharmony_ci		     le32_to_cpu(fwdata->hdr.datalength));
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
37062306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
37162306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n",
37262306a36Sopenharmony_ci			     cardp->fwseqnum, cardp->totalbytes);
37362306a36Sopenharmony_ci	} else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
37462306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n");
37562306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "Downloading FW JUMP BLOCK\n");
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		cardp->fwfinalblk = 1;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
38162306a36Sopenharmony_ci		     cardp->totalbytes);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return 0;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic int if_usb_reset_device(struct if_usb_card *cardp)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct cmd_header *cmd = cardp->ep_out_buf + 4;
38962306a36Sopenharmony_ci	int ret;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	cmd->command = cpu_to_le16(CMD_802_11_RESET);
39462306a36Sopenharmony_ci	cmd->size = cpu_to_le16(sizeof(cmd));
39562306a36Sopenharmony_ci	cmd->result = cpu_to_le16(0);
39662306a36Sopenharmony_ci	cmd->seqnum = cpu_to_le16(0x5a5a);
39762306a36Sopenharmony_ci	usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header));
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	msleep(100);
40062306a36Sopenharmony_ci	ret = usb_reset_device(cardp->udev);
40162306a36Sopenharmony_ci	msleep(100);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci#ifdef CONFIG_OLPC
40462306a36Sopenharmony_ci	if (ret && machine_is_olpc())
40562306a36Sopenharmony_ci		if_usb_reset_olpc_card(NULL);
40662306a36Sopenharmony_ci#endif
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return ret;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/**
41262306a36Sopenharmony_ci *  usb_tx_block - transfer the data to the device
41362306a36Sopenharmony_ci *  @cardp: 	pointer to &struct if_usb_card
41462306a36Sopenharmony_ci *  @payload:	pointer to payload data
41562306a36Sopenharmony_ci *  @nb:	data length
41662306a36Sopenharmony_ci *  returns:	0 for success or negative error code
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_cistatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	int ret;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* check if device is removed */
42362306a36Sopenharmony_ci	if (cardp->surprise_removed) {
42462306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "Device removed\n");
42562306a36Sopenharmony_ci		ret = -ENODEV;
42662306a36Sopenharmony_ci		goto tx_ret;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
43062306a36Sopenharmony_ci			  usb_sndbulkpipe(cardp->udev,
43162306a36Sopenharmony_ci					  cardp->ep_out),
43262306a36Sopenharmony_ci			  payload, nb, if_usb_write_bulk_callback, cardp);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
43762306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
43862306a36Sopenharmony_ci	} else {
43962306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
44062306a36Sopenharmony_ci		ret = 0;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_citx_ret:
44462306a36Sopenharmony_ci	return ret;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
44862306a36Sopenharmony_ci				  void (*callbackfn)(struct urb *urb))
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct sk_buff *skb;
45162306a36Sopenharmony_ci	int ret = -1;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) {
45462306a36Sopenharmony_ci		pr_err("No free skb\n");
45562306a36Sopenharmony_ci		goto rx_ret;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	cardp->rx_skb = skb;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Fill the receive configuration URB and initialise the Rx call back */
46162306a36Sopenharmony_ci	usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
46262306a36Sopenharmony_ci			  usb_rcvbulkpipe(cardp->udev, cardp->ep_in),
46362306a36Sopenharmony_ci			  skb->data + IPFIELD_ALIGN_OFFSET,
46462306a36Sopenharmony_ci			  MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
46562306a36Sopenharmony_ci			  cardp);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
46862306a36Sopenharmony_ci	if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) {
46962306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret);
47062306a36Sopenharmony_ci		kfree_skb(skb);
47162306a36Sopenharmony_ci		cardp->rx_skb = NULL;
47262306a36Sopenharmony_ci		ret = -1;
47362306a36Sopenharmony_ci	} else {
47462306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n");
47562306a36Sopenharmony_ci		ret = 0;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cirx_ret:
47962306a36Sopenharmony_ci	return ret;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int if_usb_submit_rx_urb(struct if_usb_card *cardp)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	return __if_usb_submit_rx_urb(cardp, &if_usb_receive);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic void if_usb_receive_fwload(struct urb *urb)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct if_usb_card *cardp = urb->context;
49562306a36Sopenharmony_ci	struct sk_buff *skb = cardp->rx_skb;
49662306a36Sopenharmony_ci	struct fwsyncheader *syncfwheader;
49762306a36Sopenharmony_ci	struct bootcmdresp bootcmdresp;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (urb->status) {
50062306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev,
50162306a36Sopenharmony_ci			     "URB status is failed during fw load\n");
50262306a36Sopenharmony_ci		kfree_skb(skb);
50362306a36Sopenharmony_ci		return;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (cardp->fwdnldover) {
50762306a36Sopenharmony_ci		__le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
51062306a36Sopenharmony_ci		    tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
51162306a36Sopenharmony_ci			pr_info("Firmware ready event received\n");
51262306a36Sopenharmony_ci			wake_up(&cardp->fw_wq);
51362306a36Sopenharmony_ci		} else {
51462306a36Sopenharmony_ci			lbs_deb_usb("Waiting for confirmation; got %x %x\n",
51562306a36Sopenharmony_ci				    le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
51662306a36Sopenharmony_ci			if_usb_submit_rx_urb_fwload(cardp);
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci		kfree_skb(skb);
51962306a36Sopenharmony_ci		return;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci	if (cardp->bootcmdresp <= 0) {
52262306a36Sopenharmony_ci		memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET,
52362306a36Sopenharmony_ci			sizeof(bootcmdresp));
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
52662306a36Sopenharmony_ci			kfree_skb(skb);
52762306a36Sopenharmony_ci			if_usb_submit_rx_urb_fwload(cardp);
52862306a36Sopenharmony_ci			cardp->bootcmdresp = BOOT_CMD_RESP_OK;
52962306a36Sopenharmony_ci			lbs_deb_usbd(&cardp->udev->dev,
53062306a36Sopenharmony_ci				     "Received valid boot command response\n");
53162306a36Sopenharmony_ci			return;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci		if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
53462306a36Sopenharmony_ci			if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
53562306a36Sopenharmony_ci			    bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
53662306a36Sopenharmony_ci			    bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
53762306a36Sopenharmony_ci				if (!cardp->bootcmdresp)
53862306a36Sopenharmony_ci					pr_info("Firmware already seems alive; resetting\n");
53962306a36Sopenharmony_ci				cardp->bootcmdresp = -1;
54062306a36Sopenharmony_ci			} else {
54162306a36Sopenharmony_ci				pr_info("boot cmd response wrong magic number (0x%x)\n",
54262306a36Sopenharmony_ci					    le32_to_cpu(bootcmdresp.magic));
54362306a36Sopenharmony_ci			}
54462306a36Sopenharmony_ci		} else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) &&
54562306a36Sopenharmony_ci			   (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) &&
54662306a36Sopenharmony_ci			   (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) {
54762306a36Sopenharmony_ci			pr_info("boot cmd response cmd_tag error (%d)\n",
54862306a36Sopenharmony_ci				bootcmdresp.cmd);
54962306a36Sopenharmony_ci		} else if (bootcmdresp.result != BOOT_CMD_RESP_OK) {
55062306a36Sopenharmony_ci			pr_info("boot cmd response result error (%d)\n",
55162306a36Sopenharmony_ci				bootcmdresp.result);
55262306a36Sopenharmony_ci		} else {
55362306a36Sopenharmony_ci			cardp->bootcmdresp = 1;
55462306a36Sopenharmony_ci			lbs_deb_usbd(&cardp->udev->dev,
55562306a36Sopenharmony_ci				     "Received valid boot command response\n");
55662306a36Sopenharmony_ci		}
55762306a36Sopenharmony_ci		kfree_skb(skb);
55862306a36Sopenharmony_ci		if_usb_submit_rx_urb_fwload(cardp);
55962306a36Sopenharmony_ci		return;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET,
56362306a36Sopenharmony_ci			       sizeof(struct fwsyncheader), GFP_ATOMIC);
56462306a36Sopenharmony_ci	if (!syncfwheader) {
56562306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n");
56662306a36Sopenharmony_ci		kfree_skb(skb);
56762306a36Sopenharmony_ci		return;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (!syncfwheader->cmd) {
57162306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n");
57262306a36Sopenharmony_ci		lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n",
57362306a36Sopenharmony_ci			     le32_to_cpu(syncfwheader->seqnum));
57462306a36Sopenharmony_ci		cardp->CRC_OK = 1;
57562306a36Sopenharmony_ci	} else {
57662306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n");
57762306a36Sopenharmony_ci		cardp->CRC_OK = 0;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	kfree_skb(skb);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* Give device 5s to either write firmware to its RAM or eeprom */
58362306a36Sopenharmony_ci	mod_timer(&cardp->fw_timeout, jiffies + (HZ*5));
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (cardp->fwfinalblk) {
58662306a36Sopenharmony_ci		cardp->fwdnldover = 1;
58762306a36Sopenharmony_ci		goto exit;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if_usb_send_fw_pkt(cardp);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci exit:
59362306a36Sopenharmony_ci	if_usb_submit_rx_urb_fwload(cardp);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	kfree(syncfwheader);
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci#define MRVDRV_MIN_PKT_LEN	30
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
60162306a36Sopenharmony_ci				       struct if_usb_card *cardp,
60262306a36Sopenharmony_ci				       struct lbs_private *priv)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN
60562306a36Sopenharmony_ci	    || recvlength < MRVDRV_MIN_PKT_LEN) {
60662306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n");
60762306a36Sopenharmony_ci		kfree_skb(skb);
60862306a36Sopenharmony_ci		return;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
61262306a36Sopenharmony_ci	skb_put(skb, recvlength);
61362306a36Sopenharmony_ci	skb_pull(skb, MESSAGE_HEADER_LEN);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	lbs_process_rxed_packet(priv, skb);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
61962306a36Sopenharmony_ci				      struct sk_buff *skb,
62062306a36Sopenharmony_ci				      struct if_usb_card *cardp,
62162306a36Sopenharmony_ci				      struct lbs_private *priv)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	unsigned long flags;
62462306a36Sopenharmony_ci	u8 i;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (recvlength > LBS_CMD_BUFFER_SIZE) {
62762306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev,
62862306a36Sopenharmony_ci			     "The receive buffer is too large\n");
62962306a36Sopenharmony_ci		kfree_skb(skb);
63062306a36Sopenharmony_ci		return;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	spin_lock_irqsave(&priv->driver_lock, flags);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	i = (priv->resp_idx == 0) ? 1 : 0;
63662306a36Sopenharmony_ci	BUG_ON(priv->resp_len[i]);
63762306a36Sopenharmony_ci	priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN);
63862306a36Sopenharmony_ci	memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN,
63962306a36Sopenharmony_ci		priv->resp_len[i]);
64062306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
64162306a36Sopenharmony_ci	lbs_notify_command_response(priv, i);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->driver_lock, flags);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	lbs_deb_usbd(&cardp->udev->dev,
64662306a36Sopenharmony_ci		    "Wake up main thread to handle cmd response\n");
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci/**
65062306a36Sopenharmony_ci *  if_usb_receive - read the packet into the upload buffer,
65162306a36Sopenharmony_ci *  wake up the main thread and initialise the Rx callack
65262306a36Sopenharmony_ci *
65362306a36Sopenharmony_ci *  @urb:	pointer to &struct urb
65462306a36Sopenharmony_ci *  returns:	N/A
65562306a36Sopenharmony_ci */
65662306a36Sopenharmony_cistatic void if_usb_receive(struct urb *urb)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct if_usb_card *cardp = urb->context;
65962306a36Sopenharmony_ci	struct sk_buff *skb = cardp->rx_skb;
66062306a36Sopenharmony_ci	struct lbs_private *priv = cardp->priv;
66162306a36Sopenharmony_ci	int recvlength = urb->actual_length;
66262306a36Sopenharmony_ci	uint8_t *recvbuff = NULL;
66362306a36Sopenharmony_ci	uint32_t recvtype = 0;
66462306a36Sopenharmony_ci	__le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
66562306a36Sopenharmony_ci	uint32_t event;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (recvlength) {
66862306a36Sopenharmony_ci		if (urb->status) {
66962306a36Sopenharmony_ci			lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
67062306a36Sopenharmony_ci				     urb->status);
67162306a36Sopenharmony_ci			kfree_skb(skb);
67262306a36Sopenharmony_ci			goto setup_for_next;
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		recvbuff = skb->data + IPFIELD_ALIGN_OFFSET;
67662306a36Sopenharmony_ci		recvtype = le32_to_cpu(pkt[0]);
67762306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev,
67862306a36Sopenharmony_ci			    "Recv length = 0x%x, Recv type = 0x%X\n",
67962306a36Sopenharmony_ci			    recvlength, recvtype);
68062306a36Sopenharmony_ci	} else if (urb->status) {
68162306a36Sopenharmony_ci		kfree_skb(skb);
68262306a36Sopenharmony_ci		return;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	switch (recvtype) {
68662306a36Sopenharmony_ci	case CMD_TYPE_DATA:
68762306a36Sopenharmony_ci		process_cmdtypedata(recvlength, skb, cardp, priv);
68862306a36Sopenharmony_ci		break;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	case CMD_TYPE_REQUEST:
69162306a36Sopenharmony_ci		process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	case CMD_TYPE_INDICATION:
69562306a36Sopenharmony_ci		/* Event handling */
69662306a36Sopenharmony_ci		event = le32_to_cpu(pkt[1]);
69762306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event);
69862306a36Sopenharmony_ci		kfree_skb(skb);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		/* Icky undocumented magic special case */
70162306a36Sopenharmony_ci		if (event & 0xffff0000) {
70262306a36Sopenharmony_ci			u32 trycount = (event & 0xffff0000) >> 16;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci			lbs_send_tx_feedback(priv, trycount);
70562306a36Sopenharmony_ci		} else
70662306a36Sopenharmony_ci			lbs_queue_event(priv, event & 0xFF);
70762306a36Sopenharmony_ci		break;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	default:
71062306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n",
71162306a36Sopenharmony_ci			     recvtype);
71262306a36Sopenharmony_ci		kfree_skb(skb);
71362306a36Sopenharmony_ci		break;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cisetup_for_next:
71762306a36Sopenharmony_ci	if_usb_submit_rx_urb(cardp);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/**
72162306a36Sopenharmony_ci *  if_usb_host_to_card - downloads data to FW
72262306a36Sopenharmony_ci *  @priv:	pointer to &struct lbs_private structure
72362306a36Sopenharmony_ci *  @type:	type of data
72462306a36Sopenharmony_ci *  @payload:	pointer to data buffer
72562306a36Sopenharmony_ci *  @nb:	number of bytes
72662306a36Sopenharmony_ci *  returns:	0 for success or negative error code
72762306a36Sopenharmony_ci */
72862306a36Sopenharmony_cistatic int if_usb_host_to_card(struct lbs_private *priv, uint8_t type,
72962306a36Sopenharmony_ci			       uint8_t *payload, uint16_t nb)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct if_usb_card *cardp = priv->card;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type);
73462306a36Sopenharmony_ci	lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (type == MVMS_CMD) {
73762306a36Sopenharmony_ci		*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
73862306a36Sopenharmony_ci		priv->dnld_sent = DNLD_CMD_SENT;
73962306a36Sopenharmony_ci	} else {
74062306a36Sopenharmony_ci		*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA);
74162306a36Sopenharmony_ci		priv->dnld_sent = DNLD_DATA_SENT;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci/**
75062306a36Sopenharmony_ci *  if_usb_issue_boot_command - issues Boot command to the Boot2 code
75162306a36Sopenharmony_ci *  @cardp:	pointer to &if_usb_card
75262306a36Sopenharmony_ci *  @ivalue:	1:Boot from FW by USB-Download
75362306a36Sopenharmony_ci *		2:Boot from FW in EEPROM
75462306a36Sopenharmony_ci *  returns:	0 for success or negative error code
75562306a36Sopenharmony_ci */
75662306a36Sopenharmony_cistatic int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct bootcmd *bootcmd = cardp->ep_out_buf;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/* Prepare command */
76162306a36Sopenharmony_ci	bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER);
76262306a36Sopenharmony_ci	bootcmd->cmd = ivalue;
76362306a36Sopenharmony_ci	memset(bootcmd->pad, 0, sizeof(bootcmd->pad));
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	/* Issue command */
76662306a36Sopenharmony_ci	usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd));
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return 0;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci/**
77362306a36Sopenharmony_ci *  check_fwfile_format - check the validity of Boot2/FW image
77462306a36Sopenharmony_ci *
77562306a36Sopenharmony_ci *  @data:	pointer to image
77662306a36Sopenharmony_ci *  @totlen:	image length
77762306a36Sopenharmony_ci *  returns:     0 (good) or 1 (failure)
77862306a36Sopenharmony_ci */
77962306a36Sopenharmony_cistatic int check_fwfile_format(const uint8_t *data, uint32_t totlen)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	uint32_t bincmd, exit;
78262306a36Sopenharmony_ci	uint32_t blksize, offset, len;
78362306a36Sopenharmony_ci	int ret;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	ret = 1;
78662306a36Sopenharmony_ci	exit = len = 0;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	do {
78962306a36Sopenharmony_ci		struct fwheader *fwh = (void *)data;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		bincmd = le32_to_cpu(fwh->dnldcmd);
79262306a36Sopenharmony_ci		blksize = le32_to_cpu(fwh->datalength);
79362306a36Sopenharmony_ci		switch (bincmd) {
79462306a36Sopenharmony_ci		case FW_HAS_DATA_TO_RECV:
79562306a36Sopenharmony_ci			offset = sizeof(struct fwheader) + blksize;
79662306a36Sopenharmony_ci			data += offset;
79762306a36Sopenharmony_ci			len += offset;
79862306a36Sopenharmony_ci			if (len >= totlen)
79962306a36Sopenharmony_ci				exit = 1;
80062306a36Sopenharmony_ci			break;
80162306a36Sopenharmony_ci		case FW_HAS_LAST_BLOCK:
80262306a36Sopenharmony_ci			exit = 1;
80362306a36Sopenharmony_ci			ret = 0;
80462306a36Sopenharmony_ci			break;
80562306a36Sopenharmony_ci		default:
80662306a36Sopenharmony_ci			exit = 1;
80762306a36Sopenharmony_ci			break;
80862306a36Sopenharmony_ci		}
80962306a36Sopenharmony_ci	} while (!exit);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (ret)
81262306a36Sopenharmony_ci		pr_err("firmware file format check FAIL\n");
81362306a36Sopenharmony_ci	else
81462306a36Sopenharmony_ci		lbs_deb_fw("firmware file format check PASS\n");
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return ret;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic void if_usb_prog_firmware(struct lbs_private *priv, int ret,
82062306a36Sopenharmony_ci				 const struct firmware *fw,
82162306a36Sopenharmony_ci				 const struct firmware *unused)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct if_usb_card *cardp = priv->card;
82462306a36Sopenharmony_ci	int i = 0;
82562306a36Sopenharmony_ci	static int reset_count = 10;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (ret) {
82862306a36Sopenharmony_ci		pr_err("failed to find firmware (%d)\n", ret);
82962306a36Sopenharmony_ci		goto done;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	cardp->fw = fw;
83362306a36Sopenharmony_ci	if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
83462306a36Sopenharmony_ci		ret = -EINVAL;
83562306a36Sopenharmony_ci		goto done;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* Cancel any pending usb business */
83962306a36Sopenharmony_ci	usb_kill_urb(cardp->rx_urb);
84062306a36Sopenharmony_ci	usb_kill_urb(cardp->tx_urb);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	cardp->fwlastblksent = 0;
84362306a36Sopenharmony_ci	cardp->fwdnldover = 0;
84462306a36Sopenharmony_ci	cardp->totalbytes = 0;
84562306a36Sopenharmony_ci	cardp->fwfinalblk = 0;
84662306a36Sopenharmony_ci	cardp->bootcmdresp = 0;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cirestart:
84962306a36Sopenharmony_ci	if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
85062306a36Sopenharmony_ci		lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
85162306a36Sopenharmony_ci		ret = -EIO;
85262306a36Sopenharmony_ci		goto done;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	cardp->bootcmdresp = 0;
85662306a36Sopenharmony_ci	do {
85762306a36Sopenharmony_ci		int j = 0;
85862306a36Sopenharmony_ci		i++;
85962306a36Sopenharmony_ci		if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
86062306a36Sopenharmony_ci		/* wait for command response */
86162306a36Sopenharmony_ci		do {
86262306a36Sopenharmony_ci			j++;
86362306a36Sopenharmony_ci			msleep_interruptible(100);
86462306a36Sopenharmony_ci		} while (cardp->bootcmdresp == 0 && j < 10);
86562306a36Sopenharmony_ci	} while (cardp->bootcmdresp == 0 && i < 5);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) {
86862306a36Sopenharmony_ci		/* Return to normal operation */
86962306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
87062306a36Sopenharmony_ci		usb_kill_urb(cardp->rx_urb);
87162306a36Sopenharmony_ci		usb_kill_urb(cardp->tx_urb);
87262306a36Sopenharmony_ci		if (if_usb_submit_rx_urb(cardp) < 0)
87362306a36Sopenharmony_ci			ret = -EIO;
87462306a36Sopenharmony_ci		goto done;
87562306a36Sopenharmony_ci	} else if (cardp->bootcmdresp <= 0) {
87662306a36Sopenharmony_ci		if (--reset_count >= 0) {
87762306a36Sopenharmony_ci			if_usb_reset_device(cardp);
87862306a36Sopenharmony_ci			goto restart;
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci		ret = -EIO;
88162306a36Sopenharmony_ci		goto done;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	i = 0;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	cardp->totalbytes = 0;
88762306a36Sopenharmony_ci	cardp->fwlastblksent = 0;
88862306a36Sopenharmony_ci	cardp->CRC_OK = 1;
88962306a36Sopenharmony_ci	cardp->fwdnldover = 0;
89062306a36Sopenharmony_ci	cardp->fwseqnum = -1;
89162306a36Sopenharmony_ci	cardp->totalbytes = 0;
89262306a36Sopenharmony_ci	cardp->fwfinalblk = 0;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* Send the first firmware packet... */
89562306a36Sopenharmony_ci	if_usb_send_fw_pkt(cardp);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* ... and wait for the process to complete */
89862306a36Sopenharmony_ci	wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	del_timer_sync(&cardp->fw_timeout);
90162306a36Sopenharmony_ci	usb_kill_urb(cardp->rx_urb);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (!cardp->fwdnldover) {
90462306a36Sopenharmony_ci		pr_info("failed to load fw, resetting device!\n");
90562306a36Sopenharmony_ci		if (--reset_count >= 0) {
90662306a36Sopenharmony_ci			if_usb_reset_device(cardp);
90762306a36Sopenharmony_ci			goto restart;
90862306a36Sopenharmony_ci		}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		pr_info("FW download failure, time = %d ms\n", i * 100);
91162306a36Sopenharmony_ci		ret = -EIO;
91262306a36Sopenharmony_ci		goto done;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	cardp->priv->fw_ready = 1;
91662306a36Sopenharmony_ci	if_usb_submit_rx_urb(cardp);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (lbs_start_card(priv))
91962306a36Sopenharmony_ci		goto done;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if_usb_setup_firmware(priv);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/*
92462306a36Sopenharmony_ci	 * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware.
92562306a36Sopenharmony_ci	 */
92662306a36Sopenharmony_ci	priv->wol_criteria = EHS_REMOVE_WAKEUP;
92762306a36Sopenharmony_ci	if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL))
92862306a36Sopenharmony_ci		priv->ehs_remove_supported = false;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci done:
93162306a36Sopenharmony_ci	cardp->fw = NULL;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci#ifdef CONFIG_PM
93662306a36Sopenharmony_cistatic int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct if_usb_card *cardp = usb_get_intfdata(intf);
93962306a36Sopenharmony_ci	struct lbs_private *priv = cardp->priv;
94062306a36Sopenharmony_ci	int ret;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (priv->psstate != PS_STATE_FULL_POWER) {
94362306a36Sopenharmony_ci		ret = -1;
94462306a36Sopenharmony_ci		goto out;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci#ifdef CONFIG_OLPC
94862306a36Sopenharmony_ci	if (machine_is_olpc()) {
94962306a36Sopenharmony_ci		if (priv->wol_criteria == EHS_REMOVE_WAKEUP)
95062306a36Sopenharmony_ci			olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN);
95162306a36Sopenharmony_ci		else
95262306a36Sopenharmony_ci			olpc_ec_wakeup_set(EC_SCI_SRC_WLAN);
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci#endif
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	ret = lbs_suspend(priv);
95762306a36Sopenharmony_ci	if (ret)
95862306a36Sopenharmony_ci		goto out;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	/* Unlink tx & rx urb */
96162306a36Sopenharmony_ci	usb_kill_urb(cardp->tx_urb);
96262306a36Sopenharmony_ci	usb_kill_urb(cardp->rx_urb);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci out:
96562306a36Sopenharmony_ci	return ret;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int if_usb_resume(struct usb_interface *intf)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	struct if_usb_card *cardp = usb_get_intfdata(intf);
97162306a36Sopenharmony_ci	struct lbs_private *priv = cardp->priv;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if_usb_submit_rx_urb(cardp);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	lbs_resume(priv);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci#else
98062306a36Sopenharmony_ci#define if_usb_suspend NULL
98162306a36Sopenharmony_ci#define if_usb_resume NULL
98262306a36Sopenharmony_ci#endif
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic struct usb_driver if_usb_driver = {
98562306a36Sopenharmony_ci	.name = DRV_NAME,
98662306a36Sopenharmony_ci	.probe = if_usb_probe,
98762306a36Sopenharmony_ci	.disconnect = if_usb_disconnect,
98862306a36Sopenharmony_ci	.id_table = if_usb_table,
98962306a36Sopenharmony_ci	.suspend = if_usb_suspend,
99062306a36Sopenharmony_ci	.resume = if_usb_resume,
99162306a36Sopenharmony_ci	.reset_resume = if_usb_resume,
99262306a36Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
99362306a36Sopenharmony_ci};
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_cimodule_usb_driver(if_usb_driver);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ciMODULE_DESCRIPTION("8388 USB WLAN Driver");
99862306a36Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc.");
99962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1000