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