forked from Minki/linux
libertas: Extend MESH_CONFIG command to access non-volatile configuration
This patch is based on a patch from Shailendra Govardhan and Brian Cavagnolo. It extends the MESH_CONFIG command to configure non-volatile parameters on libertas devices that support them (e.g. OLPC Active Antenna). This patch only implements the driver/firmware interface. See http://dev.laptop.org/ticket/6823 for minimal testing results and known issues. Signed-off-by: Javier Cardona <javier@cozybit.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
75bf45a7b4
commit
edaea5ce05
@ -603,7 +603,8 @@ static int assoc_helper_channel(struct lbs_private *priv,
|
||||
/* Change mesh channel first; 21.p21 firmware won't let
|
||||
you change channel otherwise (even though it'll return
|
||||
an error to this */
|
||||
lbs_mesh_config(priv, 0, assoc_req->channel);
|
||||
lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
|
||||
assoc_req->channel);
|
||||
}
|
||||
|
||||
lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
|
||||
@ -642,7 +643,8 @@ static int assoc_helper_channel(struct lbs_private *priv,
|
||||
|
||||
restore_mesh:
|
||||
if (priv->mesh_dev)
|
||||
lbs_mesh_config(priv, 1, priv->curbssparams.channel);
|
||||
lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
|
||||
priv->curbssparams.channel);
|
||||
|
||||
done:
|
||||
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
||||
|
@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/ieee80211.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include "host.h"
|
||||
#include "hostcmd.h"
|
||||
@ -998,24 +999,69 @@ int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan)
|
||||
int lbs_mesh_config_send(struct lbs_private *priv,
|
||||
struct cmd_ds_mesh_config *cmd,
|
||||
uint16_t action, uint16_t type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_CMD);
|
||||
|
||||
cmd->hdr.command = cpu_to_le16(CMD_MESH_CONFIG);
|
||||
cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
|
||||
cmd->hdr.result = 0;
|
||||
|
||||
cmd->type = cpu_to_le16(type);
|
||||
cmd->action = cpu_to_le16(action);
|
||||
|
||||
ret = lbs_cmd_with_response(priv, CMD_MESH_CONFIG, cmd);
|
||||
|
||||
lbs_deb_leave(LBS_DEB_CMD);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
|
||||
* START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
|
||||
* are all handled by preparing a struct cmd_ds_mesh_config and passing it to
|
||||
* lbs_mesh_config_send.
|
||||
*/
|
||||
int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
|
||||
{
|
||||
struct cmd_ds_mesh_config cmd;
|
||||
struct mrvl_meshie *ie;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.action = cpu_to_le16(enable);
|
||||
cmd.channel = cpu_to_le16(chan);
|
||||
cmd.type = cpu_to_le16(priv->mesh_tlv);
|
||||
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
|
||||
ie = (struct mrvl_meshie *)cmd.data;
|
||||
|
||||
if (enable) {
|
||||
cmd.length = cpu_to_le16(priv->mesh_ssid_len);
|
||||
memcpy(cmd.data, priv->mesh_ssid, priv->mesh_ssid_len);
|
||||
switch (action) {
|
||||
case CMD_ACT_MESH_CONFIG_START:
|
||||
ie->hdr.id = MFIE_TYPE_GENERIC;
|
||||
ie->val.oui[0] = 0x00;
|
||||
ie->val.oui[1] = 0x50;
|
||||
ie->val.oui[2] = 0x43;
|
||||
ie->val.type = MARVELL_MESH_IE_TYPE;
|
||||
ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
|
||||
ie->val.version = MARVELL_MESH_IE_VERSION;
|
||||
ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
|
||||
ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
|
||||
ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
|
||||
ie->val.mesh_id_len = priv->mesh_ssid_len;
|
||||
memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
|
||||
ie->hdr.len = sizeof(struct mrvl_meshie_val) -
|
||||
IW_ESSID_MAX_SIZE + priv->mesh_ssid_len;
|
||||
cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
|
||||
break;
|
||||
case CMD_ACT_MESH_CONFIG_STOP:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
lbs_deb_cmd("mesh config enable %d TLV %x channel %d SSID %s\n",
|
||||
enable, priv->mesh_tlv, chan,
|
||||
lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
|
||||
action, priv->mesh_tlv, chan,
|
||||
escape_essid(priv->mesh_ssid, priv->mesh_ssid_len));
|
||||
return lbs_cmd_with_response(priv, CMD_MESH_CONFIG, &cmd);
|
||||
|
||||
return lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
|
||||
}
|
||||
|
||||
static int lbs_cmd_bcn_ctrl(struct lbs_private * priv,
|
||||
|
@ -39,6 +39,9 @@ int lbs_set_data_rate(struct lbs_private *priv, u8 rate);
|
||||
int lbs_get_channel(struct lbs_private *priv);
|
||||
int lbs_set_channel(struct lbs_private *priv, u8 channel);
|
||||
|
||||
int lbs_mesh_config_send(struct lbs_private *priv,
|
||||
struct cmd_ds_mesh_config *cmd,
|
||||
uint16_t action, uint16_t type);
|
||||
int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
|
||||
|
||||
int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria);
|
||||
|
@ -170,6 +170,16 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
|
||||
|
||||
#define MARVELL_MESH_IE_LENGTH 9
|
||||
|
||||
/* Values used to populate the struct mrvl_mesh_ie. The only time you need this
|
||||
* is when enabling the mesh using CMD_MESH_CONFIG.
|
||||
*/
|
||||
#define MARVELL_MESH_IE_TYPE 4
|
||||
#define MARVELL_MESH_IE_SUBTYPE 0
|
||||
#define MARVELL_MESH_IE_VERSION 0
|
||||
#define MARVELL_MESH_PROTO_ID_HWMP 0
|
||||
#define MARVELL_MESH_METRIC_ID 0
|
||||
#define MARVELL_MESH_CAPABILITY 0
|
||||
|
||||
/** INT status Bit Definition*/
|
||||
#define MRVDRV_TX_DNLD_RDY 0x0001
|
||||
#define MRVDRV_RX_UPLD_RDY 0x0002
|
||||
|
@ -256,6 +256,23 @@ enum cmd_mesh_access_opts {
|
||||
CMD_ACT_MESH_GET_AUTOSTART_ENABLED,
|
||||
};
|
||||
|
||||
/* Define actions and types for CMD_MESH_CONFIG */
|
||||
enum cmd_mesh_config_actions {
|
||||
CMD_ACT_MESH_CONFIG_STOP = 0,
|
||||
CMD_ACT_MESH_CONFIG_START,
|
||||
CMD_ACT_MESH_CONFIG_SET,
|
||||
CMD_ACT_MESH_CONFIG_GET,
|
||||
};
|
||||
|
||||
enum cmd_mesh_config_types {
|
||||
CMD_TYPE_MESH_SET_BOOTFLAG = 1,
|
||||
CMD_TYPE_MESH_SET_BOOTTIME,
|
||||
CMD_TYPE_MESH_SET_DEF_CHANNEL,
|
||||
CMD_TYPE_MESH_SET_MESH_IE,
|
||||
CMD_TYPE_MESH_GET_DEFAULTS,
|
||||
CMD_TYPE_MESH_GET_MESH_IE, /* GET_DEFAULTS is superset of GET_MESHIE */
|
||||
};
|
||||
|
||||
/** Card Event definition */
|
||||
#define MACREG_INT_CODE_TX_PPA_FREE 0
|
||||
#define MACREG_INT_CODE_TX_DMA_DONE 1
|
||||
|
@ -344,14 +344,15 @@ static ssize_t lbs_mesh_set(struct device *dev,
|
||||
{
|
||||
struct lbs_private *priv = to_net_dev(dev)->priv;
|
||||
int enable;
|
||||
int ret;
|
||||
int ret, action = CMD_ACT_MESH_CONFIG_STOP;
|
||||
|
||||
sscanf(buf, "%x", &enable);
|
||||
enable = !!enable;
|
||||
if (enable == !!priv->mesh_dev)
|
||||
return count;
|
||||
|
||||
ret = lbs_mesh_config(priv, enable, priv->curbssparams.channel);
|
||||
if (enable)
|
||||
action = CMD_ACT_MESH_CONFIG_START;
|
||||
ret = lbs_mesh_config(priv, action, priv->curbssparams.channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1257,9 +1258,11 @@ int lbs_start_card(struct lbs_private *priv)
|
||||
useful */
|
||||
|
||||
priv->mesh_tlv = 0x100 + 291;
|
||||
if (lbs_mesh_config(priv, 1, priv->curbssparams.channel)) {
|
||||
if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
|
||||
priv->curbssparams.channel)) {
|
||||
priv->mesh_tlv = 0x100 + 37;
|
||||
if (lbs_mesh_config(priv, 1, priv->curbssparams.channel))
|
||||
if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
|
||||
priv->curbssparams.channel))
|
||||
priv->mesh_tlv = 0;
|
||||
}
|
||||
if (priv->mesh_tlv) {
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/ieee80211.h>
|
||||
|
||||
struct ieeetypes_cfparamset {
|
||||
u8 elementid;
|
||||
@ -252,4 +254,32 @@ struct mrvlietypes_ledbhv {
|
||||
struct led_bhv ledbhv[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Meant to be packed as the value member of a struct ieee80211_info_element.
|
||||
* Note that the len member of the ieee80211_info_element varies depending on
|
||||
* the mesh_id_len */
|
||||
struct mrvl_meshie_val {
|
||||
uint8_t oui[P80211_OUI_LEN];
|
||||
uint8_t type;
|
||||
uint8_t subtype;
|
||||
uint8_t version;
|
||||
uint8_t active_protocol_id;
|
||||
uint8_t active_metric_id;
|
||||
uint8_t mesh_capability;
|
||||
uint8_t mesh_id_len;
|
||||
uint8_t mesh_id[IW_ESSID_MAX_SIZE];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct mrvl_meshie {
|
||||
struct ieee80211_info_element hdr;
|
||||
struct mrvl_meshie_val val;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct mrvl_mesh_defaults {
|
||||
__le32 bootflag;
|
||||
uint8_t boottime;
|
||||
uint8_t reserved;
|
||||
__le16 channel;
|
||||
struct mrvl_meshie meshie;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif
|
||||
|
@ -1002,7 +1002,7 @@ static int lbs_mesh_set_freq(struct net_device *dev,
|
||||
else if (priv->mode == IW_MODE_ADHOC)
|
||||
lbs_stop_adhoc_network(priv);
|
||||
}
|
||||
lbs_mesh_config(priv, 1, fwrq->m);
|
||||
lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m);
|
||||
lbs_update_channel(priv);
|
||||
ret = 0;
|
||||
|
||||
@ -2011,7 +2011,8 @@ static int lbs_mesh_set_essid(struct net_device *dev,
|
||||
priv->mesh_ssid_len = dwrq->length;
|
||||
}
|
||||
|
||||
lbs_mesh_config(priv, 1, priv->curbssparams.channel);
|
||||
lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
|
||||
priv->curbssparams.channel);
|
||||
out:
|
||||
lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
|
||||
return ret;
|
||||
|
Loading…
Reference in New Issue
Block a user