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