-
Notifications
You must be signed in to change notification settings - Fork 0
/
keyboard.c
257 lines (214 loc) · 8.43 KB
/
keyboard.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#include <stddef.h>
#include <stdint.h>
#include "keyboard.h"
#include "ports.h"
#include "buffer.h"
#include "ps2.h"
#include "idt.h"
#include "sys.h"
#include "heap.h"
#include "uni_list.h"
#include "clib/stdio.h"
// Lista funkcji będących słuchaczami zdarzeń
UNI_LIST_C(kblistener, kb_listener_t)
// Funkcje statyczne
static int keyboard_is_printable(unsigned char c);
static char convert_scancode_to_ascii(unsigned char c, int shift);
static void keyboard_set_led(int ScrollLock, int NumberLock, int CapsLock);
static void keyboard_set_typematic(unsigned int rate, unsigned int delay);
static void notify_listeners_about_scancode(uint8_t scancode, int extra);
#define NOSHIFT_CODE 0
#define SHIFT_CODE 1
// Tabela służąca do zamiany kodów skaningowych na kody ascii
unsigned char translation_table[0x100][2] = {
[0x1E] = {'a', 'A'}, [0x30] = {'b', 'B'}, [0x2E] = {'c', 'C'} , [0x20] = {'d', 'D'}, [0x12] = {'e', 'E'},
[0x21] = {'f', 'F'}, [0x22] = {'g', 'G'}, [0x23] = {'h', 'H'} , [0x17] = {'i', 'I'}, [0x24] = {'j', 'J'},
[0x25] = {'k', 'K'}, [0x26] = {'l', 'L'}, [0x32] = {'m', 'M'} , [0x31] = {'n', 'N'}, [0x18] = {'o', 'O'},
[0x19] = {'p', 'P'}, [0x10] = {'q', 'Q'}, [0x13] = {'r', 'R'} , [0x1F] = {'s', 'S'}, [0x14] = {'t', 'T'},
[0x16] = {'u', 'U'}, [0x2F] = {'v', 'V'}, [0x11] = {'w', 'W'} , [0x2D] = {'x', 'X'}, [0x15] = {'y', 'Y'},
[0x2c] = {'z', 'Z'}, [0x0B] = {'0', ')'}, [0x02] = {'1', '!'} , [0x03] = {'2', '@'}, [0x04] = {'3', '#'},
[0x05] = {'4', '$'}, [0x06] = {'5', '%'}, [0x07] = {'6', '^'} , [0x08] = {'7', '&'}, [0x09] = {'8', '*'},
[0x0A] = {'9', '('}, [0x39] = {' ', ' '}, [0x52] = {'0', '0'} , [0x4F] = {'1', '1'}, [0x50] = {'2', '2'},
[0x51] = {'3', '3'}, [0x4B] = {'4', '4'}, [0x4C] = {'5', '5'} , [0x4D] = {'6', '6'}, [0x47] = {'7', '7'},
[0x48] = {'8', '8'}, [0x49] = {'9', '9'}, [0x8D] = {'=', '+'} , [0x0C] = {'-', '_'}, [0x1A] = {'[', '{'},
[0x34] = {'.', '>'}, [0x35] = {'/', '?'}, [0x37] = {'*', '*'} , [0x4A] = {'-', '-'}, [0x4E] = {'+', '+'},
[0x1B] = {']', '}'}, [0x27] = {';', ':'}, [0x28] = {'\'', '"'}, [0x2B] = {'\\', '|'}, [0x33] = {',', '<'},
[0x53] = {',', ','}
};
// tablice słuchaczy zdarzeń
struct list_kblistener_t *kblisteners_with_extra[0x100] = { NULL };
struct list_kblistener_t *kblisteners_without_extra[0x100] = { NULL };
// Flagi mówiące czy któryś z tych klawiszy jest wciśnięty
int lshift_flag = 0;
int rshift_flag = 0;
int capslock_flag = 0;
int numlock_flag = 0;
int scrolllock_flag = 0;
// Stan "Automatu"
int state = 0;
// Bufor klawiatury
struct buffer_t keyboard_buffer;
// Inicjalizacja klawiatury
void keyboard_initialize(void)
{
buffer_initialize(&keyboard_buffer);
asm("cli");
// Wyłączamy urządzeń, żeby nie przeszkadzały w konfiguracji
ps2_write_command(COMMAND_DISABLE_FIRST_PORT);
ps2_write_command(COMMAND_DISABLE_SECOND_PORT);
// Włączenie przerwania klawiatury - chociaż zazwyczaj jest już włączone
uint8_t config_byte = ps2_get_config_byte();
config_byte |= CONFIG_FIRST_PORT_INTERRUPT;
ps2_set_config_byte(config_byte);
// Zmiana częstotliwości powtarzania klawiszy
keyboard_set_typematic(REPEAT_RATE_2HZ, DELAY_500);
// Wykonanie testu klawiatury
ps2_write_command(COMMAND_TEST_FIRST_PORT);
uint8_t test_result = ps2_read_data();
if(test_result == DEVICE_TEST_PASSED) printf("PS2 Keyboard Test Passed\n");
else report_error("PS2 Keyboard Test Failed");
// Włączenie urządzeń po skończeniu konfiguracji
ps2_write_command(COMMAND_ENABLE_FIRST_PORT);
ps2_write_command(COMMAND_ENABLE_SECOND_PORT);
asm("sti");
interrupt_register(33, keyboard_interrupt_handler);
printf("Keyboard ready\n");
}
// Zwraca bufor klawiatury
struct buffer_t *keyboard_get_buffer(void)
{
return &keyboard_buffer;
}
// Procedura obsługi przerwania klawiatury
void keyboard_interrupt_handler(void)
{
// Odczytuje kod skaningowy z buforu
unsigned char scancode = inportb(PS2_DATA_PORT);
// Sekwencja kodów, zakładamy max 2
if(scancode == VK_EXTRA_CODE)
{
state = 1;
return;
}
// Kody dwuskładnikowe
if(state)
{
state = 0;
notify_listeners_about_scancode(scancode, 1);
}
// Kody jednoskładnikowe
else
{
// Aktualizuję flagi klawiszy specialnych
if(scancode == VK_LEFT_SHIFT)
lshift_flag = 1;
else if(scancode == RELEASED(VK_LEFT_SHIFT))
lshift_flag = 0;
else if(scancode == VK_RIGHT_SHIFT)
rshift_flag = 1;
else if(scancode == RELEASED(VK_RIGHT_SHIFT))
rshift_flag = 0;
else if(scancode == VK_CAPS_LOCK)
capslock_flag = !capslock_flag;
else if(scancode == VK_NUMBER_LOCK)
numlock_flag = !numlock_flag;
else if(scancode == VK_SCROLL_LOCK)
scrolllock_flag = !scrolllock_flag;
// Aktualizuje diody LED
if(scancode == VK_CAPS_LOCK || scancode == VK_NUMBER_LOCK || scancode == VK_SCROLL_LOCK)
keyboard_set_led(scrolllock_flag, numlock_flag, capslock_flag);
// Umieszczanie w buforze drukowalnych znaków
if(keyboard_is_printable(scancode))
{
char character = convert_scancode_to_ascii(scancode, lshift_flag | rshift_flag | capslock_flag);
buffer_put(&keyboard_buffer, character);
}
if(scancode == VK_ENTER)
buffer_put(&keyboard_buffer, '\n');
if(scancode == VK_BACKSPACE)
buffer_put(&keyboard_buffer, '\b');
notify_listeners_about_scancode(scancode, 0);
}
}
// Ustawia szybkość i opóźnienie powtarzania klawiszy
// Funkcja nie daje efektu, chociaż kontroler potwierdza wykonanie. Zakładam, że to wina systemu hosta, który na to nie pozwala.
static void keyboard_set_typematic(unsigned int rate, unsigned int delay)
{
uint8_t typematic_byte = rate || delay << 4;
ps2_write_command(COMMAND_KB_SET_TYPEMATIC);
ps2_write_data(typematic_byte);
uint8_t byte = ps2_read_data();
}
// Steruje diodami LED - zakładam że działa, w laptopie nie mam diód
static void keyboard_set_led(int ScrollLock, int NumberLock, int CapsLock)
{
uint8_t led_byte = ScrollLock | NumberLock << 1 | CapsLock << 2;
ps2_write_command(COMMAND_KB_SET_LED);
ps2_write_data(led_byte);
ps2_read_data();
}
// Sprawdza czy podany kod skaningowy odpowiada klawiszowi który da się wydrukować
static int keyboard_is_printable(unsigned char c)
{
if(translation_table[c][0]!=0) return 1;
return 0;
}
// Zamienia kod skaningowy na kod ascii
static char convert_scancode_to_ascii(unsigned char c, int shift)
{
if(shift)
return translation_table[c][SHIFT_CODE];
else
return translation_table[c][NOSHIFT_CODE];
return 0;
}
// Wywołuje wszystkie funkcje związane z danym przyciskiem
static void notify_listeners_about_scancode(uint8_t scancode, int extra)
{
struct list_kblistener_t *list = NULL;
if(extra) list = kblisteners_with_extra[scancode];
else list = kblisteners_without_extra[scancode];
if(list == NULL) return;
struct node_kblistener_t *current = list->head;
while(current!=NULL)
{
kb_listener_t listener = current->data;
listener();
current = current->next;
}
}
// Funkcja dodaje nowego słuchacza czekającego na scancode
void keyboard_register_listener(uint8_t scancode, int extra, kb_listener_t listener)
{
// Wybiera odpowiednią liste
struct list_kblistener_t **list = NULL;
if(extra) list = &kblisteners_with_extra[scancode];
else list = &kblisteners_without_extra[scancode];
// Do kodu nie ma przypisanych jeszcz zdarzeń
if(*list == NULL)
{
*list = list_kblistener_create();
list_kblistener_push_back(*list, listener);
}
// Do kodu jest już przypisane jakieś zdarzenie
else
list_kblistener_push_back(*list, listener);
}
// Funkcja usówa słuchacza
void keyboard_unregister_listener(uint8_t scancode, int extra, kb_listener_t listener)
{
// Wybiera odpowiednią liste
struct list_kblistener_t *list = NULL;
if(extra) list = kblisteners_with_extra[scancode];
else list = kblisteners_without_extra[scancode];
struct node_kblistener_t *current = list->head;
while(current!=NULL)
{
if(current->data == listener)
{
list_kblistener_remove_node(list, current);
if(list->size==0) list = NULL;
}
current = current->next;
}
}