From edaea5ce05ca804cc55438c586ca2f947d49f56f Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Sat, 17 May 2008 00:55:10 -0700 Subject: [PATCH] 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 Signed-off-by: David Woodhouse Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/assoc.c | 6 ++- drivers/net/wireless/libertas/cmd.c | 68 ++++++++++++++++++++++----- drivers/net/wireless/libertas/cmd.h | 3 ++ drivers/net/wireless/libertas/defs.h | 10 ++++ drivers/net/wireless/libertas/host.h | 17 +++++++ drivers/net/wireless/libertas/main.c | 13 +++-- drivers/net/wireless/libertas/types.h | 30 ++++++++++++ drivers/net/wireless/libertas/wext.c | 5 +- 8 files changed, 132 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c index c9c3640ce9fb..953a44f750e1 100644 --- a/drivers/net/wireless/libertas/assoc.c +++ b/drivers/net/wireless/libertas/assoc.c @@ -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); diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index b494aba869c5..7ccec987faaa 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -4,6 +4,7 @@ */ #include +#include #include #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)); - - if (enable) { - cmd.length = cpu_to_le16(priv->mesh_ssid_len); - memcpy(cmd.data, priv->mesh_ssid, priv->mesh_ssid_len); + ie = (struct mrvl_meshie *)cmd.data; + + 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, diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h index f4019c22adfc..00d290e2818f 100644 --- a/drivers/net/wireless/libertas/cmd.h +++ b/drivers/net/wireless/libertas/cmd.h @@ -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); diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index d39520111062..3793cb92296a 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -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 diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h index 3915c3144fad..c92e41b4faf4 100644 --- a/drivers/net/wireless/libertas/host.h +++ b/drivers/net/wireless/libertas/host.h @@ -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 diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index a87febad8c29..01299c8ed27c 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -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) { diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h index 4031be420862..e0c2599da92f 100644 --- a/drivers/net/wireless/libertas/types.h +++ b/drivers/net/wireless/libertas/types.h @@ -6,6 +6,8 @@ #include #include +#include +#include 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 diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c index 0973d015a520..d4b19f11b785 100644 --- a/drivers/net/wireless/libertas/wext.c +++ b/drivers/net/wireless/libertas/wext.c @@ -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; -- 2.20.1