Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
4 * Copyright (c) 2021, Edwin Hoksberg <mail@edwinhoksberg.nl>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <AK/Assertions.h>
10#include <AK/Types.h>
11#include <Kernel/API/Ioctl.h>
12#include <Kernel/Devices/HID/KeyboardDevice.h>
13#include <Kernel/Sections.h>
14#include <Kernel/TTY/VirtualConsole.h>
15
16namespace Kernel {
17
18static constexpr KeyCode unshifted_key_map[0x80] = {
19 Key_Invalid,
20 Key_Escape,
21 Key_1,
22 Key_2,
23 Key_3,
24 Key_4,
25 Key_5,
26 Key_6,
27 Key_7,
28 Key_8,
29 Key_9,
30 Key_0,
31 Key_Minus,
32 Key_Equal,
33 Key_Backspace,
34 Key_Tab, // 15
35 Key_Q,
36 Key_W,
37 Key_E,
38 Key_R,
39 Key_T,
40 Key_Y,
41 Key_U,
42 Key_I,
43 Key_O,
44 Key_P,
45 Key_LeftBracket,
46 Key_RightBracket,
47 Key_Return, // 28
48 Key_Control, // 29
49 Key_A,
50 Key_S,
51 Key_D,
52 Key_F,
53 Key_G,
54 Key_H,
55 Key_J,
56 Key_K,
57 Key_L,
58 Key_Semicolon,
59 Key_Apostrophe,
60 Key_Backtick,
61 Key_LeftShift, // 42
62 Key_Backslash,
63 Key_Z,
64 Key_X,
65 Key_C,
66 Key_V,
67 Key_B,
68 Key_N,
69 Key_M,
70 Key_Comma,
71 Key_Period,
72 Key_Slash,
73 Key_RightShift, // 54
74 Key_Asterisk,
75 Key_Alt, // 56
76 Key_Space, // 57
77 Key_CapsLock, // 58
78 Key_F1,
79 Key_F2,
80 Key_F3,
81 Key_F4,
82 Key_F5,
83 Key_F6,
84 Key_F7,
85 Key_F8,
86 Key_F9,
87 Key_F10,
88 Key_NumLock,
89 Key_Invalid, // 70
90 Key_Home,
91 Key_Up,
92 Key_PageUp,
93 Key_Minus,
94 Key_Left,
95 Key_Invalid,
96 Key_Right, // 77
97 Key_Plus,
98 Key_End,
99 Key_Down, // 80
100 Key_PageDown,
101 Key_Insert,
102 Key_Delete, // 83
103 Key_Invalid,
104 Key_Invalid,
105 Key_Backslash,
106 Key_F11,
107 Key_F12,
108 Key_Invalid,
109 Key_Invalid,
110 Key_Super,
111 Key_Invalid,
112 Key_Menu,
113};
114
115static constexpr KeyCode shifted_key_map[0x100] = {
116 Key_Invalid,
117 Key_Escape,
118 Key_ExclamationPoint,
119 Key_AtSign,
120 Key_Hashtag,
121 Key_Dollar,
122 Key_Percent,
123 Key_Circumflex,
124 Key_Ampersand,
125 Key_Asterisk,
126 Key_LeftParen,
127 Key_RightParen,
128 Key_Underscore,
129 Key_Plus,
130 Key_Backspace,
131 Key_Tab,
132 Key_Q,
133 Key_W,
134 Key_E,
135 Key_R,
136 Key_T,
137 Key_Y,
138 Key_U,
139 Key_I,
140 Key_O,
141 Key_P,
142 Key_LeftBrace,
143 Key_RightBrace,
144 Key_Return,
145 Key_Control,
146 Key_A,
147 Key_S,
148 Key_D,
149 Key_F,
150 Key_G,
151 Key_H,
152 Key_J,
153 Key_K,
154 Key_L,
155 Key_Colon,
156 Key_DoubleQuote,
157 Key_Tilde,
158 Key_LeftShift, // 42
159 Key_Pipe,
160 Key_Z,
161 Key_X,
162 Key_C,
163 Key_V,
164 Key_B,
165 Key_N,
166 Key_M,
167 Key_LessThan,
168 Key_GreaterThan,
169 Key_QuestionMark,
170 Key_RightShift, // 54
171 Key_Asterisk,
172 Key_Alt,
173 Key_Space, // 57
174 Key_CapsLock, // 58
175 Key_F1,
176 Key_F2,
177 Key_F3,
178 Key_F4,
179 Key_F5,
180 Key_F6,
181 Key_F7,
182 Key_F8,
183 Key_F9,
184 Key_F10,
185 Key_NumLock,
186 Key_Invalid, // 70
187 Key_Home,
188 Key_Up,
189 Key_PageUp,
190 Key_Minus,
191 Key_Left,
192 Key_Invalid,
193 Key_Right, // 77
194 Key_Plus,
195 Key_End,
196 Key_Down, // 80
197 Key_PageDown,
198 Key_Insert,
199 Key_Delete, // 83
200 Key_Invalid,
201 Key_Invalid,
202 Key_Pipe,
203 Key_F11,
204 Key_F12,
205 Key_Invalid,
206 Key_Invalid,
207 Key_Super,
208 Key_Invalid,
209 Key_Menu,
210};
211
212void KeyboardDevice::key_state_changed(u8 scan_code, bool pressed)
213{
214 KeyCode key = (m_modifiers & Mod_Shift) ? shifted_key_map[scan_code] : unshifted_key_map[scan_code];
215
216 if (key == Key_NumLock && pressed)
217 m_num_lock_on = !m_num_lock_on;
218
219 if (m_num_lock_on && !m_has_e0_prefix) {
220 if (scan_code >= 0x47 && scan_code <= 0x53) {
221 u8 index = scan_code - 0x47;
222 constexpr KeyCode numpad_key_map[13] = { Key_7, Key_8, Key_9, Key_Invalid, Key_4, Key_5, Key_6, Key_Invalid, Key_1, Key_2, Key_3, Key_0, Key_Comma };
223 KeyCode newKey = numpad_key_map[index];
224
225 if (newKey != Key_Invalid) {
226 key = newKey;
227 }
228 }
229 }
230
231 Event event;
232 event.key = key;
233 event.scancode = m_has_e0_prefix ? 0xe000 + scan_code : scan_code;
234 event.flags = m_modifiers;
235 event.e0_prefix = m_has_e0_prefix;
236 event.caps_lock_on = m_caps_lock_on;
237 event.code_point = HIDManagement::the().get_char_from_character_map(event);
238
239 // If using a non-QWERTY layout, event.key needs to be updated to be the same as event.code_point
240 KeyCode mapped_key = code_point_to_key_code(event.code_point);
241 if (mapped_key != KeyCode::Key_Invalid) {
242 event.key = mapped_key;
243 key = mapped_key;
244 }
245
246 if (!g_caps_lock_remapped_to_ctrl && key == Key_CapsLock && pressed)
247 m_caps_lock_on = !m_caps_lock_on;
248
249 if (g_caps_lock_remapped_to_ctrl && key == Key_CapsLock)
250 m_caps_lock_to_ctrl_pressed = pressed;
251
252 if (g_caps_lock_remapped_to_ctrl)
253 update_modifier(Mod_Ctrl, m_caps_lock_to_ctrl_pressed);
254
255 if (pressed)
256 event.flags |= Is_Press;
257
258 {
259 SpinlockLocker locker(HIDManagement::the().m_client_lock);
260 if (HIDManagement::the().m_client)
261 HIDManagement::the().m_client->on_key_pressed(event);
262 }
263
264 {
265 SpinlockLocker lock(m_queue_lock);
266 m_queue.enqueue(event);
267 }
268
269 m_has_e0_prefix = false;
270
271 evaluate_block_conditions();
272}
273
274// FIXME: UNMAP_AFTER_INIT is fine for now, but for hot-pluggable devices
275// like USB keyboards, we need to remove this
276UNMAP_AFTER_INIT KeyboardDevice::KeyboardDevice()
277 : HIDDevice(85, HIDManagement::the().generate_minor_device_number_for_keyboard())
278{
279}
280
281// FIXME: UNMAP_AFTER_INIT is fine for now, but for hot-pluggable devices
282// like USB keyboards, we need to remove this
283UNMAP_AFTER_INIT KeyboardDevice::~KeyboardDevice() = default;
284
285bool KeyboardDevice::can_read(OpenFileDescription const&, u64) const
286{
287 return !m_queue.is_empty();
288}
289
290ErrorOr<size_t> KeyboardDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size)
291{
292 size_t nread = 0;
293 SpinlockLocker lock(m_queue_lock);
294 while (nread < size) {
295 if (m_queue.is_empty())
296 break;
297 // Don't return partial data frames.
298 if (size - nread < sizeof(Event))
299 break;
300 auto event = m_queue.dequeue();
301
302 lock.unlock();
303
304 auto result = TRY(buffer.write_buffered<sizeof(Event)>(sizeof(Event), [&](Bytes bytes) {
305 memcpy(bytes.data(), &event, sizeof(Event));
306 return bytes.size();
307 }));
308 VERIFY(result == sizeof(Event));
309 nread += sizeof(Event);
310
311 lock.lock();
312 }
313 return nread;
314}
315
316ErrorOr<void> KeyboardDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
317{
318 switch (request) {
319 case KEYBOARD_IOCTL_GET_NUM_LOCK: {
320 auto output = static_ptr_cast<bool*>(arg);
321 return copy_to_user(output, &m_num_lock_on);
322 }
323 case KEYBOARD_IOCTL_SET_NUM_LOCK: {
324 // In this case we expect the value to be a boolean and not a pointer.
325 auto num_lock_value = static_cast<u8>(arg.ptr());
326 if (num_lock_value != 0 && num_lock_value != 1)
327 return EINVAL;
328 m_num_lock_on = !!num_lock_value;
329 return {};
330 }
331 case KEYBOARD_IOCTL_GET_CAPS_LOCK: {
332 auto output = static_ptr_cast<bool*>(arg);
333 return copy_to_user(output, &m_caps_lock_on);
334 }
335 case KEYBOARD_IOCTL_SET_CAPS_LOCK: {
336 auto caps_lock_value = static_cast<u8>(arg.ptr());
337 if (caps_lock_value != 0 && caps_lock_value != 1)
338 return EINVAL;
339 m_caps_lock_on = !!caps_lock_value;
340 return {};
341 }
342 default:
343 return EINVAL;
344 };
345}
346
347}