2021-08-13 20:47:13 +01:00
/*
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <Kernel/Bus/USB/UHCIController.h>
# include <Kernel/Bus/USB/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 = {
2021-08-16 19:05:46 -04:00
{
sizeof ( USBDeviceDescriptor ) , // 18 bytes long
DESCRIPTOR_TYPE_DEVICE ,
} ,
2021-08-13 20:47:13 +01:00
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 = {
2021-08-16 19:05:46 -04:00
{
sizeof ( USBConfigurationDescriptor ) , // 9 bytes long
DESCRIPTOR_TYPE_CONFIGURATION ,
} ,
2021-08-13 20:47:13 +01:00
sizeof ( USBConfigurationDescriptor ) + sizeof ( USBInterfaceDescriptor ) + sizeof ( USBEndpointDescriptor ) + sizeof ( USBHubDescriptor ) , // Combined length of configuration, interface, endpoint and hub 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 = {
2021-08-16 19:05:46 -04:00
{
sizeof ( USBInterfaceDescriptor ) , // 9 bytes long
DESCRIPTOR_TYPE_INTERFACE ,
} ,
2021-08-13 20:47:13 +01:00
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 = {
2021-08-16 19:05:46 -04:00
{
sizeof ( USBEndpointDescriptor ) , // 7 bytes long
DESCRIPTOR_TYPE_ENDPOINT ,
} ,
2021-08-13 20:47:13 +01:00
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 = {
2021-08-16 19:05:46 -04:00
{
sizeof ( USBHubDescriptor ) , // 7 bytes long. FIXME: Add the size of the VLAs at the end once they're supported.
DESCRIPTOR_TYPE_HUB ,
} ,
2021-08-13 20:47:13 +01:00
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
} ;
KResultOr < NonnullOwnPtr < UHCIRootHub > > UHCIRootHub : : try_create ( NonnullRefPtr < UHCIController > uhci_controller )
{
auto root_hub = adopt_own_if_nonnull ( new ( nothrow ) UHCIRootHub ( uhci_controller ) ) ;
if ( ! root_hub )
return ENOMEM ;
return root_hub . release_nonnull ( ) ;
}
UHCIRootHub : : UHCIRootHub ( NonnullRefPtr < UHCIController > uhci_controller )
: m_uhci_controller ( uhci_controller )
{
}
KResult UHCIRootHub : : setup ( Badge < UHCIController > )
{
auto hub_or_error = Hub : : try_create_root_hub ( m_uhci_controller , Device : : DeviceSpeed : : FullSpeed ) ;
if ( hub_or_error . is_error ( ) )
return hub_or_error . error ( ) ;
m_hub = hub_or_error . release_value ( ) ;
// 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.
auto result = m_hub - > enumerate_device ( ) ;
if ( result . is_error ( ) )
return result ;
// NOTE: The root hub is no longer on the default address.
result = m_hub - > enumerate_and_power_on_hub ( ) ;
if ( result . is_error ( ) )
return result ;
return KSuccess ;
}
KResultOr < size_t > UHCIRootHub : : handle_control_transfer ( Transfer & transfer )
{
auto & 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 :
length = min ( transfer . transfer_data_size ( ) , sizeof ( USBConfigurationDescriptor ) ) ;
VERIFY ( length < = sizeof ( USBConfigurationDescriptor ) ) ;
memcpy ( request_data , ( void * ) & uhci_root_hub_configuration_descriptor , length ) ;
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 ;
auto result = m_uhci_controller - > set_port_feature ( { } , port - 1 , feature_selector ) ;
if ( result . is_error ( ) )
return result . error ( ) ;
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 ;
auto result = m_uhci_controller - > clear_port_feature ( { } , port - 1 , feature_selector ) ;
if ( result . is_error ( ) )
return result . error ( ) ;
break ;
}
default :
return EINVAL ;
}
transfer . set_complete ( ) ;
return length ;
}
}