libertas_tf: move under marvell vendor directory

Part of reorganising wireless drivers directory and Kconfig.

Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Cette révision appartient à :
Kalle Valo
2015-11-17 21:11:21 +02:00
Parent f988d64010
révision dd3f92dea8
12 fichiers modifiés avec 21 ajouts et 21 suppressions

Voir le fichier

@@ -0,0 +1,18 @@
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.

Voir le fichier

@@ -0,0 +1,6 @@
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

Voir le fichier

@@ -0,0 +1,807 @@
/*
* 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;
}

Voir le fichier

@@ -0,0 +1,104 @@
/**
* 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

Voir le fichier

@@ -0,0 +1,928 @@
/*
* 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");

Voir le fichier

@@ -0,0 +1,98 @@
/*
* 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

Voir le fichier

@@ -0,0 +1,519 @@
/*
* 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);

Voir le fichier

@@ -0,0 +1,766 @@
/*
* 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");