2017-09-04 18:12:04

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 1/9] plugins/sixaxis: Remove LEDs handling

It's done in the kernel since 2014 in linux kernel commit
8025087acf9d2b941bae93b3e0967560e7e03e87
---
plugins/sixaxis.c | 295 +-----------------------------------------------------
1 file changed, 5 insertions(+), 290 deletions(-)

diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index 7e3c950b4..7d3a8f3ac 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -71,17 +71,6 @@ static const struct {
},
};

-struct leds_data {
- char *syspath_prefix;
- uint8_t bitmap;
-};
-
-static void leds_data_destroy(struct leds_data *data)
-{
- free(data->syspath_prefix);
- free(data);
-}
-
static struct udev *ctx = NULL;
static struct udev_monitor *monitor = NULL;
static guint watch_id = 0;
@@ -146,115 +135,6 @@ static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
return ret;
}

-static uint8_t calc_leds_bitmap(int number)
-{
- uint8_t bitmap = 0;
-
- /* TODO we could support up to 10 (1 + 2 + 3 + 4) */
- if (number > 7)
- return bitmap;
-
- if (number > 4) {
- bitmap |= 0x10;
- number -= 4;
- }
-
- bitmap |= 0x01 << number;
-
- return bitmap;
-}
-
-static void set_leds_hidraw(int fd, uint8_t leds_bitmap)
-{
- /*
- * the total time the led is active (0xff means forever)
- * | duty_length: cycle time in deciseconds (0 - "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 means 100%)
- * | | | | |
- * 0xff, 0x27, 0x10, 0x00, 0x32,
- */
- uint8_t leds_report[] = {
- 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, /* rumble values TBD */
- 0x00, 0x00, 0x00, 0x00, 0x00, /* 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 ret;
-
- leds_report[10] = leds_bitmap;
-
- ret = write(fd, leds_report, sizeof(leds_report));
- if (ret == sizeof(leds_report))
- return;
-
- if (ret < 0)
- error("sixaxis: failed to set LEDS (%s)", strerror(errno));
- else
- error("sixaxis: failed to set LEDS (%d bytes written)", ret);
-}
-
-static bool set_leds_sysfs(struct leds_data *data)
-{
- int i;
-
- if (!data->syspath_prefix)
- return false;
-
- /* start from 1, LED0 is never used */
- for (i = 1; i <= 4; i++) {
- char path[PATH_MAX] = { 0 };
- char buf[2] = { 0 };
- int fd;
- int ret;
-
- snprintf(path, PATH_MAX, "%s%d/brightness",
- data->syspath_prefix, i);
-
- fd = open(path, O_WRONLY);
- if (fd < 0) {
- error("sixaxis: cannot open %s (%s)", path,
- strerror(errno));
- return false;
- }
-
- buf[0] = '0' + !!(data->bitmap & (1 << i));
- ret = write(fd, buf, sizeof(buf));
- close(fd);
- if (ret != sizeof(buf))
- return false;
- }
-
- return true;
-}
-
-static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
- gpointer user_data)
-{
- struct leds_data *data = user_data;
-
- if (!data)
- return FALSE;
-
- if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
- goto out;
-
- if(!set_leds_sysfs(data)) {
- int fd = g_io_channel_unix_get_fd(channel);
- set_leds_hidraw(fd, data->bitmap);
- }
-
-out:
- leds_data_destroy(data);
-
- return FALSE;
-}
-
static bool setup_device(int fd, int index, struct btd_adapter *adapter)
{
char device_addr[18], master_addr[18], adapter_addr[18];
@@ -269,8 +149,7 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
return false;

/* This can happen if controller was plugged while already connected
- * eg. to charge up battery.
- * Don't set LEDs in that case, hence return false */
+ * eg. to charge up battery. */
device = btd_adapter_find_device(adapter, &device_bdaddr,
BDADDR_BREDR);
if (device && btd_device_is_connected(device))
@@ -307,152 +186,6 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
return true;
}

-static int get_js_number(struct udev_device *udevice)
-{
- struct udev_list_entry *devices, *dev_list_entry;
- struct udev_enumerate *enumerate;
- struct udev_device *hid_parent;
- const char *hidraw_node;
- const char *hid_id;
- int number = 0;
-
- hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
- "hid", NULL);
-
- /*
- * Look for HID_UNIQ first for the correct behavior via BT, if
- * HID_UNIQ is not available it means the USB bus is being used and we
- * can rely on HID_PHYS.
- */
- hid_id = udev_device_get_property_value(hid_parent, "HID_UNIQ");
- if (!hid_id)
- hid_id = udev_device_get_property_value(hid_parent,
- "HID_PHYS");
-
- hidraw_node = udev_device_get_devnode(udevice);
- if (!hid_id || !hidraw_node)
- return 0;
-
- 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) {
- struct udev_device *input_parent;
- struct udev_device *js_dev;
- const char *input_id;
- const char *devname;
-
- 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)
- goto next;
-
- /* check if this is the joystick relative to the hidraw device
- * above */
- input_id = udev_device_get_sysattr_value(input_parent, "uniq");
-
- /*
- * A strlen() check is needed because input device over USB
- * have the UNIQ attribute defined but with an empty value.
- */
- if (!input_id || strlen(input_id) == 0)
- input_id = udev_device_get_sysattr_value(input_parent,
- "phys");
-
- if (!input_id)
- goto next;
-
- if (!strcmp(input_id, hid_id)) {
- number = atoi(udev_device_get_sysnum(js_dev));
-
- /* joystick numbers start from 0, leds from 1 */
- number++;
-
- udev_device_unref(js_dev);
- break;
- }
-next:
- udev_device_unref(js_dev);
- }
-
- udev_enumerate_unref(enumerate);
-
- return number;
-}
-
-static char *get_leds_syspath_prefix(struct udev_device *udevice)
-{
- struct udev_list_entry *dev_list_entry;
- struct udev_enumerate *enumerate;
- struct udev_device *hid_parent;
- const char *syspath;
- char *syspath_prefix;
-
- hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
- "hid", NULL);
-
- enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
- udev_enumerate_add_match_parent(enumerate, hid_parent);
- udev_enumerate_add_match_subsystem(enumerate, "leds");
- udev_enumerate_scan_devices(enumerate);
-
- dev_list_entry = udev_enumerate_get_list_entry(enumerate);
- if (!dev_list_entry) {
- syspath_prefix = NULL;
- goto out;
- }
-
- syspath = udev_list_entry_get_name(dev_list_entry);
-
- /*
- * All the sysfs paths of the LEDs have the same structure, just the
- * number changes, so strip it and store only the common prefix.
- *
- * Subtracting 1 here means assuming that the LED number is a single
- * digit, this is safe as the kernel driver only exposes 4 LEDs.
- */
- syspath_prefix = strndup(syspath, strlen(syspath) - 1);
-
-out:
- udev_enumerate_unref(enumerate);
-
- return syspath_prefix;
-}
-
-static struct leds_data *get_leds_data(struct udev_device *udevice)
-{
- struct leds_data *data;
- int number;
-
- number = get_js_number(udevice);
- DBG("number %d", number);
-
- data = malloc0(sizeof(*data));
- if (!data)
- return NULL;
-
- data->bitmap = calc_leds_bitmap(number);
- if (data->bitmap == 0) {
- leds_data_destroy(data);
- return NULL;
- }
-
- /*
- * It's OK if this fails, set_leds_hidraw() will be used in
- * case data->syspath_prefix is NULL.
- */
- data->syspath_prefix = get_leds_syspath_prefix(udevice);
-
- return data;
-}
-
static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
{
struct udev_device *hid_parent;
@@ -481,7 +214,6 @@ static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
static void device_added(struct udev_device *udevice)
{
struct btd_adapter *adapter;
- GIOChannel *io;
uint16_t bus;
int index;
int fd;
@@ -493,6 +225,8 @@ static void device_added(struct udev_device *udevice)
index = get_supported_device(udevice, &bus);
if (index < 0)
return;
+ if (bus != BUS_USB)
+ return;

info("sixaxis: compatible device connected: %s (%04X:%04X)",
devices[index].name, devices[index].vid,
@@ -502,27 +236,8 @@ static void device_added(struct udev_device *udevice)
if (fd < 0)
return;

- io = g_io_channel_unix_new(fd);
-
- switch (bus) {
- case BUS_USB:
- if (!setup_device(fd, index, adapter))
- break;
-
- /* fall through */
- case BUS_BLUETOOTH:
- /* wait for events before setting leds */
- g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- setup_leds, get_leds_data(udevice));
-
- break;
- default:
- DBG("uknown bus type (%u)", bus);
- break;
- }
-
- g_io_channel_set_close_on_unref(io, TRUE);
- g_io_channel_unref(io);
+ setup_device(fd, index, adapter);
+ close(fd);
}

static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
--
2.14.1



2017-09-27 09:14:24

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH 3/9] sixaxis: Ask user whether cable configuration should be allowed

Hi Bastien,

