/* * Copyright (C) 1985 by Sun Microsystems, Inc. */ #include "kbd.h" #if NKBD > 0 /* * Keyboard input line discipline. * Console output line discipline. */ #include "../h/param.h" #include "../h/systm.h" #include "../h/stream.h" #include "../h/ttyio.h" #include "../h/ttyld.h" #include "../h/conf.h" #include "../machine/sunromvec.h" #include "../sundev/kbdvar.h" /* * For now these are shared. */ extern int nkeytables; extern struct keyboard *keytables[]; extern char keystringtab[16][KTAB_STRLEN]; #define BUSY 02 #define TIMEOUT 04 struct kbdld { /* Added for 9th edition */ struct queue *key_q; /* queue for the keyboard */ struct queue *cons_q; /* queue for output to the console */ short c_state; /* state of the console */ /* Defined by sun */ u_char k_id; u_char k_idstate; u_char k_state; u_char k_rptkey; u_int k_buckybits; u_int k_shiftmask; struct keyboard *k_curkeyboard; u_int k_togglemask; /* Toggle shifts state */ } kbd[NKBD], nullkbd; /* * States of keyboard ID recognizer */ #define KID_NONE 0 /* startup */ #define KID_IDLE 1 /* saw IDLE code */ #define KID_LKSUN2 2 /* probably Sun-2 */ #define KID_OK 3 /* locked on ID */ /* * Constants setup during the first open of a kbd (so that hz is defined). */ int kbd_repeatrate; int kbd_repeatdelay; int kbdopen(), kbdclose(), kbdldin(), kbdisrv(), kbdldout(); static struct qinit kbdrinit = { kbdldin, kbdisrv, kbdopen, kbdclose,300, 60}; static struct qinit kbdwinit = { kbdldout, NULL, kbdopen, kbdclose, 200, 100 }; struct streamtab kbdinfo = { &kbdrinit, &kbdwinit}; /* * Keyboard and console open */ kbdopen(qp, dev) register struct queue *qp; { register struct kbdld *k; /* Set these up only once so that they could be changed from adb */ if (!kbd_repeatrate) { kbd_repeatrate = (hz+29)/30; kbd_repeatdelay = hz/2; } if (qp->ptr) /* already attached */ return(1); for (k = kbd; k->key_q != 0; k++) if (k >= &kbd[NKBD]) return(0); *k = nullkbd; k->key_q = qp; k->cons_q = WR(qp); qp->ptr = (caddr_t)k; WR(qp)->ptr = (caddr_t)k; kbdreset(k); return(1); } kbdclose(qp) register struct queue *qp; { register struct kbdld *k = (struct kbdld *)qp->ptr; k->key_q = 0; k->cons_q = 0; } /* * Console write put routine */ kbdldout(q, bp) register struct queue *q; register struct block *bp; { register union stmsg *sp; register struct kbdld *k = (struct kbdld *)q->ptr; switch(bp->type) { case M_IOCTL: sp = (union stmsg *)bp->rptr; switch (sp->ioc0.com) { case TIOCGDEV: sp->ioc3.sb.ispeed = sp->ioc3.sb.ospeed = B9600; bp->type = M_IOCACK; qreply(q, bp); return; case TIOCSDEV: bp->wptr = bp->rptr; bp->type = M_IOCACK; qreply(q, bp); return; default: bp->type = M_IOCNAK; bp->wptr = bp->rptr; qreply(q, bp); return; } case M_STOP: k->c_state |= TTSTOP; break; case M_START: k->c_state &= ~TTSTOP; kbdstart(k); break; case M_FLUSH: flushq(q, 0); break; case M_DELAY: case M_DATA: putq(q, bp); kbdstart(k); return; default: break; } freeb(bp); } kbdtime(k) register struct kbdld *k; { k->c_state &= ~TIMEOUT; kbdstart(k); } kbdstart(k) register struct kbdld *k; { int kbdtocons(); if (k->cons_q==NULL) return; if ((k->c_state & (BUSY|TIMEOUT|TTSTOP))==0 && k->cons_q->count) { k->c_state |= BUSY; softcall(kbdtocons, (caddr_t)k); } } kbdtocons(k) register struct kbdld *k; { register struct block *bp; register u_char *cp; if (k->cons_q==NULL) return; while ((k->c_state & TTSTOP)==0 && k->cons_q->count) { bp = getq(k->cons_q); switch (bp->type) { case M_DATA: /* Must clear high bit for monitor */ for(cp = bp->rptr; cp < bp->wptr; cp++) *cp &= 0177; (*romp->v_fwritestr)(bp->rptr, bp->wptr - bp->rptr, romp->v_fbaddr); freeb(bp); break; case M_DELAY: k->c_state |= TIMEOUT; k->c_state &= ~BUSY; timeout(kbdtime, (caddr_t)k, (int)*bp->rptr); freeb(bp); return; default: freeb(bp); break; } } k->c_state &= ~BUSY; } kbdldin(q, bp) struct queue *q; register struct block *bp; { register struct kbdld *k = (struct kbdld *)q->ptr; /* Pass along anything but data */ if (bp->type != M_DATA) { (*q->next->qinfo->putp)(q->next, bp); return; } while (bp->rptr < bp->wptr) kbdinput(*bp->rptr++, k); freeb(bp); while ((q->next->flag&QFULL)==0 && (bp = getq(q))) (*q->next->qinfo->putp)(q->next, bp); } /* * keyboard server processing. */ kbdisrv(q) register struct queue *q; { register struct block *bp; while ((q->next->flag&QFULL)==0 && (bp = getq(q))) (*q->next->qinfo->putp)(q->next, bp); } /* * kbdclick is used to remember the current click value of the * Sun-3 keyboard. This brain damaged keyboard will reset the * clicking to the "default" value after a reset command and * there is no way to read out the current click value. We * cannot send a click command immediately after the reset * command or the keyboard gets screwed up. So we wait until * we get the ID byte before we send back the click command. * Unfortunately, this means that there is a small window * where the keyboard can click when it really shouldn't be. * A value of -1 means that kbdclick has not been initialized yet. */ int kbdclick = -1; /* * Send command byte to keyboard */ kbdcmd(k, cmd) register struct kbdld *k; char cmd; { register struct queue *q = WR(k->key_q)->next; putd(q->qinfo->putp, q, cmd); if (cmd == KBD_CMD_NOCLICK) kbdclick = 0; else if (cmd == KBD_CMD_CLICK) kbdclick = 1; } /* * Reset the keyboard */ kbdreset(k) register struct kbdld *k; { k->k_idstate = KID_NONE; k->k_state = NORMAL; kbdcmd(k, KBD_CMD_RESET); } kbdidletimeout(k) register struct kbdld *k; { untimeout(kbdidletimeout, (caddr_t)k); /* * Double check that was waiting for idle timeout. */ if (k->k_idstate == KID_IDLE) kbdinput(IDLEKEY, k); } /* * Process a keypress */ kbdinput(key, k) register u_char key; register struct kbdld *k; { switch (k->k_idstate) { case KID_NONE: if (key == IDLEKEY) { k->k_idstate = KID_IDLE; timeout(kbdidletimeout, (caddr_t)k, hz/10); } else if (key == RESETKEY) k->k_idstate = KID_LKSUN2; return; case KID_IDLE: if (key == IDLEKEY) kbdid(k, KB_KLUNK); else if (key == RESETKEY) k->k_idstate = KID_LKSUN2; else if (key & 0x80) kbdid(k, (int)(KB_VT100 | (key&0x40))); else kbdreset(k); return; case KID_LKSUN2: if (key == 0x02) { /* Sun-2 keyboard */ kbdid(k, KB_SUN2); return; } if (key == 0x03) { /* Sun-3 keyboard */ kbdid(k, KB_SUN3); /* * We just did a reset command to a Sun-3 keyboard * which sets the click back to the default * (which is currently ON!). We use the kbdclick * variable to see if the keyboard should be * turned on or off. If it has not been set, * then on a sun3 we use the eeprom to determine * if the default value is on or off. In the * sun2 case, we default to off. */ switch (kbdclick) { case 0: kbdcmd(k, KBD_CMD_NOCLICK); break; case 1: kbdcmd(k, KBD_CMD_CLICK); break; case -1: default: { #ifdef sun3 #include "../sun3/eeprom.h" if (EEPROM->ee_diag.eed_keyclick == EED_KEYCLICK) kbdcmd(k, KBD_CMD_CLICK); else #endif sun3 kbdcmd(k, KBD_CMD_NOCLICK); } break; } return; } kbdreset(k); return; case KID_OK: if (key == 0 || key == 0xFF) { kbdreset(k); return; } break; } switch (k->k_state) { normalstate: k->k_state = NORMAL; case NORMAL: if (k->k_curkeyboard && key == k->k_curkeyboard->k_abort1) { k->k_state = ABORT1; break; } kbdtranslate(k, key); if (key == IDLEKEY) k->k_state = IDLE1; break; case IDLE1: if (key & 0x80) { /* ID byte */ if (k->k_id == KB_VT100) k->k_state = IDLE2; else kbdreset(k); break; } if (key != IDLEKEY) goto normalstate; /* real data */ break; case IDLE2: if (key == IDLEKEY) k->k_state = IDLE1; else goto normalstate; break; case ABORT1: if (k->k_curkeyboard) { if (key == k->k_curkeyboard->k_abort2) { DELAY(100000); montrap(*romp->v_abortent); k->k_state = NORMAL; kbdtranslate(k, (u_char)IDLEKEY); /* fake */ return; } else { kbdtranslate(k, k->k_curkeyboard->k_abort1); goto normalstate; } } } } kbdid(k, id) register struct kbdld *k; int id; { k->k_id = id & 0xF; k->k_idstate = KID_OK; k->k_shiftmask = 0; if (id & 0x40) /* Not a transition so don't send event */ k->k_shiftmask |= CAPSMASK; k->k_buckybits = 0; k->k_curkeyboard = keytables[k->k_id]; k->k_rptkey = IDLEKEY; /* Nothing happening now */ } /* * This routine determines which table we should look in to decode * the current keycode. */ struct keymap * settable(k, mask) register struct kbdld *k; register u_int mask; { register struct keyboard *kp; kp = k->k_curkeyboard; if (kp == NULL) return (NULL); if (mask & UPMASK) return (kp->k_up); if (mask & CTRLMASK) return (kp->k_control); if (mask & SHIFTMASK) return (kp->k_shifted); if (mask & CAPSMASK) return (kp->k_caps); return (kp->k_normal); } kbdrpt(k) register struct kbdld *k; { kbdtranslate(k, k->k_rptkey); if (k->k_rptkey != IDLEKEY) timeout(kbdrpt, (caddr_t)k, kbd_repeatrate); } kbdcancelrpt(k) register struct kbdld *k; { if (k->k_rptkey != IDLEKEY) { untimeout(kbdrpt, (caddr_t)k); k->k_rptkey = IDLEKEY; } } kbdtranslate(k, keycode) register struct kbdld *k; register u_char keycode; { register u_char key, newstate, entry; register u_char enF0; register char *cp; struct keymap *km; register struct queue *q = k->key_q; newstate = STATEOF(keycode); key = KEYOF(keycode); km = settable(k, (u_int)(k->k_shiftmask | newstate)); if (km == NULL) { /* gross error */ kbdcancelrpt(k); return; } entry = km->keymap[key]; enF0 = entry & 0xF0; /* * Handle the state of toggle shifts specially. * Toggle shifts should only come on downs. */ if (((entry >> 4) == (SHIFTKEYS >> 4)) && ((1 << (entry & 0x0F)) & k->k_curkeyboard->k_toggleshifts)) { if ((1 << (entry & 0x0F)) & k->k_togglemask) { newstate = RELEASED; } else { newstate = PRESSED; } } if (newstate == PRESSED && entry != NOSCROLL && enF0 != SHIFTKEYS && enF0 != BUCKYBITS && !(entry >= LEFTFUNC && entry <= BOTTOMFUNC+15)) { if (k->k_rptkey != keycode) { kbdcancelrpt(k); timeout(kbdrpt, (caddr_t)k, kbd_repeatdelay); k->k_rptkey = keycode; } } else if (key == KEYOF(k->k_rptkey)) /* key going up */ kbdcancelrpt(k); switch (entry >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: putd(putq, q, entry | k->k_buckybits); break; case SHIFTKEYS >> 4: { u_int shiftbit = 1 << (entry & 0x0F); /* Modify toggle state (see toggle processing above) */ if (shiftbit & k->k_curkeyboard->k_toggleshifts) { if (newstate == RELEASED) { k->k_togglemask &= ~shiftbit; k->k_shiftmask &= ~shiftbit; } else { k->k_togglemask |= shiftbit; k->k_shiftmask |= shiftbit; } } else k->k_shiftmask ^= shiftbit; break; } case BUCKYBITS >> 4: k->k_buckybits ^= 1 << (7 + (entry & 0x0F)); break; case FUNNY >> 4: switch (entry) { case NOP: break; /* * NOSCROLL/CTRLS/CTRLQ exist so that these keys, on keyboards * with NOSCROLL, interact smoothly. If a user changes * his tty output control keys to be something other than those * in keytables for CTRLS & CTRLQ then he effectively disables * his NOSCROLL key. One could imagine computing CTRLS & CTRLQ * dynamically by watching TIOCSETC ioctl's go by in kbdioctl. */ case NOSCROLL: if (k->k_shiftmask & CTLSMASK) goto sendcq; else goto sendcs; case CTRLS: sendcs: k->k_shiftmask |= CTLSMASK; putd(putq, q, ('S'-0x40) | k->k_buckybits); break; case CTRLQ: sendcq: putd(putq, q, ('Q'-0x40) | k->k_buckybits); k->k_shiftmask &= ~CTLSMASK; break; case IDLE: /* * Minor hack to prevent keyboards unplugged * in caps lock from retaining their capslock * state when replugged. This should be * solved by using the capslock info in the * KBDID byte. */ if (keycode == NOTPRESENT) k->k_shiftmask = 0; /* Fall thru into RESET code */ case RESET: gotreset: k->k_shiftmask &= k->k_curkeyboard->k_idleshifts; k->k_shiftmask |= k->k_togglemask; k->k_buckybits &= k->k_curkeyboard->k_idlebuckys; kbdcancelrpt(k); break; case ERROR: printf("kbd: Error detected\r\n"); goto gotreset; /* * Remember when adding new entries that, * if they should NOT auto-repeat, * they should be put into the IF statement * just above this switch block. */ default: goto badentry; } break; case STRING >> 4: cp = &keystringtab[entry & 0x0F][0]; while (*cp != '\0') { putd(putq, q, *cp); cp++; } break; /* * Remember when adding new entries that, * if they should NOT auto-repeat, * they should be put into the IF statement * just above this switch block. */ default: if (entry >= LEFTFUNC && entry <= BOTTOMFUNC+15) { char buf[10], *strsetwithdecimal(); if (newstate == RELEASED) break; cp = strsetwithdecimal(&buf[0], (u_int)entry, sizeof (buf) - 1); putd(putq, q, '\033'); putd(putq, q, '['); while (*cp != '\0') { putd(putq, q, *cp); cp++; } putd(putq, q, 'z'); } badentry: break; } } char * strsetwithdecimal(buf, val, maxdigs) char *buf; u_int val, maxdigs; { int hradix = 5; char *bp; int lowbit; char *tab = "0123456789abcdef"; bp = buf + maxdigs; *(--bp) = '\0'; while (val) { lowbit = val & 1; val = (val >> 1); *(--bp) = tab[val % hradix * 2 + lowbit]; val /= hradix; } return (bp); } #endif NKBD > 0