public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Add sixaxis cable-pairing plugin
@ 2009-06-04  9:28 Bastien Nocera
  2009-06-04 18:13 ` Marcel Holtmann
  0 siblings, 1 reply; 13+ messages in thread
From: Bastien Nocera @ 2009-06-04  9:28 UTC (permalink / raw)
  To: BlueZ development

[-- Attachment #1: Type: text/plain, Size: 88 bytes --]

Works for me, comments welcome.

It uses gudev. See http://blog.fubar.dk/?p=106

Cheers

[-- Attachment #2: 0001-Add-sixaxis-cable-pairing-plugin.patch --]
[-- Type: text/x-patch, Size: 14610 bytes --]

>From f23a9556acf6308e217b11e0a882e89d799f2517 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Thu, 4 Jun 2009 10:23:58 +0100
Subject: [PATCH] Add sixaxis cable-pairing plugin

Implement the old "sixpair" using gudev (a GObject layer on top
of udev's monitoring system), and libusb-1.0.

When a Sixaxis device is plugged in, events are filtered, and
the device is selected, poked around to set the default Bluetooth
address, and added to the database of the current default adapter.
---
 acinclude.m4        |   16 +++
 configure.ac        |    1 +
 plugins/Makefile.am |   11 ++-
 plugins/cable.c     |  361 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 388 insertions(+), 1 deletions(-)
 create mode 100644 plugins/cable.c

diff --git a/acinclude.m4 b/acinclude.m4
index eb7cdeb..dac1120 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -159,6 +159,12 @@ AC_DEFUN([AC_PATH_USB], [
 			[Define to 1 if you need the usb_interrupt_read() function.]))
 ])
 
+AC_DEFUN([AC_PATH_CABLE], [
+	PKG_CHECK_MODULES(CABLE, gudev-1.0 libusb-1.0, cable_found=yes, cable_found=no)
+	AC_SUBST(CABLE_CFLAGS)
+	AC_SUBST(CABLE_LIBS)
+])
+
 AC_DEFUN([AC_PATH_NETLINK], [
 	PKG_CHECK_MODULES(NETLINK, libnl-1, netlink_found=yes, netlink_found=no)
 	AC_SUBST(NETLINK_CFLAGS)
@@ -179,6 +185,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	netlink_enable=no
 	hal_enable=${hal_found}
 	usb_enable=${usb_found}
+	cable_enable=${cable_found}
 	alsa_enable=${alsa_found}
 	gstreamer_enable=${gstreamer_found}
 	audio_enable=yes
@@ -241,6 +248,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		usb_enable=${enableval}
 	])
 
+	AC_ARG_ENABLE(cable, AC_HELP_STRING([--enable-cable], [enable DeviceKit support]), [
+		cable_enable=${enableval}
+	])
+
 	AC_ARG_ENABLE(netlink, AC_HELP_STRING([--enable-netlink], [enable NETLINK support]), [
 		netlink_enable=${enableval}
 	])
@@ -324,6 +335,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.])
 	fi
 
+	if (test "${cable_enable}" = "yes" && test "${cable_found}" = "yes"); then
+		AC_DEFINE(HAVE_CABLE, 1, [Define to 1 if you have libcable.])
+	fi
+
 	AC_SUBST([BLUEZ_CFLAGS], ['-I$(top_builddir)/include'])
 	AC_SUBST([BLUEZ_LIBS], ['$(top_builddir)/lib/libbluetooth.la'])
 
@@ -357,4 +372,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AM_CONDITIONAL(CONFIGFILES, test "${configfiles_enable}" = "yes")
 	AM_CONDITIONAL(INITSCRIPTS, test "${initscripts_enable}" = "yes")
 	AM_CONDITIONAL(PCMCIARULES, test "${pcmciarules_enable}" = "yes")
+	AM_CONDITIONAL(CABLE, test "${cable_enable}" = "yes" && test "${cable_found}" = "yes")
 ])
diff --git a/configure.ac b/configure.ac
index 1686d18..339d45c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,7 @@ AC_PATH_GLIB
 AC_PATH_ALSA
 AC_PATH_GSTREAMER
 AC_PATH_USB
+AC_PATH_CABLE
 AC_PATH_NETLINK
 AC_PATH_SNDFILE
 
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 9d9f970..e43584a 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -26,10 +26,16 @@ builtin_sources += hal.c
 builtin_modules += storage
 builtin_sources += storage.c
 
+if CABLE
+plugin_LTLIBRARIES += cable.la
+cable_la_LIBADD = @CABLE_CFLAGS@
+endif
+
 noinst_LTLIBRARIES = libbuiltin.la echo.la
 
 libbuiltin_la_SOURCES = $(builtin_sources)
 libbuiltin_la_LDFLAGS =
+libbuiltin_la_LIBADD = @CABLE_LIBS@
 libbuiltin_la_CFLAGS = $(AM_CFLAGS) \
 			$(builtin_cflags) -DBLUETOOTH_PLUGIN_BUILTIN
 
@@ -40,7 +46,7 @@ nodist_libbuiltin_la_SOURCES = $(BUILT_SOURCES)
 AM_LDFLAGS = -module -avoid-version -no-undefined
 
 AM_CFLAGS = -fvisibility=hidden @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ \
-			@GLIB_CFLAGS@ @GDBUS_CFLAGS@ @NETLINK_CFLAGS@
+			@GLIB_CFLAGS@ @GDBUS_CFLAGS@ @NETLINK_CFLAGS@ @CABLE_CFLAGS@
 
 INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/src
 
@@ -49,6 +55,7 @@ CLEANFILES = $(BUILT_SOURCES)
 MAINTAINERCLEANFILES = Makefile.in
 
 builtin.h:
+	echo $(builtin_modules)
 	echo "" > $@
 	list='$(builtin_modules)'; for i in $$list; \
 	  do echo "extern struct bluetooth_plugin_desc __bluetooth_builtin_$$i;" >> $@; done
@@ -63,9 +70,11 @@ all-local:
 	@$(LN_S) -f $(top_srcdir)/audio/.libs/audio.so
 	@$(LN_S) -f $(top_srcdir)/serial/.libs/serial.so
 	@$(LN_S) -f $(top_srcdir)/network/.libs/network.so
+	@$(LN_S) -f $(top_srcdir)/plugins/.libs/cable.so
 
 clean-local:
 	@rm -f network.so
 	@rm -f serial.so
 	@rm -f audio.so
 	@rm -f input.so
+	@rm -f cable.so
diff --git a/plugins/cable.c b/plugins/cable.c
new file mode 100644
index 0000000..9f24b55
--- /dev/null
+++ b/plugins/cable.c
@@ -0,0 +1,361 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009  Bastien Nocera <hadess@hadess.net>
+ *
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
+#include <dbus/dbus.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <libusb.h>
+
+#include "plugin.h"
+#include "logging.h"
+
+#include "manager.h"
+#include "adapter.h"
+#include "device.h"
+
+#include "storage.h"
+#include "sdp_lib.h"
+
+/* Vendor and product ID for the Sixaxis PS3 controller */
+#define VENDOR 0x054c
+#define PRODUCT 0x0268
+#define SIXAXIS_PNP_RECORD "3601920900000A000100000900013503191124090004350D35061901000900113503190011090006350909656E09006A0901000900093508350619112409010009000D350F350D350619010009001335031900110901002513576972656C65737320436F6E74726F6C6C65720901012513576972656C65737320436F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E7465727461696E6D656E740902000901000902010901000902020800090203082109020428010902052801090206359A35980822259405010904A101A102850175089501150026FF00810375019513150025013500450105091901291381027501950D0600FF8103150026FF0005010901A10075089504350046FF0009300931093209358102C0050175089527090181027508953009019102750895300901B102C0A1028502750895300901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C0090207350835060904090901000902082800090209280109020A280109020B09010009020C093E8009020D280009020E2800"
+#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
+
+struct btd_device *create_cable_association(DBusConnection *conn,
+					    struct btd_adapter *adapter,
+					    const char *name,
+					    const char *address,
+					    guint32 vendor_id,
+					    guint32 product_id,
+					    const char *pnp_record)
+{
+	sdp_record_t *rec;
+	struct btd_device *device;
+	bdaddr_t src, dst;
+	char srcaddr[18];
+
+	device = adapter_find_device(adapter, address);
+	if (device == NULL)
+		device = adapter_create_device(conn, adapter, address);
+	if (device != NULL) {
+		device_set_temporary(device, FALSE);
+		device_set_name(device, name);
+	}
+
+	str2ba(address, &dst);
+	adapter_get_address(adapter, &src);
+	ba2str(&src, srcaddr);
+
+	write_device_name(&dst, &src, (char *) name);
+
+	/* Store the device's SDP record */
+	rec = record_from_string(pnp_record);
+	store_record(srcaddr, address, rec);
+	sdp_record_free(rec);
+	/* Set the device id */
+	store_device_id(srcaddr, address, 0xffff, vendor_id, product_id, 0);
+	/* Don't write a profile, it will be updated when the device connects */
+
+	write_trust(srcaddr, address, "[all]", TRUE);
+
+	return device;
+}
+
+static char *get_bdaddr(libusb_device_handle *devh, int itfnum)
+{
+	unsigned char msg[17];
+	char *address;
+	int res;
+
+	res = libusb_control_transfer(devh,
+				      LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+				      0x01, 0x03f2, itfnum,
+				      (void*) msg, sizeof(msg),
+				      5000);
+
+	if (res < 0) {
+		debug("Getting the device Bluetooth address failed");
+		return NULL;
+	}
+
+	address = g_strdup_printf("%02X:%02X:%02X:%02X:%02X:%02X",
+				  msg[4], msg[5], msg[6], msg[7], msg[8], msg[9]);
+
+	debug("Device Bluetooth address: %s\n", address);
+
+	return address;
+}
+
+static gboolean set_master_bdaddr(libusb_device_handle *devh, int itfnum, char *host)
+{
+	unsigned char msg[8];
+	int mac[6];
+	int res;
+
+	if (sscanf(host, "%X:%X:%X:%X:%X:%X",
+		   &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6) {
+		return FALSE;
+	}
+
+	msg[0] = 0x01;
+	msg[1] = 0x00;
+	msg[2] = mac[0];
+	msg[3] = mac[1];
+	msg[4] = mac[2];
+	msg[5] = mac[3];
+	msg[6] = mac[4];
+	msg[7] = mac[5];
+
+	res = libusb_control_transfer(devh,
+				      LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+				      0x09, 0x03f5, itfnum,
+				      (void*) msg, sizeof(msg),
+				      5000);
+
+	if (res < 0) {
+		debug("Setting the master Bluetooth address failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+static void handle_usb_device(struct btd_adapter *adapter,
+			      libusb_device *dev,
+			      struct libusb_config_descriptor *cfg,
+			      int itfnum,
+			      const struct libusb_interface_descriptor *alt)
+{
+	DBusConnection *conn;
+	libusb_device_handle *devh;
+	char *device_bdaddr;
+	char adapter_bdaddr[18];
+	struct btd_device *device;
+	bdaddr_t dst;
+
+	conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+	if (conn == NULL) {
+		debug("Failed to get on the bus");
+		return;
+	}
+
+	if (libusb_open(dev, &devh) < 0) {
+		debug("Can't open device");
+		goto bail;
+	}
+	libusb_detach_kernel_driver(devh, itfnum);
+
+	if (libusb_claim_interface(devh, itfnum) < 0) {
+		debug("Can't claim interface %d", itfnum);
+		goto bail;
+	}
+
+	device_bdaddr = get_bdaddr(devh, itfnum);
+	if (device_bdaddr == NULL) {
+		debug("Failed to get the Bluetooth address from the device");
+		goto bail;
+	}
+
+	device = create_cable_association(conn,
+					  adapter,
+					  "PLAYSTATION(R)3 Controller",
+					  device_bdaddr,
+					  VENDOR, PRODUCT, SIXAXIS_PNP_RECORD);
+	btd_device_add_uuid(device, HID_UUID);
+
+	adapter_get_address(adapter, &dst);
+	ba2str(&dst, adapter_bdaddr);
+	debug("Adapter bdaddr %s", adapter_bdaddr);
+
+	if (set_master_bdaddr(devh, itfnum, adapter_bdaddr) == FALSE) {
+		debug("Failed to set the master Bluetooth address");
+		goto bail;
+	}
+
+bail:
+	dbus_connection_unref(conn);
+	g_free(device_bdaddr);
+	libusb_release_interface(devh, itfnum);
+	/* We ignore errors from the reattach, as there's nothing we
+	 * can do about it */
+	libusb_attach_kernel_driver(devh, itfnum);
+	if (devh != NULL)
+		libusb_close(devh);
+}
+
+static void handle_device_plug(GUdevDevice *device)
+{
+	struct btd_adapter *adapter;
+	int adapter_id;
+	guint i;
+
+	libusb_device **list, *usbdev;
+	ssize_t num_devices;
+	struct libusb_device_descriptor desc;
+	guint8 j;
+
+	if (g_udev_device_has_property(device, "ID_SERIAL") == FALSE ||
+	    g_strcmp0(g_udev_device_get_property(device, "ID_SERIAL"), "Sony_PLAYSTATION_R_3_Controller") != 0)
+		return;
+	/* Don't look at events with an associated driver */
+	if (g_udev_device_has_property(device, "ID_USB_DRIVER") != FALSE)
+		return;
+
+	debug("Found Sixaxis device");
+
+	/* Look for the default adapter */
+	adapter_id = manager_get_default_adapter();
+	if (adapter_id == -1) {
+		debug("No adapters, exiting");
+		return;
+	}
+	adapter = manager_find_adapter_by_id(adapter_id);
+	if (adapter == NULL)
+		return;
+
+	/* Look for the USB device */
+	libusb_init(NULL);
+
+	num_devices = libusb_get_device_list(NULL, &list);
+	if (num_devices < 0) {
+		debug("libusb_get_device_list failed");
+		return;
+	}
+
+	usbdev = NULL;
+	for (i = 0; i < num_devices; i++) {
+		char *path;
+
+		path = g_strdup_printf("%s/%03d/%03d", "/dev/bus/usb",
+				       libusb_get_bus_number(list[i]),
+				       libusb_get_device_address(list[i]));
+		if (g_strcmp0(path, g_udev_device_get_device_file(device)) == 0) {
+			g_free(path);
+			usbdev = libusb_ref_device(list[i]);
+			break;
+		}
+		g_free(path);
+	}
+
+	libusb_free_device_list(list, TRUE);
+	if (usbdev == NULL) {
+		debug("Found a Sixaxis, but couldn't find it via libusb");
+		goto out;
+	}
+
+	if (libusb_get_device_descriptor(usbdev, &desc) < 0) {
+		debug("libusb_get_device_descriptor() failed");
+		goto out;
+	}
+
+	/* Look for the interface number that interests us */
+	for (j = 0; j < desc.bNumConfigurations; j++) {
+		struct libusb_config_descriptor *config;
+		guint8 k;
+
+		if (libusb_get_config_descriptor(usbdev, j, &config) < 0) {
+			debug("Failed to get config descriptor %d", j);
+			continue;
+		}
+
+		for (k = 0; k < config->bNumInterfaces; k++) {
+			const struct libusb_interface *itf = &config->interface[k];
+			int l;
+
+			for (l = 0; l < itf->num_altsetting ; l++) {
+				struct libusb_interface_descriptor alt;
+
+				alt = itf->altsetting[l];
+				if (alt.bInterfaceClass == 3) {
+					handle_usb_device(adapter, usbdev, config, l, &alt);
+				}
+			}
+		}
+	}
+
+out:
+	if (usbdev != NULL)
+		libusb_unref_device(usbdev);
+	libusb_exit(NULL);
+}
+
+static gboolean device_event_idle(GUdevDevice *device)
+{
+	handle_device_plug(device);
+	return FALSE;
+}
+
+static void device_event_cb(GUdevClient  *client,
+			    const char    *action,
+			    GUdevDevice  *device,
+			    gpointer       user_data)
+{
+	if (g_strcmp0(action, "add") == 0) {
+		/* FIXME:
+		 * This happens because we need to wait for the
+		 * device node to be created by udev,
+		 * we should really just call: handle_device_plug(device); */
+		g_object_ref(device);
+		g_timeout_add_seconds(1, (GSourceFunc) device_event_idle, device);
+	}
+}
+
+GUdevClient *client = NULL;
+
+static int cable_init(void)
+{
+	const char * subsystems[] = { "usb", NULL };
+
+	debug("Setup cable plugin");
+
+	g_type_init();
+
+	client = g_udev_client_new(subsystems);
+
+	/* Listen for newly connected devices */
+	g_signal_connect(G_OBJECT(client),
+			 "device-event",
+			 G_CALLBACK(device_event_cb),
+			 NULL);
+
+	return 0;
+}
+
+static void cable_exit(void)
+{
+	debug("Cleanup cable plugin");
+
+	if (client != NULL) {
+		g_object_unref(client);
+		client = NULL;
+	}
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cable, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, cable_init, cable_exit)
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 13+ messages in thread
* [PATCH] Add sixaxis cable-pairing plugin
@ 2009-10-09 13:19 Bastien Nocera
  2009-10-11  9:40 ` Marcel Holtmann
  0 siblings, 1 reply; 13+ messages in thread
