]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbBusDxe / UsbEnumer.c
index 0aa896173dbfc1e52b13ff13de36d23009fd9865..0cded1eea7436254733d230a27d76e722f7410d2 100644 (file)
@@ -2,14 +2,8 @@
 \r
     Usb bus enumeration support.\r
 \r
-Copyright (c) 2007 - 2013, Intel Corporation. All rights reserved.<BR>\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -33,9 +27,9 @@ UsbGetEndpointDesc (
   USB_ENDPOINT_DESC       *EpDesc;\r
   UINT8                   Index;\r
   UINT8                   NumEndpoints;\r
-  \r
+\r
   NumEndpoints = UsbIf->IfSetting->Desc.NumEndpoints;\r
-  \r
+\r
   for (Index = 0; Index < NumEndpoints; Index++) {\r
     EpDesc = UsbIf->IfSetting->Endpoints[Index];\r
 \r
@@ -53,28 +47,33 @@ UsbGetEndpointDesc (
 \r
   @param  UsbIf                 The USB interface to free.\r
 \r
+  @retval EFI_ACCESS_DENIED     The interface is still occupied.\r
+  @retval EFI_SUCCESS           The interface is freed.\r
 **/\r
-VOID\r
+EFI_STATUS\r
 UsbFreeInterface (\r
   IN USB_INTERFACE        *UsbIf\r
   )\r
 {\r
-  UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);\r
+  EFI_STATUS              Status;\r
 \r
-  gBS->UninstallMultipleProtocolInterfaces (\r
-         UsbIf->Handle,\r
-         &gEfiDevicePathProtocolGuid,\r
-         UsbIf->DevicePath,\r
-         &gEfiUsbIoProtocolGuid,\r
-         &UsbIf->UsbIo,\r
-         NULL\r
-         );\r
+  UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);\r
 \r
-  if (UsbIf->DevicePath != NULL) {\r
-    FreePool (UsbIf->DevicePath);\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  UsbIf->Handle,\r
+                  &gEfiDevicePathProtocolGuid, UsbIf->DevicePath,\r
+                  &gEfiUsbIoProtocolGuid,      &UsbIf->UsbIo,\r
+                  NULL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    if (UsbIf->DevicePath != NULL) {\r
+      FreePool (UsbIf->DevicePath);\r
+    }\r
+    FreePool (UsbIf);\r
+  } else {\r
+    UsbOpenHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);\r
   }\r
-\r
-  FreePool (UsbIf);\r
+  return Status;\r
 }\r
 \r
 \r
