Bastien Nocera f1feb4
From 5e78f3524c73fe5041bf88dada41f5dd3105e9fc 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 dac328
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 110fc2
 acinclude.m4    |   38 ++++++
Bastien Nocera 110fc2
 configure.ac    |    3 +
Bastien Nocera 110fc2
 plugins/cable.c |  385 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
Bastien Nocera 110fc2
 4 files changed, 433 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 a91986
index 36ffde3..1e3ff96 100644
Bastien Nocera ad8c9b
--- a/Makefile.am
Bastien Nocera ad8c9b
+++ b/Makefile.am
Bastien Nocera a91986
@@ -168,6 +168,11 @@ builtin_modules += service
Bastien Nocera ad8c9b
 builtin_sources += plugins/service.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 ad8c9b
 builtin_modules += hciops
Bastien Nocera ad8c9b
 builtin_sources += plugins/hciops.c
Bastien Nocera ad8c9b
 
Bastien Nocera a91986
@@ -198,7 +203,7 @@ src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
Bastien Nocera d02fae
 			src/dbus-common.c src/dbus-common.h \
Bastien Nocera d02fae
 			src/dbus-hci.h src/dbus-hci.c
Bastien Nocera d02fae
 src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
Bastien Nocera d02fae
-							@CAPNG_LIBS@ -ldl
Bastien Nocera d02fae
+							@CAPNG_LIBS@ @CABLE_LIBS@ -ldl
Bastien Nocera d02fae
 src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
Bastien Nocera d02fae
 					-Wl,--version-script=src/bluetooth.ver
Bastien Nocera d02fae
 src_bluetoothd_DEPENDENCIES = src/bluetooth.ver lib/libbluetooth.la
Bastien Nocera a91986
@@ -312,7 +317,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 dac328
index f7bb047..25005c0 100644
Bastien Nocera ad8c9b
--- a/acinclude.m4
Bastien Nocera ad8c9b
+++ b/acinclude.m4
Bastien Nocera 110fc2
@@ -146,6 +146,24 @@ AC_DEFUN([AC_PATH_USB], [
Bastien Nocera ad8c9b
 			[Define to 1 if you need the usb_interrupt_read() function.]))
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_NETLINK], [
Bastien Nocera 110fc2
+	PKG_CHECK_MODULES(NETLINK, libnl-1, netlink_found=yes, netlink_found=no)
Bastien Nocera 110fc2
+	AC_SUBST(NETLINK_CFLAGS)
Bastien Nocera 110fc2
+	AC_SUBST(NETLINK_LIBS)
Bastien Nocera 110fc2
+])
Bastien Nocera 110fc2
+
Bastien Nocera 110fc2
+AC_DEFUN([AC_PATH_CAPNG], [
Bastien Nocera 110fc2
+        PKG_CHECK_MODULES(CAPNG, libcap-ng, capng_found=yes, capng_found=no)
Bastien Nocera 110fc2
+        AC_SUBST(CAPNG_CFLAGS)
Bastien Nocera 110fc2
+        AC_SUBST(CAPNG_LIBS)
Bastien Nocera 110fc2
+])
Bastien Nocera 110fc2
+
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 110fc2
@@ -157,9 +175,12 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera 110fc2
 	optimization_enable=yes
Bastien Nocera 110fc2
 	fortify_enable=yes
Bastien Nocera 110fc2
 	pie_enable=yes
Bastien Nocera 110fc2
+	capng_enable=${capng_found}
Bastien Nocera 110fc2
 	sndfile_enable=${sndfile_found}
Bastien Nocera 110fc2
+	netlink_enable=no
Bastien Nocera ad8c9b
 	hal_enable=${hal_found}
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 110fc2
@@ -231,6 +252,14 @@ 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(netlink, AC_HELP_STRING([--enable-netlink], [enable NETLINK support]), [
Bastien Nocera 110fc2
+		netlink_enable=${enableval}
Bastien Nocera 110fc2
+	])
Bastien Nocera 110fc2
+
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 110fc2
@@ -314,6 +343,14 @@ 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 110fc2
+	if (test "${capng_enable}" = "yes" && test "${capng_found}" = "yes"); then
Bastien Nocera 110fc2
+		AC_DEFINE(HAVE_CAPNG, 1, [Define to 1 if you have capabilities library.])
Bastien Nocera 110fc2
+	fi
Bastien Nocera 110fc2
+
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 dac328
@@ -340,4 +377,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera ad8c9b
 	AM_CONDITIONAL(DFUTOOL, test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes")
Bastien Nocera ad8c9b
 	AM_CONDITIONAL(UDEVRULES, test "${udevrules_enable}" = "yes")
Bastien Nocera ad8c9b
 	AM_CONDITIONAL(CONFIGFILES, test "${configfiles_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 f1feb4
index 154d0dc..0e7084f 100644
Bastien Nocera 22e68e
--- a/configure.ac
Bastien Nocera 22e68e
+++ b/configure.ac
Bastien Nocera 110fc2
@@ -40,7 +40,10 @@ AC_PATH_GLIB
Bastien Nocera 22e68e
 AC_PATH_ALSA
Bastien Nocera 22e68e
 AC_PATH_GSTREAMER
Bastien Nocera 22e68e
 AC_PATH_USB
Bastien Nocera 22e68e
+AC_PATH_CABLE
Bastien Nocera 110fc2
+AC_PATH_NETLINK
Bastien Nocera 22e68e
 AC_PATH_SNDFILE
Bastien Nocera 110fc2
+AC_PATH_CAPNG
Bastien Nocera 110fc2
 
Bastien Nocera 110fc2
 AC_ARG_BLUEZ
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 f1feb4
index 0000000..d74c771
Bastien Nocera 22e68e
--- /dev/null
Bastien Nocera 22e68e
+++ b/plugins/cable.c
Bastien Nocera 110fc2
@@ -0,0 +1,385 @@
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 "manager.h"
Bastien Nocera 22e68e
+#include "adapter.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 22e68e
+	if (device == NULL)
Bastien Nocera 22e68e
+		device = adapter_create_device(conn, adapter, address);
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
+	int adapter_id;
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 22e68e
+	adapter_id = manager_get_default_adapter();
Bastien Nocera 22e68e
+	if (adapter_id == -1) {
Bastien Nocera f1feb4
+		DBG("No adapters, exiting");
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+	}
Bastien Nocera 22e68e
+	adapter = manager_find_adapter_by_id(adapter_id);
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 22e68e
-- 
Bastien Nocera dac328
1.7.0.1
Bastien Nocera 22e68e