diff options
author | Felix Fietkau <nbd@nbd.name> | 2020-08-07 14:33:33 +0200 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2020-10-28 04:49:11 +0100 |
commit | d59f3ddcbaf0784a01aa8534cdcb20554748e6e5 (patch) | |
tree | 85c87a5e30606dc06cecf41f95553ab6fae42b79 | |
parent | a3016c4512487a9673d13a4b09ba05e774df1e73 (diff) |
vlandev: add pass-through hotplug ops that pass the VLAN info to the bridge
Only used for 802.1q devices
Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r-- | vlandev.c | 66 |
1 files changed, 66 insertions, 0 deletions
@@ -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; @@ -57,6 +58,67 @@ struct vlandev_device { struct vlandev_config config; }; +static int +vlandev_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) +{ + 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", mvdev->config.vid); + blobmsg_close_array(&b, a); + + return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); +} + +static int +vlandev_hotplug_del(struct device *dev, struct device *member) +{ + 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->del(dev, member); +} + +static int +vlandev_hotplug_prepare(struct device *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); +} + +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) || + 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 +131,9 @@ 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; default: return; } @@ -179,6 +244,7 @@ vlandev_config_init(struct device *dev) basedev = device_get(blobmsg_data(mvdev->ifname), true); 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) |