| /* |
| * |
| * Connection Manager |
| * |
| * Copyright (C) 2013 Intel Corporation. All rights reserved. |
| * |
| * 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 |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <errno.h> |
| #include <string.h> |
| |
| #define CONNMAN_API_SUBJECT_TO_CHANGE |
| #include <connman/plugin.h> |
| #include <connman/dbus.h> |
| #include <connman/technology.h> |
| #include <connman/device.h> |
| #include <connman/inet.h> |
| #include <gdbus.h> |
| |
| #define BLUEZ_SERVICE "org.bluez" |
| #define BLUEZ_PATH "/org/bluez" |
| #define BLUETOOTH_PAN_PANU "00001115-0000-1000-8000-00805f9b34fb" |
| #define BLUETOOTH_PAN_NAP "00001116-0000-1000-8000-00805f9b34fb" |
| #define BLUETOOTH_PAN_GN "00001117-0000-1000-8000-00805f9b34fb" |
| |
| #define BLUETOOTH_ADDR_LEN 6 |
| |
| static DBusConnection *connection; |
| static GDBusClient *client; |
| static GHashTable *devices; |
| static GHashTable *networks; |
| static bool bluetooth_tethering; |
| |
| struct bluetooth_pan { |
| struct connman_network *network; |
| GDBusProxy *btdevice_proxy; |
| GDBusProxy *btnetwork_proxy; |
| const char *pan_role; |
| }; |
| |
| static void address2ident(const char *address, char *ident) |
| { |
| int i; |
| |
| for (i = 0; i < BLUETOOTH_ADDR_LEN; i++) { |
| ident[i * 2] = address[i * 3]; |
| ident[i * 2 + 1] = address[i * 3 + 1]; |
| } |
| ident[BLUETOOTH_ADDR_LEN * 2] = '\0'; |
| } |
| |
| static const char *proxy_get_string(GDBusProxy *proxy, const char *property) |
| { |
| DBusMessageIter iter; |
| const char *str; |
| |
| if (!g_dbus_proxy_get_property(proxy, property, &iter)) |
| return NULL; |
| dbus_message_iter_get_basic(&iter, &str); |
| return str; |
| } |
| |
| static bool proxy_get_bool(GDBusProxy *proxy, const char *property) |
| { |
| DBusMessageIter iter; |
| dbus_bool_t value; |
| |
| if (!g_dbus_proxy_get_property(proxy, property, &iter)) |
| return false; |
| dbus_message_iter_get_basic(&iter, &value); |
| return value; |
| } |
| |
| static const char *proxy_get_role(GDBusProxy *proxy) |
| { |
| DBusMessageIter iter, value; |
| const char *pref = NULL; |
| |
| if (!proxy) |
| return NULL; |
| |
| if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) |
| return NULL; |
| |
| dbus_message_iter_recurse(&iter, &value); |
| while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { |
| const char *uuid; |
| |
| dbus_message_iter_get_basic(&value, &uuid); |
| /* |
| * If a device offers more than one role, we prefer NAP, |
| * then GN, then PANU. |
| */ |
| if (!strcmp(uuid, BLUETOOTH_PAN_NAP)) |
| return "nap"; |
| if (!strcmp(uuid, BLUETOOTH_PAN_GN)) |
| pref = "gn"; |
| if (!strcmp(uuid, BLUETOOTH_PAN_PANU) && !pref) |
| pref = "panu"; |
| |
| dbus_message_iter_next(&value); |
| } |
| |
| return pref; |
| } |
| |
| static int bluetooth_pan_probe(struct connman_network *network) |
| { |
| GHashTableIter iter; |
| gpointer key, value; |
| |
| DBG("network %p", network); |
| |
| g_hash_table_iter_init(&iter, networks); |
| |
| while (g_hash_table_iter_next(&iter, &key, &value)) { |
| struct bluetooth_pan *pan = value; |
| |
| if (network == pan->network) |
| return 0; |
| } |
| |
| return -EOPNOTSUPP; |
| } |
| |
| static void pan_remove_nap(struct bluetooth_pan *pan) |
| { |
| struct connman_device *device; |
| struct connman_network *network = pan->network; |
| |
| DBG("network %p pan %p", pan->network, pan); |
| |
| if (!network) |
| return; |
| |
| pan->network = NULL; |
| connman_network_set_data(network, NULL); |
| |
| device = connman_network_get_device(network); |
| if (device) |
| connman_device_remove_network(device, network); |
| |
| connman_network_unref(network); |
| } |
| |
| static void bluetooth_pan_remove(struct connman_network *network) |
| { |
| struct bluetooth_pan *pan = connman_network_get_data(network); |
| |
| DBG("network %p pan %p", network, pan); |
| |
| connman_network_set_data(network, NULL); |
| |
| if (pan) |
| pan_remove_nap(pan); |
| } |
| |
| static bool pan_connect(struct bluetooth_pan *pan, |
| const char *iface) |
| { |
| int index; |
| |
| if (!iface) { |
| if (!proxy_get_bool(pan->btnetwork_proxy, "Connected")) |
| return false; |
| iface = proxy_get_string(pan->btnetwork_proxy, "Interface"); |
| } |
| |
| if (!iface) |
| return false; |
| |
| index = connman_inet_ifindex(iface); |
| if (index < 0) { |
| DBG("network %p invalid index %d", pan->network, index); |
| return false; |
| } |
| |
| connman_network_set_index(pan->network, index); |
| connman_network_set_connected(pan->network, true); |
| |
| return true; |
| } |
| |
| static void pan_connect_cb(DBusMessage *message, void *user_data) |
| { |
| const char *path = user_data; |
| const char *iface = NULL; |
| struct bluetooth_pan *pan; |
| DBusMessageIter iter; |
| |
| pan = g_hash_table_lookup(networks, path); |
| if (!pan) { |
| DBG("network already removed"); |
| return; |
| } |
| |
| if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { |
| const char *dbus_error = dbus_message_get_error_name(message); |
| |
| DBG("network %p %s", pan->network, dbus_error); |
| |
| if (strcmp(dbus_error, |
| "org.bluez.Error.AlreadyConnected") != 0) { |
| connman_network_set_error(pan->network, |
| CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); |
| return; |
| } |
| } else { |
| if (dbus_message_iter_init(message, &iter) && |
| dbus_message_iter_get_arg_type(&iter) == |
| DBUS_TYPE_STRING) |
| dbus_message_iter_get_basic(&iter, &iface); |
| } |
| |
| DBG("network %p interface %s", pan->network, iface); |
| |
| pan_connect(pan, iface); |
| } |
| |
| static void pan_connect_append(DBusMessageIter *iter, |
| void *user_data) |
| { |
| const char *path = user_data; |
| struct bluetooth_pan *pan; |
| |
| pan = g_hash_table_lookup(networks, path); |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pan->pan_role); |
| } |
| |
| static int bluetooth_pan_connect(struct connman_network *network) |
| { |
| struct bluetooth_pan *pan = connman_network_get_data(network); |
| const char *path; |
| |
| DBG("network %p", network); |
| |
| if (!pan) |
| return -EINVAL; |
| |
| path = g_dbus_proxy_get_path(pan->btnetwork_proxy); |
| |
| if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Connect", |
| pan_connect_append, pan_connect_cb, |
| g_strdup(path), g_free)) |
| return -EIO; |
| |
| connman_network_set_associating(pan->network, true); |
| |
| return -EINPROGRESS; |
| } |
| |
| static void pan_disconnect_cb(DBusMessage *message, void *user_data) |
| { |
| const char *path = user_data; |
| struct bluetooth_pan *pan; |
| |
| pan = g_hash_table_lookup(networks, path); |
| if (!pan) { |
| DBG("network already removed"); |
| return; |
| } |
| |
| if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { |
| const char *dbus_error = dbus_message_get_error_name(message); |
| |
| DBG("network %p %s", pan->network, dbus_error); |
| } |
| |
| DBG("network %p", pan->network); |
| |
| connman_network_set_connected(pan->network, false); |
| } |
| |
| static int bluetooth_pan_disconnect(struct connman_network *network, bool user_initiated) |
| { |
| struct bluetooth_pan *pan = connman_network_get_data(network); |
| const char *path; |
| |
| DBG("network %p", network); |
| |
| if (!pan) |
| return -EINVAL; |
| |
| path = g_dbus_proxy_get_path(pan->btnetwork_proxy); |
| |
| if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Disconnect", |
| NULL, pan_disconnect_cb, g_strdup(path), g_free)) |
| return -EIO; |
| |
| return -EINPROGRESS; |
| } |
| |
| static void btnetwork_property_change(GDBusProxy *proxy, const char *name, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct bluetooth_pan *pan; |
| dbus_bool_t connected; |
| bool proxy_connected, network_connected; |
| |
| if (strcmp(name, "Connected") != 0) |
| return; |
| |
| pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy)); |
| if (!pan || !pan->network) |
| return; |
| |
| dbus_message_iter_get_basic(iter, &connected); |
| proxy_connected = connected; |
| |
| network_connected = connman_network_get_connected(pan->network); |
| |
| DBG("network %p network connected %d proxy connected %d", |
| pan->network, network_connected, proxy_connected); |
| |
| if (network_connected != proxy_connected) |
| connman_network_set_connected(pan->network, proxy_connected); |
| } |
| |
| static void pan_create_nap(struct bluetooth_pan *pan) |
| { |
| struct connman_device *device; |
| const char* role; |
| |
| role = proxy_get_role(pan->btdevice_proxy); |
| if (!role) { |
| pan_remove_nap(pan); |
| return; |
| } |
| |
| device = g_hash_table_lookup(devices, |
| proxy_get_string(pan->btdevice_proxy, "Adapter")); |
| |
| if (!device || !connman_device_get_powered(device)) |
| return; |
| |
| if (!pan->network) { |
| const char *address; |
| char ident[BLUETOOTH_ADDR_LEN * 2 + 1]; |
| const char *name, *path; |
| |
| address = proxy_get_string(pan->btdevice_proxy, "Address"); |
| if (!address) { |
| connman_warn("Bluetooth device address missing"); |
| return; |
| } |
| |
| address2ident(address, ident); |
| |
| pan->network = connman_network_create(ident, |
| CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN); |
| |
| name = proxy_get_string(pan->btdevice_proxy, "Alias"); |
| path = g_dbus_proxy_get_path(pan->btnetwork_proxy); |
| |
| DBG("network %p %s %s", pan->network, path, name); |
| |
| if (!pan->network) { |
| connman_warn("Bluetooth network %s creation failed", |
| path); |
| return; |
| } |
| |
| connman_network_set_data(pan->network, pan); |
| connman_network_set_name(pan->network, name); |
| connman_network_set_group(pan->network, ident); |
| } |
| |
| pan->pan_role = role; |
| connman_device_add_network(device, pan->network); |
| |
| if (pan_connect(pan, NULL)) |
| DBG("network %p already connected", pan->network); |
| } |
| |
| static void btdevice_property_change(GDBusProxy *proxy, const char *name, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct bluetooth_pan *pan; |
| const char *old_role = NULL; |
| const char *new_role; |
| |
| if (strcmp(name, "UUIDs")) |
| return; |
| |
| pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy)); |
| if (!pan) |
| return; |
| |
| if (pan->network && |
| connman_network_get_device(pan->network)) |
| old_role = pan->pan_role; |
| new_role = proxy_get_role(pan->btdevice_proxy); |
| |
| DBG("network %p network role %s proxy role %s", pan->network, old_role, |
| new_role); |
| |
| if (old_role && new_role && !strcmp(old_role, new_role)) |
| return; |
| |
| pan_create_nap(pan); |
| } |
| |
| static void pan_free(gpointer data) |
| { |
| struct bluetooth_pan *pan = data; |
| |
| if (pan->btnetwork_proxy) { |
| g_dbus_proxy_unref(pan->btnetwork_proxy); |
| pan->btnetwork_proxy = NULL; |
| } |
| |
| if (pan->btdevice_proxy) { |
| g_dbus_proxy_unref(pan->btdevice_proxy); |
| pan->btdevice_proxy = NULL; |
| } |
| |
| pan_remove_nap(pan); |
| |
| g_free(pan); |
| } |
| |
| static void pan_create(GDBusProxy *network_proxy) |
| { |
| const char *path = g_dbus_proxy_get_path(network_proxy); |
| struct bluetooth_pan *pan; |
| |
| pan = g_try_new0(struct bluetooth_pan, 1); |
| |
| if (!pan) { |
| connman_error("Out of memory creating PAN NAP"); |
| return; |
| } |
| |
| g_hash_table_replace(networks, g_strdup(path), pan); |
| |
| pan->btnetwork_proxy = g_dbus_proxy_ref(network_proxy); |
| pan->btdevice_proxy = g_dbus_proxy_new(client, path, |
| "org.bluez.Device1"); |
| |
| if (!pan->btdevice_proxy) { |
| connman_error("Cannot create BT PAN watcher %s", path); |
| g_hash_table_remove(networks, path); |
| return; |
| } |
| |
| g_dbus_proxy_set_property_watch(pan->btnetwork_proxy, |
| btnetwork_property_change, NULL); |
| |
| g_dbus_proxy_set_property_watch(pan->btdevice_proxy, |
| btdevice_property_change, NULL); |
| |
| DBG("pan %p %s role %s", pan, path, proxy_get_role(pan->btdevice_proxy)); |
| |
| pan_create_nap(pan); |
| } |
| |
| static struct connman_network_driver network_driver = { |
| .name = "bluetooth", |
| .type = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN, |
| .probe = bluetooth_pan_probe, |
| .remove = bluetooth_pan_remove, |
| .connect = bluetooth_pan_connect, |
| .disconnect = bluetooth_pan_disconnect, |
| }; |
| |
| static void enable_device(struct connman_device *device, const char *path) |
| { |
| GHashTableIter iter; |
| gpointer key, value; |
| |
| DBG("device %p %s", device, path); |
| connman_device_set_powered(device, true); |
| |
| g_hash_table_iter_init(&iter, networks); |
| while (g_hash_table_iter_next(&iter, &key, &value)) { |
| struct bluetooth_pan *pan = value; |
| |
| if (g_strcmp0(proxy_get_string(pan->btdevice_proxy, "Adapter"), |
| path) == 0) { |
| |
| DBG("enable network %p", pan->network); |
| pan_create_nap(pan); |
| } |
| } |
| } |
| |
| static void device_enable_cb(const DBusError *error, void *user_data) |
| { |
| char *path = user_data; |
| struct connman_device *device; |
| |
| device = g_hash_table_lookup(devices, path); |
| if (!device) { |
| DBG("device already removed"); |
| goto out; |
| } |
| |
| if (dbus_error_is_set(error)) { |
| connman_warn("Bluetooth device %s not enabled %s", |
| path, error->message); |
| goto out; |
| } |
| |
| enable_device(device, path); |
| out: |
| g_free(path); |
| } |
| |
| static int bluetooth_device_enable(struct connman_device *device) |
| { |
| GDBusProxy *proxy = connman_device_get_data(device); |
| dbus_bool_t device_powered = TRUE; |
| const char *path; |
| |
| if (!proxy) |
| return 0; |
| |
| path = g_dbus_proxy_get_path(proxy); |
| |
| if (proxy_get_bool(proxy, "Powered")) { |
| DBG("already enabled %p %s", device, path); |
| return -EALREADY; |
| } |
| |
| DBG("device %p %s", device, path); |
| |
| g_dbus_proxy_set_property_basic(proxy, "Powered", |
| DBUS_TYPE_BOOLEAN, &device_powered, |
| device_enable_cb, g_strdup(path), NULL); |
| |
| return -EINPROGRESS; |
| } |
| |
| static void disable_device(struct connman_device *device, const char *path) |
| { |
| GHashTableIter iter; |
| gpointer key, value; |
| |
| DBG("device %p %s", device, path); |
| connman_device_set_powered(device, false); |
| |
| g_hash_table_iter_init(&iter, networks); |
| while (g_hash_table_iter_next(&iter, &key, &value)) { |
| struct bluetooth_pan *pan = value; |
| |
| if (pan->network && connman_network_get_device(pan->network) |
| == device) { |
| DBG("disable network %p", pan->network); |
| connman_device_remove_network(device, pan->network); |
| } |
| } |
| } |
| |
| static void device_disable_cb(const DBusError *error, void *user_data) |
| { |
| char *path = user_data; |
| struct connman_device *device; |
| |
| device = g_hash_table_lookup(devices, path); |
| if (!device) { |
| DBG("device already removed"); |
| goto out; |
| } |
| |
| if (dbus_error_is_set(error)) { |
| connman_warn("Bluetooth device %s not disabled: %s", |
| path, error->message); |
| goto out; |
| } |
| |
| disable_device(device, path); |
| |
| out: |
| g_free(path); |
| } |
| |
| static int bluetooth_device_disable(struct connman_device *device) |
| { |
| GDBusProxy *proxy = connman_device_get_data(device); |
| dbus_bool_t device_powered = FALSE; |
| const char *path; |
| |
| if (!proxy) |
| return 0; |
| |
| path = g_dbus_proxy_get_path(proxy); |
| |
| if (!proxy_get_bool(proxy, "Powered")) { |
| DBG("already disabled %p %s", device, path); |
| return -EALREADY; |
| } |
| |
| DBG("device %p %s", device, path); |
| |
| g_dbus_proxy_set_property_basic(proxy, "Powered", |
| DBUS_TYPE_BOOLEAN, &device_powered, |
| device_disable_cb, g_strdup(path), NULL); |
| |
| return -EINPROGRESS; |
| } |
| |
| static void adapter_property_change(GDBusProxy *proxy, const char *name, |
| DBusMessageIter *iter, void *user_data) |
| { |
| struct connman_device *device; |
| const char *path; |
| bool adapter_powered, device_powered; |
| |
| if (strcmp(name, "Powered") != 0) |
| return; |
| |
| path = g_dbus_proxy_get_path(proxy); |
| device = g_hash_table_lookup(devices, path); |
| |
| adapter_powered = proxy_get_bool(proxy, "Powered"); |
| device_powered = connman_device_get_powered(device); |
| |
| DBG("device %p %s device powered %d adapter powered %d", device, path, |
| device_powered, adapter_powered); |
| |
| if (device_powered != adapter_powered) { |
| if (adapter_powered) |
| enable_device(device, path); |
| else |
| disable_device(device, path); |
| } |
| } |
| |
| static void device_free(gpointer data) |
| { |
| struct connman_device *device = data; |
| GDBusProxy *proxy = connman_device_get_data(device); |
| |
| connman_device_set_data(device, NULL); |
| if (proxy) |
| g_dbus_proxy_unref(proxy); |
| |
| connman_device_unregister(device); |
| connman_device_unref(device); |
| } |
| |
| struct tethering_info { |
| struct connman_technology *technology; |
| char *bridge; |
| bool enable; |
| }; |
| |
| static void tethering_free(void *user_data) |
| { |
| struct tethering_info *tethering = user_data; |
| |
| g_free(tethering->bridge); |
| g_free(tethering); |
| } |
| |
| static void tethering_create_cb(DBusMessage *message, void *user_data) |
| { |
| struct tethering_info *tethering = user_data; |
| |
| if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { |
| const char *dbus_error = dbus_message_get_error_name(message); |
| |
| DBG("%s tethering failed: %s", |
| tethering->enable ? "enable" : "disable", |
| dbus_error); |
| return; |
| } |
| |
| DBG("bridge %s %s", tethering->bridge, tethering->enable ? |
| "enabled": "disabled"); |
| |
| if (tethering->technology) |
| connman_technology_tethering_notify(tethering->technology, |
| tethering->enable); |
| } |
| |
| static void tethering_append(DBusMessageIter *iter, void *user_data) |
| { |
| struct tethering_info *tethering = user_data; |
| const char *nap = "nap"; |
| |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &nap); |
| if (tethering->enable) |
| dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, |
| &tethering->bridge); |
| } |
| |
| static bool tethering_create(const char *path, |
| struct connman_technology *technology, const char *bridge, |
| bool enabled) |
| { |
| struct tethering_info *tethering = g_new0(struct tethering_info, 1); |
| GDBusProxy *proxy; |
| const char *method; |
| bool result; |
| |
| DBG("path %s bridge %s", path, bridge); |
| |
| if (!bridge) |
| return -EINVAL; |
| |
| proxy = g_dbus_proxy_new(client, path, "org.bluez.NetworkServer1"); |
| if (!proxy) |
| return false; |
| |
| tethering->technology = technology; |
| tethering->bridge = g_strdup(bridge); |
| tethering->enable = enabled; |
| |
| if (tethering->enable) |
| method = "Register"; |
| else |
| method = "Unregister"; |
| |
| result = g_dbus_proxy_method_call(proxy, method, tethering_append, |
| tethering_create_cb, tethering, tethering_free); |
| |
| g_dbus_proxy_unref(proxy); |
| |
| return result; |
| } |
| |
| static void device_create(GDBusProxy *proxy) |
| { |
| struct connman_device *device = NULL; |
| const char *path = g_dbus_proxy_get_path(proxy); |
| const char *address; |
| char ident[BLUETOOTH_ADDR_LEN * 2 + 1]; |
| bool powered; |
| |
| address = proxy_get_string(proxy, "Address"); |
| if (!address) |
| return; |
| |
| address2ident(address, ident); |
| |
| device = connman_device_create("bluetooth", |
| CONNMAN_DEVICE_TYPE_BLUETOOTH); |
| if (!device) |
| return; |
| |
| connman_device_set_data(device, g_dbus_proxy_ref(proxy)); |
| connman_device_set_ident(device, ident); |
| |
| g_hash_table_replace(devices, g_strdup(path), device); |
| |
| DBG("device %p %s device powered %d adapter powered %d", device, |
| path, connman_device_get_powered(device), |
| proxy_get_bool(proxy, "Powered")); |
| |
| if (connman_device_register(device) < 0) { |
| g_hash_table_remove(devices, device); |
| return; |
| } |
| |
| g_dbus_proxy_set_property_watch(proxy, adapter_property_change, NULL); |
| |
| powered = proxy_get_bool(proxy, "Powered"); |
| connman_device_set_powered(device, powered); |
| |
| if (proxy_get_role(proxy) && !bluetooth_tethering) |
| tethering_create(path, NULL, NULL, false); |
| } |
| |
| static void object_added(GDBusProxy *proxy, void *user_data) |
| { |
| const char *interface; |
| |
| interface = g_dbus_proxy_get_interface(proxy); |
| if (!interface) { |
| connman_warn("Interface or proxy missing when adding " |
| "bluetooth object"); |
| return; |
| } |
| |
| if (strcmp(interface, "org.bluez.Adapter1") == 0) { |
| DBG("%s %s", interface, g_dbus_proxy_get_path(proxy)); |
| device_create(proxy); |
| return; |
| } |
| |
| if (strcmp(interface, "org.bluez.Network1") == 0) { |
| DBG("%s %s", interface, g_dbus_proxy_get_path(proxy)); |
| pan_create(proxy); |
| return; |
| } |
| } |
| |
| static void object_removed(GDBusProxy *proxy, void *user_data) |
| { |
| const char *interface, *path; |
| |
| interface = g_dbus_proxy_get_interface(proxy); |
| if (!interface) { |
| connman_warn("Interface or proxy missing when removing " |
| "bluetooth object"); |
| return; |
| } |
| |
| if (strcmp(interface, "org.bluez.Adapter1") == 0) { |
| path = g_dbus_proxy_get_path(proxy); |
| DBG("%s %s", interface, path); |
| |
| g_hash_table_remove(devices, path); |
| } |
| |
| if (strcmp(interface, "org.bluez.Network1") == 0) { |
| path = g_dbus_proxy_get_path(proxy); |
| DBG("%s %s", interface, path); |
| |
| g_hash_table_remove(networks, path); |
| } |
| |
| } |
| |
| static int bluetooth_device_probe(struct connman_device *device) |
| { |
| GHashTableIter iter; |
| gpointer key, value; |
| |
| g_hash_table_iter_init(&iter, devices); |
| |
| while (g_hash_table_iter_next(&iter, &key, &value)) { |
| struct connman_device *known = value; |
| |
| if (device == known) |
| return 0; |
| } |
| |
| return -EOPNOTSUPP; |
| } |
| |
| static void bluetooth_device_remove(struct connman_device *device) |
| { |
| DBG("%p", device); |
| } |
| |
| static struct connman_device_driver device_driver = { |
| .name = "bluetooth", |
| .type = CONNMAN_DEVICE_TYPE_BLUETOOTH, |
| .probe = bluetooth_device_probe, |
| .remove = bluetooth_device_remove, |
| .enable = bluetooth_device_enable, |
| .disable = bluetooth_device_disable, |
| }; |
| |
| static int bluetooth_tech_probe(struct connman_technology *technology) |
| { |
| return 0; |
| } |
| |
| static void bluetooth_tech_remove(struct connman_technology *technology) |
| { |
| |
| } |
| |
| static int bluetooth_tech_set_tethering(struct connman_technology *technology, |
| const char *identifier, const char *passphrase, |
| const char *bridge, bool enabled) |
| { |
| GHashTableIter hash_iter; |
| gpointer key, value; |
| int i = 0; |
| |
| bluetooth_tethering = enabled; |
| |
| g_hash_table_iter_init(&hash_iter, devices); |
| |
| while (g_hash_table_iter_next(&hash_iter, &key, &value)) { |
| const char *path = key; |
| struct connman_device *device = value; |
| |
| DBG("device %p", device); |
| |
| if (tethering_create(path, technology, bridge, enabled) |
| ) |
| i++; |
| } |
| |
| DBG("%s %d device(s)", enabled ? "enabled" : "disabled", i); |
| |
| if (i == 0) |
| return -ENODEV; |
| |
| return 0; |
| } |
| |
| static struct connman_technology_driver tech_driver = { |
| .name = "bluetooth", |
| .type = CONNMAN_SERVICE_TYPE_BLUETOOTH, |
| .probe = bluetooth_tech_probe, |
| .remove = bluetooth_tech_remove, |
| .set_tethering = bluetooth_tech_set_tethering, |
| }; |
| |
| static int bluetooth_init(void) |
| { |
| connection = connman_dbus_get_connection(); |
| if (!connection) |
| goto out; |
| |
| if (connman_technology_driver_register(&tech_driver) < 0) { |
| connman_warn("Failed to initialize technology for Bluez 5"); |
| goto out; |
| } |
| |
| devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, |
| device_free); |
| |
| if (connman_device_driver_register(&device_driver) < 0) { |
| connman_warn("Failed to initialize device driver for " |
| BLUEZ_SERVICE); |
| connman_technology_driver_unregister(&tech_driver); |
| goto out; |
| } |
| |
| if (connman_network_driver_register(&network_driver) < 0) { |
| connman_technology_driver_unregister(&tech_driver); |
| connman_device_driver_unregister(&device_driver); |
| goto out; |
| } |
| |
| networks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, |
| pan_free); |
| |
| client = g_dbus_client_new(connection, BLUEZ_SERVICE, BLUEZ_PATH); |
| if (!client) { |
| connman_warn("Failed to initialize D-Bus client for " |
| BLUEZ_SERVICE); |
| goto out; |
| } |
| |
| g_dbus_client_set_proxy_handlers(client, object_added, object_removed, |
| NULL, NULL); |
| |
| return 0; |
| |
| out: |
| if (networks) |
| g_hash_table_destroy(networks); |
| |
| if (devices) |
| g_hash_table_destroy(devices); |
| |
| if (client) |
| g_dbus_client_unref(client); |
| |
| if (connection) |
| dbus_connection_unref(connection); |
| |
| return -EIO; |
| } |
| |
| static void bluetooth_exit(void) |
| { |
| /* |
| * We unset the disabling of the Bluetooth device when shutting down |
| * so that non-PAN BT connections are not affected. |
| */ |
| device_driver.disable = NULL; |
| |
| g_dbus_client_unref(client); |
| |
| connman_network_driver_unregister(&network_driver); |
| g_hash_table_destroy(networks); |
| |
| connman_device_driver_unregister(&device_driver); |
| g_hash_table_destroy(devices); |
| |
| connman_technology_driver_unregister(&tech_driver); |
| dbus_connection_unref(connection); |
| } |
| |
| CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION, |
| CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit) |