1 /* $NetBSD: dnkbd.c,v 1.14 2023/01/15 06:19:45 tsutsui Exp $ */
2 /* $OpenBSD: dnkbd.c,v 1.17 2009/07/23 21:05:56 blambert Exp $ */
3
4 /*
5 * Copyright (c) 2005, Miodrag Vallat
6 * Copyright (c) 1997 Michael Smith. 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 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * Driver for the Apollo Domain keyboard and mouse.
32 *
33 * Random notes on the Apollo keyboard :
34 *
35 * - Powers up in 'cooked' mode, where the alpha keys generate ascii rather
36 * than make/break codes. Other keys seem to behave OK though.
37 *
38 * - Alt L/R keys generate two-byte sequence :
39 * make break
40 * L 0xfe,2 0xfe,3
41 * R 0xfe,0 0xfe,1
42 *
43 * - Mouse activity shows up inline in 4-byte packets introduced with 0xdf.
44 * Byte 1 is 1MRL0000 where M, R, L are the mouse buttons, and 0 is
45 * down, 1 is up.
46 * Byte 2 is 2's complement X movement, +ve to the right.
47 * Byte 3 is 2's complement Y movement, +ve is up.
48 *
49 * - Keyboard recognises commands sent to it, all preceded by 0xff. Commands
50 * are echoed once sent completely.
51 *
52 * 0x00 go to cooked mode.
53 * 0x01 go to 'raw' (scancode) mode.
54 * 0x12,0x21 status report as <id1>\r<id2>\r<model>\r followed by 0xff
55 * and then the cooked/raw status.
56 * 0x21,0x81 beep on
57 * 0x21,0x82 beep off
58 *
59 * Some version examples :
60 *
61 * <3-@> <1-0> <SD-03687-MS> Apollo p/n 007121 REV 00 ('old-style' US layout)
62 * <3-@> <2-0> <SD-03683-MS> Apollo p/n 007121 REV 01 ('old-style' US layout)
63 * <3-@> <2-0> <SD-03980-MS> Apollo 3500? keyboard.
64 * <3-@> <X-X> <RX-60857-HW> HP p/n A1630-82001 R2
65 * ('new-style' off 425t, US layout),
66 * also Apollo p/n 014555-002
67 * ('new-style' off DN5500, US layout).
68 */
69
70 #include "opt_wsdisplay_compat.h"
71
72 #include "wsdisplay.h"
73 #include "wsmouse.h"
74
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/device.h>
78 #include <sys/ioctl.h>
79 #include <sys/kernel.h>
80 #include <sys/callout.h>
81 #include <sys/conf.h>
82 #include <sys/bus.h>
83 #include <sys/cpu.h>
84
85 #include <machine/autoconf.h>
86
87 #include <dev/cons.h>
88
89 #include <dev/wscons/wsconsio.h>
90 #include <dev/wscons/wskbdvar.h>
91 #include <dev/wscons/wsksymdef.h>
92 #include <dev/wscons/wsksymvar.h>
93 #if NWSDISPLAY > 0
94 #include <dev/wscons/wsdisplayvar.h>
95 #endif
96 #if NWSMOUSE > 0
97 #include <dev/wscons/wsmousevar.h>
98 #endif
99
100 #include <dev/ic/ns16550reg.h>
101 #include <dev/ic/comreg.h>
102
103 #include <hp300/dev/dnkbdmap.h>
104 #include <hp300/dev/frodoreg.h>
105 #include <hp300/dev/frodovar.h>
106
107 #include "hilkbd.h"
108 #include "ioconf.h"
109
110 /*
111 * Keyboard key codes
112 */
113
114 #define DNKEY_CAPSLOCK 0x7e
115 #define DNKEY_REPEAT 0x7f
116 #define DNKEY_RELEASE 0x80
117 #define DNKEY_CHANNEL 0xff
118
119 /*
120 * Channels
121 */
122
123 #define DNCHANNEL_RESET 0x00
124 #define DNCHANNEL_KBD 0x01
125 #define DNCHANNEL_MOUSE 0x02
126
127 /*
128 * Keyboard modes
129 */
130
131 #define DNMODE_COOKED 0x00
132 #define DNMODE_RAW 0x01
133
134 /*
135 * Keyboard commands
136 */
137
138 #define DNCMD_PREFIX 0xff
139 #define DNCMD_COOKED DNMODE_COOKED
140 #define DNCMD_RAW DNMODE_RAW
141 #define DNCMD_IDENT_1 0x12
142 #define DNCMD_IDENT_2 0x21
143
144 /*
145 * Bell commands
146 */
147
148 #define DNCMD_BELL 0x21
149 #define DNCMD_BELL_ON 0x81
150 #define DNCMD_BELL_OFF 0x82
151
152 /*
153 * Mouse status
154 */
155
156 #define DNBUTTON_L 0x10
157 #define DNBUTTON_R 0x20
158 #define DNBUTTON_M 0x40
159
160 struct dnkbd_softc {
161 device_t sc_dev;
162 bus_space_tag_t sc_bst;
163 bus_space_handle_t sc_bsh;
164
165 int sc_flags;
166 #define SF_ENABLED 0x01 /* keyboard enabled */
167 #define SF_CONSOLE 0x02 /* keyboard is console */
168 #define SF_POLLING 0x04 /* polling mode */
169 #define SF_PLUGGED 0x08 /* keyboard has been seen plugged */
170 #define SF_ATTACHED 0x10 /* subdevices have been attached */
171 #define SF_MOUSE 0x20 /* mouse enabled */
172 #define SF_BELL 0x40 /* bell is active */
173 #define SF_BELL_TMO 0x80 /* bell stop timeout is scheduled */
174
175 u_int sc_identlen;
176 #define MAX_IDENTLEN 32
177 char sc_ident[MAX_IDENTLEN];
178 kbd_t sc_layout;
179
180 enum { STATE_KEYBOARD, STATE_MOUSE, STATE_CHANNEL, STATE_ECHO }
181 sc_state, sc_prevstate;
182 u_int sc_echolen;
183
184 uint8_t sc_mousepkt[3]; /* mouse packet being constructed */
185 u_int sc_mousepos; /* index in above */
186
187 struct callout sc_bellstop_tmo;
188
189 device_t sc_wskbddev;
190 #if NWSMOUSE > 0
191 device_t sc_wsmousedev;
192 #endif
193
194 #ifdef WSDISPLAY_COMPAT_RAWKBD
195 int sc_rawkbd;
196 #endif
197 };
198
199 static int dnkbd_match(device_t, cfdata_t, void *);
200 static void dnkbd_attach(device_t, device_t, void *);
201
202 CFATTACH_DECL_NEW(dnkbd, sizeof(struct dnkbd_softc),
203 dnkbd_match, dnkbd_attach, NULL, NULL);
204
205 static void dnkbd_init(struct dnkbd_softc *, uint16_t, uint16_t);
206 static int dnkbd_enable(void *, int);
207 static void dnkbd_set_leds(void *, int);
208 static int dnkbd_ioctl(void *, u_long, void *, int, struct lwp *);
209
210 static const struct wskbd_accessops dnkbd_accessops = {
211 dnkbd_enable,
212 dnkbd_set_leds,
213 dnkbd_ioctl
214 };
215
216 #if NWSMOUSE > 0
217 static int dnmouse_enable(void *);
218 static int dnmouse_ioctl(void *, u_long, void *, int, struct lwp *);
219 static void dnmouse_disable(void *);
220
221 static const struct wsmouse_accessops dnmouse_accessops = {
222 dnmouse_enable,
223 dnmouse_ioctl,
224 dnmouse_disable
225 };
226 #endif
227
228 static void dnkbd_bell(void *, u_int, u_int, u_int);
229 static void dnkbd_cngetc(void *, u_int *, int *);
230 static void dnkbd_cnpollc(void *, int);
231
232 static const struct wskbd_consops dnkbd_consops = {
233 dnkbd_cngetc,
234 dnkbd_cnpollc,
235 dnkbd_bell
236 };
237
238 static struct wskbd_mapdata dnkbd_keymapdata = {
239 dnkbd_keydesctab,
240 #ifdef DNKBD_LAYOUT
241 DNKBD_LAYOUT
242 #else
243 KB_US
244 #endif
245 };
246
247 typedef enum { EVENT_NONE, EVENT_KEYBOARD, EVENT_MOUSE } dnevent;
248
249 #define APCIBRD(x) (500000 / (x))
250
251 static void dnevent_kbd(struct dnkbd_softc *, int);
252 static void dnevent_kbd_internal(struct dnkbd_softc *, int);
253 static void dnevent_mouse(struct dnkbd_softc *, uint8_t *);
254 static void dnkbd_attach_subdevices(struct dnkbd_softc *);
255 static void dnkbd_bellstop(void *);
256 static void dnkbd_decode(int, u_int *, int *);
257 static dnevent dnkbd_input(struct dnkbd_softc *, int);
258 static int dnkbd_intr(void *);
259 static int dnkbd_pollin(struct dnkbd_softc *, u_int);
260 static int dnkbd_pollout(struct dnkbd_softc *, int);
261 static int dnkbd_probe(struct dnkbd_softc *);
262 static int dnkbd_send(struct dnkbd_softc *, const uint8_t *, size_t);
263 static void dnkbd_break(struct dnkbd_softc *, int);
264
265 int
dnkbd_match(device_t parent,cfdata_t cf,void * aux)266 dnkbd_match(device_t parent, cfdata_t cf, void *aux)
267 {
268 struct frodo_attach_args *fa = aux;
269
270 if (strcmp(fa->fa_name, dnkbd_cd.cd_name) != 0)
271 return 0;
272
273 if (machineid == HP_382) {
274 /* 382 has frodo but no Domain keyboard connector. */
275 return 0;
276 }
277
278 /* only attach to the first frodo port */
279 return fa->fa_offset == FRODO_APCI_OFFSET(0);
280 }
281
282 void
dnkbd_attach(device_t parent,device_t self,void * aux)283 dnkbd_attach(device_t parent, device_t self, void *aux)
284 {
285 struct dnkbd_softc *sc = device_private(self);
286 struct frodo_attach_args *fa = aux;
287
288 aprint_normal(": ");
289
290 sc->sc_dev = self;
291 sc->sc_bst = fa->fa_bst;
292 if (bus_space_map(sc->sc_bst, fa->fa_base + fa->fa_offset,
293 FRODO_APCISPACE, 0, &sc->sc_bsh) != 0) {
294 aprint_error(": can't map i/o space\n");
295 return;
296 }
297
298 callout_init(&sc->sc_bellstop_tmo, 0);
299 callout_setfunc(&sc->sc_bellstop_tmo, dnkbd_bellstop, sc);
300
301 /* reset the port */
302 dnkbd_init(sc, 1200, LCR_8BITS | LCR_PEVEN | LCR_PENAB);
303
304 frodo_intr_establish(parent, dnkbd_intr, sc, fa->fa_line, IPL_VM);
305
306 /* send break to reset keyboard state */
307 dnkbd_break(sc, 1);
308 delay(10 * 1000); /* 10ms for 12 space bits */
309 dnkbd_break(sc, 0);
310 delay(10 * 1000);
311
312 /* probe for keyboard */
313 if (dnkbd_probe(sc) != 0) {
314 aprint_normal("no keyboard\n");
315 return;
316 }
317
318 dnkbd_attach_subdevices(sc);
319 }
320
321 void
dnkbd_init(struct dnkbd_softc * sc,uint16_t rate,uint16_t lctl)322 dnkbd_init(struct dnkbd_softc *sc, uint16_t rate, uint16_t lctl)
323 {
324 bus_space_tag_t bst;
325 bus_space_handle_t bsh;
326 u_int divisor;
327
328 bst = sc->sc_bst;
329 bsh = sc->sc_bsh;
330
331 divisor = APCIBRD(rate);
332 bus_space_write_1(bst, bsh, com_lctl, LCR_DLAB);
333 bus_space_write_1(bst, bsh, com_dlbl, divisor & 0xff);
334 bus_space_write_1(bst, bsh, com_dlbh, (divisor >> 8) & 0xff);
335 bus_space_write_1(bst, bsh, com_lctl, lctl);
336 bus_space_write_1(bst, bsh, com_ier, IER_ERXRDY | IER_ETXRDY);
337 bus_space_write_1(bst, bsh, com_fifo,
338 FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1);
339 bus_space_write_1(bst, bsh, com_mcr, MCR_DTR | MCR_RTS);
340
341 delay(100);
342 (void)bus_space_read_1(bst, bsh, com_iir);
343 }
344
345 void
dnkbd_attach_subdevices(struct dnkbd_softc * sc)346 dnkbd_attach_subdevices(struct dnkbd_softc *sc)
347 {
348 struct wskbddev_attach_args ka;
349 #if NWSMOUSE > 0
350 struct wsmousedev_attach_args ma;
351 #endif
352 #if NHILKBD > 0
353 extern int hil_is_console;
354 #endif
355
356 /*
357 * If both hilkbd and dnkbd are configured, prefer the Domain
358 * keyboard as console (if we are here, we know the keyboard is
359 * plugged), unless the console keyboard has been claimed already
360 * (i.e. late hotplug with hil keyboard plugged first).
361 */
362 #if NWSDISPLAY > 0
363 if (cn_tab->cn_putc == wsdisplay_cnputc) {
364 #if NHILKBD > 0
365 if (hil_is_console == -1) {
366 ka.console = 1;
367 hil_is_console = 0;
368 } else
369 ka.console = 0;
370 #else
371 ka.console = 1;
372 #endif
373 } else
374 #endif
375 {
376 ka.console = 0;
377 }
378
379 ka.keymap = &dnkbd_keymapdata;
380 ka.accessops = &dnkbd_accessops;
381 ka.accesscookie = sc;
382 #ifndef DKKBD_LAYOUT
383 dnkbd_keymapdata.layout = sc->sc_layout;
384 #endif
385
386 if (ka.console) {
387 sc->sc_flags = SF_PLUGGED | SF_CONSOLE | SF_ENABLED;
388 wskbd_cnattach(&dnkbd_consops, sc, &dnkbd_keymapdata);
389 } else {
390 sc->sc_flags = SF_PLUGGED;
391 }
392
393 sc->sc_wskbddev = config_found(sc->sc_dev, &ka, wskbddevprint,
394 CFARGS(.iattr = "wskbddev"));
395
396 #if NWSMOUSE > 0
397 ma.accessops = &dnmouse_accessops;
398 ma.accesscookie = sc;
399
400 sc->sc_wsmousedev = config_found(sc->sc_dev, &ma, wsmousedevprint,
401 CFARGS(.iattr = "wsmousedev"));
402 #endif
403
404 SET(sc->sc_flags, SF_ATTACHED);
405 }
406
407 int
dnkbd_probe(struct dnkbd_softc * sc)408 dnkbd_probe(struct dnkbd_softc *sc)
409 {
410 int dat, rc, flags;
411 uint8_t cmdbuf[2];
412 char rspbuf[MAX_IDENTLEN], *word, *end;
413 u_int i;
414 int s;
415
416 s = spltty();
417 flags = sc->sc_flags;
418 SET(sc->sc_flags, SF_POLLING);
419 sc->sc_state = STATE_CHANNEL;
420 splx(s);
421
422 /*
423 * Switch keyboard to raw mode.
424 */
425 cmdbuf[0] = DNCMD_RAW;
426 rc = dnkbd_send(sc, cmdbuf, 1);
427 if (rc != 0)
428 goto out;
429
430 /*
431 * Send the identify command.
432 */
433 cmdbuf[0] = DNCMD_IDENT_1;
434 cmdbuf[1] = DNCMD_IDENT_2;
435 rc = dnkbd_send(sc, cmdbuf, 2);
436 if (rc != 0)
437 goto out;
438
439 for (i = 0; ; i++) {
440 dat = dnkbd_pollin(sc, 10000);
441 if (dat == -1)
442 break;
443
444 if (i < sizeof(rspbuf))
445 rspbuf[i] = dat;
446 }
447
448 if (i > sizeof(rspbuf) || i == 0) {
449 aprint_error_dev(sc->sc_dev,
450 "unexpected identify string length %d\n", i);
451 rc = ENXIO;
452 goto out;
453 }
454
455 /*
456 * Make sure the identification string is NULL terminated
457 * (overwriting the keyboard mode byte if necessary).
458 */
459 i--;
460 if (rspbuf[i] != 0)
461 rspbuf[i] = 0;
462
463 /*
464 * Now display the identification strings, if they changed.
465 */
466 if (i != sc->sc_identlen || memcmp(rspbuf, sc->sc_ident, i) != 0) {
467 sc->sc_layout = KB_US;
468 sc->sc_identlen = i;
469 memcpy(sc->sc_ident, rspbuf, i);
470
471 if (cold == 0)
472 aprint_normal_dev(sc->sc_dev, "");
473 aprint_normal("model ");
474 word = rspbuf;
475 for (i = 0; i < 3; i++) {
476 end = strchr(word, '\r');
477 if (end == NULL)
478 break;
479 *end++ = '\0';
480 aprint_normal("<%s> ", word);
481 /*
482 * Parse the layout code if applicable
483 */
484 if (i == 1 && *word++ == '3') {
485 if (*word == '-')
486 word++;
487 switch (*word) {
488 #if 0
489 default:
490 case ' ':
491 sc->sc_layout = KB_US;
492 break;
493 #endif
494 case 'a':
495 sc->sc_layout = KB_DE;
496 break;
497 case 'b':
498 sc->sc_layout = KB_FR;
499 break;
500 case 'c':
501 sc->sc_layout = KB_DK;
502 break;
503 case 'd':
504 sc->sc_layout = KB_SV;
505 break;
506 case 'e':
507 sc->sc_layout = KB_UK;
508 break;
509 case 'f':
510 sc->sc_layout = KB_JP;
511 break;
512 case 'g':
513 sc->sc_layout = KB_SG;
514 break;
515 }
516 }
517 word = end;
518 }
519 aprint_normal("\n");
520 }
521
522 /*
523 * Ready to work, the default channel is the keyboard.
524 */
525 sc->sc_state = STATE_KEYBOARD;
526
527 out:
528 s = spltty();
529 sc->sc_flags = flags;
530 splx(s);
531
532 return rc;
533 }
534
535 /*
536 * State machine.
537 *
538 * In raw mode, the keyboard may feed us the following sequences:
539 * - on the keyboard channel:
540 * + a raw key code, in the range 0x01-0x7e, or'ed with 0x80 if key release.
541 * + the key repeat sequence 0x7f.
542 * - on the mouse channel:
543 * + a 3 byte mouse sequence (buttons state, dx move, dy move).
544 * - at any time:
545 * + a 2 byte channel sequence (0xff followed by the channel number) telling
546 * us which device the following input will come from.
547 * + if we get 0xff but an invalid channel number, this is a command echo.
548 * Currently we only handle this for bell commands, which size are known.
549 * Other commands are issued through dnkbd_send() which ``eats'' the echo.
550 *
551 * Older keyboards reset the channel to the keyboard (by sending ff 01) after
552 * every mouse packet.
553 */
554
555 dnevent
dnkbd_input(struct dnkbd_softc * sc,int dat)556 dnkbd_input(struct dnkbd_softc *sc, int dat)
557 {
558 dnevent event = EVENT_NONE;
559
560 switch (sc->sc_state) {
561 case STATE_KEYBOARD:
562 switch (dat) {
563 case DNKEY_REPEAT:
564 /*
565 * We ignore event repeats, as wskbd does its own
566 * soft repeat processing.
567 */
568 break;
569 case DNKEY_CHANNEL:
570 sc->sc_prevstate = sc->sc_state;
571 sc->sc_state = STATE_CHANNEL;
572 break;
573 default:
574 event = EVENT_KEYBOARD;
575 break;
576 }
577 break;
578
579 case STATE_MOUSE:
580 if (dat == DNKEY_CHANNEL && sc->sc_mousepos == 0) {
581 sc->sc_prevstate = sc->sc_state;
582 sc->sc_state = STATE_CHANNEL;
583 } else {
584 sc->sc_mousepkt[sc->sc_mousepos++] = dat;
585 if (sc->sc_mousepos == sizeof(sc->sc_mousepkt)) {
586 sc->sc_mousepos = 0;
587 event = EVENT_MOUSE;
588 }
589 }
590 break;
591
592 case STATE_CHANNEL:
593 switch (dat) {
594 case DNKEY_CHANNEL:
595 /*
596 * During hotplug, we might get spurious 0xff bytes.
597 * Ignore them.
598 */
599 break;
600 case DNCHANNEL_RESET:
601 /*
602 * Identify the keyboard again. This will switch it to
603 * raw mode again. If this fails, we'll consider the
604 * keyboard as unplugged (to ignore further events until
605 * a successful reset).
606 */
607 if (dnkbd_probe(sc) == 0) {
608 /*
609 * We need to attach wskbd and wsmouse children
610 * if this is a live first plug.
611 */
612 if (!ISSET(sc->sc_flags, SF_ATTACHED))
613 dnkbd_attach_subdevices(sc);
614 SET(sc->sc_flags, SF_PLUGGED);
615 } else {
616 CLR(sc->sc_flags, SF_PLUGGED);
617 }
618
619 sc->sc_state = STATE_KEYBOARD;
620 break;
621 case DNCHANNEL_KBD:
622 sc->sc_state = STATE_KEYBOARD;
623 break;
624 case DNCHANNEL_MOUSE:
625 sc->sc_state = STATE_MOUSE;
626 sc->sc_mousepos = 0; /* just in case */
627 break;
628 case DNCMD_BELL:
629 /*
630 * We are getting a bell command echoed to us.
631 * Ignore it.
632 */
633 sc->sc_state = STATE_ECHO;
634 sc->sc_echolen = 1; /* one byte to follow */
635 break;
636 default:
637 printf("%s: unexpected channel byte %02x\n",
638 device_xname(sc->sc_dev), dat);
639 break;
640 }
641 break;
642
643 case STATE_ECHO:
644 if (--sc->sc_echolen == 0) {
645 /* get back to the state we were in before the echo */
646 sc->sc_state = sc->sc_prevstate;
647 }
648 break;
649 }
650
651 return event;
652 }
653
654 /*
655 * Event breakers.
656 */
657
658 void
dnkbd_decode(int keycode,u_int * type,int * key)659 dnkbd_decode(int keycode, u_int *type, int *key)
660 {
661 *type = (keycode & DNKEY_RELEASE) ?
662 WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
663 *key = (keycode & ~DNKEY_RELEASE);
664 }
665
666 void
dnevent_kbd(struct dnkbd_softc * sc,int dat)667 dnevent_kbd(struct dnkbd_softc *sc, int dat)
668 {
669 if (!ISSET(sc->sc_flags, SF_PLUGGED))
670 return;
671
672 if (sc->sc_wskbddev == NULL)
673 return;
674
675 if (!ISSET(sc->sc_flags, SF_ENABLED))
676 return;
677
678 /*
679 * Even in raw mode, the caps lock key is treated specially:
680 * first key press causes event 0x7e, release causes no event;
681 * then a new key press causes nothing, and release causes
682 * event 0xfe. Moreover, while kept down, it does not produce
683 * repeat events.
684 *
685 * So the best we can do is fake the missed events, but this
686 * will not allow the capslock key to be remapped as a control
687 * key since it will not be possible to chord it with anything.
688 */
689 dnevent_kbd_internal(sc, dat);
690 if ((dat & ~DNKEY_RELEASE) == DNKEY_CAPSLOCK)
691 dnevent_kbd_internal(sc, dat ^ DNKEY_RELEASE);
692 }
693
694 void
dnevent_kbd_internal(struct dnkbd_softc * sc,int dat)695 dnevent_kbd_internal(struct dnkbd_softc *sc, int dat)
696 {
697 u_int type;
698 int key;
699 int s;
700
701 dnkbd_decode(dat, &type, &key);
702
703 #ifdef WSDISPLAY_COMPAT_RAWKBD
704 if (sc->sc_rawkbd) {
705 u_char cbuf[2];
706 int c, j;
707
708 j = 0;
709 c = dnkbd_raw[key];
710 if (c != 0) {
711 /* fake extended scancode if necessary */
712 if (c & 0x80)
713 cbuf[j++] = 0xe0;
714 cbuf[j] = c & 0x7f;
715 if (type == WSCONS_EVENT_KEY_UP)
716 cbuf[j] |= 0x80;
717 j++;
718 }
719
720 if (j != 0) {
721 s = spltty();
722 wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
723 splx(s);
724 }
725 } else
726 #endif
727 {
728 s = spltty();
729 wskbd_input(sc->sc_wskbddev, type, key);
730 splx(s);
731 }
732 }
733
734 #if NWSMOUSE > 0
735 void
dnevent_mouse(struct dnkbd_softc * sc,uint8_t * dat)736 dnevent_mouse(struct dnkbd_softc *sc, uint8_t *dat)
737 {
738 if (!ISSET(sc->sc_flags, SF_PLUGGED))
739 return;
740
741 if (sc->sc_wsmousedev == NULL)
742 return;
743
744 if (!ISSET(sc->sc_flags, SF_MOUSE))
745 return;
746
747 /*
748 * First byte is button status. It has the 0x80 bit always set, and
749 * the next 3 bits are *cleared* when the mouse buttons are pressed.
750 */
751 #ifdef DEBUG
752 if (!ISSET(*dat, 0x80)) {
753 printf("%s: incorrect mouse packet %02x %02x %02x\n",
754 device_xname(sc->sc_dev), dat[0], dat[1], dat[2]);
755 return;
756 }
757 #endif
758
759 wsmouse_input(sc->sc_wsmousedev,
760 (~dat[0] & (DNBUTTON_L | DNBUTTON_M | DNBUTTON_R)) >> 4,
761 (int8_t)dat[1], (int8_t)dat[2], 0, 0, WSMOUSE_INPUT_DELTA);
762 }
763 #endif
764
765 /*
766 * Low-level communication routines.
767 */
768
769 int
dnkbd_pollin(struct dnkbd_softc * sc,u_int tries)770 dnkbd_pollin(struct dnkbd_softc *sc, u_int tries)
771 {
772 bus_space_tag_t bst;
773 bus_space_handle_t bsh;
774 u_int cnt;
775
776 bst = sc->sc_bst;
777 bsh = sc->sc_bsh;
778
779 for (cnt = tries; cnt != 0; cnt--) {
780 if (bus_space_read_1(bst, bsh, com_lsr) & LSR_RXRDY)
781 break;
782 DELAY(10);
783 }
784
785 if (cnt == 0)
786 return -1;
787 else
788 return (int)bus_space_read_1(bst, bsh, com_data);
789 }
790
791 int
dnkbd_pollout(struct dnkbd_softc * sc,int dat)792 dnkbd_pollout(struct dnkbd_softc *sc, int dat)
793 {
794 bus_space_tag_t bst;
795 bus_space_handle_t bsh;
796 u_int cnt;
797
798 bst = sc->sc_bst;
799 bsh = sc->sc_bsh;
800
801 for (cnt = 10000; cnt != 0; cnt--) {
802 if (bus_space_read_1(bst, bsh, com_lsr) & LSR_TXRDY)
803 break;
804 DELAY(10);
805 }
806 if (cnt == 0)
807 return EBUSY;
808 else {
809 bus_space_write_1(bst, bsh, com_data, dat);
810 return 0;
811 }
812 }
813
814 int
dnkbd_send(struct dnkbd_softc * sc,const uint8_t * cmdbuf,size_t cmdlen)815 dnkbd_send(struct dnkbd_softc *sc, const uint8_t *cmdbuf, size_t cmdlen)
816 {
817 int cnt, rc, dat;
818 u_int cmdpos;
819
820 /* drain rxfifo */
821 for (cnt = 10; cnt != 0; cnt--) {
822 if (dnkbd_pollin(sc, 10) == -1)
823 break;
824 }
825 if (cnt == 0)
826 return EBUSY;
827
828 /* send command escape */
829 if ((rc = dnkbd_pollout(sc, DNCMD_PREFIX)) != 0)
830 return rc;
831
832 /* send command buffer */
833 for (cmdpos = 0; cmdpos < cmdlen; cmdpos++) {
834 if ((rc = dnkbd_pollout(sc, cmdbuf[cmdpos])) != 0)
835 return rc;
836 }
837
838 /* wait for command echo */
839 do {
840 dat = dnkbd_pollin(sc, 10000);
841 if (dat == -1)
842 return EIO;
843 } while (dat != DNCMD_PREFIX);
844
845 for (cmdpos = 0; cmdpos < cmdlen; cmdpos++) {
846 dat = dnkbd_pollin(sc, 10000);
847 if (dat != cmdbuf[cmdpos])
848 return EIO;
849 }
850
851 return 0;
852 }
853
854 void
dnkbd_break(struct dnkbd_softc * sc,int onoff)855 dnkbd_break(struct dnkbd_softc *sc, int onoff)
856 {
857 bus_space_tag_t bst;
858 bus_space_handle_t bsh;
859 uint8_t reg;
860
861 bst = sc->sc_bst;
862 bsh = sc->sc_bsh;
863
864 reg = bus_space_read_1(bst, bsh, com_lctl);
865 if (onoff)
866 reg |= LCR_SBREAK;
867 else
868 reg &= ~LCR_SBREAK;
869 bus_space_write_1(bst, bsh, com_lctl, reg);
870 }
871
872 int
dnkbd_intr(void * v)873 dnkbd_intr(void *v)
874 {
875 struct dnkbd_softc *sc = v;
876 bus_space_tag_t bst;
877 bus_space_handle_t bsh;
878 uint8_t iir, lsr, c;
879 int claimed = 0;
880
881 bst = sc->sc_bst;
882 bsh = sc->sc_bsh;
883
884 for (;;) {
885 iir = bus_space_read_1(bst, bsh, com_iir);
886
887 switch (iir & IIR_IMASK) {
888 case IIR_RLS:
889 /*
890 * Line status change. This should never happen,
891 * so silently ack the interrupt.
892 */
893 c = bus_space_read_1(bst, bsh, com_lsr);
894 break;
895
896 case IIR_RXRDY:
897 case IIR_RXTOUT:
898 /*
899 * Data available. We process it byte by byte,
900 * unless we are doing polling work...
901 */
902 if (ISSET(sc->sc_flags, SF_POLLING)) {
903 return 1;
904 }
905
906 for (;;) {
907 c = bus_space_read_1(bst, bsh, com_data);
908 switch (dnkbd_input(sc, c)) {
909 case EVENT_KEYBOARD:
910 dnevent_kbd(sc, c);
911 break;
912 #if NWSMOUSE > 0
913 case EVENT_MOUSE:
914 dnevent_mouse(sc, sc->sc_mousepkt);
915 break;
916 #endif
917 default: /* appease gcc */
918 break;
919 }
920 lsr = bus_space_read_1(bst, bsh, com_lsr) &
921 LSR_RCV_MASK;
922 if (lsr == 0)
923 break;
924 else if (lsr != LSR_RXRDY) {
925 /* ignore error */
926 break;
927 }
928 }
929 break;
930
931 case IIR_TXRDY:
932 /*
933 * Transmit available. Since we do all our commands
934 * in polling mode, we do not need to do anything here.
935 */
936 break;
937
938 default:
939 if (iir & IIR_NOPEND)
940 return claimed;
941 /* FALLTHROUGH */
942
943 case IIR_MLSC:
944 /*
945 * Modem status change. This should never happen,
946 * so silently ack the interrupt.
947 */
948 c = bus_space_read_1(bst, bsh, com_msr);
949 break;
950 }
951
952 claimed = 1;
953 }
954 }
955
956 /*
957 * Wskbd callbacks
958 */
959
960 int
dnkbd_enable(void * v,int on)961 dnkbd_enable(void *v, int on)
962 {
963 struct dnkbd_softc *sc = v;
964
965 if (on) {
966 if (ISSET(sc->sc_flags, SF_ENABLED))
967 return EBUSY;
968 SET(sc->sc_flags, SF_ENABLED);
969 } else {
970 if (ISSET(sc->sc_flags, SF_CONSOLE))
971 return EBUSY;
972 CLR(sc->sc_flags, SF_ENABLED);
973 }
974
975 return 0;
976 }
977
978 void
dnkbd_set_leds(void * v,int leds)979 dnkbd_set_leds(void *v, int leds)
980 {
981 /*
982 * Not supported. There is only one LED on this keyboard, and
983 * is hardware tied to the caps lock key.
984 */
985 }
986
987 int
dnkbd_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)988 dnkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
989 {
990 #ifdef WSDISPLAY_COMPAT_RAWKBD
991 struct dnkbd_softc *sc = v;
992 #endif
993
994 #define WSKBD_TYPE_UNKNOWN 0
995
996 switch (cmd) {
997 case WSKBDIO_GTYPE:
998 *(int *)data = WSKBD_TYPE_UNKNOWN; /* XXX */
999 return 0;
1000 case WSKBDIO_SETLEDS:
1001 return ENXIO;
1002 case WSKBDIO_GETLEDS:
1003 *(int *)data = 0;
1004 return 0;
1005 case WSKBDIO_COMPLEXBELL:
1006 #define d ((struct wskbd_bell_data *)data)
1007 dnkbd_bell(v, d->period, d->pitch, d->volume);
1008 #undef d
1009 return 0;
1010 #ifdef WSDISPLAY_COMPAT_RAWKBD
1011 case WSKBDIO_SETMODE:
1012 sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
1013 return 0;
1014 #endif
1015 }
1016
1017 return EPASSTHROUGH;
1018 }
1019
1020 #if NWSMOUSE > 0
1021 /*
1022 * Wsmouse callbacks
1023 */
1024
1025 int
dnmouse_enable(void * v)1026 dnmouse_enable(void *v)
1027 {
1028 struct dnkbd_softc *sc = v;
1029
1030 if (ISSET(sc->sc_flags, SF_MOUSE))
1031 return EBUSY;
1032 SET(sc->sc_flags, SF_MOUSE);
1033
1034 return 0;
1035 }
1036
1037 int
dnmouse_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)1038 dnmouse_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
1039 {
1040 #if 0
1041 struct dnkbd_softc *sc = v;
1042 #endif
1043
1044 #define WSMOUSE_TYPE_UNKNOWN 0
1045
1046 switch (cmd) {
1047 case WSMOUSEIO_GTYPE:
1048 *(int *)data = WSMOUSE_TYPE_UNKNOWN; /* XXX */
1049 return 0;
1050 }
1051
1052 return -1;
1053 }
1054
1055 void
dnmouse_disable(void * v)1056 dnmouse_disable(void *v)
1057 {
1058 struct dnkbd_softc *sc = v;
1059
1060 CLR(sc->sc_flags, SF_MOUSE);
1061 }
1062 #endif
1063
1064 /*
1065 * Console support
1066 */
1067
1068 void
dnkbd_cngetc(void * v,u_int * type,int * data)1069 dnkbd_cngetc(void *v, u_int *type, int *data)
1070 {
1071 static int lastdat = 0;
1072 struct dnkbd_softc *sc = v;
1073 int s;
1074 int dat;
1075
1076 /* Take care of caps lock */
1077 if ((lastdat & ~DNKEY_RELEASE) == DNKEY_CAPSLOCK) {
1078 dat = lastdat ^ DNKEY_RELEASE;
1079 lastdat = 0;
1080 } else {
1081 for (;;) {
1082 s = splhigh();
1083 dat = dnkbd_pollin(sc, 10000);
1084 if (dat != -1) {
1085 if (dnkbd_input(sc, dat) == EVENT_KEYBOARD) {
1086 splx(s);
1087 break;
1088 }
1089 }
1090 splx(s);
1091 }
1092 lastdat = dat;
1093 }
1094
1095 dnkbd_decode(dat, type, data);
1096 }
1097
1098 void
dnkbd_cnpollc(void * v,int on)1099 dnkbd_cnpollc(void *v, int on)
1100 {
1101 struct dnkbd_softc *sc = v;
1102
1103 if (on)
1104 SET(sc->sc_flags, SF_POLLING);
1105 else
1106 CLR(sc->sc_flags, SF_POLLING);
1107 }
1108
1109 /*
1110 * Bell routines.
1111 */
1112 void
dnkbd_bell(void * v,u_int period,u_int pitch,u_int volume)1113 dnkbd_bell(void *v, u_int period, u_int pitch, u_int volume)
1114 {
1115 struct dnkbd_softc *sc = v;
1116 int s;
1117
1118 s = spltty();
1119
1120 if (pitch == 0 || period == 0 || volume == 0) {
1121 if (ISSET(sc->sc_flags, SF_BELL_TMO)) {
1122 callout_stop(&sc->sc_bellstop_tmo);
1123 dnkbd_bellstop(v);
1124 }
1125 } else {
1126
1127 if (!ISSET(sc->sc_flags, SF_BELL)) {
1128 dnkbd_pollout(sc, DNCMD_PREFIX);
1129 dnkbd_pollout(sc, DNCMD_BELL);
1130 dnkbd_pollout(sc, DNCMD_BELL_ON);
1131 SET(sc->sc_flags, SF_BELL);
1132 }
1133
1134 if (ISSET(sc->sc_flags, SF_BELL_TMO))
1135 callout_stop(&sc->sc_bellstop_tmo);
1136 callout_schedule(&sc->sc_bellstop_tmo, period);
1137 SET(sc->sc_flags, SF_BELL_TMO);
1138 }
1139
1140 splx(s);
1141 }
1142
1143 void
dnkbd_bellstop(void * v)1144 dnkbd_bellstop(void *v)
1145 {
1146 struct dnkbd_softc *sc = v;
1147 int s;
1148
1149 s = spltty();
1150
1151 dnkbd_pollout(sc, DNCMD_PREFIX);
1152 dnkbd_pollout(sc, DNCMD_BELL);
1153 dnkbd_pollout(sc, DNCMD_BELL_OFF);
1154 CLR(sc->sc_flags, SF_BELL);
1155 CLR(sc->sc_flags, SF_BELL_TMO);
1156
1157 splx(s);
1158 }
1159