Annotation of sys/dev/wscons/wskbd.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: wskbd.c,v 1.56 2007/06/02 07:19:28 tedu Exp $ */
! 2: /* $NetBSD: wskbd.c,v 1.80 2005/05/04 01:52:16 augustss Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
! 6: *
! 7: * Keysym translator:
! 8: * Contributed to The NetBSD Foundation by Juergen Hannken-Illjes.
! 9: *
! 10: * Redistribution and use in source and binary forms, with or without
! 11: * modification, are permitted provided that the following conditions
! 12: * are met:
! 13: * 1. Redistributions of source code must retain the above copyright
! 14: * notice, this list of conditions and the following disclaimer.
! 15: * 2. Redistributions in binary form must reproduce the above copyright
! 16: * notice, this list of conditions and the following disclaimer in the
! 17: * documentation and/or other materials provided with the distribution.
! 18: * 3. All advertising materials mentioning features or use of this software
! 19: * must display the following acknowledgement:
! 20: * This product includes software developed by Christopher G. Demetriou
! 21: * for the NetBSD Project.
! 22: * 4. The name of the author may not be used to endorse or promote products
! 23: * derived from this software without specific prior written permission
! 24: *
! 25: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 26: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 27: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 28: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 29: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 30: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 31: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 32: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 33: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 34: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 35: */
! 36:
! 37: /*
! 38: * Copyright (c) 1992, 1993
! 39: * The Regents of the University of California. All rights reserved.
! 40: *
! 41: * This software was developed by the Computer Systems Engineering group
! 42: * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
! 43: * contributed to Berkeley.
! 44: *
! 45: * All advertising materials mentioning features or use of this software
! 46: * must display the following acknowledgement:
! 47: * This product includes software developed by the University of
! 48: * California, Lawrence Berkeley Laboratory.
! 49: *
! 50: * Redistribution and use in source and binary forms, with or without
! 51: * modification, are permitted provided that the following conditions
! 52: * are met:
! 53: * 1. Redistributions of source code must retain the above copyright
! 54: * notice, this list of conditions and the following disclaimer.
! 55: * 2. Redistributions in binary form must reproduce the above copyright
! 56: * notice, this list of conditions and the following disclaimer in the
! 57: * documentation and/or other materials provided with the distribution.
! 58: * 3. Neither the name of the University nor the names of its contributors
! 59: * may be used to endorse or promote products derived from this software
! 60: * without specific prior written permission.
! 61: *
! 62: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 63: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 64: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 65: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 66: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 67: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 68: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 69: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 70: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 71: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 72: * SUCH DAMAGE.
! 73: *
! 74: * @(#)kbd.c 8.2 (Berkeley) 10/30/93
! 75: */
! 76:
! 77: /*
! 78: * Keyboard driver (/dev/wskbd*). Translates incoming bytes to ASCII or
! 79: * to `wscons_events' and passes them up to the appropriate reader.
! 80: */
! 81:
! 82: #include <sys/param.h>
! 83: #include <sys/conf.h>
! 84: #include <sys/device.h>
! 85: #include <sys/ioctl.h>
! 86: #include <sys/kernel.h>
! 87: #include <sys/proc.h>
! 88: #include <sys/syslog.h>
! 89: #include <sys/systm.h>
! 90: #include <sys/timeout.h>
! 91: #include <sys/malloc.h>
! 92: #include <sys/tty.h>
! 93: #include <sys/signalvar.h>
! 94: #include <sys/errno.h>
! 95: #include <sys/fcntl.h>
! 96: #include <sys/vnode.h>
! 97: #include <sys/poll.h>
! 98: #include <sys/workq.h>
! 99:
! 100: #include <ddb/db_var.h>
! 101:
! 102: #include <dev/wscons/wsconsio.h>
! 103: #include <dev/wscons/wskbdvar.h>
! 104: #include <dev/wscons/wsksymdef.h>
! 105: #include <dev/wscons/wsksymvar.h>
! 106: #include <dev/wscons/wsdisplayvar.h>
! 107: #include <dev/wscons/wseventvar.h>
! 108: #include <dev/wscons/wscons_callbacks.h>
! 109:
! 110: #include "audio.h" /* NAUDIO (mixer tuning) */
! 111: #include "wsdisplay.h"
! 112: #include "wskbd.h"
! 113: #include "wsmux.h"
! 114:
! 115: #ifndef SMALL_KERNEL
! 116: #define BURNER_SUPPORT
! 117: #define SCROLLBACK_SUPPORT
! 118: #endif
! 119:
! 120: #ifdef WSKBD_DEBUG
! 121: #define DPRINTF(x) if (wskbddebug) printf x
! 122: int wskbddebug = 0;
! 123: #else
! 124: #define DPRINTF(x)
! 125: #endif
! 126:
! 127: #include <dev/wscons/wsmuxvar.h>
! 128:
! 129: struct wskbd_internal {
! 130: const struct wskbd_mapdata *t_keymap;
! 131:
! 132: const struct wskbd_consops *t_consops;
! 133: void *t_consaccesscookie;
! 134:
! 135: int t_modifiers;
! 136: int t_composelen; /* remaining entries in t_composebuf */
! 137: keysym_t t_composebuf[2];
! 138:
! 139: int t_flags;
! 140: #define WSKFL_METAESC 1
! 141:
! 142: #define MAXKEYSYMSPERKEY 2 /* ESC <key> at max */
! 143: keysym_t t_symbols[MAXKEYSYMSPERKEY];
! 144:
! 145: struct wskbd_softc *t_sc; /* back pointer */
! 146: };
! 147:
! 148: struct wskbd_softc {
! 149: struct wsevsrc sc_base;
! 150:
! 151: struct wskbd_internal *id;
! 152:
! 153: const struct wskbd_accessops *sc_accessops;
! 154: void *sc_accesscookie;
! 155:
! 156: int sc_ledstate;
! 157:
! 158: int sc_isconsole;
! 159:
! 160: struct wskbd_bell_data sc_bell_data;
! 161: struct wskbd_keyrepeat_data sc_keyrepeat_data;
! 162:
! 163: int sc_repeating; /* we've called timeout() */
! 164: int sc_repkey;
! 165: struct timeout sc_repeat_ch;
! 166: u_int sc_repeat_type;
! 167: int sc_repeat_value;
! 168:
! 169: int sc_translating; /* xlate to chars for emulation */
! 170:
! 171: int sc_maplen; /* number of entries in sc_map */
! 172: struct wscons_keymap *sc_map; /* current translation map */
! 173: kbd_t sc_layout; /* current layout */
! 174:
! 175: int sc_refcnt;
! 176: u_char sc_dying; /* device is being detached */
! 177: };
! 178:
! 179: #define MOD_SHIFT_L (1 << 0)
! 180: #define MOD_SHIFT_R (1 << 1)
! 181: #define MOD_SHIFTLOCK (1 << 2)
! 182: #define MOD_CAPSLOCK (1 << 3)
! 183: #define MOD_CONTROL_L (1 << 4)
! 184: #define MOD_CONTROL_R (1 << 5)
! 185: #define MOD_META_L (1 << 6)
! 186: #define MOD_META_R (1 << 7)
! 187: #define MOD_MODESHIFT (1 << 8)
! 188: #define MOD_NUMLOCK (1 << 9)
! 189: #define MOD_COMPOSE (1 << 10)
! 190: #define MOD_HOLDSCREEN (1 << 11)
! 191: #define MOD_COMMAND (1 << 12)
! 192: #define MOD_COMMAND1 (1 << 13)
! 193: #define MOD_COMMAND2 (1 << 14)
! 194: #define MOD_MODELOCK (1 << 15)
! 195:
! 196: #define MOD_ANYSHIFT (MOD_SHIFT_L | MOD_SHIFT_R | MOD_SHIFTLOCK)
! 197: #define MOD_ANYCONTROL (MOD_CONTROL_L | MOD_CONTROL_R)
! 198: #define MOD_ANYMETA (MOD_META_L | MOD_META_R)
! 199:
! 200: #define MOD_ONESET(id, mask) (((id)->t_modifiers & (mask)) != 0)
! 201: #define MOD_ALLSET(id, mask) (((id)->t_modifiers & (mask)) == (mask))
! 202:
! 203: keysym_t ksym_upcase(keysym_t);
! 204:
! 205: int wskbd_match(struct device *, void *, void *);
! 206: void wskbd_attach(struct device *, struct device *, void *);
! 207: int wskbd_detach(struct device *, int);
! 208: int wskbd_activate(struct device *, enum devact);
! 209:
! 210: int wskbd_displayioctl(struct device *, u_long, caddr_t, int, struct proc *);
! 211:
! 212: void update_leds(struct wskbd_internal *);
! 213: void update_modifier(struct wskbd_internal *, u_int, int, int);
! 214: int internal_command(struct wskbd_softc *, u_int *, keysym_t, keysym_t);
! 215: int wskbd_translate(struct wskbd_internal *, u_int, int);
! 216: int wskbd_enable(struct wskbd_softc *, int);
! 217: #if NWSDISPLAY > 0
! 218: void change_displayparam(struct wskbd_softc *, int, int, int);
! 219: void wskbd_holdscreen(struct wskbd_softc *, int);
! 220: #endif
! 221:
! 222: int wskbd_do_ioctl_sc(struct wskbd_softc *, u_long, caddr_t, int,
! 223: struct proc *);
! 224: void wskbd_deliver_event(struct wskbd_softc *sc, u_int type, int value);
! 225:
! 226: #if NWSMUX > 0
! 227: int wskbd_mux_open(struct wsevsrc *, struct wseventvar *);
! 228: int wskbd_mux_close(struct wsevsrc *);
! 229: #else
! 230: #define wskbd_mux_open NULL
! 231: #define wskbd_mux_close NULL
! 232: #endif
! 233:
! 234: int wskbd_do_open(struct wskbd_softc *, struct wseventvar *);
! 235: int wskbd_do_ioctl(struct device *, u_long, caddr_t, int, struct proc *);
! 236:
! 237: struct cfdriver wskbd_cd = {
! 238: NULL, "wskbd", DV_TTY
! 239: };
! 240:
! 241: struct cfattach wskbd_ca = {
! 242: sizeof (struct wskbd_softc), wskbd_match, wskbd_attach,
! 243: wskbd_detach, wskbd_activate
! 244: };
! 245:
! 246: extern int kbd_reset;
! 247:
! 248: #ifndef WSKBD_DEFAULT_BELL_PITCH
! 249: #define WSKBD_DEFAULT_BELL_PITCH 400 /* 400Hz */
! 250: #endif
! 251: #ifndef WSKBD_DEFAULT_BELL_PERIOD
! 252: #define WSKBD_DEFAULT_BELL_PERIOD 100 /* 100ms */
! 253: #endif
! 254: #ifndef WSKBD_DEFAULT_BELL_VOLUME
! 255: #define WSKBD_DEFAULT_BELL_VOLUME 50 /* 50% volume */
! 256: #endif
! 257:
! 258: struct wskbd_bell_data wskbd_default_bell_data = {
! 259: WSKBD_BELL_DOALL,
! 260: WSKBD_DEFAULT_BELL_PITCH,
! 261: WSKBD_DEFAULT_BELL_PERIOD,
! 262: WSKBD_DEFAULT_BELL_VOLUME,
! 263: };
! 264:
! 265: #ifndef WSKBD_DEFAULT_KEYREPEAT_DEL1
! 266: #define WSKBD_DEFAULT_KEYREPEAT_DEL1 400 /* 400ms to start repeating */
! 267: #endif
! 268: #ifndef WSKBD_DEFAULT_KEYREPEAT_DELN
! 269: #define WSKBD_DEFAULT_KEYREPEAT_DELN 100 /* 100ms to between repeats */
! 270: #endif
! 271:
! 272: struct wskbd_keyrepeat_data wskbd_default_keyrepeat_data = {
! 273: WSKBD_KEYREPEAT_DOALL,
! 274: WSKBD_DEFAULT_KEYREPEAT_DEL1,
! 275: WSKBD_DEFAULT_KEYREPEAT_DELN,
! 276: };
! 277:
! 278: #if NWSMUX > 0 || NWSDISPLAY > 0
! 279: struct wssrcops wskbd_srcops = {
! 280: WSMUX_KBD,
! 281: wskbd_mux_open, wskbd_mux_close, wskbd_do_ioctl,
! 282: wskbd_displayioctl,
! 283: #if NWSDISPLAY > 0
! 284: wskbd_set_display
! 285: #else
! 286: NULL
! 287: #endif
! 288: };
! 289: #endif
! 290:
! 291: #if NWSDISPLAY > 0
! 292: void wskbd_repeat(void *v);
! 293: #endif
! 294:
! 295: static int wskbd_console_initted;
! 296: static struct wskbd_softc *wskbd_console_device;
! 297: static struct wskbd_internal wskbd_console_data;
! 298:
! 299: void wskbd_update_layout(struct wskbd_internal *, kbd_t);
! 300:
! 301: #if NAUDIO > 0
! 302: extern int wskbd_set_mixervolume(long dir);
! 303: #endif
! 304:
! 305: void
! 306: wskbd_update_layout(struct wskbd_internal *id, kbd_t enc)
! 307: {
! 308: if (enc & KB_METAESC)
! 309: id->t_flags |= WSKFL_METAESC;
! 310: else
! 311: id->t_flags &= ~WSKFL_METAESC;
! 312: }
! 313:
! 314: /*
! 315: * Print function (for parent devices).
! 316: */
! 317: int
! 318: wskbddevprint(void *aux, const char *pnp)
! 319: {
! 320: #if 0
! 321: struct wskbddev_attach_args *ap = aux;
! 322: #endif
! 323:
! 324: if (pnp)
! 325: printf("wskbd at %s", pnp);
! 326: #if 0
! 327: printf(" console %d", ap->console);
! 328: #endif
! 329:
! 330: return (UNCONF);
! 331: }
! 332:
! 333: int
! 334: wskbd_match(struct device *parent, void *match, void *aux)
! 335: {
! 336: struct cfdata *cf = match;
! 337: struct wskbddev_attach_args *ap = aux;
! 338:
! 339: if (cf->wskbddevcf_console != WSKBDDEVCF_CONSOLE_UNK) {
! 340: /*
! 341: * If console-ness of device specified, either match
! 342: * exactly (at high priority), or fail.
! 343: */
! 344: if (cf->wskbddevcf_console != 0 && ap->console != 0)
! 345: return (10);
! 346: else
! 347: return (0);
! 348: }
! 349:
! 350: /* If console-ness unspecified, it wins. */
! 351: return (1);
! 352: }
! 353:
! 354: void
! 355: wskbd_attach(struct device *parent, struct device *self, void *aux)
! 356: {
! 357: struct wskbd_softc *sc = (struct wskbd_softc *)self;
! 358: struct wskbddev_attach_args *ap = aux;
! 359: #if NWSMUX > 0
! 360: int mux, error;
! 361: #endif
! 362:
! 363: sc->sc_isconsole = ap->console;
! 364:
! 365: #if NWSMUX > 0 || NWSDISPLAY > 0
! 366: sc->sc_base.me_ops = &wskbd_srcops;
! 367: #endif
! 368: #if NWSMUX > 0
! 369: mux = sc->sc_base.me_dv.dv_cfdata->wskbddevcf_mux;
! 370: if (ap->console) {
! 371: /* Ignore mux for console; it always goes to the console mux. */
! 372: /* printf(" (mux %d ignored for console)", mux); */
! 373: mux = -1;
! 374: }
! 375: if (mux >= 0)
! 376: printf(" mux %d", mux);
! 377: #else
! 378: #if 0 /* not worth keeping, especially since the default value is not -1... */
! 379: if (sc->sc_base.me_dv.dv_cfdata->wskbddevcf_mux >= 0)
! 380: printf(" (mux ignored)");
! 381: #endif
! 382: #endif /* NWSMUX > 0 */
! 383:
! 384: if (ap->console) {
! 385: sc->id = &wskbd_console_data;
! 386: } else {
! 387: sc->id = malloc(sizeof(struct wskbd_internal),
! 388: M_DEVBUF, M_WAITOK);
! 389: bzero(sc->id, sizeof(struct wskbd_internal));
! 390: sc->id->t_keymap = ap->keymap;
! 391: wskbd_update_layout(sc->id, ap->keymap->layout);
! 392: }
! 393:
! 394: #if NWSDISPLAY > 0
! 395: timeout_set(&sc->sc_repeat_ch, wskbd_repeat, sc);
! 396: #endif
! 397:
! 398: sc->id->t_sc = sc;
! 399:
! 400: sc->sc_accessops = ap->accessops;
! 401: sc->sc_accesscookie = ap->accesscookie;
! 402: sc->sc_repeating = 0;
! 403: sc->sc_translating = 1;
! 404: sc->sc_ledstate = -1; /* force update */
! 405:
! 406: if (wskbd_load_keymap(sc->id->t_keymap,
! 407: &sc->sc_map, &sc->sc_maplen) != 0)
! 408: panic("cannot load keymap");
! 409:
! 410: sc->sc_layout = sc->id->t_keymap->layout;
! 411:
! 412: /* set default bell and key repeat data */
! 413: sc->sc_bell_data = wskbd_default_bell_data;
! 414: sc->sc_keyrepeat_data = wskbd_default_keyrepeat_data;
! 415:
! 416: if (ap->console) {
! 417: KASSERT(wskbd_console_initted);
! 418: KASSERT(wskbd_console_device == NULL);
! 419:
! 420: wskbd_console_device = sc;
! 421:
! 422: printf(": console keyboard");
! 423:
! 424: #if NWSDISPLAY > 0
! 425: wsdisplay_set_console_kbd(&sc->sc_base); /* sets me_dispdv */
! 426: if (sc->sc_displaydv != NULL)
! 427: printf(", using %s", sc->sc_displaydv->dv_xname);
! 428: #endif
! 429: }
! 430: printf("\n");
! 431:
! 432: #if NWSMUX > 0
! 433: if (mux >= 0) {
! 434: error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
! 435: if (error)
! 436: printf("%s: attach error=%d\n",
! 437: sc->sc_base.me_dv.dv_xname, error);
! 438: }
! 439: #endif
! 440:
! 441: #if WSDISPLAY > 0 && NWSMUX == 0
! 442: if (ap->console == 0) {
! 443: /*
! 444: * In the non-wsmux world, always connect wskbd0 and wsdisplay0
! 445: * together.
! 446: */
! 447: extern struct cfdriver wsdisplay_cd;
! 448:
! 449: if (wsdisplay_cd.cd_ndevs != 0 && self->dv_unit == 0) {
! 450: if (wskbd_set_display(self,
! 451: wsdisplay_cd.cd_devs[0]) == 0)
! 452: wsdisplay_set_kbd(wsdisplay_cd.cd_devs[0],
! 453: (struct wsevsrc *)sc);
! 454: }
! 455: }
! 456: #endif
! 457:
! 458: }
! 459:
! 460: void
! 461: wskbd_cnattach(const struct wskbd_consops *consops, void *conscookie,
! 462: const struct wskbd_mapdata *mapdata)
! 463: {
! 464:
! 465: KASSERT(!wskbd_console_initted);
! 466:
! 467: wskbd_console_data.t_keymap = mapdata;
! 468: wskbd_update_layout(&wskbd_console_data, mapdata->layout);
! 469:
! 470: wskbd_console_data.t_consops = consops;
! 471: wskbd_console_data.t_consaccesscookie = conscookie;
! 472:
! 473: #if NWSDISPLAY > 0
! 474: wsdisplay_set_cons_kbd(wskbd_cngetc, wskbd_cnpollc, wskbd_cnbell);
! 475: #endif
! 476:
! 477: wskbd_console_initted = 1;
! 478: }
! 479:
! 480: void
! 481: wskbd_cndetach()
! 482: {
! 483: KASSERT(wskbd_console_initted);
! 484:
! 485: wskbd_console_data.t_keymap = 0;
! 486:
! 487: wskbd_console_data.t_consops = 0;
! 488: wskbd_console_data.t_consaccesscookie = 0;
! 489:
! 490: #if NWSDISPLAY > 0
! 491: wsdisplay_unset_cons_kbd();
! 492: #endif
! 493:
! 494: wskbd_console_initted = 0;
! 495: }
! 496:
! 497: #if NWSDISPLAY > 0
! 498: void
! 499: wskbd_repeat(void *v)
! 500: {
! 501: struct wskbd_softc *sc = (struct wskbd_softc *)v;
! 502: int s = spltty();
! 503:
! 504: if (!sc->sc_repeating) {
! 505: /*
! 506: * race condition: a "key up" event came in when wskbd_repeat()
! 507: * was already called but not yet spltty()'d
! 508: */
! 509: splx(s);
! 510: return;
! 511: }
! 512: if (sc->sc_translating) {
! 513: /* deliver keys */
! 514: if (sc->sc_base.me_dispdv != NULL) {
! 515: int i;
! 516: for (i = 0; i < sc->sc_repeating; i++)
! 517: wsdisplay_kbdinput(sc->sc_base.me_dispdv,
! 518: sc->id->t_symbols[i]);
! 519: }
! 520: } else {
! 521: /* queue event */
! 522: wskbd_deliver_event(sc, sc->sc_repeat_type,
! 523: sc->sc_repeat_value);
! 524: }
! 525: if (sc->sc_keyrepeat_data.delN != 0)
! 526: timeout_add(&sc->sc_repeat_ch,
! 527: (hz * sc->sc_keyrepeat_data.delN) / 1000);
! 528: splx(s);
! 529: }
! 530: #endif
! 531:
! 532: int
! 533: wskbd_activate(struct device *self, enum devact act)
! 534: {
! 535: struct wskbd_softc *sc = (struct wskbd_softc *)self;
! 536:
! 537: if (act == DVACT_DEACTIVATE)
! 538: sc->sc_dying = 1;
! 539: return (0);
! 540: }
! 541:
! 542: /*
! 543: * Detach a keyboard. To keep track of users of the softc we keep
! 544: * a reference count that's incremented while inside, e.g., read.
! 545: * If the keyboard is active and the reference count is > 0 (0 is the
! 546: * normal state) we post an event and then wait for the process
! 547: * that had the reference to wake us up again. Then we blow away the
! 548: * vnode and return (which will deallocate the softc).
! 549: */
! 550: int
! 551: wskbd_detach(struct device *self, int flags)
! 552: {
! 553: struct wskbd_softc *sc = (struct wskbd_softc *)self;
! 554: struct wseventvar *evar;
! 555: int maj, mn;
! 556: int s;
! 557:
! 558: #if NWSMUX > 0
! 559: /* Tell parent mux we're leaving. */
! 560: if (sc->sc_base.me_parent != NULL)
! 561: wsmux_detach_sc(&sc->sc_base);
! 562: #endif
! 563:
! 564: #if NWSDISPLAY > 0
! 565: if (sc->sc_repeating) {
! 566: sc->sc_repeating = 0;
! 567: timeout_del(&sc->sc_repeat_ch);
! 568: }
! 569: #endif
! 570:
! 571: if (sc->sc_isconsole) {
! 572: KASSERT(wskbd_console_device == sc);
! 573: wskbd_console_device = NULL;
! 574: }
! 575:
! 576: evar = sc->sc_base.me_evp;
! 577: if (evar != NULL && evar->io != NULL) {
! 578: s = spltty();
! 579: if (--sc->sc_refcnt >= 0) {
! 580: /* Wake everyone by generating a dummy event. */
! 581: if (++evar->put >= WSEVENT_QSIZE)
! 582: evar->put = 0;
! 583: WSEVENT_WAKEUP(evar);
! 584: /* Wait for processes to go away. */
! 585: if (tsleep(sc, PZERO, "wskdet", hz * 60))
! 586: printf("wskbd_detach: %s didn't detach\n",
! 587: sc->sc_base.me_dv.dv_xname);
! 588: }
! 589: splx(s);
! 590: }
! 591:
! 592: /* locate the major number */
! 593: for (maj = 0; maj < nchrdev; maj++)
! 594: if (cdevsw[maj].d_open == wskbdopen)
! 595: break;
! 596:
! 597: /* Nuke the vnodes for any open instances. */
! 598: mn = self->dv_unit;
! 599: vdevgone(maj, mn, mn, VCHR);
! 600:
! 601: return (0);
! 602: }
! 603:
! 604: void
! 605: wskbd_input(struct device *dev, u_int type, int value)
! 606: {
! 607: struct wskbd_softc *sc = (struct wskbd_softc *)dev;
! 608: #if NWSDISPLAY > 0
! 609: int num, i;
! 610: #endif
! 611:
! 612: #if NWSDISPLAY > 0
! 613: if (sc->sc_repeating) {
! 614: sc->sc_repeating = 0;
! 615: timeout_del(&sc->sc_repeat_ch);
! 616: }
! 617:
! 618: /*
! 619: * If /dev/wskbdN is not connected in event mode translate and
! 620: * send upstream.
! 621: */
! 622: if (sc->sc_translating) {
! 623: #ifdef BURNER_SUPPORT
! 624: if (type == WSCONS_EVENT_KEY_DOWN && sc->sc_displaydv != NULL)
! 625: wsdisplay_burn(sc->sc_displaydv, WSDISPLAY_BURN_KBD);
! 626: #endif
! 627: num = wskbd_translate(sc->id, type, value);
! 628: if (num > 0) {
! 629: if (sc->sc_base.me_dispdv != NULL) {
! 630: #ifdef SCROLLBACK_SUPPORT
! 631: /* XXX - Shift_R+PGUP(release) emits PrtSc */
! 632: if (sc->id->t_symbols[0] != KS_Print_Screen) {
! 633: wsscrollback(sc->sc_base.me_dispdv,
! 634: WSDISPLAY_SCROLL_RESET);
! 635: }
! 636: #endif
! 637: for (i = 0; i < num; i++) {
! 638: wsdisplay_kbdinput(sc->sc_base.me_dispdv,
! 639: sc->id->t_symbols[i]);
! 640: }
! 641: }
! 642:
! 643: if (sc->sc_keyrepeat_data.del1 != 0) {
! 644: sc->sc_repeating = num;
! 645: timeout_add(&sc->sc_repeat_ch,
! 646: (hz * sc->sc_keyrepeat_data.del1) / 1000);
! 647: }
! 648: }
! 649: return;
! 650: }
! 651: #endif
! 652:
! 653: wskbd_deliver_event(sc, type, value);
! 654:
! 655: #if NWSDISPLAY > 0
! 656: /* Repeat key presses if enabled. */
! 657: if (type == WSCONS_EVENT_KEY_DOWN && sc->sc_keyrepeat_data.del1 != 0) {
! 658: sc->sc_repeat_type = type;
! 659: sc->sc_repeat_value = value;
! 660: sc->sc_repeating = 1;
! 661: timeout_add(&sc->sc_repeat_ch,
! 662: (hz * sc->sc_keyrepeat_data.del1) / 1000);
! 663: }
! 664: #endif
! 665: }
! 666:
! 667: /*
! 668: * Keyboard is generating events. Turn this keystroke into an
! 669: * event and put it in the queue. If the queue is full, the
! 670: * keystroke is lost (sorry!).
! 671: */
! 672: void
! 673: wskbd_deliver_event(struct wskbd_softc *sc, u_int type, int value)
! 674: {
! 675: struct wseventvar *evar;
! 676: struct wscons_event *ev;
! 677: int put;
! 678:
! 679: evar = sc->sc_base.me_evp;
! 680:
! 681: if (evar == NULL) {
! 682: DPRINTF(("wskbd_input: not open\n"));
! 683: return;
! 684: }
! 685:
! 686: #ifdef DIAGNOSTIC
! 687: if (evar->q == NULL) {
! 688: printf("wskbd_input: evar->q=NULL\n");
! 689: return;
! 690: }
! 691: #endif
! 692:
! 693: put = evar->put;
! 694: ev = &evar->q[put];
! 695: put = (put + 1) % WSEVENT_QSIZE;
! 696: if (put == evar->get) {
! 697: log(LOG_WARNING, "%s: event queue overflow\n",
! 698: sc->sc_base.me_dv.dv_xname);
! 699: return;
! 700: }
! 701: ev->type = type;
! 702: ev->value = value;
! 703: nanotime(&ev->time);
! 704: evar->put = put;
! 705: WSEVENT_WAKEUP(evar);
! 706: }
! 707:
! 708: #ifdef WSDISPLAY_COMPAT_RAWKBD
! 709: void
! 710: wskbd_rawinput(struct device *dev, u_char *buf, int len)
! 711: {
! 712: #if NWSDISPLAY > 0
! 713: struct wskbd_softc *sc = (struct wskbd_softc *)dev;
! 714: int i;
! 715:
! 716: if (sc->sc_base.me_dispdv != NULL)
! 717: for (i = 0; i < len; i++)
! 718: wsdisplay_kbdinput(sc->sc_base.me_dispdv, buf[i]);
! 719: /* this is KS_GROUP_Ascii */
! 720: #endif
! 721: }
! 722: #endif /* WSDISPLAY_COMPAT_RAWKBD */
! 723:
! 724: #if NWSDISPLAY > 0
! 725: void
! 726: wskbd_holdscreen(struct wskbd_softc *sc, int hold)
! 727: {
! 728: int new_state;
! 729:
! 730: if (sc->sc_base.me_dispdv != NULL) {
! 731: wsdisplay_kbdholdscreen(sc->sc_base.me_dispdv, hold);
! 732: new_state = sc->sc_ledstate;
! 733: if (hold)
! 734: new_state |= WSKBD_LED_SCROLL;
! 735: else
! 736: new_state &= ~WSKBD_LED_SCROLL;
! 737: if (new_state != sc->sc_ledstate) {
! 738: (*sc->sc_accessops->set_leds)(sc->sc_accesscookie,
! 739: new_state);
! 740: sc->sc_ledstate = new_state;
! 741: }
! 742: }
! 743: }
! 744: #endif
! 745:
! 746: int
! 747: wskbd_enable(struct wskbd_softc *sc, int on)
! 748: {
! 749: int error;
! 750:
! 751: #if NWSDISPLAY > 0
! 752: if (sc->sc_base.me_dispdv != NULL)
! 753: return (0);
! 754:
! 755: /* Always cancel auto repeat when fiddling with the kbd. */
! 756: if (sc->sc_repeating) {
! 757: sc->sc_repeating = 0;
! 758: timeout_del(&sc->sc_repeat_ch);
! 759: }
! 760: #endif
! 761:
! 762: error = (*sc->sc_accessops->enable)(sc->sc_accesscookie, on);
! 763: DPRINTF(("wskbd_enable: sc=%p on=%d res=%d\n", sc, on, error));
! 764: return (error);
! 765: }
! 766:
! 767: #if NWSMUX > 0
! 768: int
! 769: wskbd_mux_open(struct wsevsrc *me, struct wseventvar *evp)
! 770: {
! 771: struct wskbd_softc *sc = (struct wskbd_softc *)me;
! 772:
! 773: if (sc->sc_dying)
! 774: return (EIO);
! 775:
! 776: if (sc->sc_base.me_evp != NULL)
! 777: return (EBUSY);
! 778:
! 779: return (wskbd_do_open(sc, evp));
! 780: }
! 781: #endif
! 782:
! 783: int
! 784: wskbdopen(dev_t dev, int flags, int mode, struct proc *p)
! 785: {
! 786: struct wskbd_softc *sc;
! 787: struct wseventvar *evar;
! 788: int unit, error;
! 789:
! 790: unit = minor(dev);
! 791: if (unit >= wskbd_cd.cd_ndevs || /* make sure it was attached */
! 792: (sc = wskbd_cd.cd_devs[unit]) == NULL)
! 793: return (ENXIO);
! 794:
! 795: #if NWSMUX > 0
! 796: DPRINTF(("wskbdopen: %s mux=%p p=%p\n", sc->sc_base.me_dv.dv_xname,
! 797: sc->sc_base.me_parent, p));
! 798: #endif
! 799:
! 800: if (sc->sc_dying)
! 801: return (EIO);
! 802:
! 803: if ((flags & (FREAD | FWRITE)) == FWRITE) {
! 804: /* Not opening for read, only ioctl is available. */
! 805: return (0);
! 806: }
! 807:
! 808: #if NWSMUX > 0
! 809: if (sc->sc_base.me_parent != NULL) {
! 810: /* Grab the keyboard out of the greedy hands of the mux. */
! 811: DPRINTF(("wskbdopen: detach\n"));
! 812: wsmux_detach_sc(&sc->sc_base);
! 813: }
! 814: #endif
! 815:
! 816: if (sc->sc_base.me_evp != NULL)
! 817: return (EBUSY);
! 818:
! 819: evar = &sc->sc_base.me_evar;
! 820: wsevent_init(evar);
! 821: evar->io = p;
! 822:
! 823: error = wskbd_do_open(sc, evar);
! 824: if (error) {
! 825: DPRINTF(("wskbdopen: %s open failed\n",
! 826: sc->sc_base.me_dv.dv_xname));
! 827: sc->sc_base.me_evp = NULL;
! 828: wsevent_fini(evar);
! 829: }
! 830: return (error);
! 831: }
! 832:
! 833: int
! 834: wskbd_do_open(struct wskbd_softc *sc, struct wseventvar *evp)
! 835: {
! 836: sc->sc_base.me_evp = evp;
! 837: sc->sc_translating = 0;
! 838:
! 839: return (wskbd_enable(sc, 1));
! 840: }
! 841:
! 842: int
! 843: wskbdclose(dev_t dev, int flags, int mode, struct proc *p)
! 844: {
! 845: struct wskbd_softc *sc =
! 846: (struct wskbd_softc *)wskbd_cd.cd_devs[minor(dev)];
! 847: struct wseventvar *evar = sc->sc_base.me_evp;
! 848:
! 849: if (evar == NULL)
! 850: /* not open for read */
! 851: return (0);
! 852:
! 853: sc->sc_base.me_evp = NULL;
! 854: sc->sc_translating = 1;
! 855: (void)wskbd_enable(sc, 0);
! 856: wsevent_fini(evar);
! 857:
! 858: return (0);
! 859: }
! 860:
! 861: #if NWSMUX > 0
! 862: int
! 863: wskbd_mux_close(struct wsevsrc *me)
! 864: {
! 865: struct wskbd_softc *sc = (struct wskbd_softc *)me;
! 866:
! 867: sc->sc_base.me_evp = NULL;
! 868: sc->sc_translating = 1;
! 869: (void)wskbd_enable(sc, 0);
! 870:
! 871: return (0);
! 872: }
! 873: #endif
! 874:
! 875: int
! 876: wskbdread(dev_t dev, struct uio *uio, int flags)
! 877: {
! 878: struct wskbd_softc *sc = wskbd_cd.cd_devs[minor(dev)];
! 879: int error;
! 880:
! 881: if (sc->sc_dying)
! 882: return (EIO);
! 883:
! 884: #ifdef DIAGNOSTIC
! 885: if (sc->sc_base.me_evp == NULL) {
! 886: printf("wskbdread: evp == NULL\n");
! 887: return (EINVAL);
! 888: }
! 889: #endif
! 890:
! 891: sc->sc_refcnt++;
! 892: error = wsevent_read(&sc->sc_base.me_evar, uio, flags);
! 893: if (--sc->sc_refcnt < 0) {
! 894: wakeup(sc);
! 895: error = EIO;
! 896: }
! 897: return (error);
! 898: }
! 899:
! 900: int
! 901: wskbdioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
! 902: {
! 903: return (wskbd_do_ioctl(wskbd_cd.cd_devs[minor(dev)], cmd, data, flag,p));
! 904: }
! 905:
! 906: /* A wrapper around the ioctl() workhorse to make reference counting easy. */
! 907: int
! 908: wskbd_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
! 909: struct proc *p)
! 910: {
! 911: struct wskbd_softc *sc = (struct wskbd_softc *)dv;
! 912: int error;
! 913:
! 914: sc->sc_refcnt++;
! 915: error = wskbd_do_ioctl_sc(sc, cmd, data, flag, p);
! 916: if (--sc->sc_refcnt < 0)
! 917: wakeup(sc);
! 918: return (error);
! 919: }
! 920:
! 921: int
! 922: wskbd_do_ioctl_sc(struct wskbd_softc *sc, u_long cmd, caddr_t data, int flag,
! 923: struct proc *p)
! 924: {
! 925: int error;
! 926:
! 927: /*
! 928: * Try the generic ioctls that the wskbd interface supports.
! 929: */
! 930: switch (cmd) {
! 931: case FIONBIO: /* we will remove this someday (soon???) */
! 932: return (0);
! 933:
! 934: case FIOASYNC:
! 935: if (sc->sc_base.me_evp == NULL)
! 936: return (EINVAL);
! 937: sc->sc_base.me_evp->async = *(int *)data != 0;
! 938: return (0);
! 939:
! 940: case FIOSETOWN:
! 941: if (sc->sc_base.me_evp == NULL)
! 942: return (EINVAL);
! 943: if (-*(int *)data != sc->sc_base.me_evp->io->p_pgid &&
! 944: *(int *)data != sc->sc_base.me_evp->io->p_pid)
! 945: return (EPERM);
! 946: return (0);
! 947:
! 948: case TIOCSPGRP:
! 949: if (sc->sc_base.me_evp == NULL)
! 950: return (EINVAL);
! 951: if (*(int *)data != sc->sc_base.me_evp->io->p_pgid)
! 952: return (EPERM);
! 953: return (0);
! 954: }
! 955:
! 956: /*
! 957: * Try the keyboard driver for WSKBDIO ioctls. It returns -1
! 958: * if it didn't recognize the request.
! 959: */
! 960: error = wskbd_displayioctl(&sc->sc_base.me_dv, cmd, data, flag, p);
! 961: return (error != -1 ? error : ENOTTY);
! 962: }
! 963:
! 964: /*
! 965: * WSKBDIO ioctls, handled in both emulation mode and in ``raw'' mode.
! 966: * Some of these have no real effect in raw mode, however.
! 967: */
! 968: int
! 969: wskbd_displayioctl(struct device *dev, u_long cmd, caddr_t data, int flag,
! 970: struct proc *p)
! 971: {
! 972: struct wskbd_softc *sc = (struct wskbd_softc *)dev;
! 973: struct wskbd_bell_data *ubdp, *kbdp;
! 974: struct wskbd_keyrepeat_data *ukdp, *kkdp;
! 975: struct wskbd_map_data *umdp;
! 976: struct wskbd_mapdata md;
! 977: kbd_t enc;
! 978: void *buf;
! 979: int len, error;
! 980:
! 981: switch (cmd) {
! 982: case WSKBDIO_BELL:
! 983: case WSKBDIO_COMPLEXBELL:
! 984: case WSKBDIO_SETBELL:
! 985: case WSKBDIO_SETKEYREPEAT:
! 986: case WSKBDIO_SETDEFAULTKEYREPEAT:
! 987: case WSKBDIO_SETMAP:
! 988: case WSKBDIO_SETENCODING:
! 989: if ((flag & FWRITE) == 0)
! 990: return (EACCES);
! 991: }
! 992:
! 993: switch (cmd) {
! 994: #define SETBELL(dstp, srcp, dfltp) \
! 995: do { \
! 996: (dstp)->pitch = ((srcp)->which & WSKBD_BELL_DOPITCH) ? \
! 997: (srcp)->pitch : (dfltp)->pitch; \
! 998: (dstp)->period = ((srcp)->which & WSKBD_BELL_DOPERIOD) ? \
! 999: (srcp)->period : (dfltp)->period; \
! 1000: (dstp)->volume = ((srcp)->which & WSKBD_BELL_DOVOLUME) ? \
! 1001: (srcp)->volume : (dfltp)->volume; \
! 1002: (dstp)->which = WSKBD_BELL_DOALL; \
! 1003: } while (0)
! 1004:
! 1005: case WSKBDIO_BELL:
! 1006: return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
! 1007: WSKBDIO_COMPLEXBELL, (caddr_t)&sc->sc_bell_data, flag, p));
! 1008:
! 1009: case WSKBDIO_COMPLEXBELL:
! 1010: ubdp = (struct wskbd_bell_data *)data;
! 1011: SETBELL(ubdp, ubdp, &sc->sc_bell_data);
! 1012: return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
! 1013: WSKBDIO_COMPLEXBELL, (caddr_t)ubdp, flag, p));
! 1014:
! 1015: case WSKBDIO_SETBELL:
! 1016: kbdp = &sc->sc_bell_data;
! 1017: setbell:
! 1018: ubdp = (struct wskbd_bell_data *)data;
! 1019: SETBELL(kbdp, ubdp, kbdp);
! 1020: return (0);
! 1021:
! 1022: case WSKBDIO_GETBELL:
! 1023: kbdp = &sc->sc_bell_data;
! 1024: getbell:
! 1025: ubdp = (struct wskbd_bell_data *)data;
! 1026: SETBELL(ubdp, kbdp, kbdp);
! 1027: return (0);
! 1028:
! 1029: case WSKBDIO_SETDEFAULTBELL:
! 1030: if ((error = suser(p, 0)) != 0)
! 1031: return (error);
! 1032: kbdp = &wskbd_default_bell_data;
! 1033: goto setbell;
! 1034:
! 1035:
! 1036: case WSKBDIO_GETDEFAULTBELL:
! 1037: kbdp = &wskbd_default_bell_data;
! 1038: goto getbell;
! 1039:
! 1040: #undef SETBELL
! 1041:
! 1042: #define SETKEYREPEAT(dstp, srcp, dfltp) \
! 1043: do { \
! 1044: (dstp)->del1 = ((srcp)->which & WSKBD_KEYREPEAT_DODEL1) ? \
! 1045: (srcp)->del1 : (dfltp)->del1; \
! 1046: (dstp)->delN = ((srcp)->which & WSKBD_KEYREPEAT_DODELN) ? \
! 1047: (srcp)->delN : (dfltp)->delN; \
! 1048: (dstp)->which = WSKBD_KEYREPEAT_DOALL; \
! 1049: } while (0)
! 1050:
! 1051: case WSKBDIO_SETKEYREPEAT:
! 1052: kkdp = &sc->sc_keyrepeat_data;
! 1053: setkeyrepeat:
! 1054: ukdp = (struct wskbd_keyrepeat_data *)data;
! 1055: SETKEYREPEAT(kkdp, ukdp, kkdp);
! 1056: return (0);
! 1057:
! 1058: case WSKBDIO_GETKEYREPEAT:
! 1059: kkdp = &sc->sc_keyrepeat_data;
! 1060: getkeyrepeat:
! 1061: ukdp = (struct wskbd_keyrepeat_data *)data;
! 1062: SETKEYREPEAT(ukdp, kkdp, kkdp);
! 1063: return (0);
! 1064:
! 1065: case WSKBDIO_SETDEFAULTKEYREPEAT:
! 1066: if ((error = suser(p, 0)) != 0)
! 1067: return (error);
! 1068: kkdp = &wskbd_default_keyrepeat_data;
! 1069: goto setkeyrepeat;
! 1070:
! 1071:
! 1072: case WSKBDIO_GETDEFAULTKEYREPEAT:
! 1073: kkdp = &wskbd_default_keyrepeat_data;
! 1074: goto getkeyrepeat;
! 1075:
! 1076: #undef SETKEYREPEAT
! 1077:
! 1078: case WSKBDIO_SETMAP:
! 1079: umdp = (struct wskbd_map_data *)data;
! 1080: if (umdp->maplen > WSKBDIO_MAXMAPLEN)
! 1081: return (EINVAL);
! 1082:
! 1083: len = umdp->maplen * sizeof(struct wscons_keymap);
! 1084: buf = malloc(len, M_TEMP, M_WAITOK);
! 1085: error = copyin(umdp->map, buf, len);
! 1086: if (error == 0) {
! 1087: wskbd_init_keymap(umdp->maplen,
! 1088: &sc->sc_map, &sc->sc_maplen);
! 1089: memcpy(sc->sc_map, buf, len);
! 1090: /* drop the variant bits handled by the map */
! 1091: sc->sc_layout = KB_USER |
! 1092: (KB_VARIANT(sc->sc_layout) & KB_HANDLEDBYWSKBD);
! 1093: wskbd_update_layout(sc->id, sc->sc_layout);
! 1094: }
! 1095: free(buf, M_TEMP);
! 1096: return(error);
! 1097:
! 1098: case WSKBDIO_GETMAP:
! 1099: umdp = (struct wskbd_map_data *)data;
! 1100: if (umdp->maplen > sc->sc_maplen)
! 1101: umdp->maplen = sc->sc_maplen;
! 1102: error = copyout(sc->sc_map, umdp->map,
! 1103: umdp->maplen*sizeof(struct wscons_keymap));
! 1104: return(error);
! 1105:
! 1106: case WSKBDIO_GETENCODING:
! 1107: *((kbd_t *) data) = sc->sc_layout;
! 1108: return(0);
! 1109:
! 1110: case WSKBDIO_SETENCODING:
! 1111: enc = *((kbd_t *)data);
! 1112: if (KB_ENCODING(enc) == KB_USER) {
! 1113: /* user map must already be loaded */
! 1114: if (KB_ENCODING(sc->sc_layout) != KB_USER)
! 1115: return (EINVAL);
! 1116: /* map variants make no sense */
! 1117: if (KB_VARIANT(enc) & ~KB_HANDLEDBYWSKBD)
! 1118: return (EINVAL);
! 1119: } else {
! 1120: md = *(sc->id->t_keymap); /* structure assignment */
! 1121: md.layout = enc;
! 1122: error = wskbd_load_keymap(&md, &sc->sc_map,
! 1123: &sc->sc_maplen);
! 1124: if (error)
! 1125: return(error);
! 1126: }
! 1127: sc->sc_layout = enc;
! 1128: wskbd_update_layout(sc->id, enc);
! 1129: return (0);
! 1130: }
! 1131:
! 1132: /*
! 1133: * Try the keyboard driver for WSKBDIO ioctls. It returns -1
! 1134: * if it didn't recognize the request, and in turn we return
! 1135: * -1 if we didn't recognize the request.
! 1136: */
! 1137: /* printf("kbdaccess\n"); */
! 1138: error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data,
! 1139: flag, p);
! 1140: #ifdef WSDISPLAY_COMPAT_RAWKBD
! 1141: if (!error && cmd == WSKBDIO_SETMODE && *(int *)data == WSKBD_RAW) {
! 1142: int s = spltty();
! 1143: sc->id->t_modifiers &= ~(MOD_SHIFT_L | MOD_SHIFT_R
! 1144: | MOD_CONTROL_L | MOD_CONTROL_R
! 1145: | MOD_META_L | MOD_META_R
! 1146: | MOD_COMMAND
! 1147: | MOD_COMMAND1 | MOD_COMMAND2);
! 1148: #if NWSDISPLAY > 0
! 1149: if (sc->sc_repeating) {
! 1150: sc->sc_repeating = 0;
! 1151: timeout_del(&sc->sc_repeat_ch);
! 1152: }
! 1153: #endif
! 1154: splx(s);
! 1155: }
! 1156: #endif
! 1157: return (error);
! 1158: }
! 1159:
! 1160: int
! 1161: wskbdpoll(dev_t dev, int events, struct proc *p)
! 1162: {
! 1163: struct wskbd_softc *sc = wskbd_cd.cd_devs[minor(dev)];
! 1164:
! 1165: if (sc->sc_base.me_evp == NULL)
! 1166: return (POLLERR);
! 1167: return (wsevent_poll(sc->sc_base.me_evp, events, p));
! 1168: }
! 1169:
! 1170: #if NWSDISPLAY > 0
! 1171:
! 1172: int
! 1173: wskbd_pickfree()
! 1174: {
! 1175: int i;
! 1176: struct wskbd_softc *sc;
! 1177:
! 1178: for (i = 0; i < wskbd_cd.cd_ndevs; i++) {
! 1179: if ((sc = wskbd_cd.cd_devs[i]) == NULL)
! 1180: continue;
! 1181: if (sc->sc_displaydv == NULL)
! 1182: return (i);
! 1183: }
! 1184: return (-1);
! 1185: }
! 1186:
! 1187: struct wsevsrc *
! 1188: wskbd_set_console_display(struct device *displaydv, struct wsevsrc *me)
! 1189: {
! 1190: struct wskbd_softc *sc = wskbd_console_device;
! 1191:
! 1192: if (sc == NULL)
! 1193: return (NULL);
! 1194: sc->sc_base.me_dispdv = displaydv;
! 1195: #if NWSMUX > 0
! 1196: (void)wsmux_attach_sc((struct wsmux_softc *)me, &sc->sc_base);
! 1197: #endif
! 1198: return (&sc->sc_base);
! 1199: }
! 1200:
! 1201: int
! 1202: wskbd_set_display(struct device *dv, struct device *displaydv)
! 1203: {
! 1204: struct wskbd_softc *sc = (struct wskbd_softc *)dv;
! 1205: struct device *odisplaydv;
! 1206: int error;
! 1207:
! 1208: DPRINTF(("wskbd_set_display: %s odisp=%p disp=%p cons=%d\n",
! 1209: dv->dv_xname, sc->sc_base.me_dispdv, displaydv,
! 1210: sc->sc_isconsole));
! 1211:
! 1212: if (sc->sc_isconsole)
! 1213: return (EBUSY);
! 1214:
! 1215: if (displaydv != NULL) {
! 1216: if (sc->sc_base.me_dispdv != NULL)
! 1217: return (EBUSY);
! 1218: } else {
! 1219: if (sc->sc_base.me_dispdv == NULL)
! 1220: return (ENXIO);
! 1221: }
! 1222:
! 1223: odisplaydv = sc->sc_base.me_dispdv;
! 1224: sc->sc_base.me_dispdv = NULL;
! 1225: error = wskbd_enable(sc, displaydv != NULL);
! 1226: sc->sc_base.me_dispdv = displaydv;
! 1227: if (error) {
! 1228: sc->sc_base.me_dispdv = odisplaydv;
! 1229: return (error);
! 1230: }
! 1231:
! 1232: if (displaydv)
! 1233: printf("%s: connecting to %s\n",
! 1234: sc->sc_base.me_dv.dv_xname, displaydv->dv_xname);
! 1235: else
! 1236: printf("%s: disconnecting from %s\n",
! 1237: sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname);
! 1238:
! 1239: return (0);
! 1240: }
! 1241:
! 1242: #endif /* NWSDISPLAY > 0 */
! 1243:
! 1244: #if NWSMUX > 0
! 1245: int
! 1246: wskbd_add_mux(int unit, struct wsmux_softc *muxsc)
! 1247: {
! 1248: struct wskbd_softc *sc;
! 1249:
! 1250: if (unit < 0 || unit >= wskbd_cd.cd_ndevs ||
! 1251: (sc = wskbd_cd.cd_devs[unit]) == NULL)
! 1252: return (ENXIO);
! 1253:
! 1254: if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
! 1255: return (EBUSY);
! 1256:
! 1257: return (wsmux_attach_sc(muxsc, &sc->sc_base));
! 1258: }
! 1259: #endif
! 1260:
! 1261: /*
! 1262: * Console interface.
! 1263: */
! 1264: int
! 1265: wskbd_cngetc(dev_t dev)
! 1266: {
! 1267: static int num = 0;
! 1268: static int pos;
! 1269: u_int type;
! 1270: int data;
! 1271: keysym_t ks;
! 1272:
! 1273: if (!wskbd_console_initted)
! 1274: return 0;
! 1275:
! 1276: if (wskbd_console_device != NULL &&
! 1277: !wskbd_console_device->sc_translating)
! 1278: return 0;
! 1279:
! 1280: for(;;) {
! 1281: if (num-- > 0) {
! 1282: ks = wskbd_console_data.t_symbols[pos++];
! 1283: if (KS_GROUP(ks) == KS_GROUP_Ascii)
! 1284: return (KS_VALUE(ks));
! 1285: } else {
! 1286: (*wskbd_console_data.t_consops->getc)
! 1287: (wskbd_console_data.t_consaccesscookie,
! 1288: &type, &data);
! 1289: num = wskbd_translate(&wskbd_console_data, type, data);
! 1290: pos = 0;
! 1291: }
! 1292: }
! 1293: }
! 1294:
! 1295: void
! 1296: wskbd_cnpollc(dev_t dev, int poll)
! 1297: {
! 1298:
! 1299: if (!wskbd_console_initted)
! 1300: return;
! 1301:
! 1302: if (wskbd_console_device != NULL &&
! 1303: !wskbd_console_device->sc_translating)
! 1304: return;
! 1305:
! 1306: (*wskbd_console_data.t_consops->pollc)
! 1307: (wskbd_console_data.t_consaccesscookie, poll);
! 1308: }
! 1309:
! 1310: void
! 1311: wskbd_cnbell(dev_t dev, u_int pitch, u_int period, u_int volume)
! 1312: {
! 1313: if (!wskbd_console_initted)
! 1314: return;
! 1315:
! 1316: if (wskbd_console_data.t_consops->bell != NULL)
! 1317: (*wskbd_console_data.t_consops->bell)
! 1318: (wskbd_console_data.t_consaccesscookie, pitch, period,
! 1319: volume);
! 1320: }
! 1321:
! 1322: void
! 1323: update_leds(struct wskbd_internal *id)
! 1324: {
! 1325: int new_state;
! 1326:
! 1327: new_state = 0;
! 1328: if (id->t_modifiers & (MOD_SHIFTLOCK | MOD_CAPSLOCK))
! 1329: new_state |= WSKBD_LED_CAPS;
! 1330: if (id->t_modifiers & MOD_NUMLOCK)
! 1331: new_state |= WSKBD_LED_NUM;
! 1332: if (id->t_modifiers & MOD_COMPOSE)
! 1333: new_state |= WSKBD_LED_COMPOSE;
! 1334: if (id->t_modifiers & MOD_HOLDSCREEN)
! 1335: new_state |= WSKBD_LED_SCROLL;
! 1336:
! 1337: if (id->t_sc && new_state != id->t_sc->sc_ledstate) {
! 1338: (*id->t_sc->sc_accessops->set_leds)
! 1339: (id->t_sc->sc_accesscookie, new_state);
! 1340: id->t_sc->sc_ledstate = new_state;
! 1341: }
! 1342: }
! 1343:
! 1344: void
! 1345: update_modifier(struct wskbd_internal *id, u_int type, int toggle, int mask)
! 1346: {
! 1347: if (toggle) {
! 1348: if (type == WSCONS_EVENT_KEY_DOWN)
! 1349: id->t_modifiers ^= mask;
! 1350: } else {
! 1351: if (type == WSCONS_EVENT_KEY_DOWN)
! 1352: id->t_modifiers |= mask;
! 1353: else
! 1354: id->t_modifiers &= ~mask;
! 1355: }
! 1356: }
! 1357:
! 1358: #if NWSDISPLAY > 0
! 1359: void
! 1360: change_displayparam(struct wskbd_softc *sc, int param, int updown,
! 1361: int wraparound)
! 1362: {
! 1363: int res;
! 1364: struct wsdisplay_param dp;
! 1365:
! 1366: dp.param = param;
! 1367: res = wsdisplay_param(sc->sc_base.me_dispdv, WSDISPLAYIO_GETPARAM, &dp);
! 1368:
! 1369: if (res == EINVAL)
! 1370: return; /* no such parameter */
! 1371:
! 1372: dp.curval += updown;
! 1373: if (dp.max < dp.curval)
! 1374: dp.curval = wraparound ? dp.min : dp.max;
! 1375: else
! 1376: if (dp.curval < dp.min)
! 1377: dp.curval = wraparound ? dp.max : dp.min;
! 1378: wsdisplay_param(sc->sc_base.me_dispdv, WSDISPLAYIO_SETPARAM, &dp);
! 1379: }
! 1380: #endif
! 1381:
! 1382: int
! 1383: internal_command(struct wskbd_softc *sc, u_int *type, keysym_t ksym,
! 1384: keysym_t ksym2)
! 1385: {
! 1386: switch (ksym) {
! 1387: case KS_Cmd:
! 1388: update_modifier(sc->id, *type, 0, MOD_COMMAND);
! 1389: ksym = ksym2;
! 1390: break;
! 1391:
! 1392: case KS_Cmd1:
! 1393: update_modifier(sc->id, *type, 0, MOD_COMMAND1);
! 1394: break;
! 1395:
! 1396: case KS_Cmd2:
! 1397: update_modifier(sc->id, *type, 0, MOD_COMMAND2);
! 1398: break;
! 1399: }
! 1400:
! 1401: if (*type != WSCONS_EVENT_KEY_DOWN)
! 1402: return (0);
! 1403:
! 1404: #ifdef SCROLLBACK_SUPPORT
! 1405: #if NWSDISPLAY > 0
! 1406: switch (ksym) {
! 1407: case KS_Cmd_ScrollBack:
! 1408: if (MOD_ONESET(sc->id, MOD_ANYSHIFT)) {
! 1409: if (sc->sc_displaydv != NULL)
! 1410: wsscrollback(sc->sc_displaydv,
! 1411: WSDISPLAY_SCROLL_BACKWARD);
! 1412: return (1);
! 1413: }
! 1414: break;
! 1415:
! 1416: case KS_Cmd_ScrollFwd:
! 1417: if (MOD_ONESET(sc->id, MOD_ANYSHIFT)) {
! 1418: if (sc->sc_displaydv != NULL)
! 1419: wsscrollback(sc->sc_displaydv,
! 1420: WSDISPLAY_SCROLL_FORWARD);
! 1421: return (1);
! 1422: }
! 1423: break;
! 1424: }
! 1425: #endif
! 1426: #endif
! 1427:
! 1428: if (!MOD_ONESET(sc->id, MOD_COMMAND) &&
! 1429: !MOD_ALLSET(sc->id, MOD_COMMAND1 | MOD_COMMAND2))
! 1430: return (0);
! 1431:
! 1432: #ifdef DDB
! 1433: if (ksym == KS_Cmd_Debugger) {
! 1434: if (sc->sc_isconsole && db_console)
! 1435: Debugger();
! 1436: /* discard this key (ddb discarded command modifiers) */
! 1437: *type = WSCONS_EVENT_KEY_UP;
! 1438: return (1);
! 1439: }
! 1440: #endif
! 1441:
! 1442: #if NWSDISPLAY > 0
! 1443: if (sc->sc_base.me_dispdv == NULL)
! 1444: return (0);
! 1445:
! 1446: switch (ksym) {
! 1447: case KS_Cmd_Screen0:
! 1448: case KS_Cmd_Screen1:
! 1449: case KS_Cmd_Screen2:
! 1450: case KS_Cmd_Screen3:
! 1451: case KS_Cmd_Screen4:
! 1452: case KS_Cmd_Screen5:
! 1453: case KS_Cmd_Screen6:
! 1454: case KS_Cmd_Screen7:
! 1455: case KS_Cmd_Screen8:
! 1456: case KS_Cmd_Screen9:
! 1457: case KS_Cmd_Screen10:
! 1458: case KS_Cmd_Screen11:
! 1459: wsdisplay_switch(sc->sc_displaydv, ksym - KS_Cmd_Screen0, 0);
! 1460: return (1);
! 1461: case KS_Cmd_ResetEmul:
! 1462: wsdisplay_reset(sc->sc_displaydv, WSDISPLAY_RESETEMUL);
! 1463: return (1);
! 1464: case KS_Cmd_ResetClose:
! 1465: wsdisplay_reset(sc->sc_displaydv, WSDISPLAY_RESETCLOSE);
! 1466: return (1);
! 1467: #if defined(__i386__) || defined(__amd64__)
! 1468: case KS_Cmd_KbdReset:
! 1469: if (kbd_reset == 1) {
! 1470: kbd_reset = 0;
! 1471: psignal(initproc, SIGUSR1);
! 1472: }
! 1473: return (1);
! 1474: #endif
! 1475: case KS_Cmd_BacklightOn:
! 1476: case KS_Cmd_BacklightOff:
! 1477: case KS_Cmd_BacklightToggle:
! 1478: change_displayparam(sc, WSDISPLAYIO_PARAM_BACKLIGHT,
! 1479: ksym == KS_Cmd_BacklightOff ? -1 : 1,
! 1480: ksym == KS_Cmd_BacklightToggle ? 1 : 0);
! 1481: return (1);
! 1482: case KS_Cmd_BrightnessUp:
! 1483: case KS_Cmd_BrightnessDown:
! 1484: case KS_Cmd_BrightnessRotate:
! 1485: change_displayparam(sc, WSDISPLAYIO_PARAM_BRIGHTNESS,
! 1486: ksym == KS_Cmd_BrightnessDown ? -1 : 1,
! 1487: ksym == KS_Cmd_BrightnessRotate ? 1 : 0);
! 1488: return (1);
! 1489: case KS_Cmd_ContrastUp:
! 1490: case KS_Cmd_ContrastDown:
! 1491: case KS_Cmd_ContrastRotate:
! 1492: change_displayparam(sc, WSDISPLAYIO_PARAM_CONTRAST,
! 1493: ksym == KS_Cmd_ContrastDown ? -1 : 1,
! 1494: ksym == KS_Cmd_ContrastRotate ? 1 : 0);
! 1495: return (1);
! 1496: }
! 1497: #endif
! 1498: return (0);
! 1499: }
! 1500:
! 1501: int
! 1502: wskbd_translate(struct wskbd_internal *id, u_int type, int value)
! 1503: {
! 1504: struct wskbd_softc *sc = id->t_sc;
! 1505: keysym_t ksym, res, *group;
! 1506: struct wscons_keymap kpbuf, *kp;
! 1507: int gindex, iscommand = 0;
! 1508:
! 1509: if (type == WSCONS_EVENT_ALL_KEYS_UP) {
! 1510: #if NWSDISPLAY > 0
! 1511: if (sc != NULL && sc->sc_repeating) {
! 1512: sc->sc_repeating = 0;
! 1513: timeout_del(&sc->sc_repeat_ch);
! 1514: }
! 1515: #endif
! 1516: id->t_modifiers &= ~(MOD_SHIFT_L | MOD_SHIFT_R |
! 1517: MOD_CONTROL_L | MOD_CONTROL_R |
! 1518: MOD_META_L | MOD_META_R |
! 1519: MOD_MODESHIFT | MOD_MODELOCK |
! 1520: MOD_COMMAND | MOD_COMMAND1 | MOD_COMMAND2);
! 1521: update_leds(id);
! 1522: return (0);
! 1523: }
! 1524:
! 1525: if (sc != NULL) {
! 1526: if (value < 0 || value >= sc->sc_maplen) {
! 1527: #ifdef DEBUG
! 1528: printf("wskbd_translate: keycode %d out of range\n",
! 1529: value);
! 1530: #endif
! 1531: return (0);
! 1532: }
! 1533: kp = sc->sc_map + value;
! 1534: } else {
! 1535: kp = &kpbuf;
! 1536: wskbd_get_mapentry(id->t_keymap, value, kp);
! 1537: }
! 1538:
! 1539: /* if this key has a command, process it first */
! 1540: if (sc != NULL && kp->command != KS_voidSymbol)
! 1541: iscommand = internal_command(sc, &type, kp->command,
! 1542: kp->group1[0]);
! 1543:
! 1544: /* Now update modifiers */
! 1545: switch (kp->group1[0]) {
! 1546: case KS_Shift_L:
! 1547: update_modifier(id, type, 0, MOD_SHIFT_L);
! 1548: break;
! 1549:
! 1550: case KS_Shift_R:
! 1551: update_modifier(id, type, 0, MOD_SHIFT_R);
! 1552: break;
! 1553:
! 1554: case KS_Shift_Lock:
! 1555: update_modifier(id, type, 1, MOD_SHIFTLOCK);
! 1556: break;
! 1557:
! 1558: case KS_Caps_Lock:
! 1559: update_modifier(id, type, 1, MOD_CAPSLOCK);
! 1560: break;
! 1561:
! 1562: case KS_Control_L:
! 1563: update_modifier(id, type, 0, MOD_CONTROL_L);
! 1564: break;
! 1565:
! 1566: case KS_Control_R:
! 1567: update_modifier(id, type, 0, MOD_CONTROL_R);
! 1568: break;
! 1569:
! 1570: case KS_Alt_L:
! 1571: update_modifier(id, type, 0, MOD_META_L);
! 1572: break;
! 1573:
! 1574: case KS_Alt_R:
! 1575: update_modifier(id, type, 0, MOD_META_R);
! 1576: break;
! 1577:
! 1578: case KS_Mode_switch:
! 1579: update_modifier(id, type, 0, MOD_MODESHIFT);
! 1580: break;
! 1581:
! 1582: case KS_Mode_Lock:
! 1583: update_modifier(id, type, 1, MOD_MODELOCK);
! 1584: break;
! 1585:
! 1586: case KS_Num_Lock:
! 1587: update_modifier(id, type, 1, MOD_NUMLOCK);
! 1588: break;
! 1589:
! 1590: #if NWSDISPLAY > 0
! 1591: case KS_Hold_Screen:
! 1592: if (sc != NULL) {
! 1593: update_modifier(id, type, 1, MOD_HOLDSCREEN);
! 1594: wskbd_holdscreen(sc, id->t_modifiers & MOD_HOLDSCREEN);
! 1595: }
! 1596: break;
! 1597:
! 1598: default:
! 1599: if (sc != NULL && sc->sc_repeating &&
! 1600: ((type == WSCONS_EVENT_KEY_UP && value != sc->sc_repkey) ||
! 1601: (type == WSCONS_EVENT_KEY_DOWN && value == sc->sc_repkey)))
! 1602: return (0);
! 1603: break;
! 1604: #endif
! 1605: }
! 1606:
! 1607: #if NWSDISPLAY > 0
! 1608: if (sc != NULL) {
! 1609: if (sc->sc_repeating) {
! 1610: sc->sc_repeating = 0;
! 1611: timeout_del(&sc->sc_repeat_ch);
! 1612: }
! 1613: sc->sc_repkey = value;
! 1614: }
! 1615: #endif
! 1616:
! 1617: /* If this is a key release or we are in command mode, we are done */
! 1618: if (type != WSCONS_EVENT_KEY_DOWN || iscommand) {
! 1619: update_leds(id);
! 1620: return (0);
! 1621: }
! 1622:
! 1623: /* Get the keysym */
! 1624: if (id->t_modifiers & (MOD_MODESHIFT|MOD_MODELOCK) &&
! 1625: !MOD_ONESET(id, MOD_ANYCONTROL))
! 1626: group = & kp->group2[0];
! 1627: else
! 1628: group = & kp->group1[0];
! 1629:
! 1630: if ((id->t_modifiers & MOD_NUMLOCK) &&
! 1631: KS_GROUP(group[1]) == KS_GROUP_Keypad) {
! 1632: gindex = !MOD_ONESET(id, MOD_ANYSHIFT);
! 1633: ksym = group[gindex];
! 1634: } else {
! 1635: /* CAPS alone should only affect letter keys */
! 1636: if ((id->t_modifiers & (MOD_CAPSLOCK | MOD_ANYSHIFT)) ==
! 1637: MOD_CAPSLOCK) {
! 1638: gindex = 0;
! 1639: ksym = ksym_upcase(group[0]);
! 1640: } else {
! 1641: gindex = MOD_ONESET(id, MOD_ANYSHIFT);
! 1642: ksym = group[gindex];
! 1643: }
! 1644: }
! 1645:
! 1646: /* Submit Audio keys for hotkey processing */
! 1647: if (KS_GROUP(ksym) == KS_GROUP_Function) {
! 1648: switch (ksym) {
! 1649: #if NAUDIO > 0
! 1650: case KS_AudioMute:
! 1651: workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
! 1652: (void *)(long)0, NULL);
! 1653: break;
! 1654: case KS_AudioLower:
! 1655: workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
! 1656: (void *)(long)-1, NULL);
! 1657: break;
! 1658: case KS_AudioRaise:
! 1659: workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
! 1660: (void *)(long)1, NULL);
! 1661: return (0);
! 1662: #endif
! 1663: default:
! 1664: break;
! 1665: }
! 1666: }
! 1667:
! 1668: /* Process compose sequence and dead accents */
! 1669: res = KS_voidSymbol;
! 1670:
! 1671: switch (KS_GROUP(ksym)) {
! 1672: case KS_GROUP_Ascii:
! 1673: case KS_GROUP_Keypad:
! 1674: case KS_GROUP_Function:
! 1675: res = ksym;
! 1676: break;
! 1677:
! 1678: case KS_GROUP_Mod:
! 1679: if (ksym == KS_Multi_key) {
! 1680: update_modifier(id, 1, 0, MOD_COMPOSE);
! 1681: id->t_composelen = 2;
! 1682: }
! 1683: break;
! 1684:
! 1685: case KS_GROUP_Dead:
! 1686: if (id->t_composelen == 0) {
! 1687: update_modifier(id, 1, 0, MOD_COMPOSE);
! 1688: id->t_composelen = 1;
! 1689: id->t_composebuf[0] = ksym;
! 1690: } else
! 1691: res = ksym;
! 1692: break;
! 1693: }
! 1694:
! 1695: if (res == KS_voidSymbol) {
! 1696: update_leds(id);
! 1697: return (0);
! 1698: }
! 1699:
! 1700: if (id->t_composelen > 0) {
! 1701: /*
! 1702: * If the compose key also serves as AltGr (i.e. set to both
! 1703: * KS_Multi_key and KS_Mode_switch), and would provide a valid,
! 1704: * distinct combination as AltGr, leave compose mode.
! 1705: */
! 1706: if (id->t_composelen == 2 && group == &kp->group2[0]) {
! 1707: if (kp->group1[gindex] != kp->group2[gindex])
! 1708: id->t_composelen = 0;
! 1709: }
! 1710:
! 1711: if (id->t_composelen != 0) {
! 1712: id->t_composebuf[2 - id->t_composelen] = res;
! 1713: if (--id->t_composelen == 0) {
! 1714: res = wskbd_compose_value(id->t_composebuf);
! 1715: update_modifier(id, 0, 0, MOD_COMPOSE);
! 1716: } else {
! 1717: return (0);
! 1718: }
! 1719: }
! 1720: }
! 1721:
! 1722: update_leds(id);
! 1723:
! 1724: /* We are done, return the symbol */
! 1725: if (KS_GROUP(res) == KS_GROUP_Ascii) {
! 1726: if (MOD_ONESET(id, MOD_ANYCONTROL)) {
! 1727: if ((res >= KS_at && res <= KS_z) || res == KS_space)
! 1728: res = res & 0x1f;
! 1729: else if (res == KS_2)
! 1730: res = 0x00;
! 1731: else if (res >= KS_3 && res <= KS_7)
! 1732: res = KS_Escape + (res - KS_3);
! 1733: else if (res == KS_8)
! 1734: res = KS_Delete;
! 1735: }
! 1736: if (MOD_ONESET(id, MOD_ANYMETA)) {
! 1737: if (id->t_flags & WSKFL_METAESC) {
! 1738: id->t_symbols[0] = KS_Escape;
! 1739: id->t_symbols[1] = res;
! 1740: return (2);
! 1741: } else
! 1742: res |= 0x80;
! 1743: }
! 1744: }
! 1745:
! 1746: id->t_symbols[0] = res;
! 1747: return (1);
! 1748: }
CVSweb