On Monday, 4 September 2017 20:12:06 CEST Bastien Nocera wrote:
> Previously, users doing cable configuration of Sixaxis PS3 controllers
> would only get asked whether a device was allowed to connect to the
> computer when switching it to Bluetooth mode: unplugging it, and
> pressing the PS button.
>
> Instead, we should ask the user straight away, through the agent,
> whether the pad should be allowed to connect.
>
> This makes it easier to setup those devices, while keeping security.
> ---
> plugins/sixaxis.c | 83
> +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63
> insertions(+), 20 deletions(-)
>
> diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
> index 7d3a8f3ac..c8305d52f 100644
> --- a/plugins/sixaxis.c
> +++ b/plugins/sixaxis.c
> @@ -44,6 +44,7 @@
>
> #include "src/adapter.h"
> #include "src/device.h"
> +#include "src/agent.h"
> #include "src/plugin.h"
> #include "src/log.h"
> #include "src/shared/util.h"
> @@ -71,6 +72,13 @@ static const struct {
> },
> };
>
> +struct authentication_closure {
> + struct btd_adapter *adapter;
> + struct btd_device *device;
> + int fd;
> + char device_addr[18];
> +};
> +
> static struct udev *ctx = NULL;
> static struct udev_monitor *monitor = NULL;
> static guint watch_id = 0;
> @@ -135,19 +143,51 @@ static int set_master_bdaddr(int fd, const bdaddr_t
> *bdaddr) return ret;
> }
>
> +static void agent_auth_cb(DBusError *derr,
> + void *user_data)
> +{
> + struct authentication_closure *closure = user_data;
> + char master_addr[18], adapter_addr[18];
> + bdaddr_t master_bdaddr;
> + const bdaddr_t *adapter_bdaddr;
> +
> + if (derr != NULL) {
> + DBG("Agent replied negatively, removing temporary device");
> + goto error;
> + }
> +
> + btd_device_set_temporary(closure->device, false);
> +
> + if (get_master_bdaddr(closure->fd, &master_bdaddr) < 0)
> + goto error;
> +
> + adapter_bdaddr = btd_adapter_get_address(closure->adapter);
> + if (bacmp(adapter_bdaddr, &master_bdaddr)) {
> + if (set_master_bdaddr(closure->fd, adapter_bdaddr) < 0)
> + goto error;
> + }
> +
> + ba2str(&master_bdaddr, master_addr);
> + ba2str(adapter_bdaddr, adapter_addr);
> + DBG("remote %s old_master %s new_master %s",
> + closure->device_addr, master_addr, adapter_addr);
> +
> +error:
> + close(closure->fd);
> + g_free(closure);
> +}
> +
> static bool setup_device(int fd, int index, struct btd_adapter *adapter)
> {
> - char device_addr[18], master_addr[18], adapter_addr[18];
> - bdaddr_t device_bdaddr, master_bdaddr;
> + char device_addr[18];
> + bdaddr_t device_bdaddr;
> const bdaddr_t *adapter_bdaddr;
> struct btd_device *device;
> + struct authentication_closure *closure;
>
> if (get_device_bdaddr(fd, &device_bdaddr) < 0)
> return false;
>
> - if (get_master_bdaddr(fd, &master_bdaddr) < 0)
> - return false;
> -
> /* This can happen if controller was plugged while already connected
> * eg. to charge up battery. */
> device = btd_adapter_find_device(adapter, &device_bdaddr,
> @@ -155,25 +195,14 @@ static bool setup_device(int fd, int index, struct
> btd_adapter *adapter) if (device && btd_device_is_connected(device))
> return false;
>
> - adapter_bdaddr = btd_adapter_get_address(adapter);
> -
> - if (bacmp(adapter_bdaddr, &master_bdaddr)) {
> - if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
> - return false;
> - }
> -
> ba2str(&device_bdaddr, device_addr);
> - ba2str(&master_bdaddr, master_addr);
> - ba2str(adapter_bdaddr, adapter_addr);
> - DBG("remote %s old_master %s new_master %s",
> - device_addr, master_addr, adapter_addr);
>
> device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
>
> if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
> (GCompareFunc)strcasecmp)) {
> DBG("device %s already known, skipping", device_addr);
> - return true;
> + return false;
> }
>
> info("sixaxis: setting up new device");
> @@ -181,7 +210,20 @@ static bool setup_device(int fd, int index, struct
> btd_adapter *adapter) btd_device_device_set_name(device,
> devices[index].name);
> btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
> devices[index].pid, devices[index].version);
> - btd_device_set_temporary(device, false);
> + btd_device_set_temporary(device, true);
> +
> + closure = g_try_new0(struct authentication_closure, 1);
> + if (!closure) {
> + btd_adapter_remove_device(adapter, device);
> + return false;
> + }
> + closure->adapter = adapter;
> + closure->device = device;
> + closure->fd = fd;
> + memcpy(&closure->device_addr, device_addr, sizeof(device_addr));
> + adapter_bdaddr = btd_adapter_get_address(adapter);
> + btd_request_authorization_cable_configured(adapter_bdaddr, &device_bdaddr,
> + HID_UUID, agent_auth_cb, closure);
>
> return true;
> }
> @@ -236,8 +278,9 @@ static void device_added(struct udev_device *udevice)
> if (fd < 0)
> return;
>
> - setup_device(fd, index, adapter);
> - close(fd);
> + /* Only close the fd if an authentication is not pending */
> + if (!setup_device(fd, index, adapter))
> + close(fd);
> }
>
> static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,

So user will be asked for consent after DS3 was configured? Not that I have
strong opinion on this but did you consider configuring it only after user
gave consent?

--
pozdrawiam
Szymon Janc

2017-09-27 09:12:04

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH 3/9] sixaxis: Ask user whether cable configuration should be allowed

Hi Bastien,

On Monday, 4 September 2017 20:12:06 CEST Bastien Nocera wrote:
> Previously, users doing cable configuration of Sixaxis PS3 controllers
> would only get asked whether a device was allowed to connect to the
> computer when switching it to Bluetooth mode: unplugging it, and
> pressing the PS button.
>
> Instead, we should ask the user straight away, through the agent,
> whether the pad should be allowed to connect.
>
> This makes it easier to setup those devices, while keeping security.
> ---
> plugins/sixaxis.c | 83
> +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63
> insertions(+), 20 deletions(-)
>
> diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
> index 7d3a8f3ac..c8305d52f 100644
> --- a/plugins/sixaxis.c
> +++ b/plugins/sixaxis.c
> @@ -44,6 +44,7 @@
>
> #include "src/adapter.h"
> #include "src/device.h"
> +#include "src/agent.h"
> #include "src/plugin.h"
> #include "src/log.h"
> #include "src/shared/util.h"
> @@ -71,6 +72,13 @@ static const struct {
> },
> };
>
> +struct authentication_closure {
> + struct btd_adapter *adapter;
> + struct btd_device *device;
> + int fd;
> + char device_addr[18];

If this is needed only for debug then just convert address to string in place
where it is printed.

> +};
> +
> static struct udev *ctx = NULL;
> static struct udev_monitor *monitor = NULL;
> static guint watch_id = 0;
> @@ -135,19 +143,51 @@ static int set_master_bdaddr(int fd, const bdaddr_t
> *bdaddr) return ret;
> }
>
> +static void agent_auth_cb(DBusError *derr,
> + void *user_data)
> +{
> + struct authentication_closure *closure = user_data;
> + char master_addr[18], adapter_addr[18];
> + bdaddr_t master_bdaddr;
> + const bdaddr_t *adapter_bdaddr;
> +
> + if (derr != NULL) {
> + DBG("Agent replied negatively, removing temporary device");
> + goto error;
> + }
> +
> + btd_device_set_temporary(closure->device, false);
> +
> + if (get_master_bdaddr(closure->fd, &master_bdaddr) < 0)
> + goto error;
> +
> + adapter_bdaddr = btd_adapter_get_address(closure->adapter);
> + if (bacmp(adapter_bdaddr, &master_bdaddr)) {
> + if (set_master_bdaddr(closure->fd, adapter_bdaddr) < 0)
> + goto error;
> + }
> +
> + ba2str(&master_bdaddr, master_addr);
> + ba2str(adapter_bdaddr, adapter_addr);
> + DBG("remote %s old_master %s new_master %s",
> + closure->device_addr, master_addr, adapter_addr);
> +
> +error:
> + close(closure->fd);
> + g_free(closure);
> +}
> +
> static bool setup_device(int fd, int index, struct btd_adapter *adapter)
> {
> - char device_addr[18], master_addr[18], adapter_addr[18];
> - bdaddr_t device_bdaddr, master_bdaddr;
> + char device_addr[18];
> + bdaddr_t device_bdaddr;
> const bdaddr_t *adapter_bdaddr;
> struct btd_device *device;
> + struct authentication_closure *closure;
>
> if (get_device_bdaddr(fd, &device_bdaddr) < 0)
> return false;
>
> - if (get_master_bdaddr(fd, &master_bdaddr) < 0)
> - return false;
> -
> /* This can happen if controller was plugged while already connected
> * eg. to charge up battery. */
> device = btd_adapter_find_device(adapter, &device_bdaddr,
> @@ -155,25 +195,14 @@ static bool setup_device(int fd, int index, struct
> btd_adapter *adapter) if (device && btd_device_is_connected(device))
> return false;
>
> - adapter_bdaddr = btd_adapter_get_address(adapter);
> -
> - if (bacmp(adapter_bdaddr, &master_bdaddr)) {
> - if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
> - return false;
> - }
> -
> ba2str(&device_bdaddr, device_addr);
> - ba2str(&master_bdaddr, master_addr);
> - ba2str(adapter_bdaddr, adapter_addr);
> - DBG("remote %s old_master %s new_master %s",
> - device_addr, master_addr, adapter_addr);
>
> device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
>
> if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
> (GCompareFunc)strcasecmp)) {
> DBG("device %s already known, skipping", device_addr);
> - return true;
> + return false;
> }
>
> info("sixaxis: setting up new device");
> @@ -181,7 +210,20 @@ static bool setup_device(int fd, int index, struct
> btd_adapter *adapter) btd_device_device_set_name(device,
> devices[index].name);
> btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
> devices[index].pid, devices[index].version);
> - btd_device_set_temporary(device, false);
> + btd_device_set_temporary(device, true);
> +
> + closure = g_try_new0(struct authentication_closure, 1);

Just use new0() instead.

> + if (!closure) {
> + btd_adapter_remove_device(adapter, device);
> + return false;
> + }
> + closure->adapter = adapter;
> + closure->device = device;
> + closure->fd = fd;
> + memcpy(&closure->device_addr, device_addr, sizeof(device_addr));
> + adapter_bdaddr = btd_adapter_get_address(adapter);
> + btd_request_authorization_cable_configured(adapter_bdaddr, &device_bdaddr,
> + HID_UUID, agent_auth_cb, closure);
>
> return true;
> }
> @@ -236,8 +278,9 @@ static void device_added(struct udev_device *udevice)
> if (fd < 0)
> return;
>
> - setup_device(fd, index, adapter);
> - close(fd);
> + /* Only close the fd if an authentication is not pending */
> + if (!setup_device(fd, index, adapter))
> + close(fd);
> }
>
> static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,


--
pozdrawiam
Szymon Janc

2017-09-27 09:12:01

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH 8/9] plugins/sixaxis: Add support for DualShock 4/PS4 cable pairing

Hi Bastien,

