/* * Mach Operating System * Copyright (c) 1991,1990,1989 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * File: lk201.c * Author: Alessandro Forin, Carnegie Mellon University * Date: 9/90 * * Routines for the LK201 Keyboard Driver */ #include #if NLK > 0 #include #include #include #include #include /* spl definitions */ #include #include #include #include #include /* * Structures describing the keyboard status */ typedef struct { unsigned short kbd_flags; unsigned short kbd_previous; char kbd_gen_shift; char kbd_ctrl; char kbd_lock; char kbd_meta; char kbd_shift; } lk201_kbd_state_t; #define mapLOCKtoCNTRL 0x1 /* flags */ typedef struct { char led_active; char led_pattern; char led_increasing; char led_lights; int led_interval; int led_light_count; } lk201_led_state_t; /* * Keyboard state */ struct lk201_softc { lk201_kbd_state_t kbd; lk201_led_state_t led; int sl_unit; } lk201_softc_data[NLK]; typedef struct lk201_softc *lk201_softc_t; lk201_softc_t lk201_softc[NLK]; /* * Forward decls */ io_return_t lk201_translation( int key, char c, int tabcode ); int lk201_input( int unit, unsigned short data); /* * Autoconf (sort-of) */ lk201_probe( int unit) { lk201_softc[unit] = &lk201_softc_data[unit]; return 1; } void lk201_attach( int unit, int sl_unit) { lk201_softc[unit]->sl_unit = sl_unit; lk201_selftest(unit); } /* * Keyboard initialization */ static unsigned char lk201_reset_string[] = { LK_CMD_LEDS_ON, LK_PARAM_LED_MASK(0xf), /* show we are resetting */ LK_CMD_SET_DEFAULTS, LK_CMD_MODE(LK_MODE_RPT_DOWN,1), LK_CMD_MODE(LK_MODE_RPT_DOWN,2), LK_CMD_MODE(LK_MODE_RPT_DOWN,3), LK_CMD_MODE(LK_MODE_RPT_DOWN,4), LK_CMD_MODE(LK_MODE_DOWN_UP,5), LK_CMD_MODE(LK_MODE_DOWN_UP,6), LK_CMD_MODE(LK_MODE_RPT_DOWN,7), LK_CMD_MODE(LK_MODE_RPT_DOWN,8), LK_CMD_MODE(LK_MODE_RPT_DOWN,9), LK_CMD_MODE(LK_MODE_RPT_DOWN,10), LK_CMD_MODE(LK_MODE_RPT_DOWN,11), LK_CMD_MODE(LK_MODE_RPT_DOWN,12), LK_CMD_MODE(LK_MODE_DOWN,13), LK_CMD_MODE(LK_MODE_RPT_DOWN,14), LK_CMD_ENB_RPT, /* LK_CMD_ENB_KEYCLK, LK_PARAM_VOLUME(4), */ LK_CMD_DIS_KEYCLK, LK_CMD_RESUME, LK_CMD_ENB_BELL, LK_PARAM_VOLUME(4), LK_CMD_LEDS_OFF, LK_PARAM_LED_MASK(0xf) }; void lk201_reset( int unit) { register int i, sl; register spl_t s; lk201_softc_t lk; lk = lk201_softc[unit]; sl = lk->sl_unit; s = spltty(); for (i = 0; i < sizeof(lk201_reset_string); i++) { (*console_putc)(sl, SCREEN_LINE_KEYBOARD, lk201_reset_string[i]); delay(100); } /* zero any state associated with previous keypresses */ bzero(lk, sizeof(*lk)); lk->sl_unit = sl; splx(s); } lk201_selftest( int unit) { int messg[4], sl; spl_t s; sl = lk201_softc[unit]->sl_unit; s = spltty(); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_CMD_REQ_ID); delay(10000);/* arbitrary */ messg[0] = (*console_getc)(sl, SCREEN_LINE_KEYBOARD, TRUE, TRUE); messg[1] = (*console_getc)(sl, SCREEN_LINE_KEYBOARD, TRUE, TRUE); splx(s); printf("( lk201 id %x.%x", messg[0], messg[1]); s = spltty(); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_CMD_POWER_UP); /* cannot do this, waiting too long might cause receiver overruns */ /* delay(80000);/* spec says 70 msecs or less */ messg[0] = (*console_getc)(sl, SCREEN_LINE_KEYBOARD, TRUE, TRUE); messg[1] = (*console_getc)(sl, SCREEN_LINE_KEYBOARD, TRUE, TRUE); messg[2] = (*console_getc)(sl, SCREEN_LINE_KEYBOARD, TRUE, TRUE); messg[3] = (*console_getc)(sl, SCREEN_LINE_KEYBOARD, TRUE, TRUE); splx(s); printf(", self-test "); if (messg[0] != 0x01 || messg[1] || messg[2] || messg[3]) printf("bad [%x %x %x %x]", messg[0], messg[1], messg[2], messg[3]); else printf("ok )"); lk201_reset(unit); } /* * Tinkerbell */ void lk201_ring_bell( int unit) { spl_t s = spltty(); (*console_putc)(lk201_softc[unit]->sl_unit, SCREEN_LINE_KEYBOARD, LK_CMD_BELL); splx(s); } /* * Here is your LED toy, Bob */ void lk201_lights( int unit, boolean_t on) { unsigned int sl; spl_t s; sl = lk201_softc[unit]->sl_unit; s = spltty(); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_CMD_LEDS_OFF); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_PARAM_LED_MASK(0xf)); if (on < 16 && on > 0) { (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_CMD_LEDS_ON); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_PARAM_LED_MASK(on)); } splx(s); } lk201_led( int unit) { lk201_led_state_t *leds = &lk201_softc[unit]->led; unsigned int sl; spl_t s; sl = lk201_softc[unit]->sl_unit; if (leds->led_interval) { /* leds are on */ if (leds->led_light_count <= 0) { /* hit this lights */ if (leds->led_lights <= 0) leds->led_lights= 1; /* sanity */ if (leds->led_lights > 16) leds->led_lights = 16;/* sanity */ leds->led_light_count = leds->led_interval; /* reset */ s = spltty(); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_CMD_LEDS_OFF); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_PARAM_LED_MASK(leds->led_lights)); switch (leds->led_pattern) { case LED_OFF: leds->led_interval = 0; /* since you can now set */ break; /* the interval even if off */ case LED_COUNT: leds->led_lights++; if (leds->led_lights > 16) leds->led_lights = 1; break; case LED_ROTATE: leds->led_lights <<= 1; if (leds->led_lights > 8) leds->led_lights = 1; break; case LED_CYLON: if (leds->led_increasing) { leds->led_lights <<= 1; if (leds->led_lights > 8) { leds->led_lights >>= 2; leds->led_increasing = 0; } } else { leds->led_lights >>= 1; if (leds->led_lights <= 0) { leds->led_lights = 2; leds->led_increasing = 1; } } break; } (*console_putc)( sl, SCREEN_LINE_KEYBOARD, LK_CMD_LEDS_ON); (*console_putc)( sl, SCREEN_LINE_KEYBOARD, LK_PARAM_LED_MASK(leds->led_lights)); splx(s); } leds->led_light_count--; } else { if (leds->led_lights) { s = spltty(); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_CMD_LEDS_OFF); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_PARAM_LED_MASK(0xf)); leds->led_lights = 0; splx(s); } leds->led_active = 0; #if NBM > 0 screen_enable_vretrace(unit, 0); /* interrupts off */ #endif } } /* * Special user-visible ops */ io_return_t lk201_set_status( int unit, dev_flavor_t flavor, dev_status_t status, natural_t status_count) { lk201_led_state_t *leds = &lk201_softc[unit]->led; lk201_kbd_state_t *kbd = &lk201_softc[unit]->kbd; switch( flavor ) { case LK201_SEND_CMD:{ register lk201_cmd_t *cmd = (lk201_cmd_t*)status; unsigned int cnt, s, sl; if ((status_count < (sizeof(*cmd)/sizeof(int))) || ((cnt = cmd->len) > 2)) return D_INVALID_SIZE; if (cnt == 0) cmd->command |= LK_PARAM; else cmd->params[cnt-1] |= LK_PARAM; sl = lk201_softc[unit]->sl_unit; s = spltty(); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, cmd->command); if (cnt > 0) (*console_putc)(sl, SCREEN_LINE_KEYBOARD, cmd->params[0]); if (cnt > 1) (*console_putc)(sl, SCREEN_LINE_KEYBOARD, cmd->params[1]); splx(s); return D_SUCCESS; } case LK201_LED_PATTERN:{ register int ptn = * (int *) status; if (ptn != LED_OFF && ptn != LED_COUNT && ptn != LED_ROTATE && ptn != LED_CYLON ) { return -1; } else { leds->led_pattern = ptn; } break; } case LK201_LED_INTERVAL:{ register int lcnt = * (int *) status; if (lcnt < 0) lcnt = 1; leds->led_interval = lcnt; break; } case LK201_mapLOCKtoCNTRL:{ boolean_t enable = * (boolean_t*) status; if (enable) kbd->kbd_flags |= mapLOCKtoCNTRL; else kbd->kbd_flags &= ~mapLOCKtoCNTRL; return D_SUCCESS; } case LK201_REMAP_KEY:{ register KeyMap *k = (KeyMap *) status; int mode; if (status_count < (sizeof(KeyMap)/sizeof(int))) return D_INVALID_SIZE; mode = k->shifted ? 1 : 0; if (k->meta) mode += 2; return lk201_translation( k->in_keyval, k->out_keyval, mode ); } default: return D_INVALID_OPERATION; } leds->led_lights = 1; leds->led_active = 1; #if NBM > 0 screen_enable_vretrace(unit, 1); /* interrupts on */ #endif return D_SUCCESS; } /* * Keycode translation tables * * NOTE: these tables have been compressed a little bit * because the lk201 cannot generate very small codes. */ unsigned char lk201_xlate_key[] = { /* 86 */ 0 ,0 /* 88 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 96 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 104 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 112 */ ,0 ,0x1b ,0x08 ,'\n' ,0 ,0 ,0 ,0 /* 120 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 128 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 136 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 144 */ ,0 ,0 ,'0',0 ,'.','\r','1','2' /* 152 */ ,'3','4','5','6',',','7','8','9' /* 160 */ ,'-',0 ,0 ,0 ,0 ,0 ,0 ,0 /* 168 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 176 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 184 */ ,0 ,0 ,0 ,0 ,0x7f,'\r','\t','`' /* 192 */ ,'1' ,'q' ,'a' ,'z' ,0 ,'2' ,'w' ,'s' /* 200 */ ,'x' ,'<' ,0 ,'3' ,'e' ,'d' ,'c' ,0 /* 208 */ ,'4' ,'r' ,'f' ,'v' ,' ' ,0 ,'5' ,'t' /* 216 */ ,'g' ,'b' ,0 ,'6' ,'y' ,'h' ,'n' ,0 /* 224 */ ,'7' ,'u' ,'j' ,'m' ,0 ,'8' ,'i' ,'k' /* 232 */ ,',' ,0 ,'9' ,'o' ,'l' ,'.' ,0 ,'0' /* 240 */ ,'p' ,0 ,';' ,'/' ,0 ,'=' ,']' ,'\\' /* 248 */ ,0 ,'-' ,'[' ,'\'' ,0 ,0 ,0 ,0 }; unsigned char lk201_xlate_shifted[] = { /* 86 */ 0 ,0 /* 88 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 96 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 104 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 112 */ ,0 ,0x1b ,0x08 ,'\n' ,0 ,0 ,0 ,0 /* 120 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 128 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 136 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 144 */ ,0 ,0 ,'0',0 ,'.','\r','1','2' /* 152 */ ,'3','4','5','6',',','7','8','9' /* 160 */ ,'-',0 ,0 ,0 ,0 ,0 ,0 ,0 /* 168 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 176 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 184 */ ,0 ,0 ,0 ,0 ,0x7f ,'\r' ,'\t' ,'~' /* 192 */ ,'!' ,'Q' ,'A' ,'Z' ,0 ,'@' ,'W' ,'S' /* 200 */ ,'X' ,'>' ,0 ,'#' ,'E' ,'D' ,'C' ,0 /* 208 */ ,'$' ,'R' ,'F' ,'V' ,' ' ,0 ,'%' ,'T' /* 216 */ ,'G' ,'B' ,0 ,'^' ,'Y' ,'H' ,'N' ,0 /* 224 */ ,'&' ,'U' ,'J' ,'M' ,0 ,'*' ,'I' ,'K' /* 232 */ ,'<' ,0 ,'(' ,'O' ,'L' ,'>' ,0 ,')' /* 240 */ ,'P' ,0 ,':' ,'?' ,0 ,'+' ,'}' ,'|' /* 248 */ ,0 ,'_' ,'{' ,'"' ,0 ,0 ,0 ,0 }; unsigned char lk201_xlate_meta[] = { /* 86 */ 0 ,0 /* 88 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 96 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 104 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 112 */ ,0 ,0x1b ,0x08 ,'\n' ,0 ,0 ,0 ,0 /* 120 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 128 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 136 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 144 */ ,0 ,0 ,'0',0 ,'.','\r','1','2' /* 152 */ ,'3','4','5','6',',','7','8','9' /* 160 */ ,'-',0 ,0 ,0 ,0 ,0 ,0 ,0 /* 168 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 176 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 184 */ ,0 ,0 ,0 ,0 ,0x7f ,'\r' ,'\t' ,'~' /* 192 */ ,'!' ,'Q' ,'A' ,'Z' ,0 ,'@' ,'W' ,'S' /* 200 */ ,'X' ,'>' ,0 ,'#' ,'E' ,'D' ,'C' ,0 /* 208 */ ,'$' ,'R' ,'F' ,'V' ,' ' ,0 ,'%' ,'T' /* 216 */ ,'G' ,'B' ,0 ,'^' ,'Y' ,'H' ,'N' ,0 /* 224 */ ,'&' ,'U' ,'J' ,'M' ,0 ,'*' ,'I' ,'K' /* 232 */ ,'<' ,0 ,'(' ,'O' ,'L' ,'>' ,0 ,')' /* 240 */ ,'P' ,0 ,':' ,'?' ,0 ,'+' ,'}' ,'|' /* 248 */ ,0 ,'_' ,'{' ,'"' ,0 ,0 ,0 ,0 }; unsigned char lk201_xlate_shifted_meta[] = { /* 86 */ 0 ,0 /* 88 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 96 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 104 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 112 */ ,0 ,0x1b ,0x08 ,'\n' ,0 ,0 ,0 ,0 /* 120 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 128 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 136 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 144 */ ,0 ,0 ,'0',0 ,'.','\r','1','2' /* 152 */ ,'3','4','5','6',',','7','8','9' /* 160 */ ,'-',0 ,0 ,0 ,0 ,0 ,0 ,0 /* 168 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 176 */ ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 /* 184 */ ,0 ,0 ,0 ,0 ,0x7f ,'\r' ,'\t' ,'~' /* 192 */ ,'!' ,'Q' ,'A' ,'Z' ,0 ,'@' ,'W' ,'S' /* 200 */ ,'X' ,'>' ,0 ,'#' ,'E' ,'D' ,'C' ,0 /* 208 */ ,'$' ,'R' ,'F' ,'V' ,' ' ,0 ,'%' ,'T' /* 216 */ ,'G' ,'B' ,0 ,'^' ,'Y' ,'H' ,'N' ,0 /* 224 */ ,'&' ,'U' ,'J' ,'M' ,0 ,'*' ,'I' ,'K' /* 232 */ ,'<' ,0 ,'(' ,'O' ,'L' ,'>' ,0 ,')' /* 240 */ ,'P' ,0 ,':' ,'?' ,0 ,'+' ,'}' ,'|' /* 248 */ ,0 ,'_' ,'{' ,'"' ,0 ,0 ,0 ,0 }; io_return_t lk201_translation( int key, char c, int tabcode ) { unsigned char *table; if ((key &= 0xff) < LK_MINCODE) return D_INVALID_OPERATION; switch (tabcode) { case 3: table = lk201_xlate_shifted_meta; break; case 2: table = lk201_xlate_meta; break; case 1: table = lk201_xlate_shifted; break; case 0: default: table = lk201_xlate_key; break; } table[key - LK_MINCODE] = c; return D_SUCCESS; } /* * Input character processing */ lk201_rint( int unit, unsigned short data, boolean_t handle_shift, boolean_t from_kernel) { int c; lk201_kbd_state_t *kbd = &lk201_softc[unit]->kbd; /* * Keyboard touched, clean char to 8 bits. */ #if NBM > 0 ssaver_bump(unit); #endif data &= 0xff; /* Translate keycode into ASCII */ if ((c = lk201_input(unit, data)) == -1) return -1; #if NBM > 0 /* * Notify X, unless we are called from inside kernel */ if (!from_kernel && screen_keypress_event(unit, DEV_KEYBD, data, EVT_BUTTON_RAW)) return -1; #endif /* Handle shifting if need to */ if (kbd->kbd_gen_shift) return (handle_shift) ? cngetc() : -1; return c; } /* * Routine to grock a character from LK201 */ #if MACH_KDB int lk201_allow_kdb = 1; #endif int lk201_debug = 0; lk201_input( int unit, unsigned short data) { int c, sl; lk201_kbd_state_t *kbd = &lk201_softc[unit]->kbd; kbd->kbd_gen_shift = 0; #if MACH_KDB if (lk201_allow_kdb && (data == LK_DO)) { kdb_kintr(); return -2; } #endif /* * Sanity checks */ if (data == LK_INPUT_ERR || data == LK_OUTPUT_ERR) { printf(" Keyboard error, code = %x\n",data); return -1; } if (data < LK_MINCODE) return -1; /* * Check special keys: shifts, ups, .. */ if (data == LK_LOCK && (kbd->kbd_flags&mapLOCKtoCNTRL)) data = LK_CNTRL; switch (data) { case LK_LOCK: kbd->kbd_lock ^= 1; kbd->kbd_gen_shift = 1; sl = lk201_softc[unit]->sl_unit; /* called from interrupt, no need for spl */ if (kbd->kbd_lock) (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_CMD_LEDS_ON); else (*console_putc)(sl,SCREEN_LINE_KEYBOARD, LK_CMD_LEDS_OFF); (*console_putc)(sl, SCREEN_LINE_KEYBOARD, LK_PARAM_LED_MASK(0x4)); return 0; case LK_ALT: case LK_L_ALT: case LK_R_ALT: case LK_R_COMPOSE: kbd->kbd_meta ^= 1; kbd->kbd_gen_shift = 1; return 0; case LK_SHIFT: case LK_R_SHIFT: kbd->kbd_shift ^= 1; kbd->kbd_gen_shift = 1; return 0; case LK_CNTRL: kbd->kbd_ctrl ^= 1; kbd->kbd_gen_shift = 1; return 0; case LK_ALLUP: kbd->kbd_ctrl = 0; kbd->kbd_shift = 0; kbd->kbd_meta = 0; kbd->kbd_gen_shift = 1; return 0; case LK_REPEAT: c = kbd->kbd_previous; break; default: /* * Do the key translation to ASCII */ if (kbd->kbd_ctrl || kbd->kbd_lock || kbd->kbd_shift) { c = ((kbd->kbd_meta) ? lk201_xlate_shifted_meta : lk201_xlate_shifted) [data - LK_MINCODE]; if (kbd->kbd_ctrl) c &= 0x1f; } else c = ((kbd->kbd_meta) ? lk201_xlate_meta : lk201_xlate_key) [data-LK_MINCODE]; break; } kbd->kbd_previous = c; /* * DEBUG code DEBUG */ if (lk201_debug && (c == 0)) { printf("lk201: [%x]\n", data); } return c; } #endif /* NLK > 0 */