summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2014-10-13 14:28:13 +0200
committerThomas Haller <thaller@redhat.com>2014-10-13 14:30:27 +0200
commitd00fed839aac074b30f7de9bcd57ed4bd13074c6 (patch)
tree785b45936853090866567c3ec4935d2af3d45d9a
parentec84e4f00a9607ccea0139d728d24c2c2d714e6b (diff)
parentf1c9595311f52d8b79e8d2032e006005613a8fb1 (diff)
bluez: merge branch 'lr-bluez5-dun' (rh #1055628)
https://bugzilla.redhat.com/show_bug.cgi?id=1055628 Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r--configure.ac14
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec3
-rw-r--r--src/devices/bluetooth/Makefile.am14
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c163
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h5
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c2
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c409
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h46
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c2
-rw-r--r--src/devices/bluetooth/nm-bt-error.c33
-rw-r--r--src/devices/bluetooth/nm-bt-error.h35
-rw-r--r--src/devices/bluetooth/nm-device-bt.c13
-rw-r--r--src/devices/bluetooth/nm-device-bt.h6
13 files changed, 678 insertions, 67 deletions
diff --git a/configure.ac b/configure.ac
index 6da2e5f286..7ef899a122 100644
--- a/configure.ac
+++ b/configure.ac
@@ -616,6 +616,20 @@ else
fi
AM_CONDITIONAL(WITH_MODEM_MANAGER_1, test "${with_modem_manager_1}" = "yes")
+# Bluez5 DUN support
+PKG_CHECK_MODULES(BLUEZ5, [bluez >= 5], [have_bluez5=yes],[have_bluez5=no])
+AC_ARG_ENABLE(bluez5-dun, AS_HELP_STRING([--enable-bluez5-dun], [enable Bluez5 DUN support]),
+ [enable_bluez5_dun=${enableval}], [enable_bluez5_dun=${have_bluez5}])
+if (test "${enable_bluez5_dun}" = "yes"); then
+ if test x"$have_bluez5" = x"no"; then
+ AC_MSG_ERROR(Bluez 5.x development headers are required)
+ fi
+ AC_DEFINE(WITH_BLUEZ5_DUN, 1, [Define if you have Bluez 5 libraries])
+else
+ AC_DEFINE(HAVE_BLUEZ5_DUN, 0, [Define if you have Bluez 5 libraries])
+fi
+AM_CONDITIONAL(WITH_BLUEZ5_DUN, test "${enable_bluez5_dun}" = "yes")
+
# DHCP client support
AC_ARG_WITH([dhclient], AS_HELP_STRING([--with-dhclient=yes|no|path], [Enable dhclient 4.x support]))
AC_ARG_WITH([dhcpcd], AS_HELP_STRING([--with-dhcpcd=yes|no|path], [Enable dhcpcd 4.x support]))
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index 6ec39b65d0..be95bfc8ca 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -145,6 +145,9 @@ BuildRequires: libuuid-devel
BuildRequires: libgudev1-devel >= 143
BuildRequires: vala-tools
BuildRequires: iptables
+%if 0%{?with_bluetooth} && 0%{?fedora} > 19
+BuildRequires: bluez-libs-devel
+%endif
%if 0%{?with_wimax}
BuildRequires: wimax-devel
%endif
diff --git a/src/devices/bluetooth/Makefile.am b/src/devices/bluetooth/Makefile.am
index 07c8f01024..8ab561acf2 100644
--- a/src/devices/bluetooth/Makefile.am
+++ b/src/devices/bluetooth/Makefile.am
@@ -20,7 +20,7 @@ AM_CPPFLAGS = \
GLIB_GENERATED = nm-bt-enum-types.h nm-bt-enum-types.c
GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
-nm_bt_enum_types_sources = $(srcdir)/nm-device-bt.h
+nm_bt_enum_types_sources = $(srcdir)/nm-bt-error.h
nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
@@ -43,6 +43,8 @@ libnm_device_plugin_bluetooth_la_SOURCES = \
nm-bluez4-manager.h \
nm-bluez5-manager.c \
nm-bluez5-manager.h \
+ nm-bt-error.h \
+ nm-bt-error.c \
\
nm-device-bt.c \
nm-device-bt.h \
@@ -58,6 +60,16 @@ libnm_device_plugin_bluetooth_la_LIBADD = \
$(DBUS_LIBS) \
$(GUDEV_LIBS)
+if WITH_BLUEZ5_DUN
+AM_CPPFLAGS += $(BLUEZ5_CFLAGS)
+
+libnm_device_plugin_bluetooth_la_SOURCES += \
+ nm-bluez5-dun.c \
+ nm-bluez5-dun.h
+
+libnm_device_plugin_bluetooth_la_LIBADD += $(BLUEZ5_LIBS)
+endif
+
CLEANFILES = $(BUILT_SOURCES)
EXTRA_DIST = $(SYMBOL_VIS_FILE)
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 8196d66007..6c1693533c 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -32,7 +32,8 @@
#include "nm-logging.h"
#include "nm-utils.h"
#include "nm-settings-connection.h"
-
+#include "nm-bluez5-dun.h"
+#include "NetworkManagerUtils.h"
G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
@@ -53,12 +54,14 @@ typedef struct {
gboolean usable;
NMBluetoothCapabilities connection_bt_type;
+ char *adapter_address;
char *address;
char *name;
guint32 capabilities;
gboolean connected;
- char *bt_iface;
+ char *b4_iface;
+ NMBluez5DunContext *b5_dun_context;
NMConnectionProvider *provider;
GSList *connections;
@@ -152,9 +155,12 @@ nm_bluez_device_get_capabilities (NMBluezDevice *self)
gboolean
nm_bluez_device_get_connected (NMBluezDevice *self)
{
+ NMBluezDevicePrivate *priv;
+
g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->connected;
+ priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+ return priv->connected;
}
static void
@@ -257,13 +263,12 @@ check_emit_usable (NMBluezDevice *self)
gboolean new_usable;
/* only expect the supported capabilities set. */
- g_assert (priv->bluez_version != 4 || ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE ));
- g_assert (priv->bluez_version != 5 || ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP )) == NM_BT_CAPABILITY_NONE ));
+ g_assert ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE );
new_usable = (priv->initialized && priv->capabilities && priv->name &&
((priv->bluez_version == 4) ||
(priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered) ) &&
- priv->dbus_connection && priv->address);
+ priv->dbus_connection && priv->address && priv->adapter_address);
if (!new_usable)
goto END;
@@ -410,26 +415,33 @@ nm_bluez_device_disconnect (NMBluezDevice *self)
{
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
GVariant *args = NULL;
- const char *dbus_iface;
+ const char *dbus_iface = NULL;
g_return_if_fail (priv->dbus_connection);
- if (priv->bluez_version == 5) {
- g_return_if_fail (priv->connection_bt_type == NM_BT_CAPABILITY_NAP);
- dbus_iface = BLUEZ5_NETWORK_INTERFACE;
- } else if (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
- /* Can't pass a NULL interface name through dbus to bluez, so just
- * ignore the disconnect if the interface isn't known.
- */
- if (!priv->bt_iface)
- return;
-
- args = g_variant_new ("(s)", priv->bt_iface),
- dbus_iface = BLUEZ4_SERIAL_INTERFACE;
- } else {
- g_return_if_fail (priv->bluez_version == 4 && priv->connection_bt_type == NM_BT_CAPABILITY_NAP);
- dbus_iface = BLUEZ4_NETWORK_INTERFACE;
- }
+ if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
+ if (priv->bluez_version == 4) {
+ /* Can't pass a NULL interface name through dbus to bluez, so just
+ * ignore the disconnect if the interface isn't known.
+ */
+ if (!priv->b4_iface)
+ goto out;
+ args = g_variant_new ("(s)", priv->b4_iface),
+ dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+ } else if (priv->bluez_version == 5) {
+ nm_bluez5_dun_cleanup (priv->b5_dun_context);
+ priv->connected = FALSE;
+ goto out;
+ }
+ } else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) {
+ if (priv->bluez_version == 4)
+ dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+ else if (priv->bluez_version == 5)
+ dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+ else
+ g_assert_not_reached ();
+ } else
+ g_assert_not_reached ();
g_dbus_connection_call (priv->dbus_connection,
BLUEZ_SERVICE,
@@ -444,6 +456,8 @@ nm_bluez_device_disconnect (NMBluezDevice *self)
(GAsyncReadyCallback) bluez_disconnect_cb,
g_object_ref (self));
+out:
+ g_clear_pointer (&priv->b4_iface, g_free);
priv->connection_bt_type = NM_BT_CAPABILITY_NONE;
}
@@ -470,7 +484,7 @@ bluez_connect_cb (GDBusConnection *dbus_connection,
g_simple_async_result_set_op_res_gpointer (result,
g_strdup (device),
g_free);
- priv->bt_iface = device;
+ priv->b4_iface = device;
g_variant_unref (variant);
}
@@ -479,6 +493,26 @@ bluez_connect_cb (GDBusConnection *dbus_connection,
g_object_unref (result_object);
}
+static void
+bluez5_dun_connect_cb (NMBluez5DunContext *context,
+ const char *device,
+ GError *error,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
+
+ if (error) {
+ g_simple_async_result_take_error (result, error);
+ } else {
+ g_simple_async_result_set_op_res_gpointer (result,
+ g_strdup (device),
+ g_free);
+ }
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+}
+
void
nm_bluez_device_connect_async (NMBluezDevice *self,
NMBluetoothCapabilities connection_bt_type,
@@ -487,26 +521,35 @@ nm_bluez_device_connect_async (NMBluezDevice *self,
{
GSimpleAsyncResult *simple;
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- const char *dbus_iface;
- const char *connect_type = BLUETOOTH_CONNECT_NAP;
+ const char *dbus_iface = NULL;
+ const char *connect_type = NULL;
g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
- if (priv->bluez_version == 5) {
- g_return_if_fail (connection_bt_type == NM_BT_CAPABILITY_NAP);
- dbus_iface = BLUEZ5_NETWORK_INTERFACE;
- } else if (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_DUN) {
- dbus_iface = BLUEZ4_SERIAL_INTERFACE;
- connect_type = BLUETOOTH_CONNECT_DUN;
- } else {
- g_return_if_fail (priv->bluez_version == 4 && connection_bt_type == NM_BT_CAPABILITY_NAP);
- dbus_iface = BLUEZ4_NETWORK_INTERFACE;
- }
-
simple = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
nm_bluez_device_connect_async);
+ priv->connection_bt_type = connection_bt_type;
+
+ if (connection_bt_type == NM_BT_CAPABILITY_NAP) {
+ connect_type = BLUETOOTH_CONNECT_NAP;
+ if (priv->bluez_version == 4)
+ dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+ else if (priv->bluez_version == 5)
+ dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+ } else if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+ connect_type = BLUETOOTH_CONNECT_DUN;
+ if (priv->bluez_version == 4)
+ dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+ else if (priv->bluez_version == 5) {
+ if (priv->b5_dun_context == NULL)
+ priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address);
+ nm_bluez5_dun_connect (priv->b5_dun_context, bluez5_dun_connect_cb, simple);
+ return;
+ }
+ } else
+ g_assert_not_reached ();
g_dbus_connection_call (priv->dbus_connection,
BLUEZ_SERVICE,
@@ -520,8 +563,6 @@ nm_bluez_device_connect_async (NMBluezDevice *self,
NULL,
(GAsyncReadyCallback) bluez_connect_cb,
simple);
-
- priv->connection_bt_type = connection_bt_type;
}
const char *
@@ -529,6 +570,7 @@ nm_bluez_device_connect_finish (NMBluezDevice *self,
GAsyncResult *result,
GError **error)
{
+ NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
GSimpleAsyncResult *simple;
const char *device;
@@ -543,13 +585,28 @@ nm_bluez_device_connect_finish (NMBluezDevice *self,
return NULL;
device = (const char *) g_simple_async_result_get_op_res_gpointer (simple);
+ if (device && priv->bluez_version == 5)
+ priv->connected = TRUE;
+
return device;
}
/***********************************************************/
+static void
+set_adapter_address (NMBluezDevice *self, const char *address)
+{
+ NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+
+ g_return_if_fail (address);
+
+ if (priv->adapter_address)
+ g_free (priv->adapter_address);
+ priv->adapter_address = g_strdup (address);
+}
+
static guint32
-convert_uuids_to_capabilities (const char **strings, int bluez_version)
+convert_uuids_to_capabilities (const char **strings)
{
const char **iter;
guint32 capabilities = 0;
@@ -561,8 +618,7 @@ convert_uuids_to_capabilities (const char **strings, int bluez_version)
if (parts && parts[0]) {
switch (g_ascii_strtoull (parts[0], NULL, 16)) {
case 0x1103:
- if (bluez_version == 4)
- capabilities |= NM_BT_CAPABILITY_DUN;
+ capabilities |= NM_BT_CAPABILITY_DUN;
break;
case 0x1116:
capabilities |= NM_BT_CAPABILITY_NAP;
@@ -583,7 +639,7 @@ _set_property_capabilities (NMBluezDevice *self, const char **uuids)
guint32 uint_val;
NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- uint_val = convert_uuids_to_capabilities (uuids, priv->bluez_version);
+ uint_val = convert_uuids_to_capabilities (uuids);
if (priv->capabilities != uint_val) {
if (priv->capabilities) {
/* changing (relevant) capabilities is not supported and ignored -- except setting initially */
@@ -735,6 +791,10 @@ adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
if (v)
g_variant_unref (v);
+ v = g_dbus_proxy_get_cached_property (priv->adapter5, "Address");
+ if (VARIANT_IS_OF_TYPE_STRING (v))
+ set_adapter_address (self, g_variant_get_string (v, NULL));
+
priv->initialized = TRUE;
g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
@@ -940,7 +1000,10 @@ on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
/********************************************************************/
NMBluezDevice *
-nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez_version)
+nm_bluez_device_new (const char *path,
+ const char *adapter_address,
+ NMConnectionProvider *provider,
+ int bluez_version)
{
NMBluezDevice *self;
NMBluezDevicePrivate *priv;
@@ -961,8 +1024,10 @@ nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez
priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
priv->bluez_version = bluez_version;
-
priv->provider = provider;
+ g_return_val_if_fail (bluez_version == 5 || (bluez_version == 4 && adapter_address), NULL);
+ if (adapter_address)
+ set_adapter_address (self, adapter_address);
g_signal_connect (priv->provider,
NM_CP_SIGNAL_CONNECTION_ADDED,
@@ -1026,6 +1091,11 @@ dispose (GObject *object)
priv->pan_connection = NULL;
}
+ if (priv->b5_dun_context) {
+ nm_bluez5_dun_free (priv->b5_dun_context);
+ priv->b5_dun_context = NULL;
+ }
+
g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_added, self);
g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_removed, self);
g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_updated, self);
@@ -1054,9 +1124,10 @@ finalize (GObject *object)
nm_log_dbg (LOGD_BT, "bluez[%s]: finalize NMBluezDevice", priv->path);
g_free (priv->path);
+ g_free (priv->adapter_address);
g_free (priv->address);
g_free (priv->name);
- g_free (priv->bt_iface);
+ g_free (priv->b4_iface);
if (priv->proxy)
g_signal_handlers_disconnect_by_data (priv->proxy, object);
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
index 6b8dc97a75..92d99fa75a 100644
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ b/src/devices/bluetooth/nm-bluez-device.h
@@ -62,7 +62,10 @@ typedef struct {
GType nm_bluez_device_get_type (void);
-NMBluezDevice *nm_bluez_device_new (const char *path, NMConnectionProvider *provider, int bluez_version);
+NMBluezDevice *nm_bluez_device_new (const char *path,
+ const char *adapter_address,
+ NMConnectionProvider *provider,
+ int bluez_version);
const char *nm_bluez_device_get_path (NMBluezDevice *self);
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
index 7a2cdd6c05..dd3304eab5 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -162,7 +162,7 @@ device_created (DBusGProxy *proxy, const char *path, gpointer user_data)
NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
NMBluezDevice *device;
- device = nm_bluez_device_new (path, priv->provider, 4);
+ device = nm_bluez_device_new (path, priv->address, priv->provider, 4);
g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
new file mode 100644
index 0000000000..dcd3d73b9d
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -0,0 +1,409 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <sys/socket.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <net/ethernet.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "nm-bluez5-dun.h"
+#include "nm-bt-error.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+
+typedef struct _NMBluez5DunContext {
+ bdaddr_t src;
+ bdaddr_t dst;
+ char *src_str;
+ char *dst_str;
+ int rfcomm_channel;
+ int rfcomm_fd;
+ int rfcomm_tty_fd;
+ int rfcomm_id;
+ NMBluez5DunFunc callback;
+ gpointer user_data;
+ sdp_session_t *sdp_session;
+ guint sdp_watch_id;
+} NMBluez5DunContext;
+
+static void
+dun_connect (NMBluez5DunContext *context)
+{
+ struct sockaddr_rc sa;
+ int devid, try = 30;
+ char tty[100];
+ const int ttylen = sizeof (tty) - 1;
+ GError *error = NULL;
+
+ struct rfcomm_dev_req req = {
+ .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
+ .dev_id = -1,
+ .channel = context->rfcomm_channel
+ };
+
+ context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (context->rfcomm_fd < 0) {
+ int errsv = errno;
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Failed to create RFCOMM socket: (%d) %s",
+ errsv, strerror (errsv));
+ goto done;
+ }
+
+ /* Connect to the remote device */
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = 0;
+ memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
+ if (bind (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof(sa))) {
+ int errsv = errno;
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Failed to bind socket: (%d) %s",
+ errsv, strerror (errsv));
+ goto done;
+ }
+
+ sa.rc_channel = context->rfcomm_channel;
+ memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
+ if (connect (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof (sa)) ) {
+ int errsv = errno;
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Failed to connect to remote device: (%d) %s",
+ errsv, strerror (errsv));
+ goto done;
+ }
+
+ nm_log_dbg (LOGD_BT, "(%s): connected to %s on channel %d",
+ context->src_str, context->dst_str, context->rfcomm_channel);
+
+ /* Create an RFCOMM kernel device for the DUN channel */
+ memcpy (&req.src, &context->src, ETH_ALEN);
+ memcpy (&req.dst, &context->dst, ETH_ALEN);
+ devid = ioctl (context->rfcomm_fd, RFCOMMCREATEDEV, &req);
+ if (devid < 0) {
+ int errsv = errno;
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Failed to create rfcomm device: (%d) %s",
+ errsv, strerror (errsv));
+ goto done;
+ }
+ context->rfcomm_id = devid;
+
+ snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
+ while ((context->rfcomm_tty_fd = open (tty, O_RDONLY | O_NOCTTY)) < 0 && try--) {
+ if (try) {
+ g_usleep (100 * 1000);
+ continue;
+ }
+
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Failed to find rfcomm device: %s",
+ tty);
+ break;
+ }
+
+done:
+ context->callback (context, tty, error, context->user_data);
+}
+
+static void
+sdp_search_cleanup (NMBluez5DunContext *context)
+{
+ if (context->sdp_session) {
+ sdp_close (context->sdp_session);
+ context->sdp_session = NULL;
+ }
+
+ if (context->sdp_watch_id) {
+ g_source_remove (context->sdp_watch_id);
+ context->sdp_watch_id = 0;
+ }
+}
+
+static void
+sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ int scanned, seqlen = 0, bytesleft = size;
+ uint8_t dataType;
+ int channel = -1;
+
+ nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished with type=%d status=%d",
+ context->src_str, context->dst_str, status, type);
+
+ /* SDP response received */
+ if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+ GError *error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Did not get a Service Discovery response");
+ context->callback (context, NULL, error, context->user_data);
+ goto done;
+ }
+
+ scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
+
+ nm_log_dbg (LOGD_BT, "(%s -> %s): SDP sequence type scanned=%d length=%d",
+ context->src_str, context->dst_str, scanned, seqlen);
+
+ scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
+ if (!scanned || !seqlen) {
+ /* Short read or unknown sequence type */
+ GError *error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Improper Service Discovery response");
+ context->callback (context, NULL, error, context->user_data);
+ goto done;
+ }
+
+ rsp += scanned;
+ bytesleft -= scanned;
+ do {
+ sdp_record_t *rec;
+ int recsize = 0;
+ sdp_list_t *protos;
+
+ rec = sdp_extract_pdu (rsp, bytesleft, &recsize);
+ if (!rec)
+ break;
+
+ if (!recsize) {
+ sdp_record_free (rec);
+ break;
+ }
+
+ if (sdp_get_access_protos (rec, &protos) == 0) {
+ /* Extract the DUN channel number */
+ channel = sdp_get_proto_port (protos, RFCOMM_UUID);
+ sdp_list_free (protos, NULL);
+
+ nm_log_dbg (LOGD_BT, "(%s -> %s): SDP channel=%d",
+ context->src_str, context->dst_str, channel);
+ }
+ sdp_record_free (rec);
+
+ scanned += recsize;
+ rsp += recsize;
+ bytesleft -= recsize;
+ } while ((scanned < (ssize_t) size) && (bytesleft > 0) && (channel < 0));
+
+done:
+ if (channel != -1) {
+ context->rfcomm_channel = channel;
+ dun_connect (context);
+ }
+
+ sdp_search_cleanup (context);
+}
+
+static gboolean
+sdp_search_process_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+
+ nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search progressed with condition=%d",
+ context->src_str, context->dst_str, condition);
+
+ if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ GError *error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Service Discovery interrupted");
+ context->callback (context, NULL, error, context->user_data);
+ sdp_search_cleanup (context);
+ return FALSE;
+ }
+
+ if (sdp_process (context->sdp_session) < 0) {
+ nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished",
+ context->src_str, context->dst_str);
+
+ /* Search finished successfully. */
+ return FALSE;
+ }
+
+ /* Search progressed successfully. */
+ return TRUE;
+}
+
+static gboolean
+sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ sdp_list_t *search, *attrs;
+ uuid_t svclass;
+ uint16_t attr;
+ int fd, err, fd_err = 0;
+ socklen_t len = sizeof (fd_err);
+ GError *error = NULL;
+
+ context->sdp_watch_id = 0;
+
+ fd = g_io_channel_unix_get_fd (channel);
+ if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &fd_err, &len) < 0) {
+ nm_log_dbg (LOGD_BT, "(%s -> %s): getsockopt error=%d",
+ context->src_str, context->dst_str, errno);
+ err = errno;
+ } else {
+ nm_log_dbg (LOGD_BT, "(%s -> %s): SO_ERROR error=%d",
+ context->src_str, context->dst_str, fd_err);
+ err = fd_err;
+ }
+
+ if (err != 0) {
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Error on Service Discovery socket: (%d) %s",
+ err, strerror (err));
+ goto done;
+ }
+
+ if (sdp_set_notify (context->sdp_session, sdp_search_completed_cb, context) < 0) {
+ /* Should not be reached, only can fail if we passed bad sdp_session. */
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Could not request Service Discovery notification");
+ goto done;
+ }
+
+ sdp_uuid16_create (&svclass, DIALUP_NET_SVCLASS_ID);
+ search = sdp_list_append (NULL, &svclass);
+ attr = SDP_ATTR_PROTO_DESC_LIST;
+ attrs = sdp_list_append (NULL, &attr);
+
+ if (!sdp_service_search_attr_async (context->sdp_session, search, SDP_ATTR_REQ_INDIVIDUAL, attrs)) {
+ /* Set callback responsible for update the internal SDP transaction */
+ context->sdp_watch_id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ sdp_search_process_cb,
+ context);
+ } else {
+ err = sdp_get_error (context->sdp_session);
+ error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Error starting Service Discovery: (%d) %s",
+ err, strerror (err));
+ }
+
+ sdp_list_free (attrs, NULL);
+ sdp_list_free (search, NULL);
+
+done:
+ if (error) {
+ context->callback (context, NULL, error, context->user_data);
+ sdp_search_cleanup (context);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+NMBluez5DunContext *
+nm_bluez5_dun_new (const char *adapter,
+ const char *remote)
+
+{
+ NMBluez5DunContext *context;
+
+ context = g_slice_new0 (NMBluez5DunContext);
+ str2ba (adapter, &context->src);
+ str2ba (remote, &context->dst);
+ context->src_str = g_strdup (adapter);
+ context->dst_str = g_strdup (remote);
+ context->rfcomm_channel = -1;
+ context->rfcomm_id = -1;
+ context->rfcomm_fd = -1;
+ return context;
+}
+
+void
+nm_bluez5_dun_connect (NMBluez5DunContext *context,
+ NMBluez5DunFunc callback,
+ gpointer user_data)
+{
+ GIOChannel *channel;
+
+ context->callback = callback;
+ context->user_data = user_data;
+
+ if (context->rfcomm_channel != -1) {
+ nm_log_dbg (LOGD_BT, "(%s): channel number on device %s cached: %d",
+ context->src_str, context->dst_str, context->rfcomm_channel);
+ dun_connect (context);
+ return;
+ }
+
+ nm_log_dbg (LOGD_BT, "(%s): starting channel number discovery for device %s",
+ context->src_str, context->dst_str);
+
+ context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
+ if (!context->sdp_session) {
+ GError *error;
+ int err = sdp_get_error (context->sdp_session);
+
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Failed to connect to the SDP server: (%d) %s",
+ err, strerror (err));
+ context->callback (context, NULL, error, context->user_data);
+ return;
+ }
+
+ channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session));
+ context->sdp_watch_id = g_io_add_watch (channel,
+ G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ sdp_connect_watch,
+ context);
+ g_io_channel_unref (channel);
+}
+
+/* Only clean up connection-related stuff to allow reconnect */
+void
+nm_bluez5_dun_cleanup (NMBluez5DunContext *context)
+{
+ g_return_if_fail (context != NULL);
+
+ sdp_search_cleanup (context);
+
+ if (context->rfcomm_fd >= 0) {
+ if (context->rfcomm_id >= 0) {
+ struct rfcomm_dev_req req = { 0 };
+
+ req.dev_id = context->rfcomm_id;
+ ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req);
+ context->rfcomm_id = -1;
+ }
+ close (context->rfcomm_fd);
+ context->rfcomm_fd = -1;
+ }
+
+ close (context->rfcomm_tty_fd);
+ context->rfcomm_tty_fd = -1;
+}
+
+void
+nm_bluez5_dun_free (NMBluez5DunContext *context)
+{
+ g_return_if_fail (context != NULL);
+
+ nm_bluez5_dun_cleanup (context);
+ g_clear_pointer (&context->src_str, g_free);
+ g_clear_pointer (&context->dst_str, g_free);
+ g_slice_free (NMBluez5DunContext, context);
+}
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
new file mode 100644
index 0000000000..7e25972551
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef _NM_BLUEZ5_UTILS_H_
+#define _NM_BLUEZ5_UTILS_H_
+
+#include <glib.h>
+#include <gio/gio.h>
+
+typedef struct _NMBluez5DunContext NMBluez5DunContext;
+
+typedef void (*NMBluez5DunFunc) (NMBluez5DunContext *context,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data);
+
+NMBluez5DunContext *nm_bluez5_dun_new (const char *adapter,
+ const char *remote);
+
+void nm_bluez5_dun_connect (NMBluez5DunContext *context,
+ NMBluez5DunFunc callback, gpointer user_data);
+
+/* Clean up connection resources */
+void nm_bluez5_dun_cleanup (NMBluez5DunContext *context);
+
+/* Clean up and dispose all resources */
+void nm_bluez5_dun_free (NMBluez5DunContext *context);
+
+#endif /* _NM_BLUEZ5_UTILS_H_ */
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 63006b3ab8..8653c285ea 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -143,7 +143,7 @@ device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
NMBluezDevice *device;
- device = nm_bluez_device_new (path, priv->provider, 5);
+ device = nm_bluez_device_new (path, NULL, priv->provider, 5);
g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
diff --git a/src/devices/bluetooth/nm-bt-error.c b/src/devices/bluetooth/nm-bt-error.c
new file mode 100644
index 0000000000..d014a107c3
--- /dev/null
+++ b/src/devices/bluetooth/nm-bt-error.c
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include "nm-bt-error.h"
+
+GQuark
+nm_bt_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("nm-bt-error");
+ return quark;
+}
+
+
diff --git a/src/devices/bluetooth/nm-bt-error.h b/src/devices/bluetooth/nm-bt-error.h
new file mode 100644
index 0000000000..fa3a95705e
--- /dev/null
+++ b/src/devices/bluetooth/nm-bt-error.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#ifndef _NM_BLUEZ5_ERROR_H_
+#define _NM_BLUEZ5_ERROR_H_
+
+typedef enum {
+ NM_BT_ERROR_CONNECTION_NOT_BT = 0, /*< nick=ConnectionNotBt >*/
+ NM_BT_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/
+ NM_BT_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
+ NM_BT_ERROR_DUN_CONNECT_FAILED, /*< nick=DunConnectFailed >*/
+} NMBtError;
+
+#define NM_BT_ERROR (nm_bt_error_quark ())
+GQuark nm_bt_error_quark (void);
+
+#endif /* _NM_BT_ERROR_H_ */
+
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index e08064b120..18b7ab1bd8 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -44,6 +44,8 @@
#include "NetworkManagerUtils.h"
#include "nm-bt-enum-types.h"
#include "nm-utils.h"
+#include "nm-bt-error.h"
+#include "nm-bt-enum-types.h"
#define MM_DBUS_SERVICE "org.freedesktop.ModemManager1"
@@ -94,17 +96,6 @@ enum {
static guint signals[LAST_SIGNAL] = { 0 };
-#define NM_BT_ERROR (nm_bt_error_quark ())
-
-static GQuark
-nm_bt_error_quark (void)
-{
- static GQuark quark = 0;
- if (!quark)
- quark = g_quark_from_static_string ("nm-bt-error");
- return quark;
-}
-
guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
{
g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE);
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index 6e6ae546ca..c1e95d915f 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -34,12 +34,6 @@ G_BEGIN_DECLS
#define NM_IS_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BT))
#define NM_DEVICE_BT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BT, NMDeviceBtClass))
-typedef enum {
- NM_BT_ERROR_CONNECTION_NOT_BT = 0, /*< nick=ConnectionNotBt >*/
- NM_BT_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/
- NM_BT_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
-} NMBtError;
-
#define NM_DEVICE_BT_NAME "name"
#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
#define NM_DEVICE_BT_DEVICE "bt-device"