FreeBSD/Linux Kernel Cross Reference
sys/dev/hid/hidms.c
1 /* $OpenBSD: hidms.c,v 1.9 2022/06/16 20:52:38 bru 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 int
65 hidms_setup(struct device *self, struct hidms *ms, uint32_t quirks,
66 int id, void *desc, int dlen)
67 {
68 struct hid_item h;
69 struct hid_data *d;
70 uint32_t flags;
71 int i, wheel, twheel;
72
73 ms->sc_device = self;
74 ms->sc_rawmode = 1;
75
76 ms->sc_flags = quirks;
77
78 if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), id,
79 hid_input, &ms->sc_loc_x, &flags))
80 ms->sc_loc_x.size = 0;
81
82 switch(flags & MOUSE_FLAGS_MASK) {
83 case 0:
84 ms->sc_flags |= HIDMS_ABSX;
85 break;
86 case HIO_RELATIVE:
87 break;
88 default:
89 printf("\n%s: X report 0x%04x not supported\n",
90 self->dv_xname, flags);
91 return ENXIO;
92 }
93
94 if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), id,
95 hid_input, &ms->sc_loc_y, &flags))
96 ms->sc_loc_y.size = 0;
97
98 switch(flags & MOUSE_FLAGS_MASK) {
99 case 0:
100 ms->sc_flags |= HIDMS_ABSY;
101 break;
102 case HIO_RELATIVE:
103 break;
104 default:
105 printf("\n%s: Y report 0x%04x not supported\n",
106 self->dv_xname, flags);
107 return ENXIO;
108 }
109
110 /*
111 * Try to guess the Z activator: check WHEEL, TWHEEL, and Z,
112 * in that order.
113 */
114
115 wheel = hid_locate(desc, dlen,
116 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), id,
117 hid_input, &ms->sc_loc_z, &flags);
118 if (wheel == 0)
119 twheel = hid_locate(desc, dlen,
120 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), id,
121 hid_input, &ms->sc_loc_z, &flags);
122 else
123 twheel = 0;
124
125 if (wheel || twheel) {
126 if (NOTMOUSE(flags)) {
127 DPRINTF(("\n%s: Wheel report 0x%04x not supported\n",
128 self->dv_xname, flags));
129 ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
130 } else {
131 ms->sc_flags |= HIDMS_Z;
132 /* Wheels need the Z axis reversed. */
133 ms->sc_flags ^= HIDMS_REVZ;
134 }
135 /*
136 * We might have both a wheel and Z direction; in this case,
137 * report the Z direction on the W axis.
138 *
139 * Otherwise, check for a W direction as an AC Pan input used
140 * on some newer mice.
141 */
142 if (hid_locate(desc, dlen,
143 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id,
144 hid_input, &ms->sc_loc_w, &flags)) {
145 if (NOTMOUSE(flags)) {
146 DPRINTF(("\n%s: Z report 0x%04x not supported\n",
147 self->dv_xname, flags));
148 /* Bad Z coord, ignore it */
149 ms->sc_loc_w.size = 0;
150 }
151 else
152 ms->sc_flags |= HIDMS_W;
153 } else if (hid_locate(desc, dlen,
154 HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), id, hid_input,
155 &ms->sc_loc_w, &flags)) {
156 ms->sc_flags |= HIDMS_W;
157 }
158 } else if (hid_locate(desc, dlen,
159 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id,
160 hid_input, &ms->sc_loc_z, &flags)) {
161 if (NOTMOUSE(flags)) {
162 DPRINTF(("\n%s: Z report 0x%04x not supported\n",
163 self->dv_xname, flags));
164 ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
165 } else {
166 ms->sc_flags |= HIDMS_Z;
167 }
168 }
169
170 /*
171 * The Microsoft Wireless Intellimouse 2.0 reports its wheel
172 * using 0x0048 (I've called it HUG_TWHEEL) and seems to expect
173 * us to know that the byte after the wheel is the tilt axis.
174 * There are no other HID axis descriptors other than X, Y and
175 * TWHEEL, so we report TWHEEL on the W axis.
176 */
177 if (twheel) {
178 ms->sc_loc_w = ms->sc_loc_z;
179 ms->sc_loc_w.pos = ms->sc_loc_w.pos + 8;
180 ms->sc_flags |= HIDMS_W | HIDMS_LEADINGBYTE;
181 /* Wheels need their axis reversed. */
182 ms->sc_flags ^= HIDMS_REVW;
183 }
184
185 /* figure out the number of buttons */
186 for (i = 1; i <= MAX_BUTTONS; i++)
187 if (!hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, i), id,
188 hid_input, &ms->sc_loc_btn[i - 1], NULL))
189 break;
190 ms->sc_num_buttons = i - 1;
191
192 /*
193 * The Kensington Slimblade reports some of its buttons as binary
194 * inputs in the first vendor usage page (0xff00). Add such inputs
195 * as buttons if the device has this quirk.
196 */
197 if (ms->sc_flags & HIDMS_VENDOR_BUTTONS) {
198 for (i = 1; ms->sc_num_buttons < MAX_BUTTONS; i++) {
199 if (!hid_locate(desc, dlen,
200 HID_USAGE2(HUP_MICROSOFT, i), id, hid_input,
201 &ms->sc_loc_btn[ms->sc_num_buttons], NULL))
202 break;
203 ms->sc_num_buttons++;
204 }
205 }
206
207 if (ms->sc_num_buttons < MAX_BUTTONS &&
208 hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
209 HUD_TIP_SWITCH), id, hid_input,
210 &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
211 ms->sc_flags |= HIDMS_TIP;
212 ms->sc_num_buttons++;
213 }
214
215 if (ms->sc_num_buttons < MAX_BUTTONS &&
216 hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
217 HUD_ERASER), id, hid_input,
218 &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
219 ms->sc_flags |= HIDMS_ERASER;
220 ms->sc_num_buttons++;
221 }
222
223 if (ms->sc_num_buttons < MAX_BUTTONS &&
224 hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS,
225 HUD_BARREL_SWITCH), id, hid_input,
226 &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){
227 ms->sc_flags |= HIDMS_BARREL;
228 ms->sc_num_buttons++;
229 }
230
231 /*
232 * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
233 * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
234 * all of its other button positions are all off. It also reports that
235 * it has two additional buttons and a tilt wheel.
236 */
237 if (ms->sc_flags & HIDMS_MS_BAD_CLASS) {
238 /* HIDMS_LEADINGBYTE cleared on purpose */
239 ms->sc_flags = HIDMS_Z | HIDMS_SPUR_BUT_UP;
240 ms->sc_num_buttons = 3;
241 /* XXX change sc_hdev isize to 5? */
242 /* 1st byte of descriptor report contains garbage */
243 ms->sc_loc_x.pos = 16;
244 ms->sc_loc_y.pos = 24;
245 ms->sc_loc_z.pos = 32;
246 ms->sc_loc_btn[0].pos = 8;
247 ms->sc_loc_btn[1].pos = 9;
248 ms->sc_loc_btn[2].pos = 10;
249 }
250 /* Parse descriptors to get touch panel bounds */
251 d = hid_start_parse(desc, dlen, hid_input);
252 while (hid_get_item(d, &h)) {
253 if (h.kind != hid_input ||
254 HID_GET_USAGE_PAGE(h.usage) != HUP_GENERIC_DESKTOP)
255 continue;
256 DPRINTF(("hidms: usage=0x%x range %d..%d\n",
257 h.usage, h.logical_minimum, h.logical_maximum));
258 switch (HID_GET_USAGE(h.usage)) {
259 case HUG_X:
260 if (ms->sc_flags & HIDMS_ABSX) {
261 ms->sc_tsscale.minx = h.logical_minimum;
262 ms->sc_tsscale.maxx = h.logical_maximum;
263 }
264 break;
265 case HUG_Y:
266 if (ms->sc_flags & HIDMS_ABSY) {
267 ms->sc_tsscale.miny = h.logical_minimum;
268 ms->sc_tsscale.maxy = h.logical_maximum;
269 }
270 break;
271 }
272 }
273 hid_end_parse(d);
274 return 0;
275 }
276
277 void
278 hidms_attach(struct hidms *ms, const struct wsmouse_accessops *ops)
279 {
280 struct wsmousedev_attach_args a;
281 #ifdef HIDMS_DEBUG
282 int i;
283 #endif
284
285 printf(": %d button%s",
286 ms->sc_num_buttons, ms->sc_num_buttons == 1 ? "" : "s");
287 switch (ms->sc_flags & (HIDMS_Z | HIDMS_W)) {
288 case HIDMS_Z:
289 printf(", Z dir");
290 break;
291 case HIDMS_W:
292 printf(", W dir");
293 break;
294 case HIDMS_Z | HIDMS_W:
295 printf(", Z and W dir");
296 break;
297 }
298
299 if (ms->sc_flags & HIDMS_TIP)
300 printf(", tip");
301 if (ms->sc_flags & HIDMS_BARREL)
302 printf(", barrel");
303 if (ms->sc_flags & HIDMS_ERASER)
304 printf(", eraser");
305
306 printf("\n");
307
308 #ifdef HIDMS_DEBUG
309 DPRINTF(("hidms_attach: ms=%p\n", ms));
310 DPRINTF(("hidms_attach: X\t%d/%d\n",
311 ms->sc_loc_x.pos, ms->sc_loc_x.size));
312 DPRINTF(("hidms_attach: Y\t%d/%d\n",
313 ms->sc_loc_y.pos, ms->sc_loc_y.size));
314 if (ms->sc_flags & HIDMS_Z)
315 DPRINTF(("hidms_attach: Z\t%d/%d\n",
316 ms->sc_loc_z.pos, ms->sc_loc_z.size));
317 if (ms->sc_flags & HIDMS_W)
318 DPRINTF(("hidms_attach: W\t%d/%d\n",
319 ms->sc_loc_w.pos, ms->sc_loc_w.size));
320 for (i = 1; i <= ms->sc_num_buttons; i++) {
321 DPRINTF(("hidms_attach: B%d\t%d/%d\n",
322 i, ms->sc_loc_btn[i - 1].pos, ms->sc_loc_btn[i - 1].size));
323 }
324 #endif
325
326 a.accessops = ops;
327 a.accesscookie = ms->sc_device;
328 ms->sc_wsmousedev = config_found(ms->sc_device, &a, wsmousedevprint);
329 }
330
331 int
332 hidms_detach(struct hidms *ms, int flags)
333 {
334 int rv = 0;
335
336 DPRINTF(("hidms_detach: ms=%p flags=%d\n", ms, flags));
337
338 /* No need to do reference counting of hidms, wsmouse has all the goo */
339 if (ms->sc_wsmousedev != NULL)
340 rv = config_detach(ms->sc_wsmousedev, flags);
341
342 return (rv);
343 }
344
345 void
346 hidms_input(struct hidms *ms, uint8_t *data, u_int len)
347 {
348 int dx, dy, dz, dw;
349 u_int32_t buttons = 0;
350 int i, s;
351
352 DPRINTFN(5,("hidms_input: len=%d\n", len));
353
354 /*
355 * The Microsoft Wireless Intellimouse 2.0 sends one extra leading
356 * byte of data compared to most USB mice. This byte frequently
357 * switches from 0x01 (usual state) to 0x02. It may be used to
358 * report non-standard events (such as battery life). However,
359 * at the same time, it generates a left click event on the
360 * button byte, where there shouldn't be any. We simply discard
361 * the packet in this case.
362 *
363 * This problem affects the MS Wireless Notebook Optical Mouse, too.
364 * However, the leading byte for this mouse is normally 0x11, and
365 * the phantom mouse click occurs when it's 0x14.
366 */
367 if (ms->sc_flags & HIDMS_LEADINGBYTE) {
368 if (*data++ == 0x02)
369 return;
370 /* len--; */
371 } else if (ms->sc_flags & HIDMS_SPUR_BUT_UP) {
372 if (*data == 0x14 || *data == 0x15)
373 return;
374 }
375
376 dx = hid_get_data(data, len, &ms->sc_loc_x);
377 dy = -hid_get_data(data, len, &ms->sc_loc_y);
378 dz = hid_get_data(data, len, &ms->sc_loc_z);
379 dw = hid_get_data(data, len, &ms->sc_loc_w);
380
381 if (ms->sc_flags & HIDMS_ABSY)
382 dy = -dy;
383 if (ms->sc_flags & HIDMS_REVZ)
384 dz = -dz;
385 if (ms->sc_flags & HIDMS_REVW)
386 dw = -dw;
387
388 if (ms->sc_tsscale.swapxy && !ms->sc_rawmode) {
389 int tmp = dx;
390 dx = dy;
391 dy = tmp;
392 }
393
394 if (!ms->sc_rawmode &&
395 (ms->sc_tsscale.maxx - ms->sc_tsscale.minx) != 0 &&
396 (ms->sc_tsscale.maxy - ms->sc_tsscale.miny) != 0) {
397 /* Scale down to the screen resolution. */
398 dx = ((dx - ms->sc_tsscale.minx) * ms->sc_tsscale.resx) /
399 (ms->sc_tsscale.maxx - ms->sc_tsscale.minx);
400 dy = ((dy - ms->sc_tsscale.miny) * ms->sc_tsscale.resy) /
401 (ms->sc_tsscale.maxy - ms->sc_tsscale.miny);
402 }
403
404 for (i = 0; i < ms->sc_num_buttons; i++)
405 if (hid_get_data(data, len, &ms->sc_loc_btn[i]))
406 buttons |= (1 << HIDMS_BUT(i));
407
408 if (dx != 0 || dy != 0 || dz != 0 || dw != 0 ||
409 buttons != ms->sc_buttons) {
410 DPRINTFN(10, ("hidms_input: x:%d y:%d z:%d w:%d buttons:0x%x\n",
411 dx, dy, dz, dw, buttons));
412 ms->sc_buttons = buttons;
413 if (ms->sc_wsmousedev != NULL) {
414 s = spltty();
415 if (ms->sc_flags & HIDMS_ABSX) {
416 wsmouse_set(ms->sc_wsmousedev,
417 WSMOUSE_ABS_X, dx, 0);
418 dx = 0;
419 }
420 if (ms->sc_flags & HIDMS_ABSY) {
421 wsmouse_set(ms->sc_wsmousedev,
422 WSMOUSE_ABS_Y, dy, 0);
423 dy = 0;
424 }
425 WSMOUSE_INPUT(ms->sc_wsmousedev,
426 buttons, dx, dy, dz, dw);
427 splx(s);
428 }
429 }
430 }
431
432 int
433 hidms_enable(struct hidms *ms)
434 {
435 if (ms->sc_enabled)
436 return EBUSY;
437
438 ms->sc_enabled = 1;
439 ms->sc_buttons = 0;
440 return 0;
441 }
442
443 int
444 hidms_ioctl(struct hidms *ms, u_long cmd, caddr_t data, int flag,
445 struct proc *p)
446 {
447 struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
448
449 switch (cmd) {
450 case WSMOUSEIO_SCALIBCOORDS:
451 if (!(wsmc->minx >= -32768 && wsmc->maxx >= -32768 &&
452 wsmc->miny >= -32768 && wsmc->maxy >= -32768 &&
453 wsmc->resx >= 0 && wsmc->resy >= 0 &&
454 wsmc->minx < 32768 && wsmc->maxx < 32768 &&
455 wsmc->miny < 32768 && wsmc->maxy < 32768 &&
456 (wsmc->maxx - wsmc->minx) != 0 &&
457 (wsmc->maxy - wsmc->miny) != 0 &&
458 wsmc->resx < 32768 && wsmc->resy < 32768 &&
459 wsmc->swapxy >= 0 && wsmc->swapxy <= 1 &&
460 wsmc->samplelen >= 0 && wsmc->samplelen <= 1))
461 return (EINVAL);
462
463 ms->sc_tsscale.minx = wsmc->minx;
464 ms->sc_tsscale.maxx = wsmc->maxx;
465 ms->sc_tsscale.miny = wsmc->miny;
466 ms->sc_tsscale.maxy = wsmc->maxy;
467 ms->sc_tsscale.swapxy = wsmc->swapxy;
468 ms->sc_tsscale.resx = wsmc->resx;
469 ms->sc_tsscale.resy = wsmc->resy;
470 ms->sc_rawmode = wsmc->samplelen;
471 return 0;
472 case WSMOUSEIO_GCALIBCOORDS:
473 wsmc->minx = ms->sc_tsscale.minx;
474 wsmc->maxx = ms->sc_tsscale.maxx;
475 wsmc->miny = ms->sc_tsscale.miny;
476 wsmc->maxy = ms->sc_tsscale.maxy;
477 wsmc->swapxy = ms->sc_tsscale.swapxy;
478 wsmc->resx = ms->sc_tsscale.resx;
479 wsmc->resy = ms->sc_tsscale.resy;
480 wsmc->samplelen = ms->sc_rawmode;
481 return 0;
482 case WSMOUSEIO_GTYPE:
483 if (ms->sc_flags & HIDMS_ABSX && ms->sc_flags & HIDMS_ABSY) {
484 *(u_int *)data = WSMOUSE_TYPE_TPANEL;
485 return 0;
486 }
487 /* FALLTHROUGH */
488 default:
489 return -1;
490 }
491 }
492
493 void
494 hidms_disable(struct hidms *ms)
495 {
496 ms->sc_enabled = 0;
497 }
Cache object: 32e34f27ae275b269d7e1a82067118fb
|