xref: /openbsd/sys/dev/hid/hidms.c (revision 3bef86f7)
1 /*	$OpenBSD: hidms.c,v 1.10 2023/08/12 20:47:06 miod Exp $ */
2 /*	$NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $	*/
3 
4 /*
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (lennart@augustsson.net) at
10  * Carlstedt Research & Technology.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/device.h>
42 #include <sys/ioctl.h>
43 
44 #include <dev/wscons/wsconsio.h>
45 #include <dev/wscons/wsmousevar.h>
46 
47 #include <dev/hid/hid.h>
48 #include <dev/hid/hidmsvar.h>
49 
50 #ifdef HIDMS_DEBUG
51 #define DPRINTF(x)	do { if (hidmsdebug) printf x; } while (0)
52 #define DPRINTFN(n,x)	do { if (hidmsdebug>(n)) printf x; } while (0)
53 int	hidmsdebug = 0;
54 #else
55 #define DPRINTF(x)
56 #define DPRINTFN(n,x)
57 #endif
58 
59 #define HIDMS_BUT(i)	((i) == 1 || (i) == 2 ? 3 - (i) : i)
60 
61 #define MOUSE_FLAGS_MASK	(HIO_CONST | HIO_RELATIVE)
62 #define NOTMOUSE(f)		(((f) & MOUSE_FLAGS_MASK) != HIO_RELATIVE)
63 
64 void
65 hidms_stylus_hid_parse(struct hidms *ms, struct hid_data *d,
66     struct hid_location *loc_stylus_btn)
67 {
68 	struct hid_item h;
69 
70 	while (hid_get_item(d, &h)) {
71 		if (h.kind == hid_endcollection)
72 			break;
73 		if (h.kind != hid_input || (h.flags & HIO_CONST) != 0)
74 			continue;
75 		/* All the possible stylus reported usages go here */
76 #ifdef HIDMS_DEBUG
77 		printf("stylus usage: 0x%x\n", h.usage);
78 #endif
79 		switch (h.usage) {
80 		/* Buttons */
81 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TIP_SWITCH):
82 			DPRINTF("Stylus usage tip set\n");
83 			if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
84 				break;
85 			loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
86 			ms->sc_flags |= HIDMS_TIP;
87 			break;
88 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_BARREL_SWITCH):
89 			DPRINTF("Stylus usage barrel set\n");
90 			if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
91 				break;
92 			loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
93 			ms->sc_flags |= HIDMS_BARREL;
94 			break;
95 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS,
96 		    HUD_SECONDARY_BARREL_SWITCH):
97 			DPRINTF("Stylus usage secondary barrel set\n");
98 			if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
99 				break;
100 			loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
101 			ms->sc_flags |= HIDMS_SEC_BARREL;
102 			break;
103 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_IN_RANGE):
104 			DPRINTF("Stylus usage in range set\n");
105 			if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
106 				break;
107 			loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
108 			break;
109 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_QUALITY):
110 			DPRINTF("Stylus usage quality set\n");
111 			if (ms->sc_num_stylus_buttons >= MAX_BUTTONS)
112 				break;
113 			loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc;
114 			break;
115 		/* Axes */
116 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_X):
117 			DPRINTF("Stylus usage x set\n");
118 			ms->sc_loc_x = h.loc;
119 			ms->sc_tsscale.minx = h.logical_minimum;
120 			ms->sc_tsscale.maxx = h.logical_maximum;
121 			ms->sc_flags |= HIDMS_ABSX;
122 			break;
123 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_Y):
124 			DPRINTF("Stylus usage y set\n");
125 			ms->sc_loc_y = h.loc;
126 			ms->sc_tsscale.miny = h.logical_minimum;
127 			ms->sc_tsscale.maxy = h.logical_maximum;
128 			ms->sc_flags |= HIDMS_ABSY;
129 			break;
130 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TIP_PRESSURE):
131 			DPRINTF("Stylus usage pressure set\n");
132 			ms->sc_loc_z = h.loc;
133 			ms->sc_tsscale.minz = h.logical_minimum;
134 			ms->sc_tsscale.maxz = h.logical_maximum;
135 			ms->sc_flags |= HIDMS_Z;
136 			break;
137 		case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_DISTANCE):
138 			DPRINTF("Stylus usage distance set\n");
139 			ms->sc_loc_w = h.loc;
140 			ms->sc_tsscale.minw = h.logical_minimum;
141 			ms->sc_tsscale.maxw = h.logical_maximum;
142 			ms->sc_flags |= HIDMS_W;
143 			break;
144 		default:
145 #ifdef HIDMS_DEBUG
146 			printf("Unknown stylus usage: 0x%x\n",
147 			    h.usage);
148 #endif
149 			break;
150 		}
151 	}
152 }
153 
154 void
155 hidms_pad_buttons_hid_parse(struct hidms *ms, struct hid_data *d,
156     struct hid_location *loc_pad_btn)
157 {
158 	struct hid_item h;
159 
160 	while (hid_get_item(d, &h)) {
161 		if (h.kind == hid_endcollection)
162 			break;
163 		if (h.kind == hid_input && (h.flags & HIO_CONST) != 0 &&
164 		    h.usage == HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS,
165 		    HUD_WACOM_PAD_BUTTONS00 | ms->sc_num_pad_buttons)) {
166 			if (ms->sc_num_pad_buttons >= MAX_BUTTONS)
167 				break;
168 			loc_pad_btn[ms->sc_num_pad_buttons++] = h.loc;
169 		}
170 	}
171 }
172 
173 int
174 hidms_wacom_setup(struct device *self, struct hidms *ms, void *desc, int dlen)
175 {
176 	struct hid_data *hd;
177 	int i;
178 	struct hid_location loc_pad_btn[MAX_BUTTONS];
179 	struct hid_location loc_stylus_btn[MAX_BUTTONS];
180 
181 	ms->sc_flags = 0;
182 
183 	/* Set x,y,z and w to zero by default */
184 	ms->sc_loc_x.size = 0;
185 	ms->sc_loc_y.size = 0;
186 	ms->sc_loc_z.size = 0;
187 	ms->sc_loc_w.size = 0;
188 
189 	if ((hd = hid_get_collection_data(desc, dlen,
190 	     HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_DIGITIZER),
191 	     HCOLL_APPLICATION))) {
192 		DPRINTF("found the global collection\n");
193 		hid_end_parse(hd);
194 		if ((hd = hid_get_collection_data(desc, dlen,
195 		     HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_STYLUS),
196 		     HCOLL_PHYSICAL))) {
197 			DPRINTF("found stylus collection\n");
198 			hidms_stylus_hid_parse(ms, hd, loc_stylus_btn);
199 			hid_end_parse(hd);
200 		}
201 		if ((hd = hid_get_collection_data(desc, dlen,
202 		     HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TABLET_FKEYS),
203 		     HCOLL_PHYSICAL))) {
204 			DPRINTF("found tablet keys collection\n");
205 			hidms_pad_buttons_hid_parse(ms, hd, loc_pad_btn);
206 			hid_end_parse(hd);
207 		}
208 #ifdef notyet
209 		if ((hd = hid_get_collection_data(desc, dlen,
210 		     HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_BATTERY),
211 		     HCOLL_PHYSICAL))) {
212 			DPRINTF("found battery collection\n");
213 			/* parse and set the battery info */
214 			/* not yet used */
215 			hid_end_parse(hd);
216 		}
217 #endif
218 		/*
219 		 * Ignore the device config, it's not really needed;
220 		 * Ignore the usage 0x10AC which is the debug collection, and
221 		 * ignore firmware collection and other collections for now.
222 		 */
223 	}
224 
225 	/* Map the pad and stylus buttons to mouse buttons */
226 	for (i = 0; i < ms->sc_num_stylus_buttons; i++)
227 		memcpy(&ms->sc_loc_btn[i], &loc_stylus_btn[i],
228 		    sizeof(struct hid_location));
229 	if (ms->sc_num_pad_buttons + ms->sc_num_stylus_buttons >= MAX_BUTTONS)
230 		ms->sc_num_pad_buttons =
231 		    MAX_BUTTONS - ms->sc_num_stylus_buttons;
232 	for (; i < ms->sc_num_pad_buttons + ms->sc_num_stylus_buttons; i++)
233 		memcpy(&ms->sc_loc_btn[i], &loc_pad_btn[i],
234 		    sizeof(struct hid_location));
235 	ms->sc_num_buttons = i;
236 	DPRINTF("Button information\n");
237 #ifdef HIDMS_DEBUG
238 	for (i = 0; i < ms->sc_num_buttons; i++)
239 		printf("size: 0x%x, pos: 0x%x, count: 0x%x\n",
240 		    ms->sc_loc_btn[i].size, ms->sc_loc_btn[i].pos,
241 		    ms->sc_loc_btn[i].count);
242 #endif
243 	return 0;
244 }
245 
246 int
247 hidms_setup(struct device *self, struct hidms *ms, uint32_t quirks,
248     int id, void *desc, int dlen)
249 {
250 	struct hid_item h;
251 	struct hid_data *d;
252 	uint32_t flags;
253 	int i, wheel, twheel;
254 
255 	ms->sc_device = self;
256 	ms->sc_rawmode = 1;
257 
258 	ms->sc_flags = quirks;
259 
260 	/* We are setting up a Wacom tablet, not a regular mouse */
261 	if (quirks & HIDMS_WACOM_SETUP)
262 		return hidms_wacom_setup(self, ms, desc, dlen);
263 
264 	if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), id,
265 	    hid_input, &ms->sc_loc_x, &flags))
266 		ms->sc_loc_x.size = 0;
267 
268 	switch (flags & MOUSE_FLAGS_MASK) {
269 	case 0:
270 		ms->sc_flags |= HIDMS_ABSX;
271 		break;
272 	case HIO_RELATIVE:
273 		break;
274 	default:
275 		printf("\n%s: X report 0x%04x not supported\n",
276 		    self->dv_xname, flags);
277 		return ENXIO;
278 	}
279 
280 	if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), id,
281 	    hid_input, &ms->sc_loc_y, &flags))
282 		ms->sc_loc_y.size = 0;
283 
284 	switch(flags & MOUSE_FLAGS_MASK) {
285 	case 0:
286 		ms->sc_flags |= HIDMS_ABSY;
287 		break;
288 	case HIO_RELATIVE:
289 		break;
290 	default:
291 		printf("\n%s: Y report 0x%04x not supported\n",
292 		    self->dv_xname, flags);
293 		return ENXIO;
294 	}
295 
296 	/*
297 	 * Try to guess the Z activator: check WHEEL, TWHEEL, and Z,
298 	 * in that order.
299 	 */
300 
301 	wheel = hid_locate(desc, dlen,
302 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), id,
303 	    hid_input, &ms->sc_loc_z, &flags);
304 	if (wheel == 0)
305 		twheel = hid_locate(desc, dlen,
306 		    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), id,
307 		    hid_input, &ms->sc_loc_z, &flags);
308 	else
309 		twheel = 0;
310 
311 	if (wheel || twheel) {
312 		if (NOTMOUSE(flags)) {
313 			DPRINTF(("\n%s: Wheel report 0x%04x not supported\n",
314 			    self->dv_xname, flags));
315 			ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
316 		} else {
317 			ms->sc_flags |= HIDMS_Z;
318 			/* Wheels need the Z axis reversed. */
319 			ms->sc_flags ^= HIDMS_REVZ;
320 		}
321 		/*
322 		 * We might have both a wheel and Z direction; in this case,
323 		 * report the Z direction on the W axis.
324 		 *
325 		 * Otherwise, check for a W direction as an AC Pan input used
326 		 * on some newer mice.
327 		 */
328 		if (hid_locate(desc, dlen,
329 		    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id,
330 		    hid_input, &ms->sc_loc_w, &flags)) {
331 			if (NOTMOUSE(flags)) {
332 				DPRINTF(("\n%s: Z report 0x%04x not supported\n",
333 				    self->dv_xname, flags));
334 				/* Bad Z coord, ignore it */
335 				ms->sc_loc_w.size = 0;
336 			}
337 			else
338 				ms->sc_flags |= HIDMS_W;
339 		} else if (hid_locate(desc, dlen,
340 		    HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), id, hid_input,
341 		    &ms->sc_loc_w, &flags)) {
342 			ms->sc_flags |= HIDMS_W;
343 		}
344 	} else if (hid_locate(desc, dlen,
345 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id,
346 	    hid_input, &ms->sc_loc_z, &flags)) {
347 		if (NOTMOUSE(flags)) {
348 			DPRINTF(("\n%s: Z report 0x%04x not supported\n",
349 			    self->dv_xname, flags));
350 			ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
351 		} else {
352 			ms->sc_flags |= HIDMS_Z;
353 		}
354 	}
355 
356 	/*
357 	 * The Microsoft Wireless Intellimouse 2.0 reports its wheel
358 	 * using 0x0048 (I've called it HUG_TWHEEL) and seems to expect
359 	 * us to know that the byte after the wheel is the tilt axis.
360 	 * There are no other HID axis descriptors other than X, Y and
361 	 * TWHEEL, so we report TWHEEL on the W axis.
362 	 */
363 	if (twheel) {
364 		ms->sc_loc_w = ms->sc_loc_z;
365 		ms->sc_loc_w.pos = ms->sc_loc_w.pos + 8;
366 		ms->sc_flags |= HIDMS_W | HIDMS_LEADINGBYTE;
367 		/* Wheels need their axis reversed. */
368 		ms->sc_flags ^= HIDMS_REVW;
369 	}
370 
371 	/* figure out the number of buttons */
372 	for (i = 1; i <= MAX_BUTTONS; i++)
373 		if (!hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, i), id,
374 		    hid_input, &ms->sc_loc_btn[i - 1], NULL))
375 			break;
376 	ms->sc_num_buttons = i - 1;
377 
378 	/*
379 	 * The Kensington Slimblade reports some of its buttons as binary
380 	 * inputs in the first vendor usage page (0xff00). Add such inputs
381 	 * as buttons if the device has this quirk.
382 	 */
383 	if (ms->sc_flags & HIDMS_VENDOR_BUTTONS) {
384 		for (i = 1; ms->sc_num_buttons < MAX_BUTTONS; i++) {
385 			if (!hid_locate(desc, dlen,
386 			    HID_USAGE2(HUP_MICROSOFT, i), id, hid_input,
387 			    &ms->sc_loc_btn[ms->sc_num_buttons], NULL))
388 				break;
389 			ms->sc_num_buttons++;
390 		}
391 	}
392 
393 	if (ms->sc_num_buttons < MAX_BUTTONS &&
394 	    hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
395 	    HUD_TIP_SWITCH), id, hid_input,
396 	    &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
397 		ms->sc_flags |= HIDMS_TIP;
398 		ms->sc_num_buttons++;
399 	}
400 
401 	if (ms->sc_num_buttons < MAX_BUTTONS &&
402 	    hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
403 	    HUD_ERASER), id, hid_input,
404 	    &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
405 		ms->sc_flags |= HIDMS_ERASER;
406 		ms->sc_num_buttons++;
407 	}
408 
409 	if (ms->sc_num_buttons < MAX_BUTTONS &&
410 	    hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
411 	    HUD_BARREL_SWITCH), id, hid_input,
412 	    &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
413 		ms->sc_flags |= HIDMS_BARREL;
414 		ms->sc_num_buttons++;
415 	}
416 
417 	/*
418 	 * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
419 	 * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
420 	 * all of its other button positions are all off. It also reports that
421 	 * it has two additional buttons and a tilt wheel.
422 	 */
423 	if (ms->sc_flags & HIDMS_MS_BAD_CLASS) {
424 		/* HIDMS_LEADINGBYTE cleared on purpose */
425 		ms->sc_flags = HIDMS_Z | HIDMS_SPUR_BUT_UP;
426 		ms->sc_num_buttons = 3;
427 		/* XXX change sc_hdev isize to 5? */
428 		/* 1st byte of descriptor report contains garbage */
429 		ms->sc_loc_x.pos = 16;
430 		ms->sc_loc_y.pos = 24;
431 		ms->sc_loc_z.pos = 32;
432 		ms->sc_loc_btn[0].pos = 8;
433 		ms->sc_loc_btn[1].pos = 9;
434 		ms->sc_loc_btn[2].pos = 10;
435 	}
436 	/* Parse descriptors to get touch panel bounds */
437 	d = hid_start_parse(desc, dlen, hid_input);
438 	while (hid_get_item(d, &h)) {
439 		if (h.kind != hid_input ||
440 		    HID_GET_USAGE_PAGE(h.usage) != HUP_GENERIC_DESKTOP)
441 			continue;
442 		DPRINTF(("hidms: usage=0x%x range %d..%d\n",
443 			h.usage, h.logical_minimum, h.logical_maximum));
444 		switch (HID_GET_USAGE(h.usage)) {
445 		case HUG_X:
446 			if (ms->sc_flags & HIDMS_ABSX) {
447 				ms->sc_tsscale.minx = h.logical_minimum;
448 				ms->sc_tsscale.maxx = h.logical_maximum;
449 			}
450 			break;
451 		case HUG_Y:
452 			if (ms->sc_flags & HIDMS_ABSY) {
453 				ms->sc_tsscale.miny = h.logical_minimum;
454 				ms->sc_tsscale.maxy = h.logical_maximum;
455 			}
456 			break;
457 		}
458 	}
459 	hid_end_parse(d);
460 	return 0;
461 }
462 
463 void
464 hidms_attach(struct hidms *ms, const struct wsmouse_accessops *ops)
465 {
466 	struct wsmousedev_attach_args a;
467 #ifdef HIDMS_DEBUG
468 	int i;
469 #endif
470 
471 	printf(": %d button%s",
472 	    ms->sc_num_buttons, ms->sc_num_buttons == 1 ? "" : "s");
473 	switch (ms->sc_flags & (HIDMS_Z | HIDMS_W)) {
474 	case HIDMS_Z:
475 		printf(", Z dir");
476 		break;
477 	case HIDMS_W:
478 		printf(", W dir");
479 		break;
480 	case HIDMS_Z | HIDMS_W:
481 		printf(", Z and W dir");
482 		break;
483 	}
484 
485 	if (ms->sc_flags & HIDMS_TIP)
486 		printf(", tip");
487 	if (ms->sc_flags & HIDMS_BARREL)
488 		printf(", barrel");
489 	if (ms->sc_flags & HIDMS_ERASER)
490 		printf(", eraser");
491 
492 	printf("\n");
493 
494 #ifdef HIDMS_DEBUG
495 	DPRINTF(("hidms_attach: ms=%p\n", ms));
496 	DPRINTF(("hidms_attach: X\t%d/%d\n",
497 	     ms->sc_loc_x.pos, ms->sc_loc_x.size));
498 	DPRINTF(("hidms_attach: Y\t%d/%d\n",
499 	    ms->sc_loc_y.pos, ms->sc_loc_y.size));
500 	if (ms->sc_flags & HIDMS_Z)
501 		DPRINTF(("hidms_attach: Z\t%d/%d\n",
502 		    ms->sc_loc_z.pos, ms->sc_loc_z.size));
503 	if (ms->sc_flags & HIDMS_W)
504 		DPRINTF(("hidms_attach: W\t%d/%d\n",
505 		    ms->sc_loc_w.pos, ms->sc_loc_w.size));
506 	for (i = 1; i <= ms->sc_num_buttons; i++) {
507 		DPRINTF(("hidms_attach: B%d\t%d/%d\n",
508 		    i, ms->sc_loc_btn[i - 1].pos, ms->sc_loc_btn[i - 1].size));
509 	}
510 #endif
511 
512 	a.accessops = ops;
513 	a.accesscookie = ms->sc_device;
514 	ms->sc_wsmousedev = config_found(ms->sc_device, &a, wsmousedevprint);
515 }
516 
517 int
518 hidms_detach(struct hidms *ms, int flags)
519 {
520 	int rv = 0;
521 
522 	DPRINTF(("hidms_detach: ms=%p flags=%d\n", ms, flags));
523 
524 	/* No need to do reference counting of hidms, wsmouse has all the goo */
525 	if (ms->sc_wsmousedev != NULL)
526 		rv = config_detach(ms->sc_wsmousedev, flags);
527 
528 	return (rv);
529 }
530 
531 void
532 hidms_input(struct hidms *ms, uint8_t *data, u_int len)
533 {
534 	int dx, dy, dz, dw;
535 	u_int32_t buttons = 0;
536 	int i, s;
537 
538 	DPRINTFN(5,("hidms_input: len=%d\n", len));
539 
540 	/*
541 	 * The Microsoft Wireless Intellimouse 2.0 sends one extra leading
542 	 * byte of data compared to most USB mice.  This byte frequently
543 	 * switches from 0x01 (usual state) to 0x02.  It may be used to
544 	 * report non-standard events (such as battery life).  However,
545 	 * at the same time, it generates a left click event on the
546 	 * button byte, where there shouldn't be any.  We simply discard
547 	 * the packet in this case.
548 	 *
549 	 * This problem affects the MS Wireless Notebook Optical Mouse, too.
550 	 * However, the leading byte for this mouse is normally 0x11, and
551 	 * the phantom mouse click occurs when it's 0x14.
552 	 */
553 	if (ms->sc_flags & HIDMS_LEADINGBYTE) {
554 		if (*data++ == 0x02)
555 			return;
556 		/* len--; */
557 	} else if (ms->sc_flags & HIDMS_SPUR_BUT_UP) {
558 		if (*data == 0x14 || *data == 0x15)
559 			return;
560 	}
561 
562 	dx =  hid_get_data(data, len, &ms->sc_loc_x);
563 	dy = -hid_get_data(data, len, &ms->sc_loc_y);
564 	dz =  hid_get_data(data, len, &ms->sc_loc_z);
565 	dw =  hid_get_data(data, len, &ms->sc_loc_w);
566 
567 	if (ms->sc_flags & HIDMS_ABSY)
568 		dy = -dy;
569 	if (ms->sc_flags & HIDMS_REVZ)
570 		dz = -dz;
571 	if (ms->sc_flags & HIDMS_REVW)
572 		dw = -dw;
573 
574 	if (ms->sc_tsscale.swapxy && !ms->sc_rawmode) {
575 		int tmp = dx;
576 		dx = dy;
577 		dy = tmp;
578 	}
579 
580 	if (!ms->sc_rawmode &&
581 	    (ms->sc_tsscale.maxx - ms->sc_tsscale.minx) != 0 &&
582 	    (ms->sc_tsscale.maxy - ms->sc_tsscale.miny) != 0) {
583 		/* Scale down to the screen resolution. */
584 		dx = ((dx - ms->sc_tsscale.minx) * ms->sc_tsscale.resx) /
585 		    (ms->sc_tsscale.maxx - ms->sc_tsscale.minx);
586 		dy = ((dy - ms->sc_tsscale.miny) * ms->sc_tsscale.resy) /
587 		    (ms->sc_tsscale.maxy - ms->sc_tsscale.miny);
588 	}
589 
590 	for (i = 0; i < ms->sc_num_buttons; i++)
591 		if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
592 			buttons |= (1 << HIDMS_BUT(i));
593 
594 	if (dx != 0 || dy != 0 || dz != 0 || dw != 0 ||
595 	    buttons != ms->sc_buttons) {
596 		DPRINTFN(10, ("hidms_input: x:%d y:%d z:%d w:%d buttons:0x%x\n",
597 			dx, dy, dz, dw, buttons));
598 		ms->sc_buttons = buttons;
599 		if (ms->sc_wsmousedev != NULL) {
600 			s = spltty();
601 			if (ms->sc_flags & HIDMS_ABSX) {
602 				wsmouse_set(ms->sc_wsmousedev,
603 				    WSMOUSE_ABS_X, dx, 0);
604 				dx = 0;
605 			}
606 			if (ms->sc_flags & HIDMS_ABSY) {
607 				wsmouse_set(ms->sc_wsmousedev,
608 				    WSMOUSE_ABS_Y, dy, 0);
609 				dy = 0;
610 			}
611 			WSMOUSE_INPUT(ms->sc_wsmousedev,
612 			    buttons, dx, dy, dz, dw);
613 			splx(s);
614 		}
615 	}
616 }
617 
618 int
619 hidms_enable(struct hidms *ms)
620 {
621 	if (ms->sc_enabled)
622 		return EBUSY;
623 
624 	ms->sc_enabled = 1;
625 	ms->sc_buttons = 0;
626 	return 0;
627 }
628 
629 int
630 hidms_ioctl(struct hidms *ms, u_long cmd, caddr_t data, int flag,
631     struct proc *p)
632 {
633 	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
634 
635 	switch (cmd) {
636 	case WSMOUSEIO_SCALIBCOORDS:
637 		if (!(wsmc->minx >= -32768 && wsmc->maxx >= -32768 &&
638 		    wsmc->miny >= -32768 && wsmc->maxy >= -32768 &&
639 		    wsmc->resx >= 0 && wsmc->resy >= 0 &&
640 		    wsmc->minx < 32768 && wsmc->maxx < 32768 &&
641 		    wsmc->miny < 32768 && wsmc->maxy < 32768 &&
642 		    (wsmc->maxx - wsmc->minx) != 0 &&
643 		    (wsmc->maxy - wsmc->miny) != 0 &&
644 		    wsmc->resx < 32768 && wsmc->resy < 32768 &&
645 		    wsmc->swapxy >= 0 && wsmc->swapxy <= 1 &&
646 		    wsmc->samplelen >= 0 && wsmc->samplelen <= 1))
647 			return (EINVAL);
648 
649 		ms->sc_tsscale.minx = wsmc->minx;
650 		ms->sc_tsscale.maxx = wsmc->maxx;
651 		ms->sc_tsscale.miny = wsmc->miny;
652 		ms->sc_tsscale.maxy = wsmc->maxy;
653 		ms->sc_tsscale.swapxy = wsmc->swapxy;
654 		ms->sc_tsscale.resx = wsmc->resx;
655 		ms->sc_tsscale.resy = wsmc->resy;
656 		ms->sc_rawmode = wsmc->samplelen;
657 		return 0;
658 	case WSMOUSEIO_GCALIBCOORDS:
659 		wsmc->minx = ms->sc_tsscale.minx;
660 		wsmc->maxx = ms->sc_tsscale.maxx;
661 		wsmc->miny = ms->sc_tsscale.miny;
662 		wsmc->maxy = ms->sc_tsscale.maxy;
663 		wsmc->swapxy = ms->sc_tsscale.swapxy;
664 		wsmc->resx = ms->sc_tsscale.resx;
665 		wsmc->resy = ms->sc_tsscale.resy;
666 		wsmc->samplelen = ms->sc_rawmode;
667 		return 0;
668 	case WSMOUSEIO_GTYPE:
669 		if (ms->sc_flags & HIDMS_ABSX && ms->sc_flags & HIDMS_ABSY) {
670 			*(u_int *)data = WSMOUSE_TYPE_TPANEL;
671 			return 0;
672 		}
673 		/* FALLTHROUGH */
674 	default:
675 		return -1;
676 	}
677 }
678 
679 void
680 hidms_disable(struct hidms *ms)
681 {
682 	ms->sc_enabled = 0;
683 }
684