linux/drivers/net/wireless/libertas/cmd.c
Holger Schurig 675787e29f libertas: handy function to call firmware commands
Using an arbitrary firmware command was actually very painful. One
had to change big switch() statements in cmd.c, cmdresp.c, add
structs to the big union in "struct cmd_ds_command" and add the
define for the CMD_802_11_xxx to the proper place.

With this function, this is now much easier. For now, it implements
a blocking (a.k.a. CMD_OPTION_WAITFORRSP) way where one deals directly
with command requests and response buffers. You can do everything in
one place:

Signed-off-by: Holger Schurig <hs4233@mail.mn-solutions.de>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-01-28 15:06:04 -08:00

2105 lines
55 KiB
C

/**
* This file contains the handling of command.
* It prepares command and sends it to firmware when it is ready.
*/
#include <net/iw_handler.h>
#include "host.h"
#include "hostcmd.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
#include "join.h"
#include "wext.h"
static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode);
struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
void lbs_set_cmd_ctrl_node(struct lbs_private *priv,
struct cmd_ctrl_node *ptempnode,
u16 wait_option, void *pdata_buf);
static u16 commands_allowed_in_ps[] = {
CMD_802_11_RSSI,
};
/**
* @brief This function checks if the commans is allowed
* in PS mode not.
*
* @param command the command ID
* @return TRUE or FALSE
*/
static u8 is_command_allowed_in_ps(__le16 command)
{
int i;
for (i = 0; i < ARRAY_SIZE(commands_allowed_in_ps); i++) {
if (command == cpu_to_le16(commands_allowed_in_ps[i]))
return 1;
}
return 0;
}
static int lbs_cmd_hw_spec(struct lbs_private *priv, struct cmd_ds_command *cmd)
{
struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_GET_HW_SPEC);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN);
memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_ps_mode(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action)
{
struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_PS_MODE);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) +
S_DS_GEN);
psm->action = cpu_to_le16(cmd_action);
psm->multipledtim = 0;
switch (cmd_action) {
case CMD_SUBCMD_ENTER_PS:
lbs_deb_cmd("PS command:" "SubCode- Enter PS\n");
psm->locallisteninterval = 0;
psm->nullpktinterval = 0;
psm->multipledtim =
cpu_to_le16(MRVDRV_DEFAULT_MULTIPLE_DTIM);
break;
case CMD_SUBCMD_EXIT_PS:
lbs_deb_cmd("PS command:" "SubCode- Exit PS\n");
break;
case CMD_SUBCMD_SLEEP_CONFIRMED:
lbs_deb_cmd("PS command: SubCode- sleep confirm\n");
break;
default:
break;
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action, void *pdata_buf)
{
u16 *timeout = pdata_buf;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_INACTIVITY_TIMEOUT);
cmd->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout)
+ S_DS_GEN);
cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action);
if (cmd_action)
cmd->params.inactivity_timeout.timeout = cpu_to_le16(*timeout);
else
cmd->params.inactivity_timeout.timeout = 0;
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_sleep_params(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action)
{
struct lbs_adapter *adapter = priv->adapter;
struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params;
lbs_deb_enter(LBS_DEB_CMD);
cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) +
S_DS_GEN);
cmd->command = cpu_to_le16(CMD_802_11_SLEEP_PARAMS);
if (cmd_action == CMD_ACT_GET) {
memset(&adapter->sp, 0, sizeof(struct sleep_params));
memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params));
sp->action = cpu_to_le16(cmd_action);
} else if (cmd_action == CMD_ACT_SET) {
sp->action = cpu_to_le16(cmd_action);
sp->error = cpu_to_le16(adapter->sp.sp_error);
sp->offset = cpu_to_le16(adapter->sp.sp_offset);
sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime);
sp->calcontrol = (u8) adapter->sp.sp_calcontrol;
sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk;
sp->reserved = cpu_to_le16(adapter->sp.sp_reserved);
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_set_wep(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u32 cmd_act,
void * pdata_buf)
{
struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep;
struct lbs_adapter *adapter = priv->adapter;
int ret = 0;
struct assoc_request * assoc_req = pdata_buf;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_SET_WEP);
cmd->size = cpu_to_le16(sizeof(*wep) + S_DS_GEN);
if (cmd_act == CMD_ACT_ADD) {
int i;
if (!assoc_req) {
lbs_deb_cmd("Invalid association request!");
ret = -1;
goto done;
}
wep->action = cpu_to_le16(CMD_ACT_ADD);
/* default tx key index */
wep->keyindex = cpu_to_le16((u16)(assoc_req->wep_tx_keyidx &
(u32)CMD_WEP_KEY_INDEX_MASK));
/* Copy key types and material to host command structure */
for (i = 0; i < 4; i++) {
struct enc_key * pkey = &assoc_req->wep_keys[i];
switch (pkey->len) {
case KEY_LEN_WEP_40:
wep->keytype[i] = CMD_TYPE_WEP_40_BIT;
memmove(&wep->keymaterial[i], pkey->key,
pkey->len);
lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i);
break;
case KEY_LEN_WEP_104:
wep->keytype[i] = CMD_TYPE_WEP_104_BIT;
memmove(&wep->keymaterial[i], pkey->key,
pkey->len);
lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i);
break;
case 0:
break;
default:
lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n",
i, pkey->len);
ret = -1;
goto done;
break;
}
}
} else if (cmd_act == CMD_ACT_REMOVE) {
/* ACT_REMOVE clears _all_ WEP keys */
wep->action = cpu_to_le16(CMD_ACT_REMOVE);
/* default tx key index */
wep->keyindex = cpu_to_le16((u16)(adapter->wep_tx_keyidx &
(u32)CMD_WEP_KEY_INDEX_MASK));
lbs_deb_cmd("SET_WEP: remove key %d\n", adapter->wep_tx_keyidx);
}
ret = 0;
done:
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
static int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action,
void * pdata_buf)
{
struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn;
u32 * enable = pdata_buf;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_ENABLE_RSN);
cmd->size = cpu_to_le16(sizeof(*penableRSN) + S_DS_GEN);
penableRSN->action = cpu_to_le16(cmd_action);
if (cmd_action == CMD_ACT_SET) {
if (*enable)
penableRSN->enable = cpu_to_le16(CMD_ENABLE_RSN);
else
penableRSN->enable = cpu_to_le16(CMD_DISABLE_RSN);
lbs_deb_cmd("ENABLE_RSN: %d\n", *enable);
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static ssize_t lbs_tlv_size(const u8 *tlv, u16 size)
{
ssize_t pos = 0;
struct mrvlietypesheader *tlv_h;
while (pos < size) {
u16 length;
tlv_h = (struct mrvlietypesheader *) tlv;
if (tlv_h->len == 0)
return pos;
length = le16_to_cpu(tlv_h->len) +
sizeof(struct mrvlietypesheader);
pos += length;
tlv += length;
}
return pos;
}
static void lbs_cmd_802_11_subscribe_event(struct lbs_private *priv,
struct cmd_ds_command *cmd, u16 cmd_action,
void *pdata_buf)
{
struct cmd_ds_802_11_subscribe_event *events =
(struct cmd_ds_802_11_subscribe_event *) pdata_buf;
/* pdata_buf points to a struct cmd_ds_802_11_subscribe_event and room
* for various Marvell TLVs */
lbs_deb_enter(LBS_DEB_CMD);
cmd->size = cpu_to_le16(sizeof(*events)
- sizeof(events->tlv)
+ S_DS_GEN);
cmd->params.subscribe_event.action = cpu_to_le16(cmd_action);
if (cmd_action == CMD_ACT_GET) {
cmd->params.subscribe_event.events = 0;
} else {
ssize_t sz = lbs_tlv_size(events->tlv, sizeof(events->tlv));
cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + sz);
cmd->params.subscribe_event.events = events->events;
memcpy(cmd->params.subscribe_event.tlv, events->tlv, sz);
}
lbs_deb_leave(LBS_DEB_CMD);
}
static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset,
struct enc_key * pkey)
{
lbs_deb_enter(LBS_DEB_CMD);
if (pkey->flags & KEY_INFO_WPA_ENABLED) {
pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
}
if (pkey->flags & KEY_INFO_WPA_UNICAST) {
pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
}
if (pkey->flags & KEY_INFO_WPA_MCAST) {
pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
}
pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
pkeyparamset->keytypeid = cpu_to_le16(pkey->type);
pkeyparamset->keylen = cpu_to_le16(pkey->len);
memcpy(pkeyparamset->key, pkey->key, pkey->len);
pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid)
+ sizeof(pkeyparamset->keyinfo)
+ sizeof(pkeyparamset->keylen)
+ sizeof(pkeyparamset->key));
lbs_deb_leave(LBS_DEB_CMD);
}
static int lbs_cmd_802_11_key_material(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action,
u32 cmd_oid, void *pdata_buf)
{
struct cmd_ds_802_11_key_material *pkeymaterial =
&cmd->params.keymaterial;
struct assoc_request * assoc_req = pdata_buf;
int ret = 0;
int index = 0;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_KEY_MATERIAL);
pkeymaterial->action = cpu_to_le16(cmd_action);
if (cmd_action == CMD_ACT_GET) {
cmd->size = cpu_to_le16(S_DS_GEN + sizeof (pkeymaterial->action));
ret = 0;
goto done;
}
memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet));
if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
set_one_wpa_key(&pkeymaterial->keyParamSet[index],
&assoc_req->wpa_unicast_key);
index++;
}
if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
set_one_wpa_key(&pkeymaterial->keyParamSet[index],
&assoc_req->wpa_mcast_key);
index++;
}
cmd->size = cpu_to_le16( S_DS_GEN
+ sizeof (pkeymaterial->action)
+ (index * sizeof(struct MrvlIEtype_keyParamSet)));
ret = 0;
done:
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
static int lbs_cmd_802_11_reset(struct lbs_private *priv,
struct cmd_ds_command *cmd, int cmd_action)
{
struct cmd_ds_802_11_reset *reset = &cmd->params.reset;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_RESET);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN);
reset->action = cpu_to_le16(cmd_action);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_get_log(struct lbs_private *priv,
struct cmd_ds_command *cmd)
{
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_GET_LOG);
cmd->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_get_stat(struct lbs_private *priv,
struct cmd_ds_command *cmd)
{
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_GET_STAT);
cmd->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + S_DS_GEN);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_snmp_mib(struct lbs_private *priv,
struct cmd_ds_command *cmd,
int cmd_action,
int cmd_oid, void *pdata_buf)
{
struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib;
struct lbs_adapter *adapter = priv->adapter;
u8 ucTemp;
lbs_deb_enter(LBS_DEB_CMD);
lbs_deb_cmd("SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid);
cmd->command = cpu_to_le16(CMD_802_11_SNMP_MIB);
cmd->size = cpu_to_le16(sizeof(*pSNMPMIB) + S_DS_GEN);
switch (cmd_oid) {
case OID_802_11_INFRASTRUCTURE_MODE:
{
u8 mode = (u8) (size_t) pdata_buf;
pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
pSNMPMIB->oid = cpu_to_le16((u16) DESIRED_BSSTYPE_I);
pSNMPMIB->bufsize = sizeof(u8);
if (mode == IW_MODE_ADHOC) {
ucTemp = SNMP_MIB_VALUE_ADHOC;
} else {
/* Infra and Auto modes */
ucTemp = SNMP_MIB_VALUE_INFRA;
}
memmove(pSNMPMIB->value, &ucTemp, sizeof(u8));
break;
}
case OID_802_11D_ENABLE:
{
u32 ulTemp;
pSNMPMIB->oid = cpu_to_le16((u16) DOT11D_I);
if (cmd_action == CMD_ACT_SET) {
pSNMPMIB->querytype = CMD_ACT_SET;
pSNMPMIB->bufsize = sizeof(u16);
ulTemp = *(u32 *)pdata_buf;
*((__le16 *)(pSNMPMIB->value)) =
cpu_to_le16((u16) ulTemp);
}
break;
}
case OID_802_11_FRAGMENTATION_THRESHOLD:
{
u32 ulTemp;
pSNMPMIB->oid = cpu_to_le16((u16) FRAGTHRESH_I);
if (cmd_action == CMD_ACT_GET) {
pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_GET);
} else if (cmd_action == CMD_ACT_SET) {
pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
ulTemp = *((u32 *) pdata_buf);
*((__le16 *)(pSNMPMIB->value)) =
cpu_to_le16((u16) ulTemp);
}
break;
}
case OID_802_11_RTS_THRESHOLD:
{
u32 ulTemp;
pSNMPMIB->oid = le16_to_cpu((u16) RTSTHRESH_I);
if (cmd_action == CMD_ACT_GET) {
pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_GET);
} else if (cmd_action == CMD_ACT_SET) {
pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
ulTemp = *((u32 *)pdata_buf);
*(__le16 *)(pSNMPMIB->value) =
cpu_to_le16((u16) ulTemp);
}
break;
}
case OID_802_11_TX_RETRYCOUNT:
pSNMPMIB->oid = cpu_to_le16((u16) SHORT_RETRYLIM_I);
if (cmd_action == CMD_ACT_GET) {
pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_GET);
} else if (cmd_action == CMD_ACT_SET) {
pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
*((__le16 *)(pSNMPMIB->value)) =
cpu_to_le16((u16) adapter->txretrycount);
}
break;
default:
break;
}
lbs_deb_cmd(
"SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n",
le16_to_cpu(cmd->command), le16_to_cpu(cmd->size),
le16_to_cpu(cmd->seqnum), le16_to_cpu(cmd->result));
lbs_deb_cmd(
"SNMP_CMD: action 0x%x, oid 0x%x, oidsize 0x%x, value 0x%x\n",
le16_to_cpu(pSNMPMIB->querytype), le16_to_cpu(pSNMPMIB->oid),
le16_to_cpu(pSNMPMIB->bufsize),
le16_to_cpu(*(__le16 *) pSNMPMIB->value));
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_radio_control(struct lbs_private *priv,
struct cmd_ds_command *cmd,
int cmd_action)
{
struct lbs_adapter *adapter = priv->adapter;
struct cmd_ds_802_11_radio_control *pradiocontrol = &cmd->params.radio;
lbs_deb_enter(LBS_DEB_CMD);
cmd->size =
cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) +
S_DS_GEN);
cmd->command = cpu_to_le16(CMD_802_11_RADIO_CONTROL);
pradiocontrol->action = cpu_to_le16(cmd_action);
switch (adapter->preamble) {
case CMD_TYPE_SHORT_PREAMBLE:
pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE);
break;
case CMD_TYPE_LONG_PREAMBLE:
pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE);
break;
case CMD_TYPE_AUTO_PREAMBLE:
default:
pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE);
break;
}
if (adapter->radioon)
pradiocontrol->control |= cpu_to_le16(TURN_ON_RF);
else
pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_rf_tx_power(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action, void *pdata_buf)
{
struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp;
lbs_deb_enter(LBS_DEB_CMD);
cmd->size =
cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + S_DS_GEN);
cmd->command = cpu_to_le16(CMD_802_11_RF_TX_POWER);
prtp->action = cpu_to_le16(cmd_action);
lbs_deb_cmd("RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n",
le16_to_cpu(cmd->size), le16_to_cpu(cmd->command),
le16_to_cpu(prtp->action));
switch (cmd_action) {
case CMD_ACT_TX_POWER_OPT_GET:
prtp->action = cpu_to_le16(CMD_ACT_GET);
prtp->currentlevel = 0;
break;
case CMD_ACT_TX_POWER_OPT_SET_HIGH:
prtp->action = cpu_to_le16(CMD_ACT_SET);
prtp->currentlevel = cpu_to_le16(CMD_ACT_TX_POWER_INDEX_HIGH);
break;
case CMD_ACT_TX_POWER_OPT_SET_MID:
prtp->action = cpu_to_le16(CMD_ACT_SET);
prtp->currentlevel = cpu_to_le16(CMD_ACT_TX_POWER_INDEX_MID);
break;
case CMD_ACT_TX_POWER_OPT_SET_LOW:
prtp->action = cpu_to_le16(CMD_ACT_SET);
prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf));
break;
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_monitor_mode(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action, void *pdata_buf)
{
struct cmd_ds_802_11_monitor_mode *monitor = &cmd->params.monitor;
cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE);
cmd->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) +
S_DS_GEN);
monitor->action = cpu_to_le16(cmd_action);
if (cmd_action == CMD_ACT_SET) {
monitor->mode =
cpu_to_le16((u16) (*(u32 *) pdata_buf));
}
return 0;
}
static int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action)
{
struct cmd_ds_802_11_rate_adapt_rateset
*rateadapt = &cmd->params.rateset;
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_CMD);
cmd->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset)
+ S_DS_GEN);
cmd->command = cpu_to_le16(CMD_802_11_RATE_ADAPT_RATESET);
rateadapt->action = cpu_to_le16(cmd_action);
rateadapt->enablehwauto = cpu_to_le16(adapter->enablehwauto);
rateadapt->bitmap = cpu_to_le16(adapter->ratebitmap);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_data_rate(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action)
{
struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate;
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_CMD);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) +
S_DS_GEN);
cmd->command = cpu_to_le16(CMD_802_11_DATA_RATE);
memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate));
pdatarate->action = cpu_to_le16(cmd_action);
if (cmd_action == CMD_ACT_SET_TX_FIX_RATE) {
pdatarate->rates[0] = lbs_data_rate_to_fw_index(adapter->cur_rate);
lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n",
adapter->cur_rate);
} else if (cmd_action == CMD_ACT_SET_TX_AUTO) {
lbs_deb_cmd("DATA_RATE: setting auto\n");
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_mac_multicast_adr(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action)
{
struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr;
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_CMD);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) +
S_DS_GEN);
cmd->command = cpu_to_le16(CMD_MAC_MULTICAST_ADR);
lbs_deb_cmd("MULTICAST_ADR: setting %d addresses\n", pMCastAdr->nr_of_adrs);
pMCastAdr->action = cpu_to_le16(cmd_action);
pMCastAdr->nr_of_adrs =
cpu_to_le16((u16) adapter->nr_of_multicastmacaddr);
memcpy(pMCastAdr->maclist, adapter->multicastlist,
adapter->nr_of_multicastmacaddr * ETH_ALEN);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_rf_channel(struct lbs_private *priv,
struct cmd_ds_command *cmd,
int option, void *pdata_buf)
{
struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_RF_CHANNEL);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel) +
S_DS_GEN);
if (option == CMD_OPT_802_11_RF_CHANNEL_SET) {
rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf));
}
rfchan->action = cpu_to_le16(option);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_rssi(struct lbs_private *priv,
struct cmd_ds_command *cmd)
{
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_RSSI);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN);
cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR);
/* reset Beacon SNR/NF/RSSI values */
adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0;
adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
adapter->NF[TYPE_BEACON][TYPE_AVG] = 0;
adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_reg_access(struct lbs_private *priv,
struct cmd_ds_command *cmdptr,
u8 cmd_action, void *pdata_buf)
{
struct lbs_offset_value *offval;
lbs_deb_enter(LBS_DEB_CMD);
offval = (struct lbs_offset_value *)pdata_buf;
switch (cmdptr->command) {
case CMD_MAC_REG_ACCESS:
{
struct cmd_ds_mac_reg_access *macreg;
cmdptr->size =
cpu_to_le16(sizeof (struct cmd_ds_mac_reg_access)
+ S_DS_GEN);
macreg =
(struct cmd_ds_mac_reg_access *)&cmdptr->params.
macreg;
macreg->action = cpu_to_le16(cmd_action);
macreg->offset = cpu_to_le16((u16) offval->offset);
macreg->value = cpu_to_le32(offval->value);
break;
}
case CMD_BBP_REG_ACCESS:
{
struct cmd_ds_bbp_reg_access *bbpreg;
cmdptr->size =
cpu_to_le16(sizeof
(struct cmd_ds_bbp_reg_access)
+ S_DS_GEN);
bbpreg =
(struct cmd_ds_bbp_reg_access *)&cmdptr->params.
bbpreg;
bbpreg->action = cpu_to_le16(cmd_action);
bbpreg->offset = cpu_to_le16((u16) offval->offset);
bbpreg->value = (u8) offval->value;
break;
}
case CMD_RF_REG_ACCESS:
{
struct cmd_ds_rf_reg_access *rfreg;
cmdptr->size =
cpu_to_le16(sizeof
(struct cmd_ds_rf_reg_access) +
S_DS_GEN);
rfreg =
(struct cmd_ds_rf_reg_access *)&cmdptr->params.
rfreg;
rfreg->action = cpu_to_le16(cmd_action);
rfreg->offset = cpu_to_le16((u16) offval->offset);
rfreg->value = (u8) offval->value;
break;
}
default:
break;
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_mac_address(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action)
{
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_MAC_ADDRESS);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) +
S_DS_GEN);
cmd->result = 0;
cmd->params.macadd.action = cpu_to_le16(cmd_action);
if (cmd_action == CMD_ACT_SET) {
memcpy(cmd->params.macadd.macadd,
adapter->current_addr, ETH_ALEN);
lbs_deb_hex(LBS_DEB_CMD, "SET_CMD: MAC addr", adapter->current_addr, 6);
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_802_11_eeprom_access(struct lbs_private *priv,
struct cmd_ds_command *cmd,
int cmd_action, void *pdata_buf)
{
struct lbs_ioctl_regrdwr *ea = pdata_buf;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_802_11_EEPROM_ACCESS);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) +
S_DS_GEN);
cmd->result = 0;
cmd->params.rdeeprom.action = cpu_to_le16(ea->action);
cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset);
cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB);
cmd->params.rdeeprom.value = 0;
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_bt_access(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action, void *pdata_buf)
{
struct cmd_ds_bt_access *bt_access = &cmd->params.bt;
lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
cmd->command = cpu_to_le16(CMD_BT_ACCESS);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + S_DS_GEN);
cmd->result = 0;
bt_access->action = cpu_to_le16(cmd_action);
switch (cmd_action) {
case CMD_ACT_BT_ACCESS_ADD:
memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN);
lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr", bt_access->addr1, 6);
break;
case CMD_ACT_BT_ACCESS_DEL:
memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN);
lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr", bt_access->addr1, 6);
break;
case CMD_ACT_BT_ACCESS_LIST:
bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
break;
case CMD_ACT_BT_ACCESS_RESET:
break;
case CMD_ACT_BT_ACCESS_SET_INVERT:
bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
break;
case CMD_ACT_BT_ACCESS_GET_INVERT:
break;
default:
break;
}
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_fwt_access(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action, void *pdata_buf)
{
struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt;
lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
cmd->command = cpu_to_le16(CMD_FWT_ACCESS);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + S_DS_GEN);
cmd->result = 0;
if (pdata_buf)
memcpy(fwt_access, pdata_buf, sizeof(*fwt_access));
else
memset(fwt_access, 0, sizeof(*fwt_access));
fwt_access->action = cpu_to_le16(cmd_action);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_mesh_access(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action, void *pdata_buf)
{
struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh;
lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
cmd->command = cpu_to_le16(CMD_MESH_ACCESS);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) + S_DS_GEN);
cmd->result = 0;
if (pdata_buf)
memcpy(mesh_access, pdata_buf, sizeof(*mesh_access));
else
memset(mesh_access, 0, sizeof(*mesh_access));
mesh_access->action = cpu_to_le16(cmd_action);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_bcn_ctrl(struct lbs_private * priv,
struct cmd_ds_command *cmd,
u16 cmd_action)
{
struct cmd_ds_802_11_beacon_control
*bcn_ctrl = &cmd->params.bcn_ctrl;
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_CMD);
cmd->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control)
+ S_DS_GEN);
cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL);
bcn_ctrl->action = cpu_to_le16(cmd_action);
bcn_ctrl->beacon_enable = cpu_to_le16(adapter->beacon_enable);
bcn_ctrl->beacon_period = cpu_to_le16(adapter->beacon_period);
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
static int lbs_cmd_set_boot2_ver(struct lbs_private *priv,
struct cmd_ds_command *cmd,
u16 cmd_action, void *pdata_buf)
{
struct cmd_ds_set_boot2_ver *boot2_ver = &cmd->params.boot2_ver;
cmd->command = cpu_to_le16(CMD_SET_BOOT2_VER);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_set_boot2_ver) + S_DS_GEN);
boot2_ver->version = priv->boot2_version;
return 0;
}
/*
* Note: NEVER use lbs_queue_cmd() with addtail==0 other than for
* the command timer, because it does not account for queued commands.
*/
void lbs_queue_cmd(struct lbs_adapter *adapter,
struct cmd_ctrl_node *cmdnode,
u8 addtail)
{
unsigned long flags;
struct cmd_ds_command *cmdptr;
lbs_deb_enter(LBS_DEB_HOST);
if (!cmdnode) {
lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n");
goto done;
}
cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
if (!cmdptr) {
lbs_deb_host("QUEUE_CMD: cmdptr is NULL\n");
goto done;
}
/* Exit_PS command needs to be queued in the header always. */
if (cmdptr->command == CMD_802_11_PS_MODE) {
struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode;
if (psm->action == cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
if (adapter->psstate != PS_STATE_FULL_POWER)
addtail = 0;
}
}
spin_lock_irqsave(&adapter->driver_lock, flags);
if (addtail) {
list_add_tail((struct list_head *)cmdnode,
&adapter->cmdpendingq);
adapter->nr_cmd_pending++;
} else
list_add((struct list_head *)cmdnode, &adapter->cmdpendingq);
spin_unlock_irqrestore(&adapter->driver_lock, flags);
lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
le16_to_cpu(((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command));
done:
lbs_deb_leave(LBS_DEB_HOST);
}
/*
* TODO: Fix the issue when DownloadcommandToStation is being called the
* second time when the command times out. All the cmdptr->xxx are in little
* endian and therefore all the comparissions will fail.
* For now - we are not performing the endian conversion the second time - but
* for PS and DEEP_SLEEP we need to worry
*/
static int DownloadcommandToStation(struct lbs_private *priv,
struct cmd_ctrl_node *cmdnode)
{
unsigned long flags;
struct cmd_ds_command *cmdptr;
struct lbs_adapter *adapter = priv->adapter;
int ret = -1;
u16 cmdsize;
u16 command;
lbs_deb_enter(LBS_DEB_HOST);
if (!adapter || !cmdnode) {
lbs_deb_host("DNLD_CMD: adapter or cmdmode is NULL\n");
goto done;
}
cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
spin_lock_irqsave(&adapter->driver_lock, flags);
if (!cmdptr || !cmdptr->size) {
lbs_deb_host("DNLD_CMD: cmdptr is NULL or zero\n");
__lbs_cleanup_and_insert_cmd(priv, cmdnode);
spin_unlock_irqrestore(&adapter->driver_lock, flags);
goto done;
}
adapter->cur_cmd = cmdnode;
adapter->cur_cmd_retcode = 0;
spin_unlock_irqrestore(&adapter->driver_lock, flags);
cmdsize = cmdptr->size;
command = cpu_to_le16(cmdptr->command);
lbs_deb_host("DNLD_CMD: command 0x%04x, size %d, jiffies %lu\n",
command, le16_to_cpu(cmdptr->size), jiffies);
lbs_deb_hex(LBS_DEB_HOST, "DNLD_CMD", cmdnode->bufvirtualaddr, cmdsize);
cmdnode->cmdwaitqwoken = 0;
cmdsize = cpu_to_le16(cmdsize);
ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize);
if (ret != 0) {
lbs_deb_host("DNLD_CMD: hw_host_to_card failed\n");
spin_lock_irqsave(&adapter->driver_lock, flags);
adapter->cur_cmd_retcode = ret;
__lbs_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
adapter->nr_cmd_pending--;
adapter->cur_cmd = NULL;
spin_unlock_irqrestore(&adapter->driver_lock, flags);
goto done;
}
lbs_deb_cmd("DNLD_CMD: sent command 0x%04x, jiffies %lu\n", command, jiffies);
/* Setup the timer after transmit command */
if (command == CMD_802_11_SCAN || command == CMD_802_11_AUTHENTICATE
|| command == CMD_802_11_ASSOCIATE)
mod_timer(&adapter->command_timer, jiffies + (10*HZ));
else
mod_timer(&adapter->command_timer, jiffies + (5*HZ));
ret = 0;
done:
lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
return ret;
}
static int lbs_cmd_mac_control(struct lbs_private *priv,
struct cmd_ds_command *cmd)
{
struct cmd_ds_mac_control *mac = &cmd->params.macctrl;
lbs_deb_enter(LBS_DEB_CMD);
cmd->command = cpu_to_le16(CMD_MAC_CONTROL);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN);
mac->action = cpu_to_le16(priv->adapter->currentpacketfilter);
lbs_deb_cmd("MAC_CONTROL: action 0x%x, size %d\n",
le16_to_cpu(mac->action), le16_to_cpu(cmd->size));
lbs_deb_leave(LBS_DEB_CMD);
return 0;
}
/**
* This function inserts command node to cmdfreeq
* after cleans it. Requires adapter->driver_lock held.
*/
void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
struct cmd_ctrl_node *ptempcmd)
{
struct lbs_adapter *adapter = priv->adapter;
if (!ptempcmd)
return;
cleanup_cmdnode(ptempcmd);
list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq);
}
static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
struct cmd_ctrl_node *ptempcmd)
{
unsigned long flags;
spin_lock_irqsave(&priv->adapter->driver_lock, flags);
__lbs_cleanup_and_insert_cmd(priv, ptempcmd);
spin_unlock_irqrestore(&priv->adapter->driver_lock, flags);
}
int lbs_set_radio_control(struct lbs_private *priv)
{
int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
ret = lbs_prepare_and_send_command(priv,
CMD_802_11_RADIO_CONTROL,
CMD_ACT_SET,
CMD_OPTION_WAITFORRSP, 0, NULL);
lbs_deb_cmd("RADIO_SET: radio %d, preamble %d\n",
priv->adapter->radioon, priv->adapter->preamble);
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
int lbs_set_mac_packet_filter(struct lbs_private *priv)
{
int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
/* Send MAC control command to station */
ret = lbs_prepare_and_send_command(priv,
CMD_MAC_CONTROL, 0, 0, 0, NULL);
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
/**
* @brief This function prepare the command before send to firmware.
*
* @param priv A pointer to struct lbs_private structure
* @param cmd_no command number
* @param cmd_action command action: GET or SET
* @param wait_option wait option: wait response or not
* @param cmd_oid cmd oid: treated as sub command
* @param pdata_buf A pointer to informaion buffer
* @return 0 or -1
*/
int lbs_prepare_and_send_command(struct lbs_private *priv,
u16 cmd_no,
u16 cmd_action,
u16 wait_option, u32 cmd_oid, void *pdata_buf)
{
int ret = 0;
struct lbs_adapter *adapter = priv->adapter;
struct cmd_ctrl_node *cmdnode;
struct cmd_ds_command *cmdptr;
unsigned long flags;
lbs_deb_enter(LBS_DEB_HOST);
if (!adapter) {
lbs_deb_host("PREP_CMD: adapter is NULL\n");
ret = -1;
goto done;
}
if (adapter->surpriseremoved) {
lbs_deb_host("PREP_CMD: card removed\n");
ret = -1;
goto done;
}
cmdnode = lbs_get_cmd_ctrl_node(priv);
if (cmdnode == NULL) {
lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
/* Wake up main thread to execute next command */
wake_up_interruptible(&priv->waitq);
ret = -1;
goto done;
}
lbs_set_cmd_ctrl_node(priv, cmdnode, wait_option, pdata_buf);
cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
lbs_deb_host("PREP_CMD: command 0x%04x\n", cmd_no);
if (!cmdptr) {
lbs_deb_host("PREP_CMD: cmdptr is NULL\n");
lbs_cleanup_and_insert_cmd(priv, cmdnode);
ret = -1;
goto done;
}
/* Set sequence number, command and INT option */
adapter->seqnum++;
cmdptr->seqnum = cpu_to_le16(adapter->seqnum);
cmdptr->command = cpu_to_le16(cmd_no);
cmdptr->result = 0;
switch (cmd_no) {
case CMD_GET_HW_SPEC:
ret = lbs_cmd_hw_spec(priv, cmdptr);
break;
case CMD_802_11_PS_MODE:
ret = lbs_cmd_802_11_ps_mode(priv, cmdptr, cmd_action);
break;
case CMD_802_11_SCAN:
ret = lbs_cmd_80211_scan(priv, cmdptr, pdata_buf);
break;
case CMD_MAC_CONTROL:
ret = lbs_cmd_mac_control(priv, cmdptr);
break;
case CMD_802_11_ASSOCIATE:
case CMD_802_11_REASSOCIATE:
ret = lbs_cmd_80211_associate(priv, cmdptr, pdata_buf);
break;
case CMD_802_11_DEAUTHENTICATE:
ret = lbs_cmd_80211_deauthenticate(priv, cmdptr);
break;
case CMD_802_11_SET_WEP:
ret = lbs_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf);
break;
case CMD_802_11_AD_HOC_START:
ret = lbs_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf);
break;
case CMD_CODE_DNLD:
break;
case CMD_802_11_RESET:
ret = lbs_cmd_802_11_reset(priv, cmdptr, cmd_action);
break;
case CMD_802_11_GET_LOG:
ret = lbs_cmd_802_11_get_log(priv, cmdptr);
break;
case CMD_802_11_AUTHENTICATE:
ret = lbs_cmd_80211_authenticate(priv, cmdptr, pdata_buf);
break;
case CMD_802_11_GET_STAT:
ret = lbs_cmd_802_11_get_stat(priv, cmdptr);
break;
case CMD_802_11_SNMP_MIB:
ret = lbs_cmd_802_11_snmp_mib(priv, cmdptr,
cmd_action, cmd_oid, pdata_buf);
break;
case CMD_MAC_REG_ACCESS:
case CMD_BBP_REG_ACCESS:
case CMD_RF_REG_ACCESS:
ret = lbs_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf);
break;
case CMD_802_11_RF_CHANNEL:
ret = lbs_cmd_802_11_rf_channel(priv, cmdptr,
cmd_action, pdata_buf);
break;
case CMD_802_11_RF_TX_POWER:
ret = lbs_cmd_802_11_rf_tx_power(priv, cmdptr,
cmd_action, pdata_buf);
break;
case CMD_802_11_RADIO_CONTROL:
ret = lbs_cmd_802_11_radio_control(priv, cmdptr, cmd_action);
break;
case CMD_802_11_DATA_RATE:
ret = lbs_cmd_802_11_data_rate(priv, cmdptr, cmd_action);
break;
case CMD_802_11_RATE_ADAPT_RATESET:
ret = lbs_cmd_802_11_rate_adapt_rateset(priv,
cmdptr, cmd_action);
break;
case CMD_MAC_MULTICAST_ADR:
ret = lbs_cmd_mac_multicast_adr(priv, cmdptr, cmd_action);
break;
case CMD_802_11_MONITOR_MODE:
ret = lbs_cmd_802_11_monitor_mode(priv, cmdptr,
cmd_action, pdata_buf);
break;
case CMD_802_11_AD_HOC_JOIN:
ret = lbs_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf);
break;
case CMD_802_11_RSSI:
ret = lbs_cmd_802_11_rssi(priv, cmdptr);
break;
case CMD_802_11_AD_HOC_STOP:
ret = lbs_cmd_80211_ad_hoc_stop(priv, cmdptr);
break;
case CMD_802_11_ENABLE_RSN:
ret = lbs_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action,
pdata_buf);
break;
case CMD_802_11_KEY_MATERIAL:
ret = lbs_cmd_802_11_key_material(priv, cmdptr, cmd_action,
cmd_oid, pdata_buf);
break;
case CMD_802_11_PAIRWISE_TSC:
break;
case CMD_802_11_GROUP_TSC:
break;
case CMD_802_11_MAC_ADDRESS:
ret = lbs_cmd_802_11_mac_address(priv, cmdptr, cmd_action);
break;
case CMD_802_11_EEPROM_ACCESS:
ret = lbs_cmd_802_11_eeprom_access(priv, cmdptr,
cmd_action, pdata_buf);
break;
case CMD_802_11_SET_AFC:
case CMD_802_11_GET_AFC:
cmdptr->command = cpu_to_le16(cmd_no);
cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) +
S_DS_GEN);
memmove(&cmdptr->params.afc,
pdata_buf, sizeof(struct cmd_ds_802_11_afc));
ret = 0;
goto done;
case CMD_802_11D_DOMAIN_INFO:
ret = lbs_cmd_802_11d_domain_info(priv, cmdptr,
cmd_no, cmd_action);
break;
case CMD_802_11_SLEEP_PARAMS:
ret = lbs_cmd_802_11_sleep_params(priv, cmdptr, cmd_action);
break;
case CMD_802_11_INACTIVITY_TIMEOUT:
ret = lbs_cmd_802_11_inactivity_timeout(priv, cmdptr,
cmd_action, pdata_buf);
lbs_set_cmd_ctrl_node(priv, cmdnode, 0, pdata_buf);
break;
case CMD_802_11_TPC_CFG:
cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG);
cmdptr->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) +
S_DS_GEN);
memmove(&cmdptr->params.tpccfg,
pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg));
ret = 0;
break;
case CMD_802_11_LED_GPIO_CTRL:
{
struct mrvlietypes_ledgpio *gpio =
(struct mrvlietypes_ledgpio*)
cmdptr->params.ledgpio.data;
memmove(&cmdptr->params.ledgpio,
pdata_buf,
sizeof(struct cmd_ds_802_11_led_ctrl));
cmdptr->command =
cpu_to_le16(CMD_802_11_LED_GPIO_CTRL);
#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8
cmdptr->size =
cpu_to_le16(gpio->header.len + S_DS_GEN +
ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN);
gpio->header.len = cpu_to_le16(gpio->header.len);
ret = 0;
break;
}
case CMD_802_11_SUBSCRIBE_EVENT:
lbs_cmd_802_11_subscribe_event(priv, cmdptr,
cmd_action, pdata_buf);
break;
case CMD_802_11_PWR_CFG:
cmdptr->command = cpu_to_le16(CMD_802_11_PWR_CFG);
cmdptr->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) +
S_DS_GEN);
memmove(&cmdptr->params.pwrcfg, pdata_buf,
sizeof(struct cmd_ds_802_11_pwr_cfg));
ret = 0;
break;
case CMD_BT_ACCESS:
ret = lbs_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf);
break;
case CMD_FWT_ACCESS:
ret = lbs_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf);
break;
case CMD_MESH_ACCESS:
ret = lbs_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf);
break;
case CMD_SET_BOOT2_VER:
ret = lbs_cmd_set_boot2_ver(priv, cmdptr, cmd_action, pdata_buf);
break;
case CMD_GET_TSF:
cmdptr->command = cpu_to_le16(CMD_GET_TSF);
cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_get_tsf) +
S_DS_GEN);
ret = 0;
break;
case CMD_802_11_BEACON_CTRL:
ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
break;
default:
lbs_deb_host("PREP_CMD: unknown command 0x%04x\n", cmd_no);
ret = -1;
break;
}
/* return error, since the command preparation failed */
if (ret != 0) {
lbs_deb_host("PREP_CMD: command preparation failed\n");
lbs_cleanup_and_insert_cmd(priv, cmdnode);
ret = -1;
goto done;
}
cmdnode->cmdwaitqwoken = 0;
lbs_queue_cmd(adapter, cmdnode, 1);
wake_up_interruptible(&priv->waitq);
if (wait_option & CMD_OPTION_WAITFORRSP) {
lbs_deb_host("PREP_CMD: wait for response\n");
might_sleep();
wait_event_interruptible(cmdnode->cmdwait_q,
cmdnode->cmdwaitqwoken);
}
spin_lock_irqsave(&adapter->driver_lock, flags);
if (adapter->cur_cmd_retcode) {
lbs_deb_host("PREP_CMD: command failed with return code %d\n",
adapter->cur_cmd_retcode);
adapter->cur_cmd_retcode = 0;
ret = -1;
}
spin_unlock_irqrestore(&adapter->driver_lock, flags);
done:
lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
return ret;
}
EXPORT_SYMBOL_GPL(lbs_prepare_and_send_command);
/**
* @brief This function allocates the command buffer and link
* it to command free queue.
*
* @param priv A pointer to struct lbs_private structure
* @return 0 or -1
*/
int lbs_allocate_cmd_buffer(struct lbs_private *priv)
{
int ret = 0;
u32 ulbufsize;
u32 i;
struct cmd_ctrl_node *tempcmd_array;
u8 *ptempvirtualaddr;
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_HOST);
/* Allocate and initialize cmdCtrlNode */
ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER;
if (!(tempcmd_array = kzalloc(ulbufsize, GFP_KERNEL))) {
lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
ret = -1;
goto done;
}
adapter->cmd_array = tempcmd_array;
/* Allocate and initialize command buffers */
ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER;
for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
if (!(ptempvirtualaddr = kzalloc(ulbufsize, GFP_KERNEL))) {
lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
ret = -1;
goto done;
}
/* Update command buffer virtual */
tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr;
}
for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
init_waitqueue_head(&tempcmd_array[i].cmdwait_q);
lbs_cleanup_and_insert_cmd(priv, &tempcmd_array[i]);
}
ret = 0;
done:
lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
return ret;
}
/**
* @brief This function frees the command buffer.
*
* @param priv A pointer to struct lbs_private structure
* @return 0 or -1
*/
int lbs_free_cmd_buffer(struct lbs_private *priv)
{
u32 ulbufsize; /* Someone needs to die for this. Slowly and painfully */
unsigned int i;
struct cmd_ctrl_node *tempcmd_array;
struct lbs_adapter *adapter = priv->adapter;
lbs_deb_enter(LBS_DEB_HOST);
/* need to check if cmd array is allocated or not */
if (adapter->cmd_array == NULL) {
lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
goto done;
}
tempcmd_array = adapter->cmd_array;
/* Release shared memory buffers */
ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER;
for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
if (tempcmd_array[i].bufvirtualaddr) {
kfree(tempcmd_array[i].bufvirtualaddr);
tempcmd_array[i].bufvirtualaddr = NULL;
}
}
/* Release cmd_ctrl_node */
if (adapter->cmd_array) {
kfree(adapter->cmd_array);
adapter->cmd_array = NULL;
}
done:
lbs_deb_leave(LBS_DEB_HOST);
return 0;
}
/**
* @brief This function gets a free command node if available in
* command free queue.
*
* @param priv A pointer to struct lbs_private structure
* @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
*/
struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv)
{
struct cmd_ctrl_node *tempnode;
struct lbs_adapter *adapter = priv->adapter;
unsigned long flags;
lbs_deb_enter(LBS_DEB_HOST);
if (!adapter)
return NULL;
spin_lock_irqsave(&adapter->driver_lock, flags);
if (!list_empty(&adapter->cmdfreeq)) {
tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next;
list_del((struct list_head *)tempnode);
} else {
lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
tempnode = NULL;
}
spin_unlock_irqrestore(&adapter->driver_lock, flags);
if (tempnode)
cleanup_cmdnode(tempnode);
lbs_deb_leave(LBS_DEB_HOST);
return tempnode;
}
/**
* @brief This function cleans command node.
*
* @param ptempnode A pointer to cmdCtrlNode structure
* @return n/a
*/
static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode)
{
lbs_deb_enter(LBS_DEB_HOST);
if (!ptempnode)
return;
ptempnode->cmdwaitqwoken = 1;
wake_up_interruptible(&ptempnode->cmdwait_q);
ptempnode->wait_option = 0;
ptempnode->pdata_buf = NULL;
if (ptempnode->bufvirtualaddr != NULL)
memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
lbs_deb_leave(LBS_DEB_HOST);
}
/**
* @brief This function initializes the command node.
*
* @param priv A pointer to struct lbs_private structure
* @param ptempnode A pointer to cmd_ctrl_node structure
* @param wait_option wait option: wait response or not
* @param pdata_buf A pointer to informaion buffer
* @return 0 or -1
*/
void lbs_set_cmd_ctrl_node(struct lbs_private *priv,
struct cmd_ctrl_node *ptempnode,
u16 wait_option, void *pdata_buf)
{
lbs_deb_enter(LBS_DEB_HOST);
if (!ptempnode)
return;
ptempnode->wait_option = wait_option;
ptempnode->pdata_buf = pdata_buf;
lbs_deb_leave(LBS_DEB_HOST);
}
/**
* @brief This function executes next command in command
* pending queue. It will put fimware back to PS mode
* if applicable.
*
* @param priv A pointer to struct lbs_private structure
* @return 0 or -1
*/
int lbs_execute_next_command(struct lbs_private *priv)
{
struct lbs_adapter *adapter = priv->adapter;
struct cmd_ctrl_node *cmdnode = NULL;
struct cmd_ds_command *cmdptr;
unsigned long flags;
int ret = 0;
// Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the
// only caller to us is lbs_thread() and we get even when a
// data packet is received
lbs_deb_enter(LBS_DEB_THREAD);
spin_lock_irqsave(&adapter->driver_lock, flags);
if (adapter->cur_cmd) {
lbs_pr_alert( "EXEC_NEXT_CMD: already processing command!\n");
spin_unlock_irqrestore(&adapter->driver_lock, flags);
ret = -1;
goto done;
}
if (!list_empty(&adapter->cmdpendingq)) {
cmdnode = (struct cmd_ctrl_node *)
adapter->cmdpendingq.next;
}
spin_unlock_irqrestore(&adapter->driver_lock, flags);
if (cmdnode) {
cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
if (is_command_allowed_in_ps(cmdptr->command)) {
if ((adapter->psstate == PS_STATE_SLEEP) ||
(adapter->psstate == PS_STATE_PRE_SLEEP)) {
lbs_deb_host(
"EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n",
le16_to_cpu(cmdptr->command),
adapter->psstate);
ret = -1;
goto done;
}
lbs_deb_host("EXEC_NEXT_CMD: OK to send command "
"0x%04x in psstate %d\n",
le16_to_cpu(cmdptr->command),
adapter->psstate);
} else if (adapter->psstate != PS_STATE_FULL_POWER) {
/*
* 1. Non-PS command:
* Queue it. set needtowakeup to TRUE if current state
* is SLEEP, otherwise call lbs_ps_wakeup to send Exit_PS.
* 2. PS command but not Exit_PS:
* Ignore it.
* 3. PS command Exit_PS:
* Set needtowakeup to TRUE if current state is SLEEP,
* otherwise send this command down to firmware
* immediately.
*/
if (cmdptr->command !=
cpu_to_le16(CMD_802_11_PS_MODE)) {
/* Prepare to send Exit PS,
* this non PS command will be sent later */
if ((adapter->psstate == PS_STATE_SLEEP)
|| (adapter->psstate == PS_STATE_PRE_SLEEP)
) {
/* w/ new scheme, it will not reach here.
since it is blocked in main_thread. */
adapter->needtowakeup = 1;
} else
lbs_ps_wakeup(priv, 0);
ret = 0;
goto done;
} else {
/*
* PS command. Ignore it if it is not Exit_PS.
* otherwise send it down immediately.
*/
struct cmd_ds_802_11_ps_mode *psm =
&cmdptr->params.psmode;
lbs_deb_host(
"EXEC_NEXT_CMD: PS cmd, action 0x%02x\n",
psm->action);
if (psm->action !=
cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
lbs_deb_host(
"EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
list_del((struct list_head *)cmdnode);
lbs_cleanup_and_insert_cmd(priv, cmdnode);
ret = 0;
goto done;
}
if ((adapter->psstate == PS_STATE_SLEEP) ||
(adapter->psstate == PS_STATE_PRE_SLEEP)) {
lbs_deb_host(
"EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
list_del((struct list_head *)cmdnode);
lbs_cleanup_and_insert_cmd(priv, cmdnode);
adapter->needtowakeup = 1;
ret = 0;
goto done;
}
lbs_deb_host(
"EXEC_NEXT_CMD: sending EXIT_PS\n");
}
}
list_del((struct list_head *)cmdnode);
lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
le16_to_cpu(cmdptr->command));
DownloadcommandToStation(priv, cmdnode);
} else {
/*
* check if in power save mode, if yes, put the device back
* to PS mode
*/
if ((adapter->psmode != LBS802_11POWERMODECAM) &&
(adapter->psstate == PS_STATE_FULL_POWER) &&
((adapter->connect_status == LBS_CONNECTED) ||
(adapter->mesh_connect_status == LBS_CONNECTED))) {
if (adapter->secinfo.WPAenabled ||
adapter->secinfo.WPA2enabled) {
/* check for valid WPA group keys */
if (adapter->wpa_mcast_key.len ||
adapter->wpa_unicast_key.len) {
lbs_deb_host(
"EXEC_NEXT_CMD: WPA enabled and GTK_SET"
" go back to PS_SLEEP");
lbs_ps_sleep(priv, 0);
}
} else {
lbs_deb_host(
"EXEC_NEXT_CMD: cmdpendingq empty, "
"go back to PS_SLEEP");
lbs_ps_sleep(priv, 0);
}
}
}
ret = 0;
done:
lbs_deb_leave(LBS_DEB_THREAD);
return ret;
}
void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
{
union iwreq_data iwrq;
u8 buf[50];
lbs_deb_enter(LBS_DEB_WEXT);
memset(&iwrq, 0, sizeof(union iwreq_data));
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf) - 1, "%s", str);
iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;
/* Send Event to upper layer */
lbs_deb_wext("event indication string %s\n", (char *)buf);
lbs_deb_wext("event indication length %d\n", iwrq.data.length);
lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str);
wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf);
lbs_deb_leave(LBS_DEB_WEXT);
}
static int sendconfirmsleep(struct lbs_private *priv, u8 *cmdptr, u16 size)
{
unsigned long flags;
struct lbs_adapter *adapter = priv->adapter;
int ret = 0;
lbs_deb_enter(LBS_DEB_HOST);
lbs_deb_host("SEND_SLEEPC_CMD: before download, cmd size %d\n",
size);
lbs_deb_hex(LBS_DEB_HOST, "sleep confirm command", cmdptr, size);
ret = priv->hw_host_to_card(priv, MVMS_CMD, cmdptr, size);
priv->dnld_sent = DNLD_RES_RECEIVED;
spin_lock_irqsave(&adapter->driver_lock, flags);
if (adapter->intcounter || adapter->currenttxskb)
lbs_deb_host("SEND_SLEEPC_CMD: intcounter %d, currenttxskb %p\n",
adapter->intcounter, adapter->currenttxskb);
spin_unlock_irqrestore(&adapter->driver_lock, flags);
if (ret) {
lbs_pr_alert(
"SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n");
} else {
spin_lock_irqsave(&adapter->driver_lock, flags);
if (!adapter->intcounter) {
adapter->psstate = PS_STATE_SLEEP;
} else {
lbs_deb_host("SEND_SLEEPC_CMD: after sent, intcounter %d\n",
adapter->intcounter);
}
spin_unlock_irqrestore(&adapter->driver_lock, flags);
lbs_deb_host("SEND_SLEEPC_CMD: sent confirm sleep\n");
}
lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
return ret;
}
void lbs_ps_sleep(struct lbs_private *priv, int wait_option)
{
lbs_deb_enter(LBS_DEB_HOST);
/*
* PS is currently supported only in Infrastructure mode
* Remove this check if it is to be supported in IBSS mode also
*/
lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
CMD_SUBCMD_ENTER_PS, wait_option, 0, NULL);
lbs_deb_leave(LBS_DEB_HOST);
}
/**
* @brief This function sends Exit_PS command to firmware.
*
* @param priv A pointer to struct lbs_private structure
* @param wait_option wait response or not
* @return n/a
*/
void lbs_ps_wakeup(struct lbs_private *priv, int wait_option)
{
__le32 Localpsmode;
lbs_deb_enter(LBS_DEB_HOST);
Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM);
lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
CMD_SUBCMD_EXIT_PS,
wait_option, 0, &Localpsmode);
lbs_deb_leave(LBS_DEB_HOST);
}
/**
* @brief This function checks condition and prepares to
* send sleep confirm command to firmware if ok.
*
* @param priv A pointer to struct lbs_private structure
* @param psmode Power Saving mode
* @return n/a
*/
void lbs_ps_confirm_sleep(struct lbs_private *priv, u16 psmode)
{
unsigned long flags =0;
struct lbs_adapter *adapter = priv->adapter;
u8 allowed = 1;
lbs_deb_enter(LBS_DEB_HOST);
if (priv->dnld_sent) {
allowed = 0;
lbs_deb_host("dnld_sent was set");
}
spin_lock_irqsave(&adapter->driver_lock, flags);
if (adapter->cur_cmd) {
allowed = 0;
lbs_deb_host("cur_cmd was set");
}
if (adapter->intcounter > 0) {
allowed = 0;
lbs_deb_host("intcounter %d", adapter->intcounter);
}
spin_unlock_irqrestore(&adapter->driver_lock, flags);
if (allowed) {
lbs_deb_host("sending lbs_ps_confirm_sleep\n");
sendconfirmsleep(priv, (u8 *) & adapter->lbs_ps_confirm_sleep,
sizeof(struct PS_CMD_ConfirmSleep));
} else {
lbs_deb_host("sleep confirm has been delayed\n");
}
lbs_deb_leave(LBS_DEB_HOST);
}
/**
* @brief Simple way to call firmware functions
*
* @param priv A pointer to struct lbs_private structure
* @param psmode one of the many CMD_802_11_xxxx
* @param cmd pointer to the parameters structure for above command
* (this should not include the command, size, sequence
* and result fields from struct cmd_ds_gen)
* @param cmd_size size structure pointed to by cmd
* @param rsp pointer to an area where the result should be placed
* @param rsp_size pointer to the size of the rsp area. If the firmware
* returns fewer bytes, then this *rsp_size will be
* changed to the actual size.
* @return -1 in case of a higher level error, otherwise
* the result code from the firmware
*/
int lbs_cmd(struct lbs_private *priv,
u16 command,
void *cmd, int cmd_size,
void *rsp, int *rsp_size)
{
struct lbs_adapter *adapter = priv->adapter;
struct cmd_ctrl_node *cmdnode;
struct cmd_ds_gen *cmdptr;
unsigned long flags;
int ret = 0;
lbs_deb_enter(LBS_DEB_HOST);
lbs_deb_host("rsp at %p, rsp_size at %p\n", rsp, rsp_size);
if (!adapter || !rsp_size) {
lbs_deb_host("PREP_CMD: adapter is NULL\n");
ret = -1;
goto done;
}
if (adapter->surpriseremoved) {
lbs_deb_host("PREP_CMD: card removed\n");
ret = -1;
goto done;
}
cmdnode = lbs_get_cmd_ctrl_node(priv);
if (cmdnode == NULL) {
lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
/* Wake up main thread to execute next command */
wake_up_interruptible(&priv->waitq);
ret = -1;
goto done;
}
cmdptr = (struct cmd_ds_gen *)cmdnode->bufvirtualaddr;
cmdnode->wait_option = CMD_OPTION_WAITFORRSP;
cmdnode->pdata_buf = rsp;
cmdnode->pdata_size = rsp_size;
/* Set sequence number, clean result, move to buffer */
adapter->seqnum++;
cmdptr->command = cpu_to_le16(command);
cmdptr->size = cmd_size + S_DS_GEN;
cmdptr->seqnum = cpu_to_le16(adapter->seqnum);
cmdptr->result = 0;
memcpy(cmdptr->cmdresp, cmd, cmd_size);
lbs_deb_host("PREP_CMD: command 0x%04x\n", command);
/* here was the big old switch() statement, which is now obsolete,
* because the caller of lbs_cmd() sets up all of *cmd for us. */
cmdnode->cmdwaitqwoken = 0;
lbs_queue_cmd(adapter, cmdnode, 1);
wake_up_interruptible(&priv->waitq);
might_sleep();
wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
spin_lock_irqsave(&adapter->driver_lock, flags);
if (adapter->cur_cmd_retcode) {
lbs_deb_host("PREP_CMD: command failed with return code %d\n",
adapter->cur_cmd_retcode);
adapter->cur_cmd_retcode = 0;
ret = -1;
}
spin_unlock_irqrestore(&adapter->driver_lock, flags);
done:
lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
return ret;
}
EXPORT_SYMBOL_GPL(lbs_cmd);