Return-Path: Subject: [Patch] Update hid2hci tool from udev codebase From: Kay Sievers To: linux-bluetooth@vger.kernel.org Content-Type: text/plain; charset="ISO-8859-15" Date: Thu, 28 Apr 2011 11:08:47 +0200 Message-ID: <1303981727.1065.11.camel@zag> Mime-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org List-ID: commit 4a2b9643b6e81d12c3c5fb863d1cca353437e102 Author: Kay Sievers Date: Thu Apr 28 11:02:24 2011 +0200 Update hid2hci tool from udev codebase diff --git a/Makefile.tools b/Makefile.tools index 364db37..1bf21b2 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -102,7 +102,7 @@ endif if HID2HCI sbin_PROGRAMS += tools/hid2hci -tools_hid2hci_LDADD = @USB_LIBS@ +tools_hid2hci_LDADD = @USB_LIBS@ @UDEV_LIBS@ dist_man_MANS += tools/hid2hci.8 else diff --git a/acinclude.m4 b/acinclude.m4 index 22fcd5c..a27cd22 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -148,6 +148,12 @@ AC_DEFUN([AC_PATH_USB], [ [Define to 1 if you need the usb_interrupt_read() function.])) ]) +AC_DEFUN([AC_PATH_UDEV], [ + PKG_CHECK_MODULES(UDEV, libudev, udev_found=yes, udev_found=no) + AC_SUBST(UDEV_CFLAGS) + AC_SUBST(UDEV_LIBS) +]) + AC_DEFUN([AC_PATH_SNDFILE], [ PKG_CHECK_MODULES(SNDFILE, sndfile, sndfile_found=yes, sndfile_found=no) AC_SUBST(SNDFILE_CFLAGS) diff --git a/configure.ac b/configure.ac index 4447f79..cf32e01 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_UDEV AC_PATH_SNDFILE AC_PATH_OUI AC_PATH_READLINE diff --git a/scripts/bluetooth-hid2hci.rules b/scripts/bluetooth-hid2hci.rules index 1b231d1..3b36629 100644 --- a/scripts/bluetooth-hid2hci.rules +++ b/scripts/bluetooth-hid2hci.rules @@ -1,36 +1,30 @@ -# Variety of Dell Bluetooth devices -# -# it looks like a bit of an odd rule, because it is matching -# on a mouse device that is self powered, but that is where -# a HID report needs to be sent to switch modes. -# -# Known supported devices: -# 413c:8154 -# 413c:8158 -# 413c:8162 -ACTION=="add", ENV{ID_VENDOR}=="413c", ENV{ID_CLASS}=="mouse", ATTRS{bmAttributes}=="e0", KERNEL=="mouse*", RUN+="/usr/sbin/hid2hci --method dell -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="hid2hci_end" +SUBSYSTEM!="usb", GOTO="hid2hci_end" + +# Variety of Dell Bluetooth devices - match on a mouse device that is +# self powered and where a HID report needs to be sent to switch modes +# Known supported devices: 413c:8154, 413c:8158, 413c:8162 +ATTR{bInterfaceClass}=="03", ATTR{bInterfaceSubClass}=="01", ATTR{bInterfaceProtocol}=="02", \ + ATTRS{bDeviceClass}=="00", ATTRS{idVendor}=="413c", ATTRS{bmAttributes}=="e0", \ + RUN+="hid2hci --method=dell --devpath=%p", ENV{HID2HCI_SWITCH}="1" # Logitech devices -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c703" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c704" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c705" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70a" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70b" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70c" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70e" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c713" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c714" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71b" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71c" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" +KERNEL=="hiddev*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70[35e]", \ + RUN+="hid2hci --method=logitech-hid --devpath=%p" +KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70[4abc]|c71[34bc]", \ + RUN+="hid2hci --method=logitech-hid --devpath=%p" + +ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end" + +# When a Dell device recovers from S3, the mouse child needs to be repoked +# Unfortunately the only event seen is the BT device disappearing, so the mouse +# device needs to be chased down on the USB bus. +ATTR{bDeviceClass}=="e0", ATTR{bDeviceSubClass}=="01", ATTR{bDeviceProtocol}=="01", ATTR{idVendor}=="413c", \ + ENV{REMOVE_CMD}="/sbin/udevadm trigger --action=change --subsystem-match=usb --property-match=HID2HCI_SWITCH=1" -# CSR devices (in HID mode) -ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" -ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci" +# CSR devices +ATTR{idVendor}=="0a12|0458|05ac", ATTR{idProduct}=="1000", RUN+="hid2hci --method=csr --devpath=%p" -# CSR devices (in HCI mode) -#ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="0001" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid" -#ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="003f" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid" -#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8203" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid" -#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8204" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid" -#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8207" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid" +LABEL="hid2hci_end" diff --git a/tools/hid2hci.8 b/tools/hid2hci.8 index 5d35274..6ea7ed8 100644 --- a/tools/hid2hci.8 +++ b/tools/hid2hci.8 @@ -29,23 +29,18 @@ is used to set up switch supported Bluetooth devices into the HCI mode and back. .SH OPTIONS .TP -.BI -h -Gives a list of possible options. -.TP -.BI -q -Don't display any messages. -.TP -.BI -r [hid,hci] +.B --mode= [hid, hci] Sets the mode to switch the device into .TP -.BI -v -Specifies the 4 digit vendor ID assigned to the device being switched +.B --method= [csr, logitech-hid, dell] +Which vendor method to use for switching the device. .TP -.BI -p -Specifies the 4 digit product ID assigned to the device being switched +.B --devpath= +Specifies the device path in /sys +.TP +.B --help +Gives a list of possible options. .TP -.BI -m [csr, logitech, dell] -Which vendor method to use for switching the device. .SH AUTHOR Written by Marcel Holtmann . .br diff --git a/tools/hid2hci.c b/tools/hid2hci.c index a640772..dea3974 100644 --- a/tools/hid2hci.c +++ b/tools/hid2hci.c @@ -1,9 +1,10 @@ /* - * - * BlueZ - Bluetooth protocol stack for Linux + * hid2hci : switch the radio on devices that support + * it from HID to HCI and back * * Copyright (C) 2003-2010 Marcel Holtmann - * + * Copyright (C) 2008-2009 Mario Limonciello + * Copyright (C) 2009-2011 Kay Sievers * * 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 @@ -32,79 +33,24 @@ #include #include #include - +#include +#include #include -#ifdef NEED_USB_GET_BUSSES -static inline struct usb_bus *usb_get_busses(void) -{ - return usb_busses; -} -#endif - -#ifndef USB_DIR_OUT -#define USB_DIR_OUT 0x00 -#endif - -static char devpath[PATH_MAX + 1] = "/dev"; - -struct hiddev_devinfo { - unsigned int bustype; - unsigned int busnum; - unsigned int devnum; - unsigned int ifnum; - short vendor; - short product; - short version; - unsigned num_applications; -}; +#include "libudev.h" -struct hiddev_report_info { - unsigned report_type; - unsigned report_id; - unsigned num_fields; +enum mode { + HCI = 0, + HID = 1, }; -typedef __signed__ int __s32; - -struct hiddev_usage_ref { - unsigned report_type; - unsigned report_id; - unsigned field_index; - unsigned usage_index; - unsigned usage_code; - __s32 value; -}; - -#define HIDIOCGDEVINFO _IOR('H', 0x03, struct hiddev_devinfo) -#define HIDIOCINITREPORT _IO('H', 0x05) -#define HIDIOCSREPORT _IOW('H', 0x08, struct hiddev_report_info) -#define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref) - -#define HID_REPORT_TYPE_OUTPUT 2 - -#define HCI 0 -#define HID 1 - -struct device_info { - struct usb_device *dev; - int mode; - uint16_t vendor; - uint16_t product; -}; - -static int switch_csr(struct device_info *devinfo) +static int usb_switch_csr(struct usb_dev_handle *dev, enum mode mode) { - struct usb_dev_handle *udev; int err; - udev = usb_open(devinfo->dev); - if (!udev) - return -errno; - - err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, devinfo->mode, 0, NULL, 0, 10000); - + err = usb_control_msg(dev, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, mode, 0, NULL, 0, 10000); if (err == 0) { err = -1; errno = EALREADY; @@ -112,13 +58,10 @@ static int switch_csr(struct device_info *devinfo) if (errno == ETIMEDOUT) err = 0; } - - usb_close(udev); - return err; } -static int send_report(int fd, const char *buf, size_t size) +static int hid_logitech_send_report(int fd, const char *buf, size_t size) { struct hiddev_report_info rinfo; struct hiddev_usage_ref uref; @@ -147,72 +90,42 @@ static int send_report(int fd, const char *buf, size_t size) return err; } -static int switch_logitech(struct device_info *devinfo) +static int hid_switch_logitech(const char *filename) { - char devname[PATH_MAX + 1]; - int i, fd, err = -1; - - for (i = 0; i < 16; i++) { - struct hiddev_devinfo dinfo; - char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 }; - char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 }; - char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 }; - - sprintf(devname, "%s/hiddev%d", devpath, i); - fd = open(devname, O_RDWR); - if (fd < 0) { - sprintf(devname, "%s/usb/hiddev%d", devpath, i); - fd = open(devname, O_RDWR); - if (fd < 0) { - sprintf(devname, "%s/usb/hid/hiddev%d", devpath, i); - fd = open(devname, O_RDWR); - if (fd < 0) - continue; - } - } - - memset(&dinfo, 0, sizeof(dinfo)); - err = ioctl(fd, HIDIOCGDEVINFO, &dinfo); - if (err < 0 || (int) dinfo.busnum != atoi(devinfo->dev->bus->dirname) || - (int) dinfo.devnum != atoi(devinfo->dev->filename)) { - close(fd); - continue; - } - - err = ioctl(fd, HIDIOCINITREPORT, 0); - if (err < 0) { - close(fd); - break; - } - - err = send_report(fd, rep1, sizeof(rep1)); - if (err < 0) { - close(fd); - break; - } - - err = send_report(fd, rep2, sizeof(rep2)); - if (err < 0) { - close(fd); - break; - } - - err = send_report(fd, rep3, sizeof(rep3)); - close(fd); - break; - } - + char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 }; + char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 }; + char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 }; + int fd; + int err = -1; + + fd = open(filename, O_RDWR); + if (fd < 0) + return err; + + err = ioctl(fd, HIDIOCINITREPORT, 0); + if (err < 0) + goto out; + + err = hid_logitech_send_report(fd, rep1, sizeof(rep1)); + if (err < 0) + goto out; + + err = hid_logitech_send_report(fd, rep2, sizeof(rep2)); + if (err < 0) + goto out; + + err = hid_logitech_send_report(fd, rep3, sizeof(rep3)); +out: + close(fd); return err; } -static int switch_dell(struct device_info *devinfo) +static int usb_switch_dell(struct usb_dev_handle *dev, enum mode mode) { char report[] = { 0x7f, 0x00, 0x00, 0x00 }; - - struct usb_dev_handle *handle; int err; - switch (devinfo->mode) { + switch (mode) { case HCI: report[1] = 0x13; break; @@ -221,22 +134,16 @@ static int switch_dell(struct device_info *devinfo) break; } - handle = usb_open(devinfo->dev); - if (!handle) - return -EIO; - /* Don't need to check return, as might not be in use */ - usb_detach_kernel_driver_np(handle, 0); + usb_detach_kernel_driver_np(dev, 0); - if (usb_claim_interface(handle, 0) < 0) { - usb_close(handle); + if (usb_claim_interface(dev, 0) < 0) return -EIO; - } - err = usb_control_msg(handle, - USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + err = usb_control_msg(dev, + USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0, - report, sizeof(report), 5000); + report, sizeof(report), 5000); if (err == 0) { err = -1; @@ -245,131 +152,200 @@ static int switch_dell(struct device_info *devinfo) if (errno == ETIMEDOUT) err = 0; } - - usb_close(handle); - return err; } -static int find_device(struct device_info* devinfo) +/* + * libusb needs to scan and open all devices, just to to find the + * device we already have. This should be fixed in libusb. + */ +static struct usb_device *usb_device_open_from_udev(struct udev_device *usb_dev) { struct usb_bus *bus; - struct usb_device *dev; + const char *str; + int busnum; + int devnum; + + str = udev_device_get_sysattr_value(usb_dev, "busnum"); + if (str == NULL) + return NULL; + busnum = strtol(str, NULL, 0); + str = udev_device_get_sysattr_value(usb_dev, "devnum"); + if (str == NULL) + return NULL; + devnum = strtol(str, NULL, 0); + + usb_init(); usb_find_busses(); usb_find_devices(); - for (bus = usb_get_busses(); bus; bus = bus->next) + for (bus = usb_get_busses(); bus; bus = bus->next) { + struct usb_device *dev; + + if (strtol(bus->dirname, NULL, 10) != busnum) + continue; + for (dev = bus->devices; dev; dev = dev->next) { - if (dev->descriptor.idVendor == devinfo->vendor && - dev->descriptor.idProduct == devinfo->product) { - devinfo->dev=dev; - return 1; - } + if (dev->devnum == devnum) + return dev; } - return 0; + } + + return NULL; } -static void usage(char* error) +static struct usb_dev_handle *find_device(struct udev_device *udev_dev) +{ + struct usb_device *dev; + + dev = usb_device_open_from_udev(udev_dev); + if (dev == NULL) + return NULL; + return usb_open(dev); +} + +static void usage(const char *error) { if (error) fprintf(stderr,"\n%s\n", error); else printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n"); - printf("Usage:\n" - "\thid2hci [options]\n" - "\n"); - - printf("Options:\n" - "\t-h, --help Display help\n" - "\t-q, --quiet Don't display any messages\n" - "\t-r, --mode= Mode to switch to [hid, hci]\n" - "\t-v, --vendor= Vendor ID to act upon\n" - "\t-p, --product= Product ID to act upon\n" - "\t-m, --method= Method to use to switch [csr, logitech, dell]\n" - "\n"); - if (error) - exit(1); + printf("Usage: hid2hci [options]\n" + " --mode= mode to switch to [hid|hci] (default hci)\n" + " --devpath= sys device path\n" + " --method= method to use to switch [csr|logitech-hid|dell]\n" + " --help\n\n"); } -static struct option main_options[] = { - { "help", no_argument, 0, 'h' }, - { "quiet", no_argument, 0, 'q' }, - { "mode", required_argument, 0, 'r' }, - { "vendor", required_argument, 0, 'v' }, - { "product", required_argument, 0, 'p' }, - { "method", required_argument, 0, 'm' }, - { 0, 0, 0, 0 } -}; - int main(int argc, char *argv[]) { - struct device_info dev = { NULL, HCI, 0, 0 }; - int opt, quiet = 0; - int (*method)(struct device_info *dev) = NULL; - - while ((opt = getopt_long(argc, argv, "+r:v:p:m:qh", main_options, NULL)) != -1) { - switch (opt) { - case 'r': - if (optarg && !strcmp(optarg, "hid")) - dev.mode = HID; - else if (optarg && !strcmp(optarg, "hci")) - dev.mode = HCI; - else - usage("ERROR: Undefined radio mode\n"); + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "mode", required_argument, NULL, 'm' }, + { "devpath", required_argument, NULL, 'p' }, + { "method", required_argument, NULL, 'M' }, + { } + }; + enum method { + METHOD_UNDEF, + METHOD_CSR, + METHOD_LOGITECH_HID, + METHOD_DELL, + } method = METHOD_UNDEF; + struct udev *udev; + struct udev_device *udev_dev = NULL; + char syspath[PATH_MAX]; + int (*usb_switch)(struct usb_dev_handle *dev, enum mode mode) = NULL; + enum mode mode = HCI; + const char *devpath = NULL; + int err = -1; + int rc = 1; + + for (;;) { + int option; + + option = getopt_long(argc, argv, "m:p:M:h", options, NULL); + if (option == -1) break; - case 'v': - sscanf(optarg, "%4hx", &dev.vendor); + + switch (option) { + case 'm': + if (!strcmp(optarg, "hid")) { + mode = HID; + } else if (!strcmp(optarg, "hci")) { + mode = HCI; + } else { + usage("error: undefined radio mode\n"); + exit(1); + } break; case 'p': - sscanf(optarg, "%4hx", &dev.product); + devpath = optarg; break; - case 'm': - if (optarg && !strcmp(optarg, "csr")) - method = switch_csr; - else if (optarg && !strcmp(optarg, "logitech")) - method = switch_logitech; - else if (optarg && !strcmp(optarg, "dell")) - method = switch_dell; - else - usage("ERROR: Undefined switching method\n"); - break; - case 'q': - quiet = 1; + case 'M': + if (!strcmp(optarg, "csr")) { + method = METHOD_CSR; + usb_switch = usb_switch_csr; + } else if (!strcmp(optarg, "logitech-hid")) { + method = METHOD_LOGITECH_HID; + } else if (!strcmp(optarg, "dell")) { + method = METHOD_DELL; + usb_switch = usb_switch_dell; + } else { + usage("error: undefined switching method\n"); + exit(1); + } break; case 'h': usage(NULL); - default: - exit(0); } } - if (!quiet && (!dev.vendor || !dev.product || !method)) - usage("ERROR: Vendor ID, Product ID, and Switching Method must all be defined.\n"); + if (!devpath || method == METHOD_UNDEF) { + usage("error: --devpath= and --method= must be defined\n"); + exit(1); + } - argc -= optind; - argv += optind; - optind = 0; + udev = udev_new(); + if (udev == NULL) + goto exit; - usb_init(); - - if (!find_device(&dev)) { - if (!quiet) - fprintf(stderr, "Device %04x:%04x not found on USB bus.\n", - dev.vendor, dev.product); - exit(1); + snprintf(syspath, sizeof(syspath), "%s/%s", udev_get_sys_path(udev), devpath); + udev_dev = udev_device_new_from_syspath(udev, syspath); + if (udev_dev == NULL) { + fprintf(stderr, "error: could not find '%s'\n", devpath); + goto exit; } - if (!quiet) - printf("Attempting to switch device %04x:%04x to %s mode ", - dev.vendor, dev.product, dev.mode ? "HID" : "HCI"); - fflush(stdout); + switch (method) { + case METHOD_CSR: + case METHOD_DELL: { + struct udev_device *dev; + struct usb_dev_handle *handle; + const char *type; + + /* get the parent usb_device if needed */ + dev = udev_dev; + type = udev_device_get_devtype(dev); + if (type == NULL || strcmp(type, "usb_device") != 0) { + dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); + if (dev == NULL) { + fprintf(stderr, "error: could not find usb_device for '%s'\n", devpath); + goto exit; + } + } + + handle = find_device(dev); + if (handle == NULL) { + fprintf(stderr, "error: unable to handle '%s'\n", + udev_device_get_syspath(dev)); + goto exit; + } + err = usb_switch(handle, mode); + break; + } + case METHOD_LOGITECH_HID: { + const char *device; - if (method(&dev) < 0 && !quiet) - printf("failed (%s)\n", strerror(errno)); - else if (!quiet) - printf("was successful\n"); + device = udev_device_get_devnode(udev_dev); + if (device == NULL) { + fprintf(stderr, "error: could not find hiddev device node\n"); + goto exit; + } + err = hid_switch_logitech(device); + break; + } + default: + break; + } - return errno; + if (err < 0) + fprintf(stderr, "error: switching device '%s' failed.\n", + udev_device_get_syspath(udev_dev)); +exit: + udev_device_unref(udev_dev); + udev_unref(udev); + return rc; }