libertas: Extend MESH_CONFIG command to access non-volatile configuration
authorJavier Cardona <javier@cozybit.com>
Sat, 17 May 2008 07:55:10 +0000 (00:55 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 22 May 2008 01:47:39 +0000 (21:47 -0400)
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>
drivers/net/wireless/libertas/assoc.c
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/libertas/cmd.h
drivers/net/wireless/libertas/defs.h
drivers/net/wireless/libertas/host.h
drivers/net/wireless/libertas/main.c
drivers/net/wireless/libertas/types.h
drivers/net/wireless/libertas/wext.c

index c9c3640ce9fbb4978d4fd06cf06355f0b7da4faa..953a44f750e15b852657ebd80049767128a5ae22 100644 (file)
@@ -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);
index b494aba869c5f33690254de5efedc89af71ec970..7ccec987faaaa33919b76eb2553817a9631a1a1a 100644 (file)
@@ -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));
-
-       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,
index f4019c22adfca2368b71dec712041c2d8d01bef3..00d290e2818fcb1b25a16c891f55d71c21f39e7c 100644 (file)
@@ -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);
index d395201110628a9d16dda6bf6c0a4b0e310dded5..3793cb92296a58d4ea4a8bbd6d8cc7201c10573a 100644 (file)
@@ -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
index 3915c3144fad0e7d282b56d2cd6af0a6e8e4bbe6..c92e41b4faf4f3047d8b36cc1e275ae9c95956f6 100644 (file)
@@ -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
index a87febad8c29c6b5cd5c7b147d7b734ec459ea1b..01299c8ed27c5a31426caf899a350fd2cc963210 100644 (file)
@@ -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) {
index 4031be4208624cce879ffa734d1db5d71bf932fb..e0c2599da92f182c2e3a31b616d04fd9c2ac4323 100644 (file)
@@ -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
index 0973d015a5201e7dc604f1c86229c25b5a6bc265..d4b19f11b78510249ef6ef7ffb720c9744de25d7 100644 (file)
@@ -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;