Blob Blame History Raw
From 76986f1d8d356ddd6a1c31190627ecbe8ab1d773 Mon Sep 17 00:00:00 2001
From: Antonio Ospite <ospite@studenti.unina.it>
Date: Mon, 19 Sep 2011 18:15:40 +0200
Subject: [PATCH 1/5] adapter: add a btd_adapter_get_default_address() call
X-Face: z*RaLf`X<@C75u6Ig9}{oW$H;1_\2t5)({*|jhM<pyWR#k60!#=#>/Vb;]yA5<GWI5`6u&+
 ;6b'@y|8w"wB;4/e!7wYYrcqdJFY,~%Gk_4]cq$Ei/7<j&N3ah(m`ku?pX.&+~:_/wC~dwn^)MizBG
 !pE^+iDQQ1yC6^,)YDKkxDd!T>\I~93>J<_`<4)A{':UrE

Add a new btd_* call to get the default adapter address as a string.

This is meant to be used, for instance, by _external_ plugins which want
to know the default adapter address for some reason (e.g. to send it to
a device for some custom pairing scheme like in the case of Playstation
peripherals).

External plugins can only use symbols marked as "global" in
src/bluetooth.ver, this patch avoids making global these symbols:

  adapter_get_address
  bt_malloc
  ba2str
---
 src/adapter.c |   17 +++++++++++++++++
 src/adapter.h |    1 +
 2 files changed, 18 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 1250966..a7a2530 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -224,6 +224,23 @@ struct btd_adapter *btd_adapter_get_default(void)
 	return NULL;
 }
 
+char *btd_adapter_get_default_address(void)
+{
+	struct btd_adapter *adapter;
+	char *str;
+
+	adapter = btd_adapter_get_default();
+	if (adapter == NULL)
+		return NULL;
+
+	str = bt_malloc(18);
+	if (str == NULL)
+		return NULL;
+
+	ba2str(adapter_get_address(adapter), str);
+	return str;
+}
+
 bool btd_adapter_is_default(struct btd_adapter *adapter)
 {
 	if (!adapter)
diff --git a/src/adapter.h b/src/adapter.h
index 2de8730..ed99b55 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -38,6 +38,7 @@
 struct btd_adapter;
 
 struct btd_adapter *btd_adapter_get_default(void);
+char *btd_adapter_get_default_address(void);
 bool btd_adapter_is_default(struct btd_adapter *adapter);
 uint16_t btd_adapter_get_index(struct btd_adapter *adapter);
 
-- 
1.7.10.4


From 2321b3b669de2edc3a9be2e1980c4b1f5312e176 Mon Sep 17 00:00:00 2001
From: Antonio Ospite <ospite@studenti.unina.it>
Date: Mon, 19 Sep 2011 18:13:57 +0200
Subject: [PATCH 2/5] adapter: add a btd_create_stored_device() call
X-Face: z*RaLf`X<@C75u6Ig9}{oW$H;1_\2t5)({*|jhM<pyWR#k60!#=#>/Vb;]yA5<GWI5`6u&+
 ;6b'@y|8w"wB;4/e!7wYYrcqdJFY,~%Gk_4]cq$Ei/7<j&N3ah(m`ku?pX.&+~:_/wC~dwn^)MizBG
 !pE^+iDQQ1yC6^,)YDKkxDd!T>\I~93>J<_`<4)A{':UrE

Add a new btd_* call to add a new device to the database of known
devices.

This is particularly useful to register Bluetooth devices using
a non-Bluetooth mechanism (e.g. USB-pairing in the case of Playstation
peripherals).

The interface uses a btd_ prefix and relies only on native C types in
order to be more easily used by _external_ plugins.

External plugins can only use symbols marked as "global" in
src/bluetooth.ver, this patch avoids making global these symbols:

  store_sdp_record
  str2ba
  adapter_get_device
  device_set_name
  device_set_temporary
  device_set_trusted
---
 src/adapter.c |   52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/adapter.h |   11 +++++++++++
 2 files changed, 63 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index a7a2530..ea3a739 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -6318,3 +6318,55 @@ void adapter_shutdown(void)
 	if (!adapter_remaining)
 		btd_exit();
 }
+
+int btd_create_stored_device(char *adapter_address,
+			     char *device_address,
+			     char *name,
+			     uint16_t vendor_id_source,
+			     uint16_t vendor_id,
+			     uint16_t product_id,
+			     uint16_t version_id,
+			     const char *uuid,
+			     char *sdp_record,
+			     bool trusted)
+{
+	struct btd_adapter *adapter;
+	struct btd_device *device;
+	bdaddr_t dst;
+	int ret = 0;
+
+	store_sdp_record(adapter_address, device_address, 0x10000, sdp_record);
+
+	str2ba(device_address, &dst);
+
+	adapter = btd_adapter_get_default();
+	if (adapter == NULL) {
+		DBG("Failed to get the adapter");
+		ret = -EPERM;
+		goto out;
+	}
+
+	/* This will create the device if necessary */
+	device = adapter_get_device(adapter, &dst, BDADDR_BREDR);
+	if (device == NULL) {
+		DBG("Failed to get the device");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (name)
+		device_set_name(device, name);
+
+	btd_device_set_pnpid(device, vendor_id_source,
+			vendor_id, product_id, version_id);
+
+	if (uuid)
+		btd_device_add_uuid(device, uuid);
+
+	device_set_temporary(device, FALSE);
+
+	if (trusted)
+		device_set_trusted(device, TRUE);
+out:
+	return ret;
+}
diff --git a/src/adapter.h b/src/adapter.h
index ed99b55..2abc9ce 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -214,3 +214,14 @@ gboolean btd_adapter_check_oob_handler(struct btd_adapter *adapter);
 void btd_adapter_for_each_device(struct btd_adapter *adapter,
 			void (*cb)(struct btd_device *device, void *data),
 			void *data);
+
+int btd_create_stored_device(char *adapter_address,
+			     char *device_address,
+			     char *name,
+			     uint16_t vendor_id_source,
+			     uint16_t vendor_id,
+			     uint16_t product_id,
+			     uint16_t version_id,
+			     const char *uuid,
+			     char *sdp_record,
+			     bool trusted);
-- 
1.7.10.4


From bbdb42cb551fc8600025e2832cf6bf08fc51337e Mon Sep 17 00:00:00 2001
From: Antonio Ospite <ospite@studenti.unina.it>
Date: Wed, 5 May 2010 13:43:09 +0200
Subject: [PATCH 3/5] Add playstation-peripheral plugin: USB pairing and LEDs
 settings
X-Face: z*RaLf`X<@C75u6Ig9}{oW$H;1_\2t5)({*|jhM<pyWR#k60!#=#>/Vb;]yA5<GWI5`6u&+
 ;6b'@y|8w"wB;4/e!7wYYrcqdJFY,~%Gk_4]cq$Ei/7<j&N3ah(m`ku?pX.&+~:_/wC~dwn^)MizBG
 !pE^+iDQQ1yC6^,)YDKkxDd!T>\I~93>J<_`<4)A{':UrE

Add a plugin which handles the connection of a Playstation peripheral,
when a new hidraw device is connected the plugin:

 - Filters udev events, and select the Playstation peripheral
 - Sets the Master bluetooth address in the peripheral (USB pairing)
 - Sets LEDs to match the joystick system number if needed
   (for USB and BT)
 - Adds the device to the database of the current default
   adapter (BT association)

Signed-off-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
---
 Makefile.am                          |    7 +
 configure.ac                         |   10 +
 plugins/playstation-peripheral-hid.c |  263 ++++++++++++++++++++++
 plugins/playstation-peripheral-hid.h |   10 +
 plugins/playstation-peripheral.c     |  401 ++++++++++++++++++++++++++++++++++
 5 files changed, 691 insertions(+)
 create mode 100644 plugins/playstation-peripheral-hid.c
 create mode 100644 plugins/playstation-peripheral-hid.h
 create mode 100644 plugins/playstation-peripheral.c

diff --git a/Makefile.am b/Makefile.am
index 9d570fb..a78a8ba 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -112,6 +112,13 @@ builtin_nodist =
 
 include Makefile.plugins
 
+if PLAYSTATION_PERIPHERAL_PLUGIN
+plugin_LTLIBRARIES += plugins/playstation-peripheral.la
+plugins_playstation_peripheral_la_SOURCES = plugins/playstation-peripheral.c plugins/playstation-peripheral-hid.c
+plugins_playstation_peripheral_la_LDFLAGS = -module -avoid-version -no-undefined @UDEV_LIBS@
+plugins_playstation_peripheral_la_CFLAGS = -fvisibility=hidden @DBUS_CFLAGS@ @GLIB_CFLAGS@ @UDEV_CFLAGS@
+endif
+
 if MAINTAINER_MODE
 plugin_LTLIBRARIES += plugins/external-dummy.la
 plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
diff --git a/configure.ac b/configure.ac
index f3c58be..7e0cc0c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -229,6 +229,16 @@ AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
 					[enable_experimental=${enableval}])
 AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
 
+AC_ARG_ENABLE(playstation_peripheral, AC_HELP_STRING([--enable-playstation-peripheral],
+              [enable playstation-peripheral plugin]),
+              [enable_playstation_peripheral=${enableval}])
+
+if (test "${enble_playstation_peripheral}" != "no" && test "${enable_udev}" != "no"); then
+	AC_DEFINE(HAVE_PLAYSTATION_PERIPHERAL_PLUGIN, 1, [Define to 1 if you have playstation-peripheral plugin.])
+fi
+
+AM_CONDITIONAL(PLAYSTATION_PERIPHERAL_PLUGIN, test "${enable_playstation_peripheral}" != "no" && test "${enable_udev}" != "no")
+
 if (test "${prefix}" = "NONE"); then
 	dnl no prefix and no localstatedir, so default to /var
 	if (test "$localstatedir" = '${prefix}/var'); then
diff --git a/plugins/playstation-peripheral-hid.c b/plugins/playstation-peripheral-hid.c
new file mode 100644
index 0000000..9c5e530
--- /dev/null
+++ b/plugins/playstation-peripheral-hid.c
@@ -0,0 +1,263 @@
+/*
+ * playstation peripheral plugin: lowlevel hid functions
+ *
+ * Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
+ *
+ *
+ * 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
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/hidraw.h>
+
+#include "log.h"
+#include "playstation-peripheral-hid.h"
+
+/* Fallback definitions to compile with older headers */
+#ifndef HIDIOCGFEATURE
+#define HIDIOCGFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
+#endif
+
+#ifndef HIDIOCSFEATURE
+#define HIDIOCSFEATURE(len)    _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
+#endif
+
+#define BDADDR_STR_SIZE 18 /* strlen("00:00:00:00:00:00") + 1 */
+
+#define LED_1 (0x01 << 1)
+#define LED_2 (0x01 << 2)
+#define LED_3 (0x01 << 3)
+#define LED_4 (0x01 << 4)
+
+#define LED_STATUS_OFF 0
+#define LED_STATUS_ON  1
+
+/* Usb cable pairing section */
+static unsigned char *get_feature_report(int fd, uint8_t report_number,
+						unsigned int len)
+{
+	unsigned char *buf;
+	int ret;
+
+	buf = calloc(len, sizeof(*buf));
+	if (buf == NULL) {
+		error("%s:%s() calloc failed", __FILE__, __func__);
+		return NULL;
+	}
+
+	buf[0] = report_number;
+
+	ret = ioctl(fd, HIDIOCGFEATURE(len), buf);
+	if (ret < 0) {
+		error("%s:%s() HIDIOCGFEATURE ret = %d",
+			__FILE__, __func__, ret);
+		free(buf);
+		return NULL;
+	}
+
+	return buf;
+}
+
+static int set_feature_report(int fd, uint8_t *report, int len)
+{
+	int ret;
+
+	ret = ioctl(fd, HIDIOCSFEATURE(len), report);
+	if (ret < 0)
+		error("%s:%s() HIDIOCSFEATURE failed, ret = %d",
+			__FILE__, __func__, ret);
+
+	return ret;
+}
+
+char *sixaxis_get_device_bdaddr(int fd)
+{
+	unsigned char *buf;
+	char *address;
+
+	buf = get_feature_report(fd, 0xf2, 18);
+	if (buf == NULL) {
+		error("%s:%s() cannot get feature report", __FILE__, __func__);
+		return NULL;
+	}
+
+	address = calloc(BDADDR_STR_SIZE, sizeof(*address));
+	if (address == NULL) {
+		error("%s:%s() calloc failed", __FILE__, __func__);
+		free(buf);
+		return NULL;
+	}
+
+	snprintf(address, BDADDR_STR_SIZE,
+			"%02X:%02X:%02X:%02X:%02X:%02X",
+			buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]);
+
+	free(buf);
+	return address;
+}
+
+char *sixaxis_get_master_bdaddr(int fd)
+{
+	unsigned char *buf;
+	char *address;
+
+	buf = get_feature_report(fd, 0xf5, 8);
+	if (buf == NULL) {
+		error("%s:%s() cannot get feature report", __FILE__, __func__);
+		return NULL;
+	}
+
+	address = calloc(BDADDR_STR_SIZE, sizeof(*address));
+	if (address == NULL) {
+		error("%s:%s() calloc failed", __FILE__, __func__);
+		free(buf);
+		return NULL;
+	}
+
+	snprintf(address, BDADDR_STR_SIZE,
+			"%02X:%02X:%02X:%02X:%02X:%02X",
+			buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+
+	free(buf);
+	return address;
+}
+
+int sixaxis_set_master_bdaddr(int fd, char *adapter_bdaddr)
+{
+	uint8_t *report;
+	uint8_t addr[6];
+	int ret;
+
+	ret = sscanf(adapter_bdaddr,
+			"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+			&addr[0], &addr[1], &addr[2],
+			&addr[3], &addr[4], &addr[5]);
+	if (ret != 6) {
+		error("%s:%s() Parsing the bt address failed",
+			__FILE__, __func__);
+		return -EINVAL;
+	}
+
+	report = malloc(8);
+	if (report == NULL) {
+		error("%s:%s() malloc failed", __FILE__, __func__);
+		return -ENOMEM;
+	}
+
+	report[0] = 0xf5;
+	report[1] = 0x01;
+
+	report[2] = addr[0];
+	report[3] = addr[1];
+	report[4] = addr[2];
+	report[5] = addr[3];
+	report[6] = addr[4];
+	report[7] = addr[5];
+
+	ret = set_feature_report(fd, report, 8);
+	if (ret < 0) {
+		error("%s:%s() cannot set feature report",
+			__FILE__, __func__);
+		goto out;
+	}
+
+	DBG("New Master Bluetooth address: %s", adapter_bdaddr);
+
+out:
+	free(report);
+	return ret;
+}
+
+
+/* Led setting section */
+static int set_leds(int fd, unsigned char leds_status[4])
+{
+	int ret;
+
+	/*
+	 * the total time the led is active (0xff means forever)
+	 * |     duty_length: how long a cycle is in deciseconds:
+	 * |     |                              (0 means "blink very fast")
+	 * |     |     ??? (Maybe a phase shift or duty_length multiplier?)
+	 * |     |     |     % of duty_length led is off (0xff means 100%)
+	 * |     |     |     |     % of duty_length led is on (0xff is 100%)
+	 * |     |     |     |     |
+	 * 0xff, 0x27, 0x10, 0x00, 0x32,
+	 */
+	unsigned char leds_report[] = {
+		0x01,
+		0x00, 0x00, 0x00, 0x00, 0x00, /* rumble values TBD */
+		0x00, 0x00, 0x00, 0x00, 0x1e, /* LED_1=0x02, LED_2=0x04 ... */
+		0xff, 0x27, 0x10, 0x00, 0x32, /* LED_4 */
+		0xff, 0x27, 0x10, 0x00, 0x32, /* LED_3 */
+		0xff, 0x27, 0x10, 0x00, 0x32, /* LED_2 */
+		0xff, 0x27, 0x10, 0x00, 0x32, /* LED_1 */
+		0x00, 0x00, 0x00, 0x00, 0x00,
+	};
+
+	int leds = 0;
+	if (leds_status[0])
+		leds |= LED_1;
+	if (leds_status[1])
+		leds |= LED_2;
+	if (leds_status[2])
+		leds |= LED_3;
+	if (leds_status[3])
+		leds |= LED_4;
+
+	leds_report[10] = leds;
+
+	ret = write(fd, leds_report, sizeof(leds_report));
+	if (ret < (ssize_t) sizeof(leds_report))
+		error("%s:%s() Unable to write to hidraw device",
+			__FILE__, __func__);
+
+	return ret;
+}
+
+int set_controller_number(int fd, unsigned int n)
+{
+	unsigned char leds_status[4] = {0, 0, 0, 0};
+
+	switch (n) {
+	case 0:
+		break;
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+		leds_status[n - 1] = LED_STATUS_ON;
+		break;
+	case 5:
+	case 6:
+	case 7:
+		leds_status[4 - 1] = LED_STATUS_ON;
+		leds_status[n - 4 - 1] = LED_STATUS_ON;
+		break;
+	default:
+		error("%s:%s() Only 7 controllers supported for now",
+			__FILE__, __func__);
+		return -1;
+	}
+
+	return set_leds(fd, leds_status);
+}
diff --git a/plugins/playstation-peripheral-hid.h b/plugins/playstation-peripheral-hid.h
new file mode 100644
index 0000000..ade8fa0
--- /dev/null
+++ b/plugins/playstation-peripheral-hid.h
@@ -0,0 +1,10 @@
+#ifndef __PLAYSTATION_PERIPHERAL_HID_H
+#define __PLAYSTATION_PERIPHERAL_HID_H
+
+char *sixaxis_get_device_bdaddr(int fd);
+char *sixaxis_get_master_bdaddr(int fd);
+int sixaxis_set_master_bdaddr(int fd, char *adapter_bdaddr);
+
+int set_controller_number(int fd, unsigned int n);
+
+#endif /* __PLAYSTATION_PERIPHERAL_HID_H */
diff --git a/plugins/playstation-peripheral.c b/plugins/playstation-peripheral.c
new file mode 100644
index 0000000..9238e92
--- /dev/null
+++ b/plugins/playstation-peripheral.c
@@ -0,0 +1,401 @@
+/*
+ * playstation peripheral plugin: support for Playstation peripherals
+ *
+ * Copyright (C) 2009  Bastien Nocera <hadess@hadess.net>
+ * Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
+ *
+ *
+ * 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
+ *
+ */
+
+/*
+ * In the following this terminology is used:
+ *
+ *  - peripheral: a Playstation peripheral (Sixaxis, DS3, headset, etc.)
+ *  - controller: an input peripheral
+ *  - adapter: the bluetooth dongle on the host system.
+ *  - adapter_bdaddr: the bdaddr of the bluetooth adapter.
+ *  - device_bdaddr: the bdaddr of the Playstation peripheral.
+ *  - master_bdaddr: the bdaddr of the adapter to be configured into the
+ *    Playstation peripheral
+ *
+ * WHAT we need the plugin to do:
+ * 
+ *   - When a device is connected via USB:
+ *     + Fetch the (default) adapter bdaddr (from BlueZ) and store it into
+ *       the device
+ *     + Fetch the device bdaddr (from the device via USB/HID) and make the
+ *       device _trusted_ by the adapter (is "trusted" the correct term
+ *       here? Or maybe this is more like a "static association"? Are the
+ *       term "trusted" and "associated" in bluetooth context defined
+ *       anywhere?)
+ * 
+ *   - When the device is connected via BT:
+ *     + Nothing! It should work automatically.
+ * 
+ *   - Set LEDs when possible.
+ * 
+ * WHY we need that:
+ * 
+ *   Playstation peripherals require/support USB cable pairing.
+ * 
+ *   It is required in the sense that devices will talk only to adapters
+ *   they know.
+ * 
+ *   It is supported in the sense that this mechanism is optional on _some_
+ *   devices like the PS3 Keypad.
+ * 
+ *   On the PS3 these cable paired devices can be used via BT without
+ *   further association steps once they have been connected
+ *   _at_least_once_ via USB to a certain host (with a certain BT adapter
+ *   that is), we would like to have the same behavior with BlueZ.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE 1
+#include <libudev.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "adapter.h"
+#include "device.h"
+#include "storage.h"
+#include "sdp_lib.h"
+
+#include "playstation-peripheral-hid.h"
+
+struct playstation_peripheral {
+	uint16_t vendor_id;
+	uint16_t product_id;
+	char *name;
+	char *sdp_record;
+	char *uuid;
+
+	/* device specific callbacks to get master/device bdaddr and set
+	 * master bdaddr
+	 */
+	char * (*get_device_bdaddr)(int);
+	char * (*get_master_bdaddr)(int);
+	int (*set_master_bdaddr) (int, char *);
+};
+
+static struct playstation_peripheral peripherals[] = {
+	{
+		.vendor_id = 0x054c,
+		.product_id = 0x0268,
+		.name = "PLAYSTATION(R)3 Controller",
+		.sdp_record = "3601920900000A000100000900013503191124090004350D35061901000900113503190011090006350909656E09006A0901000900093508350619112409010009000D350F350D350619010009001335031900110901002513576972656C65737320436F6E74726F6C6C65720901012513576972656C65737320436F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E7465727461696E6D656E740902000901000902010901000902020800090203082109020428010902052801090206359A35980822259405010904A101A102850175089501150026FF00810375019513150025013500450105091901291381027501950D0600FF8103150026FF0005010901A10075089504350046FF0009300931093209358102C0050175089527090181027508953009019102750895300901B102C0A1028502750895300901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C0090207350835060904090901000902082800090209280109020A280109020B09010009020C093E8009020D280009020E2800",
+		.uuid = "00001124-0000-1000-8000-00805f9b34fb",
+		.get_device_bdaddr = sixaxis_get_device_bdaddr,
+		.get_master_bdaddr = sixaxis_get_master_bdaddr,
+		.set_master_bdaddr = sixaxis_set_master_bdaddr,
+	},
+};
+
+static struct udev *ctx;
+static struct udev_monitor *monitor;
+static guint watch_id;
+
+static int create_peripheral_association(char *adapter_address,
+					char *device_address,
+					struct playstation_peripheral *peripheral)
+{
+	return btd_create_stored_device(adapter_address, device_address,
+					peripheral->name,
+					0x0002, /* VersionIDSource = USB Implementer's Forum */
+					peripheral->vendor_id,
+					peripheral->product_id,
+					0, /* version is hardcoded to 0 for now */
+					peripheral->uuid,
+					peripheral->sdp_record,
+					1);
+}
+
+static int peripheral_pair(int fd, char *adapter_bdaddr,
+			   struct playstation_peripheral *peripheral)
+{
+	char *device_bdaddr;
+	char *master_bdaddr;
+	int ret = 0;
+
+	master_bdaddr = peripheral->get_master_bdaddr(fd);
+	if (master_bdaddr == NULL) {
+		DBG("Failed to get the Old master Bluetooth address from the device");
+		return -EPERM;
+	}
+
+	/* Only set the master bdaddr when needed, this is how the PS3 does
+	 * it, perhaps to avoid unnecessary writes to some eeprom.
+	 */
+	if (g_strcmp0(master_bdaddr, adapter_bdaddr) != 0) {
+		DBG("Old master Bluetooth address was: %s", master_bdaddr);
+		ret = peripheral->set_master_bdaddr(fd, adapter_bdaddr);
+		if (ret < 0) {
+			DBG("Failed to set the master Bluetooth address");
+			free(master_bdaddr);
+			return ret;
+		}
+	}
+
+	device_bdaddr = peripheral->get_device_bdaddr(fd);
+	if (device_bdaddr == NULL) {
+		DBG("Failed to get the Bluetooth address from the device");
+		free(master_bdaddr);
+		return -EPERM;
+	}
+
+	DBG("Device bdaddr %s", device_bdaddr);
+
+	ret = create_peripheral_association(adapter_bdaddr, device_bdaddr, peripheral);
+
+	free(device_bdaddr);
+	free(master_bdaddr);
+	return ret;
+}
+
+static inline struct playstation_peripheral *find_playstation_peripheral(const char *hid_id)
+{
+	unsigned int array_size = sizeof(peripherals)/sizeof(peripherals[0]);
+	unsigned int i;
+	int ret;
+	uint16_t protocol;
+	uint16_t vendor_id;
+	uint16_t product_id;
+
+	ret = sscanf(hid_id, "%hx:%hx:%hx", &protocol, &vendor_id, &product_id);
+	if (ret != 3) {
+		error("%s:%s() Parsing HID_ID failed",
+			__FILE__, __func__);
+		return NULL;
+	}
+
+	for (i = 0; i < array_size; i++) {
+		if (peripherals[i].vendor_id == vendor_id &&
+		    peripherals[i].product_id == product_id)
+			return &peripherals[i];
+	}
+
+	return NULL;
+}
+
+static inline int is_usb_peripheral(const char *hid_id)
+{
+	int ret;
+	uint16_t protocol;
+	uint16_t vendor_id;
+	uint16_t product_id;
+
+	ret = sscanf(hid_id, "%hx:%hx:%hx", &protocol, &vendor_id, &product_id);
+	if (ret != 3) {
+		error("%s:%s() Parsing HID_ID failed",
+			__FILE__, __func__);
+		return 0;
+	}
+
+	DBG("%hx:%hx:%hx", protocol, vendor_id, product_id);
+	return (protocol == 3);
+}
+
+static void handle_device_plug(struct udev_device *udevice)
+{
+	struct udev_device *hid_parent;
+	struct udev_enumerate *enumerate;
+	struct udev_list_entry *devices, *dev_list_entry;
+	const char *hid_id;
+	const char *hid_phys;
+	const char *hidraw_node;
+	unsigned char is_usb = FALSE;
+	int js_num = 0;
+	int fd;
+	struct playstation_peripheral *peripheral;
+
+	hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
+								"hid", NULL);
+	if (!hid_parent) {
+		error("%s:%s() cannot get parent hid device",
+			__FILE__, __func__);
+		return;
+	}
+
+	hid_id = udev_device_get_property_value(hid_parent, "HID_ID");
+	DBG("HID_ID: %s", hid_id);
+
+	peripheral = find_playstation_peripheral(hid_id);
+	if (!peripheral) {
+		error("No supported peripheral found");
+		return;
+	}
+
+	DBG("Found a Playstation peripheral: %s", peripheral->name);
+
+	hidraw_node = udev_device_get_devnode(udevice);
+
+	/* looking for joysticks */
+	hid_phys = udev_device_get_property_value(hid_parent, "HID_PHYS");
+
+	enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
+	udev_enumerate_add_match_sysname(enumerate, "js*");
+	udev_enumerate_scan_devices(enumerate);
+
+	devices = udev_enumerate_get_list_entry(enumerate);
+	udev_list_entry_foreach(dev_list_entry, devices) {
+		const char *devname;
+		struct udev_device *js_dev;
+		struct udev_device *input_parent;
+		const char *input_phys;
+
+		devname = udev_list_entry_get_name(dev_list_entry);
+		js_dev = udev_device_new_from_syspath(udev_device_get_udev(udevice),
+							devname);
+
+		input_parent = udev_device_get_parent_with_subsystem_devtype(js_dev,
+							"input", NULL);
+		if (!input_parent) {
+			error("%s:%s() cannot get parent input device.",
+				__FILE__, __func__);
+			continue;
+		}
+
+		/* check this is the joystick relative to
+		 * the hidraw device above */
+		input_phys = udev_device_get_sysattr_value(input_parent,
+								"phys");
+		if (g_strcmp0(input_phys, hid_phys) == 0) {
+			js_num = atoi(udev_device_get_sysnum(js_dev)) + 1;
+			DBG("joypad device_num: %d", js_num);
+			DBG("hidraw_node: %s", hidraw_node);
+		}
+
+		udev_device_unref(js_dev);
+	}
+
+	udev_enumerate_unref(enumerate);
+
+	fd = open(hidraw_node, O_RDWR);
+	if (fd < 0) {
+		error("%s:%s() hidraw open", __FILE__, __func__);
+		return;
+	}
+
+	is_usb = is_usb_peripheral(hid_id);
+	if (is_usb) {
+		char *adapter_bdaddr;
+
+		adapter_bdaddr = btd_adapter_get_default_address();
+		if (adapter_bdaddr == NULL) {
+			error("No adapters, exiting");
+			return;
+		}
+
+		DBG("Adapter bdaddr %s", adapter_bdaddr);
+
+		peripheral_pair(fd, adapter_bdaddr, peripheral);
+		free(adapter_bdaddr);
+	}
+
+	if (js_num > 0)
+		set_controller_number(fd, js_num);
+
+	close(fd);
+}
+
+static gboolean device_event_idle(struct udev_device *udevice)
+{
+	handle_device_plug(udevice);
+	udev_device_unref(udevice);
+	return FALSE;
+}
+
+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) {
+		udev_device_unref(udevice);
+		goto out;
+	}
+
+	/* Give UDEV some time to load kernel modules */
+	g_timeout_add_seconds(1, (GSourceFunc) device_event_idle, udevice);
+
+out:
+	return TRUE;
+}
+
+static int playstation_peripheral_init(void)
+{
+	GIOChannel *channel;
+
+	DBG("Setup Playstation peripheral plugin");
+
+	ctx = udev_new();
+	monitor = udev_monitor_new_from_netlink(ctx, "udev");
+	if (monitor == NULL) {
+		error("%s:%s() Could not get udev monitor",
+			__FILE__, __func__);
+		return -1;
+	}
+
+	/* Listen for newly connected hidraw interfaces */
+	udev_monitor_filter_add_match_subsystem_devtype(monitor,
+							"hidraw", 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 playstation_peripheral_exit(void)
+{
+	DBG("Cleanup Playstation peripheral 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(playstation_peripheral, VERSION,
+			BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+			playstation_peripheral_init,
+			playstation_peripheral_exit)
-- 
1.7.10.4


From 0b2c7efd2105f793170a8d0b90eaf49647315afb Mon Sep 17 00:00:00 2001
From: Antonio Ospite <ospite@studenti.unina.it>
Date: Fri, 5 Aug 2011 13:06:27 +0200
Subject: [PATCH 4/5] XXX: plugins/playstation-peripheral: Wait for the PS
 button before setting the LEDs
X-Face: z*RaLf`X<@C75u6Ig9}{oW$H;1_\2t5)({*|jhM<pyWR#k60!#=#>/Vb;]yA5<GWI5`6u&+
 ;6b'@y|8w"wB;4/e!7wYYrcqdJFY,~%Gk_4]cq$Ei/7<j&N3ah(m`ku?pX.&+~:_/wC~dwn^)MizBG
 !pE^+iDQQ1yC6^,)YDKkxDd!T>\I~93>J<_`<4)A{':UrE

Wait for actual input events, that is PS button has been pressed, before
setting the LEDs to indicate the controller number.
This makes setting LEDs look like on the PS3.

NOTE: This change is experimental, the logic is too simple and can fail when
multiple peripherals are handled. Don't squash it into other commits.
---
 plugins/playstation-peripheral.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/plugins/playstation-peripheral.c b/plugins/playstation-peripheral.c
index 9238e92..c7d4bdf 100644
--- a/plugins/playstation-peripheral.c
+++ b/plugins/playstation-peripheral.c
@@ -318,8 +318,16 @@ static void handle_device_plug(struct udev_device *udevice)
 		free(adapter_bdaddr);
 	}
 
-	if (js_num > 0)
+	if (js_num > 0) {
+		char c;
+
+		/* wait for events before setting leds */
+		if (read(fd, &c, 1) != 1)
+			error("%s:%s(): read error: %s",  __FILE__, __func__,
+			      strerror(errno));
+
 		set_controller_number(fd, js_num);
+	}
 
 	close(fd);
 }
-- 
1.7.10.4


From 8e72267e7af9fb223a6089e36d627d66b66924f9 Mon Sep 17 00:00:00 2001
From: Antonio Ospite <ospite@studenti.unina.it>
Date: Mon, 10 Oct 2011 22:40:49 +0200
Subject: [PATCH 5/5] XXX Add src/bluetoothd to plugins_playstation_peripheral
 dependencies
X-Face: z*RaLf`X<@C75u6Ig9}{oW$H;1_\2t5)({*|jhM<pyWR#k60!#=#>/Vb;]yA5<GWI5`6u&+
 ;6b'@y|8w"wB;4/e!7wYYrcqdJFY,~%Gk_4]cq$Ei/7<j&N3ah(m`ku?pX.&+~:_/wC~dwn^)MizBG
 !pE^+iDQQ1yC6^,)YDKkxDd!T>\I~93>J<_`<4)A{':UrE

NOTE: this change can be useful during development when we change bluez code
but it is probably overkill for the final submission. Don't squash it into
other commits.
---
 Makefile.am |    1 +
 1 file changed, 1 insertion(+)

diff --git a/Makefile.am b/Makefile.am
index a78a8ba..a14d678 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -114,6 +114,7 @@ include Makefile.plugins
 
 if PLAYSTATION_PERIPHERAL_PLUGIN
 plugin_LTLIBRARIES += plugins/playstation-peripheral.la
+plugins_playstation_peripheral_la_DEPENDENCIES = src/bluetoothd
 plugins_playstation_peripheral_la_SOURCES = plugins/playstation-peripheral.c plugins/playstation-peripheral-hid.c
 plugins_playstation_peripheral_la_LDFLAGS = -module -avoid-version -no-undefined @UDEV_LIBS@
 plugins_playstation_peripheral_la_CFLAGS = -fvisibility=hidden @DBUS_CFLAGS@ @GLIB_CFLAGS@ @UDEV_CFLAGS@
-- 
1.7.10.4