From: Bastien Nocera @ 2009-10-09 13:19 UTC (permalink / raw)
  To: BlueZ development

[-- Attachment #1: Type: text/plain, Size: 56 bytes --]

This time using libudev as opposed to libgudev.

Cheers

[-- Attachment #2: 0001-Add-sixaxis-cable-pairing-plugin.patch --]
[-- Type: text/x-patch, Size: 14788 bytes --]

>From 7194839445edb3fd112b2bc79878c303e3b7c9e2 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Tue, 1 Sep 2009 17:32:48 +0100
Subject: [PATCH] Add sixaxis cable-pairing plugin

Implement the old "sixpair" using libudev and libusb-1.0.

When a Sixaxis device is plugged in, events are filtered, and
the device is selected, poked around to set the default Bluetooth
address, and added to the database of the current default adapter.
---
 Makefile.am     |    9 +-
 acinclude.m4    |   16 +++
 configure.ac    |    1 +
 plugins/cable.c |  384 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 408 insertions(+), 2 deletions(-)
 create mode 100644 plugins/cable.c

diff --git a/Makefile.am b/Makefile.am
index c8337d6..e5eccdf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -162,6 +162,11 @@ builtin_modules += service
 builtin_sources += plugins/service.c
 endif
 
+if CABLE
+builtin_modules += cable
+builtin_sources += plugins/cable.c
+endif
+
 builtin_modules += hciops
 builtin_sources += plugins/hciops.c
 
@@ -192,7 +197,7 @@ src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
 			src/dbus-common.c src/dbus-common.h \
 			src/dbus-hci.h src/dbus-hci.c
 src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
-							@CAPNG_LIBS@ -ldl
+							@CAPNG_LIBS@ @CABLE_LIBS@ -ldl
 src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
 					-Wl,--version-script=src/bluetooth.ver
 src_bluetoothd_DEPENDENCIES = src/bluetooth.ver lib/libbluetooth.la
@@ -305,7 +310,7 @@ EXTRA_DIST += doc/manager-api.txt \
 
 AM_YFLAGS = -d
 
-AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ @CABLE_CFLAGS@ \
 		-DBLUETOOTH_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
 
 INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
diff --git a/acinclude.m4 b/acinclude.m4
index e7d1c32..10e5241 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -142,6 +142,12 @@ AC_DEFUN([AC_PATH_USB], [
 			[Define to 1 if you need the usb_interrupt_read() function.]))
 ])
 
