diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index cf8e73f4be..3ad91daff4 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -47,6 +47,9 @@ set(KERNEL_SOURCES 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/Debug.h.in b/Kernel/Debug.h.in index 23b57a1f7c..c9b8b6a368 100644 --- a/Kernel/Debug.h.in +++ b/Kernel/Debug.h.in @@ -302,6 +302,10 @@ #cmakedefine01 UHCI_VERBOSE_DEBUG #endif +#ifndef USB_DEBUG +#cmakedefine01 USB_DEBUG +#endif + #ifndef VFS_DEBUG #cmakedefine01 VFS_DEBUG #endif diff --git a/Kernel/Devices/USB/PacketTypes.h b/Kernel/Devices/USB/PacketTypes.h new file mode 100644 index 0000000000..8dc65094ef --- /dev/null +++ b/Kernel/Devices/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/Devices/USB/UHCIController.cpp b/Kernel/Devices/USB/UHCIController.cpp index 0859aac1fe..a1a497c3ce 100644 --- a/Kernel/Devices/USB/UHCIController.cpp +++ b/Kernel/Devices/USB/UHCIController.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ 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 { @@ -127,8 +129,9 @@ void UHCIController::reset() write_flbaseadd(m_framelist->physical_page(0)->paddr().get()); // Frame list (physical) address write_frnum(0); // Set the initial frame number - // Enable all interrupt types - write_frnum(UHCI_USBINTR_TIMEOUT_CRC_ENABLE | UHCI_USBINTR_RESUME_INTR_ENABLE | UHCI_USBINTR_IOC_ENABLE | UHCI_USBINTR_SHORT_PACKET_INTR_ENABLE); + // FIXME: Work out why interrupts lock up the entire system.... + // Disable UHCI Controller from raising an IRQ + write_usbintr(0); dbgln("UHCI: Reset completed"); } @@ -273,8 +276,7 @@ QueueHead* UHCIController::allocate_queue_head() const } } - VERIFY_NOT_REACHED(); // Let's just assert for now, this should never happen - return nullptr; // Huh!? We're outta queue heads! + return nullptr; // Huh!? We're outta queue heads! } TransferDescriptor* UHCIController::allocate_transfer_descriptor() const @@ -287,8 +289,7 @@ TransferDescriptor* UHCIController::allocate_transfer_descriptor() const } } - VERIFY_NOT_REACHED(); // Let's just assert for now, this should never happen - return nullptr; // Huh?! We're outta TDs!! + return nullptr; // Huh?! We're outta TDs!! } void UHCIController::stop() @@ -312,62 +313,200 @@ void UHCIController::start() dbgln("UHCI: Started"); } -struct setup_packet { - u8 bmRequestType; - u8 bRequest; - u16 wValue; - u16 wIndex; - u16 wLength; -}; - -void UHCIController::do_debug_transfer() +TransferDescriptor* UHCIController::create_transfer_descriptor(Pipe& pipe, PacketID direction, size_t data_len) { - dbgln("UHCI: Attempting a dummy transfer..."); + TransferDescriptor* td = allocate_transfer_descriptor(); + if (td == nullptr) { + return nullptr; + } - // Okay, let's set up the buffer so we can write some data - auto vmobj = ContiguousVMObject::create_with_size(PAGE_SIZE); - m_td_buffer_region = MemoryManager::the().allocate_kernel_region_with_vmobject(*vmobj, PAGE_SIZE, "UHCI Debug Data Region", Region::Access::Write); + 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 - // We need to set up THREE Transfer descriptors here - // 1. The SETUP packet TD - // 2. The DATA packet - // 3. The ACK TD that will be filled by the device - // We can use the buffer pool provided above to do this, using nasty pointer offsets! - auto setup_td = allocate_transfer_descriptor(); - auto data_td = allocate_transfer_descriptor(); - auto response_td = allocate_transfer_descriptor(); + 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()); - dbgln("BUFFER PHYSICAL ADDRESS = {}", m_td_buffer_region->physical_page(0)->paddr()); + if (pipe.type() == Pipe::Type::Isochronous) { + td->set_isochronous(); + } else { + if (direction == PacketID::IN) { + td->set_short_packet_detect(); + } + } - setup_packet* packet = reinterpret_cast(m_td_buffer_region->vaddr().as_ptr()); - packet->bmRequestType = 0x81; - packet->bRequest = 0x06; - packet->wValue = 0x2200; - packet->wIndex = 0x0; - packet->wLength = 8; + // 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(); + } - // Let's begin.... - setup_td->set_status(0x18800000); - setup_td->set_token(0x00E0002D); - setup_td->set_buffer_address(m_td_buffer_region->physical_page(0)->paddr().get()); + td->set_active(); + td->set_error_retry_counter(RETRY_COUNTER_RELOAD); - data_td->set_status(0x18800000); - data_td->set_token(0x00E80069); - data_td->set_buffer_address(m_td_buffer_region->physical_page(0)->paddr().get() + 16); + return td; +} - response_td->set_status(0x19800000); - response_td->set_token(0xFFE800E1); +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; - setup_td->insert_next_transfer_descriptor(data_td); - data_td->insert_next_transfer_descriptor(response_td); - response_td->terminate(); + // 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; + } - setup_td->print(); - data_td->print(); - response_td->print(); + current_td = create_transfer_descriptor(pipe, direction, packet_size); + if (current_td == nullptr) { + free_descriptor_chain(first_td); + return ENOMEM; + } - // Now let's (attempt) to attach to one of the queue heads - m_lowspeed_control_qh->attach_transfer_descriptor_chain(setup_td); + 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() @@ -390,34 +529,55 @@ void UHCIController::spawn_port_proc() // Reset the port port_data = read_portsc1(); write_portsc1(port_data | UHCI_PORTSC_PORT_RESET); - for (size_t i = 0; i < 50000; ++i) - IO::in8(0x80); + IO::delay(500); write_portsc1(port_data & ~UHCI_PORTSC_PORT_RESET); - for (size_t i = 0; i < 100000; ++i) - IO::in8(0x80); + IO::delay(500); write_portsc1(port_data & (~UHCI_PORTSC_PORT_ENABLE_CHANGED | ~UHCI_PORTSC_CONNECT_STATUS_CHANGED)); - } else { - dmesgln("UHCI: Device detach detected on Root Port 1!"); - } - port_data = read_portsc1(); - write_portsc1(port_data | UHCI_PORTSC_PORT_ENABLED); - dbgln("port should be enabled now: {:#04x}\n", read_portsc1()); - do_debug_transfer(); + 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()); + } else { + dmesgln("UHCI: Device detach detected on Root Port 1"); + } } } 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!"); - } else { - dmesgln("UHCI: Device detach detected on Root Port 2!"); - } + dmesgln("UHCI: Device attach detected on Root Port 2"); - UHCIController::the().write_portsc2( - UHCI_PORTSC_CONNECT_STATUS_CHANGED); + // 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()); + } else { + dmesgln("UHCI: Device detach detected on Root Port 2"); + } } } } @@ -428,14 +588,19 @@ void UHCIController::spawn_port_proc() void UHCIController::handle_irq(const RegisterState&) { + u32 status = read_usbsts(); + // Shared IRQ. Not ours! - if (!read_usbsts()) + if (!status) return; if constexpr (UHCI_DEBUG) { dbgln("UHCI: Interrupt happened!"); dbgln("Value of USBSTS: {:#04x}", read_usbsts()); } + + // Write back USBSTS to clear bits + write_usbsts(status); } } diff --git a/Kernel/Devices/USB/UHCIController.h b/Kernel/Devices/USB/UHCIController.h index 6bea0476f8..5c399db5db 100644 --- a/Kernel/Devices/USB/UHCIController.h +++ b/Kernel/Devices/USB/UHCIController.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -33,6 +35,8 @@ public: void do_debug_transfer(); + KResultOr submit_control_transfer(Transfer& transfer); + private: UHCIController(PCI::Address, PCI::ID); @@ -58,6 +62,11 @@ private: 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; @@ -78,7 +87,6 @@ private: OwnPtr m_framelist; OwnPtr m_qh_pool; OwnPtr m_td_pool; - OwnPtr m_td_buffer_region; }; } diff --git a/Kernel/Devices/USB/UHCIDescriptorTypes.h b/Kernel/Devices/USB/UHCIDescriptorTypes.h index 495fc79c82..7715bad4b4 100644 --- a/Kernel/Devices/USB/UHCIDescriptorTypes.h +++ b/Kernel/Devices/USB/UHCIDescriptorTypes.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace Kernel::USB { @@ -18,6 +19,21 @@ enum class PacketID : u8 { 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 // @@ -28,13 +44,13 @@ enum class PacketID : u8 { // struct QueueHead; struct alignas(16) TransferDescriptor final { - enum class LinkPointerBits : u32 { + enum LinkPointerBits { Terminate = 1, QHSelect = 2, DepthFlag = 4, }; - enum class StatusBits : u32 { + enum StatusBits { Reserved = (1 << 16), BitStuffError = (1 << 17), CRCTimeoutError = (1 << 18), @@ -42,12 +58,15 @@ struct alignas(16) TransferDescriptor final { BabbleDetected = (1 << 20), DataBufferError = (1 << 21), Stalled = (1 << 22), - Active = (1 << 23) + Active = (1 << 23), + ErrorMask = BitStuffError | CRCTimeoutError | NAKReceived | BabbleDetected | DataBufferError | Stalled }; - enum class ControlBits : u32 { + enum ControlBits { InterruptOnComplete = (1 << 24), IsochronousSelect = (1 << 25), + LowSpeedDevice = (1 << 26), + ShortPacketDetect = (1 << 29), }; TransferDescriptor() = delete; @@ -59,26 +78,56 @@ struct alignas(16) TransferDescriptor final { u32 link_ptr() const { return m_link_ptr; } u32 paddr() const { return m_paddr; } - u32 status() const { return (m_control_status >> 16) & 0xff; } + 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 & static_cast(StatusBits::Stalled); } - bool last_in_chain() const { return m_link_ptr & static_cast(LinkPointerBits::Terminate); } - bool active() const { return m_link_ptr & static_cast(StatusBits::Active); } + 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 |= static_cast(StatusBits::Active); + ctrl |= StatusBits::Active; m_control_status = ctrl; } void set_isochronous() { u32 ctrl = m_control_status; - ctrl |= static_cast(ControlBits::IsochronousSelect); + 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; } @@ -90,25 +139,36 @@ struct alignas(16) TransferDescriptor final { 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 |= static_cast(LinkPointerBits::QHSelect); + m_link_ptr |= LinkPointerBits::QHSelect; } void print() { - dbgln("UHCI: TD({}) @ {}: link_ptr={}, status={}, token={}, buffer_ptr={}", this, m_paddr, m_link_ptr, (u32)m_control_status, m_token, m_buffer_ptr); + 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({}) @ {}: link_ptr={}{}{}, status={}{}{}{}{}{}{}", + dbgln("UHCI: TD({:#04x}) @ {:#04x}: link_ptr={}{}{}, status={}{}{}{}{}{}{}", this, m_paddr, (last_in_chain()) ? "T " : "", @@ -144,7 +204,11 @@ struct alignas(16) TransferDescriptor final { void terminate() { m_link_ptr |= static_cast(LinkPointerBits::Terminate); } - void set_buffer_address(u32 buffer) { m_buffer_ptr = buffer; } + void set_buffer_address(Ptr32 buffer) + { + u8* buffer_address = &*buffer; + m_buffer_ptr = reinterpret_cast(buffer_address); + } // DEBUG FUNCTIONS! void set_token(u32 token) @@ -157,6 +221,14 @@ struct alignas(16) TransferDescriptor final { 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 @@ -214,7 +286,18 @@ struct alignas(16) QueueHead { { m_link_ptr = qh->paddr(); m_link_ptr |= static_cast(LinkPointerBits::QHSelect); - set_next_qh(qh); + } + + 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) @@ -230,6 +313,11 @@ struct alignas(16) QueueHead { 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() @@ -237,15 +325,28 @@ struct alignas(16) QueueHead { m_element_link_ptr = static_cast(LinkPointerBits::Terminate); } - // Clean the chain of transfer descriptors - void clean_chain() + void set_transfer(Transfer* transfer) { - // TODO + m_transfer = transfer; + } + + Transfer* transfer() + { + return m_transfer; } void print() { - dbgln("UHCI: QH({}) @ {}: link_ptr={}, element_link_ptr={}", this, m_paddr, m_link_ptr, (FlatPtr)m_element_link_ptr); + 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: @@ -258,6 +359,7 @@ private: 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? }; diff --git a/Kernel/Devices/USB/USBDescriptors.h b/Kernel/Devices/USB/USBDescriptors.h new file mode 100644 index 0000000000..0e8486bd8a --- /dev/null +++ b/Kernel/Devices/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/Devices/USB/USBDevice.cpp b/Kernel/Devices/USB/USBDevice.cpp new file mode 100644 index 0000000000..b13f568a3a --- /dev/null +++ b/Kernel/Devices/USB/USBDevice.cpp @@ -0,0 +1,104 @@ +/* + * 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 = adopt_own_if_nonnull(new Device(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++; + + return KSuccess; +} + +Device::~Device() +{ +} + +} diff --git a/Kernel/Devices/USB/USBDevice.h b/Kernel/Devices/USB/USBDevice.h new file mode 100644 index 0000000000..8b08d9552d --- /dev/null +++ b/Kernel/Devices/USB/USBDevice.h @@ -0,0 +1,58 @@ +/* + * 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: + enum class PortNumber : u8 { + Port1 = 0, + Port2 + }; + + enum class DeviceSpeed : u8 { + FullSpeed = 0, + LowSpeed + }; + +public: + static KResultOr> try_create(PortNumber, DeviceSpeed); + static Device* get(PortNumber); + + 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; } + +private: +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 + + 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 new file mode 100644 index 0000000000..4bc0769381 --- /dev/null +++ b/Kernel/Devices/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/Devices/USB/USBPipe.cpp b/Kernel/Devices/USB/USBPipe.cpp new file mode 100644 index 0000000000..9aae7d7968 --- /dev/null +++ b/Kernel/Devices/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 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 new file mode 100644 index 0000000000..ab01984451 --- /dev/null +++ b/Kernel/Devices/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/Devices/USB/USBRequest.h b/Kernel/Devices/USB/USBRequest.h new file mode 100644 index 0000000000..38f184de1f --- /dev/null +++ b/Kernel/Devices/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/Devices/USB/USBTransfer.cpp b/Kernel/Devices/USB/USBTransfer.cpp new file mode 100644 index 0000000000..c865f621af --- /dev/null +++ b/Kernel/Devices/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 adopt_ref_if_nonnull(new Transfer(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 new file mode 100644 index 0000000000..3360ea758f --- /dev/null +++ b/Kernel/Devices/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/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake index 342808ca9e..5e3936f351 100644 --- a/Meta/CMake/all_the_debug_macros.cmake +++ b/Meta/CMake/all_the_debug_macros.cmake @@ -178,6 +178,7 @@ set(UHCI_DEBUG ON) set(UHCI_VERBOSE_DEBUG ON) set(UPDATE_COALESCING_DEBUG ON) set(URL_PARSER_DEBUG ON) +set(USB_DEBUG ON) set(VFS_DEBUG ON) set(VIRTIO_DEBUG ON) set(VIRTUAL_CONSOLE_DEBUG ON)