1 /* $NetBSD: adb_ktm.c,v 1.7 2024/02/11 10:36:40 andvar 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.7 2024/02/11 10:36:40 andvar 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 <machine/adbsys.h>
45 #include <dev/adb/adbvar.h>
46
47 #include "adbdebug.h"
48
49 #ifdef KTM_DEBUG
50 #define DPRINTF printf
51 #else
52 #define DPRINTF while (0) printf
53 #endif
54
55 /*
56 * State info, per mouse instance.
57 */
58 struct ktm_softc {
59 device_t sc_dev;
60 struct adb_device *sc_adbdev;
61 struct adb_bus_accessops *sc_ops;
62
63 uint8_t sc_us; /* cmd to watch for */
64 device_t sc_wsmousedev;
65 /* buffers */
66 uint8_t sc_config[8];
67 int sc_left;
68 int sc_right;
69 int sc_poll;
70 int sc_msg_len;
71 int sc_event;
72 uint8_t sc_buffer[16];
73 };
74
75 /*
76 * Function declarations.
77 */
78 static int ktm_match(device_t, cfdata_t, void *);
79 static void ktm_attach(device_t, device_t, void *);
80 static void ktm_init(struct ktm_softc *);
81 static void ktm_write_config(struct ktm_softc *);
82 static void ktm_buttons(struct ktm_softc *);
83 static void ktm_process_event(struct ktm_softc *, int, uint8_t *);
84 static int ktm_send_sync(struct ktm_softc *, uint8_t, int, uint8_t *);
85
86 /* Driver definition. */
87 CFATTACH_DECL_NEW(ktm, sizeof(struct ktm_softc),
88 ktm_match, ktm_attach, NULL, NULL);
89
90 static int ktm_enable(void *);
91 static int ktm_ioctl(void *, u_long, void *, int, struct lwp *);
92 static void ktm_disable(void *);
93
94 static void ktm_handler(void *, int, uint8_t *);
95 static int ktm_wait(struct ktm_softc *, int);
96 static int sysctl_ktm_left(SYSCTLFN_ARGS);
97 static int sysctl_ktm_right(SYSCTLFN_ARGS);
98
99 const struct wsmouse_accessops ktm_accessops = {
100 ktm_enable,
101 ktm_ioctl,
102 ktm_disable,
103 };
104
105 static int
ktm_match(device_t parent,cfdata_t cf,void * aux)106 ktm_match(device_t parent, cfdata_t cf, void *aux)
107 {
108 struct adb_attach_args *aaa = aux;
109 if ((aaa->dev->original_addr == ADBADDR_MS) &&
110 (aaa->dev->handler_id == ADBMS_TURBO))
111 return 50; /* beat out adbms */
112 else
113 return 0;
114 }
115
116 static void
ktm_attach(device_t parent,device_t self,void * aux)117 ktm_attach(device_t parent, device_t self, void *aux)
118 {
119 struct ktm_softc *sc = device_private(self);
120 struct adb_attach_args *aaa = aux;
121 struct wsmousedev_attach_args a;
122
123 sc->sc_dev = self;
124 sc->sc_ops = aaa->ops;
125 sc->sc_adbdev = aaa->dev;
126 sc->sc_adbdev->cookie = sc;
127 sc->sc_adbdev->handler = ktm_handler;
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
ktm_turbo_csum(uint8_t * d)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
ktm_write_config(struct ktm_softc * sc)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
ktm_buttons(struct ktm_softc * sc)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
ktm_init(struct ktm_softc * sc)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
ktm_handler(void * cookie,int len,uint8_t * data)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 wakeup(&sc->sc_event);
287 } else {
288 DPRINTF("bogus message\n");
289 }
290 }
291
292 static void
ktm_process_event(struct ktm_softc * sc,int len,uint8_t * buffer)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
ktm_enable(void * v)351 ktm_enable(void *v)
352 {
353 return 0;
354 }
355
356 static int
ktm_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)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
ktm_disable(void * v)372 ktm_disable(void *v)
373 {
374 }
375
376 static int
ktm_wait(struct ktm_softc * sc,int timeout)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 while ((sc->sc_msg_len == -1) && (cnt < timeout)) {
387 tsleep(&sc->sc_event, 0, "ktmio", hz);
388 cnt++;
389 }
390 }
391 return (sc->sc_msg_len > 0);
392 }
393
394 static int
ktm_send_sync(struct ktm_softc * sc,uint8_t cmd,int len,uint8_t * msg)395 ktm_send_sync(struct ktm_softc *sc, uint8_t cmd, int len, uint8_t *msg)
396 {
397 int i;
398
399 sc->sc_msg_len = -1;
400 DPRINTF("send: %02x", cmd);
401 for (i = 0; i < len; i++)
402 DPRINTF(" %02x", msg[i]);
403 DPRINTF("\n");
404 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, len, msg);
405 ktm_wait(sc, 3);
406 return (sc->sc_msg_len != -1);
407 }
408
409 static int
sysctl_ktm_left(SYSCTLFN_ARGS)410 sysctl_ktm_left(SYSCTLFN_ARGS)
411 {
412 struct sysctlnode node = *rnode;
413 struct ktm_softc *sc = node.sysctl_data;
414 int reg = sc->sc_left;
415
416 if (newp) {
417
418 /* we're asked to write */
419 node.sysctl_data = ®
420 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
421
422 reg = *(int *)node.sysctl_data;
423 if ((reg != sc->sc_left) &&
424 (reg >= 0) &&
425 (reg < 4)) {
426 sc->sc_left = reg;
427 ktm_buttons(sc);
428 ktm_write_config(sc);
429 }
430 return 0;
431 }
432 return EINVAL;
433 } else {
434
435 node.sysctl_data = ®
436 node.sysctl_size = 4;
437 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
438 }
439
440 return 0;
441 }
442
443 static int
sysctl_ktm_right(SYSCTLFN_ARGS)444 sysctl_ktm_right(SYSCTLFN_ARGS)
445 {
446 struct sysctlnode node = *rnode;
447 struct ktm_softc *sc = node.sysctl_data;
448 int reg = sc->sc_right;
449
450 if (newp) {
451
452 /* we're asked to write */
453 node.sysctl_data = ®
454 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
455
456 reg = *(int *)node.sysctl_data;
457 if ((reg != sc->sc_right) &&
458 (reg >= 0) &&
459 (reg < 4)) {
460 sc->sc_right = reg;
461 ktm_buttons(sc);
462 ktm_write_config(sc);
463 }
464 return 0;
465 }
466 return EINVAL;
467 } else {
468
469 node.sysctl_data = ®
470 node.sysctl_size = 4;
471 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
472 }
473
474 return 0;
475 }
476
477 SYSCTL_SETUP(sysctl_ktm_setup, "sysctl ktm subtree setup")
478 {
479
480 sysctl_createv(NULL, 0, NULL, NULL,
481 CTLFLAG_PERMANENT,
482 CTLTYPE_NODE, "machdep", NULL,
483 NULL, 0, NULL, 0,
484 CTL_MACHDEP, CTL_EOL);
485 }
486