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