1 /* $OpenBSD: ums.c,v 1.5 2001/05/03 02:20:34 aaron Exp $ */ 2 /* $NetBSD: ums.c,v 1.47 2001/01/23 14:04:14 augustss Exp $ */ 3 4 /* 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Lennart Augustsson (lennart@augustsson.net) at 10 * Carlstedt Research & Technology. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the NetBSD 23 * Foundation, Inc. and its contributors. 24 * 4. Neither the name of The NetBSD Foundation nor the names of its 25 * contributors may be used to endorse or promote products derived 26 * from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41 /* 42 * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf 43 */ 44 45 /* XXX complete SPUR_UP change */ 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_alloc_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_alloc_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_x.pos, sc->sc_loc_x.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 if (rv == 0) { 348 free(sc->sc_loc_btn, M_USBDEV); 349 free(sc->sc_ibuf, M_USBDEV); 350 } 351 352 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 353 USBDEV(sc->sc_dev)); 354 355 return (rv); 356 } 357 358 void 359 ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) 360 { 361 struct ums_softc *sc = addr; 362 u_char *ibuf; 363 int dx, dy, dz; 364 u_int32_t buttons = 0; 365 int i; 366 int s; 367 368 DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); 369 DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n", 370 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2])); 371 372 if (status == USBD_CANCELLED) 373 return; 374 375 if (status) { 376 DPRINTF(("ums_intr: status=%d\n", status)); 377 usbd_clear_endpoint_stall_async(sc->sc_intrpipe); 378 return; 379 } 380 381 ibuf = sc->sc_ibuf; 382 if (sc->sc_iid != 0) { 383 if (*ibuf++ != sc->sc_iid) 384 return; 385 } 386 dx = hid_get_data(ibuf, &sc->sc_loc_x); 387 dy = -hid_get_data(ibuf, &sc->sc_loc_y); 388 dz = hid_get_data(ibuf, &sc->sc_loc_z); 389 if (sc->flags & UMS_REVZ) 390 dz = -dz; 391 for (i = 0; i < sc->nbuttons; i++) 392 if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) 393 buttons |= (1 << UMS_BUT(i)); 394 395 if (dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) { 396 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", 397 dx, dy, dz, buttons)); 398 sc->sc_buttons = buttons; 399 if (sc->sc_wsmousedev != NULL) { 400 s = spltty(); 401 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz, 402 WSMOUSE_INPUT_DELTA); 403 splx(s); 404 } 405 } 406 } 407 408 Static int 409 ums_enable(void *v) 410 { 411 struct ums_softc *sc = v; 412 413 usbd_status err; 414 415 DPRINTFN(1,("ums_enable: sc=%p\n", sc)); 416 417 if (sc->sc_dying) 418 return (EIO); 419 420 if (sc->sc_enabled) 421 return (EBUSY); 422 423 sc->sc_enabled = 1; 424 sc->sc_buttons = 0; 425 426 /* Set up interrupt pipe. */ 427 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, 428 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, 429 sc->sc_ibuf, sc->sc_isize, ums_intr, USBD_DEFAULT_INTERVAL); 430 if (err) { 431 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", 432 err)); 433 sc->sc_enabled = 0; 434 return (EIO); 435 } 436 return (0); 437 } 438 439 Static void 440 ums_disable(void *v) 441 { 442 struct ums_softc *sc = v; 443 444 DPRINTFN(1,("ums_disable: sc=%p\n", sc)); 445 #ifdef DIAGNOSTIC 446 if (!sc->sc_enabled) { 447 printf("ums_disable: not enabled\n"); 448 return; 449 } 450 #endif 451 452 /* Disable interrupts. */ 453 usbd_abort_pipe(sc->sc_intrpipe); 454 usbd_close_pipe(sc->sc_intrpipe); 455 456 sc->sc_enabled = 0; 457 } 458 459 Static int 460 ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 461 462 { 463 switch (cmd) { 464 case WSMOUSEIO_GTYPE: 465 *(u_int *)data = WSMOUSE_TYPE_USB; 466 return (0); 467 } 468 469 return (-1); 470 } 471