From: Marcel Holtmann <marcel@holtmann.org>
To: Bastien Nocera <hadess@hadess.net>
Cc: BlueZ development <linux-bluetooth@vger.kernel.org>
Subject: Re: [PATCH] Add sixaxis cable-pairing plugin
Date: Sun, 11 Oct 2009 11:40:54 +0200 [thread overview]
Message-ID: <1255254054.19127.63.camel@localhost.localdomain> (raw)
In-Reply-To: <1255094350.4201.30.camel@localhost.localdomain>
Hi Bastien,
> 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
> +
since it is not really a generic cable pairing. I prefer that we just
call this SIXPAIR and sixpair.c
> 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)"\"
This needs changing. I wanna have a separate udev and libusb1 check.
However I can do that for you in case there are problems.
> 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;
> +}
These are all blocking functions. That worries me a little bit in case
we have weird timeouts. I know that libusb1 supports async operation. Is
it possible to use that?
> +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);
> +}
It has been a long time I looked through this stuff. So does the
controller user USB control messages or are they just plain HID message.
If HID, then I would prefer if we use hidraw and don't have to detach
the kernel driver.
> +
> +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);
> +}
What we are missing from libusb1 is a function to create the device
handle directly from a udev syspath. The enumeration is actually pretty
bad since it wakes up all USB devices on the all busses.
> +
> +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;
> +}
Why is the timeout thing needed? Or do you actually want idle callback?
Regards
Marcel
next prev parent reply other threads:[~2009-10-11 9:40 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-10-09 13:19 [PATCH] Add sixaxis cable-pairing plugin Bastien Nocera
2009-10-11 9:40 ` Marcel Holtmann [this message]
-- strict thread matches above, loose matches on Subject: below --
2009-06-04 9:28 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1255254054.19127.63.camel@localhost.localdomain \
--to=marcel@holtmann.org \
--cc=hadess@hadess.net \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox