1 /* $NetBSD: adb_ktm.c,v 1.9 2025/06/16 07:51:16 macallan Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: adb_ktm.c,v 1.9 2025/06/16 07:51:16 macallan Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/sysctl.h> 35 #include <sys/condvar.h> 36 37 #include <machine/autoconf.h> 38 39 #include <dev/wscons/wsconsio.h> 40 #include <dev/wscons/wsmousevar.h> 41 42 #include <dev/adb/adbvar.h> 43 44 #include "adbdebug.h" 45 46 #ifdef KTM_DEBUG 47 #define DPRINTF printf 48 #else 49 #define DPRINTF while (0) printf 50 #endif 51 52 /* 53 * State info, per mouse instance. 54 */ 55 struct ktm_softc { 56 device_t sc_dev; 57 struct adb_device *sc_adbdev; 58 struct adb_bus_accessops *sc_ops; 59 60 uint8_t sc_us; /* cmd to watch for */ 61 device_t sc_wsmousedev; 62 /* buffers */ 63 uint8_t sc_config[8]; 64 int sc_left; 65 int sc_right; 66 int sc_poll; 67 int sc_msg_len; 68 kcondvar_t sc_event; 69 kmutex_t sc_interlock; 70 uint8_t sc_buffer[16]; 71 }; 72 73 /* 74 * Function declarations. 75 */ 76 static int ktm_match(device_t, cfdata_t, void *); 77 static void ktm_attach(device_t, device_t, void *); 78 static void ktm_init(struct ktm_softc *); 79 static void ktm_write_config(struct ktm_softc *); 80 static void ktm_buttons(struct ktm_softc *); 81 static void ktm_process_event(struct ktm_softc *, int, uint8_t *); 82 static int ktm_send_sync(struct ktm_softc *, uint8_t, int, uint8_t *); 83 84 /* Driver definition. */ 85 CFATTACH_DECL_NEW(ktm, sizeof(struct ktm_softc), 86 ktm_match, ktm_attach, NULL, NULL); 87 88 static int ktm_enable(void *); 89 static int ktm_ioctl(void *, u_long, void *, int, struct lwp *); 90 static void ktm_disable(void *); 91 92 static void ktm_handler(void *, int, uint8_t *); 93 static int ktm_wait(struct ktm_softc *, int); 94 static int sysctl_ktm_left(SYSCTLFN_ARGS); 95 static int sysctl_ktm_right(SYSCTLFN_ARGS); 96 97 const struct wsmouse_accessops ktm_accessops = { 98 ktm_enable, 99 ktm_ioctl, 100 ktm_disable, 101 }; 102 103 static int 104 ktm_match(device_t parent, cfdata_t cf, void *aux) 105 { 106 struct adb_attach_args *aaa = aux; 107 if ((aaa->dev->original_addr == ADBADDR_MS) && 108 (aaa->dev->handler_id == ADBMS_TURBO)) 109 return 50; /* beat out adbms */ 110 else 111 return 0; 112 } 113 114 static void 115 ktm_attach(device_t parent, device_t self, void *aux) 116 { 117 struct ktm_softc *sc = device_private(self); 118 struct adb_attach_args *aaa = aux; 119 struct wsmousedev_attach_args a; 120 121 sc->sc_dev = self; 122 sc->sc_ops = aaa->ops; 123 sc->sc_adbdev = aaa->dev; 124 sc->sc_adbdev->cookie = sc; 125 sc->sc_adbdev->handler = ktm_handler; 126 mutex_init(&sc->sc_interlock, MUTEX_DEFAULT, IPL_NONE); 127 cv_init(&sc->sc_event, "ktm"); 128 sc->sc_us = ADBTALK(sc->sc_adbdev->current_addr, 0); 129 printf(" addr %d: Kensington Turbo Mouse\n", 130 sc->sc_adbdev->current_addr); 131 132 sc->sc_poll = 0; 133 sc->sc_msg_len = 0; 134 135 ktm_init(sc); 136 137 a.accessops = &ktm_accessops; 138 a.accesscookie = sc; 139 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE); 140 } 141 142 static int 143 ktm_turbo_csum(uint8_t *d) 144 { 145 int i = 0, sum = 0; 146 147 for (i = 0; i < 7; i++) 148 sum ^= d[i]; 149 return (sum ^ 0xff); 150 } 151 152 static void 153 ktm_write_config(struct ktm_softc *sc) 154 { 155 uint8_t addr = sc->sc_adbdev->current_addr; 156 157 ktm_send_sync(sc, ADBFLUSH(addr), 0, NULL); 158 sc->sc_config[7] = ktm_turbo_csum(sc->sc_config); 159 ktm_send_sync(sc, ADBLISTEN(addr, 2), 8, sc->sc_config); 160 } 161 162 static uint8_t button_to_reg[] = {0, 1, 4, 6}; 163 164 static void ktm_buttons(struct ktm_softc *sc) 165 { 166 uint8_t reg; 167 168 reg = button_to_reg[sc->sc_right] | 169 (button_to_reg[sc->sc_left] << 3); 170 sc->sc_config[1] = reg; 171 } 172 173 static void 174 ktm_init(struct ktm_softc *sc) 175 { 176 const struct sysctlnode *me = NULL, *node = NULL; 177 int ret; 178 179 /* Found Kensington Turbo Mouse */ 180 181 /* 182 * byte 0 183 - 0x80 enables EMP output 184 - 0x08 seems to map both buttons together 185 - 0x04 enables the 2nd button 186 - initialized to 0x20 on power up, no idea what that does 187 188 * byte 1 assigns what which button does 189 - 0x08 - button 1 - 1, button 2 - nothing 190 - 0x09 - both buttons - 1 191 - 0x0a - button 1 - 1, button 2 - toggle 1 192 - 0x0b - button 1 - 1, button 2 - nothing 193 - 0x0c - button 1 - 1, button 2 - 2 194 - 0x0e - button 1 - 1, button 2 - 3 195 - 0x0f - button 1 - 1, button 2 - toggle 3 196 - 0x10 - button 1 toggle 1, button 2 nothing 197 - 0x11 - button 1 - toggle 1, button 2 - 1 198 - 0x12 - both toggle 1 199 - 0x14 - button 1 toggle 1, button 2 - 2 200 - 0x21 - button 1 - 2, button 2 - 1 201 - 0x31 - button 1 - 3, button 2 - 1 202 203 * byte 2 - 0x40 on powerup, seems to do nothing 204 * byte 3 - 0x01 on powerup, seems to do nothing 205 * byte 4 programs a delay for button presses, apparently in 1/100 seconds 206 * byte 5 and 6 init to 0xff 207 * byte 7 is a simple XOR checksum, writes will only stick if it's valid 208 as in, b[7] = (b[0] ^ b[1] ^ ... ^ b[6]) ^ 0xff 209 */ 210 211 /* this seems to be the most reasonable default */ 212 uint8_t data[8] = { 0xa5, 0x0e, 0, 0, 1, 0xff, 0xff, 0 }; 213 214 memcpy(sc->sc_config, data, sizeof(data)); 215 216 sc->sc_left = 1; 217 sc->sc_right = 3; 218 219 ktm_buttons(sc); 220 221 #ifdef KTM_DEBUG 222 int addr = sc->sc_adbdev->current_addr; 223 { 224 int i; 225 ktm_send_sync(sc, ADBTALK(addr, 2), 0, NULL); 226 printf("reg *"); 227 for (i = 0; i < sc->sc_msg_len; i++) 228 printf(" %02x", sc->sc_buffer[i]); 229 printf("\n"); 230 } 231 #endif 232 233 ktm_write_config(sc); 234 235 #ifdef KTM_DEBUG 236 int i, reg; 237 for (reg = 1; reg < 4; reg++) { 238 ktm_send_sync(sc, ADBTALK(addr, reg), 0, NULL); 239 printf("reg %d", reg); 240 for (i = 0; i < sc->sc_msg_len; i++) 241 printf(" %02x", sc->sc_buffer[i]); 242 printf("\n"); 243 } 244 #endif 245 ret = sysctl_createv(NULL, 0, NULL, &me, 246 CTLFLAG_READWRITE, 247 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 248 NULL, 0, NULL, 0, 249 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 250 251 ret = sysctl_createv(NULL, 0, NULL, &node, 252 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 253 CTLTYPE_INT, "left", "left button assignment", 254 sysctl_ktm_left, 1, (void *)sc, 0, 255 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 256 257 ret = sysctl_createv(NULL, 0, NULL, &node, 258 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 259 CTLTYPE_INT, "right", "right button assignment", 260 sysctl_ktm_right, 1, (void *)sc, 0, 261 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 262 __USE(ret); 263 } 264 265 static void 266 ktm_handler(void *cookie, int len, uint8_t *data) 267 { 268 struct ktm_softc *sc = cookie; 269 270 #ifdef KTM_DEBUG 271 int i; 272 printf("%s: %02x - ", device_xname(sc->sc_dev), sc->sc_us); 273 for (i = 0; i < len; i++) { 274 printf(" %02x", data[i]); 275 } 276 printf("\n"); 277 #endif 278 if (len >= 2) { 279 memcpy(sc->sc_buffer, &data[2], len - 2); 280 sc->sc_msg_len = len - 2; 281 if (data[1] == sc->sc_us) { 282 /* make sense of the mouse message */ 283 ktm_process_event(sc, sc->sc_msg_len, sc->sc_buffer); 284 return; 285 } 286 cv_signal(&sc->sc_event); 287 } else { 288 DPRINTF("bogus message\n"); 289 } 290 } 291 292 static void 293 ktm_process_event(struct ktm_softc *sc, int len, uint8_t *buffer) 294 { 295 int buttons = 0, mask, dx, dy, i; 296 int button_bit = 1; 297 298 /* Classic Mouse Protocol (up to 2 buttons) */ 299 for (i = 0; i < 2; i++, button_bit <<= 1) 300 /* 0 when button down */ 301 if (!(buffer[i] & 0x80)) 302 buttons |= button_bit; 303 else 304 buttons &= ~button_bit; 305 /* Extended Protocol (up to 6 more buttons) */ 306 for (mask = 0x80; i < len; 307 i += (mask == 0x80), button_bit <<= 1) { 308 /* 0 when button down */ 309 if (!(buffer[i] & mask)) 310 buttons |= button_bit; 311 else 312 buttons &= ~button_bit; 313 mask = ((mask >> 4) & 0xf) | ((mask & 0xf) << 4); 314 } 315 316 /* EMP crap, additional motion bits */ 317 int shift = 7, ddx, ddy, sign, smask; 318 319 #ifdef KTM_DEBUG 320 printf("EMP packet:"); 321 for (i = 0; i < len; i++) 322 printf(" %02x", buffer[i]); 323 printf("\n"); 324 #endif 325 dx = (int)buffer[1] & 0x7f; 326 dy = (int)buffer[0] & 0x7f; 327 for (i = 2; i < len; i++) { 328 ddx = (buffer[i] & 0x07); 329 ddy = (buffer[i] & 0x70) >> 4; 330 dx |= (ddx << shift); 331 dy |= (ddy << shift); 332 shift += 3; 333 } 334 sign = 1 << (shift - 1); 335 smask = 0xffffffff << shift; 336 if (dx & sign) 337 dx |= smask; 338 if (dy & sign) 339 dy |= smask; 340 #ifdef KTM_DEBUG 341 printf("%d %d %08x %d\n", dx, dy, smask, shift); 342 #endif 343 344 if (sc->sc_wsmousedev) 345 wsmouse_input(sc->sc_wsmousedev, buttons, 346 dx, -dy, 0, 0, 347 WSMOUSE_INPUT_DELTA); 348 } 349 350 static int 351 ktm_enable(void *v) 352 { 353 return 0; 354 } 355 356 static int 357 ktm_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 358 { 359 360 switch (cmd) { 361 case WSMOUSEIO_GTYPE: 362 *(u_int *)data = WSMOUSE_TYPE_ADB; 363 break; 364 365 default: 366 return EPASSTHROUGH; 367 } 368 return 0; 369 } 370 371 static void 372 ktm_disable(void *v) 373 { 374 } 375 376 static int 377 ktm_wait(struct ktm_softc *sc, int timeout) 378 { 379 int cnt = 0; 380 381 if (sc->sc_poll) { 382 while (sc->sc_msg_len == -1) { 383 sc->sc_ops->poll(sc->sc_ops->cookie); 384 } 385 } else { 386 mutex_enter(&sc->sc_interlock); 387 while ((sc->sc_msg_len == -1) && (cnt < timeout)) { 388 cv_timedwait(&sc->sc_event, &sc->sc_interlock, hz); 389 cnt++; 390 } 391 mutex_exit(&sc->sc_interlock); 392 } 393 return (sc->sc_msg_len > 0); 394 } 395 396 static int 397 ktm_send_sync(struct ktm_softc *sc, uint8_t cmd, int len, uint8_t *msg) 398 { 399 int i; 400 401 sc->sc_msg_len = -1; 402 DPRINTF("send: %02x", cmd); 403 for (i = 0; i < len; i++) 404 DPRINTF(" %02x", msg[i]); 405 DPRINTF("\n"); 406 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, len, msg); 407 ktm_wait(sc, 3); 408 return (sc->sc_msg_len != -1); 409 } 410 411 static int 412 sysctl_ktm_left(SYSCTLFN_ARGS) 413 { 414 struct sysctlnode node = *rnode; 415 struct ktm_softc *sc = node.sysctl_data; 416 int reg = sc->sc_left; 417 418 if (newp) { 419 420 /* we're asked to write */ 421 node.sysctl_data = ® 422 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 423 424 reg = *(int *)node.sysctl_data; 425 if ((reg != sc->sc_left) && 426 (reg >= 0) && 427 (reg < 4)) { 428 sc->sc_left = reg; 429 ktm_buttons(sc); 430 ktm_write_config(sc); 431 } 432 return 0; 433 } 434 return EINVAL; 435 } else { 436 437 node.sysctl_data = ® 438 node.sysctl_size = 4; 439 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 440 } 441 442 return 0; 443 } 444 445 static int 446 sysctl_ktm_right(SYSCTLFN_ARGS) 447 { 448 struct sysctlnode node = *rnode; 449 struct ktm_softc *sc = node.sysctl_data; 450 int reg = sc->sc_right; 451 452 if (newp) { 453 454 /* we're asked to write */ 455 node.sysctl_data = ® 456 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 457 458 reg = *(int *)node.sysctl_data; 459 if ((reg != sc->sc_right) && 460 (reg >= 0) && 461 (reg < 4)) { 462 sc->sc_right = reg; 463 ktm_buttons(sc); 464 ktm_write_config(sc); 465 } 466 return 0; 467 } 468 return EINVAL; 469 } else { 470 471 node.sysctl_data = ® 472 node.sysctl_size = 4; 473 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 474 } 475 476 return 0; 477 } 478 479 SYSCTL_SETUP(sysctl_ktm_setup, "sysctl ktm subtree setup") 480 { 481 482 sysctl_createv(NULL, 0, NULL, NULL, 483 CTLFLAG_PERMANENT, 484 CTLTYPE_NODE, "machdep", NULL, 485 NULL, 0, NULL, 0, 486 CTL_MACHDEP, CTL_EOL); 487 } 488