1 /* $NetBSD: ums.c,v 1.77 2009/11/27 08:35:05 mbalmer Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
34 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.77 2009/11/27 08:35:05 mbalmer Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/device.h>
45 #include <sys/ioctl.h>
47 #include <sys/select.h>
49 #include <sys/vnode.h>
52 #include <dev/usb/usb.h>
53 #include <dev/usb/usbhid.h>
55 #include <dev/usb/usbdi.h>
56 #include <dev/usb/usbdi_util.h>
57 #include <dev/usb/usbdevs.h>
58 #include <dev/usb/usb_quirks.h>
59 #include <dev/usb/uhidev.h>
60 #include <dev/usb/hid.h>
62 #include <dev/wscons/wsconsio.h>
63 #include <dev/wscons/wsmousevar.h>
66 #define DPRINTF(x) if (umsdebug) logprintf x
67 #define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x
74 #define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i)
76 #define UMSUNIT(s) (minor(s))
78 #define PS2LBUTMASK x01
79 #define PS2RBUTMASK x02
80 #define PS2MBUTMASK x04
81 #define PS2BUTMASK 0x0f
83 #define MAX_BUTTONS 31 /* must not exceed size of sc_buttons */
86 struct uhidev sc_hdev
;
88 struct hid_location sc_loc_x
, sc_loc_y
, sc_loc_z
, sc_loc_w
;
89 struct hid_location sc_loc_btn
[MAX_BUTTONS
];
93 int flags
; /* device configuration */
94 #define UMS_Z 0x01 /* z direction available */
95 #define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */
96 #define UMS_REVZ 0x04 /* Z-axis is reversed */
97 #define UMS_W 0x08 /* w direction/tilt available */
98 #define UMS_ABS 0x10 /* absolute position, touchpanel */
102 u_int32_t sc_buttons
; /* mouse button status */
103 device_t sc_wsmousedev
;
108 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
110 Static
void ums_intr(struct uhidev
*addr
, void *ibuf
, u_int len
);
112 Static
int ums_enable(void *);
113 Static
void ums_disable(void *);
114 Static
int ums_ioctl(void *, u_long
, void *, int, struct lwp
* );
116 const struct wsmouse_accessops ums_accessops
= {
122 int ums_match(device_t
, cfdata_t
, void *);
123 void ums_attach(device_t
, device_t
, void *);
124 void ums_childdet(device_t
, device_t
);
125 int ums_detach(device_t
, int);
126 int ums_activate(device_t
, enum devact
);
127 extern struct cfdriver ums_cd
;
128 CFATTACH_DECL2_NEW(ums
, sizeof(struct ums_softc
), ums_match
, ums_attach
,
129 ums_detach
, ums_activate
, NULL
, ums_childdet
);
132 ums_match(device_t parent
, cfdata_t match
, void *aux
)
134 struct uhidev_attach_arg
*uha
= aux
;
138 uhidev_get_report_desc(uha
->parent
, &desc
, &size
);
139 if (!hid_is_collection(desc
, size
, uha
->reportid
,
140 HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_MOUSE
)) &&
141 !hid_is_collection(desc
, size
, uha
->reportid
,
142 HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_POINTER
)))
143 return (UMATCH_NONE
);
145 return (UMATCH_IFACECLASS
);
149 ums_attach(device_t parent
, device_t self
, void *aux
)
151 struct ums_softc
*sc
= device_private(self
);
152 struct uhidev_attach_arg
*uha
= aux
;
153 struct wsmousedev_attach_args a
;
156 u_int32_t flags
, quirks
;
158 struct hid_location
*zloc
;
159 struct hid_location loc_btn
;
163 sc
->sc_hdev
.sc_dev
= self
;
164 sc
->sc_hdev
.sc_intr
= ums_intr
;
165 sc
->sc_hdev
.sc_parent
= uha
->parent
;
166 sc
->sc_hdev
.sc_report_id
= uha
->reportid
;
168 quirks
= usbd_get_quirks(uha
->parent
->sc_udev
)->uq_flags
;
169 if (quirks
& UQ_MS_REVZ
)
170 sc
->flags
|= UMS_REVZ
;
171 if (quirks
& UQ_SPUR_BUT_UP
)
172 sc
->flags
|= UMS_SPUR_BUT_UP
;
174 uhidev_get_report_desc(uha
->parent
, &desc
, &size
);
176 if (!pmf_device_register(self
, NULL
, NULL
))
177 aprint_error_dev(self
, "couldn't establish power handler\n");
179 if (!hid_locate(desc
, size
, HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_X
),
180 uha
->reportid
, hid_input
, &sc
->sc_loc_x
, &flags
)) {
181 aprint_error("\n%s: mouse has no X report\n",
182 USBDEVNAME(sc
->sc_hdev
.sc_dev
));
183 USB_ATTACH_ERROR_RETURN
;
185 switch (flags
& MOUSE_FLAGS_MASK
) {
187 sc
->flags
|= UMS_ABS
;
192 aprint_error("\n%s: X report 0x%04x not supported\n",
193 USBDEVNAME(sc
->sc_hdev
.sc_dev
), flags
);
194 USB_ATTACH_ERROR_RETURN
;
197 if (!hid_locate(desc
, size
, HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_Y
),
198 uha
->reportid
, hid_input
, &sc
->sc_loc_y
, &flags
)) {
199 aprint_error("\n%s: mouse has no Y report\n",
200 USBDEVNAME(sc
->sc_hdev
.sc_dev
));
201 USB_ATTACH_ERROR_RETURN
;
203 switch (flags
& MOUSE_FLAGS_MASK
) {
205 sc
->flags
|= UMS_ABS
;
210 aprint_error("\n%s: Y report 0x%04x not supported\n",
211 USBDEVNAME(sc
->sc_hdev
.sc_dev
), flags
);
212 USB_ATTACH_ERROR_RETURN
;
215 /* Try the wheel first as the Z activator since it's tradition. */
216 hl
= hid_locate(desc
,
218 HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_WHEEL
),
224 zloc
= &sc
->sc_loc_z
;
226 if ((flags
& MOUSE_FLAGS_MASK
) != HIO_RELATIVE
) {
227 aprint_verbose("\n%s: Wheel report 0x%04x not "
228 "supported\n", USBDEVNAME(sc
->sc_hdev
.sc_dev
),
230 sc
->sc_loc_z
.size
= 0; /* Bad Z coord, ignore it */
233 /* Wheels need the Z axis reversed. */
234 sc
->flags
^= UMS_REVZ
;
235 /* Put Z on the W coordinate */
236 zloc
= &sc
->sc_loc_w
;
240 hl
= hid_locate(desc
,
242 HID_USAGE2(HUP_GENERIC_DESKTOP
, HUG_Z
),
249 * The horizontal component of the scrollball can also be given by
250 * Application Control Pan in the Consumer page, so if we didnt see
251 * any Z then check that.
254 hl
= hid_locate(desc
,
256 HID_USAGE2(HUP_CONSUMER
, HUC_AC_PAN
),
264 if ((flags
& MOUSE_FLAGS_MASK
) != HIO_RELATIVE
) {
265 aprint_verbose("\n%s: Z report 0x%04x not supported\n",
266 USBDEVNAME(sc
->sc_hdev
.sc_dev
), flags
);
267 zloc
->size
= 0; /* Bad Z coord, ignore it */
269 if (sc
->flags
& UMS_Z
)
277 * The Microsoft Wireless Laser Mouse 6000 v2.0 reports a bad
278 * position for the wheel and wheel tilt controls -- should be
279 * in bytes 3 & 4 of the report. Fix this if necessary.
281 if (uha
->uaa
->vendor
== USB_VENDOR_MICROSOFT
&&
282 uha
->uaa
->product
== USB_PRODUCT_MICROSOFT_24GHZ_XCVR
) {
283 if ((sc
->flags
& UMS_Z
) && sc
->sc_loc_z
.pos
== 0)
284 sc
->sc_loc_z
.pos
= 24;
285 if ((sc
->flags
& UMS_W
) && sc
->sc_loc_w
.pos
== 0)
286 sc
->sc_loc_w
.pos
= sc
->sc_loc_z
.pos
+ 8;
289 /* figure out the number of buttons */
290 for (i
= 1; i
<= MAX_BUTTONS
; i
++)
291 if (!hid_locate(desc
, size
, HID_USAGE2(HUP_BUTTON
, i
),
292 uha
->reportid
, hid_input
, &loc_btn
, 0))
294 sc
->nbuttons
= i
- 1;
296 aprint_normal(": %d button%s%s%s%s\n",
297 sc
->nbuttons
, sc
->nbuttons
== 1 ? "" : "s",
298 sc
->flags
& UMS_W
? ", W" : "",
299 sc
->flags
& UMS_Z
? " and Z dir" : "",
300 sc
->flags
& UMS_W
? "s" : "");
302 for (i
= 1; i
<= sc
->nbuttons
; i
++)
303 hid_locate(desc
, size
, HID_USAGE2(HUP_BUTTON
, i
),
304 uha
->reportid
, hid_input
,
305 &sc
->sc_loc_btn
[i
-1], 0);
308 DPRINTF(("ums_attach: sc=%p\n", sc
));
309 DPRINTF(("ums_attach: X\t%d/%d\n",
310 sc
->sc_loc_x
.pos
, sc
->sc_loc_x
.size
));
311 DPRINTF(("ums_attach: Y\t%d/%d\n",
312 sc
->sc_loc_y
.pos
, sc
->sc_loc_y
.size
));
313 if (sc
->flags
& UMS_Z
)
314 DPRINTF(("ums_attach: Z\t%d/%d\n",
315 sc
->sc_loc_z
.pos
, sc
->sc_loc_z
.size
));
316 if (sc
->flags
& UMS_W
)
317 DPRINTF(("ums_attach: W\t%d/%d\n",
318 sc
->sc_loc_w
.pos
, sc
->sc_loc_w
.size
));
319 for (i
= 1; i
<= sc
->nbuttons
; i
++) {
320 DPRINTF(("ums_attach: B%d\t%d/%d\n",
321 i
, sc
->sc_loc_btn
[i
-1].pos
,sc
->sc_loc_btn
[i
-1].size
));
325 a
.accessops
= &ums_accessops
;
328 sc
->sc_wsmousedev
= config_found(self
, &a
, wsmousedevprint
);
330 USB_ATTACH_SUCCESS_RETURN
;
334 ums_activate(device_ptr_t self
, enum devact act
)
336 struct ums_softc
*sc
= device_private(self
);
339 case DVACT_DEACTIVATE
:
348 ums_childdet(device_t self
, device_t child
)
350 struct ums_softc
*sc
= device_private(self
);
352 KASSERT(sc
->sc_wsmousedev
== child
);
353 sc
->sc_wsmousedev
= NULL
;
357 ums_detach(device_t self
, int flags
)
359 struct ums_softc
*sc
= device_private(self
);
362 DPRINTF(("ums_detach: sc=%p flags=%d\n", sc
, flags
));
364 /* No need to do reference counting of ums, wsmouse has all the goo. */
365 if (sc
->sc_wsmousedev
!= NULL
)
366 rv
= config_detach(sc
->sc_wsmousedev
, flags
);
368 pmf_device_deregister(self
);
374 ums_intr(struct uhidev
*addr
, void *ibuf
, u_int len
)
376 struct ums_softc
*sc
= (struct ums_softc
*)addr
;
378 u_int32_t buttons
= 0;
381 DPRINTFN(5,("ums_intr: len=%d\n", len
));
383 flags
= WSMOUSE_INPUT_DELTA
; /* equals 0 */
385 dx
= hid_get_data(ibuf
, &sc
->sc_loc_x
);
386 if (sc
->flags
& UMS_ABS
) {
387 flags
|= (WSMOUSE_INPUT_ABSOLUTE_X
| WSMOUSE_INPUT_ABSOLUTE_Y
);
388 dy
= hid_get_data(ibuf
, &sc
->sc_loc_y
);
390 dy
= -hid_get_data(ibuf
, &sc
->sc_loc_y
);
391 dz
= hid_get_data(ibuf
, &sc
->sc_loc_z
);
392 dw
= hid_get_data(ibuf
, &sc
->sc_loc_w
);
394 if (sc
->flags
& UMS_REVZ
)
396 for (i
= 0; i
< sc
->nbuttons
; i
++)
397 if (hid_get_data(ibuf
, &sc
->sc_loc_btn
[i
]))
398 buttons
|= (1 << UMS_BUT(i
));
400 if (dx
!= 0 || dy
!= 0 || dz
!= 0 || dw
!= 0 ||
401 buttons
!= sc
->sc_buttons
) {
402 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d w:%d buttons:0x%x\n",
403 dx
, dy
, dz
, dw
, buttons
));
404 sc
->sc_buttons
= buttons
;
405 if (sc
->sc_wsmousedev
!= NULL
) {
407 wsmouse_input(sc
->sc_wsmousedev
, buttons
, dx
, dy
, dz
,
417 struct ums_softc
*sc
= v
;
419 DPRINTFN(1,("ums_enable: sc=%p\n", sc
));
430 return (uhidev_open(&sc
->sc_hdev
));
436 struct ums_softc
*sc
= v
;
438 DPRINTFN(1,("ums_disable: sc=%p\n", sc
));
440 if (!sc
->sc_enabled
) {
441 printf("ums_disable: not enabled\n");
447 uhidev_close(&sc
->sc_hdev
);
451 ums_ioctl(void *v
, u_long cmd
, void *data
, int flag
,
455 struct ums_softc
*sc
= v
;
458 case WSMOUSEIO_GTYPE
:
459 if (sc
->flags
& UMS_ABS
)
460 *(u_int
*)data
= WSMOUSE_TYPE_TPANEL
;
462 *(u_int
*)data
= WSMOUSE_TYPE_USB
;
466 return (EPASSTHROUGH
);