USB: Further Implement USB Structures

These are the actual structures that allow USB to work (i.e the ones
actually defined in the specification). This should provide us enough
of a baseline implementation that we can build on to support
different types of USB device.
This commit is contained in:
Jesse Buhagiar 2021-02-27 15:10:37 +11:00 committed by Ali Mohammad Pur
parent e044a3e428
commit 06f1edb516
16 changed files with 1037 additions and 89 deletions

View file

@ -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

View file

@ -302,6 +302,10 @@
#cmakedefine01 UHCI_VERBOSE_DEBUG
#endif
#ifndef USB_DEBUG
#cmakedefine01 USB_DEBUG
#endif
#ifndef VFS_DEBUG
#cmakedefine01 VFS_DEBUG
#endif

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
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;
};
}

View file

@ -9,6 +9,7 @@
#include <Kernel/CommandLine.h>
#include <Kernel/Debug.h>
#include <Kernel/Devices/USB/UHCIController.h>
#include <Kernel/Devices/USB/USBRequest.h>
#include <Kernel/Process.h>
#include <Kernel/StdLib.h>
#include <Kernel/Time/TimeManagement.h>
@ -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<u8>(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<setup_packet*>(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<u8>& 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<FlatPtr>::addition_would_overflow(reinterpret_cast<FlatPtr>(&*buffer_address), byte_count))
return EOVERFLOW;
auto buffer_pointer = Ptr32<u8>(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<size_t> 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<u8>(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);
}
}

View file

@ -11,6 +11,8 @@
#include <AK/NonnullOwnPtr.h>
#include <Kernel/Devices/USB/UHCIDescriptorTypes.h>
#include <Kernel/Devices/USB/USBDevice.h>
#include <Kernel/Devices/USB/USBTransfer.h>
#include <Kernel/IO.h>
#include <Kernel/PCI/Device.h>
#include <Kernel/Process.h>
@ -33,6 +35,8 @@ public:
void do_debug_transfer();
KResultOr<size_t> 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<u8>& 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<Region> m_framelist;
OwnPtr<Region> m_qh_pool;
OwnPtr<Region> m_td_pool;
OwnPtr<Region> m_td_buffer_region;
};
}

View file

@ -9,6 +9,7 @@
#include <AK/OwnPtr.h>
#include <AK/Ptr32.h>
#include <AK/Types.h>
#include <Kernel/Devices/USB/USBTransfer.h>
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<u32>(StatusBits::Stalled); }
bool last_in_chain() const { return m_link_ptr & static_cast<u32>(LinkPointerBits::Terminate); }
bool active() const { return m_link_ptr & static_cast<u32>(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<u32>(StatusBits::Active);
ctrl |= StatusBits::Active;
m_control_status = ctrl;
}
void set_isochronous()
{
u32 ctrl = m_control_status;
ctrl |= static_cast<u32>(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<u32>(pid); }
void link_queue_head(u32 qh_paddr)
{
m_link_ptr = qh_paddr;
m_link_ptr |= static_cast<u32>(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<u32>(LinkPointerBits::Terminate); }
void set_buffer_address(u32 buffer) { m_buffer_ptr = buffer; }
void set_buffer_address(Ptr32<u8> buffer)
{
u8* buffer_address = &*buffer;
m_buffer_ptr = reinterpret_cast<uintptr_t>(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<u32>(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<u32>(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<u32>(LinkPointerBits::Terminate); }
void terminate_element_link_ptr()
@ -237,15 +325,28 @@ struct alignas(16) QueueHead {
m_element_link_ptr = static_cast<u32>(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<QueueHead> m_next_qh { nullptr }; // Next QH
Ptr32<QueueHead> m_prev_qh { nullptr }; // Previous QH
Ptr32<TransferDescriptor> m_first_td { nullptr }; // Pointer to first TD
Ptr32<Transfer> m_transfer { nullptr }; // Pointer to transfer linked to this queue head
bool m_in_use { false }; // Is this QH currently in use?
};

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
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;
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <Kernel/Devices/USB/UHCIController.h>
#include <Kernel/Devices/USB/USBDescriptors.h>
#include <Kernel/Devices/USB/USBDevice.h>
#include <Kernel/Devices/USB/USBRequest.h>
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<NonnullOwnPtr<Device>> 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<Pipe> 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()
{
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Devices/USB/USBPipe.h>
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<NonnullOwnPtr<Device>> try_create(PortNumber, DeviceSpeed);
static Device* get(PortNumber);
Device(PortNumber, DeviceSpeed, NonnullOwnPtr<Pipe> 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<Pipe> m_default_pipe; // Default communication pipe (endpoint0) used during enumeration
};
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Devices/USB/USBDescriptors.h>
#include <Kernel/Devices/USB/USBPipe.h>
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;
};
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Devices/USB/PacketTypes.h>
#include <Kernel/Devices/USB/UHCIController.h>
#include <Kernel/Devices/USB/USBPipe.h>
#include <Kernel/Devices/USB/USBTransfer.h>
namespace Kernel::USB {
KResultOr<NonnullOwnPtr<Pipe>> 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<size_t> 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<u8*>(data), transfer->buffer().as_ptr() + sizeof(USBRequestData), length);
dbgln_if(USB_DEBUG, "Pipe: Control Transfer complete!");
return transfer_length;
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Devices/USB/USBDescriptors.h>
#include <Kernel/VM/Region.h>
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<NonnullOwnPtr<Pipe>> 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<size_t> 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
};
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
//
// 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;

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Devices/USB/USBTransfer.h>
#include <Kernel/VM/MemoryManager.h>
namespace Kernel::USB {
RefPtr<Transfer> 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<USBRequestData*>(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;
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <Kernel/Devices/USB/PacketTypes.h>
#include <Kernel/Devices/USB/USBPipe.h>
#include <Kernel/VM/ContiguousVMObject.h>
#include <Kernel/VM/PhysicalPage.h>
#include <Kernel/VM/Region.h>
// TODO: Callback stuff in this class please!
namespace Kernel::USB {
class Transfer : public RefCounted<Transfer> {
public:
static RefPtr<Transfer> 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<Region> 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?
};
}

View file

@ -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)