root/dev/hil/hilkbd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hilkbdprobe
  2. hilkbdattach
  3. hilkbddetach
  4. hilkbd_enable
  5. hilkbd_set_leds
  6. hilkbd_ioctl
  7. hilkbd_cngetc
  8. hilkbd_cnpollc
  9. hilkbd_cnbell
  10. hilkbd_bell
  11. hilkbd_callback
  12. hilkbd_decode
  13. hilkbd_is_console
  14. hilkbd_rawrepeat

    1 /*      $OpenBSD: hilkbd.c,v 1.13 2006/08/10 23:43:45 miod Exp $        */
    2 /*
    3  * Copyright (c) 2003, Miodrag Vallat.
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
   19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   25  * POSSIBILITY OF SUCH DAMAGE.
   26  *
   27  */
   28 
   29 #include <sys/param.h>
   30 #include <sys/systm.h>
   31 #include <sys/device.h>
   32 #include <sys/ioctl.h>
   33 #include <sys/kernel.h>
   34 #include <sys/timeout.h>
   35 
   36 #include <machine/autoconf.h>
   37 #include <machine/bus.h>
   38 #include <machine/cpu.h>
   39 
   40 #include <dev/hil/hilreg.h>
   41 #include <dev/hil/hilvar.h>
   42 #include <dev/hil/hildevs.h>
   43 
   44 #include <dev/wscons/wsconsio.h>
   45 #include <dev/wscons/wskbdvar.h>
   46 #include <dev/wscons/wsksymdef.h>
   47 #include <dev/wscons/wsksymvar.h>
   48 #ifdef WSDISPLAY_COMPAT_RAWKBD
   49 #include <dev/wscons/wskbdraw.h>
   50 #endif
   51 
   52 #include <dev/hil/hilkbdmap.h>
   53 
   54 struct hilkbd_softc {
   55         struct hildev_softc sc_hildev;
   56 
   57         int             sc_numleds;
   58         int             sc_ledstate;
   59         int             sc_enabled;
   60         int             sc_console;
   61         int             sc_lastarrow;
   62 
   63         struct device   *sc_wskbddev;
   64 
   65 #ifdef WSDISPLAY_COMPAT_RAWKBD
   66         int             sc_rawkbd;
   67         int             sc_nrep;
   68         char            sc_rep[HILBUFSIZE * 2];
   69         struct timeout  sc_rawrepeat_ch;
   70 #define REP_DELAY1      400
   71 #define REP_DELAYN      100
   72 #endif
   73 };
   74 
   75 int     hilkbdprobe(struct device *, void *, void *);
   76 void    hilkbdattach(struct device *, struct device *, void *);
   77 int     hilkbddetach(struct device *, int);
   78 
   79 struct cfdriver hilkbd_cd = {
   80         NULL, "hilkbd", DV_DULL
   81 };
   82 
   83 struct cfattach hilkbd_ca = {
   84         sizeof(struct hilkbd_softc), hilkbdprobe, hilkbdattach, hilkbddetach,
   85 };
   86 
   87 int     hilkbd_enable(void *, int);
   88 void    hilkbd_set_leds(void *, int);
   89 int     hilkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
   90 
   91 const struct wskbd_accessops hilkbd_accessops = {
   92         hilkbd_enable,
   93         hilkbd_set_leds,
   94         hilkbd_ioctl,
   95 };
   96 
   97 void    hilkbd_cngetc(void *, u_int *, int *);
   98 void    hilkbd_cnpollc(void *, int);
   99 void    hilkbd_cnbell(void *, u_int, u_int, u_int);
  100 
  101 const struct wskbd_consops hilkbd_consops = {
  102         hilkbd_cngetc,
  103         hilkbd_cnpollc,
  104         hilkbd_cnbell,
  105 };
  106 
  107 struct wskbd_mapdata hilkbd_keymapdata = {
  108         hilkbd_keydesctab,
  109 #ifdef HILKBD_LAYOUT
  110         HILKBD_LAYOUT,
  111 #else
  112         KB_US,
  113 #endif
  114 };
  115 
  116 struct wskbd_mapdata hilkbd_keymapdata_ps2 = {
  117         hilkbd_keydesctab_ps2,
  118 #ifdef HILKBD_LAYOUT
  119         HILKBD_LAYOUT,
  120 #else
  121         KB_US,
  122 #endif
  123 };
  124 
  125 void    hilkbd_bell(struct hil_softc *, u_int, u_int, u_int);
  126 void    hilkbd_callback(struct hildev_softc *, u_int, u_int8_t *);
  127 void    hilkbd_decode(struct hilkbd_softc *, u_int8_t, u_int *, int *, int);
  128 int     hilkbd_is_console(int);
  129 void    hilkbd_rawrepeat(void *);
  130 
  131 int     seen_hilkbd_console;
  132 
  133 int
  134 hilkbdprobe(struct device *parent, void *match, void *aux)
  135 {
  136         struct hil_attach_args *ha = aux;
  137 
  138         if (ha->ha_type != HIL_DEVICE_KEYBOARD &&
  139             ha->ha_type != HIL_DEVICE_BUTTONBOX)
  140                 return (0);
  141 
  142         return (1);
  143 }
  144 
  145 void
  146 hilkbdattach(struct device *parent, struct device *self, void *aux)
  147 {
  148         struct hilkbd_softc *sc = (void *)self;
  149         struct hil_attach_args *ha = aux;
  150         struct wskbddev_attach_args a;
  151         u_int8_t layoutcode;
  152         int ps2;
  153 
  154         sc->hd_code = ha->ha_code;
  155         sc->hd_type = ha->ha_type;
  156         sc->hd_infolen = ha->ha_infolen;
  157         bcopy(ha->ha_info, sc->hd_info, ha->ha_infolen);
  158         sc->hd_fn = hilkbd_callback;
  159 
  160         if (ha->ha_type == HIL_DEVICE_KEYBOARD) {
  161                 /*
  162                  * Determine the keyboard language configuration, but don't
  163                  * override a user-specified setting.
  164                  */
  165                 layoutcode = ha->ha_id & (MAXHILKBDLAYOUT - 1);
  166 #ifndef HILKBD_LAYOUT
  167                 if (layoutcode < MAXHILKBDLAYOUT &&
  168                     hilkbd_layouts[layoutcode] != -1)
  169                         hilkbd_keymapdata.layout =
  170                         hilkbd_keymapdata_ps2.layout =
  171                             hilkbd_layouts[layoutcode];
  172 #endif
  173 
  174                 printf(", layout %x", layoutcode);
  175         }
  176 
  177         /*
  178          * Interpret the identification bytes, if any
  179          */
  180         if (ha->ha_infolen > 2 && (ha->ha_info[1] & HIL_IOB) != 0) {
  181                 /* HILIOB_PROMPT is not always reported... */
  182                 sc->sc_numleds = (ha->ha_info[2] & HILIOB_PMASK) >> 4;
  183                 if (sc->sc_numleds != 0)
  184                         printf(", %d leds", sc->sc_numleds);
  185         }
  186 
  187         printf("\n");
  188 
  189         /*
  190          * Red lettered keyboards come in two flavours, the old one
  191          * with only one control key, to the left of the escape key,
  192          * and the modern one which has a PS/2 like layout, and leds.
  193          *
  194          * Unfortunately for us, they use the same device ID range.
  195          * We'll differentiate them by looking at the leds property.
  196          */
  197         ps2 = (sc->sc_numleds != 0);
  198 
  199 #ifdef WSDISPLAY_COMPAT_RAWKBD
  200         timeout_set(&sc->sc_rawrepeat_ch, hilkbd_rawrepeat, sc);
  201 #endif
  202 
  203         /* Do not consider button boxes as console devices. */
  204         if (ha->ha_type == HIL_DEVICE_BUTTONBOX)
  205                 a.console = 0;
  206         else
  207                 a.console = hilkbd_is_console(ha->ha_console);
  208         a.keymap = ps2 ? &hilkbd_keymapdata_ps2 : &hilkbd_keymapdata;
  209         a.accessops = &hilkbd_accessops;
  210         a.accesscookie = sc;
  211 
  212         if (a.console) {
  213                 sc->sc_console = sc->sc_enabled = 1;
  214                 wskbd_cnattach(&hilkbd_consops, sc, a.keymap);
  215         } else {
  216                 sc->sc_console = sc->sc_enabled = 0;
  217         }
  218 
  219         sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
  220 
  221         /*
  222          * If this is an old keyboard with a numeric pad but no ``num lock''
  223          * key, simulate it being pressed so that the keyboard runs in
  224          * numeric mode.
  225          */
  226         if (!ps2 && sc->sc_wskbddev != NULL) {
  227                 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, 80);
  228                 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_UP, 80);
  229         }
  230 }
  231 
  232 int
  233 hilkbddetach(struct device *self, int flags)
  234 {
  235         struct hilkbd_softc *sc = (void *)self;
  236 
  237         /*
  238          * Handle console keyboard for the best. It should have been set
  239          * as the first device in the loop anyways.
  240          */
  241         if (sc->sc_console) {
  242                 wskbd_cndetach();
  243                 seen_hilkbd_console = 0;
  244         }
  245 
  246         if (sc->sc_wskbddev != NULL)
  247                 return config_detach(sc->sc_wskbddev, flags);
  248 
  249         return (0);
  250 }
  251 
  252 int
  253 hilkbd_enable(void *v, int on)
  254 {
  255         struct hilkbd_softc *sc = v;
  256 
  257         if (on) {
  258                 if (sc->sc_enabled)
  259                         return (EBUSY);
  260         } else {
  261                 if (sc->sc_console)
  262                         return (EBUSY);
  263         }
  264 
  265         sc->sc_enabled = on;
  266 
  267         return (0);
  268 }
  269 
  270 void
  271 hilkbd_set_leds(void *v, int leds)
  272 {
  273         struct hilkbd_softc *sc = v;
  274         int changemask;
  275 
  276         if (sc->sc_numleds == 0)
  277                 return;
  278 
  279         changemask = leds ^ sc->sc_ledstate;
  280         if (changemask == 0)
  281                 return;
  282 
  283         /* We do not handle more than 3 leds here */
  284         if (changemask & WSKBD_LED_SCROLL)
  285                 send_hildev_cmd((struct hildev_softc *)sc,
  286                     (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1,
  287                     NULL, NULL);
  288         if (changemask & WSKBD_LED_NUM)
  289                 send_hildev_cmd((struct hildev_softc *)sc,
  290                     (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2,
  291                     NULL, NULL);
  292         if (changemask & WSKBD_LED_CAPS)
  293                 send_hildev_cmd((struct hildev_softc *)sc,
  294                     (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3,
  295                     NULL, NULL);
  296 
  297         sc->sc_ledstate = leds;
  298 }
  299 
  300 int
  301 hilkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
  302 {
  303         struct hilkbd_softc *sc = v;
  304 
  305         switch (cmd) {
  306         case WSKBDIO_GTYPE:
  307                 *(int *)data = WSKBD_TYPE_HIL;
  308                 return 0;
  309         case WSKBDIO_SETLEDS:
  310                 hilkbd_set_leds(v, *(int *)data);
  311                 return 0;
  312         case WSKBDIO_GETLEDS:
  313                 *(int *)data = sc->sc_ledstate;
  314                 return 0;
  315 #ifdef WSDISPLAY_COMPAT_RAWKBD
  316         case WSKBDIO_SETMODE:
  317                 sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
  318                 timeout_del(&sc->sc_rawrepeat_ch);
  319                 return 0;
  320 #endif
  321         case WSKBDIO_COMPLEXBELL:
  322 #define d ((struct wskbd_bell_data *)data)
  323                 hilkbd_bell((struct hil_softc *)sc->hd_parent,
  324                     d->pitch, d->period, d->volume);
  325 #undef d
  326                 return 0;
  327         }
  328 
  329         return -1;
  330 }
  331 
  332 void
  333 hilkbd_cngetc(void *v, u_int *type, int *data)
  334 {
  335         struct hilkbd_softc *sc = v;
  336         u_int8_t c, stat;
  337 
  338         for (;;) {
  339                 while (hil_poll_data((struct hildev_softc *)sc, &stat, &c) != 0)
  340                         ;
  341 
  342                 /*
  343                  * Disregard keyboard data packet header.
  344                  * Note that no key generates it, so we're safe.
  345                  */
  346                 if (c != HIL_KBDBUTTON)
  347                         break;
  348         }
  349 
  350         hilkbd_decode(sc, c, type, data, HIL_KBDBUTTON);
  351 }
  352 
  353 void
  354 hilkbd_cnpollc(void *v, int on)
  355 {
  356         struct hilkbd_softc *sc = v;
  357 
  358         hil_set_poll((struct hil_softc *)sc->hd_parent, on);
  359 }
  360 
  361 void
  362 hilkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
  363 {
  364         struct hilkbd_softc *sc = v;
  365 
  366         hilkbd_bell((struct hil_softc *)sc->hd_parent,
  367             pitch, period, volume);
  368 }
  369 
  370 void
  371 hilkbd_bell(struct hil_softc *sc, u_int pitch, u_int period, u_int volume)
  372 {
  373         u_int8_t buf[2];
  374 
  375         /* XXX there could be at least a pitch -> HIL pitch conversion here */
  376 #define BELLDUR         80      /* tone duration in msec (10-2560) */
  377 #define BELLFREQ        8       /* tone frequency (0-63) */
  378         buf[0] = ar_format(period - 10);
  379         buf[1] = BELLFREQ;
  380         send_hil_cmd(sc, HIL_SETTONE, buf, 2, NULL);
  381 }
  382 
  383 void
  384 hilkbd_callback(struct hildev_softc *dev, u_int buflen, u_int8_t *buf)
  385 {
  386         struct hilkbd_softc *sc = (struct hilkbd_softc *)dev;
  387         u_int type;
  388         int kbdtype, key;
  389         int i, s;
  390 
  391         /*
  392          * Ignore packet if we don't need it
  393          */
  394         if (sc->sc_enabled == 0)
  395                 return;
  396 
  397         if (buflen == 0)
  398                 return;
  399         switch ((kbdtype = *buf & HIL_KBDDATA)) {
  400         case HIL_BUTTONBOX:
  401         case HIL_KBDBUTTON:
  402                 break;
  403         default:
  404                 return;
  405         }
  406 
  407 #ifdef WSDISPLAY_COMPAT_RAWKBD
  408         if (sc->sc_rawkbd) {
  409                 u_char cbuf[HILBUFSIZE * 2];
  410                 int c, j, npress;
  411 
  412                 npress = j = 0;
  413                 for (i = 1, buf++; i < buflen; i++) {
  414                         hilkbd_decode(sc, *buf++, &type, &key, kbdtype);
  415                         c = hilkbd_raw[key];
  416                         if (c == RAWKEY_Null)
  417                                 continue;
  418                         /* fake extended scancode if necessary */
  419                         if (c & 0x80)
  420                                 cbuf[j++] = 0xe0;
  421                         cbuf[j] = c & 0x7f;
  422                         if (type == WSCONS_EVENT_KEY_UP)
  423                                 cbuf[j] |= 0x80;
  424                         else {
  425                                 /* remember pressed keys for autorepeat */
  426                                 if (c & 0x80)
  427                                         sc->sc_rep[npress++] = 0xe0;
  428                                 sc->sc_rep[npress++] = c & 0x7f;
  429                         }
  430                         j++;
  431                 }
  432 
  433                 s = spltty();
  434                 wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
  435                 splx(s);
  436                 timeout_del(&sc->sc_rawrepeat_ch);
  437                 sc->sc_nrep = npress;
  438                 if (npress != 0) {
  439                         timeout_add(&sc->sc_rawrepeat_ch,
  440                             (hz * REP_DELAY1) / 1000);
  441                 }
  442         } else
  443 #endif
  444         {
  445                 s = spltty();
  446                 for (i = 1, buf++; i < buflen; i++) {
  447                         hilkbd_decode(sc, *buf++, &type, &key, kbdtype);
  448                         if (sc->sc_wskbddev != NULL)
  449                                 wskbd_input(sc->sc_wskbddev, type, key);
  450                 }
  451                 splx(s);
  452         }
  453 }
  454 
  455 void
  456 hilkbd_decode(struct hilkbd_softc *sc, u_int8_t data, u_int *type, int *key,
  457     int kbdtype)
  458 {
  459         if (kbdtype == HIL_BUTTONBOX) {
  460                 if (data == 0x02)       /* repeat arrow */
  461                         data = sc->sc_lastarrow;
  462                 else if (data >= 0xf8)
  463                         sc->sc_lastarrow = data;
  464         }
  465 
  466         *type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
  467         *key = data >> 1;
  468 }
  469 
  470 int
  471 hilkbd_is_console(int hil_is_console)
  472 {
  473         /* if not first hil keyboard, then not the console */
  474         if (seen_hilkbd_console)
  475                 return (0);
  476 
  477         /* if PDC console does not match hil bus path, then not the console */
  478         if (hil_is_console == 0)
  479                 return (0);
  480 
  481         seen_hilkbd_console = 1;
  482         return (1);
  483 }
  484 
  485 #ifdef WSDISPLAY_COMPAT_RAWKBD
  486 void
  487 hilkbd_rawrepeat(void *v)
  488 {
  489         struct hilkbd_softc *sc = v;
  490         int s;
  491 
  492         s = spltty();
  493         wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep);
  494         splx(s);
  495         timeout_add(&sc->sc_rawrepeat_ch, (hz * REP_DELAYN) / 1000);
  496 }
  497 #endif

/* [<][>][^][v][top][bottom][index][help] */