Part of reorganising wireless drivers directory and Kconfig.
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
To compile this driver as a module, choose M here: the module will be
called ray_cs. If unsure, say N.
-config LIBERTAS_THINFIRM
- tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware"
- depends on MAC80211
- select FW_LOADER
- ---help---
- A library for Marvell Libertas 8xxx devices using thinfirm.
-
-config LIBERTAS_THINFIRM_DEBUG
- bool "Enable full debugging output in the Libertas thin firmware module."
- depends on LIBERTAS_THINFIRM
- ---help---
- Debugging support.
-
-config LIBERTAS_THINFIRM_USB
- tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware"
- depends on LIBERTAS_THINFIRM && USB
- ---help---
- A driver for Marvell Libertas 8388 USB devices using thinfirm.
-
config PCMCIA_WL3501
tristate "Planet WL3501 PCMCIA cards"
depends on CFG80211 && PCMCIA
obj-$(CONFIG_USB_ZD1201) += zd1201.o
-obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
-
obj-$(CONFIG_MWL8K) += mwl8k.o
obj-$(CONFIG_RT2X00) += rt2x00/
+++ /dev/null
-libertas_tf-objs := main.o cmd.o
-
-libertas_tf_usb-objs += if_usb.o
-
-obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf.o
-obj-$(CONFIG_LIBERTAS_THINFIRM_USB) += libertas_tf_usb.o
+++ /dev/null
-/*
- * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/hardirq.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include "libertas_tf.h"
-
-static const struct channel_range channel_ranges[] = {
- { LBTF_REGDOMAIN_US, 1, 12 },
- { LBTF_REGDOMAIN_CA, 1, 12 },
- { LBTF_REGDOMAIN_EU, 1, 14 },
- { LBTF_REGDOMAIN_JP, 1, 14 },
- { LBTF_REGDOMAIN_SP, 1, 14 },
- { LBTF_REGDOMAIN_FR, 1, 14 },
-};
-
-static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] =
-{
- LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU,
- LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP,
-};
-
-static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv);
-
-
-/**
- * lbtf_cmd_copyback - Simple callback that copies response back into command
- *
- * @priv A pointer to struct lbtf_private structure
- * @extra A pointer to the original command structure for which
- * 'resp' is a response
- * @resp A pointer to the command response
- *
- * Returns: 0 on success, error on failure
- */
-int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra,
- struct cmd_header *resp)
-{
- struct cmd_header *buf = (void *)extra;
- uint16_t copy_len;
-
- copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size));
- memcpy(buf, resp, copy_len);
- return 0;
-}
-EXPORT_SYMBOL_GPL(lbtf_cmd_copyback);
-
-#define CHAN_TO_IDX(chan) ((chan) - 1)
-
-static void lbtf_geo_init(struct lbtf_private *priv)
-{
- const struct channel_range *range = channel_ranges;
- u8 ch;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(channel_ranges); i++)
- if (channel_ranges[i].regdomain == priv->regioncode) {
- range = &channel_ranges[i];
- break;
- }
-
- for (ch = priv->range.start; ch < priv->range.end; ch++)
- priv->channels[CHAN_TO_IDX(ch)].flags = 0;
-}
-
-/**
- * lbtf_update_hw_spec: Updates the hardware details.
- *
- * @priv A pointer to struct lbtf_private structure
- *
- * Returns: 0 on success, error on failure
- */
-int lbtf_update_hw_spec(struct lbtf_private *priv)
-{
- struct cmd_ds_get_hw_spec cmd;
- int ret = -1;
- u32 i;
-
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN);
- ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd);
- if (ret)
- goto out;
-
- priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo);
-
- /* The firmware release is in an interesting format: the patch
- * level is in the most significant nibble ... so fix that: */
- priv->fwrelease = le32_to_cpu(cmd.fwrelease);
- priv->fwrelease = (priv->fwrelease << 8) |
- (priv->fwrelease >> 24 & 0xff);
-
- printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n",
- cmd.permanentaddr,
- priv->fwrelease >> 24 & 0xff,
- priv->fwrelease >> 16 & 0xff,
- priv->fwrelease >> 8 & 0xff,
- priv->fwrelease & 0xff,
- priv->fwcapinfo);
- lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
- cmd.hwifversion, cmd.version);
-
- /* Clamp region code to 8-bit since FW spec indicates that it should
- * only ever be 8-bit, even though the field size is 16-bit. Some
- * firmware returns non-zero high 8 bits here.
- */
- priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
-
- for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
- /* use the region code to search for the index */
- if (priv->regioncode == lbtf_region_code_to_index[i])
- break;
- }
-
- /* if it's unidentified region code, use the default (USA) */
- if (i >= MRVDRV_MAX_REGION_CODE) {
- priv->regioncode = 0x10;
- pr_info("unidentified region code; using the default (USA)\n");
- }
-
- if (priv->current_addr[0] == 0xff)
- memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
-
- SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr);
-
- lbtf_geo_init(priv);
-out:
- lbtf_deb_leave(LBTF_DEB_CMD);
- return ret;
-}
-
-/**
- * lbtf_set_channel: Set the radio channel
- *
- * @priv A pointer to struct lbtf_private structure
- * @channel The desired channel, or 0 to clear a locked channel
- *
- * Returns: 0 on success, error on failure
- */
-int lbtf_set_channel(struct lbtf_private *priv, u8 channel)
-{
- int ret = 0;
- struct cmd_ds_802_11_rf_channel cmd;
-
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET);
- cmd.channel = cpu_to_le16(channel);
-
- ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
- lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon)
-{
- struct cmd_ds_802_11_beacon_set cmd;
- int size;
-
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- if (beacon->len > MRVL_MAX_BCN_SIZE) {
- lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1);
- return -1;
- }
- size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len;
- cmd.hdr.size = cpu_to_le16(size);
- cmd.len = cpu_to_le16(beacon->len);
- memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len);
-
- lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size);
-
- lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0);
- return 0;
-}
-
-int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable,
- int beacon_int)
-{
- struct cmd_ds_802_11_beacon_control cmd;
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
- cmd.beacon_enable = cpu_to_le16(beacon_enable);
- cmd.beacon_period = cpu_to_le16(beacon_int);
-
- lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd));
-
- lbtf_deb_leave(LBTF_DEB_CMD);
- return 0;
-}
-
-static void lbtf_queue_cmd(struct lbtf_private *priv,
- struct cmd_ctrl_node *cmdnode)
-{
- unsigned long flags;
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- if (!cmdnode) {
- lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n");
- goto qcmd_done;
- }
-
- if (!cmdnode->cmdbuf->size) {
- lbtf_deb_host("DNLD_CMD: cmd size is zero\n");
- goto qcmd_done;
- }
-
- cmdnode->result = 0;
- spin_lock_irqsave(&priv->driver_lock, flags);
- list_add_tail(&cmdnode->list, &priv->cmdpendingq);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
- le16_to_cpu(cmdnode->cmdbuf->command));
-
-qcmd_done:
- lbtf_deb_leave(LBTF_DEB_HOST);
-}
-
-static void lbtf_submit_command(struct lbtf_private *priv,
- struct cmd_ctrl_node *cmdnode)
-{
- unsigned long flags;
- struct cmd_header *cmd;
- uint16_t cmdsize;
- uint16_t command;
- int timeo = 5 * HZ;
- int ret;
-
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- cmd = cmdnode->cmdbuf;
-
- spin_lock_irqsave(&priv->driver_lock, flags);
- priv->cur_cmd = cmdnode;
- cmdsize = le16_to_cpu(cmd->size);
- command = le16_to_cpu(cmd->command);
-
- lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
- command, le16_to_cpu(cmd->seqnum), cmdsize);
- lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);
-
- ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- if (ret) {
- pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret);
- /* Let the timer kick in and retry, and potentially reset
- the whole thing if the condition persists */
- timeo = HZ;
- }
-
- /* Setup the timer after transmit command */
- mod_timer(&priv->command_timer, jiffies + timeo);
-
- lbtf_deb_leave(LBTF_DEB_HOST);
-}
-
-/**
- * This function inserts command node to cmdfreeq
- * after cleans it. Requires priv->driver_lock held.
- */
-static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv,
- struct cmd_ctrl_node *cmdnode)
-{
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- if (!cmdnode)
- goto cl_ins_out;
-
- cmdnode->callback = NULL;
- cmdnode->callback_arg = 0;
-
- memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE);
-
- list_add_tail(&cmdnode->list, &priv->cmdfreeq);
-
-cl_ins_out:
- lbtf_deb_leave(LBTF_DEB_HOST);
-}
-
-static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv,
- struct cmd_ctrl_node *ptempcmd)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->driver_lock, flags);
- __lbtf_cleanup_and_insert_cmd(priv, ptempcmd);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-}
-
-void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd,
- int result)
-{
- cmd->result = result;
- cmd->cmdwaitqwoken = 1;
- wake_up_interruptible(&cmd->cmdwait_q);
-
- if (!cmd->callback)
- __lbtf_cleanup_and_insert_cmd(priv, cmd);
- priv->cur_cmd = NULL;
-}
-
-int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv)
-{
- struct cmd_ds_mac_multicast_addr cmd;
-
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
-
- cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr);
-
- lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs);
-
- memcpy(cmd.maclist, priv->multicastlist,
- priv->nr_of_multicastmacaddr * ETH_ALEN);
-
- lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd));
-
- lbtf_deb_leave(LBTF_DEB_CMD);
- return 0;
-}
-
-void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode)
-{
- struct cmd_ds_set_mode cmd;
- lbtf_deb_enter(LBTF_DEB_WEXT);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.mode = cpu_to_le16(mode);
- lbtf_deb_wext("Switching to mode: 0x%x\n", mode);
- lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd));
-
- lbtf_deb_leave(LBTF_DEB_WEXT);
-}
-
-void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid)
-{
- struct cmd_ds_set_bssid cmd;
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.activate = activate ? 1 : 0;
- if (activate)
- memcpy(cmd.bssid, bssid, ETH_ALEN);
-
- lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd));
- lbtf_deb_leave(LBTF_DEB_CMD);
-}
-
-int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr)
-{
- struct cmd_ds_802_11_mac_address cmd;
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
-
- memcpy(cmd.macadd, mac_addr, ETH_ALEN);
-
- lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd));
- lbtf_deb_leave(LBTF_DEB_CMD);
- return 0;
-}
-
-int lbtf_set_radio_control(struct lbtf_private *priv)
-{
- int ret = 0;
- struct cmd_ds_802_11_radio_control cmd;
-
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(CMD_ACT_SET);
-
- switch (priv->preamble) {
- case CMD_TYPE_SHORT_PREAMBLE:
- cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE);
- break;
-
- case CMD_TYPE_LONG_PREAMBLE:
- cmd.control = cpu_to_le16(SET_LONG_PREAMBLE);
- break;
-
- case CMD_TYPE_AUTO_PREAMBLE:
- default:
- cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE);
- break;
- }
-
- if (priv->radioon)
- cmd.control |= cpu_to_le16(TURN_ON_RF);
- else
- cmd.control &= cpu_to_le16(~TURN_ON_RF);
-
- lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon,
- priv->preamble);
-
- ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd);
-
- lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-void lbtf_set_mac_control(struct lbtf_private *priv)
-{
- struct cmd_ds_mac_control cmd;
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(priv->mac_control);
- cmd.reserved = 0;
-
- lbtf_cmd_async(priv, CMD_MAC_CONTROL,
- &cmd.hdr, sizeof(cmd));
-
- lbtf_deb_leave(LBTF_DEB_CMD);
-}
-
-/**
- * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue
- *
- * @priv A pointer to struct lbtf_private structure
- *
- * Returns: 0 on success.
- */
-int lbtf_allocate_cmd_buffer(struct lbtf_private *priv)
-{
- int ret = 0;
- u32 bufsize;
- u32 i;
- struct cmd_ctrl_node *cmdarray;
-
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- /* Allocate and initialize the command array */
- bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS;
- cmdarray = kzalloc(bufsize, GFP_KERNEL);
- if (!cmdarray) {
- lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
- ret = -1;
- goto done;
- }
- priv->cmd_array = cmdarray;
-
- /* Allocate and initialize each command buffer in the command array */
- for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
- cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL);
- if (!cmdarray[i].cmdbuf) {
- lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
- ret = -1;
- goto done;
- }
- }
-
- for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
- init_waitqueue_head(&cmdarray[i].cmdwait_q);
- lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]);
- }
-
- ret = 0;
-
-done:
- lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret);
- return ret;
-}
-
-/**
- * lbtf_free_cmd_buffer - Frees the cmd buffer.
- *
- * @priv A pointer to struct lbtf_private structure
- *
- * Returns: 0
- */
-int lbtf_free_cmd_buffer(struct lbtf_private *priv)
-{
- struct cmd_ctrl_node *cmdarray;
- unsigned int i;
-
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- /* need to check if cmd array is allocated or not */
- if (priv->cmd_array == NULL) {
- lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
- goto done;
- }
-
- cmdarray = priv->cmd_array;
-
- /* Release shared memory buffers */
- for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
- kfree(cmdarray[i].cmdbuf);
- cmdarray[i].cmdbuf = NULL;
- }
-
- /* Release cmd_ctrl_node */
- kfree(priv->cmd_array);
- priv->cmd_array = NULL;
-
-done:
- lbtf_deb_leave(LBTF_DEB_HOST);
- return 0;
-}
-
-/**
- * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue.
- *
- * @priv A pointer to struct lbtf_private structure
- *
- * Returns: pointer to a struct cmd_ctrl_node or NULL if none available.
- */
-static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv)
-{
- struct cmd_ctrl_node *tempnode;
- unsigned long flags;
-
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- if (!priv)
- return NULL;
-
- spin_lock_irqsave(&priv->driver_lock, flags);
-
- if (!list_empty(&priv->cmdfreeq)) {
- tempnode = list_first_entry(&priv->cmdfreeq,
- struct cmd_ctrl_node, list);
- list_del(&tempnode->list);
- } else {
- lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
- tempnode = NULL;
- }
-
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- lbtf_deb_leave(LBTF_DEB_HOST);
- return tempnode;
-}
-
-/**
- * lbtf_execute_next_command: execute next command in cmd pending queue.
- *
- * @priv A pointer to struct lbtf_private structure
- *
- * Returns: 0 on success.
- */
-int lbtf_execute_next_command(struct lbtf_private *priv)
-{
- struct cmd_ctrl_node *cmdnode = NULL;
- struct cmd_header *cmd;
- unsigned long flags;
- int ret = 0;
-
- /* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the
- * only caller to us is lbtf_thread() and we get even when a
- * data packet is received */
- lbtf_deb_enter(LBTF_DEB_THREAD);
-
- spin_lock_irqsave(&priv->driver_lock, flags);
-
- if (priv->cur_cmd) {
- pr_alert("EXEC_NEXT_CMD: already processing command!\n");
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- ret = -1;
- goto done;
- }
-
- if (!list_empty(&priv->cmdpendingq)) {
- cmdnode = list_first_entry(&priv->cmdpendingq,
- struct cmd_ctrl_node, list);
- }
-
- if (cmdnode) {
- cmd = cmdnode->cmdbuf;
-
- list_del(&cmdnode->list);
- lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
- le16_to_cpu(cmd->command));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- lbtf_submit_command(priv, cmdnode);
- } else
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- ret = 0;
-done:
- lbtf_deb_leave(LBTF_DEB_THREAD);
- return ret;
-}
-
-static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv,
- uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
- int (*callback)(struct lbtf_private *, unsigned long,
- struct cmd_header *),
- unsigned long callback_arg)
-{
- struct cmd_ctrl_node *cmdnode;
-
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- if (priv->surpriseremoved) {
- lbtf_deb_host("PREP_CMD: card removed\n");
- cmdnode = ERR_PTR(-ENOENT);
- goto done;
- }
-
- cmdnode = lbtf_get_cmd_ctrl_node(priv);
- if (cmdnode == NULL) {
- lbtf_deb_host("PREP_CMD: cmdnode is NULL\n");
-
- /* Wake up main thread to execute next command */
- queue_work(lbtf_wq, &priv->cmd_work);
- cmdnode = ERR_PTR(-ENOBUFS);
- goto done;
- }
-
- cmdnode->callback = callback;
- cmdnode->callback_arg = callback_arg;
-
- /* Copy the incoming command to the buffer */
- memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size);
-
- /* Set sequence number, clean result, move to buffer */
- priv->seqnum++;
- cmdnode->cmdbuf->command = cpu_to_le16(command);
- cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size);
- cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum);
- cmdnode->cmdbuf->result = 0;
-
- lbtf_deb_host("PREP_CMD: command 0x%04x\n", command);
-
- cmdnode->cmdwaitqwoken = 0;
- lbtf_queue_cmd(priv, cmdnode);
- queue_work(lbtf_wq, &priv->cmd_work);
-
- done:
- lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode);
- return cmdnode;
-}
-
-void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command,
- struct cmd_header *in_cmd, int in_cmd_size)
-{
- lbtf_deb_enter(LBTF_DEB_CMD);
- __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0);
- lbtf_deb_leave(LBTF_DEB_CMD);
-}
-
-int __lbtf_cmd(struct lbtf_private *priv, uint16_t command,
- struct cmd_header *in_cmd, int in_cmd_size,
- int (*callback)(struct lbtf_private *,
- unsigned long, struct cmd_header *),
- unsigned long callback_arg)
-{
- struct cmd_ctrl_node *cmdnode;
- unsigned long flags;
- int ret = 0;
-
- lbtf_deb_enter(LBTF_DEB_HOST);
-
- cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size,
- callback, callback_arg);
- if (IS_ERR(cmdnode)) {
- ret = PTR_ERR(cmdnode);
- goto done;
- }
-
- might_sleep();
- ret = wait_event_interruptible(cmdnode->cmdwait_q,
- cmdnode->cmdwaitqwoken);
- if (ret) {
- pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n",
- command, ret);
- goto done;
- }
-
- spin_lock_irqsave(&priv->driver_lock, flags);
- ret = cmdnode->result;
- if (ret)
- pr_info("PREP_CMD: command 0x%04x failed: %d\n",
- command, ret);
-
- __lbtf_cleanup_and_insert_cmd(priv, cmdnode);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
-done:
- lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret);
- return ret;
-}
-EXPORT_SYMBOL_GPL(__lbtf_cmd);
-
-/* Call holding driver_lock */
-void lbtf_cmd_response_rx(struct lbtf_private *priv)
-{
- priv->cmd_response_rxed = 1;
- queue_work(lbtf_wq, &priv->cmd_work);
-}
-EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx);
-
-int lbtf_process_rx_command(struct lbtf_private *priv)
-{
- uint16_t respcmd, curcmd;
- struct cmd_header *resp;
- int ret = 0;
- unsigned long flags;
- uint16_t result;
-
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- mutex_lock(&priv->lock);
- spin_lock_irqsave(&priv->driver_lock, flags);
-
- if (!priv->cur_cmd) {
- ret = -1;
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- goto done;
- }
-
- resp = (void *)priv->cmd_resp_buff;
- curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
- respcmd = le16_to_cpu(resp->command);
- result = le16_to_cpu(resp->result);
-
- if (net_ratelimit())
- pr_info("libertastf: cmd response 0x%04x, seq %d, size %d\n",
- respcmd, le16_to_cpu(resp->seqnum),
- le16_to_cpu(resp->size));
-
- if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- ret = -1;
- goto done;
- }
- if (respcmd != CMD_RET(curcmd)) {
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- ret = -1;
- goto done;
- }
-
- if (resp->result == cpu_to_le16(0x0004)) {
- /* 0x0004 means -EAGAIN. Drop the response, let it time out
- and be resubmitted */
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- ret = -1;
- goto done;
- }
-
- /* Now we got response from FW, cancel the command timer */
- del_timer(&priv->command_timer);
- priv->cmd_timed_out = 0;
- if (priv->nr_retries)
- priv->nr_retries = 0;
-
- /* If the command is not successful, cleanup and return failure */
- if ((result != 0 || !(respcmd & 0x8000))) {
- /*
- * Handling errors here
- */
- switch (respcmd) {
- case CMD_RET(CMD_GET_HW_SPEC):
- case CMD_RET(CMD_802_11_RESET):
- pr_info("libertastf: reset failed\n");
- break;
-
- }
- lbtf_complete_command(priv, priv->cur_cmd, result);
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- ret = -1;
- goto done;
- }
-
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
- if (priv->cur_cmd && priv->cur_cmd->callback) {
- ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
- resp);
- }
- spin_lock_irqsave(&priv->driver_lock, flags);
-
- if (priv->cur_cmd) {
- /* Clean up and Put current command back to cmdfreeq */
- lbtf_complete_command(priv, priv->cur_cmd, result);
- }
- spin_unlock_irqrestore(&priv->driver_lock, flags);
-
-done:
- mutex_unlock(&priv->lock);
- lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
- return ret;
-}
+++ /dev/null
-/**
- * This header file contains global constant/enum definitions,
- * global variable declaration.
- */
-#ifndef _LBS_DEB_DEFS_H_
-#define _LBS_DEB_DEFS_H_
-
-#ifndef DRV_NAME
-#define DRV_NAME "libertas_tf"
-#endif
-
-#include <linux/spinlock.h>
-
-#ifdef CONFIG_LIBERTAS_THINFIRM_DEBUG
-#define DEBUG
-#define PROC_DEBUG
-#endif
-
-#define LBTF_DEB_ENTER 0x00000001
-#define LBTF_DEB_LEAVE 0x00000002
-#define LBTF_DEB_MAIN 0x00000004
-#define LBTF_DEB_NET 0x00000008
-#define LBTF_DEB_MESH 0x00000010
-#define LBTF_DEB_WEXT 0x00000020
-#define LBTF_DEB_IOCTL 0x00000040
-#define LBTF_DEB_SCAN 0x00000080
-#define LBTF_DEB_ASSOC 0x00000100
-#define LBTF_DEB_JOIN 0x00000200
-#define LBTF_DEB_11D 0x00000400
-#define LBTF_DEB_DEBUGFS 0x00000800
-#define LBTF_DEB_ETHTOOL 0x00001000
-#define LBTF_DEB_HOST 0x00002000
-#define LBTF_DEB_CMD 0x00004000
-#define LBTF_DEB_RX 0x00008000
-#define LBTF_DEB_TX 0x00010000
-#define LBTF_DEB_USB 0x00020000
-#define LBTF_DEB_CS 0x00040000
-#define LBTF_DEB_FW 0x00080000
-#define LBTF_DEB_THREAD 0x00100000
-#define LBTF_DEB_HEX 0x00200000
-#define LBTF_DEB_SDIO 0x00400000
-#define LBTF_DEB_MACOPS 0x00800000
-
-extern unsigned int lbtf_debug;
-
-
-#ifdef DEBUG
-#define LBTF_DEB_LL(grp, grpnam, fmt, args...) \
-do { if ((lbtf_debug & (grp)) == (grp)) \
- printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \
- in_interrupt() ? " (INT)" : "", ## args); } while (0)
-#else
-#define LBTF_DEB_LL(grp, grpnam, fmt, args...) do {} while (0)
-#endif
-
-#define lbtf_deb_enter(grp) \
- LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s()\n", __func__);
-#define lbtf_deb_enter_args(grp, fmt, args...) \
- LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args);
-#define lbtf_deb_leave(grp) \
- LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s()\n", __func__);
-#define lbtf_deb_leave_args(grp, fmt, args...) \
- LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s(), " fmt "\n", \
- __func__, ##args);
-#define lbtf_deb_main(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MAIN, " main", fmt, ##args)
-#define lbtf_deb_net(fmt, args...) LBTF_DEB_LL(LBTF_DEB_NET, " net", fmt, ##args)
-#define lbtf_deb_mesh(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MESH, " mesh", fmt, ##args)
-#define lbtf_deb_wext(fmt, args...) LBTF_DEB_LL(LBTF_DEB_WEXT, " wext", fmt, ##args)
-#define lbtf_deb_ioctl(fmt, args...) LBTF_DEB_LL(LBTF_DEB_IOCTL, " ioctl", fmt, ##args)
-#define lbtf_deb_scan(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SCAN, " scan", fmt, ##args)
-#define lbtf_deb_assoc(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ASSOC, " assoc", fmt, ##args)
-#define lbtf_deb_join(fmt, args...) LBTF_DEB_LL(LBTF_DEB_JOIN, " join", fmt, ##args)
-#define lbtf_deb_11d(fmt, args...) LBTF_DEB_LL(LBTF_DEB_11D, " 11d", fmt, ##args)
-#define lbtf_deb_debugfs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_DEBUGFS, " debugfs", fmt, ##args)
-#define lbtf_deb_ethtool(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ETHTOOL, " ethtool", fmt, ##args)
-#define lbtf_deb_host(fmt, args...) LBTF_DEB_LL(LBTF_DEB_HOST, " host", fmt, ##args)
-#define lbtf_deb_cmd(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CMD, " cmd", fmt, ##args)
-#define lbtf_deb_rx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_RX, " rx", fmt, ##args)
-#define lbtf_deb_tx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_TX, " tx", fmt, ##args)
-#define lbtf_deb_fw(fmt, args...) LBTF_DEB_LL(LBTF_DEB_FW, " fw", fmt, ##args)
-#define lbtf_deb_usb(fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usb", fmt, ##args)
-#define lbtf_deb_usbd(dev, fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args)
-#define lbtf_deb_cs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CS, " cs", fmt, ##args)
-#define lbtf_deb_thread(fmt, args...) LBTF_DEB_LL(LBTF_DEB_THREAD, " thread", fmt, ##args)
-#define lbtf_deb_sdio(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SDIO, " thread", fmt, ##args)
-#define lbtf_deb_macops(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MACOPS, " thread", fmt, ##args)
-
-#ifdef DEBUG
-static inline void lbtf_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len)
-{
- char newprompt[32];
-
- if (len &&
- (lbtf_debug & LBTF_DEB_HEX) &&
- (lbtf_debug & grp)) {
- snprintf(newprompt, sizeof(newprompt), DRV_NAME " %s: ", prompt);
- print_hex_dump_bytes(prompt, DUMP_PREFIX_NONE, buf, len);
- }
-}
-#else
-#define lbtf_deb_hex(grp, prompt, buf, len) do {} while (0)
-#endif
-
-#endif
+++ /dev/null
-/*
- * 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/module.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)
- 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.bDeviceClass,
- udev->descriptor.bDeviceSubClass,
- udev->descriptor.bDeviceProtocol);
-
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (usb_endpoint_is_bulk_in(endpoint)) {
- cardp->ep_in_size =
- le16_to_cpu(endpoint->wMaxPacketSize);
- cardp->ep_in = usb_endpoint_num(endpoint);
-
- lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n",
- cardp->ep_in);
- lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n",
- cardp->ep_in_size);
- } else if (usb_endpoint_is_bulk_out(endpoint)) {
- cardp->ep_out_size =
- le16_to_cpu(endpoint->wMaxPacketSize);
- cardp->ep_out = usb_endpoint_num(endpoint);
-
- lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n",
- cardp->ep_out);
- lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n",
- cardp->ep_out_size);
- }
- }
- if (!cardp->ep_out_size || !cardp->ep_in_size) {
- lbtf_deb_usbd(&udev->dev, "Endpoints not found\n");
- /* Endpoints not found */
- goto dealloc;
- }
-
- cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!cardp->rx_urb) {
- lbtf_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
- goto dealloc;
- }
-
- cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!cardp->tx_urb) {
- lbtf_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
- goto dealloc;
- }
-
- cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!cardp->cmd_urb) {
- lbtf_deb_usbd(&udev->dev, "Cmd URB allocation failed\n");
- goto dealloc;
- }
-
- cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE,
- GFP_KERNEL);
- if (!cardp->ep_out_buf) {
- lbtf_deb_usbd(&udev->dev, "Could not allocate buffer\n");
- goto dealloc;
- }
-
- priv = lbtf_add_card(cardp, &udev->dev);
- if (!priv)
- goto dealloc;
-
- cardp->priv = priv;
-
- priv->hw_host_to_card = if_usb_host_to_card;
- priv->hw_prog_firmware = if_usb_prog_firmware;
- priv->hw_reset_device = if_usb_reset_device;
- cardp->boot2_version = udev->descriptor.bcdDevice;
-
- usb_get_dev(udev);
- usb_set_intfdata(intf, cardp);
-
- return 0;
-
-dealloc:
- if_usb_free(cardp);
-error:
-lbtf_deb_leave(LBTF_DEB_MAIN);
- return -ENOMEM;
-}
-
-/**
- * if_usb_disconnect - free resource and cleanup
- *
- * @intf USB interface structure
- */
-static void if_usb_disconnect(struct usb_interface *intf)
-{
- struct if_usb_card *cardp = usb_get_intfdata(intf);
- struct lbtf_private *priv = cardp->priv;
-
- lbtf_deb_enter(LBTF_DEB_MAIN);
-
- if_usb_reset_device(cardp);
-
- if (priv)
- lbtf_remove_card(priv);
-
- /* Unlink and free urb */
- if_usb_free(cardp);
-
- usb_set_intfdata(intf, NULL);
- usb_put_dev(interface_to_usbdev(intf));
-
- lbtf_deb_leave(LBTF_DEB_MAIN);
-}
-
-/**
- * if_usb_send_fw_pkt - This function downloads the FW
- *
- * @priv pointer to struct lbtf_private
- *
- * Returns: 0
- */
-static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
-{
- struct fwdata *fwdata = cardp->ep_out_buf;
- u8 *firmware = (u8 *) cardp->fw->data;
-
- lbtf_deb_enter(LBTF_DEB_FW);
-
- /* If we got a CRC failure on the last block, back
- up and retry it */
- if (!cardp->CRC_OK) {
- cardp->totalbytes = cardp->fwlastblksent;
- cardp->fwseqnum--;
- }
-
- lbtf_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
- cardp->totalbytes);
-
- /* struct fwdata (which we sent to the card) has an
- extra __le32 field in between the header and the data,
- which is not in the struct fwheader in the actual
- firmware binary. Insert the seqnum in the middle... */
- memcpy(&fwdata->hdr, &firmware[cardp->totalbytes],
- sizeof(struct fwheader));
-
- cardp->fwlastblksent = cardp->totalbytes;
- cardp->totalbytes += sizeof(struct fwheader);
-
- memcpy(fwdata->data, &firmware[cardp->totalbytes],
- le32_to_cpu(fwdata->hdr.datalength));
-
- lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
- le32_to_cpu(fwdata->hdr.datalength));
-
- fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
- cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
-
- usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
- le32_to_cpu(fwdata->hdr.datalength), 0);
-
- if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
- lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
- lbtf_deb_usb2(&cardp->udev->dev,
- "seqnum = %d totalbytes = %d\n",
- cardp->fwseqnum, cardp->totalbytes);
- } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
- lbtf_deb_usb2(&cardp->udev->dev,
- "Host has finished FW downloading\n");
- lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
-
- /* Host has finished FW downloading
- * Donwloading FW JUMP BLOCK
- */
- cardp->fwfinalblk = 1;
- }
-
- lbtf_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
- cardp->totalbytes);
-
- lbtf_deb_leave(LBTF_DEB_FW);
- return 0;
-}
-
-static int if_usb_reset_device(struct if_usb_card *cardp)
-{
- struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4;
- int ret;
-
- lbtf_deb_enter(LBTF_DEB_USB);
-
- *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
-
- cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET);
- cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset));
- cmd->hdr.result = cpu_to_le16(0);
- cmd->hdr.seqnum = cpu_to_le16(0x5a5a);
- cmd->action = cpu_to_le16(CMD_ACT_HALT);
- usb_tx_block(cardp, cardp->ep_out_buf,
- 4 + sizeof(struct cmd_ds_802_11_reset), 0);
-
- msleep(100);
- ret = usb_reset_device(cardp->udev);
- msleep(100);
-
- lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
-
- return ret;
-}
-
-/**
- * usb_tx_block - transfer data to the device
- *
- * @priv pointer to struct lbtf_private
- * @payload pointer to payload data
- * @nb data length
- * @data non-zero for data, zero for commands
- *
- * Returns: 0 on success, nonzero otherwise.
- */
-static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
- uint16_t nb, u8 data)
-{
- int ret = -1;
- struct urb *urb;
-
- lbtf_deb_enter(LBTF_DEB_USB);
- /* check if device is removed */
- if (cardp->priv->surpriseremoved) {
- lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n");
- goto tx_ret;
- }
-
- if (data)
- urb = cardp->tx_urb;
- else
- urb = cardp->cmd_urb;
-
- usb_fill_bulk_urb(urb, cardp->udev,
- usb_sndbulkpipe(cardp->udev,
- cardp->ep_out),
- payload, nb, if_usb_write_bulk_callback, cardp);
-
- urb->transfer_flags |= URB_ZERO_PACKET;
-
- if (usb_submit_urb(urb, GFP_ATOMIC)) {
- lbtf_deb_usbd(&cardp->udev->dev,
- "usb_submit_urb failed: %d\n", ret);
- goto tx_ret;
- }
-
- lbtf_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
-
- ret = 0;
-
-tx_ret:
- lbtf_deb_leave(LBTF_DEB_USB);
- return ret;
-}
-
-static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
- void (*callbackfn)(struct urb *urb))
-{
- struct sk_buff *skb;
- int ret = -1;
-
- lbtf_deb_enter(LBTF_DEB_USB);
-
- skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
- if (!skb) {
- pr_err("No free skb\n");
- lbtf_deb_leave(LBTF_DEB_USB);
- return -1;
- }
-
- cardp->rx_skb = skb;
-
- /* Fill the receive configuration URB and initialise the Rx call back */
- usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
- usb_rcvbulkpipe(cardp->udev, cardp->ep_in),
- skb_tail_pointer(skb),
- MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp);
-
- cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
-
- lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n",
- cardp->rx_urb);
- ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC);
- if (ret) {
- lbtf_deb_usbd(&cardp->udev->dev,
- "Submit Rx URB failed: %d\n", ret);
- kfree_skb(skb);
- cardp->rx_skb = NULL;
- lbtf_deb_leave(LBTF_DEB_USB);
- return -1;
- } else {
- lbtf_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n");
- lbtf_deb_leave(LBTF_DEB_USB);
- return 0;
- }
-}
-
-static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp)
-{
- return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload);
-}
-
-static int if_usb_submit_rx_urb(struct if_usb_card *cardp)
-{
- return __if_usb_submit_rx_urb(cardp, &if_usb_receive);
-}
-
-static void if_usb_receive_fwload(struct urb *urb)
-{
- struct if_usb_card *cardp = urb->context;
- struct sk_buff *skb = cardp->rx_skb;
- struct fwsyncheader *syncfwheader;
- struct bootcmdresp bcmdresp;
-
- lbtf_deb_enter(LBTF_DEB_USB);
- if (urb->status) {
- lbtf_deb_usbd(&cardp->udev->dev,
- "URB status is failed during fw load\n");
- kfree_skb(skb);
- lbtf_deb_leave(LBTF_DEB_USB);
- return;
- }
-
- if (cardp->fwdnldover) {
- __le32 *tmp = (__le32 *)(skb->data);
-
- if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
- tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
- /* Firmware ready event received */
- pr_info("Firmware ready event received\n");
- wake_up(&cardp->fw_wq);
- } else {
- lbtf_deb_usb("Waiting for confirmation; got %x %x\n",
- le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
- if_usb_submit_rx_urb_fwload(cardp);
- }
- kfree_skb(skb);
- lbtf_deb_leave(LBTF_DEB_USB);
- return;
- }
- if (cardp->bootcmdresp <= 0) {
- memcpy(&bcmdresp, skb->data, sizeof(bcmdresp));
-
- if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
- kfree_skb(skb);
- if_usb_submit_rx_urb_fwload(cardp);
- cardp->bootcmdresp = 1;
- /* Received valid boot command response */
- lbtf_deb_usbd(&cardp->udev->dev,
- "Received valid boot command response\n");
- lbtf_deb_leave(LBTF_DEB_USB);
- return;
- }
- if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
- if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
- bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
- bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
- if (!cardp->bootcmdresp)
- pr_info("Firmware already seems alive; resetting\n");
- cardp->bootcmdresp = -1;
- } else {
- pr_info("boot cmd response wrong magic number (0x%x)\n",
- le32_to_cpu(bcmdresp.magic));
- }
- } else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) {
- pr_info("boot cmd response cmd_tag error (%d)\n",
- bcmdresp.cmd);
- } else if (bcmdresp.result != BOOT_CMD_RESP_OK) {
- pr_info("boot cmd response result error (%d)\n",
- bcmdresp.result);
- } else {
- cardp->bootcmdresp = 1;
- lbtf_deb_usbd(&cardp->udev->dev,
- "Received valid boot command response\n");
- }
-
- kfree_skb(skb);
- if_usb_submit_rx_urb_fwload(cardp);
- lbtf_deb_leave(LBTF_DEB_USB);
- return;
- }
-
- syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader),
- GFP_ATOMIC);
- if (!syncfwheader) {
- lbtf_deb_usbd(&cardp->udev->dev,
- "Failure to allocate syncfwheader\n");
- kfree_skb(skb);
- lbtf_deb_leave(LBTF_DEB_USB);
- return;
- }
-
- if (!syncfwheader->cmd) {
- lbtf_deb_usb2(&cardp->udev->dev,
- "FW received Blk with correct CRC\n");
- lbtf_deb_usb2(&cardp->udev->dev,
- "FW received Blk seqnum = %d\n",
- le32_to_cpu(syncfwheader->seqnum));
- cardp->CRC_OK = 1;
- } else {
- lbtf_deb_usbd(&cardp->udev->dev,
- "FW received Blk with CRC error\n");
- cardp->CRC_OK = 0;
- }
-
- kfree_skb(skb);
-
- /* reschedule timer for 200ms hence */
- mod_timer(&cardp->fw_timeout, jiffies + (HZ/5));
-
- if (cardp->fwfinalblk) {
- cardp->fwdnldover = 1;
- goto exit;
- }
-
- if_usb_send_fw_pkt(cardp);
-
- exit:
- if_usb_submit_rx_urb_fwload(cardp);
-
- kfree(syncfwheader);
-
- lbtf_deb_leave(LBTF_DEB_USB);
-}
-
-#define MRVDRV_MIN_PKT_LEN 30
-
-static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
- struct if_usb_card *cardp,
- struct lbtf_private *priv)
-{
- if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN
- || recvlength < MRVDRV_MIN_PKT_LEN) {
- lbtf_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n");
- kfree_skb(skb);
- return;
- }
-
- skb_put(skb, recvlength);
- skb_pull(skb, MESSAGE_HEADER_LEN);
- lbtf_rx(priv, skb);
-}
-
-static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
- struct sk_buff *skb,
- struct if_usb_card *cardp,
- struct lbtf_private *priv)
-{
- if (recvlength > LBS_CMD_BUFFER_SIZE) {
- lbtf_deb_usbd(&cardp->udev->dev,
- "The receive buffer is too large\n");
- kfree_skb(skb);
- return;
- }
-
- BUG_ON(!in_interrupt());
-
- spin_lock(&priv->driver_lock);
- memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN,
- recvlength - MESSAGE_HEADER_LEN);
- kfree_skb(skb);
- lbtf_cmd_response_rx(priv);
- spin_unlock(&priv->driver_lock);
-}
-
-/**
- * if_usb_receive - read data received from the device.
- *
- * @urb pointer to struct urb
- */
-static void if_usb_receive(struct urb *urb)
-{
- struct if_usb_card *cardp = urb->context;
- struct sk_buff *skb = cardp->rx_skb;
- struct lbtf_private *priv = cardp->priv;
- int recvlength = urb->actual_length;
- uint8_t *recvbuff = NULL;
- uint32_t recvtype = 0;
- __le32 *pkt = (__le32 *) skb->data;
-
- lbtf_deb_enter(LBTF_DEB_USB);
-
- if (recvlength) {
- if (urb->status) {
- lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
- urb->status);
- kfree_skb(skb);
- goto setup_for_next;
- }
-
- recvbuff = skb->data;
- recvtype = le32_to_cpu(pkt[0]);
- lbtf_deb_usbd(&cardp->udev->dev,
- "Recv length = 0x%x, Recv type = 0x%X\n",
- recvlength, recvtype);
- } else if (urb->status) {
- kfree_skb(skb);
- lbtf_deb_leave(LBTF_DEB_USB);
- return;
- }
-
- switch (recvtype) {
- case CMD_TYPE_DATA:
- process_cmdtypedata(recvlength, skb, cardp, priv);
- break;
-
- case CMD_TYPE_REQUEST:
- process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
- break;
-
- case CMD_TYPE_INDICATION:
- {
- /* Event cause handling */
- u32 event_cause = le32_to_cpu(pkt[1]);
- lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n",
- event_cause);
-
- /* Icky undocumented magic special case */
- if (event_cause & 0xffff0000) {
- u16 tmp;
- u8 retrycnt;
- u8 failure;
-
- tmp = event_cause >> 16;
- retrycnt = tmp & 0x00ff;
- failure = (tmp & 0xff00) >> 8;
- lbtf_send_tx_feedback(priv, retrycnt, failure);
- } else if (event_cause == LBTF_EVENT_BCN_SENT)
- lbtf_bcn_sent(priv);
- else
- lbtf_deb_usbd(&cardp->udev->dev,
- "Unsupported notification %d received\n",
- event_cause);
- kfree_skb(skb);
- break;
- }
- default:
- lbtf_deb_usbd(&cardp->udev->dev,
- "libertastf: unknown command type 0x%X\n", recvtype);
- kfree_skb(skb);
- break;
- }
-
-setup_for_next:
- if_usb_submit_rx_urb(cardp);
- lbtf_deb_leave(LBTF_DEB_USB);
-}
-
-/**
- * if_usb_host_to_card - Download data to the device
- *
- * @priv pointer to struct lbtf_private structure
- * @type type of data
- * @buf pointer to data buffer
- * @len number of bytes
- *
- * Returns: 0 on success, nonzero otherwise
- */
-static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
- uint8_t *payload, uint16_t nb)
-{
- struct if_usb_card *cardp = priv->card;
- u8 data = 0;
-
- lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type);
- lbtf_deb_usbd(&cardp->udev->dev, "size after = %d\n", nb);
-
- if (type == MVMS_CMD) {
- *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
- } else {
- *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA);
- data = 1;
- }
-
- memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb);
-
- return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN,
- data);
-}
-
-/**
- * if_usb_issue_boot_command - Issue boot command to Boot2.
- *
- * @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM.
- *
- * Returns: 0
- */
-static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
-{
- struct bootcmd *bootcmd = cardp->ep_out_buf;
-
- /* Prepare command */
- bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER);
- bootcmd->cmd = ivalue;
- memset(bootcmd->pad, 0, sizeof(bootcmd->pad));
-
- /* Issue command */
- usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0);
-
- return 0;
-}
-
-
-/**
- * check_fwfile_format - Check the validity of Boot2/FW image.
- *
- * @data pointer to image
- * @totlen image length
- *
- * Returns: 0 if the image is valid, nonzero otherwise.
- */
-static int check_fwfile_format(const u8 *data, u32 totlen)
-{
- u32 bincmd, exit;
- u32 blksize, offset, len;
- int ret;
-
- ret = 1;
- exit = len = 0;
-
- do {
- struct fwheader *fwh = (void *) data;
-
- bincmd = le32_to_cpu(fwh->dnldcmd);
- blksize = le32_to_cpu(fwh->datalength);
- switch (bincmd) {
- case FW_HAS_DATA_TO_RECV:
- offset = sizeof(struct fwheader) + blksize;
- data += offset;
- len += offset;
- if (len >= totlen)
- exit = 1;
- break;
- case FW_HAS_LAST_BLOCK:
- exit = 1;
- ret = 0;
- break;
- default:
- exit = 1;
- break;
- }
- } while (!exit);
-
- if (ret)
- pr_err("firmware file format check FAIL\n");
- else
- lbtf_deb_fw("firmware file format check PASS\n");
-
- return ret;
-}
-
-
-static int if_usb_prog_firmware(struct if_usb_card *cardp)
-{
- int i = 0;
- static int reset_count = 10;
- int ret = 0;
-
- lbtf_deb_enter(LBTF_DEB_USB);
-
- kernel_param_lock(THIS_MODULE);
- ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev);
- if (ret < 0) {
- pr_err("request_firmware() failed with %#x\n", ret);
- pr_err("firmware %s not found\n", lbtf_fw_name);
- kernel_param_unlock(THIS_MODULE);
- goto done;
- }
- kernel_param_unlock(THIS_MODULE);
-
- if (check_fwfile_format(cardp->fw->data, cardp->fw->size))
- goto release_fw;
-
-restart:
- if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
- lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
- ret = -1;
- goto release_fw;
- }
-
- cardp->bootcmdresp = 0;
- do {
- int j = 0;
- i++;
- /* Issue Boot command = 1, Boot from Download-FW */
- if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
- /* wait for command response */
- do {
- j++;
- msleep_interruptible(100);
- } while (cardp->bootcmdresp == 0 && j < 10);
- } while (cardp->bootcmdresp == 0 && i < 5);
-
- if (cardp->bootcmdresp <= 0) {
- if (--reset_count >= 0) {
- if_usb_reset_device(cardp);
- goto restart;
- }
- return -1;
- }
-
- i = 0;
-
- cardp->totalbytes = 0;
- cardp->fwlastblksent = 0;
- cardp->CRC_OK = 1;
- cardp->fwdnldover = 0;
- cardp->fwseqnum = -1;
- cardp->totalbytes = 0;
- cardp->fwfinalblk = 0;
-
- /* Send the first firmware packet... */
- if_usb_send_fw_pkt(cardp);
-
- /* ... and wait for the process to complete */
- wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved ||
- cardp->fwdnldover);
-
- del_timer_sync(&cardp->fw_timeout);
- usb_kill_urb(cardp->rx_urb);
-
- if (!cardp->fwdnldover) {
- pr_info("failed to load fw, resetting device!\n");
- if (--reset_count >= 0) {
- if_usb_reset_device(cardp);
- goto restart;
- }
-
- pr_info("FW download failure, time = %d ms\n", i * 100);
- ret = -1;
- goto release_fw;
- }
-
- cardp->priv->fw_ready = 1;
-
- release_fw:
- release_firmware(cardp->fw);
- cardp->fw = NULL;
-
- if_usb_setup_firmware(cardp->priv);
-
- done:
- lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
- return ret;
-}
-
-
-#define if_usb_suspend NULL
-#define if_usb_resume NULL
-
-static struct usb_driver if_usb_driver = {
- .name = DRV_NAME,
- .probe = if_usb_probe,
- .disconnect = if_usb_disconnect,
- .id_table = if_usb_table,
- .suspend = if_usb_suspend,
- .resume = if_usb_resume,
- .disable_hub_initiated_lpm = 1,
-};
-
-module_usb_driver(if_usb_driver);
-
-MODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver");
-MODULE_AUTHOR("Cozybit Inc.");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * 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.
- */
-#include <linux/wait.h>
-#include <linux/timer.h>
-
-struct lbtf_private;
-
-/**
- * This file contains definition for USB interface.
- */
-#define CMD_TYPE_REQUEST 0xF00DFACE
-#define CMD_TYPE_DATA 0xBEADC0DE
-#define CMD_TYPE_INDICATION 0xBEEFFACE
-
-#define BOOT_CMD_FW_BY_USB 0x01
-#define BOOT_CMD_FW_IN_EEPROM 0x02
-#define BOOT_CMD_UPDATE_BOOT2 0x03
-#define BOOT_CMD_UPDATE_FW 0x04
-#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */
-
-struct bootcmd {
- __le32 magic;
- uint8_t cmd;
- uint8_t pad[11];
-};
-
-#define BOOT_CMD_RESP_OK 0x0001
-#define BOOT_CMD_RESP_FAIL 0x0000
-
-struct bootcmdresp {
- __le32 magic;
- uint8_t cmd;
- uint8_t result;
- uint8_t pad[2];
-};
-
-/** USB card description structure*/
-struct if_usb_card {
- struct usb_device *udev;
- struct urb *rx_urb, *tx_urb, *cmd_urb;
- struct lbtf_private *priv;
-
- struct sk_buff *rx_skb;
-
- uint8_t ep_in;
- uint8_t ep_out;
-
- int8_t bootcmdresp;
-
- int ep_in_size;
-
- void *ep_out_buf;
- int ep_out_size;
-
- const struct firmware *fw;
- struct timer_list fw_timeout;
- wait_queue_head_t fw_wq;
- uint32_t fwseqnum;
- uint32_t totalbytes;
- uint32_t fwlastblksent;
- uint8_t CRC_OK;
- uint8_t fwdnldover;
- uint8_t fwfinalblk;
-
- __le16 boot2_version;
-};
-
-/** fwheader */
-struct fwheader {
- __le32 dnldcmd;
- __le32 baseaddr;
- __le32 datalength;
- __le32 CRC;
-};
-
-#define FW_MAX_DATA_BLK_SIZE 600
-/** FWData */
-struct fwdata {
- struct fwheader hdr;
- __le32 seqnum;
- uint8_t data[0];
-};
-
-/** fwsyncheader */
-struct fwsyncheader {
- __le32 cmd;
- __le32 seqnum;
-};
-
-#define FW_HAS_DATA_TO_RECV 0x00000001
-#define FW_HAS_LAST_BLOCK 0x00000004
+++ /dev/null
-/*
- * Copyright (C) 2008, cozybit Inc.
- * Copyright (C) 2007, Red Hat, 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.
- */
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/kthread.h>
-#include <net/mac80211.h>
-
-#include "deb_defs.h"
-
-#ifndef DRV_NAME
-#define DRV_NAME "libertas_tf"
-#endif
-
-#define MRVL_DEFAULT_RETRIES 9
-#define MRVL_PER_PACKET_RATE 0x10
-#define MRVL_MAX_BCN_SIZE 440
-#define CMD_OPTION_WAITFORRSP 0x0002
-
-/* Return command are almost always the same as the host command, but with
- * bit 15 set high. There are a few exceptions, though...
- */
-#define CMD_RET(cmd) (0x8000 | cmd)
-
-/* Command codes */
-#define CMD_GET_HW_SPEC 0x0003
-#define CMD_802_11_RESET 0x0005
-#define CMD_MAC_MULTICAST_ADR 0x0010
-#define CMD_802_11_RADIO_CONTROL 0x001c
-#define CMD_802_11_RF_CHANNEL 0x001d
-#define CMD_802_11_RF_TX_POWER 0x001e
-#define CMD_MAC_CONTROL 0x0028
-#define CMD_802_11_MAC_ADDRESS 0x004d
-#define CMD_SET_BOOT2_VER 0x00a5
-#define CMD_802_11_BEACON_CTRL 0x00b0
-#define CMD_802_11_BEACON_SET 0x00cb
-#define CMD_802_11_SET_MODE 0x00cc
-#define CMD_802_11_SET_BSSID 0x00cd
-
-#define CMD_ACT_GET 0x0000
-#define CMD_ACT_SET 0x0001
-
-/* Define action or option for CMD_802_11_RESET */
-#define CMD_ACT_HALT 0x0003
-
-/* Define action or option for CMD_MAC_CONTROL */
-#define CMD_ACT_MAC_RX_ON 0x0001
-#define CMD_ACT_MAC_TX_ON 0x0002
-#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020
-#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040
-#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080
-#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100
-
-/* Define action or option for CMD_802_11_RADIO_CONTROL */
-#define CMD_TYPE_AUTO_PREAMBLE 0x0001
-#define CMD_TYPE_SHORT_PREAMBLE 0x0002
-#define CMD_TYPE_LONG_PREAMBLE 0x0003
-
-#define TURN_ON_RF 0x01
-#define RADIO_ON 0x01
-#define RADIO_OFF 0x00
-
-#define SET_AUTO_PREAMBLE 0x05
-#define SET_SHORT_PREAMBLE 0x03
-#define SET_LONG_PREAMBLE 0x01
-
-/* Define action or option for CMD_802_11_RF_CHANNEL */
-#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00
-#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01
-
-/* Codes for CMD_802_11_SET_MODE */
-enum lbtf_mode {
- LBTF_PASSIVE_MODE,
- LBTF_STA_MODE,
- LBTF_AP_MODE,
-};
-
-/** Card Event definition */
-#define MACREG_INT_CODE_FIRMWARE_READY 48
-/** Buffer Constants */
-
-/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical
-* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas
-* driver has more local TxPDs. Each TxPD on the host memory is associated
-* with a Tx control node. The driver maintains 8 RxPD descriptors for
-* station firmware to store Rx packet information.
-*
-* Current version of MAC has a 32x6 multicast address buffer.
-*
-* 802.11b can have up to 14 channels, the driver keeps the
-* BSSID(MAC address) of each APs or Ad hoc stations it has sensed.
-*/
-
-#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32
-#define LBS_NUM_CMD_BUFFERS 10
-#define LBS_CMD_BUFFER_SIZE (2 * 1024)
-#define MRVDRV_MAX_CHANNEL_SIZE 14
-#define MRVDRV_SNAP_HEADER_LEN 8
-
-#define LBS_UPLD_SIZE 2312
-#define DEV_NAME_LEN 32
-
-/** Misc constants */
-/* This section defines 802.11 specific contants */
-
-#define MRVDRV_MAX_REGION_CODE 6
-/**
- * the table to keep region code
- */
-#define LBTF_REGDOMAIN_US 0x10
-#define LBTF_REGDOMAIN_CA 0x20
-#define LBTF_REGDOMAIN_EU 0x30
-#define LBTF_REGDOMAIN_SP 0x31
-#define LBTF_REGDOMAIN_FR 0x32
-#define LBTF_REGDOMAIN_JP 0x40
-
-#define SBI_EVENT_CAUSE_SHIFT 3
-
-/** RxPD status */
-
-#define MRVDRV_RXPD_STATUS_OK 0x0001
-
-
-/* This is for firmware specific length */
-#define EXTRA_LEN 36
-
-#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \
- (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN)
-
-#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \
- (ETH_FRAME_LEN + sizeof(struct rxpd) \
- + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN)
-
-#define CMD_F_HOSTCMD (1 << 0)
-#define FW_CAPINFO_WPA (1 << 0)
-
-#define RF_ANTENNA_1 0x1
-#define RF_ANTENNA_2 0x2
-#define RF_ANTENNA_AUTO 0xFFFF
-
-#define LBTF_EVENT_BCN_SENT 55
-
-/** Global Variable Declaration */
-/** mv_ms_type */
-enum mv_ms_type {
- MVMS_DAT = 0,
- MVMS_CMD = 1,
- MVMS_TXDONE = 2,
- MVMS_EVENT
-};
-
-extern struct workqueue_struct *lbtf_wq;
-
-struct lbtf_private;
-
-struct lbtf_offset_value {
- u32 offset;
- u32 value;
-};
-
-struct channel_range {
- u8 regdomain;
- u8 start;
- u8 end; /* exclusive (channel must be less than end) */
-};
-
-struct if_usb_card;
-
-/** Private structure for the MV device */
-struct lbtf_private {
- void *card;
- struct ieee80211_hw *hw;
-
- /* Command response buffer */
- u8 cmd_resp_buff[LBS_UPLD_SIZE];
- /* Download sent:
- bit0 1/0=data_sent/data_tx_done,
- bit1 1/0=cmd_sent/cmd_tx_done,
- all other bits reserved 0 */
- struct ieee80211_vif *vif;
-
- struct work_struct cmd_work;
- struct work_struct tx_work;
- /** Hardware access */
- int (*hw_host_to_card) (struct lbtf_private *priv, u8 type, u8 *payload, u16 nb);
- int (*hw_prog_firmware) (struct if_usb_card *cardp);
- int (*hw_reset_device) (struct if_usb_card *cardp);
-
-
- /** Wlan adapter data structure*/
- /** STATUS variables */
- u32 fwrelease;
- u32 fwcapinfo;
- /* protected with big lock */
-
- struct mutex lock;
-
- /** command-related variables */
- u16 seqnum;
- /* protected by big lock */
-
- struct cmd_ctrl_node *cmd_array;
- /** Current command */
- struct cmd_ctrl_node *cur_cmd;
- /** command Queues */
- /** Free command buffers */
- struct list_head cmdfreeq;
- /** Pending command buffers */
- struct list_head cmdpendingq;
-
- /** spin locks */
- spinlock_t driver_lock;
-
- /** Timers */
- struct timer_list command_timer;
- int nr_retries;
- int cmd_timed_out;
-
- u8 cmd_response_rxed;
-
- /** capability Info used in Association, start, join */
- u16 capability;
-
- /** MAC address information */
- u8 current_addr[ETH_ALEN];
- u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
- u32 nr_of_multicastmacaddr;
- int cur_freq;
-
- struct sk_buff *skb_to_tx;
- struct sk_buff *tx_skb;
-
- /** NIC Operation characteristics */
- u16 mac_control;
- u16 regioncode;
- struct channel_range range;
-
- u8 radioon;
- u32 preamble;
-
- struct ieee80211_channel channels[14];
- struct ieee80211_rate rates[12];
- struct ieee80211_supported_band band;
- struct lbtf_offset_value offsetvalue;
-
- u8 fw_ready;
- u8 surpriseremoved;
- struct sk_buff_head bc_ps_buf;
-
- /* Most recently reported noise in dBm */
- s8 noise;
-};
-
-/* 802.11-related definitions */
-
-/* TxPD descriptor */
-struct txpd {
- /* Current Tx packet status */
- __le32 tx_status;
- /* Tx control */
- __le32 tx_control;
- __le32 tx_packet_location;
- /* Tx packet length */
- __le16 tx_packet_length;
- /* First 2 byte of destination MAC address */
- u8 tx_dest_addr_high[2];
- /* Last 4 byte of destination MAC address */
- u8 tx_dest_addr_low[4];
- /* Pkt Priority */
- u8 priority;
- /* Pkt Trasnit Power control */
- u8 powermgmt;
- /* Time the packet has been queued in the driver (units = 2ms) */
- u8 pktdelay_2ms;
- /* reserved */
- u8 reserved1;
-};
-
-/* RxPD Descriptor */
-struct rxpd {
- /* Current Rx packet status */
- __le16 status;
-
- /* SNR */
- u8 snr;
-
- /* Tx control */
- u8 rx_control;
-
- /* Pkt length */
- __le16 pkt_len;
-
- /* Noise Floor */
- u8 nf;
-
- /* Rx Packet Rate */
- u8 rx_rate;
-
- /* Pkt addr */
- __le32 pkt_ptr;
-
- /* Next Rx RxPD addr */
- __le32 next_rxpd_ptr;
-
- /* Pkt Priority */
- u8 priority;
- u8 reserved[3];
-};
-
-struct cmd_header {
- __le16 command;
- __le16 size;
- __le16 seqnum;
- __le16 result;
-} __packed;
-
-struct cmd_ctrl_node {
- struct list_head list;
- int result;
- /* command response */
- int (*callback)(struct lbtf_private *,
- unsigned long, struct cmd_header *);
- unsigned long callback_arg;
- /* command data */
- struct cmd_header *cmdbuf;
- /* wait queue */
- u16 cmdwaitqwoken;
- wait_queue_head_t cmdwait_q;
-};
-
-/*
- * Define data structure for CMD_GET_HW_SPEC
- * This structure defines the response for the GET_HW_SPEC command
- */
-struct cmd_ds_get_hw_spec {
- struct cmd_header hdr;
-
- /* HW Interface version number */
- __le16 hwifversion;
- /* HW version number */
- __le16 version;
- /* Max number of TxPD FW can handle */
- __le16 nr_txpd;
- /* Max no of Multicast address */
- __le16 nr_mcast_adr;
- /* MAC address */
- u8 permanentaddr[6];
-
- /* region Code */
- __le16 regioncode;
-
- /* Number of antenna used */
- __le16 nr_antenna;
-
- /* FW release number, example 0x01030304 = 2.3.4p1 */
- __le32 fwrelease;
-
- /* Base Address of TxPD queue */
- __le32 wcb_base;
- /* Read Pointer of RxPd queue */
- __le32 rxpd_rdptr;
-
- /* Write Pointer of RxPd queue */
- __le32 rxpd_wrptr;
-
- /*FW/HW capability */
- __le32 fwcapinfo;
-} __packed;
-
-struct cmd_ds_mac_control {
- struct cmd_header hdr;
- __le16 action;
- u16 reserved;
-};
-
-struct cmd_ds_802_11_mac_address {
- struct cmd_header hdr;
-
- __le16 action;
- uint8_t macadd[ETH_ALEN];
-};
-
-struct cmd_ds_mac_multicast_addr {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 nr_of_adrs;
- u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
-};
-
-struct cmd_ds_set_mode {
- struct cmd_header hdr;
-
- __le16 mode;
-};
-
-struct cmd_ds_set_bssid {
- struct cmd_header hdr;
-
- u8 bssid[6];
- u8 activate;
-};
-
-struct cmd_ds_802_11_radio_control {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 control;
-};
-
-
-struct cmd_ds_802_11_rf_channel {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 channel;
- __le16 rftype; /* unused */
- __le16 reserved; /* unused */
- u8 channellist[32]; /* unused */
-};
-
-struct cmd_ds_set_boot2_ver {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 version;
-};
-
-struct cmd_ds_802_11_reset {
- struct cmd_header hdr;
-
- __le16 action;
-};
-
-struct cmd_ds_802_11_beacon_control {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 beacon_enable;
- __le16 beacon_period;
-};
-
-struct cmd_ds_802_11_beacon_set {
- struct cmd_header hdr;
-
- __le16 len;
- u8 beacon[MRVL_MAX_BCN_SIZE];
-};
-
-struct lbtf_private;
-struct cmd_ctrl_node;
-
-/** Function Prototype Declaration */
-void lbtf_set_mac_control(struct lbtf_private *priv);
-
-int lbtf_free_cmd_buffer(struct lbtf_private *priv);
-
-int lbtf_allocate_cmd_buffer(struct lbtf_private *priv);
-int lbtf_execute_next_command(struct lbtf_private *priv);
-int lbtf_set_radio_control(struct lbtf_private *priv);
-int lbtf_update_hw_spec(struct lbtf_private *priv);
-int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv);
-void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode);
-void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid);
-int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr);
-
-int lbtf_set_channel(struct lbtf_private *priv, u8 channel);
-
-int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon);
-int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable,
- int beacon_int);
-
-
-int lbtf_process_rx_command(struct lbtf_private *priv);
-void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd,
- int result);
-void lbtf_cmd_response_rx(struct lbtf_private *priv);
-
-/* main.c */
-struct chan_freq_power *lbtf_get_region_cfp_table(u8 region,
- int *cfp_no);
-struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev);
-int lbtf_remove_card(struct lbtf_private *priv);
-int lbtf_start_card(struct lbtf_private *priv);
-int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb);
-void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail);
-void lbtf_bcn_sent(struct lbtf_private *priv);
-
-/* support functions for cmd.c */
-/* lbtf_cmd() infers the size of the buffer to copy data back into, from
- the size of the target of the pointer. Since the command to be sent
- may often be smaller, that size is set in cmd->size by the caller.*/
-#define lbtf_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \
- uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \
- (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \
- __lbtf_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \
-})
-
-#define lbtf_cmd_with_response(priv, cmdnr, cmd) \
- lbtf_cmd(priv, cmdnr, cmd, lbtf_cmd_copyback, (unsigned long) (cmd))
-
-void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command,
- struct cmd_header *in_cmd, int in_cmd_size);
-
-int __lbtf_cmd(struct lbtf_private *priv, uint16_t command,
- struct cmd_header *in_cmd, int in_cmd_size,
- int (*callback)(struct lbtf_private *, unsigned long,
- struct cmd_header *),
- unsigned long callback_arg);
-
-int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra,
- struct cmd_header *resp);
+++ /dev/null
-/*
- * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/hardirq.h>
-#include <linux/slab.h>
-
-#include <linux/etherdevice.h>
-#include <linux/module.h>
-#include "libertas_tf.h"
-
-#define DRIVER_RELEASE_VERSION "004.p0"
-/* thinfirm version: 5.132.X.pX */
-#define LBTF_FW_VER_MIN 0x05840300
-#define LBTF_FW_VER_MAX 0x0584ffff
-#define QOS_CONTROL_LEN 2
-
-/* Module parameters */
-unsigned int lbtf_debug;
-EXPORT_SYMBOL_GPL(lbtf_debug);
-module_param_named(libertas_tf_debug, lbtf_debug, int, 0644);
-
-static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION
-#ifdef DEBUG
- "-dbg"
-#endif
- "";
-
-struct workqueue_struct *lbtf_wq;
-
-static const struct ieee80211_channel lbtf_channels[] = {
- { .center_freq = 2412, .hw_value = 1 },
- { .center_freq = 2417, .hw_value = 2 },
- { .center_freq = 2422, .hw_value = 3 },
- { .center_freq = 2427, .hw_value = 4 },
- { .center_freq = 2432, .hw_value = 5 },
- { .center_freq = 2437, .hw_value = 6 },
- { .center_freq = 2442, .hw_value = 7 },
- { .center_freq = 2447, .hw_value = 8 },
- { .center_freq = 2452, .hw_value = 9 },
- { .center_freq = 2457, .hw_value = 10 },
- { .center_freq = 2462, .hw_value = 11 },
- { .center_freq = 2467, .hw_value = 12 },
- { .center_freq = 2472, .hw_value = 13 },
- { .center_freq = 2484, .hw_value = 14 },
-};
-
-/* This table contains the hardware specific values for the modulation rates. */
-static const struct ieee80211_rate lbtf_rates[] = {
- { .bitrate = 10,
- .hw_value = 0, },
- { .bitrate = 20,
- .hw_value = 1,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 55,
- .hw_value = 2,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 110,
- .hw_value = 3,
- .flags = IEEE80211_RATE_SHORT_PREAMBLE },
- { .bitrate = 60,
- .hw_value = 5,
- .flags = 0 },
- { .bitrate = 90,
- .hw_value = 6,
- .flags = 0 },
- { .bitrate = 120,
- .hw_value = 7,
- .flags = 0 },
- { .bitrate = 180,
- .hw_value = 8,
- .flags = 0 },
- { .bitrate = 240,
- .hw_value = 9,
- .flags = 0 },
- { .bitrate = 360,
- .hw_value = 10,
- .flags = 0 },
- { .bitrate = 480,
- .hw_value = 11,
- .flags = 0 },
- { .bitrate = 540,
- .hw_value = 12,
- .flags = 0 },
-};
-
-static void lbtf_cmd_work(struct work_struct *work)
-{
- struct lbtf_private *priv = container_of(work, struct lbtf_private,
- cmd_work);
-
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- spin_lock_irq(&priv->driver_lock);
- /* command response? */
- if (priv->cmd_response_rxed) {
- priv->cmd_response_rxed = 0;
- spin_unlock_irq(&priv->driver_lock);
- lbtf_process_rx_command(priv);
- spin_lock_irq(&priv->driver_lock);
- }
-
- if (priv->cmd_timed_out && priv->cur_cmd) {
- struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
-
- if (++priv->nr_retries > 10) {
- lbtf_complete_command(priv, cmdnode,
- -ETIMEDOUT);
- priv->nr_retries = 0;
- } else {
- priv->cur_cmd = NULL;
-
- /* Stick it back at the _top_ of the pending
- * queue for immediate resubmission */
- list_add(&cmdnode->list, &priv->cmdpendingq);
- }
- }
- priv->cmd_timed_out = 0;
- spin_unlock_irq(&priv->driver_lock);
-
- if (!priv->fw_ready) {
- lbtf_deb_leave_args(LBTF_DEB_CMD, "fw not ready");
- return;
- }
-
- /* Execute the next command */
- if (!priv->cur_cmd)
- lbtf_execute_next_command(priv);
-
- lbtf_deb_leave(LBTF_DEB_CMD);
-}
-
-/**
- * lbtf_setup_firmware: initialize firmware.
- *
- * @priv A pointer to struct lbtf_private structure
- *
- * Returns: 0 on success.
- */
-static int lbtf_setup_firmware(struct lbtf_private *priv)
-{
- int ret = -1;
-
- lbtf_deb_enter(LBTF_DEB_FW);
- /*
- * Read priv address from HW
- */
- eth_broadcast_addr(priv->current_addr);
- ret = lbtf_update_hw_spec(priv);
- if (ret) {
- ret = -1;
- goto done;
- }
-
- lbtf_set_mac_control(priv);
- lbtf_set_radio_control(priv);
-
- ret = 0;
-done:
- lbtf_deb_leave_args(LBTF_DEB_FW, "ret: %d", ret);
- return ret;
-}
-
-/**
- * This function handles the timeout of command sending.
- * It will re-send the same command again.
- */
-static void command_timer_fn(unsigned long data)
-{
- struct lbtf_private *priv = (struct lbtf_private *)data;
- unsigned long flags;
- lbtf_deb_enter(LBTF_DEB_CMD);
-
- spin_lock_irqsave(&priv->driver_lock, flags);
-
- if (!priv->cur_cmd) {
- printk(KERN_DEBUG "libertastf: command timer expired; "
- "no pending command\n");
- goto out;
- }
-
- printk(KERN_DEBUG "libertas: command %x timed out\n",
- le16_to_cpu(priv->cur_cmd->cmdbuf->command));
-
- priv->cmd_timed_out = 1;
- queue_work(lbtf_wq, &priv->cmd_work);
-out:
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- lbtf_deb_leave(LBTF_DEB_CMD);
-}
-
-static int lbtf_init_adapter(struct lbtf_private *priv)
-{
- lbtf_deb_enter(LBTF_DEB_MAIN);
- eth_broadcast_addr(priv->current_addr);
- mutex_init(&priv->lock);
-
- priv->vif = NULL;
- setup_timer(&priv->command_timer, command_timer_fn,
- (unsigned long)priv);
-
- INIT_LIST_HEAD(&priv->cmdfreeq);
- INIT_LIST_HEAD(&priv->cmdpendingq);
-
- spin_lock_init(&priv->driver_lock);
-
- /* Allocate the command buffers */
- if (lbtf_allocate_cmd_buffer(priv))
- return -1;
-
- lbtf_deb_leave(LBTF_DEB_MAIN);
- return 0;
-}
-
-static void lbtf_free_adapter(struct lbtf_private *priv)
-{
- lbtf_deb_enter(LBTF_DEB_MAIN);
- lbtf_free_cmd_buffer(priv);
- del_timer(&priv->command_timer);
- lbtf_deb_leave(LBTF_DEB_MAIN);
-}
-
-static void lbtf_op_tx(struct ieee80211_hw *hw,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
-{
- struct lbtf_private *priv = hw->priv;
-
- priv->skb_to_tx = skb;
- queue_work(lbtf_wq, &priv->tx_work);
- /*
- * queue will be restarted when we receive transmission feedback if
- * there are no buffered multicast frames to send
- */
- ieee80211_stop_queues(priv->hw);
-}
-
-static void lbtf_tx_work(struct work_struct *work)
-{
- struct lbtf_private *priv = container_of(work, struct lbtf_private,
- tx_work);
- unsigned int len;
- struct ieee80211_tx_info *info;
- struct txpd *txpd;
- struct sk_buff *skb = NULL;
- int err;
-
- lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX);
-
- if ((priv->vif->type == NL80211_IFTYPE_AP) &&
- (!skb_queue_empty(&priv->bc_ps_buf)))
- skb = skb_dequeue(&priv->bc_ps_buf);
- else if (priv->skb_to_tx) {
- skb = priv->skb_to_tx;
- priv->skb_to_tx = NULL;
- } else {
- lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
- return;
- }
-
- len = skb->len;
- info = IEEE80211_SKB_CB(skb);
- txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd));
-
- if (priv->surpriseremoved) {
- dev_kfree_skb_any(skb);
- lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
- return;
- }
-
- memset(txpd, 0, sizeof(struct txpd));
- /* Activate per-packet rate selection */
- txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE |
- ieee80211_get_tx_rate(priv->hw, info)->hw_value);
-
- /* copy destination address from 802.11 header */
- memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4,
- ETH_ALEN);
- txpd->tx_packet_length = cpu_to_le16(len);
- txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
- lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
- BUG_ON(priv->tx_skb);
- spin_lock_irq(&priv->driver_lock);
- priv->tx_skb = skb;
- err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len);
- spin_unlock_irq(&priv->driver_lock);
- if (err) {
- dev_kfree_skb_any(skb);
- priv->tx_skb = NULL;
- pr_err("TX error: %d", err);
- }
- lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
-}
-
-static int lbtf_op_start(struct ieee80211_hw *hw)
-{
- struct lbtf_private *priv = hw->priv;
- void *card = priv->card;
- int ret = -1;
-
- lbtf_deb_enter(LBTF_DEB_MACOPS);
-
- if (!priv->fw_ready)
- /* Upload firmware */
- if (priv->hw_prog_firmware(card))
- goto err_prog_firmware;
-
- /* poke the firmware */
- priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
- priv->radioon = RADIO_ON;
- priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
- ret = lbtf_setup_firmware(priv);
- if (ret)
- goto err_prog_firmware;
-
- if ((priv->fwrelease < LBTF_FW_VER_MIN) ||
- (priv->fwrelease > LBTF_FW_VER_MAX)) {
- ret = -1;
- goto err_prog_firmware;
- }
-
- printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");
- lbtf_deb_leave(LBTF_DEB_MACOPS);
- return 0;
-
-err_prog_firmware:
- priv->hw_reset_device(card);
- lbtf_deb_leave_args(LBTF_DEB_MACOPS, "error programming fw; ret=%d", ret);
- return ret;
-}
-
-static void lbtf_op_stop(struct ieee80211_hw *hw)
-{
- struct lbtf_private *priv = hw->priv;
- unsigned long flags;
- struct sk_buff *skb;
-
- struct cmd_ctrl_node *cmdnode;
-
- lbtf_deb_enter(LBTF_DEB_MACOPS);
-
- /* Flush pending command nodes */
- spin_lock_irqsave(&priv->driver_lock, flags);
- list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
- cmdnode->result = -ENOENT;
- cmdnode->cmdwaitqwoken = 1;
- wake_up_interruptible(&cmdnode->cmdwait_q);
- }
-
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- cancel_work_sync(&priv->cmd_work);
- cancel_work_sync(&priv->tx_work);
- while ((skb = skb_dequeue(&priv->bc_ps_buf)))
- dev_kfree_skb_any(skb);
- priv->radioon = RADIO_OFF;
- lbtf_set_radio_control(priv);
-
- lbtf_deb_leave(LBTF_DEB_MACOPS);
-}
-
-static int lbtf_op_add_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct lbtf_private *priv = hw->priv;
- lbtf_deb_enter(LBTF_DEB_MACOPS);
- if (priv->vif != NULL)
- return -EOPNOTSUPP;
-
- priv->vif = vif;
- switch (vif->type) {
- case NL80211_IFTYPE_MESH_POINT:
- case NL80211_IFTYPE_AP:
- lbtf_set_mode(priv, LBTF_AP_MODE);
- break;
- case NL80211_IFTYPE_STATION:
- lbtf_set_mode(priv, LBTF_STA_MODE);
- break;
- default:
- priv->vif = NULL;
- return -EOPNOTSUPP;
- }
- lbtf_set_mac_address(priv, (u8 *) vif->addr);
- lbtf_deb_leave(LBTF_DEB_MACOPS);
- return 0;
-}
-
-static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
-{
- struct lbtf_private *priv = hw->priv;
- lbtf_deb_enter(LBTF_DEB_MACOPS);
-
- if (priv->vif->type == NL80211_IFTYPE_AP ||
- priv->vif->type == NL80211_IFTYPE_MESH_POINT)
- lbtf_beacon_ctrl(priv, 0, 0);
- lbtf_set_mode(priv, LBTF_PASSIVE_MODE);
- lbtf_set_bssid(priv, 0, NULL);
- priv->vif = NULL;
- lbtf_deb_leave(LBTF_DEB_MACOPS);
-}
-
-static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed)
-{
- struct lbtf_private *priv = hw->priv;
- struct ieee80211_conf *conf = &hw->conf;
- lbtf_deb_enter(LBTF_DEB_MACOPS);
-
- if (conf->chandef.chan->center_freq != priv->cur_freq) {
- priv->cur_freq = conf->chandef.chan->center_freq;
- lbtf_set_channel(priv, conf->chandef.chan->hw_value);
- }
- lbtf_deb_leave(LBTF_DEB_MACOPS);
- return 0;
-}
-
-static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw,
- struct netdev_hw_addr_list *mc_list)
-{
- struct lbtf_private *priv = hw->priv;
- int i;
- struct netdev_hw_addr *ha;
- int mc_count = netdev_hw_addr_list_count(mc_list);
-
- if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE)
- return mc_count;
-
- priv->nr_of_multicastmacaddr = mc_count;
- i = 0;
- netdev_hw_addr_list_for_each(ha, mc_list)
- memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN);
-
- return mc_count;
-}
-
-#define SUPPORTED_FIF_FLAGS FIF_ALLMULTI
-static void lbtf_op_configure_filter(struct ieee80211_hw *hw,
- unsigned int changed_flags,
- unsigned int *new_flags,
- u64 multicast)
-{
- struct lbtf_private *priv = hw->priv;
- int old_mac_control = priv->mac_control;
-
- lbtf_deb_enter(LBTF_DEB_MACOPS);
-
- changed_flags &= SUPPORTED_FIF_FLAGS;
- *new_flags &= SUPPORTED_FIF_FLAGS;
-
- if (!changed_flags) {
- lbtf_deb_leave(LBTF_DEB_MACOPS);
- return;
- }
-
- priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
- if (*new_flags & (FIF_ALLMULTI) ||
- multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) {
- priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
- priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
- } else if (multicast) {
- priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
- priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
- lbtf_cmd_set_mac_multicast_addr(priv);
- } else {
- priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE |
- CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
- if (priv->nr_of_multicastmacaddr) {
- priv->nr_of_multicastmacaddr = 0;
- lbtf_cmd_set_mac_multicast_addr(priv);
- }
- }
-
-
- if (priv->mac_control != old_mac_control)
- lbtf_set_mac_control(priv);
-
- lbtf_deb_leave(LBTF_DEB_MACOPS);
-}
-
-static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_bss_conf *bss_conf,
- u32 changes)
-{
- struct lbtf_private *priv = hw->priv;
- struct sk_buff *beacon;
- lbtf_deb_enter(LBTF_DEB_MACOPS);
-
- if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) {
- switch (priv->vif->type) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_MESH_POINT:
- beacon = ieee80211_beacon_get(hw, vif);
- if (beacon) {
- lbtf_beacon_set(priv, beacon);
- kfree_skb(beacon);
- lbtf_beacon_ctrl(priv, 1,
- bss_conf->beacon_int);
- }
- break;
- default:
- break;
- }
- }
-
- if (changes & BSS_CHANGED_BSSID) {
- bool activate = !is_zero_ether_addr(bss_conf->bssid);
- lbtf_set_bssid(priv, activate, bss_conf->bssid);
- }
-
- if (changes & BSS_CHANGED_ERP_PREAMBLE) {
- if (bss_conf->use_short_preamble)
- priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
- else
- priv->preamble = CMD_TYPE_LONG_PREAMBLE;
- lbtf_set_radio_control(priv);
- }
-
- lbtf_deb_leave(LBTF_DEB_MACOPS);
-}
-
-static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx,
- struct survey_info *survey)
-{
- struct lbtf_private *priv = hw->priv;
- struct ieee80211_conf *conf = &hw->conf;
-
- if (idx != 0)
- return -ENOENT;
-
- survey->channel = conf->chandef.chan;
- survey->filled = SURVEY_INFO_NOISE_DBM;
- survey->noise = priv->noise;
-
- return 0;
-}
-
-static const struct ieee80211_ops lbtf_ops = {
- .tx = lbtf_op_tx,
- .start = lbtf_op_start,
- .stop = lbtf_op_stop,
- .add_interface = lbtf_op_add_interface,
- .remove_interface = lbtf_op_remove_interface,
- .config = lbtf_op_config,
- .prepare_multicast = lbtf_op_prepare_multicast,
- .configure_filter = lbtf_op_configure_filter,
- .bss_info_changed = lbtf_op_bss_info_changed,
- .get_survey = lbtf_op_get_survey,
-};
-
-int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)
-{
- struct ieee80211_rx_status stats;
- struct rxpd *prxpd;
- int need_padding;
- unsigned int flags;
- struct ieee80211_hdr *hdr;
-
- lbtf_deb_enter(LBTF_DEB_RX);
-
- prxpd = (struct rxpd *) skb->data;
-
- memset(&stats, 0, sizeof(stats));
- if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
- stats.flag |= RX_FLAG_FAILED_FCS_CRC;
- stats.freq = priv->cur_freq;
- stats.band = IEEE80211_BAND_2GHZ;
- stats.signal = prxpd->snr;
- priv->noise = prxpd->nf;
- /* Marvell rate index has a hole at value 4 */
- if (prxpd->rx_rate > 4)
- --prxpd->rx_rate;
- stats.rate_idx = prxpd->rx_rate;
- skb_pull(skb, sizeof(struct rxpd));
-
- hdr = (struct ieee80211_hdr *)skb->data;
- flags = le32_to_cpu(*(__le32 *)(skb->data + 4));
-
- need_padding = ieee80211_is_data_qos(hdr->frame_control);
- need_padding ^= ieee80211_has_a4(hdr->frame_control);
- need_padding ^= ieee80211_is_data_qos(hdr->frame_control) &&
- (*ieee80211_get_qos_ctl(hdr) &
- IEEE80211_QOS_CTL_A_MSDU_PRESENT);
-
- if (need_padding) {
- memmove(skb->data + 2, skb->data, skb->len);
- skb_reserve(skb, 2);
- }
-
- memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
-
- lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
- skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
- lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data,
- min_t(unsigned int, skb->len, 100));
-
- ieee80211_rx_irqsafe(priv->hw, skb);
-
- lbtf_deb_leave(LBTF_DEB_RX);
- return 0;
-}
-EXPORT_SYMBOL_GPL(lbtf_rx);
-
-/**
- * lbtf_add_card: Add and initialize the card, no fw upload yet.
- *
- * @card A pointer to card
- *
- * Returns: pointer to struct lbtf_priv.
- */
-struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev)
-{
- struct ieee80211_hw *hw;
- struct lbtf_private *priv = NULL;
-
- lbtf_deb_enter(LBTF_DEB_MAIN);
-
- hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);
- if (!hw)
- goto done;
-
- priv = hw->priv;
- if (lbtf_init_adapter(priv))
- goto err_init_adapter;
-
- priv->hw = hw;
- priv->card = card;
- priv->tx_skb = NULL;
-
- hw->queues = 1;
- ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
- hw->extra_tx_headroom = sizeof(struct txpd);
- memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels));
- memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates));
- priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates);
- priv->band.bitrates = priv->rates;
- priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
- priv->band.channels = priv->channels;
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
- hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
- skb_queue_head_init(&priv->bc_ps_buf);
-
- SET_IEEE80211_DEV(hw, dmdev);
-
- INIT_WORK(&priv->cmd_work, lbtf_cmd_work);
- INIT_WORK(&priv->tx_work, lbtf_tx_work);
- if (ieee80211_register_hw(hw))
- goto err_init_adapter;
-
- goto done;
-
-err_init_adapter:
- lbtf_free_adapter(priv);
- ieee80211_free_hw(hw);
- priv = NULL;
-
-done:
- lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv);
- return priv;
-}
-EXPORT_SYMBOL_GPL(lbtf_add_card);
-
-
-int lbtf_remove_card(struct lbtf_private *priv)
-{
- struct ieee80211_hw *hw = priv->hw;
-
- lbtf_deb_enter(LBTF_DEB_MAIN);
-
- priv->surpriseremoved = 1;
- del_timer(&priv->command_timer);
- lbtf_free_adapter(priv);
- priv->hw = NULL;
- ieee80211_unregister_hw(hw);
- ieee80211_free_hw(hw);
-
- lbtf_deb_leave(LBTF_DEB_MAIN);
- return 0;
-}
-EXPORT_SYMBOL_GPL(lbtf_remove_card);
-
-void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)
-{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
-
- ieee80211_tx_info_clear_status(info);
- /*
- * Commented out, otherwise we never go beyond 1Mbit/s using mac80211
- * default pid rc algorithm.
- *
- * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;
- */
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)
- info->flags |= IEEE80211_TX_STAT_ACK;
- skb_pull(priv->tx_skb, sizeof(struct txpd));
- ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);
- priv->tx_skb = NULL;
- if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf))
- ieee80211_wake_queues(priv->hw);
- else
- queue_work(lbtf_wq, &priv->tx_work);
-}
-EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback);
-
-void lbtf_bcn_sent(struct lbtf_private *priv)
-{
- struct sk_buff *skb = NULL;
-
- if (priv->vif->type != NL80211_IFTYPE_AP)
- return;
-
- if (skb_queue_empty(&priv->bc_ps_buf)) {
- bool tx_buff_bc = false;
-
- while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) {
- skb_queue_tail(&priv->bc_ps_buf, skb);
- tx_buff_bc = true;
- }
- if (tx_buff_bc) {
- ieee80211_stop_queues(priv->hw);
- queue_work(lbtf_wq, &priv->tx_work);
- }
- }
-
- skb = ieee80211_beacon_get(priv->hw, priv->vif);
-
- if (skb) {
- lbtf_beacon_set(priv, skb);
- kfree_skb(skb);
- }
-}
-EXPORT_SYMBOL_GPL(lbtf_bcn_sent);
-
-static int __init lbtf_init_module(void)
-{
- lbtf_deb_enter(LBTF_DEB_MAIN);
- lbtf_wq = create_workqueue("libertastf");
- if (lbtf_wq == NULL) {
- printk(KERN_ERR "libertastf: couldn't create workqueue\n");
- return -ENOMEM;
- }
- lbtf_deb_leave(LBTF_DEB_MAIN);
- return 0;
-}
-
-static void __exit lbtf_exit_module(void)
-{
- lbtf_deb_enter(LBTF_DEB_MAIN);
- destroy_workqueue(lbtf_wq);
- lbtf_deb_leave(LBTF_DEB_MAIN);
-}
-
-module_init(lbtf_init_module);
-module_exit(lbtf_exit_module);
-
-MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library");
-MODULE_AUTHOR("Cozybit Inc.");
-MODULE_LICENSE("GPL");
if WLAN_VENDOR_MARVELL
source "drivers/net/wireless/marvell/libertas/Kconfig"
+source "drivers/net/wireless/marvell/libertas_tf/Kconfig"
endif # WLAN_VENDOR_MARVELL
obj-$(CONFIG_LIBERTAS) += libertas/
+
+obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
--- /dev/null
+config LIBERTAS_THINFIRM
+ tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware"
+ depends on MAC80211
+ select FW_LOADER
+ ---help---
+ A library for Marvell Libertas 8xxx devices using thinfirm.
+
+config LIBERTAS_THINFIRM_DEBUG
+ bool "Enable full debugging output in the Libertas thin firmware module."
+ depends on LIBERTAS_THINFIRM
+ ---help---
+ Debugging support.
+
+config LIBERTAS_THINFIRM_USB
+ tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware"
+ depends on LIBERTAS_THINFIRM && USB
+ ---help---
+ A driver for Marvell Libertas 8388 USB devices using thinfirm.
--- /dev/null
+libertas_tf-objs := main.o cmd.o
+
+libertas_tf_usb-objs += if_usb.o
+
+obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf.o
+obj-$(CONFIG_LIBERTAS_THINFIRM_USB) += libertas_tf_usb.o
--- /dev/null
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hardirq.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "libertas_tf.h"
+
+static const struct channel_range channel_ranges[] = {
+ { LBTF_REGDOMAIN_US, 1, 12 },
+ { LBTF_REGDOMAIN_CA, 1, 12 },
+ { LBTF_REGDOMAIN_EU, 1, 14 },
+ { LBTF_REGDOMAIN_JP, 1, 14 },
+ { LBTF_REGDOMAIN_SP, 1, 14 },
+ { LBTF_REGDOMAIN_FR, 1, 14 },
+};
+
+static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] =
+{
+ LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU,
+ LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP,
+};
+
+static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv);
+
+
+/**
+ * lbtf_cmd_copyback - Simple callback that copies response back into command
+ *
+ * @priv A pointer to struct lbtf_private structure
+ * @extra A pointer to the original command structure for which
+ * 'resp' is a response
+ * @resp A pointer to the command response
+ *
+ * Returns: 0 on success, error on failure
+ */
+int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra,
+ struct cmd_header *resp)
+{
+ struct cmd_header *buf = (void *)extra;
+ uint16_t copy_len;
+
+ copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size));
+ memcpy(buf, resp, copy_len);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lbtf_cmd_copyback);
+
+#define CHAN_TO_IDX(chan) ((chan) - 1)
+
+static void lbtf_geo_init(struct lbtf_private *priv)
+{
+ const struct channel_range *range = channel_ranges;
+ u8 ch;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_ranges); i++)
+ if (channel_ranges[i].regdomain == priv->regioncode) {
+ range = &channel_ranges[i];
+ break;
+ }
+
+ for (ch = priv->range.start; ch < priv->range.end; ch++)
+ priv->channels[CHAN_TO_IDX(ch)].flags = 0;
+}
+
+/**
+ * lbtf_update_hw_spec: Updates the hardware details.
+ *
+ * @priv A pointer to struct lbtf_private structure
+ *
+ * Returns: 0 on success, error on failure
+ */
+int lbtf_update_hw_spec(struct lbtf_private *priv)
+{
+ struct cmd_ds_get_hw_spec cmd;
+ int ret = -1;
+ u32 i;
+
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN);
+ ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd);
+ if (ret)
+ goto out;
+
+ priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo);
+
+ /* The firmware release is in an interesting format: the patch
+ * level is in the most significant nibble ... so fix that: */
+ priv->fwrelease = le32_to_cpu(cmd.fwrelease);
+ priv->fwrelease = (priv->fwrelease << 8) |
+ (priv->fwrelease >> 24 & 0xff);
+
+ printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n",
+ cmd.permanentaddr,
+ priv->fwrelease >> 24 & 0xff,
+ priv->fwrelease >> 16 & 0xff,
+ priv->fwrelease >> 8 & 0xff,
+ priv->fwrelease & 0xff,
+ priv->fwcapinfo);
+ lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
+ cmd.hwifversion, cmd.version);
+
+ /* Clamp region code to 8-bit since FW spec indicates that it should
+ * only ever be 8-bit, even though the field size is 16-bit. Some
+ * firmware returns non-zero high 8 bits here.
+ */
+ priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
+
+ for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
+ /* use the region code to search for the index */
+ if (priv->regioncode == lbtf_region_code_to_index[i])
+ break;
+ }
+
+ /* if it's unidentified region code, use the default (USA) */
+ if (i >= MRVDRV_MAX_REGION_CODE) {
+ priv->regioncode = 0x10;
+ pr_info("unidentified region code; using the default (USA)\n");
+ }
+
+ if (priv->current_addr[0] == 0xff)
+ memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
+
+ SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr);
+
+ lbtf_geo_init(priv);
+out:
+ lbtf_deb_leave(LBTF_DEB_CMD);
+ return ret;
+}
+
+/**
+ * lbtf_set_channel: Set the radio channel
+ *
+ * @priv A pointer to struct lbtf_private structure
+ * @channel The desired channel, or 0 to clear a locked channel
+ *
+ * Returns: 0 on success, error on failure
+ */
+int lbtf_set_channel(struct lbtf_private *priv, u8 channel)
+{
+ int ret = 0;
+ struct cmd_ds_802_11_rf_channel cmd;
+
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET);
+ cmd.channel = cpu_to_le16(channel);
+
+ ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
+ lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon)
+{
+ struct cmd_ds_802_11_beacon_set cmd;
+ int size;
+
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ if (beacon->len > MRVL_MAX_BCN_SIZE) {
+ lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1);
+ return -1;
+ }
+ size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len;
+ cmd.hdr.size = cpu_to_le16(size);
+ cmd.len = cpu_to_le16(beacon->len);
+ memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len);
+
+ lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size);
+
+ lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0);
+ return 0;
+}
+
+int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable,
+ int beacon_int)
+{
+ struct cmd_ds_802_11_beacon_control cmd;
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.beacon_enable = cpu_to_le16(beacon_enable);
+ cmd.beacon_period = cpu_to_le16(beacon_int);
+
+ lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd));
+
+ lbtf_deb_leave(LBTF_DEB_CMD);
+ return 0;
+}
+
+static void lbtf_queue_cmd(struct lbtf_private *priv,
+ struct cmd_ctrl_node *cmdnode)
+{
+ unsigned long flags;
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ if (!cmdnode) {
+ lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n");
+ goto qcmd_done;
+ }
+
+ if (!cmdnode->cmdbuf->size) {
+ lbtf_deb_host("DNLD_CMD: cmd size is zero\n");
+ goto qcmd_done;
+ }
+
+ cmdnode->result = 0;
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ list_add_tail(&cmdnode->list, &priv->cmdpendingq);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
+ le16_to_cpu(cmdnode->cmdbuf->command));
+
+qcmd_done:
+ lbtf_deb_leave(LBTF_DEB_HOST);
+}
+
+static void lbtf_submit_command(struct lbtf_private *priv,
+ struct cmd_ctrl_node *cmdnode)
+{
+ unsigned long flags;
+ struct cmd_header *cmd;
+ uint16_t cmdsize;
+ uint16_t command;
+ int timeo = 5 * HZ;
+ int ret;
+
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ cmd = cmdnode->cmdbuf;
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ priv->cur_cmd = cmdnode;
+ cmdsize = le16_to_cpu(cmd->size);
+ command = le16_to_cpu(cmd->command);
+
+ lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
+ command, le16_to_cpu(cmd->seqnum), cmdsize);
+ lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);
+
+ ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ if (ret) {
+ pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret);
+ /* Let the timer kick in and retry, and potentially reset
+ the whole thing if the condition persists */
+ timeo = HZ;
+ }
+
+ /* Setup the timer after transmit command */
+ mod_timer(&priv->command_timer, jiffies + timeo);
+
+ lbtf_deb_leave(LBTF_DEB_HOST);
+}
+
+/**
+ * This function inserts command node to cmdfreeq
+ * after cleans it. Requires priv->driver_lock held.
+ */
+static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv,
+ struct cmd_ctrl_node *cmdnode)
+{
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ if (!cmdnode)
+ goto cl_ins_out;
+
+ cmdnode->callback = NULL;
+ cmdnode->callback_arg = 0;
+
+ memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE);
+
+ list_add_tail(&cmdnode->list, &priv->cmdfreeq);
+
+cl_ins_out:
+ lbtf_deb_leave(LBTF_DEB_HOST);
+}
+
+static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv,
+ struct cmd_ctrl_node *ptempcmd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ __lbtf_cleanup_and_insert_cmd(priv, ptempcmd);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+}
+
+void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd,
+ int result)
+{
+ cmd->result = result;
+ cmd->cmdwaitqwoken = 1;
+ wake_up_interruptible(&cmd->cmdwait_q);
+
+ if (!cmd->callback)
+ __lbtf_cleanup_and_insert_cmd(priv, cmd);
+ priv->cur_cmd = NULL;
+}
+
+int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv)
+{
+ struct cmd_ds_mac_multicast_addr cmd;
+
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+
+ cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr);
+
+ lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs);
+
+ memcpy(cmd.maclist, priv->multicastlist,
+ priv->nr_of_multicastmacaddr * ETH_ALEN);
+
+ lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd));
+
+ lbtf_deb_leave(LBTF_DEB_CMD);
+ return 0;
+}
+
+void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode)
+{
+ struct cmd_ds_set_mode cmd;
+ lbtf_deb_enter(LBTF_DEB_WEXT);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.mode = cpu_to_le16(mode);
+ lbtf_deb_wext("Switching to mode: 0x%x\n", mode);
+ lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd));
+
+ lbtf_deb_leave(LBTF_DEB_WEXT);
+}
+
+void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid)
+{
+ struct cmd_ds_set_bssid cmd;
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.activate = activate ? 1 : 0;
+ if (activate)
+ memcpy(cmd.bssid, bssid, ETH_ALEN);
+
+ lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd));
+ lbtf_deb_leave(LBTF_DEB_CMD);
+}
+
+int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr)
+{
+ struct cmd_ds_802_11_mac_address cmd;
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+
+ memcpy(cmd.macadd, mac_addr, ETH_ALEN);
+
+ lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd));
+ lbtf_deb_leave(LBTF_DEB_CMD);
+ return 0;
+}
+
+int lbtf_set_radio_control(struct lbtf_private *priv)
+{
+ int ret = 0;
+ struct cmd_ds_802_11_radio_control cmd;
+
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+
+ switch (priv->preamble) {
+ case CMD_TYPE_SHORT_PREAMBLE:
+ cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE);
+ break;
+
+ case CMD_TYPE_LONG_PREAMBLE:
+ cmd.control = cpu_to_le16(SET_LONG_PREAMBLE);
+ break;
+
+ case CMD_TYPE_AUTO_PREAMBLE:
+ default:
+ cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE);
+ break;
+ }
+
+ if (priv->radioon)
+ cmd.control |= cpu_to_le16(TURN_ON_RF);
+ else
+ cmd.control &= cpu_to_le16(~TURN_ON_RF);
+
+ lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon,
+ priv->preamble);
+
+ ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd);
+
+ lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+void lbtf_set_mac_control(struct lbtf_private *priv)
+{
+ struct cmd_ds_mac_control cmd;
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(priv->mac_control);
+ cmd.reserved = 0;
+
+ lbtf_cmd_async(priv, CMD_MAC_CONTROL,
+ &cmd.hdr, sizeof(cmd));
+
+ lbtf_deb_leave(LBTF_DEB_CMD);
+}
+
+/**
+ * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue
+ *
+ * @priv A pointer to struct lbtf_private structure
+ *
+ * Returns: 0 on success.
+ */
+int lbtf_allocate_cmd_buffer(struct lbtf_private *priv)
+{
+ int ret = 0;
+ u32 bufsize;
+ u32 i;
+ struct cmd_ctrl_node *cmdarray;
+
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ /* Allocate and initialize the command array */
+ bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS;
+ cmdarray = kzalloc(bufsize, GFP_KERNEL);
+ if (!cmdarray) {
+ lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
+ ret = -1;
+ goto done;
+ }
+ priv->cmd_array = cmdarray;
+
+ /* Allocate and initialize each command buffer in the command array */
+ for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
+ cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL);
+ if (!cmdarray[i].cmdbuf) {
+ lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
+ ret = -1;
+ goto done;
+ }
+ }
+
+ for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
+ init_waitqueue_head(&cmdarray[i].cmdwait_q);
+ lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]);
+ }
+
+ ret = 0;
+
+done:
+ lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * lbtf_free_cmd_buffer - Frees the cmd buffer.
+ *
+ * @priv A pointer to struct lbtf_private structure
+ *
+ * Returns: 0
+ */
+int lbtf_free_cmd_buffer(struct lbtf_private *priv)
+{
+ struct cmd_ctrl_node *cmdarray;
+ unsigned int i;
+
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ /* need to check if cmd array is allocated or not */
+ if (priv->cmd_array == NULL) {
+ lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
+ goto done;
+ }
+
+ cmdarray = priv->cmd_array;
+
+ /* Release shared memory buffers */
+ for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
+ kfree(cmdarray[i].cmdbuf);
+ cmdarray[i].cmdbuf = NULL;
+ }
+
+ /* Release cmd_ctrl_node */
+ kfree(priv->cmd_array);
+ priv->cmd_array = NULL;
+
+done:
+ lbtf_deb_leave(LBTF_DEB_HOST);
+ return 0;
+}
+
+/**
+ * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue.
+ *
+ * @priv A pointer to struct lbtf_private structure
+ *
+ * Returns: pointer to a struct cmd_ctrl_node or NULL if none available.
+ */
+static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv)
+{
+ struct cmd_ctrl_node *tempnode;
+ unsigned long flags;
+
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ if (!priv)
+ return NULL;
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+
+ if (!list_empty(&priv->cmdfreeq)) {
+ tempnode = list_first_entry(&priv->cmdfreeq,
+ struct cmd_ctrl_node, list);
+ list_del(&tempnode->list);
+ } else {
+ lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
+ tempnode = NULL;
+ }
+
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ lbtf_deb_leave(LBTF_DEB_HOST);
+ return tempnode;
+}
+
+/**
+ * lbtf_execute_next_command: execute next command in cmd pending queue.
+ *
+ * @priv A pointer to struct lbtf_private structure
+ *
+ * Returns: 0 on success.
+ */
+int lbtf_execute_next_command(struct lbtf_private *priv)
+{
+ struct cmd_ctrl_node *cmdnode = NULL;
+ struct cmd_header *cmd;
+ unsigned long flags;
+ int ret = 0;
+
+ /* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the
+ * only caller to us is lbtf_thread() and we get even when a
+ * data packet is received */
+ lbtf_deb_enter(LBTF_DEB_THREAD);
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+
+ if (priv->cur_cmd) {
+ pr_alert("EXEC_NEXT_CMD: already processing command!\n");
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ ret = -1;
+ goto done;
+ }
+
+ if (!list_empty(&priv->cmdpendingq)) {
+ cmdnode = list_first_entry(&priv->cmdpendingq,
+ struct cmd_ctrl_node, list);
+ }
+
+ if (cmdnode) {
+ cmd = cmdnode->cmdbuf;
+
+ list_del(&cmdnode->list);
+ lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
+ le16_to_cpu(cmd->command));
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ lbtf_submit_command(priv, cmdnode);
+ } else
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ ret = 0;
+done:
+ lbtf_deb_leave(LBTF_DEB_THREAD);
+ return ret;
+}
+
+static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv,
+ uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
+ int (*callback)(struct lbtf_private *, unsigned long,
+ struct cmd_header *),
+ unsigned long callback_arg)
+{
+ struct cmd_ctrl_node *cmdnode;
+
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ if (priv->surpriseremoved) {
+ lbtf_deb_host("PREP_CMD: card removed\n");
+ cmdnode = ERR_PTR(-ENOENT);
+ goto done;
+ }
+
+ cmdnode = lbtf_get_cmd_ctrl_node(priv);
+ if (cmdnode == NULL) {
+ lbtf_deb_host("PREP_CMD: cmdnode is NULL\n");
+
+ /* Wake up main thread to execute next command */
+ queue_work(lbtf_wq, &priv->cmd_work);
+ cmdnode = ERR_PTR(-ENOBUFS);
+ goto done;
+ }
+
+ cmdnode->callback = callback;
+ cmdnode->callback_arg = callback_arg;
+
+ /* Copy the incoming command to the buffer */
+ memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size);
+
+ /* Set sequence number, clean result, move to buffer */
+ priv->seqnum++;
+ cmdnode->cmdbuf->command = cpu_to_le16(command);
+ cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size);
+ cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum);
+ cmdnode->cmdbuf->result = 0;
+
+ lbtf_deb_host("PREP_CMD: command 0x%04x\n", command);
+
+ cmdnode->cmdwaitqwoken = 0;
+ lbtf_queue_cmd(priv, cmdnode);
+ queue_work(lbtf_wq, &priv->cmd_work);
+
+ done:
+ lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode);
+ return cmdnode;
+}
+
+void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command,
+ struct cmd_header *in_cmd, int in_cmd_size)
+{
+ lbtf_deb_enter(LBTF_DEB_CMD);
+ __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0);
+ lbtf_deb_leave(LBTF_DEB_CMD);
+}
+
+int __lbtf_cmd(struct lbtf_private *priv, uint16_t command,
+ struct cmd_header *in_cmd, int in_cmd_size,
+ int (*callback)(struct lbtf_private *,
+ unsigned long, struct cmd_header *),
+ unsigned long callback_arg)
+{
+ struct cmd_ctrl_node *cmdnode;
+ unsigned long flags;
+ int ret = 0;
+
+ lbtf_deb_enter(LBTF_DEB_HOST);
+
+ cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size,
+ callback, callback_arg);
+ if (IS_ERR(cmdnode)) {
+ ret = PTR_ERR(cmdnode);
+ goto done;
+ }
+
+ might_sleep();
+ ret = wait_event_interruptible(cmdnode->cmdwait_q,
+ cmdnode->cmdwaitqwoken);
+ if (ret) {
+ pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n",
+ command, ret);
+ goto done;
+ }
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ ret = cmdnode->result;
+ if (ret)
+ pr_info("PREP_CMD: command 0x%04x failed: %d\n",
+ command, ret);
+
+ __lbtf_cleanup_and_insert_cmd(priv, cmdnode);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+done:
+ lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__lbtf_cmd);
+
+/* Call holding driver_lock */
+void lbtf_cmd_response_rx(struct lbtf_private *priv)
+{
+ priv->cmd_response_rxed = 1;
+ queue_work(lbtf_wq, &priv->cmd_work);
+}
+EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx);
+
+int lbtf_process_rx_command(struct lbtf_private *priv)
+{
+ uint16_t respcmd, curcmd;
+ struct cmd_header *resp;
+ int ret = 0;
+ unsigned long flags;
+ uint16_t result;
+
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ mutex_lock(&priv->lock);
+ spin_lock_irqsave(&priv->driver_lock, flags);
+
+ if (!priv->cur_cmd) {
+ ret = -1;
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ goto done;
+ }
+
+ resp = (void *)priv->cmd_resp_buff;
+ curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
+ respcmd = le16_to_cpu(resp->command);
+ result = le16_to_cpu(resp->result);
+
+ if (net_ratelimit())
+ pr_info("libertastf: cmd response 0x%04x, seq %d, size %d\n",
+ respcmd, le16_to_cpu(resp->seqnum),
+ le16_to_cpu(resp->size));
+
+ if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ ret = -1;
+ goto done;
+ }
+ if (respcmd != CMD_RET(curcmd)) {
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ ret = -1;
+ goto done;
+ }
+
+ if (resp->result == cpu_to_le16(0x0004)) {
+ /* 0x0004 means -EAGAIN. Drop the response, let it time out
+ and be resubmitted */
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ ret = -1;
+ goto done;
+ }
+
+ /* Now we got response from FW, cancel the command timer */
+ del_timer(&priv->command_timer);
+ priv->cmd_timed_out = 0;
+ if (priv->nr_retries)
+ priv->nr_retries = 0;
+
+ /* If the command is not successful, cleanup and return failure */
+ if ((result != 0 || !(respcmd & 0x8000))) {
+ /*
+ * Handling errors here
+ */
+ switch (respcmd) {
+ case CMD_RET(CMD_GET_HW_SPEC):
+ case CMD_RET(CMD_802_11_RESET):
+ pr_info("libertastf: reset failed\n");
+ break;
+
+ }
+ lbtf_complete_command(priv, priv->cur_cmd, result);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ ret = -1;
+ goto done;
+ }
+
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+ if (priv->cur_cmd && priv->cur_cmd->callback) {
+ ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
+ resp);
+ }
+ spin_lock_irqsave(&priv->driver_lock, flags);
+
+ if (priv->cur_cmd) {
+ /* Clean up and Put current command back to cmdfreeq */
+ lbtf_complete_command(priv, priv->cur_cmd, result);
+ }
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+done:
+ mutex_unlock(&priv->lock);
+ lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret);
+ return ret;
+}
--- /dev/null
+/**
+ * This header file contains global constant/enum definitions,
+ * global variable declaration.
+ */
+#ifndef _LBS_DEB_DEFS_H_
+#define _LBS_DEB_DEFS_H_
+
+#ifndef DRV_NAME
+#define DRV_NAME "libertas_tf"
+#endif
+
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_LIBERTAS_THINFIRM_DEBUG
+#define DEBUG
+#define PROC_DEBUG
+#endif
+
+#define LBTF_DEB_ENTER 0x00000001
+#define LBTF_DEB_LEAVE 0x00000002
+#define LBTF_DEB_MAIN 0x00000004
+#define LBTF_DEB_NET 0x00000008
+#define LBTF_DEB_MESH 0x00000010
+#define LBTF_DEB_WEXT 0x00000020
+#define LBTF_DEB_IOCTL 0x00000040
+#define LBTF_DEB_SCAN 0x00000080
+#define LBTF_DEB_ASSOC 0x00000100
+#define LBTF_DEB_JOIN 0x00000200
+#define LBTF_DEB_11D 0x00000400
+#define LBTF_DEB_DEBUGFS 0x00000800
+#define LBTF_DEB_ETHTOOL 0x00001000
+#define LBTF_DEB_HOST 0x00002000
+#define LBTF_DEB_CMD 0x00004000
+#define LBTF_DEB_RX 0x00008000
+#define LBTF_DEB_TX 0x00010000
+#define LBTF_DEB_USB 0x00020000
+#define LBTF_DEB_CS 0x00040000
+#define LBTF_DEB_FW 0x00080000
+#define LBTF_DEB_THREAD 0x00100000
+#define LBTF_DEB_HEX 0x00200000
+#define LBTF_DEB_SDIO 0x00400000
+#define LBTF_DEB_MACOPS 0x00800000
+
+extern unsigned int lbtf_debug;
+
+
+#ifdef DEBUG
+#define LBTF_DEB_LL(grp, grpnam, fmt, args...) \
+do { if ((lbtf_debug & (grp)) == (grp)) \
+ printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \
+ in_interrupt() ? " (INT)" : "", ## args); } while (0)
+#else
+#define LBTF_DEB_LL(grp, grpnam, fmt, args...) do {} while (0)
+#endif
+
+#define lbtf_deb_enter(grp) \
+ LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s()\n", __func__);
+#define lbtf_deb_enter_args(grp, fmt, args...) \
+ LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args);
+#define lbtf_deb_leave(grp) \
+ LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s()\n", __func__);
+#define lbtf_deb_leave_args(grp, fmt, args...) \
+ LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s(), " fmt "\n", \
+ __func__, ##args);
+#define lbtf_deb_main(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MAIN, " main", fmt, ##args)
+#define lbtf_deb_net(fmt, args...) LBTF_DEB_LL(LBTF_DEB_NET, " net", fmt, ##args)
+#define lbtf_deb_mesh(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MESH, " mesh", fmt, ##args)
+#define lbtf_deb_wext(fmt, args...) LBTF_DEB_LL(LBTF_DEB_WEXT, " wext", fmt, ##args)
+#define lbtf_deb_ioctl(fmt, args...) LBTF_DEB_LL(LBTF_DEB_IOCTL, " ioctl", fmt, ##args)
+#define lbtf_deb_scan(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SCAN, " scan", fmt, ##args)
+#define lbtf_deb_assoc(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ASSOC, " assoc", fmt, ##args)
+#define lbtf_deb_join(fmt, args...) LBTF_DEB_LL(LBTF_DEB_JOIN, " join", fmt, ##args)
+#define lbtf_deb_11d(fmt, args...) LBTF_DEB_LL(LBTF_DEB_11D, " 11d", fmt, ##args)
+#define lbtf_deb_debugfs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_DEBUGFS, " debugfs", fmt, ##args)
+#define lbtf_deb_ethtool(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ETHTOOL, " ethtool", fmt, ##args)
+#define lbtf_deb_host(fmt, args...) LBTF_DEB_LL(LBTF_DEB_HOST, " host", fmt, ##args)
+#define lbtf_deb_cmd(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CMD, " cmd", fmt, ##args)
+#define lbtf_deb_rx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_RX, " rx", fmt, ##args)
+#define lbtf_deb_tx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_TX, " tx", fmt, ##args)
+#define lbtf_deb_fw(fmt, args...) LBTF_DEB_LL(LBTF_DEB_FW, " fw", fmt, ##args)
+#define lbtf_deb_usb(fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usb", fmt, ##args)
+#define lbtf_deb_usbd(dev, fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args)
+#define lbtf_deb_cs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CS, " cs", fmt, ##args)
+#define lbtf_deb_thread(fmt, args...) LBTF_DEB_LL(LBTF_DEB_THREAD, " thread", fmt, ##args)
+#define lbtf_deb_sdio(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SDIO, " thread", fmt, ##args)
+#define lbtf_deb_macops(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MACOPS, " thread", fmt, ##args)
+
+#ifdef DEBUG
+static inline void lbtf_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len)
+{
+ char newprompt[32];
+
+ if (len &&
+ (lbtf_debug & LBTF_DEB_HEX) &&
+ (lbtf_debug & grp)) {
+ snprintf(newprompt, sizeof(newprompt), DRV_NAME " %s: ", prompt);
+ print_hex_dump_bytes(prompt, DUMP_PREFIX_NONE, buf, len);
+ }
+}
+#else
+#define lbtf_deb_hex(grp, prompt, buf, len) do {} while (0)
+#endif
+
+#endif
--- /dev/null
+/*
+ * 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/module.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)
+ 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.bDeviceClass,
+ udev->descriptor.bDeviceSubClass,
+ udev->descriptor.bDeviceProtocol);
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (usb_endpoint_is_bulk_in(endpoint)) {
+ cardp->ep_in_size =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ cardp->ep_in = usb_endpoint_num(endpoint);
+
+ lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n",
+ cardp->ep_in);
+ lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n",
+ cardp->ep_in_size);
+ } else if (usb_endpoint_is_bulk_out(endpoint)) {
+ cardp->ep_out_size =
+ le16_to_cpu(endpoint->wMaxPacketSize);
+ cardp->ep_out = usb_endpoint_num(endpoint);
+
+ lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n",
+ cardp->ep_out);
+ lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n",
+ cardp->ep_out_size);
+ }
+ }
+ if (!cardp->ep_out_size || !cardp->ep_in_size) {
+ lbtf_deb_usbd(&udev->dev, "Endpoints not found\n");
+ /* Endpoints not found */
+ goto dealloc;
+ }
+
+ cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cardp->rx_urb) {
+ lbtf_deb_usbd(&udev->dev, "Rx URB allocation failed\n");
+ goto dealloc;
+ }
+
+ cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cardp->tx_urb) {
+ lbtf_deb_usbd(&udev->dev, "Tx URB allocation failed\n");
+ goto dealloc;
+ }
+
+ cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!cardp->cmd_urb) {
+ lbtf_deb_usbd(&udev->dev, "Cmd URB allocation failed\n");
+ goto dealloc;
+ }
+
+ cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE,
+ GFP_KERNEL);
+ if (!cardp->ep_out_buf) {
+ lbtf_deb_usbd(&udev->dev, "Could not allocate buffer\n");
+ goto dealloc;
+ }
+
+ priv = lbtf_add_card(cardp, &udev->dev);
+ if (!priv)
+ goto dealloc;
+
+ cardp->priv = priv;
+
+ priv->hw_host_to_card = if_usb_host_to_card;
+ priv->hw_prog_firmware = if_usb_prog_firmware;
+ priv->hw_reset_device = if_usb_reset_device;
+ cardp->boot2_version = udev->descriptor.bcdDevice;
+
+ usb_get_dev(udev);
+ usb_set_intfdata(intf, cardp);
+
+ return 0;
+
+dealloc:
+ if_usb_free(cardp);
+error:
+lbtf_deb_leave(LBTF_DEB_MAIN);
+ return -ENOMEM;
+}
+
+/**
+ * if_usb_disconnect - free resource and cleanup
+ *
+ * @intf USB interface structure
+ */
+static void if_usb_disconnect(struct usb_interface *intf)
+{
+ struct if_usb_card *cardp = usb_get_intfdata(intf);
+ struct lbtf_private *priv = cardp->priv;
+
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+
+ if_usb_reset_device(cardp);
+
+ if (priv)
+ lbtf_remove_card(priv);
+
+ /* Unlink and free urb */
+ if_usb_free(cardp);
+
+ usb_set_intfdata(intf, NULL);
+ usb_put_dev(interface_to_usbdev(intf));
+
+ lbtf_deb_leave(LBTF_DEB_MAIN);
+}
+
+/**
+ * if_usb_send_fw_pkt - This function downloads the FW
+ *
+ * @priv pointer to struct lbtf_private
+ *
+ * Returns: 0
+ */
+static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
+{
+ struct fwdata *fwdata = cardp->ep_out_buf;
+ u8 *firmware = (u8 *) cardp->fw->data;
+
+ lbtf_deb_enter(LBTF_DEB_FW);
+
+ /* If we got a CRC failure on the last block, back
+ up and retry it */
+ if (!cardp->CRC_OK) {
+ cardp->totalbytes = cardp->fwlastblksent;
+ cardp->fwseqnum--;
+ }
+
+ lbtf_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n",
+ cardp->totalbytes);
+
+ /* struct fwdata (which we sent to the card) has an
+ extra __le32 field in between the header and the data,
+ which is not in the struct fwheader in the actual
+ firmware binary. Insert the seqnum in the middle... */
+ memcpy(&fwdata->hdr, &firmware[cardp->totalbytes],
+ sizeof(struct fwheader));
+
+ cardp->fwlastblksent = cardp->totalbytes;
+ cardp->totalbytes += sizeof(struct fwheader);
+
+ memcpy(fwdata->data, &firmware[cardp->totalbytes],
+ le32_to_cpu(fwdata->hdr.datalength));
+
+ lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
+ le32_to_cpu(fwdata->hdr.datalength));
+
+ fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
+ cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
+
+ usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
+ le32_to_cpu(fwdata->hdr.datalength), 0);
+
+ if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
+ lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
+ lbtf_deb_usb2(&cardp->udev->dev,
+ "seqnum = %d totalbytes = %d\n",
+ cardp->fwseqnum, cardp->totalbytes);
+ } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
+ lbtf_deb_usb2(&cardp->udev->dev,
+ "Host has finished FW downloading\n");
+ lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n");
+
+ /* Host has finished FW downloading
+ * Donwloading FW JUMP BLOCK
+ */
+ cardp->fwfinalblk = 1;
+ }
+
+ lbtf_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n",
+ cardp->totalbytes);
+
+ lbtf_deb_leave(LBTF_DEB_FW);
+ return 0;
+}
+
+static int if_usb_reset_device(struct if_usb_card *cardp)
+{
+ struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4;
+ int ret;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+
+ *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
+
+ cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET);
+ cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset));
+ cmd->hdr.result = cpu_to_le16(0);
+ cmd->hdr.seqnum = cpu_to_le16(0x5a5a);
+ cmd->action = cpu_to_le16(CMD_ACT_HALT);
+ usb_tx_block(cardp, cardp->ep_out_buf,
+ 4 + sizeof(struct cmd_ds_802_11_reset), 0);
+
+ msleep(100);
+ ret = usb_reset_device(cardp->udev);
+ msleep(100);
+
+ lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
+
+ return ret;
+}
+
+/**
+ * usb_tx_block - transfer data to the device
+ *
+ * @priv pointer to struct lbtf_private
+ * @payload pointer to payload data
+ * @nb data length
+ * @data non-zero for data, zero for commands
+ *
+ * Returns: 0 on success, nonzero otherwise.
+ */
+static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload,
+ uint16_t nb, u8 data)
+{
+ int ret = -1;
+ struct urb *urb;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+ /* check if device is removed */
+ if (cardp->priv->surpriseremoved) {
+ lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n");
+ goto tx_ret;
+ }
+
+ if (data)
+ urb = cardp->tx_urb;
+ else
+ urb = cardp->cmd_urb;
+
+ usb_fill_bulk_urb(urb, cardp->udev,
+ usb_sndbulkpipe(cardp->udev,
+ cardp->ep_out),
+ payload, nb, if_usb_write_bulk_callback, cardp);
+
+ urb->transfer_flags |= URB_ZERO_PACKET;
+
+ if (usb_submit_urb(urb, GFP_ATOMIC)) {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "usb_submit_urb failed: %d\n", ret);
+ goto tx_ret;
+ }
+
+ lbtf_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
+
+ ret = 0;
+
+tx_ret:
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return ret;
+}
+
+static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
+ void (*callbackfn)(struct urb *urb))
+{
+ struct sk_buff *skb;
+ int ret = -1;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+
+ skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
+ if (!skb) {
+ pr_err("No free skb\n");
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return -1;
+ }
+
+ cardp->rx_skb = skb;
+
+ /* Fill the receive configuration URB and initialise the Rx call back */
+ usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
+ usb_rcvbulkpipe(cardp->udev, cardp->ep_in),
+ skb_tail_pointer(skb),
+ MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp);
+
+ cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+ lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n",
+ cardp->rx_urb);
+ ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC);
+ if (ret) {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "Submit Rx URB failed: %d\n", ret);
+ kfree_skb(skb);
+ cardp->rx_skb = NULL;
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return -1;
+ } else {
+ lbtf_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n");
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return 0;
+ }
+}
+
+static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp)
+{
+ return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload);
+}
+
+static int if_usb_submit_rx_urb(struct if_usb_card *cardp)
+{
+ return __if_usb_submit_rx_urb(cardp, &if_usb_receive);
+}
+
+static void if_usb_receive_fwload(struct urb *urb)
+{
+ struct if_usb_card *cardp = urb->context;
+ struct sk_buff *skb = cardp->rx_skb;
+ struct fwsyncheader *syncfwheader;
+ struct bootcmdresp bcmdresp;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+ if (urb->status) {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "URB status is failed during fw load\n");
+ kfree_skb(skb);
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return;
+ }
+
+ if (cardp->fwdnldover) {
+ __le32 *tmp = (__le32 *)(skb->data);
+
+ if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
+ tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
+ /* Firmware ready event received */
+ pr_info("Firmware ready event received\n");
+ wake_up(&cardp->fw_wq);
+ } else {
+ lbtf_deb_usb("Waiting for confirmation; got %x %x\n",
+ le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1]));
+ if_usb_submit_rx_urb_fwload(cardp);
+ }
+ kfree_skb(skb);
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return;
+ }
+ if (cardp->bootcmdresp <= 0) {
+ memcpy(&bcmdresp, skb->data, sizeof(bcmdresp));
+
+ if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
+ kfree_skb(skb);
+ if_usb_submit_rx_urb_fwload(cardp);
+ cardp->bootcmdresp = 1;
+ /* Received valid boot command response */
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "Received valid boot command response\n");
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return;
+ }
+ if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
+ if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) ||
+ bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) ||
+ bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) {
+ if (!cardp->bootcmdresp)
+ pr_info("Firmware already seems alive; resetting\n");
+ cardp->bootcmdresp = -1;
+ } else {
+ pr_info("boot cmd response wrong magic number (0x%x)\n",
+ le32_to_cpu(bcmdresp.magic));
+ }
+ } else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) {
+ pr_info("boot cmd response cmd_tag error (%d)\n",
+ bcmdresp.cmd);
+ } else if (bcmdresp.result != BOOT_CMD_RESP_OK) {
+ pr_info("boot cmd response result error (%d)\n",
+ bcmdresp.result);
+ } else {
+ cardp->bootcmdresp = 1;
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "Received valid boot command response\n");
+ }
+
+ kfree_skb(skb);
+ if_usb_submit_rx_urb_fwload(cardp);
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return;
+ }
+
+ syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader),
+ GFP_ATOMIC);
+ if (!syncfwheader) {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "Failure to allocate syncfwheader\n");
+ kfree_skb(skb);
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return;
+ }
+
+ if (!syncfwheader->cmd) {
+ lbtf_deb_usb2(&cardp->udev->dev,
+ "FW received Blk with correct CRC\n");
+ lbtf_deb_usb2(&cardp->udev->dev,
+ "FW received Blk seqnum = %d\n",
+ le32_to_cpu(syncfwheader->seqnum));
+ cardp->CRC_OK = 1;
+ } else {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "FW received Blk with CRC error\n");
+ cardp->CRC_OK = 0;
+ }
+
+ kfree_skb(skb);
+
+ /* reschedule timer for 200ms hence */
+ mod_timer(&cardp->fw_timeout, jiffies + (HZ/5));
+
+ if (cardp->fwfinalblk) {
+ cardp->fwdnldover = 1;
+ goto exit;
+ }
+
+ if_usb_send_fw_pkt(cardp);
+
+ exit:
+ if_usb_submit_rx_urb_fwload(cardp);
+
+ kfree(syncfwheader);
+
+ lbtf_deb_leave(LBTF_DEB_USB);
+}
+
+#define MRVDRV_MIN_PKT_LEN 30
+
+static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
+ struct if_usb_card *cardp,
+ struct lbtf_private *priv)
+{
+ if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN
+ || recvlength < MRVDRV_MIN_PKT_LEN) {
+ lbtf_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ skb_put(skb, recvlength);
+ skb_pull(skb, MESSAGE_HEADER_LEN);
+ lbtf_rx(priv, skb);
+}
+
+static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
+ struct sk_buff *skb,
+ struct if_usb_card *cardp,
+ struct lbtf_private *priv)
+{
+ if (recvlength > LBS_CMD_BUFFER_SIZE) {
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "The receive buffer is too large\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ BUG_ON(!in_interrupt());
+
+ spin_lock(&priv->driver_lock);
+ memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN,
+ recvlength - MESSAGE_HEADER_LEN);
+ kfree_skb(skb);
+ lbtf_cmd_response_rx(priv);
+ spin_unlock(&priv->driver_lock);
+}
+
+/**
+ * if_usb_receive - read data received from the device.
+ *
+ * @urb pointer to struct urb
+ */
+static void if_usb_receive(struct urb *urb)
+{
+ struct if_usb_card *cardp = urb->context;
+ struct sk_buff *skb = cardp->rx_skb;
+ struct lbtf_private *priv = cardp->priv;
+ int recvlength = urb->actual_length;
+ uint8_t *recvbuff = NULL;
+ uint32_t recvtype = 0;
+ __le32 *pkt = (__le32 *) skb->data;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+
+ if (recvlength) {
+ if (urb->status) {
+ lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n",
+ urb->status);
+ kfree_skb(skb);
+ goto setup_for_next;
+ }
+
+ recvbuff = skb->data;
+ recvtype = le32_to_cpu(pkt[0]);
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "Recv length = 0x%x, Recv type = 0x%X\n",
+ recvlength, recvtype);
+ } else if (urb->status) {
+ kfree_skb(skb);
+ lbtf_deb_leave(LBTF_DEB_USB);
+ return;
+ }
+
+ switch (recvtype) {
+ case CMD_TYPE_DATA:
+ process_cmdtypedata(recvlength, skb, cardp, priv);
+ break;
+
+ case CMD_TYPE_REQUEST:
+ process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
+ break;
+
+ case CMD_TYPE_INDICATION:
+ {
+ /* Event cause handling */
+ u32 event_cause = le32_to_cpu(pkt[1]);
+ lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n",
+ event_cause);
+
+ /* Icky undocumented magic special case */
+ if (event_cause & 0xffff0000) {
+ u16 tmp;
+ u8 retrycnt;
+ u8 failure;
+
+ tmp = event_cause >> 16;
+ retrycnt = tmp & 0x00ff;
+ failure = (tmp & 0xff00) >> 8;
+ lbtf_send_tx_feedback(priv, retrycnt, failure);
+ } else if (event_cause == LBTF_EVENT_BCN_SENT)
+ lbtf_bcn_sent(priv);
+ else
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "Unsupported notification %d received\n",
+ event_cause);
+ kfree_skb(skb);
+ break;
+ }
+ default:
+ lbtf_deb_usbd(&cardp->udev->dev,
+ "libertastf: unknown command type 0x%X\n", recvtype);
+ kfree_skb(skb);
+ break;
+ }
+
+setup_for_next:
+ if_usb_submit_rx_urb(cardp);
+ lbtf_deb_leave(LBTF_DEB_USB);
+}
+
+/**
+ * if_usb_host_to_card - Download data to the device
+ *
+ * @priv pointer to struct lbtf_private structure
+ * @type type of data
+ * @buf pointer to data buffer
+ * @len number of bytes
+ *
+ * Returns: 0 on success, nonzero otherwise
+ */
+static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type,
+ uint8_t *payload, uint16_t nb)
+{
+ struct if_usb_card *cardp = priv->card;
+ u8 data = 0;
+
+ lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type);
+ lbtf_deb_usbd(&cardp->udev->dev, "size after = %d\n", nb);
+
+ if (type == MVMS_CMD) {
+ *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST);
+ } else {
+ *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA);
+ data = 1;
+ }
+
+ memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb);
+
+ return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN,
+ data);
+}
+
+/**
+ * if_usb_issue_boot_command - Issue boot command to Boot2.
+ *
+ * @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM.
+ *
+ * Returns: 0
+ */
+static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
+{
+ struct bootcmd *bootcmd = cardp->ep_out_buf;
+
+ /* Prepare command */
+ bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER);
+ bootcmd->cmd = ivalue;
+ memset(bootcmd->pad, 0, sizeof(bootcmd->pad));
+
+ /* Issue command */
+ usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0);
+
+ return 0;
+}
+
+
+/**
+ * check_fwfile_format - Check the validity of Boot2/FW image.
+ *
+ * @data pointer to image
+ * @totlen image length
+ *
+ * Returns: 0 if the image is valid, nonzero otherwise.
+ */
+static int check_fwfile_format(const u8 *data, u32 totlen)
+{
+ u32 bincmd, exit;
+ u32 blksize, offset, len;
+ int ret;
+
+ ret = 1;
+ exit = len = 0;
+
+ do {
+ struct fwheader *fwh = (void *) data;
+
+ bincmd = le32_to_cpu(fwh->dnldcmd);
+ blksize = le32_to_cpu(fwh->datalength);
+ switch (bincmd) {
+ case FW_HAS_DATA_TO_RECV:
+ offset = sizeof(struct fwheader) + blksize;
+ data += offset;
+ len += offset;
+ if (len >= totlen)
+ exit = 1;
+ break;
+ case FW_HAS_LAST_BLOCK:
+ exit = 1;
+ ret = 0;
+ break;
+ default:
+ exit = 1;
+ break;
+ }
+ } while (!exit);
+
+ if (ret)
+ pr_err("firmware file format check FAIL\n");
+ else
+ lbtf_deb_fw("firmware file format check PASS\n");
+
+ return ret;
+}
+
+
+static int if_usb_prog_firmware(struct if_usb_card *cardp)
+{
+ int i = 0;
+ static int reset_count = 10;
+ int ret = 0;
+
+ lbtf_deb_enter(LBTF_DEB_USB);
+
+ kernel_param_lock(THIS_MODULE);
+ ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev);
+ if (ret < 0) {
+ pr_err("request_firmware() failed with %#x\n", ret);
+ pr_err("firmware %s not found\n", lbtf_fw_name);
+ kernel_param_unlock(THIS_MODULE);
+ goto done;
+ }
+ kernel_param_unlock(THIS_MODULE);
+
+ if (check_fwfile_format(cardp->fw->data, cardp->fw->size))
+ goto release_fw;
+
+restart:
+ if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
+ lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
+ ret = -1;
+ goto release_fw;
+ }
+
+ cardp->bootcmdresp = 0;
+ do {
+ int j = 0;
+ i++;
+ /* Issue Boot command = 1, Boot from Download-FW */
+ if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
+ /* wait for command response */
+ do {
+ j++;
+ msleep_interruptible(100);
+ } while (cardp->bootcmdresp == 0 && j < 10);
+ } while (cardp->bootcmdresp == 0 && i < 5);
+
+ if (cardp->bootcmdresp <= 0) {
+ if (--reset_count >= 0) {
+ if_usb_reset_device(cardp);
+ goto restart;
+ }
+ return -1;
+ }
+
+ i = 0;
+
+ cardp->totalbytes = 0;
+ cardp->fwlastblksent = 0;
+ cardp->CRC_OK = 1;
+ cardp->fwdnldover = 0;
+ cardp->fwseqnum = -1;
+ cardp->totalbytes = 0;
+ cardp->fwfinalblk = 0;
+
+ /* Send the first firmware packet... */
+ if_usb_send_fw_pkt(cardp);
+
+ /* ... and wait for the process to complete */
+ wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved ||
+ cardp->fwdnldover);
+
+ del_timer_sync(&cardp->fw_timeout);
+ usb_kill_urb(cardp->rx_urb);
+
+ if (!cardp->fwdnldover) {
+ pr_info("failed to load fw, resetting device!\n");
+ if (--reset_count >= 0) {
+ if_usb_reset_device(cardp);
+ goto restart;
+ }
+
+ pr_info("FW download failure, time = %d ms\n", i * 100);
+ ret = -1;
+ goto release_fw;
+ }
+
+ cardp->priv->fw_ready = 1;
+
+ release_fw:
+ release_firmware(cardp->fw);
+ cardp->fw = NULL;
+
+ if_usb_setup_firmware(cardp->priv);
+
+ done:
+ lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
+ return ret;
+}
+
+
+#define if_usb_suspend NULL
+#define if_usb_resume NULL
+
+static struct usb_driver if_usb_driver = {
+ .name = DRV_NAME,
+ .probe = if_usb_probe,
+ .disconnect = if_usb_disconnect,
+ .id_table = if_usb_table,
+ .suspend = if_usb_suspend,
+ .resume = if_usb_resume,
+ .disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(if_usb_driver);
+
+MODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver");
+MODULE_AUTHOR("Cozybit Inc.");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * 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.
+ */
+#include <linux/wait.h>
+#include <linux/timer.h>
+
+struct lbtf_private;
+
+/**
+ * This file contains definition for USB interface.
+ */
+#define CMD_TYPE_REQUEST 0xF00DFACE
+#define CMD_TYPE_DATA 0xBEADC0DE
+#define CMD_TYPE_INDICATION 0xBEEFFACE
+
+#define BOOT_CMD_FW_BY_USB 0x01
+#define BOOT_CMD_FW_IN_EEPROM 0x02
+#define BOOT_CMD_UPDATE_BOOT2 0x03
+#define BOOT_CMD_UPDATE_FW 0x04
+#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */
+
+struct bootcmd {
+ __le32 magic;
+ uint8_t cmd;
+ uint8_t pad[11];
+};
+
+#define BOOT_CMD_RESP_OK 0x0001
+#define BOOT_CMD_RESP_FAIL 0x0000
+
+struct bootcmdresp {
+ __le32 magic;
+ uint8_t cmd;
+ uint8_t result;
+ uint8_t pad[2];
+};
+
+/** USB card description structure*/
+struct if_usb_card {
+ struct usb_device *udev;
+ struct urb *rx_urb, *tx_urb, *cmd_urb;
+ struct lbtf_private *priv;
+
+ struct sk_buff *rx_skb;
+
+ uint8_t ep_in;
+ uint8_t ep_out;
+
+ int8_t bootcmdresp;
+
+ int ep_in_size;
+
+ void *ep_out_buf;
+ int ep_out_size;
+
+ const struct firmware *fw;
+ struct timer_list fw_timeout;
+ wait_queue_head_t fw_wq;
+ uint32_t fwseqnum;
+ uint32_t totalbytes;
+ uint32_t fwlastblksent;
+ uint8_t CRC_OK;
+ uint8_t fwdnldover;
+ uint8_t fwfinalblk;
+
+ __le16 boot2_version;
+};
+
+/** fwheader */
+struct fwheader {
+ __le32 dnldcmd;
+ __le32 baseaddr;
+ __le32 datalength;
+ __le32 CRC;
+};
+
+#define FW_MAX_DATA_BLK_SIZE 600
+/** FWData */
+struct fwdata {
+ struct fwheader hdr;
+ __le32 seqnum;
+ uint8_t data[0];
+};
+
+/** fwsyncheader */
+struct fwsyncheader {
+ __le32 cmd;
+ __le32 seqnum;
+};
+
+#define FW_HAS_DATA_TO_RECV 0x00000001
+#define FW_HAS_LAST_BLOCK 0x00000004
--- /dev/null
+/*
+ * Copyright (C) 2008, cozybit Inc.
+ * Copyright (C) 2007, Red Hat, 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.
+ */
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/kthread.h>
+#include <net/mac80211.h>
+
+#include "deb_defs.h"
+
+#ifndef DRV_NAME
+#define DRV_NAME "libertas_tf"
+#endif
+
+#define MRVL_DEFAULT_RETRIES 9
+#define MRVL_PER_PACKET_RATE 0x10
+#define MRVL_MAX_BCN_SIZE 440
+#define CMD_OPTION_WAITFORRSP 0x0002
+
+/* Return command are almost always the same as the host command, but with
+ * bit 15 set high. There are a few exceptions, though...
+ */
+#define CMD_RET(cmd) (0x8000 | cmd)
+
+/* Command codes */
+#define CMD_GET_HW_SPEC 0x0003
+#define CMD_802_11_RESET 0x0005
+#define CMD_MAC_MULTICAST_ADR 0x0010
+#define CMD_802_11_RADIO_CONTROL 0x001c
+#define CMD_802_11_RF_CHANNEL 0x001d
+#define CMD_802_11_RF_TX_POWER 0x001e
+#define CMD_MAC_CONTROL 0x0028
+#define CMD_802_11_MAC_ADDRESS 0x004d
+#define CMD_SET_BOOT2_VER 0x00a5
+#define CMD_802_11_BEACON_CTRL 0x00b0
+#define CMD_802_11_BEACON_SET 0x00cb
+#define CMD_802_11_SET_MODE 0x00cc
+#define CMD_802_11_SET_BSSID 0x00cd
+
+#define CMD_ACT_GET 0x0000
+#define CMD_ACT_SET 0x0001
+
+/* Define action or option for CMD_802_11_RESET */
+#define CMD_ACT_HALT 0x0003
+
+/* Define action or option for CMD_MAC_CONTROL */
+#define CMD_ACT_MAC_RX_ON 0x0001
+#define CMD_ACT_MAC_TX_ON 0x0002
+#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020
+#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040
+#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080
+#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100
+
+/* Define action or option for CMD_802_11_RADIO_CONTROL */
+#define CMD_TYPE_AUTO_PREAMBLE 0x0001
+#define CMD_TYPE_SHORT_PREAMBLE 0x0002
+#define CMD_TYPE_LONG_PREAMBLE 0x0003
+
+#define TURN_ON_RF 0x01
+#define RADIO_ON 0x01
+#define RADIO_OFF 0x00
+
+#define SET_AUTO_PREAMBLE 0x05
+#define SET_SHORT_PREAMBLE 0x03
+#define SET_LONG_PREAMBLE 0x01
+
+/* Define action or option for CMD_802_11_RF_CHANNEL */
+#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00
+#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01
+
+/* Codes for CMD_802_11_SET_MODE */
+enum lbtf_mode {
+ LBTF_PASSIVE_MODE,
+ LBTF_STA_MODE,
+ LBTF_AP_MODE,
+};
+
+/** Card Event definition */
+#define MACREG_INT_CODE_FIRMWARE_READY 48
+/** Buffer Constants */
+
+/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical
+* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas
+* driver has more local TxPDs. Each TxPD on the host memory is associated
+* with a Tx control node. The driver maintains 8 RxPD descriptors for
+* station firmware to store Rx packet information.
+*
+* Current version of MAC has a 32x6 multicast address buffer.
+*
+* 802.11b can have up to 14 channels, the driver keeps the
+* BSSID(MAC address) of each APs or Ad hoc stations it has sensed.
+*/
+
+#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32
+#define LBS_NUM_CMD_BUFFERS 10
+#define LBS_CMD_BUFFER_SIZE (2 * 1024)
+#define MRVDRV_MAX_CHANNEL_SIZE 14
+#define MRVDRV_SNAP_HEADER_LEN 8
+
+#define LBS_UPLD_SIZE 2312
+#define DEV_NAME_LEN 32
+
+/** Misc constants */
+/* This section defines 802.11 specific contants */
+
+#define MRVDRV_MAX_REGION_CODE 6
+/**
+ * the table to keep region code
+ */
+#define LBTF_REGDOMAIN_US 0x10
+#define LBTF_REGDOMAIN_CA 0x20
+#define LBTF_REGDOMAIN_EU 0x30
+#define LBTF_REGDOMAIN_SP 0x31
+#define LBTF_REGDOMAIN_FR 0x32
+#define LBTF_REGDOMAIN_JP 0x40
+
+#define SBI_EVENT_CAUSE_SHIFT 3
+
+/** RxPD status */
+
+#define MRVDRV_RXPD_STATUS_OK 0x0001
+
+
+/* This is for firmware specific length */
+#define EXTRA_LEN 36
+
+#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \
+ (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN)
+
+#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \
+ (ETH_FRAME_LEN + sizeof(struct rxpd) \
+ + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN)
+
+#define CMD_F_HOSTCMD (1 << 0)
+#define FW_CAPINFO_WPA (1 << 0)
+
+#define RF_ANTENNA_1 0x1
+#define RF_ANTENNA_2 0x2
+#define RF_ANTENNA_AUTO 0xFFFF
+
+#define LBTF_EVENT_BCN_SENT 55
+
+/** Global Variable Declaration */
+/** mv_ms_type */
+enum mv_ms_type {
+ MVMS_DAT = 0,
+ MVMS_CMD = 1,
+ MVMS_TXDONE = 2,
+ MVMS_EVENT
+};
+
+extern struct workqueue_struct *lbtf_wq;
+
+struct lbtf_private;
+
+struct lbtf_offset_value {
+ u32 offset;
+ u32 value;
+};
+
+struct channel_range {
+ u8 regdomain;
+ u8 start;
+ u8 end; /* exclusive (channel must be less than end) */
+};
+
+struct if_usb_card;
+
+/** Private structure for the MV device */
+struct lbtf_private {
+ void *card;
+ struct ieee80211_hw *hw;
+
+ /* Command response buffer */
+ u8 cmd_resp_buff[LBS_UPLD_SIZE];
+ /* Download sent:
+ bit0 1/0=data_sent/data_tx_done,
+ bit1 1/0=cmd_sent/cmd_tx_done,
+ all other bits reserved 0 */
+ struct ieee80211_vif *vif;
+
+ struct work_struct cmd_work;
+ struct work_struct tx_work;
+ /** Hardware access */
+ int (*hw_host_to_card) (struct lbtf_private *priv, u8 type, u8 *payload, u16 nb);
+ int (*hw_prog_firmware) (struct if_usb_card *cardp);
+ int (*hw_reset_device) (struct if_usb_card *cardp);
+
+
+ /** Wlan adapter data structure*/
+ /** STATUS variables */
+ u32 fwrelease;
+ u32 fwcapinfo;
+ /* protected with big lock */
+
+ struct mutex lock;
+
+ /** command-related variables */
+ u16 seqnum;
+ /* protected by big lock */
+
+ struct cmd_ctrl_node *cmd_array;
+ /** Current command */
+ struct cmd_ctrl_node *cur_cmd;
+ /** command Queues */
+ /** Free command buffers */
+ struct list_head cmdfreeq;
+ /** Pending command buffers */
+ struct list_head cmdpendingq;
+
+ /** spin locks */
+ spinlock_t driver_lock;
+
+ /** Timers */
+ struct timer_list command_timer;
+ int nr_retries;
+ int cmd_timed_out;
+
+ u8 cmd_response_rxed;
+
+ /** capability Info used in Association, start, join */
+ u16 capability;
+
+ /** MAC address information */
+ u8 current_addr[ETH_ALEN];
+ u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
+ u32 nr_of_multicastmacaddr;
+ int cur_freq;
+
+ struct sk_buff *skb_to_tx;
+ struct sk_buff *tx_skb;
+
+ /** NIC Operation characteristics */
+ u16 mac_control;
+ u16 regioncode;
+ struct channel_range range;
+
+ u8 radioon;
+ u32 preamble;
+
+ struct ieee80211_channel channels[14];
+ struct ieee80211_rate rates[12];
+ struct ieee80211_supported_band band;
+ struct lbtf_offset_value offsetvalue;
+
+ u8 fw_ready;
+ u8 surpriseremoved;
+ struct sk_buff_head bc_ps_buf;
+
+ /* Most recently reported noise in dBm */
+ s8 noise;
+};
+
+/* 802.11-related definitions */
+
+/* TxPD descriptor */
+struct txpd {
+ /* Current Tx packet status */
+ __le32 tx_status;
+ /* Tx control */
+ __le32 tx_control;
+ __le32 tx_packet_location;
+ /* Tx packet length */
+ __le16 tx_packet_length;
+ /* First 2 byte of destination MAC address */
+ u8 tx_dest_addr_high[2];
+ /* Last 4 byte of destination MAC address */
+ u8 tx_dest_addr_low[4];
+ /* Pkt Priority */
+ u8 priority;
+ /* Pkt Trasnit Power control */
+ u8 powermgmt;
+ /* Time the packet has been queued in the driver (units = 2ms) */
+ u8 pktdelay_2ms;
+ /* reserved */
+ u8 reserved1;
+};
+
+/* RxPD Descriptor */
+struct rxpd {
+ /* Current Rx packet status */
+ __le16 status;
+
+ /* SNR */
+ u8 snr;
+
+ /* Tx control */
+ u8 rx_control;
+
+ /* Pkt length */
+ __le16 pkt_len;
+
+ /* Noise Floor */
+ u8 nf;
+
+ /* Rx Packet Rate */
+ u8 rx_rate;
+
+ /* Pkt addr */
+ __le32 pkt_ptr;
+
+ /* Next Rx RxPD addr */
+ __le32 next_rxpd_ptr;
+
+ /* Pkt Priority */
+ u8 priority;
+ u8 reserved[3];
+};
+
+struct cmd_header {
+ __le16 command;
+ __le16 size;
+ __le16 seqnum;
+ __le16 result;
+} __packed;
+
+struct cmd_ctrl_node {
+ struct list_head list;
+ int result;
+ /* command response */
+ int (*callback)(struct lbtf_private *,
+ unsigned long, struct cmd_header *);
+ unsigned long callback_arg;
+ /* command data */
+ struct cmd_header *cmdbuf;
+ /* wait queue */
+ u16 cmdwaitqwoken;
+ wait_queue_head_t cmdwait_q;
+};
+
+/*
+ * Define data structure for CMD_GET_HW_SPEC
+ * This structure defines the response for the GET_HW_SPEC command
+ */
+struct cmd_ds_get_hw_spec {
+ struct cmd_header hdr;
+
+ /* HW Interface version number */
+ __le16 hwifversion;
+ /* HW version number */
+ __le16 version;
+ /* Max number of TxPD FW can handle */
+ __le16 nr_txpd;
+ /* Max no of Multicast address */
+ __le16 nr_mcast_adr;
+ /* MAC address */
+ u8 permanentaddr[6];
+
+ /* region Code */
+ __le16 regioncode;
+
+ /* Number of antenna used */
+ __le16 nr_antenna;
+
+ /* FW release number, example 0x01030304 = 2.3.4p1 */
+ __le32 fwrelease;
+
+ /* Base Address of TxPD queue */
+ __le32 wcb_base;
+ /* Read Pointer of RxPd queue */
+ __le32 rxpd_rdptr;
+
+ /* Write Pointer of RxPd queue */
+ __le32 rxpd_wrptr;
+
+ /*FW/HW capability */
+ __le32 fwcapinfo;
+} __packed;
+
+struct cmd_ds_mac_control {
+ struct cmd_header hdr;
+ __le16 action;
+ u16 reserved;
+};
+
+struct cmd_ds_802_11_mac_address {
+ struct cmd_header hdr;
+
+ __le16 action;
+ uint8_t macadd[ETH_ALEN];
+};
+
+struct cmd_ds_mac_multicast_addr {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 nr_of_adrs;
+ u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
+};
+
+struct cmd_ds_set_mode {
+ struct cmd_header hdr;
+
+ __le16 mode;
+};
+
+struct cmd_ds_set_bssid {
+ struct cmd_header hdr;
+
+ u8 bssid[6];
+ u8 activate;
+};
+
+struct cmd_ds_802_11_radio_control {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 control;
+};
+
+
+struct cmd_ds_802_11_rf_channel {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 channel;
+ __le16 rftype; /* unused */
+ __le16 reserved; /* unused */
+ u8 channellist[32]; /* unused */
+};
+
+struct cmd_ds_set_boot2_ver {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 version;
+};
+
+struct cmd_ds_802_11_reset {
+ struct cmd_header hdr;
+
+ __le16 action;
+};
+
+struct cmd_ds_802_11_beacon_control {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 beacon_enable;
+ __le16 beacon_period;
+};
+
+struct cmd_ds_802_11_beacon_set {
+ struct cmd_header hdr;
+
+ __le16 len;
+ u8 beacon[MRVL_MAX_BCN_SIZE];
+};
+
+struct lbtf_private;
+struct cmd_ctrl_node;
+
+/** Function Prototype Declaration */
+void lbtf_set_mac_control(struct lbtf_private *priv);
+
+int lbtf_free_cmd_buffer(struct lbtf_private *priv);
+
+int lbtf_allocate_cmd_buffer(struct lbtf_private *priv);
+int lbtf_execute_next_command(struct lbtf_private *priv);
+int lbtf_set_radio_control(struct lbtf_private *priv);
+int lbtf_update_hw_spec(struct lbtf_private *priv);
+int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv);
+void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode);
+void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid);
+int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr);
+
+int lbtf_set_channel(struct lbtf_private *priv, u8 channel);
+
+int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon);
+int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable,
+ int beacon_int);
+
+
+int lbtf_process_rx_command(struct lbtf_private *priv);
+void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd,
+ int result);
+void lbtf_cmd_response_rx(struct lbtf_private *priv);
+
+/* main.c */
+struct chan_freq_power *lbtf_get_region_cfp_table(u8 region,
+ int *cfp_no);
+struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev);
+int lbtf_remove_card(struct lbtf_private *priv);
+int lbtf_start_card(struct lbtf_private *priv);
+int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb);
+void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail);
+void lbtf_bcn_sent(struct lbtf_private *priv);
+
+/* support functions for cmd.c */
+/* lbtf_cmd() infers the size of the buffer to copy data back into, from
+ the size of the target of the pointer. Since the command to be sent
+ may often be smaller, that size is set in cmd->size by the caller.*/
+#define lbtf_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \
+ uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \
+ (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \
+ __lbtf_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \
+})
+
+#define lbtf_cmd_with_response(priv, cmdnr, cmd) \
+ lbtf_cmd(priv, cmdnr, cmd, lbtf_cmd_copyback, (unsigned long) (cmd))
+
+void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command,
+ struct cmd_header *in_cmd, int in_cmd_size);
+
+int __lbtf_cmd(struct lbtf_private *priv, uint16_t command,
+ struct cmd_header *in_cmd, int in_cmd_size,
+ int (*callback)(struct lbtf_private *, unsigned long,
+ struct cmd_header *),
+ unsigned long callback_arg);
+
+int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra,
+ struct cmd_header *resp);
--- /dev/null
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hardirq.h>
+#include <linux/slab.h>
+
+#include <linux/etherdevice.h>
+#include <linux/module.h>
+#include "libertas_tf.h"
+
+#define DRIVER_RELEASE_VERSION "004.p0"
+/* thinfirm version: 5.132.X.pX */
+#define LBTF_FW_VER_MIN 0x05840300
+#define LBTF_FW_VER_MAX 0x0584ffff
+#define QOS_CONTROL_LEN 2
+
+/* Module parameters */
+unsigned int lbtf_debug;
+EXPORT_SYMBOL_GPL(lbtf_debug);
+module_param_named(libertas_tf_debug, lbtf_debug, int, 0644);
+
+static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION
+#ifdef DEBUG
+ "-dbg"
+#endif
+ "";
+
+struct workqueue_struct *lbtf_wq;
+
+static const struct ieee80211_channel lbtf_channels[] = {
+ { .center_freq = 2412, .hw_value = 1 },
+ { .center_freq = 2417, .hw_value = 2 },
+ { .center_freq = 2422, .hw_value = 3 },
+ { .center_freq = 2427, .hw_value = 4 },
+ { .center_freq = 2432, .hw_value = 5 },
+ { .center_freq = 2437, .hw_value = 6 },
+ { .center_freq = 2442, .hw_value = 7 },
+ { .center_freq = 2447, .hw_value = 8 },
+ { .center_freq = 2452, .hw_value = 9 },
+ { .center_freq = 2457, .hw_value = 10 },
+ { .center_freq = 2462, .hw_value = 11 },
+ { .center_freq = 2467, .hw_value = 12 },
+ { .center_freq = 2472, .hw_value = 13 },
+ { .center_freq = 2484, .hw_value = 14 },
+};
+
+/* This table contains the hardware specific values for the modulation rates. */
+static const struct ieee80211_rate lbtf_rates[] = {
+ { .bitrate = 10,
+ .hw_value = 0, },
+ { .bitrate = 20,
+ .hw_value = 1,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55,
+ .hw_value = 2,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110,
+ .hw_value = 3,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 60,
+ .hw_value = 5,
+ .flags = 0 },
+ { .bitrate = 90,
+ .hw_value = 6,
+ .flags = 0 },
+ { .bitrate = 120,
+ .hw_value = 7,
+ .flags = 0 },
+ { .bitrate = 180,
+ .hw_value = 8,
+ .flags = 0 },
+ { .bitrate = 240,
+ .hw_value = 9,
+ .flags = 0 },
+ { .bitrate = 360,
+ .hw_value = 10,
+ .flags = 0 },
+ { .bitrate = 480,
+ .hw_value = 11,
+ .flags = 0 },
+ { .bitrate = 540,
+ .hw_value = 12,
+ .flags = 0 },
+};
+
+static void lbtf_cmd_work(struct work_struct *work)
+{
+ struct lbtf_private *priv = container_of(work, struct lbtf_private,
+ cmd_work);
+
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ spin_lock_irq(&priv->driver_lock);
+ /* command response? */
+ if (priv->cmd_response_rxed) {
+ priv->cmd_response_rxed = 0;
+ spin_unlock_irq(&priv->driver_lock);
+ lbtf_process_rx_command(priv);
+ spin_lock_irq(&priv->driver_lock);
+ }
+
+ if (priv->cmd_timed_out && priv->cur_cmd) {
+ struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
+
+ if (++priv->nr_retries > 10) {
+ lbtf_complete_command(priv, cmdnode,
+ -ETIMEDOUT);
+ priv->nr_retries = 0;
+ } else {
+ priv->cur_cmd = NULL;
+
+ /* Stick it back at the _top_ of the pending
+ * queue for immediate resubmission */
+ list_add(&cmdnode->list, &priv->cmdpendingq);
+ }
+ }
+ priv->cmd_timed_out = 0;
+ spin_unlock_irq(&priv->driver_lock);
+
+ if (!priv->fw_ready) {
+ lbtf_deb_leave_args(LBTF_DEB_CMD, "fw not ready");
+ return;
+ }
+
+ /* Execute the next command */
+ if (!priv->cur_cmd)
+ lbtf_execute_next_command(priv);
+
+ lbtf_deb_leave(LBTF_DEB_CMD);
+}
+
+/**
+ * lbtf_setup_firmware: initialize firmware.
+ *
+ * @priv A pointer to struct lbtf_private structure
+ *
+ * Returns: 0 on success.
+ */
+static int lbtf_setup_firmware(struct lbtf_private *priv)
+{
+ int ret = -1;
+
+ lbtf_deb_enter(LBTF_DEB_FW);
+ /*
+ * Read priv address from HW
+ */
+ eth_broadcast_addr(priv->current_addr);
+ ret = lbtf_update_hw_spec(priv);
+ if (ret) {
+ ret = -1;
+ goto done;
+ }
+
+ lbtf_set_mac_control(priv);
+ lbtf_set_radio_control(priv);
+
+ ret = 0;
+done:
+ lbtf_deb_leave_args(LBTF_DEB_FW, "ret: %d", ret);
+ return ret;
+}
+
+/**
+ * This function handles the timeout of command sending.
+ * It will re-send the same command again.
+ */
+static void command_timer_fn(unsigned long data)
+{
+ struct lbtf_private *priv = (struct lbtf_private *)data;
+ unsigned long flags;
+ lbtf_deb_enter(LBTF_DEB_CMD);
+
+ spin_lock_irqsave(&priv->driver_lock, flags);
+
+ if (!priv->cur_cmd) {
+ printk(KERN_DEBUG "libertastf: command timer expired; "
+ "no pending command\n");
+ goto out;
+ }
+
+ printk(KERN_DEBUG "libertas: command %x timed out\n",
+ le16_to_cpu(priv->cur_cmd->cmdbuf->command));
+
+ priv->cmd_timed_out = 1;
+ queue_work(lbtf_wq, &priv->cmd_work);
+out:
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ lbtf_deb_leave(LBTF_DEB_CMD);
+}
+
+static int lbtf_init_adapter(struct lbtf_private *priv)
+{
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+ eth_broadcast_addr(priv->current_addr);
+ mutex_init(&priv->lock);
+
+ priv->vif = NULL;
+ setup_timer(&priv->command_timer, command_timer_fn,
+ (unsigned long)priv);
+
+ INIT_LIST_HEAD(&priv->cmdfreeq);
+ INIT_LIST_HEAD(&priv->cmdpendingq);
+
+ spin_lock_init(&priv->driver_lock);
+
+ /* Allocate the command buffers */
+ if (lbtf_allocate_cmd_buffer(priv))
+ return -1;
+
+ lbtf_deb_leave(LBTF_DEB_MAIN);
+ return 0;
+}
+
+static void lbtf_free_adapter(struct lbtf_private *priv)
+{
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+ lbtf_free_cmd_buffer(priv);
+ del_timer(&priv->command_timer);
+ lbtf_deb_leave(LBTF_DEB_MAIN);
+}
+
+static void lbtf_op_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct lbtf_private *priv = hw->priv;
+
+ priv->skb_to_tx = skb;
+ queue_work(lbtf_wq, &priv->tx_work);
+ /*
+ * queue will be restarted when we receive transmission feedback if
+ * there are no buffered multicast frames to send
+ */
+ ieee80211_stop_queues(priv->hw);
+}
+
+static void lbtf_tx_work(struct work_struct *work)
+{
+ struct lbtf_private *priv = container_of(work, struct lbtf_private,
+ tx_work);
+ unsigned int len;
+ struct ieee80211_tx_info *info;
+ struct txpd *txpd;
+ struct sk_buff *skb = NULL;
+ int err;
+
+ lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX);
+
+ if ((priv->vif->type == NL80211_IFTYPE_AP) &&
+ (!skb_queue_empty(&priv->bc_ps_buf)))
+ skb = skb_dequeue(&priv->bc_ps_buf);
+ else if (priv->skb_to_tx) {
+ skb = priv->skb_to_tx;
+ priv->skb_to_tx = NULL;
+ } else {
+ lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
+ return;
+ }
+
+ len = skb->len;
+ info = IEEE80211_SKB_CB(skb);
+ txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd));
+
+ if (priv->surpriseremoved) {
+ dev_kfree_skb_any(skb);
+ lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
+ return;
+ }
+
+ memset(txpd, 0, sizeof(struct txpd));
+ /* Activate per-packet rate selection */
+ txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE |
+ ieee80211_get_tx_rate(priv->hw, info)->hw_value);
+
+ /* copy destination address from 802.11 header */
+ memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4,
+ ETH_ALEN);
+ txpd->tx_packet_length = cpu_to_le16(len);
+ txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
+ lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
+ BUG_ON(priv->tx_skb);
+ spin_lock_irq(&priv->driver_lock);
+ priv->tx_skb = skb;
+ err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len);
+ spin_unlock_irq(&priv->driver_lock);
+ if (err) {
+ dev_kfree_skb_any(skb);
+ priv->tx_skb = NULL;
+ pr_err("TX error: %d", err);
+ }
+ lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX);
+}
+
+static int lbtf_op_start(struct ieee80211_hw *hw)
+{
+ struct lbtf_private *priv = hw->priv;
+ void *card = priv->card;
+ int ret = -1;
+
+ lbtf_deb_enter(LBTF_DEB_MACOPS);
+
+ if (!priv->fw_ready)
+ /* Upload firmware */
+ if (priv->hw_prog_firmware(card))
+ goto err_prog_firmware;
+
+ /* poke the firmware */
+ priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
+ priv->radioon = RADIO_ON;
+ priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
+ ret = lbtf_setup_firmware(priv);
+ if (ret)
+ goto err_prog_firmware;
+
+ if ((priv->fwrelease < LBTF_FW_VER_MIN) ||
+ (priv->fwrelease > LBTF_FW_VER_MAX)) {
+ ret = -1;
+ goto err_prog_firmware;
+ }
+
+ printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+ return 0;
+
+err_prog_firmware:
+ priv->hw_reset_device(card);
+ lbtf_deb_leave_args(LBTF_DEB_MACOPS, "error programming fw; ret=%d", ret);
+ return ret;
+}
+
+static void lbtf_op_stop(struct ieee80211_hw *hw)
+{
+ struct lbtf_private *priv = hw->priv;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ struct cmd_ctrl_node *cmdnode;
+
+ lbtf_deb_enter(LBTF_DEB_MACOPS);
+
+ /* Flush pending command nodes */
+ spin_lock_irqsave(&priv->driver_lock, flags);
+ list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
+ cmdnode->result = -ENOENT;
+ cmdnode->cmdwaitqwoken = 1;
+ wake_up_interruptible(&cmdnode->cmdwait_q);
+ }
+
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
+ cancel_work_sync(&priv->cmd_work);
+ cancel_work_sync(&priv->tx_work);
+ while ((skb = skb_dequeue(&priv->bc_ps_buf)))
+ dev_kfree_skb_any(skb);
+ priv->radioon = RADIO_OFF;
+ lbtf_set_radio_control(priv);
+
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+}
+
+static int lbtf_op_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct lbtf_private *priv = hw->priv;
+ lbtf_deb_enter(LBTF_DEB_MACOPS);
+ if (priv->vif != NULL)
+ return -EOPNOTSUPP;
+
+ priv->vif = vif;
+ switch (vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ lbtf_set_mode(priv, LBTF_AP_MODE);
+ break;
+ case NL80211_IFTYPE_STATION:
+ lbtf_set_mode(priv, LBTF_STA_MODE);
+ break;
+ default:
+ priv->vif = NULL;
+ return -EOPNOTSUPP;
+ }
+ lbtf_set_mac_address(priv, (u8 *) vif->addr);
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+ return 0;
+}
+
+static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct lbtf_private *priv = hw->priv;
+ lbtf_deb_enter(LBTF_DEB_MACOPS);
+
+ if (priv->vif->type == NL80211_IFTYPE_AP ||
+ priv->vif->type == NL80211_IFTYPE_MESH_POINT)
+ lbtf_beacon_ctrl(priv, 0, 0);
+ lbtf_set_mode(priv, LBTF_PASSIVE_MODE);
+ lbtf_set_bssid(priv, 0, NULL);
+ priv->vif = NULL;
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+}
+
+static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct lbtf_private *priv = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ lbtf_deb_enter(LBTF_DEB_MACOPS);
+
+ if (conf->chandef.chan->center_freq != priv->cur_freq) {
+ priv->cur_freq = conf->chandef.chan->center_freq;
+ lbtf_set_channel(priv, conf->chandef.chan->hw_value);
+ }
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+ return 0;
+}
+
+static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct lbtf_private *priv = hw->priv;
+ int i;
+ struct netdev_hw_addr *ha;
+ int mc_count = netdev_hw_addr_list_count(mc_list);
+
+ if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE)
+ return mc_count;
+
+ priv->nr_of_multicastmacaddr = mc_count;
+ i = 0;
+ netdev_hw_addr_list_for_each(ha, mc_list)
+ memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN);
+
+ return mc_count;
+}
+
+#define SUPPORTED_FIF_FLAGS FIF_ALLMULTI
+static void lbtf_op_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *new_flags,
+ u64 multicast)
+{
+ struct lbtf_private *priv = hw->priv;
+ int old_mac_control = priv->mac_control;
+
+ lbtf_deb_enter(LBTF_DEB_MACOPS);
+
+ changed_flags &= SUPPORTED_FIF_FLAGS;
+ *new_flags &= SUPPORTED_FIF_FLAGS;
+
+ if (!changed_flags) {
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+ return;
+ }
+
+ priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
+ if (*new_flags & (FIF_ALLMULTI) ||
+ multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) {
+ priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
+ priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
+ } else if (multicast) {
+ priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
+ priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
+ lbtf_cmd_set_mac_multicast_addr(priv);
+ } else {
+ priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE |
+ CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
+ if (priv->nr_of_multicastmacaddr) {
+ priv->nr_of_multicastmacaddr = 0;
+ lbtf_cmd_set_mac_multicast_addr(priv);
+ }
+ }
+
+
+ if (priv->mac_control != old_mac_control)
+ lbtf_set_mac_control(priv);
+
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+}
+
+static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changes)
+{
+ struct lbtf_private *priv = hw->priv;
+ struct sk_buff *beacon;
+ lbtf_deb_enter(LBTF_DEB_MACOPS);
+
+ if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) {
+ switch (priv->vif->type) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ beacon = ieee80211_beacon_get(hw, vif);
+ if (beacon) {
+ lbtf_beacon_set(priv, beacon);
+ kfree_skb(beacon);
+ lbtf_beacon_ctrl(priv, 1,
+ bss_conf->beacon_int);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (changes & BSS_CHANGED_BSSID) {
+ bool activate = !is_zero_ether_addr(bss_conf->bssid);
+ lbtf_set_bssid(priv, activate, bss_conf->bssid);
+ }
+
+ if (changes & BSS_CHANGED_ERP_PREAMBLE) {
+ if (bss_conf->use_short_preamble)
+ priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
+ else
+ priv->preamble = CMD_TYPE_LONG_PREAMBLE;
+ lbtf_set_radio_control(priv);
+ }
+
+ lbtf_deb_leave(LBTF_DEB_MACOPS);
+}
+
+static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey)
+{
+ struct lbtf_private *priv = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+
+ if (idx != 0)
+ return -ENOENT;
+
+ survey->channel = conf->chandef.chan;
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ survey->noise = priv->noise;
+
+ return 0;
+}
+
+static const struct ieee80211_ops lbtf_ops = {
+ .tx = lbtf_op_tx,
+ .start = lbtf_op_start,
+ .stop = lbtf_op_stop,
+ .add_interface = lbtf_op_add_interface,
+ .remove_interface = lbtf_op_remove_interface,
+ .config = lbtf_op_config,
+ .prepare_multicast = lbtf_op_prepare_multicast,
+ .configure_filter = lbtf_op_configure_filter,
+ .bss_info_changed = lbtf_op_bss_info_changed,
+ .get_survey = lbtf_op_get_survey,
+};
+
+int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)
+{
+ struct ieee80211_rx_status stats;
+ struct rxpd *prxpd;
+ int need_padding;
+ unsigned int flags;
+ struct ieee80211_hdr *hdr;
+
+ lbtf_deb_enter(LBTF_DEB_RX);
+
+ prxpd = (struct rxpd *) skb->data;
+
+ memset(&stats, 0, sizeof(stats));
+ if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
+ stats.flag |= RX_FLAG_FAILED_FCS_CRC;
+ stats.freq = priv->cur_freq;
+ stats.band = IEEE80211_BAND_2GHZ;
+ stats.signal = prxpd->snr;
+ priv->noise = prxpd->nf;
+ /* Marvell rate index has a hole at value 4 */
+ if (prxpd->rx_rate > 4)
+ --prxpd->rx_rate;
+ stats.rate_idx = prxpd->rx_rate;
+ skb_pull(skb, sizeof(struct rxpd));
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ flags = le32_to_cpu(*(__le32 *)(skb->data + 4));
+
+ need_padding = ieee80211_is_data_qos(hdr->frame_control);
+ need_padding ^= ieee80211_has_a4(hdr->frame_control);
+ need_padding ^= ieee80211_is_data_qos(hdr->frame_control) &&
+ (*ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CTL_A_MSDU_PRESENT);
+
+ if (need_padding) {
+ memmove(skb->data + 2, skb->data, skb->len);
+ skb_reserve(skb, 2);
+ }
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+
+ lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
+ skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+ lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data,
+ min_t(unsigned int, skb->len, 100));
+
+ ieee80211_rx_irqsafe(priv->hw, skb);
+
+ lbtf_deb_leave(LBTF_DEB_RX);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lbtf_rx);
+
+/**
+ * lbtf_add_card: Add and initialize the card, no fw upload yet.
+ *
+ * @card A pointer to card
+ *
+ * Returns: pointer to struct lbtf_priv.
+ */
+struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev)
+{
+ struct ieee80211_hw *hw;
+ struct lbtf_private *priv = NULL;
+
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+
+ hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);
+ if (!hw)
+ goto done;
+
+ priv = hw->priv;
+ if (lbtf_init_adapter(priv))
+ goto err_init_adapter;
+
+ priv->hw = hw;
+ priv->card = card;
+ priv->tx_skb = NULL;
+
+ hw->queues = 1;
+ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+ hw->extra_tx_headroom = sizeof(struct txpd);
+ memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels));
+ memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates));
+ priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates);
+ priv->band.bitrates = priv->rates;
+ priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
+ priv->band.channels = priv->channels;
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC);
+ skb_queue_head_init(&priv->bc_ps_buf);
+
+ SET_IEEE80211_DEV(hw, dmdev);
+
+ INIT_WORK(&priv->cmd_work, lbtf_cmd_work);
+ INIT_WORK(&priv->tx_work, lbtf_tx_work);
+ if (ieee80211_register_hw(hw))
+ goto err_init_adapter;
+
+ goto done;
+
+err_init_adapter:
+ lbtf_free_adapter(priv);
+ ieee80211_free_hw(hw);
+ priv = NULL;
+
+done:
+ lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv);
+ return priv;
+}
+EXPORT_SYMBOL_GPL(lbtf_add_card);
+
+
+int lbtf_remove_card(struct lbtf_private *priv)
+{
+ struct ieee80211_hw *hw = priv->hw;
+
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+
+ priv->surpriseremoved = 1;
+ del_timer(&priv->command_timer);
+ lbtf_free_adapter(priv);
+ priv->hw = NULL;
+ ieee80211_unregister_hw(hw);
+ ieee80211_free_hw(hw);
+
+ lbtf_deb_leave(LBTF_DEB_MAIN);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lbtf_remove_card);
+
+void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
+
+ ieee80211_tx_info_clear_status(info);
+ /*
+ * Commented out, otherwise we never go beyond 1Mbit/s using mac80211
+ * default pid rc algorithm.
+ *
+ * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;
+ */
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ skb_pull(priv->tx_skb, sizeof(struct txpd));
+ ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);
+ priv->tx_skb = NULL;
+ if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf))
+ ieee80211_wake_queues(priv->hw);
+ else
+ queue_work(lbtf_wq, &priv->tx_work);
+}
+EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback);
+
+void lbtf_bcn_sent(struct lbtf_private *priv)
+{
+ struct sk_buff *skb = NULL;
+
+ if (priv->vif->type != NL80211_IFTYPE_AP)
+ return;
+
+ if (skb_queue_empty(&priv->bc_ps_buf)) {
+ bool tx_buff_bc = false;
+
+ while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) {
+ skb_queue_tail(&priv->bc_ps_buf, skb);
+ tx_buff_bc = true;
+ }
+ if (tx_buff_bc) {
+ ieee80211_stop_queues(priv->hw);
+ queue_work(lbtf_wq, &priv->tx_work);
+ }
+ }
+
+ skb = ieee80211_beacon_get(priv->hw, priv->vif);
+
+ if (skb) {
+ lbtf_beacon_set(priv, skb);
+ kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL_GPL(lbtf_bcn_sent);
+
+static int __init lbtf_init_module(void)
+{
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+ lbtf_wq = create_workqueue("libertastf");
+ if (lbtf_wq == NULL) {
+ printk(KERN_ERR "libertastf: couldn't create workqueue\n");
+ return -ENOMEM;
+ }
+ lbtf_deb_leave(LBTF_DEB_MAIN);
+ return 0;
+}
+
+static void __exit lbtf_exit_module(void)
+{
+ lbtf_deb_enter(LBTF_DEB_MAIN);
+ destroy_workqueue(lbtf_wq);
+ lbtf_deb_leave(LBTF_DEB_MAIN);
+}
+
+module_init(lbtf_init_module);
+module_exit(lbtf_exit_module);
+
+MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library");
+MODULE_AUTHOR("Cozybit Inc.");
+MODULE_LICENSE("GPL");