]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatform.c
MdeModulePkg/ConPlatform: Support short-form USB device path
[mirror_edk2.git] / MdeModulePkg / Universal / Console / ConPlatformDxe / ConPlatform.c
index 5fa7facfcac809393f9e7632a10d4e1723a89d60..27df8a4b56b6afa0b7f2b9c6b24d078e47f5ce75 100644 (file)
@@ -202,8 +202,7 @@ ConPlatformDriverBindingSupported (
   Start this driver on ControllerHandle by opening Simple Text Input Protocol,\r
   reading Device Path, and installing Console In Devcice GUID on ControllerHandle.\r
 \r
-  If this devcie is not one hot-plug devce, append its device path into the\r
-  console environment variables ConInDev.\r
+  Append its device path into the console environment variables ConInDev.\r
 \r
   @param  This                 Protocol instance pointer.\r
   @param  ControllerHandle     Handle of device to bind driver to\r
@@ -270,58 +269,32 @@ ConPlatformTextInDriverBindingStart (
   }\r
 \r
   //\r
-  // Check the device path, if it is a hot plug device,\r
-  // do not put the device path into ConInDev, and install\r
-  // gEfiConsoleInDeviceGuid to the device handle directly.\r
-  // The policy is, make hot plug device plug in and play immediately.\r
+  // Append the device path to the ConInDev environment variable\r
   //\r
-  if (IsHotPlugDevice (DevicePath)) {\r
+  ConPlatformUpdateDeviceVariable (\r
+    L"ConInDev",\r
+    DevicePath,\r
+    Append\r
+    );\r
+\r
+  //\r
+  // If the device path is an instance in the ConIn environment variable,\r
+  // then install EfiConsoleInDeviceGuid onto ControllerHandle\r
+  //\r
+  if (IsInConInVariable) {\r
     gBS->InstallMultipleProtocolInterfaces (\r
            &ControllerHandle,\r
            &gEfiConsoleInDeviceGuid,\r
            NULL,\r
            NULL\r
            );\r
-    //\r
-    // Append the device path to ConInDev only if it is in ConIn variable.\r
-    //\r
-    if (IsInConInVariable) {\r
-      ConPlatformUpdateDeviceVariable (\r
-        L"ConInDev",\r
-        DevicePath,\r
-        Append\r
-        );\r
-    }\r
   } else {\r
-    //\r
-    // If it is not a hot-plug device, append the device path to the\r
-    // ConInDev environment variable\r
-    //\r
-    ConPlatformUpdateDeviceVariable (\r
-      L"ConInDev",\r
-      DevicePath,\r
-      Append\r
-      );\r
-\r
-    //\r
-    // If the device path is an instance in the ConIn environment variable,\r
-    // then install EfiConsoleInDeviceGuid onto ControllerHandle\r
-    //\r
-    if (IsInConInVariable) {\r
-      gBS->InstallMultipleProtocolInterfaces (\r
-             &ControllerHandle,\r
-             &gEfiConsoleInDeviceGuid,\r
-             NULL,\r
-             NULL\r
-             );\r
-    } else {\r
-      gBS->CloseProtocol (\r
-             ControllerHandle,\r
-             &gEfiSimpleTextInProtocolGuid,\r
-             This->DriverBindingHandle,\r
-             ControllerHandle\r
-             );\r
-    }\r
+    gBS->CloseProtocol (\r
+           ControllerHandle,\r
+           &gEfiSimpleTextInProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           ControllerHandle\r
+           );\r
   }\r
 \r
   return EFI_SUCCESS;\r
@@ -334,8 +307,7 @@ ConPlatformTextInDriverBindingStart (
   reading Device Path, and installing Console Out Devcic GUID, Standard Error\r
   Device GUID on ControllerHandle.\r
 \r
-  If this devcie is not one hot-plug devce, append its device path into the\r
-  console environment variables ConOutDev, ErrOutDev.\r
+  Append its device path into the console environment variables ConOutDev, ErrOutDev.\r
 \r
   @param  This                 Protocol instance pointer.\r
   @param  ControllerHandle     Handle of device to bind driver to\r
@@ -416,95 +388,59 @@ ConPlatformTextOutDriverBindingStart (
   }\r
 \r
   //\r
-  // Check the device path, if it is a hot plug device,\r
-  // do not put the device path into ConOutDev and ErrOutDev,\r
-  // and install gEfiConsoleOutDeviceGuid to the device handle directly.\r
-  // The policy is, make hot plug device plug in and play immediately.\r
+  // Append the device path to the ConOutDev and ErrOutDev environment variable.\r
+  // For GOP device path, append the sibling device path as well.\r
   //\r
-  if (IsHotPlugDevice (DevicePath)) {\r
+  if (!ConPlatformUpdateGopCandidate (DevicePath)) {\r
+    ConPlatformUpdateDeviceVariable (\r
+      L"ConOutDev",\r
+      DevicePath,\r
+      Append\r
+      );\r
+    //\r
+    // Then append the device path to the ErrOutDev environment variable\r
+    //\r
+    ConPlatformUpdateDeviceVariable (\r
+      L"ErrOutDev",\r
+      DevicePath,\r
+      Append\r
+      );\r
+  }\r
+\r
+  //\r
+  // If the device path is an instance in the ConOut environment variable,\r
+  // then install EfiConsoleOutDeviceGuid onto ControllerHandle\r
+  //\r
+  if (IsInConOutVariable) {\r
+    NeedClose = FALSE;\r
+    Status = gBS->InstallMultipleProtocolInterfaces (\r
+                    &ControllerHandle,\r
+                    &gEfiConsoleOutDeviceGuid,\r
+                    NULL,\r
+                    NULL\r
+                    );\r
+  }\r
+  //\r
+  // If the device path is an instance in the ErrOut environment variable,\r
+  // then install EfiStandardErrorDeviceGuid onto ControllerHandle\r
+  //\r
+  if (IsInErrOutVariable) {\r
+    NeedClose = FALSE;\r
     gBS->InstallMultipleProtocolInterfaces (\r
            &ControllerHandle,\r
-           &gEfiConsoleOutDeviceGuid,\r
+           &gEfiStandardErrorDeviceGuid,\r
            NULL,\r
            NULL\r
            );\r
-    //\r
-    // Append the device path to ConOutDev only if it is in ConOut variable.\r
-    //\r
-    if (IsInConOutVariable) {\r
-      ConPlatformUpdateDeviceVariable (\r
-        L"ConOutDev",\r
-        DevicePath,\r
-        Append\r
-        );\r
-    }\r
-    //\r
-    // Append the device path to ErrOutDev only if it is in ErrOut variable.\r
-    //\r
-    if (IsInErrOutVariable) {\r
-      ConPlatformUpdateDeviceVariable (\r
-        L"ErrOutDev",\r
-        DevicePath,\r
-        Append\r
-        );\r
-    }\r
-  } else {\r
-    //\r
-    // If it is not a hot-plug device, append the device path to\r
-    // the ConOutDev and ErrOutDev environment variable.\r
-    // For GOP device path, append the sibling device path as well.\r
-    //\r
-    if (!ConPlatformUpdateGopCandidate (DevicePath)) {\r
-      ConPlatformUpdateDeviceVariable (\r
-        L"ConOutDev",\r
-        DevicePath,\r
-        Append\r
-        );\r
-      //\r
-      // Then append the device path to the ErrOutDev environment variable\r
-      //\r
-      ConPlatformUpdateDeviceVariable (\r
-        L"ErrOutDev",\r
-        DevicePath,\r
-        Append\r
-        );\r
-    }\r
-\r
-    //\r
-    // If the device path is an instance in the ConOut environment variable,\r
-    // then install EfiConsoleOutDeviceGuid onto ControllerHandle\r
-    //\r
-    if (IsInConOutVariable) {\r
-      NeedClose = FALSE;\r
-      Status = gBS->InstallMultipleProtocolInterfaces (\r
-                      &ControllerHandle,\r
-                      &gEfiConsoleOutDeviceGuid,\r
-                      NULL,\r
-                      NULL\r
-                      );\r
-    }\r
-    //\r
-    // If the device path is an instance in the ErrOut environment variable,\r
-    // then install EfiStandardErrorDeviceGuid onto ControllerHandle\r
-    //\r
-    if (IsInErrOutVariable) {\r
-      NeedClose = FALSE;\r
-      gBS->InstallMultipleProtocolInterfaces (\r
-             &ControllerHandle,\r
-             &gEfiStandardErrorDeviceGuid,\r
-             NULL,\r
-             NULL\r
-             );\r
-    }\r
+  }\r
 \r
-    if (NeedClose) {\r
-      gBS->CloseProtocol (\r
-             ControllerHandle,\r
-             &gEfiSimpleTextOutProtocolGuid,\r
-             This->DriverBindingHandle,\r
-             ControllerHandle\r
-             );\r
-    }\r
+  if (NeedClose) {\r
+    gBS->CloseProtocol (\r
+           ControllerHandle,\r
+           &gEfiSimpleTextOutProtocolGuid,\r
+           This->DriverBindingHandle,\r
+           ControllerHandle\r
+           );\r
   }\r
 \r
   return EFI_SUCCESS;\r
@@ -822,6 +758,273 @@ IsGopSibling (
   return (BOOLEAN) (CompareMem (Left, Right, (UINTN) NodeLeft - (UINTN) Left) == 0);\r
 }\r
 \r
+/**\r
+  Check whether a USB device match the specified USB Class device path. This\r
+  function follows "Load Option Processing" behavior in UEFI specification.\r
+\r
+  @param UsbIo       USB I/O protocol associated with the USB device.\r
+  @param UsbClass    The USB Class device path to match.\r
+\r
+  @retval TRUE       The USB device match the USB Class device path.\r
+  @retval FALSE      The USB device does not match the USB Class device path.\r
+\r
+**/\r
+BOOLEAN\r
+MatchUsbClass (\r
+  IN EFI_USB_IO_PROTOCOL        *UsbIo,\r
+  IN USB_CLASS_DEVICE_PATH      *UsbClass\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_USB_DEVICE_DESCRIPTOR     DevDesc;\r
+  EFI_USB_INTERFACE_DESCRIPTOR  IfDesc;\r
+  UINT8                         DeviceClass;\r
+  UINT8                         DeviceSubClass;\r
+  UINT8                         DeviceProtocol;\r
+\r
+  if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||\r
+      (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check Vendor Id and Product Id.\r
+  //\r
+  Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if ((UsbClass->VendorId != 0xffff) &&\r
+      (UsbClass->VendorId != DevDesc.IdVendor)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if ((UsbClass->ProductId != 0xffff) &&\r
+      (UsbClass->ProductId != DevDesc.IdProduct)) {\r
+    return FALSE;\r
+  }\r
+\r
+  DeviceClass    = DevDesc.DeviceClass;\r
+  DeviceSubClass = DevDesc.DeviceSubClass;\r
+  DeviceProtocol = DevDesc.DeviceProtocol;\r
+  if (DeviceClass == 0) {\r
+    //\r
+    // If Class in Device Descriptor is set to 0, use the Class, SubClass and\r
+    // Protocol in Interface Descriptor instead.\r
+    //\r
+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
+    if (EFI_ERROR (Status)) {\r
+      return FALSE;\r
+    }\r
+\r
+    DeviceClass    = IfDesc.InterfaceClass;\r
+    DeviceSubClass = IfDesc.InterfaceSubClass;\r
+    DeviceProtocol = IfDesc.InterfaceProtocol;\r
+  }\r
+\r
+  //\r
+  // Check Class, SubClass and Protocol.\r
+  //\r
+  if ((UsbClass->DeviceClass != 0xff) &&\r
+      (UsbClass->DeviceClass != DeviceClass)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if ((UsbClass->DeviceSubClass != 0xff) &&\r
+      (UsbClass->DeviceSubClass != DeviceSubClass)) {\r
+    return FALSE;\r
+  }\r
+\r
+  if ((UsbClass->DeviceProtocol != 0xff) &&\r
+      (UsbClass->DeviceProtocol != DeviceProtocol)) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Check whether a USB device match the specified USB WWID device path. This\r
+  function follows "Load Option Processing" behavior in UEFI specification.\r
+\r
+  @param UsbIo       USB I/O protocol associated with the USB device.\r
+  @param UsbWwid     The USB WWID device path to match.\r
+\r
+  @retval TRUE       The USB device match the USB WWID device path.\r
+  @retval FALSE      The USB device does not match the USB WWID device path.\r
+\r
+**/\r
+BOOLEAN\r
+MatchUsbWwid (\r
+  IN EFI_USB_IO_PROTOCOL        *UsbIo,\r
+  IN USB_WWID_DEVICE_PATH       *UsbWwid\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_USB_DEVICE_DESCRIPTOR    DevDesc;\r
+  EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
+  UINT16                       *LangIdTable;\r
+  UINT16                       TableSize;\r
+  UINT16                       Index;\r
+  CHAR16                       *CompareStr;\r
+  UINTN                        CompareLen;\r
+  CHAR16                       *SerialNumberStr;\r
+  UINTN                        Length;\r
+\r
+  if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||\r
+      (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check Vendor Id and Product Id.\r
+  //\r
+  Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+  if ((DevDesc.IdVendor != UsbWwid->VendorId) ||\r
+      (DevDesc.IdProduct != UsbWwid->ProductId)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check Interface Number.\r
+  //\r
+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+  if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check Serial Number.\r
+  //\r
+  if (DevDesc.StrSerialNumber == 0) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Get all supported languages.\r
+  //\r
+  TableSize = 0;\r
+  LangIdTable = NULL;\r
+  Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);\r
+  if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.\r
+  //\r
+  CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);\r
+  CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);\r
+  if (CompareStr[CompareLen - 1] == L'\0') {\r
+    CompareLen--;\r
+  }\r
+\r
+  //\r
+  // Compare serial number in each supported language.\r
+  //\r
+  for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {\r
+    SerialNumberStr = NULL;\r
+    Status = UsbIo->UsbGetStringDescriptor (\r
+                      UsbIo,\r
+                      LangIdTable[Index],\r
+                      DevDesc.StrSerialNumber,\r
+                      &SerialNumberStr\r
+                      );\r
+    if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {\r
+      continue;\r
+    }\r
+\r
+    Length = StrLen (SerialNumberStr);\r
+    if ((Length >= CompareLen) &&\r
+        (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {\r
+      FreePool (SerialNumberStr);\r
+      return TRUE;\r
+    }\r
+\r
+    FreePool (SerialNumberStr);\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Compare whether a full console device path matches a USB shortform device path.\r
+\r
+  @param[in] FullPath      Full console device path.\r
+  @param[in] ShortformPath Short-form device path. Short-form device node may in the beginning or in the middle.\r
+\r
+  @retval TRUE  The full console device path matches the short-form device path.\r
+  @retval FALSE The full console device path doesn't match the short-form device path.\r
+**/\r
+BOOLEAN\r
+MatchUsbShortformDevicePath (\r
+  IN EFI_DEVICE_PATH_PROTOCOL  *FullPath,\r
+  IN EFI_DEVICE_PATH_PROTOCOL  *ShortformPath\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_DEVICE_PATH_PROTOCOL  *ShortformNode;\r
+  UINTN                     ParentDevicePathSize;\r
+  EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath;\r
+  EFI_USB_IO_PROTOCOL       *UsbIo;\r
+  EFI_HANDLE                Handle;\r
+\r
+  for ( ShortformNode = ShortformPath\r
+      ; !IsDevicePathEnd (ShortformNode)\r
+      ; ShortformNode = NextDevicePathNode (ShortformNode)\r
+      ) {\r
+    if ((DevicePathType (ShortformNode) == MESSAGING_DEVICE_PATH) &&\r
+        ((DevicePathSubType (ShortformNode) == MSG_USB_CLASS_DP) ||\r
+         (DevicePathSubType (ShortformNode) == MSG_USB_WWID_DP))\r
+        ) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Skip further compare when it's not a shortform device path.\r
+  //\r
+  if (IsDevicePathEnd (ShortformNode)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Compare the parent device path when the ShortformPath doesn't start with short-form node.\r
+  //\r
+  ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) ShortformPath;\r
+  RemainingDevicePath  = FullPath;\r
+  Status               = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &RemainingDevicePath, &Handle);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+  if (ParentDevicePathSize != 0) {\r
+    if ((ParentDevicePathSize > (UINTN) RemainingDevicePath - (UINTN) FullPath) ||\r
+        (CompareMem (FullPath, ShortformPath, ParentDevicePathSize) != 0)) {\r
+      return FALSE;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Compar the USB layer.\r
+  //\r
+  Status = gBS->HandleProtocol(\r
+                  Handle,\r
+                  &gEfiUsbIoProtocolGuid,\r
+                  (VOID **) &UsbIo\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return MatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *)ShortformNode) ||\r
+         MatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *)ShortformNode);\r
+}\r
+\r
 /**\r
   Function compares a device path data structure to that of all the nodes of a\r
   second device path instance.\r
@@ -881,7 +1084,8 @@ ConPlatformMatchDevicePaths (
   // Search for the match of 'Single' in 'Multi'\r
   //\r
   while (DevicePathInst != NULL) {\r
-    if ((CompareMem (Single, DevicePathInst, Size) == 0) || IsGopSibling (Single, DevicePathInst)) {\r
+    if ((CompareMem (Single, DevicePathInst, Size) == 0) ||\r
+      IsGopSibling (Single, DevicePathInst) || MatchUsbShortformDevicePath (Single, DevicePathInst)) {\r
       if (!Delete) {\r
         //\r
         // If Delete is FALSE, return EFI_SUCCESS if Single is found in Multi.\r
@@ -1029,53 +1233,6 @@ ConPlatformUpdateDeviceVariable (
   return Status;\r
 }\r
 \r
-/**\r
-  Check if the device supports hot-plug through its device path.\r
-\r
-  This function could be updated to check more types of Hot Plug devices.\r
-  Currently, it checks USB and PCCard device.\r
-\r
-  @param  DevicePath            Pointer to device's device path.\r
-\r
-  @retval TRUE                  The devcie is a hot-plug device\r
-  @retval FALSE                 The devcie is not a hot-plug device.\r
-\r
-**/\r
-BOOLEAN\r
-IsHotPlugDevice (\r
-  IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath\r
-  )\r
-{\r
-  EFI_DEVICE_PATH_PROTOCOL     *CheckDevicePath;\r
-\r
-  CheckDevicePath = DevicePath;\r
-  while (!IsDevicePathEnd (CheckDevicePath)) {\r
-    //\r
-    // Check device whether is hot plug device or not throught Device Path\r
-    //\r
-    if ((DevicePathType (CheckDevicePath) == MESSAGING_DEVICE_PATH) &&\r
-        (DevicePathSubType (CheckDevicePath) == MSG_USB_DP ||\r
-         DevicePathSubType (CheckDevicePath) == MSG_USB_CLASS_DP ||\r
-         DevicePathSubType (CheckDevicePath) == MSG_USB_WWID_DP)) {\r
-      //\r
-      // If Device is USB device\r
-      //\r
-      return TRUE;\r
-    }\r
-    if ((DevicePathType (CheckDevicePath) == HARDWARE_DEVICE_PATH) &&\r
-        (DevicePathSubType (CheckDevicePath) == HW_PCCARD_DP)) {\r
-      //\r
-      // If Device is PCCard\r
-      //\r
-      return TRUE;\r
-    }\r
-\r
-    CheckDevicePath = NextDevicePathNode (CheckDevicePath);\r
-  }\r
-\r
-  return FALSE;\r
-}\r
-\r
 /**\r
   Update ConOutDev and ErrOutDev variables to add the device path of\r
   GOP controller itself and the sibling controllers.\r