454 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <linux/moduleparam.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/etherdevice.h>
 | |
| #include <linux/netdevice.h>
 | |
| #include <linux/if_arp.h>
 | |
| #include <linux/kthread.h>
 | |
| #include <linux/kfifo.h>
 | |
| 
 | |
| #include "host.h"
 | |
| #include "decl.h"
 | |
| #include "dev.h"
 | |
| #include "wext.h"
 | |
| #include "debugfs.h"
 | |
| #include "scan.h"
 | |
| #include "assoc.h"
 | |
| #include "cmd.h"
 | |
| 
 | |
| static int mesh_get_default_parameters(struct device *dev,
 | |
| 				       struct mrvl_mesh_defaults *defs)
 | |
| {
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
 | |
| 				   CMD_TYPE_MESH_GET_DEFAULTS);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return -EOPNOTSUPP;
 | |
| 
 | |
| 	memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Get function for sysfs attribute bootflag
 | |
|  */
 | |
| static ssize_t bootflag_get(struct device *dev,
 | |
| 			    struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Set function for sysfs attribute bootflag
 | |
|  */
 | |
| static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
 | |
| 			    const char *buf, size_t count)
 | |
| {
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	uint32_t datum;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(cmd));
 | |
| 	ret = sscanf(buf, "%d", &datum);
 | |
| 	if ((ret != 1) || (datum > 1))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	*((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
 | |
| 	cmd.length = cpu_to_le16(sizeof(uint32_t));
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
 | |
| 				   CMD_TYPE_MESH_SET_BOOTFLAG);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return strlen(buf);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Get function for sysfs attribute boottime
 | |
|  */
 | |
| static ssize_t boottime_get(struct device *dev,
 | |
| 			    struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return snprintf(buf, 12, "%d\n", defs.boottime);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Set function for sysfs attribute boottime
 | |
|  */
 | |
| static ssize_t boottime_set(struct device *dev,
 | |
| 		struct device_attribute *attr, const char *buf, size_t count)
 | |
| {
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	uint32_t datum;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(cmd));
 | |
| 	ret = sscanf(buf, "%d", &datum);
 | |
| 	if ((ret != 1) || (datum > 255))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* A too small boot time will result in the device booting into
 | |
| 	 * standalone (no-host) mode before the host can take control of it,
 | |
| 	 * so the change will be hard to revert.  This may be a desired
 | |
| 	 * feature (e.g to configure a very fast boot time for devices that
 | |
| 	 * will not be attached to a host), but dangerous.  So I'm enforcing a
 | |
| 	 * lower limit of 20 seconds:  remove and recompile the driver if this
 | |
| 	 * does not work for you.
 | |
| 	 */
 | |
| 	datum = (datum < 20) ? 20 : datum;
 | |
| 	cmd.data[0] = datum;
 | |
| 	cmd.length = cpu_to_le16(sizeof(uint8_t));
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
 | |
| 				   CMD_TYPE_MESH_SET_BOOTTIME);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return strlen(buf);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Get function for sysfs attribute channel
 | |
|  */
 | |
| static ssize_t channel_get(struct device *dev,
 | |
| 			   struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Set function for sysfs attribute channel
 | |
|  */
 | |
| static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
 | |
| 			   const char *buf, size_t count)
 | |
| {
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	uint32_t datum;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(cmd));
 | |
| 	ret = sscanf(buf, "%d", &datum);
 | |
| 	if (ret != 1 || datum < 1 || datum > 11)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	*((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
 | |
| 	cmd.length = cpu_to_le16(sizeof(uint16_t));
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
 | |
| 				   CMD_TYPE_MESH_SET_DEF_CHANNEL);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return strlen(buf);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Get function for sysfs attribute mesh_id
 | |
|  */
 | |
| static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
 | |
| 			   char *buf)
 | |
| {
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	int maxlen;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	if (defs.meshie.val.mesh_id_len > IW_ESSID_MAX_SIZE) {
 | |
| 		lbs_pr_err("inconsistent mesh ID length");
 | |
| 		defs.meshie.val.mesh_id_len = IW_ESSID_MAX_SIZE;
 | |
| 	}
 | |
| 
 | |
| 	/* SSID not null terminated: reserve room for \0 + \n */
 | |
| 	maxlen = defs.meshie.val.mesh_id_len + 2;
 | |
| 	maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE;
 | |
| 
 | |
| 	defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0';
 | |
| 
 | |
| 	return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Set function for sysfs attribute mesh_id
 | |
|  */
 | |
| static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
 | |
| 			   const char *buf, size_t count)
 | |
| {
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	struct mrvl_meshie *ie;
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	int len;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (count < 2 || count > IW_ESSID_MAX_SIZE + 1)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
 | |
| 	ie = (struct mrvl_meshie *) &cmd.data[0];
 | |
| 
 | |
| 	/* fetch all other Information Element parameters */
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
 | |
| 
 | |
| 	/* transfer IE elements */
 | |
| 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
 | |
| 
 | |
| 	len = count - 1;
 | |
| 	memcpy(ie->val.mesh_id, buf, len);
 | |
| 	/* SSID len */
 | |
| 	ie->val.mesh_id_len = len;
 | |
| 	/* IE len */
 | |
| 	ie->len = sizeof(struct mrvl_meshie_val) - IW_ESSID_MAX_SIZE + len;
 | |
| 
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
 | |
| 				   CMD_TYPE_MESH_SET_MESH_IE);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return strlen(buf);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Get function for sysfs attribute protocol_id
 | |
|  */
 | |
| static ssize_t protocol_id_get(struct device *dev,
 | |
| 			       struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Set function for sysfs attribute protocol_id
 | |
|  */
 | |
| static ssize_t protocol_id_set(struct device *dev,
 | |
| 		struct device_attribute *attr, const char *buf, size_t count)
 | |
| {
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	struct mrvl_meshie *ie;
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	uint32_t datum;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(cmd));
 | |
| 	ret = sscanf(buf, "%d", &datum);
 | |
| 	if ((ret != 1) || (datum > 255))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* fetch all other Information Element parameters */
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
 | |
| 
 | |
| 	/* transfer IE elements */
 | |
| 	ie = (struct mrvl_meshie *) &cmd.data[0];
 | |
| 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
 | |
| 	/* update protocol id */
 | |
| 	ie->val.active_protocol_id = datum;
 | |
| 
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
 | |
| 				   CMD_TYPE_MESH_SET_MESH_IE);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return strlen(buf);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Get function for sysfs attribute metric_id
 | |
|  */
 | |
| static ssize_t metric_id_get(struct device *dev,
 | |
| 		struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Set function for sysfs attribute metric_id
 | |
|  */
 | |
| static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
 | |
| 			     const char *buf, size_t count)
 | |
| {
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	struct mrvl_meshie *ie;
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	uint32_t datum;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(cmd));
 | |
| 	ret = sscanf(buf, "%d", &datum);
 | |
| 	if ((ret != 1) || (datum > 255))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* fetch all other Information Element parameters */
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
 | |
| 
 | |
| 	/* transfer IE elements */
 | |
| 	ie = (struct mrvl_meshie *) &cmd.data[0];
 | |
| 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
 | |
| 	/* update metric id */
 | |
| 	ie->val.active_metric_id = datum;
 | |
| 
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
 | |
| 				   CMD_TYPE_MESH_SET_MESH_IE);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return strlen(buf);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Get function for sysfs attribute capability
 | |