On Monday, 4 September 2017 20:12:11 CEST Bastien Nocera wrote:
> From: Juha Kuikka <[email protected]>
>
> This patch adds support for "pairing" a Dualshock4 controller over USB
> into the sixaxis plugin, similarly to what the Sixaxis/PS3 controller
> supported.
>
> Actual bonding happens on first connection, but the device will be
> marked as trusted when the agent replies.
>
> This patch is based on DS4 supprt for sixpair tool by t0xicCode:
> https://github.com/t0xicCode/sixpair/commit/975c38cb6cd61a2f0be350714f0f64ce
> f5f0432c ---
> plugins/sixaxis.c | 73
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72
> insertions(+), 1 deletion(-)
>
> diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
> index b62834d72..eb15acb92 100644
> --- a/plugins/sixaxis.c
> +++ b/plugins/sixaxis.c
> @@ -83,10 +83,34 @@ static int sixaxis_get_device_bdaddr(int fd, bdaddr_t
> *bdaddr) return 0;
> }
>
> +static int ds4_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
> +{
> + uint8_t buf[7];
> + int ret;
> +
> + memset(buf, 0, sizeof(buf));
> +
> + buf[0] = 0x81;
> +
> + ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
> + if (ret < 0) {
> + error("ds4: failed to read device address (%s)",
> + strerror(errno));

Lets prefix all sixaxis error/info logs with "sixaxis:"

> + return ret;
> + }
> +
> + /* address is little-endian on DS4 */
> + bacpy(bdaddr, (bdaddr_t*) (buf + 1));
> +
> + return 0;
> +}
> +
> static int get_device_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType
> type) {
> if (type == CABLE_PAIRING_SIXAXIS)
> return sixaxis_get_device_bdaddr(fd, bdaddr);
> + else if (type == CABLE_PAIRING_DS4)
> + return ds4_get_device_bdaddr(fd, bdaddr);
> return -1;
> }
>
> @@ -111,10 +135,34 @@ static int sixaxis_get_master_bdaddr(int fd, bdaddr_t
> *bdaddr) return 0;
> }
>
> +static int ds4_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
> +{
> + uint8_t buf[16];
> + int ret;
> +
> + memset(buf, 0, sizeof(buf));
> +
> + buf[0] = 0x12;
> +
> + ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
> + if (ret < 0) {
> + error("ds4: failed to read master address (%s)",
> + strerror(errno));
> + return ret;
> + }
> +
> + /* address is little-endian on DS4 */
> + bacpy(bdaddr, (bdaddr_t*) (buf + 10));
> +
> + return 0;
> +}
> +
> static int get_master_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType
> type) {
> if (type == CABLE_PAIRING_SIXAXIS)
> return sixaxis_get_master_bdaddr(fd, bdaddr);
> + else if (type == CABLE_PAIRING_DS4)
> + return ds4_get_master_bdaddr(fd, bdaddr);
> return -1;
> }
>
> @@ -136,11 +184,33 @@ static int sixaxis_set_master_bdaddr(int fd, const
> bdaddr_t *bdaddr) return ret;
> }
>
> +static int ds4_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
> +{
> + uint8_t buf[23];
> + int ret;
> +
> + buf[0] = 0x13;
> + bacpy((bdaddr_t*) (buf + 1), bdaddr);
> + /* TODO: we could put the key here but
> + there is no way to force a re-loading
> + of link keys to the kernel from here. */
> + memset(buf + 7, 0, 16);
> +
> + ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
> + if (ret < 0)
> + error("ds4: failed to write master address (%s)",
> + strerror(errno));
> +
> + return ret;
> +}
> +
> static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr,
> CablePairingType type)
> {
> if (type == CABLE_PAIRING_SIXAXIS)
> return sixaxis_set_master_bdaddr(fd, bdaddr);
> + else if (type == CABLE_PAIRING_DS4)
> + return ds4_set_master_bdaddr(fd, bdaddr);
> return -1;
> }
>
> @@ -279,7 +349,8 @@ static void device_added(struct udev_device *udevice)
> &name,
> &source,
> &version);
> - if (type != CABLE_PAIRING_SIXAXIS)
> + if (type != CABLE_PAIRING_SIXAXIS &&
> + type != CABLE_PAIRING_DS4)
> return;
> if (bus != BUS_USB)
> return;


--
pozdrawiam
Szymon Janc

2017-09-27 09:07:43

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH 1/9] plugins/sixaxis: Remove LEDs handling

Hi Bastien,

On Monday, 4 September 2017 20:12:04 CEST Bastien Nocera wrote:
> It's done in the kernel since 2014 in linux kernel commit
> 8025087acf9d2b941bae93b3e0967560e7e03e87
> ---
> plugins/sixaxis.c | 295
> +----------------------------------------------------- 1 file changed, 5
> insertions(+), 290 deletions(-)
>
> diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
> index 7e3c950b4..7d3a8f3ac 100644
> --- a/plugins/sixaxis.c
> +++ b/plugins/sixaxis.c
> @@ -71,17 +71,6 @@ static const struct {
> },
> };
>
> -struct leds_data {
> - char *syspath_prefix;
> - uint8_t bitmap;
> -};
> -
> -static void leds_data_destroy(struct leds_data *data)
> -{
> - free(data->syspath_prefix);
> - free(data);
> -}
> -
> static struct udev *ctx = NULL;
> static struct udev_monitor *monitor = NULL;
> static guint watch_id = 0;
> @@ -146,115 +135,6 @@ static int set_master_bdaddr(int fd, const bdaddr_t
> *bdaddr) return ret;
> }
>
> -static uint8_t calc_leds_bitmap(int number)
> -{
> - uint8_t bitmap = 0;
> -
> - /* TODO we could support up to 10 (1 + 2 + 3 + 4) */
> - if (number > 7)
> - return bitmap;
> -
> - if (number > 4) {
> - bitmap |= 0x10;
> - number -= 4;
> - }
> -
> - bitmap |= 0x01 << number;
> -
> - return bitmap;
> -}
> -
> -static void set_leds_hidraw(int fd, uint8_t leds_bitmap)
> -{
> - /*
> - * the total time the led is active (0xff means forever)
> - * | duty_length: cycle time in deciseconds (0 - "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 means 100%)
> - * | | | | |
> - * 0xff, 0x27, 0x10, 0x00, 0x32,
> - */
> - uint8_t leds_report[] = {
> - 0x01,
> - 0x00, 0x00, 0x00, 0x00, 0x00, /* rumble values TBD */
> - 0x00, 0x00, 0x00, 0x00, 0x00, /* 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 ret;
> -
> - leds_report[10] = leds_bitmap;
> -
> - ret = write(fd, leds_report, sizeof(leds_report));
> - if (ret == sizeof(leds_report))
> - return;
> -
> - if (ret < 0)
> - error("sixaxis: failed to set LEDS (%s)", strerror(errno));
> - else
> - error("sixaxis: failed to set LEDS (%d bytes written)", ret);
> -}
> -
> -static bool set_leds_sysfs(struct leds_data *data)
> -{
> - int i;
> -
> - if (!data->syspath_prefix)
> - return false;
> -
> - /* start from 1, LED0 is never used */
> - for (i = 1; i <= 4; i++) {
> - char path[PATH_MAX] = { 0 };
> - char buf[2] = { 0 };
> - int fd;
> - int ret;
> -
> - snprintf(path, PATH_MAX, "%s%d/brightness",
> - data->syspath_prefix, i);
> -
> - fd = open(path, O_WRONLY);
> - if (fd < 0) {
> - error("sixaxis: cannot open %s (%s)", path,
> - strerror(errno));
> - return false;
> - }
> -
> - buf[0] = '0' + !!(data->bitmap & (1 << i));
> - ret = write(fd, buf, sizeof(buf));
> - close(fd);
> - if (ret != sizeof(buf))
> - return false;
> - }
> -
> - return true;
> -}
> -
> -static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
> - gpointer user_data)
> -{
> - struct leds_data *data = user_data;
> -
> - if (!data)
> - return FALSE;
> -
> - if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
> - goto out;
> -
> - if(!set_leds_sysfs(data)) {
> - int fd = g_io_channel_unix_get_fd(channel);
> - set_leds_hidraw(fd, data->bitmap);
> - }
> -
> -out:
> - leds_data_destroy(data);
> -
> - return FALSE;
> -}
> -
> static bool setup_device(int fd, int index, struct btd_adapter *adapter)
> {
> char device_addr[18], master_addr[18], adapter_addr[18];
> @@ -269,8 +149,7 @@ static bool setup_device(int fd, int index, struct
> btd_adapter *adapter) return false;
>
> /* This can happen if controller was plugged while already connected
> - * eg. to charge up battery.
> - * Don't set LEDs in that case, hence return false */
> + * eg. to charge up battery. */
> device = btd_adapter_find_device(adapter, &device_bdaddr,
> BDADDR_BREDR);
> if (device && btd_device_is_connected(device))
> @@ -307,152 +186,6 @@ static bool setup_device(int fd, int index, struct
> btd_adapter *adapter) return true;
> }
>
> -static int get_js_number(struct udev_device *udevice)
> -{
> - struct udev_list_entry *devices, *dev_list_entry;
> - struct udev_enumerate *enumerate;
> - struct udev_device *hid_parent;
> - const char *hidraw_node;
> - const char *hid_id;
> - int number = 0;
> -
> - hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
> - "hid", NULL);
> -
> - /*
> - * Look for HID_UNIQ first for the correct behavior via BT, if
> - * HID_UNIQ is not available it means the USB bus is being used and we
> - * can rely on HID_PHYS.
> - */
> - hid_id = udev_device_get_property_value(hid_parent, "HID_UNIQ");
> - if (!hid_id)
> - hid_id = udev_device_get_property_value(hid_parent,
> - "HID_PHYS");
> -
> - hidraw_node = udev_device_get_devnode(udevice);
> - if (!hid_id || !hidraw_node)
> - return 0;
> -
> - 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) {
> - struct udev_device *input_parent;
> - struct udev_device *js_dev;
> - const char *input_id;
> - const char *devname;
> -
> - 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)
> - goto next;
> -
> - /* check if this is the joystick relative to the hidraw device
> - * above */
> - input_id = udev_device_get_sysattr_value(input_parent, "uniq");
> -
> - /*
> - * A strlen() check is needed because input device over USB
> - * have the UNIQ attribute defined but with an empty value.
> - */
> - if (!input_id || strlen(input_id) == 0)
> - input_id = udev_device_get_sysattr_value(input_parent,
> - "phys");
> -
> - if (!input_id)
> - goto next;
> -
> - if (!strcmp(input_id, hid_id)) {
> - number = atoi(udev_device_get_sysnum(js_dev));
> -
> - /* joystick numbers start from 0, leds from 1 */
> - number++;
> -
> - udev_device_unref(js_dev);
> - break;
> - }
> -next:
> - udev_device_unref(js_dev);
> - }
> -
> - udev_enumerate_unref(enumerate);
> -
> - return number;
> -}
> -
> -static char *get_leds_syspath_prefix(struct udev_device *udevice)
> -{
> - struct udev_list_entry *dev_list_entry;
> - struct udev_enumerate *enumerate;
> - struct udev_device *hid_parent;
> - const char *syspath;
> - char *syspath_prefix;
> -
> - hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
> - "hid", NULL);
> -
> - enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
> - udev_enumerate_add_match_parent(enumerate, hid_parent);
> - udev_enumerate_add_match_subsystem(enumerate, "leds");
> - udev_enumerate_scan_devices(enumerate);
> -
> - dev_list_entry = udev_enumerate_get_list_entry(enumerate);
> - if (!dev_list_entry) {
> - syspath_prefix = NULL;
> - goto out;
> - }
> -
> - syspath = udev_list_entry_get_name(dev_list_entry);
> -
> - /*
> - * All the sysfs paths of the LEDs have the same structure, just the
> - * number changes, so strip it and store only the common prefix.
> - *
> - * Subtracting 1 here means assuming that the LED number is a single
> - * digit, this is safe as the kernel driver only exposes 4 LEDs.
> - */
> - syspath_prefix = strndup(syspath, strlen(syspath) - 1);
> -
> -out:
> - udev_enumerate_unref(enumerate);
> -
> - return syspath_prefix;
> -}
> -
> -static struct leds_data *get_leds_data(struct udev_device *udevice)
> -{
> - struct leds_data *data;
> - int number;
> -
> - number = get_js_number(udevice);
> - DBG("number %d", number);
> -
> - data = malloc0(sizeof(*data));
> - if (!data)
> - return NULL;
> -
> - data->bitmap = calc_leds_bitmap(number);
> - if (data->bitmap == 0) {
> - leds_data_destroy(data);
> - return NULL;
> - }
> -
> - /*
> - * It's OK if this fails, set_leds_hidraw() will be used in
> - * case data->syspath_prefix is NULL.
> - */
> - data->syspath_prefix = get_leds_syspath_prefix(udevice);
> -
> - return data;
> -}
> -
> static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
> {
> struct udev_device *hid_parent;
> @@ -481,7 +214,6 @@ static int get_supported_device(struct udev_device
> *udevice, uint16_t *bus) static void device_added(struct udev_device
> *udevice)
> {
> struct btd_adapter *adapter;
> - GIOChannel *io;
> uint16_t bus;
> int index;
> int fd;
> @@ -493,6 +225,8 @@ static void device_added(struct udev_device *udevice)
> index = get_supported_device(udevice, &bus);
> if (index < 0)
> return;
> + if (bus != BUS_USB)
> + return;
>
> info("sixaxis: compatible device connected: %s (%04X:%04X)",
> devices[index].name, devices[index].vid,
> @@ -502,27 +236,8 @@ static void device_added(struct udev_device *udevice)
> if (fd < 0)
> return;
>
> - io = g_io_channel_unix_new(fd);
> -
> - switch (bus) {
> - case BUS_USB:
> - if (!setup_device(fd, index, adapter))
> - break;
> -
> - /* fall through */
> - case BUS_BLUETOOTH:
> - /* wait for events before setting leds */
> - g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
> - setup_leds, get_leds_data(udevice));
> -
> - break;
> - default:
> - DBG("uknown bus type (%u)", bus);
> - break;
> - }
> -
> - g_io_channel_set_close_on_unref(io, TRUE);
> - g_io_channel_unref(io);
> + setup_device(fd, index, adapter);
> + close(fd);
> }
>
> static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,


This patch is now applied, thanks.

--
pozdrawiam
Szymon Janc

2017-09-05 10:37:46

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 4/9] plugins/sixaxis: Move device discovery to shared header

