summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorZhang zhengguang <zhengguang.zhang@intel.com>2014-10-29 11:03:47 +0800
committerZhang zhengguang <zhengguang.zhang@intel.com>2014-10-29 11:03:47 +0800
commitbcae74da8fa2958b3fec9153fc33e41f0e0317bf (patch)
tree06a00f6457307467fee4f6580dce4a1a857751c1 /plugins
parent1b9d0a62f59bb48c8deb2f0b98d9acdffdd9abe7 (diff)
downloadconnman-bcae74da8fa2958b3fec9153fc33e41f0e0317bf.tar.gz
connman-bcae74da8fa2958b3fec9153fc33e41f0e0317bf.tar.bz2
connman-bcae74da8fa2958b3fec9153fc33e41f0e0317bf.zip
Imported Upstream version 1.26upstream/1.26
Diffstat (limited to 'plugins')
-rw-r--r--plugins/dundee.c2
-rw-r--r--plugins/ofono.c2
-rw-r--r--plugins/session_policy_local.c2
-rw-r--r--plugins/vpn.c2
-rw-r--r--plugins/wifi.c784
5 files changed, 767 insertions, 25 deletions
diff --git a/plugins/dundee.c b/plugins/dundee.c
index b72c69e5..b5420acf 100644
--- a/plugins/dundee.c
+++ b/plugins/dundee.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/plugins/ofono.c b/plugins/ofono.c
index fecef9cf..7af551ba 100644
--- a/plugins/ofono.c
+++ b/plugins/ofono.c
@@ -4,7 +4,7 @@
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * Copyright (C) 2011 BWM Car IT GmbH. All rights reserved.
+ * Copyright (C) 2011-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/plugins/session_policy_local.c b/plugins/session_policy_local.c
index 4e8b5f21..b2369bdc 100644
--- a/plugins/session_policy_local.c
+++ b/plugins/session_policy_local.c
@@ -2,7 +2,7 @@
*
* Connection Manager
*
- * Copyright (C) 2012 BMW Car IT GbmH. All rights reserved.
+ * Copyright (C) 2012-2014 BMW Car IT GmbH.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/plugins/vpn.c b/plugins/vpn.c
index 25711d70..e6e4c8e0 100644
--- a/plugins/vpn.c
+++ b/plugins/vpn.c
@@ -1078,7 +1078,7 @@ static struct vpn_route *parse_user_route(const char *user_route)
char *ptr;
long int value = strtol(netmask, &ptr, 10);
if (ptr != netmask && *ptr == '\0' &&
- value <= 32)
+ value && value <= 32)
prefix_len = value;
}
diff --git a/plugins/wifi.c b/plugins/wifi.c
index ef4dd951..5f2ebf1c 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -54,6 +54,8 @@
#include <connman/storage.h>
#include <include/setting.h>
#include <connman/provision.h>
+#include <connman/utsname.h>
+#include <connman/machine.h>
#include <gsupplicant/gsupplicant.h>
@@ -65,6 +67,9 @@
#define AUTOSCAN_DEFAULT "exponential:3:300"
#define P2P_FIND_TIMEOUT 30
+#define P2P_CONNECTION_TIMEOUT 100
+#define P2P_LISTEN_PERIOD 500
+#define P2P_LISTEN_INTERVAL 2000
static struct connman_technology *wifi_technology = NULL;
static struct connman_technology *p2p_technology = NULL;
@@ -117,10 +122,20 @@ struct wifi_data {
GSupplicantScanParams *scan_params;
unsigned int p2p_find_timeout;
+ unsigned int p2p_connection_timeout;
+ struct connman_peer *pending_peer;
+ GSupplicantPeer *peer;
+ bool p2p_connecting;
+ bool p2p_device;
+ int servicing;
};
static GList *iface_list = NULL;
+static GList *pending_wifi_device = NULL;
+static GList *p2p_iface_list = NULL;
+bool wfd_service_registered = false;
+
static void start_autoscan(struct connman_device *device);
static int p2p_tech_probe(struct connman_technology *technology)
@@ -142,6 +157,527 @@ static struct connman_technology_driver p2p_tech_driver = {
.remove = p2p_tech_remove,
};
+static bool is_p2p_connecting(void)
+{
+ GList *list;
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+
+ if (wifi->p2p_connecting)
+ return true;
+ }
+
+ return false;
+}
+
+static void add_pending_wifi_device(struct wifi_data *wifi)
+{
+ if (g_list_find(pending_wifi_device, wifi))
+ return;
+
+ pending_wifi_device = g_list_append(pending_wifi_device, wifi);
+}
+
+static struct wifi_data *get_pending_wifi_data(const char *ifname)
+{
+ GList *list;
+
+ for (list = pending_wifi_device; list; list = list->next) {
+ struct wifi_data *wifi;
+ const char *dev_name;
+
+ wifi = list->data;
+ if (!wifi || !wifi->device)
+ continue;
+
+ dev_name = connman_device_get_string(wifi->device, "Interface");
+ if (!g_strcmp0(ifname, dev_name)) {
+ pending_wifi_device = g_list_delete_link(
+ pending_wifi_device, list);
+ return wifi;
+ }
+ }
+
+ return NULL;
+}
+
+static void remove_pending_wifi_device(struct wifi_data *wifi)
+{
+ GList *link;
+
+ link = g_list_find(pending_wifi_device, wifi);
+
+ if (!link)
+ return;
+
+ pending_wifi_device = g_list_delete_link(pending_wifi_device, link);
+}
+
+static void peer_cancel_timeout(struct wifi_data *wifi)
+{
+ if (wifi->p2p_connection_timeout > 0)
+ g_source_remove(wifi->p2p_connection_timeout);
+
+ wifi->p2p_connection_timeout = 0;
+ wifi->p2p_connecting = false;
+
+ if (wifi->pending_peer) {
+ connman_peer_unref(wifi->pending_peer);
+ wifi->pending_peer = NULL;
+ }
+
+ wifi->peer = NULL;
+}
+
+static gboolean peer_connect_timeout(gpointer data)
+{
+ struct wifi_data *wifi = data;
+
+ DBG("");
+
+ if (wifi->p2p_connecting) {
+ enum connman_peer_state state = CONNMAN_PEER_STATE_FAILURE;
+
+ if (g_supplicant_peer_has_requested_connection(wifi->peer))
+ state = CONNMAN_PEER_STATE_IDLE;
+
+ connman_peer_set_state(wifi->pending_peer, state);
+ }
+
+ peer_cancel_timeout(wifi);
+
+ return FALSE;
+}
+
+static void peer_connect_callback(int result, GSupplicantInterface *interface,
+ void *user_data)
+{
+ struct wifi_data *wifi = user_data;
+ struct connman_peer *peer = wifi->pending_peer;
+
+ DBG("peer %p - %d", peer, result);
+
+ if (!peer)
+ return;
+
+ if (result < 0) {
+ peer_connect_timeout(wifi);
+ return;
+ }
+
+ connman_peer_set_state(peer, CONNMAN_PEER_STATE_ASSOCIATION);
+
+ wifi->p2p_connection_timeout = g_timeout_add_seconds(
+ P2P_CONNECTION_TIMEOUT,
+ peer_connect_timeout, wifi);
+}
+
+static int peer_connect(struct connman_peer *peer,
+ enum connman_peer_wps_method wps_method,
+ const char *wps_pin)
+{
+ struct connman_device *device = connman_peer_get_device(peer);
+ GSupplicantPeerParams *peer_params;
+ GSupplicantPeer *gs_peer;
+ struct wifi_data *wifi;
+ bool pbc, pin;
+ int ret;
+
+ DBG("peer %p", peer);
+
+ if (!device)
+ return -ENODEV;
+
+ wifi = connman_device_get_data(device);
+ if (!wifi)
+ 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)
+ return -EINVAL;
+
+ pbc = g_supplicant_peer_is_wps_pbc(gs_peer);
+ pin = g_supplicant_peer_is_wps_pin(gs_peer);
+
+ switch (wps_method) {
+ case CONNMAN_PEER_WPS_UNKNOWN:
+ if ((pbc && pin) || pin)
+ return -ENOKEY;
+ break;
+ case CONNMAN_PEER_WPS_PBC:
+ if (!pbc)
+ return -EINVAL;
+ wps_pin = NULL;
+ break;
+ case CONNMAN_PEER_WPS_PIN:
+ if (!pin || !wps_pin)
+ return -EINVAL;
+ break;
+ }
+
+ peer_params = g_try_malloc0(sizeof(GSupplicantPeerParams));
+ if (!peer_params)
+ return -ENOMEM;
+
+ peer_params->path = g_strdup(g_supplicant_peer_get_path(gs_peer));
+ if (wps_pin)
+ peer_params->wps_pin = g_strdup(wps_pin);
+
+ peer_params->master = connman_peer_service_is_master();
+
+ ret = g_supplicant_interface_p2p_connect(wifi->interface, peer_params,
+ 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)
+ g_free(peer_params);
+
+ return ret;
+}
+
+static int peer_disconnect(struct connman_peer *peer)
+{
+ struct connman_device *device = connman_peer_get_device(peer);
+ GSupplicantPeerParams peer_params = {};
+ GSupplicantPeer *gs_peer;
+ struct wifi_data *wifi;
+ int ret;
+
+ DBG("peer %p", peer);
+
+ if (!device)
+ return -ENODEV;
+
+ wifi = connman_device_get_data(device);
+ if (!wifi)
+ return -ENODEV;
+
+ gs_peer = g_supplicant_interface_peer_lookup(wifi->interface,
+ connman_peer_get_identifier(peer));
+ if (!gs_peer)
+ return -EINVAL;
+
+ peer_params.path = g_strdup(g_supplicant_peer_get_path(gs_peer));
+
+ ret = g_supplicant_interface_p2p_disconnect(wifi->interface,
+ &peer_params);
+ g_free(peer_params.path);
+
+ if (ret == -EINPROGRESS)
+ peer_cancel_timeout(wifi);
+
+ return ret;
+}
+
+struct peer_service_registration {
+ peer_service_registration_cb_t callback;
+ void *user_data;
+};
+
+static bool is_service_wfd(const unsigned char *specs, int length)
+{
+ if (length < 9 || specs[0] != 0 || specs[1] != 0 || specs[2] != 6)
+ return false;
+
+ return true;
+}
+
+static void apply_p2p_listen_on_iface(gpointer data, gpointer user_data)
+{
+ struct wifi_data *wifi = data;
+
+ if (!wifi->interface ||
+ !g_supplicant_interface_has_p2p(wifi->interface))
+ return;
+
+ if (!wifi->servicing) {
+ g_supplicant_interface_p2p_listen(wifi->interface,
+ P2P_LISTEN_PERIOD, P2P_LISTEN_INTERVAL);
+ }
+
+ wifi->servicing++;
+}
+
+static void register_wfd_service_cb(int result,
+ GSupplicantInterface *iface, void *user_data)
+{
+ struct peer_service_registration *reg_data = user_data;
+
+ DBG("");
+
+ if (result == 0)
+ g_list_foreach(iface_list, apply_p2p_listen_on_iface, NULL);
+
+ if (reg_data && reg_data->callback) {
+ reg_data->callback(result, reg_data->user_data);
+ g_free(reg_data);
+ }
+}
+
+static GSupplicantP2PServiceParams *fill_in_peer_service_params(
+ const unsigned char *spec,
+ int spec_length, const unsigned char *query,
+ int query_length, int version)
+{
+ GSupplicantP2PServiceParams *params;
+
+ params = g_try_malloc0(sizeof(GSupplicantP2PServiceParams));
+ if (!params)
+ return NULL;
+
+ if (version > 0) {
+ params->version = version;
+ params->service = g_memdup(spec, spec_length);
+ } else if (query_length > 0 && spec_length > 0) {
+ params->query = g_memdup(query, query_length);
+ params->query_length = query_length;
+
+ params->response = g_memdup(spec, spec_length);
+ params->response_length = spec_length;
+ } else {
+ params->wfd_ies = g_memdup(spec, spec_length);
+ params->wfd_ies_length = spec_length;
+ }
+
+ return params;
+}
+
+static void free_peer_service_params(GSupplicantP2PServiceParams *params)
+{
+ if (!params)
+ return;
+
+ g_free(params->service);
+ g_free(params->query);
+ g_free(params->response);
+ g_free(params->wfd_ies);
+
+ g_free(params);
+}
+
+static int peer_register_wfd_service(const unsigned char *specification,
+ int specification_length,
+ peer_service_registration_cb_t callback,
+ void *user_data)
+{
+ struct peer_service_registration *reg_data = NULL;
+ static GSupplicantP2PServiceParams *params;
+ int ret;
+
+ DBG("");
+
+ if (wfd_service_registered)
+ return -EBUSY;
+
+ params = fill_in_peer_service_params(specification,
+ specification_length, NULL, 0, 0);
+ if (!params)
+ return -ENOMEM;
+
+ reg_data = g_try_malloc0(sizeof(*reg_data));
+ if (!reg_data) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ reg_data->callback = callback;
+ reg_data->user_data = user_data;
+
+ ret = g_supplicant_set_widi_ies(params,
+ register_wfd_service_cb, reg_data);
+ if (ret < 0 && ret != -EINPROGRESS)
+ goto error;
+
+ wfd_service_registered = true;
+
+ return ret;
+error:
+ free_peer_service_params(params);
+ g_free(reg_data);
+
+ return ret;
+}
+
+static void register_peer_service_cb(int result,
+ GSupplicantInterface *iface, void *user_data)
+{
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
+ struct peer_service_registration *reg_data = user_data;
+
+ DBG("");
+
+ if (result == 0)
+ apply_p2p_listen_on_iface(wifi, NULL);
+
+ if (reg_data->callback)
+ reg_data->callback(result, reg_data->user_data);
+
+ g_free(reg_data);
+}
+
+static int peer_register_service(const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version,
+ peer_service_registration_cb_t callback,
+ void *user_data)
+{
+ struct peer_service_registration *reg_data;
+ GSupplicantP2PServiceParams *params;
+ bool found = false;
+ int ret, ret_f;
+ GList *list;
+
+ DBG("");
+
+ if (specification && !version && !query &&
+ is_service_wfd(specification, specification_length)) {
+ return peer_register_wfd_service(specification,
+ specification_length, callback, user_data);
+ }
+
+ reg_data = g_try_malloc0(sizeof(*reg_data));
+ if (!reg_data)
+ return -ENOMEM;
+
+ reg_data->callback = callback;
+ reg_data->user_data = user_data;
+
+ ret_f = -EOPNOTSUPP;
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+ GSupplicantInterface *iface = wifi->interface;
+
+ if (!g_supplicant_interface_has_p2p(iface))
+ continue;
+
+ params = fill_in_peer_service_params(specification,
+ specification_length, query,
+ query_length, version);
+ if (!params) {
+ ret = -ENOMEM;
+ continue;
+ }
+
+ if (!found) {
+ ret_f = g_supplicant_interface_p2p_add_service(iface,
+ register_peer_service_cb, params, reg_data);
+ if (ret_f == 0 || ret_f == -EINPROGRESS)
+ found = true;
+ ret = ret_f;
+ } else
+ ret = g_supplicant_interface_p2p_add_service(iface,
+ register_peer_service_cb, params, NULL);
+ if (ret != 0 && ret != -EINPROGRESS)
+ free_peer_service_params(params);
+ }
+
+ if (ret_f != 0 && ret_f != -EINPROGRESS)
+ g_free(reg_data);
+
+ return ret_f;
+}
+
+static int peer_unregister_wfd_service(void)
+{
+ GSupplicantP2PServiceParams *params;
+ GList *list;
+
+ if (!wfd_service_registered)
+ return -EALREADY;
+
+ params = fill_in_peer_service_params(NULL, 0, NULL, 0, 0);
+ if (!params)
+ return -ENOMEM;
+
+ wfd_service_registered = false;
+
+ g_supplicant_set_widi_ies(params, NULL, NULL);
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+
+ if (!g_supplicant_interface_has_p2p(wifi->interface))
+ continue;
+
+ wifi->servicing--;
+ if (!wifi->servicing || wifi->servicing < 0) {
+ g_supplicant_interface_p2p_listen(wifi->interface,
+ 0, 0);
+ wifi->servicing = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int peer_unregister_service(const unsigned char *specification,
+ int specification_length,
+ const unsigned char *query,
+ int query_length, int version)
+{
+ GSupplicantP2PServiceParams *params;
+ bool wfd = false;
+ GList *list;
+ int ret;
+
+ if (specification && !version && !query &&
+ is_service_wfd(specification, specification_length)) {
+ ret = peer_unregister_wfd_service();
+ if (ret != 0 && ret != -EINPROGRESS)
+ return ret;
+ wfd = true;
+ }
+
+ for (list = iface_list; list; list = list->next) {
+ struct wifi_data *wifi = list->data;
+ GSupplicantInterface *iface = wifi->interface;
+
+ if (wfd)
+ goto stop_listening;
+
+ if (!g_supplicant_interface_has_p2p(iface))
+ continue;
+
+ params = fill_in_peer_service_params(specification,
+ specification_length, query,
+ query_length, version);
+ if (!params) {
+ ret = -ENOMEM;
+ continue;
+ }
+
+ ret = g_supplicant_interface_p2p_del_service(iface, params);
+ if (ret != 0 && ret != -EINPROGRESS)
+ free_peer_service_params(params);
+stop_listening:
+ wifi->servicing--;
+ if (!wifi->servicing || wifi->servicing < 0) {
+ g_supplicant_interface_p2p_listen(iface, 0, 0);
+ wifi->servicing = 0;
+ }
+ }
+
+ return 0;
+}
+
+static struct connman_peer_driver peer_driver = {
+ .connect = peer_connect,
+ .disconnect = peer_disconnect,
+ .register_service = peer_register_service,
+ .unregister_service = peer_unregister_service,
+};
+
static void handle_tethering(struct wifi_data *wifi)
{
if (!wifi->tethering)
@@ -210,8 +746,10 @@ static int wifi_probe(struct connman_device *device)
wifi->watch = connman_rtnl_add_newlink_watch(wifi->index,
wifi_newlink, device);
-
- iface_list = g_list_append(iface_list, wifi);
+ if (is_p2p_connecting())
+ add_pending_wifi_device(wifi);
+ else
+ iface_list = g_list_append(iface_list, wifi);
return 0;
}
@@ -280,8 +818,10 @@ static void check_p2p_technology(void)
p2p_exists = true;
}
- if (!p2p_exists)
+ if (!p2p_exists) {
connman_technology_driver_unregister(&p2p_tech_driver);
+ connman_peer_driver_unregister(&peer_driver);
+ }
}
static void wifi_remove(struct connman_device *device)
@@ -295,15 +835,23 @@ static void wifi_remove(struct connman_device *device)
stop_autoscan(device);
- iface_list = g_list_remove(iface_list, wifi);
+ if (wifi->p2p_device)
+ p2p_iface_list = g_list_remove(p2p_iface_list, wifi);
+ else
+ iface_list = g_list_remove(iface_list, wifi);
check_p2p_technology();
+ remove_pending_wifi_device(wifi);
+
if (wifi->p2p_find_timeout) {
g_source_remove(wifi->p2p_find_timeout);
connman_device_unref(wifi->device);
}
+ if (wifi->p2p_connection_timeout)
+ g_source_remove(wifi->p2p_connection_timeout);
+
remove_networks(device, wifi);
connman_device_set_powered(device, false);
@@ -313,6 +861,8 @@ static void wifi_remove(struct connman_device *device)
g_supplicant_interface_set_data(wifi->interface, NULL);
+ g_supplicant_interface_cancel(wifi->interface);
+
if (wifi->scan_params)
g_supplicant_free_scan_params(wifi->scan_params);
@@ -787,6 +1337,9 @@ static void start_autoscan(struct connman_device *device)
if (!wifi)
return;
+ if (wifi->p2p_device)
+ return;
+
autoscan = wifi->autoscan;
if (!autoscan)
return;
@@ -871,6 +1424,9 @@ static void finalize_interface_creation(struct wifi_data *wifi)
if (!connman_setting_get_bool("BackgroundScanning"))
return;
+ if (wifi->p2p_device)
+ return;
+
/* Setting up automatic scanning */
if (g_supplicant_interface_autoscan(interface, AUTOSCAN_DEFAULT,
interface_autoscan_callback, wifi) < 0) {
@@ -915,6 +1471,9 @@ static int wifi_enable(struct connman_device *device)
if (!wifi || index < 0)
return -ENODEV;
+ if (is_p2p_connecting())
+ return -EINPROGRESS;
+
interface = connman_inet_ifname(index);
ret = g_supplicant_interface_create(interface, driver, NULL,
interface_create_callback,
@@ -948,6 +1507,7 @@ static int wifi_disable(struct connman_device *device)
if (wifi->p2p_find_timeout) {
g_source_remove(wifi->p2p_find_timeout);
wifi->p2p_find_timeout = 0;
+ connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false);
connman_device_unref(wifi->device);
}
@@ -1153,7 +1713,7 @@ error:
static int p2p_find(struct connman_device *device)
{
- struct wifi_data *wifi = connman_device_get_data(device);
+ struct wifi_data *wifi;
int ret;
DBG("");
@@ -1161,6 +1721,11 @@ static int p2p_find(struct connman_device *device)
if (!p2p_technology)
return -ENOTSUP;
+ wifi = connman_device_get_data(device);
+
+ if (g_supplicant_interface_is_p2p_finding(wifi->interface))
+ return -EALREADY;
+
reset_autoscan(device);
connman_device_ref(device);
@@ -1199,6 +1764,9 @@ static int wifi_scan(enum connman_service_type type,
if (!wifi)
return -ENODEV;
+ if (wifi->p2p_device)
+ return 0;
+
if (type == CONNMAN_SERVICE_TYPE_P2P)
return p2p_find(device);
@@ -1588,14 +2156,15 @@ static void interface_added(GSupplicantInterface *interface)
struct wifi_data *wifi;
wifi = g_supplicant_interface_get_data(interface);
+ if (!wifi) {
+ wifi = get_pending_wifi_data(ifname);
+ if (!wifi)
+ return;
- /*
- * We can get here with a NULL wifi pointer when
- * the interface added signal is sent before the
- * interface creation callback is called.
- */
- if (!wifi)
- return;
+ g_supplicant_interface_set_data(interface, wifi);
+ p2p_iface_list = g_list_append(p2p_iface_list, wifi);
+ wifi->p2p_device = true;
+ }
DBG("ifname %s driver %s wifi %p tethering %d",
ifname, driver, wifi, wifi->tethering);
@@ -1606,9 +2175,6 @@ static void interface_added(GSupplicantInterface *interface)
}
connman_device_set_powered(wifi->device, true);
-
- if (wifi->tethering)
- return;
}
static bool is_idle(struct wifi_data *wifi)
@@ -1874,6 +2440,9 @@ static void interface_removed(GSupplicantInterface *interface)
wifi = g_supplicant_interface_get_data(interface);
+ if (wifi)
+ wifi->interface = NULL;
+
if (wifi && wifi->tethering)
return;
@@ -1882,21 +2451,59 @@ static void interface_removed(GSupplicantInterface *interface)
return;
}
- wifi->interface = NULL;
connman_device_set_powered(wifi->device, false);
check_p2p_technology();
}
+static void set_device_type(const char *type, char dev_type[17])
+{
+ const char *oui = "0050F204";
+ const char *category = "0100";
+ const char *sub_category = "0000";
+
+ if (!g_strcmp0(type, "handset")) {
+ category = "0A00";
+ sub_category = "0500";
+ } else if (!g_strcmp0(type, "vm") || !g_strcmp0(type, "container"))
+ sub_category = "0100";
+ else if (!g_strcmp0(type, "server"))
+ sub_category = "0200";
+ else if (!g_strcmp0(type, "laptop"))
+ sub_category = "0500";
+ else if (!g_strcmp0(type, "desktop"))
+ sub_category = "0600";
+ else if (!g_strcmp0(type, "tablet"))
+ sub_category = "0900";
+ else if (!g_strcmp0(type, "watch"))
+ category = "FF00";
+
+ snprintf(dev_type, 17, "%s%s%s", category, oui, sub_category);
+}
+
static void p2p_support(GSupplicantInterface *interface)
{
+ char dev_type[17] = {};
+ const char *hostname;
+
DBG("");
if (!g_supplicant_interface_has_p2p(interface))
return;
- if (connman_technology_driver_register(&p2p_tech_driver) == 0)
+ if (connman_technology_driver_register(&p2p_tech_driver) < 0) {
DBG("Could not register P2P technology driver");
+ return;
+ }
+
+ hostname = connman_utsname_get_hostname();
+ if (!hostname)
+ hostname = "ConnMan";
+
+ set_device_type(connman_machine_get_type(), dev_type);
+ g_supplicant_interface_set_p2p_device_config(interface,
+ hostname, dev_type);
+ connman_peer_driver_register(&peer_driver);
}
static void scan_started(GSupplicantInterface *interface)
@@ -2072,38 +2679,171 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
}
}
+static void apply_peer_services(GSupplicantPeer *peer,
+ struct connman_peer *connman_peer)
+{
+ const unsigned char *data;
+ int length;
+
+ DBG("");
+
+ connman_peer_reset_services(connman_peer);
+
+ data = g_supplicant_peer_get_widi_ies(peer, &length);
+ if (data) {
+ connman_peer_add_service(connman_peer,
+ CONNMAN_PEER_SERVICE_WIFI_DISPLAY, data, length);
+ }
+}
+
static void peer_found(GSupplicantPeer *peer)
{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
struct connman_peer *connman_peer;
const char *identifier, *name;
+ int ret;
identifier = g_supplicant_peer_get_identifier(peer);
name = g_supplicant_peer_get_name(peer);
DBG("ident: %s", identifier);
- connman_peer = connman_peer_get(identifier);
+ connman_peer = connman_peer_get(wifi->device, identifier);
if (connman_peer)
return;
connman_peer = connman_peer_create(identifier);
connman_peer_set_name(connman_peer, name);
+ connman_peer_set_device(connman_peer, wifi->device);
+ apply_peer_services(peer, connman_peer);
- connman_peer_register(connman_peer);
+ ret = connman_peer_register(connman_peer);
+ if (ret < 0 && ret != -EALREADY)
+ connman_peer_unref(connman_peer);
}
static void peer_lost(GSupplicantPeer *peer)
{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
struct connman_peer *connman_peer;
const char *identifier;
+ if (!wifi)
+ return;
+
identifier = g_supplicant_peer_get_identifier(peer);
DBG("ident: %s", identifier);
- connman_peer = connman_peer_get(identifier);
- if (connman_peer)
+ connman_peer = connman_peer_get(wifi->device, identifier);
+ if (connman_peer) {
+ if (wifi->p2p_connecting &&
+ wifi->pending_peer == connman_peer) {
+ peer_connect_timeout(wifi);
+ }
connman_peer_unregister(connman_peer);
+ connman_peer_unref(connman_peer);
+ }
+}
+
+static void peer_changed(GSupplicantPeer *peer, GSupplicantPeerState state)
+{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
+ enum connman_peer_state p_state = CONNMAN_PEER_STATE_UNKNOWN;
+ struct connman_peer *connman_peer;
+ const char *identifier;
+
+ identifier = g_supplicant_peer_get_identifier(peer);
+
+ DBG("ident: %s", identifier);
+
+ connman_peer = connman_peer_get(wifi->device, identifier);
+ if (!connman_peer)
+ return;
+
+ switch (state) {
+ case G_SUPPLICANT_PEER_SERVICES_CHANGED:
+ apply_peer_services(peer, connman_peer);
+ connman_peer_services_changed(connman_peer);
+ return;
+ case G_SUPPLICANT_PEER_GROUP_CHANGED:
+ if (!g_supplicant_peer_is_in_a_group(peer))
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ else
+ p_state = CONNMAN_PEER_STATE_CONFIGURATION;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_STARTED:
+ break;
+ case G_SUPPLICANT_PEER_GROUP_FINISHED:
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_JOINED:
+ if (!g_supplicant_peer_is_in_a_group(peer))
+ break;
+ p_state = CONNMAN_PEER_STATE_READY;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_DISCONNECTED:
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ break;
+ case G_SUPPLICANT_PEER_GROUP_FAILED:
+ if (g_supplicant_peer_has_requested_connection(peer))
+ p_state = CONNMAN_PEER_STATE_IDLE;
+ else
+ p_state = CONNMAN_PEER_STATE_FAILURE;
+ break;
+ }
+
+ if (p_state == CONNMAN_PEER_STATE_CONFIGURATION ||
+ p_state == CONNMAN_PEER_STATE_FAILURE) {
+ if (wifi->p2p_connecting
+ && connman_peer == wifi->pending_peer)
+ peer_cancel_timeout(wifi);
+ else
+ p_state = CONNMAN_PEER_STATE_UNKNOWN;
+ }
+
+ if (p_state == CONNMAN_PEER_STATE_UNKNOWN)
+ return;
+
+ if (p_state == CONNMAN_PEER_STATE_CONFIGURATION) {
+ GSupplicantInterface *g_iface;
+ struct wifi_data *g_wifi;
+
+ g_iface = g_supplicant_peer_get_group_interface(peer);
+ if (!g_iface)
+ return;
+
+ g_wifi = g_supplicant_interface_get_data(g_iface);
+ if (!g_wifi)
+ return;
+
+ connman_peer_set_as_master(connman_peer,
+ !g_supplicant_peer_is_client(peer));
+ connman_peer_set_sub_device(connman_peer, g_wifi->device);
+ }
+
+ connman_peer_set_state(connman_peer, p_state);
+}
+
+static void peer_request(GSupplicantPeer *peer)
+{
+ GSupplicantInterface *iface = g_supplicant_peer_get_interface(peer);
+ struct wifi_data *wifi = g_supplicant_interface_get_data(iface);
+ struct connman_peer *connman_peer;
+ const char *identifier;
+
+ identifier = g_supplicant_peer_get_identifier(peer);
+
+ DBG("ident: %s", identifier);
+
+ connman_peer = connman_peer_get(wifi->device, identifier);
+ if (!connman_peer)
+ return;
+
+ connman_peer_request_connection(connman_peer);
}
static void debug(const char *str)
@@ -2126,6 +2866,8 @@ static const GSupplicantCallbacks callbacks = {
.network_changed = network_changed,
.peer_found = peer_found,
.peer_lost = peer_lost,
+ .peer_changed = peer_changed,
+ .peer_request = peer_request,
.debug = debug,
};