xref: /openbsd-src/sys/dev/usb/ums.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: ums.c,v 1.51 2021/11/22 11:29:17 anton 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: https://www.usb.org/sites/default/files/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/usb/usb.h>
45 #include <dev/usb/usbhid.h>
46 
47 #include <dev/usb/usbdi.h>
48 #include <dev/usb/usbdi_util.h>
49 #include <dev/usb/usbdevs.h>
50 #include <dev/usb/usb_quirks.h>
51 #include <dev/usb/uhidev.h>
52 
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wscons/wsmousevar.h>
55 
56 #include <dev/hid/hidmsvar.h>
57 
58 struct ums_softc {
59 	struct uhidev	sc_hdev;
60 	struct hidms	sc_ms;
61 	uint32_t	sc_quirks;
62 };
63 
64 void ums_intr(struct uhidev *addr, void *ibuf, u_int len);
65 
66 int	ums_enable(void *);
67 void	ums_disable(void *);
68 int	ums_ioctl(void *, u_long, caddr_t, int, struct proc *);
69 void	ums_fix_elecom_descriptor(struct ums_softc *, void *, int, int);
70 
71 const struct wsmouse_accessops ums_accessops = {
72 	ums_enable,
73 	ums_ioctl,
74 	ums_disable,
75 };
76 
77 int ums_match(struct device *, void *, void *);
78 void ums_attach(struct device *, struct device *, void *);
79 int ums_detach(struct device *, int);
80 
81 struct cfdriver ums_cd = {
82 	NULL, "ums", DV_DULL
83 };
84 
85 const struct cfattach ums_ca = {
86 	sizeof(struct ums_softc), ums_match, ums_attach, ums_detach
87 };
88 
89 int
90 ums_match(struct device *parent, void *match, void *aux)
91 {
92 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
93 	int size;
94 	void *desc;
95 
96 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
97 		return (UMATCH_NONE);
98 
99 	uhidev_get_report_desc(uha->parent, &desc, &size);
100 
101 	if (hid_is_collection(desc, size, uha->reportid,
102 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER)))
103 		return (UMATCH_IFACECLASS);
104 
105 	if (hid_is_collection(desc, size, uha->reportid,
106 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
107 		return (UMATCH_IFACECLASS);
108 
109 	if (hid_is_collection(desc, size, uha->reportid,
110 	    HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN)))
111 		return (UMATCH_IFACECLASS);
112 
113 	if (hid_is_collection(desc, size, uha->reportid,
114 	    HID_USAGE2(HUP_DIGITIZERS, HUD_PEN)))
115 		return (UMATCH_IFACECLASS);
116 
117 	return (UMATCH_NONE);
118 }
119 
120 void
121 ums_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct ums_softc *sc = (struct ums_softc *)self;
124 	struct hidms *ms = &sc->sc_ms;
125 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
126 	struct usb_attach_arg *uaa = uha->uaa;
127 	int size, repid;
128 	void *desc;
129 	u_int32_t qflags = 0;
130 
131 	sc->sc_hdev.sc_intr = ums_intr;
132 	sc->sc_hdev.sc_parent = uha->parent;
133 	sc->sc_hdev.sc_udev = uaa->device;
134 	sc->sc_hdev.sc_report_id = uha->reportid;
135 
136 	usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0);
137 
138 	sc->sc_quirks = usbd_get_quirks(sc->sc_hdev.sc_udev)->uq_flags;
139 	uhidev_get_report_desc(uha->parent, &desc, &size);
140 
141 	if (uaa->vendor == USB_VENDOR_ELECOM)
142 		ums_fix_elecom_descriptor(sc, desc, size, uaa->product);
143 
144 	repid = uha->reportid;
145 	sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
146 	sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
147 	sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
148 
149 	if (sc->sc_quirks & UQ_MS_REVZ)
150 		qflags |= HIDMS_REVZ;
151 	if (sc->sc_quirks & UQ_SPUR_BUT_UP)
152 		qflags |= HIDMS_SPUR_BUT_UP;
153 	if (sc->sc_quirks & UQ_MS_BAD_CLASS)
154 		qflags |= HIDMS_MS_BAD_CLASS;
155 	if (sc->sc_quirks & UQ_MS_LEADING_BYTE)
156 		qflags |= HIDMS_LEADINGBYTE;
157 	if (sc->sc_quirks & UQ_MS_VENDOR_BUTTONS)
158 		qflags |= HIDMS_VENDOR_BUTTONS;
159 
160 	if (hidms_setup(self, ms, qflags, uha->reportid, desc, size) != 0)
161 		return;
162 
163 	/*
164 	 * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has
165 	 * five Report IDs: 19, 23, 24, 17, 18 (in the order they appear in
166 	 * report descriptor), it seems that report 17 contains the necessary
167 	 * mouse information (3-buttons, X, Y, wheel) so we specify it
168 	 * manually.
169 	 */
170 	if (uaa->vendor == USB_VENDOR_MICROSOFT &&
171 	    uaa->product == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3) {
172 		ms->sc_flags = HIDMS_Z;
173 		ms->sc_num_buttons = 3;
174 		/* XXX change sc_hdev isize to 5? */
175 		ms->sc_loc_x.pos = 8;
176 		ms->sc_loc_y.pos = 16;
177 		ms->sc_loc_z.pos = 24;
178 		ms->sc_loc_btn[0].pos = 0;
179 		ms->sc_loc_btn[1].pos = 1;
180 		ms->sc_loc_btn[2].pos = 2;
181 	}
182 
183 	hidms_attach(ms, &ums_accessops);
184 
185 	if (sc->sc_quirks & UQ_ALWAYS_OPEN) {
186 		/* open uhidev and keep it open */
187 		ums_enable(sc);
188 		/* but mark the hidms not in use */
189 		ums_disable(sc);
190 	}
191 }
192 
193 int
194 ums_detach(struct device *self, int flags)
195 {
196 	struct ums_softc *sc = (struct ums_softc *)self;
197 	struct hidms *ms = &sc->sc_ms;
198 
199 	return hidms_detach(ms, flags);
200 }
201 
202 void
203 ums_intr(struct uhidev *addr, void *buf, u_int len)
204 {
205 	struct ums_softc *sc = (struct ums_softc *)addr;
206 	struct hidms *ms = &sc->sc_ms;
207 
208 	if (ms->sc_enabled != 0)
209 		hidms_input(ms, (uint8_t *)buf, len);
210 }
211 
212 int
213 ums_enable(void *v)
214 {
215 	struct ums_softc *sc = v;
216 	struct hidms *ms = &sc->sc_ms;
217 	int rv;
218 
219 	if (usbd_is_dying(sc->sc_hdev.sc_udev))
220 		return EIO;
221 
222 	if ((rv = hidms_enable(ms)) != 0)
223 		return rv;
224 
225 	if ((sc->sc_quirks & UQ_ALWAYS_OPEN) &&
226 	    (sc->sc_hdev.sc_state & UHIDEV_OPEN))
227 		rv = 0;
228 	else
229 		rv = uhidev_open(&sc->sc_hdev);
230 
231 	return rv;
232 }
233 
234 void
235 ums_disable(void *v)
236 {
237 	struct ums_softc *sc = v;
238 	struct hidms *ms = &sc->sc_ms;
239 
240 	hidms_disable(ms);
241 
242 	if (sc->sc_quirks & UQ_ALWAYS_OPEN)
243 		return;
244 
245 	uhidev_close(&sc->sc_hdev);
246 }
247 
248 int
249 ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
250 {
251 	struct ums_softc *sc = v;
252 	struct hidms *ms = &sc->sc_ms;
253 	int rc;
254 
255 	rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p);
256 	if (rc != -1)
257 		return rc;
258 	rc = hidms_ioctl(ms, cmd, data, flag, p);
259 	if (rc != -1)
260 		return rc;
261 
262 	switch (cmd) {
263 	case WSMOUSEIO_GTYPE:
264 		*(u_int *)data = WSMOUSE_TYPE_USB;
265 		return 0;
266 	default:
267 		return -1;
268 	}
269 }
270 
271 /*
272  * Some ELECOM devices present flawed report descriptors.  Instead of a 5-bit
273  * field and a 3-bit padding as defined by the descriptor they actually use
274  * 6 or 8 Bits to report button states, see
275  *     https://flameeyes.blog/2017/04/24/elecom-deft-and-the-broken-descriptor
276  * This function adapts the Report Count value for the button page, its Usage
277  * Maximum and the report size for the padding bits (at offset 31).
278  */
279 void
280 ums_fix_elecom_descriptor(struct ums_softc *sc, void *desc, int size,
281 		int product)
282 {
283 	static uByte match[] = {    /* a descriptor fragment, offset: 12 */
284 	    0x95, 0x05, 0x75, 0x01, /* Report Count (5), Report Size (1) */
285 	    0x05, 0x09, 0x19, 0x01, /* Usage Page (Button), Usage Minimum (1) */
286 	    0x29, 0x05,             /* Usage Maximum (5) */
287 	};
288 	uByte *udesc = desc;
289 	int nbuttons;
290 
291 	switch (product) {
292 	case USB_PRODUCT_ELECOM_MXT3URBK:	/* EX-G Trackballs */
293 	case USB_PRODUCT_ELECOM_MXT3DRBK:
294 	case USB_PRODUCT_ELECOM_MXT4DRBK:
295 		nbuttons = 6;
296 		break;
297 	case USB_PRODUCT_ELECOM_MDT1URBK:	/* DEFT Trackballs */
298 	case USB_PRODUCT_ELECOM_MDT1DRBK:
299 	case USB_PRODUCT_ELECOM_MHT1URBK:	/* HUGE Trackballs */
300 	case USB_PRODUCT_ELECOM_MHT1DRBK:
301 		nbuttons = 8;
302 		break;
303 	default:
304 		return;
305 	}
306 
307 	if (udesc == NULL || size < 32
308 	    || memcmp(&udesc[12], match, sizeof(match))
309 	    || udesc[30] != 0x75 || udesc[31] != 3)
310 		return;
311 
312 	printf("%s: fixing Elecom report descriptor (buttons: %d)\n",
313 		sc->sc_hdev.sc_dev.dv_xname, nbuttons);
314 	udesc[13] = nbuttons;
315 	udesc[21] = nbuttons;
316 	udesc[31] = 8 - nbuttons;
317 }
318