#include "config.h"
static struct avl_tree devices;
-static struct avl_tree aliases;
-
-struct alias_device {
- struct avl_node avl;
- struct device dev;
- struct device_user dep;
- bool cleanup;
- char name[];
-};
-
-static const struct device_type alias_device_type;
static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = {
[DEV_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
.free = simple_device_free,
};
-static int
-alias_device_set_state(struct device *dev, bool state)
-{
- struct alias_device *alias;
-
- alias = container_of(dev, struct alias_device, dev);
- if (!alias->dep.dev)
- return -1;
-
- if (state)
- return device_claim(&alias->dep);
-
- device_release(&alias->dep);
- if (alias->cleanup)
- device_remove_user(&alias->dep);
- return 0;
-}
-
-static struct device *
-alias_device_create(const char *name, struct blob_attr *attr)
-{
- struct alias_device *alias;
-
- alias = calloc(1, sizeof(*alias) + strlen(name) + 1);
- strcpy(alias->name, name);
- alias->dev.set_state = alias_device_set_state;
- device_init_virtual(&alias->dev, &alias_device_type, NULL);
- alias->avl.key = alias->name;
- avl_insert(&aliases, &alias->avl);
-
- return &alias->dev;
-}
-
-static void alias_device_free(struct device *dev)
-{
- struct alias_device *alias;
-
- alias = container_of(dev, struct alias_device, dev);
- avl_delete(&aliases, &alias->avl);
- free(alias);
-}
-
-static const struct device_type alias_device_type = {
- .name = "Network alias",
- .create = alias_device_create,
- .free = alias_device_free,
-};
-
static void
device_merge_settings(struct device *dev, struct device_settings *n)
{
static void __init dev_init(void)
{
avl_init(&devices, avl_strcmp, true, NULL);
- avl_init(&aliases, avl_strcmp, false, NULL);
}
-void device_broadcast_event(struct device *dev, enum device_event ev)
-{
- struct device_user *dep, *tmp;
-
- list_for_each_entry_safe(dep, tmp, &dev->users, list) {
- if (!dep->cb)
- continue;
- dep->cb(dep, ev);
- }
-}
-
-void
-alias_notify_device(const char *name, struct device *dev)
+static void __device_broadcast_event(struct list_head *head, enum device_event ev)
{
- struct alias_device *alias;
+ struct device_user *dep;
+ static uint8_t idx[__DEV_EVENT_MAX];
+ bool found;
- device_lock();
-
- alias = avl_find_element(&aliases, name, alias, avl);
- if (!alias)
- return;
+ idx[ev]++;
+ do {
+ found = false;
- alias->cleanup = !dev;
- if (dev) {
- if (dev != alias->dep.dev) {
- device_remove_user(&alias->dep);
- strcpy(alias->dev.ifname, dev->ifname);
- device_add_user(&alias->dep, dev);
- }
- }
+ list_for_each_entry(dep, head, list) {
+ if (!dep->cb)
+ continue;
- device_set_present(&alias->dev, !!dev);
+ if (dep->ev_idx[ev] == idx[ev])
+ continue;
- if (!dev && alias->dep.dev && !alias->dep.dev->active)
- device_remove_user(&alias->dep);
+ dep->cb(dep, ev);
+ dep->ev_idx[ev] = idx[ev];
+ found = true;
+ break;
+ }
+ } while (found);
+}
- device_unlock();
+void device_broadcast_event(struct device *dev, enum device_event ev)
+{
+ __device_broadcast_event(&dev->aliases, ev);
+ __device_broadcast_event(&dev->users, ev);
}
int device_claim(struct device_user *dep)
D(DEVICE, "Initialize device '%s'\n", dev->ifname);
INIT_LIST_HEAD(&dev->users);
+ INIT_LIST_HEAD(&dev->aliases);
dev->type = type;
+
+ if (!dev->set_state)
+ dev->set_state = set_device_state;
}
int device_init(struct device *dev, const struct device_type *type, const char *ifname)
device_init_virtual(dev, type, ifname);
- if (!dev->set_state)
- dev->set_state = set_device_state;
-
dev->avl.key = dev->ifname;
ret = avl_insert(&devices, &dev->avl);
return dev;
}
-static struct device *
-device_alias_get(const char *name)
-{
- struct alias_device *alias;
-
- alias = avl_find_element(&aliases, name, alias, avl);
- if (alias)
- return &alias->dev;
-
- return alias_device_create(name, NULL);
-}
-
struct device *
device_get(const char *name, int create)
{
return device_alias_get(name + 1);
dev = avl_find_element(&devices, name, dev, avl);
- if (dev)
+ if (dev) {
+ if (create > 1 && !dev->external) {
+ dev->external = true;
+ device_set_present(dev, true);
+ }
return dev;
+ }
if (!create)
return NULL;
device_refresh_present(dev);
}
+static int device_refcount(struct device *dev)
+{
+ struct list_head *list;
+ int count = 0;
+
+ list_for_each(list, &dev->users)
+ count++;
+
+ return count;
+}
+
void device_add_user(struct device_user *dep, struct device *dev)
{
+ struct list_head *head;
+
if (dep->dev)
device_remove_user(dep);
return;
dep->dev = dev;
- list_add_tail(&dep->list, &dev->users);
+
+ if (dep->alias)
+ head = &dev->aliases;
+ else
+ head = &dev->users;
+ list_add_tail(&dep->list, head);
+ D(DEVICE, "Add user for device '%s', refcount=%d\n", dev->ifname, device_refcount(dev));
+
if (dep->cb && dev->present) {
dep->cb(dep, DEV_EVENT_ADD);
if (dev->active)
list_del(&dep->list);
dep->dev = NULL;
+ D(DEVICE, "Remove user for device '%s', refcount=%d\n", dev->ifname, device_refcount(dev));
__device_free_unused(dev);
}
if (odev) {
odev->current_config = true;
change = device_set_config(odev, type, config);
+ if (odev->external) {
+ system_if_apply_settings(odev, &odev->settings);
+ change = DEV_CONFIG_APPLIED;
+ }
switch (change) {
case DEV_CONFIG_RESTART:
case DEV_CONFIG_APPLIED:
if (st.flags & DEV_OPT_MTU)
blobmsg_add_u32(b, "mtu", st.mtu);
if (st.flags & DEV_OPT_MACADDR)
- blobmsg_add_string(b, "macaddr", ether_ntoa((struct ether_addr *) st.macaddr));
+ blobmsg_add_string(b, "macaddr", format_macaddr(st.macaddr));
if (st.flags & DEV_OPT_TXQUEUELEN)
blobmsg_add_u32(b, "txqueuelen", st.txqueuelen);
}