Bastien Nocera a4f891
From 7194839445edb3fd112b2bc79878c303e3b7c9e2 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 22e68e
Subject: [PATCH] Add sixaxis cable-pairing plugin
Bastien Nocera 22e68e
Bastien Nocera a4f891
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 a4f891
 Makefile.am     |    9 +-
Bastien Nocera 61a8de
 acinclude.m4    |   16 +++
Bastien Nocera ad8c9b
 configure.ac    |    1 +
Bastien Nocera a4f891
 plugins/cable.c |  384 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
Bastien Nocera a4f891
 4 files changed, 408 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 a4f891
index c8337d6..e5eccdf 100644
Bastien Nocera ad8c9b
--- a/Makefile.am
Bastien Nocera ad8c9b
+++ b/Makefile.am
Bastien Nocera a4f891
@@ -162,6 +162,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 a4f891
+builtin_modules += cable
Bastien Nocera a4f891
+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 a4f891
@@ -192,7 +197,7 @@ src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
Bastien Nocera a4f891
 			src/dbus-common.c src/dbus-common.h \
Bastien Nocera a4f891
 			src/dbus-hci.h src/dbus-hci.c
Bastien Nocera a4f891
 src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
Bastien Nocera a4f891
-							@CAPNG_LIBS@ -ldl
Bastien Nocera a4f891
+							@CAPNG_LIBS@ @CABLE_LIBS@ -ldl
Bastien Nocera a4f891
 src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
Bastien Nocera a4f891
 					-Wl,--version-script=src/bluetooth.ver
Bastien Nocera a4f891
 src_bluetoothd_DEPENDENCIES = src/bluetooth.ver lib/libbluetooth.la
Bastien Nocera a4f891
@@ -305,7 +310,7 @@ EXTRA_DIST += doc/manager-api.txt \
Bastien Nocera a4f891
 
Bastien Nocera a4f891
 AM_YFLAGS = -d
Bastien Nocera a4f891
 
Bastien Nocera a4f891
-AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
Bastien Nocera a4f891
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ @CABLE_CFLAGS@ \
Bastien Nocera a4f891
 		-DBLUETOOTH_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
Bastien Nocera a4f891
 
Bastien Nocera a4f891
 INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