On Tue, 2017-09-05 at 11:13 +0200, Szymon Janc wrote:
> Hi Bastien,
>
> On 5 September 2017 at 11:03, Bastien Nocera <[email protected]>
> wrote:
> > On Mon, 2017-09-04 at 20:12 +0200, Bastien Nocera wrote:
> > > <snip>
> > > +
> > > +inline CablePairingType
> > > +get_pairing_type(uint16_t vid,
> > > + uint16_t pid,
> > > + char **name,
> > > + uint16_t *source,
> > > + uint16_t *version)
> >
> > I have a slightly updated version of that section. "static" is
> > missing
> > in front of the inline function for use with RPM build's strict
> > linker
> > settings, and I'm sure the indentation police would like to see
> > that
> > function return type and name on the same line ;)
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-
> > bluetooth" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
> I'm not sure why this is in header in first place ?

It should become clear if you look at the rest of the patchset.

As we discussed in Juha's version of this patchset, we want to share
this code between the sixaxis plugin, and the input profiles code. A
code header is the easiest way to do this without getting into build
conditionals.

2017-09-05 09:13:59

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH 4/9] plugins/sixaxis: Move device discovery to shared header

Hi Bastien,

On 5 September 2017 at 11:03, Bastien Nocera <[email protected]> wrote:
> On Mon, 2017-09-04 at 20:12 +0200, Bastien Nocera wrote:
>> <snip>
>> +
>> +inline CablePairingType
>> +get_pairing_type(uint16_t vid,
>> + uint16_t pid,
>> + char **name,
>> + uint16_t *source,
>> + uint16_t *version)
>
> I have a slightly updated version of that section. "static" is missing
> in front of the inline function for use with RPM build's strict linker
> settings, and I'm sure the indentation police would like to see that
> function return type and name on the same line ;)
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

I'm not sure why this is in header in first place ?

--
pozdrawiam
Szymon K. Janc

2017-09-05 09:03:25

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 4/9] plugins/sixaxis: Move device discovery to shared header

On Mon, 2017-09-04 at 20:12 +0200, Bastien Nocera wrote:
> <snip>
> +
> +inline CablePairingType
> +get_pairing_type(uint16_t vid,
> + uint16_t pid,
> + char **name,
> + uint16_t *source,
> + uint16_t *version)

I have a slightly updated version of that section. "static" is missing
in front of the inline function for use with RPM build's strict linker
settings, and I'm sure the indentation police would like to see that
function return type and name on the same line ;)

2017-09-04 18:12:10

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 7/9] plugins/sixaxis: Rename sixaxis specific functions

And provide wrappers to prepare for the addition of other device types.
---
plugins/sixaxis.c | 39 ++++++++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 7 deletions(-)

diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index 3e8ac06ca..b62834d72 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -55,13 +55,14 @@ struct authentication_closure {
struct btd_device *device;
int fd;
char device_addr[18];
+ CablePairingType type;
};

static struct udev *ctx = NULL;
static struct udev_monitor *monitor = NULL;
static guint watch_id = 0;

-static int get_device_bdaddr(int fd, bdaddr_t *bdaddr)
+static int sixaxis_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
{
uint8_t buf[18];
int ret;
@@ -82,7 +83,14 @@ static int get_device_bdaddr(int fd, bdaddr_t *bdaddr)
return 0;
}

-static int get_master_bdaddr(int fd, bdaddr_t *bdaddr)
+static int get_device_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
+{
+ if (type == CABLE_PAIRING_SIXAXIS)
+ return sixaxis_get_device_bdaddr(fd, bdaddr);
+ return -1;
+}
+
+static int sixaxis_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
{
uint8_t buf[8];
int ret;
@@ -103,7 +111,14 @@ static int get_master_bdaddr(int fd, bdaddr_t *bdaddr)
return 0;
}

-static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
+static int get_master_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
+{
+ if (type == CABLE_PAIRING_SIXAXIS)
+ return sixaxis_get_master_bdaddr(fd, bdaddr);
+ return -1;
+}
+
+static int sixaxis_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
{
uint8_t buf[8];
int ret;
@@ -121,6 +136,14 @@ static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
return ret;
}

