1#include <console/printf.h>
2#include <drivers/usb/hid.h>
3#include <drivers/usb/kb.h>
4#include <drivers/usb/usb.h>
5#include <drivers/usb/xhci.h>
6#include <log.h>
7#include <math/bit.h>
8#include <mem/alloc.h>
9#include <mem/page.h>
10#include <mem/pmm.h>
11#include <mem/vmm.h>
12#include <sch/sched.h>
13#include <string.h>
14#include <thread/thread.h>
15#include <time/spin_sleep.h>
16
17static const char keycode_to_ascii[256] = {
18 [0x04] = 'a', [0x05] = 'b', [0x06] = 'c', [0x07] = 'd', [0x08] = 'e',
19 [0x09] = 'f', [0x0A] = 'g', [0x0B] = 'h', [0x0C] = 'i', [0x0D] = 'j',
20 [0x0E] = 'k', [0x0F] = 'l', [0x10] = 'm', [0x11] = 'n', [0x12] = 'o',
21 [0x13] = 'p', [0x14] = 'q', [0x15] = 'r', [0x16] = 's', [0x17] = 't',
22 [0x18] = 'u', [0x19] = 'v', [0x1A] = 'w', [0x1B] = 'x', [0x1C] = 'y',
23 [0x1D] = 'z', [0x1E] = '1', [0x1F] = '2', [0x20] = '3', [0x21] = '4',
24 [0x22] = '5', [0x23] = '6', [0x24] = '7', [0x25] = '8', [0x26] = '9',
25 [0x27] = '0', [0x28] = '\n', [0x29] = 27, // ESC
26 [0x2A] = '\b', // Backspace
27 [0x2B] = '\t', // Tab
28 [0x2C] = ' ', [0x2D] = '-', [0x2E] = '=', [0x2F] = '[', [0x30] = ']',
29 [0x31] = '\\', [0x33] = ';', [0x34] = '\'', [0x35] = '`', [0x36] = ',',
30 [0x37] = '.', [0x38] = '/'};
31
32static const char keycode_to_ascii_shifted[256] = {
33 [0x04] = 'A', [0x05] = 'B', [0x06] = 'C', [0x07] = 'D', [0x08] = 'E',
34 [0x09] = 'F', [0x0A] = 'G', [0x0B] = 'H', [0x0C] = 'I', [0x0D] = 'J',
35 [0x0E] = 'K', [0x0F] = 'L', [0x10] = 'M', [0x11] = 'N', [0x12] = 'O',
36 [0x13] = 'P', [0x14] = 'Q', [0x15] = 'R', [0x16] = 'S', [0x17] = 'T',
37 [0x18] = 'U', [0x19] = 'V', [0x1A] = 'W', [0x1B] = 'X', [0x1C] = 'Y',
38 [0x1D] = 'Z', [0x1E] = '!', [0x1F] = '@', [0x20] = '#', [0x21] = '$',
39 [0x22] = '%', [0x23] = '^', [0x24] = '&', [0x25] = '*', [0x26] = '(',
40 [0x27] = ')', [0x2D] = '_', [0x2E] = '+', [0x2F] = '{', [0x30] = '}',
41 [0x31] = '|', [0x33] = ':', [0x34] = '"', [0x35] = '~', [0x36] = '<',
42 [0x37] = '>', [0x38] = '?'};
43
44static inline bool generic_keyboard_is_modifier(uint32_t keycode) {
45 return keycode >= USB_HID_MODIFIER_BASE &&
46 keycode < USB_HID_MODIFIER_BASE + 8;
47}
48
49static cc_unused void generic_keyboard_dispatch(struct generic_keyboard *kbd,
50 uint32_t keycode,
51 bool pressed) {
52 if (generic_keyboard_is_modifier(keycode)) {
53 uint8_t bit = keycode - USB_HID_MODIFIER_BASE;
54 if (pressed)
55 kbd->modifiers |= BIT(bit);
56 else
57 kbd->modifiers &= ~BIT(bit);
58 return;
59 }
60
61 kbd->emit(kbd, keycode, pressed);
62}
63
64static inline bool usb_kbd_shift_active(const struct generic_keyboard *kbd) {
65 const uint8_t shift_mask = BIT(1) | /* Left Shift */
66 BIT(5); /* Right Shift */
67
68 return (kbd->modifiers & shift_mask) != 0;
69}
70
71static void tty_keyboard_emit(struct generic_keyboard *kbd, uint32_t keycode,
72 bool pressed) {
73 if (!pressed)
74 return;
75
76 bool shift = usb_kbd_shift_active(kbd);
77 char ch =
78 shift ? keycode_to_ascii_shifted[keycode] : keycode_to_ascii[keycode];
79
80 printf(format: "%c", ch);
81}
82
83static bool key_in_report(uint8_t key, const struct usb_kbd_report *r) {
84 for (int i = 0; i < 6; i++)
85 if (r->keys[i] == key)
86 return true;
87 return false;
88}
89
90static void usb_kbd_process_report(struct usb_hid_keyboard *kbd,
91 const struct usb_kbd_report *cur) {
92 const struct usb_kbd_report *prev = &kbd->last;
93
94 uint8_t changed = prev->modifiers ^ cur->modifiers;
95 if (changed) {
96 for (int bit = 0; bit < 8; bit++) {
97 if (!BIT_TEST(changed, bit))
98 continue;
99
100 uint32_t keycode = USB_HID_MODIFIER_BASE + bit;
101 bool pressed = BIT_TEST(cur->modifiers, bit);
102
103 kbd->gkbd.emit(&kbd->gkbd, keycode, pressed);
104 }
105 }
106
107 for (int i = 0; i < 6; i++) {
108 uint8_t key = prev->keys[i];
109 if (!key)
110 continue;
111
112 if (!key_in_report(key, r: cur)) {
113 kbd->gkbd.emit(&kbd->gkbd, key, false);
114 }
115 }
116
117 for (int i = 0; i < 6; i++) {
118 uint8_t key = cur->keys[i];
119 if (!key)
120 continue;
121
122 if (!key_in_report(key, r: prev)) {
123 kbd->gkbd.emit(&kbd->gkbd, key, true);
124 }
125 }
126}
127
128static enum usb_error usb_keyboard_get_descriptor(struct usb_device *dev,
129 uint8_t interface_number,
130 uint16_t len, void *buf) {
131 uint8_t bm = usb_construct_rq_bitmap(USB_REQUEST_TRANS_DTH,
132 USB_REQUEST_TYPE_STANDARD,
133 USB_REQUEST_RECIPIENT_INTERFACE);
134
135 struct usb_setup_packet setup = {
136 .bitmap_request_type = bm,
137 .request = USB_RQ_CODE_GET_DESCRIPTOR,
138 .value = USB_HID_DESC_TYPE_REPORT << 8,
139 .length = len,
140 .index = interface_number,
141 };
142
143 struct usb_request req = {
144 .setup = &setup,
145 .buffer = buf,
146 .dev = dev,
147 };
148
149 return usb_transfer_sync(fn: dev->host->ops->submit_control_transfer, request: &req);
150}
151
152static void usb_kbd_worker(void *arg) {
153 struct usb_hid_keyboard *kbd = arg;
154
155 while (true) {
156 enum usb_error ret = usb_transfer_sync(
157 fn: kbd->dev->host->ops->submit_interrupt_transfer, request: &kbd->req);
158 if (ret != USB_OK)
159 break;
160
161 usb_kbd_process_report(kbd, cur: &kbd->cur);
162 kbd->last = kbd->cur;
163 }
164 atomic_store(&kbd->worker_here, false);
165}
166
167static struct usb_hid_keyboard *cc_nullable
168usb_keyboard_create(struct usb_device *dev, struct usb_endpoint *ep) {
169 struct usb_hid_keyboard *kbd = vnew(kbd, ALLOC_ZERO);
170 if (!kbd)
171 return NULL;
172
173 kbd->dev = dev;
174 kbd->ep = ep;
175
176 kbd->gkbd.emit = tty_keyboard_emit;
177 kbd->gkbd.priv = kbd;
178
179 kbd->req = (struct usb_request){
180 .buffer = &kbd->cur,
181 .length = sizeof(kbd->cur),
182 .ep = kbd->ep,
183 .dev = dev,
184 };
185
186 thread_spawn("usb_kbd_worker", usb_kbd_worker, .arg = kbd);
187 atomic_store(&kbd->worker_here, true);
188 return kbd;
189}
190
191LOG_HANDLE_DEFINE_PRINT(usbkb);
192LOG_SITE_DEFINE_PRINT(usbkb);
193#define usbkb_log(log_level, fmt, ...) \
194 log(LOG_SITE(usbkb), LOG_HANDLE(usbkb), log_level, fmt, ##__VA_ARGS__)
195
196static enum usb_error usb_keyboard_bringup(struct usb_device *dev) {
197 struct usb_interface_descriptor *intf =
198 usb_find_interface(dev, USB_CLASS_HID, USB_SUBCLASS_HID_BOOT_INTERFACE,
199 USB_PROTOCOL_HID_KEYBOARD);
200 if (!intf)
201 return USB_ERR_NO_DEVICE;
202
203 usbkb_log(LOG_INFO, "Keyboard connected");
204
205 uint8_t iface_num = intf->interface_number;
206
207 uint8_t *report_buf = valloc_aligned(256, PAGE_SIZE, ALLOC_ZERO);
208 if (!report_buf)
209 return USB_ERR_OOM;
210
211 enum usb_error err = USB_OK;
212 if ((err = usb_keyboard_get_descriptor(dev, interface_number: iface_num, len: 256, buf: report_buf)) !=
213 USB_OK) {
214 vfree_aligned(report_buf);
215 return err;
216 }
217
218 vfree_aligned(report_buf);
219
220 for (uint8_t i = 0; i < dev->num_endpoints; i++) {
221 struct usb_endpoint *ep = dev->endpoints[i];
222 if ((ep->address & 0x80) &&
223 ep->type == USB_ENDPOINT_ATTR_TRANS_TYPE_INTERRUPT) {
224
225 dev->driver_private = usb_keyboard_create(dev, ep);
226 return dev->driver_private ? USB_OK : USB_ERR_OOM;
227 }
228 }
229
230 usbkb_log(LOG_WARN, "No interrupt IN endpoint found");
231 return USB_ERR_NO_ENDPOINT;
232}
233
234static void usb_keyboard_teardown(struct usb_device *dev) {
235 struct usb_hid_keyboard *kb = dev->driver_private;
236 if (!kb)
237 return;
238
239 while (atomic_load(&kb->worker_here))
240 scheduler_yield();
241}
242
243static void usb_keyboard_free(struct usb_device *dev) {
244 vfree(dev->driver_private);
245 usbkb_log(LOG_INFO, "Keyboard disconnected");
246}
247
248USB_DRIVER_REGISTER(keyboard, USB_CLASS_HID, USB_SUBCLASS_HID_BOOT_INTERFACE,
249 USB_PROTOCOL_HID_KEYBOARD, usb_keyboard_bringup,
250 usb_keyboard_teardown, usb_keyboard_free);
251