+AC_DEFUN([AC_PATH_CABLE], [
+	PKG_CHECK_MODULES(CABLE, libudev libusb-1.0, cable_found=yes, cable_found=no)
+	AC_SUBST(CABLE_CFLAGS)
+	AC_SUBST(CABLE_LIBS)
+])
+
 AC_DEFUN([AC_PATH_NETLINK], [
 	PKG_CHECK_MODULES(NETLINK, libnl-1, netlink_found=yes, netlink_found=no)
 	AC_SUBST(NETLINK_CFLAGS)
@@ -170,6 +176,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	netlink_enable=no
 	hal_enable=${hal_found}
 	usb_enable=${usb_found}
+	cable_enable=${cable_found}
 	alsa_enable=${alsa_found}
 	gstreamer_enable=${gstreamer_found}
 	audio_enable=yes
@@ -239,6 +246,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		usb_enable=${enableval}
 	])
 
+	AC_ARG_ENABLE(cable, AC_HELP_STRING([--enable-cable], [enable DeviceKit support]), [
+		cable_enable=${enableval}
+	])
+
 	AC_ARG_ENABLE(netlink, AC_HELP_STRING([--enable-netlink], [enable NETLINK support]), [
 		netlink_enable=${enableval}
 	])
@@ -326,6 +337,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		AC_DEFINE(HAVE_CAPNG, 1, [Define to 1 if you have capabilities library.])
 	fi
 
