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