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