+	if (test "${cable_enable}" = "yes" && test "${cable_found}" = "yes"); then
+		AC_DEFINE(HAVE_CABLE, 1, [Define to 1 if you have libcable.])
+	fi
+
 	AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes")
 	AM_CONDITIONAL(NETLINK, test "${netlink_enable}" = "yes" && test "${netlink_found}" = "yes")
 	AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes")
@@ -350,4 +365,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AM_CONDITIONAL(DFUTOOL, test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes")
 	AM_CONDITIONAL(UDEVRULES, test "${udevrules_enable}" = "yes")
 	AM_CONDITIONAL(CONFIGFILES, test "${configfiles_enable}" = "yes")
+	AM_CONDITIONAL(CABLE, test "${cable_enable}" = "yes" && test "${cable_found}" = "yes")
 ])
diff --git a/configure.ac b/configure.ac
index b93cca0..5df134f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,6 +40,7 @@ AC_PATH_GLIB
 AC_PATH_ALSA
 AC_PATH_GSTREAMER
 AC_PATH_USB
+AC_PATH_CABLE
 AC_PATH_NETLINK
 AC_PATH_SNDFILE
 AC_PATH_CAPNG
diff --git a/plugins/cable.c b/plugins/cable.c
new file mode 100644
index 0000000..0b7cc7a
--- /dev/null
+++ b/plugins/cable.c
@@ -0,0 +1,384 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009  Bastien Nocera <hadess@hadess.net>
+ *
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE 1
+#include <libudev.h>
+#include <dbus/dbus.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <libusb.h>
+
+#include "plugin.h"
+#include "logging.h"
+
+#include "manager.h"
+#include "adapter.h"
+#include "device.h"
+
+#include "storage.h"
+#include "sdp_lib.h"
+
+/* Vendor and product ID for the Sixaxis PS3 controller */
+#define VENDOR 0x054c
+#define PRODUCT 0x0268
+#define SIXAXIS_PNP_RECORD "3601920900000A000100000900013503191124090004350D35061901000900113503190011090006350909656E09006A0901000900093508350619112409010009000D350F350D350619010009001335031900110901002513576972656C65737320436F6E74726F6C6C65720901012513576972656C65737320436F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E7465727461696E6D656E740902000901000902010901000902020800090203082109020428010902052801090206359A35980822259405010904A101A102850175089501150026FF00810375019513150025013500450105091901291381027501950D0600FF8103150026FF0005010901A10075089504350046FF0009300931093209358102C0050175089527090181027508953009019102750895300901B102C0A1028502750895300901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C0090207350835060904090901000902082800090209280109020A280109020B09010009020C093E8009020D280009020E2800"
+#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
+
+static struct btd_device *create_cable_association(DBusConnection *conn,
+						    struct btd_adapter *adapter,
+						    const char *name,
+						    const char *address,
+						    guint32 vendor_id,
+						    guint32 product_id,
+						    const char *pnp_record)
+{
+	sdp_record_t *rec;
+	struct btd_device *device;
+	bdaddr_t src, dst;
+	char srcaddr[18];
+
+	device = adapter_find_device(adapter, address);
+	if (device == NULL)
+		device = adapter_create_device(conn, adapter, address);
+	if (device != NULL) {
+		device_set_temporary(device, FALSE);
+		device_set_name(device, name);
+	}
+
+	str2ba(address, &dst);
+	adapter_get_address(adapter, &src);
+	ba2str(&src, srcaddr);
+
+	write_device_name(&dst, &src, (char *) name);
+
+	/* Store the device's SDP record */
+	rec = record_from_string(pnp_record);
+	store_record(srcaddr, address, rec);
+	sdp_record_free(rec);
+	/* Set the device id */
+	store_device_id(srcaddr, address, 0xffff, vendor_id, product_id, 0);
+	/* Don't write a profile, it will be updated when the device connects */
+
+	write_trust(srcaddr, address, "[all]", TRUE);
+
+	return device;
+}
+
+static char *get_bdaddr(libusb_device_handle *devh, int itfnum)
+{
+	unsigned char msg[17];
+	char *address;
+	int res;
+
+	res = libusb_control_transfer(devh,
+				      LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+				      0x01, 0x03f2, itfnum,
+				      (void*) msg, sizeof(msg),
+				      5000);
+
+	if (res < 0) {
+		debug("Getting the device Bluetooth address failed");
+		return NULL;
+	}
+
+	address = g_strdup_printf("%02X:%02X:%02X:%02X:%02X:%02X",
+				  msg[4], msg[5], msg[6], msg[7], msg[8], msg[9]);
+
+	debug("Device Bluetooth address: %s\n", address);
+
+	return address;
+}
+
+static gboolean set_master_bdaddr(libusb_device_handle *devh, int itfnum, char *host)
+{
+	unsigned char msg[8];
+	int mac[6];
+	int res;
+
+	if (sscanf(host, "%X:%X:%X:%X:%X:%X",
+		   &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6) {
+		return FALSE;
+	}
+
+	msg[0] = 0x01;
+	msg[1] = 0x00;
+	msg[2] = mac[0];
+	msg[3] = mac[1];
+	msg[4] = mac[2];
+	msg[5] = mac[3];
+	msg[6] = mac[4];
+	msg[7] = mac[5];
+
+	res = libusb_control_transfer(devh,
+				      LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+				      0x09, 0x03f5, itfnum,
+				      (void*) msg, sizeof(msg),
+				      5000);
+
+	if (res < 0) {
+		debug("Setting the master Bluetooth address failed");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+static void handle_usb_device(struct btd_adapter *adapter,
+			      libusb_device *dev,
+			      struct libusb_config_descriptor *cfg,
+			      int itfnum,
+			      const struct libusb_interface_descriptor *alt)
+{
+	DBusConnection *conn;
+	libusb_device_handle *devh;
+	char *device_bdaddr;
+	char adapter_bdaddr[18];
+	struct btd_device *device;
+	bdaddr_t dst;
+
+	conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+	if (conn == NULL) {
+		debug("Failed to get on the bus");
+		return;
+	}
+
+	if (libusb_open(dev, &devh) < 0) {
+		debug("Can't open device");
+		goto bail;
+	}
+	libusb_detach_kernel_driver(devh, itfnum);
+
+	if (libusb_claim_interface(devh, itfnum) < 0) {
+		debug("Can't claim interface %d", itfnum);
+		goto bail;
+	}
+
+	device_bdaddr = get_bdaddr(devh, itfnum);
+	if (device_bdaddr == NULL) {
+		debug("Failed to get the Bluetooth address from the device");
+		goto bail;
+	}
+
+	device = create_cable_association(conn,
+					  adapter,
+					  "PLAYSTATION(R)3 Controller",
+					  device_bdaddr,
+					  VENDOR, PRODUCT, SIXAXIS_PNP_RECORD);
+	btd_device_add_uuid(device, HID_UUID);
+
+	adapter_get_address(adapter, &dst);
+	ba2str(&dst, adapter_bdaddr);
+	debug("Adapter bdaddr %s", adapter_bdaddr);
+
+	if (set_master_bdaddr(devh, itfnum, adapter_bdaddr) == FALSE) {
+		debug("Failed to set the master Bluetooth address");
+		goto bail;
+	}
+
+bail:
+	dbus_connection_unref(conn);
+	g_free(device_bdaddr);
+	libusb_release_interface(devh, itfnum);
+	/* We ignore errors from the reattach, as there's nothing we
+	 * can do about it */
+	libusb_attach_kernel_driver(devh, itfnum);
+	if (devh != NULL)
+		libusb_close(devh);
+}
+
+static void handle_device_plug(struct udev_device *udevice)
+{
+	struct btd_adapter *adapter;
+	int adapter_id;
+	guint i;
+
+	libusb_device **list, *usbdev;
+	ssize_t num_devices;
+	struct libusb_device_descriptor desc;
+	guint8 j;
+
+	if (g_strcmp0(udev_device_get_property_value(udevice, "ID_SERIAL"),
+		      "Sony_PLAYSTATION_R_3_Controller") != 0)
+		return;
+	/* Don't look at events with an associated driver */
+	if (udev_device_get_property_value(udevice, "ID_USB_DRIVER") != NULL)
+		return;
+
+	debug("Found Sixaxis device");
+
+	/* Look for the default adapter */
+	adapter_id = manager_get_default_adapter();
+	if (adapter_id == -1) {
+		debug("No adapters, exiting");
+		return;
+	}
+	adapter = manager_find_adapter_by_id(adapter_id);
+	if (adapter == NULL)
+		return;
+
+	/* Look for the USB device */
+	libusb_init(NULL);
+
+	num_devices = libusb_get_device_list(NULL, &list);
+	if (num_devices < 0) {
+		debug("libusb_get_device_list failed");
+		return;
+	}
+
+	usbdev = NULL;
+	for (i = 0; i < num_devices; i++) {
+		char *path;
+
+		path = g_strdup_printf("%s/%03d/%03d", "/dev/bus/usb",
+				       libusb_get_bus_number(list[i]),
+				       libusb_get_device_address(list[i]));
+		if (g_strcmp0(path, udev_device_get_devnode(udevice)) == 0) {
+			g_free(path);
+			usbdev = libusb_ref_device(list[i]);
+			break;
+		}
+		g_free(path);
+	}
+
+	libusb_free_device_list(list, TRUE);
+	if (usbdev == NULL) {
+		debug("Found a Sixaxis, but couldn't find it via libusb");
+		goto out;
+	}
+
+	if (libusb_get_device_descriptor(usbdev, &desc) < 0) {
+		debug("libusb_get_device_descriptor() failed");
+		goto out;
+	}
+
+	/* Look for the interface number that interests us */
+	for (j = 0; j < desc.bNumConfigurations; j++) {
+		struct libusb_config_descriptor *config;
+		guint8 k;
+
+		if (libusb_get_config_descriptor(usbdev, j, &config) < 0) {
+			debug("Failed to get config descriptor %d", j);
+			continue;
+		}
+
+		for (k = 0; k < config->bNumInterfaces; k++) {
+			const struct libusb_interface *itf = &config->interface[k];
+			int l;
+
+			for (l = 0; l < itf->num_altsetting ; l++) {
+				struct libusb_interface_descriptor alt;
+
+				alt = itf->altsetting[l];
+				if (alt.bInterfaceClass == 3) {
+					handle_usb_device(adapter, usbdev, config, l, &alt);
+				}
+			}
+		}
+	}
+
+out:
+	if (usbdev != NULL)
+		libusb_unref_device(usbdev);
+	libusb_exit(NULL);
+}
+
+static gboolean device_event_idle(struct udev_device *udevice)
+{
+	handle_device_plug(udevice);
+	udev_device_unref(udevice);
+	return FALSE;
+}
+
+static struct udev *ctx = NULL;
+static struct udev_monitor *monitor = NULL;
+static guint watch_id = 0;
+
+static gboolean
+monitor_event(GIOChannel *source,
+	      GIOCondition condition,
+	      gpointer data)
+{
+	struct udev_device *udevice;
+
+	udevice = udev_monitor_receive_device(monitor);
+	if (udevice == NULL)
+		goto out;
+	if (g_strcmp0(udev_device_get_action(udevice), "add") != 0)
+		goto out;
+
+	g_timeout_add_seconds(1, (GSourceFunc) device_event_idle, udevice);
+
+out:
+	return TRUE;
+}
+
+
+static int cable_init(void)
+{
+	GIOChannel *channel;
+
+	debug("Setup cable plugin");
+
+	ctx = udev_new();
+	monitor = udev_monitor_new_from_netlink(ctx, "udev");
+	if (monitor == NULL) {
+		error ("Could not get udev monitor");
+		return -1;
+	}
+
+	/* Listen for newly connected usb device */
+	udev_monitor_filter_add_match_subsystem_devtype(monitor,
+							"usb", NULL);
+	udev_monitor_enable_receiving(monitor);
+
+	channel = g_io_channel_unix_new(udev_monitor_get_fd(monitor));
+	watch_id = g_io_add_watch(channel, G_IO_IN, monitor_event, NULL);
+	g_io_channel_unref(channel);
+
+	return 0;
+}
+
+static void cable_exit(void)
+{
+	debug("Cleanup cable plugin");
+
+	if (watch_id != 0) {
+		g_source_remove(watch_id);
+		watch_id = 0;
+	}
+	if (monitor != NULL) {
+		udev_monitor_unref(monitor);
+		monitor = NULL;
+	}
+	if (ctx != NULL) {
+		udev_unref(ctx);
+		ctx = NULL;
+	}
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cable, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, cable_init, cable_exit)
-- 
1.6.4.4


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2009-10-11  9:40 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-04  9:28 [PATCH] Add sixaxis cable-pairing plugin Bastien Nocera
2009-06-04 18:13 ` Marcel Holtmann
2009-06-05 10:42   ` Bastien Nocera
2009-06-05 13:06     ` Marcel Holtmann
2009-06-05 13:52       ` Bastien Nocera
2009-06-05 14:15         ` Marcel Holtmann
2009-06-05 14:48           ` Bastien Nocera
2009-06-05 14:56             ` Marcel Holtmann
2009-06-05 15:06               ` Bastien Nocera
2009-06-06 17:41                 ` Luiz Augusto von Dentz
2009-06-06 17:51                   ` Marcel Holtmann
  -- strict thread matches above, loose matches on Subject: below --
2009-10-09 13:19 Bastien Nocera
2009-10-11  9:40 ` Marcel Holtmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox