census logo
[bootcensus] / src / keyb.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include "keyb.h"
21 #include "intr.h"
22 #include "asmops.h"
23 #include "kbregs.h"
24 #include "kbscan.h"
25 #include "power.h"
26 #include "panic.h"
27
28 #define delay7us() \
29         do { \
30                 iodelay(); iodelay(); iodelay(); iodelay(); \
31                 iodelay(); iodelay(); iodelay(); \
32         } while(0)
33
34 static void set_ccb(unsigned char ccb);
35 static unsigned char get_ccb(void);
36
37 static void kbintr();
38
39 #define BUFSZ   64
40 #define ADVANCE(x)      ((x) = ((x) + 1) & (BUFSZ - 1))
41
42 static int buffer[BUFSZ];
43 static int buf_ridx, buf_widx;
44
45 static unsigned int num_pressed;
46 static unsigned char keystate[256];
47
48
49 void kb_init(void)
50 {
51         buf_ridx = buf_widx = 0;
52         num_pressed = 0;
53         memset(keystate, 0, sizeof keystate);
54
55         /* make sure set1 translation is enabled */
56         kb_set_translate(1);
57
58         interrupt(IRQ_TO_INTR(KB_IRQ), kbintr);
59         kb_intr_enable();
60 }
61
62 void kb_intr_enable(void)
63 {
64         unsigned char ccb = get_ccb();
65         ccb |= KB_CCB_KB_INTREN;
66         set_ccb(ccb);
67 }
68
69 void kb_intr_disable(void)
70 {
71         unsigned char ccb = get_ccb();
72         ccb &= ~KB_CCB_KB_INTREN;
73         set_ccb(ccb);
74 }
75
76 int kb_setmode(int mode)
77 {
78         kb_send_data(0xf0);
79         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
80                 return -1;
81         }
82         kb_send_data(mode);
83         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
84                 return -1;
85         }
86         return 0;
87 }
88
89 int kb_getmode(void)
90 {
91         int mode;
92
93         kb_send_data(0xf0);
94         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
95                 return -1;
96         }
97         kb_send_data(0);
98         if(!kb_wait_read() || kb_read_data() != KB_ACK) {
99                 return -1;
100         }
101         mode = kb_read_data();
102
103         switch(mode) {
104         case 0x43: return 1;
105         case 0x41: return 2;
106         case 0x3f: return 3;
107         default:
108                 break;
109         }
110         return mode;
111 }
112
113 void kb_set_translate(int xlat)
114 {
115         unsigned char ccb = get_ccb();
116         if(xlat) {
117                 ccb |= KB_CCB_KB_XLAT;
118         } else {
119                 ccb &= ~KB_CCB_KB_XLAT;
120         }
121         set_ccb(ccb);
122 }
123
124 int kb_get_translate(void)
125 {
126         return get_ccb() & KB_CCB_KB_XLAT;
127 }
128
129 int kb_isdown(int key)
130 {
131         switch(key) {
132         case KB_ANY:
133                 return num_pressed;
134
135         case KB_ALT:
136                 return keystate[KB_LALT] + keystate[KB_RALT];
137
138         case KB_CTRL:
139                 return keystate[KB_LCTRL] + keystate[KB_RCTRL];
140         }
141         return keystate[key];
142 }
143
144 void kb_wait(void)
145 {
146         int key;
147         while((key = kb_getkey()) == -1) {
148                 /* put the processor to sleep while waiting for keypresses, but first
149                  * make sure interrupts are enabled, or we'll sleep forever
150                  */
151                 enable_intr();
152                 halt_cpu();
153         }
154         kb_putback(key);
155 }
156
157 int kb_getkey(void)
158 {
159         int res;
160
161         if(buf_ridx == buf_widx) {
162                 return -1;
163         }
164         res = buffer[buf_ridx];
165         ADVANCE(buf_ridx);
166         return res;
167 }
168
169 void kb_putback(int key)
170 {
171         /* go back a place */
172         if(--buf_ridx < 0) {
173                 buf_ridx += BUFSZ;
174         }
175
176         /* if the write end hasn't caught up with us, go back one place
177          * and put it there, otherwise just overwrite the oldest key which
178          * is right where we were.
179          */
180         if(buf_ridx == buf_widx) {
181                 ADVANCE(buf_ridx);
182         }
183
184         buffer[buf_ridx] = key;
185 }
186
187 int kb_wait_write(void)
188 {
189         int i;
190         for(i=0; i<32768; i++) {
191                 if(!(inb(KB_STATUS_PORT) & KB_STAT_INBUF_FULL)) {
192                         return 1;
193                 }
194                 iodelay();
195         }
196         /*printf("kb_wait_write timeout\n");*/
197         return 0;
198 }
199
200 int kb_wait_read(void)
201 {
202         int i;
203         for(i=0; i<32768; i++) {
204                 if((inb(KB_STATUS_PORT) & KB_STAT_OUTBUF_FULL)) {
205                         return 1;
206                 }
207                 iodelay();
208         }
209         /*printf("kb_wait_read timeout\n");*/
210         return 0;
211 }
212
213 void kb_send_cmd(unsigned char cmd)
214 {
215         kb_wait_write();
216         outb(cmd, KB_CMD_PORT);
217 }
218
219 void kb_send_data(unsigned char data)
220 {
221         kb_wait_write();
222         outb(data, KB_DATA_PORT);
223 }
224
225 unsigned char kb_read_data(void)
226 {
227         kb_wait_read();
228         delay7us();
229         return inb(KB_DATA_PORT);
230 }
231
232 static void set_ccb(unsigned char ccb)
233 {
234         kb_send_cmd(KB_CMD_SET_CMDBYTE);
235         kb_send_data(ccb);
236
237         if(kb_wait_read()) {
238                 kb_read_data();
239         }
240 }
241
242 static unsigned char get_ccb(void)
243 {
244         kb_send_cmd(KB_CMD_GET_CMDBYTE);
245         return kb_read_data();
246 }
247
248 static void kbintr()
249 {
250         unsigned char code;
251         int key, press;
252         static int ext = 0;
253
254         code = inb(KB_DATA_PORT);
255
256         if(code == 0xe0) {
257                 ext = 1;
258                 return;
259         }
260
261         if(code & 0x80) {
262                 press = 0;
263                 code &= 0x7f;
264
265                 if(num_pressed > 0) {
266                         num_pressed--;
267                 }
268         } else {
269                 press = 1;
270
271                 num_pressed++;
272         }
273
274         key = ext ? scantbl_set1_ext[code] : scantbl_set1[code];
275         ext = 0;
276
277         if(press) {
278                 if(key == KB_DEL && (keystate[KB_LALT] || keystate[KB_RALT]) && (keystate[KB_LCTRL] || keystate[KB_RCTRL])) {
279                         reboot();
280                 }
281                 /* append to buffer */
282                 buffer[buf_widx] = key;
283                 ADVANCE(buf_widx);
284                 /* if the write end overtook the read end, advance the read end
285                  * too, to discard the oldest keypress from the buffer
286                  */
287                 if(buf_widx == buf_ridx) {
288                         ADVANCE(buf_ridx);
289                 }
290         }
291
292         /* and update keystate table */
293         keystate[key] = press;
294 }