1/*	$NetBSD: adb_kbd.c,v 1.33 2022/05/14 01:16:55 manu Exp $	*/
2
3/*
4 * Copyright (C) 1998	Colin Wood
5 * Copyright (C) 2006, 2007 Michael Lorenz
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by Colin Wood.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: adb_kbd.c,v 1.33 2022/05/14 01:16:55 manu Exp $");
36
37#ifdef _KERNEL_OPT
38#include "opt_ddb.h"
39#endif
40
41#include <sys/param.h>
42#include <sys/device.h>
43#include <sys/fcntl.h>
44#include <sys/poll.h>
45#include <sys/select.h>
46#include <sys/proc.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/sysctl.h>
50
51#include <dev/wscons/wsconsio.h>
52#include <dev/wscons/wskbdvar.h>
53#include <dev/wscons/wsksymdef.h>
54#include <dev/wscons/wsksymvar.h>
55#include <dev/wscons/wsmousevar.h>
56
57#include <dev/sysmon/sysmonvar.h>
58#include <dev/sysmon/sysmon_taskq.h>
59
60#include <machine/autoconf.h>
61#include <machine/keyboard.h>
62#include <machine/adbsys.h>
63
64#include <dev/adb/adbvar.h>
65#include <dev/adb/adb_keymap.h>
66
67#include "ioconf.h"
68
69#include "opt_wsdisplay_compat.h"
70#include "opt_adbkbd.h"
71#include "adbdebug.h"
72#include "wsmouse.h"
73
74struct adbkbd_softc {
75	device_t sc_dev;
76	struct adb_device *sc_adbdev;
77	struct adb_bus_accessops *sc_ops;
78	device_t sc_wskbddev;
79#if NWSMOUSE > 0
80	device_t sc_wsmousedev;
81#endif
82	struct sysmon_pswitch sc_sm_pbutton;
83	int sc_leds;
84	int sc_have_led_control;
85	int sc_power_button_delay;
86	int sc_msg_len;
87	int sc_event;
88	int sc_poll;
89	int sc_polled_chars;
90	int sc_trans[3];
91	int sc_capslock;
92	uint32_t sc_timestamp;
93#ifdef WSDISPLAY_COMPAT_RAWKBD
94	int sc_rawkbd;
95#endif
96	int sc_emul_usb;
97	bool sc_power_dbg;
98
99	uint32_t sc_power;
100	uint8_t sc_buffer[16];
101	uint8_t sc_pollbuf[16];
102	uint8_t sc_us, sc_pe;
103};
104
105/*
106 * Function declarations.
107 */
108static int	adbkbd_match(device_t, cfdata_t, void *);
109static void	adbkbd_attach(device_t, device_t, void *);
110
111static void	adbkbd_initleds(struct adbkbd_softc *);
112static void	adbkbd_keys(struct adbkbd_softc *, uint8_t, uint8_t);
113static inline void adbkbd_key(struct adbkbd_softc *, uint8_t);
114static int	adbkbd_wait(struct adbkbd_softc *, int);
115
116/* Driver definition. */
117CFATTACH_DECL_NEW(adbkbd, sizeof(struct adbkbd_softc),
118    adbkbd_match, adbkbd_attach, NULL, NULL);
119
120static int adbkbd_enable(void *, int);
121static int adbkbd_ioctl(void *, u_long, void *, int, struct lwp *);
122static void adbkbd_set_leds(void *, int);
123static void adbkbd_handler(void *, int, uint8_t *);
124static void adbkbd_powerbutton(void *);
125
126struct wskbd_accessops adbkbd_accessops = {
127	adbkbd_enable,
128	adbkbd_set_leds,
129	adbkbd_ioctl,
130};
131
132static void adbkbd_cngetc(void *, u_int *, int *);
133static void adbkbd_cnpollc(void *, int);
134
135struct wskbd_consops adbkbd_consops = {
136	adbkbd_cngetc,
137	adbkbd_cnpollc,
138};
139
140struct wskbd_mapdata adbkbd_keymapdata = {
141	akbd_keydesctab,
142#ifdef ADBKBD_LAYOUT
143	ADBKBD_LAYOUT,
144#else
145	KB_US | KB_APPLE,
146#endif
147};
148
149#if NWSMOUSE > 0
150static int adbkms_enable(void *);
151static int adbkms_ioctl(void *, u_long, void *, int, struct lwp *);
152static void adbkms_disable(void *);
153
154const struct wsmouse_accessops adbkms_accessops = {
155	adbkms_enable,
156	adbkms_ioctl,
157	adbkms_disable,
158};
159
160static int  adbkbd_sysctl_mid(SYSCTLFN_ARGS);
161static int  adbkbd_sysctl_right(SYSCTLFN_ARGS);
162static int  adbkbd_sysctl_usb(SYSCTLFN_ARGS);
163
164#endif /* NWSMOUSE > 0 */
165
166static void adbkbd_setup_sysctl(struct adbkbd_softc *);
167
168#ifdef ADBKBD_DEBUG
169#define DPRINTF printf
170#else
171#define DPRINTF while (0) printf
172#endif
173
174static int adbkbd_is_console = 0;
175static int adbkbd_console_attached = 0;
176
177static int
178adbkbd_match(device_t parent, cfdata_t cf, void *aux)
179{
180	struct adb_attach_args *aaa = aux;
181
182	if (aaa->dev->original_addr == ADBADDR_KBD)
183		return 1;
184	else
185		return 0;
186}
187
188static void
189adbkbd_attach(device_t parent, device_t self, void *aux)
190{
191	struct adbkbd_softc *sc = device_private(self);
192	struct adb_attach_args *aaa = aux;
193	short cmd;
194	struct wskbddev_attach_args a;
195#if NWSMOUSE > 0
196	struct wsmousedev_attach_args am;
197#endif
198	uint8_t buffer[2];
199
200	sc->sc_dev = self;
201	sc->sc_ops = aaa->ops;
202	sc->sc_adbdev = aaa->dev;
203	sc->sc_adbdev->cookie = sc;
204	sc->sc_adbdev->handler = adbkbd_handler;
205	sc->sc_us = ADBTALK(sc->sc_adbdev->current_addr, 0);
206
207	sc->sc_leds = 0;	/* initially off */
208	sc->sc_have_led_control = 0;
209
210	/*
211	 * If this is != 0 then pushing the power button will not immadiately
212	 * send a shutdown event to sysmon but instead require another key
213	 * press within 5 seconds with a gap of at least two seconds. The
214	 * reason to do this is the fact that some PowerBook keyboards,
215	 * like the 2400, 3400 and original G3 have their power buttons
216	 * right next to the backspace key and it's extremely easy to hit
217	 * it by accident.
218	 * On most other keyboards the power button is sufficiently far out
219	 * of the way so we don't need this.
220	 */
221	sc->sc_power_button_delay = 0;
222	sc->sc_msg_len = 0;
223	sc->sc_poll = 0;
224	sc->sc_capslock = 0;
225	sc->sc_trans[1] = 103;	/* F11 */
226	sc->sc_trans[2] = 111;	/* F12 */
227
228	/*
229	 * Most ADB keyboards send 0x7f 0x7f when the power button is pressed.
230	 * Some older PowerBooks, like the 3400c, will send a single scancode
231	 * 0x7e instead. Unfortunately Fn-Command on some more recent *Books
232	 * sends the same scancode, so by default sc_power is set to a value
233	 * that can't occur as a scancode and only set to 0x7e on hardware that
234	 * needs it
235	 */
236	sc->sc_power = 0xffff;
237	sc->sc_timestamp = 0;
238	sc->sc_emul_usb = ADB_EMUL_USB_NONE;
239#ifdef ADBKBD_POWER_DDB
240	sc->sc_power_dbg = TRUE;
241#else
242	sc->sc_power_dbg = FALSE;
243#endif
244
245	aprint_normal(" addr %d: ", sc->sc_adbdev->current_addr);
246
247	switch (sc->sc_adbdev->handler_id) {
248	case ADB_STDKBD:
249		aprint_normal("standard keyboard\n");
250		break;
251	case ADB_ISOKBD:
252		aprint_normal("standard keyboard (ISO layout)\n");
253		break;
254	case ADB_EXTKBD:
255		cmd = ADBTALK(sc->sc_adbdev->current_addr, 1);
256		sc->sc_msg_len = 0;
257		sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
258		adbkbd_wait(sc, 10);
259
260		/* Ignore Logitech MouseMan/Trackman pseudo keyboard */
261		/* XXX needs testing */
262		if (sc->sc_buffer[2] == 0x9a && sc->sc_buffer[3] == 0x20) {
263			aprint_normal("Mouseman (non-EMP) pseudo keyboard\n");
264			return;
265		} else if (sc->sc_buffer[2] == 0x9a &&
266		    sc->sc_buffer[3] == 0x21) {
267			aprint_normal("Trackman (non-EMP) pseudo keyboard\n");
268			return;
269		} else {
270			aprint_normal("extended keyboard\n");
271			adbkbd_initleds(sc);
272		}
273		break;
274	case ADB_EXTISOKBD:
275		aprint_normal("extended keyboard (ISO layout)\n");
276		adbkbd_initleds(sc);
277		break;
278	case ADB_KBDII:
279		aprint_normal("keyboard II\n");
280		break;
281	case ADB_ISOKBDII:
282		aprint_normal("keyboard II (ISO layout)\n");
283		break;
284	case ADB_PBKBD:
285		aprint_normal("PowerBook keyboard\n");
286		sc->sc_power = 0x7e;
287		sc->sc_power_button_delay = 1;
288		break;
289	case ADB_PBISOKBD:
290		aprint_normal("PowerBook keyboard (ISO layout)\n");
291		sc->sc_power = 0x7e;
292		sc->sc_power_button_delay = 1;
293		break;
294	case ADB_ADJKPD:
295		aprint_normal("adjustable keypad\n");
296		break;
297	case ADB_ADJKBD:
298		aprint_normal("adjustable keyboard\n");
299		break;
300	case ADB_ADJISOKBD:
301		aprint_normal("adjustable keyboard (ISO layout)\n");
302		break;
303	case ADB_ADJJAPKBD:
304		aprint_normal("adjustable keyboard (Japanese layout)\n");
305		break;
306	case ADB_PBEXTISOKBD:
307		aprint_normal("PowerBook extended keyboard (ISO layout)\n");
308		sc->sc_power_button_delay = 1;
309		sc->sc_power = 0x7e;
310		break;
311	case ADB_PBEXTJAPKBD:
312		aprint_normal("PowerBook extended keyboard (Japanese layout)\n");
313		sc->sc_power_button_delay = 1;
314		sc->sc_power = 0x7e;
315		break;
316	case ADB_JPKBDII:
317		aprint_normal("keyboard II (Japanese layout)\n");
318		break;
319	case ADB_PBEXTKBD:
320		aprint_normal("PowerBook extended keyboard\n");
321		sc->sc_power_button_delay = 1;
322		sc->sc_power = 0x7e;
323		break;
324	case ADB_DESIGNKBD:
325		aprint_normal("extended keyboard\n");
326		adbkbd_initleds(sc);
327		break;
328	case ADB_PBJPKBD:
329		aprint_normal("PowerBook keyboard (Japanese layout)\n");
330		sc->sc_power_button_delay = 1;
331		sc->sc_power = 0x7e;
332		break;
333	case ADB_PBG3KBD:
334		aprint_normal("PowerBook G3 keyboard\n");
335		break;
336	case ADB_PBG3JPKBD:
337		aprint_normal("PowerBook G3 keyboard (Japanese layout)\n");
338		break;
339	case ADB_IBOOKKBD:
340		aprint_normal("iBook keyboard\n");
341		break;
342	default:
343		aprint_normal("mapped device (%d)\n", sc->sc_adbdev->handler_id);
344		break;
345	}
346
347	/*
348	 * try to switch to extended protocol
349	 * as in, tell the keyboard to distinguish between left and right
350	 * Shift, Control and Alt keys
351	 */
352	cmd = ADBLISTEN(sc->sc_adbdev->current_addr, 3);
353	buffer[0] = sc->sc_adbdev->current_addr;
354	buffer[1] = 3;
355	sc->sc_msg_len = 0;
356	sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 2, buffer);
357	adbkbd_wait(sc, 10);
358
359	cmd = ADBTALK(sc->sc_adbdev->current_addr, 3);
360	sc->sc_msg_len = 0;
361	sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
362	adbkbd_wait(sc, 10);
363	if ((sc->sc_msg_len == 4) && (sc->sc_buffer[3] == 3)) {
364		aprint_verbose_dev(sc->sc_dev, "extended protocol enabled\n");
365	}
366
367#ifdef ADBKBD_DEBUG
368	cmd = ADBTALK(sc->sc_adbdev->current_addr, 1);
369	sc->sc_msg_len = 0;
370	sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
371	adbkbd_wait(sc, 10);
372	printf("buffer: %02x %02x\n", sc->sc_buffer[0], sc->sc_buffer[1]);
373#endif
374
375	if (adbkbd_is_console && (adbkbd_console_attached == 0)) {
376		wskbd_cnattach(&adbkbd_consops, sc, &adbkbd_keymapdata);
377		adbkbd_console_attached = 1;
378		a.console = 1;
379	} else {
380		a.console = 0;
381	}
382	a.keymap = &adbkbd_keymapdata;
383	a.accessops = &adbkbd_accessops;
384	a.accesscookie = sc;
385
386	sc->sc_wskbddev = config_found(self, &a, wskbddevprint,
387	    CFARGS(.iattr = "wskbddev"));
388#ifdef ADBKBD_EMUL_USB
389	/* Values from Linux's drivers/macintosh/adbhud.c */
390        switch (sc->sc_adbdev->handler_id) {
391	case ADB_ISOKBD:	/* FALLTHROUGH */
392	case ADB_EXTISOKBD:	/* FALLTHROUGH */
393	case 0x07:		/* FALLTHROUGH */
394	case ADB_ISOKBDII:	/* FALLTHROUGH */
395	case ADB_PBISOKBD:	/* FALLTHROUGH */
396	case ADB_ADJISOKBD:	/* FALLTHROUGH */
397	case ADB_PBEXTISOKBD:	/* FALLTHROUGH */
398	case 0x19:		/* FALLTHROUGH */
399	case 0x1d:		/* FALLTHROUGH */
400	case 0xc1:		/* FALLTHROUGH */
401	case ADB_IBOOKKBD:	/* FALLTHROUGH */
402	case 0xc7:
403		sc->sc_emul_usb = ADB_EMUL_USB_ISO;
404		wskbd_set_evtrans(sc->sc_wskbddev, adb_to_usb_iso, 128);
405		break;
406#ifdef notyet
407	case ADB_ADJJAPKBD:	/* FALLTHROUGH */
408	case ADB_PBEXTJAPKBD:	/* FALLTHROUGH */
409	case ADB_JPKBDII:	/* FALLTHROUGH */
410	case 0x17:		/* FALLTHROUGH */
411	case 0x1a:		/* FALLTHROUGH */
412	case ADB_PBJPKBD:	/* FALLTHROUGH */
413	case 0xc2:		/* FALLTHROUGH */
414	case 0xc5:		/* FALLTHROUGH */
415	case 0xc8:		/* FALLTHROUGH */
416	case 0xc9:
417		sc->sc_emul_usb = ADB_EMUL_USB_JIS;
418		wskbd_set_evtrans(sc->sc_wskbddev, adb_to_usb_jis, 128);
419		break;
420#endif
421	case ADB_STDKBD:	/* FALLTHROUGH */
422	case ADB_EXTKBD:	/* FALLTHROUGH */
423	case 0x03:		/* FALLTHROUGH */
424	case 0x06:		/* FALLTHROUGH */
425	case ADB_KBDII:		/* FALLTHROUGH */
426	case ADB_PBKBD:		/* FALLTHROUGH */
427	case ADB_ADJKBD:	/* FALLTHROUGH */
428	case ADB_PBEXTKBD:	/* FALLTHROUGH */
429	case ADB_DESIGNKBD:	/* FALLTHROUGH */
430	case 0x1c:		/* FALLTHROUGH */
431	case 0xc0:		/* FALLTHROUGH */
432	case ADB_PBG3KBD:	/* FALLTHROUGH */
433	case 0xc6:		/* FALLTHROUGH */
434	default:	/* default to ANSI for unknown values */
435		sc->sc_emul_usb = ADB_EMUL_USB_ANSI;
436		wskbd_set_evtrans(sc->sc_wskbddev, adb_to_usb_ansi, 128);
437		break;
438	}
439#endif /* ADBKBD_EMUL_USB */
440
441#if NWSMOUSE > 0
442	/* attach the mouse device */
443	am.accessops = &adbkms_accessops;
444	am.accesscookie = sc;
445	sc->sc_wsmousedev = config_found(self, &am, wsmousedevprint,
446	    CFARGS(.iattr = "wsmousedev"));
447#endif
448	adbkbd_setup_sysctl(sc);
449
450	/* finally register the power button */
451	sysmon_task_queue_init();
452	memset(&sc->sc_sm_pbutton, 0, sizeof(struct sysmon_pswitch));
453	sc->sc_sm_pbutton.smpsw_name = device_xname(sc->sc_dev);
454	sc->sc_sm_pbutton.smpsw_type = PSWITCH_TYPE_POWER;
455	if (sysmon_pswitch_register(&sc->sc_sm_pbutton) != 0)
456		aprint_error_dev(sc->sc_dev,
457		    "unable to register power button with sysmon\n");
458}
459
460static void
461adbkbd_handler(void *cookie, int len, uint8_t *data)
462{
463	struct adbkbd_softc *sc = cookie;
464
465#ifdef ADBKBD_DEBUG
466	int i;
467	printf("%s: %02x - ", device_xname(sc->sc_dev), sc->sc_us);
468	for (i = 0; i < len; i++) {
469		printf(" %02x", data[i]);
470	}
471	printf("\n");
472#endif
473	if (len >= 2) {
474		if (data[1] == sc->sc_us) {
475			adbkbd_keys(sc, data[2], data[3]);
476			return;
477		} else {
478			memcpy(sc->sc_buffer, data, len);
479		}
480		sc->sc_msg_len = len;
481		wakeup(&sc->sc_event);
482	} else {
483		DPRINTF("bogus message\n");
484	}
485}
486
487static int
488adbkbd_wait(struct adbkbd_softc *sc, int timeout)
489{
490	int cnt = 0;
491
492	if (sc->sc_poll) {
493		while (sc->sc_msg_len == 0) {
494			sc->sc_ops->poll(sc->sc_ops->cookie);
495		}
496	} else {
497		while ((sc->sc_msg_len == 0) && (cnt < timeout)) {
498			tsleep(&sc->sc_event, 0, "adbkbdio", hz);
499			cnt++;
500		}
501	}
502	return (sc->sc_msg_len > 0);
503}
504
505static void
506adbkbd_keys(struct adbkbd_softc *sc, uint8_t k1, uint8_t k2)
507{
508
509	/* keyboard event processing */
510
511	DPRINTF("[%02x %02x]", k1, k2);
512
513	if (((k1 == k2) && (k1 == 0x7f)) || (k1 == sc->sc_power)) {
514		uint32_t now = time_second;
515		uint32_t diff = now - sc->sc_timestamp;
516
517		sc->sc_timestamp = now;
518		if (((diff > 1) && (diff < 5)) ||
519		     (sc->sc_power_button_delay == 0)) {
520#ifdef DDB
521			if (sc->sc_power_dbg) {
522				Debugger();
523				return;
524			}
525#endif
526			/* power button, report to sysmon */
527			sc->sc_pe = k1;
528			sysmon_task_queue_sched(0, adbkbd_powerbutton, sc);
529		}
530	} else {
531
532		adbkbd_key(sc, k1);
533		if (k2 != 0xff)
534			adbkbd_key(sc, k2);
535	}
536}
537
538static void
539adbkbd_powerbutton(void *cookie)
540{
541	struct adbkbd_softc *sc = cookie;
542
543	sysmon_pswitch_event(&sc->sc_sm_pbutton,
544	    ADBK_PRESS(sc->sc_pe) ? PSWITCH_EVENT_PRESSED :
545	    PSWITCH_EVENT_RELEASED);
546
547}
548
549static inline void
550adbkbd_key(struct adbkbd_softc *sc, uint8_t k)
551{
552
553	if (sc->sc_poll) {
554		if (sc->sc_polled_chars >= 16) {
555			aprint_error_dev(sc->sc_dev,"polling buffer is full\n");
556		}
557		sc->sc_pollbuf[sc->sc_polled_chars] = k;
558		sc->sc_polled_chars++;
559		return;
560	}
561
562#if NWSMOUSE > 0
563	/* translate some keys to mouse events */
564	if (sc->sc_wsmousedev != NULL) {
565		if (ADBK_KEYVAL(k) == sc->sc_trans[1]) {
566			wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 2 : 0,
567			      0, 0, 0, 0,
568			      WSMOUSE_INPUT_DELTA);
569			return;
570		}
571		if (ADBK_KEYVAL(k) == sc->sc_trans[2]) {
572			wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 4 : 0,
573			      0, 0, 0, 0,
574			      WSMOUSE_INPUT_DELTA);
575			return;
576		}
577	}
578#endif
579
580#ifdef WSDISPLAY_COMPAT_RAWKBD
581	if (sc->sc_rawkbd) {
582		char cbuf[2];
583		int s;
584
585		cbuf[0] = k;
586
587		s = spltty();
588		wskbd_rawinput(sc->sc_wskbddev, cbuf, 1);
589		splx(s);
590	} else {
591#endif
592
593	if (ADBK_KEYVAL(k) == 0x39) {
594		/* caps lock - send up and down */
595		if (ADBK_PRESS(k) != sc->sc_capslock) {
596			sc->sc_capslock = ADBK_PRESS(k);
597			wskbd_input(sc->sc_wskbddev,
598			    WSCONS_EVENT_KEY_DOWN, 0x39);
599			wskbd_input(sc->sc_wskbddev,
600			    WSCONS_EVENT_KEY_UP, 0x39);
601		}
602	} else {
603		/* normal event */
604		int type;
605
606		type = ADBK_PRESS(k) ?
607		    WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
608		wskbd_input(sc->sc_wskbddev, type, ADBK_KEYVAL(k));
609	}
610#ifdef WSDISPLAY_COMPAT_RAWKBD
611	}
612#endif
613}
614
615/*
616 * Set the keyboard LED's.
617 *
618 * Automatically translates from ioctl/softc format to the
619 * actual keyboard register format
620 */
621static void
622adbkbd_set_leds(void *cookie, int leds)
623{
624	struct adbkbd_softc *sc = cookie;
625	int aleds;
626	short cmd;
627	uint8_t buffer[2];
628
629	DPRINTF("adbkbd_set_leds: %02x\n", leds);
630	if ((leds & 0x07) == (sc->sc_leds & 0x07))
631		return;
632
633 	if (sc->sc_have_led_control) {
634
635		aleds = (~leds & 0x04) | 3;
636		if (leds & 1)
637			aleds &= ~2;
638		if (leds & 2)
639			aleds &= ~1;
640
641		buffer[0] = 0xff;
642		buffer[1] = aleds | 0xf8;
643
644		cmd = ADBLISTEN(sc->sc_adbdev->current_addr, 2);
645		sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 2,
646		    buffer);
647	}
648
649	sc->sc_leds = leds & 7;
650}
651
652static void
653adbkbd_initleds(struct adbkbd_softc *sc)
654{
655	short cmd;
656
657	/* talk R2 */
658	cmd = ADBTALK(sc->sc_adbdev->current_addr, 2);
659	sc->sc_msg_len = 0;
660	sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
661	if (!adbkbd_wait(sc, 10)) {
662		aprint_error_dev(sc->sc_dev, "unable to read LED state\n");
663		return;
664	}
665	sc->sc_have_led_control = 1;
666	DPRINTF("have LED control\n");
667	return;
668}
669
670static int
671adbkbd_enable(void *v, int on)
672{
673	return 0;
674}
675
676static int
677adbkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
678{
679	struct adbkbd_softc *sc = (struct adbkbd_softc *) v;
680
681	switch (cmd) {
682
683	case WSKBDIO_GTYPE:
684		if (sc->sc_emul_usb != ADB_EMUL_USB_NONE) {
685			*(int *)data = WSKBD_TYPE_USB;
686		} else {
687			*(int *)data = WSKBD_TYPE_ADB;
688		}
689		return 0;
690	case WSKBDIO_SETLEDS:
691		adbkbd_set_leds(sc, *(int *)data);
692		return 0;
693	case WSKBDIO_GETLEDS:
694		*(int *)data = sc->sc_leds;
695		return 0;
696#ifdef WSDISPLAY_COMPAT_RAWKBD
697	case WSKBDIO_SETMODE:
698		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
699		return 0;
700#endif
701	}
702
703	return EPASSTHROUGH;
704}
705
706int
707adbkbd_cnattach(void)
708{
709
710	adbkbd_is_console = 1;
711	return 0;
712}
713
714static void
715adbkbd_cngetc(void *v, u_int *type, int *data)
716{
717	struct adbkbd_softc *sc = v;
718	int key, press, val;
719	int s;
720
721	s = splhigh();
722
723	KASSERT(sc->sc_poll);
724
725	DPRINTF("polling...");
726	while (sc->sc_polled_chars == 0) {
727		sc->sc_ops->poll(sc->sc_ops->cookie);
728	}
729	DPRINTF(" got one\n");
730	splx(s);
731
732	key = sc->sc_pollbuf[0];
733	sc->sc_polled_chars--;
734	memmove(sc->sc_pollbuf, sc->sc_pollbuf + 1,
735		sc->sc_polled_chars);
736
737	press = ADBK_PRESS(key);
738	val = ADBK_KEYVAL(key);
739
740	*data = val;
741	*type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
742}
743
744static void
745adbkbd_cnpollc(void *v, int on)
746{
747	struct adbkbd_softc *sc = v;
748
749	sc->sc_poll = on;
750	if (!on) {
751		int i;
752
753		/* feed the poll buffer's content to wskbd */
754		for (i = 0; i < sc->sc_polled_chars; i++) {
755			adbkbd_key(sc, sc->sc_pollbuf[i]);
756		}
757		sc->sc_polled_chars = 0;
758	}
759}
760
761#if NWSMOUSE > 0
762/* stuff for the pseudo mouse */
763static int
764adbkms_enable(void *v)
765{
766	return 0;
767}
768
769static int
770adbkms_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
771{
772
773	switch (cmd) {
774	case WSMOUSEIO_GTYPE:
775		*(u_int *)data = WSMOUSE_TYPE_PSEUDO;
776		break;
777
778	default:
779		return (EPASSTHROUGH);
780	}
781	return (0);
782}
783
784static void
785adbkms_disable(void *v)
786{
787}
788
789static int
790adbkbd_sysctl_mid(SYSCTLFN_ARGS)
791{
792	struct sysctlnode node = *rnode;
793	struct adbkbd_softc *sc=(struct adbkbd_softc *)node.sysctl_data;
794	const int *np = newp;
795	int reg;
796
797	DPRINTF("adbkbd_sysctl_mid\n");
798	reg = sc->sc_trans[1];
799	if (np) {
800		/* we're asked to write */
801		node.sysctl_data = &reg;
802		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
803
804			sc->sc_trans[1] = *(int *)node.sysctl_data;
805			return 0;
806		}
807		return EINVAL;
808	} else {
809		node.sysctl_data = &reg;
810		node.sysctl_size = 4;
811		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
812	}
813}
814
815static int
816adbkbd_sysctl_right(SYSCTLFN_ARGS)
817{
818	struct sysctlnode node = *rnode;
819	struct adbkbd_softc *sc=(struct adbkbd_softc *)node.sysctl_data;
820	const int *np = newp;
821	int reg;
822
823	DPRINTF("adbkbd_sysctl_right\n");
824	reg = sc->sc_trans[2];
825	if (np) {
826		/* we're asked to write */
827		node.sysctl_data = &reg;
828		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
829
830			sc->sc_trans[2] = *(int *)node.sysctl_data;
831			return 0;
832		}
833		return EINVAL;
834	} else {
835		node.sysctl_data = &reg;
836		node.sysctl_size = 4;
837		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
838	}
839}
840
841#endif /* NWSMOUSE > 0 */
842
843static int
844adbkbd_sysctl_usb(SYSCTLFN_ARGS)
845{
846	struct sysctlnode node = *rnode;
847	struct adbkbd_softc *sc=(struct adbkbd_softc *)node.sysctl_data;
848	const int *np = newp;
849	int reg;
850
851	DPRINTF("%s\n", __func__);
852	reg = sc->sc_emul_usb;
853	if (np) {
854		/* we're asked to write */
855		node.sysctl_data = &reg;
856		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
857
858			sc->sc_emul_usb = *(int *)node.sysctl_data;
859			switch (sc->sc_emul_usb) {
860			case ADB_EMUL_USB_NONE:
861				wskbd_set_evtrans(sc->sc_wskbddev, NULL, 0);
862				break;
863			case ADB_EMUL_USB_ANSI:
864				wskbd_set_evtrans(sc->sc_wskbddev,
865				    adb_to_usb_ansi, 128);
866				break;
867			case ADB_EMUL_USB_ISO:
868				wskbd_set_evtrans(sc->sc_wskbddev,
869				    adb_to_usb_iso, 128);
870				break;
871			case ADB_EMUL_USB_JIS:
872				wskbd_set_evtrans(sc->sc_wskbddev,
873				    adb_to_usb_jis, 128);
874				break;
875			default:
876				return EINVAL;
877				break;
878			}
879			return 0;
880		}
881		return EINVAL;
882	} else {
883		node.sysctl_data = &reg;
884		node.sysctl_size = sizeof(reg);
885		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
886	}
887}
888
889static int
890adbkbd_sysctl_dbg(SYSCTLFN_ARGS)
891{
892	struct sysctlnode node = *rnode;
893	struct adbkbd_softc *sc=(struct adbkbd_softc *)node.sysctl_data;
894	const int *np = newp;
895	bool reg;
896
897	DPRINTF("%s\n", __func__);
898	reg = sc->sc_power_dbg;
899	if (np) {
900		/* we're asked to write */
901		node.sysctl_data = &reg;
902		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
903
904			sc->sc_power_dbg = *(bool *)node.sysctl_data;
905			return 0;
906		}
907		return EINVAL;
908	} else {
909		node.sysctl_data = &reg;
910		node.sysctl_size = sizeof(reg);
911		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
912	}
913}
914
915static void
916adbkbd_setup_sysctl(struct adbkbd_softc *sc)
917{
918	const struct sysctlnode *me, *node;
919	int ret;
920
921	DPRINTF("%s: sysctl setup\n", device_xname(sc->sc_dev));
922	ret = sysctl_createv(NULL, 0, NULL, &me,
923	       CTLFLAG_READWRITE,
924	       CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
925	       NULL, 0, NULL, 0,
926	       CTL_MACHDEP, CTL_CREATE, CTL_EOL);
927	ret = sysctl_createv(NULL, 0, NULL,
928	    (void *)&node,
929	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
930	    CTLTYPE_INT, "emulate_usb", "USB keyboard emulation",
931	    adbkbd_sysctl_usb, 1, (void *)sc, 0, CTL_MACHDEP,
932	    me->sysctl_num, CTL_CREATE, CTL_EOL);
933	ret = sysctl_createv(NULL, 0, NULL,
934	    (void *)&node,
935	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
936	    CTLTYPE_BOOL, "power_ddb", "power button triggers ddb",
937	    adbkbd_sysctl_dbg, 1, (void *)sc, 0, CTL_MACHDEP,
938	    me->sysctl_num, CTL_CREATE, CTL_EOL);
939#if NWSMOUSE > 0
940	if (sc->sc_wsmousedev != NULL) {
941		ret = sysctl_createv(NULL, 0, NULL,
942		    (void *)&node,
943		    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
944		    CTLTYPE_INT, "middle", "middle mouse button",
945		    adbkbd_sysctl_mid, 1, (void *)sc, 0, CTL_MACHDEP,
946		    me->sysctl_num, CTL_CREATE, CTL_EOL);
947
948		ret = sysctl_createv(NULL, 0, NULL,
949		    (void *)&node,
950		    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
951		    CTLTYPE_INT, "right", "right mouse button",
952		    adbkbd_sysctl_right, 2, (void *)sc, 0, CTL_MACHDEP,
953		    me->sysctl_num, CTL_CREATE, CTL_EOL);
954	}
955#endif /* NWSMOUSE > 0 */
956
957	(void)ret;
958}
959
960SYSCTL_SETUP(sysctl_adbkbdtrans_setup, "adbkbd translator setup")
961{
962
963	sysctl_createv(NULL, 0, NULL, NULL,
964		       CTLFLAG_PERMANENT,
965		       CTLTYPE_NODE, "machdep", NULL,
966		       NULL, 0, NULL, 0,
967		       CTL_MACHDEP, CTL_EOL);
968}
969