1*a81c3ea0SDavid du Colombier /*
2*a81c3ea0SDavid du Colombier * keyboard input
3*a81c3ea0SDavid du Colombier */
4*a81c3ea0SDavid du Colombier #include "u.h"
5*a81c3ea0SDavid du Colombier #include "../port/lib.h"
6*a81c3ea0SDavid du Colombier #include "mem.h"
7*a81c3ea0SDavid du Colombier #include "dat.h"
8*a81c3ea0SDavid du Colombier #include "fns.h"
9*a81c3ea0SDavid du Colombier #include "io.h"
10*a81c3ea0SDavid du Colombier #include "../port/error.h"
11*a81c3ea0SDavid du Colombier
12*a81c3ea0SDavid du Colombier enum {
13*a81c3ea0SDavid du Colombier Data= 0x60, /* data port */
14*a81c3ea0SDavid du Colombier
15*a81c3ea0SDavid du Colombier Status= 0x64, /* status port */
16*a81c3ea0SDavid du Colombier Inready= 0x01, /* input character ready */
17*a81c3ea0SDavid du Colombier Outbusy= 0x02, /* output busy */
18*a81c3ea0SDavid du Colombier Sysflag= 0x04, /* system flag */
19*a81c3ea0SDavid du Colombier Cmddata= 0x08, /* cmd==0, data==1 */
20*a81c3ea0SDavid du Colombier Inhibit= 0x10, /* keyboard/mouse inhibited */
21*a81c3ea0SDavid du Colombier Minready= 0x20, /* mouse character ready */
22*a81c3ea0SDavid du Colombier Rtimeout= 0x40, /* general timeout */
23*a81c3ea0SDavid du Colombier Parity= 0x80,
24*a81c3ea0SDavid du Colombier
25*a81c3ea0SDavid du Colombier Cmd= 0x64, /* command port (write only) */
26*a81c3ea0SDavid du Colombier
27*a81c3ea0SDavid du Colombier Spec= 0xF800, /* Unicode private space */
28*a81c3ea0SDavid du Colombier PF= Spec|0x20, /* num pad function key */
29*a81c3ea0SDavid du Colombier View= Spec|0x00, /* view (shift window up) */
30*a81c3ea0SDavid du Colombier KF= 0xF000, /* function key (begin Unicode private space) */
31*a81c3ea0SDavid du Colombier Shift= Spec|0x60,
32*a81c3ea0SDavid du Colombier Break= Spec|0x61,
33*a81c3ea0SDavid du Colombier Ctrl= Spec|0x62,
34*a81c3ea0SDavid du Colombier Latin= Spec|0x63,
35*a81c3ea0SDavid du Colombier Caps= Spec|0x64,
36*a81c3ea0SDavid du Colombier Num= Spec|0x65,
37*a81c3ea0SDavid du Colombier Middle= Spec|0x66,
38*a81c3ea0SDavid du Colombier Altgr= Spec|0x67,
39*a81c3ea0SDavid du Colombier Kmouse= Spec|0x100,
40*a81c3ea0SDavid du Colombier No= 0x00, /* peter */
41*a81c3ea0SDavid du Colombier
42*a81c3ea0SDavid du Colombier Home= KF|13,
43*a81c3ea0SDavid du Colombier Up= KF|14,
44*a81c3ea0SDavid du Colombier Pgup= KF|15,
45*a81c3ea0SDavid du Colombier Print= KF|16,
46*a81c3ea0SDavid du Colombier Left= KF|17,
47*a81c3ea0SDavid du Colombier Right= KF|18,
48*a81c3ea0SDavid du Colombier End= KF|24,
49*a81c3ea0SDavid du Colombier Down= View,
50*a81c3ea0SDavid du Colombier Pgdown= KF|19,
51*a81c3ea0SDavid du Colombier Ins= KF|20,
52*a81c3ea0SDavid du Colombier Del= 0x7F,
53*a81c3ea0SDavid du Colombier Scroll= KF|21,
54*a81c3ea0SDavid du Colombier
55*a81c3ea0SDavid du Colombier Nscan= 128,
56*a81c3ea0SDavid du Colombier
57*a81c3ea0SDavid du Colombier Int= 0, /* kbscans indices */
58*a81c3ea0SDavid du Colombier Ext,
59*a81c3ea0SDavid du Colombier Nscans,
60*a81c3ea0SDavid du Colombier };
61*a81c3ea0SDavid du Colombier
62*a81c3ea0SDavid du Colombier /*
63*a81c3ea0SDavid du Colombier * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
64*a81c3ea0SDavid du Colombier * A 'standard' keyboard doesn't produce anything above 0x58.
65*a81c3ea0SDavid du Colombier */
66*a81c3ea0SDavid du Colombier Rune kbtab[Nscan] =
67*a81c3ea0SDavid du Colombier {
68*a81c3ea0SDavid du Colombier [0x00] No, 0x1b, '1', '2', '3', '4', '5', '6',
69*a81c3ea0SDavid du Colombier [0x08] '7', '8', '9', '0', '-', '=', '\b', '\t',
70*a81c3ea0SDavid du Colombier [0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
71*a81c3ea0SDavid du Colombier [0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's',
72*a81c3ea0SDavid du Colombier [0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
73*a81c3ea0SDavid du Colombier [0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v',
74*a81c3ea0SDavid du Colombier [0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*',
75*a81c3ea0SDavid du Colombier [0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
76*a81c3ea0SDavid du Colombier [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
77*a81c3ea0SDavid du Colombier [0x48] '8', '9', '-', '4', '5', '6', '+', '1',
78*a81c3ea0SDavid du Colombier [0x50] '2', '3', '0', '.', No, No, No, KF|11,
79*a81c3ea0SDavid du Colombier [0x58] KF|12, No, No, No, No, No, No, No,
80*a81c3ea0SDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
81*a81c3ea0SDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
82*a81c3ea0SDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
83*a81c3ea0SDavid du Colombier [0x78] No, View, No, Up, No, No, No, No,
84*a81c3ea0SDavid du Colombier };
85*a81c3ea0SDavid du Colombier
86*a81c3ea0SDavid du Colombier Rune kbtabshift[Nscan] =
87*a81c3ea0SDavid du Colombier {
88*a81c3ea0SDavid du Colombier [0x00] No, 0x1b, '!', '@', '#', '$', '%', '^',
89*a81c3ea0SDavid du Colombier [0x08] '&', '*', '(', ')', '_', '+', '\b', '\t',
90*a81c3ea0SDavid du Colombier [0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
91*a81c3ea0SDavid du Colombier [0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S',
92*a81c3ea0SDavid du Colombier [0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
93*a81c3ea0SDavid du Colombier [0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V',
94*a81c3ea0SDavid du Colombier [0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*',
95*a81c3ea0SDavid du Colombier [0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
96*a81c3ea0SDavid du Colombier [0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
97*a81c3ea0SDavid du Colombier [0x48] '8', '9', '-', '4', '5', '6', '+', '1',
98*a81c3ea0SDavid du Colombier [0x50] '2', '3', '0', '.', No, No, No, KF|11,
99*a81c3ea0SDavid du Colombier [0x58] KF|12, No, No, No, No, No, No, No,
100*a81c3ea0SDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
101*a81c3ea0SDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
102*a81c3ea0SDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
103*a81c3ea0SDavid du Colombier [0x78] No, Up, No, Up, No, No, No, No,
104*a81c3ea0SDavid du Colombier };
105*a81c3ea0SDavid du Colombier
106*a81c3ea0SDavid du Colombier Rune kbtabesc1[Nscan] =
107*a81c3ea0SDavid du Colombier {
108*a81c3ea0SDavid du Colombier [0x00] No, No, No, No, No, No, No, No,
109*a81c3ea0SDavid du Colombier [0x08] No, No, No, No, No, No, No, No,
110*a81c3ea0SDavid du Colombier [0x10] No, No, No, No, No, No, No, No,
111*a81c3ea0SDavid du Colombier [0x18] No, No, No, No, '\n', Ctrl, No, No,
112*a81c3ea0SDavid du Colombier [0x20] No, No, No, No, No, No, No, No,
113*a81c3ea0SDavid du Colombier [0x28] No, No, Shift, No, No, No, No, No,
114*a81c3ea0SDavid du Colombier [0x30] No, No, No, No, No, '/', No, Print,
115*a81c3ea0SDavid du Colombier [0x38] Altgr, No, No, No, No, No, No, No,
116*a81c3ea0SDavid du Colombier [0x40] No, No, No, No, No, No, Break, Home,
117*a81c3ea0SDavid du Colombier [0x48] Up, Pgup, No, Left, No, Right, No, End,
118*a81c3ea0SDavid du Colombier [0x50] Down, Pgdown, Ins, Del, No, No, No, No,
119*a81c3ea0SDavid du Colombier [0x58] No, No, No, No, No, No, No, No,
120*a81c3ea0SDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
121*a81c3ea0SDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
122*a81c3ea0SDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
123*a81c3ea0SDavid du Colombier [0x78] No, Up, No, No, No, No, No, No,
124*a81c3ea0SDavid du Colombier };
125*a81c3ea0SDavid du Colombier
126*a81c3ea0SDavid du Colombier Rune kbtabaltgr[Nscan] =
127*a81c3ea0SDavid du Colombier {
128*a81c3ea0SDavid du Colombier [0x00] No, No, No, No, No, No, No, No,
129*a81c3ea0SDavid du Colombier [0x08] No, No, No, No, No, No, No, No,
130*a81c3ea0SDavid du Colombier [0x10] No, No, No, No, No, No, No, No,
131*a81c3ea0SDavid du Colombier [0x18] No, No, No, No, '\n', Ctrl, No, No,
132*a81c3ea0SDavid du Colombier [0x20] No, No, No, No, No, No, No, No,
133*a81c3ea0SDavid du Colombier [0x28] No, No, Shift, No, No, No, No, No,
134*a81c3ea0SDavid du Colombier [0x30] No, No, No, No, No, '/', No, Print,
135*a81c3ea0SDavid du Colombier [0x38] Altgr, No, No, No, No, No, No, No,
136*a81c3ea0SDavid du Colombier [0x40] No, No, No, No, No, No, Break, Home,
137*a81c3ea0SDavid du Colombier [0x48] Up, Pgup, No, Left, No, Right, No, End,
138*a81c3ea0SDavid du Colombier [0x50] Down, Pgdown, Ins, Del, No, No, No, No,
139*a81c3ea0SDavid du Colombier [0x58] No, No, No, No, No, No, No, No,
140*a81c3ea0SDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
141*a81c3ea0SDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
142*a81c3ea0SDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
143*a81c3ea0SDavid du Colombier [0x78] No, Up, No, No, No, No, No, No,
144*a81c3ea0SDavid du Colombier };
145*a81c3ea0SDavid du Colombier
146*a81c3ea0SDavid du Colombier Rune kbtabctrl[Nscan] =
147*a81c3ea0SDavid du Colombier {
148*a81c3ea0SDavid du Colombier [0x00] No, '', '', '', '', '', '', '',
149*a81c3ea0SDavid du Colombier [0x08] '', '', '', '', '
150*a81c3ea0SDavid du Colombier ', '', '\b', '\t',
151*a81c3ea0SDavid du Colombier [0x10] '', '', '', '', '', '', '', '\t',
152*a81c3ea0SDavid du Colombier [0x18] '', '', '', '', '\n', Ctrl, '', '',
153*a81c3ea0SDavid du Colombier [0x20] '', '', '', '\b', '\n', '', '', '',
154*a81c3ea0SDavid du Colombier [0x28] '', No, Shift, '', '', '', '', '',
155*a81c3ea0SDavid du Colombier [0x30] '', '', '
156*a81c3ea0SDavid du Colombier ', '', '', '', Shift, '\n',
157*a81c3ea0SDavid du Colombier [0x38] Latin, No, Ctrl, '', '', '', '', '',
158*a81c3ea0SDavid du Colombier [0x40] '', '', '', '
159*a81c3ea0SDavid du Colombier ', '', '', '', '',
160*a81c3ea0SDavid du Colombier [0x48] '', '', '
161*a81c3ea0SDavid du Colombier ', '', '', '', '', '',
162*a81c3ea0SDavid du Colombier [0x50] '', '', '', '', No, No, No, '',
163*a81c3ea0SDavid du Colombier [0x58] '', No, No, No, No, No, No, No,
164*a81c3ea0SDavid du Colombier [0x60] No, No, No, No, No, No, No, No,
165*a81c3ea0SDavid du Colombier [0x68] No, No, No, No, No, No, No, No,
166*a81c3ea0SDavid du Colombier [0x70] No, No, No, No, No, No, No, No,
167*a81c3ea0SDavid du Colombier [0x78] No, '', No, '\b', No, No, No, No,
168*a81c3ea0SDavid du Colombier };
169*a81c3ea0SDavid du Colombier
170*a81c3ea0SDavid du Colombier enum
171*a81c3ea0SDavid du Colombier {
172*a81c3ea0SDavid du Colombier /* controller command byte */
173*a81c3ea0SDavid du Colombier Cscs1= (1<<6), /* scan code set 1 */
174*a81c3ea0SDavid du Colombier Cauxdis= (1<<5), /* mouse disable */
175*a81c3ea0SDavid du Colombier Ckbddis= (1<<4), /* kbd disable */
176*a81c3ea0SDavid du Colombier Csf= (1<<2), /* system flag */
177*a81c3ea0SDavid du Colombier Cauxint= (1<<1), /* mouse interrupt enable */
178*a81c3ea0SDavid du Colombier Ckbdint= (1<<0), /* kbd interrupt enable */
179*a81c3ea0SDavid du Colombier };
180*a81c3ea0SDavid du Colombier
181*a81c3ea0SDavid du Colombier int mouseshifted;
182*a81c3ea0SDavid du Colombier void (*kbdmouse)(int);
183*a81c3ea0SDavid du Colombier
184*a81c3ea0SDavid du Colombier static Lock i8042lock;
185*a81c3ea0SDavid du Colombier static uchar ccc;
186*a81c3ea0SDavid du Colombier static void (*auxputc)(int, int);
187*a81c3ea0SDavid du Colombier static int nokbd = 1; /* flag: no PS/2 keyboard */
188*a81c3ea0SDavid du Colombier
outready(void)189*a81c3ea0SDavid du Colombier /*
190*a81c3ea0SDavid du Colombier * wait for output no longer busy
191*a81c3ea0SDavid du Colombier */
192*a81c3ea0SDavid du Colombier static int
193*a81c3ea0SDavid du Colombier outready(void)
194*a81c3ea0SDavid du Colombier {
195*a81c3ea0SDavid du Colombier int tries;
196*a81c3ea0SDavid du Colombier
197*a81c3ea0SDavid du Colombier for(tries = 0; (inb(Status) & Outbusy); tries++){
198*a81c3ea0SDavid du Colombier if(tries > 500)
199*a81c3ea0SDavid du Colombier return -1;
200*a81c3ea0SDavid du Colombier delay(2);
201*a81c3ea0SDavid du Colombier }
202*a81c3ea0SDavid du Colombier return 0;
203*a81c3ea0SDavid du Colombier }
204*a81c3ea0SDavid du Colombier
inready(void)205*a81c3ea0SDavid du Colombier /*
206*a81c3ea0SDavid du Colombier * wait for input
207*a81c3ea0SDavid du Colombier */
208*a81c3ea0SDavid du Colombier static int
209*a81c3ea0SDavid du Colombier inready(void)
210*a81c3ea0SDavid du Colombier {
211*a81c3ea0SDavid du Colombier int tries;
212*a81c3ea0SDavid du Colombier
213*a81c3ea0SDavid du Colombier for(tries = 0; !(inb(Status) & Inready); tries++){
214*a81c3ea0SDavid du Colombier if(tries > 500)
215*a81c3ea0SDavid du Colombier return -1;
216*a81c3ea0SDavid du Colombier delay(2);
217*a81c3ea0SDavid du Colombier }
218*a81c3ea0SDavid du Colombier return 0;
219*a81c3ea0SDavid du Colombier }
220*a81c3ea0SDavid du Colombier
221*a81c3ea0SDavid du Colombier int
222*a81c3ea0SDavid du Colombier i8042auxcmd(int cmd)
223*a81c3ea0SDavid du Colombier {
224*a81c3ea0SDavid du Colombier unsigned int c;
225*a81c3ea0SDavid du Colombier int tries;
226*a81c3ea0SDavid du Colombier static int badkbd;
227*a81c3ea0SDavid du Colombier
228*a81c3ea0SDavid du Colombier if(badkbd)
229*a81c3ea0SDavid du Colombier return -1;
230*a81c3ea0SDavid du Colombier c = 0;
231*a81c3ea0SDavid du Colombier tries = 0;
232*a81c3ea0SDavid du Colombier
233*a81c3ea0SDavid du Colombier ilock(&i8042lock);
234*a81c3ea0SDavid du Colombier do{
235*a81c3ea0SDavid du Colombier if(tries++ > 2)
236*a81c3ea0SDavid du Colombier break;
237*a81c3ea0SDavid du Colombier if(outready() < 0)
238*a81c3ea0SDavid du Colombier break;
239*a81c3ea0SDavid du Colombier outb(Cmd, 0xD4);
240*a81c3ea0SDavid du Colombier if(outready() < 0)
241*a81c3ea0SDavid du Colombier break;
242*a81c3ea0SDavid du Colombier outb(Data, cmd);
243*a81c3ea0SDavid du Colombier if(outready() < 0)
244*a81c3ea0SDavid du Colombier break;
245*a81c3ea0SDavid du Colombier if(inready() < 0)
246*a81c3ea0SDavid du Colombier break;
247*a81c3ea0SDavid du Colombier c = inb(Data);
248*a81c3ea0SDavid du Colombier } while(c == 0xFE || c == 0);
249*a81c3ea0SDavid du Colombier iunlock(&i8042lock);
250*a81c3ea0SDavid du Colombier
251*a81c3ea0SDavid du Colombier if(c != 0xFA){
252*a81c3ea0SDavid du Colombier print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
253*a81c3ea0SDavid du Colombier badkbd = 1; /* don't keep trying; there might not be one */
254*a81c3ea0SDavid du Colombier return -1;
255*a81c3ea0SDavid du Colombier }
i8042auxcmds(uchar * cmd,int ncmd)256*a81c3ea0SDavid du Colombier return 0;
257*a81c3ea0SDavid du Colombier }
258*a81c3ea0SDavid du Colombier
259*a81c3ea0SDavid du Colombier int
260*a81c3ea0SDavid du Colombier i8042auxcmds(uchar *cmd, int ncmd)
261*a81c3ea0SDavid du Colombier {
262*a81c3ea0SDavid du Colombier int i;
263*a81c3ea0SDavid du Colombier
264*a81c3ea0SDavid du Colombier ilock(&i8042lock);
265*a81c3ea0SDavid du Colombier for(i=0; i<ncmd; i++){
266*a81c3ea0SDavid du Colombier if(outready() < 0)
267*a81c3ea0SDavid du Colombier break;
268*a81c3ea0SDavid du Colombier outb(Cmd, 0xD4);
269*a81c3ea0SDavid du Colombier if(outready() < 0)
270*a81c3ea0SDavid du Colombier break;
271*a81c3ea0SDavid du Colombier outb(Data, cmd[i]);
272*a81c3ea0SDavid du Colombier }
273*a81c3ea0SDavid du Colombier iunlock(&i8042lock);
274*a81c3ea0SDavid du Colombier return i;
275*a81c3ea0SDavid du Colombier }
276*a81c3ea0SDavid du Colombier
277*a81c3ea0SDavid du Colombier typedef struct Kbscan Kbscan;
278*a81c3ea0SDavid du Colombier struct Kbscan {
279*a81c3ea0SDavid du Colombier int esc1;
280*a81c3ea0SDavid du Colombier int esc2;
281*a81c3ea0SDavid du Colombier int alt;
282*a81c3ea0SDavid du Colombier int altgr;
283*a81c3ea0SDavid du Colombier int caps;
284*a81c3ea0SDavid du Colombier int ctl;
285*a81c3ea0SDavid du Colombier int num;
286*a81c3ea0SDavid du Colombier int shift;
287*a81c3ea0SDavid du Colombier int collecting;
288*a81c3ea0SDavid du Colombier int nk;
289*a81c3ea0SDavid du Colombier Rune kc[5];
290*a81c3ea0SDavid du Colombier int buttons;
291*a81c3ea0SDavid du Colombier };
292*a81c3ea0SDavid du Colombier
293*a81c3ea0SDavid du Colombier Kbscan kbscans[Nscans]; /* kernel and external scan code state */
294*a81c3ea0SDavid du Colombier
295*a81c3ea0SDavid du Colombier static int kdebug;
296*a81c3ea0SDavid du Colombier
297*a81c3ea0SDavid du Colombier /*
298*a81c3ea0SDavid du Colombier * set keyboard's leds for lock states (scroll, numeric, caps).
299*a81c3ea0SDavid du Colombier *
300*a81c3ea0SDavid du Colombier * at least one keyboard (from Qtronics) also sets its numeric-lock
301*a81c3ea0SDavid du Colombier * behaviour to match the led state, though it has no numeric keypad,
302*a81c3ea0SDavid du Colombier * and some BIOSes bring the system up with numeric-lock set and no
303*a81c3ea0SDavid du Colombier * setting to change that. this combination steals the keys for these
setleds(Kbscan * kbscan)304*a81c3ea0SDavid du Colombier * characters and makes it impossible to generate them: uiolkjm&*().
305*a81c3ea0SDavid du Colombier * thus we'd like to be able to force the numeric-lock led (and behaviour) off.
306*a81c3ea0SDavid du Colombier */
307*a81c3ea0SDavid du Colombier static void
308*a81c3ea0SDavid du Colombier setleds(Kbscan *kbscan)
309*a81c3ea0SDavid du Colombier {
310*a81c3ea0SDavid du Colombier int leds;
311*a81c3ea0SDavid du Colombier uchar dummy;
312*a81c3ea0SDavid du Colombier
313*a81c3ea0SDavid du Colombier SET(dummy);
314*a81c3ea0SDavid du Colombier if(nokbd || kbscan != &kbscans[Int])
315*a81c3ea0SDavid du Colombier return;
316*a81c3ea0SDavid du Colombier leds = 0;
317*a81c3ea0SDavid du Colombier if(kbscan->num)
318*a81c3ea0SDavid du Colombier leds |= 1<<1;
319*a81c3ea0SDavid du Colombier if(0 && kbscan->caps) /* we don't implement caps lock */
320*a81c3ea0SDavid du Colombier leds |= 1<<2;
321*a81c3ea0SDavid du Colombier
322*a81c3ea0SDavid du Colombier ilock(&i8042lock);
323*a81c3ea0SDavid du Colombier outready();
324*a81c3ea0SDavid du Colombier outb(Data, 0xed); /* `reset keyboard lock states' */
325*a81c3ea0SDavid du Colombier if(inready() == 0)
326*a81c3ea0SDavid du Colombier dummy = inb(Data);
327*a81c3ea0SDavid du Colombier
328*a81c3ea0SDavid du Colombier outready();
329*a81c3ea0SDavid du Colombier outb(Data, leds);
330*a81c3ea0SDavid du Colombier if(inready() == 0)
331*a81c3ea0SDavid du Colombier dummy = inb(Data);
332*a81c3ea0SDavid du Colombier
333*a81c3ea0SDavid du Colombier outready();
334*a81c3ea0SDavid du Colombier iunlock(&i8042lock);
335*a81c3ea0SDavid du Colombier USED(dummy);
336*a81c3ea0SDavid du Colombier }
337*a81c3ea0SDavid du Colombier
kbdputsc(int c,int external)338*a81c3ea0SDavid du Colombier /*
339*a81c3ea0SDavid du Colombier * Scan code processing
340*a81c3ea0SDavid du Colombier */
341*a81c3ea0SDavid du Colombier void
342*a81c3ea0SDavid du Colombier kbdputsc(int c, int external)
343*a81c3ea0SDavid du Colombier {
344*a81c3ea0SDavid du Colombier int i, keyup;
345*a81c3ea0SDavid du Colombier Kbscan *kbscan;
346*a81c3ea0SDavid du Colombier
347*a81c3ea0SDavid du Colombier if(external)
348*a81c3ea0SDavid du Colombier kbscan = &kbscans[Ext];
349*a81c3ea0SDavid du Colombier else
350*a81c3ea0SDavid du Colombier kbscan = &kbscans[Int];
351*a81c3ea0SDavid du Colombier
352*a81c3ea0SDavid du Colombier if(kdebug)
353*a81c3ea0SDavid du Colombier print("sc %x ms %d\n", c, mouseshifted);
354*a81c3ea0SDavid du Colombier /*
355*a81c3ea0SDavid du Colombier * e0's is the first of a 2 character sequence, e1 the first
356*a81c3ea0SDavid du Colombier * of a 3 character sequence (on the safari)
357*a81c3ea0SDavid du Colombier */
358*a81c3ea0SDavid du Colombier if(c == 0xe0){
359*a81c3ea0SDavid du Colombier kbscan->esc1 = 1;
360*a81c3ea0SDavid du Colombier return;
361*a81c3ea0SDavid du Colombier } else if(c == 0xe1){
362*a81c3ea0SDavid du Colombier kbscan->esc2 = 2;
363*a81c3ea0SDavid du Colombier return;
364*a81c3ea0SDavid du Colombier }
365*a81c3ea0SDavid du Colombier
366*a81c3ea0SDavid du Colombier keyup = c & 0x80;
367*a81c3ea0SDavid du Colombier c &= 0x7f;
368*a81c3ea0SDavid du Colombier if(c > sizeof kbtab){
369*a81c3ea0SDavid du Colombier c |= keyup;
370*a81c3ea0SDavid du Colombier if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */
371*a81c3ea0SDavid du Colombier print("unknown key %ux\n", c);
372*a81c3ea0SDavid du Colombier return;
373*a81c3ea0SDavid du Colombier }
374*a81c3ea0SDavid du Colombier
375*a81c3ea0SDavid du Colombier if(kbscan->esc1){
376*a81c3ea0SDavid du Colombier c = kbtabesc1[c];
377*a81c3ea0SDavid du Colombier kbscan->esc1 = 0;
378*a81c3ea0SDavid du Colombier } else if(kbscan->esc2){
379*a81c3ea0SDavid du Colombier kbscan->esc2--;
380*a81c3ea0SDavid du Colombier return;
381*a81c3ea0SDavid du Colombier } else if(kbscan->shift)
382*a81c3ea0SDavid du Colombier c = kbtabshift[c];
383*a81c3ea0SDavid du Colombier else if(kbscan->altgr)
384*a81c3ea0SDavid du Colombier c = kbtabaltgr[c];
385*a81c3ea0SDavid du Colombier else if(kbscan->ctl)
386*a81c3ea0SDavid du Colombier c = kbtabctrl[c];
387*a81c3ea0SDavid du Colombier else
388*a81c3ea0SDavid du Colombier c = kbtab[c];
389*a81c3ea0SDavid du Colombier
390*a81c3ea0SDavid du Colombier if(kbscan->caps && c<='z' && c>='a')
391*a81c3ea0SDavid du Colombier c += 'A' - 'a';
392*a81c3ea0SDavid du Colombier
393*a81c3ea0SDavid du Colombier /*
394*a81c3ea0SDavid du Colombier * keyup only important for shifts
395*a81c3ea0SDavid du Colombier */
396*a81c3ea0SDavid du Colombier if(keyup){
397*a81c3ea0SDavid du Colombier switch(c){
398*a81c3ea0SDavid du Colombier case Latin:
399*a81c3ea0SDavid du Colombier kbscan->alt = 0;
400*a81c3ea0SDavid du Colombier break;
401*a81c3ea0SDavid du Colombier case Shift:
402*a81c3ea0SDavid du Colombier kbscan->shift = 0;
403*a81c3ea0SDavid du Colombier mouseshifted = 0;
404*a81c3ea0SDavid du Colombier if(kdebug)
405*a81c3ea0SDavid du Colombier print("shiftclr\n");
406*a81c3ea0SDavid du Colombier break;
407*a81c3ea0SDavid du Colombier case Ctrl:
408*a81c3ea0SDavid du Colombier kbscan->ctl = 0;
409*a81c3ea0SDavid du Colombier break;
410*a81c3ea0SDavid du Colombier case Altgr:
411*a81c3ea0SDavid du Colombier kbscan->altgr = 0;
412*a81c3ea0SDavid du Colombier break;
413*a81c3ea0SDavid du Colombier case Kmouse|1:
414*a81c3ea0SDavid du Colombier case Kmouse|2:
415*a81c3ea0SDavid du Colombier case Kmouse|3:
416*a81c3ea0SDavid du Colombier case Kmouse|4:
417*a81c3ea0SDavid du Colombier case Kmouse|5:
418*a81c3ea0SDavid du Colombier kbscan->buttons &= ~(1<<(c-Kmouse-1));
419*a81c3ea0SDavid du Colombier if(kbdmouse)
420*a81c3ea0SDavid du Colombier kbdmouse(kbscan->buttons);
421*a81c3ea0SDavid du Colombier break;
422*a81c3ea0SDavid du Colombier }
423*a81c3ea0SDavid du Colombier return;
424*a81c3ea0SDavid du Colombier }
425*a81c3ea0SDavid du Colombier
426*a81c3ea0SDavid du Colombier /*
427*a81c3ea0SDavid du Colombier * normal character
428*a81c3ea0SDavid du Colombier */
429*a81c3ea0SDavid du Colombier if(!(c & (Spec|KF))){
430*a81c3ea0SDavid du Colombier if(kbscan->ctl)
431*a81c3ea0SDavid du Colombier if(kbscan->alt && c == Del)
432*a81c3ea0SDavid du Colombier exit(0);
433*a81c3ea0SDavid du Colombier if(!kbscan->collecting){
434*a81c3ea0SDavid du Colombier kbdputc(kbdq, c);
435*a81c3ea0SDavid du Colombier return;
436*a81c3ea0SDavid du Colombier }
437*a81c3ea0SDavid du Colombier kbscan->kc[kbscan->nk++] = c;
438*a81c3ea0SDavid du Colombier c = latin1(kbscan->kc, kbscan->nk);
439*a81c3ea0SDavid du Colombier if(c < -1) /* need more keystrokes */
440*a81c3ea0SDavid du Colombier return;
441*a81c3ea0SDavid du Colombier if(c != -1) /* valid sequence */
442*a81c3ea0SDavid du Colombier kbdputc(kbdq, c);
443*a81c3ea0SDavid du Colombier else /* dump characters */
444*a81c3ea0SDavid du Colombier for(i=0; i<kbscan->nk; i++)
445*a81c3ea0SDavid du Colombier kbdputc(kbdq, kbscan->kc[i]);
446*a81c3ea0SDavid du Colombier kbscan->nk = 0;
447*a81c3ea0SDavid du Colombier kbscan->collecting = 0;
448*a81c3ea0SDavid du Colombier return;
449*a81c3ea0SDavid du Colombier } else {
450*a81c3ea0SDavid du Colombier switch(c){
451*a81c3ea0SDavid du Colombier case Caps:
452*a81c3ea0SDavid du Colombier kbscan->caps ^= 1;
453*a81c3ea0SDavid du Colombier return;
454*a81c3ea0SDavid du Colombier case Num:
455*a81c3ea0SDavid du Colombier kbscan->num ^= 1;
456*a81c3ea0SDavid du Colombier if(!external)
457*a81c3ea0SDavid du Colombier setleds(kbscan);
458*a81c3ea0SDavid du Colombier return;
459*a81c3ea0SDavid du Colombier case Shift:
460*a81c3ea0SDavid du Colombier kbscan->shift = 1;
461*a81c3ea0SDavid du Colombier if(kdebug)
462*a81c3ea0SDavid du Colombier print("shift\n");
463*a81c3ea0SDavid du Colombier mouseshifted = 1;
464*a81c3ea0SDavid du Colombier return;
465*a81c3ea0SDavid du Colombier case Latin:
466*a81c3ea0SDavid du Colombier kbscan->alt = 1;
467*a81c3ea0SDavid du Colombier /*
468*a81c3ea0SDavid du Colombier * VMware and Qemu use Ctl-Alt as the key combination
469*a81c3ea0SDavid du Colombier * to make the VM give up keyboard and mouse focus.
470*a81c3ea0SDavid du Colombier * This has the unfortunate side effect that when you
471*a81c3ea0SDavid du Colombier * come back into focus, Plan 9 thinks you want to type
472*a81c3ea0SDavid du Colombier * a compose sequence (you just typed alt).
473*a81c3ea0SDavid du Colombier *
474*a81c3ea0SDavid du Colombier * As a clumsy hack around this, we look for ctl-alt
475*a81c3ea0SDavid du Colombier * and don't treat it as the start of a compose sequence.
476*a81c3ea0SDavid du Colombier */
477*a81c3ea0SDavid du Colombier if(!kbscan->ctl){
478*a81c3ea0SDavid du Colombier kbscan->collecting = 1;
479*a81c3ea0SDavid du Colombier kbscan->nk = 0;
480*a81c3ea0SDavid du Colombier }
481*a81c3ea0SDavid du Colombier return;
482*a81c3ea0SDavid du Colombier case Ctrl:
483*a81c3ea0SDavid du Colombier kbscan->ctl = 1;
484*a81c3ea0SDavid du Colombier return;
485*a81c3ea0SDavid du Colombier case Altgr:
486*a81c3ea0SDavid du Colombier kbscan->altgr = 1;
487*a81c3ea0SDavid du Colombier return;
488*a81c3ea0SDavid du Colombier case Kmouse|1:
489*a81c3ea0SDavid du Colombier case Kmouse|2:
490*a81c3ea0SDavid du Colombier case Kmouse|3:
491*a81c3ea0SDavid du Colombier case Kmouse|4:
492*a81c3ea0SDavid du Colombier case Kmouse|5:
493*a81c3ea0SDavid du Colombier kbscan->buttons |= 1<<(c-Kmouse-1);
494*a81c3ea0SDavid du Colombier if(kbdmouse)
495*a81c3ea0SDavid du Colombier kbdmouse(kbscan->buttons);
496*a81c3ea0SDavid du Colombier return;
497*a81c3ea0SDavid du Colombier case KF|11:
498*a81c3ea0SDavid du Colombier print("kbd debug on, F12 turns it off\n");
499*a81c3ea0SDavid du Colombier kdebug = 1;
500*a81c3ea0SDavid du Colombier break;
501*a81c3ea0SDavid du Colombier case KF|12:
502*a81c3ea0SDavid du Colombier kdebug = 0;
503*a81c3ea0SDavid du Colombier break;
504*a81c3ea0SDavid du Colombier }
505*a81c3ea0SDavid du Colombier }
506*a81c3ea0SDavid du Colombier kbdputc(kbdq, c);
507*a81c3ea0SDavid du Colombier }
508*a81c3ea0SDavid du Colombier
i8042intr(Ureg *,void *)509*a81c3ea0SDavid du Colombier /*
510*a81c3ea0SDavid du Colombier * keyboard interrupt
511*a81c3ea0SDavid du Colombier */
512*a81c3ea0SDavid du Colombier static void
513*a81c3ea0SDavid du Colombier i8042intr(Ureg*, void*)
514*a81c3ea0SDavid du Colombier {
515*a81c3ea0SDavid du Colombier int s, c;
516*a81c3ea0SDavid du Colombier
517*a81c3ea0SDavid du Colombier /*
518*a81c3ea0SDavid du Colombier * get status
519*a81c3ea0SDavid du Colombier */
520*a81c3ea0SDavid du Colombier ilock(&i8042lock);
521*a81c3ea0SDavid du Colombier s = inb(Status);
522*a81c3ea0SDavid du Colombier if(!(s&Inready)){
523*a81c3ea0SDavid du Colombier iunlock(&i8042lock);
524*a81c3ea0SDavid du Colombier return;
525*a81c3ea0SDavid du Colombier }
526*a81c3ea0SDavid du Colombier
527*a81c3ea0SDavid du Colombier /*
528*a81c3ea0SDavid du Colombier * get the character
529*a81c3ea0SDavid du Colombier */
530*a81c3ea0SDavid du Colombier c = inb(Data);
531*a81c3ea0SDavid du Colombier iunlock(&i8042lock);
532*a81c3ea0SDavid du Colombier
533*a81c3ea0SDavid du Colombier /*
534*a81c3ea0SDavid du Colombier * if it's the aux port...
535*a81c3ea0SDavid du Colombier */
536*a81c3ea0SDavid du Colombier if(s & Minready){
537*a81c3ea0SDavid du Colombier if(auxputc != nil)
538*a81c3ea0SDavid du Colombier auxputc(c, kbscans[Int].shift);
539*a81c3ea0SDavid du Colombier return;
540*a81c3ea0SDavid du Colombier }
541*a81c3ea0SDavid du Colombier
542*a81c3ea0SDavid du Colombier kbdputsc(c, Int);
543*a81c3ea0SDavid du Colombier }
544*a81c3ea0SDavid du Colombier
545*a81c3ea0SDavid du Colombier void
546*a81c3ea0SDavid du Colombier i8042auxenable(void (*putc)(int, int))
547*a81c3ea0SDavid du Colombier {
548*a81c3ea0SDavid du Colombier char *err = "i8042: aux init failed\n";
549*a81c3ea0SDavid du Colombier
550*a81c3ea0SDavid du Colombier /* enable kbd/aux xfers and interrupts */
551*a81c3ea0SDavid du Colombier ccc &= ~Cauxdis;
552*a81c3ea0SDavid du Colombier ccc |= Cauxint;
553*a81c3ea0SDavid du Colombier
554*a81c3ea0SDavid du Colombier ilock(&i8042lock);
555*a81c3ea0SDavid du Colombier if(outready() < 0)
556*a81c3ea0SDavid du Colombier print(err);
557*a81c3ea0SDavid du Colombier outb(Cmd, 0x60); /* write control register */
558*a81c3ea0SDavid du Colombier if(outready() < 0)
559*a81c3ea0SDavid du Colombier print(err);
560*a81c3ea0SDavid du Colombier outb(Data, ccc);
561*a81c3ea0SDavid du Colombier if(outready() < 0)
562*a81c3ea0SDavid du Colombier print(err);
563*a81c3ea0SDavid du Colombier outb(Cmd, 0xA8); /* auxiliary device enable */
564*a81c3ea0SDavid du Colombier if(outready() < 0){
565*a81c3ea0SDavid du Colombier iunlock(&i8042lock);
566*a81c3ea0SDavid du Colombier return;
567*a81c3ea0SDavid du Colombier }
568*a81c3ea0SDavid du Colombier auxputc = putc;
569*a81c3ea0SDavid du Colombier intrenable(IL8259, i8042intr, 0, IrqAUX);
570*a81c3ea0SDavid du Colombier iunlock(&i8042lock);
571*a81c3ea0SDavid du Colombier }
outbyte(int port,int c)572*a81c3ea0SDavid du Colombier
573*a81c3ea0SDavid du Colombier static char *initfailed = "i8042: kbdinit failed\n";
574*a81c3ea0SDavid du Colombier
575*a81c3ea0SDavid du Colombier static int
576*a81c3ea0SDavid du Colombier outbyte(int port, int c)
577*a81c3ea0SDavid du Colombier {
578*a81c3ea0SDavid du Colombier outb(port, c);
579*a81c3ea0SDavid du Colombier if(outready() < 0) {
580*a81c3ea0SDavid du Colombier print(initfailed);
581*a81c3ea0SDavid du Colombier return -1;
582*a81c3ea0SDavid du Colombier }
kbdinit(void)583*a81c3ea0SDavid du Colombier return 0;
584*a81c3ea0SDavid du Colombier }
585*a81c3ea0SDavid du Colombier
586*a81c3ea0SDavid du Colombier void
587*a81c3ea0SDavid du Colombier kbdinit(void)
588*a81c3ea0SDavid du Colombier {
589*a81c3ea0SDavid du Colombier int c, try;
590*a81c3ea0SDavid du Colombier uchar dummy;
591*a81c3ea0SDavid du Colombier
592*a81c3ea0SDavid du Colombier SET(dummy);
593*a81c3ea0SDavid du Colombier kbdq = qopen(4*1024, 0, 0, 0);
594*a81c3ea0SDavid du Colombier if(kbdq == nil)
595*a81c3ea0SDavid du Colombier panic("kbdinit");
596*a81c3ea0SDavid du Colombier qnoblock(kbdq, 1);
597*a81c3ea0SDavid du Colombier
598*a81c3ea0SDavid du Colombier /* wait for a quiescent controller */
599*a81c3ea0SDavid du Colombier try = 500;
600*a81c3ea0SDavid du Colombier while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
601*a81c3ea0SDavid du Colombier if(c & Inready)
602*a81c3ea0SDavid du Colombier dummy = inb(Data);
603*a81c3ea0SDavid du Colombier delay(1);
604*a81c3ea0SDavid du Colombier }
605*a81c3ea0SDavid du Colombier if (try <= 0) {
606*a81c3ea0SDavid du Colombier print(initfailed);
607*a81c3ea0SDavid du Colombier return;
608*a81c3ea0SDavid du Colombier }
609*a81c3ea0SDavid du Colombier
610*a81c3ea0SDavid du Colombier /* get current controller command byte */
611*a81c3ea0SDavid du Colombier outb(Cmd, 0x20);
612*a81c3ea0SDavid du Colombier if(inready() < 0){
613*a81c3ea0SDavid du Colombier print("i8042: kbdinit can't read ccc\n");
614*a81c3ea0SDavid du Colombier ccc = 0;
615*a81c3ea0SDavid du Colombier } else
616*a81c3ea0SDavid du Colombier ccc = inb(Data);
617*a81c3ea0SDavid du Colombier
618*a81c3ea0SDavid du Colombier /* enable kbd xfers and interrupts */
619*a81c3ea0SDavid du Colombier ccc &= ~Ckbddis;
620*a81c3ea0SDavid du Colombier ccc |= Csf | Ckbdint | Cscs1;
621*a81c3ea0SDavid du Colombier if(outready() < 0) {
622*a81c3ea0SDavid du Colombier print(initfailed);
623*a81c3ea0SDavid du Colombier return;
624*a81c3ea0SDavid du Colombier }
625*a81c3ea0SDavid du Colombier
626*a81c3ea0SDavid du Colombier nokbd = 0;
627*a81c3ea0SDavid du Colombier
628*a81c3ea0SDavid du Colombier /* disable mouse */
629*a81c3ea0SDavid du Colombier if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
630*a81c3ea0SDavid du Colombier print("i8042: kbdinit mouse disable failed\n");
631*a81c3ea0SDavid du Colombier
632*a81c3ea0SDavid du Colombier /* see http://www.computer-engineering.org/ps2keyboard for codes */
633*a81c3ea0SDavid du Colombier if(getconf("*typematic") != nil)
634*a81c3ea0SDavid du Colombier /* set typematic rate/delay (0 -> delay=250ms & rate=30cps) */
635*a81c3ea0SDavid du Colombier if(outbyte(Data, 0xf3) < 0 || outbyte(Data, 0) < 0)
636*a81c3ea0SDavid du Colombier print("i8042: kbdinit set typematic rate failed\n");
637*a81c3ea0SDavid du Colombier
638*a81c3ea0SDavid du Colombier intrenable(IL8259, i8042intr, 0, IrqKBD);
639*a81c3ea0SDavid du Colombier
640*a81c3ea0SDavid du Colombier kbscans[Int].num = 0;
641*a81c3ea0SDavid du Colombier setleds(&kbscans[Int]);
kbdputmap(ushort m,ushort scanc,Rune r)642*a81c3ea0SDavid du Colombier USED(dummy);
643*a81c3ea0SDavid du Colombier }
644*a81c3ea0SDavid du Colombier
645*a81c3ea0SDavid du Colombier void
646*a81c3ea0SDavid du Colombier kbdputmap(ushort m, ushort scanc, Rune r)
647*a81c3ea0SDavid du Colombier {
648*a81c3ea0SDavid du Colombier if(scanc >= Nscan)
649*a81c3ea0SDavid du Colombier error(Ebadarg);
650*a81c3ea0SDavid du Colombier switch(m) {
651*a81c3ea0SDavid du Colombier default:
652*a81c3ea0SDavid du Colombier error(Ebadarg);
653*a81c3ea0SDavid du Colombier case 0:
654*a81c3ea0SDavid du Colombier kbtab[scanc] = r;
655*a81c3ea0SDavid du Colombier break;
656*a81c3ea0SDavid du Colombier case 1:
657*a81c3ea0SDavid du Colombier kbtabshift[scanc] = r;
658*a81c3ea0SDavid du Colombier break;
659*a81c3ea0SDavid du Colombier case 2:
660*a81c3ea0SDavid du Colombier kbtabesc1[scanc] = r;
661*a81c3ea0SDavid du Colombier break;
662*a81c3ea0SDavid du Colombier case 3:
663*a81c3ea0SDavid du Colombier kbtabaltgr[scanc] = r;
664*a81c3ea0SDavid du Colombier break;
665*a81c3ea0SDavid du Colombier case 4:
666*a81c3ea0SDavid du Colombier kbtabctrl[scanc] = r;
667*a81c3ea0SDavid du Colombier break;
kbdgetmap(uint offset,int * t,int * sc,Rune * r)668*a81c3ea0SDavid du Colombier }
669*a81c3ea0SDavid du Colombier }
670*a81c3ea0SDavid du Colombier
671*a81c3ea0SDavid du Colombier int
672*a81c3ea0SDavid du Colombier kbdgetmap(uint offset, int *t, int *sc, Rune *r)
673*a81c3ea0SDavid du Colombier {
674*a81c3ea0SDavid du Colombier if ((int)offset < 0)
675*a81c3ea0SDavid du Colombier error(Ebadarg);
676*a81c3ea0SDavid du Colombier *t = offset/Nscan;
677*a81c3ea0SDavid du Colombier *sc = offset%Nscan;
678*a81c3ea0SDavid du Colombier switch(*t) {
679*a81c3ea0SDavid du Colombier default:
680*a81c3ea0SDavid du Colombier return 0;
681*a81c3ea0SDavid du Colombier case 0:
682*a81c3ea0SDavid du Colombier *r = kbtab[*sc];
683*a81c3ea0SDavid du Colombier return 1;
684*a81c3ea0SDavid du Colombier case 1:
685*a81c3ea0SDavid du Colombier *r = kbtabshift[*sc];
686*a81c3ea0SDavid du Colombier return 1;
687*a81c3ea0SDavid du Colombier case 2:
688*a81c3ea0SDavid du Colombier *r = kbtabesc1[*sc];
689*a81c3ea0SDavid du Colombier return 1;
690*a81c3ea0SDavid du Colombier case 3:
691*a81c3ea0SDavid du Colombier *r = kbtabaltgr[*sc];
692*a81c3ea0SDavid du Colombier return 1;
693*a81c3ea0SDavid du Colombier case 4:
694 *r = kbtabctrl[*sc];
695 return 1;
696 }
697 }
698