adb_ktm.c revision 1.9 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