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