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