1 /* $NetBSD: ums.c,v 1.44 2000/06/01 14:29:01 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/usbhid10.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 buttons%s\n", USBDEVNAME(sc->sc_dev), 271 sc->nbuttons, sc->flags & UMS_Z ? " and Z dir." : ""); 272 273 for (i = 1; i <= sc->nbuttons; i++) 274 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 275 hid_input, &sc->sc_loc_btn[i-1], 0); 276 277 sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); 278 sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_NOWAIT); 279 if (sc->sc_ibuf == NULL) { 280 printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); 281 free(sc->sc_loc_btn, M_USBDEV); 282 USB_ATTACH_ERROR_RETURN; 283 } 284 285 sc->sc_ep_addr = ed->bEndpointAddress; 286 free(desc, M_TEMP); 287 288 #ifdef USB_DEBUG 289 DPRINTF(("ums_attach: sc=%p\n", sc)); 290 DPRINTF(("ums_attach: X\t%d/%d\n", 291 sc->sc_loc_x.pos, sc->sc_loc_x.size)); 292 DPRINTF(("ums_attach: Y\t%d/%d\n", 293 sc->sc_loc_x.pos, sc->sc_loc_x.size)); 294 if (sc->flags & UMS_Z) 295 DPRINTF(("ums_attach: Z\t%d/%d\n", 296 sc->sc_loc_z.pos, sc->sc_loc_z.size)); 297 for (i = 1; i <= sc->nbuttons; i++) { 298 DPRINTF(("ums_attach: B%d\t%d/%d\n", 299 i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); 300 } 301 DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); 302 #endif 303 304 a.accessops = &ums_accessops; 305 a.accesscookie = sc; 306 307 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 308 309 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, 310 USBDEV(sc->sc_dev)); 311 312 USB_ATTACH_SUCCESS_RETURN; 313 } 314 315 int 316 ums_activate(device_ptr_t self, enum devact act) 317 { 318 struct ums_softc *sc = (struct ums_softc *)self; 319 int rv = 0; 320 321 switch (act) { 322 case DVACT_ACTIVATE: 323 return (EOPNOTSUPP); 324 break; 325 326 case DVACT_DEACTIVATE: 327 if (sc->sc_wsmousedev != NULL) 328 rv = config_deactivate(sc->sc_wsmousedev); 329 sc->sc_dying = 1; 330 break; 331 } 332 return (rv); 333 } 334 335 USB_DETACH(ums) 336 { 337 USB_DETACH_START(ums, sc); 338 int rv = 0; 339 340 DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags)); 341 342 /* No need to do reference counting of ums, wsmouse has all the goo. */ 343 if (sc->sc_wsmousedev != NULL) 344 rv = config_detach(sc->sc_wsmousedev, flags); 345 if (rv == 0) { 346 free(sc->sc_loc_btn, M_USBDEV); 347 free(sc->sc_ibuf, M_USBDEV); 348 } 349 350 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 351 USBDEV(sc->sc_dev)); 352 353 return (rv); 354 } 355 356 void 357 ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) 358 { 359 struct ums_softc *sc = addr; 360 u_char *ibuf; 361 int dx, dy, dz; 362 u_int32_t buttons = 0; 363 int i; 364 int s; 365 366 DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); 367 DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n", 368 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2])); 369 370 if (status == USBD_CANCELLED) 371 return; 372 373 if (status) { 374 DPRINTF(("ums_intr: status=%d\n", status)); 375 usbd_clear_endpoint_stall_async(sc->sc_intrpipe); 376 return; 377 } 378 379 ibuf = sc->sc_ibuf; 380 if (sc->sc_iid != 0) { 381 if (*ibuf++ != sc->sc_iid) 382 return; 383 } 384 dx = hid_get_data(ibuf, &sc->sc_loc_x); 385 dy = -hid_get_data(ibuf, &sc->sc_loc_y); 386 dz = hid_get_data(ibuf, &sc->sc_loc_z); 387 if (sc->flags & UMS_REVZ) 388 dz = -dz; 389 for (i = 0; i < sc->nbuttons; i++) 390 if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) 391 buttons |= (1 << UMS_BUT(i)); 392 393 if (dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) { 394 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", 395 dx, dy, dz, buttons)); 396 sc->sc_buttons = buttons; 397 if (sc->sc_wsmousedev != NULL) { 398 s = spltty(); 399 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz, 400 WSMOUSE_INPUT_DELTA); 401 splx(s); 402 } 403 } 404 } 405 406 Static int 407 ums_enable(void *v) 408 { 409 struct ums_softc *sc = v; 410 411 usbd_status err; 412 413 DPRINTFN(1,("ums_enable: sc=%p\n", sc)); 414 415 if (sc->sc_dying) 416 return (EIO); 417 418 if (sc->sc_enabled) 419 return (EBUSY); 420 421 sc->sc_enabled = 1; 422 sc->sc_buttons = 0; 423 424 /* Set up interrupt pipe. */ 425 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, 426 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, 427 sc->sc_ibuf, sc->sc_isize, ums_intr, USBD_DEFAULT_INTERVAL); 428 if (err) { 429 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", 430 err)); 431 sc->sc_enabled = 0; 432 return (EIO); 433 } 434 return (0); 435 } 436 437 Static void 438 ums_disable(void *v) 439 { 440 struct ums_softc *sc = v; 441 442 DPRINTFN(1,("ums_disable: sc=%p\n", sc)); 443 #ifdef DIAGNOSTIC 444 if (!sc->sc_enabled) { 445 printf("ums_disable: not enabled\n"); 446 return; 447 } 448 #endif 449 450 /* Disable interrupts. */ 451 usbd_abort_pipe(sc->sc_intrpipe); 452 usbd_close_pipe(sc->sc_intrpipe); 453 454 sc->sc_enabled = 0; 455 } 456 457 Static int 458 ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 459 460 { 461 switch (cmd) { 462 case WSMOUSEIO_GTYPE: 463 *(u_int *)data = WSMOUSE_TYPE_USB; 464 return (0); 465 } 466 467 return (-1); 468 } 469