From 32f51f53f0c8cf9ce62cfade8f321889e2eac153 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 3 Feb 2020 13:40:03 +0200 Subject: [PATCH] Backport USB driver + UHCI --- etc/make/conf.mk | 10 +- include/sys/driver/usb/const.h | 10 + include/sys/driver/usb/device.h | 29 +++ include/sys/driver/usb/driver.h | 13 ++ include/sys/driver/usb/request.h | 97 +++++++++ include/sys/driver/usb/usb.h | 19 ++ include/sys/sched.h | 2 + sys/amd64/kernel.c | 2 + sys/driver/usb/device.c | 335 +++++++++++++++++++++++++++++++ sys/driver/usb/driver.c | 19 ++ sys/driver/usb/usb.c | 68 +++++++ sys/driver/usb/usb_uhci.c | 74 +++---- sys/driver/usb/usbkbd.c | 154 ++++++++++++++ sys/sched.c | 2 +- 14 files changed, 795 insertions(+), 39 deletions(-) create mode 100644 include/sys/driver/usb/const.h create mode 100644 include/sys/driver/usb/device.h create mode 100644 include/sys/driver/usb/driver.h create mode 100644 include/sys/driver/usb/request.h create mode 100644 include/sys/driver/usb/usb.h create mode 100644 sys/driver/usb/device.c create mode 100644 sys/driver/usb/driver.c create mode 100644 sys/driver/usb/usb.c create mode 100644 sys/driver/usb/usbkbd.c diff --git a/etc/make/conf.mk b/etc/make/conf.mk index 431c7c7..4e8f3a0 100644 --- a/etc/make/conf.mk +++ b/etc/make/conf.mk @@ -67,14 +67,20 @@ OBJS+=$(O)/sys/debug.o \ OBJS+=$(O)/sys/driver/pci/pci.o \ $(O)/sys/driver/pci/pcidb.o \ - $(O)/sys/driver/ata/ahci.o + $(O)/sys/driver/ata/ahci.o \ + $(O)/sys/driver/usb/usb_uhci.o \ + $(O)/sys/driver/usb/usb.o \ + $(O)/sys/driver/usb/driver.o \ + $(O)/sys/driver/usb/device.o \ + $(O)/sys/driver/usb/usbkbd.o DIRS+=$(O)/sys/fs \ $(O)/sys/fs/ext2 \ $(O)/sys/char \ $(O)/sys/block \ $(O)/sys/driver/pci \ - $(O)/sys/driver/ata + $(O)/sys/driver/ata \ + $(O)/sys/driver/usb ifeq ($(DEBUG_COUNTERS),1) CFLAGS+=-DDEBUG_COUNTERS diff --git a/include/sys/driver/usb/const.h b/include/sys/driver/usb/const.h new file mode 100644 index 0000000..d502a8a --- /dev/null +++ b/include/sys/driver/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/driver/usb/device.h b/include/sys/driver/usb/device.h new file mode 100644 index 0000000..7a23827 --- /dev/null +++ b/include/sys/driver/usb/device.h @@ -0,0 +1,29 @@ +#pragma once +#include "sys/driver/usb/request.h" +#include "sys/types.h" + +struct usb_transfer; +struct usb_request; + +struct usb_device { + void *hc; + + uint16_t address; + 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; +}; + +struct usb_device *usb_device_create(void); +int usb_device_init(struct usb_device *dev); +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); diff --git a/include/sys/driver/usb/driver.h b/include/sys/driver/usb/driver.h new file mode 100644 index 0000000..9113088 --- /dev/null +++ b/include/sys/driver/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/include/sys/driver/usb/request.h b/include/sys/driver/usb/request.h new file mode 100644 index 0000000..6a1493c --- /dev/null +++ b/include/sys/driver/usb/request.h @@ -0,0 +1,97 @@ +#pragma once +#include "sys/types.h" + +#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; + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t length; +}; + +#define USB_TRANSFER_COMPLETE (1 << 0) +#define USB_TRANSFER_SUCCESS (1 << 1) + +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; + uint16_t data[0]; +} __attribute__((packed)); + +struct usb_desc_device { + uint8_t length; + uint8_t type; + uint16_t bcd_usb; + uint8_t dev_class; + uint8_t dev_subclass; + uint8_t dev_protocol; + uint8_t max_packet_zero; + // After 8 bytes + uint16_t id_vendor; + uint16_t id_product; + uint16_t bcd_device; + uint8_t idx_manufacturer; + uint8_t idx_product; + uint8_t idx_serial_number; + uint8_t num_configurations; +} __attribute__((packed)); diff --git a/include/sys/driver/usb/usb.h b/include/sys/driver/usb/usb.h new file mode 100644 index 0000000..73c7a7c --- /dev/null +++ b/include/sys/driver/usb/usb.h @@ -0,0 +1,19 @@ +#pragma once +#include "sys/types.h" + +#define USB_SPEC_UHCI 0x00 + +#define USB_SPEED_LOW 0x00 +#define USB_SPEED_FULL 0x01 +#define USB_SPEED_HIGH 0x02 + +struct usb_controller { + uint8_t spec; + void (*hc_poll)(struct usb_controller *hc); + + struct usb_controller *next; +}; + +void usb_controller_add(struct usb_controller *hc); +void usb_daemon_start(void); +void usb_poll(void); diff --git a/include/sys/sched.h b/include/sys/sched.h index 19bf342..741dabc 100644 --- a/include/sys/sched.h +++ b/include/sys/sched.h @@ -8,5 +8,7 @@ void sched_unqueue(struct thread *thr, enum thread_state new_state); void sched_debug_cycle(uint64_t delta_ms); void sched_reboot(unsigned int cmd); +void yield(void); + void sched_init(void); void sched_enter(void); diff --git a/sys/amd64/kernel.c b/sys/amd64/kernel.c index 9aca6e8..5b21b17 100644 --- a/sys/amd64/kernel.c +++ b/sys/amd64/kernel.c @@ -1,5 +1,6 @@ #include "sys/amd64/hw/rs232.h" #include "sys/driver/pci/pci.h" +#include "sys/driver/usb/usb.h" #include "sys/amd64/hw/apic.h" #include "sys/amd64/hw/acpi.h" #include "sys/amd64/mm/phys.h" @@ -88,6 +89,7 @@ void kernel_main(struct amd64_loader_data *data) { sched_init(); sysfs_populate(); + usb_daemon_start(); user_init_start(); sched_enter(); diff --git a/sys/driver/usb/device.c b/sys/driver/usb/device.c new file mode 100644 index 0000000..db7c188 --- /dev/null +++ b/sys/driver/usb/device.c @@ -0,0 +1,335 @@ +#include "sys/driver/usb/request.h" +#include "sys/driver/usb/device.h" +#include "sys/driver/usb/driver.h" +#include "sys/string.h" +#include "sys/assert.h" +#include "sys/debug.h" +#include "sys/heap.h" + +static uint32_t g_device_address = 0; + +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; + req.request = request; + req.type = type; + req.value = value; + req.index = index; + req.length = len; + + struct usb_transfer t; + t.request = &req; + t.data = data; + t.length = len; + t.flags = 0; + + _assert(dev->hc_control); + dev->hc_control(dev, &t); + + return !(t.flags & USB_TRANSFER_SUCCESS); +} + +static int usb_device_get_langs(struct usb_device *dev, uint16_t *langs, uint16_t lim) { + char buf[256]; + struct usb_desc_string *desc; + int res; + + langs[0] = 0; + desc = (struct usb_desc_string *) buf; + + // Get string length + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + 0, + USB_DESCRIPTOR_STRING << 8, + 1, desc)) != 0) { + kwarn("Failed to get string descriptor length\n"); + return res; + } + + if (((desc->len - 2) / 2) >= lim) { + kwarn("Not enough buffer space to store device language data\n"); + return -1; + } + + // Get lang data + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + 0, + USB_DESCRIPTOR_STRING << 8, + desc->len, desc))) { + kwarn("Failed to get string descriptor\n"); + return res; + } + + uint16_t lang_len = (desc->len - 2) / 2; + uint16_t i; + for (i = 0; i < lang_len; ++i) { + langs[i] = desc->data[i]; + } + langs[i] = 0; + + return 0; +} + +static int usb_device_get_string(struct usb_device *dev, char *buf, size_t lim, uint16_t lang, uint16_t index) { + buf[0] = 0; + if (!index) { + return 0; + } + + char _buf[256]; + struct usb_desc_string *desc = (struct usb_desc_string *) _buf; + int res; + + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + lang, + (USB_DESCRIPTOR_STRING << 8) | index, + 1, desc)) != 0) { + kwarn("Failed to get string length\n"); + return res; + } + + size_t len = (desc->len - 2) / 2; + // Check that buffer has enough space to fit the string + if (lim <= len) { + return -1; + } + + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + lang, + (USB_DESCRIPTOR_STRING << 8) | index, + desc->len, desc)) != 0) { + kwarn("Failed to get string length\n"); + return res; + } + + // Fuck unicode, we're only doing ASCII here + for (size_t i = 0; i < len; ++i) { + buf[i] = (char) desc->data[i]; + } + buf[len] = 0; + + return len; +} + +int usb_device_init(struct usb_device *dev) { + struct usb_desc_device device_desc; + char string_buffer[256]; + uint16_t langs[32]; + uint32_t dev_addr; + int res; + + kinfo("Getting partial descriptor\n"); + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + 0, + USB_DESCRIPTOR_DEVICE << 8, + 8, &device_desc)) != 0) { + kwarn("Failed to get device descriptor\n"); + return res; + } + + dev->max_packet = device_desc.max_packet_zero; + + // Allocate a new address for the device + dev_addr = ++g_device_address; + + kinfo("Assigning new address\n"); + if ((res = usb_device_request(dev, + 0, + USB_REQUEST_SET_ADDRESS, + 0, + dev_addr, + 0, 0))) { + kwarn("Failed to set device address\n"); + return res; + } + dev->address = dev_addr; + + // Wait a bit + for (size_t _ = 0; _ < 100000; ++_); + + kinfo("Reading full descriptor\n"); + memset(&device_desc, 0, sizeof(struct usb_desc_device)); + // Read full device descriptor + if ((res = usb_device_request(dev, + USB_REQUEST_TYPE_D2H, + USB_REQUEST_GET_DESCRIPTOR, + 0, + USB_DESCRIPTOR_DEVICE << 8, + sizeof(struct usb_desc_device), &device_desc)) != 0) { + kwarn("Failed to get full device descriptor\n"); + return res; + } + + kinfo("Specification version: %04x\n", device_desc.bcd_usb); + kinfo("Device class: %02x:%02x\n", device_desc.dev_class, device_desc.dev_subclass); + kinfo("Protocol: %02x\n", device_desc.dev_protocol); + kinfo("Device ID: %04x:%04x\n", device_desc.id_vendor, device_desc.id_product); + + // Dump detailed info + if ((res = usb_device_get_langs(dev, langs, sizeof(langs) / sizeof(langs[0]))) != 0) { + return res; + } + + + 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", string_buffer); + } + + 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", 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); + + if (usb_device_get_string(dev, string_buffer, sizeof(string_buffer), lang, interface_desc->idx_interface) > 0) { + kinfo("Interface name: \"%s\"\n", string_buffer); + } + + break; + case USB_DESCRIPTOR_ENDPOINT: + endpoint_desc = (struct usb_desc_endpoint *) data; + if (!picked_endpoint_desc) { + picked_endpoint_desc = endpoint_desc; + } + kinfo("* Endpoint descriptor\n"); + kinfo("Address: %02x\n", endpoint_desc->address); + switch (endpoint_desc->attributes & 0x3) { + case 0: + kinfo("Control transfers\n"); + break; + case 1: + kinfo("Isochronous transfers\n"); + break; + case 2: + kinfo("Bulk transfers\n"); + break; + case 3: + kinfo("Interrupt transfers\n"); + break; + } + 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/driver/usb/driver.c b/sys/driver/usb/driver.c new file mode 100644 index 0000000..04573a0 --- /dev/null +++ b/sys/driver/usb/driver.c @@ -0,0 +1,19 @@ +#include "sys/driver/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/driver/usb/usb.c b/sys/driver/usb/usb.c new file mode 100644 index 0000000..f8af821 --- /dev/null +++ b/sys/driver/usb/usb.c @@ -0,0 +1,68 @@ +#include "sys/driver/usb/device.h" +#include "sys/driver/usb/driver.h" +#include "sys/driver/usb/usb.h" +#include "sys/amd64/cpu.h" +#include "sys/thread.h" +#include "sys/assert.h" +#include "sys/sched.h" +#include "sys/debug.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); + } + } +} + +static void *usb_daemon(void *arg) { + kinfo("USB daemon started\n"); + + while (1) { + usb_poll(); + // Sleep for 10ms + thread_sleep(thread_self, system_time + 10000000ULL, NULL); + } + + panic("This code should not run\n"); +} + +void usb_daemon_start(void) { + static struct thread usbd_thread; + if (!g_hc_list) { + kinfo("Not starting USB daemon - no HCs\n"); + // No controllers to poll + return; + } + + _assert(thread_init(&usbd_thread, (uintptr_t) usb_daemon, NULL, 0) == 0); + usbd_thread.pid = thread_alloc_pid(0); + sched_queue(&usbd_thread); +} + +void usb_controller_add(struct usb_controller *hc) { + hc->next = g_hc_list; + g_hc_list = hc; +} diff --git a/sys/driver/usb/usb_uhci.c b/sys/driver/usb/usb_uhci.c index b18ff61..b680670 100644 --- a/sys/driver/usb/usb_uhci.c +++ b/sys/driver/usb/usb_uhci.c @@ -1,13 +1,14 @@ -#include "sys/amd64/hw/pci/usb_uhci.h" +#include "sys/driver/usb/request.h" +#include "sys/driver/usb/device.h" +#include "sys/driver/pci/pci.h" +#include "sys/driver/usb/usb.h" #include "sys/amd64/mm/phys.h" #include "sys/amd64/hw/io.h" -#include "sys/usb/request.h" -#include "sys/usb/device.h" -#include "sys/usb/usb.h" #include "sys/string.h" #include "sys/assert.h" #include "sys/debug.h" #include "sys/heap.h" +#include "sys/attr.h" #include "sys/mm.h" #define IO_USBCMD 0x00 @@ -444,7 +445,7 @@ static void uhci_device_control(struct usb_device *dev, struct usb_transfer *t) } } -/* static */ void uhci_poll(struct usb_controller *_hc) { +static void uhci_poll(struct usb_controller *_hc) { struct uhci *hc = (struct uhci *) _hc; struct uhci_qh *qh = hc->qh_async; @@ -461,34 +462,35 @@ static void uhci_device_control(struct usb_device *dev, struct usb_transfer *t) } } -//void pci_usb_uhci_init(pci_addr_t addr) { -//} -// uint32_t bar4; -// struct uhci *hc = kmalloc(sizeof(struct uhci)); -// _assert(hc); -// -// kdebug("Initializing USB UHCI at " PCI_FMTADDR "\n", PCI_VAADDR(addr)); -// -// bar4 = pci_config_read_dword(addr, PCI_CONFIG_BAR(4)); -// _assert(bar4 & 1); -// uhci_data_init(hc, bar4); -// kdebug("Base addr %p\n", hc->frame_list); -// -// // Disable IRQs -// outw(hc->iobase + IO_USBINTR, 0); -// // Setup frame lists -// outl(hc->iobase + IO_FRBASEADD, (uint32_t) MM_PHYS(hc->frame_list)); -// outw(hc->iobase + IO_FRNUM, 0); -// outw(hc->iobase + IO_SOFMOD, 0x40); -// // Clear status -// outw(hc->iobase + IO_USBSTS, 0xFFFF); -// // Enable controller -// outw(hc->iobase + IO_USBCMD, USBCMD_RUN); -// -// uhci_probe(hc); -// -// hc->hc.spec = USB_SPEC_UHCI; -// hc->hc.hc_poll = uhci_poll; -// -// usb_controller_add((struct usb_controller *) hc); -//} +void pci_usb_uhci_init(struct pci_device *pci_dev) { + uint32_t bar4; + struct uhci *hc = kmalloc(sizeof(struct uhci)); + _assert(hc); + + bar4 = pci_config_read_dword(pci_dev, PCI_CONFIG_BAR(4)); + _assert(bar4 & 1); + uhci_data_init(hc, bar4); + kdebug("Base addr %p\n", hc->frame_list); + + // Disable IRQs + outw(hc->iobase + IO_USBINTR, 0); + // Setup frame lists + outl(hc->iobase + IO_FRBASEADD, (uint32_t) MM_PHYS(hc->frame_list)); + outw(hc->iobase + IO_FRNUM, 0); + outw(hc->iobase + IO_SOFMOD, 0x40); + // Clear status + outw(hc->iobase + IO_USBSTS, 0xFFFF); + // Enable controller + outw(hc->iobase + IO_USBCMD, USBCMD_RUN); + + uhci_probe(hc); + + hc->hc.spec = USB_SPEC_UHCI; + hc->hc.hc_poll = uhci_poll; + + usb_controller_add((struct usb_controller *) hc); +} + +static __init void pci_usb_uhci_register(void) { + pci_add_class_driver(0x0C0300, pci_usb_uhci_init); +} diff --git a/sys/driver/usb/usbkbd.c b/sys/driver/usb/usbkbd.c new file mode 100644 index 0000000..e885cd8 --- /dev/null +++ b/sys/driver/usb/usbkbd.c @@ -0,0 +1,154 @@ +#include "sys/driver/usb/driver.h" +#include "sys/driver/usb/device.h" +#include "sys/driver/usb/const.h" +#include "sys/char/input.h" +#include "sys/char/tty.h" +#include "sys/string.h" +#include "sys/assert.h" +#include "sys/debug.h" +#include "sys/attr.h" +#include "sys/heap.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/sched.c b/sys/sched.c index 326a4b1..f2714fb 100644 --- a/sys/sched.c +++ b/sys/sched.c @@ -212,7 +212,7 @@ void sched_reboot(unsigned int cmd) { void sched_init(void) { thread_init(&thread_idle, (uintptr_t) idle, 0, 0); - thread_idle.pid = -1; + thread_idle.pid = 0; } void sched_enter(void) {