device: added DEVICE_EVENT_MODE_CHANGED

Rather than have device.c manage the creation/removal of
AP/AdHoc interfaces this new event was introduced. Now
anyone can listen for device events and if the mode changes
handle accordingly. This fixes potential memory leaks
in WSC when switching modes as well.
This commit is contained in:
James Prestwood 2018-07-17 16:39:54 -07:00 committed by Denis Kenzior
parent 06c2d227c8
commit 8cf44499d1
7 changed files with 140 additions and 125 deletions

View File

@ -62,6 +62,8 @@ struct sta_state {
bool authenticated : 1;
};
static uint32_t device_watch;
static void adhoc_sta_free(void *data)
{
struct sta_state *sta = data;
@ -516,18 +518,8 @@ static void adhoc_destroy_interface(void *user_data)
adhoc_free(adhoc);
}
bool adhoc_init(void)
{
return l_dbus_register_interface(dbus_get_bus(), IWD_ADHOC_INTERFACE,
adhoc_setup_interface, adhoc_destroy_interface, false);
}
void adhoc_exit(void)
{
l_dbus_unregister_interface(dbus_get_bus(), IWD_ADHOC_INTERFACE);
}
bool adhoc_add_interface(struct device *device)
static void adhoc_add_interface(struct device *device)
{
struct adhoc_state *adhoc;
@ -539,12 +531,43 @@ bool adhoc_add_interface(struct device *device)
adhoc_netdev_notify, adhoc);
/* setup ap dbus interface */
return l_dbus_object_add_interface(dbus_get_bus(),
l_dbus_object_add_interface(dbus_get_bus(),
device_get_path(device), IWD_ADHOC_INTERFACE, adhoc);
}
bool adhoc_remove_interface(struct device *device)
static void adhoc_remove_interface(struct device *device)
{
return l_dbus_object_remove_interface(dbus_get_bus(),
l_dbus_object_remove_interface(dbus_get_bus(),
device_get_path(device), IWD_ADHOC_INTERFACE);
}
static void ap_device_event(struct device *device, enum device_event event,
void *userdata)
{
switch (event) {
case DEVICE_EVENT_MODE_CHANGED:
if (device_get_mode(device) == DEVICE_MODE_ADHOC)
adhoc_add_interface(device);
else
adhoc_remove_interface(device);
default:
break;
}
}
bool adhoc_init(void)
{
device_watch = device_watch_add(ap_device_event, NULL, NULL);
if (!device_watch)
return false;
return l_dbus_register_interface(dbus_get_bus(), IWD_ADHOC_INTERFACE,
adhoc_setup_interface, adhoc_destroy_interface, false);
}
void adhoc_exit(void)
{
device_watch_remove(device_watch);
l_dbus_unregister_interface(dbus_get_bus(), IWD_ADHOC_INTERFACE);
}

View File

@ -22,8 +22,5 @@
struct device;
bool adhoc_add_interface(struct device *device);
bool adhoc_remove_interface(struct device *device);
bool adhoc_init(void);
void adhoc_exit(void);

View File

@ -82,6 +82,7 @@ struct sta_state {
};
static struct l_genl_family *nl80211 = NULL;
static uint32_t device_watch;
static void ap_sta_free(void *data)
{
@ -1421,8 +1422,45 @@ static void ap_destroy_interface(void *user_data)
ap_free(ap);
}
static void ap_add_interface(struct device *device)
{
struct ap_state *ap;
/* just allocate/set device, Start method will complete setup */
ap = l_new(struct ap_state, 1);
ap->device = device;
/* setup ap dbus interface */
l_dbus_object_add_interface(dbus_get_bus(),
device_get_path(device), IWD_AP_INTERFACE, ap);
}
static void ap_remove_interface(struct device *device)
{
l_dbus_object_remove_interface(dbus_get_bus(),
device_get_path(device), IWD_AP_INTERFACE);
}
static void ap_device_event(struct device *device, enum device_event event,
void *userdata)
{
switch (event) {
case DEVICE_EVENT_MODE_CHANGED:
if (device_get_mode(device) == DEVICE_MODE_AP)
ap_add_interface(device);
else
ap_remove_interface(device);
default:
break;
}
}
bool ap_init(struct l_genl_family *in)
{
device_watch = device_watch_add(ap_device_event, NULL, NULL);
if (!device_watch)
return false;
nl80211 = in;
return l_dbus_register_interface(dbus_get_bus(), IWD_AP_INTERFACE,
@ -1435,24 +1473,7 @@ bool ap_init(struct l_genl_family *in)
void ap_exit(void)
{
device_watch_remove(device_watch);
l_dbus_unregister_interface(dbus_get_bus(), IWD_AP_INTERFACE);
}
bool ap_add_interface(struct device *device)
{
struct ap_state *ap;
/* just allocate/set device, Start method will complete setup */
ap = l_new(struct ap_state, 1);
ap->device = device;
/* setup ap dbus interface */
return l_dbus_object_add_interface(dbus_get_bus(),
device_get_path(device), IWD_AP_INTERFACE, ap);
}
bool ap_remove_interface(struct device *device)
{
return l_dbus_object_remove_interface(dbus_get_bus(),
device_get_path(device), IWD_AP_INTERFACE);
}

View File

@ -22,8 +22,5 @@
struct device;
bool ap_add_interface(struct device *device);
bool ap_remove_interface(struct device *device);
bool ap_init(struct l_genl_family *in);
void ap_exit(void);

View File

@ -2081,78 +2081,6 @@ static void device_prepare_adhoc_ap_mode(struct device *device)
device->networks_sorted = l_queue_new();
}
static struct l_dbus_message *device_set_mode_adhoc(struct device *device,
struct l_dbus_message *message)
{
if (device->mode == DEVICE_MODE_ADHOC)
return dbus_error_already_exists(message);
if (device->state != DEVICE_STATE_DISCONNECTED &&
device->state != DEVICE_STATE_AUTOCONNECT)
return dbus_error_busy(message);
l_debug("");
device_prepare_adhoc_ap_mode(device);
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_ADHOC);
device->mode = DEVICE_MODE_ADHOC;
adhoc_add_interface(device);
return NULL;
}
static struct l_dbus_message *device_set_mode_ap(struct device *device,
struct l_dbus_message *message)
{
if (device->mode == DEVICE_MODE_AP)
return dbus_error_already_exists(message);
if (device->state != DEVICE_STATE_DISCONNECTED &&
device->state != DEVICE_STATE_AUTOCONNECT)
return dbus_error_busy(message);
l_debug("");
device_prepare_adhoc_ap_mode(device);
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_AP);
device->mode = DEVICE_MODE_AP;
ap_add_interface(device);
return NULL;
}
static struct l_dbus_message *device_set_mode_sta(struct device *device,
struct l_dbus_message *message)
{
if (device->mode == DEVICE_MODE_STATION)
return dbus_error_already_exists(message);
switch (device->mode) {
case DEVICE_MODE_AP:
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION);
ap_remove_interface(device);
break;
case DEVICE_MODE_ADHOC:
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION);
adhoc_remove_interface(device);
break;
default:
return dbus_error_not_found(message);
}
device->mode = DEVICE_MODE_STATION;
l_debug("");
return NULL;
}
static bool device_network_is_known(const char *ssid, enum security security)
{
const struct network_info *network_info =
@ -2547,6 +2475,40 @@ static bool device_property_get_mode(struct l_dbus *dbus,
return true;
}
static struct l_dbus_message *device_change_mode(struct device *device,
struct l_dbus_message *message, enum device_mode mode)
{
if (device->mode == mode)
return dbus_error_already_exists(message);
/* ensure correct connection state in AP/AdHoc mode */
if ((mode == DEVICE_MODE_AP || mode == DEVICE_MODE_ADHOC) &&
(device->state != DEVICE_STATE_DISCONNECTED &&
device->state != DEVICE_STATE_AUTOCONNECT))
return dbus_error_busy(message);
switch (mode) {
case DEVICE_MODE_AP:
device_prepare_adhoc_ap_mode(device);
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_AP);
break;
case DEVICE_MODE_ADHOC:
device_prepare_adhoc_ap_mode(device);
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_ADHOC);
break;
case DEVICE_MODE_STATION:
netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION);
break;
}
device->mode = mode;
WATCHLIST_NOTIFY(&device_watches, device_watch_func_t, device,
DEVICE_EVENT_MODE_CHANGED);
return NULL;
}
static struct l_dbus_message *device_property_set_mode(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_iter *new_value,
@ -2556,25 +2518,23 @@ static struct l_dbus_message *device_property_set_mode(struct l_dbus *dbus,
struct device *device = user_data;
struct l_dbus_message *reply;
const char* mode;
enum device_mode change;
if (!l_dbus_message_iter_get_variant(new_value, "s", &mode))
return dbus_error_invalid_args(message);
if (!strcmp(mode, "station")) {
reply = device_set_mode_sta(device, message);
if (reply)
return reply;
} else if (!strcmp(mode, "ap")) {
reply = device_set_mode_ap(device, message);
if (reply)
return reply;
} else if (!strcmp(mode, "ad-hoc")) {
reply = device_set_mode_adhoc(device, message);
if (reply)
return reply;
} else {
if (!strcmp(mode, "station"))
change = DEVICE_MODE_STATION;
else if (!strcmp(mode, "ap"))
change = DEVICE_MODE_AP;
else if (!strcmp(mode, "ad-hoc"))
change = DEVICE_MODE_ADHOC;
else
return dbus_error_invalid_args(message);
}
reply = device_change_mode(device, message, change);
if (reply)
return reply;
complete(dbus, message, NULL);

View File

@ -31,6 +31,7 @@ struct device;
enum device_event {
DEVICE_EVENT_INSERTED,
DEVICE_EVENT_REMOVED,
DEVICE_EVENT_MODE_CHANGED,
};
enum device_state {

View File

@ -1069,6 +1069,20 @@ static void device_disappeared(struct device *device, void *userdata)
IWD_WSC_INTERFACE);
}
static void device_mode_changed(struct device *device, void *userdata)
{
enum device_mode mode = device_get_mode(device);
switch (mode) {
case DEVICE_MODE_STATION:
device_appeared(device, userdata);
break;
default:
device_disappeared(device, userdata);
break;
}
}
static void device_event(struct device *device, enum device_event event,
void *userdata)
{
@ -1077,6 +1091,8 @@ static void device_event(struct device *device, enum device_event event,
return device_appeared(device, userdata);
case DEVICE_EVENT_REMOVED:
return device_disappeared(device, userdata);
case DEVICE_EVENT_MODE_CHANGED:
return device_mode_changed(device, userdata);
}
}