diff options
Diffstat (limited to 'plugins/wifi.c')
-rw-r--r-- | plugins/wifi.c | 494 |
1 files changed, 399 insertions, 95 deletions
diff --git a/plugins/wifi.c b/plugins/wifi.c index 42dd4074..34c16dfd 100644 --- a/plugins/wifi.c +++ b/plugins/wifi.c @@ -71,13 +71,27 @@ #define P2P_LISTEN_PERIOD 500 #define P2P_LISTEN_INTERVAL 2000 +#define ASSOC_STATUS_NO_CLIENT 17 +#define LOAD_SHAPING_MAX_RETRIES 3 + static struct connman_technology *wifi_technology = NULL; static struct connman_technology *p2p_technology = NULL; +enum wifi_ap_capability{ + WIFI_AP_UNKNOWN = 0, + WIFI_AP_SUPPORTED = 1, + WIFI_AP_NOT_SUPPORTED = 2, +}; + struct hidden_params { char ssid[32]; unsigned int ssid_len; char *identity; + char *anonymous_identity; + char *subject_match; + char *altsubject_match; + char *domain_suffix_match; + char *domain_match; char *passphrase; char *security; GSupplicantScanParams *scan_params; @@ -95,6 +109,13 @@ struct autoscan_params { unsigned int timeout; }; +struct wifi_tethering_info { + struct wifi_data *wifi; + struct connman_technology *technology; + char *ifname; + GSupplicantSSID *ssid; +}; + struct wifi_data { char *identifier; struct connman_device *device; @@ -106,6 +127,7 @@ struct wifi_data { bool connected; bool disconnecting; bool tethering; + enum wifi_ap_capability ap_supported; bool bridged; bool interface_ready; const char *bridge; @@ -113,8 +135,10 @@ struct wifi_data { unsigned flags; unsigned int watch; int retries; + int load_shaping_retries; struct hidden_params *hidden; bool postpone_hidden; + struct wifi_tethering_info *tethering_param; /** * autoscan "emulation". */ @@ -124,10 +148,12 @@ struct wifi_data { unsigned int p2p_find_timeout; unsigned int p2p_connection_timeout; struct connman_peer *pending_peer; - GSupplicantPeer *peer; + GSList *peers; bool p2p_connecting; bool p2p_device; int servicing; + int disconnect_code; + int assoc_code; }; static GList *iface_list = NULL; @@ -137,6 +163,9 @@ static GList *p2p_iface_list = NULL; bool wfd_service_registered = false; static void start_autoscan(struct connman_device *device); +static int tech_set_tethering(struct connman_technology *technology, + const char *identifier, const char *passphrase, + const char *bridge, bool enabled); static int p2p_tech_probe(struct connman_technology *technology) { @@ -226,8 +255,6 @@ static void peer_cancel_timeout(struct wifi_data *wifi) connman_peer_unref(wifi->pending_peer); wifi->pending_peer = NULL; } - - wifi->peer = NULL; } static gboolean peer_connect_timeout(gpointer data) @@ -238,8 +265,11 @@ static gboolean peer_connect_timeout(gpointer data) if (wifi->p2p_connecting) { enum connman_peer_state state = CONNMAN_PEER_STATE_FAILURE; + GSupplicantPeer *gs_peer = + g_supplicant_interface_peer_lookup(wifi->interface, + connman_peer_get_identifier(wifi->pending_peer)); - if (g_supplicant_peer_has_requested_connection(wifi->peer)) + if (g_supplicant_peer_has_requested_connection(gs_peer)) state = CONNMAN_PEER_STATE_IDLE; connman_peer_set_state(wifi->pending_peer, state); @@ -290,14 +320,12 @@ static int peer_connect(struct connman_peer *peer, return -ENODEV; wifi = connman_device_get_data(device); - if (!wifi) + if (!wifi || !wifi->interface) return -ENODEV; if (wifi->p2p_connecting) return -EBUSY; - wifi->peer = NULL; - gs_peer = g_supplicant_interface_peer_lookup(wifi->interface, connman_peer_get_identifier(peer)); if (!gs_peer) @@ -336,10 +364,12 @@ static int peer_connect(struct connman_peer *peer, peer_connect_callback, wifi); if (ret == -EINPROGRESS) { wifi->pending_peer = connman_peer_ref(peer); - wifi->peer = gs_peer; wifi->p2p_connecting = true; - } else if (ret < 0) + } else if (ret < 0) { + g_free(peer_params->path); + g_free(peer_params->wps_pin); g_free(peer_params); + } return ret; } @@ -372,8 +402,10 @@ static int peer_disconnect(struct connman_peer *peer) &peer_params); g_free(peer_params.path); - if (ret == -EINPROGRESS) + if (ret == -EINPROGRESS) { peer_cancel_timeout(wifi); + wifi->p2p_device = false; + } return ret; } @@ -737,6 +769,8 @@ static int wifi_probe(struct connman_device *device) return -ENOMEM; wifi->state = G_SUPPLICANT_STATE_INACTIVE; + wifi->ap_supported = WIFI_AP_UNKNOWN; + wifi->tethering_param = NULL; connman_device_set_data(device, wifi); wifi->device = connman_device_ref(device); @@ -770,6 +804,21 @@ static void remove_networks(struct connman_device *device, wifi->networks = NULL; } +static void remove_peers(struct wifi_data *wifi) +{ + GSList *list; + + for (list = wifi->peers; list; list = list->next) { + struct connman_peer *peer = list->data; + + connman_peer_unregister(peer); + connman_peer_unref(peer); + } + + g_slist_free(wifi->peers); + wifi->peers = NULL; +} + static void reset_autoscan(struct connman_device *device) { struct wifi_data *wifi = connman_device_get_data(device); @@ -853,6 +902,7 @@ static void wifi_remove(struct connman_device *device) g_source_remove(wifi->p2p_connection_timeout); remove_networks(device, wifi); + remove_peers(wifi); connman_device_set_powered(device, false); connman_device_set_data(device, NULL); @@ -1500,6 +1550,7 @@ static int wifi_disable(struct connman_device *device) } remove_networks(device, wifi); + remove_peers(wifi); ret = g_supplicant_interface_remove(wifi->interface, NULL, NULL); if (ret < 0) @@ -1654,12 +1705,14 @@ static gboolean p2p_find_stop(gpointer data) DBG(""); - wifi->p2p_find_timeout = 0; + if (wifi) { + wifi->p2p_find_timeout = 0; + + g_supplicant_interface_p2p_stop_find(wifi->interface); + } connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false); - g_supplicant_interface_p2p_stop_find(wifi->interface); - connman_device_unref(device); reset_autoscan(device); @@ -1674,6 +1727,9 @@ static void p2p_find_callback(int result, GSupplicantInterface *interface, DBG("result %d wifi %p", result, wifi); + if (!wifi) + goto error; + if (wifi->p2p_find_timeout) { g_source_remove(wifi->p2p_find_timeout); wifi->p2p_find_timeout = 0; @@ -1746,16 +1802,16 @@ static int wifi_scan(enum connman_service_type type, return -ENODEV; if (wifi->p2p_device) - return 0; + return -EBUSY; + + if (wifi->tethering) + return -EBUSY; if (type == CONNMAN_SERVICE_TYPE_P2P) return p2p_find(device); DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, ssid); - if (wifi->tethering) - return 0; - scanning = connman_device_get_scanning(device); if (!ssid || ssid_len == 0 || ssid_len > 32) { @@ -2004,8 +2060,18 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network) ssid->identity = connman_network_get_string(network, "WiFi.AgentIdentity"); + ssid->anonymous_identity = connman_network_get_string(network, + "WiFi.AnonymousIdentity"); ssid->ca_cert_path = connman_network_get_string(network, "WiFi.CACertFile"); + ssid->subject_match = connman_network_get_string(network, + "WiFi.SubjectMatch"); + ssid->altsubject_match = connman_network_get_string(network, + "WiFi.AltSubjectMatch"); + ssid->domain_suffix_match = connman_network_get_string(network, + "WiFi.DomainSuffixMatch"); + ssid->domain_match = connman_network_get_string(network, + "WiFi.DomainMatch"); ssid->client_cert_path = connman_network_get_string(network, "WiFi.ClientCertFile"); ssid->private_key_path = connman_network_get_string(network, @@ -2073,20 +2139,12 @@ static void disconnect_callback(int result, GSupplicantInterface *interface, } if (wifi->network) { - /* - * if result < 0 supplican return an error because - * the network is not current. - * we wont receive G_SUPPLICANT_STATE_DISCONNECTED since it - * failed, call connman_network_set_connected to report - * disconnect is completed. - */ - if (result < 0) - connman_network_set_connected(wifi->network, false); + connman_network_set_connected(wifi->network, false); + wifi->network = NULL; } - wifi->network = NULL; - wifi->disconnecting = false; + wifi->connected = false; if (wifi->pending_network) { network_connect(wifi->pending_network); @@ -2145,6 +2203,7 @@ static void interface_added(GSupplicantInterface *interface) if (!wifi) return; + wifi->interface = interface; g_supplicant_interface_set_data(interface, wifi); p2p_iface_list = g_list_append(p2p_iface_list, wifi); wifi->p2p_device = true; @@ -2253,6 +2312,19 @@ static bool handle_wps_completion(GSupplicantInterface *interface, return true; } +static bool handle_assoc_status_code(GSupplicantInterface *interface, + struct wifi_data *wifi) +{ + if (wifi->state == G_SUPPLICANT_STATE_ASSOCIATING && + wifi->assoc_code == ASSOC_STATUS_NO_CLIENT && + wifi->load_shaping_retries < LOAD_SHAPING_MAX_RETRIES) { + wifi->load_shaping_retries ++; + return TRUE; + } + wifi->load_shaping_retries = 0; + return FALSE; +} + static bool handle_4way_handshake_failure(GSupplicantInterface *interface, struct connman_network *network, struct wifi_data *wifi) @@ -2262,6 +2334,9 @@ static bool handle_4way_handshake_failure(GSupplicantInterface *interface, if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE) return false; + if (wifi->connected) + return false; + service = connman_service_lookup_from_network(network); if (!service) return false; @@ -2286,6 +2361,7 @@ static void interface_state(GSupplicantInterface *interface) struct wifi_data *wifi; GSupplicantState state = g_supplicant_interface_get_state(interface); bool wps; + bool old_connected; wifi = g_supplicant_interface_get_data(interface); @@ -2294,6 +2370,14 @@ static void interface_state(GSupplicantInterface *interface) if (!wifi) return; + if (state == G_SUPPLICANT_STATE_COMPLETED) { + if (wifi->tethering_param) { + g_free(wifi->tethering_param->ssid); + g_free(wifi->tethering_param); + wifi->tethering_param = NULL; + } + } + device = wifi->device; if (!device) return; @@ -2310,6 +2394,9 @@ static void interface_state(GSupplicantInterface *interface) switch (state) { case G_SUPPLICANT_STATE_SCANNING: + if (wifi->connected) + connman_network_set_connected(network, false); + break; case G_SUPPLICANT_STATE_AUTHENTICATING: @@ -2329,6 +2416,10 @@ static void interface_state(GSupplicantInterface *interface) break; connman_network_set_connected(network, true); + + wifi->disconnect_code = 0; + wifi->assoc_code = 0; + wifi->load_shaping_retries = 0; break; case G_SUPPLICANT_STATE_DISCONNECTED: @@ -2346,6 +2437,9 @@ static void interface_state(GSupplicantInterface *interface) if (is_idle(wifi)) break; + if (handle_assoc_status_code(interface, wifi)) + break; + /* If previous state was 4way-handshake, then * it's either: psk was incorrect and thus we retry * or if we reach the maximum retries we declare the @@ -2354,12 +2448,19 @@ static void interface_state(GSupplicantInterface *interface) network, wifi)) break; - /* We disable the selected network, if not then - * wpa_supplicant will loop retrying */ - if (g_supplicant_interface_enable_selected_network(interface, - FALSE) != 0) - DBG("Could not disables selected network"); + /* See table 8-36 Reason codes in IEEE Std 802.11 */ + switch (wifi->disconnect_code) { + case 1: /* Unspecified reason */ + /* Let's assume it's because we got blocked */ + case 6: /* Class 2 frame received from nonauthenticated STA */ + connman_network_set_error(network, + CONNMAN_NETWORK_ERROR_BLOCKED); + break; + + default: + break; + } connman_network_set_connected(network, false); connman_network_set_associating(network, false); wifi->disconnecting = false; @@ -2382,6 +2483,7 @@ static void interface_state(GSupplicantInterface *interface) break; } + old_connected = wifi->connected; wifi->state = state; /* Saving wpa_s state policy: @@ -2401,8 +2503,12 @@ static void interface_state(GSupplicantInterface *interface) if (wifi->connected) connman_warn("Probably roaming right now!" " Staying connected..."); - else - wifi->connected = false; + break; + case G_SUPPLICANT_STATE_SCANNING: + wifi->connected = false; + + if (old_connected) + start_autoscan(device); break; case G_SUPPLICANT_STATE_COMPLETED: wifi->connected = true; @@ -2443,24 +2549,24 @@ static void interface_removed(GSupplicantInterface *interface) static void set_device_type(const char *type, char dev_type[17]) { const char *oui = "0050F204"; - const char *category = "0100"; + const char *category = "0001"; const char *sub_category = "0000"; if (!g_strcmp0(type, "handset")) { - category = "0A00"; - sub_category = "0500"; + category = "000A"; + sub_category = "0005"; } else if (!g_strcmp0(type, "vm") || !g_strcmp0(type, "container")) - sub_category = "0100"; + sub_category = "0001"; else if (!g_strcmp0(type, "server")) - sub_category = "0200"; + sub_category = "0002"; else if (!g_strcmp0(type, "laptop")) - sub_category = "0500"; + sub_category = "0005"; else if (!g_strcmp0(type, "desktop")) - sub_category = "0600"; + sub_category = "0006"; else if (!g_strcmp0(type, "tablet")) - sub_category = "0900"; + sub_category = "0009"; else if (!g_strcmp0(type, "watch")) - category = "FF00"; + category = "00FF"; snprintf(dev_type, 17, "%s%s%s", category, oui, sub_category); } @@ -2472,6 +2578,9 @@ static void p2p_support(GSupplicantInterface *interface) DBG(""); + if (!interface) + return; + if (!g_supplicant_interface_has_p2p(interface)) return; @@ -2500,6 +2609,36 @@ static void scan_finished(GSupplicantInterface *interface) DBG(""); } +static void ap_create_fail(GSupplicantInterface *interface) +{ + struct wifi_data *wifi = g_supplicant_interface_get_data(interface); + int ret; + + if ((wifi->tethering) && (wifi->tethering_param)) { + DBG("%s create AP fail \n", + g_supplicant_interface_get_ifname(wifi->interface)); + + connman_inet_remove_from_bridge(wifi->index, wifi->bridge); + wifi->ap_supported = WIFI_AP_NOT_SUPPORTED; + wifi->tethering = false; + + ret = tech_set_tethering(wifi->tethering_param->technology, + wifi->tethering_param->ssid->ssid, + wifi->tethering_param->ssid->passphrase, + wifi->bridge, true); + + if ((ret == -EOPNOTSUPP) && (wifi_technology)) { + connman_technology_tethering_notify(wifi_technology,false); + } + + g_free(wifi->tethering_param->ssid); + g_free(wifi->tethering_param); + wifi->tethering_param = NULL; + } + + return; +} + static unsigned char calculate_strength(GSupplicantNetwork *supplicant_network) { unsigned char strength; @@ -2663,6 +2802,57 @@ static void network_changed(GSupplicantNetwork *network, const char *property) } } +static void network_associated(GSupplicantNetwork *network) +{ + GSupplicantInterface *interface; + struct wifi_data *wifi; + struct connman_network *connman_network; + const char *identifier; + + DBG(""); + + interface = g_supplicant_network_get_interface(network); + if (!interface) + return; + + wifi = g_supplicant_interface_get_data(interface); + if (!wifi) + return; + + identifier = g_supplicant_network_get_identifier(network); + + connman_network = connman_device_get_network(wifi->device, identifier); + if (!connman_network) + return; + + if (wifi->network) { + if (wifi->network == connman_network) + return; + + /* + * This should never happen, we got associated with + * a network different than the one we were expecting. + */ + DBG("Associated to %p while expecting %p", + connman_network, wifi->network); + + connman_network_set_associating(wifi->network, false); + } + + DBG("Reconnecting to previous network %p from wpa_s", connman_network); + + wifi->network = connman_network_ref(connman_network); + wifi->retries = 0; + + /* + * Interface state changes callback (interface_state) is always + * called before network_associated callback thus we need to call + * interface_state again in order to process the new state now that + * we have the network properly set. + */ + interface_state(interface); +} + static void apply_peer_services(GSupplicantPeer *peer, struct connman_peer *connman_peer) { @@ -2705,6 +2895,8 @@ static void peer_found(GSupplicantPeer *peer) ret = connman_peer_register(connman_peer); if (ret < 0 && ret != -EALREADY) connman_peer_unref(connman_peer); + else + wifi->peers = g_slist_prepend(wifi->peers, connman_peer); } static void peer_lost(GSupplicantPeer *peer) @@ -2730,6 +2922,8 @@ static void peer_lost(GSupplicantPeer *peer) connman_peer_unregister(connman_peer); connman_peer_unref(connman_peer); } + + wifi->peers = g_slist_remove(wifi->peers, connman_peer); } static void peer_changed(GSupplicantPeer *peer, GSupplicantPeerState state) @@ -2744,6 +2938,9 @@ static void peer_changed(GSupplicantPeer *peer, GSupplicantPeerState state) DBG("ident: %s", identifier); + if (!wifi) + return; + connman_peer = connman_peer_get(wifi->device, identifier); if (!connman_peer) return; @@ -2806,6 +3003,14 @@ static void peer_changed(GSupplicantPeer *peer, GSupplicantPeerState state) connman_peer_set_as_master(connman_peer, !g_supplicant_peer_is_client(peer)); connman_peer_set_sub_device(connman_peer, g_wifi->device); + + /* + * If wpa_supplicant didn't create a dedicated p2p-group + * interface then mark this interface as p2p_device to avoid + * scan and auto-scan are launched on it while P2P is connected. + */ + if (!g_list_find(p2p_iface_list, g_wifi)) + wifi->p2p_device = true; } connman_peer_set_state(connman_peer, p_state); @@ -2835,6 +3040,25 @@ static void debug(const char *str) connman_debug("%s", str); } +static void disconnect_reasoncode(GSupplicantInterface *interface, + int reasoncode) +{ + struct wifi_data *wifi = g_supplicant_interface_get_data(interface); + + if (wifi != NULL) { + wifi->disconnect_code = reasoncode; + } +} + +static void assoc_status_code(GSupplicantInterface *interface, int status_code) +{ + struct wifi_data *wifi = g_supplicant_interface_get_data(interface); + + if (wifi != NULL) { + wifi->assoc_code = status_code; + } +} + static const GSupplicantCallbacks callbacks = { .system_ready = system_ready, .system_killed = system_killed, @@ -2844,14 +3068,18 @@ static const GSupplicantCallbacks callbacks = { .p2p_support = p2p_support, .scan_started = scan_started, .scan_finished = scan_finished, + .ap_create_fail = ap_create_fail, .network_added = network_added, .network_removed = network_removed, .network_changed = network_changed, + .network_associated = network_associated, .peer_found = peer_found, .peer_lost = peer_lost, .peer_changed = peer_changed, .peer_request = peer_request, .debug = debug, + .disconnect_reasoncode = disconnect_reasoncode, + .assoc_status_code = assoc_status_code, }; @@ -2867,13 +3095,6 @@ static void tech_remove(struct connman_technology *technology) wifi_technology = NULL; } -struct wifi_tethering_info { - struct wifi_data *wifi; - struct connman_technology *technology; - char *ifname; - GSupplicantSSID *ssid; -}; - static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase) { GSupplicantSSID *ap; @@ -2910,10 +3131,16 @@ static void ap_start_callback(int result, GSupplicantInterface *interface, DBG("result %d index %d bridge %s", result, info->wifi->index, info->wifi->bridge); - if (result < 0) { + if ((result < 0) || (info->wifi->ap_supported != WIFI_AP_SUPPORTED)) { connman_inet_remove_from_bridge(info->wifi->index, info->wifi->bridge); - connman_technology_tethering_notify(info->technology, false); + + if (info->wifi->ap_supported == WIFI_AP_SUPPORTED) { + connman_technology_tethering_notify(info->technology, false); + g_free(info->wifi->tethering_param->ssid); + g_free(info->wifi->tethering_param); + info->wifi->tethering_param = NULL; + } } g_free(info->ifname); @@ -2929,10 +3156,17 @@ static void ap_create_callback(int result, DBG("result %d ifname %s", result, g_supplicant_interface_get_ifname(interface)); - if (result < 0) { + if ((result < 0) || (info->wifi->ap_supported != WIFI_AP_SUPPORTED)) { connman_inet_remove_from_bridge(info->wifi->index, info->wifi->bridge); - connman_technology_tethering_notify(info->technology, false); + + if (info->wifi->ap_supported == WIFI_AP_SUPPORTED) { + connman_technology_tethering_notify(info->technology, false); + g_free(info->wifi->tethering_param->ssid); + g_free(info->wifi->tethering_param); + info->wifi->tethering_param = NULL; + + } g_free(info->ifname); g_free(info->ssid); @@ -2959,27 +3193,32 @@ static void sta_remove_callback(int result, DBG("ifname %s result %d ", info->ifname, result); - if (result < 0) { - info->wifi->tethering = true; + if ((result < 0) || (info->wifi->ap_supported != WIFI_AP_SUPPORTED)) { + info->wifi->tethering = false; + connman_technology_tethering_notify(info->technology, false); g_free(info->ifname); g_free(info->ssid); g_free(info); + + if (info->wifi->ap_supported == WIFI_AP_SUPPORTED) { + g_free(info->wifi->tethering_param->ssid); + g_free(info->wifi->tethering_param); + info->wifi->tethering_param = NULL; + } return; } info->wifi->interface = NULL; - connman_technology_tethering_notify(info->technology, true); - g_supplicant_interface_create(info->ifname, driver, info->wifi->bridge, ap_create_callback, info); } -static int tech_set_tethering(struct connman_technology *technology, - const char *identifier, const char *passphrase, - const char *bridge, bool enabled) +static int enable_wifi_tethering(struct connman_technology *technology, + const char *bridge, const char *identifier, + const char *passphrase, bool available) { GList *list; GSupplicantInterface *interface; @@ -2987,75 +3226,140 @@ static int tech_set_tethering(struct connman_technology *technology, struct wifi_tethering_info *info; const char *ifname; unsigned int mode; - int err; - - DBG(""); - - if (!enabled) { - for (list = iface_list; list; list = list->next) { - wifi = list->data; - - if (wifi->tethering) { - wifi->tethering = false; - - connman_inet_remove_from_bridge(wifi->index, - bridge); - wifi->bridged = false; - } - } - - connman_technology_tethering_notify(technology, false); - - return 0; - } + int err, berr = 0; for (list = iface_list; list; list = list->next) { wifi = list->data; + DBG("wifi %p network %p pending_network %p", wifi, + wifi->network, wifi->pending_network); + interface = wifi->interface; if (!interface) continue; ifname = g_supplicant_interface_get_ifname(wifi->interface); + if (!ifname) + continue; + + if (wifi->ap_supported == WIFI_AP_NOT_SUPPORTED) { + DBG("%s does not support AP mode (detected)", ifname); + continue; + } mode = g_supplicant_interface_get_mode(interface); if ((mode & G_SUPPLICANT_CAPABILITY_MODE_AP) == 0) { - DBG("%s does not support AP mode", ifname); + wifi->ap_supported = WIFI_AP_NOT_SUPPORTED; + DBG("%s does not support AP mode (capability)", ifname); continue; } + if (wifi->network && available) + continue; + info = g_try_malloc0(sizeof(struct wifi_tethering_info)); if (!info) return -ENOMEM; + wifi->tethering_param = g_try_malloc0(sizeof(struct wifi_tethering_info)); + if (!wifi->tethering_param) { + g_free(info); + return -ENOMEM; + } + info->wifi = wifi; info->technology = technology; info->wifi->bridge = bridge; info->ssid = ssid_ap_init(identifier, passphrase); - if (!info->ssid) { - g_free(info); - continue; - } + if (!info->ssid) + goto failed; + info->ifname = g_strdup(ifname); - if (!info->ifname) { - g_free(info->ssid); - g_free(info); - continue; - } + + wifi->tethering_param->technology = technology; + wifi->tethering_param->ssid = ssid_ap_init(identifier, passphrase); + if (!wifi->tethering_param->ssid) + goto failed; info->wifi->tethering = true; + info->wifi->ap_supported = WIFI_AP_SUPPORTED; + + berr = connman_technology_tethering_notify(technology, true); + if (berr < 0) + goto failed; err = g_supplicant_interface_remove(interface, sta_remove_callback, info); - if (err == 0) - return err; + if (err >= 0) { + DBG("tethering wifi %p ifname %s", wifi, ifname); + return 0; + } + + failed: + g_free(info->ifname); + g_free(info->ssid); + g_free(info); + g_free(wifi->tethering_param); + wifi->tethering_param = NULL; + + /* + * Remove bridge if it was correctly created but remove + * operation failed. Instead, if bridge creation failed then + * break out and do not try again on another interface, + * bridge set-up does not depend on it. + */ + if (berr == 0) + connman_technology_tethering_notify(technology, false); + else + break; } return -EOPNOTSUPP; } +static int tech_set_tethering(struct connman_technology *technology, + const char *identifier, const char *passphrase, + const char *bridge, bool enabled) +{ + GList *list; + struct wifi_data *wifi; + int err; + + DBG(""); + + if (!enabled) { + for (list = iface_list; list; list = list->next) { + wifi = list->data; + + if (wifi->tethering) { + wifi->tethering = false; + + connman_inet_remove_from_bridge(wifi->index, + bridge); + wifi->bridged = false; + } + } + + connman_technology_tethering_notify(technology, false); + + return 0; + } + + DBG("trying tethering for available devices"); + err = enable_wifi_tethering(technology, bridge, identifier, passphrase, + true); + + if (err < 0) { + DBG("trying tethering for any device"); + err = enable_wifi_tethering(technology, bridge, identifier, + passphrase, false); + } + + return err; +} + static void regdom_callback(int result, const char *alpha2, void *user_data) { DBG(""); |