diff --git a/etc/make/amd64/platform.mk b/etc/make/amd64/platform.mk index 748871d..b51b95d 100644 --- a/etc/make/amd64/platform.mk +++ b/etc/make/amd64/platform.mk @@ -58,6 +58,8 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \ $(O)/sys/amd64/hw/pci/usb.o \ $(O)/sys/amd64/hw/pci/usb_uhci.o \ $(O)/sys/amd64/hw/usb/device.o \ + $(O)/sys/amd64/hw/usb/driver.o \ + $(O)/sys/amd64/hw/usb/usbkbd.o \ $(O)/sys/amd64/hw/usb/usb.o kernel_LINKER=sys/amd64/link.ld diff --git a/etc/make/conf.mk b/etc/make/conf.mk index 6b7d2f6..375d0a6 100644 --- a/etc/make/conf.mk +++ b/etc/make/conf.mk @@ -79,7 +79,8 @@ OBJS+=$(O)/sys/debug.o \ $(O)/sys/thread.o \ $(O)/sys/ctype.o \ $(O)/sys/line.o \ - $(O)/sys/config.o + $(O)/sys/config.o \ + $(O)/sys/input.o # \ $(O)/sys/vfs/pty.o \ diff --git a/include/sys/amd64/hw/ps2.h b/include/sys/amd64/hw/ps2.h index 3ce364c..a32309a 100644 --- a/include/sys/amd64/hw/ps2.h +++ b/include/sys/amd64/hw/ps2.h @@ -3,4 +3,3 @@ struct chrdev; void ps2_init(void); void ps2_register_device(void); -void ps2_kbd_set_tty(struct chrdev *dev); diff --git a/include/sys/amd64/hw/usb/const.h b/include/sys/amd64/hw/usb/const.h new file mode 100644 index 0000000..d502a8a --- /dev/null +++ b/include/sys/amd64/hw/usb/const.h @@ -0,0 +1,10 @@ +#pragma once + +// Classes +#define USB_CLASS_HID 0x03 + +// HID subclasses +#define USB_SUBCLASS_BOOT 0x01 + +// HID protocols +#define USB_PROTOCOL_KEYBOARD 0x01 diff --git a/include/sys/amd64/hw/usb/device.h b/include/sys/amd64/hw/usb/device.h index ff5aaf6..e58296f 100644 --- a/include/sys/amd64/hw/usb/device.h +++ b/include/sys/amd64/hw/usb/device.h @@ -1,5 +1,6 @@ #pragma once #include "sys/types.h" +#include "sys/amd64/hw/usb/request.h" struct usb_transfer; struct usb_request; @@ -11,7 +12,14 @@ struct usb_device { uint8_t speed; uint32_t max_packet; + uint8_t endpoint_toggle; + struct usb_desc_interface desc_interface; + struct usb_desc_endpoint desc_endpoint; + struct usb_driver *driver; + void *driver_data; + void (*hc_control)(struct usb_device *, struct usb_transfer *); + void (*hc_interrupt)(struct usb_device *, struct usb_transfer *); struct usb_device *next; }; diff --git a/include/sys/amd64/hw/usb/request.h b/include/sys/amd64/hw/usb/request.h index 9f646b3..cfc54f5 100644 --- a/include/sys/amd64/hw/usb/request.h +++ b/include/sys/amd64/hw/usb/request.h @@ -1,13 +1,24 @@ #pragma once #define USB_REQUEST_TYPE_D2H (1 << 7) +#define USB_REQUEST_TYPE_CLASS (1 << 5) +#define USB_REQUEST_TO_INTERFACE (1 << 0) + +// General #define USB_REQUEST_GET_STATUS 0 #define USB_REQUEST_SET_ADDRESS 5 #define USB_REQUEST_GET_DESCRIPTOR 6 +#define USB_REQUEST_SET_CONFIGURATION 9 + +// USB HID +#define USB_REQUEST_SET_IDLE 0x0A #define USB_DESCRIPTOR_DEVICE 1 +#define USB_DESCRIPTOR_CONFIGURATION 2 #define USB_DESCRIPTOR_STRING 3 +#define USB_DESCRIPTOR_INTERFACE 4 +#define USB_DESCRIPTOR_ENDPOINT 5 struct usb_request { uint8_t type; @@ -22,11 +33,44 @@ struct usb_request { struct usb_transfer { struct usb_request *request; + uint8_t endp; void *data; size_t length; uint32_t flags; }; +struct usb_desc_configuration { + uint8_t length; + uint8_t type; + uint16_t total_length; + uint8_t num_interfaces; + uint8_t configuration_value; + uint8_t idx_configuration; + uint8_t attributes; + uint8_t max_power; +} __attribute__((packed)); + +struct usb_desc_interface { + uint8_t length; + uint8_t type; + uint8_t interface_number; + uint8_t alternate_setting; + uint8_t num_endpoints; + uint8_t interface_class; + uint8_t interface_subclass; + uint8_t interface_protocol; + uint8_t idx_interface; +} __attribute__((packed)); + +struct usb_desc_endpoint { + uint8_t length; + uint8_t type; + uint8_t address; + uint8_t attributes; + uint16_t max_packet_size; + uint8_t interval; +} __attribute__((packed)); + struct usb_desc_string { uint8_t len; uint8_t type; diff --git a/include/sys/amd64/hw/usb/usb.h b/include/sys/amd64/hw/usb/usb.h index 49a0131..5835092 100644 --- a/include/sys/amd64/hw/usb/usb.h +++ b/include/sys/amd64/hw/usb/usb.h @@ -9,6 +9,8 @@ struct usb_controller { uint8_t spec; + void (*hc_poll)(struct usb_controller *hc); + struct usb_controller *next; }; diff --git a/include/sys/input.h b/include/sys/input.h new file mode 100644 index 0000000..0c564eb --- /dev/null +++ b/include/sys/input.h @@ -0,0 +1,18 @@ +#pragma once +#include "sys/types.h" + +struct chrdev; +extern struct chrdev *g_keyboard_tty; + +#define INPUT_MOD_CONTROL (1 << 0) +#define INPUT_MOD_SHIFT (1 << 1) +#define INPUT_MOD_CAPS (1 << 2) + +#define INPUT_KEY_LEFT 0x80 +#define INPUT_KEY_RIGHT 0x81 +#define INPUT_KEY_UP 0x82 +#define INPUT_KEY_DOWN 0x83 +#define INPUT_KEY_HOME 0x84 +#define INPUT_KEY_END 0x85 + +void input_key(uint8_t byte, uint8_t mods, const char *map0, const char *map1); diff --git a/include/sys/string.h b/include/sys/string.h index b2a18b5..8782694 100644 --- a/include/sys/string.h +++ b/include/sys/string.h @@ -16,6 +16,7 @@ uint16_t *memsetw(uint16_t *dst, uint16_t v, size_t count); uint32_t *memsetl(uint32_t *dst, uint32_t v, size_t count); uint64_t *memsetq(uint64_t *dst, uint64_t v, size_t count); int memcmp(const void *a, const void *b, size_t count); +void *memchr(const void *a, int c, size_t count); size_t strlen(const char *s); int strncmp(const char *a, const char *b, size_t lim); diff --git a/sys/amd64/hw/pci/usb_uhci.c b/sys/amd64/hw/pci/usb_uhci.c index 6f5eade..d9d7d00 100644 --- a/sys/amd64/hw/pci/usb_uhci.c +++ b/sys/amd64/hw/pci/usb_uhci.c @@ -78,7 +78,10 @@ struct uhci_qh { // System uint32_t list_index; struct usb_transfer *transfer; + struct uhci_qh *prev, *next; + struct uhci_td *head_real; uint32_t used; + uintptr_t pad; }; struct uhci { @@ -96,6 +99,7 @@ struct uhci { static struct uhci_td *uhci_alloc_td(struct uhci *hc) { for (size_t i = 0; i < UHCI_MAX_TD; ++i) { if (!(hc->td_pool[i].system.flags & UHCI_TD_USED)) { + _assert(!(((uintptr_t) &hc->td_pool[i]) & 0xF)); hc->td_pool[i].system.flags |= UHCI_TD_USED; return &hc->td_pool[i]; } @@ -106,6 +110,7 @@ static struct uhci_td *uhci_alloc_td(struct uhci *hc) { static struct uhci_qh *uhci_alloc_qh(struct uhci *hc) { for (size_t i = 0; i < UHCI_MAX_QH; ++i) { if (!hc->qh_pool[i].used) { + _assert(!(((uintptr_t) &hc->qh_pool[i]) & 0xF)); hc->qh_pool[i].used = 1; return &hc->qh_pool[i]; } @@ -134,6 +139,8 @@ static void uhci_data_init(struct uhci *data, uint32_t bar4) { data->qh_async->head_td = USB_FRAME_EMPTY; data->qh_async->element_td = USB_FRAME_EMPTY; data->qh_async->transfer = NULL; + data->qh_async->prev = data->qh_async; + data->qh_async->next = data->qh_async; for (uint32_t i = 0; i < 2048; ++i) { data->frame_list[i] = USB_FRAME_QUEUE | MM_PHYS(data->qh_async); @@ -182,69 +189,104 @@ static uint16_t uhci_port_reset(struct uhci *uhci, int port) { } static void uhci_start_qh(struct uhci *hc, struct uhci_qh *qh) { - _assert(hc->qh_async->head_td == USB_FRAME_EMPTY || hc->qh_async->head_td == 0); - hc->qh_async->head_td = (uint32_t) MM_PHYS(qh) | USB_FRAME_QUEUE; - hc->qh_async->element_td = 0; //(uint32_t) MM_PHYS(qh) | USB_FRAME_QUEUE; - //for (size_t i = 0; i < 1024; ++i) { - // //if ((hc->frame_list[i] & ~0xF) == MM_PHYS(hc->qh_async)) { - // // _assert(!(MM_PHYS(qh) & 0xF)); - // // qh->list_index = i; - // // hc->frame_list[i] = MM_PHYS(qh) | USB_FRAME_QUEUE; - // // return; - // //} - //} - //panic("Failed to allocate a frame for USB transfer\n"); + //kdebug("Assign QH: %p\n", qh); + struct uhci_qh *list = hc->qh_async; + _assert(list); + struct uhci_qh *end = hc->qh_async->prev; + _assert(end); + + qh->head_td = USB_FRAME_EMPTY; + end->head_td = (uint32_t) MM_PHYS(qh) | USB_FRAME_QUEUE; + qh->prev = end; + end->next = qh; + list->prev = qh; + qh->next = list; + + //kdebug("---- After add ----\n"); + //uhci_dumpq(hc); + //kdebug("---- After add ----\n"); +} + +static void uhci_remove_qh(struct uhci *hc, struct uhci_qh *qh) { + //kdebug("Unassign QH: %p\n", qh); + struct uhci_qh *list = hc->qh_async; + _assert(list); + struct uhci_qh *prev = qh->prev; + _assert(prev); + + prev->head_td = qh->head_td; + prev->next = list; + list->prev = prev; + + qh->next = NULL; + qh->prev = NULL; + + //kdebug("---- After remove ----\n"); + //uhci_dumpq(hc); + //kdebug("---- After remove ----\n"); +} + +static void uhci_free_td(struct uhci_td *td) { + //kdebug("Free TD: %p\n", td); + td->system.flags = 0; + td->next_td = USB_FRAME_EMPTY; + td->buffer = 0; +} + +static void uhci_free_qh(struct uhci_qh *qh) { + //kdebug("Free QH: %p\n", qh); + qh->transfer = 0; + qh->used = 0; + qh->head_real = NULL; + qh->head_td = 0; + qh->element_td = 0; +} + +static void uhci_process_qh(struct uhci *hc, struct uhci_qh *qh) { + struct usb_transfer *t = qh->transfer; + uint32_t elem_phys = qh->element_td & ~0xF; + + if (!elem_phys) { + t->flags = USB_TRANSFER_COMPLETE | USB_TRANSFER_SUCCESS; + } else { + struct uhci_td *td = (struct uhci_td *) MM_VIRTUALIZE(elem_phys); + + if (td->status & (1 << 22)) { + kwarn("Transfer descriptor is stalled\n"); + t->flags = USB_TRANSFER_COMPLETE; + } + } + + if (t->flags & USB_TRANSFER_COMPLETE) { + //kdebug("Transfer is complete\n"); + uhci_remove_qh(hc, qh); + + (void) uhci_free_td; + // Release TDs + uint32_t td_ptr = 1234; + struct uhci_td *td = qh->head_real; + _assert(td); + while (1) { + td_ptr = td->next_td; + + uhci_free_td(td); + + if (!(td_ptr & ~0xF) || (td_ptr & USB_FRAME_EMPTY)) { + break; + } + td = (struct uhci_td *) MM_VIRTUALIZE(td_ptr & ~0xF); + } + + uhci_free_qh(qh); + } } static void uhci_wait_qh(struct uhci *hc, struct uhci_qh *qh) { + //kdebug("Wait for QH: %p\n", qh); struct usb_transfer *t = qh->transfer; while (!(t->flags & USB_TRANSFER_COMPLETE)) { - uint32_t elem_phys = qh->element_td & ~0xF; - - if (!elem_phys) { - t->flags = USB_TRANSFER_COMPLETE | USB_TRANSFER_SUCCESS; - break; - } else { - struct uhci_td *td = (struct uhci_td *) MM_VIRTUALIZE(elem_phys); - - if (td->status & (1 << 22)) { - kdebug("Transfer descriptor is stalled\n"); - t->flags = USB_TRANSFER_COMPLETE; - break; - } - } + uhci_process_qh(hc, qh); } - hc->qh_async->head_td = USB_FRAME_EMPTY; - uint32_t td_ptr = (qh->head_td & ~0xF); - _assert(td_ptr); - - do { - if (!td_ptr) { - break; - } - - struct uhci_td *td = (struct uhci_td *) MM_VIRTUALIZE(td_ptr); - if (td->next_td == USB_FRAME_EMPTY || !td->next_td) { - td->next_td = USB_FRAME_EMPTY; - td->buffer = 0; - td->system.flags &= ~UHCI_TD_USED; - break; - } - - td_ptr = td->next_td & ~0xF; - _assert(td_ptr); - - td->system.flags &= ~UHCI_TD_USED; - td->next_td = USB_FRAME_EMPTY; - td->buffer = 0; - } while (1); - - // TODO: Properly release the queue - qh->transfer = NULL; - qh->head_td = USB_FRAME_EMPTY; - qh->element_td = USB_FRAME_EMPTY; - qh->used = 0; - kdebug("Transfer is complete\n"); } @@ -286,6 +328,35 @@ static void uhci_init_td(struct uhci_td *td, struct uhci_td *prev, uint8_t speed td->buffer = MM_PHYS(buf); } +static void uhci_device_interrupt(struct usb_device *dev, struct usb_transfer *t) { + struct uhci *hc = dev->hc; + + uint8_t speed = dev->speed; + uint8_t addr = dev->address; + uint8_t endp = dev->desc_endpoint.address & 0xF; + + struct uhci_td *td = uhci_alloc_td(hc); + _assert(td); + + struct uhci_td *head = td; + struct uhci_td *prev = NULL; + + uint8_t toggle = dev->endpoint_toggle; + uint8_t packet_type = 0x69; + uint8_t packet_size = t->length; + + uhci_init_td(td, prev, speed, addr, endp, toggle, packet_type, packet_size, t->data); + + struct uhci_qh *qh = uhci_alloc_qh(hc); + _assert(qh); + qh->head_real = head; + qh->head_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ; + qh->element_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ; + qh->transfer = t; + + uhci_start_qh(hc, qh); +} + static void uhci_device_control(struct usb_device *dev, struct usb_transfer *t) { struct uhci *hc = dev->hc; struct usb_request *req = t->request; @@ -335,6 +406,7 @@ static void uhci_device_control(struct usb_device *dev, struct usb_transfer *t) qh = uhci_alloc_qh(hc); _assert(qh); + qh->head_real = head; qh->head_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ; qh->element_td = (uint32_t) MM_PHYS(head) | USB_FRAME_FULLQ; qh->transfer = t; @@ -357,7 +429,9 @@ static void uhci_probe(struct uhci *uhci) { dev->hc = uhci; dev->max_packet = 8; dev->hc_control = uhci_device_control; + dev->hc_interrupt = uhci_device_interrupt; dev->address = 0; + dev->endpoint_toggle = 0; if (portsc & USBPORT_LSPD) { dev->speed = USB_SPEED_LOW; @@ -370,6 +444,23 @@ static void uhci_probe(struct uhci *uhci) { } } +static void uhci_poll(struct usb_controller *_hc) { + struct uhci *hc = (struct uhci *) _hc; + + struct uhci_qh *qh = hc->qh_async; + struct uhci_qh *next; + while (qh) { + next = qh->next; + + if (qh->transfer) { + uhci_process_qh(hc, qh); + break; + } + + qh = next; + } +} + void pci_usb_uhci_init(pci_addr_t addr) { uint32_t bar4; struct uhci *hc = kmalloc(sizeof(struct uhci)); @@ -396,6 +487,7 @@ void pci_usb_uhci_init(pci_addr_t addr) { uhci_probe(hc); hc->hc.spec = USB_SPEC_UHCI; + hc->hc.hc_poll = uhci_poll; usb_controller_add((struct usb_controller *) hc); } diff --git a/sys/amd64/hw/ps2.c b/sys/amd64/hw/ps2.c index fe2611a..6779e0d 100644 --- a/sys/amd64/hw/ps2.c +++ b/sys/amd64/hw/ps2.c @@ -1,6 +1,7 @@ #include "sys/amd64/hw/ps2.h" #include "sys/panic.h" #include "sys/amd64/hw/irq.h" +#include "sys/input.h" #include "sys/amd64/hw/io.h" #include "sys/ctype.h" #include "sys/signum.h" @@ -21,19 +22,14 @@ #define PS2_KEY_CAPS_LOCK_DOWN 0x3A -#define PS2_MOD_SHIFT (1 << 0) -#define PS2_MOD_CTRL (1 << 1) -#define PS2_MOD_CAPS (1 << 2) - // Mouse stuff #define PS2_AUX_BM (1 << 2) #define PS2_AUX_BR (1 << 1) #define PS2_AUX_BL (1 << 0) // Controller stuff +// ... -// TTY owning the keyboard -static struct chrdev *_keyboard_tty = NULL; static uint32_t ps2_kbd_mods = 0; static uint32_t ps2_aux_state = 0; @@ -76,10 +72,6 @@ static const char ps2_key_table_1[128] = { [0x7F] = 0 }; -void ps2_kbd_set_tty(struct chrdev *dev) { - _keyboard_tty = dev; -} - uint32_t ps2_irq_keyboard(void *ctx) { uint8_t st = inb(0x64); @@ -89,92 +81,34 @@ uint32_t ps2_irq_keyboard(void *ctx) { uint8_t key = inb(0x60); + if (key == 0xE0) { + kinfo("0xE0 key\n"); + } + if (key == PS2_KEY_LSHIFT_DOWN || key == PS2_KEY_RSHIFT_DOWN) { - ps2_kbd_mods |= PS2_MOD_SHIFT; + ps2_kbd_mods |= INPUT_MOD_SHIFT; } if (key == PS2_KEY_LSHIFT_UP || key == PS2_KEY_RSHIFT_UP) { - ps2_kbd_mods &= ~PS2_MOD_SHIFT; + ps2_kbd_mods &= ~INPUT_MOD_SHIFT; } if (key == PS2_KEY_LCTRL_DOWN) { - ps2_kbd_mods |= PS2_MOD_CTRL; + ps2_kbd_mods |= INPUT_MOD_CONTROL; } if (key == PS2_KEY_LCTRL_UP) { - ps2_kbd_mods &= ~PS2_MOD_CTRL; + ps2_kbd_mods &= ~INPUT_MOD_CONTROL; } if (key == PS2_KEY_CAPS_LOCK_DOWN) { - ps2_kbd_mods ^= PS2_MOD_CAPS; + ps2_kbd_mods ^= INPUT_MOD_CAPS; } - if (!_keyboard_tty) { + if (!g_keyboard_tty) { // No TTY to send strokes to return IRQ_HANDLED; } - if (!(key & 0x80) && !(ps2_kbd_mods & PS2_MOD_CTRL)) { - // Special keys - switch (key) { - case 0x01: - tty_data_write(_keyboard_tty, '\033'); - return IRQ_HANDLED; - case 0x47: - tty_data_write(_keyboard_tty, '\033'); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, 'H'); - return IRQ_HANDLED; - case 0x48: - tty_data_write(_keyboard_tty, '\033'); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, 'A'); - return IRQ_HANDLED; - case 0x50: - tty_data_write(_keyboard_tty, '\033'); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, 'B'); - return IRQ_HANDLED; - case 0x4D: - tty_data_write(_keyboard_tty, '\033'); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, 'C'); - return IRQ_HANDLED; - case 0x4F: - tty_data_write(_keyboard_tty, '\033'); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, 'F'); - return IRQ_HANDLED; - case 0x4B: - tty_data_write(_keyboard_tty, '\033'); - tty_data_write(_keyboard_tty, '['); - tty_data_write(_keyboard_tty, 'D'); - return IRQ_HANDLED; - default: - break; - } - - char key_char = ((ps2_kbd_mods & PS2_MOD_SHIFT) ? ps2_key_table_1 : ps2_key_table_0)[key]; - - if (ps2_kbd_mods & PS2_MOD_CAPS) { - if (isupper(key_char)) { - key_char = tolower(key_char); - } else if (islower(key_char)) { - key_char = toupper(key_char); - } - } - - if (key_char != 0) { - tty_data_write(_keyboard_tty, key_char); - } - } else { - switch (key) { - case 0x20: // ^D - tty_control_write(_keyboard_tty, 'd'); - break; - case 0x2E: // ^C - tty_control_write(_keyboard_tty, 'c'); - break; - } + if (!(key & 0x80)) { + input_key(ps2_kbd_mods, key, ps2_key_table_0, ps2_key_table_1); } return IRQ_HANDLED; diff --git a/sys/amd64/hw/usb/device.c b/sys/amd64/hw/usb/device.c index 9eebf0c..369b772 100644 --- a/sys/amd64/hw/usb/device.c +++ b/sys/amd64/hw/usb/device.c @@ -1,24 +1,13 @@ #include "sys/amd64/hw/usb/device.h" +#include "sys/amd64/hw/usb/driver.h" #include "sys/amd64/hw/usb/request.h" #include "sys/string.h" #include "sys/assert.h" #include "sys/debug.h" #include "sys/heap.h" -static struct usb_device *g_usb_devices = NULL; static uint32_t g_device_address = 0; -struct usb_device *usb_device_create(void) { - struct usb_device *res = kmalloc(sizeof(struct usb_device)); - _assert(res); - - res->hc_control = NULL; - res->next = g_usb_devices; - g_usb_devices = res; - - return res; -} - int usb_device_request(struct usb_device *dev, uint8_t type, uint8_t request, uint16_t index, uint16_t value, uint16_t len, void *data) { _assert(dev); struct usb_request req; @@ -132,8 +121,7 @@ static int usb_device_get_string(struct usb_device *dev, char *buf, size_t lim, int usb_device_init(struct usb_device *dev) { struct usb_desc_device device_desc; - char vendor_name[256]; - char product_name[256]; + char string_buffer[256]; uint16_t langs[32]; uint32_t dev_addr; int res; @@ -192,23 +180,136 @@ int usb_device_init(struct usb_device *dev) { return res; } - if (langs[0]) { - uint16_t lang = langs[0]; - if ((res = usb_device_get_string(dev, vendor_name, sizeof(vendor_name), lang, device_desc.idx_manufacturer)) <= 0) { + uint16_t lang = langs[0]; + if (langs[0]) { + if ((res = usb_device_get_string(dev, string_buffer, sizeof(string_buffer), lang, device_desc.idx_manufacturer)) <= 0) { kwarn("Failed to get device vendor name\n"); } else { - kinfo("Vendor name: %s\n", vendor_name); + kinfo("Vendor name: %s\n", string_buffer); } - if ((res = usb_device_get_string(dev, product_name, sizeof(product_name), lang, device_desc.idx_product)) <= 0) { + if ((res = usb_device_get_string(dev, string_buffer, sizeof(string_buffer), lang, device_desc.idx_product)) <= 0) { kwarn("Failed to get device product name\n"); } else { - kinfo("Product name: %s\n", product_name); + kinfo("Product name: %s\n", string_buffer); } } else { kwarn("No language data for device\n"); } + // Get device configuration + char config_buf[256]; + struct usb_desc_configuration *configuration_desc = (struct usb_desc_configuration *) config_buf; + struct usb_desc_interface *interface_desc = NULL; + struct usb_desc_endpoint *endpoint_desc = NULL; + + // This stores first valid configuration + uint8_t picked_config_value = 0; + struct usb_desc_interface *picked_interface_desc = NULL; + struct usb_desc_endpoint *picked_endpoint_desc = NULL; + + for (uint32_t conf_index = 0; conf_index < device_desc.num_configurations; ++conf_index) { + // Get configuration size + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + 0, + (USB_DESCRIPTOR_CONFIGURATION << 8) | conf_index, + 4, config_buf)) != 0) { + kwarn("Failed to get configuration header\n"); + continue; + } + + if (configuration_desc->total_length > sizeof(config_buf)) { + kwarn("Configuration buffer too small\n"); + continue; + } + + // Read the rest of data + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + 0, + (USB_DESCRIPTOR_CONFIGURATION << 8) | conf_index, + configuration_desc->total_length, config_buf)) != 0) { + kwarn("Failed to get device configuration\n"); + continue; + } + + // Dump configuration info + kinfo("Device %04x, configuration %u:\n", dev->address, conf_index); + kinfo("Interface count: %u, value: %u\n", + configuration_desc->num_interfaces, configuration_desc->configuration_value); + if (usb_device_get_string(dev, string_buffer, sizeof(string_buffer), lang, configuration_desc->idx_configuration) > 0) { + kinfo("Configuration name: \"%s\"\n", string_buffer); + } + + // Select a configuration + void *data = config_buf + configuration_desc->length; + void *end = config_buf + configuration_desc->total_length; + kinfo("Configuration data:\n"); + + picked_interface_desc = NULL; + picked_endpoint_desc = NULL; + + while (data < end) { + uint8_t len = ((uint8_t *) data)[0]; + uint8_t type = ((uint8_t *) data)[1]; + + switch (type) { + case USB_DESCRIPTOR_INTERFACE: + interface_desc = (struct usb_desc_interface *) data; + if (!picked_interface_desc) { + picked_interface_desc = interface_desc; + } + + kinfo("* Interface descriptor\n"); + kinfo("Number %02x, %u endpoints\n", + interface_desc->interface_number, + interface_desc->num_endpoints); + kinfo("Class: %02x:%02x, Protocol %02x\n", + interface_desc->interface_class, + interface_desc->interface_subclass, + interface_desc->interface_protocol); + break; + case USB_DESCRIPTOR_ENDPOINT: + kinfo("* Endpoint descriptor\n"); + endpoint_desc = (struct usb_desc_endpoint *) data; + if (!picked_endpoint_desc) { + picked_endpoint_desc = endpoint_desc; + } + break; + } + + data += len; + } + + if (picked_interface_desc && picked_endpoint_desc) { + picked_config_value = configuration_desc->configuration_value; + break; + } + } + + if (picked_interface_desc && picked_endpoint_desc) { + kinfo("Configuring device with value %02x\n", picked_config_value); + + if ((res = usb_device_request(dev, + 0, + USB_REQUEST_SET_CONFIGURATION, + 0, + picked_config_value, + 0, 0)) != 0) { + return res; + } + + memcpy(&dev->desc_interface, picked_interface_desc, sizeof(struct usb_desc_interface)); + memcpy(&dev->desc_endpoint, picked_endpoint_desc, sizeof(struct usb_desc_endpoint)); + + if ((res = usb_select_driver(dev)) != 0) { + kwarn("Failed to pick driver for the device\n"); + } + } + return 0; } diff --git a/sys/amd64/hw/usb/driver.c b/sys/amd64/hw/usb/driver.c new file mode 100644 index 0000000..25c5eec --- /dev/null +++ b/sys/amd64/hw/usb/driver.c @@ -0,0 +1,19 @@ +#include "sys/amd64/hw/usb/driver.h" +#include "sys/types.h" + +static struct usb_driver *g_driver_head = NULL; + +int usb_select_driver(struct usb_device *dev) { + for (struct usb_driver *drv = g_driver_head; drv; drv = drv->next) { + if (drv->usb_init && drv->usb_init(dev) == 0) { + return 0; + } + } + + return -1; +} + +void usb_add_driver(struct usb_driver *drv) { + drv->next = g_driver_head; + g_driver_head = drv; +} diff --git a/sys/amd64/hw/usb/driver.h b/sys/amd64/hw/usb/driver.h new file mode 100644 index 0000000..9113088 --- /dev/null +++ b/sys/amd64/hw/usb/driver.h @@ -0,0 +1,13 @@ +#pragma once + +struct usb_device; + +struct usb_driver { + int (*usb_init)(struct usb_device *dev); + void (*usb_poll)(struct usb_device *dev); + + struct usb_driver *next; +}; + +int usb_select_driver(struct usb_device *dev); +void usb_add_driver(struct usb_driver *drv); diff --git a/sys/amd64/hw/usb/usb.c b/sys/amd64/hw/usb/usb.c index 7e1e277..4e57cdf 100644 --- a/sys/amd64/hw/usb/usb.c +++ b/sys/amd64/hw/usb/usb.c @@ -1,6 +1,37 @@ +#include "sys/amd64/hw/usb/device.h" +#include "sys/amd64/hw/usb/driver.h" #include "sys/amd64/hw/usb/usb.h" +#include "sys/assert.h" +#include "sys/heap.h" static struct usb_controller *g_hc_list = NULL; +static struct usb_device *g_usb_devices = NULL; + +struct usb_device *usb_device_create(void) { + struct usb_device *res = kmalloc(sizeof(struct usb_device)); + _assert(res); + + res->hc_control = NULL; + res->next = g_usb_devices; + res->driver = NULL; + g_usb_devices = res; + + return res; +} + +void usb_poll(void) { + for (struct usb_controller *hc = g_hc_list; hc; hc = hc->next) { + if (hc->hc_poll) { + hc->hc_poll(hc); + } + } + + for (struct usb_device *dev = g_usb_devices; dev; dev = dev->next) { + if (dev->driver && dev->driver->usb_poll) { + dev->driver->usb_poll(dev); + } + } +} void usb_controller_add(struct usb_controller *hc) { hc->next = g_hc_list; diff --git a/sys/amd64/hw/usb/usbkbd.c b/sys/amd64/hw/usb/usbkbd.c new file mode 100644 index 0000000..4204e83 --- /dev/null +++ b/sys/amd64/hw/usb/usbkbd.c @@ -0,0 +1,154 @@ +#include "sys/amd64/hw/usb/driver.h" +#include "sys/amd64/hw/usb/device.h" +#include "sys/amd64/hw/usb/const.h" +#include "sys/string.h" +#include "sys/assert.h" +#include "sys/input.h" +#include "sys/debug.h" +#include "sys/attr.h" +#include "sys/heap.h" +#include "sys/tty.h" + +#define USB_KBD_MOD_SHIFT ((1 << 1) | (1 << 5)) +#define USB_KBD_MOD_CONTROL ((1 << 0) | (1 << 4)) +#define USB_KBD_CAPS 0x39 + +static int usb_kbd_init(struct usb_device *dev); +static void usb_kbd_poll(struct usb_device *dev); + +static const char usb_kbd_ascii_0[256] = { + 0, + [0x04] = 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + [0x0C] = 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + [0x14] = 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + [0x1C] = 'y', 'z', '1', '2', '3', '4', '5', '6', + [0x24] = '7', '8', '9', '0', + [0x28] = '\n', '\033', '\b', '\t', + [0x2C] = ' ', '-', '=', '[', ']', '\\', '.', ';', + [0x34] = '\'', '`', ',', '.', '/', + // ... + [0x4F] = INPUT_KEY_RIGHT, + [0x50] = INPUT_KEY_LEFT, + [0x51] = INPUT_KEY_DOWN, + [0x52] = INPUT_KEY_UP, +}; + +static const char usb_kbd_ascii_1[256] = { + 0, + [0x04] = 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + [0x0C] = 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + [0x14] = 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + [0x1C] = 'Y', 'Z', '!', '@', '#', '$', '%', '^', + [0x24] = '&', '*', '(', ')', + [0x28] = '\n', '\033', '\b', '\t', + [0x2C] = ' ', '_', '+', '{', '}', '|', '.', ':', + [0x34] = '"', '~', '<', '>', '?', + // ... + [0x4F] = INPUT_KEY_RIGHT, + [0x50] = INPUT_KEY_LEFT, + [0x51] = INPUT_KEY_DOWN, + [0x52] = INPUT_KEY_UP, +}; + +struct usb_kbd_data { + struct usb_transfer transfer; + uint8_t mods; + char buf[8]; + char keys_down[6]; +}; + +static struct usb_driver g_usbkbd = { + .usb_init = usb_kbd_init, + .usb_poll = usb_kbd_poll +}; + +static void usb_kbd_handle_data(struct usb_kbd_data *data) { + data->mods &= ~(INPUT_MOD_CONTROL | INPUT_MOD_SHIFT); + if (data->buf[0] & USB_KBD_MOD_SHIFT) { + data->mods |= INPUT_MOD_SHIFT; + } + if (data->buf[0] & USB_KBD_MOD_CONTROL) { + data->mods |= INPUT_MOD_CONTROL; + } + + for (size_t i = 2; i < 8; ++i) { + uint8_t key = data->buf[i]; + + if (key >= 4) { + if (!memchr(data->keys_down, data->buf[i], 6)) { + if (key == 0x39) { + data->mods ^= INPUT_MOD_CAPS; + } else { + // Key press detected + input_key(data->buf[i], data->mods, usb_kbd_ascii_0, usb_kbd_ascii_1); + } + } + } else if (key) { + kinfo("Invalid key: %02x\n", key); + } + } + + memcpy(data->keys_down, &data->buf[2], 6); +} + +static void usb_kbd_poll(struct usb_device *dev) { + struct usb_kbd_data *data = dev->driver_data; + _assert(data); + struct usb_transfer *t = &data->transfer; + + if (t->flags & USB_TRANSFER_COMPLETE) { + if (t->flags & USB_TRANSFER_SUCCESS) { + usb_kbd_handle_data(data); + } + + t->flags = 0; + t->data = data->buf; + t->length = 8; + dev->endpoint_toggle ^= 1; + _assert(dev->hc_interrupt); + dev->hc_interrupt(dev, t); + } +} + +static int usb_kbd_init(struct usb_device *dev) { + int res; + if (dev->desc_interface.interface_class == USB_CLASS_HID && + dev->desc_interface.interface_subclass == USB_SUBCLASS_BOOT && + dev->desc_interface.interface_protocol == USB_PROTOCOL_KEYBOARD) { + kinfo("Found keyboard!\n"); + + uint8_t interface_index = dev->desc_interface.idx_interface; + + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_CLASS | USB_REQUEST_TO_INTERFACE, + 0x0A, + interface_index, + 0, + 0, 0)) != 0) { + kwarn("USB_REQUEST_SET_IDLE failed\n"); + } + + dev->driver = &g_usbkbd; + struct usb_kbd_data *data = kmalloc(sizeof(struct usb_kbd_data)); + _assert(data); + + struct usb_transfer *t = &data->transfer; + t->endp = dev->desc_endpoint.address & 0xF; + t->request = 0; + t->data = data->buf; + t->length = 8; + t->flags = 0; + + dev->driver_data = data; + + _assert(dev->hc_interrupt); + dev->hc_interrupt(dev, t); + + return 0; + } + return -1; +} + +static __init void usb_kbd_register_driver(void) { + usb_add_driver(&g_usbkbd); +} diff --git a/sys/init.c b/sys/init.c index 14556c3..34a78e8 100644 --- a/sys/init.c +++ b/sys/init.c @@ -1,3 +1,4 @@ +#include "sys/amd64/hw/usb/usb.h" #include "sys/amd64/mm/pool.h" #include "sys/amd64/mm/map.h" #include "sys/amd64/hw/io.h" @@ -19,6 +20,7 @@ #include "sys/mm.h" extern uintptr_t argp_copy(struct thread *thr, const char *const argv[], size_t *arg_count); +static struct thread t_usbd; static int user_init_start(const char *init_filename) { const char *init_console = (const char *) kernel_config[CFG_CONSOLE]; @@ -125,7 +127,30 @@ static int user_init_start(const char *init_filename) { return 0; } +static void usbd_func(void *arg) { + thread_set_name(get_cpu()->thread, "usbd"); + + while (1) { + usb_poll(); + } +} + void init_func(void *arg) { + // Start kernel threads for stuff handling + thread_init( + &t_usbd, + mm_kernel, + (uintptr_t) usbd_func, + 0, + 0, + 0, + 0, + THREAD_KERNEL, + THREAD_INIT_CTX, + NULL); + + sched_add(&t_usbd); + const char *root_dev_name = (const char *) kernel_config[CFG_ROOT]; const char *root_fallback = "ram0"; const char *init_filename = diff --git a/sys/input.c b/sys/input.c new file mode 100644 index 0000000..c5c47e1 --- /dev/null +++ b/sys/input.c @@ -0,0 +1,74 @@ +#include "sys/input.h" +#include "sys/debug.h" +#include "sys/ctype.h" +#include "sys/tty.h" + +struct chrdev *g_keyboard_tty = 0; + +void input_key(uint8_t key, uint8_t mods, const char *map0, const char *map1) { + if (!(mods & INPUT_MOD_CONTROL)) { + const char *map = (mods & INPUT_MOD_SHIFT) ? map1 : map0; + uint8_t c = map[key]; + + if (c && c < 0x7F) { + if (mods & INPUT_MOD_CAPS) { + if (islower(c)) { + c = toupper(c); + } else if (isupper(c)) { + c = tolower(c); + } + } + + // Normal key + tty_data_write(g_keyboard_tty, c); + } else { + // Special keys + switch (c) { + case INPUT_KEY_UP: + tty_data_write(g_keyboard_tty, '\033'); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, 'A'); + break; + case INPUT_KEY_DOWN: + tty_data_write(g_keyboard_tty, '\033'); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, 'B'); + break; + case INPUT_KEY_RIGHT: + tty_data_write(g_keyboard_tty, '\033'); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, 'C'); + break; + case INPUT_KEY_LEFT: + tty_data_write(g_keyboard_tty, '\033'); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, 'D'); + break; + case INPUT_KEY_HOME: + tty_data_write(g_keyboard_tty, '\033'); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, 'H'); + break; + case INPUT_KEY_END: + tty_data_write(g_keyboard_tty, '\033'); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, '['); + tty_data_write(g_keyboard_tty, 'F'); + break; + default: + kdebug("Ignoring unhandled key: (scan %02x): %02x\n", key, c); + break; + } + } + } else { + char c = map0[key]; + + switch (c) { + case 'c': + case 'd': + tty_control_write(g_keyboard_tty, c); + break; + } + } +} diff --git a/sys/string.c b/sys/string.c index 4315947..1e26af7 100644 --- a/sys/string.c +++ b/sys/string.c @@ -95,6 +95,15 @@ char *strrchr(const char *s, int c) { return NULL; } +void *memchr(const void *a, int v, size_t c) { + for (size_t i = 0; i < c; ++i) { + if (((const char *) a)[i] == v) { + return (void *) a + i; + } + } + return 0; +} + int memcmp(const void *a, const void *b, size_t n) { const char *l = a; const char *r = b; diff --git a/sys/tty.c b/sys/tty.c index 0fe34d5..6a7bc35 100644 --- a/sys/tty.c +++ b/sys/tty.c @@ -1,5 +1,6 @@ #include "sys/tty.h" #include "sys/chr.h" +#include "sys/input.h" #include "sys/ctype.h" #include "sys/line.h" #include "sys/errno.h" @@ -125,7 +126,7 @@ void tty_putc(struct chrdev *tty, char c) { void tty_init(void) { // tty0 ring_init(&_dev_tty0.buffer, 16); - ps2_kbd_set_tty(&_dev_tty0); + g_keyboard_tty = &_dev_tty0; dev_add(DEV_CLASS_CHAR, DEV_CHAR_TTY, &_dev_tty0, "tty0"); // ttyS0