1 /* $NetBSD: btms.c,v 1.12 2014/12/13 19:28:55 nonaka Exp $ */ 2 3 /* 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net) at 9 * Carlstedt Research & Technology. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /*- 34 * Copyright (c) 2006 Itronix Inc. 35 * All rights reserved. 36 * 37 * Written by Iain Hibbert for Itronix Inc. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. The name of Itronix Inc. may not be used to endorse 48 * or promote products derived from this software without specific 49 * prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 53 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 54 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 55 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 56 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 57 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 58 * ON ANY THEORY OF LIABILITY, WHETHER IN 59 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 60 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 61 * POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64 /* 65 * based on dev/usb/ums.c 66 */ 67 68 #include <sys/cdefs.h> 69 __KERNEL_RCSID(0, "$NetBSD: btms.c,v 1.12 2014/12/13 19:28:55 nonaka Exp $"); 70 71 #include <sys/param.h> 72 #include <sys/conf.h> 73 #include <sys/device.h> 74 #include <sys/proc.h> 75 #include <sys/systm.h> 76 77 #include <netbt/bluetooth.h> 78 79 #include <dev/bluetooth/bthid.h> 80 #include <dev/bluetooth/bthidev.h> 81 82 #include <dev/usb/hid.h> 83 #include <dev/usb/usb.h> 84 #include <dev/usb/usbhid.h> 85 86 #include <dev/wscons/wsconsio.h> 87 #include <dev/wscons/wsmousevar.h> 88 89 #ifdef BTMS_DEBUG 90 int btms_debug = 0; 91 #define BTMSDBG(s) if (btms_debug) printf s 92 #define BTMSDBGN(n,s) if (btms_debug > (n)) printf s 93 #else 94 #define BTMSDBG(s) 95 #define BTMSDBGN(n,s) 96 #endif 97 98 #define MAX_BUTTONS 31 99 #define BUTTON(n) (1 << (((n) == 1 || (n) == 2) ? 3 - (n) : (n))) 100 #define NOTMOUSE(f) (((f) & (HIO_CONST | HIO_RELATIVE)) != HIO_RELATIVE) 101 102 struct btms_softc { 103 struct bthidev sc_hidev; /* device+ */ 104 105 device_t sc_wsmouse; /* child */ 106 int sc_enabled; 107 uint16_t sc_flags; 108 109 /* locators */ 110 struct hid_location sc_loc_x; 111 struct hid_location sc_loc_y; 112 struct hid_location sc_loc_z; 113 struct hid_location sc_loc_w; 114 struct hid_location sc_loc_button[MAX_BUTTONS]; 115 116 int sc_num_buttons; 117 uint32_t sc_buttons; 118 }; 119 120 /* sc_flags */ 121 #define BTMS_REVZ (1 << 0) /* reverse Z direction */ 122 #define BTMS_HASZ (1 << 1) /* has Z direction */ 123 #define BTMS_HASW (1 << 2) /* has W direction */ 124 125 /* autoconf(9) methods */ 126 static int btms_match(device_t, cfdata_t, void *); 127 static void btms_attach(device_t, device_t, void *); 128 static int btms_detach(device_t, int); 129 130 CFATTACH_DECL_NEW(btms, sizeof(struct btms_softc), 131 btms_match, btms_attach, btms_detach, NULL); 132 133 /* wsmouse(4) accessops */ 134 static int btms_wsmouse_enable(void *); 135 static int btms_wsmouse_ioctl(void *, unsigned long, void *, int, struct lwp *); 136 static void btms_wsmouse_disable(void *); 137 138 static const struct wsmouse_accessops btms_wsmouse_accessops = { 139 btms_wsmouse_enable, 140 btms_wsmouse_ioctl, 141 btms_wsmouse_disable, 142 }; 143 144 /* bthid methods */ 145 static void btms_input(struct bthidev *, uint8_t *, int); 146 147 #ifdef BTMS_DEBUG 148 static void btms_print_device(struct btms_softc *); 149 #endif 150 151 /* 152 * quirks 153 */ 154 static const struct btms_quirk { 155 int vendor; 156 int product; 157 158 uint32_t flags; 159 #define BTMS_QUIRK_ELECOM __BIT(0) 160 } btms_quirk_table[] = { 161 /* ELECOM M-XG2BB */ 162 { 0x056e, 0x00d2, BTMS_QUIRK_ELECOM }, 163 }; 164 165 static uint32_t 166 btms_lookup_quirk_flags(int vendor, int product) 167 { 168 const struct btms_quirk *q; 169 int i; 170 171 for (i = 0; i < __arraycount(btms_quirk_table); ++i) { 172 q = &btms_quirk_table[i]; 173 if (vendor == q->vendor && product == q->product) 174 return q->flags; 175 } 176 return 0; 177 } 178 179 static void btms_fixup_elecom(struct bthidev_attach_args *,struct btms_softc *); 180 181 /***************************************************************************** 182 * 183 * btms autoconf(9) routines 184 */ 185 186 static int 187 btms_match(device_t parent, cfdata_t match, void *aux) 188 { 189 struct bthidev_attach_args *ba = aux; 190 191 if (hid_is_collection(ba->ba_desc, ba->ba_dlen, ba->ba_id, 192 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) 193 return 1; 194 195 return 0; 196 } 197 198 static void 199 btms_attach(device_t parent, device_t self, void *aux) 200 { 201 struct btms_softc *sc = device_private(self); 202 struct bthidev_attach_args *ba = aux; 203 struct wsmousedev_attach_args wsma; 204 struct hid_location *zloc; 205 uint32_t flags, quirks; 206 int i, hl; 207 208 ba->ba_input = btms_input; 209 210 quirks = btms_lookup_quirk_flags(ba->ba_vendor, ba->ba_product); 211 212 /* control the horizontal */ 213 hl = hid_locate(ba->ba_desc, 214 ba->ba_dlen, 215 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 216 ba->ba_id, 217 hid_input, 218 &sc->sc_loc_x, 219 &flags); 220 221 if (hl == 0 || NOTMOUSE(flags)) { 222 aprint_error("X report 0x%04x not supported\n", flags); 223 return; 224 } 225 226 /* control the vertical */ 227 hl = hid_locate(ba->ba_desc, 228 ba->ba_dlen, 229 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 230 ba->ba_id, 231 hid_input, 232 &sc->sc_loc_y, 233 &flags); 234 235 if (hl == 0 || NOTMOUSE(flags)) { 236 aprint_error("Y report 0x%04x not supported\n", flags); 237 return; 238 } 239 240 /* Try the wheel first as the Z activator since it's tradition. */ 241 hl = hid_locate(ba->ba_desc, 242 ba->ba_dlen, 243 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), 244 ba->ba_id, 245 hid_input, 246 &sc->sc_loc_z, 247 &flags); 248 249 zloc = &sc->sc_loc_z; 250 if (hl) { 251 if (NOTMOUSE(flags)) { 252 aprint_error("Wheel report 0x%04x ignored\n", flags); 253 254 /* ignore Bad Z coord */ 255 sc->sc_loc_z.size = 0; 256 } else { 257 sc->sc_flags |= BTMS_HASZ; 258 /* Wheels need the Z axis reversed. */ 259 sc->sc_flags ^= BTMS_REVZ; 260 /* Put Z on the W coordinate */ 261 zloc = &sc->sc_loc_w; 262 } 263 } 264 265 hl = hid_locate(ba->ba_desc, 266 ba->ba_dlen, 267 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), 268 ba->ba_id, 269 hid_input, 270 zloc, 271 &flags); 272 273 /* 274 * The horizontal component of the scrollball can also be given by 275 * Application Control Pan in the Consumer page, so if we didnt see 276 * any Z then check that. 277 */ 278 if (!hl) { 279 hl = hid_locate(ba->ba_desc, 280 ba->ba_dlen, 281 HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), 282 ba->ba_id, 283 hid_input, 284 zloc, 285 &flags); 286 } 287 288 if (hl) { 289 if (NOTMOUSE(flags)) 290 zloc->size = 0; /* ignore Z */ 291 else { 292 if (sc->sc_flags & BTMS_HASZ) 293 sc->sc_flags |= BTMS_HASW; 294 else 295 sc->sc_flags |= BTMS_HASZ; 296 } 297 } 298 299 for (i = 1 ; i <= MAX_BUTTONS ; i++) { 300 hl = hid_locate(ba->ba_desc, 301 ba->ba_dlen, 302 HID_USAGE2(HUP_BUTTON, i), 303 ba->ba_id, 304 hid_input, 305 &sc->sc_loc_button[i - 1], 306 NULL); 307 308 if (hl == 0) 309 break; 310 } 311 sc->sc_num_buttons = i - 1; 312 313 if (ISSET(quirks, BTMS_QUIRK_ELECOM)) 314 btms_fixup_elecom(ba, sc); 315 316 aprint_normal(": %d button%s%s%s%s.\n", 317 sc->sc_num_buttons, 318 sc->sc_num_buttons == 1 ? "" : "s", 319 sc->sc_flags & BTMS_HASW ? ", W" : "", 320 sc->sc_flags & BTMS_HASZ ? " and Z dir" : "", 321 sc->sc_flags & BTMS_HASW ? "s" : ""); 322 #ifdef BTMS_DEBUG 323 if (btms_debug) 324 btms_print_device(sc); 325 #endif 326 327 wsma.accessops = &btms_wsmouse_accessops; 328 wsma.accesscookie = sc; 329 330 sc->sc_wsmouse = config_found(self, &wsma, wsmousedevprint); 331 332 pmf_device_register(self, NULL, NULL); 333 } 334 335 static int 336 btms_detach(device_t self, int flags) 337 { 338 struct btms_softc *sc = device_private(self); 339 int err = 0; 340 341 pmf_device_deregister(self); 342 343 if (sc->sc_wsmouse != NULL) { 344 err = config_detach(sc->sc_wsmouse, flags); 345 sc->sc_wsmouse = NULL; 346 } 347 348 return err; 349 } 350 351 /***************************************************************************** 352 * 353 * wsmouse(4) accessops 354 */ 355 356 static int 357 btms_wsmouse_enable(void *cookie) 358 { 359 struct btms_softc *sc = cookie; 360 361 if (sc->sc_enabled) 362 return EBUSY; 363 364 sc->sc_enabled = 1; 365 return 0; 366 } 367 368 static int 369 btms_wsmouse_ioctl(void *cookie, unsigned long cmd, void *data, 370 int flag, struct lwp *l) 371 { 372 373 switch (cmd) { 374 case WSMOUSEIO_GTYPE: 375 *(uint *)data = WSMOUSE_TYPE_BLUETOOTH; 376 break; 377 378 default: 379 return EPASSTHROUGH; 380 } 381 382 return 0; 383 } 384 385 static void 386 btms_wsmouse_disable(void *cookie) 387 { 388 struct btms_softc *sc = cookie; 389 390 sc->sc_enabled = 0; 391 } 392 393 /***************************************************************************** 394 * 395 * btms input routine, called from our parent 396 */ 397 398 static void 399 btms_input(struct bthidev *hidev, uint8_t *data, int len) 400 { 401 struct btms_softc *sc = (struct btms_softc *)hidev; 402 int dx, dy, dz, dw; 403 uint32_t buttons; 404 int i, s; 405 406 if (sc->sc_wsmouse == NULL || sc->sc_enabled == 0) 407 return; 408 409 #ifdef BTMS_DEBUG 410 if (btms_debug > 9) { 411 printf("%s: data: ", __func__); 412 for (i = 0; i < len; ++i) { 413 printf("%02x", data[i]); 414 } 415 printf("\n"); 416 } 417 #endif 418 419 dx = hid_get_data(data, &sc->sc_loc_x); 420 dy = -hid_get_data(data, &sc->sc_loc_y); 421 dz = hid_get_data(data, &sc->sc_loc_z); 422 dw = hid_get_data(data, &sc->sc_loc_w); 423 424 if (sc->sc_flags & BTMS_REVZ) 425 dz = -dz; 426 427 buttons = 0; 428 for (i = 0 ; i < sc->sc_num_buttons ; i++) 429 if (hid_get_data(data, &sc->sc_loc_button[i])) 430 buttons |= BUTTON(i); 431 432 BTMSDBGN(9,("%s: dx=%d, dy=%d, dz=%d, dw=%d, buttons=0x%08x\n", 433 __func__, dx, dy, dz, dw, buttons)); 434 if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || buttons != sc->sc_buttons) { 435 sc->sc_buttons = buttons; 436 437 s = spltty(); 438 wsmouse_input(sc->sc_wsmouse, 439 buttons, 440 dx, dy, dz, dw, 441 WSMOUSE_INPUT_DELTA); 442 splx(s); 443 } 444 } 445 446 #ifdef BTMS_DEBUG 447 static void 448 btms_print_device(struct btms_softc *sc) 449 { 450 int i; 451 452 printf("btms: X: pos=%d, size=%d\n", 453 sc->sc_loc_x.pos, sc->sc_loc_x.size); 454 printf("btms: Y: pos=%d, size=%d\n", 455 sc->sc_loc_y.pos, sc->sc_loc_y.size); 456 if (sc->sc_flags & BTMS_HASZ) { 457 printf("btms: Z: pos=%d, size=%d%s\n", 458 sc->sc_loc_z.pos, sc->sc_loc_z.size, 459 ((sc->sc_flags & BTMS_REVZ) ? ", REVZ" : "")); 460 } 461 if (sc->sc_flags & BTMS_HASW) { 462 printf("btms: W: pos=%d, size=%d\n", 463 sc->sc_loc_w.pos, sc->sc_loc_w.size); 464 } 465 466 for (i = 0; i < sc->sc_num_buttons; ++i) { 467 printf("btms: button%d: pos=%d, size=%d\n", i, 468 sc->sc_loc_button[i].pos, sc->sc_loc_button[i].size); 469 } 470 } 471 #endif 472 473 /***************************************************************************** 474 * 475 * fixup routines 476 */ 477 static void 478 btms_fixup_elecom(struct bthidev_attach_args *ba, struct btms_softc *sc) 479 { 480 481 switch (ba->ba_product) { 482 case 0x00d2: /* M-XG2BB */ 483 /* invalid Wheel and AC_Pan */ 484 BTMSDBG(("%s: fixup ELECOM M-XG2BB\n", __func__)); 485 sc->sc_loc_z.pos = 40; 486 sc->sc_loc_z.size = 8; 487 sc->sc_loc_w.pos = 0; 488 sc->sc_loc_w.size = 0; 489 sc->sc_flags = BTMS_HASZ | BTMS_REVZ; 490 break; 491 } 492 } 493