ums.c revision 190734
1184610Salfred/*- 2184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc. 3184610Salfred * All rights reserved. 4184610Salfred * 5184610Salfred * This code is derived from software contributed to The NetBSD Foundation 6184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at 7184610Salfred * Carlstedt Research & Technology. 8184610Salfred * 9184610Salfred * Redistribution and use in source and binary forms, with or without 10184610Salfred * modification, are permitted provided that the following conditions 11184610Salfred * are met: 12184610Salfred * 1. Redistributions of source code must retain the above copyright 13184610Salfred * notice, this list of conditions and the following disclaimer. 14184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 15184610Salfred * notice, this list of conditions and the following disclaimer in the 16184610Salfred * documentation and/or other materials provided with the distribution. 17184610Salfred * 3. All advertising materials mentioning features or use of this software 18184610Salfred * must display the following acknowledgement: 19184610Salfred * This product includes software developed by the NetBSD 20184610Salfred * Foundation, Inc. and its contributors. 21184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its 22184610Salfred * contributors may be used to endorse or promote products derived 23184610Salfred * from this software without specific prior written permission. 24184610Salfred * 25184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35184610Salfred * POSSIBILITY OF SUCH DAMAGE. 36184610Salfred */ 37184610Salfred 38184610Salfred#include <sys/cdefs.h> 39184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/input/ums.c 190734 2009-04-05 18:20:38Z thompsa $"); 40184610Salfred 41184610Salfred/* 42184610Salfred * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 43184610Salfred */ 44184610Salfred 45188746Sthompsa#include "usbdevs.h" 46188942Sthompsa#include <dev/usb/usb.h> 47188942Sthompsa#include <dev/usb/usb_mfunc.h> 48188942Sthompsa#include <dev/usb/usb_error.h> 49188942Sthompsa#include <dev/usb/usbhid.h> 50184610Salfred 51184610Salfred#define USB_DEBUG_VAR ums_debug 52184610Salfred 53188942Sthompsa#include <dev/usb/usb_core.h> 54188942Sthompsa#include <dev/usb/usb_util.h> 55188942Sthompsa#include <dev/usb/usb_debug.h> 56188942Sthompsa#include <dev/usb/usb_busdma.h> 57188942Sthompsa#include <dev/usb/usb_process.h> 58188942Sthompsa#include <dev/usb/usb_transfer.h> 59188942Sthompsa#include <dev/usb/usb_request.h> 60188942Sthompsa#include <dev/usb/usb_dynamic.h> 61188942Sthompsa#include <dev/usb/usb_mbuf.h> 62188942Sthompsa#include <dev/usb/usb_dev.h> 63188942Sthompsa#include <dev/usb/usb_hid.h> 64184610Salfred 65188942Sthompsa#include <dev/usb/quirk/usb_quirk.h> 66184610Salfred 67184610Salfred#include <sys/ioccom.h> 68184610Salfred#include <sys/filio.h> 69184610Salfred#include <sys/tty.h> 70184610Salfred#include <sys/mouse.h> 71184610Salfred 72184610Salfred#if USB_DEBUG 73184610Salfredstatic int ums_debug = 0; 74184610Salfred 75184610SalfredSYSCTL_NODE(_hw_usb2, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); 76184610SalfredSYSCTL_INT(_hw_usb2_ums, OID_AUTO, debug, CTLFLAG_RW, 77184610Salfred &ums_debug, 0, "Debug level"); 78184610Salfred#endif 79184610Salfred 80184610Salfred#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) 81184610Salfred#define MOUSE_FLAGS (HIO_RELATIVE) 82184610Salfred 83184610Salfred#define UMS_BUF_SIZE 8 /* bytes */ 84184610Salfred#define UMS_IFQ_MAXLEN 50 /* units */ 85184610Salfred#define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ 86184610Salfred#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) 87184610Salfred 88187259Sthompsaenum { 89187259Sthompsa UMS_INTR_DT, 90188981Sthompsa UMS_N_TRANSFER, 91187259Sthompsa}; 92187259Sthompsa 93184610Salfredstruct ums_softc { 94184610Salfred struct usb2_fifo_sc sc_fifo; 95184610Salfred struct mtx sc_mtx; 96184610Salfred struct usb2_callout sc_callout; 97184610Salfred struct hid_location sc_loc_w; 98184610Salfred struct hid_location sc_loc_x; 99184610Salfred struct hid_location sc_loc_y; 100184610Salfred struct hid_location sc_loc_z; 101184610Salfred struct hid_location sc_loc_t; 102184610Salfred struct hid_location sc_loc_btn[UMS_BUTTON_MAX]; 103184610Salfred mousehw_t sc_hw; 104184610Salfred mousemode_t sc_mode; 105184610Salfred mousestatus_t sc_status; 106184610Salfred 107184610Salfred struct usb2_xfer *sc_xfer[UMS_N_TRANSFER]; 108184610Salfred 109184610Salfred uint32_t sc_flags; 110184610Salfred#define UMS_FLAG_X_AXIS 0x0001 111184610Salfred#define UMS_FLAG_Y_AXIS 0x0002 112184610Salfred#define UMS_FLAG_Z_AXIS 0x0004 113184610Salfred#define UMS_FLAG_T_AXIS 0x0008 114184610Salfred#define UMS_FLAG_SBU 0x0010 /* spurious button up events */ 115188981Sthompsa#define UMS_FLAG_REVZ 0x0020 /* Z-axis is reversed */ 116188981Sthompsa#define UMS_FLAG_W_AXIS 0x0040 117184610Salfred 118184610Salfred uint8_t sc_buttons; 119184610Salfred uint8_t sc_iid; 120189583Sthompsa uint8_t sc_iid_w; 121189583Sthompsa uint8_t sc_iid_x; 122189583Sthompsa uint8_t sc_iid_y; 123189583Sthompsa uint8_t sc_iid_z; 124189583Sthompsa uint8_t sc_iid_t; 125189583Sthompsa uint8_t sc_iid_btn[UMS_BUTTON_MAX]; 126184610Salfred uint8_t sc_temp[64]; 127184610Salfred}; 128184610Salfred 129184610Salfredstatic void ums_put_queue_timeout(void *__sc); 130184610Salfred 131184610Salfredstatic usb2_callback_t ums_intr_callback; 132184610Salfred 133184610Salfredstatic device_probe_t ums_probe; 134184610Salfredstatic device_attach_t ums_attach; 135184610Salfredstatic device_detach_t ums_detach; 136184610Salfred 137184610Salfredstatic usb2_fifo_cmd_t ums_start_read; 138184610Salfredstatic usb2_fifo_cmd_t ums_stop_read; 139184610Salfredstatic usb2_fifo_open_t ums_open; 140184610Salfredstatic usb2_fifo_close_t ums_close; 141184610Salfredstatic usb2_fifo_ioctl_t ums_ioctl; 142184610Salfred 143184610Salfredstatic void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons); 144184610Salfred 145184610Salfredstatic struct usb2_fifo_methods ums_fifo_methods = { 146184610Salfred .f_open = &ums_open, 147184610Salfred .f_close = &ums_close, 148184610Salfred .f_ioctl = &ums_ioctl, 149184610Salfred .f_start_read = &ums_start_read, 150184610Salfred .f_stop_read = &ums_stop_read, 151184610Salfred .basename[0] = "ums", 152184610Salfred}; 153184610Salfred 154184610Salfredstatic void 155184610Salfredums_put_queue_timeout(void *__sc) 156184610Salfred{ 157184610Salfred struct ums_softc *sc = __sc; 158184610Salfred 159184610Salfred mtx_assert(&sc->sc_mtx, MA_OWNED); 160184610Salfred 161184610Salfred ums_put_queue(sc, 0, 0, 0, 0, 0); 162184610Salfred} 163184610Salfred 164184610Salfredstatic void 165184610Salfredums_intr_callback(struct usb2_xfer *xfer) 166184610Salfred{ 167184610Salfred struct ums_softc *sc = xfer->priv_sc; 168184610Salfred uint8_t *buf = sc->sc_temp; 169184610Salfred uint16_t len = xfer->actlen; 170184610Salfred int32_t buttons = 0; 171184610Salfred int32_t dw; 172184610Salfred int32_t dx; 173184610Salfred int32_t dy; 174184610Salfred int32_t dz; 175184610Salfred int32_t dt; 176184610Salfred uint8_t i; 177189583Sthompsa uint8_t id; 178184610Salfred 179184610Salfred switch (USB_GET_STATE(xfer)) { 180184610Salfred case USB_ST_TRANSFERRED: 181184610Salfred DPRINTFN(6, "sc=%p actlen=%d\n", sc, len); 182184610Salfred 183184610Salfred if (len > sizeof(sc->sc_temp)) { 184184610Salfred DPRINTFN(6, "truncating large packet to %zu bytes\n", 185184610Salfred sizeof(sc->sc_temp)); 186184610Salfred len = sizeof(sc->sc_temp); 187184610Salfred } 188188981Sthompsa if (len == 0) 189184610Salfred goto tr_setup; 190188981Sthompsa 191184610Salfred usb2_copy_out(xfer->frbuffers, 0, buf, len); 192184610Salfred 193184610Salfred DPRINTFN(6, "data = %02x %02x %02x %02x " 194184610Salfred "%02x %02x %02x %02x\n", 195184610Salfred (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0, 196184610Salfred (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0, 197184610Salfred (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0, 198184610Salfred (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0); 199184610Salfred 200184610Salfred if (sc->sc_iid) { 201189583Sthompsa id = *buf; 202184610Salfred 203184610Salfred len--; 204184610Salfred buf++; 205184610Salfred 206184610Salfred } else { 207189583Sthompsa id = 0; 208184610Salfred if (sc->sc_flags & UMS_FLAG_SBU) { 209184610Salfred if ((*buf == 0x14) || (*buf == 0x15)) { 210184610Salfred goto tr_setup; 211184610Salfred } 212184610Salfred } 213184610Salfred } 214184610Salfred 215189583Sthompsa if ((sc->sc_flags & UMS_FLAG_W_AXIS) && (id == sc->sc_iid_w)) 216189583Sthompsa dw = hid_get_data(buf, len, &sc->sc_loc_w); 217189583Sthompsa else 218189583Sthompsa dw = 0; 219184610Salfred 220189583Sthompsa if ((sc->sc_flags & UMS_FLAG_X_AXIS) && (id == sc->sc_iid_x)) 221189583Sthompsa dx = hid_get_data(buf, len, &sc->sc_loc_x); 222189583Sthompsa else 223189583Sthompsa dx = 0; 224184610Salfred 225189583Sthompsa if ((sc->sc_flags & UMS_FLAG_Y_AXIS) && (id == sc->sc_iid_y)) 226189583Sthompsa dy = -hid_get_data(buf, len, &sc->sc_loc_y); 227189583Sthompsa else 228189583Sthompsa dy = 0; 229184610Salfred 230189583Sthompsa if ((sc->sc_flags & UMS_FLAG_Z_AXIS) && (id == sc->sc_iid_z)) 231189583Sthompsa dz = -hid_get_data(buf, len, &sc->sc_loc_z); 232189583Sthompsa else 233189583Sthompsa dz = 0; 234184610Salfred 235189583Sthompsa if (sc->sc_flags & UMS_FLAG_REVZ) 236184610Salfred dz = -dz; 237184610Salfred 238189583Sthompsa if ((sc->sc_flags & UMS_FLAG_T_AXIS) && (id == sc->sc_iid_t)) 239189583Sthompsa dt = -hid_get_data(buf, len, &sc->sc_loc_t); 240189583Sthompsa else 241189583Sthompsa dt = 0; 242189583Sthompsa 243184610Salfred for (i = 0; i < sc->sc_buttons; i++) { 244189583Sthompsa if (id != sc->sc_iid_btn[i]) 245189583Sthompsa continue; 246184610Salfred if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) { 247184610Salfred buttons |= (1 << UMS_BUT(i)); 248184610Salfred } 249184610Salfred } 250184610Salfred 251184610Salfred if (dx || dy || dz || dt || dw || 252184610Salfred (buttons != sc->sc_status.button)) { 253184610Salfred 254184610Salfred DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n", 255184610Salfred dx, dy, dz, dt, dw, buttons); 256184610Salfred 257184610Salfred sc->sc_status.button = buttons; 258184610Salfred sc->sc_status.dx += dx; 259184610Salfred sc->sc_status.dy += dy; 260184610Salfred sc->sc_status.dz += dz; 261184610Salfred /* 262184610Salfred * sc->sc_status.dt += dt; 263184610Salfred * no way to export this yet 264184610Salfred */ 265184610Salfred 266184610Salfred /* 267188981Sthompsa * The Qtronix keyboard has a built in PS/2 268188981Sthompsa * port for a mouse. The firmware once in a 269188981Sthompsa * while posts a spurious button up 270188981Sthompsa * event. This event we ignore by doing a 271188981Sthompsa * timeout for 50 msecs. If we receive 272188981Sthompsa * dx=dy=dz=buttons=0 before we add the event 273188981Sthompsa * to the queue. In any other case we delete 274188981Sthompsa * the timeout event. 275184610Salfred */ 276184610Salfred if ((sc->sc_flags & UMS_FLAG_SBU) && 277184610Salfred (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) && 278184610Salfred (dw == 0) && (buttons == 0)) { 279184610Salfred 280184610Salfred usb2_callout_reset(&sc->sc_callout, hz / 20, 281184610Salfred &ums_put_queue_timeout, sc); 282184610Salfred } else { 283184610Salfred 284184610Salfred usb2_callout_stop(&sc->sc_callout); 285184610Salfred 286184610Salfred ums_put_queue(sc, dx, dy, dz, dt, buttons); 287184610Salfred } 288184610Salfred } 289184610Salfred case USB_ST_SETUP: 290184610Salfredtr_setup: 291188981Sthompsa /* check if we can put more data into the FIFO */ 292188981Sthompsa if (usb2_fifo_put_bytes_max( 293188981Sthompsa sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { 294188981Sthompsa xfer->frlengths[0] = xfer->max_data_length; 295188981Sthompsa usb2_start_hardware(xfer); 296184610Salfred } 297188981Sthompsa break; 298184610Salfred 299184610Salfred default: /* Error */ 300184610Salfred if (xfer->error != USB_ERR_CANCELLED) { 301188981Sthompsa /* try clear stall first */ 302188981Sthompsa xfer->flags.stall_pipe = 1; 303188981Sthompsa goto tr_setup; 304184610Salfred } 305188981Sthompsa break; 306184610Salfred } 307184610Salfred} 308184610Salfred 309184610Salfredstatic const struct usb2_config ums_config[UMS_N_TRANSFER] = { 310184610Salfred 311187259Sthompsa [UMS_INTR_DT] = { 312184610Salfred .type = UE_INTERRUPT, 313184610Salfred .endpoint = UE_ADDR_ANY, 314184610Salfred .direction = UE_DIR_IN, 315190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 316190734Sthompsa .bufsize = 0, /* use wMaxPacketSize */ 317190734Sthompsa .callback = &ums_intr_callback, 318184610Salfred }, 319184610Salfred}; 320184610Salfred 321184610Salfredstatic int 322184610Salfredums_probe(device_t dev) 323184610Salfred{ 324184610Salfred struct usb2_attach_arg *uaa = device_get_ivars(dev); 325184610Salfred struct usb2_interface_descriptor *id; 326184610Salfred void *d_ptr; 327188981Sthompsa int error; 328184610Salfred uint16_t d_len; 329184610Salfred 330184610Salfred DPRINTFN(11, "\n"); 331184610Salfred 332188981Sthompsa if (uaa->usb2_mode != USB_MODE_HOST) 333184610Salfred return (ENXIO); 334188981Sthompsa 335184610Salfred id = usb2_get_interface_descriptor(uaa->iface); 336184610Salfred 337184610Salfred if ((id == NULL) || 338188981Sthompsa (id->bInterfaceClass != UICLASS_HID)) 339184610Salfred return (ENXIO); 340188981Sthompsa 341190172Sthompsa error = usb2_req_get_hid_desc(uaa->device, NULL, 342184610Salfred &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); 343184610Salfred 344188981Sthompsa if (error) 345184610Salfred return (ENXIO); 346188981Sthompsa 347184610Salfred if (hid_is_collection(d_ptr, d_len, 348188981Sthompsa HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) 349184610Salfred error = 0; 350188981Sthompsa else if ((id->bInterfaceSubClass == UISUBCLASS_BOOT) && 351188981Sthompsa (id->bInterfaceProtocol == UIPROTO_MOUSE)) 352184610Salfred error = 0; 353188981Sthompsa else 354184610Salfred error = ENXIO; 355184610Salfred 356184610Salfred free(d_ptr, M_TEMP); 357184610Salfred return (error); 358184610Salfred} 359184610Salfred 360184610Salfredstatic int 361184610Salfredums_attach(device_t dev) 362184610Salfred{ 363184610Salfred struct usb2_attach_arg *uaa = device_get_ivars(dev); 364184610Salfred struct ums_softc *sc = device_get_softc(dev); 365184610Salfred void *d_ptr = NULL; 366184610Salfred int unit = device_get_unit(dev); 367188981Sthompsa int isize; 368188981Sthompsa int isizebits; 369188981Sthompsa int err; 370184610Salfred uint32_t flags; 371184610Salfred uint16_t d_len; 372184610Salfred uint8_t i; 373184610Salfred 374184610Salfred DPRINTFN(11, "sc=%p\n", sc); 375184610Salfred 376184610Salfred device_set_usb2_desc(dev); 377184610Salfred 378184610Salfred mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE); 379184610Salfred 380186454Sthompsa usb2_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 381184610Salfred 382184610Salfred /* 383184610Salfred * Force the report (non-boot) protocol. 384184610Salfred * 385184610Salfred * Mice without boot protocol support may choose not to implement 386184610Salfred * Set_Protocol at all; Ignore any error. 387184610Salfred */ 388184610Salfred err = usb2_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1); 389184610Salfred 390184610Salfred err = usb2_transfer_setup(uaa->device, 391184610Salfred &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config, 392184610Salfred UMS_N_TRANSFER, sc, &sc->sc_mtx); 393184610Salfred 394184610Salfred if (err) { 395184610Salfred DPRINTF("error=%s\n", usb2_errstr(err)); 396184610Salfred goto detach; 397184610Salfred } 398190172Sthompsa err = usb2_req_get_hid_desc(uaa->device, NULL, &d_ptr, 399184610Salfred &d_len, M_TEMP, uaa->info.bIfaceIndex); 400184610Salfred 401184610Salfred if (err) { 402184610Salfred device_printf(dev, "error reading report description\n"); 403184610Salfred goto detach; 404184610Salfred } 405184610Salfred if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 406189583Sthompsa hid_input, &sc->sc_loc_x, &flags, &sc->sc_iid_x)) { 407184610Salfred 408184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 409184610Salfred sc->sc_flags |= UMS_FLAG_X_AXIS; 410184610Salfred } 411184610Salfred } 412184610Salfred if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 413189583Sthompsa hid_input, &sc->sc_loc_y, &flags, &sc->sc_iid_y)) { 414184610Salfred 415184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 416184610Salfred sc->sc_flags |= UMS_FLAG_Y_AXIS; 417184610Salfred } 418184610Salfred } 419184610Salfred /* Try the wheel first as the Z activator since it's tradition. */ 420184610Salfred if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, 421189583Sthompsa HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z) || 422184610Salfred hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, 423189583Sthompsa HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z)) { 424184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 425184610Salfred sc->sc_flags |= UMS_FLAG_Z_AXIS; 426184610Salfred } 427184610Salfred /* 428184610Salfred * We might have both a wheel and Z direction, if so put 429184610Salfred * put the Z on the W coordinate. 430184610Salfred */ 431184610Salfred if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, 432189583Sthompsa HUG_Z), hid_input, &sc->sc_loc_w, &flags, &sc->sc_iid_w)) { 433184610Salfred 434184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 435184610Salfred sc->sc_flags |= UMS_FLAG_W_AXIS; 436184610Salfred } 437184610Salfred } 438184610Salfred } else if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, 439189583Sthompsa HUG_Z), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z)) { 440184610Salfred 441184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 442184610Salfred sc->sc_flags |= UMS_FLAG_Z_AXIS; 443184610Salfred } 444184610Salfred } 445184610Salfred /* 446184610Salfred * The Microsoft Wireless Intellimouse 2.0 reports it's wheel 447184610Salfred * using 0x0048, which is HUG_TWHEEL, and seems to expect you 448184610Salfred * to know that the byte after the wheel is the tilt axis. 449184610Salfred * There are no other HID axis descriptors other than X,Y and 450184610Salfred * TWHEEL 451184610Salfred */ 452184610Salfred if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), 453189583Sthompsa hid_input, &sc->sc_loc_t, &flags, &sc->sc_iid_t)) { 454184610Salfred 455184610Salfred sc->sc_loc_t.pos += 8; 456184610Salfred 457184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 458184610Salfred sc->sc_flags |= UMS_FLAG_T_AXIS; 459184610Salfred } 460184610Salfred } 461184610Salfred /* figure out the number of buttons */ 462184610Salfred 463184610Salfred for (i = 0; i < UMS_BUTTON_MAX; i++) { 464184610Salfred if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i + 1)), 465189583Sthompsa hid_input, &sc->sc_loc_btn[i], NULL, &sc->sc_iid_btn[i])) { 466184610Salfred break; 467184610Salfred } 468184610Salfred } 469184610Salfred 470184610Salfred sc->sc_buttons = i; 471184610Salfred 472189583Sthompsa isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid); 473184610Salfred 474184610Salfred /* 475184610Salfred * The Microsoft Wireless Notebook Optical Mouse seems to be in worse 476184610Salfred * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and 477184610Salfred * all of its other button positions are all off. It also reports that 478184610Salfred * it has two addional buttons and a tilt wheel. 479184610Salfred */ 480184610Salfred if (usb2_test_quirk(uaa, UQ_MS_BAD_CLASS)) { 481184610Salfred sc->sc_flags = (UMS_FLAG_X_AXIS | 482184610Salfred UMS_FLAG_Y_AXIS | 483184610Salfred UMS_FLAG_Z_AXIS | 484184610Salfred UMS_FLAG_SBU); 485184610Salfred sc->sc_buttons = 3; 486184610Salfred isize = 5; 487184610Salfred sc->sc_iid = 0; 488189583Sthompsa sc->sc_iid_x = 0; 489189583Sthompsa sc->sc_iid_y = 0; 490189583Sthompsa sc->sc_iid_z = 0; 491189583Sthompsa sc->sc_iid_btn[0] = 0; 492189583Sthompsa sc->sc_iid_btn[1] = 0; 493189583Sthompsa sc->sc_iid_btn[2] = 0; 494184610Salfred /* 1st byte of descriptor report contains garbage */ 495184610Salfred sc->sc_loc_x.pos = 16; 496184610Salfred sc->sc_loc_y.pos = 24; 497184610Salfred sc->sc_loc_z.pos = 32; 498184610Salfred sc->sc_loc_btn[0].pos = 8; 499184610Salfred sc->sc_loc_btn[1].pos = 9; 500184610Salfred sc->sc_loc_btn[2].pos = 10; 501184610Salfred } 502188981Sthompsa 503184610Salfred /* 504188981Sthompsa * Some Microsoft devices have incorrectly high location 505188981Sthompsa * positions. Correct this: 506184610Salfred */ 507188981Sthompsa isizebits = isize * 8; 508188981Sthompsa if ((sc->sc_iid != 0) && (isizebits > 8)) { 509188981Sthompsa isizebits -= 8; /* remove size of report ID */ 510188981Sthompsa sc->sc_loc_w.pos %= isizebits; 511188981Sthompsa sc->sc_loc_x.pos %= isizebits; 512188981Sthompsa sc->sc_loc_y.pos %= isizebits; 513188981Sthompsa sc->sc_loc_z.pos %= isizebits; 514188981Sthompsa sc->sc_loc_t.pos %= isizebits; 515188981Sthompsa for (i = 0; i != UMS_BUTTON_MAX; i++) 516188981Sthompsa sc->sc_loc_btn[i].pos %= isizebits; 517184610Salfred } 518188981Sthompsa 519184610Salfred if (usb2_test_quirk(uaa, UQ_MS_REVZ)) { 520184610Salfred /* Some wheels need the Z axis reversed. */ 521184610Salfred sc->sc_flags |= UMS_FLAG_REVZ; 522184610Salfred } 523187259Sthompsa if (isize > sc->sc_xfer[UMS_INTR_DT]->max_frame_size) { 524184610Salfred DPRINTF("WARNING: report size, %d bytes, is larger " 525184610Salfred "than interrupt size, %d bytes!\n", 526187259Sthompsa isize, sc->sc_xfer[UMS_INTR_DT]->max_frame_size); 527184610Salfred } 528184610Salfred /* announce information about the mouse */ 529184610Salfred 530184610Salfred device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates\n", 531184610Salfred (sc->sc_buttons), 532184610Salfred (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "", 533184610Salfred (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "", 534184610Salfred (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "", 535184610Salfred (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "", 536184610Salfred (sc->sc_flags & UMS_FLAG_W_AXIS) ? "W" : ""); 537184610Salfred 538184610Salfred free(d_ptr, M_TEMP); 539184610Salfred d_ptr = NULL; 540184610Salfred 541184610Salfred#if USB_DEBUG 542184610Salfred DPRINTF("sc=%p\n", sc); 543189583Sthompsa DPRINTF("X\t%d/%d id=%d\n", sc->sc_loc_x.pos, 544189583Sthompsa sc->sc_loc_x.size, sc->sc_iid_x); 545189583Sthompsa DPRINTF("Y\t%d/%d id=%d\n", sc->sc_loc_y.pos, 546189583Sthompsa sc->sc_loc_y.size, sc->sc_iid_y); 547189583Sthompsa DPRINTF("Z\t%d/%d id=%d\n", sc->sc_loc_z.pos, 548189583Sthompsa sc->sc_loc_z.size, sc->sc_iid_z); 549189583Sthompsa DPRINTF("T\t%d/%d id=%d\n", sc->sc_loc_t.pos, 550189583Sthompsa sc->sc_loc_t.size, sc->sc_iid_t); 551189583Sthompsa DPRINTF("W\t%d/%d id=%d\n", sc->sc_loc_w.pos, 552189583Sthompsa sc->sc_loc_w.size, sc->sc_iid_w); 553184610Salfred 554184610Salfred for (i = 0; i < sc->sc_buttons; i++) { 555189583Sthompsa DPRINTF("B%d\t%d/%d id=%d\n", 556189583Sthompsa i + 1, sc->sc_loc_btn[i].pos, 557189583Sthompsa sc->sc_loc_btn[i].size, sc->sc_iid_btn[i]); 558184610Salfred } 559184610Salfred DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid); 560184610Salfred#endif 561184610Salfred 562184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 563184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 564184610Salfred else 565184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 566184610Salfred 567184610Salfred sc->sc_hw.iftype = MOUSE_IF_USB; 568184610Salfred sc->sc_hw.type = MOUSE_MOUSE; 569184610Salfred sc->sc_hw.model = MOUSE_MODEL_GENERIC; 570184610Salfred sc->sc_hw.hwid = 0; 571184610Salfred 572184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 573184610Salfred sc->sc_mode.rate = -1; 574184610Salfred sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; 575184610Salfred sc->sc_mode.accelfactor = 0; 576184610Salfred sc->sc_mode.level = 0; 577184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 578184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 579184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 580184610Salfred 581184610Salfred sc->sc_status.flags = 0; 582184610Salfred sc->sc_status.button = 0; 583184610Salfred sc->sc_status.obutton = 0; 584184610Salfred sc->sc_status.dx = 0; 585184610Salfred sc->sc_status.dy = 0; 586184610Salfred sc->sc_status.dz = 0; 587184610Salfred 588184610Salfred err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, 589184610Salfred &ums_fifo_methods, &sc->sc_fifo, 590189110Sthompsa unit, 0 - 1, uaa->info.bIfaceIndex, 591189110Sthompsa UID_ROOT, GID_OPERATOR, 0644); 592184610Salfred if (err) { 593184610Salfred goto detach; 594184610Salfred } 595184610Salfred return (0); 596184610Salfred 597184610Salfreddetach: 598184610Salfred if (d_ptr) { 599184610Salfred free(d_ptr, M_TEMP); 600184610Salfred } 601184610Salfred ums_detach(dev); 602184610Salfred return (ENOMEM); 603184610Salfred} 604184610Salfred 605184610Salfredstatic int 606184610Salfredums_detach(device_t self) 607184610Salfred{ 608184610Salfred struct ums_softc *sc = device_get_softc(self); 609184610Salfred 610184610Salfred DPRINTF("sc=%p\n", sc); 611184610Salfred 612184610Salfred usb2_fifo_detach(&sc->sc_fifo); 613184610Salfred 614184610Salfred usb2_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER); 615184610Salfred 616184610Salfred usb2_callout_drain(&sc->sc_callout); 617184610Salfred 618184610Salfred mtx_destroy(&sc->sc_mtx); 619184610Salfred 620184610Salfred return (0); 621184610Salfred} 622184610Salfred 623184610Salfredstatic void 624184610Salfredums_start_read(struct usb2_fifo *fifo) 625184610Salfred{ 626184610Salfred struct ums_softc *sc = fifo->priv_sc0; 627184610Salfred 628187259Sthompsa usb2_transfer_start(sc->sc_xfer[UMS_INTR_DT]); 629184610Salfred} 630184610Salfred 631184610Salfredstatic void 632184610Salfredums_stop_read(struct usb2_fifo *fifo) 633184610Salfred{ 634184610Salfred struct ums_softc *sc = fifo->priv_sc0; 635184610Salfred 636187259Sthompsa usb2_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); 637184610Salfred usb2_callout_stop(&sc->sc_callout); 638184610Salfred} 639184610Salfred 640184610Salfred 641184610Salfred#if ((MOUSE_SYS_PACKETSIZE != 8) || \ 642184610Salfred (MOUSE_MSC_PACKETSIZE != 5)) 643184610Salfred#error "Software assumptions are not met. Please update code." 644184610Salfred#endif 645184610Salfred 646184610Salfredstatic void 647184610Salfredums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, 648184610Salfred int32_t dz, int32_t dt, int32_t buttons) 649184610Salfred{ 650184610Salfred uint8_t buf[8]; 651184610Salfred 652184610Salfred if (1) { 653184610Salfred 654184610Salfred if (dx > 254) 655184610Salfred dx = 254; 656184610Salfred if (dx < -256) 657184610Salfred dx = -256; 658184610Salfred if (dy > 254) 659184610Salfred dy = 254; 660184610Salfred if (dy < -256) 661184610Salfred dy = -256; 662184610Salfred if (dz > 126) 663184610Salfred dz = 126; 664184610Salfred if (dz < -128) 665184610Salfred dz = -128; 666184610Salfred if (dt > 126) 667184610Salfred dt = 126; 668184610Salfred if (dt < -128) 669184610Salfred dt = -128; 670184610Salfred 671184610Salfred buf[0] = sc->sc_mode.syncmask[1]; 672184610Salfred buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; 673184610Salfred buf[1] = dx >> 1; 674184610Salfred buf[2] = dy >> 1; 675184610Salfred buf[3] = dx - (dx >> 1); 676184610Salfred buf[4] = dy - (dy >> 1); 677184610Salfred 678184610Salfred if (sc->sc_mode.level == 1) { 679184610Salfred buf[5] = dz >> 1; 680184610Salfred buf[6] = dz - (dz >> 1); 681184610Salfred buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); 682184610Salfred } 683184610Salfred usb2_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, 684184610Salfred sc->sc_mode.packetsize, 1); 685184610Salfred 686184610Salfred } else { 687184610Salfred DPRINTF("Buffer full, discarded packet\n"); 688184610Salfred } 689184610Salfred} 690184610Salfred 691184610Salfredstatic void 692184610Salfredums_reset_buf(struct ums_softc *sc) 693184610Salfred{ 694184610Salfred /* reset read queue */ 695184610Salfred usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); 696184610Salfred} 697184610Salfred 698184610Salfredstatic int 699189110Sthompsaums_open(struct usb2_fifo *fifo, int fflags) 700184610Salfred{ 701184610Salfred struct ums_softc *sc = fifo->priv_sc0; 702184610Salfred 703184610Salfred DPRINTFN(2, "\n"); 704184610Salfred 705184610Salfred if (fflags & FREAD) { 706184610Salfred 707184610Salfred /* reset status */ 708184610Salfred 709184610Salfred sc->sc_status.flags = 0; 710184610Salfred sc->sc_status.button = 0; 711184610Salfred sc->sc_status.obutton = 0; 712184610Salfred sc->sc_status.dx = 0; 713184610Salfred sc->sc_status.dy = 0; 714184610Salfred sc->sc_status.dz = 0; 715184610Salfred /* sc->sc_status.dt = 0; */ 716184610Salfred 717184610Salfred if (usb2_fifo_alloc_buffer(fifo, 718184610Salfred UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) { 719184610Salfred return (ENOMEM); 720184610Salfred } 721184610Salfred } 722184610Salfred return (0); 723184610Salfred} 724184610Salfred 725184610Salfredstatic void 726189110Sthompsaums_close(struct usb2_fifo *fifo, int fflags) 727184610Salfred{ 728184610Salfred if (fflags & FREAD) { 729184610Salfred usb2_fifo_free_buffer(fifo); 730184610Salfred } 731184610Salfred} 732184610Salfred 733184610Salfredstatic int 734189110Sthompsaums_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags) 735184610Salfred{ 736184610Salfred struct ums_softc *sc = fifo->priv_sc0; 737184610Salfred mousemode_t mode; 738184610Salfred int error = 0; 739184610Salfred 740184610Salfred DPRINTFN(2, "\n"); 741184610Salfred 742184610Salfred mtx_lock(&sc->sc_mtx); 743184610Salfred 744184610Salfred switch (cmd) { 745184610Salfred case MOUSE_GETHWINFO: 746184610Salfred *(mousehw_t *)addr = sc->sc_hw; 747184610Salfred break; 748184610Salfred 749184610Salfred case MOUSE_GETMODE: 750184610Salfred *(mousemode_t *)addr = sc->sc_mode; 751184610Salfred break; 752184610Salfred 753184610Salfred case MOUSE_SETMODE: 754184610Salfred mode = *(mousemode_t *)addr; 755184610Salfred 756184610Salfred if (mode.level == -1) { 757184610Salfred /* don't change the current setting */ 758184610Salfred } else if ((mode.level < 0) || (mode.level > 1)) { 759184610Salfred error = EINVAL; 760184610Salfred goto done; 761184610Salfred } else { 762184610Salfred sc->sc_mode.level = mode.level; 763184610Salfred } 764184610Salfred 765184610Salfred if (sc->sc_mode.level == 0) { 766184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 767184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 768184610Salfred else 769184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 770184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 771184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 772184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 773184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 774184610Salfred } else if (sc->sc_mode.level == 1) { 775184610Salfred if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) 776184610Salfred sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; 777184610Salfred else 778184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 779184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 780184610Salfred sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 781184610Salfred sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 782184610Salfred sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 783184610Salfred } 784184610Salfred ums_reset_buf(sc); 785184610Salfred break; 786184610Salfred 787184610Salfred case MOUSE_GETLEVEL: 788184610Salfred *(int *)addr = sc->sc_mode.level; 789184610Salfred break; 790184610Salfred 791184610Salfred case MOUSE_SETLEVEL: 792184610Salfred if (*(int *)addr < 0 || *(int *)addr > 1) { 793184610Salfred error = EINVAL; 794184610Salfred goto done; 795184610Salfred } 796184610Salfred sc->sc_mode.level = *(int *)addr; 797184610Salfred 798184610Salfred if (sc->sc_mode.level == 0) { 799184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 800184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 801184610Salfred else 802184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 803184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 804184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 805184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 806184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 807184610Salfred } else if (sc->sc_mode.level == 1) { 808184610Salfred if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) 809184610Salfred sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; 810184610Salfred else 811184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 812184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 813184610Salfred sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 814184610Salfred sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 815184610Salfred sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 816184610Salfred } 817184610Salfred ums_reset_buf(sc); 818184610Salfred break; 819184610Salfred 820184610Salfred case MOUSE_GETSTATUS:{ 821184610Salfred mousestatus_t *status = (mousestatus_t *)addr; 822184610Salfred 823184610Salfred *status = sc->sc_status; 824184610Salfred sc->sc_status.obutton = sc->sc_status.button; 825184610Salfred sc->sc_status.button = 0; 826184610Salfred sc->sc_status.dx = 0; 827184610Salfred sc->sc_status.dy = 0; 828184610Salfred sc->sc_status.dz = 0; 829184610Salfred /* sc->sc_status.dt = 0; */ 830184610Salfred 831184610Salfred if (status->dx || status->dy || status->dz /* || status->dt */ ) { 832184610Salfred status->flags |= MOUSE_POSCHANGED; 833184610Salfred } 834184610Salfred if (status->button != status->obutton) { 835184610Salfred status->flags |= MOUSE_BUTTONSCHANGED; 836184610Salfred } 837184610Salfred break; 838184610Salfred } 839184610Salfred default: 840184610Salfred error = ENOTTY; 841184610Salfred } 842184610Salfred 843184610Salfreddone: 844184610Salfred mtx_unlock(&sc->sc_mtx); 845184610Salfred return (error); 846184610Salfred} 847184610Salfred 848184610Salfredstatic devclass_t ums_devclass; 849184610Salfred 850184610Salfredstatic device_method_t ums_methods[] = { 851184610Salfred DEVMETHOD(device_probe, ums_probe), 852184610Salfred DEVMETHOD(device_attach, ums_attach), 853184610Salfred DEVMETHOD(device_detach, ums_detach), 854184610Salfred {0, 0} 855184610Salfred}; 856184610Salfred 857184610Salfredstatic driver_t ums_driver = { 858184610Salfred .name = "ums", 859184610Salfred .methods = ums_methods, 860184610Salfred .size = sizeof(struct ums_softc), 861184610Salfred}; 862184610Salfred 863189275SthompsaDRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0); 864188942SthompsaMODULE_DEPEND(ums, usb, 1, 1, 1); 865