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