xref: /netbsd-src/sys/dev/adb/adb_kbd.c (revision 267197ec1eebfcb9810ea27a89625b6ddf68e3e7)
1 /*	$NetBSD: adb_kbd.c,v 1.11 2007/10/10 18:36:52 macallan 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.11 2007/10/10 18:36:52 macallan Exp $");
36 
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/fcntl.h>
40 #include <sys/poll.h>
41 #include <sys/select.h>
42 #include <sys/proc.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/sysctl.h>
46 
47 #include <dev/wscons/wsconsio.h>
48 #include <dev/wscons/wskbdvar.h>
49 #include <dev/wscons/wsksymdef.h>
50 #include <dev/wscons/wsksymvar.h>
51 #include <dev/wscons/wsmousevar.h>
52 
53 #include <dev/sysmon/sysmonvar.h>
54 #include <dev/sysmon/sysmon_taskq.h>
55 
56 #include <machine/autoconf.h>
57 #include <machine/keyboard.h>
58 #include <machine/adbsys.h>
59 
60 #include <dev/adb/adbvar.h>
61 #include <dev/adb/adb_keymap.h>
62 
63 #include "opt_wsdisplay_compat.h"
64 #include "adbdebug.h"
65 #include "wsmouse.h"
66 
67 struct adbkbd_softc {
68 	struct device sc_dev;
69 	struct adb_device *sc_adbdev;
70 	struct adb_bus_accessops *sc_ops;
71 	struct device *sc_wskbddev;
72 #if NWSMOUSE > 0
73 	struct device *sc_wsmousedev;
74 #endif
75 	struct sysmon_pswitch sc_sm_pbutton;
76 	int sc_leds;
77 	int sc_have_led_control;
78 	int sc_msg_len;
79 	int sc_event;
80 	int sc_poll;
81 	int sc_polled_chars;
82 	int sc_trans[3];
83 	int sc_capslock;
84 	uint32_t sc_timestamp;
85 #ifdef WSDISPLAY_COMPAT_RAWKBD
86 	int sc_rawkbd;
87 #endif
88 	uint8_t sc_buffer[16];
89 	uint8_t sc_pollbuf[16];
90 	uint8_t sc_us;
91 	uint8_t sc_power, sc_pe;
92 };
93 
94 /*
95  * Function declarations.
96  */
97 static int	adbkbd_match(struct device *, struct cfdata *, void *);
98 static void	adbkbd_attach(struct device *, struct device *, void *);
99 
100 static void	adbkbd_initleds(struct adbkbd_softc *);
101 static void	adbkbd_keys(struct adbkbd_softc *, uint8_t, uint8_t);
102 static inline void adbkbd_key(struct adbkbd_softc *, uint8_t);
103 static int	adbkbd_wait(struct adbkbd_softc *, int);
104 
105 /* Driver definition. */
106 CFATTACH_DECL(adbkbd, sizeof(struct adbkbd_softc),
107     adbkbd_match, adbkbd_attach, NULL, NULL);
108 
109 extern struct cfdriver adbkbd_cd;
110 
111 static int adbkbd_enable(void *, int);
112 static int adbkbd_ioctl(void *, u_long, void *, int, struct lwp *);
113 static void adbkbd_set_leds(void *, int);
114 static void adbkbd_handler(void *, int, uint8_t *);
115 static void adbkbd_powerbutton(void *);
116 
117 struct wskbd_accessops adbkbd_accessops = {
118 	adbkbd_enable,
119 	adbkbd_set_leds,
120 	adbkbd_ioctl,
121 };
122 
123 static void adbkbd_cngetc(void *, u_int *, int *);
124 static void adbkbd_cnpollc(void *, int);
125 
126 struct wskbd_consops adbkbd_consops = {
127 	adbkbd_cngetc,
128 	adbkbd_cnpollc,
129 };
130 
131 struct wskbd_mapdata adbkbd_keymapdata = {
132 	akbd_keydesctab,
133 #ifdef AKBD_LAYOUT
134 	AKBD_LAYOUT,
135 #else
136 	KB_US,
137 #endif
138 };
139 
140 #if NWSMOUSE > 0
141 static int adbkms_enable(void *);
142 static int adbkms_ioctl(void *, u_long, void *, int, struct lwp *);
143 static void adbkms_disable(void *);
144 
145 const struct wsmouse_accessops adbkms_accessops = {
146 	adbkms_enable,
147 	adbkms_ioctl,
148 	adbkms_disable,
149 };
150 
151 static int  adbkbd_sysctl_button(SYSCTLFN_ARGS);
152 static void adbkbd_setup_sysctl(struct adbkbd_softc *);
153 
154 #endif /* NWSMOUSE > 0 */
155 
156 #ifdef ADBKBD_DEBUG
157 #define DPRINTF printf
158 #else
159 #define DPRINTF while (0) printf
160 #endif
161 
162 static int adbkbd_is_console = 0;
163 static int adbkbd_console_attached = 0;
164 
165 static int
166 adbkbd_match(parent, cf, aux)
167 	struct device *parent;
168 	struct cfdata *cf;
169 	void   *aux;
170 {
171 	struct adb_attach_args *aaa = aux;
172 
173 	if (aaa->dev->original_addr == ADBADDR_KBD)
174 		return 1;
175 	else
176 		return 0;
177 }
178 
179 static void
180 adbkbd_attach(struct device *parent, struct device *self, void *aux)
181 {
182 	struct adbkbd_softc *sc = (struct adbkbd_softc *)self;
183 	struct adb_attach_args *aaa = aux;
184 	short cmd;
185 	struct wskbddev_attach_args a;
186 #if NWSMOUSE > 0
187 	struct wsmousedev_attach_args am;
188 #endif
189 
190 	sc->sc_ops = aaa->ops;
191 	sc->sc_adbdev = aaa->dev;
192 	sc->sc_adbdev->cookie = sc;
193 	sc->sc_adbdev->handler = adbkbd_handler;
194 	sc->sc_us = ADBTALK(sc->sc_adbdev->current_addr, 0);
195 
196 	sc->sc_leds = 0;	/* initially off */
197 	sc->sc_have_led_control = 0;
198 	sc->sc_msg_len = 0;
199 	sc->sc_poll = 0;
200 	sc->sc_capslock = 0;
201 	sc->sc_trans[1] = 103;	/* F11 */
202 	sc->sc_trans[2] = 111;	/* F12 */
203 	sc->sc_power = 0x7f;
204 	sc->sc_timestamp = 0;
205 
206 	printf(" addr %d: ", sc->sc_adbdev->current_addr);
207 
208 	switch (sc->sc_adbdev->handler_id) {
209 	case ADB_STDKBD:
210 		printf("standard keyboard\n");
211 		break;
212 	case ADB_ISOKBD:
213 		printf("standard keyboard (ISO layout)\n");
214 		break;
215 	case ADB_EXTKBD:
216 		cmd = ADBTALK(sc->sc_adbdev->current_addr, 1);
217 		sc->sc_msg_len = 0;
218 		sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
219 		adbkbd_wait(sc, 10);
220 
221 		/* Ignore Logitech MouseMan/Trackman pseudo keyboard */
222 		/* XXX needs testing */
223 		if (sc->sc_buffer[2] == 0x9a && sc->sc_buffer[3] == 0x20) {
224 			printf("Mouseman (non-EMP) pseudo keyboard\n");
225 			return;
226 		} else if (sc->sc_buffer[2] == 0x9a &&
227 		    sc->sc_buffer[3] == 0x21) {
228 			printf("Trackman (non-EMP) pseudo keyboard\n");
229 			return;
230 		} else {
231 			printf("extended keyboard\n");
232 			adbkbd_initleds(sc);
233 		}
234 		break;
235 	case ADB_EXTISOKBD:
236 		printf("extended keyboard (ISO layout)\n");
237 		adbkbd_initleds(sc);
238 		break;
239 	case ADB_KBDII:
240 		printf("keyboard II\n");
241 		break;
242 	case ADB_ISOKBDII:
243 		printf("keyboard II (ISO layout)\n");
244 		break;
245 	case ADB_PBKBD:
246 		printf("PowerBook keyboard\n");
247 		sc->sc_power = 0x7e;
248 		break;
249 	case ADB_PBISOKBD:
250 		printf("PowerBook keyboard (ISO layout)\n");
251 		sc->sc_power = 0x7e;
252 		break;
253 	case ADB_ADJKPD:
254 		printf("adjustable keypad\n");
255 		break;
256 	case ADB_ADJKBD:
257 		printf("adjustable keyboard\n");
258 		break;
259 	case ADB_ADJISOKBD:
260 		printf("adjustable keyboard (ISO layout)\n");
261 		break;
262 	case ADB_ADJJAPKBD:
263 		printf("adjustable keyboard (Japanese layout)\n");
264 		break;
265 	case ADB_PBEXTISOKBD:
266 		printf("PowerBook extended keyboard (ISO layout)\n");
267 		sc->sc_power = 0x7e;
268 		break;
269 	case ADB_PBEXTJAPKBD:
270 		printf("PowerBook extended keyboard (Japanese layout)\n");
271 		sc->sc_power = 0x7e;
272 		break;
273 	case ADB_JPKBDII:
274 		printf("keyboard II (Japanese layout)\n");
275 		break;
276 	case ADB_PBEXTKBD:
277 		printf("PowerBook extended keyboard\n");
278 		sc->sc_power = 0x7e;
279 		break;
280 	case ADB_DESIGNKBD:
281 		printf("extended keyboard\n");
282 		adbkbd_initleds(sc);
283 		break;
284 	case ADB_PBJPKBD:
285 		printf("PowerBook keyboard (Japanese layout)\n");
286 		sc->sc_power = 0x7e;
287 		break;
288 	case ADB_PBG3KBD:
289 		printf("PowerBook G3 keyboard\n");
290 		sc->sc_power = 0x7e;
291 		break;
292 	case ADB_PBG3JPKBD:
293 		printf("PowerBook G3 keyboard (Japanese layout)\n");
294 		sc->sc_power = 0x7e;
295 		break;
296 	case ADB_IBOOKKBD:
297 		printf("iBook keyboard\n");
298 		break;
299 	default:
300 		printf("mapped device (%d)\n", sc->sc_adbdev->handler_id);
301 		break;
302 	}
303 
304 	if (adbkbd_is_console && (adbkbd_console_attached == 0)) {
305 		wskbd_cnattach(&adbkbd_consops, sc, &adbkbd_keymapdata);
306 		adbkbd_console_attached = 1;
307 		a.console = 1;
308 	} else {
309 		a.console = 0;
310 	}
311 	a.keymap = &adbkbd_keymapdata;
312 	a.accessops = &adbkbd_accessops;
313 	a.accesscookie = sc;
314 
315 	sc->sc_wskbddev = config_found_ia(self, "wskbddev", &a, wskbddevprint);
316 
317 #if NWSMOUSE > 0
318 	/* attach the mouse device */
319 	am.accessops = &adbkms_accessops;
320 	am.accesscookie = sc;
321 	sc->sc_wsmousedev = config_found_ia(self, "wsmousedev", &am,
322 	    wsmousedevprint);
323 
324 	if (sc->sc_wsmousedev != NULL)
325 		adbkbd_setup_sysctl(sc);
326 #endif
327 
328 	/* finally register the power button */
329 	sysmon_task_queue_init();
330 	memset(&sc->sc_sm_pbutton, 0, sizeof(struct sysmon_pswitch));
331 	sc->sc_sm_pbutton.smpsw_name = sc->sc_dev.dv_xname;
332 	sc->sc_sm_pbutton.smpsw_type = PSWITCH_TYPE_POWER;
333 	if (sysmon_pswitch_register(&sc->sc_sm_pbutton) != 0)
334 		printf("%s: unable to register power button with sysmon\n",
335 		    sc->sc_dev.dv_xname);
336 }
337 
338 static void
339 adbkbd_handler(void *cookie, int len, uint8_t *data)
340 {
341 	struct adbkbd_softc *sc = cookie;
342 
343 #ifdef ADBKBD_DEBUG
344 	int i;
345 	printf("%s: %02x - ", sc->sc_dev.dv_xname, sc->sc_us);
346 	for (i = 0; i < len; i++) {
347 		printf(" %02x", data[i]);
348 	}
349 	printf("\n");
350 #endif
351 	if (len >= 2) {
352 		if (data[1] == sc->sc_us) {
353 			adbkbd_keys(sc, data[2], data[3]);
354 			return;
355 		} else {
356 			memcpy(sc->sc_buffer, data, len);
357 		}
358 		sc->sc_msg_len = len;
359 		wakeup(&sc->sc_event);
360 	} else {
361 		DPRINTF("bogus message\n");
362 	}
363 }
364 
365 static int
366 adbkbd_wait(struct adbkbd_softc *sc, int timeout)
367 {
368 	int cnt = 0;
369 
370 	if (sc->sc_poll) {
371 		while (sc->sc_msg_len == 0) {
372 			sc->sc_ops->poll(sc->sc_ops->cookie);
373 		}
374 	} else {
375 		while ((sc->sc_msg_len == 0) && (cnt < timeout)) {
376 			tsleep(&sc->sc_event, 0, "adbkbdio", hz);
377 			cnt++;
378 		}
379 	}
380 	return (sc->sc_msg_len > 0);
381 }
382 
383 static void
384 adbkbd_keys(struct adbkbd_softc *sc, uint8_t k1, uint8_t k2)
385 {
386 
387 	/* keyboard event processing */
388 
389 	DPRINTF("[%02x %02x]", k1, k2);
390 
391 	if (((k1 == k2) && (k1 == 0x7f)) || (k1 == sc->sc_power)) {
392 		uint32_t now = time_second;
393 		uint32_t diff = now - sc->sc_timestamp;
394 
395 		sc->sc_timestamp = now;
396 		if ((diff > 1) && (diff < 5)) {
397 
398 			/* power button, report to sysmon */
399 			sc->sc_pe = k1;
400 
401 			sysmon_task_queue_sched(0, adbkbd_powerbutton, sc);
402 		}
403 	} else {
404 
405 		adbkbd_key(sc, k1);
406 		if (k2 != 0xff)
407 			adbkbd_key(sc, k2);
408 	}
409 }
410 
411 static void
412 adbkbd_powerbutton(void *cookie)
413 {
414 	struct adbkbd_softc *sc = cookie;
415 
416 	sysmon_pswitch_event(&sc->sc_sm_pbutton,
417 	    ADBK_PRESS(sc->sc_pe) ? PSWITCH_EVENT_PRESSED :
418 	    PSWITCH_EVENT_RELEASED);
419 }
420 
421 static inline void
422 adbkbd_key(struct adbkbd_softc *sc, uint8_t k)
423 {
424 
425 	if (sc->sc_poll) {
426 		if (sc->sc_polled_chars >= 16) {
427 			printf("%s: polling buffer is full\n",
428 			    sc->sc_dev.dv_xname);
429 		}
430 		sc->sc_pollbuf[sc->sc_polled_chars] = k;
431 		sc->sc_polled_chars++;
432 		return;
433 	}
434 
435 #if NWSMOUSE > 0
436 	/* translate some keys to mouse events */
437 	if (sc->sc_wsmousedev != NULL) {
438 		if (ADBK_KEYVAL(k) == sc->sc_trans[1]) {
439 			wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 2 : 0,
440 			      0, 0, 0, 0,
441 			      WSMOUSE_INPUT_DELTA);
442 			return;
443 		}
444 		if (ADBK_KEYVAL(k) == sc->sc_trans[2]) {
445 			wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 4 : 0,
446 			      0, 0, 0, 0,
447 			      WSMOUSE_INPUT_DELTA);
448 			return;
449 		}
450 	}
451 #endif
452 
453 #ifdef WSDISPLAY_COMPAT_RAWKBD
454 	if (sc->sc_rawkbd) {
455 		char cbuf[2];
456 		int s;
457 
458 		cbuf[0] = k;
459 
460 		s = spltty();
461 		wskbd_rawinput(sc->sc_wskbddev, cbuf, 1);
462 		splx(s);
463 	} else {
464 #endif
465 
466 	if (ADBK_KEYVAL(k) == 0x39) {
467 		/* caps lock - send up and down */
468 		if (ADBK_PRESS(k) != sc->sc_capslock) {
469 			sc->sc_capslock = ADBK_PRESS(k);
470 			wskbd_input(sc->sc_wskbddev,
471 			    WSCONS_EVENT_KEY_DOWN, 0x39);
472 			wskbd_input(sc->sc_wskbddev,
473 			    WSCONS_EVENT_KEY_UP, 0x39);
474 		}
475 	} else {
476 		/* normal event */
477 		int type;
478 
479 		type = ADBK_PRESS(k) ?
480 		    WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
481 		wskbd_input(sc->sc_wskbddev, type, ADBK_KEYVAL(k));
482 	}
483 #ifdef WSDISPLAY_COMPAT_RAWKBD
484 	}
485 #endif
486 }
487 
488 /*
489  * Set the keyboard LED's.
490  *
491  * Automatically translates from ioctl/softc format to the
492  * actual keyboard register format
493  */
494 static void
495 adbkbd_set_leds(void *cookie, int leds)
496 {
497 	struct adbkbd_softc *sc = cookie;
498 	int aleds;
499 	short cmd;
500 	uint8_t buffer[2];
501 
502 	DPRINTF("adbkbd_set_leds: %02x\n", leds);
503 	if ((leds & 0x07) == (sc->sc_leds & 0x07))
504 		return;
505 
506  	if (sc->sc_have_led_control) {
507 
508 		aleds = (~leds & 0x04) | 3;
509 		if (leds & 1)
510 			aleds &= ~2;
511 		if (leds & 2)
512 			aleds &= ~1;
513 
514 		buffer[0] = 0xff;
515 		buffer[1] = aleds | 0xf8;
516 
517 		cmd = ADBLISTEN(sc->sc_adbdev->current_addr, 2);
518 		sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 2,
519 		    buffer);
520 	}
521 
522 	sc->sc_leds = leds & 7;
523 }
524 
525 static void
526 adbkbd_initleds(struct adbkbd_softc *sc)
527 {
528 	short cmd;
529 
530 	/* talk R2 */
531 	cmd = ADBTALK(sc->sc_adbdev->current_addr, 2);
532 	sc->sc_msg_len = 0;
533 	sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL);
534 	if (!adbkbd_wait(sc, 10)) {
535 		printf("unable to read LED state\n");
536 		return;
537 	}
538 	sc->sc_have_led_control = 1;
539 	DPRINTF("have LED control\n");
540 	return;
541 }
542 
543 static int
544 adbkbd_enable(void *v, int on)
545 {
546 	return 0;
547 }
548 
549 static int
550 adbkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
551 {
552 	struct adbkbd_softc *sc = (struct adbkbd_softc *) v;
553 
554 	switch (cmd) {
555 
556 	case WSKBDIO_GTYPE:
557 		*(int *)data = WSKBD_TYPE_ADB;
558 		return 0;
559 	case WSKBDIO_SETLEDS:
560 		adbkbd_set_leds(sc, *(int *)data);
561 		return 0;
562 	case WSKBDIO_GETLEDS:
563 		*(int *)data = sc->sc_leds;
564 		return 0;
565 #ifdef WSDISPLAY_COMPAT_RAWKBD
566 	case WSKBDIO_SETMODE:
567 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
568 		return 0;
569 #endif
570 	}
571 
572 	return EPASSTHROUGH;
573 }
574 
575 int
576 adbkbd_cnattach()
577 {
578 
579 	adbkbd_is_console = 1;
580 	return 0;
581 }
582 
583 static void
584 adbkbd_cngetc(void *v, u_int *type, int *data)
585 {
586 	struct adbkbd_softc *sc = v;
587 	int key, press, val;
588 	int s;
589 
590 	s = splhigh();
591 
592 	KASSERT(sc->sc_poll);
593 
594 	DPRINTF("polling...");
595 	while (sc->sc_polled_chars == 0) {
596 		sc->sc_ops->poll(sc->sc_ops->cookie);
597 	}
598 	DPRINTF(" got one\n");
599 	splx(s);
600 
601 	key = sc->sc_pollbuf[0];
602 	sc->sc_polled_chars--;
603 	memmove(sc->sc_pollbuf, sc->sc_pollbuf + 1,
604 		sc->sc_polled_chars);
605 
606 	press = ADBK_PRESS(key);
607 	val = ADBK_KEYVAL(key);
608 
609 	*data = val;
610 	*type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
611 }
612 
613 static void
614 adbkbd_cnpollc(void *v, int on)
615 {
616 	struct adbkbd_softc *sc = v;
617 
618 	sc->sc_poll = on;
619 	if (!on) {
620 		int i;
621 
622 		/* feed the poll buffer's content to wskbd */
623 		for (i = 0; i < sc->sc_polled_chars; i++) {
624 			adbkbd_key(sc, sc->sc_pollbuf[i]);
625 		}
626 		sc->sc_polled_chars = 0;
627 	}
628 }
629 
630 #if NWSMOUSE > 0
631 /* stuff for the pseudo mouse */
632 static int
633 adbkms_enable(void *v)
634 {
635 	return 0;
636 }
637 
638 static int
639 adbkms_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
640 {
641 
642 	switch (cmd) {
643 	case WSMOUSEIO_GTYPE:
644 		*(u_int *)data = WSMOUSE_TYPE_PSEUDO;
645 		break;
646 
647 	default:
648 		return (EPASSTHROUGH);
649 	}
650 	return (0);
651 }
652 
653 static void
654 adbkms_disable(void *v)
655 {
656 }
657 
658 static void
659 adbkbd_setup_sysctl(struct adbkbd_softc *sc)
660 {
661 	struct sysctlnode *node, *me;
662 	int ret;
663 
664 	DPRINTF("%s: sysctl setup\n", sc->sc_dev.dv_xname);
665 	ret = sysctl_createv(NULL, 0, NULL, (const struct sysctlnode **)&me,
666 	       CTLFLAG_READWRITE,
667 	       CTLTYPE_NODE, sc->sc_dev.dv_xname, NULL,
668 	       NULL, 0, NULL, 0,
669 	       CTL_MACHDEP, CTL_CREATE, CTL_EOL);
670 
671 	ret = sysctl_createv(NULL, 0, NULL,
672 	    (const struct sysctlnode **)&node,
673 	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE,
674 	    CTLTYPE_INT, "middle", "middle mouse button", adbkbd_sysctl_button,
675 		    1, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE,
676 		    CTL_EOL);
677 	node->sysctl_data = sc;
678 
679 	ret = sysctl_createv(NULL, 0, NULL,
680 	    (const struct sysctlnode **)&node,
681 	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE,
682 	    CTLTYPE_INT, "right", "right mouse button", adbkbd_sysctl_button,
683 		    2, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE,
684 		    CTL_EOL);
685 	node->sysctl_data = sc;
686 }
687 
688 static int
689 adbkbd_sysctl_button(SYSCTLFN_ARGS)
690 {
691 	struct sysctlnode node = *rnode;
692 	struct adbkbd_softc *sc=(struct adbkbd_softc *)node.sysctl_data;
693 	const int *np = newp;
694 	int btn = node.sysctl_idata, reg;
695 
696 	DPRINTF("adbkbd_sysctl_button %d\n", btn);
697 	node.sysctl_idata = sc->sc_trans[btn];
698 	reg = sc->sc_trans[btn];
699 	if (np) {
700 		/* we're asked to write */
701 		node.sysctl_data = &reg;
702 		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
703 
704 			sc->sc_trans[btn] = node.sysctl_idata;
705 			return 0;
706 		}
707 		return EINVAL;
708 	} else {
709 		node.sysctl_size = 4;
710 		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
711 	}
712 }
713 
714 SYSCTL_SETUP(sysctl_adbkbdtrans_setup, "adbkbd translator setup")
715 {
716 
717 	sysctl_createv(NULL, 0, NULL, NULL,
718 		       CTLFLAG_PERMANENT,
719 		       CTLTYPE_NODE, "machdep", NULL,
720 		       NULL, 0, NULL, 0,
721 		       CTL_MACHDEP, CTL_EOL);
722 }
723 #endif /* NWSMOUSE > 0 */
724