mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 08:54:58 +00:00

Until now, our kernel has reimplemented a number of AK classes to provide automatic internal locking: - RefPtr - NonnullRefPtr - WeakPtr - Weakable This patch renames the Kernel classes so that they can coexist with the original AK classes: - RefPtr => LockRefPtr - NonnullRefPtr => NonnullLockRefPtr - WeakPtr => LockWeakPtr - Weakable => LockWeakable The goal here is to eventually get rid of the Lock* classes in favor of using external locking.
254 lines
11 KiB
C++
254 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
|
|
#include <Kernel/Bus/USB/UHCI/UHCIRootHub.h>
|
|
#include <Kernel/Bus/USB/USBClasses.h>
|
|
#include <Kernel/Bus/USB/USBConstants.h>
|
|
#include <Kernel/Bus/USB/USBEndpoint.h>
|
|
#include <Kernel/Bus/USB/USBHub.h>
|
|
#include <Kernel/Bus/USB/USBRequest.h>
|
|
|
|
namespace Kernel::USB {
|
|
|
|
static USBDeviceDescriptor uhci_root_hub_device_descriptor = {
|
|
{
|
|
sizeof(USBDeviceDescriptor), // 18 bytes long
|
|
DESCRIPTOR_TYPE_DEVICE,
|
|
},
|
|
0x0110, // USB 1.1
|
|
(u8)USB_CLASS_HUB,
|
|
0, // Hubs use subclass 0
|
|
0, // Full Speed Hub
|
|
64, // Max packet size
|
|
0x0, // Vendor ID
|
|
0x0, // Product ID
|
|
0x0110, // Product version (can be anything, currently matching usb_spec_compliance_bcd)
|
|
0, // Index of manufacturer string. FIXME: There is currently no support for string descriptors.
|
|
0, // Index of product string. FIXME: There is currently no support for string descriptors.
|
|
0, // Index of serial string. FIXME: There is currently no support for string descriptors.
|
|
1, // One configuration descriptor
|
|
};
|
|
|
|
static USBConfigurationDescriptor uhci_root_hub_configuration_descriptor = {
|
|
{
|
|
sizeof(USBConfigurationDescriptor), // 9 bytes long
|
|
DESCRIPTOR_TYPE_CONFIGURATION,
|
|
},
|
|
sizeof(USBConfigurationDescriptor) + sizeof(USBInterfaceDescriptor) + sizeof(USBEndpointDescriptor), // Combined length of configuration, interface and endpoint and descriptors.
|
|
1, // One interface descriptor
|
|
1, // Configuration #1
|
|
0, // Index of configuration string. FIXME: There is currently no support for string descriptors.
|
|
(1 << 7) | (1 << 6), // Bit 6 is set to indicate that the root hub is self powered. Bit 7 must always be 1.
|
|
0, // 0 mA required from the bus (self-powered)
|
|
};
|
|
|
|
static USBInterfaceDescriptor uhci_root_hub_interface_descriptor = {
|
|
{
|
|
sizeof(USBInterfaceDescriptor), // 9 bytes long
|
|
DESCRIPTOR_TYPE_INTERFACE,
|
|
},
|
|
0, // Interface #0
|
|
0, // Alternate setting
|
|
1, // One endpoint
|
|
(u8)USB_CLASS_HUB,
|
|
0, // Hubs use subclass 0
|
|
0, // Full Speed Hub
|
|
0, // Index of interface string. FIXME: There is currently no support for string descriptors
|
|
};
|
|
|
|
static USBEndpointDescriptor uhci_root_hub_endpoint_descriptor = {
|
|
{
|
|
sizeof(USBEndpointDescriptor), // 7 bytes long
|
|
DESCRIPTOR_TYPE_ENDPOINT,
|
|
},
|
|
USBEndpoint::ENDPOINT_ADDRESS_DIRECTION_IN | 1, // IN Endpoint #1
|
|
USBEndpoint::ENDPOINT_ATTRIBUTES_TRANSFER_TYPE_INTERRUPT, // Interrupt endpoint
|
|
2, // Max Packet Size FIXME: I'm not sure what this is supposed to be as it is implementation defined. 2 is the number of bytes Get Port Status returns.
|
|
0xFF, // Max possible interval
|
|
};
|
|
|
|
// NOTE: UHCI does not provide us anything for the Root Hub's Hub Descriptor.
|
|
static USBHubDescriptor uhci_root_hub_hub_descriptor = {
|
|
{
|
|
sizeof(USBHubDescriptor), // 7 bytes long. FIXME: Add the size of the VLAs at the end once they're supported.
|
|
DESCRIPTOR_TYPE_HUB,
|
|
},
|
|
UHCIController::NUMBER_OF_ROOT_PORTS, // 2 ports
|
|
0x0, // Ganged power switching, not a compound device, global over-current protection.
|
|
0x0, // UHCI ports are always powered, so there's no time from power on to power good.
|
|
0x0, // Self-powered
|
|
};
|
|
|
|
ErrorOr<NonnullOwnPtr<UHCIRootHub>> UHCIRootHub::try_create(NonnullLockRefPtr<UHCIController> uhci_controller)
|
|
{
|
|
return adopt_nonnull_own_or_enomem(new (nothrow) UHCIRootHub(move(uhci_controller)));
|
|
}
|
|
|
|
UHCIRootHub::UHCIRootHub(NonnullLockRefPtr<UHCIController> uhci_controller)
|
|
: m_uhci_controller(move(uhci_controller))
|
|
{
|
|
}
|
|
|
|
ErrorOr<void> UHCIRootHub::setup(Badge<UHCIController>)
|
|
{
|
|
m_hub = TRY(Hub::try_create_root_hub(m_uhci_controller, Device::DeviceSpeed::FullSpeed));
|
|
|
|
// NOTE: The root hub will be on the default address at this point.
|
|
// The root hub must be the first device to be created, otherwise the HCD will intercept all default address transfers as though they're targeted at the root hub.
|
|
TRY(m_hub->enumerate_device());
|
|
|
|
// NOTE: The root hub is no longer on the default address.
|
|
TRY(m_hub->enumerate_and_power_on_hub());
|
|
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<size_t> UHCIRootHub::handle_control_transfer(Transfer& transfer)
|
|
{
|
|
auto const& request = transfer.request();
|
|
auto* request_data = transfer.buffer().as_ptr() + sizeof(USBRequestData);
|
|
|
|
if constexpr (UHCI_DEBUG) {
|
|
dbgln("UHCIRootHub: Received control transfer.");
|
|
dbgln("UHCIRootHub: Request Type: 0x{:02x}", request.request_type);
|
|
dbgln("UHCIRootHub: Request: 0x{:02x}", request.request);
|
|
dbgln("UHCIRootHub: Value: 0x{:04x}", request.value);
|
|
dbgln("UHCIRootHub: Index: 0x{:04x}", request.index);
|
|
dbgln("UHCIRootHub: Length: 0x{:04x}", request.length);
|
|
}
|
|
|
|
size_t length = 0;
|
|
|
|
switch (request.request) {
|
|
case HubRequest::GET_STATUS: {
|
|
if (request.index > UHCIController::NUMBER_OF_ROOT_PORTS)
|
|
return EINVAL;
|
|
|
|
length = min(transfer.transfer_data_size(), sizeof(HubStatus));
|
|
VERIFY(length <= sizeof(HubStatus));
|
|
HubStatus hub_status {};
|
|
|
|
if (request.index == 0) {
|
|
// If index == 0, the actual request is Get Hub Status
|
|
// UHCI does not provide "Local Power Source" or "Over-current" and their corresponding change flags.
|
|
// The members of hub_status are initialized to 0, so we can memcpy it straight away.
|
|
memcpy(request_data, (void*)&hub_status, length);
|
|
break;
|
|
}
|
|
|
|
// If index != 0, the actual request is Get Port Status
|
|
m_uhci_controller->get_port_status({}, request.index - 1, hub_status);
|
|
memcpy(request_data, (void*)&hub_status, length);
|
|
break;
|
|
}
|
|
case HubRequest::GET_DESCRIPTOR: {
|
|
u8 descriptor_type = request.value >> 8;
|
|
switch (descriptor_type) {
|
|
case DESCRIPTOR_TYPE_DEVICE:
|
|
length = min(transfer.transfer_data_size(), sizeof(USBDeviceDescriptor));
|
|
VERIFY(length <= sizeof(USBDeviceDescriptor));
|
|
memcpy(request_data, (void*)&uhci_root_hub_device_descriptor, length);
|
|
break;
|
|
case DESCRIPTOR_TYPE_CONFIGURATION: {
|
|
auto index = 0u;
|
|
|
|
// Send over the whole descriptor chain
|
|
length = uhci_root_hub_configuration_descriptor.total_length;
|
|
VERIFY(length <= sizeof(USBConfigurationDescriptor) + sizeof(USBInterfaceDescriptor) + sizeof(USBEndpointDescriptor));
|
|
memcpy(request_data, (void*)&uhci_root_hub_configuration_descriptor, sizeof(USBConfigurationDescriptor));
|
|
index += sizeof(uhci_root_hub_configuration_descriptor);
|
|
memcpy(request_data + index, (void*)&uhci_root_hub_interface_descriptor, sizeof(USBInterfaceDescriptor));
|
|
index += sizeof(uhci_root_hub_interface_descriptor);
|
|
memcpy(request_data + index, (void*)&uhci_root_hub_endpoint_descriptor, sizeof(USBEndpointDescriptor));
|
|
break;
|
|
}
|
|
case DESCRIPTOR_TYPE_INTERFACE:
|
|
length = min(transfer.transfer_data_size(), sizeof(USBInterfaceDescriptor));
|
|
VERIFY(length <= sizeof(USBInterfaceDescriptor));
|
|
memcpy(request_data, (void*)&uhci_root_hub_interface_descriptor, length);
|
|
break;
|
|
case DESCRIPTOR_TYPE_ENDPOINT:
|
|
length = min(transfer.transfer_data_size(), sizeof(USBEndpointDescriptor));
|
|
VERIFY(length <= sizeof(USBEndpointDescriptor));
|
|
memcpy(request_data, (void*)&uhci_root_hub_endpoint_descriptor, length);
|
|
break;
|
|
case DESCRIPTOR_TYPE_HUB:
|
|
length = min(transfer.transfer_data_size(), sizeof(USBHubDescriptor));
|
|
VERIFY(length <= sizeof(USBHubDescriptor));
|
|
memcpy(request_data, (void*)&uhci_root_hub_hub_descriptor, length);
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
break;
|
|
}
|
|
case USB_REQUEST_SET_ADDRESS:
|
|
dbgln_if(UHCI_DEBUG, "UHCIRootHub: Attempt to set address to {}, ignoring.", request.value);
|
|
if (request.value > USB_MAX_ADDRESS)
|
|
return EINVAL;
|
|
// Ignore SET_ADDRESS requests. USBDevice sets its internal address to the new allocated address that it just sent to us.
|
|
// The internal address is used to check if the request is directed at the root hub or not.
|
|
break;
|
|
case HubRequest::SET_FEATURE: {
|
|
if (request.index == 0) {
|
|
// If index == 0, the actual request is Set Hub Feature.
|
|
// UHCI does not provide "Local Power Source" or "Over-current" and their corresponding change flags.
|
|
// Therefore, ignore the request, but return an error if the value is not "Local Power Source" or "Over-current"
|
|
switch (request.value) {
|
|
case HubFeatureSelector::C_HUB_LOCAL_POWER:
|
|
case HubFeatureSelector::C_HUB_OVER_CURRENT:
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// If index != 0, the actual request is Set Port Feature.
|
|
u8 port = request.index & 0xFF;
|
|
if (port > UHCIController::NUMBER_OF_ROOT_PORTS)
|
|
return EINVAL;
|
|
|
|
auto feature_selector = (HubFeatureSelector)request.value;
|
|
TRY(m_uhci_controller->set_port_feature({}, port - 1, feature_selector));
|
|
break;
|
|
}
|
|
case HubRequest::CLEAR_FEATURE: {
|
|
if (request.index == 0) {
|
|
// If index == 0, the actual request is Clear Hub Feature.
|
|
// UHCI does not provide "Local Power Source" or "Over-current" and their corresponding change flags.
|
|
// Therefore, ignore the request, but return an error if the value is not "Local Power Source" or "Over-current"
|
|
switch (request.value) {
|
|
case HubFeatureSelector::C_HUB_LOCAL_POWER:
|
|
case HubFeatureSelector::C_HUB_OVER_CURRENT:
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// If index != 0, the actual request is Clear Port Feature.
|
|
u8 port = request.index & 0xFF;
|
|
if (port > UHCIController::NUMBER_OF_ROOT_PORTS)
|
|
return EINVAL;
|
|
|
|
auto feature_selector = (HubFeatureSelector)request.value;
|
|
TRY(m_uhci_controller->clear_port_feature({}, port - 1, feature_selector));
|
|
break;
|
|
}
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
|
|
transfer.set_complete();
|
|
return length;
|
|
}
|
|
|
|
}
|