Serenity Operating System
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}