xref: /netbsd-src/sys/dev/adb/adb_ktm.c (revision 48491f9819751125d121614a85f27fc971ad5fe6)
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 = &reg;
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 = &reg;
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 = &reg;
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 = &reg;
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