@@ -274,7 +273,7 @@ UsbConnectDriver (
     // twisted TPL used. It should be no problem for us to connect\r
     // or disconnect at CALLBACK.\r
     //\r
-    \r
+\r
     //\r
     // Only recursively wanted usb child device\r
     //\r
@@ -318,7 +317,7 @@ UsbSelectSetting (
   )\r
 {\r
   USB_INTERFACE_SETTING   *Setting;\r
-  UINT8                   Index;\r
+  UINTN                   Index;\r
 \r
   //\r
   // Locate the active alternate setting\r
@@ -420,6 +419,7 @@ UsbSelectConfig (
     UsbIf = UsbCreateInterface (Device, ConfigDesc->Interfaces[Index]);\r
 \r
     if (UsbIf == NULL) {\r
+      Device->NumOfInterface = Index;\r
       return EFI_OUT_OF_RESOURCES;\r
     }\r
 \r
@@ -434,7 +434,11 @@ UsbSelectConfig (
     Status = UsbConnectDriver (UsbIf);\r
 \r
     if (EFI_ERROR (Status)) {\r
-      DEBUG ((EFI_D_ERROR, "UsbSelectConfig: failed to connect driver %r, ignored\n", Status));\r
+      DEBUG ((\r
+        DEBUG_WARN,\r
+        "UsbSelectConfig: failed to connect driver %r, ignored\n",\r
+        Status\r
+        ));\r
     }\r
   }\r
 \r
@@ -450,7 +454,7 @@ UsbSelectConfig (
   @param  UsbIf                 The interface to disconnect driver from.\r
 \r
 **/\r
-VOID\r
+EFI_STATUS\r
 UsbDisconnectDriver (\r
   IN USB_INTERFACE        *UsbIf\r
   )\r
@@ -462,8 +466,9 @@ UsbDisconnectDriver (
   // Release the hub if it's a hub controller, otherwise\r
   // disconnect the driver if it is managed by other drivers.\r
   //\r
+  Status = EFI_SUCCESS;\r
   if (UsbIf->IsHub) {\r
-    UsbIf->HubApi->Release (UsbIf);\r
+    Status = UsbIf->HubApi->Release (UsbIf);\r
 \r
   } else if (UsbIf->IsManaged) {\r
     //\r
@@ -479,13 +484,17 @@ UsbDisconnectDriver (
     gBS->RestoreTPL (TPL_CALLBACK);\r
 \r
     Status = gBS->DisconnectController (UsbIf->Handle, NULL, NULL);\r
-    UsbIf->IsManaged = FALSE;\r
+    if (!EFI_ERROR (Status)) {\r
+      UsbIf->IsManaged = FALSE;\r
+    }\r
 \r
     DEBUG (( EFI_D_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl(), Status));\r
     ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
 \r
     gBS->RaiseTPL (OldTpl);\r
   }\r
+\r
+  return Status;\r
 }\r
 \r
 \r
@@ -495,18 +504,21 @@ UsbDisconnectDriver (
   @param  Device                The USB device to remove configuration from.\r
 \r
 **/\r
-VOID\r
+EFI_STATUS\r
 UsbRemoveConfig (\r
   IN USB_DEVICE           *Device\r
   )\r
 {\r
   USB_INTERFACE           *UsbIf;\r
   UINTN                   Index;\r
+  EFI_STATUS              Status;\r
+  EFI_STATUS              ReturnStatus;\r
 \r
   //\r
   // Remove each interface of the device\r
   //\r
-  for (Index = 0; Index < Device->NumOfInterface; Index++) {    \r
+  ReturnStatus = EFI_SUCCESS;\r
+  for (Index = 0; Index < Device->NumOfInterface; Index++) {\r
     ASSERT (Index < USB_MAX_INTERFACE);\r
     UsbIf = Device->Interfaces[Index];\r
 \r
@@ -514,13 +526,23 @@ UsbRemoveConfig (
       continue;\r
     }\r
 \r
-    UsbDisconnectDriver (UsbIf);\r
-    UsbFreeInterface (UsbIf);\r
-    Device->Interfaces[Index] = NULL;\r
+    Status = UsbDisconnectDriver (UsbIf);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = UsbFreeInterface (UsbIf);\r
+      if (EFI_ERROR (Status)) {\r
+        UsbConnectDriver (UsbIf);\r
+      }\r
+    }\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      Device->Interfaces[Index] = NULL;\r
+    } else {\r
+      ReturnStatus = Status;\r
+    }\r
   }\r
 \r
   Device->ActiveConfig    = NULL;\r
-  Device->NumOfInterface  = 0;\r
+  return ReturnStatus;\r
 }\r
 \r
 \r
@@ -540,6 +562,7 @@ UsbRemoveDevice (
   USB_BUS                 *Bus;\r
   USB_DEVICE              *Child;\r
   EFI_STATUS              Status;\r
+  EFI_STATUS              ReturnStatus;\r
   UINTN                   Index;\r
 \r
   Bus = Device->Bus;\r
@@ -548,6 +571,7 @@ UsbRemoveDevice (
   // Remove all the devices on its downstream ports. Search from devices[1].\r
   // Devices[0] is the root hub.\r
   //\r
+  ReturnStatus = EFI_SUCCESS;\r
   for (Index = 1; Index < Bus->MaxDevices; Index++) {\r
     Child = Bus->Devices[Index];\r
 \r
@@ -557,21 +581,31 @@ UsbRemoveDevice (
 \r
     Status = UsbRemoveDevice (Child);\r
 \r
-    if (EFI_ERROR (Status)) {\r
-      DEBUG ((EFI_D_ERROR, "UsbRemoveDevice: failed to remove child, ignore error\n"));\r
+    if (!EFI_ERROR (Status)) {\r
       Bus->Devices[Index] = NULL;\r
+    } else {\r
+      Bus->Devices[Index]->DisconnectFail = TRUE;\r
+      ReturnStatus = Status;\r
+      DEBUG ((EFI_D_INFO, "UsbRemoveDevice: failed to remove child %p at parent %p\n", Child, Device));\r
     }\r
   }\r
 \r
-  UsbRemoveConfig (Device);\r
+  if (EFI_ERROR (ReturnStatus)) {\r
+    return ReturnStatus;\r
+  }\r
 \r
-  DEBUG (( EFI_D_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address));\r
+  Status = UsbRemoveConfig (Device);\r
 \r
-  ASSERT (Device->Address < Bus->MaxDevices);\r
-  Bus->Devices[Device->Address] = NULL;\r
-  UsbFreeDevice (Device);\r
+  if (!EFI_ERROR (Status)) {\r
+    DEBUG (( EFI_D_INFO, "UsbRemoveDevice: device %d removed\n", Device->Address));\r
 \r
-  return EFI_SUCCESS;\r
+    ASSERT (Device->Address < Bus->MaxDevices);\r
+    Bus->Devices[Device->Address] = NULL;\r
+    UsbFreeDevice (Device);\r
+  } else {\r
+    Bus->Devices[Device->Address]->DisconnectFail = TRUE;\r
+  }\r
+  return Status;\r
 }\r
 \r
 \r
@@ -618,6 +652,7 @@ UsbFindChild (
 \r
   @param  HubIf                 The HUB that has the device connected.\r
   @param  Port                  The port index of the hub (started with zero).\r
+  @param  ResetIsNeeded         The boolean to control whether skip the reset of the port.\r
 \r
   @retval EFI_SUCCESS           The device is enumerated (added or removed).\r
   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource for the device.\r
@@ -627,7 +662,8 @@ UsbFindChild (
 EFI_STATUS\r
 UsbEnumerateNewDev (\r
   IN USB_INTERFACE        *HubIf,\r
-  IN UINT8                Port\r
+  IN UINT8                Port,\r
+  IN BOOLEAN              ResetIsNeeded\r
   )\r
 {\r
   USB_BUS                 *Bus;\r
@@ -641,27 +677,29 @@ UsbEnumerateNewDev (
 \r
   Parent  = HubIf->Device;\r
   Bus     = Parent->Bus;\r
-  HubApi  = HubIf->HubApi;  \r
+  HubApi  = HubIf->HubApi;\r
   Address = Bus->MaxDevices;\r
 \r
   gBS->Stall (USB_WAIT_PORT_STABLE_STALL);\r
-  \r
+\r
   //\r
   // Hub resets the device for at least 10 milliseconds.\r
   // Host learns device speed. If device is of low/full speed\r
   // and the hub is a EHCI root hub, ResetPort will release\r
   // the device to its companion UHCI and return an error.\r
   //\r
-  Status = HubApi->ResetPort (HubIf, Port);\r
-\r
-  if (EFI_ERROR (Status)) {\r
-    DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));\r
+  if (ResetIsNeeded) {\r
+    Status = HubApi->ResetPort (HubIf, Port);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status));\r
 \r
-    return Status;\r
+      return Status;\r
+    }\r
+    DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port));\r
+  } else {\r
+    DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d reset is skipped\n", Port));\r
   }\r
 \r
-  DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port));\r
-\r
   Child = UsbCreateDevice (HubIf, Port);\r
 \r
   if (Child == NULL) {\r
@@ -883,68 +921,72 @@ UsbEnumeratePort (
   // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET.\r
   // ENABLE/RESET is used to reset port. SUSPEND isn't supported.\r
   //\r
-  \r
-  if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) {     \r
+\r
+  if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) {\r
 \r
     if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) {\r
       //\r
       // Case1:\r
-      //   Both OverCurrent and OverCurrentChange set, means over current occurs, \r
+      //   Both OverCurrent and OverCurrentChange set, means over current occurs,\r
       //   which probably is caused by short circuit. It has to wait system hardware\r
       //   to perform recovery.\r
       //\r
       DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port));\r
       return EFI_DEVICE_ERROR;\r
-      \r
-    } \r
+\r
+    }\r
     //\r
     // Case2:\r
-    //   Only OverCurrentChange set, means system has been recoveried from \r
+    //   Only OverCurrentChange set, means system has been recoveried from\r
     //   over current. As a result, all ports are nearly power-off, so\r
-    //   it's necessary to detach and enumerate all ports again. \r
+    //   it's necessary to detach and enumerate all ports again.\r
     //\r
-    DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port)); \r
+    DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port));\r
   }\r
 \r
