| 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 | |
| 17 | static 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 | |
| 32 | static 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 | |
| 44 | static 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 | |
| 49 | static 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 | |
| 64 | static 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 | |
| 71 | static 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 | |
| 83 | static 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 | |
| 90 | static 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 | |
| 128 | static 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 | |
| 152 | static 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 | |
| 167 | static struct usb_hid_keyboard *cc_nullable |
| 168 | usb_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 | |
| 191 | LOG_HANDLE_DEFINE_PRINT(usbkb); |
| 192 | LOG_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 | |
| 196 | static 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 | |
| 234 | static 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 | |
| 243 | static void usb_keyboard_free(struct usb_device *dev) { |
| 244 | vfree(dev->driver_private); |
| 245 | usbkb_log(LOG_INFO, "Keyboard disconnected" ); |
| 246 | } |
| 247 | |
| 248 | USB_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 | |