1c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/*
2c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Copyright (C) 2008, cozybit Inc.
3c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Copyright (C) 2003-2006, Marvell International Ltd.
4c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
5c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  This program is free software; you can redistribute it and/or modify
6c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  it under the terms of the GNU General Public License as published by
7c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  the Free Software Foundation; either version 2 of the License, or (at
8c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  your option) any later version.
9c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
10e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier#define DRV_NAME "lbtf_usb"
11e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
12edfcba15bdfa520d8c64b496c9260a9d9e0b6d18John W. Linville#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13edfcba15bdfa520d8c64b496c9260a9d9e0b6d18John W. Linville
14e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier#include "libertas_tf.h"
15e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier#include "if_usb.h"
16e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
17c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#include <linux/delay.h>
18ac5c24e9e613df556f054f1fa811fca0c24fe500Paul Gortmaker#include <linux/module.h>
19c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#include <linux/firmware.h>
20c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#include <linux/netdevice.h>
215a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
22c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#include <linux/usb.h>
23c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
24e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier#define INSANEDEBUG	0
25e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier#define lbtf_deb_usb2(...) do { if (INSANEDEBUG) lbtf_deb_usbd(__VA_ARGS__); } while (0)
26c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
27c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#define MESSAGE_HEADER_LEN	4
28c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
29c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic char *lbtf_fw_name = "lbtf_usb.bin";
30c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobomodule_param_named(fw_name, lbtf_fw_name, charp, 0644);
31c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
32790e7560c09a0184afcc00ac0f8df95de7468accBen HutchingsMODULE_FIRMWARE("lbtf_usb.bin");
33790e7560c09a0184afcc00ac0f8df95de7468accBen Hutchings
34c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic struct usb_device_id if_usb_table[] = {
35c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* Enter the device signature inside */
36c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	{ USB_DEVICE(0x1286, 0x2001) },
37c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	{ USB_DEVICE(0x05a3, 0x8388) },
38c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	{}	/* Terminating entry */
39c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo};
40c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
41c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos CoboMODULE_DEVICE_TABLE(usb, if_usb_table);
42c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
43c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_receive(struct urb *urb);
44c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_receive_fwload(struct urb *urb);
45c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_prog_firmware(struct if_usb_card *cardp);
46c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
47c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			       uint8_t *payload, uint16_t nb);
48c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
49c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			uint16_t nb, u8 data);
50c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_free(struct if_usb_card *cardp);
51c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_submit_rx_urb(struct if_usb_card *cardp);
52c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_reset_device(struct if_usb_card *cardp);
53c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
54c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
55c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_wrike_bulk_callback -  call back to handle URB status
56c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
57422f8d19d68d0530dfd37be97bac431ca7435e69Thomas Klute *  @param urb		pointer to urb structure
58c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
59c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_write_bulk_callback(struct urb *urb)
60c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
61e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (urb->status != 0) {
62e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		/* print the failure status number for debug */
63e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_info("URB in failure status: %d\n", urb->status);
64e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	} else {
65e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usb2(&urb->dev->dev, "URB status is successful\n");
66e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n",
67e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			     urb->actual_length);
68e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
69c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
70c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
71c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
72c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_free - free tx/rx urb, skb and rx buffer
73c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
74c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @param cardp	pointer if_usb_card
75c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
76c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_free(struct if_usb_card *cardp)
77c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
78e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
79e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
80c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* Unlink tx & rx urb */
81c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_kill_urb(cardp->tx_urb);
82c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_kill_urb(cardp->rx_urb);
83c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_kill_urb(cardp->cmd_urb);
84c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
85c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_free_urb(cardp->tx_urb);
86c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->tx_urb = NULL;
87c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
88c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_free_urb(cardp->rx_urb);
89c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->rx_urb = NULL;
90c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
91c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_free_urb(cardp->cmd_urb);
92c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->cmd_urb = NULL;
93c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
94c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	kfree(cardp->ep_out_buf);
95c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->ep_out_buf = NULL;
96e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
97e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_USB);
98c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
99c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
100c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_setup_firmware(struct lbtf_private *priv)
101c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
102c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct if_usb_card *cardp = priv->card;
103c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct cmd_ds_set_boot2_ver b2_cmd;
104c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
105e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
106e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
107c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_submit_rx_urb(cardp);
108c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd));
109c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	b2_cmd.action = 0;
110c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	b2_cmd.version = cardp->boot2_version;
111c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
112c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd))
113e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usb("Setting boot2 version failed\n");
114e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
115e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_USB);
116c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
117c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
118c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_fw_timeo(unsigned long priv)
119c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
120c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct if_usb_card *cardp = (void *)priv;
121c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
122e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
123e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!cardp->fwdnldover) {
124c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		/* Download timed out */
125c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->priv->surpriseremoved = 1;
126e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_err("Download timed out\n");
127e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	} else {
128e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usb("Download complete, no event. Assuming success\n");
129e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
130c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	wake_up(&cardp->fw_wq);
131e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_USB);
132c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
133c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
134c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
135c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_probe - sets the configuration values
136c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
137c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @ifnum	interface number
138c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @id		pointer to usb_device_id
139c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
140c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Returns: 0 on success, error code on failure
141c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
142c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_probe(struct usb_interface *intf,
143c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			const struct usb_device_id *id)
144c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
145c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct usb_device *udev;
146c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct usb_host_interface *iface_desc;
147c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct usb_endpoint_descriptor *endpoint;
148c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct lbtf_private *priv;
149c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct if_usb_card *cardp;
150c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	int i;
151c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
152e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
153c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	udev = interface_to_usbdev(intf);
154c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
155c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL);
156e404decb0fb017be80552adee894b35307b6c7b4Joe Perches	if (!cardp)
157c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto error;
158c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
159c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp);
160c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	init_waitqueue_head(&cardp->fw_wq);
161c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
162c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->udev = udev;
163c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	iface_desc = intf->cur_altsetting;
164c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
165e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
166e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
167e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     le16_to_cpu(udev->descriptor.bcdUSB),
168e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     udev->descriptor.bDeviceClass,
169e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     udev->descriptor.bDeviceSubClass,
170e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     udev->descriptor.bDeviceProtocol);
171e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
172c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
173c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		endpoint = &iface_desc->endpoint[i].desc;
174c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (usb_endpoint_is_bulk_in(endpoint)) {
175c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			cardp->ep_in_size =
176c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				le16_to_cpu(endpoint->wMaxPacketSize);
177c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			cardp->ep_in = usb_endpoint_num(endpoint);
178e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
179139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n",
180139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute				cardp->ep_in);
181139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n",
182139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute				cardp->ep_in_size);
183c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		} else if (usb_endpoint_is_bulk_out(endpoint)) {
184c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			cardp->ep_out_size =
185c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				le16_to_cpu(endpoint->wMaxPacketSize);
186c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			cardp->ep_out = usb_endpoint_num(endpoint);
187e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
188139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n",
189139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute				cardp->ep_out);
190e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n",
191422f8d19d68d0530dfd37be97bac431ca7435e69Thomas Klute				cardp->ep_out_size);
192c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		}
193c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
194e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!cardp->ep_out_size || !cardp->ep_in_size) {
195e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&udev->dev, "Endpoints not found\n");
196c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		/* Endpoints not found */
197c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto dealloc;
198e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
199c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
200c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
201e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!cardp->rx_urb) {
202e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
203c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto dealloc;
204e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
205c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
206c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
207e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!cardp->tx_urb) {
208e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
209c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto dealloc;
210e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
211c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
212c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
213e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!cardp->cmd_urb) {
214e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&udev->dev, "Cmd URB allocation failed\n");
215c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto dealloc;
216e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
217c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
218c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE,
219c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				    GFP_KERNEL);
220e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!cardp->ep_out_buf) {
221e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&udev->dev, "Could not allocate buffer\n");
222c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto dealloc;
223e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
224c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
225c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	priv = lbtf_add_card(cardp, &udev->dev);
226c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (!priv)
227c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto dealloc;
228c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
229c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->priv = priv;
230c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
231c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	priv->hw_host_to_card = if_usb_host_to_card;
232c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	priv->hw_prog_firmware = if_usb_prog_firmware;
233c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	priv->hw_reset_device = if_usb_reset_device;
234c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->boot2_version = udev->descriptor.bcdDevice;
235c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
236c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_get_dev(udev);
237c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_set_intfdata(intf, cardp);
238c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
239c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return 0;
240c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
241c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobodealloc:
242c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_free(cardp);
243c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Coboerror:
244e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosierlbtf_deb_leave(LBTF_DEB_MAIN);
245c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return -ENOMEM;
246c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
247c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
248c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
249c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_disconnect -  free resource and cleanup
250c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
251c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @intf	USB interface structure
252c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
253c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_disconnect(struct usb_interface *intf)
254c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
255c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct if_usb_card *cardp = usb_get_intfdata(intf);
2562c208890c6d4e16076c6664137703ec813e8fa6cJoe Perches	struct lbtf_private *priv = cardp->priv;
257c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
258e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_MAIN);
259e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
260c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_reset_device(cardp);
261c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
262c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (priv)
263c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		lbtf_remove_card(priv);
264c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
265c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* Unlink and free urb */
266c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_free(cardp);
267c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
268c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_set_intfdata(intf, NULL);
269c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_put_dev(interface_to_usbdev(intf));
270e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
271e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_MAIN);
272c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
273c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
274c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
275c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_send_fw_pkt -  This function downloads the FW
276c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
277c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @priv	pointer to struct lbtf_private
278c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
279c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Returns: 0
280c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
281c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_send_fw_pkt(struct if_usb_card *cardp)
282c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
283c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct fwdata *fwdata = cardp->ep_out_buf;
284c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	u8 *firmware = (u8 *) cardp->fw->data;
285c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
286e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_FW);
287e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
288c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* If we got a CRC failure on the last block, back
289c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	   up and retry it */
290c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (!cardp->CRC_OK) {
291c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->totalbytes = cardp->fwlastblksent;
292c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->fwseqnum--;
293c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
294c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
295e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
296e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     cardp->totalbytes);
297e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
298c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* struct fwdata (which we sent to the card) has an
299c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	   extra __le32 field in between the header and the data,
300c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	   which is not in the struct fwheader in the actual
301c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	   firmware binary. Insert the seqnum in the middle... */
302c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	memcpy(&fwdata->hdr, &firmware[cardp->totalbytes],
303c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	       sizeof(struct fwheader));
304c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
305c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->fwlastblksent = cardp->totalbytes;
306c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->totalbytes += sizeof(struct fwheader);
307c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
308c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	memcpy(fwdata->data, &firmware[cardp->totalbytes],
309c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	       le32_to_cpu(fwdata->hdr.datalength));
310c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
311e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
312e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     le32_to_cpu(fwdata->hdr.datalength));
313e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
314c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
315c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
316c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
317c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
318c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		     le32_to_cpu(fwdata->hdr.datalength), 0);
319c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
320e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
321e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
322139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usb2(&cardp->udev->dev,
323139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"seqnum = %d totalbytes = %d\n",
324139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			cardp->fwseqnum, cardp->totalbytes);
325e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	} else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
326139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usb2(&cardp->udev->dev,
327139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"Host has finished FW downloading\n");
328e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
329e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
330c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		/* Host has finished FW downloading
331c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		 * Donwloading FW JUMP BLOCK
332c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		 */
333c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->fwfinalblk = 1;
334e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
335c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
336e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
337e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		     cardp->totalbytes);
338e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
339e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_FW);
340c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return 0;
341c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
342c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
343c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_reset_device(struct if_usb_card *cardp)
344c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
345c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4;
346c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	int ret;
347c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
348e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
349e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
350c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
351c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
352c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET);
353c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset));
354c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cmd->hdr.result = cpu_to_le16(0);
355c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cmd->hdr.seqnum = cpu_to_le16(0x5a5a);
356c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cmd->action = cpu_to_le16(CMD_ACT_HALT);
357c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_tx_block(cardp, cardp->ep_out_buf,
358c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		     4 + sizeof(struct cmd_ds_802_11_reset), 0);
359c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
360c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	msleep(100);
361c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	ret = usb_reset_device(cardp->udev);
362c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	msleep(100);
363c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
364e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
365e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
366c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return ret;
367c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
368c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos CoboEXPORT_SYMBOL_GPL(if_usb_reset_device);
369c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
370c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
371c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  usb_tx_block - transfer data to the device
372c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
373422f8d19d68d0530dfd37be97bac431ca7435e69Thomas Klute *  @priv	pointer to struct lbtf_private
374c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @payload	pointer to payload data
375c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @nb		data length
376c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @data	non-zero for data, zero for commands
377c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
378c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Returns: 0 on success, nonzero otherwise.
379c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
380c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
381c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			uint16_t nb, u8 data)
382c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
383e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	int ret = -1;
384c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct urb *urb;
385c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
386e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
387c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* check if device is removed */
388e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (cardp->priv->surpriseremoved) {
389e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n");
390e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		goto tx_ret;
391e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
392c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
393c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (data)
394c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		urb = cardp->tx_urb;
395c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	else
396c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		urb = cardp->cmd_urb;
397c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
398c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_fill_bulk_urb(urb, cardp->udev,
399c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			  usb_sndbulkpipe(cardp->udev,
400c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo					  cardp->ep_out),
401c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			  payload, nb, if_usb_write_bulk_callback, cardp);
402c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
403c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	urb->transfer_flags |= URB_ZERO_PACKET;
404c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
405e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (usb_submit_urb(urb, GFP_ATOMIC)) {
406139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usbd(&cardp->udev->dev,
407139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"usb_submit_urb failed: %d\n", ret);
408e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		goto tx_ret;
409e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
410e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
411e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
412e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
413e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	ret = 0;
414e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
415e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosiertx_ret:
416e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_USB);
417e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	return ret;
418c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
419c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
420c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
421c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				  void (*callbackfn)(struct urb *urb))
422c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
423c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct sk_buff *skb;
424e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	int ret = -1;
425e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
426e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
427c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
428c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
429e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!skb) {
430e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_err("No free skb\n");
431e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
432c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return -1;
433e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
434c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
435c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->rx_skb = skb;
436c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
437c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* Fill the receive configuration URB and initialise the Rx call back */
438c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
439c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			  usb_rcvbulkpipe(cardp->udev, cardp->ep_in),
4409b44fb89cab6e01816cdc05d6b59fdcf8100b8c3Johannes Berg			  skb_tail_pointer(skb),
441c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			  MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp);
442c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
443c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
444c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
445139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute	lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n",
446139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		cardp->rx_urb);
447e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC);
448e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (ret) {
449139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usbd(&cardp->udev->dev,
450139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"Submit Rx URB failed: %d\n", ret);
451c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
452c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->rx_skb = NULL;
453e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
454c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return -1;
455e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	} else {
456e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n");
457e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
458c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return 0;
459e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
460c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
461c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
462c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp)
463c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
464c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload);
465c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
466c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
467c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_submit_rx_urb(struct if_usb_card *cardp)
468c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
469c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return __if_usb_submit_rx_urb(cardp, &if_usb_receive);
470c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
471c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
472c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_receive_fwload(struct urb *urb)
473c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
474c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct if_usb_card *cardp = urb->context;
475c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct sk_buff *skb = cardp->rx_skb;
476c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct fwsyncheader *syncfwheader;
477c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct bootcmdresp bcmdresp;
478c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
479e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
480c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (urb->status) {
481e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&cardp->udev->dev,
482e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			     "URB status is failed during fw load\n");
483c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
484e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
485c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return;
486c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
487c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
488c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (cardp->fwdnldover) {
489c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		__le32 *tmp = (__le32 *)(skb->data);
490c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
491c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
492e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		    tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
493c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			/* Firmware ready event received */
494e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			pr_info("Firmware ready event received\n");
495c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			wake_up(&cardp->fw_wq);
496e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		} else {
497e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			lbtf_deb_usb("Waiting for confirmation; got %x %x\n",
498e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier				    le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
499c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			if_usb_submit_rx_urb_fwload(cardp);
500e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		}
501c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
502e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
503c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return;
504c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
505c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (cardp->bootcmdresp <= 0) {
506c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		memcpy(&bcmdresp, skb->data, sizeof(bcmdresp));
507c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
508c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
509c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			kfree_skb(skb);
510c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			if_usb_submit_rx_urb_fwload(cardp);
511c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			cardp->bootcmdresp = 1;
512c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			/* Received valid boot command response */
513e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			lbtf_deb_usbd(&cardp->udev->dev,
514e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier				     "Received valid boot command response\n");
515e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			lbtf_deb_leave(LBTF_DEB_USB);
516c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			return;
517c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		}
518c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
519c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
520c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			    bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
521e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			    bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
522e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier				if (!cardp->bootcmdresp)
523e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier					pr_info("Firmware already seems alive; resetting\n");
524c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				cardp->bootcmdresp = -1;
525e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			} else {
526e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier				pr_info("boot cmd response wrong magic number (0x%x)\n",
527e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier					    le32_to_cpu(bcmdresp.magic));
528e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			}
529e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		} else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) {
530e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			pr_info("boot cmd response cmd_tag error (%d)\n",
531139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute				bcmdresp.cmd);
532e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		} else if (bcmdresp.result != BOOT_CMD_RESP_OK) {
533e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			pr_info("boot cmd response result error (%d)\n",
534139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute				bcmdresp.result);
535e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		} else {
536c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			cardp->bootcmdresp = 1;
537e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			lbtf_deb_usbd(&cardp->udev->dev,
538139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute				"Received valid boot command response\n");
539e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		}
540c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
541c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
542c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if_usb_submit_rx_urb_fwload(cardp);
543e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
544c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return;
545c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
546c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
54702730029530e7ca2a4d413d6afa67bbc9548c8c2Julia Lawall	syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader),
54802730029530e7ca2a4d413d6afa67bbc9548c8c2Julia Lawall			       GFP_ATOMIC);
549c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (!syncfwheader) {
550139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usbd(&cardp->udev->dev,
551139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"Failure to allocate syncfwheader\n");
552c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
553e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
554c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return;
555c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
556c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
557e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	if (!syncfwheader->cmd) {
558139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usb2(&cardp->udev->dev,
559139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"FW received Blk with correct CRC\n");
560139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usb2(&cardp->udev->dev,
561139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"FW received Blk seqnum = %d\n",
562139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			le32_to_cpu(syncfwheader->seqnum));
563c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->CRC_OK = 1;
564e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	} else {
565139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usbd(&cardp->udev->dev,
566139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			"FW received Blk with CRC error\n");
567c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->CRC_OK = 0;
568e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	}
569e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
570c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	kfree_skb(skb);
571c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
572c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* reschedule timer for 200ms hence */
573c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	mod_timer(&cardp->fw_timeout, jiffies + (HZ/5));
574c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
575c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (cardp->fwfinalblk) {
576c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		cardp->fwdnldover = 1;
577c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto exit;
578c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
579c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
580c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_send_fw_pkt(cardp);
581c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
582c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo exit:
583c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_submit_rx_urb_fwload(cardp);
584c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
585c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	kfree(syncfwheader);
586c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
587e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_USB);
588c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
589c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
590c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#define MRVDRV_MIN_PKT_LEN	30
591c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
592c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
593c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				       struct if_usb_card *cardp,
594c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				       struct lbtf_private *priv)
595c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
596c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN
597c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	    || recvlength < MRVDRV_MIN_PKT_LEN) {
598e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n");
599c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
600c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return;
601c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
602c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
603c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	skb_put(skb, recvlength);
604c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	skb_pull(skb, MESSAGE_HEADER_LEN);
605c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	lbtf_rx(priv, skb);
606c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
607c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
608c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
609c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				      struct sk_buff *skb,
610c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				      struct if_usb_card *cardp,
611c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				      struct lbtf_private *priv)
612c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
613c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (recvlength > LBS_CMD_BUFFER_SIZE) {
614e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&cardp->udev->dev,
615e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			     "The receive buffer is too large\n");
616c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
617c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return;
618c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
619c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
6200ee904c35cc3fdd26a9c76077d9692d458309186Alexander Beregalov	BUG_ON(!in_interrupt());
621c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
622c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	spin_lock(&priv->driver_lock);
623c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN,
624c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	       recvlength - MESSAGE_HEADER_LEN);
625c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	kfree_skb(skb);
626c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	lbtf_cmd_response_rx(priv);
627c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	spin_unlock(&priv->driver_lock);
628c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
629c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
630c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
631c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_receive - read data received from the device.
632c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
633c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @urb		pointer to struct urb
634c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
635c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic void if_usb_receive(struct urb *urb)
636c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
637c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct if_usb_card *cardp = urb->context;
638c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct sk_buff *skb = cardp->rx_skb;
639c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct lbtf_private *priv = cardp->priv;
640c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	int recvlength = urb->actual_length;
641c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	uint8_t *recvbuff = NULL;
642c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	uint32_t recvtype = 0;
643c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	__le32 *pkt = (__le32 *) skb->data;
644c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
645e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
646e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
647c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (recvlength) {
648c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (urb->status) {
649e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
650e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier				     urb->status);
651c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			kfree_skb(skb);
652c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			goto setup_for_next;
653c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		}
654c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
655c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		recvbuff = skb->data;
656c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		recvtype = le32_to_cpu(pkt[0]);
657e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&cardp->udev->dev,
658e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			    "Recv length = 0x%x, Recv type = 0x%X\n",
659e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			    recvlength, recvtype);
660c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	} else if (urb->status) {
661c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
662e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_leave(LBTF_DEB_USB);
663c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return;
664c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
665c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
666c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	switch (recvtype) {
667c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	case CMD_TYPE_DATA:
668c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		process_cmdtypedata(recvlength, skb, cardp, priv);
669c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		break;
670c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
671c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	case CMD_TYPE_REQUEST:
672c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
673c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		break;
674c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
675c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	case CMD_TYPE_INDICATION:
676c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	{
677c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		/* Event cause handling */
678c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		u32 event_cause = le32_to_cpu(pkt[1]);
679139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute		lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n",
680139455c3912bfed4bd42824d59c82113cc667f91Thomas Klute			event_cause);
681c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
682c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		/* Icky undocumented magic special case */
683c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (event_cause & 0xffff0000) {
684c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			u16 tmp;
685c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			u8 retrycnt;
686c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			u8 failure;
687c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
688c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			tmp = event_cause >> 16;
689c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			retrycnt = tmp & 0x00ff;
690c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			failure = (tmp & 0xff00) >> 8;
691c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			lbtf_send_tx_feedback(priv, retrycnt, failure);
692c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		} else if (event_cause == LBTF_EVENT_BCN_SENT)
693c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			lbtf_bcn_sent(priv);
694c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		else
695e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier			lbtf_deb_usbd(&cardp->udev->dev,
696c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			       "Unsupported notification %d received\n",
697c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			       event_cause);
698c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
699c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		break;
700c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
701c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	default:
702e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&cardp->udev->dev,
703422f8d19d68d0530dfd37be97bac431ca7435e69Thomas Klute			"libertastf: unknown command type 0x%X\n", recvtype);
704c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		kfree_skb(skb);
705c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		break;
706c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
707c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
708c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobosetup_for_next:
709c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_submit_rx_urb(cardp);
710e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave(LBTF_DEB_USB);
711c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
712c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
713c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
714c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_host_to_card -  Download data to the device
715c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
716c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @priv		pointer to struct lbtf_private structure
717c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @type		type of data
718c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @buf		pointer to data buffer
719c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @len		number of bytes
720c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
721c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Returns: 0 on success, nonzero otherwise
722c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
723c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
724c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			       uint8_t *payload, uint16_t nb)
725c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
726c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct if_usb_card *cardp = priv->card;
727c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	u8 data = 0;
728c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
729e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type);
730e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_usbd(&cardp->udev->dev, "size after = %d\n", nb);
731e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
732c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (type == MVMS_CMD) {
733c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
734c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	} else {
735c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		*(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA);
736c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		data = 1;
737c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
738c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
739c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb);
740c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
741c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN,
742c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			    data);
743c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
744c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
745c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
746c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  if_usb_issue_boot_command - Issue boot command to Boot2.
747c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
748c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @ivalue   1 boots from FW by USB-Download, 2 boots from FW in EEPROM.
749c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
750c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Returns: 0
751c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
752c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
753c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
754c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	struct bootcmd *bootcmd = cardp->ep_out_buf;
755c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
756c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* Prepare command */
757c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER);
758c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	bootcmd->cmd = ivalue;
759c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	memset(bootcmd->pad, 0, sizeof(bootcmd->pad));
760c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
761c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* Issue command */
762c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0);
763c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
764c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return 0;
765c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
766c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
767c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
768c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo/**
769c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  check_fwfile_format - Check the validity of Boot2/FW image.
770c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
771c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @data	pointer to image
772c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  @totlen	image length
773c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *
774c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo *  Returns: 0 if the image is valid, nonzero otherwise.
775c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo */
776c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int check_fwfile_format(const u8 *data, u32 totlen)
777c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
778c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	u32 bincmd, exit;
779c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	u32 blksize, offset, len;
780c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	int ret;
781c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
782c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	ret = 1;
783c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	exit = len = 0;
784c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
785c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	do {
786c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		struct fwheader *fwh = (void *) data;
787c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
788c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		bincmd = le32_to_cpu(fwh->dnldcmd);
789c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		blksize = le32_to_cpu(fwh->datalength);
790c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		switch (bincmd) {
791c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		case FW_HAS_DATA_TO_RECV:
792c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			offset = sizeof(struct fwheader) + blksize;
793c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			data += offset;
794c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			len += offset;
795c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			if (len >= totlen)
796c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo				exit = 1;
797c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			break;
798c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		case FW_HAS_LAST_BLOCK:
799c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			exit = 1;
800c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			ret = 0;
801c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			break;
802c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		default:
803c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			exit = 1;
804c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			break;
805c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		}
806c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	} while (!exit);
807c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
808c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (ret)
809e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_err("firmware file format check FAIL\n");
810e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	else
811e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_fw("firmware file format check PASS\n");
812e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
813c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return ret;
814c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
815c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
816c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
817c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic int if_usb_prog_firmware(struct if_usb_card *cardp)
818c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo{
819c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	int i = 0;
820c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	static int reset_count = 10;
821c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	int ret = 0;
822c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
823e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_enter(LBTF_DEB_USB);
824e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier
825d6d1b650ae6acce73d55dd0246de22180303ae73Rusty Russell	kparam_block_sysfs_write(fw_name);
826c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev);
827c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (ret < 0) {
828e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_err("request_firmware() failed with %#x\n", ret);
829e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_err("firmware %s not found\n", lbtf_fw_name);
830d6d1b650ae6acce73d55dd0246de22180303ae73Rusty Russell		kparam_unblock_sysfs_write(fw_name);
831c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto done;
832c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
833d6d1b650ae6acce73d55dd0246de22180303ae73Rusty Russell	kparam_unblock_sysfs_write(fw_name);
834c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
835c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (check_fwfile_format(cardp->fw->data, cardp->fw->size))
836c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto release_fw;
837c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
838c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Coborestart:
839c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
840e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
841c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		ret = -1;
842c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto release_fw;
843c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
844c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
845c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->bootcmdresp = 0;
846c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	do {
847c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		int j = 0;
848c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		i++;
849c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		/* Issue Boot command = 1, Boot from Download-FW */
850c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
851c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		/* wait for command response */
852c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		do {
853c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			j++;
854c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			msleep_interruptible(100);
855c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		} while (cardp->bootcmdresp == 0 && j < 10);
856c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	} while (cardp->bootcmdresp == 0 && i < 5);
857c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
858c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (cardp->bootcmdresp <= 0) {
859c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (--reset_count >= 0) {
860c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			if_usb_reset_device(cardp);
861c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			goto restart;
862c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		}
863c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		return -1;
864c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
865c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
866c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	i = 0;
867c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
868c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->totalbytes = 0;
869c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->fwlastblksent = 0;
870c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->CRC_OK = 1;
871c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->fwdnldover = 0;
872c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->fwseqnum = -1;
873c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->totalbytes = 0;
874c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->fwfinalblk = 0;
875c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
876c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* Send the first firmware packet... */
877c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_send_fw_pkt(cardp);
878c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
879c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	/* ... and wait for the process to complete */
880c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved ||
881c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo					       cardp->fwdnldover);
882c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
883c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	del_timer_sync(&cardp->fw_timeout);
884c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	usb_kill_urb(cardp->rx_urb);
885c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
886c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if (!cardp->fwdnldover) {
887e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_info("failed to load fw, resetting device!\n");
888c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		if (--reset_count >= 0) {
889c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			if_usb_reset_device(cardp);
890c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo			goto restart;
891c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		}
892c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
893e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier		pr_info("FW download failure, time = %d ms\n", i * 100);
894c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		ret = -1;
895c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo		goto release_fw;
896c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	}
897c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
898c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->priv->fw_ready = 1;
899c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
900c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo release_fw:
901c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	release_firmware(cardp->fw);
902c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	cardp->fw = NULL;
903c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
904c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	if_usb_setup_firmware(cardp->priv);
905c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
906c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo done:
907e9bd5bcde7af27ebb92bb866afde5ef5e4f3dc6cSteve deRosier	lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
908c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	return ret;
909c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo}
910c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos CoboEXPORT_SYMBOL_GPL(if_usb_prog_firmware);
911c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
912c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
913c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#define if_usb_suspend NULL
914c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo#define if_usb_resume NULL
915c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
916c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobostatic struct usb_driver if_usb_driver = {
917c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	.name = DRV_NAME,
918c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	.probe = if_usb_probe,
919c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	.disconnect = if_usb_disconnect,
920c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	.id_table = if_usb_table,
921c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	.suspend = if_usb_suspend,
922c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo	.resume = if_usb_resume,
923e1f12eb6ba6f1e74007eb01ed26fad7c5239d62bSarah Sharp	.disable_hub_initiated_lpm = 1,
924c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo};
925c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
926d632eb1bf22e11def74e4e53cc47d790fbdba105Greg Kroah-Hartmanmodule_usb_driver(if_usb_driver);
927c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos Cobo
928c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos CoboMODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver");
929c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos CoboMODULE_AUTHOR("Cozybit Inc.");
930c305a19a0d0a47ac59a58865a4a63be65b1bf7c8Luis Carlos CoboMODULE_LICENSE("GPL");
931