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