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