Bastien Nocera bd38d2
From 6c73cb253c7a3bdf9376b32c0f2d6d9e01b89653 Mon Sep 17 00:00:00 2001
Bastien Nocera 22e68e
From: Bastien Nocera <hadess@hadess.net>
Bastien Nocera ad8c9b
Date: Tue, 1 Sep 2009 17:32:48 +0100
Bastien Nocera c0dc6a
Subject: [PATCH] Add sixaxis cable-pairing plugin
Bastien Nocera 22e68e
Bastien Nocera d02fae
Implement the old "sixpair" using libudev and libusb-1.0.
Bastien Nocera 22e68e
Bastien Nocera 22e68e
When a Sixaxis device is plugged in, events are filtered, and
Bastien Nocera 22e68e
the device is selected, poked around to set the default Bluetooth
Bastien Nocera 22e68e
address, and added to the database of the current default adapter.
Bastien Nocera 22e68e
---
Bastien Nocera d02fae
 Makefile.am     |    9 +-
Bastien Nocera ab2479
 acinclude.m4    |   16 +++
Bastien Nocera ab2479
 configure.ac    |    1 +
Bastien Nocera 9befcf
 plugins/cable.c |  382 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
Bastien Nocera 9befcf
 src/adapter.c   |   19 +++
Bastien Nocera 9befcf
 src/adapter.h   |    3 +
Bastien Nocera 978b40
 6 files changed, 428 insertions(+), 2 deletions(-)
Bastien Nocera 22e68e
 create mode 100644 plugins/cable.c
Bastien Nocera 22e68e
Bastien Nocera ad8c9b
diff --git a/Makefile.am b/Makefile.am
Bastien Nocera bd38d2
index fab05eb..a21bd7d 100644
Bastien Nocera ad8c9b
--- a/Makefile.am
Bastien Nocera ad8c9b
+++ b/Makefile.am
Bastien Nocera bd38d2
@@ -208,6 +208,11 @@ builtin_sources += health/hdp_main.c health/hdp_types.h \
Bastien Nocera 87f537
 			health/hdp_util.h health/hdp_util.c
Bastien Nocera ad8c9b
 endif
Bastien Nocera ad8c9b
 
Bastien Nocera ad8c9b
+if CABLE
Bastien Nocera d02fae
+builtin_modules += cable
Bastien Nocera d02fae
+builtin_sources += plugins/cable.c
Bastien Nocera ad8c9b
+endif
Bastien Nocera ad8c9b
+
Bastien Nocera 43188c
 builtin_modules += hciops mgmtops
Bastien Nocera 43188c
 builtin_sources += plugins/hciops.c plugins/mgmtops.c
Bastien Nocera ad8c9b
 
Bastien Nocera bd38d2
@@ -262,7 +267,7 @@ src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
Bastien Nocera c0dc6a
 			src/event.h src/event.c \
Bastien Nocera c0dc6a
 			src/oob.h src/oob.c src/eir.h src/eir.c
Bastien Nocera d02fae
 src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
Bastien Nocera fb14d2
-							@CAPNG_LIBS@ -ldl -lrt
Bastien Nocera fb14d2
+							@CAPNG_LIBS@ @CABLE_LIBS@ -ldl -lrt
Bastien Nocera d02fae
 src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
Bastien Nocera 7be5a3
 				-Wl,--version-script=$(srcdir)/src/bluetooth.ver
Bastien Nocera 7be5a3
 
Bastien Nocera bd38d2
@@ -377,7 +382,7 @@ EXTRA_DIST += doc/manager-api.txt \
Bastien Nocera d02fae
 
Bastien Nocera d02fae
 AM_YFLAGS = -d
Bastien Nocera d02fae
 
Bastien Nocera d02fae
-AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
Bastien Nocera d02fae
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ @CABLE_CFLAGS@ \
Bastien Nocera d02fae
 		-DBLUETOOTH_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
Bastien Nocera d02fae
 
Bastien Nocera d02fae
 INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