-  if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) {  \r
+  if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) {\r
     //\r
     // Case3:\r
     //   1.1 roothub port reg doesn't reflect over-current state, while its counterpart\r
-    //   on 2.0 roothub does. When over-current has influence on 1.1 device, the port \r
+    //   on 2.0 roothub does. When over-current has influence on 1.1 device, the port\r
     //   would be disabled, so it's also necessary to detach and enumerate again.\r
     //\r
     DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port));\r
   }\r
-  \r
+\r
   if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) {\r
     //\r
     // Case4:\r
-    //   Device connected or disconnected normally. \r
+    //   Device connected or disconnected normally.\r
     //\r
-    DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: Device Connect/Disconnect Normally\n", Port));\r
+    DEBUG ((EFI_D_INFO, "UsbEnumeratePort: Device Connect/Disconnect Normally\n", Port));\r
   }\r
 \r
-  // \r
+  //\r
   // Following as the above cases, it's safety to remove and create again.\r
   //\r
   Child = UsbFindChild (HubIf, Port);\r
-  \r
+\r
   if (Child != NULL) {\r
     DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf));\r
     UsbRemoveDevice (Child);\r
   }\r
-  \r
+\r
   if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) {\r
     //\r
-    // Now, new device connected, enumerate and configure the device \r
+    // Now, new device connected, enumerate and configure the device\r
     //\r
     DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port));\r
