/** Interface function to reset the port. @param HubIf The hub interface. @param Port The port to reset. @retval EFI_SUCCESS The hub port is reset. @retval EFI_TIMEOUT Failed to reset the port in time. @retval Others Failed to reset the port. **/ EFI_STATUS UsbHubResetPort ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) { EFI_USB_PORT_STATUS PortState; UINTN Index; EFI_STATUS Status; Status = UsbHubGetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { return Status; } else if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) { DEBUG (( EFI_D_INFO, "UsbHubResetPort: skip reset on hub %p port %d\n", HubIf, Port)); return EFI_SUCCESS; } Status = UsbHubSetPortFeature (HubIf, Port, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_RESET); if (EFI_ERROR (Status)) { return Status; } // // Drive the reset signal for worst 20ms. Check USB 2.0 Spec // section 7.1.7.5 for timing requirements. // gBS->Stall (USB_SET_PORT_RESET_STALL); // // Check USB_PORT_STAT_C_RESET bit to see if the resetting state is done. // ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS)); for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { Status = UsbHubGetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { return Status; } if (!EFI_ERROR (Status) && USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) { gBS->Stall (USB_SET_PORT_RECOVERY_STALL); return EFI_SUCCESS; } gBS->Stall (USB_WAIT_PORT_STS_CHANGE_STALL); } return EFI_TIMEOUT; }
/** Clear the port change status. @param HubIf The hub interface. @param Port The hub port. **/ VOID UsbHubClearPortChange ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) { EFI_USB_PORT_STATUS PortState; USB_CHANGE_FEATURE_MAP *Map; UINTN Index; EFI_STATUS Status; Status = UsbHubGetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { return; } // // OK, get the usb port status, now ACK the change bits. // Don't return error when failed to clear the change bits. // It may lead to extra port state report. USB bus should // be able to handle this. // for (Index = 0; Index < USB_HUB_MAP_SIZE; Index++) { Map = &mHubFeatureMap[Index]; if (USB_BIT_IS_SET (PortState.PortChangeStatus, Map->ChangedBit)) { UsbHubCtrlClearPortFeature (HubIf->Device, Port, (UINT16) Map->Feature); } } }
VOID EFIAPI UsbHubEnumeration ( IN EFI_EVENT Event, IN VOID *Context ) /*++ Routine Description: Enumerate all the changed hub ports Arguments: Event - The event that is triggered Context - The context to the event Returns: None --*/ { USB_INTERFACE *HubIf; UINT8 Byte; UINT8 Bit; UINT8 Index; ASSERT (Context); HubIf = (USB_INTERFACE *) Context; if (HubIf->ChangeMap == NULL) { return ; } // // HUB starts its port index with 1. // Byte = 0; Bit = 1; for (Index = 0; Index < HubIf->NumOfPort; Index++) { if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) { UsbEnumeratePort (HubIf, Index); } USB_NEXT_BIT (Byte, Bit); } UsbHubAckHubStatus (HubIf->Device); gBS->FreePool (HubIf->ChangeMap); HubIf->ChangeMap = NULL; return ; }
/** Enumerate all the changed hub ports. @param Event The event that is triggered. @param Context The context to the event. **/ VOID EFIAPI UsbHubEnumeration ( IN EFI_EVENT Event, IN VOID *Context ) { USB_INTERFACE *HubIf; UINT8 Byte; UINT8 Bit; UINT8 Index; USB_DEVICE *Child; // ASSERT (Context != NULL); if (!Context) { return; } HubIf = (USB_INTERFACE *) Context; for (Index = 0; Index < HubIf->NumOfPort; Index++) { Child = UsbFindChild (HubIf, Index); if ((Child != NULL) && (Child->DisconnectFail == TRUE)) { // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from hub %p, try again\n", Index, HubIf)); DBG("UsbEnumeratePort: The device disconnect fails at port %d from hub %p, try again\n", Index, HubIf); UsbRemoveDevice (Child); } } if (HubIf->ChangeMap == NULL) { return ; } // // HUB starts its port index with 1. // Byte = 0; Bit = 1; DBG("Enumerate %d ports\n", HubIf->NumOfPort); for (Index = 0; Index < HubIf->NumOfPort; Index++) { if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) { UsbEnumeratePort (HubIf, Index); DBG("Port %d enumerated\n", Index); } USB_NEXT_BIT (Byte, Bit); } UsbHubAckHubStatus (HubIf->Device); gBS->FreePool (HubIf->ChangeMap); HubIf->ChangeMap = NULL; return ; }
/** Ack the hub change bits. If these bits are not ACKed, Hub will always return changed bit map from its interrupt endpoint. @param HubDev The hub device. @retval EFI_SUCCESS The hub change status is ACKed. @retval Others Failed to ACK the hub status. **/ EFI_STATUS UsbHubAckHubStatus ( IN USB_DEVICE *HubDev ) { EFI_USB_PORT_STATUS HubState; EFI_STATUS Status; Status = UsbHubCtrlGetHubStatus (HubDev, (UINT32 *) &HubState); if (EFI_ERROR (Status)) { return Status; } if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_LOCAL_POWER)) { UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_LOCAL_POWER); } if (USB_BIT_IS_SET (HubState.PortChangeStatus, USB_HUB_STAT_C_OVER_CURRENT)) { UsbHubCtrlClearHubFeature (HubDev, USB_HUB_C_HUB_OVER_CURRENT); } return EFI_SUCCESS; }
/** Interface function to reset the port. @param HubIf The hub interface. @param Port The port to reset. @retval EFI_SUCCESS The hub port is reset. @retval EFI_TIMEOUT Failed to reset the port in time. @retval Others Failed to reset the port. **/ EFI_STATUS UsbHubResetPort ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) { EFI_USB_PORT_STATUS PortState; UINTN Index; EFI_STATUS Status; Status = UsbHubSetPortFeature (HubIf, Port, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_RESET); if (EFI_ERROR (Status)) { return Status; } // // Drive the reset signal for at least 10ms. Check USB 2.0 Spec // section 7.1.7.5 for timing requirements. // gBS->Stall (USB_SET_PORT_RESET_STALL); // // USB hub will clear RESET bit if reset is actually finished. // ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS)); for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { Status = UsbHubGetPortStatus (HubIf, Port, &PortState); if (!EFI_ERROR (Status) && !USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) { return EFI_SUCCESS; } gBS->Stall (USB_WAIT_PORT_STS_CHANGE_STALL); } return EFI_TIMEOUT; }
/** Enumerate all the changed hub ports. @param Event The event that is triggered. @param Context The context to the event. **/ VOID EFIAPI UsbHubEnumeration ( IN EFI_EVENT Event, IN VOID *Context ) { USB_INTERFACE *HubIf; UINT8 Byte; UINT8 Bit; UINT8 Index; ASSERT (Context != NULL); HubIf = (USB_INTERFACE *) Context; if (HubIf->ChangeMap == NULL) { return ; } // // HUB starts its port index with 1. // Byte = 0; Bit = 1; for (Index = 0; Index < HubIf->NumOfPort; Index++) { if (USB_BIT_IS_SET (HubIf->ChangeMap[Byte], USB_BIT (Bit))) { UsbEnumeratePort (HubIf, Index); } USB_NEXT_BIT (Byte, Bit); } UsbHubAckHubStatus (HubIf->Device); gBS->FreePool (HubIf->ChangeMap); HubIf->ChangeMap = NULL; return ; }
/** Initialize the device for a non-root hub. @param HubIf The USB hub interface. @retval EFI_SUCCESS The hub is initialized. @retval EFI_DEVICE_ERROR Failed to initialize the hub. **/ EFI_STATUS UsbHubInit ( IN USB_INTERFACE *HubIf ) { EFI_USB_HUB_DESCRIPTOR HubDesc; USB_ENDPOINT_DESC *EpDesc; USB_INTERFACE_SETTING *Setting; EFI_USB_IO_PROTOCOL *UsbIo; USB_DEVICE *HubDev; EFI_STATUS Status; UINT8 Index; UINT8 NumEndpoints; // // Locate the interrupt endpoint for port change map // HubIf->IsHub = FALSE; Setting = HubIf->IfSetting; HubDev = HubIf->Device; EpDesc = NULL; NumEndpoints = Setting->Desc.NumEndpoints; for (Index = 0; Index < NumEndpoints; Index++) { ASSERT ((Setting->Endpoints != NULL) && (Setting->Endpoints[Index] != NULL)); EpDesc = Setting->Endpoints[Index]; if (USB_BIT_IS_SET (EpDesc->Desc.EndpointAddress, USB_ENDPOINT_DIR_IN) && (USB_ENDPOINT_TYPE (&EpDesc->Desc) == USB_ENDPOINT_INTERRUPT)) { break; } } if (Index == NumEndpoints) { DEBUG (( EFI_D_ERROR, "UsbHubInit: no interrupt endpoint found for hub %d\n", HubDev->Address)); return EFI_DEVICE_ERROR; } Status = UsbHubReadDesc (HubDev, &HubDesc); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbHubInit: failed to read HUB descriptor %r\n", Status)); return Status; } HubIf->NumOfPort = HubDesc.NumPorts; DEBUG (( EFI_D_INFO, "UsbHubInit: hub %d has %d ports\n", HubDev->Address,HubIf->NumOfPort)); // // Create an event to enumerate the hub's port. On // Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK, UsbHubEnumeration, HubIf, &HubIf->HubNotify ); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbHubInit: failed to create signal for hub %d - %r\n", HubDev->Address, Status)); return Status; } // // Create AsyncInterrupt to query hub port change endpoint // periodically. If the hub ports are changed, hub will return // changed port map from the interrupt endpoint. The port map // must be able to hold (HubIf->NumOfPort + 1) bits (one bit for // host change status). // UsbIo = &HubIf->UsbIo; Status = UsbIo->UsbAsyncInterruptTransfer ( UsbIo, EpDesc->Desc.EndpointAddress, TRUE, USB_HUB_POLL_INTERVAL, HubIf->NumOfPort / 8 + 1, UsbOnHubInterrupt, HubIf ); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbHubInit: failed to queue interrupt transfer for hub %d - %r\n", HubDev->Address, Status)); gBS->CloseEvent (HubIf->HubNotify); HubIf->HubNotify = NULL; return Status; } // // OK, set IsHub to TRUE. Now usb bus can handle this device // as a working HUB. If failed eariler, bus driver will not // recognize it as a hub. Other parts of the bus should be able // to work. // HubIf->IsHub = TRUE; HubIf->HubApi = &mUsbHubApi; HubIf->HubEp = EpDesc; // // Feed power to all the hub ports. It should be ok // for both gang/individual powered hubs. // for (Index = 0; Index < HubDesc.NumPorts; Index++) { UsbHubCtrlSetPortFeature (HubIf->Device, Index, (EFI_USB_PORT_FEATURE) USB_HUB_PORT_POWER); } gBS->Stall (HubDesc.PwrOn2PwrGood * USB_SET_PORT_POWER_STALL); UsbHubAckHubStatus (HubIf->Device); DEBUG (( EFI_D_INFO, "UsbHubInit: hub %d initialized\n", HubDev->Address)); return Status; }
/** The callback function to the USB hub status change interrupt endpoint. It is called periodically by the underlying host controller. @param Data The data read. @param DataLength The length of the data read. @param Context The context. @param Result The result of the last interrupt transfer. @retval EFI_SUCCESS The process is OK. @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. **/ EFI_STATUS EFIAPI UsbOnHubInterrupt ( IN VOID *Data, IN UINTN DataLength, IN VOID *Context, IN UINT32 Result ) { USB_INTERFACE *HubIf; EFI_USB_IO_PROTOCOL *UsbIo; EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc; EFI_STATUS Status; HubIf = (USB_INTERFACE *) Context; UsbIo = &(HubIf->UsbIo); EpDesc = &(HubIf->HubEp->Desc); if (Result != EFI_USB_NOERROR) { // // If endpoint is stalled, clear the stall. Use UsbIo to access // the control transfer so internal status are maintained. // if (USB_BIT_IS_SET (Result, EFI_USB_ERR_STALL)) { UsbIoClearFeature ( UsbIo, USB_TARGET_ENDPOINT, USB_FEATURE_ENDPOINT_HALT, EpDesc->EndpointAddress ); } // // Delete and submit a new async interrupt // Status = UsbIo->UsbAsyncInterruptTransfer ( UsbIo, EpDesc->EndpointAddress, FALSE, 0, 0, NULL, NULL ); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbOnHubInterrupt: failed to remove async transfer - %r\n", Status)); return Status; } Status = UsbIo->UsbAsyncInterruptTransfer ( UsbIo, EpDesc->EndpointAddress, TRUE, USB_HUB_POLL_INTERVAL, HubIf->NumOfPort / 8 + 1, UsbOnHubInterrupt, HubIf ); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbOnHubInterrupt: failed to submit new async transfer - %r\n", Status)); } return Status; } if ((DataLength == 0) || (Data == NULL)) { return EFI_SUCCESS; } // // OK, actually something is changed, save the change map // then signal the HUB to do enumeration. This is a good // practise since UsbOnHubInterrupt is called in the context // of host contrller's AsyncInterrupt monitor. // HubIf->ChangeMap = AllocateZeroPool (DataLength); if (HubIf->ChangeMap == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem (HubIf->ChangeMap, Data, DataLength); gBS->SignalEvent (HubIf->HubNotify); return EFI_SUCCESS; }
/** Interface function to reset the root hub port. @param RootIf The root hub interface. @param Port The port to reset. @retval EFI_SUCCESS The hub port is reset. @retval EFI_TIMEOUT Failed to reset the port in time. @retval EFI_NOT_FOUND The low/full speed device connected to high speed. root hub is released to the companion UHCI. @retval Others Failed to reset the port. **/ EFI_STATUS UsbRootHubResetPort ( IN USB_INTERFACE *RootIf, IN UINT8 Port ) { USB_BUS *Bus; EFI_STATUS Status; EFI_USB_PORT_STATUS PortState; UINTN Index; // // Notice: although EHCI requires that ENABLED bit be cleared // when reset the port, we don't need to care that here. It // should be handled in the EHCI driver. // Bus = RootIf->Device->Bus; Status = UsbHcSetRootHubPortFeature (Bus, Port, EfiUsbPortReset); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: failed to start reset on port %d\n", Port)); return Status; } // // Drive the reset signal for at least 50ms. Check USB 2.0 Spec // section 7.1.7.5 for timing requirements. // gBS->Stall (USB_SET_ROOT_PORT_RESET_STALL); Status = UsbHcClearRootHubPortFeature (Bus, Port, EfiUsbPortReset); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: failed to clear reset on port %d\n", Port)); return Status; } gBS->Stall (USB_CLR_ROOT_PORT_RESET_STALL); // // USB host controller won't clear the RESET bit until // reset is actually finished. // ZeroMem (&PortState, sizeof (EFI_USB_PORT_STATUS)); for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { Status = UsbHcGetRootHubPortStatus (Bus, Port, &PortState); if (EFI_ERROR (Status)) { return Status; } if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_RESET)) { break; } gBS->Stall (USB_WAIT_PORT_STS_CHANGE_STALL); } if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { DEBUG ((EFI_D_ERROR, "UsbRootHubResetPort: reset not finished in time on port %d\n", Port)); return EFI_TIMEOUT; } if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_ENABLE)) { // // OK, the port is reset. If root hub is of high speed and // the device is of low/full speed, release the ownership to // companion UHCI. If root hub is of full speed, it won't // automatically enable the port, we need to enable it manually. // if (RootIf->MaxSpeed == EFI_USB_SPEED_HIGH) { DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: release low/full speed device (%d) to UHCI\n", Port)); UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortOwner); return EFI_NOT_FOUND; } else { Status = UsbRootHubSetPortFeature (RootIf, Port, EfiUsbPortEnable); if (EFI_ERROR (Status)) { DEBUG (( EFI_D_ERROR, "UsbRootHubResetPort: failed to enable port %d for UHCI\n", Port)); return Status; } gBS->Stall (USB_SET_ROOT_PORT_ENABLE_STALL); } } return EFI_SUCCESS; }
/** Process the events on the port. @param HubIf The HUB that has the device connected. @param Port The port index of the hub (started with zero). @retval EFI_SUCCESS The device is enumerated (added or removed). @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device. @retval Others Failed to enumerate the device. **/ EFI_STATUS UsbEnumeratePort ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) { USB_HUB_API *HubApi; USB_DEVICE *Child; EFI_USB_PORT_STATUS PortState; EFI_STATUS Status; Child = NULL; HubApi = HubIf->HubApi; // // Host learns of the new device by polling the hub for port changes. // Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port)); DBG("UsbEnumeratePort: failed to get state of port %d\n", Port); return Status; } // // Only handle connection/enable/overcurrent/reset change. // Usb super speed hub may report other changes, such as warm reset change. Ignore them. // if ((PortState.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { return EFI_SUCCESS; } // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n", //Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf)); DBG( "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n", Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf); // // This driver only process two kinds of events now: over current and // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET. // ENABLE/RESET is used to reset port. SUSPEND isn't supported. // if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) { if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) { // // Case1: // Both OverCurrent and OverCurrentChange set, means over current occurs, // which probably is caused by short circuit. It has to wait system hardware // to perform recovery. // // DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port)); DBG("UsbEnumeratePort: Critical Over Current\n", Port); return EFI_DEVICE_ERROR; } // // Case2: // Only OverCurrentChange set, means system has been recoveried from // over current. As a result, all ports are nearly power-off, so // it's necessary to detach and enumerate all ports again. // // DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port)); DBG("UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) { // // Case3: // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart // on 2.0 roothub does. When over-current has influence on 1.1 device, the port // would be disabled, so it's also necessary to detach and enumerate again. // // DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port)); DBG("UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) { // // Case4: // Device connected or disconnected normally. // // DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: Device Connect/Discount Normally\n", Port)); DBG("UsbEnumeratePort: Device Connect/Discount Normally\n"); } // // Following as the above cases, it's safety to remove and create again. // Child = UsbFindChild (HubIf, Port); if (Child != NULL) { // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf)); DBG("UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf); UsbRemoveDevice (Child); } if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { // // Now, new device connected, enumerate and configure the device // // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port)); DBG("UsbEnumeratePort: new device connected at port %d\n", Port); Status = UsbEnumerateNewDev (HubIf, Port); } else { // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port)); DBG("UsbEnumeratePort: device disconnected event on port %d status=0x%x\n", Port, PortState.PortStatus); } HubApi->ClearPortChange (HubIf, Port); DBG("ClearPortChange\n"); return Status; }
/** Send reset signal over the given root hub port. @param PeiServices Describes the list of possible PEI Services. @param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. @param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. @param PortNum The port to be reset. @param RetryIndex The retry times. **/ VOID ResetRootPort ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi, IN UINT8 PortNum, IN UINT8 RetryIndex ) { EFI_STATUS Status; UINTN Index; EFI_USB_PORT_STATUS PortStatus; if (Usb2HcPpi != NULL) { MicroSecondDelay (200 * 1000); // // reset root port // Status = Usb2HcPpi->SetRootHubPortFeature ( PeiServices, Usb2HcPpi, PortNum, EfiUsbPortReset ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "SetRootHubPortFeature EfiUsbPortReset Failed\n")); return; } // // Drive the reset signal for at least 50ms. Check USB 2.0 Spec // section 7.1.7.5 for timing requirements. // MicroSecondDelay (USB_SET_ROOT_PORT_RESET_STALL); // // clear reset root port // Status = Usb2HcPpi->ClearRootHubPortFeature ( PeiServices, Usb2HcPpi, PortNum, EfiUsbPortReset ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "ClearRootHubPortFeature EfiUsbPortReset Failed\n")); return; } MicroSecondDelay (USB_CLR_ROOT_PORT_RESET_STALL); // // USB host controller won't clear the RESET bit until // reset is actually finished. // ZeroMem (&PortStatus, sizeof (EFI_USB_PORT_STATUS)); for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { Status = Usb2HcPpi->GetRootHubPortStatus ( PeiServices, Usb2HcPpi, PortNum, &PortStatus ); if (EFI_ERROR (Status)) { return; } if (!USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_RESET)) { break; } MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); } if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { DEBUG ((EFI_D_ERROR, "ResetRootPort: reset not finished in time on port %d\n", PortNum)); return; } Usb2HcPpi->ClearRootHubPortFeature ( PeiServices, Usb2HcPpi, PortNum, EfiUsbPortResetChange ); Usb2HcPpi->ClearRootHubPortFeature ( PeiServices, Usb2HcPpi, PortNum, EfiUsbPortConnectChange ); // // Set port enable // Usb2HcPpi->SetRootHubPortFeature( PeiServices, Usb2HcPpi, PortNum, EfiUsbPortEnable ); Usb2HcPpi->ClearRootHubPortFeature ( PeiServices, Usb2HcPpi, PortNum, EfiUsbPortEnableChange ); MicroSecondDelay ((RetryIndex + 1) * 50 * 1000); } else { MicroSecondDelay (200 * 1000); // // reset root port // Status = UsbHcPpi->SetRootHubPortFeature ( PeiServices, UsbHcPpi, PortNum, EfiUsbPortReset ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "SetRootHubPortFeature EfiUsbPortReset Failed\n")); return; } // // Drive the reset signal for at least 50ms. Check USB 2.0 Spec // section 7.1.7.5 for timing requirements. // MicroSecondDelay (USB_SET_ROOT_PORT_RESET_STALL); // // clear reset root port // Status = UsbHcPpi->ClearRootHubPortFeature ( PeiServices, UsbHcPpi, PortNum, EfiUsbPortReset ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "ClearRootHubPortFeature EfiUsbPortReset Failed\n")); return; } MicroSecondDelay (USB_CLR_ROOT_PORT_RESET_STALL); // // USB host controller won't clear the RESET bit until // reset is actually finished. // ZeroMem (&PortStatus, sizeof (EFI_USB_PORT_STATUS)); for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { Status = UsbHcPpi->GetRootHubPortStatus ( PeiServices, UsbHcPpi, PortNum, &PortStatus ); if (EFI_ERROR (Status)) { return; } if (!USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_RESET)) { break; } MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); } if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { DEBUG ((EFI_D_ERROR, "ResetRootPort: reset not finished in time on port %d\n", PortNum)); return; } UsbHcPpi->ClearRootHubPortFeature ( PeiServices, UsbHcPpi, PortNum, EfiUsbPortResetChange ); UsbHcPpi->ClearRootHubPortFeature ( PeiServices, UsbHcPpi, PortNum, EfiUsbPortConnectChange ); // // Set port enable // UsbHcPpi->SetRootHubPortFeature( PeiServices, UsbHcPpi, PortNum, EfiUsbPortEnable ); UsbHcPpi->ClearRootHubPortFeature ( PeiServices, UsbHcPpi, PortNum, EfiUsbPortEnableChange ); MicroSecondDelay ((RetryIndex + 1) * 50 * 1000); } return; }
STATIC EFI_STATUS UsbEnumeratePort ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) /*++ Routine Description: Process the events on the port. Arguments: HubIf - The HUB that has the device connected Port - The port index of the hub (started with zero) Returns: EFI_SUCCESS - The device is enumerated (added or removed) EFI_OUT_OF_RESOURCES - Failed to allocate resource for the device Others - Failed to enumerate the device --*/ { USB_HUB_API *HubApi; USB_DEVICE *Child; EFI_USB_PORT_STATUS PortState; EFI_STATUS Status; Child = NULL; HubApi = HubIf->HubApi; // // Host learns of the new device by polling the hub for port changes. // Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumeratePort: failed to get state of port %d\n", Port)); return Status; } if (PortState.PortChangeStatus == 0) { return EFI_SUCCESS; } USB_DEBUG (("UsbEnumeratePort: port %d state - %x, change - %x\n", Port, PortState.PortStatus, PortState.PortChangeStatus)); // // This driver only process two kinds of events now: over current and // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET. // ENABLE/RESET is used to reset port. SUSPEND isn't supported. // if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) { if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) { // // Case1: // Both OverCurrent and OverCurrentChange set, means over current occurs, // which probably is caused by short circuit. It has to wait system hardware // to perform recovery. // USB_DEBUG (("UsbEnumeratePort: Critical Over Current\n", Port)); return EFI_DEVICE_ERROR; } // // Case2: // Only OverCurrentChange set, means system has been recoveried from // over current. As a result, all ports are nearly power-off, so // it's necessary to detach and enumerate all ports again. // USB_DEBUG (("UsbEnumeratePort: 2.0 Device Recovery Over Current\n", Port)); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) { // // Case3: // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart // on 2.0 roothub does. When over-current has influence on 1.1 device, the port // would be disabled, so it's also necessary to detach and enumerate again. // USB_DEBUG (("UsbEnumeratePort: 1.1 Device Recovery Over Current\n", Port)); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) { // // Case4: // Device connected or disconnected normally. // USB_DEBUG (("UsbEnumeratePort: Device Connect/Discount Normally\n", Port)); } // // Following as the above cases, it's safety to remove and create again. // Child = UsbFindChild (HubIf, Port); if (Child != NULL) { USB_DEBUG (("UsbEnumeratePort: device at port %d removed from system\n", Port)); UsbRemoveDevice (Child); } if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { // // Now, new device connected, enumerate and configure the device // USB_DEBUG (("UsbEnumeratePort: new device connected at port %d\n", Port)); Status = UsbEnumerateNewDev (HubIf, Port); } else { USB_DEBUG (("UsbEnumeratePort: device disconnected event on port %d\n", Port)); } HubApi->ClearPortChange (HubIf, Port); return Status; }
STATIC EFI_STATUS UsbEnumerateNewDev ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) /*++ Routine Description: Enumerate and configure the new device on the port of this HUB interface. Arguments: HubIf - The HUB that has the device connected Port - The port index of the hub (started with zero) Returns: EFI_SUCCESS - The device is enumerated (added or removed) EFI_OUT_OF_RESOURCES - Failed to allocate resource for the device Others - Failed to enumerate the device --*/ { USB_BUS *Bus; USB_HUB_API *HubApi; USB_DEVICE *Child; USB_DEVICE *Parent; EFI_USB_PORT_STATUS PortState; UINT8 Address; UINT8 Config; EFI_STATUS Status; Address = USB_MAX_DEVICES; Parent = HubIf->Device; Bus = Parent->Bus; HubApi = HubIf->HubApi; gBS->Stall (USB_WAIT_PORT_STABLE_STALL); // // Hub resets the device for at least 10 milliseconds. // Host learns device speed. If device is of low/full speed // and the hub is a EHCI root hub, ResetPort will release // the device to its companion UHCI and return an error. // Status = HubApi->ResetPort (HubIf, Port); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status)); return Status; } USB_DEBUG (("UsbEnumerateNewDev: hub port %d is reset\n", Port)); Child = UsbCreateDevice (HubIf, Port); if (Child == NULL) { return EFI_OUT_OF_RESOURCES; } // // OK, now identify the device speed. After reset, hub // fully knows the actual device speed. // Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to get speed of port %d\n", Port)); goto ON_ERROR; } if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { Child->Speed = EFI_USB_SPEED_LOW; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { Child->Speed = EFI_USB_SPEED_HIGH; } else { Child->Speed = EFI_USB_SPEED_FULL; } USB_DEBUG (("UsbEnumerateNewDev: device is of %d speed\n", Child->Speed)); if (Child->Speed != EFI_USB_SPEED_HIGH) { // // If the child isn't a high speed device, it is necessary to // set the transaction translator. Port TT is 1-based. // This is quite simple: // 1. if parent is of high speed, then parent is our translator // 2. otherwise use parent's translator. // if (Parent->Speed == EFI_USB_SPEED_HIGH) { Child->Translator.TranslatorHubAddress = Parent->Address; Child->Translator.TranslatorPortNumber = Port + 1; } else { Child->Translator = Parent->Translator; } USB_DEBUG (("UsbEnumerateNewDev: device uses translator (%d, %d)\n", Child->Translator.TranslatorHubAddress, Child->Translator.TranslatorPortNumber)); } // // After port is reset, hub establishes a signal path between // the device and host (DEFALUT state). Device¡¯s registers are // reset, use default address 0 (host enumerates one device at // a time) , and ready to respond to control transfer at EP 0. // // // Host sends a Get_Descriptor request to learn the max packet // size of default pipe (only part of the device¡¯s descriptor). // Status = UsbGetMaxPacketSize0 (Child); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); goto ON_ERROR; } USB_DEBUG (("UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); // // Host assigns an address to the device. Device completes the // status stage with default address, then switches to new address. // ADDRESS state. Address zero is reserved for root hub. // for (Address = 1; Address < USB_MAX_DEVICES; Address++) { if (Bus->Devices[Address] == NULL) { break; } } if (Address == USB_MAX_DEVICES) { USB_ERROR (("UsbEnumerateNewDev: address pool is full for port %d\n", Port)); Status = EFI_ACCESS_DENIED; goto ON_ERROR; } Bus->Devices[Address] = Child; Status = UsbSetAddress (Child, Address); Child->Address = Address; if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to set device address - %r\n", Status)); goto ON_ERROR; } gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL); USB_DEBUG (("UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address)); // // Host learns about the device¡¯s abilities by requesting device's // entire descriptions. // Status = UsbBuildDescTable (Child); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status)); goto ON_ERROR; } // // Select a default configuration: UEFI must set the configuration // before the driver can connect to the device. // Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue; Status = UsbSetConfig (Child, Config); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status)); goto ON_ERROR; } USB_DEBUG (("UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address)); // // Host assigns and loads a device driver. // Status = UsbSelectConfig (Child, Config); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to create interfaces - %r\n", Status)); goto ON_ERROR; } return EFI_SUCCESS; ON_ERROR: if (Address != USB_MAX_DEVICES) { Bus->Devices[Address] = NULL; } if (Child != NULL) { UsbFreeDevice (Child); } return Status; }
/** The enumeration routine to detect device change. @param PeiServices Describes the list of possible PEI Services. @param UsbHcPpi The pointer of PEI_USB_HOST_CONTROLLER_PPI instance. @param Usb2HcPpi The pointer of PEI_USB2_HOST_CONTROLLER_PPI instance. @retval EFI_SUCCESS The usb is enumerated successfully. @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. @retval Others Other failure occurs. **/ EFI_STATUS PeiUsbEnumeration ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_USB_HOST_CONTROLLER_PPI *UsbHcPpi, IN PEI_USB2_HOST_CONTROLLER_PPI *Usb2HcPpi ) { UINT8 NumOfRootPort; EFI_STATUS Status; UINT8 Index; EFI_USB_PORT_STATUS PortStatus; PEI_USB_DEVICE *PeiUsbDevice; UINTN MemPages; EFI_PHYSICAL_ADDRESS AllocateAddress; UINT8 CurrentAddress; UINTN InterfaceIndex; UINTN EndpointIndex; CurrentAddress = 0; if (Usb2HcPpi != NULL) { Usb2HcPpi->GetRootHubPortNumber ( PeiServices, Usb2HcPpi, (UINT8 *) &NumOfRootPort ); } else if (UsbHcPpi != NULL) { UsbHcPpi->GetRootHubPortNumber ( PeiServices, UsbHcPpi, (UINT8 *) &NumOfRootPort ); } else { ASSERT (FALSE); return EFI_INVALID_PARAMETER; } DEBUG ((EFI_D_INFO, "PeiUsbEnumeration: NumOfRootPort: %x\n", NumOfRootPort)); for (Index = 0; Index < NumOfRootPort; Index++) { // // First get root port status to detect changes happen // if (Usb2HcPpi != NULL) { Usb2HcPpi->GetRootHubPortStatus ( PeiServices, Usb2HcPpi, (UINT8) Index, &PortStatus ); } else { UsbHcPpi->GetRootHubPortStatus ( PeiServices, UsbHcPpi, (UINT8) Index, &PortStatus ); } DEBUG ((EFI_D_INFO, "USB Status --- Port: %x ConnectChange[%04x] Status[%04x]\n", Index, PortStatus.PortChangeStatus, PortStatus.PortStatus)); // // Only handle connection/enable/overcurrent/reset change. // if ((PortStatus.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { continue; } else { if (IsPortConnect (PortStatus.PortStatus)) { MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; Status = PeiServicesAllocatePages ( EfiBootServicesCode, MemPages, &AllocateAddress ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } PeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress); ZeroMem (PeiUsbDevice, sizeof (PEI_USB_DEVICE)); PeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE; PeiUsbDevice->DeviceAddress = 0; PeiUsbDevice->MaxPacketSize0 = 8; PeiUsbDevice->DataToggle = 0; CopyMem ( &(PeiUsbDevice->UsbIoPpi), &mUsbIoPpi, sizeof (PEI_USB_IO_PPI) ); CopyMem ( &(PeiUsbDevice->UsbIoPpiList), &mUsbIoPpiList, sizeof (EFI_PEI_PPI_DESCRIPTOR) ); PeiUsbDevice->UsbIoPpiList.Ppi = &PeiUsbDevice->UsbIoPpi; PeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress; PeiUsbDevice->UsbHcPpi = UsbHcPpi; PeiUsbDevice->Usb2HcPpi = Usb2HcPpi; PeiUsbDevice->IsHub = 0x0; PeiUsbDevice->DownStreamPortNo = 0x0; if (((PortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0) || ((PortStatus.PortStatus & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) == 0)) { // // If the port already has reset change flag and is connected and enabled, skip the port reset logic. // ResetRootPort ( PeiServices, PeiUsbDevice->UsbHcPpi, PeiUsbDevice->Usb2HcPpi, Index, 0 ); if (Usb2HcPpi != NULL) { Usb2HcPpi->GetRootHubPortStatus ( PeiServices, Usb2HcPpi, (UINT8) Index, &PortStatus ); } else { UsbHcPpi->GetRootHubPortStatus ( PeiServices, UsbHcPpi, (UINT8) Index, &PortStatus ); } } else { if (Usb2HcPpi != NULL) { Usb2HcPpi->ClearRootHubPortFeature ( PeiServices, Usb2HcPpi, (UINT8) Index, EfiUsbPortResetChange ); } else { UsbHcPpi->ClearRootHubPortFeature ( PeiServices, UsbHcPpi, (UINT8) Index, EfiUsbPortResetChange ); } } PeiUsbDevice->DeviceSpeed = (UINT8) PeiUsbGetDeviceSpeed (PortStatus.PortStatus); DEBUG ((EFI_D_INFO, "Device Speed =%d\n", PeiUsbDevice->DeviceSpeed)); if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ PeiUsbDevice->MaxPacketSize0 = 512; } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { PeiUsbDevice->MaxPacketSize0 = 64; } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_LOW_SPEED)) { PeiUsbDevice->MaxPacketSize0 = 8; } else { PeiUsbDevice->MaxPacketSize0 = 8; } // // Configure that Usb Device // Status = PeiConfigureUsbDevice ( PeiServices, PeiUsbDevice, Index, &CurrentAddress ); if (EFI_ERROR (Status)) { continue; } DEBUG ((EFI_D_INFO, "PeiUsbEnumeration: PeiConfigureUsbDevice Success\n")); Status = PeiServicesInstallPpi (&PeiUsbDevice->UsbIoPpiList); if (PeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { PeiUsbDevice->IsHub = 0x1; Status = PeiDoHubConfig (PeiServices, PeiUsbDevice); if (EFI_ERROR (Status)) { return Status; } PeiHubEnumeration (PeiServices, PeiUsbDevice, &CurrentAddress); } for (InterfaceIndex = 1; InterfaceIndex < PeiUsbDevice->ConfigDesc->NumInterfaces; InterfaceIndex++) { // // Begin to deal with the new device // MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; Status = PeiServicesAllocatePages ( EfiBootServicesCode, MemPages, &AllocateAddress ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } CopyMem ((VOID *)(UINTN)AllocateAddress, PeiUsbDevice, sizeof (PEI_USB_DEVICE)); PeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress); PeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress; PeiUsbDevice->UsbIoPpiList.Ppi = &PeiUsbDevice->UsbIoPpi; PeiUsbDevice->InterfaceDesc = PeiUsbDevice->InterfaceDescList[InterfaceIndex]; for (EndpointIndex = 0; EndpointIndex < PeiUsbDevice->InterfaceDesc->NumEndpoints; EndpointIndex++) { PeiUsbDevice->EndpointDesc[EndpointIndex] = PeiUsbDevice->EndpointDescList[InterfaceIndex][EndpointIndex]; } Status = PeiServicesInstallPpi (&PeiUsbDevice->UsbIoPpiList); if (PeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { PeiUsbDevice->IsHub = 0x1; Status = PeiDoHubConfig (PeiServices, PeiUsbDevice); if (EFI_ERROR (Status)) { return Status; } PeiHubEnumeration (PeiServices, PeiUsbDevice, &CurrentAddress); } } } else { // // Disconnect change happen, currently we don't support // } } } return EFI_SUCCESS; }
/** The Hub Enumeration just scans the hub ports one time. It also doesn't support hot-plug. @param PeiServices Describes the list of possible PEI Services. @param PeiUsbDevice The pointer of PEI_USB_DEVICE instance. @param CurrentAddress The DeviceAddress of usb device. @retval EFI_SUCCESS The usb hub is enumerated successfully. @retval EFI_OUT_OF_RESOURCES Can't allocate memory resource. @retval Others Other failure occurs. **/ EFI_STATUS PeiHubEnumeration ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_USB_DEVICE *PeiUsbDevice, IN UINT8 *CurrentAddress ) { UINTN Index; EFI_STATUS Status; PEI_USB_IO_PPI *UsbIoPpi; EFI_USB_PORT_STATUS PortStatus; UINTN MemPages; EFI_PHYSICAL_ADDRESS AllocateAddress; PEI_USB_DEVICE *NewPeiUsbDevice; UINTN InterfaceIndex; UINTN EndpointIndex; UsbIoPpi = &PeiUsbDevice->UsbIoPpi; DEBUG ((EFI_D_INFO, "PeiHubEnumeration: DownStreamPortNo: %x\n", PeiUsbDevice->DownStreamPortNo)); for (Index = 0; Index < PeiUsbDevice->DownStreamPortNo; Index++) { Status = PeiHubGetPortStatus ( PeiServices, UsbIoPpi, (UINT8) (Index + 1), (UINT32 *) &PortStatus ); if (EFI_ERROR (Status)) { continue; } DEBUG ((EFI_D_INFO, "USB Status --- Port: %x ConnectChange[%04x] Status[%04x]\n", Index, PortStatus.PortChangeStatus, PortStatus.PortStatus)); // // Only handle connection/enable/overcurrent/reset change. // if ((PortStatus.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { continue; } else { if (IsPortConnect (PortStatus.PortStatus)) { // // Begin to deal with the new device // MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; Status = PeiServicesAllocatePages ( EfiBootServicesCode, MemPages, &AllocateAddress ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } NewPeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress); ZeroMem (NewPeiUsbDevice, sizeof (PEI_USB_DEVICE)); NewPeiUsbDevice->Signature = PEI_USB_DEVICE_SIGNATURE; NewPeiUsbDevice->DeviceAddress = 0; NewPeiUsbDevice->MaxPacketSize0 = 8; NewPeiUsbDevice->DataToggle = 0; CopyMem ( &(NewPeiUsbDevice->UsbIoPpi), &mUsbIoPpi, sizeof (PEI_USB_IO_PPI) ); CopyMem ( &(NewPeiUsbDevice->UsbIoPpiList), &mUsbIoPpiList, sizeof (EFI_PEI_PPI_DESCRIPTOR) ); NewPeiUsbDevice->UsbIoPpiList.Ppi = &NewPeiUsbDevice->UsbIoPpi; NewPeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress; NewPeiUsbDevice->UsbHcPpi = PeiUsbDevice->UsbHcPpi; NewPeiUsbDevice->Usb2HcPpi = PeiUsbDevice->Usb2HcPpi; NewPeiUsbDevice->Tier = (UINT8) (PeiUsbDevice->Tier + 1); NewPeiUsbDevice->IsHub = 0x0; NewPeiUsbDevice->DownStreamPortNo = 0x0; if (((PortStatus.PortChangeStatus & USB_PORT_STAT_C_RESET) == 0) || ((PortStatus.PortStatus & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) == 0)) { // // If the port already has reset change flag and is connected and enabled, skip the port reset logic. // PeiResetHubPort (PeiServices, UsbIoPpi, (UINT8)(Index + 1)); PeiHubGetPortStatus ( PeiServices, UsbIoPpi, (UINT8) (Index + 1), (UINT32 *) &PortStatus ); } else { PeiHubClearPortFeature ( PeiServices, UsbIoPpi, (UINT8) (Index + 1), EfiUsbPortResetChange ); } NewPeiUsbDevice->DeviceSpeed = (UINT8) PeiUsbGetDeviceSpeed (PortStatus.PortStatus); DEBUG ((EFI_D_INFO, "Device Speed =%d\n", PeiUsbDevice->DeviceSpeed)); if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ NewPeiUsbDevice->MaxPacketSize0 = 512; } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { NewPeiUsbDevice->MaxPacketSize0 = 64; } else if (USB_BIT_IS_SET (PortStatus.PortStatus, USB_PORT_STAT_LOW_SPEED)) { NewPeiUsbDevice->MaxPacketSize0 = 8; } else { NewPeiUsbDevice->MaxPacketSize0 = 8; } if(NewPeiUsbDevice->DeviceSpeed != EFI_USB_SPEED_HIGH) { if (PeiUsbDevice->DeviceSpeed == EFI_USB_SPEED_HIGH) { NewPeiUsbDevice->Translator.TranslatorPortNumber = (UINT8)Index; NewPeiUsbDevice->Translator.TranslatorHubAddress = *CurrentAddress; } else { CopyMem(&(NewPeiUsbDevice->Translator), &(PeiUsbDevice->Translator), sizeof(EFI_USB2_HC_TRANSACTION_TRANSLATOR)); } } // // Configure that Usb Device // Status = PeiConfigureUsbDevice ( PeiServices, NewPeiUsbDevice, (UINT8) (Index + 1), CurrentAddress ); if (EFI_ERROR (Status)) { continue; } DEBUG ((EFI_D_INFO, "PeiHubEnumeration: PeiConfigureUsbDevice Success\n")); Status = PeiServicesInstallPpi (&NewPeiUsbDevice->UsbIoPpiList); if (NewPeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { NewPeiUsbDevice->IsHub = 0x1; Status = PeiDoHubConfig (PeiServices, NewPeiUsbDevice); if (EFI_ERROR (Status)) { return Status; } PeiHubEnumeration (PeiServices, NewPeiUsbDevice, CurrentAddress); } for (InterfaceIndex = 1; InterfaceIndex < NewPeiUsbDevice->ConfigDesc->NumInterfaces; InterfaceIndex++) { // // Begin to deal with the new device // MemPages = sizeof (PEI_USB_DEVICE) / EFI_PAGE_SIZE + 1; Status = PeiServicesAllocatePages ( EfiBootServicesCode, MemPages, &AllocateAddress ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } CopyMem ((VOID *)(UINTN)AllocateAddress, NewPeiUsbDevice, sizeof (PEI_USB_DEVICE)); NewPeiUsbDevice = (PEI_USB_DEVICE *) ((UINTN) AllocateAddress); NewPeiUsbDevice->AllocateAddress = (UINTN) AllocateAddress; NewPeiUsbDevice->UsbIoPpiList.Ppi = &NewPeiUsbDevice->UsbIoPpi; NewPeiUsbDevice->InterfaceDesc = NewPeiUsbDevice->InterfaceDescList[InterfaceIndex]; for (EndpointIndex = 0; EndpointIndex < NewPeiUsbDevice->InterfaceDesc->NumEndpoints; EndpointIndex++) { NewPeiUsbDevice->EndpointDesc[EndpointIndex] = NewPeiUsbDevice->EndpointDescList[InterfaceIndex][EndpointIndex]; } Status = PeiServicesInstallPpi (&NewPeiUsbDevice->UsbIoPpiList); if (NewPeiUsbDevice->InterfaceDesc->InterfaceClass == 0x09) { NewPeiUsbDevice->IsHub = 0x1; Status = PeiDoHubConfig (PeiServices, NewPeiUsbDevice); if (EFI_ERROR (Status)) { return Status; } PeiHubEnumeration (PeiServices, NewPeiUsbDevice, CurrentAddress); } } } } } return EFI_SUCCESS; }
/** Enumerate and configure the new device on the port of this HUB interface. @param HubIf The HUB that has the device connected. @param Port The port index of the hub (started with zero). @retval EFI_SUCCESS The device is enumerated (added or removed). @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device. @retval Others Failed to enumerate the device. **/ EFI_STATUS UsbEnumerateNewDev ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) { USB_BUS *Bus; USB_HUB_API *HubApi; USB_DEVICE *Child; USB_DEVICE *Parent; EFI_USB_PORT_STATUS PortState; UINTN Address; UINT8 Config; EFI_STATUS Status; Parent = HubIf->Device; Bus = Parent->Bus; HubApi = HubIf->HubApi; Address = Bus->MaxDevices; DBG("USB_WAIT_PORT_STABLE_STALL\n"); gBS->Stall (USB_WAIT_PORT_STABLE_STALL); //100ms // // Hub resets the device for at least 10 milliseconds. // Host learns device speed. If device is of low/full speed // and the hub is a EHCI root hub, ResetPort will release // the device to its companion UHCI and return an error. // Status = HubApi->ResetPort (HubIf, Port); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status)); return Status; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port)); Child = UsbCreateDevice (HubIf, Port); if (Child == NULL) { return EFI_OUT_OF_RESOURCES; } // // OK, now identify the device speed. After reset, hub // fully knows the actual device speed. // DBG("GetPortStatus\n"); Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get speed of port %d\n", Port)); goto ON_ERROR; } if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: No device presented at port %d\n", Port)); goto ON_ERROR; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ Child->Speed = EFI_USB_SPEED_SUPER; Child->MaxPacket0 = 512; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { Child->Speed = EFI_USB_SPEED_HIGH; Child->MaxPacket0 = 64; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { Child->Speed = EFI_USB_SPEED_LOW; Child->MaxPacket0 = 8; } else { Child->Speed = EFI_USB_SPEED_FULL; Child->MaxPacket0 = 8; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed)); DBG("UsbEnumerateNewDev: device is of %d speed\n", Child->Speed); if (((Child->Speed == EFI_USB_SPEED_LOW) || (Child->Speed == EFI_USB_SPEED_FULL)) && (Parent->Speed == EFI_USB_SPEED_HIGH)) { // // If the child is a low or full speed device, it is necessary to // set the transaction translator. Port TT is 1-based. // This is quite simple: // 1. if parent is of high speed, then parent is our translator // 2. otherwise use parent's translator. // Child->Translator.TranslatorHubAddress = Parent->Address; Child->Translator.TranslatorPortNumber = (UINT8) (Port + 1); } else { Child->Translator = Parent->Translator; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device uses translator (%d, %d)\n", // Child->Translator.TranslatorHubAddress, // Child->Translator.TranslatorPortNumber)); DBG("UsbEnumerateNewDev: device uses translator (%d, %d)\n", Child->Translator.TranslatorHubAddress, Child->Translator.TranslatorPortNumber); // // After port is reset, hub establishes a signal path between // the device and host (DEFALUT state). Device's registers are // reset, use default address 0 (host enumerates one device at // a time) , and ready to respond to control transfer at EP 0. // // // Host assigns an address to the device. Device completes the // status stage with default address, then switches to new address. // ADDRESS state. Address zero is reserved for root hub. // // ASSERT (Bus->MaxDevices <= 256); for (Address = 1; Address < Bus->MaxDevices; Address++) { if (Bus->Devices[Address] == NULL) { break; } } if (Address >= Bus->MaxDevices) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port)); DBG("UsbEnumerateNewDev: address pool is full for port %d\n", Port); Status = EFI_ACCESS_DENIED; goto ON_ERROR; } Status = UsbSetAddress (Child, (UINT8)Address); Child->Address = (UINT8)Address; Bus->Devices[Address] = Child; if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status)); goto ON_ERROR; } gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL); // DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address)); DBG("UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address); // // Host sends a Get_Descriptor request to learn the max packet // size of default pipe (only part of the device's descriptor). // Status = UsbGetMaxPacketSize0 (Child); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); DBG("UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status); goto ON_ERROR; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); // // Host learns about the device's abilities by requesting device's // entire descriptions. // Status = UsbBuildDescTable (Child); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status)); DBG("UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status); goto ON_ERROR; } // // Select a default configuration: UEFI must set the configuration // before the driver can connect to the device. // Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue; Status = UsbSetConfig (Child, Config); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status)); DBG("UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status); goto ON_ERROR; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address)); DBG("UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address); // // Host assigns and loads a device driver. // Status = UsbSelectConfig (Child, Config); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status)); DBG("UsbEnumerateNewDev: failed to create interfaces - %r\n", Status); goto ON_ERROR; } // // Report Status Code to indicate USB device has been detected by hotplug // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, (EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG), Bus->DevicePath ); return EFI_SUCCESS; ON_ERROR: // // If reach here, it means the enumeration process on a given port is interrupted due to error. // The s/w resources, including the assigned address(Address) and the allocated usb device data // structure(Bus->Devices[Address]), will NOT be freed here. These resources will be freed when // the device is unplugged from the port or DriverBindingStop() is invoked. // // This way is used to co-work with the lower layer EDKII UHCI/EHCI/XHCI host controller driver. // It's mainly because to keep UEFI spec unchanged EDKII XHCI driver have to maintain a state machine // to keep track of the mapping between actual address and request address. If the request address // (Address) is freed here, the Address value will be used by next enumerated device. Then EDKII XHCI // host controller driver will have wrong information, which will cause further transaction error. // // EDKII UHCI/EHCI doesn't get impacted as it's make sense to reserve s/w resource till it gets unplugged. // /* if (Address != Bus->MaxDevices) { Bus->Devices[Address] = NULL; } if (Child != NULL) { UsbFreeDevice (Child); } */ return Status; }
/** Send reset signal over the given root hub port. @param PeiServices General-purpose services that are available to every PEIM. @param UsbIoPpi Indicates the PEI_USB_IO_PPI instance. @param PortNum Usb hub port number (starting from 1). **/ VOID PeiResetHubPort ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_USB_IO_PPI *UsbIoPpi, IN UINT8 PortNum ) { EFI_STATUS Status; UINTN Index; EFI_USB_PORT_STATUS HubPortStatus; MicroSecondDelay (100 * 1000); // // reset root port // PeiHubSetPortFeature ( PeiServices, UsbIoPpi, PortNum, EfiUsbPortReset ); // // Drive the reset signal for worst 20ms. Check USB 2.0 Spec // section 7.1.7.5 for timing requirements. // MicroSecondDelay (USB_SET_PORT_RESET_STALL); // // Check USB_PORT_STAT_C_RESET bit to see if the resetting state is done. // ZeroMem (&HubPortStatus, sizeof (EFI_USB_PORT_STATUS)); for (Index = 0; Index < USB_WAIT_PORT_STS_CHANGE_LOOP; Index++) { Status = PeiHubGetPortStatus ( PeiServices, UsbIoPpi, PortNum, (UINT32 *) &HubPortStatus ); if (EFI_ERROR (Status)) { return; } if (USB_BIT_IS_SET (HubPortStatus.PortChangeStatus, USB_PORT_STAT_C_RESET)) { break; } MicroSecondDelay (USB_WAIT_PORT_STS_CHANGE_STALL); } if (Index == USB_WAIT_PORT_STS_CHANGE_LOOP) { DEBUG ((EFI_D_ERROR, "PeiResetHubPort: reset not finished in time on port %d\n", PortNum)); return; } // // clear reset change root port // PeiHubClearPortFeature ( PeiServices, UsbIoPpi, PortNum, EfiUsbPortResetChange ); MicroSecondDelay (1 * 1000); PeiHubClearPortFeature ( PeiServices, UsbIoPpi, PortNum, EfiUsbPortConnectChange ); // // Set port enable // PeiHubSetPortFeature ( PeiServices, UsbIoPpi, PortNum, EfiUsbPortEnable ); // // Clear any change status // PeiHubClearPortFeature ( PeiServices, UsbIoPpi, PortNum, EfiUsbPortEnableChange ); MicroSecondDelay (10 * 1000); return; }