Diff between 4be993ccf130ca100f58f6fd4bbb19bc4d7c1c6e and fee1eda5a5f846b8323ec315653bfc3f2d4cc2ae

Changed Files

File Additions Deletions Status
doc/device-api.txt +12 -0 modified
src/device.c +78 -1 modified

Full Patch

diff --git a/doc/device-api.txt b/doc/device-api.txt
index 1f0dc96..cd96ef5 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -56,6 +56,18 @@ Methods		dict GetProperties()
 					 org.bluez.Error.Failed
 					 org.bluez.Error.NotAuthorized
 
+		void Connect()
+
+			This is a generic method to connect any profiles
+			the remote device supports that can be connected
+			to and have been flagged as auto-connectable on
+			our side.
+
+			Possible errors: org.bluez.Error.NotReady
+					 org.bluez.Error.Failed
+					 org.bluez.Error.InProgress
+					 org.bluez.Error.AlreadyConnected
+
 		void Disconnect()
 
 			This method disconnects a specific remote device by
diff --git a/src/device.c b/src/device.c
index aa3a607..a7e363b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -135,7 +135,7 @@ struct btd_device {
 	GSList		*services;		/* Primary services path */
 	GSList		*primaries;		/* List of primary services */
 	GSList		*profiles;		/* Probed profiles */
-	GSList		*conns;			/* Connected profiles */
+	GSList		*pending;		/* Pending profiles */
 	GSList		*watches;		/* List of disconnect_data */
 	gboolean	temporary;
 	struct agent	*agent;
@@ -145,6 +145,7 @@ struct btd_device {
 	struct bonding_req *bonding;
 	struct authentication_req *authr;	/* authentication request */
 	GSList		*disconnects;		/* disconnects message */
+	DBusMessage	*connect;		/* connect message */
 	GAttrib		*attrib;
 	GSList		*attios;
 	GSList		*attios_offline;
@@ -152,6 +153,7 @@ struct btd_device {
 	guint		auto_id;		/* Auto connect source id */
 
 	gboolean	connected;
+	gboolean	profiles_connected;	/* Profile level connected */
 
 	sdp_list_t	*tmp_records;
 
@@ -867,6 +869,79 @@ static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
 	return NULL;
 }
 
+static int connect_next(struct btd_device *dev, btd_profile_cb cb)
+{
+	struct btd_profile *profile;
+	int err = -ENOENT;
+
+	while (dev->pending) {
+		int err;
+
+		profile = dev->pending->data;
+
+		err = profile->connect(dev, profile, cb);
+		if (err == 0)
+			return 0;
+
+		error("Failed to connect %s", profile->name);
+		dev->pending = g_slist_remove(dev->pending, profile);
+	}
+
+	return err;
+}
+
+static void dev_profile_connected(struct btd_profile *profile,
+					struct btd_device *dev, int err)
+{
+	dev->pending = g_slist_remove(dev->pending, profile);
+
+	if (connect_next(dev, dev_profile_connected) == 0)
+		return;
+
+	dev->profiles_connected = TRUE;
+
+	if (!dev->connect)
+		return;
+
+	g_dbus_send_reply(btd_get_dbus_connection(), dev->connect,
+							DBUS_TYPE_INVALID);
+	dbus_message_unref(dev->connect);
+	dev->connect = NULL;
+}
+
+static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
+							void *user_data)
+{
+	struct btd_device *dev = user_data;
+	struct btd_profile *p;
+	GSList *l;
+	int err;
+
+	if (dev->profiles_connected)
+		return btd_error_already_connected(msg);
+
+	if (dev->pending || dev->connect)
+		return btd_error_in_progress(msg);
+
+	for (l = dev->profiles; l != NULL; l = g_slist_next(l)) {
+		p = l->data;
+
+		if (p->auto_connect)
+			dev->pending = g_slist_append(dev->pending, p);
+	}
+
+	if (!dev->pending)
+		return btd_error_not_available(msg);
+
+	err = connect_next(dev, dev_profile_connected);
+	if (err < 0)
+		return btd_error_failed(msg, strerror(-err));
+
+	dev->connect = dbus_message_ref(msg);
+
+	return NULL;
+}
+
 static const GDBusMethodTable device_methods[] = {
 	{ GDBUS_METHOD("GetProperties",
 				NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -880,6 +955,7 @@ static const GDBusMethodTable device_methods[] = {
 			discover_services) },
 	{ GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
 	{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, disconnect) },
+	{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
 	{ }
 };
 
@@ -921,6 +997,7 @@ void device_remove_connection(struct btd_device *device)
 	}
 
 	device->connected = FALSE;
+	device->profiles_connected = FALSE;
 
 	if (device->disconn_timer > 0) {
 		g_source_remove(device->disconn_timer);