1 1.6 thorpej /* $NetBSD: hidms.c,v 1.6 2021/08/07 16:19:11 thorpej Exp $ */ 2 1.1 bouyer 3 1.1 bouyer /* 4 1.1 bouyer * Copyright (c) 1998, 2017 The NetBSD Foundation, Inc. 5 1.1 bouyer * All rights reserved. 6 1.1 bouyer * 7 1.1 bouyer * This code is derived from software contributed to The NetBSD Foundation 8 1.1 bouyer * by Lennart Augustsson (lennart (at) augustsson.net) at 9 1.1 bouyer * Carlstedt Research & Technology. 10 1.1 bouyer * 11 1.1 bouyer * Redistribution and use in source and binary forms, with or without 12 1.1 bouyer * modification, are permitted provided that the following conditions 13 1.1 bouyer * are met: 14 1.1 bouyer * 1. Redistributions of source code must retain the above copyright 15 1.1 bouyer * notice, this list of conditions and the following disclaimer. 16 1.1 bouyer * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 bouyer * notice, this list of conditions and the following disclaimer in the 18 1.1 bouyer * documentation and/or other materials provided with the distribution. 19 1.1 bouyer * 20 1.1 bouyer * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.1 bouyer * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.1 bouyer * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.1 bouyer * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.1 bouyer * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.1 bouyer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.1 bouyer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.1 bouyer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.1 bouyer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.1 bouyer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.1 bouyer * POSSIBILITY OF SUCH DAMAGE. 31 1.1 bouyer */ 32 1.1 bouyer 33 1.1 bouyer /* 34 1.1 bouyer * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 35 1.1 bouyer */ 36 1.1 bouyer 37 1.1 bouyer #include <sys/cdefs.h> 38 1.6 thorpej __KERNEL_RCSID(0, "$NetBSD: hidms.c,v 1.6 2021/08/07 16:19:11 thorpej Exp $"); 39 1.1 bouyer 40 1.1 bouyer #include <sys/param.h> 41 1.1 bouyer #include <sys/systm.h> 42 1.1 bouyer #include <sys/kernel.h> 43 1.1 bouyer #include <sys/device.h> 44 1.1 bouyer 45 1.1 bouyer #include <dev/hid/hid.h> 46 1.1 bouyer #include <dev/hid/hidms.h> 47 1.1 bouyer 48 1.1 bouyer #ifdef HIDMS_DEBUG 49 1.1 bouyer #define DPRINTF(x) if (hidmsdebug) printf x 50 1.1 bouyer #define DPRINTFN(n,x) if (hidmsdebug>(n)) printf x 51 1.1 bouyer int hidmsdebug = 0; 52 1.1 bouyer #else 53 1.1 bouyer #define DPRINTF(x) 54 1.1 bouyer #define DPRINTFN(n,x) 55 1.1 bouyer #endif 56 1.1 bouyer 57 1.1 bouyer #define HIDMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) 58 1.1 bouyer 59 1.1 bouyer #define PS2LBUTMASK x01 60 1.1 bouyer #define PS2RBUTMASK x02 61 1.1 bouyer #define PS2MBUTMASK x04 62 1.1 bouyer #define PS2BUTMASK 0x0f 63 1.1 bouyer 64 1.1 bouyer static const struct { 65 1.1 bouyer u_int feature; 66 1.1 bouyer u_int flag; 67 1.1 bouyer } digbut[] = { 68 1.1 bouyer { HUD_TIP_SWITCH, HIDMS_TIP_SWITCH }, 69 1.1 bouyer { HUD_SEC_TIP_SWITCH, HIDMS_SEC_TIP_SWITCH }, 70 1.1 bouyer { HUD_BARREL_SWITCH, HIDMS_BARREL_SWITCH }, 71 1.1 bouyer { HUD_ERASER, HIDMS_ERASER }, 72 1.1 bouyer }; 73 1.1 bouyer 74 1.1 bouyer #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) 75 1.1 bouyer 76 1.1 bouyer bool 77 1.1 bouyer hidms_setup(device_t self, struct hidms *ms, int id, void *desc, int size) 78 1.1 bouyer { 79 1.1 bouyer uint32_t flags; 80 1.1 bouyer struct hid_location *zloc; 81 1.1 bouyer bool isdigitizer; 82 1.1 bouyer int i, hl; 83 1.1 bouyer 84 1.4 ryoon /* Sync with ims_match() */ 85 1.1 bouyer isdigitizer = hid_is_collection(desc, size, id, 86 1.4 ryoon HID_USAGE2(HUP_DIGITIZERS, HUD_PEN)) || 87 1.4 ryoon hid_is_collection(desc, size, id, 88 1.4 ryoon HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCH_SCREEN)); 89 1.1 bouyer 90 1.1 bouyer if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 91 1.1 bouyer id, hid_input, &ms->hidms_loc_x, &flags)) { 92 1.1 bouyer aprint_error("\n%s: mouse has no X report\n", 93 1.1 bouyer device_xname(self)); 94 1.1 bouyer return false; 95 1.1 bouyer } 96 1.1 bouyer switch (flags & MOUSE_FLAGS_MASK) { 97 1.1 bouyer case 0: 98 1.1 bouyer ms->flags |= HIDMS_ABS; 99 1.1 bouyer break; 100 1.1 bouyer case HIO_RELATIVE: 101 1.1 bouyer break; 102 1.1 bouyer default: 103 1.1 bouyer aprint_error("\n%s: X report 0x%04x not supported\n", 104 1.1 bouyer device_xname(self), flags); 105 1.1 bouyer return false; 106 1.1 bouyer } 107 1.1 bouyer 108 1.1 bouyer if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 109 1.1 bouyer id, hid_input, &ms->hidms_loc_y, &flags)) { 110 1.1 bouyer aprint_error("\n%s: mouse has no Y report\n", 111 1.1 bouyer device_xname(self)); 112 1.1 bouyer return false; 113 1.1 bouyer } 114 1.1 bouyer switch (flags & MOUSE_FLAGS_MASK) { 115 1.1 bouyer case 0: 116 1.1 bouyer ms->flags |= HIDMS_ABS; 117 1.1 bouyer break; 118 1.1 bouyer case HIO_RELATIVE: 119 1.1 bouyer break; 120 1.1 bouyer default: 121 1.1 bouyer aprint_error("\n%s: Y report 0x%04x not supported\n", 122 1.1 bouyer device_xname(self), flags); 123 1.1 bouyer return false; 124 1.1 bouyer } 125 1.1 bouyer 126 1.1 bouyer /* Try the wheel first as the Z activator since it's tradition. */ 127 1.1 bouyer hl = hid_locate(desc, 128 1.1 bouyer size, 129 1.1 bouyer HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), 130 1.1 bouyer id, 131 1.1 bouyer hid_input, 132 1.1 bouyer &ms->hidms_loc_z, 133 1.1 bouyer &flags); 134 1.1 bouyer 135 1.1 bouyer zloc = &ms->hidms_loc_z; 136 1.1 bouyer if (hl) { 137 1.1 bouyer if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) { 138 1.1 bouyer aprint_verbose("\n%s: Wheel report 0x%04x not " 139 1.1 bouyer "supported\n", device_xname(self), 140 1.1 bouyer flags); 141 1.1 bouyer ms->hidms_loc_z.size = 0; /* Bad Z coord, ignore it */ 142 1.1 bouyer } else { 143 1.1 bouyer ms->flags |= HIDMS_Z; 144 1.1 bouyer /* Wheels need the Z axis reversed. */ 145 1.1 bouyer ms->flags ^= HIDMS_REVZ; 146 1.1 bouyer /* Put Z on the W coordinate */ 147 1.1 bouyer zloc = &ms->hidms_loc_w; 148 1.1 bouyer } 149 1.1 bouyer } 150 1.1 bouyer 151 1.1 bouyer hl = hid_locate(desc, 152 1.1 bouyer size, 153 1.1 bouyer HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), 154 1.1 bouyer id, 155 1.1 bouyer hid_input, 156 1.1 bouyer zloc, 157 1.1 bouyer &flags); 158 1.1 bouyer 159 1.1 bouyer /* 160 1.1 bouyer * The horizontal component of the scrollball can also be given by 161 1.1 bouyer * Application Control Pan in the Consumer page, so if we didnt see 162 1.1 bouyer * any Z then check that. 163 1.1 bouyer */ 164 1.1 bouyer if (!hl) { 165 1.1 bouyer hl = hid_locate(desc, 166 1.1 bouyer size, 167 1.1 bouyer HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), 168 1.1 bouyer id, 169 1.1 bouyer hid_input, 170 1.1 bouyer zloc, 171 1.1 bouyer &flags); 172 1.1 bouyer } 173 1.1 bouyer 174 1.1 bouyer if (hl) { 175 1.1 bouyer if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) { 176 1.1 bouyer aprint_verbose("\n%s: Z report 0x%04x not supported\n", 177 1.1 bouyer device_xname(self), flags); 178 1.1 bouyer zloc->size = 0; /* Bad Z coord, ignore it */ 179 1.1 bouyer } else { 180 1.1 bouyer if (ms->flags & HIDMS_Z) 181 1.1 bouyer ms->flags |= HIDMS_W; 182 1.1 bouyer else 183 1.1 bouyer ms->flags |= HIDMS_Z; 184 1.1 bouyer } 185 1.1 bouyer } 186 1.1 bouyer 187 1.1 bouyer /* figure out the number of buttons */ 188 1.1 bouyer for (i = 1; i <= MAX_BUTTONS; i++) 189 1.1 bouyer if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 190 1.1 bouyer id, hid_input, &ms->hidms_loc_btn[i - 1], 0)) 191 1.1 bouyer break; 192 1.1 bouyer 193 1.1 bouyer if (isdigitizer) { 194 1.1 bouyer ms->flags |= HIDMS_DIGITIZER; 195 1.1 bouyer for (size_t j = 0; j < __arraycount(digbut); j++) { 196 1.1 bouyer if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 197 1.1 bouyer digbut[j].feature), id, hid_input, 198 1.1 bouyer &ms->hidms_loc_btn[i - 1], 0)) { 199 1.1 bouyer if (i <= MAX_BUTTONS) { 200 1.1 bouyer i++; 201 1.1 bouyer ms->flags |= digbut[j].flag; 202 1.1 bouyer } else 203 1.1 bouyer aprint_error_dev(self, 204 1.1 bouyer "ran out of buttons\n"); 205 1.1 bouyer } 206 1.1 bouyer } 207 1.1 bouyer } 208 1.1 bouyer ms->nbuttons = i - 1; 209 1.1 bouyer return true; 210 1.1 bouyer } 211 1.1 bouyer 212 1.1 bouyer void 213 1.1 bouyer hidms_attach(device_t self, struct hidms *ms, 214 1.1 bouyer const struct wsmouse_accessops *ops) 215 1.1 bouyer { 216 1.1 bouyer struct wsmousedev_attach_args a; 217 1.2 ryoon #ifdef HIDMS_DEBUG 218 1.2 ryoon int i; 219 1.2 ryoon #endif 220 1.1 bouyer aprint_normal(": %d button%s%s%s%s%s%s%s%s%s\n", 221 1.1 bouyer ms->nbuttons, ms->nbuttons == 1 ? "" : "s", 222 1.1 bouyer ms->flags & HIDMS_W ? ", W" : "", 223 1.1 bouyer ms->flags & HIDMS_Z ? " and Z dir" : "", 224 1.1 bouyer ms->flags & HIDMS_W ? "s" : "", 225 1.1 bouyer ms->flags & HIDMS_DIGITIZER ? " digitizer" : "", 226 1.1 bouyer ms->flags & HIDMS_TIP_SWITCH ? ", tip" : "", 227 1.1 bouyer ms->flags & HIDMS_SEC_TIP_SWITCH ? ", sec tip" : "", 228 1.1 bouyer ms->flags & HIDMS_BARREL_SWITCH ? ", barrel" : "", 229 1.1 bouyer ms->flags & HIDMS_ERASER ? ", eraser" : ""); 230 1.1 bouyer 231 1.1 bouyer #ifdef HIDMS_DEBUG 232 1.1 bouyer DPRINTF(("hidms_attach: ms=%p\n", ms)); 233 1.1 bouyer DPRINTF(("hidms_attach: X\t%d/%d\n", 234 1.1 bouyer ms->hidms_loc_x.pos, ms->hidms_loc_x.size)); 235 1.1 bouyer DPRINTF(("hidms_attach: Y\t%d/%d\n", 236 1.1 bouyer ms->hidms_loc_y.pos, ms->hidms_loc_y.size)); 237 1.1 bouyer if (ms->flags & HIDMS_Z) 238 1.1 bouyer DPRINTF(("hidms_attach: Z\t%d/%d\n", 239 1.1 bouyer ms->hidms_loc_z.pos, ms->hidms_loc_z.size)); 240 1.1 bouyer if (ms->flags & HIDMS_W) 241 1.1 bouyer DPRINTF(("hidms_attach: W\t%d/%d\n", 242 1.1 bouyer ms->hidms_loc_w.pos, ms->hidms_loc_w.size)); 243 1.1 bouyer for (i = 1; i <= ms->nbuttons; i++) { 244 1.1 bouyer DPRINTF(("hidms_attach: B%d\t%d/%d\n", 245 1.1 bouyer i, ms->hidms_loc_btn[i-1].pos,ms->hidms_loc_btn[i-1].size)); 246 1.1 bouyer } 247 1.1 bouyer #endif 248 1.1 bouyer 249 1.1 bouyer a.accessops = ops; 250 1.1 bouyer a.accesscookie = device_private(self); 251 1.1 bouyer 252 1.5 thorpej ms->hidms_wsmousedev = config_found(self, &a, wsmousedevprint, 253 1.6 thorpej CFARGS_NONE); 254 1.1 bouyer 255 1.1 bouyer return; 256 1.1 bouyer } 257 1.1 bouyer 258 1.1 bouyer 259 1.1 bouyer void 260 1.1 bouyer hidms_intr(struct hidms *ms, void *ibuf, u_int len) 261 1.1 bouyer { 262 1.1 bouyer int dx, dy, dz, dw; 263 1.1 bouyer uint32_t buttons = 0; 264 1.1 bouyer int i, flags, s; 265 1.1 bouyer 266 1.1 bouyer DPRINTFN(5,("hidms_intr: len=%d\n", len)); 267 1.1 bouyer 268 1.1 bouyer flags = WSMOUSE_INPUT_DELTA; /* equals 0 */ 269 1.1 bouyer 270 1.1 bouyer dx = hid_get_data(ibuf, &ms->hidms_loc_x); 271 1.1 bouyer if (ms->flags & HIDMS_ABS) { 272 1.1 bouyer flags |= (WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 273 1.1 bouyer dy = hid_get_data(ibuf, &ms->hidms_loc_y); 274 1.3 ryoon tpcalib_trans(&ms->sc_tpcalib, dx, dy, &dx, &dy); 275 1.1 bouyer } else 276 1.1 bouyer dy = -hid_get_data(ibuf, &ms->hidms_loc_y); 277 1.1 bouyer dz = hid_get_data(ibuf, &ms->hidms_loc_z); 278 1.1 bouyer dw = hid_get_data(ibuf, &ms->hidms_loc_w); 279 1.1 bouyer 280 1.1 bouyer if (ms->flags & HIDMS_REVZ) 281 1.1 bouyer dz = -dz; 282 1.1 bouyer for (i = 0; i < ms->nbuttons; i++) 283 1.1 bouyer if (hid_get_data(ibuf, &ms->hidms_loc_btn[i])) 284 1.1 bouyer buttons |= (1 << HIDMS_BUT(i)); 285 1.1 bouyer 286 1.1 bouyer if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || 287 1.1 bouyer buttons != ms->hidms_buttons) { 288 1.1 bouyer DPRINTFN(10, ("hidms_intr: x:%d y:%d z:%d w:%d buttons:0x%x\n", 289 1.1 bouyer dx, dy, dz, dw, buttons)); 290 1.1 bouyer ms->hidms_buttons = buttons; 291 1.1 bouyer if (ms->hidms_wsmousedev != NULL) { 292 1.1 bouyer s = spltty(); 293 1.1 bouyer wsmouse_input(ms->hidms_wsmousedev, buttons, dx, dy, dz, 294 1.1 bouyer dw, flags); 295 1.1 bouyer splx(s); 296 1.1 bouyer } 297 1.1 bouyer } 298 1.1 bouyer } 299