X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=vlandev.c;h=4d6e76a75e43230f646170eb0dd7b19b069412ec;hb=HEAD;hp=10b78e24157643ef96eef08830fbb370bdce7e54;hpb=74e0222eeb9e62f4d5073a5b3d9208678782a198;p=project%2Fnetifd.git diff --git a/vlandev.c b/vlandev.c index 10b78e2..4d6e76a 100644 --- a/vlandev.c +++ b/vlandev.c @@ -31,7 +31,7 @@ enum { static const struct blobmsg_policy vlandev_attrs[__VLANDEV_ATTR_MAX] = { [VLANDEV_ATTR_IFNAME] = { "ifname", BLOBMSG_TYPE_STRING }, - [VLANDEV_ATTR_VID] = { "vid", BLOBMSG_TYPE_INT32 }, + [VLANDEV_ATTR_VID] = { "vid", BLOBMSG_TYPE_STRING }, [VLANDEV_ATTR_INGRESS_QOS_MAPPING] = { "ingress_qos_mapping", BLOBMSG_TYPE_ARRAY }, [VLANDEV_ATTR_EGRESS_QOS_MAPPING] = { "egress_qos_mapping", BLOBMSG_TYPE_ARRAY }, }; @@ -45,6 +45,7 @@ static const struct uci_blob_param_list vlandev_attr_list = { }; static struct device_type vlan8021q_device_type; +static struct blob_buf b; struct vlandev_device { struct device dev; @@ -54,9 +55,77 @@ struct vlandev_device { struct blob_attr *config_data; struct blob_attr *ifname; + struct blob_attr *vid; + struct vlandev_config config; }; +static int +__vlandev_hotplug_op(struct device *dev, struct device *member, struct blob_attr *vlan, bool add) +{ + struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev); + void *a; + + dev = mvdev->parent.dev; + if (!dev || !dev->hotplug_ops) + return UBUS_STATUS_NOT_SUPPORTED; + + blob_buf_init(&b, 0); + a = blobmsg_open_array(&b, "vlans"); + blobmsg_printf(&b, NULL, "%d:u", mvdev->config.vid); + if (vlan && blobmsg_len(vlan)) + blob_put_raw(&b, blobmsg_data(vlan), blobmsg_len(vlan)); + blobmsg_close_array(&b, a); + + if (add) + return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); + else + return dev->hotplug_ops->del(dev, member, blobmsg_data(b.head)); +} + +static int +vlandev_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) +{ + return __vlandev_hotplug_op(dev, member, vlan, true); +} + +static int +vlandev_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan) +{ + return __vlandev_hotplug_op(dev, member, vlan, false); +} + +static int +vlandev_hotplug_prepare(struct device *dev, struct device **bridge_dev) +{ + struct vlandev_device *mvdev = container_of(dev, struct vlandev_device, dev); + + dev = mvdev->parent.dev; + if (!dev || !dev->hotplug_ops) + return UBUS_STATUS_NOT_SUPPORTED; + + return dev->hotplug_ops->prepare(dev, bridge_dev); +} + +static void vlandev_hotplug_check(struct vlandev_device *mvdev) +{ + static const struct device_hotplug_ops hotplug_ops = { + .prepare = vlandev_hotplug_prepare, + .add = vlandev_hotplug_add, + .del = vlandev_hotplug_del + }; + struct device *dev = mvdev->parent.dev; + + if (!dev || !dev->hotplug_ops || avl_is_empty(&dev->vlans.avl) || + mvdev->dev.type != &vlan8021q_device_type) { + mvdev->dev.hotplug_ops = NULL; + return; + } + + mvdev->dev.hotplug_ops = &hotplug_ops; +} + + static void vlandev_base_cb(struct device_user *dev, enum device_event ev) { @@ -69,6 +138,13 @@ vlandev_base_cb(struct device_user *dev, enum device_event ev) case DEV_EVENT_REMOVE: device_set_present(&mvdev->dev, false); break; + case DEV_EVENT_UPDATE_IFNAME: + vlandev_hotplug_check(mvdev); + break; + case DEV_EVENT_TOPO_CHANGE: + /* Propagate topo changes */ + device_broadcast_event(&mvdev->dev, DEV_EVENT_TOPO_CHANGE); + break; default: return; } @@ -115,7 +191,7 @@ vlandev_set_state(struct device *dev, bool up) { struct vlandev_device *mvdev; - D(SYSTEM, "vlandev_set_state(%s, %u)\n", dev->ifname, up); + D(SYSTEM, "vlandev_set_state(%s, %u)", dev->ifname, up); mvdev = container_of(dev, struct vlandev_device, dev); if (up) @@ -164,10 +240,33 @@ vlandev_dump_info(struct device *dev, struct blob_buf *b) mvdev = container_of(dev, struct vlandev_device, dev); blobmsg_add_string(b, "parent", mvdev->parent.dev->ifname); system_if_dump_info(dev, b); + blobmsg_add_u32(b, "vid", mvdev->config.vid); vlandev_qos_mapping_dump(b, "ingress_qos_mapping", &mvdev->config.ingress_qos_mapping_list); vlandev_qos_mapping_dump(b, "egress_qos_mapping", &mvdev->config.egress_qos_mapping_list); } +static uint16_t +vlandev_get_vid(struct device *dev, const char *id_str) +{ + unsigned long id; + uint16_t *alias_id; + char *err; + + id = strtoul(id_str, &err, 10); + if (err && *err) { + if (!dev) + return 1; + + alias_id = kvlist_get(&dev->vlan_aliases, id_str); + if (!alias_id) + return 1; + + id = *alias_id; + } + + return (uint16_t)id; +} + static void vlandev_config_init(struct device *dev) { @@ -178,14 +277,21 @@ vlandev_config_init(struct device *dev) if (mvdev->ifname) basedev = device_get(blobmsg_data(mvdev->ifname), true); + if (mvdev->vid) + mvdev->config.vid = vlandev_get_vid(basedev, blobmsg_get_string(mvdev->vid)); + else + mvdev->config.vid = 1; + device_add_user(&mvdev->parent, basedev); + vlandev_hotplug_check(mvdev); } static void vlandev_qos_mapping_list_apply(struct vlist_simple_tree *qos_mapping_li, struct blob_attr *list) { struct blob_attr *cur; struct vlan_qos_mapping *qos_mapping; - int rem, rc; + size_t rem; + int rc; blobmsg_for_each_attr(cur, list, rem) { if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) @@ -215,14 +321,10 @@ vlandev_apply_settings(struct vlandev_device *mvdev, struct blob_attr **tb) cfg->proto = (mvdev->dev.type == &vlan8021q_device_type) ? VLAN_PROTO_8021Q : VLAN_PROTO_8021AD; - cfg->vid = 1; vlist_simple_update(&cfg->ingress_qos_mapping_list); vlist_simple_update(&cfg->egress_qos_mapping_list); - if ((cur = tb[VLANDEV_ATTR_VID])) - cfg->vid = (uint16_t) blobmsg_get_u32(cur); - if ((cur = tb[VLANDEV_ATTR_INGRESS_QOS_MAPPING])) vlandev_qos_mapping_list_apply(&cfg->ingress_qos_mapping_list, cur); @@ -252,6 +354,7 @@ vlandev_reload(struct device *dev, struct blob_attr *attr) device_init_settings(dev, tb_dev); vlandev_apply_settings(mvdev, tb_mv); mvdev->ifname = tb_mv[VLANDEV_ATTR_IFNAME]; + mvdev->vid = tb_mv[VLANDEV_ATTR_VID]; if (mvdev->config_data) { struct blob_attr *otb_dev[__DEV_ATTR_MAX];