kernel-fxtec-pro1x/drivers/net/wireless/libertas/persistcfg.c
Brian Cavagnolo fb904907fb libertas: check bounds and only use decimal for sysfs persistent features.
Some persistent settings were using hex and others decimal.  In some cases,
values were set in hex but reported in decimal.  Confusing.

Signed-off-by: Brian Cavagnolo <brian@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-29 16:55:03 -04:00

453 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)->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)->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)->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)->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)->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->hdr.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)->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)->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)->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);
}