struct avl_tree wireless_drivers;
static struct blob_buf b;
static int drv_fd;
+static LIST_HEAD(handlers);
+static bool handler_pending;
-static const struct blobmsg_policy wdev_policy =
- { .name = "disabled", .type = BLOBMSG_TYPE_BOOL };
+enum {
+ WDEV_ATTR_DISABLED,
+ WDEV_ATTR_RECONF,
+ WDEV_ATTR_SERIALIZE,
+ __WDEV_ATTR_MAX,
+};
+
+static const struct blobmsg_policy wdev_policy[__WDEV_ATTR_MAX] = {
+ [WDEV_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
+ [WDEV_ATTR_RECONF] = { .name = "reconf", .type = BLOBMSG_TYPE_BOOL },
+ [WDEV_ATTR_SERIALIZE] = { .name = "serialize", .type = BLOBMSG_TYPE_BOOL },
+};
static const struct uci_blob_param_list wdev_param = {
- .n_params = 1,
- .params = &wdev_policy,
+ .n_params = ARRAY_SIZE(wdev_policy),
+ .params = wdev_policy,
};
enum {
VIF_ATTR_DISABLED,
VIF_ATTR_NETWORK,
+ VIF_ATTR_ISOLATE,
+ VIF_ATTR_MODE,
__VIF_ATTR_MAX,
};
static const struct blobmsg_policy vif_policy[__VIF_ATTR_MAX] = {
[VIF_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL },
[VIF_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY },
+ [VIF_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL },
+ [VIF_ATTR_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
};
static const struct uci_blob_param_list vif_param = {
.params = vif_policy,
};
+static void
+wireless_handler_stop(struct wireless_device *wdev)
+{
+ if (wdev->handler_pending) {
+ wdev->handler_pending = false;
+ list_del(&wdev->handler);
+ }
+}
+
static void
put_container(struct blob_buf *buf, struct blob_attr *attr, const char *name)
{
if (!dev)
return;
- if (dev->type != &bridge_device_type)
+ if (!dev->type->bridge_capability)
return;
}
dev->hotplug_ops->prepare(dev);
blobmsg_add_string(buf, "bridge", dev->ifname);
+
+ if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST)
+ blobmsg_add_u8(buf, "multicast_to_unicast",
+ dev->settings.multicast_to_unicast);
}
static void
{
struct wireless_interface *vif;
+ wireless_handler_stop(wdev);
uloop_timeout_cancel(&wdev->script_check);
uloop_timeout_cancel(&wdev->timeout);
wireless_complete_kill_request(wdev);
if (up) {
struct device *dev = device_get(vif->ifname, 2);
- if (dev)
+ if (dev) {
+ dev->wireless_isolate = vif->isolate;
dev->wireless = true;
+ dev->wireless_ap = vif->ap_mode;
+ }
}
blobmsg_for_each_attr(cur, vif->network, rem) {
if (wdev->cancel)
return;
+ wireless_handler_stop(wdev);
D(WIRELESS, "Cancel wireless device '%s' setup\n", wdev->name);
wdev->cancel = true;
uloop_timeout_set(&wdev->timeout, 10 * 1000);
int i = 0;
int fds[2] = { -1, -1 };
+ wireless_handler_stop(wdev);
+
+ if (handler_pending && wdev->serialize) {
+ wdev->handler_action = up;
+ wdev->handler_pending = true;
+ list_add_tail(&wdev->handler, &handlers);
+ return;
+ }
+ if (wdev->serialize)
+ handler_pending = true;
+
D(WIRELESS, "Wireless device '%s' run %s handler\n", wdev->name, action);
- prepare_config(wdev, &b, up);
- config = blobmsg_format_json(b.head, true);
+ if (!up && wdev->prev_config) {
+ config = blobmsg_format_json(wdev->prev_config, true);
+ free(wdev->prev_config);
+ wdev->prev_config = NULL;
+ } else {
+ prepare_config(wdev, &b, up);
+ config = blobmsg_format_json(b.head, true);
+ }
argv[i++] = wdev->drv->script;
argv[i++] = wdev->drv->name;
}
static void
-__wireless_device_set_up(struct wireless_device *wdev)
+wireless_handler_next(void)
+{
+ struct wireless_device *wdev;
+
+ if (handler_pending)
+ return;
+ if (list_empty(&handlers))
+ return;
+ wdev = list_first_entry(&handlers, struct wireless_device, handler);
+ list_del(&wdev->handler);
+ wdev->handler_pending = false;
+ wireless_device_run_handler(wdev, wdev->handler_action);
+}
+
+static void
+__wireless_device_set_up(struct wireless_device *wdev, int force)
{
if (wdev->disabled)
return;
- if (wdev->state != IFS_DOWN || config_init)
+ if (wdev->retry_setup_failed)
+ return;
+
+ if (!wdev->autostart)
+ return;
+
+ if (!force && (wdev->state != IFS_DOWN || config_init))
return;
+ free(wdev->prev_config);
+ wdev->prev_config = NULL;
wdev->state = IFS_SETUP;
wireless_device_run_handler(wdev, true);
}
static void
wireless_device_free(struct wireless_device *wdev)
{
+ wireless_handler_stop(wdev);
vlist_flush_all(&wdev->interfaces);
avl_delete(&wireless_devices.avl, &wdev->node.avl);
free(wdev->config);
+ free(wdev->prev_config);
free(wdev);
}
switch(state) {
case IFC_NORMAL:
case IFC_RELOAD:
+ __wireless_device_set_up(wdev, 0);
+
wdev->config_state = IFC_NORMAL;
- if (wdev->autostart)
- __wireless_device_set_up(wdev);
break;
case IFC_REMOVE:
wireless_device_free(wdev);
{
struct wireless_device *wdev = container_of(timeout, struct wireless_device, timeout);
+ if (wdev->handler_pending) {
+ wdev->handler_pending = false;
+ list_del(&wdev->handler);
+ }
netifd_kill_process(&wdev->script_task);
wdev->script_task.cb(&wdev->script_task, -1);
wireless_device_mark_down(wdev);
{
wdev->retry = WIRELESS_SETUP_RETRY;
wdev->autostart = true;
- __wireless_device_set_up(wdev);
+ __wireless_device_set_up(wdev, 0);
+}
+
+void
+wireless_device_reconf(struct wireless_device *wdev)
+{
+ wdev->retry = WIRELESS_SETUP_RETRY;
+ wdev->autostart = true;
+ __wireless_device_set_up(wdev, wdev->reconf && (wdev->state == IFS_UP));
}
static void
return;
if (--wdev->retry < 0)
- wdev->autostart = false;
+ wdev->retry_setup_failed = true;
__wireless_device_set_down(wdev);
}
default:
break;
}
+
+ if (wdev->serialize) {
+ handler_pending = false;
+ wireless_handler_next();
+ }
}
void
wireless_device_set_down(struct wireless_device *wdev)
{
+ wdev->retry_setup_failed = false;
wdev->autostart = false;
__wireless_device_set_down(wdev);
}
wdev->config_state = s;
if (wdev->state == IFS_DOWN)
wdev_handle_config_change(wdev);
- else
+ else if (!wdev->reconf || wdev->state != IFS_UP)
__wireless_device_set_down(wdev);
}
+static void
+wdev_prepare_prev_config(struct wireless_device *wdev)
+{
+ if (wdev->prev_config)
+ return;
+
+ prepare_config(wdev, &b, false);
+ wdev->prev_config = blob_memdup(b.head);
+}
+
static void
wdev_change_config(struct wireless_device *wdev, struct wireless_device *wd_new)
{
free(wd_new);
+ wdev_prepare_prev_config(wdev);
if (blob_attr_equal(wdev->config, new_config) && wdev->disabled == disabled)
return;
free(wdev->config);
wdev->config = blob_memdup(new_config);
wdev->disabled = disabled;
+ wdev->retry_setup_failed = false;
wdev_set_config_state(wdev, IFC_RELOAD);
}
struct wireless_device *wd_new = container_of(node_new, struct wireless_device, node);
if (wd_old && wd_new) {
+ D(WIRELESS, "Update wireless device '%s'\n", wd_old->name);
wdev_change_config(wd_old, wd_new);
} else if (wd_old) {
D(WIRELESS, "Delete wireless device '%s'\n", wd_old->name);
if ((cur = tb[VIF_ATTR_NETWORK]))
vif->network = cur;
+
+ cur = tb[VIF_ATTR_ISOLATE];
+ if (cur)
+ vif->isolate = blobmsg_get_bool(cur);
+
+ cur = tb[VIF_ATTR_MODE];
+ if (cur)
+ vif->ap_mode = !strcmp(blobmsg_get_string(cur), "ap");
}
static void
}
D(WIRELESS, "Update wireless interface %s on device %s\n", vif_new->name, wdev->name);
+ wireless_interface_handle_link(vif_old, false);
free(vif_old->config);
vif_old->config = blob_memdup(vif_new->config);
+ vif_old->isolate = vif_new->isolate;
+ vif_old->ap_mode = vif_new->ap_mode;
wireless_interface_init_config(vif_old);
free(vif_new);
} else if (vif_new) {
wireless_interface_init_config(vif_new);
} else if (vif_old) {
D(WIRELESS, "Delete wireless interface %s on device %s\n", vif_old->name, wdev->name);
+ wireless_interface_handle_link(vif_old, false);
free((void *) vif_old->section);
free(vif_old->config);
free(vif_old);
{
struct wireless_device *wdev;
char *name_buf;
- struct blob_attr *disabled;
+ struct blob_attr *tb[__WDEV_ATTR_MAX];
+ struct blob_attr *cur;
- blobmsg_parse(&wdev_policy, 1, &disabled, blob_data(data), blob_len(data));
+ blobmsg_parse(wdev_policy, __WDEV_ATTR_MAX, tb, blob_data(data), blob_len(data));
wdev = calloc_a(sizeof(*wdev), &name_buf, strlen(name) + 1);
- if (disabled && blobmsg_get_bool(disabled))
- wdev->disabled = true;
+
+ cur = tb[WDEV_ATTR_DISABLED];
+ wdev->disabled = cur && blobmsg_get_bool(cur);
+
wdev->drv = drv;
wdev->state = IFS_DOWN;
wdev->config_state = IFC_NORMAL;
wdev->name = strcpy(name_buf, name);
wdev->config = data;
- wdev->config_autostart = true;
- wdev->autostart = wdev->config_autostart;
+ wdev->handler_pending = false;
+
+ cur = tb[WDEV_ATTR_SERIALIZE];
+ wdev->serialize = cur && blobmsg_get_bool(cur);
+
+ cur = tb[WDEV_ATTR_RECONF];
+ wdev->reconf = cur && blobmsg_get_bool(cur);
+
+ wdev->retry_setup_failed = false;
+ wdev->autostart = true;
INIT_LIST_HEAD(&wdev->script_proc);
vlist_init(&wdev->interfaces, avl_strcmp, vif_update);
wdev->interfaces.keep_old = true;
vif->wdev = wdev;
vif->config = data;
vif->section = section;
+ vif->isolate = false;
+
vlist_add(&wdev->interfaces, &vif->node, vif->name);
}
blobmsg_add_u8(b, "pending", wdev->state == IFS_SETUP || wdev->state == IFS_TEARDOWN);
blobmsg_add_u8(b, "autostart", wdev->autostart);
blobmsg_add_u8(b, "disabled", wdev->disabled);
+ blobmsg_add_u8(b, "retry_setup_failed", wdev->retry_setup_failed);
put_container(b, wdev->config, "config");
i = blobmsg_open_array(b, "interfaces");
struct wireless_device *wdev;
vlist_for_each_element(&wireless_devices, wdev, node)
- if (wdev->autostart)
- __wireless_device_set_up(wdev);
+ __wireless_device_set_up(wdev, 0);
}