From 570b4832641e62a817403c70913a2ee2acf1d418 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sat, 20 May 2017 01:39:38 +0200 Subject: [PATCH] device: Add a SignalLevelAgent D-bus API Allow registering and unregistering agent object to receive RSSI level notifications. The methods are similar to the ones related to the password agent, including a Release method for the agent. --- src/dbus.h | 1 + src/device.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/src/dbus.h b/src/dbus.h index 94f2db84..21d99e5c 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -31,6 +31,7 @@ #define IWD_AGENT_INTERFACE "net.connman.iwd.Agent" #define IWD_WSC_INTERFACE "net.connman.iwd.WiFiSimpleConfiguration" #define IWD_KNOWN_NETWORKS_INTERFACE "net.connman.iwd.KnownNetworks" +#define IWD_SIGNAL_AGENT_INTERFACE "net.connman.iwd.SignalLevelAgent" #define IWD_AGENT_MANAGER_PATH "/" #define IWD_KNOWN_NETWORKS_PATH "/" diff --git a/src/device.c b/src/device.c index 4676a0ed..f6deb070 100644 --- a/src/device.c +++ b/src/device.c @@ -77,6 +77,7 @@ struct device { struct l_timeout *roam_trigger_timeout; uint32_t roam_scan_id; uint8_t preauth_bssid[ETH_ALEN]; + struct signal_agent *signal_agent; struct wiphy *wiphy; struct netdev *netdev; @@ -87,6 +88,12 @@ struct device { bool signal_low : 1; }; +struct signal_agent { + char *owner; + char *path; + unsigned int disconnect_watch; +}; + static struct watchlist device_watches; static struct l_queue *device_list; @@ -1356,6 +1363,38 @@ static void device_connect_cb(struct netdev *netdev, enum netdev_result result, device->autoconnect = true; } +static void device_signal_agent_notify(struct signal_agent *agent, + const char *device_path, int level) +{ + struct l_dbus_message *msg; + uint8_t value = level; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + agent->owner, agent->path, + IWD_SIGNAL_AGENT_INTERFACE, + "SignalLevelChanged"); + l_dbus_message_set_arguments(msg, "oy", device_path, value); + l_dbus_message_set_no_reply(msg, true); + + l_dbus_send(dbus_get_bus(), msg); +} + +static void device_signal_agent_release(struct signal_agent *agent, + const char *device_path) +{ + struct l_dbus_message *msg; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + agent->owner, agent->path, + IWD_SIGNAL_AGENT_INTERFACE, + "Release"); + l_dbus_message_set_arguments(msg, "o", device_path); + l_dbus_message_set_no_reply(msg, true); + + l_dbus_send(dbus_get_bus(), msg); +} + + static void device_netdev_event(struct netdev *netdev, enum netdev_event event, void *user_data) { @@ -1410,6 +1449,11 @@ static void device_netdev_event(struct netdev *netdev, enum netdev_event event, break; case NETDEV_EVENT_RSSI_LEVEL_NOTIFY: + if (device->signal_agent) + device_signal_agent_notify(device->signal_agent, + device_get_path(device), + netdev_get_rssi_level(device->netdev)); + break; }; } @@ -1635,6 +1679,113 @@ static struct l_dbus_message *device_get_networks(struct l_dbus *dbus, return reply; } +static void signal_agent_free(void *data) +{ + struct signal_agent *agent = data; + + l_free(agent->owner); + l_free(agent->path); + l_dbus_remove_watch(dbus_get_bus(), agent->disconnect_watch); + l_free(agent); +} + +static void signal_agent_disconnect(struct l_dbus *dbus, void *user_data) +{ + struct device *device = user_data; + + l_debug("signal_agent %s disconnected", device->signal_agent->owner); + + l_idle_oneshot(signal_agent_free, device->signal_agent, NULL); + device->signal_agent = NULL; + + netdev_set_rssi_report_levels(device->netdev, NULL, 0); +} + +static struct l_dbus_message *device_signal_agent_register(struct l_dbus *dbus, + struct l_dbus_message *message, + void *user_data) +{ + struct device *device = user_data; + const char *path, *sender; + struct l_dbus_message_iter level_iter; + int8_t levels[16]; + int err; + int16_t val; + size_t count = 0; + + if (device->signal_agent) + return dbus_error_already_exists(message); + + l_debug("signal agent register called"); + + if (!l_dbus_message_get_arguments(message, "oan", &path, &level_iter)) + return dbus_error_invalid_args(message); + + while (l_dbus_message_iter_next_entry(&level_iter, &val)) { + if (count >= L_ARRAY_SIZE(levels) || val > 127 || val < -127) + return dbus_error_invalid_args(message); + + levels[count++] = val; + } + + if (count < 1 || count > 16) + return dbus_error_invalid_args(message); + + err = netdev_set_rssi_report_levels(device->netdev, levels, count); + if (err == -ENOTSUP) + return dbus_error_not_supported(message); + else if (err < 0) + return dbus_error_failed(message); + + sender = l_dbus_message_get_sender(message); + + device->signal_agent = l_new(struct signal_agent, 1); + device->signal_agent->owner = l_strdup(sender); + device->signal_agent->path = l_strdup(path); + device->signal_agent->disconnect_watch = + l_dbus_add_disconnect_watch(dbus, sender, + signal_agent_disconnect, + device, NULL); + + l_debug("agent %s path %s", sender, path); + + /* + * TODO: send an initial notification in a oneshot idle callback, + * if state is connected. + */ + + return l_dbus_message_new_method_return(message); +} + +static struct l_dbus_message *device_signal_agent_unregister( + struct l_dbus *dbus, + struct l_dbus_message *message, + void *user_data) +{ + struct device *device = user_data; + const char *path, *sender; + + if (!device->signal_agent) + return dbus_error_failed(message); + + l_debug("signal agent unregister"); + + if (!l_dbus_message_get_arguments(message, "o", &path)) + return dbus_error_invalid_args(message); + + sender = l_dbus_message_get_sender(message); + + if (strcmp(device->signal_agent->owner, sender)) + return dbus_error_not_found(message); + + signal_agent_free(device->signal_agent); + device->signal_agent = NULL; + + netdev_set_rssi_report_levels(device->netdev, NULL, 0); + + return l_dbus_message_new_method_return(message); +} + static bool device_property_get_name(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, @@ -1806,6 +1957,12 @@ static void setup_device_interface(struct l_dbus_interface *interface) l_dbus_interface_method(interface, "GetOrderedNetworks", 0, device_get_networks, "a(osns)", "", "networks"); + l_dbus_interface_method(interface, "RegisterSignalLevelAgent", 0, + device_signal_agent_register, + "", "oan", "path", "levels"); + l_dbus_interface_method(interface, "UnregisterSignalLevelAgent", 0, + device_signal_agent_unregister, + "", "o", "path"); l_dbus_interface_property(interface, "Name", 0, "s", device_property_get_name, NULL); @@ -1950,6 +2107,12 @@ static void device_free(void *user) dbus_pending_reply(&device->connect_pending, dbus_error_aborted(device->connect_pending)); + if (device->signal_agent) { + device_signal_agent_release(device->signal_agent, + device_get_path(device)); + signal_agent_free(device->signal_agent); + } + if (device->state != DEVICE_STATE_OFF) WATCHLIST_NOTIFY(&device_watches, device_watch_func_t, device, DEVICE_EVENT_REMOVED);