All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Add bluetooth-hid2hci to udev-extras
@ 2009-06-10 23:02 Mario Limonciello
  2009-06-11 13:03 ` Marcel Holtmann
                   ` (9 more replies)
  0 siblings, 10 replies; 11+ messages in thread
From: Mario Limonciello @ 2009-06-10 23:02 UTC (permalink / raw)
  To: linux-hotplug


[-- Attachment #1.1: Type: text/plain, Size: 345 bytes --]

Hi:

I wanted to move this tool out of the bluez project, into udev-extras. 
It seems to make more sense to me there as it's not really meant to be
ran by the user.

I'm attaching the patch as my mail server would eat it up otherwise
(Exchange).

Thanks,
-- 
Mario Limonciello
*Dell | Linux Engineering*
mario_limonciello@dell.com

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: bluetooth_hid2hci.diff --]
[-- Type: text/x-patch; name="bluetooth_hid2hci.diff", Size: 14358 bytes --]

=== modified file 'Makefile.am'
--- Makefile.am	2009-06-01 19:54:07 +0000
+++ Makefile.am	2009-06-10 22:31:35 +0000
@@ -7,7 +7,8 @@
 	keymap \
 	v4l_id \
 	udev-acl \
-	gudev
+	gudev \
+	bluetooth-hid2hci
 
 ACLOCAL_AMFLAGS = -I m4
 

=== added directory 'bluetooth-hid2hci'
=== added file 'bluetooth-hid2hci/62-bluetooth-hid2hci.rules'
--- bluetooth-hid2hci/62-bluetooth-hid2hci.rules	1970-01-01 00:00:00 +0000
+++ bluetooth-hid2hci/62-bluetooth-hid2hci.rules	2009-06-10 22:53:14 +0000
@@ -0,0 +1,36 @@
+# 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+="/lib/udev/bluetooth_hid2hci --method dell -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# Logitech devices
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c703" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c704" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c705" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70a" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70b" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70c" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70e" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c713" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c714" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71b" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71c" RUN+="/lib/udev/bluetooth_hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HID mode)
+ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="1000" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="1000" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="1000" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HCI mode)
+#ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="0001" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="003f" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8203" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8204" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8207" RUN+="/lib/udev/bluetooth_hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"

=== added file 'bluetooth-hid2hci/Makefile.am'
--- bluetooth-hid2hci/Makefile.am	1970-01-01 00:00:00 +0000
+++ bluetooth-hid2hci/Makefile.am	2009-06-10 22:29:36 +0000
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.am.inc
+
+udevhomedir = $(udev_prefix)/lib/udev
+udevhome_PROGRAMS = bluetooth_hid2hci
+
+udevrulesdir = $(udev_prefix)/lib/udev/rules.d
+dist_udevrules_DATA = 62-bluetooth-hid2hci.rules
+
+bluetooth_hid2hci_SOURCES = bluetooth_hid2hci.c
+bluetooth_hid2hci_LDADD = @LIBUSB_LIBS@