|  */
 | |
| static ssize_t capability_get(struct device *dev,
 | |
| 		struct device_attribute *attr, char *buf)
 | |
| {
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Set function for sysfs attribute capability
 | |
|  */
 | |
| static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
 | |
| 			      const char *buf, size_t count)
 | |
| {
 | |
| 	struct cmd_ds_mesh_config cmd;
 | |
| 	struct mrvl_mesh_defaults defs;
 | |
| 	struct mrvl_meshie *ie;
 | |
| 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
 | |
| 	uint32_t datum;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(&cmd, 0, sizeof(cmd));
 | |
| 	ret = sscanf(buf, "%d", &datum);
 | |
| 	if ((ret != 1) || (datum > 255))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	/* fetch all other Information Element parameters */
 | |
| 	ret = mesh_get_default_parameters(dev, &defs);
 | |
| 
 | |
| 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
 | |
| 
 | |
| 	/* transfer IE elements */
 | |
| 	ie = (struct mrvl_meshie *) &cmd.data[0];
 | |
| 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
 | |
| 	/* update value */
 | |
| 	ie->val.mesh_capability = datum;
 | |
| 
 | |
| 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
 | |
| 				   CMD_TYPE_MESH_SET_MESH_IE);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return strlen(buf);
 | |
| }
 | |
| 
 | |
| 
 | |
| static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
 | |
| static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
 | |
| static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
 | |
| static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
 | |
| static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
 | |
| static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
 | |
| static DEVICE_ATTR(capability, 0644, capability_get, capability_set);
 | |
| 
 | |
| static struct attribute *boot_opts_attrs[] = {
 | |
| 	&dev_attr_bootflag.attr,
 | |
| 	&dev_attr_boottime.attr,
 | |
| 	&dev_attr_channel.attr,
 | |
| 	NULL
 | |
| };
 | |
| 
 | |
| static struct attribute_group boot_opts_group = {
 | |
| 	.name = "boot_options",
 | |
| 	.attrs = boot_opts_attrs,
 | |
| };
 | |
| 
 | |
| static struct attribute *mesh_ie_attrs[] = {
 | |
| 	&dev_attr_mesh_id.attr,
 | |
| 	&dev_attr_protocol_id.attr,
 | |
| 	&dev_attr_metric_id.attr,
 | |
| 	&dev_attr_capability.attr,
 | |
| 	NULL
 | |
| };
 | |
| 
 | |
| static struct attribute_group mesh_ie_group = {
 | |
| 	.name = "mesh_ie",
 | |
| 	.attrs = mesh_ie_attrs,
 | |
| };
 | |
| 
 | |
| void lbs_persist_config_init(struct net_device *dev)
 | |
| {
 | |
| 	int ret;
 | |
| 	ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
 | |
| 	ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
 | |
| }
 | |
| 
 | |
| void lbs_persist_config_remove(struct net_device *dev)
 | |
| {
 | |
| 	sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
 | |
| 	sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
 | |
| }
 |