+static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr,
+ CablePairingType type)
+{
+ if (type == CABLE_PAIRING_SIXAXIS)
+ return sixaxis_set_master_bdaddr(fd, bdaddr);
+ return -1;
+}
+
static void agent_auth_cb(DBusError *derr,
void *user_data)
{
@@ -136,12 +159,12 @@ static void agent_auth_cb(DBusError *derr,

btd_device_set_temporary(closure->device, false);

- if (get_master_bdaddr(closure->fd, &master_bdaddr) < 0)
+ if (get_master_bdaddr(closure->fd, &master_bdaddr, closure->type) < 0)
goto error;

adapter_bdaddr = btd_adapter_get_address(closure->adapter);
if (bacmp(adapter_bdaddr, &master_bdaddr)) {
- if (set_master_bdaddr(closure->fd, adapter_bdaddr) < 0)
+ if (set_master_bdaddr(closure->fd, adapter_bdaddr, closure->type) < 0)
goto error;
}

@@ -161,6 +184,7 @@ static bool setup_device(int fd,
uint16_t vid,
uint16_t pid,
uint16_t version,
+ CablePairingType type,
struct btd_adapter *adapter)
{
char device_addr[18];
@@ -169,7 +193,7 @@ static bool setup_device(int fd,
struct btd_device *device;
struct authentication_closure *closure;

- if (get_device_bdaddr(fd, &device_bdaddr) < 0)
+ if (get_device_bdaddr(fd, &device_bdaddr, type) < 0)
return false;

/* This can happen if controller was plugged while already connected
@@ -203,6 +227,7 @@ static bool setup_device(int fd,
closure->adapter = adapter;
closure->device = device;
closure->fd = fd;
+ closure->type = type;
memcpy(&closure->device_addr, device_addr, sizeof(device_addr));
adapter_bdaddr = btd_adapter_get_address(adapter);
btd_request_authorization_cable_configured(adapter_bdaddr, &device_bdaddr,
@@ -269,7 +294,7 @@ static void device_added(struct udev_device *udevice)
}

/* Only close the fd if an authentication is not pending */
- if (!setup_device(fd, name, source, vid, pid, version, adapter))
+ if (!setup_device(fd, name, source, vid, pid, version, type, adapter))
close(fd);

g_free(name);
--
2.14.1


2017-09-04 18:12:11

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 8/9] plugins/sixaxis: Add support for DualShock 4/PS4 cable pairing

From: Juha Kuikka <[email protected]>

This patch adds support for "pairing" a Dualshock4 controller over USB
into the sixaxis plugin, similarly to what the Sixaxis/PS3 controller
supported.

Actual bonding happens on first connection, but the device will be
marked as trusted when the agent replies.

This patch is based on DS4 supprt for sixpair tool by t0xicCode:
https://github.com/t0xicCode/sixpair/commit/975c38cb6cd61a2f0be350714f0f64cef5f0432c
---
plugins/sixaxis.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 72 insertions(+), 1 deletion(-)

diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index b62834d72..eb15acb92 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -83,10 +83,34 @@ static int sixaxis_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
return 0;
}

+static int ds4_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
+{
+ uint8_t buf[7];
+ int ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = 0x81;
+
+ ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+ if (ret < 0) {
+ error("ds4: failed to read device address (%s)",
+ strerror(errno));
+ return ret;
+ }
+
+ /* address is little-endian on DS4 */
+ bacpy(bdaddr, (bdaddr_t*) (buf + 1));
+
+ return 0;
+}
+
static int get_device_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
{
if (type == CABLE_PAIRING_SIXAXIS)
return sixaxis_get_device_bdaddr(fd, bdaddr);
+ else if (type == CABLE_PAIRING_DS4)
+ return ds4_get_device_bdaddr(fd, bdaddr);
return -1;
}

@@ -111,10 +135,34 @@ static int sixaxis_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
return 0;
}

+static int ds4_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
+{
+ uint8_t buf[16];
+ int ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0] = 0x12;
+
+ ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+ if (ret < 0) {
+ error("ds4: failed to read master address (%s)",
+ strerror(errno));
+ return ret;
+ }
+
+ /* address is little-endian on DS4 */
+ bacpy(bdaddr, (bdaddr_t*) (buf + 10));
+
+ return 0;
+}
+
static int get_master_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
{
if (type == CABLE_PAIRING_SIXAXIS)
return sixaxis_get_master_bdaddr(fd, bdaddr);
+ else if (type == CABLE_PAIRING_DS4)
+ return ds4_get_master_bdaddr(fd, bdaddr);
return -1;
}

@@ -136,11 +184,33 @@ static int sixaxis_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
return ret;
}

+static int ds4_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
+{
+ uint8_t buf[23];
+ int ret;
+
+ buf[0] = 0x13;
+ bacpy((bdaddr_t*) (buf + 1), bdaddr);
+ /* TODO: we could put the key here but
+ there is no way to force a re-loading
+ of link keys to the kernel from here. */
+ memset(buf + 7, 0, 16);
+
+ ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
+ if (ret < 0)
+ error("ds4: failed to write master address (%s)",
+ strerror(errno));
+
+ return ret;
+}
+
static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr,
CablePairingType type)
{
if (type == CABLE_PAIRING_SIXAXIS)
return sixaxis_set_master_bdaddr(fd, bdaddr);
+ else if (type == CABLE_PAIRING_DS4)
+ return ds4_set_master_bdaddr(fd, bdaddr);
return -1;
}

@@ -279,7 +349,8 @@ static void device_added(struct udev_device *udevice)
&name,
&source,
&version);
- if (type != CABLE_PAIRING_SIXAXIS)
+ if (type != CABLE_PAIRING_SIXAXIS &&
+ type != CABLE_PAIRING_DS4)
return;
if (bus != BUS_USB)
return;
--
2.14.1


2017-09-04 18:12:06

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 3/9] sixaxis: Ask user whether cable configuration should be allowed

Previously, users doing cable configuration of Sixaxis PS3 controllers
would only get asked whether a device was allowed to connect to the
computer when switching it to Bluetooth mode: unplugging it, and
pressing the PS button.

Instead, we should ask the user straight away, through the agent,
whether the pad should be allowed to connect.

This makes it easier to setup those devices, while keeping security.
---
plugins/sixaxis.c | 83 +++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 63 insertions(+), 20 deletions(-)

diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index 7d3a8f3ac..c8305d52f 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -44,6 +44,7 @@

#include "src/adapter.h"
#include "src/device.h"
+#include "src/agent.h"
#include "src/plugin.h"
#include "src/log.h"
#include "src/shared/util.h"
@@ -71,6 +72,13 @@ static const struct {
},
};

+struct authentication_closure {
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ int fd;
+ char device_addr[18];
+};
+
static struct udev *ctx = NULL;
static struct udev_monitor *monitor = NULL;
static guint watch_id = 0;
@@ -135,19 +143,51 @@ static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
return ret;
}

+static void agent_auth_cb(DBusError *derr,
+ void *user_data)
+{
+ struct authentication_closure *closure = user_data;
+ char master_addr[18], adapter_addr[18];
+ bdaddr_t master_bdaddr;
+ const bdaddr_t *adapter_bdaddr;
+
+ if (derr != NULL) {
+ DBG("Agent replied negatively, removing temporary device");
+ goto error;
+ }
+
+ btd_device_set_temporary(closure->device, false);
+
+ if (get_master_bdaddr(closure->fd, &master_bdaddr) < 0)
+ goto error;
+
+ adapter_bdaddr = btd_adapter_get_address(closure->adapter);
+ if (bacmp(adapter_bdaddr, &master_bdaddr)) {
+ if (set_master_bdaddr(closure->fd, adapter_bdaddr) < 0)
+ goto error;
+ }
+
+ ba2str(&master_bdaddr, master_addr);
+ ba2str(adapter_bdaddr, adapter_addr);
+ DBG("remote %s old_master %s new_master %s",
+ closure->device_addr, master_addr, adapter_addr);
+
+error:
+ close(closure->fd);
+ g_free(closure);
+}
+
static bool setup_device(int fd, int index, struct btd_adapter *adapter)
{
- char device_addr[18], master_addr[18], adapter_addr[18];
- bdaddr_t device_bdaddr, master_bdaddr;
+ char device_addr[18];
+ bdaddr_t device_bdaddr;
const bdaddr_t *adapter_bdaddr;
struct btd_device *device;
+ struct authentication_closure *closure;

if (get_device_bdaddr(fd, &device_bdaddr) < 0)
return false;

- if (get_master_bdaddr(fd, &master_bdaddr) < 0)
- return false;
-
/* This can happen if controller was plugged while already connected
* eg. to charge up battery. */
device = btd_adapter_find_device(adapter, &device_bdaddr,
@@ -155,25 +195,14 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
if (device && btd_device_is_connected(device))
return false;

- adapter_bdaddr = btd_adapter_get_address(adapter);
-
- if (bacmp(adapter_bdaddr, &master_bdaddr)) {
- if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
- return false;
- }
-
ba2str(&device_bdaddr, device_addr);
- ba2str(&master_bdaddr, master_addr);
- ba2str(adapter_bdaddr, adapter_addr);
- DBG("remote %s old_master %s new_master %s",
- device_addr, master_addr, adapter_addr);

device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);

if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
(GCompareFunc)strcasecmp)) {
DBG("device %s already known, skipping", device_addr);
- return true;
+ return false;
}

info("sixaxis: setting up new device");
@@ -181,7 +210,20 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
btd_device_device_set_name(device, devices[index].name);
btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
devices[index].pid, devices[index].version);
- btd_device_set_temporary(device, false);
+ btd_device_set_temporary(device, true);
+
+ closure = g_try_new0(struct authentication_closure, 1);
+ if (!closure) {
+ btd_adapter_remove_device(adapter, device);
+ return false;
+ }
+ closure->adapter = adapter;
+ closure->device = device;
+ closure->fd = fd;
+ memcpy(&closure->device_addr, device_addr, sizeof(device_addr));
+ adapter_bdaddr = btd_adapter_get_address(adapter);
+ btd_request_authorization_cable_configured(adapter_bdaddr, &device_bdaddr,
+ HID_UUID, agent_auth_cb, closure);

return true;
}
@@ -236,8 +278,9 @@ static void device_added(struct udev_device *udevice)
if (fd < 0)
return;

- setup_device(fd, index, adapter);
- close(fd);
+ /* Only close the fd if an authentication is not pending */
+ if (!setup_device(fd, index, adapter))
+ close(fd);
}

static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
--
2.14.1


2017-09-04 18:12:07

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 4/9] plugins/sixaxis: Move device discovery to shared header

Move the struct containing the Sixaxis-compatible devices to a
header shared with the input profiles code, so as to reduce device
declaration.

Adding support for new devices should be as easy as adding the device's
declaration in profiles/input/sixaxis.h
---
plugins/sixaxis.c | 82 +++++++++++++++++++++------------------------
profiles/input/sixaxis.h | 86 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 123 insertions(+), 45 deletions(-)
create mode 100644 profiles/input/sixaxis.h

diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index c8305d52f..3e8ac06ca 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -48,29 +48,7 @@
#include "src/plugin.h"
#include "src/log.h"
#include "src/shared/util.h"
-
-static const struct {
- const char *name;
- uint16_t source;
- uint16_t vid;
- uint16_t pid;
- uint16_t version;
-} devices[] = {
- {
- .name = "Sony PLAYSTATION(R)3 Controller",
- .source = 0x0002,
- .vid = 0x054c,
- .pid = 0x0268,
- .version = 0x0000,
- },
- {
- .name = "Navigation Controller",
- .source = 0x0002,
- .vid = 0x054c,
- .pid = 0x042f,
- .version = 0x0000,
- },
-};
+#include "profiles/input/sixaxis.h"

struct authentication_closure {
struct btd_adapter *adapter;
@@ -177,7 +155,13 @@ error:
g_free(closure);
}

-static bool setup_device(int fd, int index, struct btd_adapter *adapter)
+static bool setup_device(int fd,
+ const char *name,
+ uint16_t source,
+ uint16_t vid,
+ uint16_t pid,
+ uint16_t version,
+ struct btd_adapter *adapter)
{
char device_addr[18];
bdaddr_t device_bdaddr;
@@ -207,9 +191,8 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)

info("sixaxis: setting up new device");

