1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2006-09-15     QiuYi        the first version
9  * 2017-08-16     Parai        the 2nd version
10  */
11 
12 #include <rtthread.h>
13 #include <rthw.h>
14 
15 #include <bsp.h>
16 #include "keyboard.h"
17 #include "keymap.h"
18 
19 #define FALSE RT_FALSE
20 #define TRUE  RT_TRUE
21 #define PRIVATE static
22 #define PUBLIC
23 #define t_bool  rt_bool_t
24 #define t_8     rt_uint8_t
25 #define t_32    rt_uint32_t
26 
27 PRIVATE KB_INPUT    kb_in;
28 PRIVATE t_bool      code_with_E0    = FALSE;
29 PRIVATE t_bool      shift_l;        /* l shift state    */
30 PRIVATE t_bool      shift_r;        /* r shift state    */
31 PRIVATE t_bool      alt_l;          /* l alt state      */
32 PRIVATE t_bool      alt_r;          /* r left state     */
33 PRIVATE t_bool      ctrl_l;         /* l ctrl state     */
34 PRIVATE t_bool      ctrl_r;         /* l ctrl state     */
35 PRIVATE t_bool      caps_lock;      /* Caps Lock        */
36 PRIVATE t_bool      num_lock;       /* Num Lock     */
37 PRIVATE t_bool      scroll_lock;        /* Scroll Lock      */
38 PRIVATE int     column      = 0;    /* keyrow[column] is one value of keymap */
39 
40 PRIVATE t_8 get_byte_from_kb_buf();
41 PRIVATE void    set_leds();
42 PRIVATE void    kb_wait();
43 PRIVATE void    kb_ack();
44 
init_keyboard()45 PUBLIC void init_keyboard()
46 {
47     kb_in.count = 0;
48     kb_in.p_head = kb_in.p_tail = kb_in.buf;
49 
50     caps_lock   = 0;
51     num_lock    = 1;
52     scroll_lock = 0;
53 
54     set_leds();
55 }
keyboard_read(rt_uint32_t * pkey)56 PUBLIC rt_bool_t keyboard_read(rt_uint32_t *pkey)
57 {
58     t_8 scan_code;
59     t_bool  make;   /* TRUE : make  */
60             /* FALSE: break */
61     t_32    key = 0;
62     t_32*   keyrow;
63 
64     if(kb_in.count > 0){
65         code_with_E0 = FALSE;
66         scan_code = get_byte_from_kb_buf();
67 
68         /* start scan */
69         if (scan_code == 0xE1) {
70             int i;
71             static const t_8 pausebreak_scan_code[] = {0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5};
72             t_bool is_pausebreak = TRUE;
73             for(i=1;i<6;i++){
74                 if (get_byte_from_kb_buf() != pausebreak_scan_code[i]) {
75                     is_pausebreak = FALSE;
76                     break;
77                 }
78             }
79             if (is_pausebreak) {
80                 key = PAUSEBREAK;
81             }
82         }
83         else if (scan_code == 0xE0) {
84             code_with_E0 = TRUE;
85             scan_code = get_byte_from_kb_buf();
86 
87             /* PrintScreen pressed */
88             if (scan_code == 0x2A) {
89                 code_with_E0 = FALSE;
90                 if ((scan_code = get_byte_from_kb_buf()) == 0xE0) {
91                     code_with_E0 = TRUE;
92                     if ((scan_code = get_byte_from_kb_buf()) == 0x37) {
93                         key = PRINTSCREEN;
94                         make = TRUE;
95                     }
96                 }
97             }
98             /* PrintScreen released */
99             else if (scan_code == 0xB7) {
100                 code_with_E0 = FALSE;
101                 if ((scan_code = get_byte_from_kb_buf()) == 0xE0) {
102                     code_with_E0 = TRUE;
103                     if ((scan_code = get_byte_from_kb_buf()) == 0xAA) {
104                         key = PRINTSCREEN;
105                         make = FALSE;
106                     }
107                 }
108             }
109         } /* if is not PrintScreen, scan_code is the one after 0xE0 */
110         if ((key != PAUSEBREAK) && (key != PRINTSCREEN)) {
111             /* is Make Code or Break Code */
112             make = (scan_code & FLAG_BREAK ? FALSE : TRUE);
113 
114             keyrow = &keymap[(scan_code & 0x7F) * MAP_COLS];
115 
116             column = 0;
117 
118             t_bool caps = shift_l || shift_r;
119             if (caps_lock) {
120                 if ((keyrow[0] >= 'a') && (keyrow[0] <= 'z')){
121                     caps = !caps;
122                 }
123             }
124             if (caps) {
125                 column = 1;
126             }
127 
128             if (code_with_E0) {
129                 column = 2;
130             }
131 
132             key = keyrow[column];
133 
134             switch(key) {
135             case SHIFT_L:
136                 shift_l = make;
137                 break;
138             case SHIFT_R:
139                 shift_r = make;
140                 break;
141             case CTRL_L:
142                 ctrl_l  = make;
143                 break;
144             case CTRL_R:
145                 ctrl_r  = make;
146                 break;
147             case ALT_L:
148                 alt_l   = make;
149                 break;
150             case ALT_R:
151                 alt_l   = make;
152                 break;
153             case CAPS_LOCK:
154                 if (make) {
155                     caps_lock   = !caps_lock;
156                     set_leds();
157                 }
158                 break;
159             case NUM_LOCK:
160                 if (make) {
161                     num_lock    = !num_lock;
162                     set_leds();
163                 }
164                 break;
165             case SCROLL_LOCK:
166                 if (make) {
167                     scroll_lock = !scroll_lock;
168                     set_leds();
169                 }
170                 break;
171             default:
172                 break;
173             }
174         }
175 
176         if(make){ /* ignore Break Code */
177             t_bool pad = FALSE;
178 
179             /* handle the small pad first */
180             if ((key >= PAD_SLASH) && (key <= PAD_9)) {
181                 pad = TRUE;
182                 switch(key) {   /* '/', '*', '-', '+', and 'Enter' in num pad  */
183                 case PAD_SLASH:
184                     key = '/';
185                     break;
186                 case PAD_STAR:
187                     key = '*';
188                     break;
189                 case PAD_MINUS:
190                     key = '-';
191                     break;
192                 case PAD_PLUS:
193                     key = '+';
194                     break;
195                 case PAD_ENTER:
196                     key = ENTER;
197                     break;
198                 default:    /* keys whose value depends on the NumLock */
199                     if (num_lock) { /* '0' ~ '9' and '.' in num pad */
200                         if ((key >= PAD_0) && (key <= PAD_9)) {
201                             key = key - PAD_0 + '0';
202                         }
203                         else if (key == PAD_DOT) {
204                             key = '.';
205                         }
206                     }
207                     else{
208                         switch(key) {
209                         case PAD_HOME:
210                             key = HOME;
211                             break;
212                         case PAD_END:
213                             key = END;
214                             break;
215                         case PAD_PAGEUP:
216                             key = PAGEUP;
217                             break;
218                         case PAD_PAGEDOWN:
219                             key = PAGEDOWN;
220                             break;
221                         case PAD_INS:
222                             key = INSERT;
223                             break;
224                         case PAD_UP:
225                             key = UP;
226                             break;
227                         case PAD_DOWN:
228                             key = DOWN;
229                             break;
230                         case PAD_LEFT:
231                             key = LEFT;
232                             break;
233                         case PAD_RIGHT:
234                             key = RIGHT;
235                             break;
236                         case PAD_DOT:
237                             key = DELETE;
238                             break;
239                         default:
240                             break;
241                         }
242                     }
243                     break;
244                 }
245             }
246             key |= shift_l  ? FLAG_SHIFT_L  : 0;
247             key |= shift_r  ? FLAG_SHIFT_R  : 0;
248             key |= ctrl_l   ? FLAG_CTRL_L   : 0;
249             key |= ctrl_r   ? FLAG_CTRL_R   : 0;
250             key |= alt_l    ? FLAG_ALT_L    : 0;
251             key |= alt_r    ? FLAG_ALT_R    : 0;
252             key |= pad  ? FLAG_PAD  : 0;
253 
254             *pkey = key;
255             return TRUE;
256         }
257     }
258 
259     return FALSE;
260 }
261 
get_byte_from_kb_buf()262 PRIVATE t_8 get_byte_from_kb_buf()
263 {
264     t_8 scan_code;
265 
266     RT_ASSERT(kb_in.count>0);
267     scan_code = *(kb_in.p_tail);
268     kb_in.p_tail++;
269     if (kb_in.p_tail == kb_in.buf + KB_IN_BYTES) {
270         kb_in.p_tail = kb_in.buf;
271     }
272     kb_in.count--;
273 
274     return scan_code;
275 }
276 
kb_wait()277 PRIVATE void kb_wait() /* wait inpit cache of 8042 */
278 {
279     t_8 kb_stat;
280 
281     do {
282         kb_stat = inb(KB_CMD);
283     } while (kb_stat & 0x02);
284 }
285 
kb_ack()286 PRIVATE void kb_ack()
287 {
288     t_8 kb_read;
289 
290     do {
291         kb_read = inb(KB_DATA);
292     } while (kb_read != KB_ACK);
293 }
294 
set_leds()295 PRIVATE void set_leds()
296 {
297     t_8 leds = (caps_lock << 2) | (num_lock << 1) | scroll_lock;
298 
299     kb_wait();
300     outb(KB_DATA, LED_CODE);
301     kb_ack();
302 
303     kb_wait();
304     outb(KB_DATA, leds);
305     kb_ack();
306 }
307 
308 /**
309  * @addtogroup QEMU
310  */
311 /*@{*/
312 
rt_keyboard_isr(void)313 void rt_keyboard_isr(void)
314 {
315     rt_uint8_t data;
316 
317     if ((inb(KBSTATP) & KBS_DIB) == 0)
318         return ;
319 
320     data = inb(KBDATAP);
321 
322     if (kb_in.count < KB_IN_BYTES) {
323         *(kb_in.p_head) = data;
324         kb_in.p_head++;
325         if (kb_in.p_head == kb_in.buf + KB_IN_BYTES) {
326             kb_in.p_head = kb_in.buf;
327         }
328         kb_in.count++;
329     }
330 }
331 /* generally, this should be called in task level for all key inpit support,
332 but here only support a key that is composed of 2 bytes */
rt_keyboard_getc(char * c)333 rt_bool_t rt_keyboard_getc(char* c)
334 {
335     if(kb_in.count>=2)
336     {
337         rt_uint32_t key = 0;
338         rt_bool_t rv=keyboard_read(&key);
339 
340         switch(key)
341         {
342             case TAB:
343                 *c = '\t';
344                 break;
345             case ENTER:
346                 *c = '\n';
347                 break;
348             case BACKSPACE:
349                 *c = '\b';
350                 break;
351             default:
352                 *c = key;
353                 break;
354         }
355 
356         return rv;
357     }
358 
359     return RT_FALSE;
360 }
361 
362 /*@}*/
363