ums.c revision 207077
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: head/sys/dev/usb/input/ums.c 207077 2010-04-22 21:31:34Z thompsa $");
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/linker_set.h>
47194677Sthompsa#include <sys/module.h>
48194677Sthompsa#include <sys/lock.h>
49194677Sthompsa#include <sys/mutex.h>
50194677Sthompsa#include <sys/condvar.h>
51194677Sthompsa#include <sys/sysctl.h>
52194677Sthompsa#include <sys/sx.h>
53194677Sthompsa#include <sys/unistd.h>
54194677Sthompsa#include <sys/callout.h>
55194677Sthompsa#include <sys/malloc.h>
56194677Sthompsa#include <sys/priv.h>
57194677Sthompsa#include <sys/conf.h>
58194677Sthompsa#include <sys/fcntl.h>
59198373Sthompsa#include <sys/sbuf.h>
60194677Sthompsa
61188942Sthompsa#include <dev/usb/usb.h>
62194677Sthompsa#include <dev/usb/usbdi.h>
63194677Sthompsa#include <dev/usb/usbdi_util.h>
64188942Sthompsa#include <dev/usb/usbhid.h>
65194677Sthompsa#include "usbdevs.h"
66184610Salfred
67184610Salfred#define	USB_DEBUG_VAR ums_debug
68188942Sthompsa#include <dev/usb/usb_debug.h>
69184610Salfred
70188942Sthompsa#include <dev/usb/quirk/usb_quirk.h>
71184610Salfred
72184610Salfred#include <sys/ioccom.h>
73184610Salfred#include <sys/filio.h>
74184610Salfred#include <sys/tty.h>
75184610Salfred#include <sys/mouse.h>
76184610Salfred
77207077Sthompsa#ifdef USB_DEBUG
78184610Salfredstatic int ums_debug = 0;
79184610Salfred
80192502SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
81192502SthompsaSYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
82184610Salfred    &ums_debug, 0, "Debug level");
83184610Salfred#endif
84184610Salfred
85184610Salfred#define	MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
86184610Salfred#define	MOUSE_FLAGS (HIO_RELATIVE)
87184610Salfred
88184610Salfred#define	UMS_BUF_SIZE      8		/* bytes */
89184610Salfred#define	UMS_IFQ_MAXLEN   50		/* units */
90184610Salfred#define	UMS_BUTTON_MAX   31		/* exclusive, must be less than 32 */
91184610Salfred#define	UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
92190741Sthompsa#define	UMS_INFO_MAX	  2		/* maximum number of HID sets */
93184610Salfred
94187259Sthompsaenum {
95187259Sthompsa	UMS_INTR_DT,
96188981Sthompsa	UMS_N_TRANSFER,
97187259Sthompsa};
98187259Sthompsa
99190741Sthompsastruct ums_info {
100184610Salfred	struct hid_location sc_loc_w;
101184610Salfred	struct hid_location sc_loc_x;
102184610Salfred	struct hid_location sc_loc_y;
103184610Salfred	struct hid_location sc_loc_z;
104184610Salfred	struct hid_location sc_loc_t;
105184610Salfred	struct hid_location sc_loc_btn[UMS_BUTTON_MAX];
106184610Salfred
107184610Salfred	uint32_t sc_flags;
108184610Salfred#define	UMS_FLAG_X_AXIS     0x0001
109184610Salfred#define	UMS_FLAG_Y_AXIS     0x0002
110184610Salfred#define	UMS_FLAG_Z_AXIS     0x0004
111184610Salfred#define	UMS_FLAG_T_AXIS     0x0008
112184610Salfred#define	UMS_FLAG_SBU        0x0010	/* spurious button up events */
113188981Sthompsa#define	UMS_FLAG_REVZ	    0x0020	/* Z-axis is reversed */
114188981Sthompsa#define	UMS_FLAG_W_AXIS     0x0040
115184610Salfred
116189583Sthompsa	uint8_t	sc_iid_w;
117189583Sthompsa	uint8_t	sc_iid_x;
118189583Sthompsa	uint8_t	sc_iid_y;
119189583Sthompsa	uint8_t	sc_iid_z;
120189583Sthompsa	uint8_t	sc_iid_t;
121189583Sthompsa	uint8_t	sc_iid_btn[UMS_BUTTON_MAX];
122190741Sthompsa	uint8_t	sc_buttons;
123190741Sthompsa};
124190741Sthompsa
125190741Sthompsastruct ums_softc {
126192984Sthompsa	struct usb_fifo_sc sc_fifo;
127190741Sthompsa	struct mtx sc_mtx;
128192984Sthompsa	struct usb_callout sc_callout;
129190741Sthompsa	struct ums_info sc_info[UMS_INFO_MAX];
130190741Sthompsa
131190741Sthompsa	mousehw_t sc_hw;
132190741Sthompsa	mousemode_t sc_mode;
133190741Sthompsa	mousestatus_t sc_status;
134190741Sthompsa
135192984Sthompsa	struct usb_xfer *sc_xfer[UMS_N_TRANSFER];
136190741Sthompsa
137195959Salfred	int sc_pollrate;
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
205184610Salfred		if (len > 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
289184610Salfred			sc->sc_status.button = buttons;
290184610Salfred			sc->sc_status.dx += dx;
291184610Salfred			sc->sc_status.dy += dy;
292184610Salfred			sc->sc_status.dz += dz;
293184610Salfred			/*
294184610Salfred			 * sc->sc_status.dt += dt;
295184610Salfred			 * no way to export this yet
296184610Salfred			 */
297184610Salfred
298184610Salfred			/*
299188981Sthompsa		         * The Qtronix keyboard has a built in PS/2
300188981Sthompsa		         * port for a mouse.  The firmware once in a
301188981Sthompsa		         * while posts a spurious button up
302188981Sthompsa		         * event. This event we ignore by doing a
303188981Sthompsa		         * timeout for 50 msecs.  If we receive
304188981Sthompsa		         * dx=dy=dz=buttons=0 before we add the event
305188981Sthompsa		         * to the queue.  In any other case we delete
306188981Sthompsa		         * the timeout event.
307184610Salfred		         */
308190741Sthompsa			if ((sc->sc_info[0].sc_flags & UMS_FLAG_SBU) &&
309184610Salfred			    (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
310184610Salfred			    (dw == 0) && (buttons == 0)) {
311184610Salfred
312194228Sthompsa				usb_callout_reset(&sc->sc_callout, hz / 20,
313184610Salfred				    &ums_put_queue_timeout, sc);
314184610Salfred			} else {
315184610Salfred
316194228Sthompsa				usb_callout_stop(&sc->sc_callout);
317184610Salfred
318184610Salfred				ums_put_queue(sc, dx, dy, dz, dt, buttons);
319184610Salfred			}
320184610Salfred		}
321184610Salfred	case USB_ST_SETUP:
322184610Salfredtr_setup:
323188981Sthompsa		/* check if we can put more data into the FIFO */
324194228Sthompsa		if (usb_fifo_put_bytes_max(
325188981Sthompsa		    sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
326194677Sthompsa			usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
327194228Sthompsa			usbd_transfer_submit(xfer);
328184610Salfred		}
329188981Sthompsa		break;
330184610Salfred
331184610Salfred	default:			/* Error */
332194677Sthompsa		if (error != USB_ERR_CANCELLED) {
333188981Sthompsa			/* try clear stall first */
334194677Sthompsa			usbd_xfer_set_stall(xfer);
335188981Sthompsa			goto tr_setup;
336184610Salfred		}
337188981Sthompsa		break;
338184610Salfred	}
339184610Salfred}
340184610Salfred
341192984Sthompsastatic const struct usb_config ums_config[UMS_N_TRANSFER] = {
342184610Salfred
343187259Sthompsa	[UMS_INTR_DT] = {
344184610Salfred		.type = UE_INTERRUPT,
345184610Salfred		.endpoint = UE_ADDR_ANY,
346184610Salfred		.direction = UE_DIR_IN,
347190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
348190734Sthompsa		.bufsize = 0,	/* use wMaxPacketSize */
349190734Sthompsa		.callback = &ums_intr_callback,
350184610Salfred	},
351184610Salfred};
352184610Salfred
353184610Salfredstatic int
354184610Salfredums_probe(device_t dev)
355184610Salfred{
356192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
357184610Salfred	void *d_ptr;
358188981Sthompsa	int error;
359184610Salfred	uint16_t d_len;
360184610Salfred
361184610Salfred	DPRINTFN(11, "\n");
362184610Salfred
363192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
364184610Salfred		return (ENXIO);
365188981Sthompsa
366194068Sthompsa	if (uaa->info.bInterfaceClass != UICLASS_HID)
367184610Salfred		return (ENXIO);
368188981Sthompsa
369194068Sthompsa	if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
370194068Sthompsa	    (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE))
371199169Snwhitehorn		return (BUS_PROBE_GENERIC);
372194068Sthompsa
373194228Sthompsa	error = usbd_req_get_hid_desc(uaa->device, NULL,
374184610Salfred	    &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
375184610Salfred
376188981Sthompsa	if (error)
377184610Salfred		return (ENXIO);
378188981Sthompsa
379184610Salfred	if (hid_is_collection(d_ptr, d_len,
380188981Sthompsa	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
381199169Snwhitehorn		error = BUS_PROBE_GENERIC;
382188981Sthompsa	else
383184610Salfred		error = ENXIO;
384184610Salfred
385184610Salfred	free(d_ptr, M_TEMP);
386184610Salfred	return (error);
387184610Salfred}
388184610Salfred
389190741Sthompsastatic void
390190741Sthompsaums_hid_parse(struct ums_softc *sc, device_t dev, const uint8_t *buf,
391190741Sthompsa    uint16_t len, uint8_t index)
392184610Salfred{
393190741Sthompsa	struct ums_info *info = &sc->sc_info[index];
394184610Salfred	uint32_t flags;
395184610Salfred	uint8_t i;
396184610Salfred
397190741Sthompsa	if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
398190741Sthompsa	    hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) {
399184610Salfred
400184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
401190741Sthompsa			info->sc_flags |= UMS_FLAG_X_AXIS;
402184610Salfred		}
403184610Salfred	}
404190741Sthompsa	if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
405190741Sthompsa	    hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) {
406184610Salfred
407184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
408190741Sthompsa			info->sc_flags |= UMS_FLAG_Y_AXIS;
409184610Salfred		}
410184610Salfred	}
411184610Salfred	/* Try the wheel first as the Z activator since it's tradition. */
412190741Sthompsa	if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
413190741Sthompsa	    HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags,
414190741Sthompsa	    &info->sc_iid_z) ||
415190741Sthompsa	    hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
416190741Sthompsa	    HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags,
417190741Sthompsa	    &info->sc_iid_z)) {
418184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
419190741Sthompsa			info->sc_flags |= UMS_FLAG_Z_AXIS;
420184610Salfred		}
421184610Salfred		/*
422184610Salfred		 * We might have both a wheel and Z direction, if so put
423184610Salfred		 * put the Z on the W coordinate.
424184610Salfred		 */
425190741Sthompsa		if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
426190741Sthompsa		    HUG_Z), hid_input, index, &info->sc_loc_w, &flags,
427190741Sthompsa		    &info->sc_iid_w)) {
428184610Salfred
429184610Salfred			if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
430190741Sthompsa				info->sc_flags |= UMS_FLAG_W_AXIS;
431184610Salfred			}
432184610Salfred		}
433190741Sthompsa	} else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
434190741Sthompsa	    HUG_Z), hid_input, index, &info->sc_loc_z, &flags,
435190741Sthompsa	    &info->sc_iid_z)) {
436184610Salfred
437184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
438190741Sthompsa			info->sc_flags |= UMS_FLAG_Z_AXIS;
439184610Salfred		}
440184610Salfred	}
441184610Salfred	/*
442184610Salfred	 * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
443184610Salfred	 * using 0x0048, which is HUG_TWHEEL, and seems to expect you
444184610Salfred	 * to know that the byte after the wheel is the tilt axis.
445184610Salfred	 * There are no other HID axis descriptors other than X,Y and
446184610Salfred	 * TWHEEL
447184610Salfred	 */
448190741Sthompsa	if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
449190741Sthompsa	    HUG_TWHEEL), hid_input, index, &info->sc_loc_t,
450190741Sthompsa	    &flags, &info->sc_iid_t)) {
451184610Salfred
452190741Sthompsa		info->sc_loc_t.pos += 8;
453184610Salfred
454184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
455190741Sthompsa			info->sc_flags |= UMS_FLAG_T_AXIS;
456184610Salfred		}
457184610Salfred	}
458184610Salfred	/* figure out the number of buttons */
459184610Salfred
460184610Salfred	for (i = 0; i < UMS_BUTTON_MAX; i++) {
461190741Sthompsa		if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)),
462190741Sthompsa		    hid_input, index, &info->sc_loc_btn[i], NULL,
463190741Sthompsa		    &info->sc_iid_btn[i])) {
464184610Salfred			break;
465184610Salfred		}
466184610Salfred	}
467190741Sthompsa	info->sc_buttons = i;
468184610Salfred
469190741Sthompsa	if (i > sc->sc_buttons)
470190741Sthompsa		sc->sc_buttons = i;
471184610Salfred
472190741Sthompsa	if (info->sc_flags == 0)
473190741Sthompsa		return;
474190741Sthompsa
475190741Sthompsa	/* announce information about the mouse */
476190741Sthompsa	device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n",
477190741Sthompsa	    (info->sc_buttons),
478190741Sthompsa	    (info->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
479190741Sthompsa	    (info->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
480190741Sthompsa	    (info->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
481190741Sthompsa	    (info->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
482190741Sthompsa	    (info->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "",
483190741Sthompsa	    info->sc_iid_x);
484190741Sthompsa}
485190741Sthompsa
486190741Sthompsastatic int
487190741Sthompsaums_attach(device_t dev)
488190741Sthompsa{
489192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
490190741Sthompsa	struct ums_softc *sc = device_get_softc(dev);
491190741Sthompsa	struct ums_info *info;
492190741Sthompsa	void *d_ptr = NULL;
493190741Sthompsa	int isize;
494190741Sthompsa	int err;
495190741Sthompsa	uint16_t d_len;
496190741Sthompsa	uint8_t i;
497207077Sthompsa#ifdef USB_DEBUG
498190741Sthompsa	uint8_t j;
499207077Sthompsa#endif
500190741Sthompsa
501190741Sthompsa	DPRINTFN(11, "sc=%p\n", sc);
502190741Sthompsa
503194228Sthompsa	device_set_usb_desc(dev);
504190741Sthompsa
505190741Sthompsa	mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
506190741Sthompsa
507194228Sthompsa	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
508190741Sthompsa
509190741Sthompsa	/*
510190741Sthompsa         * Force the report (non-boot) protocol.
511190741Sthompsa         *
512190741Sthompsa         * Mice without boot protocol support may choose not to implement
513190741Sthompsa         * Set_Protocol at all; Ignore any error.
514190741Sthompsa         */
515194228Sthompsa	err = usbd_req_set_protocol(uaa->device, NULL,
516190741Sthompsa	    uaa->info.bIfaceIndex, 1);
517190741Sthompsa
518194228Sthompsa	err = usbd_transfer_setup(uaa->device,
519190741Sthompsa	    &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
520190741Sthompsa	    UMS_N_TRANSFER, sc, &sc->sc_mtx);
521190741Sthompsa
522190741Sthompsa	if (err) {
523194228Sthompsa		DPRINTF("error=%s\n", usbd_errstr(err));
524190741Sthompsa		goto detach;
525190741Sthompsa	}
526195959Salfred
527195959Salfred	/* Get HID descriptor */
528194228Sthompsa	err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
529190741Sthompsa	    &d_len, M_TEMP, uaa->info.bIfaceIndex);
530190741Sthompsa
531190741Sthompsa	if (err) {
532190741Sthompsa		device_printf(dev, "error reading report description\n");
533190741Sthompsa		goto detach;
534190741Sthompsa	}
535190741Sthompsa
536189583Sthompsa	isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid);
537184610Salfred
538184610Salfred	/*
539184610Salfred	 * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
540184610Salfred	 * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
541184610Salfred	 * all of its other button positions are all off. It also reports that
542184610Salfred	 * it has two addional buttons and a tilt wheel.
543184610Salfred	 */
544194228Sthompsa	if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
545195959Salfred
546195959Salfred		sc->sc_iid = 0;
547195959Salfred
548190741Sthompsa		info = &sc->sc_info[0];
549190741Sthompsa		info->sc_flags = (UMS_FLAG_X_AXIS |
550184610Salfred		    UMS_FLAG_Y_AXIS |
551184610Salfred		    UMS_FLAG_Z_AXIS |
552184610Salfred		    UMS_FLAG_SBU);
553190741Sthompsa		info->sc_buttons = 3;
554184610Salfred		isize = 5;
555184610Salfred		/* 1st byte of descriptor report contains garbage */
556190741Sthompsa		info->sc_loc_x.pos = 16;
557195959Salfred		info->sc_loc_x.size = 8;
558190741Sthompsa		info->sc_loc_y.pos = 24;
559195959Salfred		info->sc_loc_y.size = 8;
560190741Sthompsa		info->sc_loc_z.pos = 32;
561195959Salfred		info->sc_loc_z.size = 8;
562190741Sthompsa		info->sc_loc_btn[0].pos = 8;
563195959Salfred		info->sc_loc_btn[0].size = 1;
564190741Sthompsa		info->sc_loc_btn[1].pos = 9;
565195959Salfred		info->sc_loc_btn[1].size = 1;
566190741Sthompsa		info->sc_loc_btn[2].pos = 10;
567195959Salfred		info->sc_loc_btn[2].size = 1;
568188981Sthompsa
569190741Sthompsa		/* Announce device */
570190741Sthompsa		device_printf(dev, "3 buttons and [XYZ] "
571190741Sthompsa		    "coordinates ID=0\n");
572190741Sthompsa
573190741Sthompsa	} else {
574190741Sthompsa		/* Search the HID descriptor and announce device */
575190741Sthompsa		for (i = 0; i < UMS_INFO_MAX; i++) {
576190741Sthompsa			ums_hid_parse(sc, dev, d_ptr, d_len, i);
577190741Sthompsa		}
578184610Salfred	}
579188981Sthompsa
580194228Sthompsa	if (usb_test_quirk(uaa, UQ_MS_REVZ)) {
581190741Sthompsa		info = &sc->sc_info[0];
582184610Salfred		/* Some wheels need the Z axis reversed. */
583190741Sthompsa		info->sc_flags |= UMS_FLAG_REVZ;
584184610Salfred	}
585194677Sthompsa	if (isize > usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])) {
586184610Salfred		DPRINTF("WARNING: report size, %d bytes, is larger "
587194677Sthompsa		    "than interrupt size, %d bytes!\n", isize,
588194677Sthompsa		    usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT]));
589184610Salfred	}
590184610Salfred	free(d_ptr, M_TEMP);
591184610Salfred	d_ptr = NULL;
592184610Salfred
593207077Sthompsa#ifdef USB_DEBUG
594190741Sthompsa	for (j = 0; j < UMS_INFO_MAX; j++) {
595190741Sthompsa		info = &sc->sc_info[j];
596184610Salfred
597190741Sthompsa		DPRINTF("sc=%p, index=%d\n", sc, j);
598190741Sthompsa		DPRINTF("X\t%d/%d id=%d\n", info->sc_loc_x.pos,
599190741Sthompsa		    info->sc_loc_x.size, info->sc_iid_x);
600190741Sthompsa		DPRINTF("Y\t%d/%d id=%d\n", info->sc_loc_y.pos,
601190741Sthompsa		    info->sc_loc_y.size, info->sc_iid_y);
602190741Sthompsa		DPRINTF("Z\t%d/%d id=%d\n", info->sc_loc_z.pos,
603190741Sthompsa		    info->sc_loc_z.size, info->sc_iid_z);
604190741Sthompsa		DPRINTF("T\t%d/%d id=%d\n", info->sc_loc_t.pos,
605190741Sthompsa		    info->sc_loc_t.size, info->sc_iid_t);
606190741Sthompsa		DPRINTF("W\t%d/%d id=%d\n", info->sc_loc_w.pos,
607190741Sthompsa		    info->sc_loc_w.size, info->sc_iid_w);
608190741Sthompsa
609190741Sthompsa		for (i = 0; i < info->sc_buttons; i++) {
610190741Sthompsa			DPRINTF("B%d\t%d/%d id=%d\n",
611190741Sthompsa			    i + 1, info->sc_loc_btn[i].pos,
612190741Sthompsa			    info->sc_loc_btn[i].size, info->sc_iid_btn[i]);
613190741Sthompsa		}
614184610Salfred	}
615184610Salfred	DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
616184610Salfred#endif
617184610Salfred
618184610Salfred	if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
619184610Salfred		sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
620184610Salfred	else
621184610Salfred		sc->sc_hw.buttons = sc->sc_buttons;
622184610Salfred
623184610Salfred	sc->sc_hw.iftype = MOUSE_IF_USB;
624184610Salfred	sc->sc_hw.type = MOUSE_MOUSE;
625184610Salfred	sc->sc_hw.model = MOUSE_MODEL_GENERIC;
626184610Salfred	sc->sc_hw.hwid = 0;
627184610Salfred
628184610Salfred	sc->sc_mode.protocol = MOUSE_PROTO_MSC;
629184610Salfred	sc->sc_mode.rate = -1;
630184610Salfred	sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
631184610Salfred	sc->sc_mode.accelfactor = 0;
632184610Salfred	sc->sc_mode.level = 0;
633184610Salfred	sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
634184610Salfred	sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
635184610Salfred	sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
636184610Salfred
637194228Sthompsa	err = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
638184610Salfred	    &ums_fifo_methods, &sc->sc_fifo,
639190741Sthompsa	    device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
640189110Sthompsa  	    UID_ROOT, GID_OPERATOR, 0644);
641184610Salfred	if (err) {
642184610Salfred		goto detach;
643184610Salfred	}
644198373Sthompsa	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
645198373Sthompsa	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
646198373Sthompsa	    OID_AUTO, "parseinfo", CTLTYPE_STRING|CTLFLAG_RD,
647198373Sthompsa	    sc, 0, ums_sysctl_handler_parseinfo,
648198373Sthompsa	    "", "Dump UMS report parsing information");
649198373Sthompsa
650184610Salfred	return (0);
651184610Salfred
652184610Salfreddetach:
653184610Salfred	if (d_ptr) {
654184610Salfred		free(d_ptr, M_TEMP);
655184610Salfred	}
656184610Salfred	ums_detach(dev);
657184610Salfred	return (ENOMEM);
658184610Salfred}
659184610Salfred
660184610Salfredstatic int
661184610Salfredums_detach(device_t self)
662184610Salfred{
663184610Salfred	struct ums_softc *sc = device_get_softc(self);
664184610Salfred
665184610Salfred	DPRINTF("sc=%p\n", sc);
666184610Salfred
667194228Sthompsa	usb_fifo_detach(&sc->sc_fifo);
668184610Salfred
669194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
670184610Salfred
671194228Sthompsa	usb_callout_drain(&sc->sc_callout);
672184610Salfred
673184610Salfred	mtx_destroy(&sc->sc_mtx);
674184610Salfred
675184610Salfred	return (0);
676184610Salfred}
677184610Salfred
678184610Salfredstatic void
679192984Sthompsaums_start_read(struct usb_fifo *fifo)
680184610Salfred{
681194677Sthompsa	struct ums_softc *sc = usb_fifo_softc(fifo);
682195959Salfred	int rate;
683184610Salfred
684195959Salfred	/* Check if we should override the default polling interval */
685195959Salfred	rate = sc->sc_pollrate;
686195959Salfred	/* Range check rate */
687195959Salfred	if (rate > 1000)
688195959Salfred		rate = 1000;
689195959Salfred	/* Check for set rate */
690195959Salfred	if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) {
691195959Salfred		DPRINTF("Setting pollrate = %d\n", rate);
692195959Salfred		/* Stop current transfer, if any */
693195959Salfred		usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
694195959Salfred		/* Set new interval */
695195959Salfred		usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate);
696195959Salfred		/* Only set pollrate once */
697195959Salfred		sc->sc_pollrate = 0;
698195959Salfred	}
699195959Salfred
700194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
701184610Salfred}
702184610Salfred
703184610Salfredstatic void
704192984Sthompsaums_stop_read(struct usb_fifo *fifo)
705184610Salfred{
706194677Sthompsa	struct ums_softc *sc = usb_fifo_softc(fifo);
707184610Salfred
708194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
709194228Sthompsa	usb_callout_stop(&sc->sc_callout);
710184610Salfred}
711184610Salfred
712184610Salfred
713184610Salfred#if ((MOUSE_SYS_PACKETSIZE != 8) || \
714184610Salfred     (MOUSE_MSC_PACKETSIZE != 5))
715184610Salfred#error "Software assumptions are not met. Please update code."
716184610Salfred#endif
717184610Salfred
718184610Salfredstatic void
719184610Salfredums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
720184610Salfred    int32_t dz, int32_t dt, int32_t buttons)
721184610Salfred{
722184610Salfred	uint8_t buf[8];
723184610Salfred
724184610Salfred	if (1) {
725184610Salfred
726184610Salfred		if (dx > 254)
727184610Salfred			dx = 254;
728184610Salfred		if (dx < -256)
729184610Salfred			dx = -256;
730184610Salfred		if (dy > 254)
731184610Salfred			dy = 254;
732184610Salfred		if (dy < -256)
733184610Salfred			dy = -256;
734184610Salfred		if (dz > 126)
735184610Salfred			dz = 126;
736184610Salfred		if (dz < -128)
737184610Salfred			dz = -128;
738184610Salfred		if (dt > 126)
739184610Salfred			dt = 126;
740184610Salfred		if (dt < -128)
741184610Salfred			dt = -128;
742184610Salfred
743184610Salfred		buf[0] = sc->sc_mode.syncmask[1];
744184610Salfred		buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
745184610Salfred		buf[1] = dx >> 1;
746184610Salfred		buf[2] = dy >> 1;
747184610Salfred		buf[3] = dx - (dx >> 1);
748184610Salfred		buf[4] = dy - (dy >> 1);
749184610Salfred
750184610Salfred		if (sc->sc_mode.level == 1) {
751184610Salfred			buf[5] = dz >> 1;
752184610Salfred			buf[6] = dz - (dz >> 1);
753184610Salfred			buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
754184610Salfred		}
755194228Sthompsa		usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
756184610Salfred		    sc->sc_mode.packetsize, 1);
757184610Salfred
758184610Salfred	} else {
759184610Salfred		DPRINTF("Buffer full, discarded packet\n");
760184610Salfred	}
761184610Salfred}
762184610Salfred
763184610Salfredstatic void
764184610Salfredums_reset_buf(struct ums_softc *sc)
765184610Salfred{
766184610Salfred	/* reset read queue */
767194228Sthompsa	usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
768184610Salfred}
769184610Salfred
770184610Salfredstatic int
771192984Sthompsaums_open(struct usb_fifo *fifo, int fflags)
772184610Salfred{
773194677Sthompsa	struct ums_softc *sc = usb_fifo_softc(fifo);
774184610Salfred
775184610Salfred	DPRINTFN(2, "\n");
776184610Salfred
777184610Salfred	if (fflags & FREAD) {
778184610Salfred
779184610Salfred		/* reset status */
780184610Salfred
781184610Salfred		sc->sc_status.flags = 0;
782184610Salfred		sc->sc_status.button = 0;
783184610Salfred		sc->sc_status.obutton = 0;
784184610Salfred		sc->sc_status.dx = 0;
785184610Salfred		sc->sc_status.dy = 0;
786184610Salfred		sc->sc_status.dz = 0;
787184610Salfred		/* sc->sc_status.dt = 0; */
788184610Salfred
789194228Sthompsa		if (usb_fifo_alloc_buffer(fifo,
790184610Salfred		    UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
791184610Salfred			return (ENOMEM);
792184610Salfred		}
793184610Salfred	}
794184610Salfred	return (0);
795184610Salfred}
796184610Salfred
797184610Salfredstatic void
798192984Sthompsaums_close(struct usb_fifo *fifo, int fflags)
799184610Salfred{
800184610Salfred	if (fflags & FREAD) {
801194228Sthompsa		usb_fifo_free_buffer(fifo);
802184610Salfred	}
803184610Salfred}
804184610Salfred
805184610Salfredstatic int
806192984Sthompsaums_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
807184610Salfred{
808194677Sthompsa	struct ums_softc *sc = usb_fifo_softc(fifo);
809184610Salfred	mousemode_t mode;
810184610Salfred	int error = 0;
811184610Salfred
812184610Salfred	DPRINTFN(2, "\n");
813184610Salfred
814184610Salfred	mtx_lock(&sc->sc_mtx);
815184610Salfred
816184610Salfred	switch (cmd) {
817184610Salfred	case MOUSE_GETHWINFO:
818184610Salfred		*(mousehw_t *)addr = sc->sc_hw;
819184610Salfred		break;
820184610Salfred
821184610Salfred	case MOUSE_GETMODE:
822184610Salfred		*(mousemode_t *)addr = sc->sc_mode;
823184610Salfred		break;
824184610Salfred
825184610Salfred	case MOUSE_SETMODE:
826184610Salfred		mode = *(mousemode_t *)addr;
827184610Salfred
828184610Salfred		if (mode.level == -1) {
829184610Salfred			/* don't change the current setting */
830184610Salfred		} else if ((mode.level < 0) || (mode.level > 1)) {
831184610Salfred			error = EINVAL;
832184610Salfred			goto done;
833184610Salfred		} else {
834184610Salfred			sc->sc_mode.level = mode.level;
835184610Salfred		}
836184610Salfred
837195959Salfred		/* store polling rate */
838195959Salfred		sc->sc_pollrate = mode.rate;
839195959Salfred
840184610Salfred		if (sc->sc_mode.level == 0) {
841184610Salfred			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
842184610Salfred				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
843184610Salfred			else
844184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
845184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
846184610Salfred			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
847184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
848184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
849184610Salfred		} else if (sc->sc_mode.level == 1) {
850184610Salfred			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
851184610Salfred				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
852184610Salfred			else
853184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
854184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
855184610Salfred			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
856184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
857184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
858184610Salfred		}
859184610Salfred		ums_reset_buf(sc);
860184610Salfred		break;
861184610Salfred
862184610Salfred	case MOUSE_GETLEVEL:
863184610Salfred		*(int *)addr = sc->sc_mode.level;
864184610Salfred		break;
865184610Salfred
866184610Salfred	case MOUSE_SETLEVEL:
867184610Salfred		if (*(int *)addr < 0 || *(int *)addr > 1) {
868184610Salfred			error = EINVAL;
869184610Salfred			goto done;
870184610Salfred		}
871184610Salfred		sc->sc_mode.level = *(int *)addr;
872184610Salfred
873184610Salfred		if (sc->sc_mode.level == 0) {
874184610Salfred			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
875184610Salfred				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
876184610Salfred			else
877184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
878184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
879184610Salfred			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
880184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
881184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
882184610Salfred		} else if (sc->sc_mode.level == 1) {
883184610Salfred			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
884184610Salfred				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
885184610Salfred			else
886184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
887184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
888184610Salfred			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
889184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
890184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
891184610Salfred		}
892184610Salfred		ums_reset_buf(sc);
893184610Salfred		break;
894184610Salfred
895184610Salfred	case MOUSE_GETSTATUS:{
896184610Salfred			mousestatus_t *status = (mousestatus_t *)addr;
897184610Salfred
898184610Salfred			*status = sc->sc_status;
899184610Salfred			sc->sc_status.obutton = sc->sc_status.button;
900184610Salfred			sc->sc_status.button = 0;
901184610Salfred			sc->sc_status.dx = 0;
902184610Salfred			sc->sc_status.dy = 0;
903184610Salfred			sc->sc_status.dz = 0;
904184610Salfred			/* sc->sc_status.dt = 0; */
905184610Salfred
906184610Salfred			if (status->dx || status->dy || status->dz /* || status->dt */ ) {
907184610Salfred				status->flags |= MOUSE_POSCHANGED;
908184610Salfred			}
909184610Salfred			if (status->button != status->obutton) {
910184610Salfred				status->flags |= MOUSE_BUTTONSCHANGED;
911184610Salfred			}
912184610Salfred			break;
913184610Salfred		}
914184610Salfred	default:
915184610Salfred		error = ENOTTY;
916184610Salfred	}
917184610Salfred
918184610Salfreddone:
919184610Salfred	mtx_unlock(&sc->sc_mtx);
920184610Salfred	return (error);
921184610Salfred}
922184610Salfred
923198373Sthompsastatic int
924198373Sthompsaums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS)
925198373Sthompsa{
926198373Sthompsa	struct ums_softc *sc = arg1;
927198373Sthompsa	struct ums_info *info;
928198373Sthompsa	struct sbuf *sb;
929198373Sthompsa	int i, j, err;
930198373Sthompsa
931198373Sthompsa	sb = sbuf_new_auto();
932198373Sthompsa	for (i = 0; i < UMS_INFO_MAX; i++) {
933198373Sthompsa		info = &sc->sc_info[i];
934198373Sthompsa
935198373Sthompsa		/* Don't emit empty info */
936198373Sthompsa		if ((info->sc_flags &
937198373Sthompsa		    (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS |
938198373Sthompsa		     UMS_FLAG_T_AXIS | UMS_FLAG_W_AXIS)) == 0 &&
939198373Sthompsa		    info->sc_buttons == 0)
940198373Sthompsa			continue;
941198373Sthompsa
942198373Sthompsa		sbuf_printf(sb, "i%d:", i + 1);
943198373Sthompsa		if (info->sc_flags & UMS_FLAG_X_AXIS)
944198373Sthompsa			sbuf_printf(sb, " X:r%d, p%d, s%d;",
945198373Sthompsa			    (int)info->sc_iid_x,
946198373Sthompsa			    (int)info->sc_loc_x.pos,
947198373Sthompsa			    (int)info->sc_loc_x.size);
948198373Sthompsa		if (info->sc_flags & UMS_FLAG_Y_AXIS)
949198373Sthompsa			sbuf_printf(sb, " Y:r%d, p%d, s%d;",
950198373Sthompsa			    (int)info->sc_iid_y,
951198373Sthompsa			    (int)info->sc_loc_y.pos,
952198373Sthompsa			    (int)info->sc_loc_y.size);
953198373Sthompsa		if (info->sc_flags & UMS_FLAG_Z_AXIS)
954198373Sthompsa			sbuf_printf(sb, " Z:r%d, p%d, s%d;",
955198373Sthompsa			    (int)info->sc_iid_z,
956198373Sthompsa			    (int)info->sc_loc_z.pos,
957198373Sthompsa			    (int)info->sc_loc_z.size);
958198373Sthompsa		if (info->sc_flags & UMS_FLAG_T_AXIS)
959198373Sthompsa			sbuf_printf(sb, " T:r%d, p%d, s%d;",
960198373Sthompsa			    (int)info->sc_iid_t,
961198373Sthompsa			    (int)info->sc_loc_t.pos,
962198373Sthompsa			    (int)info->sc_loc_t.size);
963198373Sthompsa		if (info->sc_flags & UMS_FLAG_W_AXIS)
964198373Sthompsa			sbuf_printf(sb, " W:r%d, p%d, s%d;",
965198373Sthompsa			    (int)info->sc_iid_w,
966198373Sthompsa			    (int)info->sc_loc_w.pos,
967198373Sthompsa			    (int)info->sc_loc_w.size);
968198373Sthompsa
969198373Sthompsa		for (j = 0; j < info->sc_buttons; j++) {
970198373Sthompsa			sbuf_printf(sb, " B%d:r%d, p%d, s%d;", j + 1,
971198373Sthompsa			    (int)info->sc_iid_btn[j],
972198373Sthompsa			    (int)info->sc_loc_btn[j].pos,
973198373Sthompsa			    (int)info->sc_loc_btn[j].size);
974198373Sthompsa		}
975198373Sthompsa		sbuf_printf(sb, "\n");
976198373Sthompsa	}
977198373Sthompsa	sbuf_finish(sb);
978198373Sthompsa	err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
979198373Sthompsa	sbuf_delete(sb);
980198373Sthompsa
981198373Sthompsa	return (err);
982198373Sthompsa}
983198373Sthompsa
984184610Salfredstatic devclass_t ums_devclass;
985184610Salfred
986184610Salfredstatic device_method_t ums_methods[] = {
987184610Salfred	DEVMETHOD(device_probe, ums_probe),
988184610Salfred	DEVMETHOD(device_attach, ums_attach),
989184610Salfred	DEVMETHOD(device_detach, ums_detach),
990184610Salfred	{0, 0}
991184610Salfred};
992184610Salfred
993184610Salfredstatic driver_t ums_driver = {
994184610Salfred	.name = "ums",
995184610Salfred	.methods = ums_methods,
996184610Salfred	.size = sizeof(struct ums_softc),
997184610Salfred};
998184610Salfred
999189275SthompsaDRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0);
1000188942SthompsaMODULE_DEPEND(ums, usb, 1, 1, 1);
1001