1 /* $NetBSD: ums.c,v 1.52 2001/11/13 06:24:56 lukem 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf 42 */ 43 44 #include <sys/cdefs.h> 45 __KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.52 2001/11/13 06:24:56 lukem Exp $"); 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/kernel.h> 50 #include <sys/malloc.h> 51 #include <sys/device.h> 52 #include <sys/ioctl.h> 53 #include <sys/tty.h> 54 #include <sys/file.h> 55 #include <sys/select.h> 56 #include <sys/proc.h> 57 #include <sys/vnode.h> 58 #include <sys/poll.h> 59 60 #include <dev/usb/usb.h> 61 #include <dev/usb/usbhid.h> 62 63 #include <dev/usb/usbdi.h> 64 #include <dev/usb/usbdi_util.h> 65 #include <dev/usb/usbdevs.h> 66 #include <dev/usb/usb_quirks.h> 67 #include <dev/usb/hid.h> 68 69 #include <dev/wscons/wsconsio.h> 70 #include <dev/wscons/wsmousevar.h> 71 72 #ifdef USB_DEBUG 73 #define DPRINTF(x) if (umsdebug) logprintf x 74 #define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x 75 int umsdebug = 0; 76 #else 77 #define DPRINTF(x) 78 #define DPRINTFN(n,x) 79 #endif 80 81 #define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) 82 83 #define UMSUNIT(s) (minor(s)) 84 85 #define PS2LBUTMASK x01 86 #define PS2RBUTMASK x02 87 #define PS2MBUTMASK x04 88 #define PS2BUTMASK 0x0f 89 90 struct ums_softc { 91 USBBASEDEVICE sc_dev; /* base device */ 92 usbd_device_handle sc_udev; 93 usbd_interface_handle sc_iface; /* interface */ 94 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ 95 int sc_ep_addr; 96 97 u_char *sc_ibuf; 98 u_int8_t sc_iid; 99 int sc_isize; 100 struct hid_location sc_loc_x, sc_loc_y, sc_loc_z; 101 struct hid_location *sc_loc_btn; 102 103 int sc_enabled; 104 105 int flags; /* device configuration */ 106 #define UMS_Z 0x01 /* z direction available */ 107 #define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */ 108 #define UMS_REVZ 0x04 /* Z-axis is reversed */ 109 110 int nbuttons; 111 #define MAX_BUTTONS 31 /* chosen because sc_buttons is u_int32_t */ 112 113 u_int32_t sc_buttons; /* mouse button status */ 114 struct device *sc_wsmousedev; 115 116 char sc_dying; 117 }; 118 119 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) 120 #define MOUSE_FLAGS (HIO_RELATIVE) 121 122 Static void ums_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); 123 124 Static int ums_enable(void *); 125 Static void ums_disable(void *); 126 Static int ums_ioctl(void *, u_long, caddr_t, int, struct proc *); 127 128 const struct wsmouse_accessops ums_accessops = { 129 ums_enable, 130 ums_ioctl, 131 ums_disable, 132 }; 133 134 USB_DECLARE_DRIVER(ums); 135 136 USB_MATCH(ums) 137 { 138 USB_MATCH_START(ums, uaa); 139 usb_interface_descriptor_t *id; 140 int size, ret; 141 void *desc; 142 usbd_status err; 143 144 if (uaa->iface == NULL) 145 return (UMATCH_NONE); 146 id = usbd_get_interface_descriptor(uaa->iface); 147 if (id == NULL || id->bInterfaceClass != UICLASS_HID) 148 return (UMATCH_NONE); 149 150 err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); 151 if (err) 152 return (UMATCH_NONE); 153 154 if (hid_is_collection(desc, size, 155 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) 156 ret = UMATCH_IFACECLASS; 157 else 158 ret = UMATCH_NONE; 159 160 free(desc, M_TEMP); 161 return (ret); 162 } 163 164 USB_ATTACH(ums) 165 { 166 USB_ATTACH_START(ums, sc, uaa); 167 usbd_interface_handle iface = uaa->iface; 168 usb_interface_descriptor_t *id; 169 usb_endpoint_descriptor_t *ed; 170 struct wsmousedev_attach_args a; 171 int size; 172 void *desc; 173 usbd_status err; 174 char devinfo[1024]; 175 u_int32_t flags, quirks; 176 int i, wheel; 177 struct hid_location loc_btn; 178 179 sc->sc_udev = uaa->device; 180 sc->sc_iface = iface; 181 id = usbd_get_interface_descriptor(iface); 182 usbd_devinfo(uaa->device, 0, devinfo); 183 USB_ATTACH_SETUP; 184 printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), 185 devinfo, id->bInterfaceClass, id->bInterfaceSubClass); 186 ed = usbd_interface2endpoint_descriptor(iface, 0); 187 if (ed == NULL) { 188 printf("%s: could not read endpoint descriptor\n", 189 USBDEVNAME(sc->sc_dev)); 190 USB_ATTACH_ERROR_RETURN; 191 } 192 193 DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d " 194 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" 195 " bInterval=%d\n", 196 ed->bLength, ed->bDescriptorType, 197 ed->bEndpointAddress & UE_ADDR, 198 UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", 199 ed->bmAttributes & UE_XFERTYPE, 200 UGETW(ed->wMaxPacketSize), ed->bInterval)); 201 202 if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || 203 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { 204 printf("%s: unexpected endpoint\n", 205 USBDEVNAME(sc->sc_dev)); 206 USB_ATTACH_ERROR_RETURN; 207 } 208 209 quirks = usbd_get_quirks(uaa->device)->uq_flags; 210 if (quirks & UQ_MS_REVZ) 211 sc->flags |= UMS_REVZ; 212 if (quirks & UQ_SPUR_BUT_UP) 213 sc->flags |= UMS_SPUR_BUT_UP; 214 215 err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP); 216 if (err) 217 USB_ATTACH_ERROR_RETURN; 218 219 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 220 hid_input, &sc->sc_loc_x, &flags)) { 221 printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev)); 222 USB_ATTACH_ERROR_RETURN; 223 } 224 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { 225 printf("%s: X report 0x%04x not supported\n", 226 USBDEVNAME(sc->sc_dev), flags); 227 USB_ATTACH_ERROR_RETURN; 228 } 229 230 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 231 hid_input, &sc->sc_loc_y, &flags)) { 232 printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev)); 233 USB_ATTACH_ERROR_RETURN; 234 } 235 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { 236 printf("%s: Y report 0x%04x not supported\n", 237 USBDEVNAME(sc->sc_dev), flags); 238 USB_ATTACH_ERROR_RETURN; 239 } 240 241 /* Try to guess the Z activator: first check Z, then WHEEL. */ 242 wheel = 0; 243 if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), 244 hid_input, &sc->sc_loc_z, &flags) || 245 (wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, 246 HUG_WHEEL), 247 hid_input, &sc->sc_loc_z, &flags))) { 248 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { 249 sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ 250 } else { 251 sc->flags |= UMS_Z; 252 /* Wheels need the Z axis reversed. */ 253 if (wheel) 254 sc->flags ^= UMS_REVZ; 255 } 256 } 257 258 /* figure out the number of buttons */ 259 for (i = 1; i <= MAX_BUTTONS; i++) 260 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 261 hid_input, &loc_btn, 0)) 262 break; 263 sc->nbuttons = i - 1; 264 sc->sc_loc_btn = malloc(sizeof(struct hid_location) * sc->nbuttons, 265 M_USBDEV, M_NOWAIT); 266 if (!sc->sc_loc_btn) { 267 printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); 268 USB_ATTACH_ERROR_RETURN; 269 } 270 271 printf("%s: %d button%s%s\n", USBDEVNAME(sc->sc_dev), 272 sc->nbuttons, sc->nbuttons == 1 ? "" : "s", 273 sc->flags & UMS_Z ? " and Z dir." : ""); 274 275 for (i = 1; i <= sc->nbuttons; i++) 276 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 277 hid_input, &sc->sc_loc_btn[i-1], 0); 278 279 sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); 280 sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_NOWAIT); 281 if (sc->sc_ibuf == NULL) { 282 printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); 283 free(sc->sc_loc_btn, M_USBDEV); 284 USB_ATTACH_ERROR_RETURN; 285 } 286 287 sc->sc_ep_addr = ed->bEndpointAddress; 288 free(desc, M_TEMP); 289 290 #ifdef USB_DEBUG 291 DPRINTF(("ums_attach: sc=%p\n", sc)); 292 DPRINTF(("ums_attach: X\t%d/%d\n", 293 sc->sc_loc_x.pos, sc->sc_loc_x.size)); 294 DPRINTF(("ums_attach: Y\t%d/%d\n", 295 sc->sc_loc_y.pos, sc->sc_loc_y.size)); 296 if (sc->flags & UMS_Z) 297 DPRINTF(("ums_attach: Z\t%d/%d\n", 298 sc->sc_loc_z.pos, sc->sc_loc_z.size)); 299 for (i = 1; i <= sc->nbuttons; i++) { 300 DPRINTF(("ums_attach: B%d\t%d/%d\n", 301 i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); 302 } 303 DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); 304 #endif 305 306 a.accessops = &ums_accessops; 307 a.accesscookie = sc; 308 309 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, 310 USBDEV(sc->sc_dev)); 311 312 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 313 314 USB_ATTACH_SUCCESS_RETURN; 315 } 316 317 int 318 ums_activate(device_ptr_t self, enum devact act) 319 { 320 struct ums_softc *sc = (struct ums_softc *)self; 321 int rv = 0; 322 323 switch (act) { 324 case DVACT_ACTIVATE: 325 return (EOPNOTSUPP); 326 break; 327 328 case DVACT_DEACTIVATE: 329 if (sc->sc_wsmousedev != NULL) 330 rv = config_deactivate(sc->sc_wsmousedev); 331 sc->sc_dying = 1; 332 break; 333 } 334 return (rv); 335 } 336 337 USB_DETACH(ums) 338 { 339 USB_DETACH_START(ums, sc); 340 int rv = 0; 341 342 DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags)); 343 344 /* No need to do reference counting of ums, wsmouse has all the goo. */ 345 if (sc->sc_wsmousedev != NULL) 346 rv = config_detach(sc->sc_wsmousedev, flags); 347 #ifdef DIAGNOSTIC 348 if (sc->sc_intrpipe != NULL) { 349 printf("ums_disable: intr pipe still open\n"); 350 usbd_abort_pipe(sc->sc_intrpipe); 351 usbd_close_pipe(sc->sc_intrpipe); 352 sc->sc_intrpipe = NULL; 353 } 354 #endif 355 if (rv == 0) { 356 free(sc->sc_loc_btn, M_USBDEV); 357 free(sc->sc_ibuf, M_USBDEV); 358 } 359 360 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 361 USBDEV(sc->sc_dev)); 362 363 return (rv); 364 } 365 366 void 367 ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) 368 { 369 struct ums_softc *sc = addr; 370 u_char *ibuf; 371 int dx, dy, dz; 372 u_int32_t buttons = 0; 373 int i; 374 int s; 375 376 DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); 377 DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n", 378 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2])); 379 380 if (status == USBD_CANCELLED) 381 return; 382 383 if (status) { 384 DPRINTF(("ums_intr: status=%d\n", status)); 385 usbd_clear_endpoint_stall_async(sc->sc_intrpipe); 386 return; 387 } 388 389 ibuf = sc->sc_ibuf; 390 if (sc->sc_iid != 0) { 391 if (*ibuf++ != sc->sc_iid) 392 return; 393 } 394 dx = hid_get_data(ibuf, &sc->sc_loc_x); 395 dy = -hid_get_data(ibuf, &sc->sc_loc_y); 396 dz = hid_get_data(ibuf, &sc->sc_loc_z); 397 if (sc->flags & UMS_REVZ) 398 dz = -dz; 399 for (i = 0; i < sc->nbuttons; i++) 400 if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) 401 buttons |= (1 << UMS_BUT(i)); 402 403 if (dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) { 404 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", 405 dx, dy, dz, buttons)); 406 sc->sc_buttons = buttons; 407 if (sc->sc_wsmousedev != NULL) { 408 s = spltty(); 409 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz, 410 WSMOUSE_INPUT_DELTA); 411 splx(s); 412 } 413 } 414 } 415 416 Static int 417 ums_enable(void *v) 418 { 419 struct ums_softc *sc = v; 420 421 usbd_status err; 422 423 DPRINTFN(1,("ums_enable: sc=%p\n", sc)); 424 425 if (sc->sc_dying) 426 return (EIO); 427 428 if (sc->sc_enabled) 429 return (EBUSY); 430 431 sc->sc_enabled = 1; 432 sc->sc_buttons = 0; 433 434 /* Set up interrupt pipe. */ 435 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, 436 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, 437 sc->sc_ibuf, sc->sc_isize, ums_intr, USBD_DEFAULT_INTERVAL); 438 if (err) { 439 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", 440 err)); 441 sc->sc_enabled = 0; 442 return (EIO); 443 } 444 return (0); 445 } 446 447 Static void 448 ums_disable(void *v) 449 { 450 struct ums_softc *sc = v; 451 452 DPRINTFN(1,("ums_disable: sc=%p\n", sc)); 453 #ifdef DIAGNOSTIC 454 if (!sc->sc_enabled) { 455 printf("ums_disable: not enabled\n"); 456 return; 457 } 458 #endif 459 460 /* Disable interrupts. */ 461 usbd_abort_pipe(sc->sc_intrpipe); 462 usbd_close_pipe(sc->sc_intrpipe); 463 sc->sc_intrpipe = NULL; 464 465 sc->sc_enabled = 0; 466 } 467 468 Static int 469 ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 470 471 { 472 switch (cmd) { 473 case WSMOUSEIO_GTYPE: 474 *(u_int *)data = WSMOUSE_TYPE_USB; 475 return (0); 476 } 477 478 return (-1); 479 } 480