| From: Antonio Ospite <ospite-aNJ+ML1ZbiP93QAQaVx+gl6hYfS7NtTn@public.gmane.org> |
| Subject: [PATCH BlueZ v2 3/3] Add playstation-peripheral plugin: USB pairing and LEDs settings |
| Date: Wed, 18 Apr 2012 11:38:11 +0200 |
| |
| 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-0MeiytkfxGOsTnJN9+BGXg@public.gmane.org> |
| Signed-off-by: Antonio Ospite <ospite-aNJ+ML1ZbiP93QAQaVx+gl6hYfS7NtTn@public.gmane.org> |
| --- |
| |
| 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/autopair.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(WIIMOTEPLUGIN, test "${wiimote_enable}" = "yes") |
| AM_CONDITIONAL(THERMOMETERPLUGIN, test "${thermometer_enable}" = "yes") |
| AM_CONDITIONAL(AUTOPAIRPLUGIN, test "${autopair_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-aNJ+ML1ZbiP93QAQaVx+gl6hYfS7NtTn@public.gmane.org> |
| + * |
| + * |
| + * 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-0MeiytkfxGOsTnJN9+BGXg@public.gmane.org> |
| + * Copyright (C) 2011 Antonio Ospite <ospite-aNJ+ML1ZbiP93QAQaVx+gl6hYfS7NtTn@public.gmane.org> |
| + * |
| + * |
| + * 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 |
| |