Bastien Nocera ad8c9b
diff --git a/acinclude.m4 b/acinclude.m4
Bastien Nocera bd38d2
index af97cce..9d38789 100644
Bastien Nocera ad8c9b
--- a/acinclude.m4
Bastien Nocera ad8c9b
+++ b/acinclude.m4
Bastien Nocera bd38d2
@@ -155,6 +155,12 @@ AC_DEFUN([AC_PATH_UDEV], [
Bastien Nocera c0dc6a
 	AC_SUBST(UDEV_LIBS)
Bastien Nocera ad8c9b
 ])
Bastien Nocera ad8c9b
 
Bastien Nocera ad8c9b
+AC_DEFUN([AC_PATH_CABLE], [
Bastien Nocera d02fae
+	PKG_CHECK_MODULES(CABLE, libudev libusb-1.0, cable_found=yes, cable_found=no)
Bastien Nocera ad8c9b
+	AC_SUBST(CABLE_CFLAGS)
Bastien Nocera ad8c9b
+	AC_SUBST(CABLE_LIBS)
Bastien Nocera ad8c9b
+])
Bastien Nocera ad8c9b
+
Bastien Nocera 110fc2
 AC_DEFUN([AC_PATH_SNDFILE], [
Bastien Nocera 110fc2
 	PKG_CHECK_MODULES(SNDFILE, sndfile, sndfile_found=yes, sndfile_found=no)
Bastien Nocera 110fc2
 	AC_SUBST(SNDFILE_CFLAGS)
Bastien Nocera bd38d2
@@ -186,6 +192,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera 110fc2
 	sndfile_enable=${sndfile_found}
Bastien Nocera ab2479
 	hal_enable=no
Bastien Nocera ad8c9b
 	usb_enable=${usb_found}
Bastien Nocera ad8c9b
+	cable_enable=${cable_found}
Bastien Nocera ad8c9b
 	alsa_enable=${alsa_found}
Bastien Nocera ad8c9b
 	gstreamer_enable=${gstreamer_found}
Bastien Nocera ad8c9b
 	audio_enable=yes
Bastien Nocera bd38d2
@@ -279,6 +286,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera ad8c9b
 		usb_enable=${enableval}
Bastien Nocera ad8c9b
 	])
Bastien Nocera ad8c9b
 
Bastien Nocera ad8c9b
+	AC_ARG_ENABLE(cable, AC_HELP_STRING([--enable-cable], [enable DeviceKit support]), [
Bastien Nocera ad8c9b
+		cable_enable=${enableval}
Bastien Nocera ad8c9b
+	])
Bastien Nocera ad8c9b
+
Bastien Nocera 110fc2
 	AC_ARG_ENABLE(tracer, AC_HELP_STRING([--enable-tracer], [install Tracing daemon]), [
Bastien Nocera 110fc2
 		tracer_enable=${enableval}
Bastien Nocera ad8c9b
 	])
Bastien Nocera bd38d2
@@ -370,6 +381,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera 110fc2
 		AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.])
Bastien Nocera ad8c9b
 	fi
Bastien Nocera ad8c9b
 
Bastien Nocera ad8c9b
+	if (test "${cable_enable}" = "yes" && test "${cable_found}" = "yes"); then
Bastien Nocera ad8c9b
+		AC_DEFINE(HAVE_CABLE, 1, [Define to 1 if you have libcable.])
Bastien Nocera ad8c9b
+	fi
Bastien Nocera ad8c9b
+
Bastien Nocera ad8c9b
 	AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes")
Bastien Nocera ad8c9b
 	AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes")
Bastien Nocera dac328
 	AM_CONDITIONAL(SBC, test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes" ||
Bastien Nocera bd38d2
@@ -403,4 +418,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera bd38d2
 	AM_CONDITIONAL(DATAFILES, test "${datafiles_enable}" = "yes")
Bastien Nocera 775e26
 	AM_CONDITIONAL(MAEMO6PLUGIN, test "${maemo6_enable}" = "yes")
Bastien Nocera c0dc6a
 	AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
Bastien Nocera ad8c9b
+	AM_CONDITIONAL(CABLE, test "${cable_enable}" = "yes" && test "${cable_found}" = "yes")
Bastien Nocera ad8c9b
 ])
