/*
* Copyright (C) 2008, cozybit Inc.
* Copyright (C) 2003-2006, Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#define DRV_NAME "lbtf_usb"
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "libertas_tf.h"
#include "if_usb.h"
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/usb.h>
#define INSANEDEBUG 0
#define lbtf_deb_usb2(...) do { if (INSANEDEBUG) lbtf_deb_usbd(__VA_ARGS__); } while (0)
#define MESSAGE_HEADER_LEN 4
static char *lbtf_fw_name = "lbtf_usb.bin";
module_param_named(fw_name, lbtf_fw_name, charp, 0644);
MODULE_FIRMWARE("lbtf_usb.bin");
static struct usb_device_id if_usb_table[] = {
/* Enter the device signature inside */
{ USB_DEVICE(0x1286, 0x2001) },
{ USB_DEVICE(0x05a3, 0x8388) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, if_usb_table);
static void if_usb_receive(struct urb *urb);
static void if_usb_receive_fwload(struct urb *urb);
static int if_usb_prog_firmware(struct if_usb_card *cardp);
static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
uint8_t *payload, uint16_t nb);
static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
uint16_t nb, u8 data);
static void if_usb_free(struct if_usb_card *cardp);
static int if_usb_submit_rx_urb(struct if_usb_card *cardp);
static int if_usb_reset_device(struct if_usb_card *cardp);
/**
* if_usb_wrike_bulk_callback - call back to handle URB status
*
* @param urb pointer to urb structure
*/
static void if_usb_write_bulk_callback(struct urb *urb)
{
if (urb->status != 0) {
/* print the failure status number for debug */
pr_info("URB in failure status: %d\n", urb->status);
} else {
lbtf_deb_usb2(&urb->dev->dev, "URB status is successful\n");
lbtf_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n",
urb->actual_length);
}
}
/**
* if_usb_free - free tx/rx urb, skb and rx buffer
*
* @param cardp pointer if_usb_card
*/
static void if_usb_free(struct if_usb_card *cardp)
{
lbtf_deb_enter(LBTF_DEB_USB);
/* Unlink tx & rx urb */
usb_kill_urb(cardp->tx_urb);
usb_kill_urb(cardp->rx_urb);
usb_kill_urb(cardp->cmd_urb);
usb_free_urb(cardp->tx_urb);
cardp->tx_urb = NULL;
usb_free_urb(cardp->rx_urb);
cardp->rx_urb = NULL;
usb_free_urb(cardp->cmd_urb);
cardp->cmd_urb = NULL;
kfree(cardp->ep_out_buf);
cardp->ep_out_buf = NULL;
lbtf_deb_leave(LBTF_DEB_USB);
}
static void if_usb_setup_firmware(struct lbtf_private *priv)
{
struct if_usb_card *cardp = priv->card;
struct cmd_ds_set_boot2_ver b2_cmd;
lbtf_deb_enter(LBTF_DEB_USB);
if_usb_submit_rx_urb(cardp);
b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd));
b2_cmd.action = 0;
b2_cmd.version = cardp->boot2_version;
if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd))
lbtf_deb_usb("Setting boot2 version failed\n");
lbtf_deb_leave(LBTF_DEB_USB);
}
static void if_usb_fw_timeo(unsigned long priv)
{
struct if_usb_card *cardp = (void *)priv;
lbtf_deb_enter(LBTF_DEB_USB);
if (!cardp->fwdnldover) {
/* Download timed out */
cardp->priv->surpriseremoved = 1;
pr_err("Download timed out\n");
} else {
lbtf_deb_usb("Download complete, no event. Assuming success\n");
}
wake_up(&cardp->fw_wq);
lbtf_deb_leave(LBTF_DEB_USB);
}
/**
* if_usb_probe - sets the configuration values
*
* @ifnum interface number
* @id pointer to usb_device_id
*
* Returns: 0 on success, error code on failure
*/
static int if_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct lbtf_private *priv;
struct if_usb_card *cardp;
int i;
lbtf_deb_enter(LBTF_DEB_USB);
udev = interface_to_usbdev(intf);
cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL);
if (!cardp) {
pr_err("Out of memory allocating private data.\n");
goto error;
}
setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp);
init_waitqueue_head(&cardp->fw_wq);
cardp->udev = udev;
iface_desc = intf->cur_altsetting;
lbtf_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
" bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
le16_to_cpu(udev->descriptor.bcdUSB),
udev->descriptor.