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