1 | /* $NetBSD: pckbd.c,v 1.32 2015/07/16 15:01:04 prlw1 Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2009 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Charles M. Hannum. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /*- |
33 | * Copyright (c) 1990 The Regents of the University of California. |
34 | * All rights reserved. |
35 | * |
36 | * This code is derived from software contributed to Berkeley by |
37 | * William Jolitz and Don Ahn. |
38 | * |
39 | * Redistribution and use in source and binary forms, with or without |
40 | * modification, are permitted provided that the following conditions |
41 | * are met: |
42 | * 1. Redistributions of source code must retain the above copyright |
43 | * notice, this list of conditions and the following disclaimer. |
44 | * 2. Redistributions in binary form must reproduce the above copyright |
45 | * notice, this list of conditions and the following disclaimer in the |
46 | * documentation and/or other materials provided with the distribution. |
47 | * 3. Neither the name of the University nor the names of its contributors |
48 | * may be used to endorse or promote products derived from this software |
49 | * without specific prior written permission. |
50 | * |
51 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
52 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
53 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
54 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
55 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
56 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
57 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
58 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
59 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
60 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
61 | * SUCH DAMAGE. |
62 | * |
63 | * @(#)pccons.c 5.11 (Berkeley) 5/21/91 |
64 | */ |
65 | |
66 | /* |
67 | * code to work keyboard for PC-style console |
68 | */ |
69 | |
70 | #include <sys/cdefs.h> |
71 | __KERNEL_RCSID(0, "$NetBSD: pckbd.c,v 1.32 2015/07/16 15:01:04 prlw1 Exp $" ); |
72 | |
73 | #include <sys/param.h> |
74 | #include <sys/systm.h> |
75 | #include <sys/device.h> |
76 | #include <sys/malloc.h> |
77 | #include <sys/ioctl.h> |
78 | |
79 | #include <sys/bus.h> |
80 | |
81 | #include <dev/pckbport/pckbportvar.h> |
82 | |
83 | #include <dev/wscons/wsconsio.h> |
84 | #include <dev/wscons/wskbdvar.h> |
85 | #include <dev/wscons/wsksymdef.h> |
86 | #include <dev/wscons/wsksymvar.h> |
87 | |
88 | #include <dev/pckbport/pckbdreg.h> |
89 | #include <dev/pckbport/pckbdvar.h> |
90 | #include <dev/pckbport/wskbdmap_mfii.h> |
91 | |
92 | #include "locators.h" |
93 | |
94 | #include "opt_pckbd_layout.h" |
95 | #include "opt_pckbd_cnattach_may_fail.h" |
96 | #include "opt_wsdisplay_compat.h" |
97 | |
98 | struct pckbd_internal { |
99 | int t_isconsole; |
100 | pckbport_tag_t t_kbctag; |
101 | pckbport_slot_t t_kbcslot; |
102 | |
103 | int t_translating; |
104 | |
105 | int t_lastchar; |
106 | int t_extended0; |
107 | int t_extended1; |
108 | int t_releasing; |
109 | |
110 | struct pckbd_softc *t_sc; /* back pointer */ |
111 | }; |
112 | |
113 | struct pckbd_softc { |
114 | device_t sc_dev; |
115 | |
116 | struct pckbd_internal *id; |
117 | int sc_enabled; |
118 | |
119 | int sc_ledstate; |
120 | |
121 | device_t sc_wskbddev; |
122 | #ifdef WSDISPLAY_COMPAT_RAWKBD |
123 | int rawkbd; |
124 | #endif |
125 | }; |
126 | |
127 | static int pckbd_is_console(pckbport_tag_t, pckbport_slot_t); |
128 | |
129 | int pckbdprobe(device_t, cfdata_t, void *); |
130 | void pckbdattach(device_t, device_t, void *); |
131 | |
132 | CFATTACH_DECL_NEW(pckbd, sizeof(struct pckbd_softc), |
133 | pckbdprobe, pckbdattach, NULL, NULL); |
134 | |
135 | int pckbd_enable(void *, int); |
136 | void pckbd_set_leds(void *, int); |
137 | int pckbd_ioctl(void *, u_long, void *, int, struct lwp *); |
138 | |
139 | const struct wskbd_accessops pckbd_accessops = { |
140 | pckbd_enable, |
141 | pckbd_set_leds, |
142 | pckbd_ioctl, |
143 | }; |
144 | |
145 | void pckbd_cngetc(void *, u_int *, int *); |
146 | void pckbd_cnpollc(void *, int); |
147 | void pckbd_cnbell(void *, u_int, u_int, u_int); |
148 | |
149 | const struct wskbd_consops pckbd_consops = { |
150 | pckbd_cngetc, |
151 | pckbd_cnpollc, |
152 | pckbd_cnbell, |
153 | }; |
154 | |
155 | const struct wskbd_mapdata pckbd_keymapdata = { |
156 | pckbd_keydesctab, |
157 | #ifdef PCKBD_LAYOUT |
158 | PCKBD_LAYOUT, |
159 | #else |
160 | KB_US, |
161 | #endif |
162 | }; |
163 | |
164 | /* |
165 | * Hackish support for a bell on the PC Keyboard; when a suitable feeper |
166 | * is found, it attaches itself into the pckbd driver here. |
167 | */ |
168 | void (*pckbd_bell_fn)(void *, u_int, u_int, u_int, int); |
169 | void *pckbd_bell_fn_arg; |
170 | |
171 | void pckbd_bell(u_int, u_int, u_int, int); |
172 | |
173 | int pckbd_scancode_translate(struct pckbd_internal *, int); |
174 | int pckbd_set_xtscancode(pckbport_tag_t, pckbport_slot_t, |
175 | struct pckbd_internal *); |
176 | int pckbd_init(struct pckbd_internal *, pckbport_tag_t, pckbport_slot_t, |
177 | int); |
178 | void pckbd_input(void *, int); |
179 | |
180 | static int pckbd_decode(struct pckbd_internal *, int, u_int *, int *); |
181 | static int pckbd_led_encode(int); |
182 | static int pckbd_led_decode(int); |
183 | |
184 | struct pckbd_internal pckbd_consdata; |
185 | |
186 | int |
187 | pckbd_set_xtscancode(pckbport_tag_t kbctag, pckbport_slot_t kbcslot, |
188 | struct pckbd_internal *id) |
189 | { |
190 | int xt, res = 0; |
191 | u_char cmd[2]; |
192 | |
193 | /* |
194 | * Some keyboard/8042 combinations do not seem to work if the keyboard |
195 | * is set to table 1; in fact, it would appear that some keyboards just |
196 | * ignore the command altogether. So by default, we use the AT scan |
197 | * codes and have the 8042 translate them. Unfortunately, this is |
198 | * known to not work on some PS/2 machines. We try desperately to deal |
199 | * with this by checking the (lack of a) translate bit in the 8042 and |
200 | * attempting to set the keyboard to XT mode. If this all fails, well, |
201 | * tough luck. If the PCKBC_CANT_TRANSLATE pckbc flag was set, we |
202 | * enable software translation. |
203 | * |
204 | * XXX It would perhaps be a better choice to just use AT scan codes |
205 | * and not bother with this. |
206 | */ |
207 | xt = pckbport_xt_translation(kbctag, kbcslot, 1); |
208 | if (xt == 1) { |
209 | /* The 8042 is translating for us; use AT codes. */ |
210 | cmd[0] = KBC_SETTABLE; |
211 | cmd[1] = 2; |
212 | res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0); |
213 | if (res) { |
214 | u_char cmdb[1]; |
215 | aprint_debug("pckbd: error setting scanset 2\n" ); |
216 | /* |
217 | * XXX at least one keyboard is reported to lock up |
218 | * if a "set table" is attempted, thus the "reset". |
219 | * XXX ignore errors, scanset 2 should be |
220 | * default anyway. |
221 | */ |
222 | cmdb[0] = KBC_RESET; |
223 | (void)pckbport_poll_cmd(kbctag, kbcslot, cmdb, 1, 1, 0, 1); |
224 | pckbport_flush(kbctag, kbcslot); |
225 | res = 0; |
226 | } |
227 | if (id != NULL) |
228 | id->t_translating = 1; |
229 | } else if (xt == -1) { |
230 | /* Software translation required */ |
231 | if (id != NULL) |
232 | id->t_translating = 0; |
233 | } else { |
234 | /* Stupid 8042; set keyboard to XT codes. */ |
235 | cmd[0] = KBC_SETTABLE; |
236 | cmd[1] = 1; |
237 | res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0); |
238 | if (res) |
239 | aprint_debug("pckbd: error setting scanset 1\n" ); |
240 | if (id != NULL) |
241 | id->t_translating = 1; |
242 | } |
243 | return res; |
244 | } |
245 | |
246 | static int |
247 | pckbd_is_console(pckbport_tag_t tag, pckbport_slot_t slot) |
248 | { |
249 | |
250 | return pckbd_consdata.t_isconsole && |
251 | tag == pckbd_consdata.t_kbctag && slot == pckbd_consdata.t_kbcslot; |
252 | } |
253 | |
254 | static bool |
255 | pckbd_suspend(device_t dv, const pmf_qual_t *qual) |
256 | { |
257 | struct pckbd_softc *sc = device_private(dv); |
258 | u_char cmd[1]; |
259 | int res; |
260 | |
261 | /* XXX duped from pckbd_enable, but we want to disable |
262 | * it even if it's the console kbd |
263 | */ |
264 | cmd[0] = KBC_DISABLE; |
265 | res = pckbport_enqueue_cmd(sc->id->t_kbctag, |
266 | sc->id->t_kbcslot, cmd, 1, 0, 1, 0); |
267 | if (res) |
268 | return false; |
269 | |
270 | pckbport_slot_enable(sc->id->t_kbctag, |
271 | sc->id->t_kbcslot, 0); |
272 | |
273 | sc->sc_enabled = 0; |
274 | return true; |
275 | } |
276 | |
277 | static bool |
278 | pckbd_resume(device_t dv, const pmf_qual_t *qual) |
279 | { |
280 | struct pckbd_softc *sc = device_private(dv); |
281 | u_char cmd[1], resp[1]; |
282 | int res; |
283 | |
284 | /* XXX jmcneill reset the keyboard */ |
285 | pckbport_flush(sc->id->t_kbctag, sc->id->t_kbcslot); |
286 | |
287 | cmd[0] = KBC_RESET; |
288 | res = pckbport_poll_cmd(sc->id->t_kbctag, |
289 | sc->id->t_kbcslot, cmd, 1, 1, resp, 1); |
290 | if (res) |
291 | aprint_debug("%s: reset error %d\n" , __func__, res); |
292 | if (resp[0] != KBR_RSTDONE) |
293 | printf("%s: reset response 0x%x\n" , __func__, resp[0]); |
294 | |
295 | pckbport_flush(sc->id->t_kbctag, sc->id->t_kbcslot); |
296 | |
297 | pckbd_enable(sc, 1); |
298 | |
299 | return true; |
300 | } |
301 | |
302 | /* |
303 | * these are both bad jokes |
304 | */ |
305 | int |
306 | pckbdprobe(device_t parent, cfdata_t cf, void *aux) |
307 | { |
308 | struct pckbport_attach_args *pa = aux; |
309 | int res; |
310 | u_char cmd[1], resp[1]; |
311 | |
312 | /* |
313 | * XXX There are rumours that a keyboard can be connected |
314 | * to the aux port as well. For me, this didn't work. |
315 | * For further experiments, allow it if explicitly |
316 | * wired in the config file. |
317 | */ |
318 | if ((pa->pa_slot != PCKBPORT_KBD_SLOT) && |
319 | (cf->cf_loc[PCKBPORTCF_SLOT] == PCKBPORTCF_SLOT_DEFAULT)) |
320 | return 0; |
321 | |
322 | /* Flush any garbage. */ |
323 | pckbport_flush(pa->pa_tag, pa->pa_slot); |
324 | |
325 | /* Reset the keyboard. */ |
326 | cmd[0] = KBC_RESET; |
327 | res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 1, resp, 1); |
328 | if (res) { |
329 | aprint_debug("pckbdprobe: reset error %d\n" , res); |
330 | /* |
331 | * There is probably no keyboard connected. |
332 | * Let the probe succeed if the keyboard is used |
333 | * as console input - it can be connected later. |
334 | */ |
335 | return pckbd_is_console(pa->pa_tag, pa->pa_slot) ? 1 : 0; |
336 | } |
337 | if (resp[0] != KBR_RSTDONE) { |
338 | printf("pckbdprobe: reset response 0x%x\n" , resp[0]); |
339 | return 0; |
340 | } |
341 | |
342 | /* |
343 | * Some keyboards seem to leave a second ack byte after the reset. |
344 | * This is kind of stupid, but we account for them anyway by just |
345 | * flushing the buffer. |
346 | */ |
347 | pckbport_flush(pa->pa_tag, pa->pa_slot); |
348 | |
349 | if (pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot, NULL)) |
350 | return 0; |
351 | |
352 | return 2; |
353 | } |
354 | |
355 | void |
356 | pckbdattach(device_t parent, device_t self, void *aux) |
357 | { |
358 | struct pckbd_softc *sc = device_private(self); |
359 | struct pckbport_attach_args *pa = aux; |
360 | struct wskbddev_attach_args a; |
361 | int isconsole; |
362 | u_char cmd[1]; |
363 | |
364 | aprint_naive("\n" ); |
365 | aprint_normal("\n" ); |
366 | |
367 | sc->sc_dev = self; |
368 | isconsole = pckbd_is_console(pa->pa_tag, pa->pa_slot); |
369 | |
370 | if (isconsole) { |
371 | sc->id = &pckbd_consdata; |
372 | |
373 | /* |
374 | * Some keyboards are not enabled after a reset, |
375 | * so make sure it is enabled now. |
376 | */ |
377 | cmd[0] = KBC_ENABLE; |
378 | (void) pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
379 | cmd, 1, 0, 0, 0); |
380 | sc->sc_enabled = 1; |
381 | } else { |
382 | sc->id = malloc(sizeof(struct pckbd_internal), |
383 | M_DEVBUF, M_WAITOK); |
384 | (void) pckbd_init(sc->id, pa->pa_tag, pa->pa_slot, 0); |
385 | |
386 | /* no interrupts until enabled */ |
387 | cmd[0] = KBC_DISABLE; |
388 | (void) pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
389 | cmd, 1, 0, 0, 0); |
390 | sc->sc_enabled = 0; |
391 | } |
392 | |
393 | sc->id->t_sc = sc; |
394 | |
395 | pckbport_set_inputhandler(sc->id->t_kbctag, sc->id->t_kbcslot, |
396 | pckbd_input, sc, device_xname(sc->sc_dev)); |
397 | |
398 | a.console = isconsole; |
399 | |
400 | a.keymap = &pckbd_keymapdata; |
401 | |
402 | a.accessops = &pckbd_accessops; |
403 | a.accesscookie = sc; |
404 | |
405 | if (!pmf_device_register(self, pckbd_suspend, pckbd_resume)) |
406 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
407 | |
408 | /* |
409 | * Attach the wskbd, saving a handle to it. |
410 | * XXX XXX XXX |
411 | */ |
412 | sc->sc_wskbddev = config_found_ia(self, "wskbddev" , &a, wskbddevprint); |
413 | } |
414 | |
415 | int |
416 | pckbd_enable(void *v, int on) |
417 | { |
418 | struct pckbd_softc *sc = v; |
419 | int res; |
420 | u_char cmd[1]; |
421 | |
422 | if (on) { |
423 | if (sc->sc_enabled) { |
424 | aprint_debug("pckbd_enable: bad enable\n" ); |
425 | return EBUSY; |
426 | } |
427 | |
428 | pckbport_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 1); |
429 | |
430 | cmd[0] = KBC_ENABLE; |
431 | res = pckbport_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
432 | cmd, 1, 0, NULL, 0); |
433 | if (res) { |
434 | printf("pckbd_enable: command error\n" ); |
435 | return (res); |
436 | } |
437 | |
438 | res = pckbd_set_xtscancode(sc->id->t_kbctag, |
439 | sc->id->t_kbcslot, sc->id); |
440 | if (res) |
441 | return res; |
442 | |
443 | sc->sc_enabled = 1; |
444 | } else { |
445 | if (sc->id->t_isconsole) |
446 | return EBUSY; |
447 | |
448 | cmd[0] = KBC_DISABLE; |
449 | res = pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
450 | cmd, 1, 0, 1, 0); |
451 | if (res) { |
452 | printf("pckbd_disable: command error\n" ); |
453 | return res; |
454 | } |
455 | |
456 | pckbport_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 0); |
457 | |
458 | sc->sc_enabled = 0; |
459 | } |
460 | |
461 | return 0; |
462 | } |
463 | |
464 | const u_int8_t pckbd_xtbl[] = { |
465 | /* 0x00 */ |
466 | 0, |
467 | 0x43, /* F9 */ |
468 | 0x89, /* SunStop */ |
469 | 0x3f, /* F5 */ |
470 | 0x3d, /* F3 */ |
471 | 0x3b, /* F1 */ |
472 | 0x3c, /* F2 */ |
473 | 0x58, /* F12 */ |
474 | 0, |
475 | 0x44, /* F10 */ |
476 | 0x42, /* F8 */ |
477 | 0x40, /* F6 */ |
478 | 0x3e, /* F4 */ |
479 | 0x0f, /* Tab */ |
480 | 0x29, /* ` ~ */ |
481 | 0, |
482 | /* 0x10 */ |
483 | 0, |
484 | 0x38, /* Left Alt */ |
485 | 0x2a, /* Left Shift */ |
486 | 0, |
487 | 0x1d, /* Left Ctrl */ |
488 | 0x10, /* q */ |
489 | 0x02, /* 1 ! */ |
490 | 0, |
491 | 0, |
492 | 0, |
493 | 0x2c, /* z */ |
494 | 0x1f, /* s */ |
495 | 0x1e, /* a */ |
496 | 0x11, /* w */ |
497 | 0x03, /* 2 @ */ |
498 | 0, |
499 | /* 0x20 */ |
500 | 0, |
501 | 0x2e, /* c */ |
502 | 0x2d, /* x */ |
503 | 0x20, /* d */ |
504 | 0x12, /* e */ |
505 | 0x05, /* 4 $ */ |
506 | 0x04, /* 3 # */ |
507 | 0, |
508 | 0, |
509 | 0x39, /* Space */ |
510 | 0x2f, /* v */ |
511 | 0x21, /* f */ |
512 | 0x14, /* t */ |
513 | 0x13, /* r */ |
514 | 0x06, /* 5 % */ |
515 | 0, |
516 | /* 0x30 */ |
517 | 0, |
518 | 0x31, /* n */ |
519 | 0x30, /* b */ |
520 | 0x23, /* h */ |
521 | 0x22, /* g */ |
522 | 0x15, /* y */ |
523 | 0x07, /* 6 ^ */ |
524 | 0, |
525 | 0, |
526 | 0, |
527 | 0x32, /* m */ |
528 | 0x24, /* j */ |
529 | 0x16, /* u */ |
530 | 0x08, /* 7 & */ |
531 | 0x09, /* 8 * */ |
532 | 0, |
533 | /* 0x40 */ |
534 | 0, |
535 | 0x33, /* , < */ |
536 | 0x25, /* k */ |
537 | 0x17, /* i */ |
538 | 0x18, /* o */ |
539 | 0x0b, /* 0 ) */ |
540 | 0x0a, /* 9 ( */ |
541 | 0, |
542 | 0, |
543 | 0x34, /* . > */ |
544 | 0x35, /* / ? */ |
545 | 0x26, /* l */ |
546 | 0x27, /* ; : */ |
547 | 0x19, /* p */ |
548 | 0x0c, /* - _ */ |
549 | 0, |
550 | /* 0x50 */ |
551 | 0, |
552 | 0, |
553 | 0x28, /* ' " */ |
554 | 0, |
555 | 0x1a, /* [ { */ |
556 | 0x0d, /* = + */ |
557 | 0, |
558 | 0, |
559 | 0x3a, /* Caps Lock */ |
560 | 0x36, /* Right Shift */ |
561 | 0x1c, /* Return */ |
562 | 0x1b, /* ] } */ |
563 | 0, |
564 | 0x2b, /* \ | */ |
565 | 0, |
566 | 0, |
567 | /* 0x60 */ |
568 | 0, |
569 | 0, |
570 | 0, |
571 | 0, |
572 | 0, |
573 | 0, |
574 | 0x0e, /* Back Space */ |
575 | 0, |
576 | 0, |
577 | 0x4f, /* KP 1 */ |
578 | 0, |
579 | 0x4b, /* KP 4 */ |
580 | 0x47, /* KP 7 */ |
581 | 0, |
582 | 0, |
583 | 0, |
584 | /* 0x70 */ |
585 | 0x52, /* KP 0 */ |
586 | 0x53, /* KP . */ |
587 | 0x50, /* KP 2 */ |
588 | 0x4c, /* KP 5 */ |
589 | 0x4d, /* KP 6 */ |
590 | 0x48, /* KP 8 */ |
591 | 0x01, /* Escape */ |
592 | 0x45, /* Num Lock */ |
593 | 0x57, /* F11 */ |
594 | 0x4e, /* KP + */ |
595 | 0x51, /* KP 3 */ |
596 | 0x4a, /* KP - */ |
597 | 0x37, /* KP * */ |
598 | 0x49, /* KP 9 */ |
599 | 0x46, /* Scroll Lock */ |
600 | 0, |
601 | /* 0x80 */ |
602 | 0, |
603 | 0, |
604 | 0, |
605 | 0x41, /* F7 (produced as an actual 8 bit code) */ |
606 | 0, /* Alt-Print Screen */ |
607 | 0, |
608 | 0, |
609 | 0, |
610 | 0, |
611 | 0, |
612 | 0, |
613 | 0, |
614 | 0, |
615 | 0, |
616 | 0, |
617 | 0, |
618 | /* 0x90 */ |
619 | 0xdb, /* Left Meta */ |
620 | 0x88, /* SunHelp */ |
621 | 0x8a, /* SunAgain */ |
622 | 0x8c, /* SunUndo */ |
623 | 0x8e, /* SunCopy */ |
624 | 0x90, /* SunPaste */ |
625 | 0x92, /* SunCut */ |
626 | 0x8b, /* SunProps */ |
627 | 0x8d, /* SunFront */ |
628 | 0x8f, /* SunOpen */ |
629 | 0x91 /* SunFind */ |
630 | }; |
631 | |
632 | const u_int8_t pckbd_xtbl_ext[] = { |
633 | /* 0x00 */ |
634 | 0, |
635 | 0, |
636 | 0, |
637 | 0, |
638 | 0, |
639 | 0, |
640 | 0, |
641 | 0, |
642 | 0, |
643 | 0, |
644 | 0, |
645 | 0, |
646 | 0, |
647 | 0, |
648 | 0, |
649 | 0, |
650 | /* 0x10 */ |
651 | 0, |
652 | 0x38, /* Right Alt */ |
653 | 0, /* E0 12, to be ignored */ |
654 | 0, |
655 | 0x1d, /* Right Ctrl */ |
656 | 0, |
657 | 0, |
658 | 0, |
659 | 0, |
660 | 0, |
661 | 0, |
662 | 0, |
663 | 0, |
664 | 0, |
665 | 0, |
666 | 0, |
667 | /* 0x20 */ |
668 | 0, |
669 | 0, |
670 | 0, |
671 | 0, |
672 | 0, |
673 | 0, |
674 | 0, |
675 | 0, |
676 | 0, |
677 | 0, |
678 | 0, |
679 | 0, |
680 | 0, |
681 | 0, |
682 | 0, |
683 | 0xdd, /* Compose */ |
684 | /* 0x30 */ |
685 | 0, |
686 | 0, |
687 | 0, |
688 | 0, |
689 | 0, |
690 | 0, |
691 | 0, |
692 | 0, |
693 | 0, |
694 | 0, |
695 | 0, |
696 | 0, |
697 | 0, |
698 | 0, |
699 | 0, |
700 | 0, |
701 | /* 0x40 */ |
702 | 0, |
703 | 0, |
704 | 0, |
705 | 0, |
706 | 0, |
707 | 0, |
708 | 0, |
709 | 0, |
710 | 0, |
711 | 0, |
712 | 0xb5, /* KP / */ |
713 | 0, |
714 | 0, |
715 | 0, |
716 | 0, |
717 | 0, |
718 | /* 0x50 */ |
719 | 0, |
720 | 0, |
721 | 0, |
722 | 0, |
723 | 0, |
724 | 0, |
725 | 0, |
726 | 0, |
727 | 0, |
728 | 0, |
729 | 0x1c, /* KP Return */ |
730 | 0, |
731 | 0, |
732 | 0, |
733 | 0, |
734 | 0, |
735 | /* 0x60 */ |
736 | 0, |
737 | 0, |
738 | 0, |
739 | 0, |
740 | 0, |
741 | 0, |
742 | 0, |
743 | 0, |
744 | 0, |
745 | 0x4f, /* End */ |
746 | 0, |
747 | 0x4b, /* Left */ |
748 | 0x47, /* Home */ |
749 | 0, |
750 | 0, |
751 | 0, |
752 | /* 0x70 */ |
753 | 0x52, /* Insert */ |
754 | 0x53, /* Delete */ |
755 | 0x50, /* Down */ |
756 | 0, |
757 | 0x4d, /* Right */ |
758 | 0x48, /* Up */ |
759 | 0, |
760 | 0, |
761 | 0, |
762 | 0, |
763 | 0x51, /* Page Down */ |
764 | 0, |
765 | 0x37, /* Print Screen */ |
766 | 0x49, /* Page Up */ |
767 | 0x46, /* Ctrl-Break */ |
768 | 0 |
769 | }; |
770 | |
771 | /* |
772 | * Translate scan codes from set 2 to set 1 |
773 | */ |
774 | int |
775 | pckbd_scancode_translate(struct pckbd_internal *id, int datain) |
776 | { |
777 | if (id->t_translating != 0) |
778 | return datain; |
779 | |
780 | if (datain == KBR_BREAK) { |
781 | id->t_releasing = 0x80; /* next keycode is a release */ |
782 | return 0; /* consume scancode */ |
783 | } |
784 | |
785 | /* |
786 | * Handle extended sequences |
787 | */ |
788 | if (datain == KBR_EXTENDED0 || datain == KBR_EXTENDED1) |
789 | return datain; |
790 | |
791 | /* |
792 | * Convert BREAK sequence (14 77 -> 1D 45) |
793 | */ |
794 | if (id->t_extended1 == 2 && datain == 0x14) |
795 | return 0x1d | id->t_releasing; |
796 | else if (id->t_extended1 == 1 && datain == 0x77) |
797 | return 0x45 | id->t_releasing; |
798 | |
799 | if (id->t_extended0 != 0) { |
800 | if (datain >= sizeof pckbd_xtbl_ext) |
801 | datain = 0; |
802 | else |
803 | datain = pckbd_xtbl_ext[datain]; |
804 | } else { |
805 | if (datain >= sizeof pckbd_xtbl) |
806 | datain = 0; |
807 | else |
808 | datain = pckbd_xtbl[datain]; |
809 | } |
810 | |
811 | /* |
812 | * If we are mapping in the range 128-254, then make this |
813 | * an extended keycode, as table 1 codes are limited to |
814 | * the range 0-127 (the top bit is used for key up/break). |
815 | */ |
816 | if (datain > 0x7f) { |
817 | datain &= 0x7f; |
818 | id->t_extended0 = 0x80; |
819 | } |
820 | |
821 | if (datain == 0) { |
822 | /* |
823 | * We don't know how to translate this scan code, but |
824 | * we can't silently eat it either (because there might |
825 | * have been an extended byte transmitted already). |
826 | * Hopefully this value will be harmless to the upper |
827 | * layers. |
828 | */ |
829 | return 0xff; |
830 | } |
831 | return datain | id->t_releasing; |
832 | } |
833 | |
834 | static int |
835 | pckbd_decode(struct pckbd_internal *id, int datain, u_int *type, int *dataout) |
836 | { |
837 | int key; |
838 | int releasing; |
839 | |
840 | if (datain == KBR_EXTENDED0) { |
841 | id->t_extended0 = 0x80; |
842 | return 0; |
843 | } else if (datain == KBR_EXTENDED1) { |
844 | id->t_extended1 = 2; |
845 | return 0; |
846 | } |
847 | |
848 | releasing = datain & 0x80; |
849 | datain &= 0x7f; |
850 | |
851 | if (id->t_extended0 == 0x80) { |
852 | switch (datain) { |
853 | case 0x2a: |
854 | case 0x36: |
855 | id->t_extended0 = 0; |
856 | return 0; |
857 | default: |
858 | break; |
859 | } |
860 | } |
861 | |
862 | /* map extended keys to (unused) codes 128-254 */ |
863 | key = datain | id->t_extended0; |
864 | id->t_extended0 = 0; |
865 | |
866 | /* |
867 | * process PAUSE (also break) key (EXT1 1D 45 EXT1 9D C5): |
868 | * map to (unused) code 7F |
869 | */ |
870 | if (id->t_extended1 == 2 && (datain == 0x1d || datain == 0x9d)) { |
871 | id->t_extended1 = 1; |
872 | return 0; |
873 | } else if (id->t_extended1 == 1 && |
874 | (datain == 0x45 || datain == 0xc5)) { |
875 | id->t_extended1 = 0; |
876 | key = 0x7f; |
877 | } else if (id->t_extended1 > 0) { |
878 | id->t_extended1 = 0; |
879 | } |
880 | |
881 | if (id->t_translating != 0) { |
882 | id->t_releasing = releasing; |
883 | } else { |
884 | /* id->t_releasing computed in pckbd_scancode_translate() */ |
885 | } |
886 | |
887 | if (id->t_releasing) { |
888 | id->t_releasing = 0; |
889 | id->t_lastchar = 0; |
890 | *type = WSCONS_EVENT_KEY_UP; |
891 | } else { |
892 | /* Always ignore typematic keys */ |
893 | if (key == id->t_lastchar) |
894 | return 0; |
895 | id->t_lastchar = key; |
896 | *type = WSCONS_EVENT_KEY_DOWN; |
897 | } |
898 | |
899 | *dataout = key; |
900 | return 1; |
901 | } |
902 | |
903 | int |
904 | pckbd_init(struct pckbd_internal *t, pckbport_tag_t kbctag, |
905 | pckbport_slot_t kbcslot, int console) |
906 | { |
907 | |
908 | memset(t, 0, sizeof(struct pckbd_internal)); |
909 | |
910 | t->t_isconsole = console; |
911 | t->t_kbctag = kbctag; |
912 | t->t_kbcslot = kbcslot; |
913 | |
914 | return pckbd_set_xtscancode(kbctag, kbcslot, t); |
915 | } |
916 | |
917 | static int |
918 | pckbd_led_encode(int led) |
919 | { |
920 | int res; |
921 | |
922 | res = 0; |
923 | |
924 | if (led & WSKBD_LED_SCROLL) |
925 | res |= 0x01; |
926 | if (led & WSKBD_LED_NUM) |
927 | res |= 0x02; |
928 | if (led & WSKBD_LED_CAPS) |
929 | res |= 0x04; |
930 | return res; |
931 | } |
932 | |
933 | static int |
934 | pckbd_led_decode(int led) |
935 | { |
936 | int res; |
937 | |
938 | res = 0; |
939 | if (led & 0x01) |
940 | res |= WSKBD_LED_SCROLL; |
941 | if (led & 0x02) |
942 | res |= WSKBD_LED_NUM; |
943 | if (led & 0x04) |
944 | res |= WSKBD_LED_CAPS; |
945 | return res; |
946 | } |
947 | |
948 | void |
949 | pckbd_set_leds(void *v, int leds) |
950 | { |
951 | struct pckbd_softc *sc = v; |
952 | u_char cmd[2]; |
953 | |
954 | cmd[0] = KBC_MODEIND; |
955 | cmd[1] = pckbd_led_encode(leds); |
956 | sc->sc_ledstate = cmd[1]; |
957 | |
958 | (void)pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
959 | cmd, 2, 0, 0, 0); |
960 | } |
961 | |
962 | /* |
963 | * Got a console receive interrupt - |
964 | * the console processor wants to give us a character. |
965 | */ |
966 | void |
967 | pckbd_input(void *vsc, int data) |
968 | { |
969 | struct pckbd_softc *sc = vsc; |
970 | int key; |
971 | u_int type; |
972 | |
973 | data = pckbd_scancode_translate(sc->id, data); |
974 | if (data == 0) |
975 | return; |
976 | |
977 | #ifdef WSDISPLAY_COMPAT_RAWKBD |
978 | if (sc->rawkbd) { |
979 | u_char d = data; |
980 | wskbd_rawinput(sc->sc_wskbddev, &d, 1); |
981 | return; |
982 | } |
983 | #endif |
984 | if (pckbd_decode(sc->id, data, &type, &key)) |
985 | wskbd_input(sc->sc_wskbddev, type, key); |
986 | } |
987 | |
988 | int |
989 | pckbd_ioctl(void *v, u_long cmd, void *data, int flag, |
990 | struct lwp *l) |
991 | { |
992 | struct pckbd_softc *sc = v; |
993 | |
994 | switch (cmd) { |
995 | case WSKBDIO_GTYPE: |
996 | *(int *)data = WSKBD_TYPE_PC_XT; |
997 | return 0; |
998 | case WSKBDIO_SETLEDS: |
999 | { |
1000 | int res; |
1001 | u_char cmdb[2]; |
1002 | |
1003 | cmdb[0] = KBC_MODEIND; |
1004 | cmdb[1] = pckbd_led_encode(*(int *)data); |
1005 | sc->sc_ledstate = cmdb[1]; |
1006 | res = pckbport_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, |
1007 | cmdb, 2, 0, 1, 0); |
1008 | return res; |
1009 | } |
1010 | case WSKBDIO_GETLEDS: |
1011 | *(int *)data = pckbd_led_decode(sc->sc_ledstate); |
1012 | return 0; |
1013 | case WSKBDIO_COMPLEXBELL: |
1014 | #define d ((struct wskbd_bell_data *)data) |
1015 | /* |
1016 | * Keyboard can't beep directly; we have an |
1017 | * externally-provided global hook to do this. |
1018 | */ |
1019 | pckbd_bell(d->pitch, d->period, d->volume, 0); |
1020 | #undef d |
1021 | return 0; |
1022 | #ifdef WSDISPLAY_COMPAT_RAWKBD |
1023 | case WSKBDIO_SETMODE: |
1024 | sc->rawkbd = (*(int *)data == WSKBD_RAW); |
1025 | return 0; |
1026 | #endif |
1027 | } |
1028 | return EPASSTHROUGH; |
1029 | } |
1030 | |
1031 | void |
1032 | pckbd_bell(u_int pitch, u_int period, u_int volume, int poll) |
1033 | { |
1034 | |
1035 | if (pckbd_bell_fn != NULL) |
1036 | (*pckbd_bell_fn)(pckbd_bell_fn_arg, pitch, period, |
1037 | volume, poll); |
1038 | } |
1039 | |
1040 | void |
1041 | pckbd_unhook_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg) |
1042 | { |
1043 | if (pckbd_bell_fn != fn && pckbd_bell_fn_arg != arg) |
1044 | return; |
1045 | pckbd_bell_fn = NULL; |
1046 | pckbd_bell_fn_arg = NULL; |
1047 | } |
1048 | |
1049 | void |
1050 | pckbd_hookup_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *arg) |
1051 | { |
1052 | |
1053 | if (pckbd_bell_fn == NULL) { |
1054 | pckbd_bell_fn = fn; |
1055 | pckbd_bell_fn_arg = arg; |
1056 | } |
1057 | } |
1058 | |
1059 | int |
1060 | pckbd_cnattach(pckbport_tag_t kbctag, int kbcslot) |
1061 | { |
1062 | int res; |
1063 | u_char cmd[1]; |
1064 | |
1065 | res = pckbd_init(&pckbd_consdata, kbctag, kbcslot, 1); |
1066 | /* We may allow the console to be attached if no keyboard is present */ |
1067 | #if defined(PCKBD_CNATTACH_MAY_FAIL) |
1068 | if (res) |
1069 | return res; |
1070 | #endif |
1071 | |
1072 | /* Just to be sure. */ |
1073 | cmd[0] = KBC_ENABLE; |
1074 | res = pckbport_poll_cmd(kbctag, kbcslot, cmd, 1, 0, 0, 0); |
1075 | |
1076 | #if defined(PCKBD_CNATTACH_MAY_FAIL) |
1077 | if (res) |
1078 | return res; |
1079 | #endif |
1080 | |
1081 | wskbd_cnattach(&pckbd_consops, &pckbd_consdata, &pckbd_keymapdata); |
1082 | |
1083 | return res; |
1084 | } |
1085 | |
1086 | /* ARGSUSED */ |
1087 | void |
1088 | pckbd_cngetc(void *v, u_int *type, int *data) |
1089 | { |
1090 | struct pckbd_internal *t = v; |
1091 | int val; |
1092 | |
1093 | for (;;) { |
1094 | val = pckbport_poll_data(t->t_kbctag, t->t_kbcslot); |
1095 | if (val == -1) |
1096 | continue; |
1097 | |
1098 | val = pckbd_scancode_translate(t, val); |
1099 | if (val == 0) |
1100 | continue; |
1101 | |
1102 | if (pckbd_decode(t, val, type, data)) |
1103 | return; |
1104 | } |
1105 | } |
1106 | |
1107 | void |
1108 | pckbd_cnpollc(void *v, int on) |
1109 | { |
1110 | struct pckbd_internal *t = v; |
1111 | |
1112 | pckbport_set_poll(t->t_kbctag, t->t_kbcslot, on); |
1113 | } |
1114 | |
1115 | void |
1116 | pckbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) |
1117 | { |
1118 | |
1119 | pckbd_bell(pitch, period, volume, 1); |
1120 | } |
1121 | |