adb_ktm.c revision 1.1 1 /* $NetBSD: adb_ktm.c,v 1.1 2019/09/08 05:55:15 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.1 2019/09/08 05:55:15 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 <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
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
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);
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 uint8_t addr;
179
180 /* Found Kensington Turbo Mouse */
181
182 /*
183 * byte 0
184 - 0x80 enables EMP output
185 - 0x08 seems to map both buttons together
186 - 0x04 enables the 2nd button
187 - initialized to 0x20 on power up, no idea what that does
188
189 * byte 1 assigns what which button does
190 - 0x08 - button 1 - 1, button 2 - nothing
191 - 0x09 - both buttons - 1
192 - 0x0a - butoon 1 - 1, button 2 - toggle 1
193 - 0x0b - button 1 - 1, button 2 - nothing
194 - 0x0c - button 1 - 1, button 2 - 2
195 - 0x0e - button 1 - 1, button 2 - 3
196 - 0x0f - button 1 - 1, button 2 - toggle 3
197 - 0x10 - button 1 toggle 1, button 2 nothing
198 - 0x11 - button 1 - toggle 1, button 2 - 1
199 - 0x12 - both toggle 1
200 - 0x14 - button 1 toggle 1, button 2 - 2
201 - 0x21 - button 1 - 2, button 2 - 1
202 - 0x31 - button 1 - 3, button 2 - 1
203
204 * byte 2 - 0x40 on powerup, seems to do nothing
205 * byte 3 - 0x01 on powerup, seems to do nothing
206 * byte 4 programs a delay for button presses, apparently in 1/100 seconds
207 * byte 5 and 6 init to 0xff
208 * byte 7 is a simple XOR checksum, writes will only stick if it's valid
209 as in, b[7] = (b[0] ^ b[1] ^ ... ^ b[6]) ^ 0xff
210 */
211
212 /* this seems to be the most reasonable default */
213 uint8_t data[8] = { 0xa5, 0x0e, 0, 0, 1, 0xff, 0xff, 0 };
214
215 addr = sc->sc_adbdev->current_addr;
216 memcpy(sc->sc_config, data, sizeof(data));
217
218 sc->sc_left = 1;
219 sc->sc_right = 3;
220
221 ktm_buttons(sc);
222
223 #ifdef KTM_DEBUG
224 {
225 int i;
226 ktm_send_sync(sc, ADBTALK(addr, 2), 0, NULL);
227 printf("reg *");
228 for (i = 0; i < sc->sc_msg_len; i++)
229 printf(" %02x", sc->sc_buffer[i]);
230 printf("\n");
231 }
232 #endif
233
234 ktm_write_config(sc);
235
236 #ifdef KTM_DEBUG
237 int i, reg;
238 for (reg = 1; reg < 4; reg++) {
239 ktm_send_sync(sc, ADBTALK(addr, reg), 0, NULL);
240 printf("reg %d", reg);
241 for (i = 0; i < sc->sc_msg_len; i++)
242 printf(" %02x", sc->sc_buffer[i]);
243 printf("\n");
244 }
245 #endif
246 ret = sysctl_createv(NULL, 0, NULL, &me,
247 CTLFLAG_READWRITE,
248 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
249 NULL, 0, NULL, 0,
250 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
251
252 ret = sysctl_createv(NULL, 0, NULL, &node,
253 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
254 CTLTYPE_INT, "left", "left button assigmnent",
255 sysctl_ktm_left, 1, (void *)sc, 0,
256 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
257
258 ret = sysctl_createv(NULL, 0, NULL, &node,
259 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
260 CTLTYPE_INT, "right", "right button assigmnent",
261 sysctl_ktm_right, 1, (void *)sc, 0,
262 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
263 __USE(ret);
264 }
265
266 static void
267 ktm_handler(void *cookie, int len, uint8_t *data)
268 {
269 struct ktm_softc *sc = cookie;
270
271 #ifdef KTM_DEBUG
272 int i;
273 printf("%s: %02x - ", device_xname(sc->sc_dev), sc->sc_us);
274 for (i = 0; i < len; i++) {
275 printf(" %02x", data[i]);
276 }
277 printf("\n");
278 #endif
279 if (len >= 2) {
280 memcpy(sc->sc_buffer, &data[2], len - 2);
281 sc->sc_msg_len = len - 2;
282 if (data[1] == sc->sc_us) {
283 /* make sense of the mouse message */
284 ktm_process_event(sc, sc->sc_msg_len, sc->sc_buffer);
285 return;
286 }
287 wakeup(&sc->sc_event);
288 } else {
289 DPRINTF("bogus message\n");
290 }
291 }
292
293 static void
294 ktm_process_event(struct ktm_softc *sc, int len, uint8_t *buffer)
295 {
296 int buttons = 0, mask, dx, dy, i;
297 int button_bit = 1;
298
299 /* Classic Mouse Protocol (up to 2 buttons) */
300 for (i = 0; i < 2; i++, button_bit <<= 1)
301 /* 0 when button down */
302 if (!(buffer[i] & 0x80))
303 buttons |= button_bit;
304 else
305 buttons &= ~button_bit;
306 /* Extended Protocol (up to 6 more buttons) */
307 for (mask = 0x80; i < len;
308 i += (mask == 0x80), button_bit <<= 1) {
309 /* 0 when button down */
310 if (!(buffer[i] & mask))
311 buttons |= button_bit;
312 else
313 buttons &= ~button_bit;
314 mask = ((mask >> 4) & 0xf) | ((mask & 0xf) << 4);
315 }
316
317 /* EMP crap, additional motion bits */
318 int shift = 7, ddx, ddy, sign, smask;
319
320 #ifdef KTM_DEBUG
321 printf("EMP packet:");
322 for (i = 0; i < len; i++)
323 printf(" %02x", buffer[i]);
324 printf("\n");
325 #endif
326 dx = (int)buffer[1] & 0x7f;
327 dy = (int)buffer[0] & 0x7f;
328 for (i = 2; i < len; i++) {
329 ddx = (buffer[i] & 0x07);
330 ddy = (buffer[i] & 0x70) >> 4;
331 dx |= (ddx << shift);
332 dy |= (ddy << shift);
333 shift += 3;
334 }
335 sign = 1 << (shift - 1);
336 smask = 0xffffffff << shift;
337 if (dx & sign)
338 dx |= smask;
339 if (dy & sign)
340 dy |= smask;
341 #ifdef KTM_DEBUG
342 printf("%d %d %08x %d\n", dx, dy, smask, shift);
343 #endif
344
345 if (sc->sc_wsmousedev)
346 wsmouse_input(sc->sc_wsmousedev, buttons,
347 dx, -dy, 0, 0,
348 WSMOUSE_INPUT_DELTA);
349 }
350
351 static int
352 ktm_enable(void *v)
353 {
354 return 0;
355 }
356
357 static int
358 ktm_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
359 {
360
361 switch (cmd) {
362 case WSMOUSEIO_GTYPE:
363 *(u_int *)data = WSMOUSE_TYPE_ADB;
364 break;
365
366 default:
367 return (EPASSTHROUGH);
368 }
369 return (0);
370 }
371
372 static void
373 ktm_disable(void *v)
374 {
375 }
376
377 static int
378 ktm_wait(struct ktm_softc *sc, int timeout)
379 {
380 int cnt = 0;
381
382 if (sc->sc_poll) {
383 while (sc->sc_msg_len == -1) {
384 sc->sc_ops->poll(sc->sc_ops->cookie);
385 }
386 } else {
387 while ((sc->sc_msg_len == -1) && (cnt < timeout)) {
388 tsleep(&sc->sc_event, 0, "ktmio", hz);
389 cnt++;
390 }
391 }
392 return (sc->sc_msg_len > 0);
393 }
394
395 static int
396 ktm_send_sync(struct ktm_softc *sc, uint8_t cmd, int len, uint8_t *msg)
397 {
398 int i;
399
400 sc->sc_msg_len = -1;
401 DPRINTF("send: %02x", cmd);
402 for (i = 0; i < len; i++)
403 DPRINTF(" %02x", msg[i]);
404 DPRINTF("\n");
405 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, len, msg);
406 ktm_wait(sc, 3);
407 return (sc->sc_msg_len != -1);
408 }
409
410 static int
411 sysctl_ktm_left(SYSCTLFN_ARGS)
412 {
413 struct sysctlnode node = *rnode;
414 struct ktm_softc *sc = node.sysctl_data;
415 int reg = sc->sc_left;
416
417 if (newp) {
418
419 /* we're asked to write */
420 node.sysctl_data = ®
421 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
422
423 reg = *(int *)node.sysctl_data;
424 if ((reg != sc->sc_left) &&
425 (reg >= 0) &&
426 (reg < 4)) {
427 sc->sc_left = reg;
428 ktm_buttons(sc);
429 ktm_write_config(sc);
430 }
431 return 0;
432 }
433 return EINVAL;
434 } else {
435
436 node.sysctl_data = ®
437 node.sysctl_size = 4;
438 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
439 }
440
441 return 0;
442 }
443
444 static int
445 sysctl_ktm_right(SYSCTLFN_ARGS)
446 {
447 struct sysctlnode node = *rnode;
448 struct ktm_softc *sc = node.sysctl_data;
449 int reg = sc->sc_right;
450
451 if (newp) {
452
453 /* we're asked to write */
454 node.sysctl_data = ®
455 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
456
457 reg = *(int *)node.sysctl_data;
458 if ((reg != sc->sc_right) &&
459 (reg >= 0) &&
460 (reg < 4)) {
461 sc->sc_right = reg;
462 ktm_buttons(sc);
463 ktm_write_config(sc);
464 }
465 return 0;
466 }
467 return EINVAL;
468 } else {
469
470 node.sysctl_data = ®
471 node.sysctl_size = 4;
472 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
473 }
474
475 return 0;
476 }
477
478 SYSCTL_SETUP(sysctl_ktm_setup, "sysctl ktm subtree setup")
479 {
480
481 sysctl_createv(NULL, 0, NULL, NULL,
482 CTLFLAG_PERMANENT,
483 CTLTYPE_NODE, "machdep", NULL,
484 NULL, 0, NULL, 0,
485 CTL_MACHDEP, CTL_EOL);
486 }
487