Bastien Nocera ad8c9b
diff --git a/acinclude.m4 b/acinclude.m4
Bastien Nocera a4f891
index e7d1c32..10e5241 100644
Bastien Nocera ad8c9b
--- a/acinclude.m4
Bastien Nocera ad8c9b
+++ b/acinclude.m4
Bastien Nocera ad8c9b
@@ -142,6 +142,12 @@ 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 a4f891
+	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 ad8c9b
 AC_DEFUN([AC_PATH_NETLINK], [
Bastien Nocera ad8c9b
 	PKG_CHECK_MODULES(NETLINK, libnl-1, netlink_found=yes, netlink_found=no)
Bastien Nocera ad8c9b
 	AC_SUBST(NETLINK_CFLAGS)
Bastien Nocera 6daa96
@@ -170,6 +176,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera ad8c9b
 	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 6daa96
@@ -239,6 +246,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 ad8c9b
 	AC_ARG_ENABLE(netlink, AC_HELP_STRING([--enable-netlink], [enable NETLINK support]), [
Bastien Nocera ad8c9b
 		netlink_enable=${enableval}
Bastien Nocera ad8c9b
 	])
Bastien Nocera 61a8de
@@ -326,6 +337,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
Bastien Nocera 6daa96
 		AC_DEFINE(HAVE_CAPNG, 1, [Define to 1 if you have capabilities library.])
Bastien Nocera 6daa96
 	fi
Bastien Nocera 6daa96
 
Bastien Nocera 61a8de
+	if (test "${cable_enable}" = "yes" && test "${cable_found}" = "yes"); then
Bastien Nocera 61a8de
+		AC_DEFINE(HAVE_CABLE, 1, [Define to 1 if you have libcable.])
Bastien Nocera 61a8de
+	fi
Bastien Nocera 61a8de
+
Bastien Nocera ad8c9b
 	AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes")
Bastien Nocera 61a8de
 	AM_CONDITIONAL(NETLINK, test "${netlink_enable}" = "yes" && test "${netlink_found}" = "yes")
Bastien Nocera 61a8de
 	AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes")
Bastien Nocera 61a8de
@@ -350,4 +365,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 6daa96
index b93cca0..5df134f 100644
Bastien Nocera 22e68e
--- a/configure.ac
Bastien Nocera 22e68e
+++ b/configure.ac
Bastien Nocera ad8c9b
@@ -40,6 +40,7 @@ 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 22e68e
 AC_PATH_NETLINK
Bastien Nocera 22e68e
 AC_PATH_SNDFILE
Bastien Nocera 6daa96
 AC_PATH_CAPNG
Bastien Nocera 22e68e
diff --git a/plugins/cable.c b/plugins/cable.c
Bastien Nocera 22e68e
new file mode 100644
Bastien Nocera a4f891
index 0000000..0b7cc7a
Bastien Nocera 22e68e
--- /dev/null
Bastien Nocera 22e68e
+++ b/plugins/cable.c
Bastien Nocera a4f891
@@ -0,0 +1,384 @@
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 a4f891
+#include <glib.h>
Bastien Nocera a4f891
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE 1
Bastien Nocera a4f891
+#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 22e68e
+#include "logging.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 22e68e
+		debug("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 22e68e
+	debug("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 22e68e
+		debug("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 22e68e
+	conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
Bastien Nocera 22e68e
+	if (conn == NULL) {
Bastien Nocera 22e68e
+		debug("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 22e68e
+		debug("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 22e68e
+		debug("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 22e68e
+		debug("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 22e68e
+	debug("Adapter bdaddr %s", adapter_bdaddr);
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	if (set_master_bdaddr(devh, itfnum, adapter_bdaddr) == FALSE) {
Bastien Nocera 22e68e
+		debug("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 a4f891
+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 a4f891
+	if (g_strcmp0(udev_device_get_property_value(udevice, "ID_SERIAL"),
Bastien Nocera a4f891
+		      "Sony_PLAYSTATION_R_3_Controller") != 0)
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+	/* Don't look at events with an associated driver */
Bastien Nocera a4f891
+	if (udev_device_get_property_value(udevice, "ID_USB_DRIVER") != NULL)
Bastien Nocera 22e68e
+		return;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	debug("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 22e68e
+		debug("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 22e68e
+		debug("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 a4f891
+		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 22e68e
+		debug("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 22e68e
+		debug("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 22e68e
+			debug("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 a4f891
+static gboolean device_event_idle(struct udev_device *udevice)
Bastien Nocera 22e68e
+{
Bastien Nocera a4f891
+	handle_device_plug(udevice);
Bastien Nocera a4f891
+	udev_device_unref(udevice);
Bastien Nocera 22e68e
+	return FALSE;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera a4f891
+static struct udev *ctx = NULL;
Bastien Nocera a4f891
+static struct udev_monitor *monitor = NULL;
Bastien Nocera a4f891
+static guint watch_id = 0;
Bastien Nocera a4f891
+
Bastien Nocera a4f891
+static gboolean
Bastien Nocera a4f891
+monitor_event(GIOChannel *source,
Bastien Nocera a4f891
+	      GIOCondition condition,
Bastien Nocera a4f891
+	      gpointer data)
Bastien Nocera 22e68e
+{
Bastien Nocera a4f891
+	struct udev_device *udevice;
Bastien Nocera a4f891
+
Bastien Nocera a4f891
+	udevice = udev_monitor_receive_device(monitor);
Bastien Nocera a4f891
+	if (udevice == NULL)
Bastien Nocera a4f891
+		goto out;
Bastien Nocera a4f891
+	if (g_strcmp0(udev_device_get_action(udevice), "add") != 0)
Bastien Nocera a4f891
+		goto out;
Bastien Nocera a4f891
+
Bastien Nocera a4f891
+	g_timeout_add_seconds(1, (GSourceFunc) device_event_idle, udevice);
Bastien Nocera a4f891
+
Bastien Nocera a4f891
+out:
Bastien Nocera a4f891
+	return TRUE;
Bastien Nocera 22e68e
+}
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+static int cable_init(void)
Bastien Nocera 22e68e
+{
Bastien Nocera a4f891
+	GIOChannel *channel;
Bastien Nocera 22e68e
+
Bastien Nocera 22e68e
+	debug("Setup cable plugin");
Bastien Nocera 22e68e
+
Bastien Nocera a4f891
+	ctx = udev_new();
Bastien Nocera a4f891
+	monitor = udev_monitor_new_from_netlink(ctx, "udev");
Bastien Nocera a4f891
+	if (monitor == NULL) {
Bastien Nocera a4f891
+		error ("Could not get udev monitor");
Bastien Nocera a4f891
+		return -1;
Bastien Nocera a4f891
+	}
Bastien Nocera 22e68e
+
Bastien Nocera a4f891
+	/* Listen for newly connected usb device */
Bastien Nocera a4f891
+	udev_monitor_filter_add_match_subsystem_devtype(monitor,
Bastien Nocera a4f891
+							"usb", NULL);
Bastien Nocera a4f891
+	udev_monitor_enable_receiving(monitor);
Bastien Nocera 22e68e
+
Bastien Nocera a4f891
+	channel = g_io_channel_unix_new(udev_monitor_get_fd(monitor));
Bastien Nocera a4f891
+	watch_id = g_io_add_watch(channel, G_IO_IN, monitor_event, NULL);
Bastien Nocera a4f891
+	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 22e68e
+	debug("Cleanup cable plugin");
Bastien Nocera 22e68e
+
Bastien Nocera a4f891
+	if (watch_id != 0) {
Bastien Nocera a4f891
+		g_source_remove(watch_id);
Bastien Nocera a4f891
+		watch_id = 0;
Bastien Nocera a4f891
+	}
Bastien Nocera a4f891
+	if (monitor != NULL) {
Bastien Nocera a4f891
+		udev_monitor_unref(monitor);
Bastien Nocera a4f891
+		monitor = NULL;
Bastien Nocera a4f891
+	}
Bastien Nocera a4f891
+	if (ctx != NULL) {
Bastien Nocera a4f891
+		udev_unref(ctx);
Bastien Nocera a4f891
+		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 6daa96
1.6.4.4
Bastien Nocera 22e68e