19c1d3970SDavid du Colombier /*
29c1d3970SDavid du Colombier * keyboard input
39c1d3970SDavid du Colombier */
43e12c5d1SDavid du Colombier #include "u.h"
53e12c5d1SDavid du Colombier #include "../port/lib.h"
63e12c5d1SDavid du Colombier #include "mem.h"
73e12c5d1SDavid du Colombier #include "dat.h"
83e12c5d1SDavid du Colombier #include "fns.h"
93e12c5d1SDavid du Colombier #include "io.h"
10bd389b36SDavid du Colombier #include "../port/error.h"
113e12c5d1SDavid du Colombier
123e12c5d1SDavid du Colombier enum {
133e12c5d1SDavid du Colombier Data= 0x60, /* data port */
143e12c5d1SDavid du Colombier
153e12c5d1SDavid du Colombier Status= 0x64, /* status port */
163e12c5d1SDavid du Colombier Inready= 0x01, /* input character ready */
173e12c5d1SDavid du Colombier Outbusy= 0x02, /* output busy */
183e12c5d1SDavid du Colombier Sysflag= 0x04, /* system flag */
193e12c5d1SDavid du Colombier Cmddata= 0x08, /* cmd==0, data==1 */
203e12c5d1SDavid du Colombier Inhibit= 0x10, /* keyboard/mouse inhibited */
213e12c5d1SDavid du Colombier Minready= 0x20, /* mouse character ready */
223e12c5d1SDavid du Colombier Rtimeout= 0x40, /* general timeout */
233e12c5d1SDavid du Colombier Parity= 0x80,
243e12c5d1SDavid du Colombier
253e12c5d1SDavid du Colombier Cmd= 0x64, /* command port (write only) */
263e12c5d1SDavid du Colombier
27c49c9d4eSDavid du Colombier Spec= 0xF800, /* Unicode private space */
283e12c5d1SDavid du Colombier PF= Spec|0x20, /* num pad function key */
293e12c5d1SDavid du Colombier View= Spec|0x00, /* view (shift window up) */
307dd7cddfSDavid du Colombier KF= 0xF000, /* function key (begin Unicode private space) */
313e12c5d1SDavid du Colombier Shift= Spec|0x60,
323e12c5d1SDavid du Colombier Break= Spec|0x61,
333e12c5d1SDavid du Colombier Ctrl= Spec|0x62,
343e12c5d1SDavid du Colombier Latin= Spec|0x63,
353e12c5d1SDavid du Colombier Caps= Spec|0x64,
363e12c5d1SDavid du Colombier Num= Spec|0x65,
373e12c5d1SDavid du Colombier Middle= Spec|0x66,
383901e6d0SDavid du Colombier Altgr= Spec|0x67,
39e29df7b0SDavid du Colombier Kmouse= Spec|0x100,
403e12c5d1SDavid du Colombier No= 0x00, /* peter */
413e12c5d1SDavid du Colombier
423e12c5d1SDavid du Colombier Home= KF|13,
433e12c5d1SDavid du Colombier Up= KF|14,
443e12c5d1SDavid du Colombier Pgup= KF|15,
453e12c5d1SDavid du Colombier Print= KF|16,
467dd7cddfSDavid du Colombier Left= KF|17,
477dd7cddfSDavid du Colombier Right= KF|18,
48651dbb72SDavid du Colombier End= KF|24,
493e12c5d1SDavid du Colombier Down= View,
507dd7cddfSDavid du Colombier Pgdown= KF|19,
513e12c5d1SDavid du Colombier Ins= KF|20,
523e12c5d1SDavid du Colombier Del= 0x7F,
537dd7cddfSDavid du Colombier Scroll= KF|21,
543901e6d0SDavid du Colombier
553901e6d0SDavid du Colombier Nscan= 128,
56d5b6fab5SDavid du Colombier
57d5b6fab5SDavid du Colombier Int= 0, /* kbscans indices */
58d5b6fab5SDavid du Colombier Ext,
59d5b6fab5SDavid du Colombier Nscans,
603e12c5d1SDavid du Colombier };
613e12c5d1SDavid du Colombier
627dd7cddfSDavid du Colombier /*
6326d1d1dfSDavid du Colombier * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
647dd7cddfSDavid du Colombier * A 'standard' keyboard doesn't produce anything above 0x58.
657dd7cddfSDavid du Colombier */
663901e6d0SDavid du Colombier Rune kbtab[Nscan] =
673e12c5d1SDavid du Colombier {
683e12c5d1SDavid du Colombier [0x00] No, 0x1b, '1', '2', '3', '4', '5', '6',
693e12c5d1SDavid du Colombier [0x08] '7', '8', '9', '0', '-', '=', '\b', '\t',
703e12c5d1SDavid du Colombier [0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
713e12c5d1SDavid du Colombier [0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's',
723e12c5d1SDavid du Colombier [0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
733e12c5d1SDavid du Colombier [0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v',
743e12c5d1SDavid du Colombier [0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*',
753e12c5d1SDavid du Colombier [0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
767dd7cddfSDavid du Colombier [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
773e12c5d1SDavid du Colombier [0x48] '8', '9', '-', '4', '5', '6', '+', '1',
787dd7cddfSDavid du Colombier [0x50] '2', '3', '0', '.', No, No, No, KF|11,
793e12c5d1SDavid du Colombier [0x58] KF|12, No, No, No, No, No, No, No,
807dd7cddfSDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
817dd7cddfSDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
827dd7cddfSDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
837dd7cddfSDavid du Colombier [0x78] No, View, No, Up, No, No, No, No,
843e12c5d1SDavid du Colombier };
853e12c5d1SDavid du Colombier
863901e6d0SDavid du Colombier Rune kbtabshift[Nscan] =
873e12c5d1SDavid du Colombier {
883e12c5d1SDavid du Colombier [0x00] No, 0x1b, '!', '@', '#', '$', '%', '^',
893e12c5d1SDavid du Colombier [0x08] '&', '*', '(', ')', '_', '+', '\b', '\t',
903e12c5d1SDavid du Colombier [0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
913e12c5d1SDavid du Colombier [0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S',
923e12c5d1SDavid du Colombier [0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
933e12c5d1SDavid du Colombier [0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V',
943e12c5d1SDavid du Colombier [0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*',
953e12c5d1SDavid du Colombier [0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
967dd7cddfSDavid du Colombier [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
973e12c5d1SDavid du Colombier [0x48] '8', '9', '-', '4', '5', '6', '+', '1',
983e12c5d1SDavid du Colombier [0x50] '2', '3', '0', '.', No, No, No, KF|11,
993e12c5d1SDavid du Colombier [0x58] KF|12, No, No, No, No, No, No, No,
1007dd7cddfSDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
1017dd7cddfSDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
1027dd7cddfSDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
1037dd7cddfSDavid du Colombier [0x78] No, Up, No, Up, No, No, No, No,
1043e12c5d1SDavid du Colombier };
1053e12c5d1SDavid du Colombier
1063901e6d0SDavid du Colombier Rune kbtabesc1[Nscan] =
1073e12c5d1SDavid du Colombier {
1083e12c5d1SDavid du Colombier [0x00] No, No, No, No, No, No, No, No,
1093e12c5d1SDavid du Colombier [0x08] No, No, No, No, No, No, No, No,
1103e12c5d1SDavid du Colombier [0x10] No, No, No, No, No, No, No, No,
1113e12c5d1SDavid du Colombier [0x18] No, No, No, No, '\n', Ctrl, No, No,
1123e12c5d1SDavid du Colombier [0x20] No, No, No, No, No, No, No, No,
1133e12c5d1SDavid du Colombier [0x28] No, No, Shift, No, No, No, No, No,
1143e12c5d1SDavid du Colombier [0x30] No, No, No, No, No, '/', No, Print,
1153901e6d0SDavid du Colombier [0x38] Altgr, No, No, No, No, No, No, No,
1163e12c5d1SDavid du Colombier [0x40] No, No, No, No, No, No, Break, Home,
1173e12c5d1SDavid du Colombier [0x48] Up, Pgup, No, Left, No, Right, No, End,
1183e12c5d1SDavid du Colombier [0x50] Down, Pgdown, Ins, Del, No, No, No, No,
1193e12c5d1SDavid du Colombier [0x58] No, No, No, No, No, No, No, No,
1207dd7cddfSDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
1217dd7cddfSDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
1227dd7cddfSDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
1237dd7cddfSDavid du Colombier [0x78] No, Up, No, No, No, No, No, No,
1243e12c5d1SDavid du Colombier };
1253e12c5d1SDavid du Colombier
1263901e6d0SDavid du Colombier Rune kbtabaltgr[Nscan] =
1273901e6d0SDavid du Colombier {
1283901e6d0SDavid du Colombier [0x00] No, No, No, No, No, No, No, No,
1293901e6d0SDavid du Colombier [0x08] No, No, No, No, No, No, No, No,
1303901e6d0SDavid du Colombier [0x10] No, No, No, No, No, No, No, No,
131c49c9d4eSDavid du Colombier [0x18] No, No, No, No, '\n', Ctrl, No, No,
1323901e6d0SDavid du Colombier [0x20] No, No, No, No, No, No, No, No,
133c49c9d4eSDavid du Colombier [0x28] No, No, Shift, No, No, No, No, No,
134c49c9d4eSDavid du Colombier [0x30] No, No, No, No, No, '/', No, Print,
135c49c9d4eSDavid du Colombier [0x38] Altgr, No, No, No, No, No, No, No,
136c49c9d4eSDavid du Colombier [0x40] No, No, No, No, No, No, Break, Home,
137c49c9d4eSDavid du Colombier [0x48] Up, Pgup, No, Left, No, Right, No, End,
138c49c9d4eSDavid du Colombier [0x50] Down, Pgdown, Ins, Del, No, No, No, No,
1393901e6d0SDavid du Colombier [0x58] No, No, No, No, No, No, No, No,
1403901e6d0SDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
1413901e6d0SDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
1423901e6d0SDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
1433901e6d0SDavid du Colombier [0x78] No, Up, No, No, No, No, No, No,
1443901e6d0SDavid du Colombier };
1453901e6d0SDavid du Colombier
14683030dd5SDavid du Colombier Rune kbtabctrl[Nscan] =
1473901e6d0SDavid du Colombier {
1483901e6d0SDavid du Colombier [0x00] No, '', '', '', '', '', '', '',
1493901e6d0SDavid du Colombier [0x08] '', '', '', '', '
1503901e6d0SDavid du Colombier ', '', '\b', '\t',
1513901e6d0SDavid du Colombier [0x10] '', '', '', '', '', '', '', '\t',
1523901e6d0SDavid du Colombier [0x18] '', '', '', '', '\n', Ctrl, '', '',
1533901e6d0SDavid du Colombier [0x20] '', '', '', '\b', '\n', '', '', '',
1543901e6d0SDavid du Colombier [0x28] '', No, Shift, '', '', '', '', '',
1553901e6d0SDavid du Colombier [0x30] '', '', '
1563901e6d0SDavid du Colombier ', '', '', '', Shift, '\n',
1573901e6d0SDavid du Colombier [0x38] Latin, No, Ctrl, '', '', '', '', '',
1583901e6d0SDavid du Colombier [0x40] '', '', '', '
1593901e6d0SDavid du Colombier ', '', '', '', '',
1603901e6d0SDavid du Colombier [0x48] '', '', '
1613901e6d0SDavid du Colombier ', '', '', '', '', '',
1623901e6d0SDavid du Colombier [0x50] '', '', '', '', No, No, No, '',
1633901e6d0SDavid du Colombier [0x58] '', No, No, No, No, No, No, No,
1643901e6d0SDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
1653901e6d0SDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
1663e12c5d1SDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
1673e12c5d1SDavid du Colombier [0x78] No, '', No, '\b', No, No, No, No,
168bd389b36SDavid du Colombier };
169bd389b36SDavid du Colombier
1707dd7cddfSDavid du Colombier enum
171bd389b36SDavid du Colombier {
172bd389b36SDavid du Colombier /* controller command byte */
1737dd7cddfSDavid du Colombier Cscs1= (1<<6), /* scan code set 1 */
174bd389b36SDavid du Colombier Cauxdis= (1<<5), /* mouse disable */
1753e12c5d1SDavid du Colombier Ckbddis= (1<<4), /* kbd disable */
176bd389b36SDavid du Colombier Csf= (1<<2), /* system flag */
1773ff48bf5SDavid du Colombier Cauxint= (1<<1), /* mouse interrupt enable */
1783039af76SDavid du Colombier Ckbdint= (1<<0), /* kbd interrupt enable */
1793ff48bf5SDavid du Colombier };
1807dd7cddfSDavid du Colombier
1817dd7cddfSDavid du Colombier int mouseshifted;
1827dd7cddfSDavid du Colombier void (*kbdmouse)(int);
183d5b6fab5SDavid du Colombier
1843e12c5d1SDavid du Colombier static Lock i8042lock;
1853e12c5d1SDavid du Colombier static uchar ccc;
1863e12c5d1SDavid du Colombier static void (*auxputc)(int, int);
1873e12c5d1SDavid du Colombier static int nokbd = 1; /* flag: no PS/2 keyboard */
1883e12c5d1SDavid du Colombier
outready(void)1893e12c5d1SDavid du Colombier /*
1903e12c5d1SDavid du Colombier * wait for output no longer busy
1913e12c5d1SDavid du Colombier */
1923e12c5d1SDavid du Colombier static int
193bd389b36SDavid du Colombier outready(void)
194bd389b36SDavid du Colombier {
1953e12c5d1SDavid du Colombier int tries;
196bd389b36SDavid du Colombier
197bd389b36SDavid du Colombier for(tries = 0; (inb(Status) & Outbusy); tries++){
1983e12c5d1SDavid du Colombier if(tries > 500)
1993e12c5d1SDavid du Colombier return -1;
2003e12c5d1SDavid du Colombier delay(2);
2013e12c5d1SDavid du Colombier }
2023e12c5d1SDavid du Colombier return 0;
2033e12c5d1SDavid du Colombier }
2043e12c5d1SDavid du Colombier
inready(void)2053e12c5d1SDavid du Colombier /*
2063e12c5d1SDavid du Colombier * wait for input
2073e12c5d1SDavid du Colombier */
2083e12c5d1SDavid du Colombier static int
209bd389b36SDavid du Colombier inready(void)
210bd389b36SDavid du Colombier {
2113e12c5d1SDavid du Colombier int tries;
212bd389b36SDavid du Colombier
213bd389b36SDavid du Colombier for(tries = 0; !(inb(Status) & Inready); tries++){
2143e12c5d1SDavid du Colombier if(tries > 500)
2153e12c5d1SDavid du Colombier return -1;
2163e12c5d1SDavid du Colombier delay(2);
2173e12c5d1SDavid du Colombier }
2181045bea1SDavid du Colombier return 0;
2191045bea1SDavid du Colombier }
2201045bea1SDavid du Colombier
i8042a20(void)2211045bea1SDavid du Colombier /*
2221045bea1SDavid du Colombier * ask 8042 to enable the use of address bit 20
2231045bea1SDavid du Colombier */
2241045bea1SDavid du Colombier void
2251045bea1SDavid du Colombier i8042a20(void)
2261045bea1SDavid du Colombier {
2271045bea1SDavid du Colombier outready();
2281045bea1SDavid du Colombier outb(Cmd, 0xD1);
2291045bea1SDavid du Colombier outready();
2301045bea1SDavid du Colombier outb(Data, 0xDF);
2313e12c5d1SDavid du Colombier outready();
2323e12c5d1SDavid du Colombier }
2333e12c5d1SDavid du Colombier
i8042reset(void)2343e12c5d1SDavid du Colombier /*
2353e12c5d1SDavid du Colombier * ask 8042 to reset the machine
236219b2ee8SDavid du Colombier */
237219b2ee8SDavid du Colombier void
238c3ca4b29SDavid du Colombier i8042reset(void)
239c3ca4b29SDavid du Colombier {
240c3ca4b29SDavid du Colombier int i, x;
241c3ca4b29SDavid du Colombier
242219b2ee8SDavid du Colombier if(nokbd)
243bd389b36SDavid du Colombier return;
2447dd7cddfSDavid du Colombier
245bd389b36SDavid du Colombier *((ushort*)KADDR(0x472)) = 0x1234; /* BIOS warm-boot flag */
246bd389b36SDavid du Colombier
2477dd7cddfSDavid du Colombier /*
248bd389b36SDavid du Colombier * newer reset the machine command
249219b2ee8SDavid du Colombier */
250bd389b36SDavid du Colombier outready();
251219b2ee8SDavid du Colombier outb(Cmd, 0xFE);
252bd389b36SDavid du Colombier outready();
253219b2ee8SDavid du Colombier
254219b2ee8SDavid du Colombier /*
255219b2ee8SDavid du Colombier * Pulse it by hand (old somewhat reliable)
2563e12c5d1SDavid du Colombier */
2573e12c5d1SDavid du Colombier x = 0xDF;
2583e12c5d1SDavid du Colombier for(i = 0; i < 5; i++){
259219b2ee8SDavid du Colombier x ^= 1;
260219b2ee8SDavid du Colombier outready();
2613e12c5d1SDavid du Colombier outb(Cmd, 0xD1);
262219b2ee8SDavid du Colombier outready();
263219b2ee8SDavid du Colombier outb(Data, x); /* toggle reset */
2647dd7cddfSDavid du Colombier delay(100);
2657dd7cddfSDavid du Colombier }
2663e12c5d1SDavid du Colombier }
2677dd7cddfSDavid du Colombier
2687dd7cddfSDavid du Colombier int
2695acbe002SDavid du Colombier i8042auxcmd(int cmd)
2703e12c5d1SDavid du Colombier {
2715acbe002SDavid du Colombier unsigned int c;
2725acbe002SDavid du Colombier int tries;
2737dd7cddfSDavid du Colombier static int badkbd;
2747dd7cddfSDavid du Colombier
2753e12c5d1SDavid du Colombier if(badkbd)
2767dd7cddfSDavid du Colombier return -1;
2777dd7cddfSDavid du Colombier c = 0;
2787dd7cddfSDavid du Colombier tries = 0;
2797dd7cddfSDavid du Colombier
280bd389b36SDavid du Colombier ilock(&i8042lock);
2817dd7cddfSDavid du Colombier do{
2827dd7cddfSDavid du Colombier if(tries++ > 2)
2833e12c5d1SDavid du Colombier break;
2847dd7cddfSDavid du Colombier if(outready() < 0)
2857dd7cddfSDavid du Colombier break;
2867dd7cddfSDavid du Colombier outb(Cmd, 0xD4);
2877dd7cddfSDavid du Colombier if(outready() < 0)
2887dd7cddfSDavid du Colombier break;
2897dd7cddfSDavid du Colombier outb(Data, cmd);
2907dd7cddfSDavid du Colombier if(outready() < 0)
2917dd7cddfSDavid du Colombier break;
2927dd7cddfSDavid du Colombier if(inready() < 0)
2933e12c5d1SDavid du Colombier break;
2947dd7cddfSDavid du Colombier c = inb(Data);
2957dd7cddfSDavid du Colombier } while(c == 0xFE || c == 0);
2965acbe002SDavid du Colombier iunlock(&i8042lock);
297219b2ee8SDavid du Colombier
2983e12c5d1SDavid du Colombier if(c != 0xFA){
299bd389b36SDavid du Colombier print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
300bd389b36SDavid du Colombier badkbd = 1; /* don't keep trying; there might not be one */
301bd389b36SDavid du Colombier return -1;
3023ff48bf5SDavid du Colombier }
i8042auxcmds(uchar * cmd,int ncmd)3033ff48bf5SDavid du Colombier return 0;
3043ff48bf5SDavid du Colombier }
3053ff48bf5SDavid du Colombier
3063ff48bf5SDavid du Colombier int
3073ff48bf5SDavid du Colombier i8042auxcmds(uchar *cmd, int ncmd)
3083ff48bf5SDavid du Colombier {
3093ff48bf5SDavid du Colombier int i;
3103ff48bf5SDavid du Colombier
3113ff48bf5SDavid du Colombier ilock(&i8042lock);
3123ff48bf5SDavid du Colombier for(i=0; i<ncmd; i++){
3133ff48bf5SDavid du Colombier if(outready() < 0)
3143ff48bf5SDavid du Colombier break;
3153ff48bf5SDavid du Colombier outb(Cmd, 0xD4);
3163ff48bf5SDavid du Colombier if(outready() < 0)
3173ff48bf5SDavid du Colombier break;
3183ff48bf5SDavid du Colombier outb(Data, cmd[i]);
3193ff48bf5SDavid du Colombier }
3209c1d3970SDavid du Colombier iunlock(&i8042lock);
3219c1d3970SDavid du Colombier return i;
322e29df7b0SDavid du Colombier }
323e29df7b0SDavid du Colombier
324e29df7b0SDavid du Colombier typedef struct Kbscan Kbscan;
325e29df7b0SDavid du Colombier struct Kbscan {
326e29df7b0SDavid du Colombier int esc1;
327e29df7b0SDavid du Colombier int esc2;
328e29df7b0SDavid du Colombier int alt;
329e29df7b0SDavid du Colombier int altgr;
330e29df7b0SDavid du Colombier int caps;
331e29df7b0SDavid du Colombier int ctl;
332e29df7b0SDavid du Colombier int num;
3333039af76SDavid du Colombier int shift;
3349c1d3970SDavid du Colombier int collecting;
3359c1d3970SDavid du Colombier int nk;
336d5b6fab5SDavid du Colombier Rune kc[5];
337d5b6fab5SDavid du Colombier int buttons;
33883030dd5SDavid du Colombier };
339d5b6fab5SDavid du Colombier
340d5b6fab5SDavid du Colombier Kbscan kbscans[Nscans]; /* kernel and external scan code state */
341d5b6fab5SDavid du Colombier
342d5b6fab5SDavid du Colombier static int kdebug;
343d5b6fab5SDavid du Colombier
344d5b6fab5SDavid du Colombier /*
345d5b6fab5SDavid du Colombier * set keyboard's leds for lock states (scroll, numeric, caps).
346d5b6fab5SDavid du Colombier *
347d5b6fab5SDavid du Colombier * at least one keyboard (from Qtronics) also sets its numeric-lock
348d5b6fab5SDavid du Colombier * behaviour to match the led state, though it has no numeric keypad,
349d5b6fab5SDavid du Colombier * and some BIOSes bring the system up with numeric-lock set and no
350d5b6fab5SDavid du Colombier * setting to change that. this combination steals the keys for these
setleds(Kbscan * kbscan)351d5b6fab5SDavid du Colombier * characters and makes it impossible to generate them: uiolkjm&*().
352d5b6fab5SDavid du Colombier * thus we'd like to be able to force the numeric-lock led (and behaviour) off.
353d5b6fab5SDavid du Colombier */
354d5b6fab5SDavid du Colombier static void
355d5b6fab5SDavid du Colombier setleds(Kbscan *kbscan)
356d5b6fab5SDavid du Colombier {
357d5b6fab5SDavid du Colombier int leds;
358d5b6fab5SDavid du Colombier
359d5b6fab5SDavid du Colombier if(nokbd || kbscan != &kbscans[Int])
360d5b6fab5SDavid du Colombier return;
361d5b6fab5SDavid du Colombier leds = 0;
36255a30970SDavid du Colombier if(kbscan->num)
363d5b6fab5SDavid du Colombier leds |= 1<<1;
364d5b6fab5SDavid du Colombier if(0 && kbscan->caps) /* we don't implement caps lock */
365d5b6fab5SDavid du Colombier leds |= 1<<2;
36655a30970SDavid du Colombier
36755a30970SDavid du Colombier ilock(&i8042lock);
36855a30970SDavid du Colombier outready();
369d5b6fab5SDavid du Colombier outb(Data, 0xed); /* `reset keyboard lock states' */
370d5b6fab5SDavid du Colombier if(inready() == 0)
37155a30970SDavid du Colombier inb(Data);
37255a30970SDavid du Colombier
37355a30970SDavid du Colombier outready();
374d5b6fab5SDavid du Colombier outb(Data, leds);
375d5b6fab5SDavid du Colombier if(inready() == 0)
376d5b6fab5SDavid du Colombier inb(Data);
377d5b6fab5SDavid du Colombier
3789c1d3970SDavid du Colombier outready();
3799c1d3970SDavid du Colombier iunlock(&i8042lock);
3809c1d3970SDavid du Colombier }
3819c1d3970SDavid du Colombier
kbdputsc(int c,int external)3829c1d3970SDavid du Colombier /*
3839c1d3970SDavid du Colombier * Scan code processing
3849c1d3970SDavid du Colombier */
3859c1d3970SDavid du Colombier void
3869c1d3970SDavid du Colombier kbdputsc(int c, int external)
3879c1d3970SDavid du Colombier {
388d5b6fab5SDavid du Colombier int i, keyup;
3899c1d3970SDavid du Colombier Kbscan *kbscan;
390d5b6fab5SDavid du Colombier
3919c1d3970SDavid du Colombier if(external)
39283030dd5SDavid du Colombier kbscan = &kbscans[Ext];
39383030dd5SDavid du Colombier else
3949c1d3970SDavid du Colombier kbscan = &kbscans[Int];
3959c1d3970SDavid du Colombier
3969c1d3970SDavid du Colombier if(kdebug)
3979c1d3970SDavid du Colombier print("sc %x ms %d\n", c, mouseshifted);
3989c1d3970SDavid du Colombier /*
3999c1d3970SDavid du Colombier * e0's is the first of a 2 character sequence, e1 the first
4009c1d3970SDavid du Colombier * of a 3 character sequence (on the safari)
4019c1d3970SDavid du Colombier */
4029c1d3970SDavid du Colombier if(c == 0xe0){
4039c1d3970SDavid du Colombier kbscan->esc1 = 1;
4049c1d3970SDavid du Colombier return;
4059c1d3970SDavid du Colombier } else if(c == 0xe1){
4069c1d3970SDavid du Colombier kbscan->esc2 = 2;
4079c1d3970SDavid du Colombier return;
4089c1d3970SDavid du Colombier }
4099c1d3970SDavid du Colombier
4109c1d3970SDavid du Colombier keyup = c & 0x80;
4119c1d3970SDavid du Colombier c &= 0x7f;
4129c1d3970SDavid du Colombier if(c > sizeof kbtab){
4139c1d3970SDavid du Colombier c |= keyup;
4149c1d3970SDavid du Colombier if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */
4159c1d3970SDavid du Colombier print("unknown key %ux\n", c);
4169c1d3970SDavid du Colombier return;
4179c1d3970SDavid du Colombier }
4189c1d3970SDavid du Colombier
4199c1d3970SDavid du Colombier if(kbscan->esc1){
4209c1d3970SDavid du Colombier c = kbtabesc1[c];
4219c1d3970SDavid du Colombier kbscan->esc1 = 0;
4229c1d3970SDavid du Colombier } else if(kbscan->esc2){
4239c1d3970SDavid du Colombier kbscan->esc2--;
4249c1d3970SDavid du Colombier return;
4259c1d3970SDavid du Colombier } else if(kbscan->shift)
4269c1d3970SDavid du Colombier c = kbtabshift[c];
4279c1d3970SDavid du Colombier else if(kbscan->altgr)
4289c1d3970SDavid du Colombier c = kbtabaltgr[c];
4299c1d3970SDavid du Colombier else if(kbscan->ctl)
4309c1d3970SDavid du Colombier c = kbtabctrl[c];
4319c1d3970SDavid du Colombier else
4329c1d3970SDavid du Colombier c = kbtab[c];
4339c1d3970SDavid du Colombier
4349c1d3970SDavid du Colombier if(kbscan->caps && c<='z' && c>='a')
4359c1d3970SDavid du Colombier c += 'A' - 'a';
4369c1d3970SDavid du Colombier
4379c1d3970SDavid du Colombier /*
4389c1d3970SDavid du Colombier * keyup only important for shifts
4399c1d3970SDavid du Colombier */
4409c1d3970SDavid du Colombier if(keyup){
4419c1d3970SDavid du Colombier switch(c){
4429c1d3970SDavid du Colombier case Latin:
4439c1d3970SDavid du Colombier kbscan->alt = 0;
44483030dd5SDavid du Colombier break;
44583030dd5SDavid du Colombier case Shift:
4469c1d3970SDavid du Colombier kbscan->shift = 0;
4479c1d3970SDavid du Colombier mouseshifted = 0;
4489c1d3970SDavid du Colombier if(kdebug)
4499c1d3970SDavid du Colombier print("shiftclr\n");
4509c1d3970SDavid du Colombier break;
4519c1d3970SDavid du Colombier case Ctrl:
4529c1d3970SDavid du Colombier kbscan->ctl = 0;
4539c1d3970SDavid du Colombier break;
4549c1d3970SDavid du Colombier case Altgr:
4559c1d3970SDavid du Colombier kbscan->altgr = 0;
4569c1d3970SDavid du Colombier break;
4579c1d3970SDavid du Colombier case Kmouse|1:
4589c1d3970SDavid du Colombier case Kmouse|2:
4599c1d3970SDavid du Colombier case Kmouse|3:
4609c1d3970SDavid du Colombier case Kmouse|4:
4619c1d3970SDavid du Colombier case Kmouse|5:
4629c1d3970SDavid du Colombier kbscan->buttons &= ~(1<<(c-Kmouse-1));
4639c1d3970SDavid du Colombier if(kbdmouse)
4649c1d3970SDavid du Colombier kbdmouse(kbscan->buttons);
4659c1d3970SDavid du Colombier break;
4669c1d3970SDavid du Colombier }
4679c1d3970SDavid du Colombier return;
4689c1d3970SDavid du Colombier }
4699c1d3970SDavid du Colombier
4709c1d3970SDavid du Colombier /*
4719c1d3970SDavid du Colombier * normal character
4729c1d3970SDavid du Colombier */
4739c1d3970SDavid du Colombier if(!(c & (Spec|KF))){
4749c1d3970SDavid du Colombier if(kbscan->ctl)
4759c1d3970SDavid du Colombier if(kbscan->alt && c == Del)
4769c1d3970SDavid du Colombier exit(0);
4779c1d3970SDavid du Colombier if(!kbscan->collecting){
4789c1d3970SDavid du Colombier kbdputc(kbdq, c);
4799c1d3970SDavid du Colombier return;
4809c1d3970SDavid du Colombier }
4819c1d3970SDavid du Colombier kbscan->kc[kbscan->nk++] = c;
4829c1d3970SDavid du Colombier c = latin1(kbscan->kc, kbscan->nk);
4839c1d3970SDavid du Colombier if(c < -1) /* need more keystrokes */
4849c1d3970SDavid du Colombier return;
4859c1d3970SDavid du Colombier if(c != -1) /* valid sequence */
4869c1d3970SDavid du Colombier kbdputc(kbdq, c);
4879c1d3970SDavid du Colombier else /* dump characters */
4889c1d3970SDavid du Colombier for(i=0; i<kbscan->nk; i++)
4899c1d3970SDavid du Colombier kbdputc(kbdq, kbscan->kc[i]);
4909c1d3970SDavid du Colombier kbscan->nk = 0;
4919c1d3970SDavid du Colombier kbscan->collecting = 0;
4929c1d3970SDavid du Colombier return;
4939c1d3970SDavid du Colombier } else {
4949c1d3970SDavid du Colombier switch(c){
4959c1d3970SDavid du Colombier case Caps:
496d5b6fab5SDavid du Colombier kbscan->caps ^= 1;
497d5b6fab5SDavid du Colombier return;
4989c1d3970SDavid du Colombier case Num:
4999c1d3970SDavid du Colombier kbscan->num ^= 1;
5009c1d3970SDavid du Colombier if(!external)
50183030dd5SDavid du Colombier setleds(kbscan);
50283030dd5SDavid du Colombier return;
5039c1d3970SDavid du Colombier case Shift:
5049c1d3970SDavid du Colombier kbscan->shift = 1;
5059c1d3970SDavid du Colombier if(kdebug)
5069c1d3970SDavid du Colombier print("shift\n");
5079c1d3970SDavid du Colombier mouseshifted = 1;
5089c1d3970SDavid du Colombier return;
5099c1d3970SDavid du Colombier case Latin:
5109c1d3970SDavid du Colombier kbscan->alt = 1;
5119c1d3970SDavid du Colombier /*
5129c1d3970SDavid du Colombier * VMware and Qemu use Ctl-Alt as the key combination
5139c1d3970SDavid du Colombier * to make the VM give up keyboard and mouse focus.
5149c1d3970SDavid du Colombier * This has the unfortunate side effect that when you
5159c1d3970SDavid du Colombier * come back into focus, Plan 9 thinks you want to type
5169c1d3970SDavid du Colombier * a compose sequence (you just typed alt).
5179c1d3970SDavid du Colombier *
5189c1d3970SDavid du Colombier * As a clumsy hack around this, we look for ctl-alt
5199c1d3970SDavid du Colombier * and don't treat it as the start of a compose sequence.
5209c1d3970SDavid du Colombier */
5219c1d3970SDavid du Colombier if(!kbscan->ctl){
5229c1d3970SDavid du Colombier kbscan->collecting = 1;
5239c1d3970SDavid du Colombier kbscan->nk = 0;
5249c1d3970SDavid du Colombier }
5259c1d3970SDavid du Colombier return;
5269c1d3970SDavid du Colombier case Ctrl:
5279c1d3970SDavid du Colombier kbscan->ctl = 1;
5289c1d3970SDavid du Colombier return;
5299c1d3970SDavid du Colombier case Altgr:
5309c1d3970SDavid du Colombier kbscan->altgr = 1;
5319c1d3970SDavid du Colombier return;
5329c1d3970SDavid du Colombier case Kmouse|1:
5339c1d3970SDavid du Colombier case Kmouse|2:
5349c1d3970SDavid du Colombier case Kmouse|3:
5359c1d3970SDavid du Colombier case Kmouse|4:
5369c1d3970SDavid du Colombier case Kmouse|5:
53783030dd5SDavid du Colombier kbscan->buttons |= 1<<(c-Kmouse-1);
538d5b6fab5SDavid du Colombier if(kbdmouse)
53983030dd5SDavid du Colombier kbdmouse(kbscan->buttons);
54083030dd5SDavid du Colombier return;
54183030dd5SDavid du Colombier case KF|11:
54283030dd5SDavid du Colombier print("kbd debug on, F12 turns it off\n");
54383030dd5SDavid du Colombier kdebug = 1;
5449c1d3970SDavid du Colombier break;
5459c1d3970SDavid du Colombier case KF|12:
5469c1d3970SDavid du Colombier kdebug = 0;
5479c1d3970SDavid du Colombier break;
548e29df7b0SDavid du Colombier }
549bd389b36SDavid du Colombier }
5503e12c5d1SDavid du Colombier kbdputc(kbdq, c);
5513e12c5d1SDavid du Colombier }
5527dd7cddfSDavid du Colombier
i8042intr(Ureg *,void *)5537dd7cddfSDavid du Colombier /*
5543e12c5d1SDavid du Colombier * keyboard interrupt
5559c1d3970SDavid du Colombier */
5563e12c5d1SDavid du Colombier static void
5573e12c5d1SDavid du Colombier i8042intr(Ureg*, void*)
5583e12c5d1SDavid du Colombier {
5593e12c5d1SDavid du Colombier int s, c;
560b8d70f5aSDavid du Colombier
5613e12c5d1SDavid du Colombier /*
5627dd7cddfSDavid du Colombier * get status
563b8d70f5aSDavid du Colombier */
564219b2ee8SDavid du Colombier ilock(&i8042lock);
5657dd7cddfSDavid du Colombier s = inb(Status);
5663e12c5d1SDavid du Colombier if(!(s&Inready)){
5673e12c5d1SDavid du Colombier iunlock(&i8042lock);
5683e12c5d1SDavid du Colombier return;
5693e12c5d1SDavid du Colombier }
5703e12c5d1SDavid du Colombier
571b8d70f5aSDavid du Colombier /*
5723e12c5d1SDavid du Colombier * get the character
5733e12c5d1SDavid du Colombier */
5747dd7cddfSDavid du Colombier c = inb(Data);
5753e12c5d1SDavid du Colombier iunlock(&i8042lock);
5763e12c5d1SDavid du Colombier
5777dd7cddfSDavid du Colombier /*
578d5b6fab5SDavid du Colombier * if it's the aux port...
579219b2ee8SDavid du Colombier */
5803e12c5d1SDavid du Colombier if(s & Minready){
5813e12c5d1SDavid du Colombier if(auxputc != nil)
582d5b6fab5SDavid du Colombier auxputc(c, kbscans[Int].shift);
5833e12c5d1SDavid du Colombier return;
5843e12c5d1SDavid du Colombier }
5853e12c5d1SDavid du Colombier
5867dd7cddfSDavid du Colombier kbdputsc(c, Int);
5873e12c5d1SDavid du Colombier }
5887dd7cddfSDavid du Colombier
589219b2ee8SDavid du Colombier void
5907dd7cddfSDavid du Colombier i8042auxenable(void (*putc)(int, int))
5917dd7cddfSDavid du Colombier {
5927dd7cddfSDavid du Colombier char *err = "i8042: aux init failed\n";
5937dd7cddfSDavid du Colombier
5947dd7cddfSDavid du Colombier /* enable kbd/aux xfers and interrupts */
5957dd7cddfSDavid du Colombier ccc &= ~Cauxdis;
5967dd7cddfSDavid du Colombier ccc |= Cauxint;
5977dd7cddfSDavid du Colombier
5987dd7cddfSDavid du Colombier ilock(&i8042lock);
5997dd7cddfSDavid du Colombier if(outready() < 0)
6007dd7cddfSDavid du Colombier print(err);
6017dd7cddfSDavid du Colombier outb(Cmd, 0x60); /* write control register */
6027dd7cddfSDavid du Colombier if(outready() < 0)
60326d1d1dfSDavid du Colombier print(err);
6047dd7cddfSDavid du Colombier outb(Data, ccc);
6057dd7cddfSDavid du Colombier if(outready() < 0)
606219b2ee8SDavid du Colombier print(err);
6077dd7cddfSDavid du Colombier outb(Cmd, 0xA8); /* auxiliary device enable */
6087dd7cddfSDavid du Colombier if(outready() < 0){
6097dd7cddfSDavid du Colombier iunlock(&i8042lock);
6107dd7cddfSDavid du Colombier return;
6117dd7cddfSDavid du Colombier }
6127dd7cddfSDavid du Colombier auxputc = putc;
613c3ca4b29SDavid du Colombier intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
614c3ca4b29SDavid du Colombier iunlock(&i8042lock);
615c3ca4b29SDavid du Colombier }
outbyte(int port,int c)616c3ca4b29SDavid du Colombier
617c3ca4b29SDavid du Colombier static char *initfailed = "i8042: kbdinit failed\n";
618c3ca4b29SDavid du Colombier
619c3ca4b29SDavid du Colombier static int
620c3ca4b29SDavid du Colombier outbyte(int port, int c)
621c3ca4b29SDavid du Colombier {
622c3ca4b29SDavid du Colombier outb(port, c);
623c3ca4b29SDavid du Colombier if(outready() < 0) {
624c3ca4b29SDavid du Colombier print(initfailed);
625c3ca4b29SDavid du Colombier return -1;
6267dd7cddfSDavid du Colombier }
kbdinit(void)6277dd7cddfSDavid du Colombier return 0;
6287dd7cddfSDavid du Colombier }
629c3ca4b29SDavid du Colombier
6307dd7cddfSDavid du Colombier void
6317dd7cddfSDavid du Colombier kbdinit(void)
6327d80c4cdSDavid du Colombier {
633c3ca4b29SDavid du Colombier int c, try;
6347dd7cddfSDavid du Colombier
6357dd7cddfSDavid du Colombier /* wait for a quiescent controller */
636c3ca4b29SDavid du Colombier try = 500;
637c3ca4b29SDavid du Colombier while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
638c3ca4b29SDavid du Colombier if(c & Inready)
639c3ca4b29SDavid du Colombier inb(Data);
640c3ca4b29SDavid du Colombier delay(1);
641c3ca4b29SDavid du Colombier }
6427dd7cddfSDavid du Colombier if (try <= 0) {
6437dd7cddfSDavid du Colombier print(initfailed);
6447dd7cddfSDavid du Colombier return;
6457dd7cddfSDavid du Colombier }
646c3ca4b29SDavid du Colombier
6477dd7cddfSDavid du Colombier /* get current controller command byte */
6487dd7cddfSDavid du Colombier outb(Cmd, 0x20);
6497dd7cddfSDavid du Colombier if(inready() < 0){
6507dd7cddfSDavid du Colombier print("i8042: kbdinit can't read ccc\n");
6517dd7cddfSDavid du Colombier ccc = 0;
6527dd7cddfSDavid du Colombier } else
6537dd7cddfSDavid du Colombier ccc = inb(Data);
654c3ca4b29SDavid du Colombier
655c3ca4b29SDavid du Colombier /* enable kbd xfers and interrupts */
656c3ca4b29SDavid du Colombier ccc &= ~Ckbddis;
657c3ca4b29SDavid du Colombier ccc |= Csf | Ckbdint | Cscs1;
658c3ca4b29SDavid du Colombier if(outready() < 0) {
659c3ca4b29SDavid du Colombier print(initfailed);
660c3ca4b29SDavid du Colombier return;
661c3ca4b29SDavid du Colombier }
662c3ca4b29SDavid du Colombier
663c3ca4b29SDavid du Colombier nokbd = 0;
664f7db6155SDavid du Colombier
665*9a3e0102SDavid du Colombier /* disable mouse */
666*9a3e0102SDavid du Colombier if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
667f7db6155SDavid du Colombier print("i8042: kbdinit mouse disable failed\n");
668f7db6155SDavid du Colombier
669f7db6155SDavid du Colombier /* see http://www.computer-engineering.org/ps2keyboard for codes */
6703e12c5d1SDavid du Colombier if(getconf("*typematic") != nil)
6719a747e4fSDavid du Colombier /* set typematic rate/delay (0 -> delay=250ms & rate=30cps) */
6729a747e4fSDavid du Colombier if(outbyte(Data, 0xf3) < 0 || outbyte(Data, 0) < 0)
kbdenable(void)6739a747e4fSDavid du Colombier print("i8042: kbdinit set typematic rate failed\n");
6749a747e4fSDavid du Colombier }
6759a747e4fSDavid du Colombier
6769a747e4fSDavid du Colombier void
6779a747e4fSDavid du Colombier kbdenable(void)
6789a747e4fSDavid du Colombier {
6799a747e4fSDavid du Colombier kbdq = qopen(4*1024, 0, 0, 0);
6809a747e4fSDavid du Colombier if(kbdq == nil)
6819a747e4fSDavid du Colombier panic("kbdinit");
6829a747e4fSDavid du Colombier qnoblock(kbdq, 1);
6839a747e4fSDavid du Colombier
684d5b6fab5SDavid du Colombier ioalloc(Data, 1, 0, "kbd");
685d5b6fab5SDavid du Colombier ioalloc(Cmd, 1, 0, "kbd");
686d5b6fab5SDavid du Colombier
6879a747e4fSDavid du Colombier intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
6883901e6d0SDavid du Colombier
6893901e6d0SDavid du Colombier kbscans[Int].num = 0;
kbdputmap(ushort m,ushort scanc,Rune r)6903901e6d0SDavid du Colombier setleds(&kbscans[Int]);
6913901e6d0SDavid du Colombier }
6923901e6d0SDavid du Colombier
6933901e6d0SDavid du Colombier void
6943901e6d0SDavid du Colombier kbdputmap(ushort m, ushort scanc, Rune r)
6953901e6d0SDavid du Colombier {
6963901e6d0SDavid du Colombier if(scanc >= Nscan)
6973901e6d0SDavid du Colombier error(Ebadarg);
6983901e6d0SDavid du Colombier switch(m) {
6993901e6d0SDavid du Colombier default:
7003901e6d0SDavid du Colombier error(Ebadarg);
7013901e6d0SDavid du Colombier case 0:
7023901e6d0SDavid du Colombier kbtab[scanc] = r;
7033901e6d0SDavid du Colombier break;
7043901e6d0SDavid du Colombier case 1:
7053901e6d0SDavid du Colombier kbtabshift[scanc] = r;
7063901e6d0SDavid du Colombier break;
7073901e6d0SDavid du Colombier case 2:
7083901e6d0SDavid du Colombier kbtabesc1[scanc] = r;
7093901e6d0SDavid du Colombier break;
7103901e6d0SDavid du Colombier case 3:
7113901e6d0SDavid du Colombier kbtabaltgr[scanc] = r;
7123901e6d0SDavid du Colombier break;
7133901e6d0SDavid du Colombier case 4:
7143901e6d0SDavid du Colombier kbtabctrl[scanc] = r;
7153901e6d0SDavid du Colombier break;
kbdgetmap(uint offset,int * t,int * sc,Rune * r)7169c1d3970SDavid du Colombier }
7173901e6d0SDavid du Colombier }
7189c1d3970SDavid du Colombier
7199c1d3970SDavid du Colombier int
7203901e6d0SDavid du Colombier kbdgetmap(uint offset, int *t, int *sc, Rune *r)
7213901e6d0SDavid du Colombier {
7223901e6d0SDavid du Colombier if ((int)offset < 0)
7233901e6d0SDavid du Colombier error(Ebadarg);
7243901e6d0SDavid du Colombier *t = offset/Nscan;
7253901e6d0SDavid du Colombier *sc = offset%Nscan;
7263901e6d0SDavid du Colombier switch(*t) {
7273901e6d0SDavid du Colombier default:
7283901e6d0SDavid du Colombier return 0;
7293901e6d0SDavid du Colombier case 0:
7303901e6d0SDavid du Colombier *r = kbtab[*sc];
7313901e6d0SDavid du Colombier return 1;
7323901e6d0SDavid du Colombier case 1:
7333901e6d0SDavid du Colombier *r = kbtabshift[*sc];
7343901e6d0SDavid du Colombier return 1;
7353901e6d0SDavid du Colombier case 2:
7363901e6d0SDavid du Colombier *r = kbtabesc1[*sc];
7373901e6d0SDavid du Colombier return 1;
7383901e6d0SDavid du Colombier case 3:
7393901e6d0SDavid du Colombier *r = kbtabaltgr[*sc];
7403901e6d0SDavid du Colombier return 1;
7413901e6d0SDavid du Colombier case 4:
742 *r = kbtabctrl[*sc];
743 return 1;
744 }
745 }
746