Serenity Operating System
at master 303 lines 14 kB view raw
1/* 2 * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <Kernel/Arch/Delay.h> 8#include <Kernel/Bus/USB/USBClasses.h> 9#include <Kernel/Bus/USB/USBController.h> 10#include <Kernel/Bus/USB/USBHub.h> 11#include <Kernel/Bus/USB/USBRequest.h> 12#include <Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.h> 13#include <Kernel/IOWindow.h> 14#include <Kernel/StdLib.h> 15 16namespace Kernel::USB { 17 18ErrorOr<NonnullLockRefPtr<Hub>> Hub::try_create_root_hub(NonnullLockRefPtr<USBController> controller, DeviceSpeed device_speed) 19{ 20 // NOTE: Enumeration does not happen here, as the controller must know what the device address is at all times during enumeration to intercept requests. 21 auto pipe = TRY(ControlPipe::create(controller, 0, 8, 0)); 22 auto hub = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Hub(controller, device_speed, move(pipe)))); 23 return hub; 24} 25 26ErrorOr<NonnullLockRefPtr<Hub>> Hub::try_create_from_device(Device const& device) 27{ 28 auto pipe = TRY(ControlPipe::create(device.controller(), 0, device.device_descriptor().max_packet_size, device.address())); 29 auto hub = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Hub(device, move(pipe)))); 30 TRY(hub->enumerate_and_power_on_hub()); 31 return hub; 32} 33 34Hub::Hub(NonnullLockRefPtr<USBController> controller, DeviceSpeed device_speed, NonnullOwnPtr<ControlPipe> default_pipe) 35 : Device(move(controller), 1 /* Port 1 */, device_speed, move(default_pipe)) 36{ 37} 38 39Hub::Hub(Device const& device, NonnullOwnPtr<ControlPipe> default_pipe) 40 : Device(device, move(default_pipe)) 41{ 42} 43 44ErrorOr<void> Hub::enumerate_and_power_on_hub() 45{ 46 // USBDevice::enumerate_device must be called before this. 47 VERIFY(m_address > 0); 48 49 m_sysfs_device_info_node = TRY(SysFSUSBDeviceInformation::create(*this)); 50 51 if (m_device_descriptor.device_class != USB_CLASS_HUB) { 52 dbgln("USB Hub: Trying to enumerate and power on a device that says it isn't a hub."); 53 return EINVAL; 54 } 55 56 dbgln_if(USB_DEBUG, "USB Hub: Enumerating and powering on for address {}", m_address); 57 58 USBHubDescriptor descriptor {}; 59 60 // Get the first hub descriptor. All hubs are required to have a hub descriptor at index 0. USB 2.0 Specification Section 11.24.2.5. 61 auto transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS, HubRequest::GET_DESCRIPTOR, (DESCRIPTOR_TYPE_HUB << 8), 0, sizeof(USBHubDescriptor), &descriptor)); 62 63 // FIXME: This be "not equal to" instead of "less than", but control transfers report a higher transfer length than expected. 64 if (transfer_length < sizeof(USBHubDescriptor)) { 65 dbgln("USB Hub: Unexpected hub descriptor size. Expected {}, got {}", sizeof(USBHubDescriptor), transfer_length); 66 return EIO; 67 } 68 69 if constexpr (USB_DEBUG) { 70 dbgln("USB Hub Descriptor for {:04x}:{:04x}", m_vendor_id, m_product_id); 71 dbgln("Number of Downstream Ports: {}", descriptor.number_of_downstream_ports); 72 dbgln("Hub Characteristics: 0x{:04x}", descriptor.hub_characteristics); 73 dbgln("Power On to Power Good Time: {} ms ({} * 2ms)", descriptor.power_on_to_power_good_time * 2, descriptor.power_on_to_power_good_time); 74 dbgln("Hub Controller Current: {} mA", descriptor.hub_controller_current); 75 } 76 77 // FIXME: Queue the status change interrupt 78 79 // Enable all the ports 80 for (u8 port_index = 0; port_index < descriptor.number_of_downstream_ports; ++port_index) { 81 auto result = m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::SET_FEATURE, HubFeatureSelector::PORT_POWER, port_index + 1, 0, nullptr); 82 if (result.is_error()) 83 dbgln("USB: Failed to power on port {} on hub at address {}.", port_index + 1, m_address); 84 } 85 86 // Wait for the ports to power up. power_on_to_power_good_time is in units of 2 ms and we want in us, so multiply by 2000. 87 microseconds_delay(descriptor.power_on_to_power_good_time * 2000); 88 89 memcpy(&m_hub_descriptor, &descriptor, sizeof(USBHubDescriptor)); 90 91 return {}; 92} 93 94// USB 2.0 Specification Section 11.24.2.7 95ErrorOr<void> Hub::get_port_status(u8 port, HubStatus& hub_status) 96{ 97 // Ports are 1-based. 98 if (port == 0 || port > m_hub_descriptor.number_of_downstream_ports) 99 return EINVAL; 100 101 auto transfer_length = TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::GET_STATUS, 0, port, sizeof(HubStatus), &hub_status)); 102 103 // FIXME: This be "not equal to" instead of "less than", but control transfers report a higher transfer length than expected. 104 if (transfer_length < sizeof(HubStatus)) { 105 dbgln("USB Hub: Unexpected hub status size. Expected {}, got {}.", sizeof(HubStatus), transfer_length); 106 return EIO; 107 } 108 109 return {}; 110} 111 112// USB 2.0 Specification Section 11.24.2.2 113ErrorOr<void> Hub::clear_port_feature(u8 port, HubFeatureSelector feature_selector) 114{ 115 // Ports are 1-based. 116 if (port == 0 || port > m_hub_descriptor.number_of_downstream_ports) 117 return EINVAL; 118 119 TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::CLEAR_FEATURE, feature_selector, port, 0, nullptr)); 120 return {}; 121} 122 123// USB 2.0 Specification Section 11.24.2.13 124ErrorOr<void> Hub::set_port_feature(u8 port, HubFeatureSelector feature_selector) 125{ 126 // Ports are 1-based. 127 if (port == 0 || port > m_hub_descriptor.number_of_downstream_ports) 128 return EINVAL; 129 130 TRY(m_default_pipe->submit_control_transfer(USB_REQUEST_TRANSFER_DIRECTION_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_REQUEST_RECIPIENT_OTHER, HubRequest::SET_FEATURE, feature_selector, port, 0, nullptr)); 131 return {}; 132} 133 134void Hub::remove_children_from_sysfs() 135{ 136 for (auto& child : m_children) 137 SysFSUSBBusDirectory::the().unplug({}, child.sysfs_device_info_node({})); 138} 139 140void Hub::check_for_port_updates() 141{ 142 for (u8 port_number = 1; port_number < m_hub_descriptor.number_of_downstream_ports + 1; ++port_number) { 143 dbgln_if(USB_DEBUG, "USB Hub: Checking for port updates on port {}...", port_number); 144 145 HubStatus port_status {}; 146 if (auto result = get_port_status(port_number, port_status); result.is_error()) { 147 dbgln("USB Hub: Error occurred when getting status for port {}: {}. Checking next port instead.", port_number, result.error()); 148 continue; 149 } 150 151 if (port_status.change & PORT_STATUS_CONNECT_STATUS_CHANGED) { 152 // Clear the connection status change notification. 153 if (auto result = clear_port_feature(port_number, HubFeatureSelector::C_PORT_CONNECTION); result.is_error()) { 154 dbgln("USB Hub: Error occurred when clearing port connection change for port {}: {}.", port_number, result.error()); 155 return; 156 } 157 158 if (port_status.status & PORT_STATUS_CURRENT_CONNECT_STATUS) { 159 dbgln("USB Hub: Device attached to port {}!", port_number); 160 161 // Debounce the port. USB 2.0 Specification Page 150 162 // Debounce interval is 100 ms (100000 us). USB 2.0 Specification Page 188 Table 7-14. 163 constexpr u32 debounce_interval = 100 * 1000; 164 165 // We must check if the device disconnected every so often. If it disconnects, we must reset the debounce timer. 166 // This doesn't seem to be specified. Let's check every 10ms (10000 us). 167 constexpr u32 debounce_disconnect_check_interval = 10 * 1000; 168 169 u32 debounce_timer = 0; 170 171 dbgln_if(USB_DEBUG, "USB Hub: Debouncing..."); 172 173 // FIXME: Timeout 174 while (debounce_timer < debounce_interval) { 175 microseconds_delay(debounce_disconnect_check_interval); 176 debounce_timer += debounce_disconnect_check_interval; 177 178 if (auto result = get_port_status(port_number, port_status); result.is_error()) { 179 dbgln("USB Hub: Error occurred when getting status while debouncing port {}: {}.", port_number, result.error()); 180 return; 181 } 182 183 if (!(port_status.change & PORT_STATUS_CONNECT_STATUS_CHANGED)) 184 continue; 185 186 dbgln_if(USB_DEBUG, "USB Hub: Connection status changed while debouncing, resetting debounce timer."); 187 debounce_timer = 0; 188 189 if (auto result = clear_port_feature(port_number, HubFeatureSelector::C_PORT_CONNECTION); result.is_error()) { 190 dbgln("USB Hub: Error occurred when clearing port connection change while debouncing port {}: {}.", port_number, result.error()); 191 return; 192 } 193 } 194 195 // Reset the port 196 dbgln_if(USB_DEBUG, "USB Hub: Debounce finished. Driving reset..."); 197 if (auto result = set_port_feature(port_number, HubFeatureSelector::PORT_RESET); result.is_error()) { 198 dbgln("USB Hub: Error occurred when resetting port {}: {}.", port_number, result.error()); 199 return; 200 } 201 202 // FIXME: Timeout 203 for (;;) { 204 // Wait at least 10 ms for the port to reset. 205 // This is T DRST in the USB 2.0 Specification Page 186 Table 7-13. 206 constexpr u16 reset_delay = 10 * 1000; 207 microseconds_delay(reset_delay); 208 209 if (auto result = get_port_status(port_number, port_status); result.is_error()) { 210 dbgln("USB Hub: Error occurred when getting status while resetting port {}: {}.", port_number, result.error()); 211 return; 212 } 213 214 if (port_status.change & PORT_STATUS_RESET_CHANGED) 215 break; 216 } 217 218 // Stop asserting reset. This also causes the port to become enabled. 219 220 if (auto result = clear_port_feature(port_number, HubFeatureSelector::C_PORT_RESET); result.is_error()) { 221 dbgln("USB Hub: Error occurred when resetting port {}: {}.", port_number, result.error()); 222 return; 223 } 224 225 // Wait 10 ms for the port to recover. 226 // This is T RSTRCY in the USB 2.0 Specification Page 188 Table 7-14. 227 constexpr u16 reset_recovery_delay = 10 * 1000; 228 microseconds_delay(reset_recovery_delay); 229 230 dbgln_if(USB_DEBUG, "USB Hub: Reset complete!"); 231 232 // The port is ready to go. This is where we start communicating with the device to set up a driver for it. 233 234 if (auto result = get_port_status(port_number, port_status); result.is_error()) { 235 dbgln("USB Hub: Error occurred when getting status for port {} after reset: {}.", port_number, result.error()); 236 return; 237 } 238 239 // FIXME: Check for high speed. 240 auto speed = port_status.status & PORT_STATUS_LOW_SPEED_DEVICE_ATTACHED ? USB::Device::DeviceSpeed::LowSpeed : USB::Device::DeviceSpeed::FullSpeed; 241 242 auto device_or_error = USB::Device::try_create(m_controller, port_number, speed); 243 if (device_or_error.is_error()) { 244 dbgln("USB Hub: Failed to create device for port {}: {}", port_number, device_or_error.error()); 245 return; 246 } 247 248 auto device = device_or_error.release_value(); 249 250 dbgln_if(USB_DEBUG, "USB Hub: Created device with address {}!", device->address()); 251 252 if (device->device_descriptor().device_class == USB_CLASS_HUB) { 253 auto hub_or_error = Hub::try_create_from_device(*device); 254 if (hub_or_error.is_error()) { 255 dbgln("USB Hub: Failed to upgrade device to hub for port {}: {}", port_number, device_or_error.error()); 256 return; 257 } 258 259 dbgln_if(USB_DEBUG, "USB Hub: Upgraded device at address {} to hub!", device->address()); 260 261 auto hub = hub_or_error.release_value(); 262 m_children.append(hub); 263 SysFSUSBBusDirectory::the().plug({}, hub->sysfs_device_info_node({})); 264 } else { 265 m_children.append(device); 266 SysFSUSBBusDirectory::the().plug({}, device->sysfs_device_info_node({})); 267 } 268 269 } else { 270 dbgln("USB Hub: Device detached on port {}!", port_number); 271 272 LockRefPtr<Device> device_to_remove = nullptr; 273 for (auto& child : m_children) { 274 if (port_number == child.port()) { 275 device_to_remove = &child; 276 break; 277 } 278 } 279 280 if (device_to_remove) { 281 SysFSUSBBusDirectory::the().unplug({}, device_to_remove->sysfs_device_info_node({})); 282 if (device_to_remove->device_descriptor().device_class == USB_CLASS_HUB) { 283 auto* hub_child = static_cast<Hub*>(device_to_remove.ptr()); 284 hub_child->remove_children_from_sysfs(); 285 } 286 m_children.remove(*device_to_remove); 287 } else { 288 dbgln_if(USB_DEBUG, "USB Hub: No child set up on port {}, ignoring detachment.", port_number); 289 } 290 } 291 } 292 } 293 294 for (auto& child : m_children) { 295 if (child.device_descriptor().device_class == USB_CLASS_HUB) { 296 auto& hub_child = static_cast<Hub&>(child); 297 dbgln_if(USB_DEBUG, "USB Hub: Checking for port updates on child hub at address {}...", child.address()); 298 hub_child.check_for_port_updates(); 299 } 300 } 301} 302 303}