#define DPRINTF(...) do {} while(0)
#endif
+#define SWCONFIG_DEVNAME "switch%d"
+
+#include "swconfig_leds.c"
+
MODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>");
MODULE_LICENSE("GPL");
static int swdev_id = 0;
static struct list_head swdevs;
-static spinlock_t swdevs_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(swdevs_lock);
struct swconfig_callback;
struct swconfig_callback
if (val->port_vlan >= dev->vlans)
return -EINVAL;
- if (!dev->get_vlan_ports)
+ if (!dev->ops->get_vlan_ports)
return -EOPNOTSUPP;
- ret = dev->get_vlan_ports(dev, val);
+ ret = dev->ops->get_vlan_ports(dev, val);
return ret;
}
swconfig_set_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
{
struct switch_port *ports = val->value.ports;
+ const struct switch_dev_ops *ops = dev->ops;
int i;
if (val->port_vlan >= dev->vlans)
if (val->len > dev->ports)
return -EINVAL;
- if (!dev->set_vlan_ports)
+ if (!ops->set_vlan_ports)
return -EOPNOTSUPP;
for (i = 0; i < val->len; i++) {
if (ports[i].id >= dev->ports)
return -EINVAL;
- if (dev->set_port_pvid && !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
- dev->set_port_pvid(dev, ports[i].id, val->port_vlan);
+ if (ops->set_port_pvid &&
+ !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
+ ops->set_port_pvid(dev, ports[i].id, val->port_vlan);
}
- return dev->set_vlan_ports(dev, val);
+ return ops->set_vlan_ports(dev, val);
}
static int
if (val->port_vlan >= dev->ports)
return -EINVAL;
- if (!dev->set_port_pvid)
+ if (!dev->ops->set_port_pvid)
return -EOPNOTSUPP;
- return dev->set_port_pvid(dev, val->port_vlan, val->value.i);
+ return dev->ops->set_port_pvid(dev, val->port_vlan, val->value.i);
}
static int
if (val->port_vlan >= dev->ports)
return -EINVAL;
- if (!dev->get_port_pvid)
+ if (!dev->ops->get_port_pvid)
+ return -EOPNOTSUPP;
+
+ return dev->ops->get_port_pvid(dev, val->port_vlan, &val->value.i);
+}
+
+static const char *
+swconfig_speed_str(enum switch_port_speed speed)
+{
+ switch (speed) {
+ case SWITCH_PORT_SPEED_10:
+ return "10baseT";
+ case SWITCH_PORT_SPEED_100:
+ return "100baseT";
+ case SWITCH_PORT_SPEED_1000:
+ return "1000baseT";
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+static int
+swconfig_get_link(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+ struct switch_port_link link;
+ int len;
+ int ret;
+
+ if (val->port_vlan >= dev->ports)
+ return -EINVAL;
+
+ if (!dev->ops->get_port_link)
return -EOPNOTSUPP;
- return dev->get_port_pvid(dev, val->port_vlan, &val->value.i);
+ memset(&link, 0, sizeof(link));
+ ret = dev->ops->get_port_link(dev, val->port_vlan, &link);
+ if (ret)
+ return ret;
+
+ memset(dev->buf, 0, sizeof(dev->buf));
+
+ if (link.link)
+ len = snprintf(dev->buf, sizeof(dev->buf),
+ "port:%d link:up speed:%s %s-duplex %s%s%s",
+ val->port_vlan,
+ swconfig_speed_str(link.speed),
+ link.duplex ? "full" : "half",
+ link.tx_flow ? "txflow ": "",
+ link.rx_flow ? "rxflow " : "",
+ link.aneg ? "auto" : "");
+ else
+ len = snprintf(dev->buf, sizeof(dev->buf), "port:%d link:down",
+ val->port_vlan);
+
+ val->value.s = dev->buf;
+ val->len = len;
+
+ return 0;
}
static int
swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
{
/* don't complain if not supported by the switch driver */
- if (!dev->apply_config)
+ if (!dev->ops->apply_config)
return 0;
- return dev->apply_config(dev);
+ return dev->ops->apply_config(dev);
}
static int
swconfig_reset_switch(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
{
/* don't complain if not supported by the switch driver */
- if (!dev->reset_switch)
+ if (!dev->ops->reset_switch)
return 0;
- return dev->reset_switch(dev);
+ return dev->ops->reset_switch(dev);
}
enum global_defaults {
enum port_defaults {
PORT_PVID,
+ PORT_LINK,
};
static struct switch_attr default_global[] = {
.description = "Primary VLAN ID",
.set = swconfig_set_pvid,
.get = swconfig_get_pvid,
+ },
+ [PORT_LINK] = {
+ .type = SWITCH_TYPE_STRING,
+ .name = "link",
+ .description = "Get port link information",
+ .set = NULL,
+ .get = swconfig_get_link,
}
};
},
};
+static const struct switch_attr *
+swconfig_find_attr_by_name(const struct switch_attrlist *alist, const char *name)
+{
+ int i;
+
+ for (i = 0; i < alist->n_attr; i++)
+ if (strcmp(name, alist->attr[i].name) == 0)
+ return &alist->attr[i];
+
+ return NULL;
+}
static void swconfig_defaults_init(struct switch_dev *dev)
{
+ const struct switch_dev_ops *ops = dev->ops;
+
dev->def_global = 0;
dev->def_vlan = 0;
dev->def_port = 0;
- if (dev->get_vlan_ports || dev->set_vlan_ports)
+ if (ops->get_vlan_ports || ops->set_vlan_ports)
set_bit(VLAN_PORTS, &dev->def_vlan);
- if (dev->get_port_pvid || dev->set_port_pvid)
+ if (ops->get_port_pvid || ops->set_port_pvid)
set_bit(PORT_PVID, &dev->def_port);
+ if (ops->get_port_link &&
+ !swconfig_find_attr_by_name(&ops->attr_port, "link"))
+ set_bit(PORT_LINK, &dev->def_port);
+
/* always present, can be no-op */
set_bit(GLOBAL_APPLY, &dev->def_global);
set_bit(GLOBAL_RESET, &dev->def_global);
if (cb->close(cb, arg) < 0)
goto error;
}
- err = genlmsg_unicast(cb->msg, info->snd_pid);
+ err = genlmsg_reply(cb->msg, info);
cb->msg = NULL;
if (err < 0)
goto error;
switch(hdr->cmd) {
case SWITCH_CMD_LIST_GLOBAL:
- alist = &dev->attr_global;
+ alist = &dev->ops->attr_global;
def_list = default_global;
def_active = &dev->def_global;
n_def = ARRAY_SIZE(default_global);
break;
case SWITCH_CMD_LIST_VLAN:
- alist = &dev->attr_vlan;
+ alist = &dev->ops->attr_vlan;
def_list = default_vlan;
def_active = &dev->def_vlan;
n_def = ARRAY_SIZE(default_vlan);
break;
case SWITCH_CMD_LIST_PORT:
- alist = &dev->attr_port;
+ alist = &dev->ops->attr_port;
def_list = default_port;
def_active = &dev->def_port;
n_def = ARRAY_SIZE(default_port);
if (!cb.msg)
return 0;
- return genlmsg_unicast(cb.msg, info->snd_pid);
+ return genlmsg_reply(cb.msg, info);
error:
if (cb.msg)
switch(hdr->cmd) {
case SWITCH_CMD_SET_GLOBAL:
case SWITCH_CMD_GET_GLOBAL:
- alist = &dev->attr_global;
+ alist = &dev->ops->attr_global;
def_list = default_global;
def_active = &dev->def_global;
n_def = ARRAY_SIZE(default_global);
break;
case SWITCH_CMD_SET_VLAN:
case SWITCH_CMD_GET_VLAN:
- alist = &dev->attr_vlan;
+ alist = &dev->ops->attr_vlan;
def_list = default_vlan;
def_active = &dev->def_vlan;
n_def = ARRAY_SIZE(default_vlan);
break;
case SWITCH_CMD_SET_PORT:
case SWITCH_CMD_GET_PORT:
- alist = &dev->attr_port;
+ alist = &dev->ops->attr_port;
def_list = default_port;
def_active = &dev->def_port;
n_def = ARRAY_SIZE(default_port);
goto nla_put_failure;
swconfig_put_dev(dev);
- return genlmsg_unicast(msg, info->snd_pid);
+ return genlmsg_reply(msg, info);
nla_put_failure:
if (msg)
return -1;
NLA_PUT_U32(msg, SWITCH_ATTR_ID, dev->id);
- NLA_PUT_STRING(msg, SWITCH_ATTR_NAME, dev->name);
NLA_PUT_STRING(msg, SWITCH_ATTR_DEV_NAME, dev->devname);
+ NLA_PUT_STRING(msg, SWITCH_ATTR_ALIAS, dev->alias);
+ NLA_PUT_STRING(msg, SWITCH_ATTR_NAME, dev->name);
NLA_PUT_U32(msg, SWITCH_ATTR_VLANS, dev->vlans);
NLA_PUT_U32(msg, SWITCH_ATTR_PORTS, dev->ports);
NLA_PUT_U32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port);
int
register_switch(struct switch_dev *dev, struct net_device *netdev)
{
+ struct switch_dev *sdev;
+ const int max_switches = 8 * sizeof(unsigned long);
+ unsigned long in_use = 0;
+ int err;
+ int i;
+
INIT_LIST_HEAD(&dev->dev_list);
if (netdev) {
dev->netdev = netdev;
- if (!dev->devname)
- dev->devname = netdev->name;
+ if (!dev->alias)
+ dev->alias = netdev->name;
}
- BUG_ON(!dev->devname);
+ BUG_ON(!dev->alias);
if (dev->ports > 0) {
dev->portbuf = kzalloc(sizeof(struct switch_port) * dev->ports,
if (!dev->portbuf)
return -ENOMEM;
}
- dev->id = ++swdev_id;
swconfig_defaults_init(dev);
spin_lock_init(&dev->lock);
swconfig_lock();
+ dev->id = ++swdev_id;
+
+ list_for_each_entry(sdev, &swdevs, dev_list) {
+ if (!sscanf(sdev->devname, SWCONFIG_DEVNAME, &i))
+ continue;
+ if (i < 0 || i > max_switches)
+ continue;
+
+ set_bit(i, &in_use);
+ }
+ i = find_first_zero_bit(&in_use, max_switches);
+
+ if (i == max_switches) {
+ swconfig_unlock();
+ return -ENFILE;
+ }
+
+ /* fill device name */
+ snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i);
+
list_add(&dev->dev_list, &swdevs);
swconfig_unlock();
+ err = swconfig_create_led_trigger(dev);
+ if (err)
+ return err;
+
return 0;
}
EXPORT_SYMBOL_GPL(register_switch);
void
unregister_switch(struct switch_dev *dev)
{
+ swconfig_destroy_led_trigger(dev);
kfree(dev->portbuf);
spin_lock(&dev->lock);
swconfig_lock();