From bf3c99ef23118d148a9dbde109bbf45b284e70cb Mon Sep 17 00:00:00 2001 From: b14ckcat Date: Sat, 15 Oct 2022 20:34:49 -0400 Subject: Kernel/USB: Refactor USB Pipe Decompose the current monolithic USBD Pipe interface into several subclasses, one for each pair of endpoint type & direction. This is to make it more clear what data and functionality belongs to which Pipe type, and prevent nonsensical things like trying to execute a control transfer on a non-control pipe. This is important, because the Pipe class is the interface by which USB device drivers will interact with the HCD, so the clearer and more explicit this interface is the better. --- Kernel/Bus/USB/USBDevice.cpp | 8 +-- Kernel/Bus/USB/USBDevice.h | 8 +-- Kernel/Bus/USB/USBHub.cpp | 8 +-- Kernel/Bus/USB/USBHub.h | 4 +- Kernel/Bus/USB/USBPipe.cpp | 128 ++++++++++++++++++++++++++++++------------- Kernel/Bus/USB/USBPipe.h | 84 ++++++++++++++++++++++++---- 6 files changed, 175 insertions(+), 65 deletions(-) (limited to 'Kernel/Bus') diff --git a/Kernel/Bus/USB/USBDevice.cpp b/Kernel/Bus/USB/USBDevice.cpp index 955da88ae7..53a7f2a78e 100644 --- a/Kernel/Bus/USB/USBDevice.cpp +++ b/Kernel/Bus/USB/USBDevice.cpp @@ -18,7 +18,7 @@ namespace Kernel::USB { ErrorOr> Device::try_create(USBController const& controller, u8 port, DeviceSpeed speed) { - auto pipe = TRY(Pipe::try_create_pipe(controller, Pipe::Type::Control, Pipe::Direction::Bidirectional, 0, 8, 0)); + auto pipe = TRY(ControlPipe::create(controller, 0, 8, 0)); auto device = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Device(controller, port, speed, move(pipe)))); auto sysfs_node = TRY(SysFSUSBDeviceInformation::create(*device)); device->m_sysfs_device_info_node = move(sysfs_node); @@ -26,7 +26,7 @@ ErrorOr> Device::try_create(USBController const& contr return device; } -Device::Device(USBController const& controller, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe) +Device::Device(USBController const& controller, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe) : m_device_port(port) , m_device_speed(speed) , m_address(0) @@ -35,7 +35,7 @@ Device::Device(USBController const& controller, u8 port, DeviceSpeed speed, Nonn { } -Device::Device(NonnullLockRefPtr controller, u8 address, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe) +Device::Device(NonnullLockRefPtr controller, u8 address, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe) : m_device_port(port) , m_device_speed(speed) , m_address(address) @@ -44,7 +44,7 @@ Device::Device(NonnullLockRefPtr controller, u8 address, u8 port, { } -Device::Device(Device const& device, NonnullOwnPtr default_pipe) +Device::Device(Device const& device, NonnullOwnPtr default_pipe) : m_device_port(device.port()) , m_device_speed(device.speed()) , m_address(device.address()) diff --git a/Kernel/Bus/USB/USBDevice.h b/Kernel/Bus/USB/USBDevice.h index 0eb5b20718..c1069b9939 100644 --- a/Kernel/Bus/USB/USBDevice.h +++ b/Kernel/Bus/USB/USBDevice.h @@ -36,8 +36,8 @@ public: static ErrorOr> try_create(USBController const&, u8, DeviceSpeed); - Device(USBController const&, u8, DeviceSpeed, NonnullOwnPtr default_pipe); - Device(Device const& device, NonnullOwnPtr default_pipe); + Device(USBController const&, u8, DeviceSpeed, NonnullOwnPtr default_pipe); + Device(Device const& device, NonnullOwnPtr default_pipe); virtual ~Device(); ErrorOr enumerate_device(); @@ -59,7 +59,7 @@ public: SysFSUSBDeviceInformation& sysfs_device_info_node(Badge) { return *m_sysfs_device_info_node; } protected: - Device(NonnullLockRefPtr controller, u8 address, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe); + Device(NonnullLockRefPtr controller, u8 address, u8 port, DeviceSpeed speed, NonnullOwnPtr default_pipe); u8 m_device_port { 0 }; // What port is this device attached to. NOTE: This is 1-based. DeviceSpeed m_device_speed; // What speed is this device running at @@ -72,7 +72,7 @@ protected: Vector m_configurations; // Configurations for this device NonnullLockRefPtr m_controller; - NonnullOwnPtr m_default_pipe; // Default communication pipe (endpoint0) used during enumeration + NonnullOwnPtr m_default_pipe; // Default communication pipe (endpoint0) used during enumeration private: IntrusiveListNode> m_hub_child_node; diff --git a/Kernel/Bus/USB/USBHub.cpp b/Kernel/Bus/USB/USBHub.cpp index bb80647ed2..629c00f299 100644 --- a/Kernel/Bus/USB/USBHub.cpp +++ b/Kernel/Bus/USB/USBHub.cpp @@ -18,25 +18,25 @@ namespace Kernel::USB { ErrorOr> Hub::try_create_root_hub(NonnullLockRefPtr controller, DeviceSpeed device_speed) { // NOTE: Enumeration does not happen here, as the controller must know what the device address is at all times during enumeration to intercept requests. - auto pipe = TRY(Pipe::try_create_pipe(controller, Pipe::Type::Control, Pipe::Direction::Bidirectional, 0, 8, 0)); + auto pipe = TRY(ControlPipe::create(controller, 0, 8, 0)); auto hub = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Hub(controller, device_speed, move(pipe)))); return hub; } ErrorOr> Hub::try_create_from_device(Device const& device) { - auto pipe = TRY(Pipe::try_create_pipe(device.controller(), Pipe::Type::Control, Pipe::Direction::Bidirectional, 0, device.device_descriptor().max_packet_size, device.address())); + auto pipe = TRY(ControlPipe::create(device.controller(), 0, device.device_descriptor().max_packet_size, device.address())); auto hub = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Hub(device, move(pipe)))); TRY(hub->enumerate_and_power_on_hub()); return hub; } -Hub::Hub(NonnullLockRefPtr controller, DeviceSpeed device_speed, NonnullOwnPtr default_pipe) +Hub::Hub(NonnullLockRefPtr controller, DeviceSpeed device_speed, NonnullOwnPtr default_pipe) : Device(move(controller), 1 /* Port 1 */, device_speed, move(default_pipe)) { } -Hub::Hub(Device const& device, NonnullOwnPtr default_pipe) +Hub::Hub(Device const& device, NonnullOwnPtr default_pipe) : Device(device, move(default_pipe)) { } diff --git a/Kernel/Bus/USB/USBHub.h b/Kernel/Bus/USB/USBHub.h index aaefe513d4..6108bf8828 100644 --- a/Kernel/Bus/USB/USBHub.h +++ b/Kernel/Bus/USB/USBHub.h @@ -96,9 +96,9 @@ public: private: // Root Hub constructor - Hub(NonnullLockRefPtr, DeviceSpeed, NonnullOwnPtr default_pipe); + Hub(NonnullLockRefPtr, DeviceSpeed, NonnullOwnPtr default_pipe); - Hub(Device const&, NonnullOwnPtr default_pipe); + Hub(Device const&, NonnullOwnPtr default_pipe); USBHubDescriptor m_hub_descriptor {}; diff --git a/Kernel/Bus/USB/USBPipe.cpp b/Kernel/Bus/USB/USBPipe.cpp index ab1cd8bb3b..ac4c79bc57 100644 --- a/Kernel/Bus/USB/USBPipe.cpp +++ b/Kernel/Bus/USB/USBPipe.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Jesse Buhagiar + * Copyright (c) 2022, blackcat * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,48 +13,33 @@ namespace Kernel::USB { -ErrorOr> Pipe::try_create_pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size, u8 poll_interval) -{ - auto dma_region = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB device DMA buffer"sv, Memory::Region::Access::ReadWrite)); - return adopt_nonnull_own_or_enomem(new (nothrow) Pipe(controller, type, direction, endpoint_address, max_packet_size, poll_interval, device_address, move(dma_region))); -} - -Pipe::Pipe(USBController const& controller, Type type, Pipe::Direction direction, u16 max_packet_size, NonnullOwnPtr dma_buffer) +Pipe::Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) : m_controller(controller) , m_type(type) , m_direction(direction) - , m_endpoint_address(0) + , m_device_address(device_address) + , m_endpoint_address(endpoint_address) , m_max_packet_size(max_packet_size) - , m_poll_interval(0) , m_data_toggle(false) , m_dma_buffer(move(dma_buffer)) { } -Pipe::Pipe(USBController const& controller, Type type, Direction direction, USBEndpointDescriptor& endpoint [[maybe_unused]], NonnullOwnPtr dma_buffer) - : m_controller(controller) - , m_type(type) - , m_direction(direction) - , m_dma_buffer(move(dma_buffer)) +ErrorOr> ControlPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size) { - // TODO: decode endpoint structure + auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB device DMA buffer"sv, Memory::Region::Access::ReadWrite)); + return adopt_nonnull_own_or_enomem(new (nothrow) ControlPipe(controller, endpoint_address, max_packet_size, device_address, move(dma_buffer))); } -Pipe::Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address, NonnullOwnPtr dma_buffer) - : m_controller(controller) - , 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) - , m_dma_buffer(move(dma_buffer)) +ControlPipe::ControlPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) + : Pipe(controller, Type::Control, Direction::Bidirectional, endpoint_address, max_packet_size, device_address, move(dma_buffer)) { } -ErrorOr Pipe::control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data) +ErrorOr ControlPipe::control_transfer(u8 request_type, u8 request, u16 value, u16 index, size_t length, void* data) { + VERIFY(length <= m_dma_buffer->size()); + MutexLocker lock(m_dma_buffer_lock); USBRequestData usb_request; @@ -67,7 +53,7 @@ ErrorOr Pipe::control_transfer(u8 request_type, u8 request, u16 value, u auto transfer = TRY(Transfer::try_create(*this, length, *m_dma_buffer)); transfer->set_setup_packet(usb_request); - dbgln_if(USB_DEBUG, "Pipe: Transfer allocated @ {}", transfer->buffer_physical()); + dbgln_if(USB_DEBUG, "ControlPipe: Transfer allocated @ {}", transfer->buffer_physical()); auto transfer_length = TRY(m_controller->submit_control_transfer(*transfer)); // TODO: Check transfer for completion and copy data from transfer buffer into data @@ -78,26 +64,90 @@ ErrorOr Pipe::control_transfer(u8 request_type, u8 request, u16 value, u return transfer_length; } -ErrorOr Pipe::bulk_transfer(u16 length, void* data) +ErrorOr> BulkInPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size) { + VERIFY(buffer_size >= max_packet_size); + auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); + return adopt_nonnull_own_or_enomem(new (nothrow) BulkInPipe(controller, endpoint_address, max_packet_size, device_address, move(dma_buffer))); +} + +BulkInPipe::BulkInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) + : Pipe(controller, Pipe::Type::Bulk, Direction::In, endpoint_address, max_packet_size, device_address, move(dma_buffer)) +{ +} + +ErrorOr BulkInPipe::bulk_in_transfer(size_t length, void* data) +{ + VERIFY(length <= m_dma_buffer->size()); + MutexLocker lock(m_dma_buffer_lock); size_t transfer_length = 0; + auto transfer = TRY(Transfer::try_create(*this, length, *m_dma_buffer)); - if (m_direction == Direction::In) { - dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer allocated @ {}", transfer->buffer_physical()); - transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); - memcpy(data, transfer->buffer().as_ptr(), min(length, transfer_length)); - dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer complete!"); - } else if (m_direction == Direction::Out) { - TRY(transfer->write_buffer(length, data)); - dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer allocated @ {}", transfer->buffer_physical()); - transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); - dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer complete!"); - } + dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer allocated @ {}", transfer->buffer_physical()); + transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); + memcpy(data, transfer->buffer().as_ptr(), min(length, transfer_length)); + dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer complete!"); return transfer_length; } +ErrorOr> BulkOutPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size) +{ + VERIFY(buffer_size >= max_packet_size); + auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); + return adopt_nonnull_own_or_enomem(new (nothrow) BulkOutPipe(controller, endpoint_address, max_packet_size, device_address, move(dma_buffer))); +} + +BulkOutPipe::BulkOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer) + : Pipe(controller, Type::Bulk, Direction::Out, endpoint_address, max_packet_size, device_address, move(dma_buffer)) + +{ +} + +ErrorOr BulkOutPipe::bulk_out_transfer(size_t length, void* data) +{ + VERIFY(length <= m_dma_buffer->size()); + + MutexLocker lock(m_dma_buffer_lock); + + size_t transfer_length = 0; + auto transfer = TRY(Transfer::try_create(*this, length, *m_dma_buffer)); + + TRY(transfer->write_buffer(length, data)); + dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer allocated @ {}", transfer->buffer_physical()); + transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer)); + dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer complete!"); + + return transfer_length; +} + +ErrorOr> InterruptInPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size) +{ + VERIFY(buffer_size >= max_packet_size); + auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); + return adopt_nonnull_own_or_enomem(new (nothrow) InterruptInPipe(controller, endpoint_address, max_packet_size, device_address, poll_interval, move(dma_buffer))); +} + +InterruptInPipe::InterruptInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_buffer) + : Pipe(controller, Type::Interrupt, Direction::In, endpoint_address, max_packet_size, device_address, move(dma_buffer)) + , m_poll_interval(poll_interval) +{ +} + +ErrorOr> InterruptOutPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size) +{ + VERIFY(buffer_size >= max_packet_size); + auto dma_buffer = TRY(MM.allocate_dma_buffer_pages(TRY(Memory::page_round_up(buffer_size)), "USB pipe DMA buffer"sv, Memory::Region::Access::ReadWrite)); + return adopt_nonnull_own_or_enomem(new (nothrow) InterruptOutPipe(controller, endpoint_address, max_packet_size, device_address, poll_interval, move(dma_buffer))); +} + +InterruptOutPipe::InterruptOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_buffer) + : Pipe(controller, Type::Interrupt, Direction::In, endpoint_address, max_packet_size, device_address, move(dma_buffer)) + , m_poll_interval(poll_interval) +{ +} + } diff --git a/Kernel/Bus/USB/USBPipe.h b/Kernel/Bus/USB/USBPipe.h index 73c423b2d2..578852dc0f 100644 --- a/Kernel/Bus/USB/USBPipe.h +++ b/Kernel/Bus/USB/USBPipe.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Jesse Buhagiar + * Copyright (c) 2022, blackcat * * SPDX-License-Identifier: BSD-2-Clause */ @@ -15,6 +16,7 @@ namespace Kernel::USB { class USBController; +class Transfer; // // A pipe is the logical connection between a memory buffer on the PC (host) and @@ -41,8 +43,6 @@ public: FullSpeed }; - static ErrorOr> try_create_pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size = PAGE_SIZE, u8 poll_interval = 0); - Type type() const { return m_type; } Direction direction() const { return m_direction; } DeviceSpeed device_speed() const { return m_speed; } @@ -50,23 +50,17 @@ public: 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; } - ErrorOr control_transfer(u8 request_type, u8 request, u16 value, u16 index, u16 length, void* data); - ErrorOr bulk_transfer(u16 length, void* data); - - Pipe(USBController const& controller, Type type, Direction direction, u16 max_packet_size, NonnullOwnPtr dma_buffer); - Pipe(USBController const& controller, Type type, Direction direction, USBEndpointDescriptor& endpoint, NonnullOwnPtr dma_buffer); - Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, u8 poll_interval, i8 device_address, NonnullOwnPtr dma_buffer); - -private: +protected: friend class Device; + Pipe(USBController const& controller, Type type, Direction direction, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); + NonnullLockRefPtr m_controller; Type m_type; @@ -76,11 +70,77 @@ private: 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 Mutex m_dma_buffer_lock { "USB pipe mutex"sv }; NonnullOwnPtr m_dma_buffer; }; + +class ControlPipe : public Pipe { +public: + static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size = PAGE_SIZE); + + ErrorOr control_transfer(u8 request_type, u8 request, u16 value, u16 index, size_t length, void* data); + +private: + ControlPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); +}; + +class BulkInPipe : public Pipe { +public: + static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size = PAGE_SIZE); + + ErrorOr bulk_in_transfer(size_t length, void* data); + +private: + BulkInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); +}; + +class BulkOutPipe : public Pipe { +public: + static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, size_t buffer_size = PAGE_SIZE); + + ErrorOr bulk_out_transfer(size_t length, void* data); + +private: + BulkOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, NonnullOwnPtr dma_buffer); +}; + +class InterruptInPipe : public Pipe { +public: + static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size = PAGE_SIZE); + + u16 poll_interval() const { return m_poll_interval; } + +private: + InterruptInPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_pool); + + u16 m_poll_interval; +}; + +class InterruptOutPipe : public Pipe { +public: + static ErrorOr> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size = PAGE_SIZE); + + u16 poll_interval() const { return m_poll_interval; } + +private: + InterruptOutPipe(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, NonnullOwnPtr dma_pool); + + u16 m_poll_interval; +}; + +class IsochronousInPipe : public Pipe { + // TODO +public: +private: +}; + +class IsochronousOutPipe : public Pipe { + // TODO +public: +private: +}; + } -- cgit v1.2.3