• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdint.h>
32 #include <string.h>
33 #include <getopt.h>
34 #include <sys/ioctl.h>
35 
36 #include <usb.h>
37 
38 #ifdef NEED_USB_GET_BUSSES
usb_get_busses(void)39 static inline struct usb_bus *usb_get_busses(void)
40 {
41 	return usb_busses;
42 }
43 #endif
44 
45 #ifndef USB_DIR_OUT
46 #define USB_DIR_OUT	0x00
47 #endif
48 
49 static char devpath[PATH_MAX + 1] = "/dev";
50 
51 struct hiddev_devinfo {
52 	unsigned int bustype;
53 	unsigned int busnum;
54 	unsigned int devnum;
55 	unsigned int ifnum;
56 	short vendor;
57 	short product;
58 	short version;
59 	unsigned num_applications;
60 };
61 
62 struct hiddev_report_info {
63 	unsigned report_type;
64 	unsigned report_id;
65 	unsigned num_fields;
66 };
67 
68 typedef __signed__ int __s32;
69 
70 struct hiddev_usage_ref {
71 	unsigned report_type;
72 	unsigned report_id;
73 	unsigned field_index;
74 	unsigned usage_index;
75 	unsigned usage_code;
76 	__s32 value;
77 };
78 
79 #define HIDIOCGDEVINFO		_IOR('H', 0x03, struct hiddev_devinfo)
80 #define HIDIOCINITREPORT	_IO('H', 0x05)
81 #define HIDIOCSREPORT		_IOW('H', 0x08, struct hiddev_report_info)
82 #define HIDIOCSUSAGE		_IOW('H', 0x0C, struct hiddev_usage_ref)
83 
84 #define HID_REPORT_TYPE_OUTPUT	2
85 
86 #define HCI 0
87 #define HID 1
88 
89 struct device_info {
90 	struct usb_device *dev;
91 	int mode;
92 	uint16_t vendor;
93 	uint16_t product;
94 };
95 
switch_csr(struct device_info * devinfo)96 static int switch_csr(struct device_info *devinfo)
97 {
98 	struct usb_dev_handle *udev;
99 	int err;
100 
101 	udev = usb_open(devinfo->dev);
102 	if (!udev)
103 		return -errno;
104 
105 	err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
106 				0, devinfo->mode, 0, NULL, 0, 10000);
107 
108 	if (err == 0) {
109 		err = -1;
110 		errno = EALREADY;
111 	} else {
112 		if (errno == ETIMEDOUT)
113 			err = 0;
114 	}
115 
116 	usb_close(udev);
117 
118 	return err;
119 }
120 
send_report(int fd,const char * buf,size_t size)121 static int send_report(int fd, const char *buf, size_t size)
122 {
123 	struct hiddev_report_info rinfo;
124 	struct hiddev_usage_ref uref;
125 	unsigned int i;
126 	int err;
127 
128 	for (i = 0; i < size; i++) {
129 		memset(&uref, 0, sizeof(uref));
130 		uref.report_type = HID_REPORT_TYPE_OUTPUT;
131 		uref.report_id   = 0x10;
132 		uref.field_index = 0;
133 		uref.usage_index = i;
134 		uref.usage_code  = 0xff000001;
135 		uref.value       = buf[i] & 0x000000ff;
136 		err = ioctl(fd, HIDIOCSUSAGE, &uref);
137 		if (err < 0)
138 			return err;
139 	}
140 
141 	memset(&rinfo, 0, sizeof(rinfo));
142 	rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
143 	rinfo.report_id   = 0x10;
144 	rinfo.num_fields  = 1;
145 	err = ioctl(fd, HIDIOCSREPORT, &rinfo);
146 
147 	return err;
148 }
149 
switch_logitech(struct device_info * devinfo)150 static int switch_logitech(struct device_info *devinfo)
151 {
152 	char devname[PATH_MAX + 1];
153 	int i, fd, err = -1;
154 
155 	for (i = 0; i < 16; i++) {
156 		struct hiddev_devinfo dinfo;
157 		char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
158 		char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
159 		char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
160 
161 		sprintf(devname, "%s/hiddev%d", devpath, i);
162 		fd = open(devname, O_RDWR);
163 		if (fd < 0) {
164 			sprintf(devname, "%s/usb/hiddev%d", devpath, i);
165 			fd = open(devname, O_RDWR);
166 			if (fd < 0) {
167 				sprintf(devname, "%s/usb/hid/hiddev%d", devpath, i);
168 				fd = open(devname, O_RDWR);
169 				if (fd < 0)
170 					continue;
171 			}
172 		}
173 
174 		memset(&dinfo, 0, sizeof(dinfo));
175 		err = ioctl(fd, HIDIOCGDEVINFO, &dinfo);
176 		if (err < 0 || (int) dinfo.busnum != atoi(devinfo->dev->bus->dirname) ||
177 				(int) dinfo.devnum != atoi(devinfo->dev->filename)) {
178 			close(fd);
179 			continue;
180 		}
181 
182 		err = ioctl(fd, HIDIOCINITREPORT, 0);
183 		if (err < 0) {
184 			close(fd);
185 			break;
186 		}
187 
188 		err = send_report(fd, rep1, sizeof(rep1));
189 		if (err < 0) {
190 			close(fd);
191 			break;
192 		}
193 
194 		err = send_report(fd, rep2, sizeof(rep2));
195 		if (err < 0) {
196 			close(fd);
197 			break;
198 		}
199 
200 		err = send_report(fd, rep3, sizeof(rep3));
201 		close(fd);
202 		break;
203 	}
204 
205 	return err;
206 }
207 
switch_dell(struct device_info * devinfo)208 static int switch_dell(struct device_info *devinfo)
209 {
210 	char report[] = { 0x7f, 0x00, 0x00, 0x00 };
211 
212 	struct usb_dev_handle *handle;
213 	int err;
214 
215 	switch (devinfo->mode) {
216 	case HCI:
217 		report[1] = 0x13;
218 		break;
219 	case HID:
220 		report[1] = 0x14;
221 		break;
222 	}
223 
224 	handle = usb_open(devinfo->dev);
225 	if (!handle)
226 		return -EIO;
227 
228 	/* Don't need to check return, as might not be in use */
229 	usb_detach_kernel_driver_np(handle, 0);
230 
231 	if (usb_claim_interface(handle, 0) < 0) {
232 		usb_close(handle);
233 		return -EIO;
234 	}
235 
236 	err = usb_control_msg(handle,
237 		USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
238 			USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
239 						report, sizeof(report), 5000);
240 
241 	if (err == 0) {
242 		err = -1;
243 		errno = EALREADY;
244 	} else {
245 		if (errno == ETIMEDOUT)
246 			err = 0;
247 	}
248 
249 	usb_close(handle);
250 
251 	return err;
252 }
253 
find_device(struct device_info * devinfo)254 static int find_device(struct device_info* devinfo)
255 {
256 	struct usb_bus *bus;
257 	struct usb_device *dev;
258 
259 	usb_find_busses();
260 	usb_find_devices();
261 
262 	for (bus = usb_get_busses(); bus; bus = bus->next)
263 		for (dev = bus->devices; dev; dev = dev->next) {
264 			if (dev->descriptor.idVendor == devinfo->vendor &&
265 			    dev->descriptor.idProduct == devinfo->product) {
266 				devinfo->dev=dev;
267 				return 1;
268 			}
269 		}
270 	return 0;
271 }
272 
usage(char * error)273 static void usage(char* error)
274 {
275 	if (error)
276 		fprintf(stderr,"\n%s\n", error);
277 	else
278 		printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n");
279 
280 	printf("Usage:\n"
281 		"\thid2hci [options]\n"
282 		"\n");
283 
284 	printf("Options:\n"
285 		"\t-h, --help           Display help\n"
286 		"\t-q, --quiet          Don't display any messages\n"
287 		"\t-r, --mode=          Mode to switch to [hid, hci]\n"
288 		"\t-v, --vendor=        Vendor ID to act upon\n"
289 		"\t-p, --product=       Product ID to act upon\n"
290 		"\t-m, --method=        Method to use to switch [csr, logitech, dell]\n"
291 		"\n");
292 	if (error)
293 		exit(1);
294 }
295 
296 static struct option main_options[] = {
297 	{ "help",	no_argument, 0, 'h' },
298 	{ "quiet",	no_argument, 0, 'q' },
299 	{ "mode",	required_argument, 0, 'r' },
300 	{ "vendor",	required_argument, 0, 'v' },
301 	{ "product",	required_argument, 0, 'p' },
302 	{ "method",	required_argument, 0, 'm' },
303 	{ 0, 0, 0, 0 }
304 };
305 
main(int argc,char * argv[])306 int main(int argc, char *argv[])
307 {
308 	struct device_info dev = { NULL, HCI, 0, 0 };
309 	int opt, quiet = 0;
310 	int (*method)(struct device_info *dev) = NULL;
311 
312 	while ((opt = getopt_long(argc, argv, "+r:v:p:m:qh", main_options, NULL)) != -1) {
313 		switch (opt) {
314 		case 'r':
315 			if (optarg && !strcmp(optarg, "hid"))
316 				dev.mode = HID;
317 			else if (optarg && !strcmp(optarg, "hci"))
318 				dev.mode = HCI;
319 			else
320 				usage("ERROR: Undefined radio mode\n");
321 			break;
322 		case 'v':
323 			sscanf(optarg, "%4hx", &dev.vendor);
324 			break;
325 		case 'p':
326 			sscanf(optarg, "%4hx", &dev.product);
327 			break;
328 		case 'm':
329 			if (optarg && !strcmp(optarg, "csr"))
330 				method = switch_csr;
331 			else if (optarg && !strcmp(optarg, "logitech"))
332 				method = switch_logitech;
333 			else if (optarg && !strcmp(optarg, "dell"))
334 				method = switch_dell;
335 			else
336 				usage("ERROR: Undefined switching method\n");
337 			break;
338 		case 'q':
339 			quiet = 1;
340 			break;
341 		case 'h':
342 			usage(NULL);
343 		default:
344 			exit(0);
345 		}
346 	}
347 
348 	if (!quiet && (!dev.vendor || !dev.product || !method))
349 		usage("ERROR: Vendor ID, Product ID, and Switching Method must all be defined.\n");
350 
351 	argc -= optind;
352 	argv += optind;
353 	optind = 0;
354 
355 	usb_init();
356 
357 	if (!find_device(&dev)) {
358 		if (!quiet)
359 			fprintf(stderr, "Device %04x:%04x not found on USB bus.\n",
360 				dev.vendor, dev.product);
361 		exit(1);
362 	}
363 
364 	if (!quiet)
365 		printf("Attempting to switch device %04x:%04x to %s mode ",
366 			dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
367 	fflush(stdout);
368 
369 	if (method(&dev) < 0 && !quiet)
370 		printf("failed (%s)\n", strerror(errno));
371 	else if (!quiet)
372 		printf("was successful\n");
373 
374 	return errno;
375 }
376