X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FBus%2FPci%2FEhciDxe%2FEhci.c;h=0b7270f4e9305136b17425e05581a0f5a9be7f22;hb=1436aea4d5707e672672a11bda72be2c63c936c3;hp=53d11bf9cd4754ec7ae422ab8ee92606d113df82;hpb=aa79b0b3799e95bc21e0df32a135cc5a4d749e4b;p=mirror_edk2.git diff --git a/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.c b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.c index 53d11bf9cd..0b7270f4e9 100644 --- a/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.c +++ b/MdeModulePkg/Bus/Pci/EhciDxe/Ehci.c @@ -1,18 +1,20 @@ /** @file - The Ehci controller driver. -Copyright (c) 2006 - 2008, Intel Corporation -All rights reserved. This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php + EhciDxe driver is responsible for managing the behavior of EHCI controller. + It implements the interfaces of monitoring the status of all ports and transferring + Control, Bulk, Interrupt and Isochronous requests to Usb2.0 device. -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + Note that EhciDxe driver is enhanced to guarantee that the EHCI controller get attached + to the EHCI controller before a UHCI or OHCI driver attaches to the companion UHCI or + OHCI controller. This way avoids the control transfer on a shared port between EHCI + and companion host controller when UHCI or OHCI gets attached earlier than EHCI and a + USB 2.0 device inserts. -**/ +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent +**/ #include "Ehci.h" @@ -21,33 +23,33 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // to the UEFI protocol's port state (change). // USB_PORT_STATE_MAP mUsbPortStateMap[] = { - {PORTSC_CONN, USB_PORT_STAT_CONNECTION}, - {PORTSC_ENABLED, USB_PORT_STAT_ENABLE}, - {PORTSC_SUSPEND, USB_PORT_STAT_SUSPEND}, - {PORTSC_OVERCUR, USB_PORT_STAT_OVERCURRENT}, - {PORTSC_RESET, USB_PORT_STAT_RESET}, - {PORTSC_POWER, USB_PORT_STAT_POWER}, - {PORTSC_OWNER, USB_PORT_STAT_OWNER} + { PORTSC_CONN, USB_PORT_STAT_CONNECTION }, + { PORTSC_ENABLED, USB_PORT_STAT_ENABLE }, + { PORTSC_SUSPEND, USB_PORT_STAT_SUSPEND }, + { PORTSC_OVERCUR, USB_PORT_STAT_OVERCURRENT }, + { PORTSC_RESET, USB_PORT_STAT_RESET }, + { PORTSC_POWER, USB_PORT_STAT_POWER }, + { PORTSC_OWNER, USB_PORT_STAT_OWNER } }; USB_PORT_STATE_MAP mUsbPortChangeMap[] = { - {PORTSC_CONN_CHANGE, USB_PORT_STAT_C_CONNECTION}, - {PORTSC_ENABLE_CHANGE, USB_PORT_STAT_C_ENABLE}, - {PORTSC_OVERCUR_CHANGE, USB_PORT_STAT_C_OVERCURRENT} + { PORTSC_CONN_CHANGE, USB_PORT_STAT_C_CONNECTION }, + { PORTSC_ENABLE_CHANGE, USB_PORT_STAT_C_ENABLE }, + { PORTSC_OVERCUR_CHANGE, USB_PORT_STAT_C_OVERCURRENT } }; EFI_DRIVER_BINDING_PROTOCOL -gEhciDriverBinding = { + gEhciDriverBinding = { EhcDriverBindingSupported, EhcDriverBindingStart, EhcDriverBindingStop, - 0x10, + 0x30, NULL, NULL }; /** - Retrieves the capablility of root hub ports. + Retrieves the capability of root hub ports. @param This This EFI_USB_HC_PROTOCOL instance. @param MaxSpeed Max speed supported by the controller. @@ -68,27 +70,26 @@ EhcGetCapability ( OUT UINT8 *Is64BitCapable ) { - USB2_HC_DEV *Ehc; - EFI_TPL OldTpl; + USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; if ((MaxSpeed == NULL) || (PortNumber == NULL) || (Is64BitCapable == NULL)) { return EFI_INVALID_PARAMETER; } - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); *MaxSpeed = EFI_USB_SPEED_HIGH; - *PortNumber = (UINT8) (Ehc->HcStructParams & HCSP_NPORTS); - *Is64BitCapable = (UINT8) (Ehc->HcCapParams & HCCP_64BIT); + *PortNumber = (UINT8)(Ehc->HcStructParams & HCSP_NPORTS); + *Is64BitCapable = (UINT8)Ehc->Support64BitDma; - DEBUG ((EFI_D_INFO, "EhcGetCapability: %d ports, 64 bit %d\n", *PortNumber, *Is64BitCapable)); + DEBUG ((DEBUG_INFO, "EhcGetCapability: %d ports, 64 bit %d\n", *PortNumber, *Is64BitCapable)); gBS->RestoreTPL (OldTpl); return EFI_SUCCESS; } - /** Provides software reset for the USB host controller. @@ -105,68 +106,84 @@ EhcGetCapability ( EFI_STATUS EFIAPI EhcReset ( - IN EFI_USB2_HC_PROTOCOL *This, - IN UINT16 Attributes + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT16 Attributes ) { - USB2_HC_DEV *Ehc; - EFI_TPL OldTpl; - EFI_STATUS Status; + USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; + EFI_STATUS Status; - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + Ehc = EHC_FROM_THIS (This); - switch (Attributes) { - case EFI_USB_HC_RESET_GLOBAL: - // - // Flow through, same behavior as Host Controller Reset - // - case EFI_USB_HC_RESET_HOST_CONTROLLER: + if (Ehc->DevicePath != NULL) { // - // Host Controller must be Halt when Reset it + // Report Status Code to indicate reset happens // - if (!EhcIsHalt (Ehc)) { - Status = EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT); + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_USB | EFI_IOB_PC_RESET), + Ehc->DevicePath + ); + } - if (EFI_ERROR (Status)) { - Status = EFI_DEVICE_ERROR; - goto ON_EXIT; - } - } + OldTpl = gBS->RaiseTPL (EHC_TPL); + switch (Attributes) { + case EFI_USB_HC_RESET_GLOBAL: // - // Clean up the asynchronous transfers, currently only - // interrupt supports asynchronous operation. + // Flow through, same behavior as Host Controller Reset // - EhciDelAllAsyncIntTransfers (Ehc); - EhcAckAllInterrupt (Ehc); - EhcFreeSched (Ehc); + case EFI_USB_HC_RESET_HOST_CONTROLLER: + // + // Host Controller must be Halt when Reset it + // + if (EhcIsDebugPortInUse (Ehc, NULL)) { + Status = EFI_SUCCESS; + goto ON_EXIT; + } - Status = EhcResetHC (Ehc, EHC_RESET_TIMEOUT); + if (!EhcIsHalt (Ehc)) { + Status = EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT); - if (EFI_ERROR (Status)) { - goto ON_EXIT; - } + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto ON_EXIT; + } + } - Status = EhcInitHC (Ehc); - break; + // + // Clean up the asynchronous transfers, currently only + // interrupt supports asynchronous operation. + // + EhciDelAllAsyncIntTransfers (Ehc); + EhcAckAllInterrupt (Ehc); + EhcFreeSched (Ehc); - case EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG: - case EFI_USB_HC_RESET_HOST_WITH_DEBUG: - Status = EFI_UNSUPPORTED; - break; + Status = EhcResetHC (Ehc, EHC_RESET_TIMEOUT); - default: - Status = EFI_INVALID_PARAMETER; + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = EhcInitHC (Ehc); + break; + + case EFI_USB_HC_RESET_GLOBAL_WITH_DEBUG: + case EFI_USB_HC_RESET_HOST_WITH_DEBUG: + Status = EFI_UNSUPPORTED; + break; + + default: + Status = EFI_INVALID_PARAMETER; } ON_EXIT: - DEBUG ((EFI_D_INFO, "EhcReset: exit status %r\n", Status)); + DEBUG ((DEBUG_INFO, "EhcReset: exit status %r\n", Status)); gBS->RestoreTPL (OldTpl); return Status; } - /** Retrieve the current state of the USB host controller. @@ -183,19 +200,19 @@ ON_EXIT: EFI_STATUS EFIAPI EhcGetState ( - IN CONST EFI_USB2_HC_PROTOCOL *This, - OUT EFI_USB_HC_STATE *State + IN EFI_USB2_HC_PROTOCOL *This, + OUT EFI_USB_HC_STATE *State ) { - EFI_TPL OldTpl; - USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; + USB2_HC_DEV *Ehc; if (State == NULL) { return EFI_INVALID_PARAMETER; } - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); if (EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) { *State = EfiUsbHcStateHalt; @@ -205,11 +222,10 @@ EhcGetState ( gBS->RestoreTPL (OldTpl); - DEBUG ((EFI_D_INFO, "EhcGetState: current state %d\n", *State)); + DEBUG ((DEBUG_INFO, "EhcGetState: current state %d\n", *State)); return EFI_SUCCESS; } - /** Sets the USB host controller to a specific state. @@ -225,14 +241,14 @@ EhcGetState ( EFI_STATUS EFIAPI EhcSetState ( - IN EFI_USB2_HC_PROTOCOL *This, - IN EFI_USB_HC_STATE State + IN EFI_USB2_HC_PROTOCOL *This, + IN EFI_USB_HC_STATE State ) { - USB2_HC_DEV *Ehc; - EFI_TPL OldTpl; - EFI_STATUS Status; - EFI_USB_HC_STATE CurState; + USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; + EFI_STATUS Status; + EFI_USB_HC_STATE CurState; Status = EhcGetState (This, &CurState); @@ -244,47 +260,46 @@ EhcSetState ( return EFI_SUCCESS; } - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); switch (State) { - case EfiUsbHcStateHalt: - Status = EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT); - break; - - case EfiUsbHcStateOperational: - if (EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR)) { - Status = EFI_DEVICE_ERROR; + case EfiUsbHcStateHalt: + Status = EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT); break; - } - // - // Software must not write a one to this field unless the host controller - // is in the Halted state. Doing so will yield undefined results. - // refers to Spec[EHCI1.0-2.3.1] - // - if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) { - Status = EFI_DEVICE_ERROR; - break; - } + case EfiUsbHcStateOperational: + if (EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR)) { + Status = EFI_DEVICE_ERROR; + break; + } - Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT); - break; + // + // Software must not write a one to this field unless the host controller + // is in the Halted state. Doing so will yield undefined results. + // refers to Spec[EHCI1.0-2.3.1] + // + if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) { + Status = EFI_DEVICE_ERROR; + break; + } - case EfiUsbHcStateSuspend: - Status = EFI_UNSUPPORTED; - break; + Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT); + break; - default: - Status = EFI_INVALID_PARAMETER; + case EfiUsbHcStateSuspend: + Status = EFI_UNSUPPORTED; + break; + + default: + Status = EFI_INVALID_PARAMETER; } - DEBUG ((EFI_D_INFO, "EhcSetState: exit status %r\n", Status)); + DEBUG ((DEBUG_INFO, "EhcSetState: exit status %r\n", Status)); gBS->RestoreTPL (OldTpl); return Status; } - /** Retrieves the current status of a USB root hub port. @@ -302,28 +317,28 @@ EhcSetState ( EFI_STATUS EFIAPI EhcGetRootHubPortStatus ( - IN CONST EFI_USB2_HC_PROTOCOL *This, - IN CONST UINT8 PortNumber, - OUT EFI_USB_PORT_STATUS *PortStatus + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus ) { - USB2_HC_DEV *Ehc; - EFI_TPL OldTpl; - UINT32 Offset; - UINT32 State; - UINT32 TotalPort; - UINTN Index; - UINTN MapSize; - EFI_STATUS Status; + USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + UINTN Index; + UINTN MapSize; + EFI_STATUS Status; if (PortStatus == NULL) { return EFI_INVALID_PARAMETER; } - OldTpl = gBS->RaiseTPL (EHC_TPL); + OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); - Status = EFI_SUCCESS; + Ehc = EHC_FROM_THIS (This); + Status = EFI_SUCCESS; TotalPort = (Ehc->HcStructParams & HCSP_NPORTS); @@ -332,11 +347,15 @@ EhcGetRootHubPortStatus ( goto ON_EXIT; } - Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber)); - PortStatus->PortStatus = 0; - PortStatus->PortChangeStatus = 0; + Offset = (UINT32)(EHC_PORT_STAT_OFFSET + (4 * PortNumber)); + PortStatus->PortStatus = 0; + PortStatus->PortChangeStatus = 0; + + if (EhcIsDebugPortInUse (Ehc, &PortNumber)) { + goto ON_EXIT; + } - State = EhcReadOpReg (Ehc, Offset); + State = EhcReadOpReg (Ehc, Offset); // // Identify device speed. If in K state, it is low speed. @@ -346,7 +365,6 @@ EhcGetRootHubPortStatus ( // if (EHC_BIT_IS_SET (State, PORTSC_LINESTATE_K)) { PortStatus->PortStatus |= USB_PORT_STAT_LOW_SPEED; - } else if (EHC_BIT_IS_SET (State, PORTSC_ENABLED)) { PortStatus->PortStatus |= USB_PORT_STAT_HIGH_SPEED; } @@ -358,7 +376,7 @@ EhcGetRootHubPortStatus ( for (Index = 0; Index < MapSize; Index++) { if (EHC_BIT_IS_SET (State, mUsbPortStateMap[Index].HwState)) { - PortStatus->PortStatus = (UINT16) (PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState); + PortStatus->PortStatus = (UINT16)(PortStatus->PortStatus | mUsbPortStateMap[Index].UefiState); } } @@ -366,7 +384,7 @@ EhcGetRootHubPortStatus ( for (Index = 0; Index < MapSize; Index++) { if (EHC_BIT_IS_SET (State, mUsbPortChangeMap[Index].HwState)) { - PortStatus->PortChangeStatus = (UINT16) (PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState); + PortStatus->PortChangeStatus = (UINT16)(PortStatus->PortChangeStatus | mUsbPortChangeMap[Index].UefiState); } } @@ -375,7 +393,6 @@ ON_EXIT: return Status; } - /** Sets a feature for the specified root hub port. @@ -396,16 +413,16 @@ EhcSetRootHubPortFeature ( IN EFI_USB_PORT_FEATURE PortFeature ) { - USB2_HC_DEV *Ehc; - EFI_TPL OldTpl; - UINT32 Offset; - UINT32 State; - UINT32 TotalPort; - EFI_STATUS Status; - - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); - Status = EFI_SUCCESS; + USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + EFI_STATUS Status; + + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); + Status = EFI_SUCCESS; TotalPort = (Ehc->HcStructParams & HCSP_NPORTS); @@ -414,8 +431,8 @@ EhcSetRootHubPortFeature ( goto ON_EXIT; } - Offset = (UINT32) (EHC_PORT_STAT_OFFSET + (4 * PortNumber)); - State = EhcReadOpReg (Ehc, Offset); + Offset = (UINT32)(EHC_PORT_STAT_OFFSET + (4 * PortNumber)); + State = EhcReadOpReg (Ehc, Offset); // // Mask off the port status change bits, these bits are @@ -424,65 +441,68 @@ EhcSetRootHubPortFeature ( State &= ~PORTSC_CHANGE_MASK; switch (PortFeature) { - case EfiUsbPortEnable: - // - // Sofeware can't set this bit, Port can only be enable by - // EHCI as a part of the reset and enable - // - State |= PORTSC_ENABLED; - EhcWriteOpReg (Ehc, Offset, State); - break; - - case EfiUsbPortSuspend: - State |= PORTSC_SUSPEND; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortEnable: + // + // Sofeware can't set this bit, Port can only be enable by + // EHCI as a part of the reset and enable + // + State |= PORTSC_ENABLED; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortReset: - // - // Make sure Host Controller not halt before reset it - // - if (EhcIsHalt (Ehc)) { - Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT); + case EfiUsbPortSuspend: + State |= PORTSC_SUSPEND; + EhcWriteOpReg (Ehc, Offset, State); + break; - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_INFO, "EhcSetRootHubPortFeature :failed to start HC - %r\n", Status)); - break; + case EfiUsbPortReset: + // + // Make sure Host Controller not halt before reset it + // + if (EhcIsHalt (Ehc)) { + Status = EhcRunHC (Ehc, EHC_GENERIC_TIMEOUT); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "EhcSetRootHubPortFeature :failed to start HC - %r\n", Status)); + break; + } } - } - // - // Set one to PortReset bit must also set zero to PortEnable bit - // - State |= PORTSC_RESET; - State &= ~PORTSC_ENABLED; - EhcWriteOpReg (Ehc, Offset, State); - break; + // + // Set one to PortReset bit must also set zero to PortEnable bit + // + State |= PORTSC_RESET; + State &= ~PORTSC_ENABLED; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortPower: - // - // Not supported, ignore the operation - // - Status = EFI_SUCCESS; - break; + case EfiUsbPortPower: + // + // Set port power bit when PPC is 1 + // + if ((Ehc->HcCapParams & HCSP_PPC) == HCSP_PPC) { + State |= PORTSC_POWER; + EhcWriteOpReg (Ehc, Offset, State); + } - case EfiUsbPortOwner: - State |= PORTSC_OWNER; - EhcWriteOpReg (Ehc, Offset, State); - break; + break; - default: - Status = EFI_INVALID_PARAMETER; + case EfiUsbPortOwner: + State |= PORTSC_OWNER; + EhcWriteOpReg (Ehc, Offset, State); + break; + + default: + Status = EFI_INVALID_PARAMETER; } ON_EXIT: - DEBUG ((EFI_D_INFO, "EhcSetRootHubPortFeature: exit status %r\n", Status)); + DEBUG ((DEBUG_INFO, "EhcSetRootHubPortFeature: exit status %r\n", Status)); gBS->RestoreTPL (OldTpl); return Status; } - /** Clears a feature for the specified root hub port. @@ -506,16 +526,16 @@ EhcClearRootHubPortFeature ( IN EFI_USB_PORT_FEATURE PortFeature ) { - USB2_HC_DEV *Ehc; - EFI_TPL OldTpl; - UINT32 Offset; - UINT32 State; - UINT32 TotalPort; - EFI_STATUS Status; - - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); - Status = EFI_SUCCESS; + USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; + UINT32 Offset; + UINT32 State; + UINT32 TotalPort; + EFI_STATUS Status; + + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); + Status = EFI_SUCCESS; TotalPort = (Ehc->HcStructParams & HCSP_NPORTS); @@ -524,91 +544,99 @@ EhcClearRootHubPortFeature ( goto ON_EXIT; } - Offset = EHC_PORT_STAT_OFFSET + (4 * PortNumber); - State = EhcReadOpReg (Ehc, Offset); + Offset = EHC_PORT_STAT_OFFSET + (4 * PortNumber); + State = EhcReadOpReg (Ehc, Offset); State &= ~PORTSC_CHANGE_MASK; switch (PortFeature) { - case EfiUsbPortEnable: - // - // Clear PORT_ENABLE feature means disable port. - // - State &= ~PORTSC_ENABLED; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortEnable: + // + // Clear PORT_ENABLE feature means disable port. + // + State &= ~PORTSC_ENABLED; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortSuspend: - // - // A write of zero to this bit is ignored by the host - // controller. The host controller will unconditionally - // set this bit to a zero when: - // 1. software sets the Forct Port Resume bit to a zero from a one. - // 2. software sets the Port Reset bit to a one frome a zero. - // - State &= ~PORSTSC_RESUME; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortSuspend: + // + // A write of zero to this bit is ignored by the host + // controller. The host controller will unconditionally + // set this bit to a zero when: + // 1. software sets the Forct Port Resume bit to a zero from a one. + // 2. software sets the Port Reset bit to a one frome a zero. + // + State &= ~PORSTSC_RESUME; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortReset: - // - // Clear PORT_RESET means clear the reset signal. - // - State &= ~PORTSC_RESET; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortReset: + // + // Clear PORT_RESET means clear the reset signal. + // + State &= ~PORTSC_RESET; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortOwner: - // - // Clear port owner means this port owned by EHC - // - State &= ~PORTSC_OWNER; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortOwner: + // + // Clear port owner means this port owned by EHC + // + State &= ~PORTSC_OWNER; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortConnectChange: - // - // Clear connect status change - // - State |= PORTSC_CONN_CHANGE; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortConnectChange: + // + // Clear connect status change + // + State |= PORTSC_CONN_CHANGE; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortEnableChange: - // - // Clear enable status change - // - State |= PORTSC_ENABLE_CHANGE; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortEnableChange: + // + // Clear enable status change + // + State |= PORTSC_ENABLE_CHANGE; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortOverCurrentChange: - // - // Clear PortOverCurrent change - // - State |= PORTSC_OVERCUR_CHANGE; - EhcWriteOpReg (Ehc, Offset, State); - break; + case EfiUsbPortOverCurrentChange: + // + // Clear PortOverCurrent change + // + State |= PORTSC_OVERCUR_CHANGE; + EhcWriteOpReg (Ehc, Offset, State); + break; - case EfiUsbPortPower: - case EfiUsbPortSuspendChange: - case EfiUsbPortResetChange: - // - // Not supported or not related operation - // - break; + case EfiUsbPortPower: + // + // Clear port power bit when PPC is 1 + // + if ((Ehc->HcCapParams & HCSP_PPC) == HCSP_PPC) { + State &= ~PORTSC_POWER; + EhcWriteOpReg (Ehc, Offset, State); + } - default: - Status = EFI_INVALID_PARAMETER; - break; + break; + case EfiUsbPortSuspendChange: + case EfiUsbPortResetChange: + // + // Not supported or not related operation + // + break; + + default: + Status = EFI_INVALID_PARAMETER; + break; } ON_EXIT: - DEBUG ((EFI_D_INFO, "EhcClearRootHubPortFeature: exit status %r\n", Status)); + DEBUG ((DEBUG_INFO, "EhcClearRootHubPortFeature: exit status %r\n", Status)); gBS->RestoreTPL (OldTpl); return Status; } - /** Submits control transfer to a target USB device. @@ -649,11 +677,11 @@ EhcControlTransfer ( OUT UINT32 *TransferResult ) { - USB2_HC_DEV *Ehc; - URB *Urb; - EFI_TPL OldTpl; - UINT8 Endpoint; - EFI_STATUS Status; + USB2_HC_DEV *Ehc; + URB *Urb; + EFI_TPL OldTpl; + UINT8 Endpoint; + EFI_STATUS Status; // // Validate parameters @@ -664,22 +692,26 @@ EhcControlTransfer ( if ((TransferDirection != EfiUsbDataIn) && (TransferDirection != EfiUsbDataOut) && - (TransferDirection != EfiUsbNoData)) { + (TransferDirection != EfiUsbNoData)) + { return EFI_INVALID_PARAMETER; } if ((TransferDirection == EfiUsbNoData) && - ((Data != NULL) || (*DataLength != 0))) { + ((Data != NULL) || (*DataLength != 0))) + { return EFI_INVALID_PARAMETER; } if ((TransferDirection != EfiUsbNoData) && - ((Data == NULL) || (*DataLength == 0))) { + ((Data == NULL) || (*DataLength == 0))) + { return EFI_INVALID_PARAMETER; } if ((MaximumPacketLength != 8) && (MaximumPacketLength != 16) && - (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) { + (MaximumPacketLength != 32) && (MaximumPacketLength != 64)) + { return EFI_INVALID_PARAMETER; } @@ -687,14 +719,14 @@ EhcControlTransfer ( return EFI_INVALID_PARAMETER; } - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); Status = EFI_DEVICE_ERROR; *TransferResult = EFI_USB_ERR_SYSTEM; if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) { - DEBUG ((EFI_D_ERROR, "EhcControlTransfer: HC halted at entrance\n")); + DEBUG ((DEBUG_ERROR, "EhcControlTransfer: HC halted at entrance\n")); EhcAckAllInterrupt (Ehc); goto ON_EXIT; @@ -711,26 +743,26 @@ EhcControlTransfer ( // endpoint is bidirectional. EhcCreateUrb expects this // combination of Ep addr and its direction. // - Endpoint = (UINT8) (0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0)); - Urb = EhcCreateUrb ( - Ehc, - DeviceAddress, - Endpoint, - DeviceSpeed, - 0, - MaximumPacketLength, - Translator, - EHC_CTRL_TRANSFER, - Request, - Data, - *DataLength, - NULL, - NULL, - 1 - ); + Endpoint = (UINT8)(0 | ((TransferDirection == EfiUsbDataIn) ? 0x80 : 0)); + Urb = EhcCreateUrb ( + Ehc, + DeviceAddress, + Endpoint, + DeviceSpeed, + 0, + MaximumPacketLength, + Translator, + EHC_CTRL_TRANSFER, + Request, + Data, + *DataLength, + NULL, + NULL, + 1 + ); if (Urb == NULL) { - DEBUG ((EFI_D_ERROR, "EhcControlTransfer: failed to create URB")); + DEBUG ((DEBUG_ERROR, "EhcControlTransfer: failed to create URB")); Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; @@ -759,13 +791,12 @@ ON_EXIT: gBS->RestoreTPL (OldTpl); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcControlTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + DEBUG ((DEBUG_ERROR, "EhcControlTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); } return Status; } - /** Submits bulk transfer to a bulk endpoint of a USB device. @@ -813,16 +844,17 @@ EhcBulkTransfer ( OUT UINT32 *TransferResult ) { - USB2_HC_DEV *Ehc; - URB *Urb; - EFI_TPL OldTpl; - EFI_STATUS Status; + USB2_HC_DEV *Ehc; + URB *Urb; + EFI_TPL OldTpl; + EFI_STATUS Status; // // Validate the parameters // if ((DataLength == NULL) || (*DataLength == 0) || - (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) { + (Data == NULL) || (Data[0] == NULL) || (TransferResult == NULL)) + { return EFI_INVALID_PARAMETER; } @@ -832,18 +864,19 @@ EhcBulkTransfer ( if ((DeviceSpeed == EFI_USB_SPEED_LOW) || ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || - ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) { + ((EFI_USB_SPEED_HIGH == DeviceSpeed) && (MaximumPacketLength > 512))) + { return EFI_INVALID_PARAMETER; } - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); *TransferResult = EFI_USB_ERR_SYSTEM; Status = EFI_DEVICE_ERROR; if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) { - DEBUG ((EFI_D_ERROR, "EhcBulkTransfer: HC is halted\n")); + DEBUG ((DEBUG_ERROR, "EhcBulkTransfer: HC is halted\n")); EhcAckAllInterrupt (Ehc); goto ON_EXIT; @@ -873,7 +906,7 @@ EhcBulkTransfer ( ); if (Urb == NULL) { - DEBUG ((EFI_D_ERROR, "EhcBulkTransfer: failed to create URB\n")); + DEBUG ((DEBUG_ERROR, "EhcBulkTransfer: failed to create URB\n")); Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; @@ -899,13 +932,12 @@ ON_EXIT: gBS->RestoreTPL (OldTpl); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcBulkTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + DEBUG ((DEBUG_ERROR, "EhcBulkTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); } return Status; } - /** Submits an asynchronous interrupt transfer to an interrupt endpoint of a USB device. @@ -938,25 +970,24 @@ ON_EXIT: EFI_STATUS EFIAPI EhcAsyncInterruptTransfer ( - IN EFI_USB2_HC_PROTOCOL * This, - IN UINT8 DeviceAddress, - IN UINT8 EndPointAddress, - IN UINT8 DeviceSpeed, - IN UINTN MaximumPacketLength, - IN BOOLEAN IsNewTransfer, - IN OUT UINT8 *DataToggle, - IN UINTN PollingInterval, - IN UINTN DataLength, - IN EFI_USB2_HC_TRANSACTION_TRANSLATOR * Translator, - IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, - IN VOID *Context OPTIONAL + IN EFI_USB2_HC_PROTOCOL *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINTN MaximumPacketLength, + IN BOOLEAN IsNewTransfer, + IN OUT UINT8 *DataToggle, + IN UINTN PollingInterval, + IN UINTN DataLength, + IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Translator, + IN EFI_ASYNC_USB_TRANSFER_CALLBACK CallBackFunction, + IN VOID *Context OPTIONAL ) { - USB2_HC_DEV *Ehc; - URB *Urb; - EFI_TPL OldTpl; - EFI_STATUS Status; - UINT8 *Data; + USB2_HC_DEV *Ehc; + URB *Urb; + EFI_TPL OldTpl; + EFI_STATUS Status; // // Validate parameters @@ -979,8 +1010,8 @@ EhcAsyncInterruptTransfer ( } } - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); // // Delete Async interrupt transfer request. DataToggle will return @@ -989,14 +1020,14 @@ EhcAsyncInterruptTransfer ( if (!IsNewTransfer) { Status = EhciDelAsyncIntTransfer (Ehc, DeviceAddress, EndPointAddress, DataToggle); - DEBUG ((EFI_D_INFO, "EhcAsyncInterruptTransfer: remove old transfer - %r\n", Status)); + DEBUG ((DEBUG_INFO, "EhcAsyncInterruptTransfer: remove old transfer - %r\n", Status)); goto ON_EXIT; } Status = EFI_SUCCESS; if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) { - DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: HC is halt\n")); + DEBUG ((DEBUG_ERROR, "EhcAsyncInterruptTransfer: HC is halt\n")); EhcAckAllInterrupt (Ehc); Status = EFI_DEVICE_ERROR; @@ -1005,16 +1036,7 @@ EhcAsyncInterruptTransfer ( EhcAckAllInterrupt (Ehc); - Data = AllocatePool (DataLength); - - if (Data == NULL) { - DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: failed to allocate buffer\n")); - - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - Urb = EhcCreateUrb ( + Urb = EhciInsertAsyncIntTransfer ( Ehc, DeviceAddress, EndPointAddress, @@ -1022,9 +1044,6 @@ EhcAsyncInterruptTransfer ( *DataToggle, MaximumPacketLength, Translator, - EHC_INT_TRANSFER_ASYNC, - NULL, - Data, DataLength, CallBackFunction, Context, @@ -1032,20 +1051,10 @@ EhcAsyncInterruptTransfer ( ); if (Urb == NULL) { - DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: failed to create URB\n")); - - gBS->FreePool (Data); Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; } - // - // New asynchronous transfer must inserted to the head. - // Check the comments in EhcMoniteAsyncRequests - // - EhcLinkQhToPeriod (Ehc, Urb->Qh); - InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList); - ON_EXIT: Ehc->PciIo->Flush (Ehc->PciIo); gBS->RestoreTPL (OldTpl); @@ -1053,7 +1062,6 @@ ON_EXIT: return Status; } - /** Submits synchronous interrupt transfer to an interrupt endpoint of a USB device. @@ -1097,20 +1105,17 @@ EhcSyncInterruptTransfer ( OUT UINT32 *TransferResult ) { - USB2_HC_DEV *Ehc; - EFI_TPL OldTpl; - URB *Urb; - EFI_STATUS Status; + USB2_HC_DEV *Ehc; + EFI_TPL OldTpl; + URB *Urb; + EFI_STATUS Status; // // Validates parameters // if ((DataLength == NULL) || (*DataLength == 0) || - (Data == NULL) || (TransferResult == NULL)) { - return EFI_INVALID_PARAMETER; - } - - if (!EHCI_IS_DATAIN (EndPointAddress)) { + (Data == NULL) || (TransferResult == NULL)) + { return EFI_INVALID_PARAMETER; } @@ -1120,18 +1125,19 @@ EhcSyncInterruptTransfer ( if (((DeviceSpeed == EFI_USB_SPEED_LOW) && (MaximumPacketLength != 8)) || ((DeviceSpeed == EFI_USB_SPEED_FULL) && (MaximumPacketLength > 64)) || - ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 3072))) { + ((DeviceSpeed == EFI_USB_SPEED_HIGH) && (MaximumPacketLength > 3072))) + { return EFI_INVALID_PARAMETER; } - OldTpl = gBS->RaiseTPL (EHC_TPL); - Ehc = EHC_FROM_THIS (This); + OldTpl = gBS->RaiseTPL (EHC_TPL); + Ehc = EHC_FROM_THIS (This); *TransferResult = EFI_USB_ERR_SYSTEM; Status = EFI_DEVICE_ERROR; if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) { - DEBUG ((EFI_D_ERROR, "EhcSyncInterruptTransfer: HC is halt\n")); + DEBUG ((DEBUG_ERROR, "EhcSyncInterruptTransfer: HC is halt\n")); EhcAckAllInterrupt (Ehc); goto ON_EXIT; @@ -1157,7 +1163,7 @@ EhcSyncInterruptTransfer ( ); if (Urb == NULL) { - DEBUG ((EFI_D_ERROR, "EhcSyncInterruptTransfer: failed to create URB\n")); + DEBUG ((DEBUG_ERROR, "EhcSyncInterruptTransfer: failed to create URB\n")); Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; @@ -1175,18 +1181,18 @@ EhcSyncInterruptTransfer ( Status = EFI_SUCCESS; } + EhcFreeUrb (Ehc, Urb); ON_EXIT: Ehc->PciIo->Flush (Ehc->PciIo); gBS->RestoreTPL (OldTpl); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcSyncInterruptTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); + DEBUG ((DEBUG_ERROR, "EhcSyncInterruptTransfer: error - %r, transfer - %x\n", Status, *TransferResult)); } return Status; } - /** Submits isochronous transfer to a target USB device. @@ -1226,7 +1232,6 @@ EhcIsochronousTransfer ( return EFI_UNSUPPORTED; } - /** Submits Async isochronous transfer to a target USB device. @@ -1282,8 +1287,8 @@ EhcAsyncIsochronousTransfer ( EFI_STATUS EFIAPI EhcDriverEntryPoint ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable ) { return EfiLibInstallDriverBindingComponentName2 ( @@ -1296,7 +1301,6 @@ EhcDriverEntryPoint ( ); } - /** Test to see if this driver supports ControllerHandle. Any ControllerHandle that has Usb2HcProtocol installed will @@ -1313,14 +1317,14 @@ EhcDriverEntryPoint ( EFI_STATUS EFIAPI EhcDriverBindingSupported ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { - EFI_STATUS Status; - EFI_PCI_IO_PROTOCOL *PciIo; - USB_CLASSC UsbClassCReg; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + USB_CLASSC UsbClassCReg; // // Test whether there is PCI IO Protocol attached on the controller handle. @@ -1328,7 +1332,7 @@ EhcDriverBindingSupported ( Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, - (VOID **) &PciIo, + (VOID **)&PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER @@ -1341,7 +1345,7 @@ EhcDriverBindingSupported ( Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint8, - EHC_PCI_CLASSC, + PCI_CLASSCODE_OFFSET, sizeof (USB_CLASSC) / sizeof (UINT8), &UsbClassCReg ); @@ -1354,10 +1358,9 @@ EhcDriverBindingSupported ( // // Test whether the controller belongs to Ehci type // - if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || - (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) || - (UsbClassCReg.PI != EHC_PCI_CLASSC_PI)) { - + if ( (UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) + || ((UsbClassCReg.ProgInterface != PCI_IF_EHCI) && (UsbClassCReg.ProgInterface != PCI_IF_UHCI) && (UsbClassCReg.ProgInterface != PCI_IF_OHCI))) + { Status = EFI_UNSUPPORTED; } @@ -1372,11 +1375,134 @@ ON_EXIT: return Status; } +/** + Get the usb debug port related information. + + @param Ehc The EHCI device. + + @retval RETURN_SUCCESS Get debug port number, bar and offset successfully. + @retval Others The usb host controller does not supported usb debug port capability. + +**/ +EFI_STATUS +EhcGetUsbDebugPortInfo ( + IN USB2_HC_DEV *Ehc + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT16 PciStatus; + UINT8 CapabilityPtr; + UINT8 CapabilityId; + UINT16 DebugPort; + EFI_STATUS Status; + + ASSERT (Ehc->PciIo != NULL); + PciIo = Ehc->PciIo; + + // + // Detect if the EHCI host controller support Capaility Pointer. + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_PRIMARY_STATUS_OFFSET, + sizeof (UINT16), + &PciStatus + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((PciStatus & EFI_PCI_STATUS_CAPABILITY) == 0) { + // + // The Pci Device Doesn't Support Capability Pointer. + // + return EFI_UNSUPPORTED; + } + + // + // Get Pointer To Capability List + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_CAPBILITY_POINTER_OFFSET, + 1, + &CapabilityPtr + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find Capability ID 0xA, Which Is For Debug Port + // + while (CapabilityPtr != 0) { + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + CapabilityPtr, + 1, + &CapabilityId + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (CapabilityId == EHC_DEBUG_PORT_CAP_ID) { + break; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + CapabilityPtr + 1, + 1, + &CapabilityPtr + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // No Debug Port Capability Found + // + if (CapabilityPtr == 0) { + return EFI_UNSUPPORTED; + } + + // + // Get The Base Address Of Debug Port Register In Debug Port Capability Register + // + Status = PciIo->Pci.Read ( + Ehc->PciIo, + EfiPciIoWidthUint8, + CapabilityPtr + 2, + sizeof (UINT16), + &DebugPort + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Ehc->DebugPortOffset = DebugPort & 0x1FFF; + Ehc->DebugPortBarNum = (UINT8)((DebugPort >> 13) - 1); + Ehc->DebugPortNum = (UINT8)((Ehc->HcStructParams & 0x00F00000) >> 20); + + return EFI_SUCCESS; +} /** Create and initialize a USB2_HC_DEV. @param PciIo The PciIo on this device. + @param DevicePath The device path of host controller. @param OriginalPciAttributes Original PCI attributes. @return The allocated and initialized USB2_HC_DEV structure if created, @@ -1385,12 +1511,13 @@ ON_EXIT: **/ USB2_HC_DEV * EhcCreateUsb2Hc ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINT64 OriginalPciAttributes + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN UINT64 OriginalPciAttributes ) { - USB2_HC_DEV *Ehc; - EFI_STATUS Status; + USB2_HC_DEV *Ehc; + EFI_STATUS Status; Ehc = AllocateZeroPool (sizeof (USB2_HC_DEV)); @@ -1401,25 +1528,26 @@ EhcCreateUsb2Hc ( // // Init EFI_USB2_HC_PROTOCOL interface and private data structure // - Ehc->Signature = USB2_HC_DEV_SIGNATURE; - - Ehc->Usb2Hc.GetCapability = EhcGetCapability; - Ehc->Usb2Hc.Reset = EhcReset; - Ehc->Usb2Hc.GetState = EhcGetState; - Ehc->Usb2Hc.SetState = EhcSetState; - Ehc->Usb2Hc.ControlTransfer = EhcControlTransfer; - Ehc->Usb2Hc.BulkTransfer = EhcBulkTransfer; - Ehc->Usb2Hc.AsyncInterruptTransfer = EhcAsyncInterruptTransfer; - Ehc->Usb2Hc.SyncInterruptTransfer = EhcSyncInterruptTransfer; - Ehc->Usb2Hc.IsochronousTransfer = EhcIsochronousTransfer; - Ehc->Usb2Hc.AsyncIsochronousTransfer = EhcAsyncIsochronousTransfer; - Ehc->Usb2Hc.GetRootHubPortStatus = EhcGetRootHubPortStatus; - Ehc->Usb2Hc.SetRootHubPortFeature = EhcSetRootHubPortFeature; - Ehc->Usb2Hc.ClearRootHubPortFeature = EhcClearRootHubPortFeature; - Ehc->Usb2Hc.MajorRevision = 0x2; - Ehc->Usb2Hc.MinorRevision = 0x0; + Ehc->Signature = USB2_HC_DEV_SIGNATURE; + + Ehc->Usb2Hc.GetCapability = EhcGetCapability; + Ehc->Usb2Hc.Reset = EhcReset; + Ehc->Usb2Hc.GetState = EhcGetState; + Ehc->Usb2Hc.SetState = EhcSetState; + Ehc->Usb2Hc.ControlTransfer = EhcControlTransfer; + Ehc->Usb2Hc.BulkTransfer = EhcBulkTransfer; + Ehc->Usb2Hc.AsyncInterruptTransfer = EhcAsyncInterruptTransfer; + Ehc->Usb2Hc.SyncInterruptTransfer = EhcSyncInterruptTransfer; + Ehc->Usb2Hc.IsochronousTransfer = EhcIsochronousTransfer; + Ehc->Usb2Hc.AsyncIsochronousTransfer = EhcAsyncIsochronousTransfer; + Ehc->Usb2Hc.GetRootHubPortStatus = EhcGetRootHubPortStatus; + Ehc->Usb2Hc.SetRootHubPortFeature = EhcSetRootHubPortFeature; + Ehc->Usb2Hc.ClearRootHubPortFeature = EhcClearRootHubPortFeature; + Ehc->Usb2Hc.MajorRevision = 0x2; + Ehc->Usb2Hc.MinorRevision = 0x0; Ehc->PciIo = PciIo; + Ehc->DevicePath = DevicePath; Ehc->OriginalPciAttributes = OriginalPciAttributes; InitializeListHead (&Ehc->AsyncIntTransfers); @@ -1428,15 +1556,25 @@ EhcCreateUsb2Hc ( Ehc->HcCapParams = EhcReadCapRegister (Ehc, EHC_HCCPARAMS_OFFSET); Ehc->CapLen = EhcReadCapRegister (Ehc, EHC_CAPLENGTH_OFFSET) & 0x0FF; - DEBUG ((EFI_D_INFO, "EhcCreateUsb2Hc: capability length %d\n", Ehc->CapLen)); + DEBUG ((DEBUG_INFO, "EhcCreateUsb2Hc: capability length %d\n", Ehc->CapLen)); + + // + // EHCI Controllers with a CapLen of 0 are ignored. + // + if (Ehc->CapLen == 0) { + gBS->FreePool (Ehc); + return NULL; + } + + EhcGetUsbDebugPortInfo (Ehc); // // Create AsyncRequest Polling Timer // Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL, - TPL_CALLBACK, - EhcMoniteAsyncRequests, + TPL_NOTIFY, + EhcMonitorAsyncRequests, Ehc, &Ehc->PollTimer ); @@ -1449,6 +1587,30 @@ EhcCreateUsb2Hc ( return Ehc; } +/** + One notified function to stop the Host Controller when gBS->ExitBootServices() called. + + @param Event Pointer to this event + @param Context Event handler private data + +**/ +VOID +EFIAPI +EhcExitBootService ( + EFI_EVENT Event, + VOID *Context + ) + +{ + USB2_HC_DEV *Ehc; + + Ehc = (USB2_HC_DEV *)Context; + + // + // Reset the Host Controller + // + EhcResetHC (Ehc, EHC_RESET_TIMEOUT); +} /** Starting the Usb EHCI Driver. @@ -1466,17 +1628,31 @@ EhcCreateUsb2Hc ( EFI_STATUS EFIAPI EhcDriverBindingStart ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { - EFI_STATUS Status; - USB2_HC_DEV *Ehc; - EFI_PCI_IO_PROTOCOL *PciIo; - UINT64 Supports; - UINT64 OriginalPciAttributes; - BOOLEAN PciAttributesSaved; + EFI_STATUS Status; + USB2_HC_DEV *Ehc; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_PCI_IO_PROTOCOL *Instance; + UINT64 Supports; + UINT64 OriginalPciAttributes; + BOOLEAN PciAttributesSaved; + USB_CLASSC UsbClassCReg; + EFI_HANDLE *HandleBuffer; + UINTN NumberOfHandles; + UINTN Index; + UINTN CompanionSegmentNumber; + UINTN CompanionBusNumber; + UINTN CompanionDeviceNumber; + UINTN CompanionFunctionNumber; + UINTN EhciSegmentNumber; + UINTN EhciBusNumber; + UINTN EhciDeviceNumber; + UINTN EhciFunctionNumber; + EFI_DEVICE_PATH_PROTOCOL *HcDevicePath; // // Open the PciIo Protocol, then enable the USB host controller @@ -1484,17 +1660,29 @@ EhcDriverBindingStart ( Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, - (VOID **) &PciIo, + (VOID **)&PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to open PCI_IO\n")); - return EFI_DEVICE_ERROR; + return Status; } + // + // Open Device Path Protocol for on USB host controller + // + HcDevicePath = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **)&HcDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + PciAttributesSaved = FALSE; // // Save original PCI attributes @@ -1509,6 +1697,7 @@ EhcDriverBindingStart ( if (EFI_ERROR (Status)) { goto CLOSE_PCIIO; } + PciAttributesSaved = TRUE; Status = PciIo->Attributes ( @@ -1518,32 +1707,162 @@ EhcDriverBindingStart ( &Supports ); if (!EFI_ERROR (Status)) { - Supports &= EFI_PCI_DEVICE_ENABLE; - Status = PciIo->Attributes ( - PciIo, - EfiPciIoAttributeOperationEnable, - Supports, - NULL - ); + Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EhcDriverBindingStart: failed to enable controller\n")); + goto CLOSE_PCIIO; } + // + // Get the Pci device class code. + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_CLASSCODE_OFFSET, + sizeof (USB_CLASSC) / sizeof (UINT8), + &UsbClassCReg + ); + if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to enable controller\n")); + Status = EFI_UNSUPPORTED; + goto CLOSE_PCIIO; + } + + // + // Determine if the device is UHCI or OHCI host controller or not. If yes, then find out the + // companion usb ehci host controller and force EHCI driver get attached to it before + // UHCI or OHCI driver attaches to UHCI or OHCI host controller. + // + if (((UsbClassCReg.ProgInterface == PCI_IF_UHCI) || (UsbClassCReg.ProgInterface == PCI_IF_OHCI)) && + (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) && + (UsbClassCReg.SubClassCode == PCI_CLASS_SERIAL_USB)) + { + Status = PciIo->GetLocation ( + PciIo, + &CompanionSegmentNumber, + &CompanionBusNumber, + &CompanionDeviceNumber, + &CompanionFunctionNumber + ); + if (EFI_ERROR (Status)) { + goto CLOSE_PCIIO; + } + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + goto CLOSE_PCIIO; + } + + for (Index = 0; Index < NumberOfHandles; Index++) { + // + // Get the device path on this handle + // + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiPciIoProtocolGuid, + (VOID **)&Instance + ); + ASSERT_EFI_ERROR (Status); + + Status = Instance->Pci.Read ( + Instance, + EfiPciIoWidthUint8, + PCI_CLASSCODE_OFFSET, + sizeof (USB_CLASSC) / sizeof (UINT8), + &UsbClassCReg + ); + + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto CLOSE_PCIIO; + } + + if ((UsbClassCReg.ProgInterface == PCI_IF_EHCI) && + (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) && + (UsbClassCReg.SubClassCode == PCI_CLASS_SERIAL_USB)) + { + Status = Instance->GetLocation ( + Instance, + &EhciSegmentNumber, + &EhciBusNumber, + &EhciDeviceNumber, + &EhciFunctionNumber + ); + if (EFI_ERROR (Status)) { + goto CLOSE_PCIIO; + } + + // + // Currently, the judgment on the companion usb host controller is through the + // same bus number, which may vary on different platform. + // + if (EhciBusNumber == CompanionBusNumber) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + EhcDriverBindingStart (This, HandleBuffer[Index], NULL); + } + } + } + + Status = EFI_NOT_FOUND; goto CLOSE_PCIIO; } // // Create then install USB2_HC_PROTOCOL // - Ehc = EhcCreateUsb2Hc (PciIo, OriginalPciAttributes); + Ehc = EhcCreateUsb2Hc (PciIo, HcDevicePath, OriginalPciAttributes); if (Ehc == NULL) { - DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to create USB2_HC\n")); + DEBUG ((DEBUG_ERROR, "EhcDriverBindingStart: failed to create USB2_HC\n")); Status = EFI_OUT_OF_RESOURCES; goto CLOSE_PCIIO; } + // + // Enable 64-bit DMA support in the PCI layer if this controller + // supports it. + // + if (EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT)) { + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE, + NULL + ); + if (!EFI_ERROR (Status)) { + Ehc->Support64BitDma = TRUE; + } else { + DEBUG (( + DEBUG_WARN, + "%a: failed to enable 64-bit DMA on 64-bit capable controller @ %p (%r)\n", + __FUNCTION__, + Controller, + Status + )); + } + } + Status = gBS->InstallProtocolInterface ( &Controller, &gEfiUsb2HcProtocolGuid, @@ -1552,23 +1871,26 @@ EhcDriverBindingStart ( ); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to install USB2_HC Protocol\n")); + DEBUG ((DEBUG_ERROR, "EhcDriverBindingStart: failed to install USB2_HC Protocol\n")); goto FREE_POOL; } // - // Robustnesss improvement such as for UoL + // Robustnesss improvement such as for Duet platform // Default is not required. // if (FeaturePcdGet (PcdTurnOffUsbLegacySupport)) { EhcClearLegacySupport (Ehc); } - EhcResetHC (Ehc, EHC_RESET_TIMEOUT); + + if (!EhcIsDebugPortInUse (Ehc, NULL)) { + EhcResetHC (Ehc, EHC_RESET_TIMEOUT); + } Status = EhcInitHC (Ehc); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to init host controller\n")); + DEBUG ((DEBUG_ERROR, "EhcDriverBindingStart: failed to init host controller\n")); goto UNINSTALL_USBHC; } @@ -1578,12 +1900,27 @@ EhcDriverBindingStart ( Status = gBS->SetTimer (Ehc->PollTimer, TimerPeriodic, EHC_ASYNC_POLL_INTERVAL); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to start async interrupt monitor\n")); + DEBUG ((DEBUG_ERROR, "EhcDriverBindingStart: failed to start async interrupt monitor\n")); EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT); goto UNINSTALL_USBHC; } + // + // Create event to stop the HC when exit boot service. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + EhcExitBootService, + Ehc, + &gEfiEventExitBootServicesGuid, + &Ehc->ExitBootServiceEvent + ); + if (EFI_ERROR (Status)) { + goto UNINSTALL_USBHC; + } + // // Install the component name protocol, don't fail the start // because of something for display. @@ -1603,8 +1940,7 @@ EhcDriverBindingStart ( FALSE ); - - DEBUG ((EFI_D_INFO, "EhcDriverBindingStart: EHCI started for controller @ %p\n", Controller)); + DEBUG ((DEBUG_INFO, "EhcDriverBindingStart: EHCI started for controller @ %p\n", Controller)); return EFI_SUCCESS; UNINSTALL_USBHC: @@ -1625,11 +1961,11 @@ CLOSE_PCIIO: // Restore original PCI attributes // PciIo->Attributes ( - PciIo, - EfiPciIoAttributeOperationSet, - OriginalPciAttributes, - NULL - ); + PciIo, + EfiPciIoAttributeOperationSet, + OriginalPciAttributes, + NULL + ); } gBS->CloseProtocol ( @@ -1642,9 +1978,8 @@ CLOSE_PCIIO: return Status; } - /** - Stop this driver on ControllerHandle. Support stoping any child handles + Stop this driver on ControllerHandle. Support stopping any child handles created by this driver. @param This Protocol instance pointer. @@ -1659,10 +1994,10 @@ CLOSE_PCIIO: EFI_STATUS EFIAPI EhcDriverBindingStop ( - IN EFI_DRIVER_BINDING_PROTOCOL *This, - IN EFI_HANDLE Controller, - IN UINTN NumberOfChildren, - IN EFI_HANDLE *ChildHandleBuffer + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer ) { EFI_STATUS Status; @@ -1678,7 +2013,7 @@ EhcDriverBindingStop ( Status = gBS->OpenProtocol ( Controller, &gEfiUsb2HcProtocolGuid, - (VOID **) &Usb2Hc, + (VOID **)&Usb2Hc, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL @@ -1691,13 +2026,6 @@ EhcDriverBindingStop ( Ehc = EHC_FROM_THIS (Usb2Hc); PciIo = Ehc->PciIo; - // - // Stop AsyncRequest Polling timer then stop the EHCI driver - // and uninstall the EHCI protocl. - // - gBS->SetTimer (Ehc->PollTimer, TimerCancel, EHC_ASYNC_POLL_INTERVAL); - EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT); - Status = gBS->UninstallProtocolInterface ( Controller, &gEfiUsb2HcProtocolGuid, @@ -1708,25 +2036,42 @@ EhcDriverBindingStop ( return Status; } + // + // Stop AsyncRequest Polling timer then stop the EHCI driver + // and uninstall the EHCI protocl. + // + gBS->SetTimer (Ehc->PollTimer, TimerCancel, EHC_ASYNC_POLL_INTERVAL); + EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT); + if (Ehc->PollTimer != NULL) { gBS->CloseEvent (Ehc->PollTimer); } + if (Ehc->ExitBootServiceEvent != NULL) { + gBS->CloseEvent (Ehc->ExitBootServiceEvent); + } + EhcFreeSched (Ehc); if (Ehc->ControllerNameTable != NULL) { FreeUnicodeStringTable (Ehc->ControllerNameTable); } + // + // Disable routing of all ports to EHCI controller, so all ports are + // routed back to the UHCI or OHCI controller. + // + EhcClearOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC); + // // Restore original PCI attributes // PciIo->Attributes ( - PciIo, - EfiPciIoAttributeOperationSet, - Ehc->OriginalPciAttributes, - NULL - ); + PciIo, + EfiPciIoAttributeOperationSet, + Ehc->OriginalPciAttributes, + NULL + ); gBS->CloseProtocol ( Controller, @@ -1739,4 +2084,3 @@ EhcDriverBindingStop ( return EFI_SUCCESS; } -