Kernel: Add basic support for USB hubs
This is still buggy and some hubs lead to usb transaction errors. I'll have to debug this but this shouldn't prevent any already working device from working
This commit is contained in:
40
kernel/kernel/USB/Controller.cpp
Normal file
40
kernel/kernel/USB/Controller.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <kernel/USB/Controller.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
USBController::USBController()
|
||||
{
|
||||
for (auto& count : m_hubs_to_init_per_tier)
|
||||
count = 0;
|
||||
m_hubs_to_init_per_tier[0] = 1;
|
||||
}
|
||||
|
||||
uint8_t USBController::current_hub_init_tier() const
|
||||
{
|
||||
SpinLockGuard _(m_hub_init_lock);
|
||||
return m_current_hub_init_tier;
|
||||
}
|
||||
|
||||
void USBController::register_hub_to_init(uint8_t tier)
|
||||
{
|
||||
ASSERT(tier >= 1);
|
||||
ASSERT(tier <= m_hubs_to_init_per_tier.size());
|
||||
|
||||
SpinLockGuard _(m_hub_init_lock);
|
||||
m_hubs_to_init_per_tier[tier]++;
|
||||
if (tier < m_current_hub_init_tier)
|
||||
m_current_hub_init_tier = tier;
|
||||
}
|
||||
|
||||
void USBController::mark_hub_init_done(uint8_t tier)
|
||||
{
|
||||
ASSERT(tier < m_hubs_to_init_per_tier.size());
|
||||
|
||||
SpinLockGuard _(m_hub_init_lock);
|
||||
m_hubs_to_init_per_tier[tier]--;
|
||||
if (tier == m_current_hub_init_tier && m_hubs_to_init_per_tier[tier] == 0)
|
||||
m_current_hub_init_tier++;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/USB/Device.h>
|
||||
#include <kernel/USB/HID/HIDDriver.h>
|
||||
#include <kernel/USB/Hub/HubDriver.h>
|
||||
#include <kernel/USB/MassStorage/MassStorageDriver.h>
|
||||
|
||||
#define USB_DUMP_DESCRIPTORS 0
|
||||
@@ -107,8 +108,26 @@ namespace Kernel
|
||||
dprintln_if(DEBUG_USB, "Found CommunicationAndCDCControl device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case USB::DeviceBaseClass::Hub:
|
||||
dprintln_if(DEBUG_USB, "Found Hub device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
{
|
||||
if (auto ret = BAN::UniqPtr<USBHubDriver>::create(*this, m_descriptor); !ret.is_error())
|
||||
TRY(m_class_drivers.push_back(ret.release_value()));
|
||||
else
|
||||
{
|
||||
dwarnln("Failed to create USB Hub: {}", ret.error());
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
if (auto ret = m_class_drivers[0]->initialize(); ret.is_error())
|
||||
{
|
||||
dwarnln("Failed to initialize USB Hub: {}", ret.error());
|
||||
m_class_drivers.clear();
|
||||
return ret.release_error();
|
||||
}
|
||||
|
||||
dprintln_if(DEBUG_USB, "Successfully initialized USB hub");
|
||||
|
||||
return {};
|
||||
}
|
||||
case USB::DeviceBaseClass::BillboardDeviceClass:
|
||||
dprintln_if(DEBUG_USB, "Found BillboardDeviceClass device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
|
||||
451
kernel/kernel/USB/Hub/HubDriver.cpp
Normal file
451
kernel/kernel/USB/Hub/HubDriver.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/USB/Hub/Definitions.h>
|
||||
#include <kernel/USB/Hub/HubDriver.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
USBHubDriver::USBHubDriver(USBDevice& device, const USBDevice::DeviceDescriptor& desciptor)
|
||||
: m_device(device)
|
||||
, m_descriptor(desciptor)
|
||||
{}
|
||||
|
||||
USBHubDriver::~USBHubDriver()
|
||||
{
|
||||
m_running = false;
|
||||
m_changed_port_blocker.unblock();
|
||||
while (m_port_updater)
|
||||
continue;
|
||||
|
||||
for (const auto info : m_ports)
|
||||
{
|
||||
if (info.slot == 0)
|
||||
continue;
|
||||
m_device.deinitialize_device_slot(info.slot);
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBHubDriver::initialize()
|
||||
{
|
||||
#if DEBUG_USB_HUB
|
||||
switch (m_device.speed_class())
|
||||
{
|
||||
case USB::SpeedClass::LowSpeed:
|
||||
dprintln("configuring low speed hub");
|
||||
break;
|
||||
case USB::SpeedClass::FullSpeed:
|
||||
dprintln("configuring full speed hub");
|
||||
break;
|
||||
case USB::SpeedClass::HighSpeed:
|
||||
dprintln("configuring high speed hub");
|
||||
break;
|
||||
case USB::SpeedClass::SuperSpeed:
|
||||
dprintln("configuring super speed hub");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_data_region = TRY(DMARegion::create(PAGE_SIZE));
|
||||
|
||||
if (m_descriptor.configurations.empty())
|
||||
{
|
||||
dwarnln("USB hub does not have any configurations");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
const auto& configuration = m_descriptor.configurations[0];
|
||||
|
||||
{
|
||||
dprintln_if(DEBUG_USB_HUB, "setting configuration");
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Standard | USB::RequestType::Device;
|
||||
request.bRequest = USB::SET_CONFIGURATION;
|
||||
request.wValue = configuration.desciptor.bConfigurationValue;
|
||||
request.wIndex = 0;
|
||||
request.wLength = 0;
|
||||
TRY(m_device.send_request(request, 0));
|
||||
dprintln_if(DEBUG_USB_HUB, " -> done");
|
||||
}
|
||||
|
||||
if (configuration.interfaces.empty())
|
||||
{
|
||||
dwarnln("USB hub does not have any interfaces");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
const auto* interface = &configuration.interfaces[0];
|
||||
|
||||
// High speed hubs may have alternate multi tt interface
|
||||
if (m_device.speed_class() == USB::SpeedClass::HighSpeed)
|
||||
{
|
||||
for (size_t i = 0; i < configuration.interfaces.size(); i++)
|
||||
{
|
||||
if (configuration.interfaces[i].descriptor.bInterfaceProtocol != 2)
|
||||
continue;
|
||||
interface = &configuration.interfaces[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (interface->descriptor.bAlternateSetting != 0)
|
||||
{
|
||||
dprintln_if(DEBUG_USB_HUB, "enabling multi tt interface");
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Standard | USB::RequestType::Interface;
|
||||
request.bRequest = USB::SET_INTERFACE;
|
||||
request.wValue = interface->descriptor.bAlternateSetting;
|
||||
request.wIndex = interface->descriptor.iInterface;
|
||||
request.wLength = 0;
|
||||
TRY(m_device.send_request(request, 0));
|
||||
dprintln_if(DEBUG_USB_HUB, " -> done");
|
||||
}
|
||||
|
||||
m_is_multi_tt = (interface->descriptor.bInterfaceProtocol == 2);
|
||||
}
|
||||
|
||||
if (interface->endpoints.size() != 1)
|
||||
{
|
||||
dwarnln("USB hub has {} endpoints", interface->endpoints.size());
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
const auto& endpoint = interface->endpoints[0];
|
||||
|
||||
m_is_usb3 = (m_device.speed_class() == USB::SpeedClass::SuperSpeed);
|
||||
m_is_usb2 = !m_is_usb3;
|
||||
ASSERT(m_is_usb2 ^ m_is_usb3);
|
||||
|
||||
// Set multi tt to false until we know tt think time
|
||||
dprintln_if(DEBUG_USB_HUB, "configuring endpoint with 4 ports");
|
||||
TRY(m_device.configure_endpoint(endpoint.descriptor, {
|
||||
.number_of_ports = m_port_count,
|
||||
.multi_tt = false,
|
||||
.tt_think_time = 0
|
||||
}));
|
||||
dprintln_if(DEBUG_USB_HUB, " -> done");
|
||||
|
||||
// USB 3 devices use route_string and hub depth for routing packets
|
||||
if (m_is_usb3)
|
||||
{
|
||||
dprintln_if(DEBUG_USB_HUB, "setting hub depth");
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Device;
|
||||
request.bRequest = 12; // SET_HUB_DEPTH
|
||||
request.wValue = m_device.depth();
|
||||
request.wIndex = 0;
|
||||
request.wLength = 0;
|
||||
TRY(m_device.send_request(request, 0));
|
||||
dprintln_if(DEBUG_USB_HUB, " -> done");
|
||||
}
|
||||
|
||||
uint8_t tt_think_time = 0;
|
||||
|
||||
// Hub descriptor has to be requested after device is configured
|
||||
{
|
||||
dprintln_if(DEBUG_USB_HUB, "getting hub descriptor");
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Device;
|
||||
request.bRequest = USB::Request::GET_DESCRIPTOR;
|
||||
request.wValue = m_is_usb3 ? 0x2A00 : 0x2900;
|
||||
request.wIndex = 0;
|
||||
request.wLength = 8;
|
||||
if (TRY(m_device.send_request(request, m_data_region->paddr())) < 8)
|
||||
{
|
||||
dwarnln("USB hub did not respond with full hub descriptor");
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
dprintln_if(DEBUG_USB_HUB, " -> done");
|
||||
|
||||
const auto& hub_descriptor = *reinterpret_cast<USBHub::HubDescriptor*>(m_data_region->vaddr());
|
||||
|
||||
m_port_count = hub_descriptor.bNbrPorts;
|
||||
|
||||
if (m_device.speed_class() == USB::SpeedClass::HighSpeed)
|
||||
tt_think_time = (hub_descriptor.wHubCharacteristics >> 5) & 0x03;
|
||||
}
|
||||
|
||||
if (m_port_count > 31)
|
||||
{
|
||||
dwarnln("USB hubs only support up to 31 ports");
|
||||
m_port_count = 31;
|
||||
}
|
||||
|
||||
TRY(m_ports.resize(m_port_count));
|
||||
|
||||
dprintln_if(DEBUG_USB_HUB, "re-configuring endpoint with {} ports", m_port_count);
|
||||
TRY(m_device.configure_endpoint(endpoint.descriptor, {
|
||||
.number_of_ports = m_port_count,
|
||||
.multi_tt = m_is_multi_tt,
|
||||
.tt_think_time = tt_think_time
|
||||
}));
|
||||
dprintln_if(DEBUG_USB_HUB, " -> done");
|
||||
|
||||
m_endpoint_id = (endpoint.descriptor.bEndpointAddress & 0x0F) * 2 + !!(endpoint.descriptor.bEndpointAddress & 0x80);
|
||||
m_device.send_data_buffer(m_endpoint_id, m_data_region->paddr() + sizeof(USBHub::PortStatus), m_port_count / 8 + 1);
|
||||
|
||||
// Reset all ports in powered off state
|
||||
for (size_t port_id = 1; port_id <= m_port_count; port_id++)
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Other;
|
||||
request.bRequest = USB::Request::GET_STATUS;
|
||||
request.wValue = 0;
|
||||
request.wIndex = port_id;
|
||||
request.wLength = sizeof(USBHub::PortStatus);
|
||||
auto result = m_device.send_request(request, m_data_region->paddr());
|
||||
if (result.is_error() || result.value() != sizeof(USBHub::PortStatus))
|
||||
{
|
||||
dwarnln("Failed to get port {} status");
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& port_status = *reinterpret_cast<volatile USBHub::PortStatus*>(m_data_region->vaddr());
|
||||
|
||||
const bool is_powered =
|
||||
(m_is_usb3 && port_status.wPortStatus.usb3.power) ||
|
||||
(m_is_usb2 && port_status.wPortStatus.usb2.power);
|
||||
|
||||
if (!is_powered)
|
||||
if (auto ret = set_port_feature(port_id, USBHub::PORT_POWER); ret.is_error())
|
||||
dwarnln("Failed to power on USB hub port {}: {}", port_id, ret.error());
|
||||
|
||||
dprintln_if(DEBUG_USB_HUB, "port {}, status {4H}, changed {4H}",
|
||||
port_id,
|
||||
*(uint16_t*)(m_data_region->vaddr() + 0),
|
||||
*(uint16_t*)(m_data_region->vaddr() + 2)
|
||||
);
|
||||
|
||||
m_changed_ports |= 1u << port_id;
|
||||
}
|
||||
|
||||
m_port_updater = Process::create_kernel([](void* data) { reinterpret_cast<USBHubDriver*>(data)->port_updater_task(); }, this);
|
||||
if (m_port_updater == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBHubDriver::clear_port_feature(uint8_t port, uint8_t feature)
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Other;
|
||||
request.bRequest = USB::Request::CLEAR_FEATURE;
|
||||
request.wValue = feature;
|
||||
request.wIndex = port;
|
||||
request.wLength = 0;
|
||||
TRY(m_device.send_request(request, 0));
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> USBHubDriver::set_port_feature(uint8_t port, uint8_t feature)
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::HostToDevice | USB::RequestType::Class | USB::RequestType::Other;
|
||||
request.bRequest = USB::Request::SET_FEATURE;
|
||||
request.wValue = feature;
|
||||
request.wIndex = port;
|
||||
request.wLength = 0;
|
||||
TRY(m_device.send_request(request, 0));
|
||||
return {};
|
||||
}
|
||||
|
||||
void USBHubDriver::port_updater_task()
|
||||
{
|
||||
m_device.register_hub_to_init();
|
||||
|
||||
while (m_running && !m_device.can_start_hub_init())
|
||||
Processor::yield();
|
||||
|
||||
uint64_t last_port_update_ms = SystemTimer::get().ms_since_boot();
|
||||
while (m_running)
|
||||
{
|
||||
uint8_t changed_port = 0xFF;
|
||||
|
||||
{
|
||||
const auto temp = m_changed_ports.exchange(0);
|
||||
if (temp == 0)
|
||||
{
|
||||
// If there has been no changed ports in 100ms, this hub has initialized its devices
|
||||
if (!m_is_init_done && SystemTimer::get().ms_since_boot() - last_port_update_ms >= 100)
|
||||
{
|
||||
m_device.mark_hub_init_done();
|
||||
m_is_init_done = true;
|
||||
}
|
||||
|
||||
m_changed_port_blocker.block_with_timeout_ms(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
last_port_update_ms = SystemTimer::get().ms_since_boot();
|
||||
|
||||
for (size_t i = 0; i <= m_ports.size(); i++)
|
||||
{
|
||||
if (!(temp & (1u << i)))
|
||||
continue;
|
||||
changed_port = i;
|
||||
break;
|
||||
}
|
||||
|
||||
m_changed_ports |= temp & ~(1u << changed_port);
|
||||
}
|
||||
|
||||
if (changed_port == 0)
|
||||
{
|
||||
dprintln_if(DEBUG_USB_HUB, "TODO: USB Hub changed");
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
USBDeviceRequest request;
|
||||
request.bmRequestType = USB::RequestType::DeviceToHost | USB::RequestType::Class | USB::RequestType::Other;
|
||||
request.bRequest = USB::Request::GET_STATUS;
|
||||
request.wValue = 0;
|
||||
request.wIndex = changed_port;
|
||||
request.wLength = sizeof(USBHub::PortStatus);
|
||||
auto result = m_device.send_request(request, m_data_region->paddr());
|
||||
if (result.is_error() || result.value() != sizeof(USBHub::PortStatus))
|
||||
{
|
||||
dwarnln("Failed to get port {} status", changed_port);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const USBHub::PortStatus port_status {
|
||||
.wPortStatus = { .raw = reinterpret_cast<volatile USBHub::PortStatus*>(m_data_region->vaddr())->wPortStatus.raw },
|
||||
.wPortChanged = { .raw = reinterpret_cast<volatile USBHub::PortStatus*>(m_data_region->vaddr())->wPortChanged.raw },
|
||||
};
|
||||
|
||||
if (port_status.wPortChanged.connection)
|
||||
{
|
||||
(void)clear_port_feature(changed_port, USBHub::C_PORT_CONNECTION);
|
||||
|
||||
if (port_status.wPortStatus.connection)
|
||||
{
|
||||
// USB 2 devices have to be reset to enter enabled state
|
||||
if (auto ret = set_port_feature(changed_port, USBHub::PORT_RESET); ret.is_error())
|
||||
dwarnln("Failed to reset USB hub port {}: {}", changed_port, ret.error());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup port on disconnection
|
||||
auto& port_info = m_ports[changed_port - 1];
|
||||
if (port_info.slot != 0)
|
||||
m_device.deinitialize_device_slot(port_info.slot);
|
||||
port_info = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (port_status.wPortChanged.reset)
|
||||
{
|
||||
// If this is USB3 device and the port is not in enabled state don't
|
||||
// clear reset changed so hub will trigger another port changed interrupt
|
||||
if (!port_status.wPortStatus.connection || !m_is_usb3 || port_status.wPortStatus.enable)
|
||||
(void)clear_port_feature(changed_port, USBHub::C_PORT_RESET);
|
||||
}
|
||||
|
||||
if (port_status.wPortChanged.over_current)
|
||||
{
|
||||
(void)clear_port_feature(changed_port, USBHub::C_PORT_OVER_CURRENT);
|
||||
dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub port over current change");
|
||||
}
|
||||
|
||||
if (m_is_usb2)
|
||||
{
|
||||
if (port_status.wPortChanged.usb2.enable)
|
||||
(void)clear_port_feature(changed_port, USBHub::C_PORT_ENABLE);
|
||||
|
||||
if (port_status.wPortChanged.usb2.suspend)
|
||||
{
|
||||
(void)clear_port_feature(changed_port, USBHub::C_PORT_SUSPEND);
|
||||
dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub port suspend change");
|
||||
}
|
||||
}
|
||||
else if (m_is_usb3)
|
||||
{
|
||||
if (port_status.wPortChanged.usb3.link_state)
|
||||
(void)clear_port_feature(changed_port, USBHub::C_PORT_LINK_STATE);
|
||||
|
||||
if (port_status.wPortChanged.usb3.bh_reset)
|
||||
{
|
||||
(void)clear_port_feature(changed_port, USBHub::C_BH_PORT_RESET);
|
||||
dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub bh port reset change");
|
||||
}
|
||||
|
||||
if (port_status.wPortChanged.usb3.config_error)
|
||||
{
|
||||
(void)clear_port_feature(changed_port, USBHub::C_PORT_CONFIG_ERROR);
|
||||
dwarnln_if(DEBUG_USB_HUB, "TODO: USB hub port config error chage");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Initialize new devices that have not failed initialization
|
||||
if (port_status.wPortStatus.enable && !m_ports[changed_port - 1].slot && !m_ports[changed_port - 1].failed)
|
||||
{
|
||||
USB::SpeedClass speed_class;
|
||||
if (m_is_usb3)
|
||||
speed_class = USB::SpeedClass::SuperSpeed;
|
||||
else if (port_status.wPortStatus.usb2.low_speed)
|
||||
speed_class = USB::SpeedClass::LowSpeed;
|
||||
else if (port_status.wPortStatus.usb2.high_speed)
|
||||
speed_class = USB::SpeedClass::HighSpeed;
|
||||
else
|
||||
speed_class = USB::SpeedClass::FullSpeed;
|
||||
|
||||
#if DEBUG_USB_HUB
|
||||
const char* speed_str = "<invalid>";
|
||||
switch (speed_class)
|
||||
{
|
||||
case USB::SpeedClass::LowSpeed: speed_str = "low"; break;
|
||||
case USB::SpeedClass::FullSpeed: speed_str = "full"; break;
|
||||
case USB::SpeedClass::HighSpeed: speed_str = "high"; break;
|
||||
case USB::SpeedClass::SuperSpeed: speed_str = "super"; break;
|
||||
}
|
||||
dprintln("Initializing {} speed device on hub port {}", speed_str, changed_port);
|
||||
#endif
|
||||
|
||||
auto result = m_device.initialize_device_on_hub_port(changed_port, speed_class);
|
||||
if (!result.is_error())
|
||||
m_ports[changed_port - 1].slot = result.value();
|
||||
else
|
||||
{
|
||||
m_ports[changed_port - 1].failed = true;
|
||||
dwarnln("Failed to initialize USB hub port {}: {}", changed_port, result.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_port_updater = nullptr;
|
||||
}
|
||||
|
||||
void USBHubDriver::handle_stall(uint8_t endpoint_id)
|
||||
{
|
||||
(void)endpoint_id;
|
||||
dwarnln("TODO: USB hub handle stall");
|
||||
}
|
||||
|
||||
void USBHubDriver::handle_input_data(size_t byte_count, uint8_t endpoint_id)
|
||||
{
|
||||
if (endpoint_id != m_endpoint_id)
|
||||
return;
|
||||
|
||||
BAN::ScopeGuard query_changes([&] {
|
||||
m_device.send_data_buffer(m_endpoint_id, m_data_region->paddr() + sizeof(USBHub::PortStatus), m_port_count / 8 + 1);
|
||||
});
|
||||
|
||||
if (m_ports.size() / 8 + 1 < byte_count)
|
||||
byte_count = m_ports.size() / 8 + 1;
|
||||
|
||||
uint32_t new_ports = 0;
|
||||
const auto* bitmap = reinterpret_cast<const uint8_t*>(m_data_region->vaddr() + sizeof(USBHub::PortStatus));
|
||||
for (size_t i = 0; i < byte_count; i++)
|
||||
new_ports |= static_cast<uint32_t>(bitmap[i]) << (i * 8);
|
||||
|
||||
if (new_ports)
|
||||
{
|
||||
m_changed_ports |= new_ports;
|
||||
m_changed_port_blocker.unblock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -306,17 +306,28 @@ namespace Kernel
|
||||
// attached ports
|
||||
m_port_changed = true;
|
||||
|
||||
uint64_t last_port_update = SystemTimer::get().ms_since_boot();
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
bool expected { true };
|
||||
while (!m_port_changed.compare_exchange(expected, false))
|
||||
{
|
||||
// If there has been no port activity in 100 ms, mark root hub as initialized
|
||||
if (!m_ports_initialized && SystemTimer::get().ms_since_boot() - last_port_update >= 100)
|
||||
{
|
||||
mark_hub_init_done(0);
|
||||
m_ports_initialized = true;
|
||||
}
|
||||
|
||||
m_port_thread_blocker.block_with_timeout_ms(100);
|
||||
expected = true;
|
||||
}
|
||||
}
|
||||
|
||||
last_port_update = SystemTimer::get().ms_since_boot();;
|
||||
|
||||
for (size_t i = 0; i < m_ports.size(); i++)
|
||||
{
|
||||
auto& my_port = m_ports[i];
|
||||
@@ -364,7 +375,9 @@ namespace Kernel
|
||||
}
|
||||
|
||||
const uint8_t speed_id = (op_port.portsc >> XHCI::PORTSC::PORT_SPEED_SHIFT) & XHCI::PORTSC::PORT_SPEED_MASK;
|
||||
if (auto ret = initialize_device(i + 1, speed_id_to_class(speed_id)); !ret.is_error())
|
||||
if (auto temp = speed_class_to_id(speed_id_to_class(speed_id)); temp != speed_id)
|
||||
dwarnln("FIXME: Using incorrect speed id, {} instead of {}", temp, speed_id);
|
||||
if (auto ret = initialize_device(i + 1, 0, speed_id_to_class(speed_id), nullptr, 0); !ret.is_error())
|
||||
my_port.slot_id = ret.value();
|
||||
else
|
||||
{
|
||||
@@ -378,7 +391,7 @@ namespace Kernel
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<uint8_t> XHCIController::initialize_device(uint32_t route_string, USB::SpeedClass speed_class)
|
||||
BAN::ErrorOr<uint8_t> XHCIController::initialize_device(uint32_t route_string, uint8_t depth, USB::SpeedClass speed_class, XHCIDevice* parent_hub, uint8_t parent_port_id)
|
||||
{
|
||||
XHCI::TRB enable_slot { .enable_slot_command {} };
|
||||
enable_slot.enable_slot_command.trb_type = XHCI::TRBType::EnableSlotCommand;
|
||||
@@ -404,9 +417,12 @@ namespace Kernel
|
||||
#endif
|
||||
|
||||
const XHCIDevice::Info info {
|
||||
.parent_hub = parent_hub,
|
||||
.parent_port_id = parent_port_id,
|
||||
.speed_class = speed_class,
|
||||
.slot_id = slot_id,
|
||||
.route_string = route_string,
|
||||
.depth = depth
|
||||
};
|
||||
|
||||
m_slots[slot_id - 1] = TRY(XHCIDevice::create(*this, info));
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Kernel
|
||||
}
|
||||
|
||||
XHCIDevice::XHCIDevice(XHCIController& controller, const Info& info)
|
||||
: USBDevice(info.speed_class)
|
||||
: USBDevice(controller, info.speed_class, info.depth)
|
||||
, m_controller(controller)
|
||||
, m_info(info)
|
||||
{}
|
||||
@@ -34,6 +34,7 @@ namespace Kernel
|
||||
{
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
|
||||
bool is_ls_or_fs_device_on_hs_hub = false;
|
||||
|
||||
m_endpoints[0].max_packet_size = 0;
|
||||
switch (m_speed_class)
|
||||
@@ -41,6 +42,7 @@ namespace Kernel
|
||||
case USB::SpeedClass::LowSpeed:
|
||||
case USB::SpeedClass::FullSpeed:
|
||||
m_endpoints[0].max_packet_size = 8;
|
||||
is_ls_or_fs_device_on_hs_hub = m_info.parent_hub && (m_info.parent_hub->speed_class() == USB::SpeedClass::HighSpeed);
|
||||
break;
|
||||
case USB::SpeedClass::HighSpeed:
|
||||
m_endpoints[0].max_packet_size = 64;
|
||||
@@ -72,7 +74,12 @@ namespace Kernel
|
||||
slot_context.context_entries = 1;
|
||||
slot_context.interrupter_target = 0;
|
||||
slot_context.speed = m_controller.speed_class_to_id(m_info.speed_class);
|
||||
// FIXME: 4.5.2 hub
|
||||
if (is_ls_or_fs_device_on_hs_hub)
|
||||
{
|
||||
slot_context.parent_hub_slot_id = m_info.parent_hub->m_info.slot_id;
|
||||
slot_context.parent_port_number = m_info.parent_port_id;
|
||||
slot_context.multi_tt = m_info.parent_hub->is_multi_tt();
|
||||
}
|
||||
|
||||
endpoint0_context.endpoint_type = XHCI::EndpointType::Control;
|
||||
endpoint0_context.max_packet_size = m_endpoints[0].max_packet_size;
|
||||
@@ -120,6 +127,8 @@ namespace Kernel
|
||||
request.wLength = 8;
|
||||
TRY(send_request(request, kmalloc_paddr_of((vaddr_t)buffer.data()).value()));
|
||||
|
||||
dprintln_if(DEBUG_XHCI, "Got device descriptor");
|
||||
|
||||
const bool is_usb3 = (m_speed_class == USB::SpeedClass::SuperSpeed);
|
||||
const uint32_t new_max_packet_size = is_usb3 ? 1u << buffer.back() : buffer.back();
|
||||
|
||||
@@ -153,6 +162,13 @@ namespace Kernel
|
||||
return {};
|
||||
}
|
||||
|
||||
bool XHCIDevice::is_multi_tt() const
|
||||
{
|
||||
const uint32_t context_size = m_controller.context_size_set() ? 64 : 32;
|
||||
const auto& slot_context = *reinterpret_cast<const XHCI::SlotContext*>(m_input_context->vaddr() + context_size);
|
||||
return slot_context.multi_tt;
|
||||
}
|
||||
|
||||
// 6.2.3.6 Interval
|
||||
static uint32_t determine_interval(const USBEndpointDescriptor& endpoint_descriptor, USB::SpeedClass speed_class)
|
||||
{
|
||||
@@ -186,7 +202,25 @@ namespace Kernel
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::configure_endpoint(const USBEndpointDescriptor& endpoint_descriptor)
|
||||
BAN::ErrorOr<uint8_t> XHCIDevice::initialize_device_on_hub_port(uint8_t port_id, USB::SpeedClass speed_class)
|
||||
{
|
||||
if (m_info.depth + 1 > 5)
|
||||
{
|
||||
dwarnln("Invalid USB hub on depth {}", m_info.depth);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
uint32_t route_string = m_info.route_string;
|
||||
route_string |= static_cast<uint32_t>(port_id) << ((m_info.depth + 1) * 4);
|
||||
return m_controller.initialize_device(route_string, m_info.depth + 1, speed_class, this, port_id);
|
||||
}
|
||||
|
||||
void XHCIDevice::deinitialize_device_slot(uint8_t slot_id)
|
||||
{
|
||||
m_controller.deinitialize_slot(slot_id);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> XHCIDevice::configure_endpoint(const USBEndpointDescriptor& endpoint_descriptor, const HubInfo& hub_info)
|
||||
{
|
||||
const bool is_control { (endpoint_descriptor.bmAttributes & 0x03) == 0x00 };
|
||||
const bool is_isoch { (endpoint_descriptor.bmAttributes & 0x03) == 0x01 };
|
||||
@@ -254,7 +288,13 @@ namespace Kernel
|
||||
input_control_context.add_context_flags = (1u << endpoint_id) | (1 << 0);
|
||||
|
||||
slot_context.context_entries = last_valid_endpoint_id;
|
||||
// FIXME: 4.5.2 hub
|
||||
slot_context.hub = (hub_info.number_of_ports > 0);
|
||||
slot_context.number_of_ports = hub_info.number_of_ports;
|
||||
if (m_speed_class == USB::SpeedClass::HighSpeed)
|
||||
{
|
||||
slot_context.multi_tt = hub_info.multi_tt;
|
||||
slot_context.tt_think_time = hub_info.tt_think_time;
|
||||
}
|
||||
|
||||
memset(&endpoint_context, 0, context_size);
|
||||
endpoint_context.endpoint_type = endpoint_type;
|
||||
|
||||
Reference in New Issue
Block a user