[PATCH BlueZ v2 3/3] Add playstation-peripheral plugin: USB pairing and LEDs settings

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
Signed-off-by: Antonio Ospite <ospite@xxxxxxxxxxxxxxxxx>
---

For the first review round plugins/playstation-peripheral.c is the most
interesting file, in particular the handle_device_plug() and
peripheral_pair() functions.

 Makefile.am                          |    7 +
 acinclude.m4                         |   10 +
 plugins/playstation-peripheral-hid.c |  263 ++++++++++++++++++++++++
 plugins/playstation-peripheral-hid.h |   10 +
 plugins/playstation-peripheral.c     |  376 ++++++++++++++++++++++++++++++++++
 5 files changed, 666 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 62705f6..61c7a07 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -267,6 +267,13 @@ builtin_modules += dbusoob
 builtin_sources += plugins/dbusoob.c
 endif
 
+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/acinclude.m4 b/acinclude.m4
index dcf9a48..06efe2a 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -176,6 +176,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	sndfile_enable=${sndfile_found}
 	hal_enable=no
 	usb_enable=${usb_found}
+	playstation_peripheral_enable=${udev_found}
 	alsa_enable=${alsa_found}
 	gstreamer_enable=${gstreamer_found}
 	audio_enable=yes
@@ -265,6 +266,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		usb_enable=${enableval}
 	])
 
+	AC_ARG_ENABLE(playstation_peripheral, AC_HELP_STRING([--enable-playstation-peripheral], [enable playstation-peripheral plugin]), [
+		playstation_peripheral_enable=${enableval}
+	])
+
 	AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], [install Bluetooth utilities]), [
 		tools_enable=${enableval}
 	])
@@ -360,6 +365,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 		AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.])
 	fi
 
+	if (test "${playstation_peripheral_enable}" = "yes" && test "${udev_found}" = "yes"); then
+		AC_DEFINE(HAVE_PLAYSTATION_PERIPHERAL_PLUGIN, 1, [Define to 1 if you have playstation-peripheral plugin.])
+	fi
+
 	AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes")
 	AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes")
 	AM_CONDITIONAL(SBC, test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes" ||
@@ -392,4 +401,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
 	AM_CONDITIONAL(WIIMOTEPLUGIN, test "${wiimote_enable}" = "yes")
 	AM_CONDITIONAL(GATTMODULES, test "${gatt_enable}" = "yes")
+	AM_CONDITIONAL(PLAYSTATION_PERIPHERAL_PLUGIN, test "${playstation_peripheral_enable}" = "yes" && test "${udev_found}" = "yes")
 ])
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@xxxxxxxxxxxxxxxxx>
+ *
+ *
+ * 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..90d69ee
--- /dev/null
+++ b/plugins/playstation-peripheral.c
@@ -0,0 +1,376 @@
+/*
+ * playstation peripheral plugin: support for Playstation peripherals
+ *
+ * Copyright (C) 2009  Bastien Nocera <hadess@xxxxxxxxxx>
+ * Copyright (C) 2011  Antonio Ospite <ospite@xxxxxxxxxxxxxxxxx>
+ *
+ *
+ * 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
+ */
+
+#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 "manager.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(const char *adapter_address,
+					const char *device_address,
+					struct playstation_peripheral *peripheral)
+{
+	int ret = 0;
+
+	ret = btd_device_set_trusted(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);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+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_manager_get_default_adapter_address_str();
+		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

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux