1 /* $NetBSD: hidms.c,v 1.4 2019/07/09 12:56:30 ryoon 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: hidms.c,v 1.4 2019/07/09 12:56:30 ryoon Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/device.h> 44 45 #include <dev/hid/hid.h> 46 #include <dev/hid/hidms.h> 47 48 #ifdef HIDMS_DEBUG 49 #define DPRINTF(x) if (hidmsdebug) printf x 50 #define DPRINTFN(n,x) if (hidmsdebug>(n)) printf x 51 int hidmsdebug = 0; 52 #else 53 #define DPRINTF(x) 54 #define DPRINTFN(n,x) 55 #endif 56 57 #define HIDMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) 58 59 #define PS2LBUTMASK x01 60 #define PS2RBUTMASK x02 61 #define PS2MBUTMASK x04 62 #define PS2BUTMASK 0x0f 63 64 static const struct { 65 u_int feature; 66 u_int flag; 67 } digbut[] = { 68 { HUD_TIP_SWITCH, HIDMS_TIP_SWITCH }, 69 { HUD_SEC_TIP_SWITCH, HIDMS_SEC_TIP_SWITCH }, 70 { HUD_BARREL_SWITCH, HIDMS_BARREL_SWITCH }, 71 { HUD_ERASER, HIDMS_ERASER }, 72 }; 73 74 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) 75 76 bool 77 hidms_setup(device_t self, struct hidms *ms, int id, void *desc, int size) 78 { 79 uint32_t flags; 80 struct hid_location *zloc; 81 bool isdigitizer; 82 int i, hl; 83 84 /* Sync with ims_match() */ 85 isdigitizer = hid_is_collection(desc, size, id, 86 HID_USAGE2(HUP_DIGITIZERS, HUD_PEN)) || 87 hid_is_collection(desc, size, id, 88 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCH_SCREEN)); 89 90 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 91 id, hid_input, &ms->hidms_loc_x, &flags)) { 92 aprint_error("\n%s: mouse has no X report\n", 93 device_xname(self)); 94 return false; 95 } 96 switch (flags & MOUSE_FLAGS_MASK) { 97 case 0: 98 ms->flags |= HIDMS_ABS; 99 break; 100 case HIO_RELATIVE: 101 break; 102 default: 103 aprint_error("\n%s: X report 0x%04x not supported\n", 104 device_xname(self), flags); 105 return false; 106 } 107 108 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 109 id, hid_input, &ms->hidms_loc_y, &flags)) { 110 aprint_error("\n%s: mouse has no Y report\n", 111 device_xname(self)); 112 return false; 113 } 114 switch (flags & MOUSE_FLAGS_MASK) { 115 case 0: 116 ms->flags |= HIDMS_ABS; 117 break; 118 case HIO_RELATIVE: 119 break; 120 default: 121 aprint_error("\n%s: Y report 0x%04x not supported\n", 122 device_xname(self), flags); 123 return false; 124 } 125 126 /* Try the wheel first as the Z activator since it's tradition. */ 127 hl = hid_locate(desc, 128 size, 129 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), 130 id, 131 hid_input, 132 &ms->hidms_loc_z, 133 &flags); 134 135 zloc = &ms->hidms_loc_z; 136 if (hl) { 137 if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) { 138 aprint_verbose("\n%s: Wheel report 0x%04x not " 139 "supported\n", device_xname(self), 140 flags); 141 ms->hidms_loc_z.size = 0; /* Bad Z coord, ignore it */ 142 } else { 143 ms->flags |= HIDMS_Z; 144 /* Wheels need the Z axis reversed. */ 145 ms->flags ^= HIDMS_REVZ; 146 /* Put Z on the W coordinate */ 147 zloc = &ms->hidms_loc_w; 148 } 149 } 150 151 hl = hid_locate(desc, 152 size, 153 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), 154 id, 155 hid_input, 156 zloc, 157 &flags); 158 159 /* 160 * The horizontal component of the scrollball can also be given by 161 * Application Control Pan in the Consumer page, so if we didnt see 162 * any Z then check that. 163 */ 164 if (!hl) { 165 hl = hid_locate(desc, 166 size, 167 HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), 168 id, 169 hid_input, 170 zloc, 171 &flags); 172 } 173 174 if (hl) { 175 if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) { 176 aprint_verbose("\n%s: Z report 0x%04x not supported\n", 177 device_xname(self), flags); 178 zloc->size = 0; /* Bad Z coord, ignore it */ 179 } else { 180 if (ms->flags & HIDMS_Z) 181 ms->flags |= HIDMS_W; 182 else 183 ms->flags |= HIDMS_Z; 184 } 185 } 186 187 /* figure out the number of buttons */ 188 for (i = 1; i <= MAX_BUTTONS; i++) 189 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 190 id, hid_input, &ms->hidms_loc_btn[i - 1], 0)) 191 break; 192 193 if (isdigitizer) { 194 ms->flags |= HIDMS_DIGITIZER; 195 for (size_t j = 0; j < __arraycount(digbut); j++) { 196 if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 197 digbut[j].feature), id, hid_input, 198 &ms->hidms_loc_btn[i - 1], 0)) { 199 if (i <= MAX_BUTTONS) { 200 i++; 201 ms->flags |= digbut[j].flag; 202 } else 203 aprint_error_dev(self, 204 "ran out of buttons\n"); 205 } 206 } 207 } 208 ms->nbuttons = i - 1; 209 return true; 210 } 211 212 void 213 hidms_attach(device_t self, struct hidms *ms, 214 const struct wsmouse_accessops *ops) 215 { 216 struct wsmousedev_attach_args a; 217 #ifdef HIDMS_DEBUG 218 int i; 219 #endif 220 aprint_normal(": %d button%s%s%s%s%s%s%s%s%s\n", 221 ms->nbuttons, ms->nbuttons == 1 ? "" : "s", 222 ms->flags & HIDMS_W ? ", W" : "", 223 ms->flags & HIDMS_Z ? " and Z dir" : "", 224 ms->flags & HIDMS_W ? "s" : "", 225 ms->flags & HIDMS_DIGITIZER ? " digitizer" : "", 226 ms->flags & HIDMS_TIP_SWITCH ? ", tip" : "", 227 ms->flags & HIDMS_SEC_TIP_SWITCH ? ", sec tip" : "", 228 ms->flags & HIDMS_BARREL_SWITCH ? ", barrel" : "", 229 ms->flags & HIDMS_ERASER ? ", eraser" : ""); 230 231 #ifdef HIDMS_DEBUG 232 DPRINTF(("hidms_attach: ms=%p\n", ms)); 233 DPRINTF(("hidms_attach: X\t%d/%d\n", 234 ms->hidms_loc_x.pos, ms->hidms_loc_x.size)); 235 DPRINTF(("hidms_attach: Y\t%d/%d\n", 236 ms->hidms_loc_y.pos, ms->hidms_loc_y.size)); 237 if (ms->flags & HIDMS_Z) 238 DPRINTF(("hidms_attach: Z\t%d/%d\n", 239 ms->hidms_loc_z.pos, ms->hidms_loc_z.size)); 240 if (ms->flags & HIDMS_W) 241 DPRINTF(("hidms_attach: W\t%d/%d\n", 242 ms->hidms_loc_w.pos, ms->hidms_loc_w.size)); 243 for (i = 1; i <= ms->nbuttons; i++) { 244 DPRINTF(("hidms_attach: B%d\t%d/%d\n", 245 i, ms->hidms_loc_btn[i-1].pos,ms->hidms_loc_btn[i-1].size)); 246 } 247 #endif 248 249 a.accessops = ops; 250 a.accesscookie = device_private(self); 251 252 ms->hidms_wsmousedev = config_found(self, &a, wsmousedevprint); 253 254 return; 255 } 256 257 258 void 259 hidms_intr(struct hidms *ms, void *ibuf, u_int len) 260 { 261 int dx, dy, dz, dw; 262 uint32_t buttons = 0; 263 int i, flags, s; 264 265 DPRINTFN(5,("hidms_intr: len=%d\n", len)); 266 267 flags = WSMOUSE_INPUT_DELTA; /* equals 0 */ 268 269 dx = hid_get_data(ibuf, &ms->hidms_loc_x); 270 if (ms->flags & HIDMS_ABS) { 271 flags |= (WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 272 dy = hid_get_data(ibuf, &ms->hidms_loc_y); 273 tpcalib_trans(&ms->sc_tpcalib, dx, dy, &dx, &dy); 274 } else 275 dy = -hid_get_data(ibuf, &ms->hidms_loc_y); 276 dz = hid_get_data(ibuf, &ms->hidms_loc_z); 277 dw = hid_get_data(ibuf, &ms->hidms_loc_w); 278 279 if (ms->flags & HIDMS_REVZ) 280 dz = -dz; 281 for (i = 0; i < ms->nbuttons; i++) 282 if (hid_get_data(ibuf, &ms->hidms_loc_btn[i])) 283 buttons |= (1 << HIDMS_BUT(i)); 284 285 if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || 286 buttons != ms->hidms_buttons) { 287 DPRINTFN(10, ("hidms_intr: x:%d y:%d z:%d w:%d buttons:0x%x\n", 288 dx, dy, dz, dw, buttons)); 289 ms->hidms_buttons = buttons; 290 if (ms->hidms_wsmousedev != NULL) { 291 s = spltty(); 292 wsmouse_input(ms->hidms_wsmousedev, buttons, dx, dy, dz, 293 dw, flags); 294 splx(s); 295 } 296 } 297 } 298