mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:12:43 +00:00 
			
		
		
		
	 11eee67b85
			
		
	
	
		11eee67b85
		
	
	
	
	
		
			
			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;
 | |
| }
 | |
| 
 | |
| }
 |