1 /* $OpenBSD: akbd.c,v 1.16 2022/10/21 22:42:36 gkoehler Exp $ */
2 /* $NetBSD: akbd.c,v 1.17 2005/01/15 16:00:59 chs Exp $ */
3
4 /*
5 * Copyright (C) 1998 Colin Wood
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/param.h>
35 #include <sys/timeout.h>
36 #include <sys/kernel.h>
37 #include <sys/device.h>
38 #include <sys/systm.h>
39
40 #include <dev/wscons/wsconsio.h>
41 #include <dev/wscons/wskbdvar.h>
42 #include <dev/wscons/wsksymdef.h>
43 #include <dev/wscons/wsksymvar.h>
44
45 #include <machine/autoconf.h>
46 #include <machine/cpu.h>
47
48 #include <dev/adb/adb.h>
49 #include <dev/adb/akbdmap.h>
50 #include <dev/adb/akbdvar.h>
51 #include <dev/adb/keyboard.h>
52
53 /*
54 * Function declarations.
55 */
56 int akbdmatch(struct device *, void *, void *);
57 void akbdattach(struct device *, struct device *, void *);
58
59 /* Driver definition. */
60 const struct cfattach akbd_ca = {
61 sizeof(struct akbd_softc), akbdmatch, akbdattach
62 };
63 struct cfdriver akbd_cd = {
64 NULL, "akbd", DV_DULL
65 };
66
67 int akbd_enable(void *, int);
68 void akbd_set_leds(void *, int);
69 int akbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
70
71
72 struct wskbd_accessops akbd_accessops = {
73 akbd_enable,
74 akbd_set_leds,
75 akbd_ioctl,
76 };
77
78 struct wskbd_mapdata akbd_keymapdata = {
79 akbd_keydesctab,
80 #ifdef AKBD_LAYOUT
81 AKBD_LAYOUT,
82 #else
83 KB_US | KB_DEFAULT,
84 #endif
85 };
86
87 void akbd_adbcomplete(caddr_t, caddr_t, int);
88 void akbd_capslockwrapper(struct akbd_softc *, int);
89 void akbd_input(struct akbd_softc *, int);
90 void akbd_processevent(struct akbd_softc *, adb_event_t *);
91 #ifdef notyet
92 u_char getleds(int);
93 int setleds(struct akbd_softc *, u_char);
94 void blinkleds(struct akbd_softc *);
95 #endif
96
97 int
akbdmatch(struct device * parent,void * vcf,void * aux)98 akbdmatch(struct device *parent, void *vcf, void *aux)
99 {
100 struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
101
102 if (strcmp(aa_args->name, adb_device_name) != 0)
103 return (0);
104
105 if (aa_args->origaddr == ADBADDR_KBD)
106 return (1);
107 else
108 return (0);
109 }
110
111 void
akbdattach(struct device * parent,struct device * self,void * aux)112 akbdattach(struct device *parent, struct device *self, void *aux)
113 {
114 ADBSetInfoBlock adbinfo;
115 struct akbd_softc *sc = (struct akbd_softc *)self;
116 struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
117 int error, kbd_done;
118 short cmd;
119 u_char buffer[9];
120 struct wskbddev_attach_args a;
121 static int akbd_console_initted;
122 int wskbd_eligible = 1;
123
124 sc->origaddr = aa_args->origaddr;
125 sc->adbaddr = aa_args->adbaddr;
126 sc->handler_id = aa_args->handler_id;
127
128 sc->sc_leds = (u_int8_t)0x00; /* initially off */
129 sc->sc_caps = 0;
130 sc->sc_iso = 0;
131
132 adbinfo.siServiceRtPtr = (Ptr)akbd_adbcomplete;
133 adbinfo.siDataAreaAddr = (caddr_t)sc;
134
135 printf(": ");
136 switch (sc->handler_id) {
137 case ADB_STDKBD:
138 printf("standard keyboard\n");
139 break;
140 case ADB_ISOKBD:
141 printf("standard keyboard (ISO layout)\n");
142 sc->sc_iso = 1;
143 break;
144 case ADB_EXTKBD:
145 cmd = ADBTALK(sc->adbaddr, 1);
146 kbd_done =
147 (adb_op_sync((Ptr)buffer, cmd) == 0);
148
149 /* Ignore Logitech MouseMan/Trackman pseudo keyboard */
150 if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x20) {
151 printf("Mouseman (non-EMP) pseudo keyboard\n");
152 adbinfo.siServiceRtPtr = (Ptr)0;
153 adbinfo.siDataAreaAddr = (Ptr)0;
154 wskbd_eligible = 0;
155 } else if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x21) {
156 printf("Trackman (non-EMP) pseudo keyboard\n");
157 adbinfo.siServiceRtPtr = (Ptr)0;
158 adbinfo.siDataAreaAddr = (Ptr)0;
159 wskbd_eligible = 0;
160 } else {
161 printf("extended keyboard\n");
162 #ifdef notyet
163 blinkleds(sc);
164 #endif
165 }
166 break;
167 case ADB_EXTISOKBD:
168 printf("extended keyboard (ISO layout)\n");
169 sc->sc_iso = 1;
170 #ifdef notyet
171 blinkleds(sc);
172 #endif
173 break;
174 case ADB_KBDII:
175 printf("keyboard II\n");
176 break;
177 case ADB_ISOKBDII:
178 printf("keyboard II (ISO layout)\n");
179 sc->sc_iso = 1;
180 break;
181 case ADB_PBKBD:
182 printf("PowerBook keyboard\n");
183 break;
184 case ADB_PBISOKBD:
185 printf("PowerBook keyboard (ISO layout)\n");
186 sc->sc_iso = 1;
187 break;
188 case ADB_ADJKPD:
189 printf("adjustable keypad\n");
190 wskbd_eligible = 0;
191 break;
192 case ADB_ADJKBD:
193 printf("adjustable keyboard\n");
194 break;
195 case ADB_ADJISOKBD:
196 printf("adjustable keyboard (ISO layout)\n");
197 sc->sc_iso = 1;
198 break;
199 case ADB_ADJJAPKBD:
200 printf("adjustable keyboard (Japanese layout)\n");
201 break;
202 case ADB_PBEXTISOKBD:
203 printf("PowerBook extended keyboard (ISO layout)\n");
204 sc->sc_iso = 1;
205 break;
206 case ADB_PBEXTJAPKBD:
207 printf("PowerBook extended keyboard (Japanese layout)\n");
208 break;
209 case ADB_JPKBDII:
210 printf("keyboard II (Japanese layout)\n");
211 break;
212 case ADB_PBEXTKBD:
213 printf("PowerBook extended keyboard\n");
214 break;
215 case ADB_DESIGNKBD:
216 printf("extended keyboard\n");
217 #ifdef notyet
218 blinkleds(sc);
219 #endif
220 break;
221 case ADB_PBJPKBD:
222 printf("PowerBook keyboard (Japanese layout)\n");
223 break;
224 case ADB_PBG3JPKBD:
225 printf("PowerBook G3 keyboard (Japanese layout)\n");
226 break;
227 case ADB_PBG4KBD:
228 printf("PowerBook G4 keyboard (Inverted T)\n");
229 break;
230 case ADB_IBITISOKBD:
231 printf("iBook keyboard with inverted T (ISO layout)\n");
232 sc->sc_iso = 1;
233 break;
234 default:
235 printf("mapped device (%d)\n", sc->handler_id);
236 #if 0
237 wskbd_eligible = 0;
238 #endif
239 break;
240 }
241 error = set_adb_info(&adbinfo, sc->adbaddr);
242 #ifdef ADB_DEBUG
243 if (adb_debug)
244 printf("akbd: returned %d from set_adb_info\n", error);
245 #endif
246
247 if (akbd_is_console() && wskbd_eligible)
248 a.console = (++akbd_console_initted == 1);
249 else
250 a.console = 0;
251 a.keymap = &akbd_keymapdata;
252 a.accessops = &akbd_accessops;
253 a.accesscookie = sc;
254
255 sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
256 }
257
258
259 /*
260 * Handle putting the keyboard data received from the ADB into
261 * an ADB event record.
262 */
263 void
akbd_adbcomplete(caddr_t buffer,caddr_t data_area,int adb_command)264 akbd_adbcomplete(caddr_t buffer, caddr_t data_area, int adb_command)
265 {
266 adb_event_t event;
267 struct akbd_softc *sc;
268 int adbaddr;
269 #ifdef ADB_DEBUG
270 int i;
271
272 if (adb_debug)
273 printf("adb: transaction completion\n");
274 #endif
275
276 adbaddr = ADB_CMDADDR(adb_command);
277 sc = (struct akbd_softc *)data_area;
278
279 event.byte_count = buffer[0];
280 memcpy(event.bytes, buffer + 1, event.byte_count);
281
282 #ifdef ADB_DEBUG
283 if (adb_debug) {
284 printf("akbd: from %d at %d (org %d) %d:", adbaddr,
285 sc->handler_id, sc->origaddr, buffer[0]);
286 for (i = 1; i <= buffer[0]; i++)
287 printf(" %x", buffer[i]);
288 printf("\n");
289 }
290 #endif
291
292 if (sc->sc_wskbddev != NULL)
293 akbd_processevent(sc, &event);
294 }
295
296 #ifdef notyet
297 /*
298 * Get the actual hardware LED state and convert it to softc format.
299 */
300 u_char
getleds(int addr)301 getleds(int addr)
302 {
303 short cmd;
304 u_char buffer[9], leds;
305
306 leds = 0x00; /* all off */
307 buffer[0] = 0;
308
309 cmd = ADBTALK(addr, 2);
310 if (adb_op_sync((Ptr)buffer, cmd) == 0 &&
311 buffer[0] > 0)
312 leds = ~(buffer[2]) & 0x07;
313
314 return (leds);
315 }
316
317 /*
318 * Set the keyboard LED's.
319 *
320 * Automatically translates from ioctl/softc format to the
321 * actual keyboard register format
322 */
323 int
setleds(struct akbd_softc * sc,u_char leds)324 setleds(struct akbd_softc *sc, u_char leds)
325 {
326 int addr;
327 short cmd;
328 u_char buffer[9];
329
330 addr = sc->adbaddr;
331 buffer[0] = 0;
332
333 cmd = ADBTALK(addr, 2);
334 if (adb_op_sync((Ptr)buffer, cmd) || buffer[0] == 0)
335 return (EIO);
336
337 leds = ~leds & 0x07;
338 buffer[2] &= 0xf8;
339 buffer[2] |= leds;
340
341 cmd = ADBLISTEN(addr, 2);
342 adb_op_sync((Ptr)buffer, cmd);
343
344 /* talk R2 */
345 cmd = ADBTALK(addr, 2);
346 if (adb_op_sync((Ptr)buffer, cmd) || buffer[0] == 0)
347 return (EIO);
348
349 if ((buffer[2] & 0xf8) != leds)
350 return (EIO);
351 else
352 return (0);
353 }
354
355 /*
356 * Toggle all of the LED's on and off, just for show.
357 */
358 void
blinkleds(struct akbd_softc * sc)359 blinkleds(struct akbd_softc *sc)
360 {
361 u_char origleds;
362
363 origleds = getleds(sc->adbaddr);
364 setleds(sc, LED_NUMLOCK | LED_CAPSLOCK | LED_SCROLL_LOCK);
365 delay(400000);
366 setleds(sc, origleds);
367
368 if (origleds & LED_NUMLOCK)
369 sc->sc_leds |= WSKBD_LED_NUM;
370 if (origleds & LED_CAPSLOCK)
371 sc->sc_leds |= WSKBD_LED_CAPS;
372 if (origleds & LED_SCROLL_LOCK)
373 sc->sc_leds |= WSKBD_LED_SCROLL;
374 }
375 #endif
376
377 int
akbd_enable(void * v,int on)378 akbd_enable(void *v, int on)
379 {
380 return 0;
381 }
382
383 void
akbd_set_leds(void * v,int on)384 akbd_set_leds(void *v, int on)
385 {
386 #ifdef notyet
387 struct akbd_softc *sc = v;
388 int leds;
389
390 if (sc->sc_extended) {
391 if (sc->sc_leds == on)
392 return;
393
394 leds = 0;
395 if (on & WSKBD_LED_NUM)
396 leds |= LED_NUMLOCK;
397 if (on & WSKBD_LED_CAPS)
398 leds |= LED_CAPSLOCK;
399 if (on & WSKBD_LED_SCROLL)
400 leds |= LED_SCROLL_LOCK;
401
402 setleds(sc, leds);
403 }
404 #endif
405 }
406
407 int
akbd_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)408 akbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
409 {
410 struct akbd_softc *sc = v;
411
412 switch (cmd) {
413
414 case WSKBDIO_GTYPE:
415 *(int *)data = WSKBD_TYPE_ADB;
416 return 0;
417 case WSKBDIO_SETLEDS:
418 akbd_set_leds(v, *(int *)data);
419 return 0;
420 case WSKBDIO_GETLEDS:
421 *(int *)data = sc->sc_leds;
422 return 0;
423 #ifdef WSDISPLAY_COMPAT_RAWKBD
424 case WSKBDIO_SETMODE:
425 sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
426 return (0);
427 #endif
428 default:
429 return (-1);
430 }
431 }
432
433 /*
434 * The ``caps lock'' key is special: since on earlier keyboards, the physical
435 * key stays down when pressed, we will get a notification of the key press,
436 * but not of the key release. Then, when it is pressed again, we will not get
437 * a notification of the key press, but will see the key release.
438 *
439 * This is not exactly true. We see the missing release and press events both
440 * as the release of the power (reset) key.
441 *
442 * To avoid confusing them with real power key presses, we maintain two
443 * states for the caps lock key: logically down (from wscons' point of view),
444 * and ``physically'' down (from the adb messages point of view), to ignore
445 * the power key. But since one may press the power key while the caps lock
446 * is held down, we also have to remember the state of the power key... this
447 * is quite messy.
448 */
449
450 /*
451 * Values for caps lock state machine
452 */
453 #define CL_DOWN_ADB 0x01
454 #define CL_DOWN_LOGICAL 0x02
455 #define CL_DOWN_RESET 0x04
456
457 /*
458 * Given a keyboard ADB event, decode the keycodes and pass them to wskbd.
459 */
460 void
akbd_processevent(struct akbd_softc * sc,adb_event_t * event)461 akbd_processevent(struct akbd_softc *sc, adb_event_t *event)
462 {
463 switch (event->byte_count) {
464 case 1:
465 akbd_capslockwrapper(sc, event->bytes[0]);
466 break;
467 case 2:
468 /*
469 * The reset (or power) key sends 0x7f7f on press and
470 * 0xffff on release.
471 */
472 if (event->bytes[0] == event->bytes[1] &&
473 ADBK_KEYVAL(event->bytes[0]) == ADBK_RESET) {
474 if (event->bytes[0] == ADBK_KEYDOWN(ADBK_RESET)) {
475 SET(sc->sc_caps, CL_DOWN_RESET);
476 adb_power_button_intr();
477 } else {
478 if (ISSET(sc->sc_caps, CL_DOWN_RESET))
479 CLR(sc->sc_caps, CL_DOWN_RESET);
480 else if (ISSET(sc->sc_caps, CL_DOWN_ADB)) {
481 akbd_input(sc, ISSET(sc->sc_caps,
482 CL_DOWN_LOGICAL) ?
483 ADBK_KEYDOWN(ADBK_CAPSLOCK) :
484 ADBK_KEYUP(ADBK_CAPSLOCK));
485 sc->sc_caps ^= CL_DOWN_LOGICAL;
486 }
487 }
488 } else {
489 akbd_capslockwrapper(sc, event->bytes[0]);
490 akbd_capslockwrapper(sc, event->bytes[1]);
491 }
492 break;
493 default:
494 #ifdef DIAGNOSTIC
495 printf("%s: unexpected message length %d\n",
496 sc->sc_dev.dv_xname, event->byte_count);
497 #endif
498 break;
499 }
500
501 }
502
503 void
akbd_capslockwrapper(struct akbd_softc * sc,int key)504 akbd_capslockwrapper(struct akbd_softc *sc, int key)
505 {
506 if (ADBK_KEYVAL(key) == ADBK_CAPSLOCK)
507 sc->sc_caps ^= CL_DOWN_ADB;
508
509 if (key != 0xff)
510 akbd_input(sc, key);
511 }
512
513 static inline int
akbd_iso_swap(int keycode)514 akbd_iso_swap(int keycode)
515 {
516 switch (keycode) {
517 case 10:
518 return (50);
519 case 50:
520 return (10);
521 default:
522 return (keycode);
523 }
524 }
525
526 int adb_polledkey;
527 void
akbd_input(struct akbd_softc * sc,int key)528 akbd_input(struct akbd_softc *sc, int key)
529 {
530 int press, val;
531 int type;
532
533 press = ADBK_PRESS(key);
534 val = ADBK_KEYVAL(key);
535
536 if (sc->sc_iso)
537 val = akbd_iso_swap(val);
538
539 type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
540
541 if (adb_polling) {
542 adb_polledkey = key;
543 #ifdef WSDISPLAY_COMPAT_RAWKBD
544 } else if (sc->sc_rawkbd) {
545 char cbuf[2];
546 int c, j, s;
547
548 j = 0;
549
550 c = keyboard[val];
551 if (c == 0) {
552 return; /* XXX */
553 }
554 if (c & 0x80)
555 cbuf[j++] = 0xe0;
556 cbuf[j] = c & 0x7f;
557 if (type == WSCONS_EVENT_KEY_UP)
558 cbuf[j] |= 0x80;
559 j++;
560 s = spltty();
561 wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
562 splx(s);
563 #endif
564 } else {
565 wskbd_input(sc->sc_wskbddev, type, val);
566 }
567 }
568