-    Status = UsbEnumerateNewDev (HubIf, Port);\r
-  \r
+    if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_RESET)) {\r
+      Status = UsbEnumerateNewDev (HubIf, Port, FALSE);\r
+    } else {\r
+      Status = UsbEnumerateNewDev (HubIf, Port, TRUE);\r
+    }\r
+\r
   } else {\r
     DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port));\r
   }\r
-  \r
+\r
   HubApi->ClearPortChange (HubIf, Port);\r
   return Status;\r
 }\r
@@ -968,11 +1010,20 @@ UsbHubEnumeration (
   UINT8                   Byte;\r
   UINT8                   Bit;\r
   UINT8                   Index;\r
+  USB_DEVICE              *Child;\r
 \r
   ASSERT (Context != NULL);\r
 \r
   HubIf = (USB_INTERFACE *) Context;\r
 \r
+  for (Index = 0; Index < HubIf->NumOfPort; Index++) {\r
+    Child = UsbFindChild (HubIf, Index);\r
+    if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {\r
+      DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from hub %p, try again\n", Index, HubIf));\r
+      UsbRemoveDevice (Child);\r
+    }\r
+  }\r
+\r
   if (HubIf->ChangeMap == NULL) {\r
     return ;\r
   }\r
@@ -1015,10 +1066,17 @@ UsbRootHubEnumeration (
 {\r
   USB_INTERFACE           *RootHub;\r
   UINT8                   Index;\r
+  USB_DEVICE              *Child;\r
 \r
   RootHub = (USB_INTERFACE *) Context;\r
 \r
   for (Index = 0; Index < RootHub->NumOfPort; Index++) {\r
+    Child = UsbFindChild (RootHub, Index);\r
+    if ((Child != NULL) && (Child->DisconnectFail == TRUE)) {\r
+      DEBUG (( EFI_D_INFO, "UsbEnumeratePort: The device disconnect fails at port %d from root hub %p, try again\n", Index, RootHub));\r
+      UsbRemoveDevice (Child);\r
+    }\r
+\r
     UsbEnumeratePort (RootHub, Index);\r
   }\r
 }\r