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$"); 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 79248085Smariusstatic SYSCTL_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; 137235724Shselasky int sc_fflags; 138195959Salfred 139190741Sthompsa uint8_t sc_buttons; 140190741Sthompsa uint8_t sc_iid; 141184610Salfred uint8_t sc_temp[64]; 142184610Salfred}; 143184610Salfred 144184610Salfredstatic void ums_put_queue_timeout(void *__sc); 145184610Salfred 146193045Sthompsastatic usb_callback_t ums_intr_callback; 147184610Salfred 148184610Salfredstatic device_probe_t ums_probe; 149184610Salfredstatic device_attach_t ums_attach; 150184610Salfredstatic device_detach_t ums_detach; 151184610Salfred 152193045Sthompsastatic usb_fifo_cmd_t ums_start_read; 153193045Sthompsastatic usb_fifo_cmd_t ums_stop_read; 154193045Sthompsastatic usb_fifo_open_t ums_open; 155193045Sthompsastatic usb_fifo_close_t ums_close; 156193045Sthompsastatic usb_fifo_ioctl_t ums_ioctl; 157184610Salfred 158198373Sthompsastatic void ums_put_queue(struct ums_softc *, int32_t, int32_t, 159198373Sthompsa int32_t, int32_t, int32_t); 160198373Sthompsastatic int ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS); 161184610Salfred 162192984Sthompsastatic struct usb_fifo_methods ums_fifo_methods = { 163184610Salfred .f_open = &ums_open, 164184610Salfred .f_close = &ums_close, 165184610Salfred .f_ioctl = &ums_ioctl, 166184610Salfred .f_start_read = &ums_start_read, 167184610Salfred .f_stop_read = &ums_stop_read, 168184610Salfred .basename[0] = "ums", 169184610Salfred}; 170184610Salfred 171184610Salfredstatic void 172184610Salfredums_put_queue_timeout(void *__sc) 173184610Salfred{ 174184610Salfred struct ums_softc *sc = __sc; 175184610Salfred 176184610Salfred mtx_assert(&sc->sc_mtx, MA_OWNED); 177184610Salfred 178184610Salfred ums_put_queue(sc, 0, 0, 0, 0, 0); 179184610Salfred} 180184610Salfred 181184610Salfredstatic void 182194677Sthompsaums_intr_callback(struct usb_xfer *xfer, usb_error_t error) 183184610Salfred{ 184194677Sthompsa struct ums_softc *sc = usbd_xfer_softc(xfer); 185190741Sthompsa struct ums_info *info = &sc->sc_info[0]; 186194677Sthompsa struct usb_page_cache *pc; 187184610Salfred uint8_t *buf = sc->sc_temp; 188184610Salfred int32_t buttons = 0; 189195959Salfred int32_t buttons_found = 0; 190190741Sthompsa int32_t dw = 0; 191190741Sthompsa int32_t dx = 0; 192190741Sthompsa int32_t dy = 0; 193190741Sthompsa int32_t dz = 0; 194190741Sthompsa int32_t dt = 0; 195184610Salfred uint8_t i; 196189583Sthompsa uint8_t id; 197194677Sthompsa int len; 198184610Salfred 199194677Sthompsa usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 200194677Sthompsa 201184610Salfred switch (USB_GET_STATE(xfer)) { 202184610Salfred case USB_ST_TRANSFERRED: 203184610Salfred DPRINTFN(6, "sc=%p actlen=%d\n", sc, len); 204184610Salfred 205235000Shselasky if (len > (int)sizeof(sc->sc_temp)) { 206184610Salfred DPRINTFN(6, "truncating large packet to %zu bytes\n", 207184610Salfred sizeof(sc->sc_temp)); 208184610Salfred len = sizeof(sc->sc_temp); 209184610Salfred } 210188981Sthompsa if (len == 0) 211184610Salfred goto tr_setup; 212188981Sthompsa 213194677Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 214194677Sthompsa usbd_copy_out(pc, 0, buf, len); 215184610Salfred 216184610Salfred DPRINTFN(6, "data = %02x %02x %02x %02x " 217184610Salfred "%02x %02x %02x %02x\n", 218184610Salfred (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0, 219184610Salfred (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0, 220184610Salfred (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0, 221184610Salfred (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0); 222184610Salfred 223184610Salfred if (sc->sc_iid) { 224189583Sthompsa id = *buf; 225184610Salfred 226184610Salfred len--; 227184610Salfred buf++; 228184610Salfred 229184610Salfred } else { 230189583Sthompsa id = 0; 231190741Sthompsa if (sc->sc_info[0].sc_flags & UMS_FLAG_SBU) { 232184610Salfred if ((*buf == 0x14) || (*buf == 0x15)) { 233184610Salfred goto tr_setup; 234184610Salfred } 235184610Salfred } 236184610Salfred } 237184610Salfred 238190741Sthompsa repeat: 239190741Sthompsa if ((info->sc_flags & UMS_FLAG_W_AXIS) && 240190741Sthompsa (id == info->sc_iid_w)) 241190741Sthompsa dw += hid_get_data(buf, len, &info->sc_loc_w); 242184610Salfred 243190741Sthompsa if ((info->sc_flags & UMS_FLAG_X_AXIS) && 244190741Sthompsa (id == info->sc_iid_x)) 245190741Sthompsa dx += hid_get_data(buf, len, &info->sc_loc_x); 246184610Salfred 247190741Sthompsa if ((info->sc_flags & UMS_FLAG_Y_AXIS) && 248190741Sthompsa (id == info->sc_iid_y)) 249190741Sthompsa dy = -hid_get_data(buf, len, &info->sc_loc_y); 250184610Salfred 251190741Sthompsa if ((info->sc_flags & UMS_FLAG_Z_AXIS) && 252190741Sthompsa (id == info->sc_iid_z)) { 253190741Sthompsa int32_t temp; 254190741Sthompsa temp = hid_get_data(buf, len, &info->sc_loc_z); 255190741Sthompsa if (info->sc_flags & UMS_FLAG_REVZ) 256190741Sthompsa temp = -temp; 257190741Sthompsa dz -= temp; 258190741Sthompsa } 259184610Salfred 260190741Sthompsa if ((info->sc_flags & UMS_FLAG_T_AXIS) && 261190741Sthompsa (id == info->sc_iid_t)) 262190741Sthompsa dt -= hid_get_data(buf, len, &info->sc_loc_t); 263184610Salfred 264190741Sthompsa for (i = 0; i < info->sc_buttons; i++) { 265195959Salfred uint32_t mask; 266195959Salfred mask = 1UL << UMS_BUT(i); 267195959Salfred /* check for correct button ID */ 268190741Sthompsa if (id != info->sc_iid_btn[i]) 269189583Sthompsa continue; 270195959Salfred /* check for button pressed */ 271195959Salfred if (hid_get_data(buf, len, &info->sc_loc_btn[i])) 272195959Salfred buttons |= mask; 273195959Salfred /* register button mask */ 274195959Salfred buttons_found |= mask; 275184610Salfred } 276184610Salfred 277190741Sthompsa if (++info != &sc->sc_info[UMS_INFO_MAX]) 278190741Sthompsa goto repeat; 279190741Sthompsa 280195959Salfred /* keep old button value(s) for non-detected buttons */ 281195959Salfred buttons |= sc->sc_status.button & ~buttons_found; 282195959Salfred 283184610Salfred if (dx || dy || dz || dt || dw || 284184610Salfred (buttons != sc->sc_status.button)) { 285184610Salfred 286184610Salfred DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n", 287184610Salfred dx, dy, dz, dt, dw, buttons); 288184610Salfred 289208009Sthompsa /* translate T-axis into button presses until further */ 290208009Sthompsa if (dt > 0) 291208009Sthompsa buttons |= 1UL << 3; 292208009Sthompsa else if (dt < 0) 293208009Sthompsa buttons |= 1UL << 4; 294208009Sthompsa 295184610Salfred sc->sc_status.button = buttons; 296184610Salfred sc->sc_status.dx += dx; 297184610Salfred sc->sc_status.dy += dy; 298184610Salfred sc->sc_status.dz += dz; 299184610Salfred /* 300184610Salfred * sc->sc_status.dt += dt; 301184610Salfred * no way to export this yet 302184610Salfred */ 303184610Salfred 304184610Salfred /* 305188981Sthompsa * The Qtronix keyboard has a built in PS/2 306188981Sthompsa * port for a mouse. The firmware once in a 307188981Sthompsa * while posts a spurious button up 308188981Sthompsa * event. This event we ignore by doing a 309188981Sthompsa * timeout for 50 msecs. If we receive 310188981Sthompsa * dx=dy=dz=buttons=0 before we add the event 311188981Sthompsa * to the queue. In any other case we delete 312188981Sthompsa * the timeout event. 313184610Salfred */ 314190741Sthompsa if ((sc->sc_info[0].sc_flags & UMS_FLAG_SBU) && 315184610Salfred (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) && 316184610Salfred (dw == 0) && (buttons == 0)) { 317184610Salfred 318194228Sthompsa usb_callout_reset(&sc->sc_callout, hz / 20, 319184610Salfred &ums_put_queue_timeout, sc); 320184610Salfred } else { 321184610Salfred 322194228Sthompsa usb_callout_stop(&sc->sc_callout); 323184610Salfred 324184610Salfred ums_put_queue(sc, dx, dy, dz, dt, buttons); 325184610Salfred } 326184610Salfred } 327184610Salfred case USB_ST_SETUP: 328184610Salfredtr_setup: 329188981Sthompsa /* check if we can put more data into the FIFO */ 330194228Sthompsa if (usb_fifo_put_bytes_max( 331188981Sthompsa sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { 332194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 333194228Sthompsa usbd_transfer_submit(xfer); 334184610Salfred } 335188981Sthompsa break; 336184610Salfred 337184610Salfred default: /* Error */ 338194677Sthompsa if (error != USB_ERR_CANCELLED) { 339188981Sthompsa /* try clear stall first */ 340194677Sthompsa usbd_xfer_set_stall(xfer); 341188981Sthompsa goto tr_setup; 342184610Salfred } 343188981Sthompsa break; 344184610Salfred } 345184610Salfred} 346184610Salfred 347192984Sthompsastatic const struct usb_config ums_config[UMS_N_TRANSFER] = { 348184610Salfred 349187259Sthompsa [UMS_INTR_DT] = { 350184610Salfred .type = UE_INTERRUPT, 351184610Salfred .endpoint = UE_ADDR_ANY, 352184610Salfred .direction = UE_DIR_IN, 353190734Sthompsa .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 354190734Sthompsa .bufsize = 0, /* use wMaxPacketSize */ 355190734Sthompsa .callback = &ums_intr_callback, 356184610Salfred }, 357184610Salfred}; 358184610Salfred 359223521Shselasky/* A match on these entries will load ums */ 360223521Shselaskystatic const STRUCT_USB_HOST_ID __used ums_devs[] = { 361223521Shselasky {USB_IFACE_CLASS(UICLASS_HID), 362223521Shselasky USB_IFACE_SUBCLASS(UISUBCLASS_BOOT), 363223521Shselasky USB_IFACE_PROTOCOL(UIPROTO_MOUSE),}, 364223521Shselasky}; 365223521Shselasky 366184610Salfredstatic int 367184610Salfredums_probe(device_t dev) 368184610Salfred{ 369192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 370184610Salfred void *d_ptr; 371245733Shselasky int error; 372184610Salfred uint16_t d_len; 373184610Salfred 374184610Salfred DPRINTFN(11, "\n"); 375184610Salfred 376192499Sthompsa if (uaa->usb_mode != USB_MODE_HOST) 377184610Salfred return (ENXIO); 378188981Sthompsa 379194068Sthompsa if (uaa->info.bInterfaceClass != UICLASS_HID) 380184610Salfred return (ENXIO); 381188981Sthompsa 382240667Shselasky if (usb_test_quirk(uaa, UQ_UMS_IGNORE)) 383240667Shselasky return (ENXIO); 384240667Shselasky 385194068Sthompsa if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) && 386194068Sthompsa (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE)) 387222051Savg return (BUS_PROBE_DEFAULT); 388194068Sthompsa 389194228Sthompsa error = usbd_req_get_hid_desc(uaa->device, NULL, 390184610Salfred &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); 391184610Salfred 392188981Sthompsa if (error) 393184610Salfred return (ENXIO); 394188981Sthompsa 395245733Shselasky if (hid_is_mouse(d_ptr, d_len)) 396245733Shselasky error = BUS_PROBE_DEFAULT; 397245733Shselasky else 398245733Shselasky error = ENXIO; 399245733Shselasky 400184610Salfred free(d_ptr, M_TEMP); 401245733Shselasky return (error); 402184610Salfred} 403184610Salfred 404190741Sthompsastatic void 405190741Sthompsaums_hid_parse(struct ums_softc *sc, device_t dev, const uint8_t *buf, 406190741Sthompsa uint16_t len, uint8_t index) 407184610Salfred{ 408190741Sthompsa struct ums_info *info = &sc->sc_info[index]; 409184610Salfred uint32_t flags; 410184610Salfred uint8_t i; 411212129Sthompsa uint8_t j; 412184610Salfred 413190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 414190741Sthompsa hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) { 415184610Salfred 416184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 417190741Sthompsa info->sc_flags |= UMS_FLAG_X_AXIS; 418184610Salfred } 419184610Salfred } 420190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 421190741Sthompsa hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) { 422184610Salfred 423184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 424190741Sthompsa info->sc_flags |= UMS_FLAG_Y_AXIS; 425184610Salfred } 426184610Salfred } 427184610Salfred /* Try the wheel first as the Z activator since it's tradition. */ 428190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 429190741Sthompsa HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags, 430190741Sthompsa &info->sc_iid_z) || 431190741Sthompsa hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 432190741Sthompsa HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags, 433190741Sthompsa &info->sc_iid_z)) { 434184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 435190741Sthompsa info->sc_flags |= UMS_FLAG_Z_AXIS; 436184610Salfred } 437184610Salfred /* 438184610Salfred * We might have both a wheel and Z direction, if so put 439184610Salfred * put the Z on the W coordinate. 440184610Salfred */ 441190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 442190741Sthompsa HUG_Z), hid_input, index, &info->sc_loc_w, &flags, 443190741Sthompsa &info->sc_iid_w)) { 444184610Salfred 445184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 446190741Sthompsa info->sc_flags |= UMS_FLAG_W_AXIS; 447184610Salfred } 448184610Salfred } 449190741Sthompsa } else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 450190741Sthompsa HUG_Z), hid_input, index, &info->sc_loc_z, &flags, 451190741Sthompsa &info->sc_iid_z)) { 452184610Salfred 453184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 454190741Sthompsa info->sc_flags |= UMS_FLAG_Z_AXIS; 455184610Salfred } 456184610Salfred } 457184610Salfred /* 458184610Salfred * The Microsoft Wireless Intellimouse 2.0 reports it's wheel 459184610Salfred * using 0x0048, which is HUG_TWHEEL, and seems to expect you 460184610Salfred * to know that the byte after the wheel is the tilt axis. 461184610Salfred * There are no other HID axis descriptors other than X,Y and 462184610Salfred * TWHEEL 463184610Salfred */ 464190741Sthompsa if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, 465190741Sthompsa HUG_TWHEEL), hid_input, index, &info->sc_loc_t, 466190741Sthompsa &flags, &info->sc_iid_t)) { 467184610Salfred 468190741Sthompsa info->sc_loc_t.pos += 8; 469184610Salfred 470184610Salfred if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { 471190741Sthompsa info->sc_flags |= UMS_FLAG_T_AXIS; 472184610Salfred } 473208009Sthompsa } else if (hid_locate(buf, len, HID_USAGE2(HUP_CONSUMER, 474208009Sthompsa HUC_AC_PAN), hid_input, index, &info->sc_loc_t, 475208009Sthompsa &flags, &info->sc_iid_t)) { 476208009Sthompsa 477208009Sthompsa if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) 478208009Sthompsa info->sc_flags |= UMS_FLAG_T_AXIS; 479184610Salfred } 480184610Salfred /* figure out the number of buttons */ 481184610Salfred 482184610Salfred for (i = 0; i < UMS_BUTTON_MAX; i++) { 483190741Sthompsa if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)), 484190741Sthompsa hid_input, index, &info->sc_loc_btn[i], NULL, 485190741Sthompsa &info->sc_iid_btn[i])) { 486184610Salfred break; 487184610Salfred } 488184610Salfred } 489212129Sthompsa 490212129Sthompsa /* detect other buttons */ 491212129Sthompsa 492212132Sthompsa for (j = 0; (i < UMS_BUTTON_MAX) && (j < 2); i++, j++) { 493212129Sthompsa if (!hid_locate(buf, len, HID_USAGE2(HUP_MICROSOFT, (j + 1)), 494212129Sthompsa hid_input, index, &info->sc_loc_btn[i], NULL, 495212129Sthompsa &info->sc_iid_btn[i])) { 496212129Sthompsa break; 497212129Sthompsa } 498212129Sthompsa } 499212129Sthompsa 500190741Sthompsa info->sc_buttons = i; 501184610Salfred 502190741Sthompsa if (i > sc->sc_buttons) 503190741Sthompsa sc->sc_buttons = i; 504184610Salfred 505190741Sthompsa if (info->sc_flags == 0) 506190741Sthompsa return; 507190741Sthompsa 508190741Sthompsa /* announce information about the mouse */ 509190741Sthompsa device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n", 510190741Sthompsa (info->sc_buttons), 511190741Sthompsa (info->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "", 512190741Sthompsa (info->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "", 513190741Sthompsa (info->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "", 514190741Sthompsa (info->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "", 515190741Sthompsa (info->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "", 516190741Sthompsa info->sc_iid_x); 517190741Sthompsa} 518190741Sthompsa 519190741Sthompsastatic int 520190741Sthompsaums_attach(device_t dev) 521190741Sthompsa{ 522192984Sthompsa struct usb_attach_arg *uaa = device_get_ivars(dev); 523190741Sthompsa struct ums_softc *sc = device_get_softc(dev); 524190741Sthompsa struct ums_info *info; 525190741Sthompsa void *d_ptr = NULL; 526190741Sthompsa int isize; 527190741Sthompsa int err; 528190741Sthompsa uint16_t d_len; 529190741Sthompsa uint8_t i; 530207077Sthompsa#ifdef USB_DEBUG 531190741Sthompsa uint8_t j; 532207077Sthompsa#endif 533190741Sthompsa 534190741Sthompsa DPRINTFN(11, "sc=%p\n", sc); 535190741Sthompsa 536194228Sthompsa device_set_usb_desc(dev); 537190741Sthompsa 538190741Sthompsa mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE); 539190741Sthompsa 540194228Sthompsa usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); 541190741Sthompsa 542190741Sthompsa /* 543190741Sthompsa * Force the report (non-boot) protocol. 544190741Sthompsa * 545190741Sthompsa * Mice without boot protocol support may choose not to implement 546190741Sthompsa * Set_Protocol at all; Ignore any error. 547190741Sthompsa */ 548194228Sthompsa err = usbd_req_set_protocol(uaa->device, NULL, 549190741Sthompsa uaa->info.bIfaceIndex, 1); 550190741Sthompsa 551194228Sthompsa err = usbd_transfer_setup(uaa->device, 552190741Sthompsa &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config, 553190741Sthompsa UMS_N_TRANSFER, sc, &sc->sc_mtx); 554190741Sthompsa 555190741Sthompsa if (err) { 556194228Sthompsa DPRINTF("error=%s\n", usbd_errstr(err)); 557190741Sthompsa goto detach; 558190741Sthompsa } 559195959Salfred 560195959Salfred /* Get HID descriptor */ 561194228Sthompsa err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, 562190741Sthompsa &d_len, M_TEMP, uaa->info.bIfaceIndex); 563190741Sthompsa 564190741Sthompsa if (err) { 565190741Sthompsa device_printf(dev, "error reading report description\n"); 566190741Sthompsa goto detach; 567190741Sthompsa } 568190741Sthompsa 569189583Sthompsa isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid); 570184610Salfred 571184610Salfred /* 572184610Salfred * The Microsoft Wireless Notebook Optical Mouse seems to be in worse 573184610Salfred * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and 574184610Salfred * all of its other button positions are all off. It also reports that 575184610Salfred * it has two addional buttons and a tilt wheel. 576184610Salfred */ 577194228Sthompsa if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) { 578195959Salfred 579195959Salfred sc->sc_iid = 0; 580195959Salfred 581190741Sthompsa info = &sc->sc_info[0]; 582190741Sthompsa info->sc_flags = (UMS_FLAG_X_AXIS | 583184610Salfred UMS_FLAG_Y_AXIS | 584184610Salfred UMS_FLAG_Z_AXIS | 585184610Salfred UMS_FLAG_SBU); 586190741Sthompsa info->sc_buttons = 3; 587184610Salfred isize = 5; 588184610Salfred /* 1st byte of descriptor report contains garbage */ 589190741Sthompsa info->sc_loc_x.pos = 16; 590195959Salfred info->sc_loc_x.size = 8; 591190741Sthompsa info->sc_loc_y.pos = 24; 592195959Salfred info->sc_loc_y.size = 8; 593190741Sthompsa info->sc_loc_z.pos = 32; 594195959Salfred info->sc_loc_z.size = 8; 595190741Sthompsa info->sc_loc_btn[0].pos = 8; 596195959Salfred info->sc_loc_btn[0].size = 1; 597190741Sthompsa info->sc_loc_btn[1].pos = 9; 598195959Salfred info->sc_loc_btn[1].size = 1; 599190741Sthompsa info->sc_loc_btn[2].pos = 10; 600195959Salfred info->sc_loc_btn[2].size = 1; 601188981Sthompsa 602190741Sthompsa /* Announce device */ 603190741Sthompsa device_printf(dev, "3 buttons and [XYZ] " 604190741Sthompsa "coordinates ID=0\n"); 605190741Sthompsa 606190741Sthompsa } else { 607190741Sthompsa /* Search the HID descriptor and announce device */ 608190741Sthompsa for (i = 0; i < UMS_INFO_MAX; i++) { 609190741Sthompsa ums_hid_parse(sc, dev, d_ptr, d_len, i); 610190741Sthompsa } 611184610Salfred } 612188981Sthompsa 613194228Sthompsa if (usb_test_quirk(uaa, UQ_MS_REVZ)) { 614190741Sthompsa info = &sc->sc_info[0]; 615184610Salfred /* Some wheels need the Z axis reversed. */ 616190741Sthompsa info->sc_flags |= UMS_FLAG_REVZ; 617184610Salfred } 618235000Shselasky if (isize > (int)usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])) { 619184610Salfred DPRINTF("WARNING: report size, %d bytes, is larger " 620194677Sthompsa "than interrupt size, %d bytes!\n", isize, 621194677Sthompsa usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])); 622184610Salfred } 623184610Salfred free(d_ptr, M_TEMP); 624184610Salfred d_ptr = NULL; 625184610Salfred 626207077Sthompsa#ifdef USB_DEBUG 627190741Sthompsa for (j = 0; j < UMS_INFO_MAX; j++) { 628190741Sthompsa info = &sc->sc_info[j]; 629184610Salfred 630190741Sthompsa DPRINTF("sc=%p, index=%d\n", sc, j); 631190741Sthompsa DPRINTF("X\t%d/%d id=%d\n", info->sc_loc_x.pos, 632190741Sthompsa info->sc_loc_x.size, info->sc_iid_x); 633190741Sthompsa DPRINTF("Y\t%d/%d id=%d\n", info->sc_loc_y.pos, 634190741Sthompsa info->sc_loc_y.size, info->sc_iid_y); 635190741Sthompsa DPRINTF("Z\t%d/%d id=%d\n", info->sc_loc_z.pos, 636190741Sthompsa info->sc_loc_z.size, info->sc_iid_z); 637190741Sthompsa DPRINTF("T\t%d/%d id=%d\n", info->sc_loc_t.pos, 638190741Sthompsa info->sc_loc_t.size, info->sc_iid_t); 639190741Sthompsa DPRINTF("W\t%d/%d id=%d\n", info->sc_loc_w.pos, 640190741Sthompsa info->sc_loc_w.size, info->sc_iid_w); 641190741Sthompsa 642190741Sthompsa for (i = 0; i < info->sc_buttons; i++) { 643190741Sthompsa DPRINTF("B%d\t%d/%d id=%d\n", 644190741Sthompsa i + 1, info->sc_loc_btn[i].pos, 645190741Sthompsa info->sc_loc_btn[i].size, info->sc_iid_btn[i]); 646190741Sthompsa } 647184610Salfred } 648184610Salfred DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid); 649184610Salfred#endif 650184610Salfred 651194228Sthompsa err = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, 652184610Salfred &ums_fifo_methods, &sc->sc_fifo, 653235000Shselasky device_get_unit(dev), -1, uaa->info.bIfaceIndex, 654189110Sthompsa UID_ROOT, GID_OPERATOR, 0644); 655235724Shselasky if (err) 656184610Salfred goto detach; 657235724Shselasky 658198373Sthompsa SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 659198373Sthompsa SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 660198373Sthompsa OID_AUTO, "parseinfo", CTLTYPE_STRING|CTLFLAG_RD, 661198373Sthompsa sc, 0, ums_sysctl_handler_parseinfo, 662219848Shselasky "", "Dump of parsed HID report descriptor"); 663198373Sthompsa 664184610Salfred return (0); 665184610Salfred 666184610Salfreddetach: 667184610Salfred if (d_ptr) { 668184610Salfred free(d_ptr, M_TEMP); 669184610Salfred } 670184610Salfred ums_detach(dev); 671184610Salfred return (ENOMEM); 672184610Salfred} 673184610Salfred 674184610Salfredstatic int 675184610Salfredums_detach(device_t self) 676184610Salfred{ 677184610Salfred struct ums_softc *sc = device_get_softc(self); 678184610Salfred 679184610Salfred DPRINTF("sc=%p\n", sc); 680184610Salfred 681194228Sthompsa usb_fifo_detach(&sc->sc_fifo); 682184610Salfred 683194228Sthompsa usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER); 684184610Salfred 685194228Sthompsa usb_callout_drain(&sc->sc_callout); 686184610Salfred 687184610Salfred mtx_destroy(&sc->sc_mtx); 688184610Salfred 689184610Salfred return (0); 690184610Salfred} 691184610Salfred 692184610Salfredstatic void 693192984Sthompsaums_start_read(struct usb_fifo *fifo) 694184610Salfred{ 695194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 696195959Salfred int rate; 697184610Salfred 698195959Salfred /* Check if we should override the default polling interval */ 699195959Salfred rate = sc->sc_pollrate; 700195959Salfred /* Range check rate */ 701195959Salfred if (rate > 1000) 702195959Salfred rate = 1000; 703195959Salfred /* Check for set rate */ 704195959Salfred if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) { 705195959Salfred DPRINTF("Setting pollrate = %d\n", rate); 706195959Salfred /* Stop current transfer, if any */ 707195959Salfred usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); 708195959Salfred /* Set new interval */ 709195959Salfred usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate); 710195959Salfred /* Only set pollrate once */ 711195959Salfred sc->sc_pollrate = 0; 712195959Salfred } 713195959Salfred 714194228Sthompsa usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]); 715184610Salfred} 716184610Salfred 717184610Salfredstatic void 718192984Sthompsaums_stop_read(struct usb_fifo *fifo) 719184610Salfred{ 720194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 721184610Salfred 722194228Sthompsa usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); 723194228Sthompsa usb_callout_stop(&sc->sc_callout); 724184610Salfred} 725184610Salfred 726184610Salfred 727184610Salfred#if ((MOUSE_SYS_PACKETSIZE != 8) || \ 728184610Salfred (MOUSE_MSC_PACKETSIZE != 5)) 729184610Salfred#error "Software assumptions are not met. Please update code." 730184610Salfred#endif 731184610Salfred 732184610Salfredstatic void 733184610Salfredums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, 734184610Salfred int32_t dz, int32_t dt, int32_t buttons) 735184610Salfred{ 736184610Salfred uint8_t buf[8]; 737184610Salfred 738184610Salfred if (1) { 739184610Salfred 740184610Salfred if (dx > 254) 741184610Salfred dx = 254; 742184610Salfred if (dx < -256) 743184610Salfred dx = -256; 744184610Salfred if (dy > 254) 745184610Salfred dy = 254; 746184610Salfred if (dy < -256) 747184610Salfred dy = -256; 748184610Salfred if (dz > 126) 749184610Salfred dz = 126; 750184610Salfred if (dz < -128) 751184610Salfred dz = -128; 752184610Salfred if (dt > 126) 753184610Salfred dt = 126; 754184610Salfred if (dt < -128) 755184610Salfred dt = -128; 756184610Salfred 757184610Salfred buf[0] = sc->sc_mode.syncmask[1]; 758184610Salfred buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; 759184610Salfred buf[1] = dx >> 1; 760184610Salfred buf[2] = dy >> 1; 761184610Salfred buf[3] = dx - (dx >> 1); 762184610Salfred buf[4] = dy - (dy >> 1); 763184610Salfred 764184610Salfred if (sc->sc_mode.level == 1) { 765184610Salfred buf[5] = dz >> 1; 766184610Salfred buf[6] = dz - (dz >> 1); 767184610Salfred buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); 768184610Salfred } 769194228Sthompsa usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, 770184610Salfred sc->sc_mode.packetsize, 1); 771184610Salfred 772184610Salfred } else { 773184610Salfred DPRINTF("Buffer full, discarded packet\n"); 774184610Salfred } 775184610Salfred} 776184610Salfred 777184610Salfredstatic void 778184610Salfredums_reset_buf(struct ums_softc *sc) 779184610Salfred{ 780235724Shselasky /* reset read queue, must be called locked */ 781194228Sthompsa usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); 782184610Salfred} 783184610Salfred 784184610Salfredstatic int 785192984Sthompsaums_open(struct usb_fifo *fifo, int fflags) 786184610Salfred{ 787194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 788184610Salfred 789184610Salfred DPRINTFN(2, "\n"); 790184610Salfred 791235724Shselasky /* check for duplicate open, should not happen */ 792235724Shselasky if (sc->sc_fflags & fflags) 793235724Shselasky return (EBUSY); 794184610Salfred 795235724Shselasky /* check for first open */ 796235724Shselasky if (sc->sc_fflags == 0) { 797235724Shselasky 798235724Shselasky /* reset all USB mouse parameters */ 799235724Shselasky 800235724Shselasky if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 801235724Shselasky sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 802235724Shselasky else 803235724Shselasky sc->sc_hw.buttons = sc->sc_buttons; 804235724Shselasky 805235724Shselasky sc->sc_hw.iftype = MOUSE_IF_USB; 806235724Shselasky sc->sc_hw.type = MOUSE_MOUSE; 807235724Shselasky sc->sc_hw.model = MOUSE_MODEL_GENERIC; 808235724Shselasky sc->sc_hw.hwid = 0; 809235724Shselasky 810235724Shselasky sc->sc_mode.protocol = MOUSE_PROTO_MSC; 811235724Shselasky sc->sc_mode.rate = -1; 812235724Shselasky sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; 813235724Shselasky sc->sc_mode.accelfactor = 0; 814235724Shselasky sc->sc_mode.level = 0; 815235724Shselasky sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 816235724Shselasky sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 817235724Shselasky sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 818235724Shselasky 819184610Salfred /* reset status */ 820184610Salfred 821184610Salfred sc->sc_status.flags = 0; 822184610Salfred sc->sc_status.button = 0; 823184610Salfred sc->sc_status.obutton = 0; 824184610Salfred sc->sc_status.dx = 0; 825184610Salfred sc->sc_status.dy = 0; 826184610Salfred sc->sc_status.dz = 0; 827184610Salfred /* sc->sc_status.dt = 0; */ 828235724Shselasky } 829184610Salfred 830235724Shselasky if (fflags & FREAD) { 831235724Shselasky /* allocate RX buffer */ 832194228Sthompsa if (usb_fifo_alloc_buffer(fifo, 833184610Salfred UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) { 834184610Salfred return (ENOMEM); 835184610Salfred } 836184610Salfred } 837235724Shselasky 838235724Shselasky sc->sc_fflags |= fflags & (FREAD | FWRITE); 839184610Salfred return (0); 840184610Salfred} 841184610Salfred 842184610Salfredstatic void 843192984Sthompsaums_close(struct usb_fifo *fifo, int fflags) 844184610Salfred{ 845235724Shselasky struct ums_softc *sc = usb_fifo_softc(fifo); 846235724Shselasky 847235724Shselasky DPRINTFN(2, "\n"); 848235724Shselasky 849235724Shselasky if (fflags & FREAD) 850194228Sthompsa usb_fifo_free_buffer(fifo); 851235724Shselasky 852235724Shselasky sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); 853184610Salfred} 854184610Salfred 855184610Salfredstatic int 856192984Sthompsaums_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 857184610Salfred{ 858194677Sthompsa struct ums_softc *sc = usb_fifo_softc(fifo); 859184610Salfred mousemode_t mode; 860184610Salfred int error = 0; 861184610Salfred 862184610Salfred DPRINTFN(2, "\n"); 863184610Salfred 864184610Salfred mtx_lock(&sc->sc_mtx); 865184610Salfred 866184610Salfred switch (cmd) { 867184610Salfred case MOUSE_GETHWINFO: 868184610Salfred *(mousehw_t *)addr = sc->sc_hw; 869184610Salfred break; 870184610Salfred 871184610Salfred case MOUSE_GETMODE: 872184610Salfred *(mousemode_t *)addr = sc->sc_mode; 873184610Salfred break; 874184610Salfred 875184610Salfred case MOUSE_SETMODE: 876184610Salfred mode = *(mousemode_t *)addr; 877184610Salfred 878184610Salfred if (mode.level == -1) { 879184610Salfred /* don't change the current setting */ 880184610Salfred } else if ((mode.level < 0) || (mode.level > 1)) { 881184610Salfred error = EINVAL; 882235724Shselasky break; 883184610Salfred } else { 884184610Salfred sc->sc_mode.level = mode.level; 885184610Salfred } 886184610Salfred 887195959Salfred /* store polling rate */ 888195959Salfred sc->sc_pollrate = mode.rate; 889195959Salfred 890184610Salfred if (sc->sc_mode.level == 0) { 891184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 892184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 893184610Salfred else 894184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 895184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 896184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 897184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 898184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 899184610Salfred } else if (sc->sc_mode.level == 1) { 900184610Salfred if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) 901184610Salfred sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; 902184610Salfred else 903184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 904184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 905184610Salfred sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 906184610Salfred sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 907184610Salfred sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 908184610Salfred } 909184610Salfred ums_reset_buf(sc); 910184610Salfred break; 911184610Salfred 912184610Salfred case MOUSE_GETLEVEL: 913184610Salfred *(int *)addr = sc->sc_mode.level; 914184610Salfred break; 915184610Salfred 916184610Salfred case MOUSE_SETLEVEL: 917184610Salfred if (*(int *)addr < 0 || *(int *)addr > 1) { 918184610Salfred error = EINVAL; 919235724Shselasky break; 920184610Salfred } 921184610Salfred sc->sc_mode.level = *(int *)addr; 922184610Salfred 923184610Salfred if (sc->sc_mode.level == 0) { 924184610Salfred if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) 925184610Salfred sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; 926184610Salfred else 927184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 928184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_MSC; 929184610Salfred sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 930184610Salfred sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 931184610Salfred sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 932184610Salfred } else if (sc->sc_mode.level == 1) { 933184610Salfred if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) 934184610Salfred sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; 935184610Salfred else 936184610Salfred sc->sc_hw.buttons = sc->sc_buttons; 937184610Salfred sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 938184610Salfred sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 939184610Salfred sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 940184610Salfred sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 941184610Salfred } 942184610Salfred ums_reset_buf(sc); 943184610Salfred break; 944184610Salfred 945184610Salfred case MOUSE_GETSTATUS:{ 946184610Salfred mousestatus_t *status = (mousestatus_t *)addr; 947184610Salfred 948184610Salfred *status = sc->sc_status; 949184610Salfred sc->sc_status.obutton = sc->sc_status.button; 950184610Salfred sc->sc_status.button = 0; 951184610Salfred sc->sc_status.dx = 0; 952184610Salfred sc->sc_status.dy = 0; 953184610Salfred sc->sc_status.dz = 0; 954184610Salfred /* sc->sc_status.dt = 0; */ 955184610Salfred 956184610Salfred if (status->dx || status->dy || status->dz /* || status->dt */ ) { 957184610Salfred status->flags |= MOUSE_POSCHANGED; 958184610Salfred } 959184610Salfred if (status->button != status->obutton) { 960184610Salfred status->flags |= MOUSE_BUTTONSCHANGED; 961184610Salfred } 962184610Salfred break; 963184610Salfred } 964184610Salfred default: 965184610Salfred error = ENOTTY; 966235724Shselasky break; 967184610Salfred } 968184610Salfred 969184610Salfred mtx_unlock(&sc->sc_mtx); 970184610Salfred return (error); 971184610Salfred} 972184610Salfred 973198373Sthompsastatic int 974198373Sthompsaums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS) 975198373Sthompsa{ 976198373Sthompsa struct ums_softc *sc = arg1; 977198373Sthompsa struct ums_info *info; 978198373Sthompsa struct sbuf *sb; 979219848Shselasky int i, j, err, had_output; 980198373Sthompsa 981198373Sthompsa sb = sbuf_new_auto(); 982219848Shselasky for (i = 0, had_output = 0; i < UMS_INFO_MAX; i++) { 983198373Sthompsa info = &sc->sc_info[i]; 984198373Sthompsa 985198373Sthompsa /* Don't emit empty info */ 986198373Sthompsa if ((info->sc_flags & 987198373Sthompsa (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS | 988198373Sthompsa UMS_FLAG_T_AXIS | UMS_FLAG_W_AXIS)) == 0 && 989198373Sthompsa info->sc_buttons == 0) 990198373Sthompsa continue; 991198373Sthompsa 992219848Shselasky if (had_output) 993219848Shselasky sbuf_printf(sb, "\n"); 994219848Shselasky had_output = 1; 995198373Sthompsa sbuf_printf(sb, "i%d:", i + 1); 996198373Sthompsa if (info->sc_flags & UMS_FLAG_X_AXIS) 997198373Sthompsa sbuf_printf(sb, " X:r%d, p%d, s%d;", 998198373Sthompsa (int)info->sc_iid_x, 999198373Sthompsa (int)info->sc_loc_x.pos, 1000198373Sthompsa (int)info->sc_loc_x.size); 1001198373Sthompsa if (info->sc_flags & UMS_FLAG_Y_AXIS) 1002198373Sthompsa sbuf_printf(sb, " Y:r%d, p%d, s%d;", 1003198373Sthompsa (int)info->sc_iid_y, 1004198373Sthompsa (int)info->sc_loc_y.pos, 1005198373Sthompsa (int)info->sc_loc_y.size); 1006198373Sthompsa if (info->sc_flags & UMS_FLAG_Z_AXIS) 1007198373Sthompsa sbuf_printf(sb, " Z:r%d, p%d, s%d;", 1008198373Sthompsa (int)info->sc_iid_z, 1009198373Sthompsa (int)info->sc_loc_z.pos, 1010198373Sthompsa (int)info->sc_loc_z.size); 1011198373Sthompsa if (info->sc_flags & UMS_FLAG_T_AXIS) 1012198373Sthompsa sbuf_printf(sb, " T:r%d, p%d, s%d;", 1013198373Sthompsa (int)info->sc_iid_t, 1014198373Sthompsa (int)info->sc_loc_t.pos, 1015198373Sthompsa (int)info->sc_loc_t.size); 1016198373Sthompsa if (info->sc_flags & UMS_FLAG_W_AXIS) 1017198373Sthompsa sbuf_printf(sb, " W:r%d, p%d, s%d;", 1018198373Sthompsa (int)info->sc_iid_w, 1019198373Sthompsa (int)info->sc_loc_w.pos, 1020198373Sthompsa (int)info->sc_loc_w.size); 1021198373Sthompsa 1022198373Sthompsa for (j = 0; j < info->sc_buttons; j++) { 1023198373Sthompsa sbuf_printf(sb, " B%d:r%d, p%d, s%d;", j + 1, 1024198373Sthompsa (int)info->sc_iid_btn[j], 1025198373Sthompsa (int)info->sc_loc_btn[j].pos, 1026198373Sthompsa (int)info->sc_loc_btn[j].size); 1027198373Sthompsa } 1028198373Sthompsa } 1029198373Sthompsa sbuf_finish(sb); 1030198373Sthompsa err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1); 1031198373Sthompsa sbuf_delete(sb); 1032198373Sthompsa 1033198373Sthompsa return (err); 1034198373Sthompsa} 1035198373Sthompsa 1036184610Salfredstatic devclass_t ums_devclass; 1037184610Salfred 1038184610Salfredstatic device_method_t ums_methods[] = { 1039184610Salfred DEVMETHOD(device_probe, ums_probe), 1040184610Salfred DEVMETHOD(device_attach, ums_attach), 1041184610Salfred DEVMETHOD(device_detach, ums_detach), 1042184610Salfred {0, 0} 1043184610Salfred}; 1044184610Salfred 1045184610Salfredstatic driver_t ums_driver = { 1046184610Salfred .name = "ums", 1047184610Salfred .methods = ums_methods, 1048184610Salfred .size = sizeof(struct ums_softc), 1049184610Salfred}; 1050184610Salfred 1051189275SthompsaDRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0); 1052188942SthompsaMODULE_DEPEND(ums, usb, 1, 1, 1); 1053212122SthompsaMODULE_VERSION(ums, 1); 1054