=== added file 'bluetooth-hid2hci/bluetooth_hid2hci.c'
--- bluetooth-hid2hci/bluetooth_hid2hci.c	1970-01-01 00:00:00 +0000
+++ bluetooth-hid2hci/bluetooth_hid2hci.c	2009-06-10 22:31:18 +0000
@@ -0,0 +1,377 @@
+/*
+ *
+ *  bluetooth_hid2hci : a tool for switching the radio on devices that support
+ *                      it from HID to HCI and back
+ *
+ *  Copyright (C) 2003-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2008-2009  Mario Limonciello <mario_limonciello@dell.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+
+#include <usb.h>
+
+#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;
+};
+
+struct hiddev_report_info {
+	unsigned report_type;
+	unsigned report_id;
+	unsigned num_fields;
+};
+
+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)
+{
+	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);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(udev);
+
+	return err;
+}
+
+static int send_report(int fd, const char *buf, size_t size)
+{
+	struct hiddev_report_info rinfo;
+	struct hiddev_usage_ref uref;
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < size; i++) {
+		memset(&uref, 0, sizeof(uref));
+		uref.report_type = HID_REPORT_TYPE_OUTPUT;
+		uref.report_id   = 0x10;
+		uref.field_index = 0;
+		uref.usage_index = i;
+		uref.usage_code  = 0xff000001;
+		uref.value       = buf[i] & 0x000000ff;
+		err = ioctl(fd, HIDIOCSUSAGE, &uref);
+		if (err < 0)
+			return err;
+	}
+
+	memset(&rinfo, 0, sizeof(rinfo));
+	rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+	rinfo.report_id   = 0x10;
+	rinfo.num_fields  = 1;
+	err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+	return err;
+}
+
+static int switch_logitech(struct device_info *devinfo)
+{
+	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;
+	}
+
+	return err;
+}
+
+static int switch_dell(struct device_info *devinfo)
+{
+	char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+
+	struct usb_dev_handle *handle;
+	int err;
+
+	switch (devinfo->mode) {
+	case HCI:
+		report[1] = 0x13;
+		break;
+	case HID:
+		report[1] = 0x14;
+		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);
+
+	if (usb_claim_interface(handle, 0) < 0) {
+		usb_close(handle);
+		return -EIO;
+	}
+
+	err = usb_control_msg(handle,
+		USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
+						report, sizeof(report), 5000);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(handle);
+
+	return err;
+}
+
+static int find_device(struct device_info* devinfo)
+{
+	struct usb_bus *bus;
+	struct usb_device *dev;
+
+	usb_find_busses();
+	usb_find_devices();
+
+	for (bus = usb_get_busses(); bus; bus = bus->next)
+		for (dev = bus->devices; dev; dev = dev->next) {
+			if (dev->descriptor.idVendor == devinfo->vendor &&
+			    dev->descriptor.idProduct == devinfo->product) {
+				devinfo->dev=dev;
+				return 1;
+			}
+		}
+	return 0;
+}
+
+static void usage(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);
+}
+
+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");
+			break;
+		case 'v':
+			sscanf(optarg, "%4hx", &dev.vendor);
+			break;
+		case 'p':
+			sscanf(optarg, "%4hx", &dev.product);
+			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;
+			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");
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	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);
+	}
+
+	if (!quiet)
+		printf("Attempting to switch device %04x:%04x to %s mode ",
+			dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
+	fflush(stdout);
+
+	if (method(&dev) < 0 && !quiet)
+		printf("failed (%s)\n", strerror(errno));
+	else if (!quiet)
+		printf("was successful\n");
+
+	return errno;
+}

=== modified file 'configure.ac'
--- configure.ac	2009-06-01 19:50:15 +0000
+++ configure.ac	2009-06-10 22:47:57 +0000
@@ -118,6 +118,7 @@
 gudev/gudev-1.0.pc
 gudev/docs/version.xml
 gudev/docs/Makefile
+bluetooth-hid2hci/Makefile
 ])
 
 AC_OUTPUT


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
@ 2009-06-11 13:03 ` Marcel Holtmann
  2009-06-11 16:13 ` Mario Limonciello
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Marcel Holtmann @ 2009-06-11 13:03 UTC (permalink / raw)
  To: linux-hotplug

Hi Mario,

> I wanted to move this tool out of the bluez project, into udev-extras. 
> It seems to make more sense to me there as it's not really meant to be
> ran by the user.

please call it hid2hci and also name the directory that way since
otherwise we confuse the hell out of people when multiple versions are
installed.

Regards

Marcel



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
  2009-06-11 13:03 ` Marcel Holtmann
@ 2009-06-11 16:13 ` Mario Limonciello
  2009-06-11 19:41 ` Kay Sievers
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Mario Limonciello @ 2009-06-11 16:13 UTC (permalink / raw)
  To: linux-hotplug


[-- Attachment #1.1: Type: text/plain, Size: 371 bytes --]

Hi Marcel:

Marcel Holtmann wrote:
> Hi Mario,
>
> please call it hid2hci and also name the directory that way since
> otherwise we confuse the hell out of people when multiple versions are
> installed
Thanks for the feedback.  I've made the changes and am resubmitting.

Regards
-- 
Mario Limonciello
*Dell | Linux Engineering*
mario_limonciello@dell.com

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: hid2hci.diff --]
[-- Type: text/x-patch; name="hid2hci.diff", Size: 13928 bytes --]

=== modified file 'Makefile.am'
--- Makefile.am	2009-06-01 19:54:07 +0000
+++ Makefile.am	2009-06-10 22:31:35 +0000
@@ -7,7 +7,8 @@
 	keymap \
 	v4l_id \
 	udev-acl \
-	gudev
+	gudev \
+	hid2hci
 
 ACLOCAL_AMFLAGS = -I m4
 

=== added directory 'hid2hci'
=== added file 'hid2hci/62-hid2hci.rules'
--- hid2hci/62-hid2hci.rules	1970-01-01 00:00:00 +0000
+++ hid2hci/62-hid2hci.rules	2009-06-10 22:53:14 +0000
@@ -0,0 +1,36 @@
+# 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+="/lib/udev/hid2hci --method dell -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# Logitech devices
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c703" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c704" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c705" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70a" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70b" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70c" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70e" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c713" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c714" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71b" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71c" RUN+="/lib/udev/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HID mode)
+ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="1000" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="1000" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="1000" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HCI mode)
+#ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="0001" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="003f" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8203" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8204" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8207" RUN+="/lib/udev/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"

=== added file 'hid2hci/Makefile.am'
--- hid2hci/Makefile.am	1970-01-01 00:00:00 +0000
+++ hid2hci/Makefile.am	2009-06-10 22:29:36 +0000
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.am.inc
+
+udevhomedir = $(udev_prefix)/lib/udev
+udevhome_PROGRAMS = hid2hci
+
+udevrulesdir = $(udev_prefix)/lib/udev/rules.d
+dist_udevrules_DATA = 62-hid2hci.rules
+
+hid2hci_SOURCES = hid2hci.c
+hid2hci_LDADD = @LIBUSB_LIBS@

=== added file 'hid2hci/bluetooth_hid2hci.c'
--- hid2hci/hid2hci.c	1970-01-01 00:00:00 +0000
+++ hid2hci/hid2hci.c	2009-06-10 22:31:18 +0000
@@ -0,0 +1,377 @@
+/*
+ *
+ *  hid2hci : a tool for switching the radio on devices that support
+ *                      it from HID to HCI and back
+ *
+ *  Copyright (C) 2003-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2008-2009  Mario Limonciello <mario_limonciello@dell.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+
+#include <usb.h>
+
+#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;
+};
+
+struct hiddev_report_info {
+	unsigned report_type;
+	unsigned report_id;
+	unsigned num_fields;
+};
+
+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)
+{
+	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);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(udev);
+
+	return err;
+}
+
+static int send_report(int fd, const char *buf, size_t size)
+{
+	struct hiddev_report_info rinfo;
+	struct hiddev_usage_ref uref;
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < size; i++) {
+		memset(&uref, 0, sizeof(uref));
+		uref.report_type = HID_REPORT_TYPE_OUTPUT;
+		uref.report_id   = 0x10;
+		uref.field_index = 0;
+		uref.usage_index = i;
+		uref.usage_code  = 0xff000001;
+		uref.value       = buf[i] & 0x000000ff;
+		err = ioctl(fd, HIDIOCSUSAGE, &uref);
+		if (err < 0)
+			return err;
+	}
+
+	memset(&rinfo, 0, sizeof(rinfo));
+	rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+	rinfo.report_id   = 0x10;
+	rinfo.num_fields  = 1;
+	err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+	return err;
+}
+
+static int switch_logitech(struct device_info *devinfo)
+{
+	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;
+	}
+
+	return err;
+}
+
+static int switch_dell(struct device_info *devinfo)
+{
+	char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+
+	struct usb_dev_handle *handle;
+	int err;
+
+	switch (devinfo->mode) {
+	case HCI:
+		report[1] = 0x13;
+		break;
+	case HID:
+		report[1] = 0x14;
+		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);
+
+	if (usb_claim_interface(handle, 0) < 0) {
+		usb_close(handle);
+		return -EIO;
+	}
+
+	err = usb_control_msg(handle,
+		USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
+						report, sizeof(report), 5000);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(handle);
+
+	return err;
+}
+
+static int find_device(struct device_info* devinfo)
+{
+	struct usb_bus *bus;
+	struct usb_device *dev;
+
+	usb_find_busses();
+	usb_find_devices();
+
+	for (bus = usb_get_busses(); bus; bus = bus->next)
+		for (dev = bus->devices; dev; dev = dev->next) {
+			if (dev->descriptor.idVendor == devinfo->vendor &&
+			    dev->descriptor.idProduct == devinfo->product) {
+				devinfo->dev=dev;
+				return 1;
+			}
+		}
+	return 0;
+}
+
+static void usage(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);
+}
+
+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");
+			break;
+		case 'v':
+			sscanf(optarg, "%4hx", &dev.vendor);
+			break;
+		case 'p':
+			sscanf(optarg, "%4hx", &dev.product);
+			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;
+			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");
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	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);
+	}
+
+	if (!quiet)
+		printf("Attempting to switch device %04x:%04x to %s mode ",
+			dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
+	fflush(stdout);
+
+	if (method(&dev) < 0 && !quiet)
+		printf("failed (%s)\n", strerror(errno));
+	else if (!quiet)
+		printf("was successful\n");
+
+	return errno;
+}

=== modified file 'configure.ac'
--- configure.ac	2009-06-01 19:50:15 +0000
+++ configure.ac	2009-06-10 22:47:57 +0000
@@ -118,6 +118,7 @@
 gudev/gudev-1.0.pc
 gudev/docs/version.xml
 gudev/docs/Makefile
+hid2hci/Makefile
 ])
 
 AC_OUTPUT


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
  2009-06-11 13:03 ` Marcel Holtmann
  2009-06-11 16:13 ` Mario Limonciello
@ 2009-06-11 19:41 ` Kay Sievers
  2009-06-11 21:05 ` Mario Limonciello
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Kay Sievers @ 2009-06-11 19:41 UTC (permalink / raw)
  To: linux-hotplug

On Thu, Jun 11, 2009 at 18:13, Mario
Limonciello<mario_limonciello@dell.com> wrote:
> Thanks for the feedback.  I've made the changes and am resubmitting.

This is all about adding USB devices, right? So the entire rules file
should be guarded by:
  ACTION="add", ... out
  SUBSYSTEM="usb", ... out
  ENV{DEVTYPE}="usb_device", ... out

to exclude all other events from trying to match these rules. Also the
individual ACTION matches are not needed then.

+ ... ENV{ID_CLASS}="mouse", ..., KERNEL="mouse*", ...

I think the kernel name match is sufficient here, there is no need to
rely on earlier rules which set ID_CLASS.

+ ... ENV{ID_VENDOR}="046d",

This is a property of usb_id, and usually a textual string and not a
number, I guess you better match against the sysfs attribute with
ATTR{}? The current stuff might work, but you should not rely on the
implicit fallback to numbers of usb_id.

+ RUN+="/lib/udev/hid2hci ..."

Things in /lib/udev don't need the prefix, just use RUN+="hid2hci ..."

Thanks,
Kay

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
                   ` (2 preceding siblings ...)
  2009-06-11 19:41 ` Kay Sievers
@ 2009-06-11 21:05 ` Mario Limonciello
  2009-06-11 21:46 ` Kay Sievers
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Mario Limonciello @ 2009-06-11 21:05 UTC (permalink / raw)
  To: linux-hotplug


[-- Attachment #1.1: Type: text/plain, Size: 1276 bytes --]

Hi Kay:


Kay Sievers wrote:
> This is all about adding USB devices, right? So the entire rules file
> should be guarded by:
>   ACTION=="add", ... out
>   SUBSYSTEM=="usb", ... out
>   ENV{DEVTYPE}=="usb_device", ... out
>
> to exclude all other events from trying to match these rules. Also the
> individual ACTION matches are not needed then.
>   
I added an ACTION guard around the top, but those other two guards break
things.
> + ... ENV{ID_CLASS}=="mouse", ..., KERNEL=="mouse*", ...
>
> I think the kernel name match is sufficient here, there is no need to
> rely on earlier rules which set ID_CLASS.
>   
Yeah you are right.  I removed the extra ENV call, and tested with a
device, works fine.
Fixed.
> + ... ENV{ID_VENDOR}=="046d",
>
> This is a property of usb_id, and usually a textual string and not a
> number, I guess you better match against the sysfs attribute with
> ATTR{}? The current stuff might work, but you should not rely on the
> implicit fallback to numbers of usb_id.
>   
Fixed.
> + RUN+="/lib/udev/hid2hci ..."
>
> Things in /lib/udev don't need the prefix, just use RUN+="hid2hci ..."
>
> Thanks,
> Kay
>   
Fixed.

Thanks,
-- 
Mario Limonciello
*Dell | Linux Engineering*
mario_limonciello@dell.com

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: hid2hci.diff --]
[-- Type: text/x-patch; name="hid2hci.diff", Size: 13575 bytes --]

=== modified file 'Makefile.am'
--- Makefile.am	2009-06-01 19:54:07 +0000
+++ Makefile.am	2009-06-11 20:30:43 +0000
@@ -7,7 +7,8 @@
 	keymap \
 	v4l_id \
 	udev-acl \
-	gudev
+	gudev \
+	hid2hci
 
 ACLOCAL_AMFLAGS = -I m4
 

=== modified file 'configure.ac'
--- configure.ac	2009-06-01 19:50:15 +0000
+++ configure.ac	2009-06-11 20:30:43 +0000
@@ -118,6 +118,7 @@
 gudev/gudev-1.0.pc
 gudev/docs/version.xml
 gudev/docs/Makefile
+hid2hci/Makefile
 ])
 
 AC_OUTPUT

=== added directory 'hid2hci'
=== added file 'hid2hci/62-hid2hci.rules'
--- hid2hci/62-hid2hci.rules	1970-01-01 00:00:00 +0000
+++ hid2hci/62-hid2hci.rules	2009-06-11 20:58:50 +0000
@@ -0,0 +1,40 @@
+ACTION!="add", GOTO="hid2hci_end"
+
+# 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
+ATTRS{idVendor}=="413c", ATTRS{bmAttributes}=="e0", KERNEL=="mouse*", RUN+="hid2hci --method dell -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+
+# Logitech devices
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c703" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c704" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c705" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70a" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70b" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70c" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70e" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c713" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c714" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c71b" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c71c" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+
+# CSR devices (in HID mode)
+ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="1000" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="0458", ATTRS{idProduct}=="1000" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+ATTRS{idVendor}=="05ac", ATTRS{idProduct}=="1000" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+
+# CSR devices (in HCI mode)
+#ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="0001" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hid"
+#ATTRS{idVendor}=="0458", ATTRS{idProduct}=="003f" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hid"
+#ATTRS{idVendor}=="05ac", ATTRS{idProduct}=="8203" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hid"
+#ATTRS{idVendor}=="05ac", ATTRS{idProduct}=="8204" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hid"
+#ATTRS{idVendor}=="05ac", ATTRS{idProduct}=="8207" RUN+="hid2hci --method csr -v $attr{idVendor} -p $attr{idProduct} --mode hid"
+
+LABEL="hid2hci_end"

=== added file 'hid2hci/Makefile.am'
--- hid2hci/Makefile.am	1970-01-01 00:00:00 +0000
+++ hid2hci/Makefile.am	2009-06-11 20:30:43 +0000
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.am.inc
+
+udevhomedir = $(udev_prefix)/lib/udev
+udevhome_PROGRAMS = hid2hci
+
+udevrulesdir = $(udev_prefix)/lib/udev/rules.d
+dist_udevrules_DATA = 62-hid2hci.rules
+
+hid2hci_SOURCES = hid2hci.c
+hid2hci_LDADD = @LIBUSB_LIBS@

=== added file 'hid2hci/hid2hci.c'
--- hid2hci/hid2hci.c	1970-01-01 00:00:00 +0000
+++ hid2hci/hid2hci.c	2009-06-11 20:30:43 +0000
@@ -0,0 +1,377 @@
+/*
+ *
+ *  hid2hci : a tool for switching the radio on devices that support
+ *                      it from HID to HCI and back
+ *
+ *  Copyright (C) 2003-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2008-2009  Mario Limonciello <mario_limonciello@dell.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+
+#include <usb.h>
+
+#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;
+};
+
+struct hiddev_report_info {
+	unsigned report_type;
+	unsigned report_id;
+	unsigned num_fields;
+};
+
+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)
+{
+	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);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(udev);
+
+	return err;
+}
+
+static int send_report(int fd, const char *buf, size_t size)
+{
+	struct hiddev_report_info rinfo;
+	struct hiddev_usage_ref uref;
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < size; i++) {
+		memset(&uref, 0, sizeof(uref));
+		uref.report_type = HID_REPORT_TYPE_OUTPUT;
+		uref.report_id   = 0x10;
+		uref.field_index = 0;
+		uref.usage_index = i;
+		uref.usage_code  = 0xff000001;
+		uref.value       = buf[i] & 0x000000ff;
+		err = ioctl(fd, HIDIOCSUSAGE, &uref);
+		if (err < 0)
+			return err;
+	}
+
+	memset(&rinfo, 0, sizeof(rinfo));
+	rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+	rinfo.report_id   = 0x10;
+	rinfo.num_fields  = 1;
+	err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+	return err;
+}
+
+static int switch_logitech(struct device_info *devinfo)
+{
+	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;
+	}
+
+	return err;
+}
+
+static int switch_dell(struct device_info *devinfo)
+{
+	char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+
+	struct usb_dev_handle *handle;
+	int err;
+
+	switch (devinfo->mode) {
+	case HCI:
+		report[1] = 0x13;
+		break;
+	case HID:
+		report[1] = 0x14;
+		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);
+
+	if (usb_claim_interface(handle, 0) < 0) {
+		usb_close(handle);
+		return -EIO;
+	}
+
+	err = usb_control_msg(handle,
+		USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
+						report, sizeof(report), 5000);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(handle);
+
+	return err;
+}
+
+static int find_device(struct device_info* devinfo)
+{
+	struct usb_bus *bus;
+	struct usb_device *dev;
+
+	usb_find_busses();
+	usb_find_devices();
+
+	for (bus = usb_get_busses(); bus; bus = bus->next)
+		for (dev = bus->devices; dev; dev = dev->next) {
+			if (dev->descriptor.idVendor == devinfo->vendor &&
+			    dev->descriptor.idProduct == devinfo->product) {
+				devinfo->dev=dev;
+				return 1;
+			}
+		}
+	return 0;
+}
+
+static void usage(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);
+}
+
+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");
+			break;
+		case 'v':
+			sscanf(optarg, "%4hx", &dev.vendor);
+			break;
+		case 'p':
+			sscanf(optarg, "%4hx", &dev.product);
+			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;
+			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");
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	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);
+	}
+
+	if (!quiet)
+		printf("Attempting to switch device %04x:%04x to %s mode ",
+			dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
+	fflush(stdout);
+
+	if (method(&dev) < 0 && !quiet)
+		printf("failed (%s)\n", strerror(errno));
+	else if (!quiet)
+		printf("was successful\n");
+
+	return errno;
+}


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
                   ` (3 preceding siblings ...)
  2009-06-11 21:05 ` Mario Limonciello
@ 2009-06-11 21:46 ` Kay Sievers
  2009-06-12 18:31 ` Mario Limonciello
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Kay Sievers @ 2009-06-11 21:46 UTC (permalink / raw)
  To: linux-hotplug

On Thu, Jun 11, 2009 at 23:05, Mario
Limonciello<mario_limonciello@dell.com> wrote:
>> This is all about adding USB devices, right? So the entire rules file
>> should be guarded by:
>>   ACTION="add", ... out
>>   SUBSYSTEM="usb", ... out
>>   ENV{DEVTYPE}="usb_device", ... out
>>
>> to exclude all other events from trying to match these rules. Also the
>> individual ACTION matches are not needed then.
>>
> I added an ACTION guard around the top, but those other two guards break
> things.

That sounds weird. We match against properties of the usb_device
device, so we should only hook into the usb_device events. We can not
do the rules that way, they will match on _all_ child events for the
usb_device.

Using ATTRS is needlessly expensive because it walks up the chain of
parent devices to search for a match. It also does not make sure we
run only once per usb_device, it will match all usb_interface and
usb_endpoint devices.

The appendend works fine here, care to check what's going wrong on your side?

Thanks,
Kay


ACTION!="add", GOTO="hid2hci_end"
SUBSYSTEM!="usb", GOTO="hid2hci_end"
ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end"

...
ATTR{idVendor}="046d", ATTR{idProduct}="c703" RUN+="...
...

LABEL="hid2hci_end"

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
                   ` (4 preceding siblings ...)
  2009-06-11 21:46 ` Kay Sievers
@ 2009-06-12 18:31 ` Mario Limonciello
  2009-06-12 19:14 ` Kay Sievers
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Mario Limonciello @ 2009-06-12 18:31 UTC (permalink / raw)
  To: linux-hotplug


[-- Attachment #1.1: Type: text/plain, Size: 929 bytes --]

Hi Kay:

Kay Sievers wrote:
>
> That sounds weird. We match against properties of the usb_device
> device, so we should only hook into the usb_device events. We can not
> do the rules that way, they will match on _all_ child events for the
> usb_device.
>
> Using ATTRS is needlessly expensive because it walks up the chain of
> parent devices to search for a match. It also does not make sure we
> run only once per usb_device, it will match all usb_interface and
> usb_endpoint devices.
>
> The appendend works fine here, care to check what's going wrong on your side?
>   
It looks like since I'm matching as far as the kernel mouse* device, I
needed to actually match the input subsystem for the Dell stuff, whereas
it's the USB subsystem for everything else.

Here's an updated attached patch that works for me.

Regards
-- 
Mario Limonciello
*Dell | Linux Engineering*
mario_limonciello@dell.com

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: hid2hci.diff --]
[-- Type: text/x-patch; name="hid2hci.diff", Size: 13612 bytes --]

=== modified file 'Makefile.am'
--- Makefile.am	2009-06-01 19:54:07 +0000
+++ Makefile.am	2009-06-11 20:30:43 +0000
@@ -7,7 +7,8 @@
 	keymap \
 	v4l_id \
 	udev-acl \
-	gudev
+	gudev \
+	hid2hci
 
 ACLOCAL_AMFLAGS = -I m4
 

=== modified file 'configure.ac'
--- configure.ac	2009-06-01 19:50:15 +0000
+++ configure.ac	2009-06-11 20:30:43 +0000
@@ -118,6 +118,7 @@
 gudev/gudev-1.0.pc
 gudev/docs/version.xml
 gudev/docs/Makefile
+hid2hci/Makefile
 ])
 
 AC_OUTPUT

=== added directory 'hid2hci'
=== added file 'hid2hci/62-hid2hci.rules'
--- hid2hci/62-hid2hci.rules	1970-01-01 00:00:00 +0000
+++ hid2hci/62-hid2hci.rules	2009-06-12 18:28:40 +0000
@@ -0,0 +1,46 @@
+ACTION!="add", GOTO="hid2hci_end"
+SUBSYSTEM!="input", GOTO="not_dell"
+
+# 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
+ATTRS{idVendor}=="413c", ATTRS{bmAttributes}=="e0", KERNEL=="mouse*", RUN+="hid2hci --method dell -v $attr{idVendor} -p $attr{idProduct} --mode hci"
+
+LABEL="not_dell"
+
+SUBSYSTEM!="usb", GOTO="hid2hci_end"
+ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end"
+
+# Logitech devices
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c703" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c704" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c705" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70a" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70b" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70c" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70e" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c713" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c714" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71b" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71c" RUN+="hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HID mode)
+ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="1000" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="1000" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="1000" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+
+# CSR devices (in HCI mode)
+#ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="0001" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="003f" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8203" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8204" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+#ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8207" RUN+="hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+
+LABEL="hid2hci_end"

=== added file 'hid2hci/Makefile.am'
--- hid2hci/Makefile.am	1970-01-01 00:00:00 +0000
+++ hid2hci/Makefile.am	2009-06-11 20:30:43 +0000
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.am.inc
+
+udevhomedir = $(udev_prefix)/lib/udev
+udevhome_PROGRAMS = hid2hci
+
+udevrulesdir = $(udev_prefix)/lib/udev/rules.d
+dist_udevrules_DATA = 62-hid2hci.rules
+
+hid2hci_SOURCES = hid2hci.c
+hid2hci_LDADD = @LIBUSB_LIBS@

=== added file 'hid2hci/hid2hci.c'
--- hid2hci/hid2hci.c	1970-01-01 00:00:00 +0000
+++ hid2hci/hid2hci.c	2009-06-11 20:30:43 +0000
@@ -0,0 +1,377 @@
+/*
+ *
+ *  hid2hci : a tool for switching the radio on devices that support
+ *                      it from HID to HCI and back
+ *
+ *  Copyright (C) 2003-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2008-2009  Mario Limonciello <mario_limonciello@dell.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+
+#include <usb.h>
+
+#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;
+};
+
+struct hiddev_report_info {
+	unsigned report_type;
+	unsigned report_id;
+	unsigned num_fields;
+};
+
+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)
+{
+	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);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(udev);
+
+	return err;
+}
+
+static int send_report(int fd, const char *buf, size_t size)
+{
+	struct hiddev_report_info rinfo;
+	struct hiddev_usage_ref uref;
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < size; i++) {
+		memset(&uref, 0, sizeof(uref));
+		uref.report_type = HID_REPORT_TYPE_OUTPUT;
+		uref.report_id   = 0x10;
+		uref.field_index = 0;
+		uref.usage_index = i;
+		uref.usage_code  = 0xff000001;
+		uref.value       = buf[i] & 0x000000ff;
+		err = ioctl(fd, HIDIOCSUSAGE, &uref);
+		if (err < 0)
+			return err;
+	}
+
+	memset(&rinfo, 0, sizeof(rinfo));
+	rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+	rinfo.report_id   = 0x10;
+	rinfo.num_fields  = 1;
+	err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+	return err;
+}
+
+static int switch_logitech(struct device_info *devinfo)
+{
+	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;
+	}
+
+	return err;
+}
+
+static int switch_dell(struct device_info *devinfo)
+{
+	char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+
+	struct usb_dev_handle *handle;
+	int err;
+
+	switch (devinfo->mode) {
+	case HCI:
+		report[1] = 0x13;
+		break;
+	case HID:
+		report[1] = 0x14;
+		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);
+
+	if (usb_claim_interface(handle, 0) < 0) {
+		usb_close(handle);
+		return -EIO;
+	}
+
+	err = usb_control_msg(handle,
+		USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
+						report, sizeof(report), 5000);
+
+	if (err == 0) {
+		err = -1;
+		errno = EALREADY;
+	} else {
+		if (errno == ETIMEDOUT)
+			err = 0;
+	}
+
+	usb_close(handle);
+
+	return err;
+}
+
+static int find_device(struct device_info* devinfo)
+{
+	struct usb_bus *bus;
+	struct usb_device *dev;
+
+	usb_find_busses();
+	usb_find_devices();
+
+	for (bus = usb_get_busses(); bus; bus = bus->next)
+		for (dev = bus->devices; dev; dev = dev->next) {
+			if (dev->descriptor.idVendor == devinfo->vendor &&
+			    dev->descriptor.idProduct == devinfo->product) {
+				devinfo->dev=dev;
+				return 1;
+			}
+		}
+	return 0;
+}
+
+static void usage(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);
+}
+
+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");
+			break;
+		case 'v':
+			sscanf(optarg, "%4hx", &dev.vendor);
+			break;
+		case 'p':
+			sscanf(optarg, "%4hx", &dev.product);
+			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;
+			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");
+
+	argc -= optind;
+	argv += optind;
+	optind = 0;
+
+	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);
+	}
+
+	if (!quiet)
+		printf("Attempting to switch device %04x:%04x to %s mode ",
+			dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
+	fflush(stdout);
+
+	if (method(&dev) < 0 && !quiet)
+		printf("failed (%s)\n", strerror(errno));
+	else if (!quiet)
+		printf("was successful\n");
+
+	return errno;
+}


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
                   ` (5 preceding siblings ...)
  2009-06-12 18:31 ` Mario Limonciello
@ 2009-06-12 19:14 ` Kay Sievers
  2009-06-13  1:31 ` Kay Sievers
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 11+ messages in thread
From: Kay Sievers @ 2009-06-12 19:14 UTC (permalink / raw)
  To: linux-hotplug

On Fri, 2009-06-12 at 13:31 -0500, Mario Limonciello wrote:

> > Using ATTRS is needlessly expensive because it walks up the chain of
> > parent devices to search for a match. It also does not make sure we
> > run only once per usb_device, it will match all usb_interface and
> > usb_endpoint devices.
> >
> > The appendend works fine here, care to check what's going wrong on your side?
> >   
> It looks like since I'm matching as far as the kernel mouse* device, I
> needed to actually match the input subsystem for the Dell stuff, whereas
> it's the USB subsystem for everything else.

I see, then we just do: "KERNEL="mouse*", SUBSYSTEMS="usb", ..." for
this rule, and should be fine.

> Here's an updated attached patch that works for me.

Uh, why are the ID_VENDOR matches back? As mentioned in the earlier
mail, we should not rely on the numeric fallback values of the usb_id
program (happens only when no textual string is available), that is just
too fragile. It isn't even guaranteed, that usb_id will even run before
these rules.

We should be able to directly use ATTR{} of the device, not a value
imported by an earlier program.

Something like the appended should work, and we should also be able to
fold all the very similar rules into fewer pattern matches.

Sorry for insisting here, but we need to get this right, people will
copy the stuff we ship to other rules, and we need to be careful,
because all rules match on all device events on the system, and we need
to make sure not to match needlessly on things that will never be
executed. And we need to avoid needless dependencies between rules
files.

Thanks,
Kay

#################
ACTION!="add", GOTO="hid2hci_end"

KERNEL="mouse*", SUBSYSTEMS="usb", ATTRS{idVendor}="413c", ATTRS{bmAttributes}="e0", \
  RUN+="hid2hci --method dell -v $attr{idVendor} -p $attr{idProduct} --mode hci"

SUBSYSTEM!="usb", GOTO="hid2hci_end"
ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end"

ATTR{idVendor}="046d", ATTR{idProduct}="c70[345abce]" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
ATTR{idVendor}="046d", ATTR{idProduct}="c71[34bc]" RUN+="hid2hci --method logitech -v $attr{idVendor} -p $attr{idProduct} --mode hci"
...

LABEL="hid2hci_end"
#################


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
                   ` (6 preceding siblings ...)
  2009-06-12 19:14 ` Kay Sievers
@ 2009-06-13  1:31 ` Kay Sievers
  2009-06-15 14:09 ` Mario Limonciello
  2009-06-15 14:18 ` Kay Sievers
  9 siblings, 0 replies; 11+ messages in thread
From: Kay Sievers @ 2009-06-13  1:31 UTC (permalink / raw)
  To: linux-hotplug

On Fri, Jun 12, 2009 at 21:25, Mario
Limonciello<mario_limonciello@dell.com> wrote:

> Thanks for tirelessly reviewing and making these recommendations.

I've committed this now, and changed ATTRS{} to ATTR{} for the reason
mentioned earlier, and folded some of the rules into a pattern match.

Any reason why the last section in the rules file was completely
commented-out? I've removed the comment characters, if these rules
should not be in the file, let me know and we will remove them.

  http://git.kernel.org/?p=linux/hotplug/udev-extras.git;a=blob;f=hid2hci/62-hid2hci.rules;hb=HEAD

Thanks,
Kay

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
                   ` (7 preceding siblings ...)
  2009-06-13  1:31 ` Kay Sievers
@ 2009-06-15 14:09 ` Mario Limonciello
  2009-06-15 14:18 ` Kay Sievers
  9 siblings, 0 replies; 11+ messages in thread
From: Mario Limonciello @ 2009-06-15 14:09 UTC (permalink / raw)
  To: linux-hotplug

[-- Attachment #1: Type: text/plain, Size: 856 bytes --]

Hi Kay:

Kay Sievers wrote:
>
> Any reason why the last section in the rules file was completely
> commented-out? I've removed the comment characters, if these rules
> should not be in the file, let me know and we will remove them.
>
>   http://git.kernel.org/?p=linux/hotplug/udev-extras.git;a=blob;f=hid2hci/62-hid2hci.rules;hb=HEAD
>   
They were actually intentionally commented out.  I believe those vid/pid
combos should be produced when the above vid/pid combos are ran (eg when
the device switches from hid->hci).  I don't have that particular
hardware so I can't verify that for sure.  I was leaving them commented
out in the event someone said "I can't get my device out of HCI mode.
Help!", but without causing an endless loop of switching modes.
-- 
Mario Limonciello
*Dell | Linux Engineering*
mario_limonciello@dell.com


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 260 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] Add bluetooth-hid2hci to udev-extras
  2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
                   ` (8 preceding siblings ...)
  2009-06-15 14:09 ` Mario Limonciello
@ 2009-06-15 14:18 ` Kay Sievers
  9 siblings, 0 replies; 11+ messages in thread
From: Kay Sievers @ 2009-06-15 14:18 UTC (permalink / raw)
  To: linux-hotplug

On Mon, Jun 15, 2009 at 16:09, Mario
Limonciello<mario_limonciello@dell.com> wrote:
>> Any reason why the last section in the rules file was completely
>> commented-out? I've removed the comment characters, if these rules
>> should not be in the file, let me know and we will remove them.

> They were actually intentionally commented out.  I believe those vid/pid
> combos should be produced when the above vid/pid combos are ran (eg when
> the device switches from hid->hci).  I don't have that particular
> hardware so I can't verify that for sure.  I was leaving them commented
> out in the event someone said "I can't get my device out of HCI mode.
> Help!", but without causing an endless loop of switching modes.

Ok, fine, I deleted them. We don't want to collect stuff that we are
not sure we will need.

Thanks,
Kay

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2009-06-15 14:18 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-06-10 23:02 [PATCH] Add bluetooth-hid2hci to udev-extras Mario Limonciello
2009-06-11 13:03 ` Marcel Holtmann
2009-06-11 16:13 ` Mario Limonciello
2009-06-11 19:41 ` Kay Sievers
2009-06-11 21:05 ` Mario Limonciello
2009-06-11 21:46 ` Kay Sievers
2009-06-12 18:31 ` Mario Limonciello
2009-06-12 19:14 ` Kay Sievers
2009-06-13  1:31 ` Kay Sievers
2009-06-15 14:09 ` Mario Limonciello
2009-06-15 14:18 ` Kay Sievers

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.