ums.c revision 235000
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 * 18184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21184610Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28184610Salfred * POSSIBILITY OF SUCH DAMAGE. 29184610Salfred */ 30184610Salfred 31184610Salfred#include <sys/cdefs.h> 32184610Salfred__FBSDID("$FreeBSD: stable/9/sys/dev/usb/input/ums.c 235000 2012-05-04 15:05:30Z hselasky $"); 33184610Salfred 34184610Salfred/* 35184610Salfred * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 36184610Salfred */ 37184610Salfred 38194677Sthompsa#include <sys/stdint.h> 39194677Sthompsa#include <sys/stddef.h> 40194677Sthompsa#include <sys/param.h> 41194677Sthompsa#include <sys/queue.h> 42194677Sthompsa#include <sys/types.h> 43194677Sthompsa#include <sys/systm.h> 44194677Sthompsa#include <sys/kernel.h> 45194677Sthompsa#include <sys/bus.h> 46194677Sthompsa#include <sys/module.h> 47194677Sthompsa#include <sys/lock.h> 48194677Sthompsa#include <sys/mutex.h> 49194677Sthompsa#include <sys/condvar.h> 50194677Sthompsa#include <sys/sysctl.h> 51194677Sthompsa#include <sys/sx.h> 52194677Sthompsa#include <sys/unistd.h> 53194677Sthompsa#include <sys/callout.h> 54194677Sthompsa#include <sys/malloc.h> 55194677Sthompsa#include <sys/priv.h> 56194677Sthompsa#include <sys/conf.h> 57194677Sthompsa#include <sys/fcntl.h> 58198373Sthompsa#include <sys/sbuf.h> 59194677Sthompsa 60188942Sthompsa#include <dev/usb/usb.h> 61194677Sthompsa#include <dev/usb/usbdi.h> 62194677Sthompsa#include <dev/usb/usbdi_util.h> 63188942Sthompsa#include <dev/usb/usbhid.h> 64194677Sthompsa#include "usbdevs.h" 65184610Salfred 66184610Salfred#define USB_DEBUG_VAR ums_debug 67188942Sthompsa#include <dev/usb/usb_debug.h> 68184610Salfred 69188942Sthompsa#include <dev/usb/quirk/usb_quirk.h> 70184610Salfred 71184610Salfred#include <sys/ioccom.h> 72184610Salfred#include <sys/filio.h> 73184610Salfred#include <sys/tty.h> 74184610Salfred#include <sys/mouse.h> 75184610Salfred 76207077Sthompsa#ifdef USB_DEBUG 77184610Salfredstatic int ums_debug = 0; 78184610Salfred 79192502SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); 80192502SthompsaSYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW, 81184610Salfred &ums_debug, 0, "Debug level"); 82184610Salfred#endif 83184610Salfred 84184610Salfred#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) 85184610Salfred#define MOUSE_FLAGS (HIO_RELATIVE) 86184610Salfred 87184610Salfred#define UMS_BUF_SIZE 8 /* bytes */ 88184610Salfred#define UMS_IFQ_MAXLEN 50 /* units */ 89184610Salfred#define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ 90184610Salfred#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) 91190741Sthompsa#define UMS_INFO_MAX 2 /* maximum number of HID sets */ 92184610Salfred 93187259Sthompsaenum { 94187259Sthompsa UMS_INTR_DT, 95188981Sthompsa UMS_N_TRANSFER, 96187259Sthompsa}; 97187259Sthompsa 98190741Sthompsastruct ums_info { 99184610Salfred struct hid_location sc_loc_w; 100184610Salfred struct hid_location sc_loc_x; 101184610Salfred struct hid_location sc_loc_y; 102184610Salfred struct hid_location sc_loc_z; 103184610Salfred struct hid_location sc_loc_t; 104184610Salfred struct hid_location sc_loc_btn[UMS_BUTTON_MAX]; 105184610Salfred 106184610Salfred uint32_t sc_flags; 107184610Salfred#define UMS_FLAG_X_AXIS 0x0001 108184610Salfred#define UMS_FLAG_Y_AXIS 0x0002 109184610Salfred#define UMS_FLAG_Z_AXIS 0x0004 110184610Salfred#define UMS_FLAG_T_AXIS 0x0008 111184610Salfred#define UMS_FLAG_SBU 0x0010 /* spurious button up events */ 112188981Sthompsa#define UMS_FLAG_REVZ 0x0020 /* Z-axis is reversed */ 113188981Sthompsa#define UMS_FLAG_W_AXIS 0x0040 114184610Salfred 115189583Sthompsa uint8_t sc_iid_w; 116189583Sthompsa uint8_t sc_iid_x; 117189583Sthompsa uint8_t sc_iid_y; 118189583Sthompsa uint8_t sc_iid_z; 119189583Sthompsa uint8_t sc_iid_t; 120189583Sthompsa uint8_t sc_iid_btn[UMS_BUTTON_MAX]; 121190741Sthompsa uint8_t sc_buttons; 122190741Sthompsa}; 123190741Sthompsa 124190741Sthompsastruct ums_softc { 125192984Sthompsa struct usb_fifo_sc sc_fifo; 126190741Sthompsa struct mtx sc_mtx; 127192984Sthompsa struct usb_callout sc_callout; 128190741Sthompsa struct ums_info sc_info[UMS_INFO_MAX]; 129190741Sthompsa 130190741Sthompsa mousehw_t sc_hw; 131190741Sthompsa mousemode_t sc_mode; 132190741Sthompsa mousestatus_t sc_status; 133190741Sthompsa 134192984Sthompsa struct usb_xfer *sc_xfer[UMS_N_TRANSFER]; 135190741Sthompsa 136195959Salfred int sc_pollrate; 137195959Salfred 138190741Sthompsa uint8_t sc_buttons; 139190741Sthompsa uint8_t sc_iid; 140184610Salfred uint8_t sc_temp[64]; 141184610Salfred}; 142184610Salfred 143184610Salfredstatic void ums_put_queue_timeout(void *__sc); 144184610Salfred 145193045Sthompsastatic usb_callback_t ums_intr_callback; 146184610Salfred 147184610Salfredstatic device_probe_t ums_probe; 148184610Salfredstatic device_attach_t ums_attach; 149184610Salfredstatic device_detach_t ums_detach; 150184610Salfred 151193045Sthompsastatic usb_fifo_cmd_t ums_start_read; 152193045Sthompsastatic usb_fifo_cmd_t ums_stop_read; 153193045Sthompsastatic usb_fifo_open_t ums_open; 154193045Sthompsastatic usb_fifo_close_t ums_close; 155193045Sthompsastatic usb_fifo_ioctl_t ums_ioctl; 156184610Salfred 157198373Sthompsastatic void ums_put_queue(struct ums_softc *, int32_t, int32_t, 158198373Sthompsa int32_t, int32_t, int32_t); 159198373Sthompsastatic int ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS); 160184610Salfred 161192984Sthompsastatic struct usb_fifo_methods ums_fifo_methods = { 162184610Salfred .f_open = &ums_open, 163184610Salfred .f_close = &ums_close, 164184610Salfred .f_ioctl = &ums_ioctl, 165184610Salfred .f_start_read = &ums_start_read, 166184610Salfred .f_stop_read = &ums_stop_read, 167184610Salfred .basename[0] = "ums", 168184610Salfred}; 169184610Salfred 170184610Salfredstatic void 171184610Salfredums_put_queue_timeout(void *__sc) 172184610Salfred{ 173184610Salfred struct ums_softc *sc = __sc; 174184610Salfred 175184610Salfred mtx_assert(&sc->sc_mtx, MA_OWNED); 176184610Salfred 177184610Salfred ums_put_queue(sc, 0, 0, 0, 0, 0); 178184610Salfred} 179184610Salfred 180184610Salfredstatic void 181194677Sthompsaums_intr_callback(struct usb_xfer *xfer, usb_error_t error) 182184610Salfred{ 183194677Sthompsa struct ums_softc *sc = usbd_xfer_softc(xfer); 184190741Sthompsa struct ums_info *info = &sc->sc_info[0]; 185194677Sthompsa struct usb_page_cache *pc; 186184610Salfred uint8_t *buf = sc->sc_temp; 187184610Salfred int32_t buttons = 0; 188195959Salfred int32_t buttons_found = 0; 189190741Sthompsa int32_t dw = 0; 190190741Sthompsa int32_t dx = 0; 191190741Sthompsa int32_t dy = 0; 192190741Sthompsa int32_t dz = 0; 193190741Sthompsa int32_t dt = 0; 194184610Salfred uint8_t i; 195189583Sthompsa uint8_t id; 196194677Sthompsa int len; 197184610Salfred 198194677Sthompsa usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 199194677Sthompsa 200184610Salfred switch (USB_GET_STATE(xfer)) { 201184610Salfred case USB_ST_TRANSFERRED: 202184610Salfred DPRINTFN(6, "sc=%p actlen=%d\n", sc, len); 203184610Salfred 204235000Shselasky if (len > (int)sizeof(sc->sc_temp)) { 205184610Salfred DPRINTFN(6, "truncating large packet to %zu bytes\n", 206184610Salfred sizeof(sc->sc_temp)); 207184610Salfred len = sizeof(sc->sc_temp); 208184610Salfred } 209188981Sthompsa if (len == 0) 210184610Salfred goto tr_setup; 211188981Sthompsa 212194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 213194677Sthompsa usbd_copy_out(pc, 0, buf, len); 214184610Salfred 215184610Salfred DPRINTFN(6, "data = %02x %02x %02x %02x " 216184610Salfred "%02x %02x %02x %02x\n", 217184610Salfred (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0, 218184610Salfred (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0, 219184610Salfred (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0, 220184610Salfred (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0); 221184610Salfred 222184610Salfred if (sc->sc_iid) { 223189583Sthompsa id = *buf; 224184610Salfred 225184610Salfred len--; 226184610Salfred buf++; 227184610Salfred 228184610Salfred } else { 229189583Sthompsa id = 0; 230190741Sthompsa if (sc->sc_info[0].sc_flags & UMS_FLAG_SBU) { 231184610Salfred if ((*buf == 0x14) || (*buf == 0x15)) { 232184610Salfred goto tr_setup; 233184610Salfred } 234184610Salfred } 235184610Salfred } 236184610Salfred 237190741Sthompsa repeat: 238190741Sthompsa if ((info->sc_flags & UMS_FLAG_W_AXIS) && 239190741Sthompsa (id == info->sc_iid_w)) 240190741Sthompsa dw += hid_get_data(buf, len, &info->sc_loc_w); 241184610Salfred 242190741Sthompsa if ((info->sc_flags & UMS_FLAG_X_AXIS) && 243190741Sthompsa (id == info->sc_iid_x)) 244190741Sthompsa dx += hid_get_data(buf, len, &info->sc_loc_x); 245184610Salfred 246190741Sthompsa if ((info->sc_flags & UMS_FLAG_Y_AXIS) && 247190741Sthompsa (id == info->sc_iid_y)) 248190741Sthompsa dy = -hid_get_data(buf, len, &info->sc_loc_y); 249184610Salfred 250190741Sthompsa if ((info->sc_flags & UMS_FLAG_Z_AXIS) && 251190741Sthompsa (id == info->sc_iid_z)) { 252190741Sthompsa int32_t temp; 253190741Sthompsa temp = hid_get_data(buf, len, &info->sc_loc_z); 254190741Sthompsa if (info->sc_flags & UMS_FLAG_REVZ) 255190741Sthompsa temp = -temp; 256190741Sthompsa dz -= temp; 257190741Sthompsa } 258184610Salfred 259190741Sthompsa if ((info->sc_flags & UMS_FLAG_T_AXIS) && 260190741Sthompsa (id == info->sc_iid_t)) 261190741Sthompsa dt -= hid_get_data(buf, len, &info->sc_loc_t); 262184610Salfred 263190741Sthompsa for (i = 0; i < info->sc_buttons; i++) { 264195959Salfred uint32_t mask; 265195959Salfred mask = 1UL << UMS_BUT(i); 266195959Salfred /* check for correct button ID */ 267190741Sthompsa if (id != info->sc_iid_btn[i]) 268189583Sthompsa continue; 269195959Salfred /* check for button pressed */ 270195959Salfred if (hid_get_data(buf, len, &info->sc_loc_btn[i])) 271195959Salfred buttons |= mask; 272195959Salfred /* register button mask */ 273195959Salfred buttons_found |= mask; 274184610Salfred } 275184610Salfred 276190741Sthompsa if (++info != &sc->sc_info[UMS_INFO_MAX]) 277190741Sthompsa goto repeat; 278190741Sthompsa 279195959Salfred /* keep old button value(s) for non-detected buttons */ 280195959Salfred buttons |= sc->sc_status.button & ~buttons_found; 281195959Salfred 282184610Salfred if (dx || dy || dz || dt || dw || 283184610Salfred (buttons != sc->sc_status.button)) { 284184610Salfred 285184610Salfred DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n", 286184610Salfred dx, dy, dz, dt, dw, buttons); 287184610Salfred 288208009Sthompsa /* translate T-axis into button presses until further */ 289208009Sthompsa if (dt > 0) 290208009Sthompsa buttons |= 1UL << 3; 291208009Sthompsa else if (dt < 0) 292208009Sthompsa buttons |= 1UL << 4; 293208009Sthompsa 294184610Salfred sc->sc_status.button = buttons; 295184610Salfred sc->sc_status.dx += dx; 296184610Salfred sc->sc_status.dy += dy; 297184610Salfred sc->sc_status.dz += dz; 298184610Salfred /* 299184610Salfred * sc->sc_status.dt += dt; 300184610Salfred * no way to export this yet 301184610Salfred */ 302184610Salfred 303184610Salfred /* 304188981Sthompsa * The Qtronix keyboard has a built in PS/2 305188981Sthompsa * port for a mouse. The firmware once in a 306188981Sthompsa * while posts a spurious button up 307188981Sthompsa * event. This event we ignore by doing a 308188981Sthompsa * timeout for 50 msecs. If we receive 309188981Sthompsa * dx=dy=dz=buttons=0 before we add the event 310188981Sthompsa * to the queue. In any other case we delete 311188981Sthompsa * the timeout event. 312184610Salfred */ 313190741Sthompsa if ((sc->sc_info[0].sc_flags & UMS_FLAG_SBU) && 314184610Salfred (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) && 315184610Salfred (dw == 0) && (buttons == 0)) { 316184610Salfred 317194228Sthompsa usb_callout_reset(&sc->sc_callout, hz / 20, 318184610Salfred &ums_put_queue_timeout, sc); 319184610Salfred } else { 320184610Salfred 321194228Sthompsa usb_callout_stop(&sc->sc_callout); 322184610Salfred 323184610Salfred ums_put_queue(sc, dx, dy, dz, dt, buttons); 324184610Salfred } 325184610Salfred } 326184610Salfred case USB_ST_SETUP: 327184610Salfredtr_setup: 328188981Sthompsa /* check if we can put more data into the FIFO */ 329194228Sthompsa if (usb_fifo_put_bytes_max( 330188981Sthompsa sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { 331194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 332194228Sthompsa usbd_transfer_submit(xfer); 333184610Salfred } 334188981Sthompsa break; 335184610Salfred 336184610Salfred default: /* Error */ 337194677Sthompsa if (error != USB_ERR_CANCELLED) { 338188981Sthompsa /* try clear stall first */ 339194677Sthompsa usbd_xfer_set_stall(xfer); 340188981Sthompsa goto tr_setup; 341184610Salfred } 342188981Sthompsa break; 343184610Salfred } 344184610Salfred} 345184610Salfred 346192984Sthompsastatic const struct usb_config ums_config[UMS_N_TRANSFER] = { 347184610Salfred 348187259Sthompsa [UMS_INTR_DT] = { 349184610Salfred .type = UE_INTERRUPT, 350184610Salfred .endpoint = UE_ADDR_ANY, 351184610Salfred .direction = UE_DIR_IN, 352190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 353190734Sthompsa .bufsize = 0, /* use wMaxPacketSize */ 354190734Sthompsa .callback = &ums_intr_callback, 355184610Salfred }, 356184610Salfred}; 357184610Salfred 358223521Shselasky/* A match on these entries will load ums */ 359223521Shselaskystatic const STRUCT_USB_HOST_ID __used ums_devs[] = { 360223521Shselasky {USB_IFACE_CLASS(UICLASS_HID), 361223521Shselasky USB_IFACE_SUBCLASS(UISUBCLASS_BOOT), 362223521Shselasky USB_IFACE_PROTOCOL(UIPROTO_MOUSE),}, 363223521Shselasky}; 364223521Shselasky 365184610Salfredstatic int 366184610Salfredums_probe(device_t dev) 367184610Salfred{ 368192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 369184610Salfred void *d_ptr; 370224499Smav struct hid_data *hd; 371224499Smav struct hid_item hi; 372224499Smav int error, mdepth, found; 373184610Salfred uint16_t d_len; 374184610Salfred 375184610Salfred DPRINTFN(11, "\n"); 376184610Salfred 377192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) 378184610Salfred return (ENXIO); 379188981Sthompsa 380194068Sthompsa if (uaa->info.bInterfaceClass != UICLASS_HID) 381184610Salfred return (ENXIO); 382188981Sthompsa 383194068Sthompsa if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) && 384194068Sthompsa (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE)) 385222051Savg return (BUS_PROBE_DEFAULT); 386194068Sthompsa 387194228Sthompsa error = usbd_req_get_hid_desc(uaa->device, NULL, 388184610Salfred &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); 389184610Salfred 390188981Sthompsa if (error) 391184610Salfred return (ENXIO); 392188981Sthompsa 393224499Smav hd = hid_start_parse(d_ptr, d_len, 1 << hid_input); 394224499Smav if (hd == NULL) 395224499Smav return (0); 396224499Smav mdepth = 0; 397224499Smav found = 0; 398224499Smav while (hid_get_item(hd, &hi)) { 399224499Smav switch (hi.kind) { 400224499Smav case hid_collection: 401224499Smav if (mdepth != 0) 402224499Smav mdepth++; 403224499Smav else if (hi.collection == 1 && 404224499Smav hi.usage == 405224499Smav HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) 406224499Smav mdepth++; 407224499Smav break; 408224499Smav case hid_endcollection: 409224499Smav if (mdepth != 0) 410224499Smav mdepth--; 411224499Smav break; 412224499Smav case hid_input: 413224499Smav if (mdepth == 0) 414224499Smav break; 415224499Smav if (hi.usage == 416224499Smav HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X) && 417224499Smav (hi.flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) 418224499Smav found++; 419224499Smav if (hi.usage == 420224499Smav HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y) && 421224499Smav (hi.flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) 422224499Smav found++; 423224499Smav break; 424224499Smav default: 425224499Smav break; 426224499Smav } 427224499Smav } 428224499Smav hid_end_parse(hd); 429184610Salfred free(d_ptr, M_TEMP); 430224499Smav return (found ? BUS_PROBE_DEFAULT : ENXIO); 431184610Salfred} 432184610Salfred 433190741Sthompsastatic void 434190741Sthompsaums_hid_parse(struct ums_softc *sc, device_t dev, const uint8_t *buf, 435190741Sthompsa uint16_t len, uint8_t index) 436184610Salfred{ 437190741Sthompsa struct ums_info *info = &sc->sc_info[index]; 438184610Salfred uint32_t flags; 439184610Salfred uint8_t i; 440212129Sthompsa uint8_t j; 441184610Salfred 442190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 443190741Sthompsa hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) { 444184610Salfred 445184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 446190741Sthompsa info->sc_flags |= UMS_FLAG_X_AXIS; 447184610Salfred } 448184610Salfred } 449190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 450190741Sthompsa hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) { 451184610Salfred 452184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 453190741Sthompsa info->sc_flags |= UMS_FLAG_Y_AXIS; 454184610Salfred } 455184610Salfred } 456184610Salfred /* Try the wheel first as the Z activator since it's tradition. */ 457190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 458190741Sthompsa HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags, 459190741Sthompsa &info->sc_iid_z) || 460190741Sthompsa hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 461190741Sthompsa HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags, 462190741Sthompsa &info->sc_iid_z)) { 463184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 464190741Sthompsa info->sc_flags |= UMS_FLAG_Z_AXIS; 465184610Salfred } 466184610Salfred /* 467184610Salfred * We might have both a wheel and Z direction, if so put 468184610Salfred * put the Z on the W coordinate. 469184610Salfred */ 470190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 471190741Sthompsa HUG_Z), hid_input, index, &info->sc_loc_w, &flags, 472190741Sthompsa &info->sc_iid_w)) { 473184610Salfred 474184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 475190741Sthompsa info->sc_flags |= UMS_FLAG_W_AXIS; 476184610Salfred } 477184610Salfred } 478190741Sthompsa } else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 479190741Sthompsa HUG_Z), hid_input, index, &info->sc_loc_z, &flags, 480190741Sthompsa &info->sc_iid_z)) { 481184610Salfred 482184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 483190741Sthompsa info->sc_flags |= UMS_FLAG_Z_AXIS; 484184610Salfred } 485184610Salfred } 486184610Salfred /* 487184610Salfred * The Microsoft Wireless Intellimouse 2.0 reports it's wheel 488184610Salfred * using 0x0048, which is HUG_TWHEEL, and seems to expect you 489184610Salfred * to know that the byte after the wheel is the tilt axis. 490184610Salfred * There are no other HID axis descriptors other than X,Y and 491184610Salfred * TWHEEL 492184610Salfred */ 493190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 494190741Sthompsa HUG_TWHEEL), hid_input, index, &info->sc_loc_t, 495190741Sthompsa &flags, &info->sc_iid_t)) { 496184610Salfred 497190741Sthompsa info->sc_loc_t.pos += 8; 498184610Salfred 499184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 500190741Sthompsa info->sc_flags |= UMS_FLAG_T_AXIS; 501184610Salfred } 502208009Sthompsa } else if (hid_locate(buf, len, HID_USAGE2(HUP_CONSUMER, 503208009Sthompsa HUC_AC_PAN), hid_input, index, &info->sc_loc_t, 504208009Sthompsa &flags, &info->sc_iid_t)) { 505208009Sthompsa 506208009Sthompsa if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) 507208009Sthompsa info->sc_flags |= UMS_FLAG_T_AXIS; 508184610Salfred } 509184610Salfred /* figure out the number of buttons */ 510184610Salfred 511184610Salfred for (i = 0; i < UMS_BUTTON_MAX; i++) { 512190741Sthompsa if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)), 513190741Sthompsa hid_input, index, &info->sc_loc_btn[i], NULL, 514190741Sthompsa &info->sc_iid_btn[i])) { 515184610Salfred break; 516184610Salfred } 517184610Salfred } 518212129Sthompsa 519212129Sthompsa /* detect other buttons */ 520212129Sthompsa 521212132Sthompsa for (j = 0; (i < UMS_BUTTON_MAX) && (j < 2); i++, j++) { 522212129Sthompsa if (!hid_locate(buf, len, HID_USAGE2(HUP_MICROSOFT, (j + 1)), 523212129Sthompsa hid_input, index, &info->sc_loc_btn[i], NULL, 524212129Sthompsa &info->sc_iid_btn[i])) { 525212129Sthompsa break; 526212129Sthompsa } 527212129Sthompsa } 528212129Sthompsa 529190741Sthompsa info->sc_buttons = i; 530184610Salfred 531190741Sthompsa if (i > sc->sc_buttons) 532190741Sthompsa sc->sc_buttons = i; 533184610Salfred 534190741Sthompsa if (info->sc_flags == 0) 535190741Sthompsa return; 536190741Sthompsa 537190741Sthompsa /* announce information about the mouse */ 538190741Sthompsa device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n", 539190741Sthompsa (info->sc_buttons), 540190741Sthompsa (info->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "", 541190741Sthompsa (info->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "", 542190741Sthompsa (info->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "", 543190741Sthompsa (info->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "", 544190741Sthompsa (info->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "", 545190741Sthompsa info->sc_iid_x); 546190741Sthompsa} 547190741Sthompsa 548190741Sthompsastatic int 549190741Sthompsaums_attach(device_t dev) 550190741Sthompsa{ 551192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 552190741Sthompsa struct ums_softc *sc = device_get_softc(dev); 553190741Sthompsa struct ums_info *info; 554190741Sthompsa void *d_ptr = NULL; 555190741Sthompsa int isize; 556190741Sthompsa int err; 557190741Sthompsa uint16_t d_len; 558190741Sthompsa uint8_t i; 559207077Sthompsa#ifdef USB_DEBUG 560190741Sthompsa uint8_t j; 561207077Sthompsa#endif 562190741Sthompsa 563190741Sthompsa DPRINTFN(11, "sc=%p\n", sc); 564190741Sthompsa 565194228Sthompsa device_set_usb_desc(dev); 566190741Sthompsa 567190741Sthompsa mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE); 568190741Sthompsa 569194228Sthompsa usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 570190741Sthompsa 571190741Sthompsa /* 572190741Sthompsa * Force the report (non-boot) protocol. 573190741Sthompsa * 574190741Sthompsa * Mice without boot protocol support may choose not to implement 575190741Sthompsa * Set_Protocol at all; Ignore any error. 576190741Sthompsa */ 577194228Sthompsa err = usbd_req_set_protocol(uaa->device, NULL, 578190741Sthompsa uaa->info.bIfaceIndex, 1); 579190741Sthompsa 580194228Sthompsa err = usbd_transfer_setup(uaa->device, 581190741Sthompsa &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config, 582190741Sthompsa UMS_N_TRANSFER, sc, &sc->sc_mtx); 583190741Sthompsa 584190741Sthompsa if (err) { 585194228Sthompsa DPRINTF("error=%s\n", usbd_errstr(err)); 586190741Sthompsa goto detach; 587190741Sthompsa } 588195959Salfred 589195959Salfred /* Get HID descriptor */ 590194228Sthompsa err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, 591190741Sthompsa &d_len, M_TEMP, uaa->info.bIfaceIndex); 592190741Sthompsa 593190741Sthompsa if (err) { 594190741Sthompsa device_printf(dev, "error reading report description\n"); 595190741Sthompsa goto detach; 596190741Sthompsa } 597190741Sthompsa 598189583Sthompsa isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid); 599184610Salfred 600184610Salfred /* 601184610Salfred * The Microsoft Wireless Notebook Optical Mouse seems to be in worse 602184610Salfred * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and 603184610Salfred * all of its other button positions are all off. It also reports that 604184610Salfred * it has two addional buttons and a tilt wheel. 605184610Salfred */ 606194228Sthompsa if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) { 607195959Salfred 608195959Salfred sc->sc_iid = 0; 609195959Salfred 610190741Sthompsa info = &sc->sc_info[0]; 611190741Sthompsa info->sc_flags = (UMS_FLAG_X_AXIS | 612184610Salfred UMS_FLAG_Y_AXIS | 613184610Salfred UMS_FLAG_Z_AXIS | 614184610Salfred UMS_FLAG_SBU); 615190741Sthompsa info->sc_buttons = 3; 616184610Salfred isize = 5; 617184610Salfred /* 1st byte of descriptor report contains garbage */ 618190741Sthompsa info->sc_loc_x.pos = 16; 619195959Salfred info->sc_loc_x.size = 8; 620190741Sthompsa info->sc_loc_y.pos = 24; 621195959Salfred info->sc_loc_y.size = 8; 622190741Sthompsa info->sc_loc_z.pos = 32; 623195959Salfred info->sc_loc_z.size = 8; 624190741Sthompsa info->sc_loc_btn[0].pos = 8; 625195959Salfred info->sc_loc_btn[0].size = 1; 626190741Sthompsa info->sc_loc_btn[1].pos = 9; 627195959Salfred info->sc_loc_btn[1].size = 1; 628190741Sthompsa info->sc_loc_btn[2].pos = 10; 629195959Salfred info->sc_loc_btn[2].size = 1; 630188981Sthompsa 631190741Sthompsa /* Announce device */ 632190741Sthompsa device_printf(dev, "3 buttons and [XYZ] " 633190741Sthompsa "coordinates ID=0\n"); 634190741Sthompsa 635190741Sthompsa } else { 636190741Sthompsa /* Search the HID descriptor and announce device */ 637190741Sthompsa for (i = 0; i < UMS_INFO_MAX; i++) { 638190741Sthompsa ums_hid_parse(sc, dev, d_ptr, d_len, i); 639190741Sthompsa } 640184610Salfred } 641188981Sthompsa 642194228Sthompsa if (usb_test_quirk(uaa, UQ_MS_REVZ)) { 643190741Sthompsa info = &sc->sc_info[0]; 644184610Salfred /* Some wheels need the Z axis reversed. */ 645190741Sthompsa info->sc_flags |= UMS_FLAG_REVZ; 646184610Salfred } 647235000Shselasky if (isize > (int)usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])) { 648184610Salfred DPRINTF("WARNING: report size, %d bytes, is larger " 649194677Sthompsa "than interrupt size, %d bytes!\n", isize, 650194677Sthompsa usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])); 651184610Salfred } 652184610Salfred free(d_ptr, M_TEMP); 653184610Salfred d_ptr = NULL; 654184610Salfred 655207077Sthompsa#ifdef USB_DEBUG 656190741Sthompsa for (j = 0; j < UMS_INFO_MAX; j++) { 657190741Sthompsa info = &sc->sc_info[j]; 658184610Salfred 659190741Sthompsa DPRINTF("sc=%p, index=%d\n", sc, j); 660190741Sthompsa DPRINTF("X\t%d/%d id=%d\n", info->sc_loc_x.pos, 661190741Sthompsa info->sc_loc_x.size, info->sc_iid_x); 662190741Sthompsa DPRINTF("Y\t%d/%d id=%d\n", info->sc_loc_y.pos, 663190741Sthompsa info->sc_loc_y.size, info->sc_iid_y); 664190741Sthompsa DPRINTF("Z\t%d/%d id=%d\n", info->sc_loc_z.pos, 665190741Sthompsa info->sc_loc_z.size, info->sc_iid_z); 666190741Sthompsa DPRINTF("T\t%d/%d id=%d\n", info->sc_loc_t.pos, 667190741Sthompsa info->sc_loc_t.size, info->sc_iid_t); 668190741Sthompsa DPRINTF("W\t%d/%d id=%d\n", info->sc_loc_w.pos, 669190741Sthompsa info->sc_loc_w.size, info->sc_iid_w); 670190741Sthompsa 671190741Sthompsa for (i = 0; i < info->sc_buttons; i++) { 672190741Sthompsa DPRINTF("B%d\t%d/%d id=%d\n", 673190741Sthompsa i + 1, info->sc_loc_btn[i].pos, 674190741Sthompsa info->sc_loc_btn[i].size, info->sc_iid_btn[i]); 675190741Sthompsa } 676184610Salfred } 677184610Salfred DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid); 678184610Salfred#endif 679184610Salfred 680184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 681184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 682184610Salfred else 683184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 684184610Salfred 685184610Salfred sc->sc_hw.iftype = MOUSE_IF_USB; 686184610Salfred sc->sc_hw.type = MOUSE_MOUSE; 687184610Salfred sc->sc_hw.model = MOUSE_MODEL_GENERIC; 688184610Salfred sc->sc_hw.hwid = 0; 689184610Salfred 690184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 691184610Salfred sc->sc_mode.rate = -1; 692184610Salfred sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; 693184610Salfred sc->sc_mode.accelfactor = 0; 694184610Salfred sc->sc_mode.level = 0; 695184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 696184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 697184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 698184610Salfred 699194228Sthompsa err = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, 700184610Salfred &ums_fifo_methods, &sc->sc_fifo, 701235000Shselasky device_get_unit(dev), -1, uaa->info.bIfaceIndex, 702189110Sthompsa UID_ROOT, GID_OPERATOR, 0644); 703184610Salfred if (err) { 704184610Salfred goto detach; 705184610Salfred } 706198373Sthompsa SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 707198373Sthompsa SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 708198373Sthompsa OID_AUTO, "parseinfo", CTLTYPE_STRING|CTLFLAG_RD, 709198373Sthompsa sc, 0, ums_sysctl_handler_parseinfo, 710219848Shselasky "", "Dump of parsed HID report descriptor"); 711198373Sthompsa 712184610Salfred return (0); 713184610Salfred 714184610Salfreddetach: 715184610Salfred if (d_ptr) { 716184610Salfred free(d_ptr, M_TEMP); 717184610Salfred } 718184610Salfred ums_detach(dev); 719184610Salfred return (ENOMEM); 720184610Salfred} 721184610Salfred 722184610Salfredstatic int 723184610Salfredums_detach(device_t self) 724184610Salfred{ 725184610Salfred struct ums_softc *sc = device_get_softc(self); 726184610Salfred 727184610Salfred DPRINTF("sc=%p\n", sc); 728184610Salfred 729194228Sthompsa usb_fifo_detach(&sc->sc_fifo); 730184610Salfred 731194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER); 732184610Salfred 733194228Sthompsa usb_callout_drain(&sc->sc_callout); 734184610Salfred 735184610Salfred mtx_destroy(&sc->sc_mtx); 736184610Salfred 737184610Salfred return (0); 738184610Salfred} 739184610Salfred 740184610Salfredstatic void 741192984Sthompsaums_start_read(struct usb_fifo *fifo) 742184610Salfred{ 743194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 744195959Salfred int rate; 745184610Salfred 746195959Salfred /* Check if we should override the default polling interval */ 747195959Salfred rate = sc->sc_pollrate; 748195959Salfred /* Range check rate */ 749195959Salfred if (rate > 1000) 750195959Salfred rate = 1000; 751195959Salfred /* Check for set rate */ 752195959Salfred if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) { 753195959Salfred DPRINTF("Setting pollrate = %d\n", rate); 754195959Salfred /* Stop current transfer, if any */ 755195959Salfred usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); 756195959Salfred /* Set new interval */ 757195959Salfred usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate); 758195959Salfred /* Only set pollrate once */ 759195959Salfred sc->sc_pollrate = 0; 760195959Salfred } 761195959Salfred 762194228Sthompsa usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]); 763184610Salfred} 764184610Salfred 765184610Salfredstatic void 766192984Sthompsaums_stop_read(struct usb_fifo *fifo) 767184610Salfred{ 768194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 769184610Salfred 770194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); 771194228Sthompsa usb_callout_stop(&sc->sc_callout); 772184610Salfred} 773184610Salfred 774184610Salfred 775184610Salfred#if ((MOUSE_SYS_PACKETSIZE != 8) || \ 776184610Salfred (MOUSE_MSC_PACKETSIZE != 5)) 777184610Salfred#error "Software assumptions are not met. Please update code." 778184610Salfred#endif 779184610Salfred 780184610Salfredstatic void 781184610Salfredums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, 782184610Salfred int32_t dz, int32_t dt, int32_t buttons) 783184610Salfred{ 784184610Salfred uint8_t buf[8]; 785184610Salfred 786184610Salfred if (1) { 787184610Salfred 788184610Salfred if (dx > 254) 789184610Salfred dx = 254; 790184610Salfred if (dx < -256) 791184610Salfred dx = -256; 792184610Salfred if (dy > 254) 793184610Salfred dy = 254; 794184610Salfred if (dy < -256) 795184610Salfred dy = -256; 796184610Salfred if (dz > 126) 797184610Salfred dz = 126; 798184610Salfred if (dz < -128) 799184610Salfred dz = -128; 800184610Salfred if (dt > 126) 801184610Salfred dt = 126; 802184610Salfred if (dt < -128) 803184610Salfred dt = -128; 804184610Salfred 805184610Salfred buf[0] = sc->sc_mode.syncmask[1]; 806184610Salfred buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; 807184610Salfred buf[1] = dx >> 1; 808184610Salfred buf[2] = dy >> 1; 809184610Salfred buf[3] = dx - (dx >> 1); 810184610Salfred buf[4] = dy - (dy >> 1); 811184610Salfred 812184610Salfred if (sc->sc_mode.level == 1) { 813184610Salfred buf[5] = dz >> 1; 814184610Salfred buf[6] = dz - (dz >> 1); 815184610Salfred buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); 816184610Salfred } 817194228Sthompsa usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, 818184610Salfred sc->sc_mode.packetsize, 1); 819184610Salfred 820184610Salfred } else { 821184610Salfred DPRINTF("Buffer full, discarded packet\n"); 822184610Salfred } 823184610Salfred} 824184610Salfred 825184610Salfredstatic void 826184610Salfredums_reset_buf(struct ums_softc *sc) 827184610Salfred{ 828184610Salfred /* reset read queue */ 829194228Sthompsa usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); 830184610Salfred} 831184610Salfred 832184610Salfredstatic int 833192984Sthompsaums_open(struct usb_fifo *fifo, int fflags) 834184610Salfred{ 835194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 836184610Salfred 837184610Salfred DPRINTFN(2, "\n"); 838184610Salfred 839184610Salfred if (fflags & FREAD) { 840184610Salfred 841184610Salfred /* reset status */ 842184610Salfred 843184610Salfred sc->sc_status.flags = 0; 844184610Salfred sc->sc_status.button = 0; 845184610Salfred sc->sc_status.obutton = 0; 846184610Salfred sc->sc_status.dx = 0; 847184610Salfred sc->sc_status.dy = 0; 848184610Salfred sc->sc_status.dz = 0; 849184610Salfred /* sc->sc_status.dt = 0; */ 850184610Salfred 851194228Sthompsa if (usb_fifo_alloc_buffer(fifo, 852184610Salfred UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) { 853184610Salfred return (ENOMEM); 854184610Salfred } 855184610Salfred } 856184610Salfred return (0); 857184610Salfred} 858184610Salfred 859184610Salfredstatic void 860192984Sthompsaums_close(struct usb_fifo *fifo, int fflags) 861184610Salfred{ 862184610Salfred if (fflags & FREAD) { 863194228Sthompsa usb_fifo_free_buffer(fifo); 864184610Salfred } 865184610Salfred} 866184610Salfred 867184610Salfredstatic int 868192984Sthompsaums_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 869184610Salfred{ 870194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 871184610Salfred mousemode_t mode; 872184610Salfred int error = 0; 873184610Salfred 874184610Salfred DPRINTFN(2, "\n"); 875184610Salfred 876184610Salfred mtx_lock(&sc->sc_mtx); 877184610Salfred 878184610Salfred switch (cmd) { 879184610Salfred case MOUSE_GETHWINFO: 880184610Salfred *(mousehw_t *)addr = sc->sc_hw; 881184610Salfred break; 882184610Salfred 883184610Salfred case MOUSE_GETMODE: 884184610Salfred *(mousemode_t *)addr = sc->sc_mode; 885184610Salfred break; 886184610Salfred 887184610Salfred case MOUSE_SETMODE: 888184610Salfred mode = *(mousemode_t *)addr; 889184610Salfred 890184610Salfred if (mode.level == -1) { 891184610Salfred /* don't change the current setting */ 892184610Salfred } else if ((mode.level < 0) || (mode.level > 1)) { 893184610Salfred error = EINVAL; 894184610Salfred goto done; 895184610Salfred } else { 896184610Salfred sc->sc_mode.level = mode.level; 897184610Salfred } 898184610Salfred 899195959Salfred /* store polling rate */ 900195959Salfred sc->sc_pollrate = mode.rate; 901195959Salfred 902184610Salfred if (sc->sc_mode.level == 0) { 903184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 904184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 905184610Salfred else 906184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 907184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 908184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 909184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 910184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 911184610Salfred } else if (sc->sc_mode.level == 1) { 912184610Salfred if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) 913184610Salfred sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; 914184610Salfred else 915184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 916184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 917184610Salfred sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 918184610Salfred sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 919184610Salfred sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 920184610Salfred } 921184610Salfred ums_reset_buf(sc); 922184610Salfred break; 923184610Salfred 924184610Salfred case MOUSE_GETLEVEL: 925184610Salfred *(int *)addr = sc->sc_mode.level; 926184610Salfred break; 927184610Salfred 928184610Salfred case MOUSE_SETLEVEL: 929184610Salfred if (*(int *)addr < 0 || *(int *)addr > 1) { 930184610Salfred error = EINVAL; 931184610Salfred goto done; 932184610Salfred } 933184610Salfred sc->sc_mode.level = *(int *)addr; 934184610Salfred 935184610Salfred if (sc->sc_mode.level == 0) { 936184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 937184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 938184610Salfred else 939184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 940184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 941184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 942184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 943184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 944184610Salfred } else if (sc->sc_mode.level == 1) { 945184610Salfred if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) 946184610Salfred sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; 947184610Salfred else 948184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 949184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 950184610Salfred sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 951184610Salfred sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 952184610Salfred sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 953184610Salfred } 954184610Salfred ums_reset_buf(sc); 955184610Salfred break; 956184610Salfred 957184610Salfred case MOUSE_GETSTATUS:{ 958184610Salfred mousestatus_t *status = (mousestatus_t *)addr; 959184610Salfred 960184610Salfred *status = sc->sc_status; 961184610Salfred sc->sc_status.obutton = sc->sc_status.button; 962184610Salfred sc->sc_status.button = 0; 963184610Salfred sc->sc_status.dx = 0; 964184610Salfred sc->sc_status.dy = 0; 965184610Salfred sc->sc_status.dz = 0; 966184610Salfred /* sc->sc_status.dt = 0; */ 967184610Salfred 968184610Salfred if (status->dx || status->dy || status->dz /* || status->dt */ ) { 969184610Salfred status->flags |= MOUSE_POSCHANGED; 970184610Salfred } 971184610Salfred if (status->button != status->obutton) { 972184610Salfred status->flags |= MOUSE_BUTTONSCHANGED; 973184610Salfred } 974184610Salfred break; 975184610Salfred } 976184610Salfred default: 977184610Salfred error = ENOTTY; 978184610Salfred } 979184610Salfred 980184610Salfreddone: 981184610Salfred mtx_unlock(&sc->sc_mtx); 982184610Salfred return (error); 983184610Salfred} 984184610Salfred 985198373Sthompsastatic int 986198373Sthompsaums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS) 987198373Sthompsa{ 988198373Sthompsa struct ums_softc *sc = arg1; 989198373Sthompsa struct ums_info *info; 990198373Sthompsa struct sbuf *sb; 991219848Shselasky int i, j, err, had_output; 992198373Sthompsa 993198373Sthompsa sb = sbuf_new_auto(); 994219848Shselasky for (i = 0, had_output = 0; i < UMS_INFO_MAX; i++) { 995198373Sthompsa info = &sc->sc_info[i]; 996198373Sthompsa 997198373Sthompsa /* Don't emit empty info */ 998198373Sthompsa if ((info->sc_flags & 999198373Sthompsa (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS | 1000198373Sthompsa UMS_FLAG_T_AXIS | UMS_FLAG_W_AXIS)) == 0 && 1001198373Sthompsa info->sc_buttons == 0) 1002198373Sthompsa continue; 1003198373Sthompsa 1004219848Shselasky if (had_output) 1005219848Shselasky sbuf_printf(sb, "\n"); 1006219848Shselasky had_output = 1; 1007198373Sthompsa sbuf_printf(sb, "i%d:", i + 1); 1008198373Sthompsa if (info->sc_flags & UMS_FLAG_X_AXIS) 1009198373Sthompsa sbuf_printf(sb, " X:r%d, p%d, s%d;", 1010198373Sthompsa (int)info->sc_iid_x, 1011198373Sthompsa (int)info->sc_loc_x.pos, 1012198373Sthompsa (int)info->sc_loc_x.size); 1013198373Sthompsa if (info->sc_flags & UMS_FLAG_Y_AXIS) 1014198373Sthompsa sbuf_printf(sb, " Y:r%d, p%d, s%d;", 1015198373Sthompsa (int)info->sc_iid_y, 1016198373Sthompsa (int)info->sc_loc_y.pos, 1017198373Sthompsa (int)info->sc_loc_y.size); 1018198373Sthompsa if (info->sc_flags & UMS_FLAG_Z_AXIS) 1019198373Sthompsa sbuf_printf(sb, " Z:r%d, p%d, s%d;", 1020198373Sthompsa (int)info->sc_iid_z, 1021198373Sthompsa (int)info->sc_loc_z.pos, 1022198373Sthompsa (int)info->sc_loc_z.size); 1023198373Sthompsa if (info->sc_flags & UMS_FLAG_T_AXIS) 1024198373Sthompsa sbuf_printf(sb, " T:r%d, p%d, s%d;", 1025198373Sthompsa (int)info->sc_iid_t, 1026198373Sthompsa (int)info->sc_loc_t.pos, 1027198373Sthompsa (int)info->sc_loc_t.size); 1028198373Sthompsa if (info->sc_flags & UMS_FLAG_W_AXIS) 1029198373Sthompsa sbuf_printf(sb, " W:r%d, p%d, s%d;", 1030198373Sthompsa (int)info->sc_iid_w, 1031198373Sthompsa (int)info->sc_loc_w.pos, 1032198373Sthompsa (int)info->sc_loc_w.size); 1033198373Sthompsa 1034198373Sthompsa for (j = 0; j < info->sc_buttons; j++) { 1035198373Sthompsa sbuf_printf(sb, " B%d:r%d, p%d, s%d;", j + 1, 1036198373Sthompsa (int)info->sc_iid_btn[j], 1037198373Sthompsa (int)info->sc_loc_btn[j].pos, 1038198373Sthompsa (int)info->sc_loc_btn[j].size); 1039198373Sthompsa } 1040198373Sthompsa } 1041198373Sthompsa sbuf_finish(sb); 1042198373Sthompsa err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); 1043198373Sthompsa sbuf_delete(sb); 1044198373Sthompsa 1045198373Sthompsa return (err); 1046198373Sthompsa} 1047198373Sthompsa 1048184610Salfredstatic devclass_t ums_devclass; 1049184610Salfred 1050184610Salfredstatic device_method_t ums_methods[] = { 1051184610Salfred DEVMETHOD(device_probe, ums_probe), 1052184610Salfred DEVMETHOD(device_attach, ums_attach), 1053184610Salfred DEVMETHOD(device_detach, ums_detach), 1054184610Salfred {0, 0} 1055184610Salfred}; 1056184610Salfred 1057184610Salfredstatic driver_t ums_driver = { 1058184610Salfred .name = "ums", 1059184610Salfred .methods = ums_methods, 1060184610Salfred .size = sizeof(struct ums_softc), 1061184610Salfred}; 1062184610Salfred 1063189275SthompsaDRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0); 1064188942SthompsaMODULE_DEPEND(ums, usb, 1, 1, 1); 1065212122SthompsaMODULE_VERSION(ums, 1); 1066