Commit edaea5ce authored by Javier Cardona's avatar Javier Cardona Committed by John W. Linville

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: default avatarJavier Cardona <javier@cozybit.com>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 75bf45a7
...@@ -603,7 +603,8 @@ static int assoc_helper_channel(struct lbs_private *priv, ...@@ -603,7 +603,8 @@ static int assoc_helper_channel(struct lbs_private *priv,
/* Change mesh channel first; 21.p21 firmware won't let /* Change mesh channel first; 21.p21 firmware won't let
you change channel otherwise (even though it'll return you change channel otherwise (even though it'll return
an error to this */ 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", lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
...@@ -642,7 +643,8 @@ static int assoc_helper_channel(struct lbs_private *priv, ...@@ -642,7 +643,8 @@ static int assoc_helper_channel(struct lbs_private *priv,
restore_mesh: restore_mesh:
if (priv->mesh_dev) 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: done:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
#include <net/iw_handler.h> #include <net/iw_handler.h>
#include <net/ieee80211.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include "host.h" #include "host.h"
#include "hostcmd.h" #include "hostcmd.h"
...@@ -998,24 +999,69 @@ int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, ...@@ -998,24 +999,69 @@ int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
return ret; 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 cmd_ds_mesh_config cmd;
struct mrvl_meshie *ie;
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
cmd.action = cpu_to_le16(enable);
cmd.channel = cpu_to_le16(chan); cmd.channel = cpu_to_le16(chan);
cmd.type = cpu_to_le16(priv->mesh_tlv); ie = (struct mrvl_meshie *)cmd.data;
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
switch (action) {
if (enable) { case CMD_ACT_MESH_CONFIG_START:
cmd.length = cpu_to_le16(priv->mesh_ssid_len); ie->hdr.id = MFIE_TYPE_GENERIC;
memcpy(cmd.data, priv->mesh_ssid, priv->mesh_ssid_len); 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", lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
enable, priv->mesh_tlv, chan, action, priv->mesh_tlv, chan,
escape_essid(priv->mesh_ssid, priv->mesh_ssid_len)); 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, 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); ...@@ -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_get_channel(struct lbs_private *priv);
int lbs_set_channel(struct lbs_private *priv, u8 channel); 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_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria); 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 ...@@ -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 #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*/ /** INT status Bit Definition*/
#define MRVDRV_TX_DNLD_RDY 0x0001 #define MRVDRV_TX_DNLD_RDY 0x0001
#define MRVDRV_RX_UPLD_RDY 0x0002 #define MRVDRV_RX_UPLD_RDY 0x0002
......
...@@ -256,6 +256,23 @@ enum cmd_mesh_access_opts { ...@@ -256,6 +256,23 @@ enum cmd_mesh_access_opts {
CMD_ACT_MESH_GET_AUTOSTART_ENABLED, 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 */ /** Card Event definition */
#define MACREG_INT_CODE_TX_PPA_FREE 0 #define MACREG_INT_CODE_TX_PPA_FREE 0
#define MACREG_INT_CODE_TX_DMA_DONE 1 #define MACREG_INT_CODE_TX_DMA_DONE 1
......
...@@ -344,14 +344,15 @@ static ssize_t lbs_mesh_set(struct device *dev, ...@@ -344,14 +344,15 @@ static ssize_t lbs_mesh_set(struct device *dev,
{ {
struct lbs_private *priv = to_net_dev(dev)->priv; struct lbs_private *priv = to_net_dev(dev)->priv;
int enable; int enable;
int ret; int ret, action = CMD_ACT_MESH_CONFIG_STOP;
sscanf(buf, "%x", &enable); sscanf(buf, "%x", &enable);
enable = !!enable; enable = !!enable;
if (enable == !!priv->mesh_dev) if (enable == !!priv->mesh_dev)
return count; return count;
if (enable)
ret = lbs_mesh_config(priv, enable, priv->curbssparams.channel); action = CMD_ACT_MESH_CONFIG_START;
ret = lbs_mesh_config(priv, action, priv->curbssparams.channel);
if (ret) if (ret)
return ret; return ret;
...@@ -1257,9 +1258,11 @@ int lbs_start_card(struct lbs_private *priv) ...@@ -1257,9 +1258,11 @@ int lbs_start_card(struct lbs_private *priv)
useful */ useful */
priv->mesh_tlv = 0x100 + 291; 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; 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; priv->mesh_tlv = 0;
} }
if (priv->mesh_tlv) { if (priv->mesh_tlv) {
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/wireless.h>
#include <net/ieee80211.h>
struct ieeetypes_cfparamset { struct ieeetypes_cfparamset {
u8 elementid; u8 elementid;
...@@ -252,4 +254,32 @@ struct mrvlietypes_ledbhv { ...@@ -252,4 +254,32 @@ struct mrvlietypes_ledbhv {
struct led_bhv ledbhv[1]; struct led_bhv ledbhv[1];
} __attribute__ ((packed)); } __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 #endif
...@@ -1002,7 +1002,7 @@ static int lbs_mesh_set_freq(struct net_device *dev, ...@@ -1002,7 +1002,7 @@ static int lbs_mesh_set_freq(struct net_device *dev,
else if (priv->mode == IW_MODE_ADHOC) else if (priv->mode == IW_MODE_ADHOC)
lbs_stop_adhoc_network(priv); 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); lbs_update_channel(priv);
ret = 0; ret = 0;
...@@ -2011,7 +2011,8 @@ static int lbs_mesh_set_essid(struct net_device *dev, ...@@ -2011,7 +2011,8 @@ static int lbs_mesh_set_essid(struct net_device *dev,
priv->mesh_ssid_len = dwrq->length; 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: out:
lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment