1 /* $NetBSD: ums.c,v 1.91 2017/12/10 17:03:07 bouyer Exp $ */ 2 3 /* 4 * Copyright (c) 1998, 2017 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net) at 9 * Carlstedt Research & Technology. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 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. 19 * 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. 31 */ 32 33 /* 34 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.91 2017/12/10 17:03:07 bouyer Exp $"); 39 40 #ifdef _KERNEL_OPT 41 #include "opt_usb.h" 42 #endif 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/device.h> 48 #include <sys/ioctl.h> 49 #include <sys/file.h> 50 #include <sys/select.h> 51 #include <sys/proc.h> 52 #include <sys/vnode.h> 53 #include <sys/poll.h> 54 55 #include <dev/usb/usb.h> 56 #include <dev/usb/usbhid.h> 57 58 #include <dev/usb/usbdi.h> 59 #include <dev/usb/usbdi_util.h> 60 #include <dev/usb/usbdevs.h> 61 #include <dev/usb/usb_quirks.h> 62 #include <dev/usb/uhidev.h> 63 #include <dev/hid/hid.h> 64 #include <dev/hid/hidms.h> 65 66 #ifdef UMS_DEBUG 67 #define DPRINTF(x) if (umsdebug) printf x 68 #define DPRINTFN(n,x) if (umsdebug>(n)) printf x 69 int umsdebug = 0; 70 #else 71 #define DPRINTF(x) 72 #define DPRINTFN(n,x) 73 #endif 74 75 #define UMSUNIT(s) (minor(s)) 76 77 struct ums_softc { 78 struct uhidev sc_hdev; 79 struct hidms sc_ms; 80 81 int sc_enabled; 82 char sc_dying; 83 }; 84 85 Static void ums_intr(struct uhidev *, void *, u_int); 86 87 Static int ums_enable(void *); 88 Static void ums_disable(void *); 89 Static int ums_ioctl(void *, u_long, void *, int, struct lwp *); 90 91 const struct wsmouse_accessops ums_accessops = { 92 ums_enable, 93 ums_ioctl, 94 ums_disable, 95 }; 96 97 int ums_match(device_t, cfdata_t, void *); 98 void ums_attach(device_t, device_t, void *); 99 void ums_childdet(device_t, device_t); 100 int ums_detach(device_t, int); 101 int ums_activate(device_t, enum devact); 102 extern struct cfdriver ums_cd; 103 CFATTACH_DECL2_NEW(ums, sizeof(struct ums_softc), ums_match, ums_attach, 104 ums_detach, ums_activate, NULL, ums_childdet); 105 106 int 107 ums_match(device_t parent, cfdata_t match, void *aux) 108 { 109 struct uhidev_attach_arg *uha = aux; 110 int size; 111 void *desc; 112 113 /* 114 * Some (older) Griffin PowerMate knobs may masquerade as a 115 * mouse, avoid treating them as such, they have only one axis. 116 */ 117 if (uha->uiaa->uiaa_vendor == USB_VENDOR_GRIFFIN && 118 uha->uiaa->uiaa_product == USB_PRODUCT_GRIFFIN_POWERMATE) 119 return UMATCH_NONE; 120 121 uhidev_get_report_desc(uha->parent, &desc, &size); 122 if (!hid_is_collection(desc, size, uha->reportid, 123 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) && 124 !hid_is_collection(desc, size, uha->reportid, 125 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER)) && 126 !hid_is_collection(desc, size, uha->reportid, 127 HID_USAGE2(HUP_DIGITIZERS, 0x0002))) 128 return UMATCH_NONE; 129 130 return UMATCH_IFACECLASS; 131 } 132 133 void 134 ums_attach(device_t parent, device_t self, void *aux) 135 { 136 struct ums_softc *sc = device_private(self); 137 struct uhidev_attach_arg *uha = aux; 138 int size; 139 void *desc; 140 uint32_t quirks; 141 142 aprint_naive("\n"); 143 144 sc->sc_hdev.sc_dev = self; 145 sc->sc_hdev.sc_intr = ums_intr; 146 sc->sc_hdev.sc_parent = uha->parent; 147 sc->sc_hdev.sc_report_id = uha->reportid; 148 149 quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; 150 if (quirks & UQ_MS_REVZ) 151 sc->sc_ms.flags |= HIDMS_REVZ; 152 if (quirks & UQ_SPUR_BUT_UP) 153 sc->sc_ms.flags |= HIDMS_SPUR_BUT_UP; 154 155 if (!pmf_device_register(self, NULL, NULL)) 156 aprint_error_dev(self, "couldn't establish power handler\n"); 157 158 uhidev_get_report_desc(uha->parent, &desc, &size); 159 160 if (!hidms_setup(self, &sc->sc_ms, uha->reportid, desc, size)) 161 return; 162 163 if (uha->uiaa->uiaa_vendor == USB_VENDOR_MICROSOFT) { 164 int fixpos; 165 /* 166 * The Microsoft Wireless Laser Mouse 6000 v2.0 and the 167 * Microsoft Comfort Mouse 2.0 report a bad position for 168 * the wheel and wheel tilt controls -- should be in bytes 169 * 3 & 4 of the report. Fix this if necessary. 170 */ 171 switch (uha->uiaa->uiaa_product) { 172 case USB_PRODUCT_MICROSOFT_24GHZ_XCVR10: 173 case USB_PRODUCT_MICROSOFT_24GHZ_XCVR20: 174 fixpos = 24; 175 break; 176 case USB_PRODUCT_MICROSOFT_CM6000: 177 fixpos = 40; 178 break; 179 default: 180 fixpos = 0; 181 break; 182 } 183 if (fixpos) { 184 if ((sc->sc_ms.flags & HIDMS_Z) && 185 sc->sc_ms.hidms_loc_z.pos == 0) 186 sc->sc_ms.hidms_loc_z.pos = fixpos; 187 if ((sc->sc_ms.flags & HIDMS_W) && 188 sc->sc_ms.hidms_loc_w.pos == 0) 189 sc->sc_ms.hidms_loc_w.pos = 190 sc->sc_ms.hidms_loc_z.pos + 8; 191 } 192 } 193 194 hidms_attach(self, &sc->sc_ms, &ums_accessops); 195 } 196 197 int 198 ums_activate(device_t self, enum devact act) 199 { 200 struct ums_softc *sc = device_private(self); 201 202 switch (act) { 203 case DVACT_DEACTIVATE: 204 sc->sc_dying = 1; 205 return 0; 206 default: 207 return EOPNOTSUPP; 208 } 209 } 210 211 void 212 ums_childdet(device_t self, device_t child) 213 { 214 struct ums_softc *sc = device_private(self); 215 216 KASSERT(sc->sc_ms.hidms_wsmousedev == child); 217 sc->sc_ms.hidms_wsmousedev = NULL; 218 } 219 220 int 221 ums_detach(device_t self, int flags) 222 { 223 struct ums_softc *sc = device_private(self); 224 int rv = 0; 225 226 DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags)); 227 228 /* No need to do reference counting of ums, wsmouse has all the goo. */ 229 if (sc->sc_ms.hidms_wsmousedev != NULL) 230 rv = config_detach(sc->sc_ms.hidms_wsmousedev, flags); 231 232 pmf_device_deregister(self); 233 234 return rv; 235 } 236 237 void 238 ums_intr(struct uhidev *addr, void *ibuf, u_int len) 239 { 240 struct ums_softc *sc = (struct ums_softc *)addr; 241 hidms_intr(&sc->sc_ms, ibuf, len); 242 } 243 244 Static int 245 ums_enable(void *v) 246 { 247 struct ums_softc *sc = v; 248 int error; 249 250 DPRINTFN(1,("ums_enable: sc=%p\n", sc)); 251 252 if (sc->sc_dying) 253 return EIO; 254 255 if (sc->sc_enabled) 256 return EBUSY; 257 258 sc->sc_enabled = 1; 259 sc->sc_ms.hidms_buttons = 0; 260 261 error = uhidev_open(&sc->sc_hdev); 262 if (error) 263 sc->sc_enabled = 0; 264 265 return error; 266 } 267 268 Static void 269 ums_disable(void *v) 270 { 271 struct ums_softc *sc = v; 272 273 DPRINTFN(1,("ums_disable: sc=%p\n", sc)); 274 #ifdef DIAGNOSTIC 275 if (!sc->sc_enabled) { 276 printf("ums_disable: not enabled\n"); 277 return; 278 } 279 #endif 280 281 if (sc->sc_enabled) { 282 sc->sc_enabled = 0; 283 uhidev_close(&sc->sc_hdev); 284 } 285 } 286 287 Static int 288 ums_ioctl(void *v, u_long cmd, void *data, int flag, 289 struct lwp * p) 290 291 { 292 struct ums_softc *sc = v; 293 294 switch (cmd) { 295 case WSMOUSEIO_GTYPE: 296 if (sc->sc_ms.flags & HIDMS_ABS) 297 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 298 else 299 *(u_int *)data = WSMOUSE_TYPE_USB; 300 return 0; 301 } 302 303 return EPASSTHROUGH; 304 } 305