Bastien Nocera 22e68e
diff --git a/configure.ac b/configure.ac
Bastien Nocera bd38d2
index 8f54f60..6b0b024 100644
Bastien Nocera 22e68e
--- a/configure.ac
Bastien Nocera 22e68e
+++ b/configure.ac
Bastien Nocera c0dc6a
@@ -46,6 +46,7 @@ AC_PATH_GSTREAMER
Bastien Nocera 22e68e
 AC_PATH_USB
Bastien Nocera c0dc6a
 AC_PATH_UDEV
Bastien Nocera 22e68e
 AC_PATH_SNDFILE
Bastien Nocera c0dc6a
+AC_PATH_CABLE
Bastien Nocera b36fd1
 AC_PATH_OUI
Bastien Nocera c0dc6a
 AC_PATH_READLINE
Bastien Nocera 110fc2
 
Bastien Nocera 22e68e
diff --git a/plugins/cable.c b/plugins/cable.c
Bastien Nocera 22e68e
new file mode 100644
Bastien Nocera 9befcf
index 0000000..e8cff76
Bastien Nocera 22e68e
--- /dev/null
Bastien Nocera 22e68e
+++ b/plugins/cable.c
Bastien Nocera 9befcf
@@ -0,0 +1,382 @@
Bastien Nocera 22e68e
+/*
Bastien Nocera 22e68e
+ *
Bastien Nocera 22e68e
+ *  BlueZ - Bluetooth protocol stack for Linux
Bastien Nocera 22e68e
+ *
Bastien Nocera 22e68e
+ *  Copyright (C) 2009  Bastien Nocera <hadess@hadess.net>
Bastien Nocera 22e68e
+ *
Bastien Nocera 22e68e
+ *
Bastien Nocera 22e68e
+ *  This program is free software; you can redistribute it and/or modify
Bastien Nocera 22e68e
+ *  it under the terms of the GNU General Public License as published by
Bastien Nocera 22e68e
+ *  the Free Software Foundation; either version 2 of the License, or
Bastien Nocera 22e68e
+ *  (at your option) any later version.
Bastien Nocera 22e68e
+ *
Bastien Nocera 22e68e
+ *  This program is distributed in the hope that it will be useful,
Bastien Nocera 22e68e
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Bastien Nocera 22e68e
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Bastien Nocera 22e68e
+ *  GNU General Public License for more details.
Bastien Nocera 22e68e
+ *
Bastien Nocera 22e68e
+ *  You should have received a copy of the GNU General Public License
Bastien Nocera 22e68e
+ *  along with this program; if not, write to the Free Software
Bastien Nocera 22e68e
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Bastien Nocera 22e68e
+ *
Bastien Nocera 22e68e
+ */
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+#ifdef HAVE_CONFIG_H
Bastien Nocera 22e68e
+#include <config.h>
Bastien Nocera 22e68e
+#endif
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+#include <glib.h>
Bastien Nocera d02fae
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE 1
Bastien Nocera d02fae
+#include <libudev.h>
Bastien Nocera 22e68e
+#include <dbus/dbus.h>
Bastien Nocera 22e68e
+#include <bluetooth/bluetooth.h>
Bastien Nocera 22e68e
+#include <bluetooth/sdp.h>
Bastien Nocera 22e68e
+#include <libusb.h>
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+#include "plugin.h"
Bastien Nocera a91986
+#include "log.h"
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+#include "adapter.h"
Bastien Nocera 9befcf
+#include "manager.h"
Bastien Nocera 22e68e
+#include "device.h"
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+#include "storage.h"
Bastien Nocera 22e68e
+#include "sdp_lib.h"
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+/* Vendor and product ID for the Sixaxis PS3 controller */
Bastien Nocera 22e68e
+#define VENDOR 0x054c
Bastien Nocera 22e68e
+#define PRODUCT 0x0268
Bastien Nocera 22e68e
+#define SIXAXIS_PNP_RECORD "3601920900000A000100000900013503191124090004350D35061901000900113503190011090006350909656E09006A0901000900093508350619112409010009000D350F350D350619010009001335031900110901002513576972656C65737320436F6E74726F6C6C65720901012513576972656C65737320436F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E7465727461696E6D656E740902000901000902010901000902020800090203082109020428010902052801090206359A35980822259405010904A101A102850175089501150026FF00810375019513150025013500450105091901291381027501950D0600FF8103150026FF0005010901A10075089504350046FF0009300931093209358102C0050175089527090181027508953009019102750895300901B102C0A1028502750895300901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C0090207350835060904090901000902082800090209280109020A280109020B09010009020C093E8009020D280009020E2800"
Bastien Nocera 22e68e
+#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
Bastien Nocera 22e68e
+
Bastien Nocera 48f733
+static struct btd_device *create_cable_association(DBusConnection *conn,
Bastien Nocera 48f733
+						    struct btd_adapter *adapter,
Bastien Nocera 48f733
+						    const char *name,
Bastien Nocera 48f733
+						    const char *address,
Bastien Nocera 48f733
+						    guint32 vendor_id,
Bastien Nocera 48f733
+						    guint32 product_id,
Bastien Nocera 48f733
+						    const char *pnp_record)
Bastien Nocera 22e68e
+{
Bastien Nocera 22e68e
+	sdp_record_t *rec;
Bastien Nocera 22e68e
+	struct btd_device *device;
Bastien Nocera 22e68e
+	bdaddr_t src, dst;
Bastien Nocera 22e68e
+	char srcaddr[18];
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	device = adapter_find_device(adapter, address);
Bastien Nocera 9befcf
+	if (device == NULL) {
Bastien Nocera 9befcf
+		device = device_create(conn, adapter, address, DEVICE_TYPE_UNKNOWN);
Bastien Nocera 9befcf
+		if (device != NULL)
Bastien Nocera 9befcf
+			adapter_create_device_for_device(conn, adapter, device);
Bastien Nocera 9befcf
+	}
Bastien Nocera 22e68e
+	if (device != NULL) {
Bastien Nocera 22e68e
+		device_set_temporary(device, FALSE);
Bastien Nocera 22e68e
+		device_set_name(device, name);
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	str2ba(address, &dst);
Bastien Nocera 22e68e
+	adapter_get_address(adapter, &src;;
Bastien Nocera 22e68e
+	ba2str(&src, srcaddr);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	write_device_name(&dst, &src, (char *) name);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	/* Store the device's SDP record */
Bastien Nocera 22e68e
+	rec = record_from_string(pnp_record);
Bastien Nocera 22e68e
+	store_record(srcaddr, address, rec);
Bastien Nocera 22e68e
+	sdp_record_free(rec);
Bastien Nocera 22e68e
+	/* Set the device id */
Bastien Nocera 22e68e
+	store_device_id(srcaddr, address, 0xffff, vendor_id, product_id, 0);
Bastien Nocera 22e68e
+	/* Don't write a profile, it will be updated when the device connects */
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	write_trust(srcaddr, address, "[all]", TRUE);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	return device;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+static char *get_bdaddr(libusb_device_handle *devh, int itfnum)
Bastien Nocera 22e68e
+{
Bastien Nocera 22e68e
+	unsigned char msg[17];
Bastien Nocera 22e68e
+	char *address;
Bastien Nocera 22e68e
+	int res;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	res = libusb_control_transfer(devh,
Bastien Nocera 22e68e
+				      LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
Bastien Nocera 22e68e
+				      0x01, 0x03f2, itfnum,
Bastien Nocera 22e68e
+				      (void*) msg, sizeof(msg),
Bastien Nocera 22e68e
+				      5000);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (res < 0) {
Bastien Nocera f1feb4
+		DBG("Getting the device Bluetooth address failed");
Bastien Nocera 22e68e
+		return NULL;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	address = g_strdup_printf("%02X:%02X:%02X:%02X:%02X:%02X",
Bastien Nocera 22e68e
+				  msg[4], msg[5], msg[6], msg[7], msg[8], msg[9]);
Bastien Nocera 22e68e
+
Bastien Nocera f1feb4
+	DBG("Device Bluetooth address: %s\n", address);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	return address;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+static gboolean set_master_bdaddr(libusb_device_handle *devh, int itfnum, char *host)
Bastien Nocera 22e68e
+{
Bastien Nocera 22e68e
+	unsigned char msg[8];
Bastien Nocera 22e68e
+	int mac[6];
Bastien Nocera 22e68e
+	int res;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (sscanf(host, "%X:%X:%X:%X:%X:%X",
Bastien Nocera 22e68e
+		   &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6) {
Bastien Nocera 22e68e
+		return FALSE;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	msg[0] = 0x01;
Bastien Nocera 22e68e
+	msg[1] = 0x00;
Bastien Nocera 22e68e
+	msg[2] = mac[0];
Bastien Nocera 22e68e
+	msg[3] = mac[1];
Bastien Nocera 22e68e
+	msg[4] = mac[2];
Bastien Nocera 22e68e
+	msg[5] = mac[3];
Bastien Nocera 22e68e
+	msg[6] = mac[4];
Bastien Nocera 22e68e
+	msg[7] = mac[5];
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	res = libusb_control_transfer(devh,
Bastien Nocera 22e68e
+				      LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
Bastien Nocera 22e68e
+				      0x09, 0x03f5, itfnum,
Bastien Nocera 22e68e
+				      (void*) msg, sizeof(msg),
Bastien Nocera 22e68e
+				      5000);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (res < 0) {
Bastien Nocera f1feb4
+		DBG("Setting the master Bluetooth address failed");
Bastien Nocera 22e68e
+		return FALSE;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	return TRUE;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+static void handle_usb_device(struct btd_adapter *adapter,
Bastien Nocera 22e68e
+			      libusb_device *dev,
Bastien Nocera 22e68e
+			      struct libusb_config_descriptor *cfg,
Bastien Nocera 22e68e
+			      int itfnum,
Bastien Nocera 22e68e
+			      const struct libusb_interface_descriptor *alt)
Bastien Nocera 22e68e
+{
Bastien Nocera 22e68e
+	DBusConnection *conn;
Bastien Nocera 22e68e
+	libusb_device_handle *devh;
Bastien Nocera 22e68e
+	char *device_bdaddr;
Bastien Nocera 22e68e
+	char adapter_bdaddr[18];
Bastien Nocera 22e68e
+	struct btd_device *device;
Bastien Nocera 22e68e
+	bdaddr_t dst;
Bastien Nocera 22e68e
+
Bastien Nocera 110fc2
+	device_bdaddr = NULL;
Bastien Nocera 22e68e
+	conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
Bastien Nocera 22e68e
+	if (conn == NULL) {
Bastien Nocera f1feb4
+		DBG("Failed to get on the bus");
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (libusb_open(dev, &devh) < 0) {
Bastien Nocera f1feb4
+		DBG("Can't open device");
Bastien Nocera 22e68e
+		goto bail;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+	libusb_detach_kernel_driver(devh, itfnum);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (libusb_claim_interface(devh, itfnum) < 0) {
Bastien Nocera f1feb4
+		DBG("Can't claim interface %d", itfnum);
Bastien Nocera 22e68e
+		goto bail;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	device_bdaddr = get_bdaddr(devh, itfnum);
Bastien Nocera 22e68e
+	if (device_bdaddr == NULL) {
Bastien Nocera f1feb4
+		DBG("Failed to get the Bluetooth address from the device");
Bastien Nocera 22e68e
+		goto bail;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	device = create_cable_association(conn,
Bastien Nocera 22e68e
+					  adapter,
Bastien Nocera 22e68e
+					  "PLAYSTATION(R)3 Controller",
Bastien Nocera 22e68e
+					  device_bdaddr,
Bastien Nocera 22e68e
+					  VENDOR, PRODUCT, SIXAXIS_PNP_RECORD);
Bastien Nocera 22e68e
+	btd_device_add_uuid(device, HID_UUID);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	adapter_get_address(adapter, &dst);
Bastien Nocera 22e68e
+	ba2str(&dst, adapter_bdaddr);
Bastien Nocera f1feb4
+	DBG("Adapter bdaddr %s", adapter_bdaddr);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (set_master_bdaddr(devh, itfnum, adapter_bdaddr) == FALSE) {
Bastien Nocera f1feb4
+		DBG("Failed to set the master Bluetooth address");
Bastien Nocera 22e68e
+		goto bail;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+bail:
Bastien Nocera 22e68e
+	dbus_connection_unref(conn);
Bastien Nocera 22e68e
+	g_free(device_bdaddr);
Bastien Nocera 22e68e
+	libusb_release_interface(devh, itfnum);
Bastien Nocera 22e68e
+	/* We ignore errors from the reattach, as there's nothing we
Bastien Nocera 22e68e
+	 * can do about it */
Bastien Nocera 22e68e
+	libusb_attach_kernel_driver(devh, itfnum);
Bastien Nocera 22e68e
+	if (devh != NULL)
Bastien Nocera 22e68e
+		libusb_close(devh);
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+static void handle_device_plug(struct udev_device *udevice)
Bastien Nocera 22e68e
+{
Bastien Nocera 22e68e
+	struct btd_adapter *adapter;
Bastien Nocera 22e68e
+	guint i;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	libusb_device **list, *usbdev;
Bastien Nocera 22e68e
+	ssize_t num_devices;
Bastien Nocera 22e68e
+	struct libusb_device_descriptor desc;
Bastien Nocera 22e68e
+	guint8 j;
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+	if (g_strcmp0(udev_device_get_property_value(udevice, "ID_SERIAL"),
Bastien Nocera d02fae
+		      "Sony_PLAYSTATION_R_3_Controller") != 0)
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+	/* Don't look at events with an associated driver */
Bastien Nocera d02fae
+	if (udev_device_get_property_value(udevice, "ID_USB_DRIVER") != NULL)
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+
Bastien Nocera f1feb4
+	DBG("Found Sixaxis device");
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	/* Look for the default adapter */
Bastien Nocera 9befcf
+	adapter = manager_get_default_adapter();
Bastien Nocera 22e68e
+	if (adapter == NULL)
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	/* Look for the USB device */
Bastien Nocera 22e68e
+	libusb_init(NULL);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	num_devices = libusb_get_device_list(NULL, &list);
Bastien Nocera 22e68e
+	if (num_devices < 0) {
Bastien Nocera f1feb4
+		DBG("libusb_get_device_list failed");
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	usbdev = NULL;
Bastien Nocera 22e68e
+	for (i = 0; i < num_devices; i++) {
Bastien Nocera 22e68e
+		char *path;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+		path = g_strdup_printf("%s/%03d/%03d", "/dev/bus/usb",
Bastien Nocera 22e68e
+				       libusb_get_bus_number(list[i]),
Bastien Nocera 22e68e
+				       libusb_get_device_address(list[i]));
Bastien Nocera d02fae
+		if (g_strcmp0(path, udev_device_get_devnode(udevice)) == 0) {
Bastien Nocera 22e68e
+			g_free(path);
Bastien Nocera 22e68e
+			usbdev = libusb_ref_device(list[i]);
Bastien Nocera 22e68e
+			break;
Bastien Nocera 22e68e
+		}
Bastien Nocera 22e68e
+		g_free(path);
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	libusb_free_device_list(list, TRUE);
Bastien Nocera 22e68e
+	if (usbdev == NULL) {
Bastien Nocera f1feb4
+		DBG("Found a Sixaxis, but couldn't find it via libusb");
Bastien Nocera 22e68e
+		goto out;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (libusb_get_device_descriptor(usbdev, &desc) < 0) {
Bastien Nocera f1feb4
+		DBG("libusb_get_device_descriptor() failed");
Bastien Nocera 22e68e
+		goto out;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	/* Look for the interface number that interests us */
Bastien Nocera 22e68e
+	for (j = 0; j < desc.bNumConfigurations; j++) {
Bastien Nocera 22e68e
+		struct libusb_config_descriptor *config;
Bastien Nocera 22e68e
+		guint8 k;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+		if (libusb_get_config_descriptor(usbdev, j, &config) < 0) {
Bastien Nocera f1feb4
+			DBG("Failed to get config descriptor %d", j);
Bastien Nocera 22e68e
+			continue;
Bastien Nocera 22e68e
+		}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+		for (k = 0; k < config->bNumInterfaces; k++) {
Bastien Nocera 22e68e
+			const struct libusb_interface *itf = &config->interface[k];
Bastien Nocera 22e68e
+			int l;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+			for (l = 0; l < itf->num_altsetting ; l++) {
Bastien Nocera 22e68e
+				struct libusb_interface_descriptor alt;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+				alt = itf->altsetting[l];
Bastien Nocera 22e68e
+				if (alt.bInterfaceClass == 3) {
Bastien Nocera 22e68e
+					handle_usb_device(adapter, usbdev, config, l, &alt;;
Bastien Nocera 22e68e
+				}
Bastien Nocera 22e68e
+			}
Bastien Nocera 22e68e
+		}
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+out:
Bastien Nocera 22e68e
+	if (usbdev != NULL)
Bastien Nocera 22e68e
+		libusb_unref_device(usbdev);
Bastien Nocera 22e68e
+	libusb_exit(NULL);
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+static gboolean device_event_idle(struct udev_device *udevice)
Bastien Nocera 22e68e
+{
Bastien Nocera d02fae
+	handle_device_plug(udevice);
Bastien Nocera d02fae
+	udev_device_unref(udevice);
Bastien Nocera 22e68e
+	return FALSE;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+static struct udev *ctx = NULL;
Bastien Nocera d02fae
+static struct udev_monitor *monitor = NULL;
Bastien Nocera d02fae
+static guint watch_id = 0;
Bastien Nocera d02fae
+
Bastien Nocera d02fae
+static gboolean
Bastien Nocera d02fae
+monitor_event(GIOChannel *source,
Bastien Nocera d02fae
+	      GIOCondition condition,
Bastien Nocera d02fae
+	      gpointer data)
Bastien Nocera 22e68e
+{
Bastien Nocera d02fae
+	struct udev_device *udevice;
Bastien Nocera d02fae
+
Bastien Nocera d02fae
+	udevice = udev_monitor_receive_device(monitor);
Bastien Nocera d02fae
+	if (udevice == NULL)
Bastien Nocera d02fae
+		goto out;
Bastien Nocera d02fae
+	if (g_strcmp0(udev_device_get_action(udevice), "add") != 0)
Bastien Nocera d02fae
+		goto out;
Bastien Nocera d02fae
+
Bastien Nocera d02fae
+	g_timeout_add_seconds(1, (GSourceFunc) device_event_idle, udevice);
Bastien Nocera d02fae
+
Bastien Nocera d02fae
+out:
Bastien Nocera d02fae
+	return TRUE;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+static int cable_init(void)
Bastien Nocera 22e68e
+{
Bastien Nocera d02fae
+	GIOChannel *channel;
Bastien Nocera 22e68e
+
Bastien Nocera f1feb4
+	DBG("Setup cable plugin");
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+	ctx = udev_new();
Bastien Nocera d02fae
+	monitor = udev_monitor_new_from_netlink(ctx, "udev");
Bastien Nocera d02fae
+	if (monitor == NULL) {
Bastien Nocera d02fae
+		error ("Could not get udev monitor");
Bastien Nocera d02fae
+		return -1;
Bastien Nocera d02fae
+	}
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+	/* Listen for newly connected usb device */
Bastien Nocera d02fae
+	udev_monitor_filter_add_match_subsystem_devtype(monitor,
Bastien Nocera d02fae
+							"usb", NULL);
Bastien Nocera d02fae
+	udev_monitor_enable_receiving(monitor);
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+	channel = g_io_channel_unix_new(udev_monitor_get_fd(monitor));
Bastien Nocera d02fae
+	watch_id = g_io_add_watch(channel, G_IO_IN, monitor_event, NULL);
Bastien Nocera d02fae
+	g_io_channel_unref(channel);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	return 0;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+static void cable_exit(void)
Bastien Nocera 22e68e
+{
Bastien Nocera f1feb4
+	DBG("Cleanup cable plugin");
Bastien Nocera 22e68e
+
Bastien Nocera d02fae
+	if (watch_id != 0) {
Bastien Nocera d02fae
+		g_source_remove(watch_id);
Bastien Nocera d02fae
+		watch_id = 0;
Bastien Nocera d02fae
+	}
Bastien Nocera d02fae
+	if (monitor != NULL) {
Bastien Nocera d02fae
+		udev_monitor_unref(monitor);
Bastien Nocera d02fae
+		monitor = NULL;
Bastien Nocera d02fae
+	}
Bastien Nocera d02fae
+	if (ctx != NULL) {
Bastien Nocera d02fae
+		udev_unref(ctx);
Bastien Nocera d02fae
+		ctx = NULL;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+BLUETOOTH_PLUGIN_DEFINE(cable, VERSION,
Bastien Nocera 22e68e
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, cable_init, cable_exit)
Bastien Nocera 9befcf
diff --git a/src/adapter.c b/src/adapter.c
Bastien Nocera bd38d2
index 0909a22..8e63ee4 100644
Bastien Nocera 9befcf
--- a/src/adapter.c
Bastien Nocera 9befcf
+++ b/src/adapter.c
Bastien Nocera bd38d2
@@ -1091,6 +1091,25 @@ static struct btd_device *adapter_create_device(DBusConnection *conn,
Bastien Nocera 9befcf
 	return device;
Bastien Nocera 9befcf
 }
Bastien Nocera 9befcf
 
Bastien Nocera 9befcf
+void adapter_create_device_for_device(DBusConnection *conn,
Bastien Nocera 9befcf
+				      struct btd_adapter *adapter,
Bastien Nocera 9befcf
+				      struct btd_device *device)
Bastien Nocera 9befcf
+{
Bastien Nocera 9befcf
+	const char *path;
Bastien Nocera 9befcf
+
Bastien Nocera 9befcf
+	device_set_temporary(device, TRUE);
Bastien Nocera 9befcf
+
Bastien Nocera 9befcf
+	adapter->devices = g_slist_append(adapter->devices, device);
Bastien Nocera 9befcf
+
Bastien Nocera 9befcf
+	path = device_get_path(device);
Bastien Nocera 9befcf
+	g_dbus_emit_signal(conn, adapter->path,
Bastien Nocera 9befcf
+			ADAPTER_INTERFACE, "DeviceCreated",
Bastien Nocera 9befcf
+			DBUS_TYPE_OBJECT_PATH, &path,
Bastien Nocera 9befcf
+			DBUS_TYPE_INVALID);
Bastien Nocera 9befcf
+
Bastien Nocera 9befcf
+	adapter_update_devices(adapter);
Bastien Nocera 9befcf
+}
Bastien Nocera 9befcf
+
Bastien Nocera 9befcf
 void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
Bastien Nocera 9befcf
 						struct btd_device *device,
Bastien Nocera 9befcf
 						gboolean remove_storage)
Bastien Nocera 9befcf
diff --git a/src/adapter.h b/src/adapter.h
Bastien Nocera 978b40
index 38ea3ca..55c77aa 100644
Bastien Nocera 9befcf
--- a/src/adapter.h
Bastien Nocera 9befcf
+++ b/src/adapter.h
Bastien Nocera 978b40
@@ -116,6 +116,9 @@ void adapter_update_found_devices(struct btd_adapter *adapter, bdaddr_t *bdaddr,
Bastien Nocera 91d0f5
 int adapter_remove_found_device(struct btd_adapter *adapter, bdaddr_t *bdaddr);
Bastien Nocera 9befcf
 void adapter_emit_device_found(struct btd_adapter *adapter,
Bastien Nocera 91d0f5
 						struct remote_dev_info *dev);
Bastien Nocera 9befcf
+void adapter_create_device_for_device(DBusConnection *conn,
Bastien Nocera 9befcf
+				      struct btd_adapter *adapter,
Bastien Nocera 9befcf
+				      struct btd_device *device);
Bastien Nocera 9befcf
 void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode);
Bastien Nocera c0dc6a
 int adapter_update_local_name(struct btd_adapter *adapter, const char *name);
Bastien Nocera 91d0f5
 void adapter_service_insert(struct btd_adapter *adapter, void *rec);
Bastien Nocera 22e68e
-- 
Bastien Nocera 978b40
1.7.5.4
Bastien Nocera 22e68e