- btd_device_device_set_name(device, devices[index].name);
- btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
- devices[index].pid, devices[index].version);
+ btd_device_device_set_name(device, name);
+ btd_device_set_pnpid(device, source, vid, pid, version);
btd_device_set_temporary(device, true);

closure = g_try_new0(struct authentication_closure, 1);
@@ -228,12 +211,16 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
return true;
}

-static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
+static CablePairingType get_pairing_type_for_device(struct udev_device *udevice,
+ uint16_t *bus,
+ uint16_t *vid,
+ uint16_t *pid,
+ char **name,
+ uint16_t *source,
+ uint16_t *version)
{
struct udev_device *hid_parent;
- uint16_t vid, pid;
const char *hid_id;
- guint i;

hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
"hid", NULL);
@@ -242,45 +229,50 @@ static int get_supported_device(struct udev_device *udevice, uint16_t *bus)

hid_id = udev_device_get_property_value(hid_parent, "HID_ID");

- if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3)
+ if (sscanf(hid_id, "%hx:%hx:%hx", bus, vid, pid) != 3)
return -1;

- for (i = 0; i < G_N_ELEMENTS(devices); i++) {
- if (devices[i].vid == vid && devices[i].pid == pid)
- return i;
- }
-
- return -1;
+ return get_pairing_type(*vid, *pid, name, source, version);
}

static void device_added(struct udev_device *udevice)
{
struct btd_adapter *adapter;
- uint16_t bus;
- int index;
+ uint16_t bus, vid, pid, source, version;
+ char *name = NULL;
+ CablePairingType type;
int fd;

adapter = btd_adapter_get_default();
if (!adapter)
return;

- index = get_supported_device(udevice, &bus);
- if (index < 0)
+ type = get_pairing_type_for_device(udevice,
+ &bus,
+ &vid,
+ &pid,
+ &name,
+ &source,
+ &version);
+ if (type != CABLE_PAIRING_SIXAXIS)
return;
if (bus != BUS_USB)
return;

info("sixaxis: compatible device connected: %s (%04X:%04X)",
- devices[index].name, devices[index].vid,
- devices[index].pid);
+ name, vid, pid);

fd = open(udev_device_get_devnode(udevice), O_RDWR);
- if (fd < 0)
+ if (fd < 0) {
+ g_free(name);
return;
+ }

/* Only close the fd if an authentication is not pending */
- if (!setup_device(fd, index, adapter))
+ if (!setup_device(fd, name, source, vid, pid, version, adapter))
close(fd);
+
+ g_free(name);
}

static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
diff --git a/profiles/input/sixaxis.h b/profiles/input/sixaxis.h
new file mode 100644
index 000000000..589eff3da
--- /dev/null
+++ b/profiles/input/sixaxis.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2009,2017 Bastien Nocera <[email protected]>
+ * Copyright (C) 2011 Antonio Ospite <[email protected]>
+ * Copyright (C) 2013 Szymon Janc <[email protected]>
+ *
+ *
+ * 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
+ *
+ */
+
+#ifndef _SIXAXIS_H_
+#define _SIXAXIS_H_
+
+typedef enum {
+ CABLE_PAIRING_UNSUPPORTED = 0,
+ CABLE_PAIRING_SIXAXIS,
+} CablePairingType;
+
+inline CablePairingType
+get_pairing_type(uint16_t vid,
+ uint16_t pid,
+ char **name,
+ uint16_t *source,
+ uint16_t *version)
+{
+ static const struct {
+ const char *name;
+ uint16_t source;
+ uint16_t vid;
+ uint16_t pid;
+ uint16_t version;
+ CablePairingType type;
+ } devices[] = {
+ {
+ .name = "Sony PLAYSTATION(R)3 Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x0268,
+ .version = 0x0000,
+ .type = CABLE_PAIRING_SIXAXIS,
+ },
+ {
+ .name = "Navigation Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x042f,
+ .version = 0x0000,
+ .type = CABLE_PAIRING_SIXAXIS,
+ },
+ };
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS(devices); i++) {
+ if (devices[i].vid != vid)
+ continue;
+ if (devices[i].pid != pid)
+ continue;
+
+ if (name)
+ *name = g_strdup(devices[i].name);
+ if (source)
+ *source = devices[i].source;
+ if (version)
+ *version = devices[i].version;
+ return devices[i].type;
+ }
+
+ return CABLE_PAIRING_UNSUPPORTED;
+}
+
+#endif /* _SIXAXIS_H_ */
--
2.14.1


2017-09-04 18:12:12

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 9/9] plugins/sixaxis: Cancel cable pairing if unplugged

Cancel pending authorization requests when the device is unplugged
while a request is pending.
---
plugins/sixaxis.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 123 insertions(+), 15 deletions(-)

diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index eb15acb92..5cae52c68 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -51,6 +51,8 @@
#include "profiles/input/sixaxis.h"

struct authentication_closure {
+ guint auth_id;
+ char *sysfs_path;
struct btd_adapter *adapter;
struct btd_device *device;
int fd;
@@ -58,9 +60,29 @@ struct authentication_closure {
CablePairingType type;
};

+struct authentication_destroy_closure {
+ struct authentication_closure *closure;
+ bool remove_device;
+};
+
static struct udev *ctx = NULL;
static struct udev_monitor *monitor = NULL;
static guint watch_id = 0;
+static GHashTable *pending_auths = NULL; /* key = sysfs_path (const str), value = auth_closure */
+
+/* Make sure to unset auth_id if already handled */
+static void auth_closure_destroy(struct authentication_closure *closure,
+ bool remove_device)
+{
+ if (closure->auth_id)
+ btd_cancel_authorization(closure->auth_id);
+
+ if (remove_device)
+ btd_adapter_remove_device(closure->adapter, closure->device);
+ close(closure->fd);
+ g_free(closure->sysfs_path);
+ g_free(closure);
+}

static int sixaxis_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
{
@@ -214,41 +236,83 @@ static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr,
return -1;
}

+static bool is_auth_pending(struct authentication_closure *closure)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init(&iter, pending_auths);
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ struct authentication_closure *c = value;
+ if (c == closure)
+ return true;
+ }
+ return false;
+}
+
+static gboolean auth_closure_destroy_idle(gpointer user_data)
+{
+ struct authentication_destroy_closure *destroy = user_data;
+
+ auth_closure_destroy(destroy->closure, destroy->remove_device);
+ g_free(destroy);
+
+ return false;
+}
+
static void agent_auth_cb(DBusError *derr,
void *user_data)
{
struct authentication_closure *closure = user_data;
+ struct authentication_destroy_closure *destroy;
char master_addr[18], adapter_addr[18];
bdaddr_t master_bdaddr;
const bdaddr_t *adapter_bdaddr;
+ bool remove_device = true;
+
+ if (!is_auth_pending(closure))
+ return;
+
+ /* Don't try to remove this auth, we're handling it already */
+ closure->auth_id = 0;

if (derr != NULL) {
DBG("Agent replied negatively, removing temporary device");
- goto error;
+ goto out;
}

- btd_device_set_temporary(closure->device, false);
-
if (get_master_bdaddr(closure->fd, &master_bdaddr, closure->type) < 0)
- goto error;
+ goto out;

adapter_bdaddr = btd_adapter_get_address(closure->adapter);
if (bacmp(adapter_bdaddr, &master_bdaddr)) {
if (set_master_bdaddr(closure->fd, adapter_bdaddr, closure->type) < 0)
- goto error;
+ goto out;
}

+ remove_device = false;
+ btd_device_set_temporary(closure->device, false);
+
ba2str(&master_bdaddr, master_addr);
ba2str(adapter_bdaddr, adapter_addr);
DBG("remote %s old_master %s new_master %s",
closure->device_addr, master_addr, adapter_addr);

-error:
- close(closure->fd);
- g_free(closure);
+out:
+ g_hash_table_steal(pending_auths, closure->sysfs_path);
+
+ /* btd_adapter_remove_device() cannot be called in this
+ * callback or it would lead to a double-free in while
+ * trying to cancel the authentication that's being processed,
+ * so clean up in an idle */
+ destroy = g_new0(struct authentication_destroy_closure, 1);
+ destroy->closure = closure;
+ destroy->remove_device = remove_device;
+ g_idle_add(auth_closure_destroy_idle, destroy);
}

static bool setup_device(int fd,
+ const char *sysfs_path,
const char *name,
uint16_t source,
uint16_t vid,
@@ -296,12 +360,15 @@ static bool setup_device(int fd,
}
closure->adapter = adapter;
closure->device = device;
+ closure->sysfs_path = g_strdup(sysfs_path);
closure->fd = fd;
closure->type = type;
memcpy(&closure->device_addr, device_addr, sizeof(device_addr));
adapter_bdaddr = btd_adapter_get_address(adapter);
- btd_request_authorization_cable_configured(adapter_bdaddr, &device_bdaddr,
- HID_UUID, agent_auth_cb, closure);
+ closure->auth_id = btd_request_authorization_cable_configured(adapter_bdaddr, &device_bdaddr,
+ HID_UUID, agent_auth_cb, closure);
+
+ g_hash_table_insert(pending_auths, closure->sysfs_path, closure);

return true;
}
@@ -310,12 +377,14 @@ static CablePairingType get_pairing_type_for_device(struct udev_device *udevice,
uint16_t *bus,
uint16_t *vid,
uint16_t *pid,
+ char **sysfs_path,
char **name,
uint16_t *source,
uint16_t *version)
{
struct udev_device *hid_parent;
const char *hid_id;
+ CablePairingType ret;

hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
"hid", NULL);
@@ -327,14 +396,17 @@ static CablePairingType get_pairing_type_for_device(struct udev_device *udevice,
if (sscanf(hid_id, "%hx:%hx:%hx", bus, vid, pid) != 3)
return -1;

- return get_pairing_type(*vid, *pid, name, source, version);
+ ret = get_pairing_type(*vid, *pid, name, source, version);
+ *sysfs_path = g_strdup(udev_device_get_syspath(udevice));
+
+ return ret;
}

static void device_added(struct udev_device *udevice)
{
struct btd_adapter *adapter;
uint16_t bus, vid, pid, source, version;
- char *name = NULL;
+ char *name = NULL, *sysfs_path = NULL;
CablePairingType type;
int fd;

@@ -346,6 +418,7 @@ static void device_added(struct udev_device *udevice)
&bus,
&vid,
&pid,
+ &sysfs_path,
&name,
&source,
&version);
@@ -355,20 +428,39 @@ static void device_added(struct udev_device *udevice)
if (bus != BUS_USB)
return;

