| #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, "0x%x\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, "%x", &datum); |
| if (ret != 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, "0x%x\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, "%x", &datum); |
| if (ret != 1) |
| 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, "0x%x\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; |
| uint16_t datum; |
| int ret; |
| |
| memset(&cmd, 0, sizeof(cmd)); |
| ret = sscanf(buf, "%hx", &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, "%x", &datum); |
| if (ret != 1) |
| 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, "%x", &datum); |
| if (ret != 1) |
| 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, "%x", &datum); |
| if (ret != 1) |
| 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); |
| } |