diff --git a/src/ap.c b/src/ap.c index 3066eb2c..2a265130 100644 --- a/src/ap.c +++ b/src/ap.c @@ -43,12 +43,12 @@ #include "src/eapol.h" #include "src/handshake.h" #include "src/ap.h" +#include "src/dbus.h" struct ap_state { struct device *device; char *ssid; char *psk; - ap_event_cb_t event_cb; int channel; unsigned int ciphers; uint32_t beacon_interval; @@ -60,6 +60,9 @@ struct ap_state { uint16_t last_aid; struct l_queue *sta_states; + + struct l_dbus_message *pending; + bool started : 1; }; struct sta_state { @@ -80,8 +83,6 @@ struct sta_state { static struct l_genl_family *nl80211 = NULL; -static struct l_queue *ap_list = NULL; - static void ap_sta_free(void *data) { struct sta_state *sta = data; @@ -106,11 +107,14 @@ static void ap_frame_watch_remove(void *data, void *user_data) netdev_frame_watch_remove(netdev, L_PTR_TO_UINT(data)); } -static void ap_free(void *data) +static void ap_reset(struct ap_state *ap) { - struct ap_state *ap = data; struct netdev *netdev = device_get_netdev(ap->device); + if (ap->pending) + dbus_pending_reply(&ap->pending, + dbus_error_aborted(ap->pending)); + l_free(ap->ssid); memset(ap->psk, 0, strlen(ap->psk)); l_free(ap->psk); @@ -128,6 +132,15 @@ static void ap_free(void *data) if (ap->rates) l_uintset_free(ap->rates); + ap->started = false; +} + +static void ap_free(void *data) +{ + struct ap_state *ap = data; + + ap_reset(ap); + l_free(ap); } @@ -1136,15 +1149,6 @@ static void ap_deauth_cb(struct netdev *netdev, const struct mmpdu_header *hdr, ap_sta_free(sta); } -static void ap_stopped(struct ap_state *ap) -{ - ap->event_cb(ap->device, AP_EVENT_STOPPED); - - ap_free(ap); - - l_queue_remove(ap_list, ap); -} - static void ap_netdev_notify(struct netdev *netdev, enum netdev_watch_event event, void *user_data) { @@ -1152,7 +1156,7 @@ static void ap_netdev_notify(struct netdev *netdev, switch (event) { case NETDEV_WATCH_EVENT_DOWN: - ap_stopped(ap); + ap_reset(ap); break; default: break; @@ -1165,22 +1169,23 @@ static void ap_start_cb(struct l_genl_msg *msg, void *user_data) ap->start_stop_cmd_id = 0; + if (!ap->pending) + return; + if (l_genl_msg_get_error(msg) < 0) { l_error("START_AP failed: %i", l_genl_msg_get_error(msg)); - ap_stopped(ap); - } else { - l_info("START_AP ok"); + dbus_pending_reply(&ap->pending, + dbus_error_invalid_args(ap->pending)); + ap_reset(ap); - ap->event_cb(ap->device, AP_EVENT_STARTED); + return; } -} -static bool ap_match_device(const void *a, const void *b) -{ - const struct ap_state *ap = a; + dbus_pending_reply(&ap->pending, + l_dbus_message_new_method_return(ap->pending)); - return ap->device == b; + ap->started = true; } static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap) @@ -1250,24 +1255,17 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap) return cmd; } -int ap_start(struct device *device, const char *ssid, const char *psk, - ap_event_cb_t event_cb) +static int ap_start(struct ap_state *ap, const char *ssid, const char *psk, + struct l_dbus_message *message) { - struct netdev *netdev = device_get_netdev(device); - struct wiphy *wiphy = device_get_wiphy(device); - struct ap_state *ap; + struct netdev *netdev = device_get_netdev(ap->device); + struct wiphy *wiphy = device_get_wiphy(ap->device); struct l_genl_msg *cmd; const struct l_queue_entry *entry; uint32_t id; - if (l_queue_find(ap_list, ap_match_device, device)) - return -EEXIST; - - ap = l_new(struct ap_state, 1); - ap->device = device; ap->ssid = l_strdup(ssid); ap->psk = l_strdup(psk); - ap->event_cb = event_cb; /* TODO: Start a Get Survey to decide the channel */ ap->channel = 6; /* TODO: Add all ciphers supported by wiphy */ @@ -1333,15 +1331,12 @@ int ap_start(struct device *device, const char *ssid, const char *psk, ap->netdev_watch_id = netdev_watch_add(netdev, ap_netdev_notify, ap); - if (!ap_list) - ap_list = l_queue_new(); - - l_queue_push_tail(ap_list, ap); + ap->pending = l_dbus_message_ref(message); return 0; error: - ap_free(ap); + ap_reset(ap); return -EIO; } @@ -1352,12 +1347,21 @@ static void ap_stop_cb(struct l_genl_msg *msg, void *user_data) ap->start_stop_cmd_id = 0; - if (l_genl_msg_get_error(msg) < 0) - l_error("STOP_AP failed: %i", l_genl_msg_get_error(msg)); - else - l_info("STOP_AP ok"); + if (!ap->pending) + goto end; - ap_stopped(ap); + if (l_genl_msg_get_error(msg) < 0) { + l_error("STOP_AP failed: %i", l_genl_msg_get_error(msg)); + dbus_pending_reply(&ap->pending, + dbus_error_failed(ap->pending)); + goto end; + } + + dbus_pending_reply(&ap->pending, + l_dbus_message_new_method_return(ap->pending)); + +end: + ap_reset(ap); } static struct l_genl_msg *ap_build_cmd_stop_ap(struct ap_state *ap) @@ -1371,13 +1375,9 @@ static struct l_genl_msg *ap_build_cmd_stop_ap(struct ap_state *ap) return cmd; } -int ap_stop(struct device *device) +static int ap_stop(struct ap_state *ap, struct l_dbus_message *message) { struct l_genl_msg *cmd; - struct ap_state *ap = l_queue_find(ap_list, ap_match_device, device); - - if (!ap) - return -ENODEV; cmd = ap_build_cmd_stop_ap(ap); if (!cmd) @@ -1393,13 +1393,70 @@ int ap_stop(struct device *device) return -EIO; } + ap->pending = l_dbus_message_ref(message); + return 0; } -void ap_init(struct l_genl_family *in) +static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus, + struct l_dbus_message *message, void *user_data) +{ + struct ap_state *ap = user_data; + const char *ssid, *wpa2_psk; + + if (ap->pending) + return dbus_error_busy(message); + + if (ap->started) + return dbus_error_already_exists(message); + + if (!l_dbus_message_get_arguments(message, "ss", &ssid, &wpa2_psk)) + return dbus_error_invalid_args(message); + + if (ap_start(ap, ssid, wpa2_psk, message) < 0) + return dbus_error_invalid_args(message); + + return NULL; +} + +static struct l_dbus_message *ap_dbus_stop(struct l_dbus *dbus, + struct l_dbus_message *message, void *user_data) +{ + struct ap_state *ap = user_data; + + if (ap->pending) + return dbus_error_busy(message); + + /* already stopped, no-op */ + if (!ap->started) + return l_dbus_message_new_method_return(message); + + if (ap_stop(ap, message) < 0) + return dbus_error_failed(message); + + return NULL; +} + +static void ap_setup_interface(struct l_dbus_interface *interface) +{ + l_dbus_interface_method(interface, "Start", 0, ap_dbus_start, "", + "ss", "ssid", "wpa2_psk"); + l_dbus_interface_method(interface, "Stop", 0, ap_dbus_stop, "", ""); +} + +static void ap_destroy_interface(void *user_data) +{ + struct ap_state *ap = user_data; + + ap_free(ap); +} + +bool ap_init(struct l_genl_family *in) { nl80211 = in; + return l_dbus_register_interface(dbus_get_bus(), IWD_AP_INTERFACE, + ap_setup_interface, ap_destroy_interface, false); /* * TODO: Check wiphy supports AP mode, supported channels, * check wiphy's NL80211_ATTR_TX_FRAME_TYPES. @@ -1408,5 +1465,24 @@ void ap_init(struct l_genl_family *in) void ap_exit(void) { - l_queue_destroy(ap_list, ap_free); + 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); } diff --git a/src/ap.h b/src/ap.h index 8c56344c..e07d68e2 100644 --- a/src/ap.h +++ b/src/ap.h @@ -29,9 +29,8 @@ struct device; typedef void (*ap_event_cb_t)(struct device *device, enum ap_event event_type); -int ap_start(struct device *device, const char *ssid, const char *psk, - ap_event_cb_t event_cb); -int ap_stop(struct device *device); +bool ap_add_interface(struct device *device); +bool ap_remove_interface(struct device *device); -void ap_init(struct l_genl_family *in); +bool ap_init(struct l_genl_family *in); void ap_exit(void); diff --git a/src/device.c b/src/device.c index 05739be2..f8ce1dcb 100644 --- a/src/device.c +++ b/src/device.c @@ -94,6 +94,8 @@ struct device { bool seen_hidden_networks : 1; uint32_t ap_roam_watch; + + enum device_mode mode; }; struct signal_agent { @@ -169,8 +171,6 @@ static const char *device_state_to_string(enum device_state state) return "disconnecting"; case DEVICE_STATE_ROAMING: return "roaming"; - case DEVICE_STATE_AP: - return "accesspoint"; } return "invalid"; @@ -407,7 +407,7 @@ static bool new_scan_results(uint32_t wiphy_id, uint32_t ifindex, int err, if (err) return false; - if (device->state == DEVICE_STATE_AP) + if (device->mode == DEVICE_MODE_AP) return false; device_set_scan_results(device, bss_list); @@ -464,6 +464,11 @@ enum device_state device_get_state(struct device *device) return device->state; } +enum device_mode device_get_mode(struct device *device) +{ + return device->mode; +} + static void periodic_scan_trigger(int err, void *user_data) { struct device *device = user_data; @@ -537,9 +542,6 @@ static void device_enter_state(struct device *device, enum device_state state) break; case DEVICE_STATE_ROAMING: break; - case DEVICE_STATE_AP: - periodic_scan_stop(device); - break; } disconnected = device->state <= DEVICE_STATE_AUTOCONNECT; @@ -1770,7 +1772,7 @@ static struct l_dbus_message *device_scan(struct l_dbus *dbus, return dbus_error_busy(message); if (device->state == DEVICE_STATE_OFF || - device->state == DEVICE_STATE_AP) + device->mode != DEVICE_MODE_STATION) return dbus_error_failed(message); device->scan_pending = l_dbus_message_ref(message); @@ -2043,73 +2045,17 @@ static struct l_dbus_message *device_signal_agent_unregister( return l_dbus_message_new_method_return(message); } -static void device_ap_event(struct device *device, enum ap_event event_type) +static struct l_dbus_message *device_set_mode_ap(struct device *device, + struct l_dbus_message *message) { - struct l_dbus_message *reply; - - switch (event_type) { - case AP_EVENT_STARTED: - if (!device->start_ap_pending) - break; - - reply = l_dbus_message_new_method_return( - device->start_ap_pending); - - dbus_pending_reply(&device->start_ap_pending, reply); - - break; - - case AP_EVENT_STOPPED: - if (device->state == DEVICE_STATE_AP) { - netdev_set_iftype(device->netdev, - NETDEV_IFTYPE_STATION); - - device_enter_state(device, DEVICE_STATE_DISCONNECTED); - } - - if (device->start_ap_pending) { - reply = dbus_error_failed(device->start_ap_pending); - - dbus_pending_reply(&device->start_ap_pending, reply); - } else if (device->stop_ap_pending) { - reply = l_dbus_message_new_method_return( - device->stop_ap_pending); - - dbus_pending_reply(&device->stop_ap_pending, reply); - } - - break; - } -} - -static struct l_dbus_message *device_start_ap(struct l_dbus *dbus, - struct l_dbus_message *message, - void *user_data) -{ - struct device *device = user_data; - const char *ssid, *wpa2_psk; - - if (device->state == DEVICE_STATE_AP) + 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); - if (!l_dbus_message_get_arguments(message, "ss", &ssid, &wpa2_psk)) - return dbus_error_invalid_args(message); - - netdev_set_iftype(device->netdev, NETDEV_IFTYPE_AP); - - if (ap_start(device, ssid, wpa2_psk, device_ap_event) < 0) { - netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION); - - return dbus_error_failed(message); - } - - device_enter_state(device, DEVICE_STATE_AP); - - device->start_ap_pending = l_dbus_message_ref(message); + periodic_scan_stop(device); /* Drop all state we can related to client mode */ @@ -2129,29 +2075,26 @@ static struct l_dbus_message *device_start_ap(struct l_dbus *dbus, l_queue_destroy(device->networks_sorted, NULL); device->networks_sorted = l_queue_new(); + 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_stop_ap(struct l_dbus *dbus, - struct l_dbus_message *message, - void *user_data) +static struct l_dbus_message *device_set_mode_sta(struct device *device, + struct l_dbus_message *message) { - struct device *device = user_data; - - if (device->state != DEVICE_STATE_AP) + if (device->mode != DEVICE_MODE_AP) return dbus_error_not_found(message); - if (device->stop_ap_pending) - return dbus_error_busy(message); + netdev_set_iftype(device->netdev, NETDEV_IFTYPE_STATION); - if (ap_stop(device) < 0) - return dbus_error_failed(message); + device->mode = DEVICE_MODE_STATION; - if (device->start_ap_pending) - dbus_pending_reply(&device->start_ap_pending, - dbus_error_aborted(device->start_ap_pending)); - - device->stop_ap_pending = l_dbus_message_ref(message); + ap_remove_interface(device); return NULL; } @@ -2339,6 +2282,13 @@ static bool device_property_get_state(struct l_dbus *dbus, struct device *device = user_data; const char *statestr = "unknown"; + /* special case for AP mode */ + if (device->mode == DEVICE_MODE_AP) { + l_dbus_message_builder_append_basic(builder, 's', + "accesspoint"); + return true; + } + switch (device->state) { case DEVICE_STATE_CONNECTED: statestr = "connected"; @@ -2357,9 +2307,6 @@ static bool device_property_get_state(struct l_dbus *dbus, case DEVICE_STATE_ROAMING: statestr = "roaming"; break; - case DEVICE_STATE_AP: - statestr = "accesspoint"; - break; } l_dbus_message_builder_append_basic(builder, 's', statestr); @@ -2380,6 +2327,58 @@ static bool device_property_get_adapter(struct l_dbus *dbus, return true; } +static bool device_property_get_mode(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + struct device *device = user_data; + const char *modestr = "unknown"; + + switch (device->mode) { + case DEVICE_MODE_STATION: + modestr = "station"; + break; + case DEVICE_MODE_AP: + modestr = "ap"; + break; + } + + l_dbus_message_builder_append_basic(builder, 's', modestr); + + return true; +} + +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, + l_dbus_property_complete_cb_t complete, + void *user_data) +{ + struct device *device = user_data; + struct l_dbus_message *reply; + const char* mode; + + 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 { + return dbus_error_invalid_args(message); + } + + complete(dbus, message, NULL); + + return NULL; +} + static void setup_device_interface(struct l_dbus_interface *interface) { l_dbus_interface_method(interface, "Scan", 0, @@ -2395,11 +2394,6 @@ static void setup_device_interface(struct l_dbus_interface *interface) l_dbus_interface_method(interface, "UnregisterSignalLevelAgent", 0, device_signal_agent_unregister, "", "o", "path"); - l_dbus_interface_method(interface, "StartAccessPoint", 0, - device_start_ap, "", "ss", "ssid", "wpa2_psk"); - l_dbus_interface_method(interface, "StopAccessPoint", 0, - device_stop_ap, "", ""); - l_dbus_interface_property(interface, "Name", 0, "s", device_property_get_name, NULL); l_dbus_interface_property(interface, "Address", 0, "s", @@ -2419,6 +2413,9 @@ static void setup_device_interface(struct l_dbus_interface *interface) device_property_get_state, NULL); l_dbus_interface_property(interface, "Adapter", 0, "o", device_property_get_adapter, NULL); + l_dbus_interface_property(interface, "Mode", 0, "s", + device_property_get_mode, + device_property_set_mode); } static void device_netdev_notify(struct netdev *netdev, diff --git a/src/device.h b/src/device.h index 4522581f..e13d082b 100644 --- a/src/device.h +++ b/src/device.h @@ -40,8 +40,12 @@ enum device_state { DEVICE_STATE_CONNECTING, /* Connecting */ DEVICE_STATE_CONNECTED, DEVICE_STATE_DISCONNECTING, - DEVICE_STATE_ROAMING, - DEVICE_STATE_AP, + DEVICE_STATE_ROAMING +}; + +enum device_mode { + DEVICE_MODE_STATION, + DEVICE_MODE_AP }; typedef void (*device_watch_func_t)(struct device *device, @@ -62,6 +66,7 @@ struct netdev *device_get_netdev(struct device *device); uint32_t device_get_ifindex(struct device *device); const uint8_t *device_get_address(struct device *device); enum device_state device_get_state(struct device *device); +enum device_mode device_get_mode(struct device *device); uint32_t device_add_state_watch(struct device *device, device_state_watch_func_t func, diff --git a/src/wsc.c b/src/wsc.c index 1791caa9..232a18dd 100644 --- a/src/wsc.c +++ b/src/wsc.c @@ -476,6 +476,8 @@ static void wsc_check_can_connect(struct wsc *wsc, struct scan_bss *target) { l_debug("%p", wsc); + if (device_get_mode(wsc->device) != DEVICE_MODE_STATION) + goto error; /* * For now we assign the target pointer directly, since we should not * be triggering any more scans while disconnecting / connecting @@ -501,7 +503,6 @@ static void wsc_check_can_connect(struct wsc *wsc, struct scan_bss *target) case DEVICE_STATE_AUTOCONNECT: case DEVICE_STATE_OFF: case DEVICE_STATE_ROAMING: - case DEVICE_STATE_AP: l_warn("wsc_check_can_connect: invalid device state"); break; }