- info("sixaxis: compatible device connected: %s (%04X:%04X)",
- name, vid, pid);
+ info("sixaxis: compatible device connected: %s (%04X:%04X %s)",
+ name, vid, pid, sysfs_path);

fd = open(udev_device_get_devnode(udevice), O_RDWR);
if (fd < 0) {
g_free(name);
+ g_free(sysfs_path);
return;
}

/* Only close the fd if an authentication is not pending */
- if (!setup_device(fd, name, source, vid, pid, version, type, adapter))
+ if (!setup_device(fd, sysfs_path, name, source, vid, pid, version, type, adapter))
close(fd);

g_free(name);
+ g_free(sysfs_path);
+}
+
+static void device_removed(struct udev_device *udevice)
+{
+ struct authentication_closure *closure;
+ const char *sysfs_path;
+
+ sysfs_path = udev_device_get_syspath(udevice);
+ if (!sysfs_path)
+ return;
+
+ closure = g_hash_table_lookup(pending_auths, sysfs_path);
+ if (!closure)
+ return;
+
+ g_hash_table_steal(pending_auths, sysfs_path);
+ auth_closure_destroy(closure, true);
}

static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
@@ -382,6 +474,8 @@ static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,

if (!g_strcmp0(udev_device_get_action(udevice), "add"))
device_added(udevice);
+ else if (!g_strcmp0(udev_device_get_action(udevice), "remove"))
+ device_removed(udevice);

udev_device_unref(udevice);

@@ -415,13 +509,27 @@ static int sixaxis_init(void)
watch_id = g_io_add_watch(channel, G_IO_IN, monitor_watch, NULL);
g_io_channel_unref(channel);

+ pending_auths = g_hash_table_new(g_str_hash,
+ g_str_equal);
+
return 0;
}

static void sixaxis_exit(void)
{
+ GHashTableIter iter;
+ gpointer value;
+
DBG("");

+ g_hash_table_iter_init(&iter, pending_auths);
+ while (g_hash_table_iter_next(&iter, NULL, &value)) {
+ struct authentication_closure *closure = value;
+ auth_closure_destroy(closure, true);
+ }
+ g_hash_table_destroy(pending_auths);
+ pending_auths = NULL;
+
g_source_remove(watch_id);
watch_id = 0;

--
2.14.1


2017-09-04 18:12:08

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 5/9] profiles/input: Use sixaxis header to simplify device detection

Use the shared header to recognise whether a device is a Sixaxis device
or not.
---
profiles/input/server.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/profiles/input/server.c b/profiles/input/server.c
index eb3fcf843..121c334d3 100644
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -43,6 +43,7 @@
#include "src/device.h"
#include "src/profile.h"

+#include "sixaxis.h"
#include "device.h"
#include "server.h"

@@ -123,6 +124,7 @@ static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst)
{
struct btd_device *device;
uint16_t vid, pid;
+ CablePairingType type;

device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
if (!device)
@@ -131,18 +133,14 @@ static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst)
vid = btd_device_get_vendor(device);
pid = btd_device_get_product(device);

- /* DualShock 3 */
- if (vid == 0x054c && pid == 0x0268)
+ type = get_pairing_type(vid, pid, NULL, NULL, NULL);
+ if (type == CABLE_PAIRING_SIXAXIS)
return true;

/* DualShock 4 */
if (vid == 0x054c && pid == 0x05c4)
return true;

- /* Navigation Controller */
- if (vid == 0x054c && pid == 0x042f)
- return true;
-
return false;
}

--
2.14.1


2017-09-04 18:12:09

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 6/9] profiles/input: Add DS4 devices to the shared header

And simplify the detection code in server.c some more.
---
profiles/input/server.c | 7 ++-----
profiles/input/sixaxis.h | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/profiles/input/server.c b/profiles/input/server.c
index 121c334d3..ef428fefe 100644
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -134,11 +134,8 @@ static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst)
pid = btd_device_get_product(device);

type = get_pairing_type(vid, pid, NULL, NULL, NULL);
- if (type == CABLE_PAIRING_SIXAXIS)
- return true;
-
- /* DualShock 4 */
- if (vid == 0x054c && pid == 0x05c4)
+ if (type == CABLE_PAIRING_SIXAXIS ||
+ type == CABLE_PAIRING_DS4)
return true;

return false;
diff --git a/profiles/input/sixaxis.h b/profiles/input/sixaxis.h
index 589eff3da..9c074face 100644
--- a/profiles/input/sixaxis.h
+++ b/profiles/input/sixaxis.h
@@ -29,6 +29,7 @@
typedef enum {
CABLE_PAIRING_UNSUPPORTED = 0,
CABLE_PAIRING_SIXAXIS,
+ CABLE_PAIRING_DS4,
} CablePairingType;

inline CablePairingType
@@ -62,6 +63,22 @@ get_pairing_type(uint16_t vid,
.version = 0x0000,
.type = CABLE_PAIRING_SIXAXIS,
},
+ {
+ .name = "Wireless Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x05c4,
+ .version = 0x0001,
+ .type = CABLE_PAIRING_DS4,
+ },
+ {
+ .name = "Wireless Controller",
+ .source = 0x0002,
+ .vid = 0x054c,
+ .pid = 0x09cc,
+ .version = 0x0001,
+ .type = CABLE_PAIRING_DS4,
+ },
};
guint i;

--
2.14.1


2017-09-04 18:12:05

by Bastien Nocera

[permalink] [raw]
Subject: [PATCH 2/9] adapter: Add btd_request_authorization_cable_configured()

Add btd_request_authorization_cable_configured() function to allow
cable configured devices to ask the user straight away about whether the
device should be allowed to connect to the computer.

This allows us to ask the user at the time of the USB connection and
initial setup, rather than when the first Bluetooth connection is made.

The fact that the device might not be connected to the adapter when
this event is triggered is mentioned in the Agent API docs.
---
doc/agent-api.txt | 5 ++++-
src/adapter.c | 42 ++++++++++++++++++++++++++++++++++++------
src/adapter.h | 2 ++
3 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/doc/agent-api.txt b/doc/agent-api.txt
index 801ccb665..0d9347cab 100644
--- a/doc/agent-api.txt
+++ b/doc/agent-api.txt
@@ -163,7 +163,10 @@ Methods void Release()
This method gets called to request the user to
authorize an incoming pairing attempt which
would in other circumstances trigger the just-works
- model.
+ model, or when the user plugged in a device that
+ implements cable pairing. In the latter case, the
+ device would not be connected to the adapter via
+ Bluetooth yet.

Possible errors: org.bluez.Error.Rejected
org.bluez.Error.Canceled
diff --git a/src/adapter.c b/src/adapter.c
index a571b1870..b24b73e30 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -265,6 +265,11 @@ struct btd_adapter {
bool is_default; /* true if adapter is default one */
};

+typedef enum {
+ ADAPTER_AUTHORIZE_DISCONNECTED = 0,
+ ADAPTER_AUTHORIZE_CHECK_CONNECTED
+} adapter_authorize_type;
+
static struct btd_adapter *btd_adapter_lookup(uint16_t index)
{
GList *list;
@@ -6088,8 +6093,9 @@ static void svc_complete(struct btd_device *dev, int err, void *user_data)
}

static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
- const char *uuid, service_auth_cb cb,
- void *user_data)
+ const char *uuid,
+ adapter_authorize_type check_for_connection,
+ service_auth_cb cb, void *user_data)
{
struct service_auth *auth;
struct btd_device *device;
@@ -6105,7 +6111,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
}

/* Device connected? */
- if (!g_slist_find(adapter->connections, device))
+ if (check_for_connection && !g_slist_find(adapter->connections, device))
btd_error(adapter->dev_id,
"Authorization request for non-connected device!?");

@@ -6119,7 +6125,12 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
auth->device = device;
auth->adapter = adapter;
auth->id = ++id;
- auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
+ if (check_for_connection)
+ auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
+ else {
+ if (adapter->auth_idle_id == 0)
+ adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
+ }

g_queue_push_tail(adapter->auths, auth);

@@ -6138,7 +6149,8 @@ guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
if (!adapter)
return 0;

- return adapter_authorize(adapter, dst, uuid, cb, user_data);
+ return adapter_authorize(adapter, dst, uuid,
+ ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data);
}

for (l = adapters; l != NULL; l = g_slist_next(l)) {
@@ -6146,7 +6158,8 @@ guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,

adapter = l->data;

- id = adapter_authorize(adapter, dst, uuid, cb, user_data);
+ id = adapter_authorize(adapter, dst, uuid,
+ ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data);
if (id != 0)
return id;
}
@@ -6154,6 +6167,23 @@ guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
return 0;
}

+guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst,
+ const char *uuid, service_auth_cb cb,
+ void *user_data)
+{
+ struct btd_adapter *adapter;
+
+ if (bacmp(src, BDADDR_ANY) == 0)
+ return 0;
+
+ adapter = adapter_find(src);
+ if (!adapter)
+ return 0;
+
+ return adapter_authorize(adapter, dst, uuid,
+ ADAPTER_AUTHORIZE_DISCONNECTED, cb, user_data);
+}
+
static struct service_auth *find_authorization(guint id)
{
GSList *l;
diff --git a/src/adapter.h b/src/adapter.h
index f9178d59e..a85327cd1 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -120,6 +120,8 @@ int btd_register_adapter_driver(struct btd_adapter_driver *driver);
void btd_unregister_adapter_driver(struct btd_adapter_driver *driver);
guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
const char *uuid, service_auth_cb cb, void *user_data);
+guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst,
+ const char *uuid, service_auth_cb cb, void *user_data);
int btd_cancel_authorization(guint id);

int btd_adapter_restore_powered(struct btd_adapter *adapter);
--
2.14.1


2017-10-18 01:51:20

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 8/9] plugins/sixaxis: Add support for DualShock 4/PS4 cable pairing

On Wed, 2017-09-27 at 11:12 +0200, Szymon Janc wrote:
> Hi Bastien,
>
> On Monday, 4 September 2017 20:12:11 CEST Bastien Nocera wrote:
> > <snip>
>
> Lets prefix all sixaxis error/info logs with "sixaxis:"

