From 5073bf8e75a1e71be8bbcdc96c10c3711e8ae22c Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 25 Jun 2021 09:51:22 +0300 Subject: Kernel/USB: Move the USB components as a subfolder to the Bus directory --- Kernel/Bus/USB/PacketTypes.h | 39 ++ Kernel/Bus/USB/UHCIController.cpp | 791 +++++++++++++++++++++++++++++++ Kernel/Bus/USB/UHCIController.h | 100 ++++ Kernel/Bus/USB/UHCIDescriptorTypes.h | 367 ++++++++++++++ Kernel/Bus/USB/USBDescriptors.h | 104 ++++ Kernel/Bus/USB/USBDevice.cpp | 105 ++++ Kernel/Bus/USB/USBDevice.h | 59 +++ Kernel/Bus/USB/USBEndpoint.h | 60 +++ Kernel/Bus/USB/USBPipe.cpp | 84 ++++ Kernel/Bus/USB/USBPipe.h | 77 +++ Kernel/Bus/USB/USBRequest.h | 37 ++ Kernel/Bus/USB/USBTransfer.cpp | 51 ++ Kernel/Bus/USB/USBTransfer.h | 51 ++ Kernel/CMakeLists.txt | 8 +- Kernel/Devices/USB/PacketTypes.h | 39 -- Kernel/Devices/USB/UHCIController.cpp | 791 ------------------------------- Kernel/Devices/USB/UHCIController.h | 100 ---- Kernel/Devices/USB/UHCIDescriptorTypes.h | 367 -------------- Kernel/Devices/USB/USBDescriptors.h | 104 ---- Kernel/Devices/USB/USBDevice.cpp | 105 ---- Kernel/Devices/USB/USBDevice.h | 59 --- Kernel/Devices/USB/USBEndpoint.h | 60 --- Kernel/Devices/USB/USBPipe.cpp | 84 ---- Kernel/Devices/USB/USBPipe.h | 77 --- Kernel/Devices/USB/USBRequest.h | 37 -- Kernel/Devices/USB/USBTransfer.cpp | 51 -- Kernel/Devices/USB/USBTransfer.h | 51 -- Kernel/init.cpp | 2 +- 28 files changed, 1930 insertions(+), 1930 deletions(-) create mode 100644 Kernel/Bus/USB/PacketTypes.h create mode 100644 Kernel/Bus/USB/UHCIController.cpp create mode 100644 Kernel/Bus/USB/UHCIController.h create mode 100644 Kernel/Bus/USB/UHCIDescriptorTypes.h create mode 100644 Kernel/Bus/USB/USBDescriptors.h create mode 100644 Kernel/Bus/USB/USBDevice.cpp create mode 100644 Kernel/Bus/USB/USBDevice.h create mode 100644 Kernel/Bus/USB/USBEndpoint.h create mode 100644 Kernel/Bus/USB/USBPipe.cpp create mode 100644 Kernel/Bus/USB/USBPipe.h create mode 100644 Kernel/Bus/USB/USBRequest.h create mode 100644 Kernel/Bus/USB/USBTransfer.cpp create mode 100644 Kernel/Bus/USB/USBTransfer.h delete mode 100644 Kernel/Devices/USB/PacketTypes.h delete mode 100644 Kernel/Devices/USB/UHCIController.cpp delete mode 100644 Kernel/Devices/USB/UHCIController.h delete mode 100644 Kernel/Devices/USB/UHCIDescriptorTypes.h delete mode 100644 Kernel/Devices/USB/USBDescriptors.h delete mode 100644 Kernel/Devices/USB/USBDevice.cpp delete mode 100644 Kernel/Devices/USB/USBDevice.h delete mode 100644 Kernel/Devices/USB/USBEndpoint.h delete mode 100644 Kernel/Devices/USB/USBPipe.cpp delete mode 100644 Kernel/Devices/USB/USBPipe.h delete mode 100644 Kernel/Devices/USB/USBRequest.h delete mode 100644 Kernel/Devices/USB/USBTransfer.cpp delete mode 100644 Kernel/Devices/USB/USBTransfer.h (limited to 'Kernel') diff --git a/Kernel/Bus/USB/PacketTypes.h b/Kernel/Bus/USB/PacketTypes.h new file mode 100644 index 0000000000..8dc65094ef --- /dev/null +++ b/Kernel/Bus/USB/PacketTypes.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Kernel::USB { + +// Setup descriptor bit definitions +static constexpr u8 BM_REQUEST_HOST_TO_DEVICE = (0 << 7); +static constexpr u8 BM_REQUEST_DEVICE_TO_HOST = (1 << 7); +static constexpr u8 BM_REQUEST_TYPE_STANDARD = (0 << 5); +static constexpr u8 BM_REQUEST_TYPE_CLASS = (1 << 5); +static constexpr u8 BM_REQUEST_TYPE_VENDOR = (2 << 5); +static constexpr u8 BM_REQUEST_TYPE_RESERVED = (3 << 5); +static constexpr u8 BM_REQUEST_RECIPEINT_DEVICE = (0 << 0); +static constexpr u8 BM_REQUEST_RECIPIENT_INTERFACE = (1 << 0); +static constexpr u8 BM_REQUEST_RECIPIENT_ENDPOINT = (2 << 0); +static constexpr u8 BM_REQUEST_RECIPIENT_OTHER = (3 << 0); + +// +// This is also known as the "setup" packet. It's attached to the +// first TD in the chain and is the first piece of data sent to the +// USB device over the bus. +// https://beyondlogic.org/usbnutshell/usb6.shtml#StandardEndpointRequests +// +struct USBRequestData { + u8 request_type; + u8 request; + u16 value; + u16 index; + u16 length; +}; + +} diff --git a/Kernel/Bus/USB/UHCIController.cpp b/Kernel/Bus/USB/UHCIController.cpp new file mode 100644 index 0000000000..7c0a565ff4 --- /dev/null +++ b/Kernel/Bus/USB/UHCIController.cpp @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static constexpr u8 MAXIMUM_NUMBER_OF_TDS = 128; // Upper pool limit. This consumes the second page we have allocated +static constexpr u8 MAXIMUM_NUMBER_OF_QHS = 64; +static constexpr u8 RETRY_COUNTER_RELOAD = 3; + +namespace Kernel::USB { + +static UHCIController* s_the; + +static constexpr u16 UHCI_USBCMD_RUN = 0x0001; +static constexpr u16 UHCI_USBCMD_HOST_CONTROLLER_RESET = 0x0002; +static constexpr u16 UHCI_USBCMD_GLOBAL_RESET = 0x0004; +static constexpr u16 UHCI_USBCMD_ENTER_GLOBAL_SUSPEND_MODE = 0x0008; +static constexpr u16 UHCI_USBCMD_FORCE_GLOBAL_RESUME = 0x0010; +static constexpr u16 UHCI_USBCMD_SOFTWARE_DEBUG = 0x0020; +static constexpr u16 UHCI_USBCMD_CONFIGURE_FLAG = 0x0040; +static constexpr u16 UHCI_USBCMD_MAX_PACKET = 0x0080; + +static constexpr u16 UHCI_USBSTS_HOST_CONTROLLER_HALTED = 0x0020; +static constexpr u16 UHCI_USBSTS_HOST_CONTROLLER_PROCESS_ERROR = 0x0010; +static constexpr u16 UHCI_USBSTS_PCI_BUS_ERROR = 0x0008; +static constexpr u16 UHCI_USBSTS_RESUME_RECEIVED = 0x0004; +static constexpr u16 UHCI_USBSTS_USB_ERROR_INTERRUPT = 0x0002; +static constexpr u16 UHCI_USBSTS_USB_INTERRUPT = 0x0001; + +static constexpr u8 UHCI_USBINTR_TIMEOUT_CRC_ENABLE = 0x01; +static constexpr u8 UHCI_USBINTR_RESUME_INTR_ENABLE = 0x02; +static constexpr u8 UHCI_USBINTR_IOC_ENABLE = 0x04; +static constexpr u8 UHCI_USBINTR_SHORT_PACKET_INTR_ENABLE = 0x08; + +static constexpr u16 UHCI_FRAMELIST_FRAME_COUNT = 1024; // Each entry is 4 bytes in our allocated page +static constexpr u16 UHCI_FRAMELIST_FRAME_INVALID = 0x0001; + +// Port stuff +static constexpr u8 UHCI_ROOT_PORT_COUNT = 2; +static constexpr u16 UHCI_PORTSC_CURRRENT_CONNECT_STATUS = 0x0001; +static constexpr u16 UHCI_PORTSC_CONNECT_STATUS_CHANGED = 0x0002; +static constexpr u16 UHCI_PORTSC_PORT_ENABLED = 0x0004; +static constexpr u16 UHCI_PORTSC_PORT_ENABLE_CHANGED = 0x0008; +static constexpr u16 UHCI_PORTSC_LINE_STATUS = 0x0030; +static constexpr u16 UHCI_PORTSC_RESUME_DETECT = 0x40; +static constexpr u16 UHCI_PORTSC_LOW_SPEED_DEVICE = 0x0100; +static constexpr u16 UHCI_PORTSC_PORT_RESET = 0x0200; +static constexpr u16 UHCI_PORTSC_SUSPEND = 0x1000; + +// *BSD and a few other drivers seem to use this number +static constexpr u8 UHCI_NUMBER_OF_ISOCHRONOUS_TDS = 128; +static constexpr u16 UHCI_NUMBER_OF_FRAMES = 1024; + +class ProcFSUSBBusFolder; +static ProcFSUSBBusFolder* s_procfs_usb_bus_folder; + +class ProcFSUSBDeviceInformation : public ProcFSGlobalInformation { + friend class ProcFSUSBBusFolder; + +public: + virtual ~ProcFSUSBDeviceInformation() override {}; + + static NonnullRefPtr create(USB::Device&); + + RefPtr device() const { return m_device; } + +protected: + explicit ProcFSUSBDeviceInformation(USB::Device& device) + : ProcFSGlobalInformation(String::formatted("{}", device.address())) + , m_device(device) + { + } + virtual bool output(KBufferBuilder& builder) override + { + VERIFY(m_device); // Something has gone very wrong if this isn't true + + JsonArraySerializer array { builder }; + + auto obj = array.add_object(); + obj.add("usb_spec_compliance_bcd", m_device->device_descriptor().usb_spec_compliance_bcd); + obj.add("device_class", m_device->device_descriptor().device_class); + obj.add("device_sub_class", m_device->device_descriptor().device_sub_class); + obj.add("device_protocol", m_device->device_descriptor().device_protocol); + obj.add("max_packet_size", m_device->device_descriptor().max_packet_size); + obj.add("vendor_id", m_device->device_descriptor().vendor_id); + obj.add("product_id", m_device->device_descriptor().product_id); + obj.add("device_release_bcd", m_device->device_descriptor().device_release_bcd); + obj.add("manufacturer_id_descriptor_index", m_device->device_descriptor().manufacturer_id_descriptor_index); + obj.add("product_string_descriptor_index", m_device->device_descriptor().product_string_descriptor_index); + obj.add("serial_number_descriptor_index", m_device->device_descriptor().serial_number_descriptor_index); + obj.add("num_configurations", m_device->device_descriptor().num_configurations); + obj.finish(); + array.finish(); + return true; + } + IntrusiveListNode> m_list_node; + RefPtr m_device; +}; + +class ProcFSUSBBusFolder final : public ProcFSExposedFolder { + friend class ProcFSComponentsRegistrar; + +public: + static void initialize(); + void plug(USB::Device&); + void unplug(USB::Device&); + + virtual KResultOr entries_count() const override; + virtual KResult traverse_as_directory(unsigned, Function) const override; + virtual RefPtr lookup(StringView name) override; + +private: + ProcFSUSBBusFolder(const ProcFSBusDirectory&); + + RefPtr device_node_for(USB::Device& device); + + IntrusiveList, &ProcFSUSBDeviceInformation::m_list_node> m_device_nodes; + mutable SpinLock m_lock; +}; + +KResultOr ProcFSUSBBusFolder::entries_count() const +{ + ScopedSpinLock lock(m_lock); + return m_device_nodes.size_slow(); +} +KResult ProcFSUSBBusFolder::traverse_as_directory(unsigned fsid, Function callback) const +{ + ScopedSpinLock lock(m_lock); + VERIFY(m_parent_folder); + callback({ ".", { fsid, component_index() }, 0 }); + callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); + + for (auto& device_node : m_device_nodes) { + InodeIdentifier identifier = { fsid, device_node.component_index() }; + callback({ device_node.name(), identifier, 0 }); + } + return KSuccess; +} +RefPtr ProcFSUSBBusFolder::lookup(StringView name) +{ + ScopedSpinLock lock(m_lock); + for (auto& device_node : m_device_nodes) { + if (device_node.name() == name) { + return device_node; + } + } + return {}; +} + +RefPtr ProcFSUSBBusFolder::device_node_for(USB::Device& device) +{ + RefPtr checked_device = device; + for (auto& device_node : m_device_nodes) { + if (device_node.device().ptr() == checked_device.ptr()) + return device_node; + } + return {}; +} + +void ProcFSUSBBusFolder::plug(USB::Device& new_device) +{ + ScopedSpinLock lock(m_lock); + auto device_node = device_node_for(new_device); + VERIFY(!device_node); + m_device_nodes.append(ProcFSUSBDeviceInformation::create(new_device)); +} +void ProcFSUSBBusFolder::unplug(USB::Device& deleted_device) +{ + ScopedSpinLock lock(m_lock); + auto device_node = device_node_for(deleted_device); + VERIFY(device_node); + device_node->m_list_node.remove(); +} + +UNMAP_AFTER_INIT ProcFSUSBBusFolder::ProcFSUSBBusFolder(const ProcFSBusDirectory& buses_folder) + : ProcFSExposedFolder("usb"sv, buses_folder) +{ +} + +UNMAP_AFTER_INIT void ProcFSUSBBusFolder::initialize() +{ + auto folder = adopt_ref(*new ProcFSUSBBusFolder(ProcFSComponentsRegistrar::the().buses_folder())); + ProcFSComponentsRegistrar::the().register_new_bus_folder(folder); + s_procfs_usb_bus_folder = folder; +} + +NonnullRefPtr ProcFSUSBDeviceInformation::create(USB::Device& device) +{ + return adopt_ref(*new ProcFSUSBDeviceInformation(device)); +} + +UHCIController& UHCIController::the() +{ + return *s_the; +} + +UNMAP_AFTER_INIT void UHCIController::detect() +{ + if (kernel_command_line().disable_uhci_controller()) + return; + + // FIXME: We create the /proc/bus/usb representation here, but it should really be handled + // in a more broad singleton than this once we refactor things in USB subsystem. + ProcFSUSBBusFolder::initialize(); + + PCI::enumerate([&](const PCI::Address& address, PCI::ID id) { + if (address.is_null()) + return; + + if (PCI::get_class(address) == 0xc && PCI::get_subclass(address) == 0x03 && PCI::get_programming_interface(address) == 0) { + if (!s_the) { + s_the = new UHCIController(address, id); + s_the->spawn_port_proc(); + } + } + }); +} + +UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::Address address, PCI::ID id) + : PCI::Device(address) + , m_io_base(PCI::get_BAR4(pci_address()) & ~1) +{ + dmesgln("UHCI: Controller found {} @ {}", id, address); + dmesgln("UHCI: I/O base {}", m_io_base); + dmesgln("UHCI: Interrupt line: {}", PCI::get_interrupt_line(pci_address())); + + reset(); + start(); +} + +UNMAP_AFTER_INIT UHCIController::~UHCIController() +{ +} + +RefPtr const UHCIController::get_device_at_port(USB::Device::PortNumber port) +{ + if (!m_devices.at(to_underlying(port))) + return nullptr; + + return m_devices.at(to_underlying(port)); +} + +RefPtr const UHCIController::get_device_from_address(u8 device_address) +{ + for (auto const& device : m_devices) { + if (!device) + continue; + + if (device->address() == device_address) + return device; + } + + return nullptr; +} + +void UHCIController::reset() +{ + stop(); + + write_usbcmd(UHCI_USBCMD_HOST_CONTROLLER_RESET); + + // FIXME: Timeout + for (;;) { + if (read_usbcmd() & UHCI_USBCMD_HOST_CONTROLLER_RESET) + continue; + break; + } + + // Let's allocate the physical page for the Frame List (which is 4KiB aligned) + auto framelist_vmobj = ContiguousVMObject::create_with_size(PAGE_SIZE); + m_framelist = MemoryManager::the().allocate_kernel_region_with_vmobject(*framelist_vmobj, PAGE_SIZE, "UHCI Framelist", Region::Access::Write); + dbgln("UHCI: Allocated framelist at physical address {}", m_framelist->physical_page(0)->paddr()); + dbgln("UHCI: Framelist is at virtual address {}", m_framelist->vaddr()); + write_sofmod(64); // 1mS frame time + + create_structures(); + setup_schedule(); + + write_flbaseadd(m_framelist->physical_page(0)->paddr().get()); // Frame list (physical) address + write_frnum(0); // Set the initial frame number + + // FIXME: Work out why interrupts lock up the entire system.... + // Disable UHCI Controller from raising an IRQ + write_usbintr(0); + dbgln("UHCI: Reset completed"); +} + +UNMAP_AFTER_INIT void UHCIController::create_structures() +{ + // Let's allocate memory for both the QH and TD pools + // First the QH pool and all of the Interrupt QH's + auto qh_pool_vmobject = ContiguousVMObject::create_with_size(2 * PAGE_SIZE); + m_qh_pool = MemoryManager::the().allocate_kernel_region_with_vmobject(*qh_pool_vmobject, 2 * PAGE_SIZE, "UHCI Queue Head Pool", Region::Access::Write); + memset(m_qh_pool->vaddr().as_ptr(), 0, 2 * PAGE_SIZE); // Zero out both pages + + // Let's populate our free qh list (so we have some we can allocate later on) + m_free_qh_pool.resize(MAXIMUM_NUMBER_OF_TDS); + for (size_t i = 0; i < m_free_qh_pool.size(); i++) { + auto placement_addr = reinterpret_cast(m_qh_pool->vaddr().get() + (i * sizeof(QueueHead))); + auto paddr = static_cast(m_qh_pool->physical_page(0)->paddr().get() + (i * sizeof(QueueHead))); + m_free_qh_pool.at(i) = new (placement_addr) QueueHead(paddr); + } + + // Create the Full Speed, Low Speed Control and Bulk Queue Heads + m_interrupt_transfer_queue = allocate_queue_head(); + m_lowspeed_control_qh = allocate_queue_head(); + m_fullspeed_control_qh = allocate_queue_head(); + m_bulk_qh = allocate_queue_head(); + m_dummy_qh = allocate_queue_head(); + + // Now the Transfer Descriptor pool + auto td_pool_vmobject = ContiguousVMObject::create_with_size(2 * PAGE_SIZE); + m_td_pool = MemoryManager::the().allocate_kernel_region_with_vmobject(*td_pool_vmobject, 2 * PAGE_SIZE, "UHCI Transfer Descriptor Pool", Region::Access::Write); + memset(m_td_pool->vaddr().as_ptr(), 0, 2 * PAGE_SIZE); + + // Set up the Isochronous Transfer Descriptor list + m_iso_td_list.resize(UHCI_NUMBER_OF_ISOCHRONOUS_TDS); + for (size_t i = 0; i < m_iso_td_list.size(); i++) { + auto placement_addr = reinterpret_cast(m_td_pool->vaddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); + auto paddr = static_cast(m_td_pool->physical_page(0)->paddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); + + // Place a new Transfer Descriptor with a 1:1 in our region + // The pointer returned by `new()` lines up exactly with the value + // that we store in `paddr`, meaning our member functions directly + // access the raw descriptor (that we later send to the controller) + m_iso_td_list.at(i) = new (placement_addr) Kernel::USB::TransferDescriptor(paddr); + auto transfer_descriptor = m_iso_td_list.at(i); + transfer_descriptor->set_in_use(true); // Isochronous transfers are ALWAYS marked as in use (in case we somehow get allocated one...) + transfer_descriptor->set_isochronous(); + transfer_descriptor->link_queue_head(m_interrupt_transfer_queue->paddr()); + + if constexpr (UHCI_VERBOSE_DEBUG) + transfer_descriptor->print(); + } + + m_free_td_pool.resize(MAXIMUM_NUMBER_OF_TDS); + for (size_t i = 0; i < m_free_td_pool.size(); i++) { + auto placement_addr = reinterpret_cast(m_td_pool->vaddr().offset(PAGE_SIZE).get() + (i * sizeof(Kernel::USB::TransferDescriptor))); + auto paddr = static_cast(m_td_pool->physical_page(1)->paddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); + + // Place a new Transfer Descriptor with a 1:1 in our region + // The pointer returned by `new()` lines up exactly with the value + // that we store in `paddr`, meaning our member functions directly + // access the raw descriptor (that we later send to the controller) + m_free_td_pool.at(i) = new (placement_addr) Kernel::USB::TransferDescriptor(paddr); + + if constexpr (UHCI_VERBOSE_DEBUG) { + auto transfer_descriptor = m_free_td_pool.at(i); + transfer_descriptor->print(); + } + } + + if constexpr (UHCI_DEBUG) { + dbgln("UHCI: Pool information:"); + dbgln(" qh_pool: {}, length: {}", PhysicalAddress(m_qh_pool->physical_page(0)->paddr()), m_qh_pool->range().size()); + dbgln(" td_pool: {}, length: {}", PhysicalAddress(m_td_pool->physical_page(0)->paddr()), m_td_pool->range().size()); + } +} + +UNMAP_AFTER_INIT void UHCIController::setup_schedule() +{ + // + // https://github.com/alkber/minix3-usbsubsystem/blob/master/usb/uhci-hcd.c + // + // This lad probably has the best explanation as to how this is actually done. I'll try and + // explain it here to so that there's no need for anyone to go hunting for this shit again, because + // the USB spec and Intel explain next to nothing. + // According to the USB spec (and the UHCI datasheet), 90% of the bandwidth should be used for + // Isochronous and """Interrupt""" related transfers, with the rest being used for control and bulk + // transfers. + // That is, most of the time, the schedule is going to be executing either an Isochronous transfer + // in our framelist, or an Interrupt transfer. The allocation in `create_structures` reflects this. + // + // Each frame has it's own Isochronous transfer Transfer Descriptor(s) that point to each other + // horizontally in the list. The end of these transfers then point to the Interrupt Queue Headers, + // in which we can attach Transfer Descriptors (related to Interrupt Transfers). These are attached + // to the Queue Head _vertically_. We need to ensure that these are executed every 8ms, so they are inserted + // at different points in the schedule (TODO: How do we do this?!?!). After the Interrupt Transfer Queue Heads, + // we attach the Control Queue Heads. We need two in total, one for Low Speed devices, and one for Full Speed + // USB devices. Finally, we attach the Bulk Transfer Queue Head. + // Not specified in the datasheet, however, is another Queue Head with an "inactive" Transfer Descriptor. This + // is to circumvent a bug in the silicon of the PIIX4's UHCI controller. + // https://github.com/openbsd/src/blob/master/sys/dev/usb/uhci.c#L390 + // + + m_interrupt_transfer_queue->link_next_queue_head(m_lowspeed_control_qh); + m_interrupt_transfer_queue->terminate_element_link_ptr(); + + m_lowspeed_control_qh->link_next_queue_head(m_fullspeed_control_qh); + m_lowspeed_control_qh->terminate_element_link_ptr(); + + m_fullspeed_control_qh->link_next_queue_head(m_bulk_qh); + m_fullspeed_control_qh->terminate_element_link_ptr(); + + m_bulk_qh->link_next_queue_head(m_dummy_qh); + m_bulk_qh->terminate_element_link_ptr(); + + auto piix4_td_hack = allocate_transfer_descriptor(); + piix4_td_hack->terminate(); + piix4_td_hack->set_max_len(0x7ff); // Null data packet + piix4_td_hack->set_device_address(0x7f); + piix4_td_hack->set_packet_id(PacketID::IN); + m_dummy_qh->terminate_with_stray_descriptor(piix4_td_hack); + m_dummy_qh->terminate_element_link_ptr(); + + u32* framelist = reinterpret_cast(m_framelist->vaddr().as_ptr()); + for (int frame = 0; frame < UHCI_NUMBER_OF_FRAMES; frame++) { + // Each frame pointer points to iso_td % NUM_ISO_TDS + framelist[frame] = m_iso_td_list.at(frame % UHCI_NUMBER_OF_ISOCHRONOUS_TDS)->paddr(); + } + + m_interrupt_transfer_queue->print(); + m_lowspeed_control_qh->print(); + m_fullspeed_control_qh->print(); + m_bulk_qh->print(); + m_dummy_qh->print(); +} + +QueueHead* UHCIController::allocate_queue_head() const +{ + for (QueueHead* queue_head : m_free_qh_pool) { + if (!queue_head->in_use()) { + queue_head->set_in_use(true); + dbgln_if(UHCI_DEBUG, "UHCI: Allocated a new Queue Head! Located @ {} ({})", VirtualAddress(queue_head), PhysicalAddress(queue_head->paddr())); + return queue_head; + } + } + + return nullptr; // Huh!? We're outta queue heads! +} + +TransferDescriptor* UHCIController::allocate_transfer_descriptor() const +{ + for (TransferDescriptor* transfer_descriptor : m_free_td_pool) { + if (!transfer_descriptor->in_use()) { + transfer_descriptor->set_in_use(true); + dbgln_if(UHCI_DEBUG, "UHCI: Allocated a new Transfer Descriptor! Located @ {} ({})", VirtualAddress(transfer_descriptor), PhysicalAddress(transfer_descriptor->paddr())); + return transfer_descriptor; + } + } + + return nullptr; // Huh?! We're outta TDs!! +} + +void UHCIController::stop() +{ + write_usbcmd(read_usbcmd() & ~UHCI_USBCMD_RUN); + // FIXME: Timeout + for (;;) { + if (read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED) + break; + } +} + +void UHCIController::start() +{ + write_usbcmd(read_usbcmd() | UHCI_USBCMD_RUN); + // FIXME: Timeout + for (;;) { + if (!(read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED)) + break; + } + dbgln("UHCI: Started"); +} + +TransferDescriptor* UHCIController::create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len) +{ + TransferDescriptor* td = allocate_transfer_descriptor(); + if (td == nullptr) { + return nullptr; + } + + u16 max_len = (data_len > 0) ? (data_len - 1) : 0x7ff; + VERIFY(max_len <= 0x4FF || max_len == 0x7FF); // According to the datasheet, anything in the range of 0x500 to 0x7FE are illegal + + td->set_token((max_len << TD_TOKEN_MAXLEN_SHIFT) | ((pipe.data_toggle() ? 1 : 0) << TD_TOKEN_DATA_TOGGLE_SHIFT) | (pipe.endpoint_address() << TD_TOKEN_ENDPOINT_SHIFT) | (pipe.device_address() << TD_TOKEN_DEVICE_ADDR_SHIFT) | (static_cast(direction))); + pipe.set_toggle(!pipe.data_toggle()); + + if (pipe.type() == Pipe::Type::Isochronous) { + td->set_isochronous(); + } else { + if (direction == PacketID::IN) { + td->set_short_packet_detect(); + } + } + + // Set low-speed bit if the device connected to port is a low=speed device (probably unlikely...) + if (pipe.device_speed() == Pipe::DeviceSpeed::LowSpeed) { + td->set_lowspeed(); + } + + td->set_active(); + td->set_error_retry_counter(RETRY_COUNTER_RELOAD); + + return td; +} + +KResult UHCIController::create_chain(Pipe& pipe, PacketID direction, Ptr32& buffer_address, size_t max_size, size_t transfer_size, TransferDescriptor** td_chain, TransferDescriptor** last_td) +{ + // We need to create `n` transfer descriptors based on the max + // size of each transfer (which we've learned from the device already by reading + // its device descriptor, or 8 bytes). Each TD then has its buffer pointer + // set to the initial buffer address + (max_size * index), where index is + // the ID of the TD in the chain. + size_t byte_count = 0; + TransferDescriptor* current_td = nullptr; + TransferDescriptor* prev_td = nullptr; + TransferDescriptor* first_td = nullptr; + + // Keep creating transfer descriptors while we still have some data + while (byte_count < transfer_size) { + size_t packet_size = transfer_size - byte_count; + if (packet_size > max_size) { + packet_size = max_size; + } + + current_td = create_transfer_descriptor(pipe, direction, packet_size); + if (current_td == nullptr) { + free_descriptor_chain(first_td); + return ENOMEM; + } + + if (Checked::addition_would_overflow(reinterpret_cast(&*buffer_address), byte_count)) + return EOVERFLOW; + + auto buffer_pointer = Ptr32(buffer_address + byte_count); + current_td->set_buffer_address(buffer_pointer); + byte_count += packet_size; + + if (prev_td != nullptr) + prev_td->insert_next_transfer_descriptor(current_td); + else + first_td = current_td; + + prev_td = current_td; + } + + *last_td = current_td; + *td_chain = first_td; + return KSuccess; +} + +void UHCIController::free_descriptor_chain(TransferDescriptor* first_descriptor) +{ + TransferDescriptor* descriptor = first_descriptor; + + while (descriptor) { + descriptor->free(); + descriptor = descriptor->next_td(); + } +} + +KResultOr UHCIController::submit_control_transfer(Transfer& transfer) +{ + Pipe& pipe = transfer.pipe(); // Short circuit the pipe related to this transfer + bool direction_in = (transfer.request().request_type & USB_DEVICE_REQUEST_DEVICE_TO_HOST) == USB_DEVICE_REQUEST_DEVICE_TO_HOST; + + TransferDescriptor* setup_td = create_transfer_descriptor(pipe, PacketID::SETUP, sizeof(USBRequestData)); + if (!setup_td) + return ENOMEM; + + setup_td->set_buffer_address(transfer.buffer_physical().as_ptr()); + + // Create a new descriptor chain + TransferDescriptor* last_data_descriptor; + TransferDescriptor* data_descriptor_chain; + auto buffer_address = Ptr32(transfer.buffer_physical().as_ptr() + sizeof(USBRequestData)); + auto transfer_chain_create_result = create_chain(pipe, + direction_in ? PacketID::IN : PacketID::OUT, + buffer_address, + pipe.max_packet_size(), + transfer.transfer_data_size(), + &data_descriptor_chain, + &last_data_descriptor); + + if (transfer_chain_create_result != KSuccess) + return transfer_chain_create_result; + + // Status TD always has toggle set to 1 + pipe.set_toggle(true); + + TransferDescriptor* status_td = create_transfer_descriptor(pipe, direction_in ? PacketID::OUT : PacketID::IN, 0); + if (!status_td) { + free_descriptor_chain(data_descriptor_chain); + return ENOMEM; + } + status_td->terminate(); + + // Link transfers together + if (data_descriptor_chain) { + setup_td->insert_next_transfer_descriptor(data_descriptor_chain); + last_data_descriptor->insert_next_transfer_descriptor(status_td); + } else { + setup_td->insert_next_transfer_descriptor(status_td); + } + + // Cool, everything should be chained together now! Let's print it out + if constexpr (UHCI_VERBOSE_DEBUG) { + dbgln("Setup TD"); + setup_td->print(); + if (data_descriptor_chain) { + dbgln("Data TD"); + data_descriptor_chain->print(); + } + dbgln("Status TD"); + status_td->print(); + } + + QueueHead* transfer_queue = allocate_queue_head(); + if (!transfer_queue) { + free_descriptor_chain(data_descriptor_chain); + return 0; + } + + transfer_queue->attach_transfer_descriptor_chain(setup_td); + transfer_queue->set_transfer(&transfer); + + m_fullspeed_control_qh->attach_transfer_queue(*transfer_queue); + + size_t transfer_size = 0; + while (!transfer.complete()) + transfer_size = poll_transfer_queue(*transfer_queue); + + free_descriptor_chain(transfer_queue->get_first_td()); + transfer_queue->free(); + + return transfer_size; +} + +size_t UHCIController::poll_transfer_queue(QueueHead& transfer_queue) +{ + Transfer* transfer = transfer_queue.transfer(); + TransferDescriptor* descriptor = transfer_queue.get_first_td(); + bool transfer_still_in_progress = false; + size_t transfer_size = 0; + + while (descriptor) { + u32 status = descriptor->status(); + + if (status & TransferDescriptor::StatusBits::Active) { + transfer_still_in_progress = true; + break; + } + + if (status & TransferDescriptor::StatusBits::ErrorMask) { + transfer->set_complete(); + transfer->set_error_occurred(); + dbgln_if(UHCI_DEBUG, "UHCIController: Transfer failed! Reason: {:08x}", status); + return 0; + } + + transfer_size += descriptor->actual_packet_length(); + descriptor = descriptor->next_td(); + } + + if (!transfer_still_in_progress) + transfer->set_complete(); + + return transfer_size; +} + +void UHCIController::spawn_port_proc() +{ + RefPtr usb_hotplug_thread; + + Process::create_kernel_process(usb_hotplug_thread, "UHCIHotplug", [&] { + for (;;) { + for (int port = 0; port < UHCI_ROOT_PORT_COUNT; port++) { + u16 port_data = 0; + + if (port == 1) { + // Let's see what's happening on port 1 + // Current status + port_data = read_portsc1(); + if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) { + if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) { + dmesgln("UHCI: Device attach detected on Root Port 1!"); + + // Reset the port + port_data = read_portsc1(); + write_portsc1(port_data | UHCI_PORTSC_PORT_RESET); + IO::delay(500); + + write_portsc1(port_data & ~UHCI_PORTSC_PORT_RESET); + IO::delay(500); + + write_portsc1(port_data & (~UHCI_PORTSC_PORT_ENABLE_CHANGED | ~UHCI_PORTSC_CONNECT_STATUS_CHANGED)); + + port_data = read_portsc1(); + write_portsc1(port_data | UHCI_PORTSC_PORT_ENABLED); + dbgln("port should be enabled now: {:#04x}\n", read_portsc1()); + + USB::Device::DeviceSpeed speed = (port_data & UHCI_PORTSC_LOW_SPEED_DEVICE) ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; + auto device = USB::Device::try_create(USB::Device::PortNumber::Port1, speed); + + if (device.is_error()) + dmesgln("UHCI: Device creation failed on port 1 ({})", device.error()); + + m_devices.at(0) = device.value(); + VERIFY(s_procfs_usb_bus_folder); + s_procfs_usb_bus_folder->plug(device.value()); + } else { + // FIXME: Clean up (and properly) the RefPtr to the device in m_devices + VERIFY(s_procfs_usb_bus_folder); + VERIFY(m_devices.at(0)); + dmesgln("UHCI: Device detach detected on Root Port 1"); + s_procfs_usb_bus_folder->unplug(*m_devices.at(0)); + } + } + } else { + port_data = UHCIController::the().read_portsc2(); + if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) { + if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) { + dmesgln("UHCI: Device attach detected on Root Port 2"); + + // Reset the port + port_data = read_portsc2(); + write_portsc2(port_data | UHCI_PORTSC_PORT_RESET); + for (size_t i = 0; i < 50000; ++i) + IO::in8(0x80); + + write_portsc2(port_data & ~UHCI_PORTSC_PORT_RESET); + for (size_t i = 0; i < 100000; ++i) + IO::in8(0x80); + + write_portsc2(port_data & (~UHCI_PORTSC_PORT_ENABLE_CHANGED | ~UHCI_PORTSC_CONNECT_STATUS_CHANGED)); + + port_data = read_portsc2(); + write_portsc1(port_data | UHCI_PORTSC_PORT_ENABLED); + dbgln("port should be enabled now: {:#04x}\n", read_portsc1()); + USB::Device::DeviceSpeed speed = (port_data & UHCI_PORTSC_LOW_SPEED_DEVICE) ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; + auto device = USB::Device::try_create(USB::Device::PortNumber::Port2, speed); + + if (device.is_error()) + dmesgln("UHCI: Device creation failed on port 2 ({})", device.error()); + + m_devices.at(1) = device.value(); + VERIFY(s_procfs_usb_bus_folder); + s_procfs_usb_bus_folder->plug(device.value()); + } else { + // FIXME: Clean up (and properly) the RefPtr to the device in m_devices + VERIFY(s_procfs_usb_bus_folder); + VERIFY(m_devices.at(1)); + dmesgln("UHCI: Device detach detected on Root Port 2"); + s_procfs_usb_bus_folder->unplug(*m_devices.at(1)); + } + } + } + } + (void)Thread::current()->sleep(Time::from_seconds(1)); + } + }); +} + +bool UHCIController::handle_irq(const RegisterState&) +{ + u32 status = read_usbsts(); + + // Shared IRQ. Not ours! + if (!status) + return false; + + if constexpr (UHCI_DEBUG) { + dbgln("UHCI: Interrupt happened!"); + dbgln("Value of USBSTS: {:#04x}", read_usbsts()); + } + + // Write back USBSTS to clear bits + write_usbsts(status); + return true; +} + +} diff --git a/Kernel/Bus/USB/UHCIController.h b/Kernel/Bus/USB/UHCIController.h new file mode 100644 index 0000000000..a4ab4b5a60 --- /dev/null +++ b/Kernel/Bus/USB/UHCIController.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020-2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel::USB { + +class UHCIController final : public PCI::Device { + +public: + static void detect(); + static UHCIController& the(); + + virtual ~UHCIController() override; + + virtual const char* purpose() const override { return "UHCI"; } + + void reset(); + void stop(); + void start(); + void spawn_port_proc(); + + void do_debug_transfer(); + + KResultOr submit_control_transfer(Transfer& transfer); + + RefPtr const get_device_at_port(USB::Device::PortNumber); + RefPtr const get_device_from_address(u8 device_address); + +private: + UHCIController(PCI::Address, PCI::ID); + + u16 read_usbcmd() { return m_io_base.offset(0).in(); } + u16 read_usbsts() { return m_io_base.offset(0x2).in(); } + u16 read_usbintr() { return m_io_base.offset(0x4).in(); } + u16 read_frnum() { return m_io_base.offset(0x6).in(); } + u32 read_flbaseadd() { return m_io_base.offset(0x8).in(); } + u8 read_sofmod() { return m_io_base.offset(0xc).in(); } + u16 read_portsc1() { return m_io_base.offset(0x10).in(); } + u16 read_portsc2() { return m_io_base.offset(0x12).in(); } + + void write_usbcmd(u16 value) { m_io_base.offset(0).out(value); } + void write_usbsts(u16 value) { m_io_base.offset(0x2).out(value); } + void write_usbintr(u16 value) { m_io_base.offset(0x4).out(value); } + void write_frnum(u16 value) { m_io_base.offset(0x6).out(value); } + void write_flbaseadd(u32 value) { m_io_base.offset(0x8).out(value); } + void write_sofmod(u8 value) { m_io_base.offset(0xc).out(value); } + void write_portsc1(u16 value) { m_io_base.offset(0x10).out(value); } + void write_portsc2(u16 value) { m_io_base.offset(0x12).out(value); } + + virtual bool handle_irq(const RegisterState&) override; + + void create_structures(); + void setup_schedule(); + size_t poll_transfer_queue(QueueHead& transfer_queue); + + TransferDescriptor* create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len); + KResult create_chain(Pipe& pipe, PacketID direction, Ptr32& buffer_address, size_t max_size, size_t transfer_size, TransferDescriptor** td_chain, TransferDescriptor** last_td); + void free_descriptor_chain(TransferDescriptor* first_descriptor); + + QueueHead* allocate_queue_head() const; + TransferDescriptor* allocate_transfer_descriptor() const; + +private: + IOAddress m_io_base; + + Vector m_free_qh_pool; + Vector m_free_td_pool; + Vector m_iso_td_list; + + QueueHead* m_interrupt_transfer_queue; + QueueHead* m_lowspeed_control_qh; + QueueHead* m_fullspeed_control_qh; + QueueHead* m_bulk_qh; + QueueHead* m_dummy_qh; // Needed for PIIX4 hack + + OwnPtr m_framelist; + OwnPtr m_qh_pool; + OwnPtr m_td_pool; + + Array, 2> m_devices; // Devices connected to the root ports (of which there are two) +}; + +} diff --git a/Kernel/Bus/USB/UHCIDescriptorTypes.h b/Kernel/Bus/USB/UHCIDescriptorTypes.h new file mode 100644 index 0000000000..5525428a1a --- /dev/null +++ b/Kernel/Bus/USB/UHCIDescriptorTypes.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::USB { + +enum class PacketID : u8 { + IN = 0x69, + OUT = 0xe1, + SETUP = 0x2d +}; + +// Transfer Descriptor register bit offsets/masks +constexpr u16 TD_CONTROL_STATUS_ACTLEN = 0x7ff; +constexpr u8 TD_CONTROL_STATUS_ACTIVE_SHIFT = 23; +constexpr u8 TD_CONTROL_STATUS_INT_ON_COMPLETE_SHIFT = 24; +constexpr u8 TD_CONTROL_STATUS_ISOCHRONOUS_SHIFT = 25; +constexpr u8 TD_CONTROL_STATUS_LS_DEVICE_SHIFT = 26; +constexpr u8 TD_CONTROL_STATUS_ERR_CTR_SHIFT_SHIFT = 27; +constexpr u8 TD_CONTROL_STATUS_SPD_SHIFT = 29; + +constexpr u8 TD_TOKEN_PACKET_ID_SHIFT = 0; +constexpr u8 TD_TOKEN_DEVICE_ADDR_SHIFT = 8; +constexpr u8 TD_TOKEN_ENDPOINT_SHIFT = 15; +constexpr u8 TD_TOKEN_DATA_TOGGLE_SHIFT = 19; +constexpr u8 TD_TOKEN_MAXLEN_SHIFT = 21; + +// +// Transfer Descriptor +// +// Describes a single transfer event from, or to the Universal Serial Bus. +// These are, generally, attached to Queue Heads, and then executed by the +// USB Host Controller. +// Must be 16-byte aligned +// +struct QueueHead; +struct alignas(16) TransferDescriptor final { + enum LinkPointerBits { + Terminate = 1, + QHSelect = 2, + DepthFlag = 4, + }; + + enum StatusBits { + Reserved = (1 << 16), + BitStuffError = (1 << 17), + CRCTimeoutError = (1 << 18), + NAKReceived = (1 << 19), + BabbleDetected = (1 << 20), + DataBufferError = (1 << 21), + Stalled = (1 << 22), + Active = (1 << 23), + ErrorMask = BitStuffError | CRCTimeoutError | NAKReceived | BabbleDetected | DataBufferError | Stalled + }; + + enum ControlBits { + InterruptOnComplete = (1 << 24), + IsochronousSelect = (1 << 25), + LowSpeedDevice = (1 << 26), + ShortPacketDetect = (1 << 29), + }; + + TransferDescriptor() = delete; + TransferDescriptor(u32 paddr) + : m_paddr(paddr) + { + } + ~TransferDescriptor() = delete; // Prevent anything except placement new on this object + + u32 link_ptr() const { return m_link_ptr; } + u32 paddr() const { return m_paddr; } + u32 status() const { return m_control_status; } + u32 token() const { return m_token; } + u32 buffer_ptr() const { return m_buffer_ptr; } + u16 actual_packet_length() const { return (m_control_status + 1) & 0x7ff; } + + bool in_use() const { return m_in_use; } + bool stalled() const { return m_control_status & StatusBits::Stalled; } + bool last_in_chain() const { return m_link_ptr & LinkPointerBits::Terminate; } + bool active() const { return m_control_status & StatusBits::Active; } + + void set_active() + { + u32 ctrl = m_control_status; + ctrl |= StatusBits::Active; + m_control_status = ctrl; + } + + void set_isochronous() + { + u32 ctrl = m_control_status; + ctrl |= ControlBits::IsochronousSelect; + m_control_status = ctrl; + } + + void set_interrupt_on_complete() + { + u32 ctrl = m_control_status; + ctrl |= ControlBits::InterruptOnComplete; + m_control_status = ctrl; + } + + void set_lowspeed() + { + u32 ctrl = m_control_status; + ctrl |= ControlBits::LowSpeedDevice; + m_control_status = ctrl; + } + + void set_error_retry_counter(u8 num_retries) + { + VERIFY(num_retries <= 3); + u32 ctrl = m_control_status; + ctrl |= (num_retries << 27); + m_control_status = ctrl; + } + + void set_short_packet_detect() + { + u32 ctrl = m_control_status; + ctrl |= ControlBits::ShortPacketDetect; + m_control_status = ctrl; + } + + void set_control_status(u32 control_status) { m_control_status = control_status; } + void set_in_use(bool in_use) { m_in_use = in_use; } + void set_max_len(u16 max_len) + { + VERIFY(max_len < 0x500 || max_len == 0x7ff); + m_token |= (max_len << 21); + } + + void set_device_endpoint(u8 endpoint) + { + VERIFY(endpoint <= 0xf); + m_token |= (endpoint << 18); + } + + void set_device_address(u8 address) + { + VERIFY(address <= 0x7f); + m_token |= (address << 8); + } + + void set_data_toggle(bool toggle) + { + m_token |= ((toggle ? (1 << 19) : 0)); + } + + void set_packet_id(PacketID pid) { m_token |= static_cast(pid); } + void link_queue_head(u32 qh_paddr) + { + m_link_ptr = qh_paddr; + m_link_ptr |= LinkPointerBits::QHSelect; + } + + void print() + { + dbgln("UHCI: TD({:#04x}) @ {:#04x}: link_ptr={:#04x}, status={:#04x}, token={:#04x}, buffer_ptr={:#04x}", this, m_paddr, m_link_ptr, (u32)m_control_status, m_token, m_buffer_ptr); + + // Now let's print the flags! + dbgln("UHCI: TD({:#04x}) @ {:#04x}: link_ptr={}{}{}, status={}{}{}{}{}{}{}", + this, + m_paddr, + (last_in_chain()) ? "T " : "", + (m_link_ptr & static_cast(LinkPointerBits::QHSelect)) ? "QH " : "", + (m_link_ptr & static_cast(LinkPointerBits::DepthFlag)) ? "Vf " : "", + (m_control_status & static_cast(StatusBits::BitStuffError)) ? "BITSTUFF " : "", + (m_control_status & static_cast(StatusBits::CRCTimeoutError)) ? "CRCTIMEOUT " : "", + (m_control_status & static_cast(StatusBits::NAKReceived)) ? "NAK " : "", + (m_control_status & static_cast(StatusBits::BabbleDetected)) ? "BABBLE " : "", + (m_control_status & static_cast(StatusBits::DataBufferError)) ? "DATAERR " : "", + (stalled()) ? "STALL " : "", + (active()) ? "ACTIVE " : ""); + } + + // FIXME: For the love of God, use AK SMART POINTERS PLEASE!! + TransferDescriptor* next_td() { return m_next_td; } + const TransferDescriptor* next_td() const { return m_next_td; } + void set_next_td(TransferDescriptor* td) { m_next_td = td; } + + TransferDescriptor* prev_td() { return m_prev_td; } + const TransferDescriptor* prev_td() const { return m_prev_td; } + void set_previous_td(TransferDescriptor* td) { m_prev_td = td; } + + void insert_next_transfer_descriptor(TransferDescriptor* td) + { + m_link_ptr = td->paddr(); + td->set_previous_td(this); + set_next_td(td); + + // Let's set some bits for the link ptr + m_link_ptr |= static_cast(LinkPointerBits::DepthFlag); + } + + void terminate() { m_link_ptr |= static_cast(LinkPointerBits::Terminate); } + + void set_buffer_address(Ptr32 buffer) + { + u8* buffer_address = &*buffer; + m_buffer_ptr = reinterpret_cast(buffer_address); + } + + // DEBUG FUNCTIONS! + void set_token(u32 token) + { + m_token = token; + } + + void set_status(u32 status) + { + m_control_status = status; + } + + void free() + { + m_link_ptr = 0; + m_control_status = 0; + m_token = 0; + m_in_use = false; + } + +private: + u32 m_link_ptr; // Points to another Queue Head or Transfer Descriptor + volatile u32 m_control_status; // Control and status bits + u32 m_token; // Contains all information required to fill in a USB Start Token + u32 m_buffer_ptr; // Points to a data buffer for this transaction (i.e what we want to send or recv) + + // These values will be ignored by the controller, but we can use them for configuration/bookkeeping + u32 m_paddr; // Physical address where this TransferDescriptor is located + Ptr32 m_next_td { nullptr }; // Pointer to first TD + Ptr32 m_prev_td { nullptr }; // Pointer to first TD + bool m_in_use; // Has this TD been allocated (and therefore in use)? +}; + +static_assert(sizeof(TransferDescriptor) == 32); // Transfer Descriptor is always 8 Dwords + +// +// Queue Head +// +// Description here please! +// +struct alignas(16) QueueHead { + enum class LinkPointerBits : u32 { + Terminate = 1, + QHSelect = 2, + }; + + QueueHead() = delete; + QueueHead(u32 paddr) + : m_paddr(paddr) + { + } + ~QueueHead() = delete; // Prevent anything except placement new on this object + + u32 link_ptr() const { return m_link_ptr; } + u32 element_link_ptr() const { return m_element_link_ptr; } + u32 paddr() const { return m_paddr; } + bool in_use() const { return m_in_use; } + + void set_in_use(bool in_use) { m_in_use = in_use; } + void set_link_ptr(u32 val) { m_link_ptr = val; } + + // FIXME: For the love of God, use AK SMART POINTERS PLEASE!! + QueueHead* next_qh() { return m_next_qh; } + const QueueHead* next_qh() const { return m_next_qh; } + void set_next_qh(QueueHead* qh) { m_next_qh = qh; } + + QueueHead* prev_qh() { return m_prev_qh; } + const QueueHead* prev_qh() const { return m_prev_qh; } + void set_previous_qh(QueueHead* qh) + { + m_prev_qh = qh; + } + + void link_next_queue_head(QueueHead* qh) + { + m_link_ptr = qh->paddr(); + m_link_ptr |= static_cast(LinkPointerBits::QHSelect); + } + + void attach_transfer_queue(QueueHead& qh) + { + m_element_link_ptr = qh.paddr(); + m_element_link_ptr = m_element_link_ptr | static_cast(LinkPointerBits::QHSelect); + } + + // FIXME: Find out best way to walk queue and free everything + void free_transfer_queue([[maybe_unused]] QueueHead* qh) + { + TODO(); + } + + void terminate_with_stray_descriptor(TransferDescriptor* td) + { + m_link_ptr = td->paddr(); + m_link_ptr |= static_cast(LinkPointerBits::Terminate); + } + + // TODO: Should we pass in an array or vector of TDs instead???? + void attach_transfer_descriptor_chain(TransferDescriptor* td) + { + m_first_td = td; + m_element_link_ptr = td->paddr(); + } + + TransferDescriptor* get_first_td() + { + return m_first_td; + } + + void terminate() { m_link_ptr |= static_cast(LinkPointerBits::Terminate); } + + void terminate_element_link_ptr() + { + m_element_link_ptr = static_cast(LinkPointerBits::Terminate); + } + + void set_transfer(Transfer* transfer) + { + m_transfer = transfer; + } + + Transfer* transfer() + { + return m_transfer; + } + + void print() + { + dbgln("UHCI: QH({:#04x}) @ {:#04x}: link_ptr={:#04x}, element_link_ptr={:#04x}", this, m_paddr, m_link_ptr, (FlatPtr)m_element_link_ptr); + } + + void free() + { + m_link_ptr = 0; + m_element_link_ptr = 0; + m_first_td = nullptr; + m_transfer = nullptr; + m_in_use = false; + } + +private: + u32 m_link_ptr { 0 }; // Pointer to the next horizontal object that the controller will execute after this one + volatile u32 m_element_link_ptr { 0 }; // Pointer to the first data object in the queue (can be modified by hw) + + // These values will be ignored by the controller, but we can use them for configuration/bookkeeping + // Any addresses besides `paddr` are assumed virtual and can be dereferenced + u32 m_paddr { 0 }; // Physical address where this QueueHead is located + Ptr32 m_next_qh { nullptr }; // Next QH + Ptr32 m_prev_qh { nullptr }; // Previous QH + Ptr32 m_first_td { nullptr }; // Pointer to first TD + Ptr32 m_transfer { nullptr }; // Pointer to transfer linked to this queue head + bool m_in_use { false }; // Is this QH currently in use? +}; + +static_assert(sizeof(QueueHead) == 32); // Queue Head is always 8 Dwords +} diff --git a/Kernel/Bus/USB/USBDescriptors.h b/Kernel/Bus/USB/USBDescriptors.h new file mode 100644 index 0000000000..0e8486bd8a --- /dev/null +++ b/Kernel/Bus/USB/USBDescriptors.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Kernel::USB { + +struct [[gnu::packed]] USBDescriptorCommon { + u8 length; + u8 descriptor_type; +}; + +// +// Device Descriptor +// ================= +// +// This descriptor type (stored on the device), represents the device, and gives +// information related to it, such as the USB specification it complies to, +// as well as the vendor and product ID of the device. +// +// https://beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors +struct [[gnu::packed]] USBDeviceDescriptor { + USBDescriptorCommon descriptor_header; + u16 usb_spec_compliance_bcd; + u8 device_class; + u8 device_sub_class; + u8 device_protocol; + u8 max_packet_size; + u16 vendor_id; + u16 product_id; + u16 device_release_bcd; + u8 manufacturer_id_descriptor_index; + u8 product_string_descriptor_index; + u8 serial_number_descriptor_index; + u8 num_configurations; +}; + +// +// Configuration Descriptor +// ======================== +// +// A USB device can have multiple configurations, which tells us about how the +// device is physically configured (e.g how it's powered, max power consumption etc). +// +struct [[gnu::packed]] USBConfigurationDescriptor { + USBDescriptorCommon descriptor_header; + u16 total_length; + u8 number_of_interfaces; + u8 configuration_value; + u8 configuration_string_descriptor_index; + u8 attributes_bitmap; + u8 max_power_in_ma; +}; + +// +// Interface Descriptor +// ==================== +// +// An interface descriptor describes to us one or more endpoints, grouped +// together to define a singular function of a device. +// As an example, a USB webcam might have two interface descriptors; one +// for the camera, and one for the microphone. +// +struct [[gnu::packed]] USBInterfaceDescriptor { + USBDescriptorCommon descriptor_header; + u8 interface_id; + u8 alternate_setting; + u8 number_of_endpoints; + u8 interface_class_code; + u8 interface_sub_class_code; + u8 interface_protocol; + u8 interface_string_descriptor_index; +}; + +// +// Endpoint Descriptor +// =================== +// +// The lowest leaf in the configuration tree. And endpoint descriptor describes +// the physical transfer properties of the endpoint (that isn't endpoint0). +// The description given by this structure is used by a pipe to create a +// "connection" from the host to the device. +// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/usb-endpoints-and-their-pipes +struct [[gnu::packed]] USBEndpointDescriptor { + USBDescriptorCommon descriptor_header; + u8 endpoint_address; + u8 endpoint_attributes_bitmap; + u16 max_packet_size; + u8 poll_interval_in_frames; +}; + +static constexpr u8 DESCRIPTOR_TYPE_DEVICE = 0x01; +static constexpr u8 DESCRIPTOR_TYPE_CONFIGURATION = 0x02; +static constexpr u8 DESCRIPTOR_TYPE_STRING = 0x03; +static constexpr u8 DESCRIPTOR_TYPE_INTERFACE = 0x04; +static constexpr u8 DESCRIPTOR_TYPE_ENDPOINT = 0x05; +static constexpr u8 DESCRIPTOR_TYPE_DEVICE_QUALIFIER = 0x06; + +} diff --git a/Kernel/Bus/USB/USBDevice.cpp b/Kernel/Bus/USB/USBDevice.cpp new file mode 100644 index 0000000000..0feb4becee --- /dev/null +++ b/Kernel/Bus/USB/USBDevice.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +static u32 s_next_usb_address = 1; // Next address we hand out to a device once it's plugged into the machine + +namespace Kernel::USB { + +KResultOr> Device::try_create(PortNumber port, DeviceSpeed speed) +{ + auto pipe_or_error = Pipe::try_create_pipe(Pipe::Type::Control, Pipe::Direction::Bidirectional, 0, 8, 0); + if (pipe_or_error.is_error()) + return pipe_or_error.error(); + + auto device = AK::try_create(port, speed, pipe_or_error.release_value()); + if (!device) + return ENOMEM; + + auto enumerate_result = device->enumerate(); + if (enumerate_result.is_error()) + return enumerate_result; + + return device.release_nonnull(); +} + +Device::Device(PortNumber port, DeviceSpeed speed, NonnullOwnPtr default_pipe) + : m_device_port(port) + , m_device_speed(speed) + , m_address(0) + , m_default_pipe(move(default_pipe)) +{ +} + +KResult Device::enumerate() +{ + USBDeviceDescriptor dev_descriptor {}; + + // FIXME: 0x100 is a magic number for now, as I'm not quite sure how these are constructed.... + // Send 8-bytes to get at least the `max_packet_size` from the device + auto transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, 0x100, 0, 8, &dev_descriptor); + + if (transfer_length_or_error.is_error()) + return transfer_length_or_error.error(); + + auto transfer_length = transfer_length_or_error.release_value(); + + // FIXME: This shouldn't crash! Do some correct error handling on me please! + VERIFY(transfer_length > 0); + + // Ensure that this is actually a valid device descriptor... + VERIFY(dev_descriptor.descriptor_header.descriptor_type == DESCRIPTOR_TYPE_DEVICE); + m_default_pipe->set_max_packet_size(dev_descriptor.max_packet_size); + + transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, 0x100, 0, sizeof(USBDeviceDescriptor), &dev_descriptor); + + if (transfer_length_or_error.is_error()) + return transfer_length_or_error.error(); + + transfer_length = transfer_length_or_error.release_value(); + + // FIXME: This shouldn't crash! Do some correct error handling on me please! + VERIFY(transfer_length > 0); + + // Ensure that this is actually a valid device descriptor... + VERIFY(dev_descriptor.descriptor_header.descriptor_type == DESCRIPTOR_TYPE_DEVICE); + + if constexpr (USB_DEBUG) { + dbgln("USB Device Descriptor for {:04x}:{:04x}", dev_descriptor.vendor_id, dev_descriptor.product_id); + dbgln("Device Class: {:02x}", dev_descriptor.device_class); + dbgln("Device Sub-Class: {:02x}", dev_descriptor.device_sub_class); + dbgln("Device Protocol: {:02x}", dev_descriptor.device_protocol); + dbgln("Max Packet Size: {:02x} bytes", dev_descriptor.max_packet_size); + dbgln("Number of configurations: {:02x}", dev_descriptor.num_configurations); + } + + // Attempt to set devices address on the bus + transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_HOST_TO_DEVICE, USB_REQUEST_SET_ADDRESS, s_next_usb_address, 0, 0, nullptr); + + if (transfer_length_or_error.is_error()) + return transfer_length_or_error.error(); + + transfer_length = transfer_length_or_error.release_value(); + + VERIFY(transfer_length > 0); + m_address = s_next_usb_address++; + + memcpy(&m_device_descriptor, &dev_descriptor, sizeof(USBDeviceDescriptor)); + return KSuccess; +} + +Device::~Device() +{ +} + +} diff --git a/Kernel/Bus/USB/USBDevice.h b/Kernel/Bus/USB/USBDevice.h new file mode 100644 index 0000000000..c57c8ce49f --- /dev/null +++ b/Kernel/Bus/USB/USBDevice.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Kernel::USB { + +// +// Some nice info from FTDI on device enumeration and how some of this +// glues together: +// +// https://www.ftdichip.com/Support/Documents/TechnicalNotes/TN_113_Simplified%20Description%20of%20USB%20Device%20Enumeration.pdf +class Device : public RefCounted { +public: + enum class PortNumber : u8 { + Port1 = 0, + Port2 + }; + + enum class DeviceSpeed : u8 { + FullSpeed = 0, + LowSpeed + }; + +public: + static KResultOr> try_create(PortNumber, DeviceSpeed); + + Device(PortNumber, DeviceSpeed, NonnullOwnPtr default_pipe); + ~Device(); + + KResult enumerate(); + + PortNumber port() const { return m_device_port; } + DeviceSpeed speed() const { return m_device_speed; } + + u8 address() const { return m_address; } + + const USBDeviceDescriptor& device_descriptor() const { return m_device_descriptor; } + +private: + PortNumber m_device_port; // What port is this device attached to + DeviceSpeed m_device_speed; // What speed is this device running at + u8 m_address { 0 }; // USB address assigned to this device + + // Device description + u16 m_vendor_id { 0 }; // This device's vendor ID assigned by the USB group + u16 m_product_id { 0 }; // This device's product ID assigned by the USB group + USBDeviceDescriptor m_device_descriptor; // Device Descriptor obtained from USB Device + + NonnullOwnPtr m_default_pipe; // Default communication pipe (endpoint0) used during enumeration +}; +} diff --git a/Kernel/Bus/USB/USBEndpoint.h b/Kernel/Bus/USB/USBEndpoint.h new file mode 100644 index 0000000000..c29aecb2ae --- /dev/null +++ b/Kernel/Bus/USB/USBEndpoint.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel::USB { + +// +// An endpoint is the "end point" of communication of a USB device. That is, data is read from and written +// to an endpoint via a USB pipe. As an example, during device enumeration (where we assign an address to the +// device), we communicate with the device over the default endpoint, endpoint0, which all devices _must_ +// contain to be compliant with the USB specification. +// +// And endpoint describes characteristics about the transfer between the host and the device, such as: +// - The endpoint number +// - Max packet size of send/recv of the endpoint +// - Transfer type (bulk, interrupt, isochronous etc) +// +// Take for example a USB multifunction device, such as a keyboard/mouse combination. The mouse +// may need to be polled every n milliseconds, meaning the transfer may be isochronous (streamed), +// while the keyboard part would only generate data once we push a key (hence an interrupt transfer). +// Each of these data sources would be a _different_ endpoint on the device that we read from. +class USBEndpoint { + static constexpr u8 ENDPOINT_ADDRESS_NUMBER_MASK = 0x0f; + static constexpr u8 ENDPOINT_ADDRESS_DIRECTION_MASK = 0x80; + + static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK = 0x03; + static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_CONTROL = 0x00; + static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_ISOCHRONOUS = 0x01; + static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_BULK = 0x02; + static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT = 0x03; + + static constexpr u8 ENDPOINT_ATTRIBUTES_ISO_MODE_SYNC_TYPE = 0x0c; + static constexpr u8 ENDPOINT_ATTRIBUTES_ISO_MODE_USAGE_TYPE = 0x30; + +public: + const USBEndpointDescriptor& descriptor() const { return m_descriptor; } + + bool is_control() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_CONTROL; } + bool is_isochronous() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_ISOCHRONOUS; } + bool is_bulk() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_BULK; } + bool is_interrupt() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT; } + + u16 max_packet_size() const { return m_descriptor.max_packet_size; } + u8 polling_interval() const { return m_descriptor.poll_interval_in_frames; } + +private: + USBEndpoint(/* TODO */); + USBEndpointDescriptor m_descriptor; + + USBPipe m_pipe; +}; + +} diff --git a/Kernel/Bus/USB/USBPipe.cpp b/Kernel/Bus/USB/USBPipe.cpp new file mode 100644 index 0000000000..6e3b302fb0 --- /dev/null +++ b/Kernel/Bus/USB/USBPipe.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Kernel::USB { + +KResultOr> Pipe::try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval) +{ + auto pipe = adopt_own_if_nonnull(new (nothrow) Pipe(type, direction, endpoint_address, max_packet_size, device_address, poll_interval)); + if (!pipe) + return ENOMEM; + + return pipe.release_nonnull(); +} + +Pipe::Pipe(Type type, Pipe::Direction direction, u16 max_packet_size) + : m_type(type) + , m_direction(direction) + , m_endpoint_address(0) + , m_max_packet_size(max_packet_size) + , m_poll_interval(0) + , m_data_toggle(false) +{ +} + +Pipe::Pipe(Type type, Direction direction, USBEndpointDescriptor& endpoint [[maybe_unused]]) + : m_type(type) + , m_direction(direction) +{ + // TODO: decode endpoint structure +} + +Pipe::Pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address) + : m_type(type) + , m_direction(direction) + , m_device_address(device_address) + , m_endpoint_address(endpoint_address) + , m_max_packet_size(max_packet_size) + , m_poll_interval(poll_interval) + , m_data_toggle(false) +{ +} + +KResultOr Pipe::control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data) +{ + USBRequestData usb_request; + + usb_request.request_type = request_type; + usb_request.request = request; + usb_request.value = value; + usb_request.index = index; + usb_request.length = length; + + auto transfer = Transfer::try_create(*this, length); + + if (!transfer) + return ENOMEM; + + transfer->set_setup_packet(usb_request); + + dbgln_if(USB_DEBUG, "Pipe: Transfer allocated @ {:08x}", transfer->buffer_physical()); + auto transfer_len_or_error = UHCIController::the().submit_control_transfer(*transfer); + + if (transfer_len_or_error.is_error()) + return transfer_len_or_error.error(); + + auto transfer_length = transfer_len_or_error.release_value(); + + // TODO: Check transfer for completion and copy data from transfer buffer into data + if (length > 0) + memcpy(reinterpret_cast(data), transfer->buffer().as_ptr() + sizeof(USBRequestData), length); + + dbgln_if(USB_DEBUG, "Pipe: Control Transfer complete!"); + return transfer_length; +} + +} diff --git a/Kernel/Bus/USB/USBPipe.h b/Kernel/Bus/USB/USBPipe.h new file mode 100644 index 0000000000..eeffde745b --- /dev/null +++ b/Kernel/Bus/USB/USBPipe.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::USB { + +// +// A pipe is the logical connection between a memory buffer on the PC (host) and +// an endpoint on the device. In this implementation, the data buffer the pipe connects +// to is the physical buffer created when a Transfer is allocated. +// +class Pipe { +public: + enum class Type : u8 { + Control = 0, + Isochronous = 1, + Bulk = 2, + Interrupt = 3 + }; + + enum class Direction : u8 { + Out = 0, + In = 1, + Bidirectional = 2 + }; + + enum class DeviceSpeed : u8 { + LowSpeed, + FullSpeed + }; + +public: + static KResultOr> try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval = 0); + + Type type() const { return m_type; } + Direction direction() const { return m_direction; } + DeviceSpeed device_speed() const { return m_speed; } + + i8 device_address() const { return m_device_address; } + u8 endpoint_address() const { return m_endpoint_address; } + u16 max_packet_size() const { return m_max_packet_size; } + u8 poll_interval() const { return m_poll_interval; } + bool data_toggle() const { return m_data_toggle; } + + void set_max_packet_size(u16 max_size) { m_max_packet_size = max_size; } + void set_toggle(bool toggle) { m_data_toggle = toggle; } + void set_device_address(i8 addr) { m_device_address = addr; } + + KResultOr control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data); + + Pipe(Type type, Direction direction, u16 max_packet_size); + Pipe(Type type, Direction direction, USBEndpointDescriptor& endpoint); + Pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address); + +private: + friend class Device; + + Type m_type; + Direction m_direction; + DeviceSpeed m_speed; + + i8 m_device_address { 0 }; // Device address of this pipe + u8 m_endpoint_address { 0 }; // Corresponding endpoint address for this pipe + u16 m_max_packet_size { 0 }; // Max packet size for this pipe + u8 m_poll_interval { 0 }; // Polling interval (in frames) + bool m_data_toggle { false }; // Data toggle for stuffing bit +}; +} diff --git a/Kernel/Bus/USB/USBRequest.h b/Kernel/Bus/USB/USBRequest.h new file mode 100644 index 0000000000..38f184de1f --- /dev/null +++ b/Kernel/Bus/USB/USBRequest.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +// +// USB Request directions +// +// As per Section 9.4 of the USB Specification, it is noted that Requeset Types that +// Device to Host have bit 7 of `bmRequestType` set. These are here as a convenience, +// as we construct the request at the call-site to make reading transfers easier. +// +static constexpr u8 USB_DEVICE_REQUEST_DEVICE_TO_HOST = 0x80; +static constexpr u8 USB_DEVICE_REQUEST_HOST_TO_DEVICE = 0x00; +static constexpr u8 USB_INTERFACE_REQUEST_DEVICE_TO_HOST = 0x81; +static constexpr u8 USB_INTERFACE_REQUEST_HOST_TO_DEVICE = 0x01; +static constexpr u8 USB_ENDPOINT_REQUEST_DEVICE_TO_HOST = 0x82; +static constexpr u8 USB_ENDPOINT_REQUEST_HOST_TO_DEVICE = 0x02; + +// +// Standard USB request types +// +// These are found in Section 9.4 of the USB Spec +// +static constexpr u8 USB_REQUEST_GET_STATUS = 0x00; +static constexpr u8 USB_REQUEST_CLEAR_FEATURE = 0x01; +static constexpr u8 USB_REQUEST_SET_FEATURE = 0x03; +static constexpr u8 USB_REQUEST_SET_ADDRESS = 0x05; +static constexpr u8 USB_REQUEST_GET_DESCRIPTOR = 0x06; +static constexpr u8 USB_REQUEST_SET_DESCRIPTOR = 0x07; +static constexpr u8 USB_REQUEST_GET_CONFIGURATION = 0x08; +static constexpr u8 USB_REQUEST_SET_CONFIGURATION = 0x09; diff --git a/Kernel/Bus/USB/USBTransfer.cpp b/Kernel/Bus/USB/USBTransfer.cpp new file mode 100644 index 0000000000..ec67d331c8 --- /dev/null +++ b/Kernel/Bus/USB/USBTransfer.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel::USB { + +RefPtr Transfer::try_create(Pipe& pipe, u16 len) +{ + auto vmobject = ContiguousVMObject::create_with_size(PAGE_SIZE); + if (!vmobject) + return nullptr; + + return AK::try_create(pipe, len, *vmobject); +} + +Transfer::Transfer(Pipe& pipe, u16 len, ContiguousVMObject& vmobject) + : m_pipe(pipe) + , m_transfer_data_size(len) +{ + // Initialize data buffer for transfer + // This will definitely need to be refactored in the future, I doubt this will scale well... + m_data_buffer = MemoryManager::the().allocate_kernel_region_with_vmobject(vmobject, PAGE_SIZE, "USB Transfer Buffer", Region::Access::Read | Region::Access::Write); +} + +Transfer::~Transfer() +{ +} + +void Transfer::set_setup_packet(const USBRequestData& request) +{ + // Kind of a nasty hack... Because the kernel isn't in the business + // of handing out physical pointers that we can directly write to, + // we set the address of the setup packet to be the first 8 bytes of + // the data buffer, which we then set to the physical address. + auto* request_data = reinterpret_cast(buffer().as_ptr()); + + request_data->request_type = request.request_type; + request_data->request = request.request; + request_data->value = request.value; + request_data->index = request.index; + request_data->length = request.length; + + m_request = request; +} + +} diff --git a/Kernel/Bus/USB/USBTransfer.h b/Kernel/Bus/USB/USBTransfer.h new file mode 100644 index 0000000000..b54d3a73b3 --- /dev/null +++ b/Kernel/Bus/USB/USBTransfer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +// TODO: Callback stuff in this class please! +namespace Kernel::USB { + +class Transfer : public RefCounted { +public: + static RefPtr try_create(Pipe& pipe, u16 len); + +public: + Transfer() = delete; + Transfer(Pipe& pipe, u16 len, ContiguousVMObject&); + ~Transfer(); + + void set_setup_packet(const USBRequestData& request); + void set_complete() { m_complete = true; } + void set_error_occurred() { m_error_occurred = true; } + + // `const` here makes sure we don't blow up by writing to a physical address + const USBRequestData& request() const { return m_request; } + const Pipe& pipe() const { return m_pipe; } + Pipe& pipe() { return m_pipe; } + VirtualAddress buffer() const { return m_data_buffer->vaddr(); } + PhysicalAddress buffer_physical() const { return m_data_buffer->physical_page(0)->paddr(); } + u16 transfer_data_size() const { return m_transfer_data_size; } + bool complete() const { return m_complete; } + bool error_occurred() const { return m_error_occurred; } + +private: + Pipe& m_pipe; // Pipe that initiated this transfer + USBRequestData m_request; // USB request + OwnPtr m_data_buffer; // DMA Data buffer for transaction + u16 m_transfer_data_size { 0 }; // Size of the transfer's data stage + bool m_complete { false }; // Has this transfer been completed? + bool m_error_occurred { false }; // Did an error occur during this transfer? +}; +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 499b202fa2..0f8116ec36 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -33,6 +33,10 @@ set(KERNEL_SOURCES Bus/PCI/MMIOAccess.cpp Bus/PCI/Initializer.cpp Bus/PCI/WindowedMMIOAccess.cpp + Bus/USB/UHCIController.cpp + Bus/USB/USBDevice.cpp + Bus/USB/USBPipe.cpp + Bus/USB/USBTransfer.cpp CMOS.cpp CommandLine.cpp ConsoleDevice.cpp @@ -49,13 +53,9 @@ set(KERNEL_SOURCES Devices/RandomDevice.cpp Devices/SB16.cpp Devices/SerialDevice.cpp - Devices/USB/UHCIController.cpp VirtIO/VirtIO.cpp VirtIO/VirtIOQueue.cpp VirtIO/VirtIOConsole.cpp - Devices/USB/USBDevice.cpp - Devices/USB/USBPipe.cpp - Devices/USB/USBTransfer.cpp Devices/VMWareBackdoor.cpp Devices/ZeroDevice.cpp Devices/HID/I8042Controller.cpp diff --git a/Kernel/Devices/USB/PacketTypes.h b/Kernel/Devices/USB/PacketTypes.h deleted file mode 100644 index 8dc65094ef..0000000000 --- a/Kernel/Devices/USB/PacketTypes.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::USB { - -// Setup descriptor bit definitions -static constexpr u8 BM_REQUEST_HOST_TO_DEVICE = (0 << 7); -static constexpr u8 BM_REQUEST_DEVICE_TO_HOST = (1 << 7); -static constexpr u8 BM_REQUEST_TYPE_STANDARD = (0 << 5); -static constexpr u8 BM_REQUEST_TYPE_CLASS = (1 << 5); -static constexpr u8 BM_REQUEST_TYPE_VENDOR = (2 << 5); -static constexpr u8 BM_REQUEST_TYPE_RESERVED = (3 << 5); -static constexpr u8 BM_REQUEST_RECIPEINT_DEVICE = (0 << 0); -static constexpr u8 BM_REQUEST_RECIPIENT_INTERFACE = (1 << 0); -static constexpr u8 BM_REQUEST_RECIPIENT_ENDPOINT = (2 << 0); -static constexpr u8 BM_REQUEST_RECIPIENT_OTHER = (3 << 0); - -// -// This is also known as the "setup" packet. It's attached to the -// first TD in the chain and is the first piece of data sent to the -// USB device over the bus. -// https://beyondlogic.org/usbnutshell/usb6.shtml#StandardEndpointRequests -// -struct USBRequestData { - u8 request_type; - u8 request; - u16 value; - u16 index; - u16 length; -}; - -} diff --git a/Kernel/Devices/USB/UHCIController.cpp b/Kernel/Devices/USB/UHCIController.cpp deleted file mode 100644 index 5d50415b80..0000000000 --- a/Kernel/Devices/USB/UHCIController.cpp +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2020, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static constexpr u8 MAXIMUM_NUMBER_OF_TDS = 128; // Upper pool limit. This consumes the second page we have allocated -static constexpr u8 MAXIMUM_NUMBER_OF_QHS = 64; -static constexpr u8 RETRY_COUNTER_RELOAD = 3; - -namespace Kernel::USB { - -static UHCIController* s_the; - -static constexpr u16 UHCI_USBCMD_RUN = 0x0001; -static constexpr u16 UHCI_USBCMD_HOST_CONTROLLER_RESET = 0x0002; -static constexpr u16 UHCI_USBCMD_GLOBAL_RESET = 0x0004; -static constexpr u16 UHCI_USBCMD_ENTER_GLOBAL_SUSPEND_MODE = 0x0008; -static constexpr u16 UHCI_USBCMD_FORCE_GLOBAL_RESUME = 0x0010; -static constexpr u16 UHCI_USBCMD_SOFTWARE_DEBUG = 0x0020; -static constexpr u16 UHCI_USBCMD_CONFIGURE_FLAG = 0x0040; -static constexpr u16 UHCI_USBCMD_MAX_PACKET = 0x0080; - -static constexpr u16 UHCI_USBSTS_HOST_CONTROLLER_HALTED = 0x0020; -static constexpr u16 UHCI_USBSTS_HOST_CONTROLLER_PROCESS_ERROR = 0x0010; -static constexpr u16 UHCI_USBSTS_PCI_BUS_ERROR = 0x0008; -static constexpr u16 UHCI_USBSTS_RESUME_RECEIVED = 0x0004; -static constexpr u16 UHCI_USBSTS_USB_ERROR_INTERRUPT = 0x0002; -static constexpr u16 UHCI_USBSTS_USB_INTERRUPT = 0x0001; - -static constexpr u8 UHCI_USBINTR_TIMEOUT_CRC_ENABLE = 0x01; -static constexpr u8 UHCI_USBINTR_RESUME_INTR_ENABLE = 0x02; -static constexpr u8 UHCI_USBINTR_IOC_ENABLE = 0x04; -static constexpr u8 UHCI_USBINTR_SHORT_PACKET_INTR_ENABLE = 0x08; - -static constexpr u16 UHCI_FRAMELIST_FRAME_COUNT = 1024; // Each entry is 4 bytes in our allocated page -static constexpr u16 UHCI_FRAMELIST_FRAME_INVALID = 0x0001; - -// Port stuff -static constexpr u8 UHCI_ROOT_PORT_COUNT = 2; -static constexpr u16 UHCI_PORTSC_CURRRENT_CONNECT_STATUS = 0x0001; -static constexpr u16 UHCI_PORTSC_CONNECT_STATUS_CHANGED = 0x0002; -static constexpr u16 UHCI_PORTSC_PORT_ENABLED = 0x0004; -static constexpr u16 UHCI_PORTSC_PORT_ENABLE_CHANGED = 0x0008; -static constexpr u16 UHCI_PORTSC_LINE_STATUS = 0x0030; -static constexpr u16 UHCI_PORTSC_RESUME_DETECT = 0x40; -static constexpr u16 UHCI_PORTSC_LOW_SPEED_DEVICE = 0x0100; -static constexpr u16 UHCI_PORTSC_PORT_RESET = 0x0200; -static constexpr u16 UHCI_PORTSC_SUSPEND = 0x1000; - -// *BSD and a few other drivers seem to use this number -static constexpr u8 UHCI_NUMBER_OF_ISOCHRONOUS_TDS = 128; -static constexpr u16 UHCI_NUMBER_OF_FRAMES = 1024; - -class ProcFSUSBBusFolder; -static ProcFSUSBBusFolder* s_procfs_usb_bus_folder; - -class ProcFSUSBDeviceInformation : public ProcFSGlobalInformation { - friend class ProcFSUSBBusFolder; - -public: - virtual ~ProcFSUSBDeviceInformation() override {}; - - static NonnullRefPtr create(USB::Device&); - - RefPtr device() const { return m_device; } - -protected: - explicit ProcFSUSBDeviceInformation(USB::Device& device) - : ProcFSGlobalInformation(String::formatted("{}", device.address())) - , m_device(device) - { - } - virtual bool output(KBufferBuilder& builder) override - { - VERIFY(m_device); // Something has gone very wrong if this isn't true - - JsonArraySerializer array { builder }; - - auto obj = array.add_object(); - obj.add("usb_spec_compliance_bcd", m_device->device_descriptor().usb_spec_compliance_bcd); - obj.add("device_class", m_device->device_descriptor().device_class); - obj.add("device_sub_class", m_device->device_descriptor().device_sub_class); - obj.add("device_protocol", m_device->device_descriptor().device_protocol); - obj.add("max_packet_size", m_device->device_descriptor().max_packet_size); - obj.add("vendor_id", m_device->device_descriptor().vendor_id); - obj.add("product_id", m_device->device_descriptor().product_id); - obj.add("device_release_bcd", m_device->device_descriptor().device_release_bcd); - obj.add("manufacturer_id_descriptor_index", m_device->device_descriptor().manufacturer_id_descriptor_index); - obj.add("product_string_descriptor_index", m_device->device_descriptor().product_string_descriptor_index); - obj.add("serial_number_descriptor_index", m_device->device_descriptor().serial_number_descriptor_index); - obj.add("num_configurations", m_device->device_descriptor().num_configurations); - obj.finish(); - array.finish(); - return true; - } - IntrusiveListNode> m_list_node; - RefPtr m_device; -}; - -class ProcFSUSBBusFolder final : public ProcFSExposedFolder { - friend class ProcFSComponentsRegistrar; - -public: - static void initialize(); - void plug(USB::Device&); - void unplug(USB::Device&); - - virtual KResultOr entries_count() const override; - virtual KResult traverse_as_directory(unsigned, Function) const override; - virtual RefPtr lookup(StringView name) override; - -private: - ProcFSUSBBusFolder(const ProcFSBusDirectory&); - - RefPtr device_node_for(USB::Device& device); - - IntrusiveList, &ProcFSUSBDeviceInformation::m_list_node> m_device_nodes; - mutable SpinLock m_lock; -}; - -KResultOr ProcFSUSBBusFolder::entries_count() const -{ - ScopedSpinLock lock(m_lock); - return m_device_nodes.size_slow(); -} -KResult ProcFSUSBBusFolder::traverse_as_directory(unsigned fsid, Function callback) const -{ - ScopedSpinLock lock(m_lock); - VERIFY(m_parent_folder); - callback({ ".", { fsid, component_index() }, 0 }); - callback({ "..", { fsid, m_parent_folder->component_index() }, 0 }); - - for (auto& device_node : m_device_nodes) { - InodeIdentifier identifier = { fsid, device_node.component_index() }; - callback({ device_node.name(), identifier, 0 }); - } - return KSuccess; -} -RefPtr ProcFSUSBBusFolder::lookup(StringView name) -{ - ScopedSpinLock lock(m_lock); - for (auto& device_node : m_device_nodes) { - if (device_node.name() == name) { - return device_node; - } - } - return {}; -} - -RefPtr ProcFSUSBBusFolder::device_node_for(USB::Device& device) -{ - RefPtr checked_device = device; - for (auto& device_node : m_device_nodes) { - if (device_node.device().ptr() == checked_device.ptr()) - return device_node; - } - return {}; -} - -void ProcFSUSBBusFolder::plug(USB::Device& new_device) -{ - ScopedSpinLock lock(m_lock); - auto device_node = device_node_for(new_device); - VERIFY(!device_node); - m_device_nodes.append(ProcFSUSBDeviceInformation::create(new_device)); -} -void ProcFSUSBBusFolder::unplug(USB::Device& deleted_device) -{ - ScopedSpinLock lock(m_lock); - auto device_node = device_node_for(deleted_device); - VERIFY(device_node); - device_node->m_list_node.remove(); -} - -UNMAP_AFTER_INIT ProcFSUSBBusFolder::ProcFSUSBBusFolder(const ProcFSBusDirectory& buses_folder) - : ProcFSExposedFolder("usb"sv, buses_folder) -{ -} - -UNMAP_AFTER_INIT void ProcFSUSBBusFolder::initialize() -{ - auto folder = adopt_ref(*new ProcFSUSBBusFolder(ProcFSComponentsRegistrar::the().buses_folder())); - ProcFSComponentsRegistrar::the().register_new_bus_folder(folder); - s_procfs_usb_bus_folder = folder; -} - -NonnullRefPtr ProcFSUSBDeviceInformation::create(USB::Device& device) -{ - return adopt_ref(*new ProcFSUSBDeviceInformation(device)); -} - -UHCIController& UHCIController::the() -{ - return *s_the; -} - -UNMAP_AFTER_INIT void UHCIController::detect() -{ - if (kernel_command_line().disable_uhci_controller()) - return; - - // FIXME: We create the /proc/bus/usb representation here, but it should really be handled - // in a more broad singleton than this once we refactor things in USB subsystem. - ProcFSUSBBusFolder::initialize(); - - PCI::enumerate([&](const PCI::Address& address, PCI::ID id) { - if (address.is_null()) - return; - - if (PCI::get_class(address) == 0xc && PCI::get_subclass(address) == 0x03 && PCI::get_programming_interface(address) == 0) { - if (!s_the) { - s_the = new UHCIController(address, id); - s_the->spawn_port_proc(); - } - } - }); -} - -UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::Address address, PCI::ID id) - : PCI::Device(address) - , m_io_base(PCI::get_BAR4(pci_address()) & ~1) -{ - dmesgln("UHCI: Controller found {} @ {}", id, address); - dmesgln("UHCI: I/O base {}", m_io_base); - dmesgln("UHCI: Interrupt line: {}", PCI::get_interrupt_line(pci_address())); - - reset(); - start(); -} - -UNMAP_AFTER_INIT UHCIController::~UHCIController() -{ -} - -RefPtr const UHCIController::get_device_at_port(USB::Device::PortNumber port) -{ - if (!m_devices.at(to_underlying(port))) - return nullptr; - - return m_devices.at(to_underlying(port)); -} - -RefPtr const UHCIController::get_device_from_address(u8 device_address) -{ - for (auto const& device : m_devices) { - if (!device) - continue; - - if (device->address() == device_address) - return device; - } - - return nullptr; -} - -void UHCIController::reset() -{ - stop(); - - write_usbcmd(UHCI_USBCMD_HOST_CONTROLLER_RESET); - - // FIXME: Timeout - for (;;) { - if (read_usbcmd() & UHCI_USBCMD_HOST_CONTROLLER_RESET) - continue; - break; - } - - // Let's allocate the physical page for the Frame List (which is 4KiB aligned) - auto framelist_vmobj = ContiguousVMObject::create_with_size(PAGE_SIZE); - m_framelist = MemoryManager::the().allocate_kernel_region_with_vmobject(*framelist_vmobj, PAGE_SIZE, "UHCI Framelist", Region::Access::Write); - dbgln("UHCI: Allocated framelist at physical address {}", m_framelist->physical_page(0)->paddr()); - dbgln("UHCI: Framelist is at virtual address {}", m_framelist->vaddr()); - write_sofmod(64); // 1mS frame time - - create_structures(); - setup_schedule(); - - write_flbaseadd(m_framelist->physical_page(0)->paddr().get()); // Frame list (physical) address - write_frnum(0); // Set the initial frame number - - // FIXME: Work out why interrupts lock up the entire system.... - // Disable UHCI Controller from raising an IRQ - write_usbintr(0); - dbgln("UHCI: Reset completed"); -} - -UNMAP_AFTER_INIT void UHCIController::create_structures() -{ - // Let's allocate memory for both the QH and TD pools - // First the QH pool and all of the Interrupt QH's - auto qh_pool_vmobject = ContiguousVMObject::create_with_size(2 * PAGE_SIZE); - m_qh_pool = MemoryManager::the().allocate_kernel_region_with_vmobject(*qh_pool_vmobject, 2 * PAGE_SIZE, "UHCI Queue Head Pool", Region::Access::Write); - memset(m_qh_pool->vaddr().as_ptr(), 0, 2 * PAGE_SIZE); // Zero out both pages - - // Let's populate our free qh list (so we have some we can allocate later on) - m_free_qh_pool.resize(MAXIMUM_NUMBER_OF_TDS); - for (size_t i = 0; i < m_free_qh_pool.size(); i++) { - auto placement_addr = reinterpret_cast(m_qh_pool->vaddr().get() + (i * sizeof(QueueHead))); - auto paddr = static_cast(m_qh_pool->physical_page(0)->paddr().get() + (i * sizeof(QueueHead))); - m_free_qh_pool.at(i) = new (placement_addr) QueueHead(paddr); - } - - // Create the Full Speed, Low Speed Control and Bulk Queue Heads - m_interrupt_transfer_queue = allocate_queue_head(); - m_lowspeed_control_qh = allocate_queue_head(); - m_fullspeed_control_qh = allocate_queue_head(); - m_bulk_qh = allocate_queue_head(); - m_dummy_qh = allocate_queue_head(); - - // Now the Transfer Descriptor pool - auto td_pool_vmobject = ContiguousVMObject::create_with_size(2 * PAGE_SIZE); - m_td_pool = MemoryManager::the().allocate_kernel_region_with_vmobject(*td_pool_vmobject, 2 * PAGE_SIZE, "UHCI Transfer Descriptor Pool", Region::Access::Write); - memset(m_td_pool->vaddr().as_ptr(), 0, 2 * PAGE_SIZE); - - // Set up the Isochronous Transfer Descriptor list - m_iso_td_list.resize(UHCI_NUMBER_OF_ISOCHRONOUS_TDS); - for (size_t i = 0; i < m_iso_td_list.size(); i++) { - auto placement_addr = reinterpret_cast(m_td_pool->vaddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); - auto paddr = static_cast(m_td_pool->physical_page(0)->paddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); - - // Place a new Transfer Descriptor with a 1:1 in our region - // The pointer returned by `new()` lines up exactly with the value - // that we store in `paddr`, meaning our member functions directly - // access the raw descriptor (that we later send to the controller) - m_iso_td_list.at(i) = new (placement_addr) Kernel::USB::TransferDescriptor(paddr); - auto transfer_descriptor = m_iso_td_list.at(i); - transfer_descriptor->set_in_use(true); // Isochronous transfers are ALWAYS marked as in use (in case we somehow get allocated one...) - transfer_descriptor->set_isochronous(); - transfer_descriptor->link_queue_head(m_interrupt_transfer_queue->paddr()); - - if constexpr (UHCI_VERBOSE_DEBUG) - transfer_descriptor->print(); - } - - m_free_td_pool.resize(MAXIMUM_NUMBER_OF_TDS); - for (size_t i = 0; i < m_free_td_pool.size(); i++) { - auto placement_addr = reinterpret_cast(m_td_pool->vaddr().offset(PAGE_SIZE).get() + (i * sizeof(Kernel::USB::TransferDescriptor))); - auto paddr = static_cast(m_td_pool->physical_page(1)->paddr().get() + (i * sizeof(Kernel::USB::TransferDescriptor))); - - // Place a new Transfer Descriptor with a 1:1 in our region - // The pointer returned by `new()` lines up exactly with the value - // that we store in `paddr`, meaning our member functions directly - // access the raw descriptor (that we later send to the controller) - m_free_td_pool.at(i) = new (placement_addr) Kernel::USB::TransferDescriptor(paddr); - - if constexpr (UHCI_VERBOSE_DEBUG) { - auto transfer_descriptor = m_free_td_pool.at(i); - transfer_descriptor->print(); - } - } - - if constexpr (UHCI_DEBUG) { - dbgln("UHCI: Pool information:"); - dbgln(" qh_pool: {}, length: {}", PhysicalAddress(m_qh_pool->physical_page(0)->paddr()), m_qh_pool->range().size()); - dbgln(" td_pool: {}, length: {}", PhysicalAddress(m_td_pool->physical_page(0)->paddr()), m_td_pool->range().size()); - } -} - -UNMAP_AFTER_INIT void UHCIController::setup_schedule() -{ - // - // https://github.com/alkber/minix3-usbsubsystem/blob/master/usb/uhci-hcd.c - // - // This lad probably has the best explanation as to how this is actually done. I'll try and - // explain it here to so that there's no need for anyone to go hunting for this shit again, because - // the USB spec and Intel explain next to nothing. - // According to the USB spec (and the UHCI datasheet), 90% of the bandwidth should be used for - // Isochronous and """Interrupt""" related transfers, with the rest being used for control and bulk - // transfers. - // That is, most of the time, the schedule is going to be executing either an Isochronous transfer - // in our framelist, or an Interrupt transfer. The allocation in `create_structures` reflects this. - // - // Each frame has it's own Isochronous transfer Transfer Descriptor(s) that point to each other - // horizontally in the list. The end of these transfers then point to the Interrupt Queue Headers, - // in which we can attach Transfer Descriptors (related to Interrupt Transfers). These are attached - // to the Queue Head _vertically_. We need to ensure that these are executed every 8ms, so they are inserted - // at different points in the schedule (TODO: How do we do this?!?!). After the Interrupt Transfer Queue Heads, - // we attach the Control Queue Heads. We need two in total, one for Low Speed devices, and one for Full Speed - // USB devices. Finally, we attach the Bulk Transfer Queue Head. - // Not specified in the datasheet, however, is another Queue Head with an "inactive" Transfer Descriptor. This - // is to circumvent a bug in the silicon of the PIIX4's UHCI controller. - // https://github.com/openbsd/src/blob/master/sys/dev/usb/uhci.c#L390 - // - - m_interrupt_transfer_queue->link_next_queue_head(m_lowspeed_control_qh); - m_interrupt_transfer_queue->terminate_element_link_ptr(); - - m_lowspeed_control_qh->link_next_queue_head(m_fullspeed_control_qh); - m_lowspeed_control_qh->terminate_element_link_ptr(); - - m_fullspeed_control_qh->link_next_queue_head(m_bulk_qh); - m_fullspeed_control_qh->terminate_element_link_ptr(); - - m_bulk_qh->link_next_queue_head(m_dummy_qh); - m_bulk_qh->terminate_element_link_ptr(); - - auto piix4_td_hack = allocate_transfer_descriptor(); - piix4_td_hack->terminate(); - piix4_td_hack->set_max_len(0x7ff); // Null data packet - piix4_td_hack->set_device_address(0x7f); - piix4_td_hack->set_packet_id(PacketID::IN); - m_dummy_qh->terminate_with_stray_descriptor(piix4_td_hack); - m_dummy_qh->terminate_element_link_ptr(); - - u32* framelist = reinterpret_cast(m_framelist->vaddr().as_ptr()); - for (int frame = 0; frame < UHCI_NUMBER_OF_FRAMES; frame++) { - // Each frame pointer points to iso_td % NUM_ISO_TDS - framelist[frame] = m_iso_td_list.at(frame % UHCI_NUMBER_OF_ISOCHRONOUS_TDS)->paddr(); - } - - m_interrupt_transfer_queue->print(); - m_lowspeed_control_qh->print(); - m_fullspeed_control_qh->print(); - m_bulk_qh->print(); - m_dummy_qh->print(); -} - -QueueHead* UHCIController::allocate_queue_head() const -{ - for (QueueHead* queue_head : m_free_qh_pool) { - if (!queue_head->in_use()) { - queue_head->set_in_use(true); - dbgln_if(UHCI_DEBUG, "UHCI: Allocated a new Queue Head! Located @ {} ({})", VirtualAddress(queue_head), PhysicalAddress(queue_head->paddr())); - return queue_head; - } - } - - return nullptr; // Huh!? We're outta queue heads! -} - -TransferDescriptor* UHCIController::allocate_transfer_descriptor() const -{ - for (TransferDescriptor* transfer_descriptor : m_free_td_pool) { - if (!transfer_descriptor->in_use()) { - transfer_descriptor->set_in_use(true); - dbgln_if(UHCI_DEBUG, "UHCI: Allocated a new Transfer Descriptor! Located @ {} ({})", VirtualAddress(transfer_descriptor), PhysicalAddress(transfer_descriptor->paddr())); - return transfer_descriptor; - } - } - - return nullptr; // Huh?! We're outta TDs!! -} - -void UHCIController::stop() -{ - write_usbcmd(read_usbcmd() & ~UHCI_USBCMD_RUN); - // FIXME: Timeout - for (;;) { - if (read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED) - break; - } -} - -void UHCIController::start() -{ - write_usbcmd(read_usbcmd() | UHCI_USBCMD_RUN); - // FIXME: Timeout - for (;;) { - if (!(read_usbsts() & UHCI_USBSTS_HOST_CONTROLLER_HALTED)) - break; - } - dbgln("UHCI: Started"); -} - -TransferDescriptor* UHCIController::create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len) -{ - TransferDescriptor* td = allocate_transfer_descriptor(); - if (td == nullptr) { - return nullptr; - } - - u16 max_len = (data_len > 0) ? (data_len - 1) : 0x7ff; - VERIFY(max_len <= 0x4FF || max_len == 0x7FF); // According to the datasheet, anything in the range of 0x500 to 0x7FE are illegal - - td->set_token((max_len << TD_TOKEN_MAXLEN_SHIFT) | ((pipe.data_toggle() ? 1 : 0) << TD_TOKEN_DATA_TOGGLE_SHIFT) | (pipe.endpoint_address() << TD_TOKEN_ENDPOINT_SHIFT) | (pipe.device_address() << TD_TOKEN_DEVICE_ADDR_SHIFT) | (static_cast(direction))); - pipe.set_toggle(!pipe.data_toggle()); - - if (pipe.type() == Pipe::Type::Isochronous) { - td->set_isochronous(); - } else { - if (direction == PacketID::IN) { - td->set_short_packet_detect(); - } - } - - // Set low-speed bit if the device connected to port is a low=speed device (probably unlikely...) - if (pipe.device_speed() == Pipe::DeviceSpeed::LowSpeed) { - td->set_lowspeed(); - } - - td->set_active(); - td->set_error_retry_counter(RETRY_COUNTER_RELOAD); - - return td; -} - -KResult UHCIController::create_chain(Pipe& pipe, PacketID direction, Ptr32& buffer_address, size_t max_size, size_t transfer_size, TransferDescriptor** td_chain, TransferDescriptor** last_td) -{ - // We need to create `n` transfer descriptors based on the max - // size of each transfer (which we've learned from the device already by reading - // its device descriptor, or 8 bytes). Each TD then has its buffer pointer - // set to the initial buffer address + (max_size * index), where index is - // the ID of the TD in the chain. - size_t byte_count = 0; - TransferDescriptor* current_td = nullptr; - TransferDescriptor* prev_td = nullptr; - TransferDescriptor* first_td = nullptr; - - // Keep creating transfer descriptors while we still have some data - while (byte_count < transfer_size) { - size_t packet_size = transfer_size - byte_count; - if (packet_size > max_size) { - packet_size = max_size; - } - - current_td = create_transfer_descriptor(pipe, direction, packet_size); - if (current_td == nullptr) { - free_descriptor_chain(first_td); - return ENOMEM; - } - - if (Checked::addition_would_overflow(reinterpret_cast(&*buffer_address), byte_count)) - return EOVERFLOW; - - auto buffer_pointer = Ptr32(buffer_address + byte_count); - current_td->set_buffer_address(buffer_pointer); - byte_count += packet_size; - - if (prev_td != nullptr) - prev_td->insert_next_transfer_descriptor(current_td); - else - first_td = current_td; - - prev_td = current_td; - } - - *last_td = current_td; - *td_chain = first_td; - return KSuccess; -} - -void UHCIController::free_descriptor_chain(TransferDescriptor* first_descriptor) -{ - TransferDescriptor* descriptor = first_descriptor; - - while (descriptor) { - descriptor->free(); - descriptor = descriptor->next_td(); - } -} - -KResultOr UHCIController::submit_control_transfer(Transfer& transfer) -{ - Pipe& pipe = transfer.pipe(); // Short circuit the pipe related to this transfer - bool direction_in = (transfer.request().request_type & USB_DEVICE_REQUEST_DEVICE_TO_HOST) == USB_DEVICE_REQUEST_DEVICE_TO_HOST; - - TransferDescriptor* setup_td = create_transfer_descriptor(pipe, PacketID::SETUP, sizeof(USBRequestData)); - if (!setup_td) - return ENOMEM; - - setup_td->set_buffer_address(transfer.buffer_physical().as_ptr()); - - // Create a new descriptor chain - TransferDescriptor* last_data_descriptor; - TransferDescriptor* data_descriptor_chain; - auto buffer_address = Ptr32(transfer.buffer_physical().as_ptr() + sizeof(USBRequestData)); - auto transfer_chain_create_result = create_chain(pipe, - direction_in ? PacketID::IN : PacketID::OUT, - buffer_address, - pipe.max_packet_size(), - transfer.transfer_data_size(), - &data_descriptor_chain, - &last_data_descriptor); - - if (transfer_chain_create_result != KSuccess) - return transfer_chain_create_result; - - // Status TD always has toggle set to 1 - pipe.set_toggle(true); - - TransferDescriptor* status_td = create_transfer_descriptor(pipe, direction_in ? PacketID::OUT : PacketID::IN, 0); - if (!status_td) { - free_descriptor_chain(data_descriptor_chain); - return ENOMEM; - } - status_td->terminate(); - - // Link transfers together - if (data_descriptor_chain) { - setup_td->insert_next_transfer_descriptor(data_descriptor_chain); - last_data_descriptor->insert_next_transfer_descriptor(status_td); - } else { - setup_td->insert_next_transfer_descriptor(status_td); - } - - // Cool, everything should be chained together now! Let's print it out - if constexpr (UHCI_VERBOSE_DEBUG) { - dbgln("Setup TD"); - setup_td->print(); - if (data_descriptor_chain) { - dbgln("Data TD"); - data_descriptor_chain->print(); - } - dbgln("Status TD"); - status_td->print(); - } - - QueueHead* transfer_queue = allocate_queue_head(); - if (!transfer_queue) { - free_descriptor_chain(data_descriptor_chain); - return 0; - } - - transfer_queue->attach_transfer_descriptor_chain(setup_td); - transfer_queue->set_transfer(&transfer); - - m_fullspeed_control_qh->attach_transfer_queue(*transfer_queue); - - size_t transfer_size = 0; - while (!transfer.complete()) - transfer_size = poll_transfer_queue(*transfer_queue); - - free_descriptor_chain(transfer_queue->get_first_td()); - transfer_queue->free(); - - return transfer_size; -} - -size_t UHCIController::poll_transfer_queue(QueueHead& transfer_queue) -{ - Transfer* transfer = transfer_queue.transfer(); - TransferDescriptor* descriptor = transfer_queue.get_first_td(); - bool transfer_still_in_progress = false; - size_t transfer_size = 0; - - while (descriptor) { - u32 status = descriptor->status(); - - if (status & TransferDescriptor::StatusBits::Active) { - transfer_still_in_progress = true; - break; - } - - if (status & TransferDescriptor::StatusBits::ErrorMask) { - transfer->set_complete(); - transfer->set_error_occurred(); - dbgln_if(UHCI_DEBUG, "UHCIController: Transfer failed! Reason: {:08x}", status); - return 0; - } - - transfer_size += descriptor->actual_packet_length(); - descriptor = descriptor->next_td(); - } - - if (!transfer_still_in_progress) - transfer->set_complete(); - - return transfer_size; -} - -void UHCIController::spawn_port_proc() -{ - RefPtr usb_hotplug_thread; - - Process::create_kernel_process(usb_hotplug_thread, "UHCIHotplug", [&] { - for (;;) { - for (int port = 0; port < UHCI_ROOT_PORT_COUNT; port++) { - u16 port_data = 0; - - if (port == 1) { - // Let's see what's happening on port 1 - // Current status - port_data = read_portsc1(); - if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) { - if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) { - dmesgln("UHCI: Device attach detected on Root Port 1!"); - - // Reset the port - port_data = read_portsc1(); - write_portsc1(port_data | UHCI_PORTSC_PORT_RESET); - IO::delay(500); - - write_portsc1(port_data & ~UHCI_PORTSC_PORT_RESET); - IO::delay(500); - - write_portsc1(port_data & (~UHCI_PORTSC_PORT_ENABLE_CHANGED | ~UHCI_PORTSC_CONNECT_STATUS_CHANGED)); - - port_data = read_portsc1(); - write_portsc1(port_data | UHCI_PORTSC_PORT_ENABLED); - dbgln("port should be enabled now: {:#04x}\n", read_portsc1()); - - USB::Device::DeviceSpeed speed = (port_data & UHCI_PORTSC_LOW_SPEED_DEVICE) ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; - auto device = USB::Device::try_create(USB::Device::PortNumber::Port1, speed); - - if (device.is_error()) - dmesgln("UHCI: Device creation failed on port 1 ({})", device.error()); - - m_devices.at(0) = device.value(); - VERIFY(s_procfs_usb_bus_folder); - s_procfs_usb_bus_folder->plug(device.value()); - } else { - // FIXME: Clean up (and properly) the RefPtr to the device in m_devices - VERIFY(s_procfs_usb_bus_folder); - VERIFY(m_devices.at(0)); - dmesgln("UHCI: Device detach detected on Root Port 1"); - s_procfs_usb_bus_folder->unplug(*m_devices.at(0)); - } - } - } else { - port_data = UHCIController::the().read_portsc2(); - if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) { - if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) { - dmesgln("UHCI: Device attach detected on Root Port 2"); - - // Reset the port - port_data = read_portsc2(); - write_portsc2(port_data | UHCI_PORTSC_PORT_RESET); - for (size_t i = 0; i < 50000; ++i) - IO::in8(0x80); - - write_portsc2(port_data & ~UHCI_PORTSC_PORT_RESET); - for (size_t i = 0; i < 100000; ++i) - IO::in8(0x80); - - write_portsc2(port_data & (~UHCI_PORTSC_PORT_ENABLE_CHANGED | ~UHCI_PORTSC_CONNECT_STATUS_CHANGED)); - - port_data = read_portsc2(); - write_portsc1(port_data | UHCI_PORTSC_PORT_ENABLED); - dbgln("port should be enabled now: {:#04x}\n", read_portsc1()); - USB::Device::DeviceSpeed speed = (port_data & UHCI_PORTSC_LOW_SPEED_DEVICE) ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; - auto device = USB::Device::try_create(USB::Device::PortNumber::Port2, speed); - - if (device.is_error()) - dmesgln("UHCI: Device creation failed on port 2 ({})", device.error()); - - m_devices.at(1) = device.value(); - VERIFY(s_procfs_usb_bus_folder); - s_procfs_usb_bus_folder->plug(device.value()); - } else { - // FIXME: Clean up (and properly) the RefPtr to the device in m_devices - VERIFY(s_procfs_usb_bus_folder); - VERIFY(m_devices.at(1)); - dmesgln("UHCI: Device detach detected on Root Port 2"); - s_procfs_usb_bus_folder->unplug(*m_devices.at(1)); - } - } - } - } - (void)Thread::current()->sleep(Time::from_seconds(1)); - } - }); -} - -bool UHCIController::handle_irq(const RegisterState&) -{ - u32 status = read_usbsts(); - - // Shared IRQ. Not ours! - if (!status) - return false; - - if constexpr (UHCI_DEBUG) { - dbgln("UHCI: Interrupt happened!"); - dbgln("Value of USBSTS: {:#04x}", read_usbsts()); - } - - // Write back USBSTS to clear bits - write_usbsts(status); - return true; -} - -} diff --git a/Kernel/Devices/USB/UHCIController.h b/Kernel/Devices/USB/UHCIController.h deleted file mode 100644 index 54cb8cf3c0..0000000000 --- a/Kernel/Devices/USB/UHCIController.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2020-2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel::USB { - -class UHCIController final : public PCI::Device { - -public: - static void detect(); - static UHCIController& the(); - - virtual ~UHCIController() override; - - virtual const char* purpose() const override { return "UHCI"; } - - void reset(); - void stop(); - void start(); - void spawn_port_proc(); - - void do_debug_transfer(); - - KResultOr submit_control_transfer(Transfer& transfer); - - RefPtr const get_device_at_port(USB::Device::PortNumber); - RefPtr const get_device_from_address(u8 device_address); - -private: - UHCIController(PCI::Address, PCI::ID); - - u16 read_usbcmd() { return m_io_base.offset(0).in(); } - u16 read_usbsts() { return m_io_base.offset(0x2).in(); } - u16 read_usbintr() { return m_io_base.offset(0x4).in(); } - u16 read_frnum() { return m_io_base.offset(0x6).in(); } - u32 read_flbaseadd() { return m_io_base.offset(0x8).in(); } - u8 read_sofmod() { return m_io_base.offset(0xc).in(); } - u16 read_portsc1() { return m_io_base.offset(0x10).in(); } - u16 read_portsc2() { return m_io_base.offset(0x12).in(); } - - void write_usbcmd(u16 value) { m_io_base.offset(0).out(value); } - void write_usbsts(u16 value) { m_io_base.offset(0x2).out(value); } - void write_usbintr(u16 value) { m_io_base.offset(0x4).out(value); } - void write_frnum(u16 value) { m_io_base.offset(0x6).out(value); } - void write_flbaseadd(u32 value) { m_io_base.offset(0x8).out(value); } - void write_sofmod(u8 value) { m_io_base.offset(0xc).out(value); } - void write_portsc1(u16 value) { m_io_base.offset(0x10).out(value); } - void write_portsc2(u16 value) { m_io_base.offset(0x12).out(value); } - - virtual bool handle_irq(const RegisterState&) override; - - void create_structures(); - void setup_schedule(); - size_t poll_transfer_queue(QueueHead& transfer_queue); - - TransferDescriptor* create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len); - KResult create_chain(Pipe& pipe, PacketID direction, Ptr32& buffer_address, size_t max_size, size_t transfer_size, TransferDescriptor** td_chain, TransferDescriptor** last_td); - void free_descriptor_chain(TransferDescriptor* first_descriptor); - - QueueHead* allocate_queue_head() const; - TransferDescriptor* allocate_transfer_descriptor() const; - -private: - IOAddress m_io_base; - - Vector m_free_qh_pool; - Vector m_free_td_pool; - Vector m_iso_td_list; - - QueueHead* m_interrupt_transfer_queue; - QueueHead* m_lowspeed_control_qh; - QueueHead* m_fullspeed_control_qh; - QueueHead* m_bulk_qh; - QueueHead* m_dummy_qh; // Needed for PIIX4 hack - - OwnPtr m_framelist; - OwnPtr m_qh_pool; - OwnPtr m_td_pool; - - Array, 2> m_devices; // Devices connected to the root ports (of which there are two) -}; - -} diff --git a/Kernel/Devices/USB/UHCIDescriptorTypes.h b/Kernel/Devices/USB/UHCIDescriptorTypes.h deleted file mode 100644 index 7715bad4b4..0000000000 --- a/Kernel/Devices/USB/UHCIDescriptorTypes.h +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -enum class PacketID : u8 { - IN = 0x69, - OUT = 0xe1, - SETUP = 0x2d -}; - -// Transfer Descriptor register bit offsets/masks -constexpr u16 TD_CONTROL_STATUS_ACTLEN = 0x7ff; -constexpr u8 TD_CONTROL_STATUS_ACTIVE_SHIFT = 23; -constexpr u8 TD_CONTROL_STATUS_INT_ON_COMPLETE_SHIFT = 24; -constexpr u8 TD_CONTROL_STATUS_ISOCHRONOUS_SHIFT = 25; -constexpr u8 TD_CONTROL_STATUS_LS_DEVICE_SHIFT = 26; -constexpr u8 TD_CONTROL_STATUS_ERR_CTR_SHIFT_SHIFT = 27; -constexpr u8 TD_CONTROL_STATUS_SPD_SHIFT = 29; - -constexpr u8 TD_TOKEN_PACKET_ID_SHIFT = 0; -constexpr u8 TD_TOKEN_DEVICE_ADDR_SHIFT = 8; -constexpr u8 TD_TOKEN_ENDPOINT_SHIFT = 15; -constexpr u8 TD_TOKEN_DATA_TOGGLE_SHIFT = 19; -constexpr u8 TD_TOKEN_MAXLEN_SHIFT = 21; - -// -// Transfer Descriptor -// -// Describes a single transfer event from, or to the Universal Serial Bus. -// These are, generally, attached to Queue Heads, and then executed by the -// USB Host Controller. -// Must be 16-byte aligned -// -struct QueueHead; -struct alignas(16) TransferDescriptor final { - enum LinkPointerBits { - Terminate = 1, - QHSelect = 2, - DepthFlag = 4, - }; - - enum StatusBits { - Reserved = (1 << 16), - BitStuffError = (1 << 17), - CRCTimeoutError = (1 << 18), - NAKReceived = (1 << 19), - BabbleDetected = (1 << 20), - DataBufferError = (1 << 21), - Stalled = (1 << 22), - Active = (1 << 23), - ErrorMask = BitStuffError | CRCTimeoutError | NAKReceived | BabbleDetected | DataBufferError | Stalled - }; - - enum ControlBits { - InterruptOnComplete = (1 << 24), - IsochronousSelect = (1 << 25), - LowSpeedDevice = (1 << 26), - ShortPacketDetect = (1 << 29), - }; - - TransferDescriptor() = delete; - TransferDescriptor(u32 paddr) - : m_paddr(paddr) - { - } - ~TransferDescriptor() = delete; // Prevent anything except placement new on this object - - u32 link_ptr() const { return m_link_ptr; } - u32 paddr() const { return m_paddr; } - u32 status() const { return m_control_status; } - u32 token() const { return m_token; } - u32 buffer_ptr() const { return m_buffer_ptr; } - u16 actual_packet_length() const { return (m_control_status + 1) & 0x7ff; } - - bool in_use() const { return m_in_use; } - bool stalled() const { return m_control_status & StatusBits::Stalled; } - bool last_in_chain() const { return m_link_ptr & LinkPointerBits::Terminate; } - bool active() const { return m_control_status & StatusBits::Active; } - - void set_active() - { - u32 ctrl = m_control_status; - ctrl |= StatusBits::Active; - m_control_status = ctrl; - } - - void set_isochronous() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::IsochronousSelect; - m_control_status = ctrl; - } - - void set_interrupt_on_complete() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::InterruptOnComplete; - m_control_status = ctrl; - } - - void set_lowspeed() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::LowSpeedDevice; - m_control_status = ctrl; - } - - void set_error_retry_counter(u8 num_retries) - { - VERIFY(num_retries <= 3); - u32 ctrl = m_control_status; - ctrl |= (num_retries << 27); - m_control_status = ctrl; - } - - void set_short_packet_detect() - { - u32 ctrl = m_control_status; - ctrl |= ControlBits::ShortPacketDetect; - m_control_status = ctrl; - } - - void set_control_status(u32 control_status) { m_control_status = control_status; } - void set_in_use(bool in_use) { m_in_use = in_use; } - void set_max_len(u16 max_len) - { - VERIFY(max_len < 0x500 || max_len == 0x7ff); - m_token |= (max_len << 21); - } - - void set_device_endpoint(u8 endpoint) - { - VERIFY(endpoint <= 0xf); - m_token |= (endpoint << 18); - } - - void set_device_address(u8 address) - { - VERIFY(address <= 0x7f); - m_token |= (address << 8); - } - - void set_data_toggle(bool toggle) - { - m_token |= ((toggle ? (1 << 19) : 0)); - } - - void set_packet_id(PacketID pid) { m_token |= static_cast(pid); } - void link_queue_head(u32 qh_paddr) - { - m_link_ptr = qh_paddr; - m_link_ptr |= LinkPointerBits::QHSelect; - } - - void print() - { - dbgln("UHCI: TD({:#04x}) @ {:#04x}: link_ptr={:#04x}, status={:#04x}, token={:#04x}, buffer_ptr={:#04x}", this, m_paddr, m_link_ptr, (u32)m_control_status, m_token, m_buffer_ptr); - - // Now let's print the flags! - dbgln("UHCI: TD({:#04x}) @ {:#04x}: link_ptr={}{}{}, status={}{}{}{}{}{}{}", - this, - m_paddr, - (last_in_chain()) ? "T " : "", - (m_link_ptr & static_cast(LinkPointerBits::QHSelect)) ? "QH " : "", - (m_link_ptr & static_cast(LinkPointerBits::DepthFlag)) ? "Vf " : "", - (m_control_status & static_cast(StatusBits::BitStuffError)) ? "BITSTUFF " : "", - (m_control_status & static_cast(StatusBits::CRCTimeoutError)) ? "CRCTIMEOUT " : "", - (m_control_status & static_cast(StatusBits::NAKReceived)) ? "NAK " : "", - (m_control_status & static_cast(StatusBits::BabbleDetected)) ? "BABBLE " : "", - (m_control_status & static_cast(StatusBits::DataBufferError)) ? "DATAERR " : "", - (stalled()) ? "STALL " : "", - (active()) ? "ACTIVE " : ""); - } - - // FIXME: For the love of God, use AK SMART POINTERS PLEASE!! - TransferDescriptor* next_td() { return m_next_td; } - const TransferDescriptor* next_td() const { return m_next_td; } - void set_next_td(TransferDescriptor* td) { m_next_td = td; } - - TransferDescriptor* prev_td() { return m_prev_td; } - const TransferDescriptor* prev_td() const { return m_prev_td; } - void set_previous_td(TransferDescriptor* td) { m_prev_td = td; } - - void insert_next_transfer_descriptor(TransferDescriptor* td) - { - m_link_ptr = td->paddr(); - td->set_previous_td(this); - set_next_td(td); - - // Let's set some bits for the link ptr - m_link_ptr |= static_cast(LinkPointerBits::DepthFlag); - } - - void terminate() { m_link_ptr |= static_cast(LinkPointerBits::Terminate); } - - void set_buffer_address(Ptr32 buffer) - { - u8* buffer_address = &*buffer; - m_buffer_ptr = reinterpret_cast(buffer_address); - } - - // DEBUG FUNCTIONS! - void set_token(u32 token) - { - m_token = token; - } - - void set_status(u32 status) - { - m_control_status = status; - } - - void free() - { - m_link_ptr = 0; - m_control_status = 0; - m_token = 0; - m_in_use = false; - } - -private: - u32 m_link_ptr; // Points to another Queue Head or Transfer Descriptor - volatile u32 m_control_status; // Control and status bits - u32 m_token; // Contains all information required to fill in a USB Start Token - u32 m_buffer_ptr; // Points to a data buffer for this transaction (i.e what we want to send or recv) - - // These values will be ignored by the controller, but we can use them for configuration/bookkeeping - u32 m_paddr; // Physical address where this TransferDescriptor is located - Ptr32 m_next_td { nullptr }; // Pointer to first TD - Ptr32 m_prev_td { nullptr }; // Pointer to first TD - bool m_in_use; // Has this TD been allocated (and therefore in use)? -}; - -static_assert(sizeof(TransferDescriptor) == 32); // Transfer Descriptor is always 8 Dwords - -// -// Queue Head -// -// Description here please! -// -struct alignas(16) QueueHead { - enum class LinkPointerBits : u32 { - Terminate = 1, - QHSelect = 2, - }; - - QueueHead() = delete; - QueueHead(u32 paddr) - : m_paddr(paddr) - { - } - ~QueueHead() = delete; // Prevent anything except placement new on this object - - u32 link_ptr() const { return m_link_ptr; } - u32 element_link_ptr() const { return m_element_link_ptr; } - u32 paddr() const { return m_paddr; } - bool in_use() const { return m_in_use; } - - void set_in_use(bool in_use) { m_in_use = in_use; } - void set_link_ptr(u32 val) { m_link_ptr = val; } - - // FIXME: For the love of God, use AK SMART POINTERS PLEASE!! - QueueHead* next_qh() { return m_next_qh; } - const QueueHead* next_qh() const { return m_next_qh; } - void set_next_qh(QueueHead* qh) { m_next_qh = qh; } - - QueueHead* prev_qh() { return m_prev_qh; } - const QueueHead* prev_qh() const { return m_prev_qh; } - void set_previous_qh(QueueHead* qh) - { - m_prev_qh = qh; - } - - void link_next_queue_head(QueueHead* qh) - { - m_link_ptr = qh->paddr(); - m_link_ptr |= static_cast(LinkPointerBits::QHSelect); - } - - void attach_transfer_queue(QueueHead& qh) - { - m_element_link_ptr = qh.paddr(); - m_element_link_ptr = m_element_link_ptr | static_cast(LinkPointerBits::QHSelect); - } - - // FIXME: Find out best way to walk queue and free everything - void free_transfer_queue([[maybe_unused]] QueueHead* qh) - { - TODO(); - } - - void terminate_with_stray_descriptor(TransferDescriptor* td) - { - m_link_ptr = td->paddr(); - m_link_ptr |= static_cast(LinkPointerBits::Terminate); - } - - // TODO: Should we pass in an array or vector of TDs instead???? - void attach_transfer_descriptor_chain(TransferDescriptor* td) - { - m_first_td = td; - m_element_link_ptr = td->paddr(); - } - - TransferDescriptor* get_first_td() - { - return m_first_td; - } - - void terminate() { m_link_ptr |= static_cast(LinkPointerBits::Terminate); } - - void terminate_element_link_ptr() - { - m_element_link_ptr = static_cast(LinkPointerBits::Terminate); - } - - void set_transfer(Transfer* transfer) - { - m_transfer = transfer; - } - - Transfer* transfer() - { - return m_transfer; - } - - void print() - { - dbgln("UHCI: QH({:#04x}) @ {:#04x}: link_ptr={:#04x}, element_link_ptr={:#04x}", this, m_paddr, m_link_ptr, (FlatPtr)m_element_link_ptr); - } - - void free() - { - m_link_ptr = 0; - m_element_link_ptr = 0; - m_first_td = nullptr; - m_transfer = nullptr; - m_in_use = false; - } - -private: - u32 m_link_ptr { 0 }; // Pointer to the next horizontal object that the controller will execute after this one - volatile u32 m_element_link_ptr { 0 }; // Pointer to the first data object in the queue (can be modified by hw) - - // These values will be ignored by the controller, but we can use them for configuration/bookkeeping - // Any addresses besides `paddr` are assumed virtual and can be dereferenced - u32 m_paddr { 0 }; // Physical address where this QueueHead is located - Ptr32 m_next_qh { nullptr }; // Next QH - Ptr32 m_prev_qh { nullptr }; // Previous QH - Ptr32 m_first_td { nullptr }; // Pointer to first TD - Ptr32 m_transfer { nullptr }; // Pointer to transfer linked to this queue head - bool m_in_use { false }; // Is this QH currently in use? -}; - -static_assert(sizeof(QueueHead) == 32); // Queue Head is always 8 Dwords -} diff --git a/Kernel/Devices/USB/USBDescriptors.h b/Kernel/Devices/USB/USBDescriptors.h deleted file mode 100644 index 0e8486bd8a..0000000000 --- a/Kernel/Devices/USB/USBDescriptors.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::USB { - -struct [[gnu::packed]] USBDescriptorCommon { - u8 length; - u8 descriptor_type; -}; - -// -// Device Descriptor -// ================= -// -// This descriptor type (stored on the device), represents the device, and gives -// information related to it, such as the USB specification it complies to, -// as well as the vendor and product ID of the device. -// -// https://beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors -struct [[gnu::packed]] USBDeviceDescriptor { - USBDescriptorCommon descriptor_header; - u16 usb_spec_compliance_bcd; - u8 device_class; - u8 device_sub_class; - u8 device_protocol; - u8 max_packet_size; - u16 vendor_id; - u16 product_id; - u16 device_release_bcd; - u8 manufacturer_id_descriptor_index; - u8 product_string_descriptor_index; - u8 serial_number_descriptor_index; - u8 num_configurations; -}; - -// -// Configuration Descriptor -// ======================== -// -// A USB device can have multiple configurations, which tells us about how the -// device is physically configured (e.g how it's powered, max power consumption etc). -// -struct [[gnu::packed]] USBConfigurationDescriptor { - USBDescriptorCommon descriptor_header; - u16 total_length; - u8 number_of_interfaces; - u8 configuration_value; - u8 configuration_string_descriptor_index; - u8 attributes_bitmap; - u8 max_power_in_ma; -}; - -// -// Interface Descriptor -// ==================== -// -// An interface descriptor describes to us one or more endpoints, grouped -// together to define a singular function of a device. -// As an example, a USB webcam might have two interface descriptors; one -// for the camera, and one for the microphone. -// -struct [[gnu::packed]] USBInterfaceDescriptor { - USBDescriptorCommon descriptor_header; - u8 interface_id; - u8 alternate_setting; - u8 number_of_endpoints; - u8 interface_class_code; - u8 interface_sub_class_code; - u8 interface_protocol; - u8 interface_string_descriptor_index; -}; - -// -// Endpoint Descriptor -// =================== -// -// The lowest leaf in the configuration tree. And endpoint descriptor describes -// the physical transfer properties of the endpoint (that isn't endpoint0). -// The description given by this structure is used by a pipe to create a -// "connection" from the host to the device. -// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/usb-endpoints-and-their-pipes -struct [[gnu::packed]] USBEndpointDescriptor { - USBDescriptorCommon descriptor_header; - u8 endpoint_address; - u8 endpoint_attributes_bitmap; - u16 max_packet_size; - u8 poll_interval_in_frames; -}; - -static constexpr u8 DESCRIPTOR_TYPE_DEVICE = 0x01; -static constexpr u8 DESCRIPTOR_TYPE_CONFIGURATION = 0x02; -static constexpr u8 DESCRIPTOR_TYPE_STRING = 0x03; -static constexpr u8 DESCRIPTOR_TYPE_INTERFACE = 0x04; -static constexpr u8 DESCRIPTOR_TYPE_ENDPOINT = 0x05; -static constexpr u8 DESCRIPTOR_TYPE_DEVICE_QUALIFIER = 0x06; - -} diff --git a/Kernel/Devices/USB/USBDevice.cpp b/Kernel/Devices/USB/USBDevice.cpp deleted file mode 100644 index 609f059f7d..0000000000 --- a/Kernel/Devices/USB/USBDevice.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -static u32 s_next_usb_address = 1; // Next address we hand out to a device once it's plugged into the machine - -namespace Kernel::USB { - -KResultOr> Device::try_create(PortNumber port, DeviceSpeed speed) -{ - auto pipe_or_error = Pipe::try_create_pipe(Pipe::Type::Control, Pipe::Direction::Bidirectional, 0, 8, 0); - if (pipe_or_error.is_error()) - return pipe_or_error.error(); - - auto device = AK::try_create(port, speed, pipe_or_error.release_value()); - if (!device) - return ENOMEM; - - auto enumerate_result = device->enumerate(); - if (enumerate_result.is_error()) - return enumerate_result; - - return device.release_nonnull(); -} - -Device::Device(PortNumber port, DeviceSpeed speed, NonnullOwnPtr default_pipe) - : m_device_port(port) - , m_device_speed(speed) - , m_address(0) - , m_default_pipe(move(default_pipe)) -{ -} - -KResult Device::enumerate() -{ - USBDeviceDescriptor dev_descriptor {}; - - // FIXME: 0x100 is a magic number for now, as I'm not quite sure how these are constructed.... - // Send 8-bytes to get at least the `max_packet_size` from the device - auto transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, 0x100, 0, 8, &dev_descriptor); - - if (transfer_length_or_error.is_error()) - return transfer_length_or_error.error(); - - auto transfer_length = transfer_length_or_error.release_value(); - - // FIXME: This shouldn't crash! Do some correct error handling on me please! - VERIFY(transfer_length > 0); - - // Ensure that this is actually a valid device descriptor... - VERIFY(dev_descriptor.descriptor_header.descriptor_type == DESCRIPTOR_TYPE_DEVICE); - m_default_pipe->set_max_packet_size(dev_descriptor.max_packet_size); - - transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_DEVICE_TO_HOST, USB_REQUEST_GET_DESCRIPTOR, 0x100, 0, sizeof(USBDeviceDescriptor), &dev_descriptor); - - if (transfer_length_or_error.is_error()) - return transfer_length_or_error.error(); - - transfer_length = transfer_length_or_error.release_value(); - - // FIXME: This shouldn't crash! Do some correct error handling on me please! - VERIFY(transfer_length > 0); - - // Ensure that this is actually a valid device descriptor... - VERIFY(dev_descriptor.descriptor_header.descriptor_type == DESCRIPTOR_TYPE_DEVICE); - - if constexpr (USB_DEBUG) { - dbgln("USB Device Descriptor for {:04x}:{:04x}", dev_descriptor.vendor_id, dev_descriptor.product_id); - dbgln("Device Class: {:02x}", dev_descriptor.device_class); - dbgln("Device Sub-Class: {:02x}", dev_descriptor.device_sub_class); - dbgln("Device Protocol: {:02x}", dev_descriptor.device_protocol); - dbgln("Max Packet Size: {:02x} bytes", dev_descriptor.max_packet_size); - dbgln("Number of configurations: {:02x}", dev_descriptor.num_configurations); - } - - // Attempt to set devices address on the bus - transfer_length_or_error = m_default_pipe->control_transfer(USB_DEVICE_REQUEST_HOST_TO_DEVICE, USB_REQUEST_SET_ADDRESS, s_next_usb_address, 0, 0, nullptr); - - if (transfer_length_or_error.is_error()) - return transfer_length_or_error.error(); - - transfer_length = transfer_length_or_error.release_value(); - - VERIFY(transfer_length > 0); - m_address = s_next_usb_address++; - - memcpy(&m_device_descriptor, &dev_descriptor, sizeof(USBDeviceDescriptor)); - return KSuccess; -} - -Device::~Device() -{ -} - -} diff --git a/Kernel/Devices/USB/USBDevice.h b/Kernel/Devices/USB/USBDevice.h deleted file mode 100644 index d884e13b3d..0000000000 --- a/Kernel/Devices/USB/USBDevice.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel::USB { - -// -// Some nice info from FTDI on device enumeration and how some of this -// glues together: -// -// https://www.ftdichip.com/Support/Documents/TechnicalNotes/TN_113_Simplified%20Description%20of%20USB%20Device%20Enumeration.pdf -class Device : public RefCounted { -public: - enum class PortNumber : u8 { - Port1 = 0, - Port2 - }; - - enum class DeviceSpeed : u8 { - FullSpeed = 0, - LowSpeed - }; - -public: - static KResultOr> try_create(PortNumber, DeviceSpeed); - - Device(PortNumber, DeviceSpeed, NonnullOwnPtr default_pipe); - ~Device(); - - KResult enumerate(); - - PortNumber port() const { return m_device_port; } - DeviceSpeed speed() const { return m_device_speed; } - - u8 address() const { return m_address; } - - const USBDeviceDescriptor& device_descriptor() const { return m_device_descriptor; } - -private: - PortNumber m_device_port; // What port is this device attached to - DeviceSpeed m_device_speed; // What speed is this device running at - u8 m_address { 0 }; // USB address assigned to this device - - // Device description - u16 m_vendor_id { 0 }; // This device's vendor ID assigned by the USB group - u16 m_product_id { 0 }; // This device's product ID assigned by the USB group - USBDeviceDescriptor m_device_descriptor; // Device Descriptor obtained from USB Device - - NonnullOwnPtr m_default_pipe; // Default communication pipe (endpoint0) used during enumeration -}; -} diff --git a/Kernel/Devices/USB/USBEndpoint.h b/Kernel/Devices/USB/USBEndpoint.h deleted file mode 100644 index 4bc0769381..0000000000 --- a/Kernel/Devices/USB/USBEndpoint.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel::USB { - -// -// An endpoint is the "end point" of communication of a USB device. That is, data is read from and written -// to an endpoint via a USB pipe. As an example, during device enumeration (where we assign an address to the -// device), we communicate with the device over the default endpoint, endpoint0, which all devices _must_ -// contain to be compliant with the USB specification. -// -// And endpoint describes characteristics about the transfer between the host and the device, such as: -// - The endpoint number -// - Max packet size of send/recv of the endpoint -// - Transfer type (bulk, interrupt, isochronous etc) -// -// Take for example a USB multifunction device, such as a keyboard/mouse combination. The mouse -// may need to be polled every n milliseconds, meaning the transfer may be isochronous (streamed), -// while the keyboard part would only generate data once we push a key (hence an interrupt transfer). -// Each of these data sources would be a _different_ endpoint on the device that we read from. -class USBEndpoint { - static constexpr u8 ENDPOINT_ADDRESS_NUMBER_MASK = 0x0f; - static constexpr u8 ENDPOINT_ADDRESS_DIRECTION_MASK = 0x80; - - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK = 0x03; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_CONTROL = 0x00; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_ISOCHRONOUS = 0x01; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_BULK = 0x02; - static constexpr u8 ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT = 0x03; - - static constexpr u8 ENDPOINT_ATTRIBUTES_ISO_MODE_SYNC_TYPE = 0x0c; - static constexpr u8 ENDPOINT_ATTRIBUTES_ISO_MODE_USAGE_TYPE = 0x30; - -public: - const USBEndpointDescriptor& descriptor() const { return m_descriptor; } - - bool is_control() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_CONTROL; } - bool is_isochronous() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_ISOCHRONOUS; } - bool is_bulk() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_BULK; } - bool is_interrupt() const { return (m_descriptor.endpoint_attributes_bitmap & ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_MASK) == ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT; } - - u16 max_packet_size() const { return m_descriptor.max_packet_size; } - u8 polling_interval() const { return m_descriptor.poll_interval_in_frames; } - -private: - USBEndpoint(/* TODO */); - USBEndpointDescriptor m_descriptor; - - USBPipe m_pipe; -}; - -} diff --git a/Kernel/Devices/USB/USBPipe.cpp b/Kernel/Devices/USB/USBPipe.cpp deleted file mode 100644 index f7275b5b1c..0000000000 --- a/Kernel/Devices/USB/USBPipe.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Kernel::USB { - -KResultOr> Pipe::try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval) -{ - auto pipe = adopt_own_if_nonnull(new (nothrow) Pipe(type, direction, endpoint_address, max_packet_size, device_address, poll_interval)); - if (!pipe) - return ENOMEM; - - return pipe.release_nonnull(); -} - -Pipe::Pipe(Type type, Pipe::Direction direction, u16 max_packet_size) - : m_type(type) - , m_direction(direction) - , m_endpoint_address(0) - , m_max_packet_size(max_packet_size) - , m_poll_interval(0) - , m_data_toggle(false) -{ -} - -Pipe::Pipe(Type type, Direction direction, USBEndpointDescriptor& endpoint [[maybe_unused]]) - : m_type(type) - , m_direction(direction) -{ - // TODO: decode endpoint structure -} - -Pipe::Pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address) - : m_type(type) - , m_direction(direction) - , m_device_address(device_address) - , m_endpoint_address(endpoint_address) - , m_max_packet_size(max_packet_size) - , m_poll_interval(poll_interval) - , m_data_toggle(false) -{ -} - -KResultOr Pipe::control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data) -{ - USBRequestData usb_request; - - usb_request.request_type = request_type; - usb_request.request = request; - usb_request.value = value; - usb_request.index = index; - usb_request.length = length; - - auto transfer = Transfer::try_create(*this, length); - - if (!transfer) - return ENOMEM; - - transfer->set_setup_packet(usb_request); - - dbgln_if(USB_DEBUG, "Pipe: Transfer allocated @ {:08x}", transfer->buffer_physical()); - auto transfer_len_or_error = UHCIController::the().submit_control_transfer(*transfer); - - if (transfer_len_or_error.is_error()) - return transfer_len_or_error.error(); - - auto transfer_length = transfer_len_or_error.release_value(); - - // TODO: Check transfer for completion and copy data from transfer buffer into data - if (length > 0) - memcpy(reinterpret_cast(data), transfer->buffer().as_ptr() + sizeof(USBRequestData), length); - - dbgln_if(USB_DEBUG, "Pipe: Control Transfer complete!"); - return transfer_length; -} - -} diff --git a/Kernel/Devices/USB/USBPipe.h b/Kernel/Devices/USB/USBPipe.h deleted file mode 100644 index ab01984451..0000000000 --- a/Kernel/Devices/USB/USBPipe.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel::USB { - -// -// A pipe is the logical connection between a memory buffer on the PC (host) and -// an endpoint on the device. In this implementation, the data buffer the pipe connects -// to is the physical buffer created when a Transfer is allocated. -// -class Pipe { -public: - enum class Type : u8 { - Control = 0, - Isochronous = 1, - Bulk = 2, - Interrupt = 3 - }; - - enum class Direction : u8 { - Out = 0, - In = 1, - Bidirectional = 2 - }; - - enum class DeviceSpeed : u8 { - LowSpeed, - FullSpeed - }; - -public: - static KResultOr> try_create_pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, u8 poll_interval = 0); - - Type type() const { return m_type; } - Direction direction() const { return m_direction; } - DeviceSpeed device_speed() const { return m_speed; } - - i8 device_address() const { return m_device_address; } - u8 endpoint_address() const { return m_endpoint_address; } - u16 max_packet_size() const { return m_max_packet_size; } - u8 poll_interval() const { return m_poll_interval; } - bool data_toggle() const { return m_data_toggle; } - - void set_max_packet_size(u16 max_size) { m_max_packet_size = max_size; } - void set_toggle(bool toggle) { m_data_toggle = toggle; } - void set_device_address(i8 addr) { m_device_address = addr; } - - KResultOr control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data); - - Pipe(Type type, Direction direction, u16 max_packet_size); - Pipe(Type type, Direction direction, USBEndpointDescriptor& endpoint); - Pipe(Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address); - -private: - friend class Device; - - Type m_type; - Direction m_direction; - DeviceSpeed m_speed; - - i8 m_device_address { 0 }; // Device address of this pipe - u8 m_endpoint_address { 0 }; // Corresponding endpoint address for this pipe - u16 m_max_packet_size { 0 }; // Max packet size for this pipe - u8 m_poll_interval { 0 }; // Polling interval (in frames) - bool m_data_toggle { false }; // Data toggle for stuffing bit -}; -} diff --git a/Kernel/Devices/USB/USBRequest.h b/Kernel/Devices/USB/USBRequest.h deleted file mode 100644 index 38f184de1f..0000000000 --- a/Kernel/Devices/USB/USBRequest.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -// -// USB Request directions -// -// As per Section 9.4 of the USB Specification, it is noted that Requeset Types that -// Device to Host have bit 7 of `bmRequestType` set. These are here as a convenience, -// as we construct the request at the call-site to make reading transfers easier. -// -static constexpr u8 USB_DEVICE_REQUEST_DEVICE_TO_HOST = 0x80; -static constexpr u8 USB_DEVICE_REQUEST_HOST_TO_DEVICE = 0x00; -static constexpr u8 USB_INTERFACE_REQUEST_DEVICE_TO_HOST = 0x81; -static constexpr u8 USB_INTERFACE_REQUEST_HOST_TO_DEVICE = 0x01; -static constexpr u8 USB_ENDPOINT_REQUEST_DEVICE_TO_HOST = 0x82; -static constexpr u8 USB_ENDPOINT_REQUEST_HOST_TO_DEVICE = 0x02; - -// -// Standard USB request types -// -// These are found in Section 9.4 of the USB Spec -// -static constexpr u8 USB_REQUEST_GET_STATUS = 0x00; -static constexpr u8 USB_REQUEST_CLEAR_FEATURE = 0x01; -static constexpr u8 USB_REQUEST_SET_FEATURE = 0x03; -static constexpr u8 USB_REQUEST_SET_ADDRESS = 0x05; -static constexpr u8 USB_REQUEST_GET_DESCRIPTOR = 0x06; -static constexpr u8 USB_REQUEST_SET_DESCRIPTOR = 0x07; -static constexpr u8 USB_REQUEST_GET_CONFIGURATION = 0x08; -static constexpr u8 USB_REQUEST_SET_CONFIGURATION = 0x09; diff --git a/Kernel/Devices/USB/USBTransfer.cpp b/Kernel/Devices/USB/USBTransfer.cpp deleted file mode 100644 index cd9ad7f00b..0000000000 --- a/Kernel/Devices/USB/USBTransfer.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel::USB { - -RefPtr Transfer::try_create(Pipe& pipe, u16 len) -{ - auto vmobject = ContiguousVMObject::create_with_size(PAGE_SIZE); - if (!vmobject) - return nullptr; - - return AK::try_create(pipe, len, *vmobject); -} - -Transfer::Transfer(Pipe& pipe, u16 len, ContiguousVMObject& vmobject) - : m_pipe(pipe) - , m_transfer_data_size(len) -{ - // Initialize data buffer for transfer - // This will definitely need to be refactored in the future, I doubt this will scale well... - m_data_buffer = MemoryManager::the().allocate_kernel_region_with_vmobject(vmobject, PAGE_SIZE, "USB Transfer Buffer", Region::Access::Read | Region::Access::Write); -} - -Transfer::~Transfer() -{ -} - -void Transfer::set_setup_packet(const USBRequestData& request) -{ - // Kind of a nasty hack... Because the kernel isn't in the business - // of handing out physical pointers that we can directly write to, - // we set the address of the setup packet to be the first 8 bytes of - // the data buffer, which we then set to the physical address. - auto* request_data = reinterpret_cast(buffer().as_ptr()); - - request_data->request_type = request.request_type; - request_data->request = request.request; - request_data->value = request.value; - request_data->index = request.index; - request_data->length = request.length; - - m_request = request; -} - -} diff --git a/Kernel/Devices/USB/USBTransfer.h b/Kernel/Devices/USB/USBTransfer.h deleted file mode 100644 index 3360ea758f..0000000000 --- a/Kernel/Devices/USB/USBTransfer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021, Jesse Buhagiar - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -// TODO: Callback stuff in this class please! -namespace Kernel::USB { - -class Transfer : public RefCounted { -public: - static RefPtr try_create(Pipe& pipe, u16 len); - -public: - Transfer() = delete; - Transfer(Pipe& pipe, u16 len, ContiguousVMObject&); - ~Transfer(); - - void set_setup_packet(const USBRequestData& request); - void set_complete() { m_complete = true; } - void set_error_occurred() { m_error_occurred = true; } - - // `const` here makes sure we don't blow up by writing to a physical address - const USBRequestData& request() const { return m_request; } - const Pipe& pipe() const { return m_pipe; } - Pipe& pipe() { return m_pipe; } - VirtualAddress buffer() const { return m_data_buffer->vaddr(); } - PhysicalAddress buffer_physical() const { return m_data_buffer->physical_page(0)->paddr(); } - u16 transfer_data_size() const { return m_transfer_data_size; } - bool complete() const { return m_complete; } - bool error_occurred() const { return m_error_occurred; } - -private: - Pipe& m_pipe; // Pipe that initiated this transfer - USBRequestData m_request; // USB request - OwnPtr m_data_buffer; // DMA Data buffer for transaction - u16 m_transfer_data_size { 0 }; // Size of the transfer's data stage - bool m_complete { false }; // Has this transfer been completed? - bool m_error_occurred { false }; // Did an error occur during this transfer? -}; -} diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 106800382e..7ec06faac4 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3