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