Sure. I've reformulated the error messages so we know which type of
device we're printing an error about.

2017-10-18 01:51:17

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 3/9] sixaxis: Ask user whether cable configuration should be allowed

On Wed, 2017-09-27 at 11:12 +0200, Szymon Janc wrote:
> Hi Bastien,
>
> On Monday, 4 September 2017 20:12:06 CEST Bastien Nocera wrote:
> > <snip>
> > + char device_addr[18];
>
> If this is needed only for debug then just convert address to string
> in place
> where it is printed.

The string was in string format already when we get it from the device,
but I've switched that to pass around a bdaddr_t instead.
<snip>

> > + closure = g_try_new0(struct authentication_closure, 1);
>
> Just use new0() instead.

Fixed.

I've also added a btd_device_set_trusted() as well, which was bizarrely
missing from this patch. I guess it got lost when I started splitting
up the original patches.

2017-10-04 12:40:56

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 8/9] plugins/sixaxis: Add support for DualShock 4/PS4 cable pairing

On Wed, 2017-09-27 at 11:12 +0200, Szymon Janc wrote:
> Hi Bastien,
>
> On Monday, 4 September 2017 20:12:11 CEST Bastien Nocera wrote:
> > From: Juha Kuikka <[email protected]>
> >
> > This patch adds support for "pairing" a Dualshock4 controller over
> > USB
> > into the sixaxis plugin, similarly to what the Sixaxis/PS3
> > controller
> > supported.
> >
> > Actual bonding happens on first connection, but the device will be
> > marked as trusted when the agent replies.
> >
> > This patch is based on DS4 supprt for sixpair tool by t0xicCode:
> > https://github.com/t0xicCode/sixpair/commit/975c38cb6cd61a2f0be3507
> > 14f0f64ce
> > f5f0432c ---
> > plugins/sixaxis.c | 73
> > ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file
> > changed, 72
> > insertions(+), 1 deletion(-)
> >
> > diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
> > index b62834d72..eb15acb92 100644
> > --- a/plugins/sixaxis.c
> > +++ b/plugins/sixaxis.c
> > @@ -83,10 +83,34 @@ static int sixaxis_get_device_bdaddr(int fd,
> > bdaddr_t
> > *bdaddr) return 0;
> > }
> >
> > +static int ds4_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
> > +{
> > + uint8_t buf[7];
> > + int ret;
> > +
> > + memset(buf, 0, sizeof(buf));
> > +
> > + buf[0] = 0x81;
> > +
> > + ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
> > + if (ret < 0) {
> > + error("ds4: failed to read device address (%s)",
> > + strerror(errno));
>
> Lets prefix all sixaxis error/info logs with "sixaxis:"

Sure, I'll make some changes to mention whether the sixaxis or DS4
variant of the call was made.

Nothing else?

2017-10-04 12:38:08

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 3/9] sixaxis: Ask user whether cable configuration should be allowed

On Wed, 2017-09-27 at 11:14 +0200, Szymon Janc wrote:
> Hi Bastien,
>
> On Monday, 4 September 2017 20:12:06 CEST Bastien Nocera wrote:
> > Previously, users doing cable configuration of Sixaxis PS3
> > controllers
> > would only get asked whether a device was allowed to connect to the
> > computer when switching it to Bluetooth mode: unplugging it, and
> > pressing the PS button.
> >
> > Instead, we should ask the user straight away, through the agent,
> > whether the pad should be allowed to connect.
> >
> > This makes it easier to setup those devices, while keeping
> > security.
> > ---
> > plugins/sixaxis.c | 83
> > +++++++++++++++++++++++++++++++++++++++++-------------- 1 file
> > changed, 63
> > insertions(+), 20 deletions(-)
> >
> > diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
> > index 7d3a8f3ac..c8305d52f 100644
> > --- a/plugins/sixaxis.c
> > +++ b/plugins/sixaxis.c
> > @@ -44,6 +44,7 @@
> >
> > #include "src/adapter.h"
> > #include "src/device.h"
> > +#include "src/agent.h"
> > #include "src/plugin.h"
> > #include "src/log.h"
> > #include "src/shared/util.h"
> > @@ -71,6 +72,13 @@ static const struct {
> > },
> > };
> >
> > +struct authentication_closure {
> > + struct btd_adapter *adapter;
> > + struct btd_device *device;
> > + int fd;
> > + char device_addr[18];
> > +};
> > +
> > static struct udev *ctx = NULL;
> > static struct udev_monitor *monitor = NULL;
> > static guint watch_id = 0;
> > @@ -135,19 +143,51 @@ static int set_master_bdaddr(int fd, const
> > bdaddr_t
> > *bdaddr) return ret;
> > }
> >
> > +static void agent_auth_cb(DBusError *derr,
> > + void *user_data)
> > +{
> > + struct authentication_closure *closure = user_data;
> > + char master_addr[18], adapter_addr[18];
> > + bdaddr_t master_bdaddr;
> > + const bdaddr_t *adapter_bdaddr;
> > +
> > + if (derr != NULL) {
> > + DBG("Agent replied negatively, removing temporary
> > device");
> > + goto error;
> > + }
> > +
> > + btd_device_set_temporary(closure->device, false);
> > +
> > + if (get_master_bdaddr(closure->fd, &master_bdaddr) < 0)
> > + goto error;
> > +
> > + adapter_bdaddr = btd_adapter_get_address(closure-
> > >adapter);
> > + if (bacmp(adapter_bdaddr, &master_bdaddr)) {
> > + if (set_master_bdaddr(closure->fd, adapter_bdaddr)
> > < 0)
> > + goto error;
> > + }
> > +
> > + ba2str(&master_bdaddr, master_addr);
> > + ba2str(adapter_bdaddr, adapter_addr);
> > + DBG("remote %s old_master %s new_master %s",
> > + closure->device_addr, master_addr,
> > adapter_addr);
> > +
> > +error:
> > + close(closure->fd);
> > + g_free(closure);
> > +}
> > +
> > static bool setup_device(int fd, int index, struct btd_adapter
> > *adapter)
> > {
> > - char device_addr[18], master_addr[18], adapter_addr[18];
> > - bdaddr_t device_bdaddr, master_bdaddr;
> > + char device_addr[18];
> > + bdaddr_t device_bdaddr;
> > const bdaddr_t *adapter_bdaddr;
> > struct btd_device *device;
> > + struct authentication_closure *closure;
> >
> > if (get_device_bdaddr(fd, &device_bdaddr) < 0)
> > return false;
> >
> > - if (get_master_bdaddr(fd, &master_bdaddr) < 0)
> > - return false;
> > -
> > /* This can happen if controller was plugged while already
> > connected
> > * eg. to charge up battery. */
> > device = btd_adapter_find_device(adapter, &device_bdaddr,
> > @@ -155,25 +195,14 @@ static bool setup_device(int fd, int index,
> > struct
> > btd_adapter *adapter) if (device &&
> > btd_device_is_connected(device))
> > return false;
> >
> > - adapter_bdaddr = btd_adapter_get_address(adapter);
> > -
> > - if (bacmp(adapter_bdaddr, &master_bdaddr)) {
> > - if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
> > - return false;
> > - }
> > -
> > ba2str(&device_bdaddr, device_addr);
> > - ba2str(&master_bdaddr, master_addr);
> > - ba2str(adapter_bdaddr, adapter_addr);
> > - DBG("remote %s old_master %s new_master %s",
> > - device_addr, master_addr,
> > adapter_addr);
> >
> > device = btd_adapter_get_device(adapter, &device_bdaddr,
> > BDADDR_BREDR);
> >
> > if (g_slist_find_custom(btd_device_get_uuids(device),
> > HID_UUID,
> > (GCompareFunc)strc
> > asecmp)) {
> > DBG("device %s already known, skipping",
> > device_addr);
> > - return true;
> > + return false;
> > }
> >
> > info("sixaxis: setting up new device");
> > @@ -181,7 +210,20 @@ static bool setup_device(int fd, int index,
> > struct
> > btd_adapter *adapter) btd_device_device_set_name(device,
> > devices[index].name);
> > btd_device_set_pnpid(device, devices[index].source,
> > devices[index].vid,
> > devices[index].pid,
> > devices[index].version);
> > - btd_device_set_temporary(device, false);
> > + btd_device_set_temporary(device, true);
> > +
> > + closure = g_try_new0(struct authentication_closure, 1);
> > + if (!closure) {
> > + btd_adapter_remove_device(adapter, device);
> > + return false;
> > + }
> > + closure->adapter = adapter;
> > + closure->device = device;
> > + closure->fd = fd;
> > + memcpy(&closure->device_addr, device_addr,
> > sizeof(device_addr));
> > + adapter_bdaddr = btd_adapter_get_address(adapter);
> > + btd_request_authorization_cable_configured(adapter_bdaddr,
> > &device_bdaddr,
> > + HID_UUID, agent_auth_cb, closure);
> >
> > return true;
> > }
> > @@ -236,8 +278,9 @@ static void device_added(struct udev_device
> > *udevice)
> > if (fd < 0)
> > return;
> >
> > - setup_device(fd, index, adapter);
> > - close(fd);
> > + /* Only close the fd if an authentication is not pending
> > */
> > + if (!setup_device(fd, index, adapter))
> > + close(fd);
> > }
> >
> > static gboolean monitor_watch(GIOChannel *source, GIOCondition
> > condition,
>
> So user will be asked for consent after DS3 was configured? Not that
> I have
> strong opinion on this but did you consider configuring it only after
> user
> gave consent?

We need to create a temporary device so that it's visible through the
D-Bus API. The flow is:
- joypad gets plugged in
- sixaxis plugin's udev signal watch gets triggered
- we fetch the device's details (Bluetooth address, name)
- we create a pairing request which gets passed to front-ends (such as
the Bluetooth panel in GNOME's Settings)
- we get the answer back, if the pairing is cancelled, or there wasn't
an agent to answer the question, we remove the temporary device (the
device is now charging, and usable through USB), otherwise we write the
new master to the joypad, so it connects to us when wireless, and mark
it as trusted so it can connect without any further questions.

There's still no way to pair the device without user interaction, as
before this patchset, but we avoid overwriting the device's
configuration when we just want to charge it, or use it wired, and the
"can it connect yes/no" happens when we plug it via USB rather than
when we finally disconnect it and press the "P" button to connect
wirelessly.