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 39static 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 49static char devpath[PATH_MAX + 1] = "/dev"; 50 51struct 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 62struct hiddev_report_info { 63 unsigned report_type; 64 unsigned report_id; 65 unsigned num_fields; 66}; 67 68typedef __signed__ int __s32; 69 70struct 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 89struct device_info { 90 struct usb_device *dev; 91 int mode; 92 uint16_t vendor; 93 uint16_t product; 94}; 95 96static 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 121static 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 150static 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 208static 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 254static 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 273static 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 296static 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 306int 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