recognize stacked interfaces (e.g. dsa) and handle their dependencies
authorFelix Fietkau <nbd@openwrt.org>
Wed, 26 Oct 2011 18:12:32 +0000 (20:12 +0200)
committerFelix Fietkau <nbd@openwrt.org>
Wed, 26 Oct 2011 18:12:32 +0000 (20:12 +0200)
device.c
device.h
system-dummy.c
system-linux.c
system.h

index 428d001..b2db291 100644 (file)
--- a/device.c
+++ b/device.c
@@ -55,6 +55,36 @@ void device_unlock(void)
                device_free_unused(NULL);
 }
 
+static int set_device_state(struct device *dev, bool state)
+{
+       if (state)
+               system_if_up(dev);
+       else
+               system_if_down(dev);
+
+       return 0;
+}
+
+static int
+simple_device_set_state(struct device *dev, bool state)
+{
+       int ret = 0;
+
+       if (state && !dev->parent.dev)
+               dev->parent.dev = system_if_get_parent(dev);
+
+       if (dev->parent.dev) {
+               if (state)
+                       ret = device_claim(&dev->parent);
+               else
+                       device_release(&dev->parent);
+
+               if (ret < 0)
+                       return ret;
+       }
+       return set_device_state(dev, state);
+}
+
 static struct device *
 simple_device_create(const char *name, struct blob_attr *attr)
 {
@@ -66,6 +96,7 @@ simple_device_create(const char *name, struct blob_attr *attr)
        if (!dev)
                return NULL;
 
+       dev->set_state = simple_device_set_state;
        device_init_settings(dev, tb);
 
        return dev;
@@ -73,6 +104,8 @@ simple_device_create(const char *name, struct blob_attr *attr)
 
 static void simple_device_free(struct device *dev)
 {
+       if (dev->parent.dev)
+               device_remove_user(&dev->parent);
        device_cleanup(dev);
        free(dev);
 }
@@ -209,16 +242,6 @@ alias_notify_device(const char *name, struct device *dev)
        device_unlock();
 }
 
-static int set_device_state(struct device *dev, bool state)
-{
-       if (state)
-               system_if_up(dev);
-       else
-               system_if_down(dev);
-
-       return 0;
-}
-
 int device_claim(struct device_user *dep)
 {
        struct device *dev = dep->dev;
@@ -315,6 +338,7 @@ device_create_default(const char *name, bool external)
        D(DEVICE, "Create simple device '%s'\n", name);
        dev = calloc(1, sizeof(*dev));
        dev->external = external;
+       dev->set_state = simple_device_set_state;
        device_init(dev, &simple_device_type, name);
        dev->default_config = true;
        return dev;
index 057506b..a3728f3 100644 (file)
--- a/device.h
+++ b/device.h
@@ -5,6 +5,7 @@
 #include <netinet/in.h>
 
 struct device;
+struct device_user;
 struct device_hotplug_ops;
 
 typedef int (*device_state_cb)(struct device *, bool up);
@@ -45,6 +46,33 @@ enum {
        DEV_OPT_TXQUEUELEN      = (1 << 2)
 };
 
+/* events broadcasted to all users of a device */
+enum device_event {
+       DEV_EVENT_ADD,
+       DEV_EVENT_REMOVE,
+
+       DEV_EVENT_SETUP,
+       DEV_EVENT_TEARDOWN,
+       DEV_EVENT_UP,
+       DEV_EVENT_DOWN,
+
+       DEV_EVENT_LINK_UP,
+       DEV_EVENT_LINK_DOWN,
+};
+
+/*
+ * device dependency with callbacks
+ */
+struct device_user {
+       struct list_head list;
+
+       bool claimed;
+       bool hotplug;
+
+       struct device *dev;
+       void (*cb)(struct device_user *, enum device_event);
+};
+
 /* 
  * link layer device. typically represents a linux network device.
  * can be used to support VLANs as well
@@ -72,6 +100,8 @@ struct device {
 
        const struct device_hotplug_ops *hotplug_ops;
 
+       struct device_user parent;
+
        /* settings */
        unsigned int flags;
 
@@ -80,33 +110,6 @@ struct device {
        uint8_t macaddr[6];
 };
 
-/* events broadcasted to all users of a device */
-enum device_event {
-       DEV_EVENT_ADD,
-       DEV_EVENT_REMOVE,
-
-       DEV_EVENT_SETUP,
-       DEV_EVENT_TEARDOWN,
-       DEV_EVENT_UP,
-       DEV_EVENT_DOWN,
-
-       DEV_EVENT_LINK_UP,
-       DEV_EVENT_LINK_DOWN,
-};
-
-/*
- * device dependency with callbacks
- */
-struct device_user {
-       struct list_head list;
-
-       bool claimed;
-       bool hotplug;
-
-       struct device *dev;
-       void (*cb)(struct device_user *, enum device_event);
-};
-
 struct device_hotplug_ops {
        int (*add)(struct device *main, struct device *member);
        int (*del)(struct device *main, struct device *member);
index d518d6f..7d651a5 100644 (file)
@@ -79,6 +79,12 @@ int system_if_check(struct device *dev)
        return 0;
 }
 
+struct device *
+system_if_get_parent(struct device *dev)
+{
+       return NULL;
+}
+
 int system_if_dump_stats(struct device *dev, struct blob_buf *b)
 {
        return 0;
index 3ac5588..d26f181 100644 (file)
@@ -607,6 +607,37 @@ int system_if_check(struct device *dev)
        return 0;
 }
 
+struct device *
+system_if_get_parent(struct device *dev)
+{
+       char buf[64], *devname;
+       int ifindex, iflink, len;
+       FILE *f;
+
+       snprintf(buf, sizeof(buf), "/sys/class/net/%s/iflink", dev->ifname);
+       f = fopen(buf, "r");
+       if (!f)
+               return NULL;
+
+       len = fread(buf, 1, sizeof(buf) - 1, f);
+       fclose(f);
+
+       if (len <= 0)
+               return NULL;
+
+       buf[len] = 0;
+       iflink = strtoul(buf, NULL, 0);
+       ifindex = system_if_resolve(dev);
+       if (!iflink || iflink == ifindex)
+               return NULL;
+
+       devname = if_indextoname(iflink, buf);
+       if (!devname)
+               return NULL;
+
+       return device_get(devname, true);
+}
+
 int system_if_dump_stats(struct device *dev, struct blob_buf *b)
 {
        const char *const counters[] = {
index dee7137..639dbc8 100644 (file)
--- a/system.h
+++ b/system.h
@@ -39,6 +39,7 @@ int system_if_up(struct device *dev);
 int system_if_down(struct device *dev);
 int system_if_check(struct device *dev);
 int system_if_dump_stats(struct device *dev, struct blob_buf *b);
+struct device *system_if_get_parent(struct device *dev);
 
 int system_add_address(struct device *dev, struct device_addr *addr);
 int system_del_address(struct device *dev, struct device_addr *addr);