public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association
@ 2025-01-03 21:39 olegi via groups.io
  2025-01-04  2:10 ` 回复: " gaoliming via groups.io
  0 siblings, 1 reply; 5+ messages in thread
From: olegi via groups.io @ 2025-01-03 21:39 UTC (permalink / raw)
  To: devel


[-- Attachment #1.1: Type: text/plain, Size: 1173 bytes --]

USB Interface Association is a group of UsbIo that implement a USB function. UEFI device driver manages multiple UsbIo instances. Examples of such devices are: USB camera, USB serial, USB network, etc.
Current approach for supporting these devices is to respond on UsbIo installation and analyze if the current UsbIo belongs to the USB association. This algorithm is based on assumptions that may not be correct for different device configurations. Having USB association protocol that reports its associates (UsbIo) simplifies the USB device driver.

For the USB configurations that implement USB association the UsbDxeBus driver will:

* create USB association device
* install device path
* install USB association IO protocol

Request to create edk2-staging/usb_iad branch, patch is attached.


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#120949): https://edk2.groups.io/g/devel/message/120949
Mute This Topic: https://groups.io/mt/110414302/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



[-- Attachment #1.2: Type: text/html, Size: 1691 bytes --]

[-- Attachment #2: diff.patch --]
[-- Type: application/octet-stream, Size: 40885 bytes --]

diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h
index 21a24218fc..af1fed1bc4 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h
@@ -3,6 +3,7 @@
     Usb Bus Driver Binding and Bus IO Protocol.
 
 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -15,6 +16,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Protocol/Usb2HostController.h>
 #include <Protocol/UsbHostController.h>
 #include <Protocol/UsbIo.h>
+#include <Protocol/UsbAssociationIo.h>
 #include <Protocol/DevicePath.h>
 
 #include <Library/BaseLib.h>
@@ -29,10 +31,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 
 #include <IndustryStandard/Usb.h>
 
-typedef struct _USB_DEVICE     USB_DEVICE;
-typedef struct _USB_INTERFACE  USB_INTERFACE;
-typedef struct _USB_BUS        USB_BUS;
-typedef struct _USB_HUB_API    USB_HUB_API;
+typedef struct _USB_DEVICE       USB_DEVICE;
+typedef struct _USB_INTERFACE    USB_INTERFACE;
+typedef struct _USB_BUS          USB_BUS;
+typedef struct _USB_HUB_API      USB_HUB_API;
+typedef struct _USB_ASSOCIATION  USB_ASSOCIATION;
 
 #include "UsbUtility.h"
 #include "UsbDesc.h"
@@ -132,8 +135,9 @@ typedef struct _USB_HUB_API    USB_HUB_API;
 //
 #define  USB_BUS_TPL  TPL_NOTIFY
 
-#define  USB_INTERFACE_SIGNATURE  SIGNATURE_32 ('U', 'S', 'B', 'I')
-#define  USB_BUS_SIGNATURE        SIGNATURE_32 ('U', 'S', 'B', 'B')
+#define  USB_INTERFACE_SIGNATURE    SIGNATURE_32 ('U', 'S', 'B', 'I')
+#define  USB_BUS_SIGNATURE          SIGNATURE_32 ('U', 'S', 'B', 'B')
+#define  USB_ASSOCIATION_SIGNATURE  SIGNATURE_32 ('U', 'S', 'B', 'A')
 
 #define USB_BIT(a)                 ((UINTN)(1 << (a)))
 #define USB_BIT_IS_SET(Data, Bit)  ((BOOLEAN)(((Data) & (Bit)) == (Bit)))
@@ -141,6 +145,9 @@ typedef struct _USB_HUB_API    USB_HUB_API;
 #define USB_INTERFACE_FROM_USBIO(a) \
           CR(a, USB_INTERFACE, UsbIo, USB_INTERFACE_SIGNATURE)
 
+#define USB_ASSOCIATION_FROM_USBIA(a) \
+          CR(a, USB_ASSOCIATION, UsbIaProtocol, USB_ASSOCIATION_SIGNATURE)
+
 #define USB_BUS_FROM_THIS(a) \
           CR(a, USB_BUS, BusId, USB_BUS_SIGNATURE)
 
@@ -176,6 +183,9 @@ struct _USB_DEVICE {
   UINT16                                LangId[USB_MAX_LANG_ID];
   UINT16                                TotalLangId;
 
+  UINT8                                 NumOfAssociation;
+  USB_ASSOCIATION                       *Associations[USB_MAX_ASSOCIATION];
+
   UINT8                                 NumOfInterface;
   USB_INTERFACE                         *Interfaces[USB_MAX_INTERFACE];
 
@@ -230,6 +240,23 @@ struct _USB_INTERFACE {
   UINT8                       MaxSpeed;
 };
 
+//
+// Stands for a function implemented using interface association
+//
+struct _USB_ASSOCIATION {
+  UINTN                                       Signature;
+  USB_DEVICE                                  *Device;
+  USB_INTERFACE_ASSOCIATION_DESC              *IaDesc;
+
+  //
+  // Handles and protocols
+  //
+  EFI_HANDLE                                  Handle;
+  EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL    UsbIaProtocol;
+  EFI_DEVICE_PATH_PROTOCOL                    *DevicePath;
+  BOOLEAN                                     IsManaged;
+};
+
 //
 // Stands for the current USB Bus
 //
@@ -751,9 +778,10 @@ UsbBusControllerDriverStop (
   IN EFI_HANDLE                   *ChildHandleBuffer
   );
 
-extern EFI_USB_IO_PROTOCOL           mUsbIoProtocol;
-extern EFI_DRIVER_BINDING_PROTOCOL   mUsbBusDriverBinding;
-extern EFI_COMPONENT_NAME_PROTOCOL   mUsbBusComponentName;
-extern EFI_COMPONENT_NAME2_PROTOCOL  mUsbBusComponentName2;
+extern EFI_USB_IO_PROTOCOL                       mUsbIoProtocol;
+extern EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL  mUsbIaProtocol;
+extern EFI_DRIVER_BINDING_PROTOCOL               mUsbBusDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL               mUsbBusComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL              mUsbBusComponentName2;
 
 #endif
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
index dd85894346..2b866fc204 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
@@ -2,6 +2,7 @@
 #  The Usb Bus Dxe driver is used to enumerate and manage all attached usb devices.
 #
 #  Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -40,10 +41,11 @@
   UsbUtility.c
   UsbDesc.h
   UsbBus.h
+  UsbIntfAssoc.c
 
 [Packages]
   MdePkg/MdePkg.dec
-
+  MdeModulePkg/MdeModulePkg.dec
 
 [LibraryClasses]
   MemoryAllocationLib
@@ -58,6 +60,7 @@
 
 [Protocols]
   gEfiUsbIoProtocolGuid                         ## BY_START
+  gEdkiiUsbInterfaceAssociationProtocolGuid
   ## TO_START
   ## BY_START
   gEfiDevicePathProtocolGuid
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c
index 8b078e7e49..c57038ec56 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c
@@ -3,6 +3,7 @@
     Manage Usb Descriptor List
 
 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -46,6 +47,24 @@ UsbFreeInterfaceDesc (
   FreePool (Setting);
 }
 
+/**
+  Free the interface association descriptor
+
+  @param  Iad               The interface association descriptor to free.
+
+**/
+VOID
+UsbFreeInterfaceAssociationDesc (
+  IN USB_INTERFACE_ASSOCIATION_DESC  *Iad
+  )
+{
+  if (Iad->Interfaces != NULL) {
+    FreePool (Iad->Interfaces);
+  }
+
+  FreePool (Iad);
+}
+
 /**
   Free a configuration descriptor with its interface
   descriptors. It may be initialized partially.
@@ -167,6 +186,11 @@ UsbCreateDesc (
       CtrlLen = sizeof (USB_ENDPOINT_DESC);
       break;
 
+    case USB_DESC_TYPE_INTERFACE_ASSOCIATION:
+      DescLen = sizeof (EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR);
+      CtrlLen = sizeof (USB_INTERFACE_ASSOCIATION_DESC);
+      break;
+
     default:
       ASSERT (FALSE);
       return NULL;
@@ -231,6 +255,7 @@ UsbCreateDesc (
     return NULL;
   }
 
+  ASSERT (CtrlLen >= DescLen);
   CopyMem (Desc, Head, (UINTN)DescLen);
 
   *Consumed = Offset;
@@ -319,6 +344,85 @@ ON_ERROR:
   return NULL;
 }
 
+/**
+  Parse an interface association and its interfaces.
+
+  @param  DescBuf               The buffer of raw interface association descriptor.
+  @param  Len                   The length of the raw descriptor buffer.
+  @param  Config                The device configuration buffer.
+  @param  Consumed              The number of raw descriptor consumed.
+
+  @return The created interface association descriptor or NULL if failed.
+
+**/
+USB_INTERFACE_ASSOCIATION_DESC *
+UsbParseInterfaceAssociationDesc (
+  IN UINT8            *DescBuf,
+  IN UINTN            Len,
+  IN USB_CONFIG_DESC  *Config,
+  OUT UINTN           *Consumed
+  )
+{
+  USB_INTERFACE_ASSOCIATION_DESC  *Iad;
+  UINT8                           Index;
+  UINT8                           IfIndex;
+  UINT8                           i;
+  UINTN                           NumIf;
+  UINTN                           Used;
+
+  *Consumed = 0;
+  Iad       = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_INTERFACE_ASSOCIATION, &Used);
+
+  if (Iad == NULL) {
+    return NULL;
+  }
+
+  //
+  // Create an array to hold the association's interfaces
+  //
+  NumIf = Iad->Desc.InterfaceCount;
+
+  DEBUG ((
+    DEBUG_INFO,
+    "UsbParseInterfaceAssociationDesc: interface association has %d interfaces\n",
+    (UINT32)NumIf
+    ));
+
+  ASSERT (NumIf > 0);
+  if (NumIf == 0) {
+    DEBUG ((DEBUG_ERROR, "UsbParseInterfaceAssociationDesc: no interfaces are reported for this association.\n"));
+    goto ON_ERROR;
+  }
+
+  Iad->Interfaces = AllocateZeroPool (sizeof (USB_INTERFACE_DESC *) * NumIf);
+
+  //
+  // Populate interfaces for this association
+  //
+  IfIndex = 0;
+  for (Index = Iad->Desc.FirstInterface; Index < (UINT8)(Iad->Desc.FirstInterface + Iad->Desc.InterfaceCount); Index++) {
+    for (i = 0; i < Config->Desc.NumInterfaces; i++) {
+      if (Index == Config->Interfaces[i]->Settings[0]->Desc.InterfaceNumber) {
+        break;
+      }
+    }
+
+    if (i == Config->Desc.NumInterfaces) {
+      DEBUG ((DEBUG_ERROR, "UsbParseInterfaceAssociationDesc: interface %d not found.\n", Index));
+      goto ON_ERROR;
+    }
+
+    Iad->Interfaces[IfIndex++] = Config->Interfaces[i];
+  }
+
+  *Consumed = Used;
+  return Iad;
+
+ON_ERROR:
+  UsbFreeInterfaceAssociationDesc (Iad);
+  return NULL;
+}
+
 /**
   Parse the configuration descriptor and its interfaces.
 
@@ -334,16 +438,22 @@ UsbParseConfigDesc (
   IN UINTN  Len
   )
 {
-  USB_CONFIG_DESC        *Config;
-  USB_INTERFACE_SETTING  *Setting;
-  USB_INTERFACE_DESC     *Interface;
-  UINTN                  Index;
-  UINTN                  NumIf;
-  UINTN                  Consumed;
+  USB_CONFIG_DESC                 *Config;
+  USB_INTERFACE_SETTING           *Setting;
+  USB_INTERFACE_DESC              *Interface;
+  UINTN                           Index;
+  UINTN                           NumIf;
+  UINTN                           Consumed;
+  UINT8                           *Buffer;
+  UINTN                           Length;
+  USB_INTERFACE_ASSOCIATION_DESC  *Iad;
 
   ASSERT (DescBuf != NULL);
 
-  Config = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_CONFIG, &Consumed);
+  Buffer = DescBuf;
+  Length = Len;
+
+  Config = UsbCreateDesc (Buffer, Length, USB_DESC_TYPE_CONFIG, &Consumed);
 
   if (Config == NULL) {
     return NULL;
@@ -384,15 +494,15 @@ UsbParseConfigDesc (
   // setting 1|interface 1, setting 0|interface 2, setting 0|. Check
   // USB2.0 spec, page 267.
   //
-  DescBuf += Consumed;
-  Len     -= Consumed;
+  Buffer += Consumed;
+  Length -= Consumed;
 
   //
   // Make allowances for devices that return extra data at the
   // end of their config descriptors
   //
-  while (Len >= sizeof (EFI_USB_INTERFACE_DESCRIPTOR)) {
-    Setting = UsbParseInterfaceDesc (DescBuf, Len, &Consumed);
+  while (Length >= sizeof (EFI_USB_INTERFACE_DESCRIPTOR)) {
+    Setting = UsbParseInterfaceDesc (Buffer, Length, &Consumed);
 
     if (Setting == NULL) {
       DEBUG ((DEBUG_ERROR, "UsbParseConfigDesc: warning: failed to get interface setting, stop parsing now.\n"));
@@ -416,8 +526,36 @@ UsbParseConfigDesc (
     Interface->Settings[Interface->NumOfSetting] = Setting;
     Interface->NumOfSetting++;
 
-    DescBuf += Consumed;
-    Len     -= Consumed;
+    Buffer += Consumed;
+    Length -= Consumed;
+  }
+
+  //
+  // Process interface association descriptors.
+  //
+  Buffer = DescBuf;
+  Length = Len;
+
+  while (Length >= sizeof (EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR)) {
+    Iad = UsbParseInterfaceAssociationDesc (Buffer, Length, Config, &Consumed);
+
+    if (Iad == NULL) {
+      break;
+    }
+
+    //
+    // Insert the association descriptor to the corresponding set.
+    //
+    if (Config->NumOfIads >= USB_MAX_ASSOCIATION) {
+      DEBUG ((DEBUG_ERROR, "UsbParseConfigDesc: too many interface association descriptors in this configuration.\n"));
+      goto ON_ERROR;
+    }
+
+    Config->Iads[Config->NumOfIads] = Iad;
+    Config->NumOfIads++;
+
+    Buffer += Consumed;
+    Length -= Consumed;
   }
 
   return Config;
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h
index ce205e706d..03f38c7afa 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h
@@ -3,6 +3,7 @@
     Manage Usb Descriptor List
 
 Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -11,6 +12,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #define _USB_DESCRIPTOR_H_
 
 #define USB_MAX_INTERFACE_SETTING  256
+#define USB_MAX_ASSOCIATION        16
 
 //
 // The RequestType in EFI_USB_DEVICE_REQUEST is composed of
@@ -62,8 +64,15 @@ typedef struct {
 } USB_INTERFACE_DESC;
 
 typedef struct {
-  EFI_USB_CONFIG_DESCRIPTOR    Desc;
-  USB_INTERFACE_DESC           **Interfaces;
+  EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR    Desc;
+  USB_INTERFACE_DESC                          **Interfaces;
+} USB_INTERFACE_ASSOCIATION_DESC;
+
+typedef struct {
+  EFI_USB_CONFIG_DESCRIPTOR         Desc;
+  USB_INTERFACE_ASSOCIATION_DESC    *Iads[USB_MAX_ASSOCIATION];
+  UINT8                             NumOfIads;
+  USB_INTERFACE_DESC                **Interfaces;
 } USB_CONFIG_DESC;
 
 typedef struct {
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
index b3a40639f2..f6e5418931 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
@@ -3,6 +3,7 @@
     Usb bus enumeration support.
 
 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -41,6 +42,41 @@ UsbGetEndpointDesc (
   return NULL;
 }
 
+/**
+  Check if given Usb interface is a part of Usb association.
+
+  @param[in]  Device    The device may have the interface association descriptor.
+  @param[in]  IfDesc    The interface descriptor to check for.
+  @param[out] IfAssoc   The USB association device pointer.
+
+  @retval EFI_SUCCESS   IfDesc is found within associations, IfAssoc has valid pointer.
+  @retval EFI_NOT_FOUND IfDesc does no belong to any association.
+
+**/
+EFI_STATUS
+GetInterfaceAssociation (
+  IN USB_DEVICE          *Device,
+  IN USB_INTERFACE_DESC  *IfDesc,
+  OUT USB_ASSOCIATION    **IfAssoc
+  )
+{
+  UINTN            Index;
+  UINTN            i;
+  USB_ASSOCIATION  *Ia;
+
+  for (Index = 0; Index < Device->NumOfAssociation; Index++) {
+    Ia = Device->Associations[Index];
+    for (i = 0; i < Ia->IaDesc->Desc.InterfaceCount; i++) {
+      if (IfDesc->Settings[0]->Desc.InterfaceNumber == Ia->IaDesc->Interfaces[i]->Settings[0]->Desc.InterfaceNumber) {
+        *IfAssoc = Ia;
+        return EFI_SUCCESS;
+      }
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
 /**
   Free the resource used by USB interface.
 
@@ -54,9 +90,25 @@ UsbFreeInterface (
   IN USB_INTERFACE  *UsbIf
   )
 {
-  EFI_STATUS  Status;
+  EFI_STATUS       Status;
+  USB_ASSOCIATION  *UsbIa;
 
-  UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+  Status = GetInterfaceAssociation (UsbIf->Device, UsbIf->IfDesc, &UsbIa);
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // Close USB Interface Association Protocol by Child
+    //
+    Status = gBS->CloseProtocol (
+                    UsbIa->Handle,
+                    &gEdkiiUsbInterfaceAssociationProtocolGuid,
+                    mUsbBusDriverBinding.DriverBindingHandle,
+                    UsbIf->Handle
+                    );
+    DEBUG ((DEBUG_INFO, "UsbFreeInterface: close IAD protocol by child, %r\n", Status));
+  } else {
+    UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+  }
 
   Status = gBS->UninstallMultipleProtocolInterfaces (
                   UsbIf->Handle,
@@ -73,7 +125,66 @@ UsbFreeInterface (
 
     FreePool (UsbIf);
   } else {
-    UsbOpenHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+    Status = GetInterfaceAssociation (UsbIf->Device, UsbIf->IfDesc, &UsbIa);
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // Reopen USB Interface Assiciation Protocol by Child
+      //
+      Status = gBS->OpenProtocol (
+                      UsbIa->Handle,
+                      &gEdkiiUsbInterfaceAssociationProtocolGuid,
+                      (VOID **)&UsbIa,
+                      mUsbBusDriverBinding.DriverBindingHandle,
+                      UsbIf->Handle,
+                      EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                      );
+      DEBUG ((DEBUG_INFO, "UsbFreeInterface: reopen IAD for child, Status = %r\n", Status));
+    } else {
+      //
+      // Reopen USB Host Controller Protocol by Child
+      //
+      Status = UsbOpenHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+      DEBUG ((DEBUG_INFO, "UsbFreeInterface: reopen host controller for child, Status = %r\n", Status));
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Free the resource used by USB association.
+
+  @param  UsbIa                 The USB association to free.
+
+  @retval EFI_ACCESS_DENIED     The Usb association resource is still occupied.
+  @retval EFI_SUCCESS           The Usb association resource is freed.
+
+**/
+EFI_STATUS
+UsbFreeAssociation (
+  IN USB_ASSOCIATION  *UsbIa
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+                  UsbIa->Handle,
+                  &gEfiDevicePathProtocolGuid,
+                  UsbIa->DevicePath,
+                  &gEdkiiUsbInterfaceAssociationProtocolGuid,
+                  &UsbIa->UsbIaProtocol,
+                  NULL
+                  );
+
+  if (!EFI_ERROR (Status)) {
+    if (UsbIa->DevicePath != NULL) {
+      FreePool (UsbIa->DevicePath);
+    }
+
+    FreePool (UsbIa);
+  } else {
+    UsbOpenHostProtoByChild (UsbIa->Device->Bus, UsbIa->Handle);
   }
 
   return Status;
@@ -82,6 +193,7 @@ UsbFreeInterface (
 /**
   Create an interface for the descriptor IfDesc. Each
   device's configuration can have several interfaces.
+  Interface may belong to interface association.
 
   @param  Device                The device has the interface descriptor.
   @param  IfDesc                The interface descriptor.
@@ -95,10 +207,12 @@ UsbCreateInterface (
   IN USB_INTERFACE_DESC  *IfDesc
   )
 {
-  USB_DEVICE_PATH  UsbNode;
-  USB_INTERFACE    *UsbIf;
-  USB_INTERFACE    *HubIf;
-  EFI_STATUS       Status;
+  USB_DEVICE_PATH           UsbNode;
+  USB_INTERFACE             *UsbIf;
+  USB_INTERFACE             *HubIf;
+  USB_ASSOCIATION           *UsbIa;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePathProtocol;
+  EFI_STATUS                Status;
 
   UsbIf = AllocateZeroPool (sizeof (USB_INTERFACE));
 
@@ -128,10 +242,17 @@ UsbCreateInterface (
 
   SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));
 
-  HubIf = Device->ParentIf;
-  ASSERT (HubIf != NULL);
+  Status = GetInterfaceAssociation (Device, IfDesc, &UsbIa);
+
+  if (!EFI_ERROR (Status)) {
+    DevicePathProtocol = UsbIa->DevicePath;
+  } else {
+    HubIf = Device->ParentIf;
+    ASSERT (HubIf != NULL);
+    DevicePathProtocol = HubIf->DevicePath;
+  }
 
-  UsbIf->DevicePath = AppendDevicePathNode (HubIf->DevicePath, &UsbNode.Header);
+  UsbIf->DevicePath = AppendDevicePathNode (DevicePathProtocol, &UsbNode.Header);
 
   if (UsbIf->DevicePath == NULL) {
     DEBUG ((DEBUG_ERROR, "UsbCreateInterface: failed to create device path\n"));
@@ -154,10 +275,28 @@ UsbCreateInterface (
     goto ON_ERROR;
   }
 
-  //
-  // Open USB Host Controller Protocol by Child
-  //
-  Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle);
+  Status = GetInterfaceAssociation (Device, IfDesc, &UsbIa);
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // Open USB Interface Assiciation Protocol by Child
+    //
+    Status = gBS->OpenProtocol (
+                    UsbIa->Handle,
+                    &gEdkiiUsbInterfaceAssociationProtocolGuid,
+                    (VOID **)&UsbIa,
+                    mUsbBusDriverBinding.DriverBindingHandle,
+                    UsbIf->Handle,
+                    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                    );
+    DEBUG ((DEBUG_INFO, "UsbCreateInterface: open IAD for child, Status = %r\n", Status));
+  } else {
+    //
+    // Open USB Host Controller Protocol by Child
+    //
+    Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle);
+    DEBUG ((DEBUG_INFO, "UsbCreateInterface: open host controller for child, Status = %r\n", Status));
+  }
 
   if (EFI_ERROR (Status)) {
     gBS->UninstallMultipleProtocolInterfaces (
@@ -184,6 +323,112 @@ ON_ERROR:
   return NULL;
 }
 
+/**
+  Create an interface association instance and install protocols to manage it.
+
+  @param  Device        The Usb device that has the interface association.
+  @param  Index         The interface association index within Device.
+  @param  IfAssocDesc   The interface association descriptor.
+
+  @return The created USB interface association, or NULL.
+
+**/
+USB_ASSOCIATION *
+UsbCreateAssociation (
+  IN USB_DEVICE                      *Device,
+  IN UINT8                           Index,
+  IN USB_INTERFACE_ASSOCIATION_DESC  *IfAssocDesc
+  )
+{
+  USB_DEVICE_PATH  UsbNode;
+  USB_ASSOCIATION  *UsbAssoc;
+  EFI_STATUS       Status;
+
+  UsbAssoc = AllocateZeroPool (sizeof (USB_ASSOCIATION));
+
+  if (UsbAssoc == NULL) {
+    return NULL;
+  }
+
+  UsbAssoc->Signature = USB_ASSOCIATION_SIGNATURE;
+  UsbAssoc->Device    = Device;
+  UsbAssoc->IaDesc    = IfAssocDesc;
+
+  CopyMem (
+    &(UsbAssoc->UsbIaProtocol),
+    &mUsbIaProtocol,
+    sizeof (EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL)
+    );
+
+  //
+  // Install USB association protocols
+  //
+  // Device path protocol for the association has the USB node similar to the
+  // one installed for USB interface.
+  //
+
+  UsbNode.Header.Type      = MESSAGING_DEVICE_PATH;
+  UsbNode.Header.SubType   = MSG_USB_DP;
+  UsbNode.ParentPortNumber = Device->ParentPort;
+  UsbNode.InterfaceNumber  = Index;
+
+  SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));
+
+  ASSERT (Device->ParentIf != NULL);
+
+  UsbAssoc->DevicePath = AppendDevicePathNode (Device->ParentIf->DevicePath, &UsbNode.Header);
+
+  if (UsbAssoc->DevicePath == NULL) {
+    DEBUG ((DEBUG_ERROR, "UsbCreateAssociation: failed to create device path\n"));
+
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &UsbAssoc->Handle,
+                  &gEfiDevicePathProtocolGuid,
+                  UsbAssoc->DevicePath,
+                  &gEdkiiUsbInterfaceAssociationProtocolGuid,
+                  &UsbAssoc->UsbIaProtocol,
+                  NULL
+                  );
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "UsbCreateAssociation: failed to install UsbIad - %r\n", Status));
+    goto ON_ERROR;
+  }
+
+  //
+  // Open USB Host Controller Protocol by Child
+  //
+  Status = UsbOpenHostProtoByChild (Device->Bus, UsbAssoc->Handle);
+
+  if (EFI_ERROR (Status)) {
+    gBS->UninstallMultipleProtocolInterfaces (
+           UsbAssoc->Handle,
+           &gEfiDevicePathProtocolGuid,
+           UsbAssoc->DevicePath,
+           &gEdkiiUsbInterfaceAssociationProtocolGuid,
+           &UsbAssoc->UsbIaProtocol,
+           NULL
+           );
+
+    DEBUG ((DEBUG_ERROR, "UsbCreateAssociation: failed to open host for child - %r\n", Status));
+    goto ON_ERROR;
+  }
+
+  return UsbAssoc;
+
+ON_ERROR:
+  if (UsbAssoc->DevicePath != NULL) {
+    FreePool (UsbAssoc->DevicePath);
+  }
+
+  FreePool (UsbAssoc);
+  return NULL;
+}
+
 /**
   Free the resource used by this USB device.
 
@@ -236,6 +481,41 @@ UsbCreateDevice (
   return Device;
 }
 
+/**
+  Connect USB controller at TPL_CALLBACK
+
+  This function is called in both UsbIoControlTransfer and
+  the timer callback in hub enumeration. So, at least it is
+  called at TPL_CALLBACK. Some driver sitting on USB has
+  twisted TPL used. It should be no problem for us to connect
+  or disconnect at CALLBACK.
+
+  @param  Handle        Controller handle to be connected
+
+**/
+EFI_STATUS
+UsbConnectController (
+  EFI_HANDLE  Handle
+  )
+{
+  EFI_STATUS  Status;
+  EFI_TPL     OldTpl;
+
+  OldTpl = UsbGetCurrentTpl ();
+  DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL before connect is %d, %p\n", (UINT32)OldTpl, Handle));
+
+  gBS->RestoreTPL (TPL_CALLBACK);
+
+  Status = gBS->ConnectController (Handle, NULL, NULL, TRUE);
+
+  DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl ()));
+  ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
+
+  gBS->RaiseTPL (OldTpl);
+
+  return Status;
+}
+
 /**
   Connect the USB interface with its driver. EFI USB bus will
   create a USB interface for each separate interface descriptor.
@@ -252,7 +532,6 @@ UsbConnectDriver (
   )
 {
   EFI_STATUS  Status;
-  EFI_TPL     OldTpl;
 
   Status = EFI_SUCCESS;
 
@@ -264,30 +543,12 @@ UsbConnectDriver (
     DEBUG ((DEBUG_INFO, "UsbConnectDriver: found a hub device\n"));
     Status = mUsbHubApi.Init (UsbIf);
   } else {
-    //
-    // This function is called in both UsbIoControlTransfer and
-    // the timer callback in hub enumeration. So, at least it is
-    // called at TPL_CALLBACK. Some driver sitting on USB has
-    // twisted TPL used. It should be no problem for us to connect
-    // or disconnect at CALLBACK.
-    //
-
     //
     // Only recursively wanted usb child device
     //
     if (UsbBusIsWantedUsbIO (UsbIf->Device->Bus, UsbIf)) {
-      OldTpl = UsbGetCurrentTpl ();
-      DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL before connect is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));
-
-      gBS->RestoreTPL (TPL_CALLBACK);
-
-      Status           = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);
+      Status           = UsbConnectController (UsbIf->Handle);
       UsbIf->IsManaged = (BOOLEAN) !EFI_ERROR (Status);
-
-      DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl ()));
-      ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
-
-      gBS->RaiseTPL (OldTpl);
     }
   }
 
@@ -378,6 +639,7 @@ UsbSelectConfig (
   USB_INTERFACE       *UsbIf;
   EFI_STATUS          Status;
   UINT8               Index;
+  USB_ASSOCIATION     *UsbIa;
 
   //
   // Locate the active config, then set the device's pointer
@@ -393,7 +655,7 @@ UsbSelectConfig (
     }
   }
 
-  if (Index == DevDesc->Desc.NumConfigurations) {
+  if ((ConfigDesc == NULL) || (Index == DevDesc->Desc.NumConfigurations)) {
     return EFI_NOT_FOUND;
   }
 
@@ -406,6 +668,19 @@ UsbSelectConfig (
     Device->Address
     ));
 
+  //
+  // Create interfaces for each USB interface association descriptor.
+  //
+  Device->NumOfAssociation = ConfigDesc->NumOfIads;
+
+  for (Index = 0; Index < ConfigDesc->NumOfIads; Index++) {
+    DEBUG ((DEBUG_INFO, "UsbSelectConfig: process IAD %d\n", Index));
+
+    UsbIa = UsbCreateAssociation (Device, Index, ConfigDesc->Iads[Index]);
+    ASSERT (Index < USB_MAX_ASSOCIATION);
+    Device->Associations[Index] = UsbIa;
+  }
+
   //
   // Create interfaces for each USB interface descriptor.
   //
@@ -448,9 +723,55 @@ UsbSelectConfig (
 
   Device->NumOfInterface = Index;
 
+  //
+  // Connect association device drivers. Connection may fail if if device driver has been already
+  // started for any UsbIo that belongs to the association.
+  //
+  for (Index = 0; Index < Device->NumOfAssociation; Index++) {
+    Status                                 = gBS->ConnectController (Device->Associations[Index]->Handle, NULL, NULL, TRUE);
+    Device->Associations[Index]->IsManaged = (BOOLEAN) !EFI_ERROR (Status);
+
+    DEBUG ((DEBUG_INFO, "UsbSelectConfig: association driver connect: %r\n", Status));
+  }
+
   return EFI_SUCCESS;
 }
 
+/**
+  Disconnect USB controller at TPL_CALLBACK
+
+  This function is called in both UsbIoControlTransfer and
+  the timer callback in hub enumeration. So, at least it is
+  called at TPL_CALLBACK. Some driver sitting on USB has
+  twisted TPL used. It should be no problem for us to connect
+  or disconnect at CALLBACK.
+
+  @param  Handle        Controller handle to be disconnected
+
+**/
+EFI_STATUS
+UsbDisconnectController (
+  EFI_HANDLE  Handle
+  )
+{
+  EFI_TPL     OldTpl;
+  EFI_STATUS  Status;
+
+  OldTpl = UsbGetCurrentTpl ();
+  DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: old TPL is %d, %p\n", (UINT32)OldTpl, Handle));
+
+  gBS->RestoreTPL (TPL_CALLBACK);
+
+  Status = gBS->DisconnectController (Handle, NULL, NULL);
+
+  DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl (), Status));
+  ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
+
+  gBS->RaiseTPL (OldTpl);
+
+  return Status;
+}
+
 /**
   Disconnect the USB interface with its driver.
 
@@ -462,7 +783,6 @@ UsbDisconnectDriver (
   IN USB_INTERFACE  *UsbIf
   )
 {
-  EFI_TPL     OldTpl;
   EFI_STATUS  Status;
 
   //
@@ -473,27 +793,11 @@ UsbDisconnectDriver (
   if (UsbIf->IsHub) {
     Status = UsbIf->HubApi->Release (UsbIf);
   } else if (UsbIf->IsManaged) {
-    //
-    // This function is called in both UsbIoControlTransfer and
-    // the timer callback in hub enumeration. So, at least it is
-    // called at TPL_CALLBACK. Some driver sitting on USB has
-    // twisted TPL used. It should be no problem for us to connect
-    // or disconnect at CALLBACK.
-    //
-    OldTpl = UsbGetCurrentTpl ();
-    DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: old TPL is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));
+    Status = UsbDisconnectController (UsbIf->Handle);
 
-    gBS->RestoreTPL (TPL_CALLBACK);
-
-    Status = gBS->DisconnectController (UsbIf->Handle, NULL, NULL);
     if (!EFI_ERROR (Status)) {
       UsbIf->IsManaged = FALSE;
     }
-
-    DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl (), Status));
-    ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
-
-    gBS->RaiseTPL (OldTpl);
   }
 
   return Status;
@@ -510,10 +814,11 @@ UsbRemoveConfig (
   IN USB_DEVICE  *Device
   )
 {
-  USB_INTERFACE  *UsbIf;
-  UINTN          Index;
-  EFI_STATUS     Status;
-  EFI_STATUS     ReturnStatus;
+  USB_INTERFACE    *UsbIf;
+  USB_ASSOCIATION  *UsbIa;
+  UINTN            Index;
+  EFI_STATUS       Status;
+  EFI_STATUS       ReturnStatus;
 
   //
   // Remove each interface of the device
@@ -543,6 +848,35 @@ UsbRemoveConfig (
   }
 
   Device->ActiveConfig = NULL;
+
+  if (EFI_ERROR (ReturnStatus)) {
+    return ReturnStatus;
+  }
+
+  // ReturnStatus is EFI_SUCCESS
+
+  //
+  // Remove each interface association
+  //
+  for (Index = 0; Index < Device->NumOfAssociation; Index++) {
+    UsbIa = Device->Associations[Index];
+
+    Status = UsbDisconnectController (UsbIa->Handle);
+    if (!EFI_ERROR (Status)) {
+      Status = UsbFreeAssociation (UsbIa);
+      DEBUG ((DEBUG_INFO, "UsbRemoveConfig: free association: %r\n", Status));
+      if (EFI_ERROR (Status)) {
+        UsbConnectController (UsbIa->Handle);
+      }
+    }
+
+    if (!EFI_ERROR (Status)) {
+      Device->Associations[Index] = NULL;
+    } else {
+      ReturnStatus = Status;
+    }
+  }
+
   return ReturnStatus;
 }
 
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbIntfAssoc.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbIntfAssoc.c
new file mode 100644
index 0000000000..15ea41dce9
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbIntfAssoc.c
@@ -0,0 +1,153 @@
+/** @file
+
+  Usb Interface Association Protocol implementation.
+
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "UsbBus.h"
+
+/**
+  Get the interface from interface association.
+
+  @param[in]  UsbIa   A pointer to the interface association data.
+  @param[in]  InterfaceNumber Interface to look for inside the association.
+
+  @retval A pointer to the interface data
+  @retval NULL  Interface is not found in the association.
+
+**/
+USB_INTERFACE_DESC *
+UsbAssocFindInterface (
+  IN USB_ASSOCIATION  *UsbIa,
+  IN UINT8            InterfaceNumber
+  )
+{
+  UINT8  Index;
+  UINT8  Intrf;
+
+  Intrf = UsbIa->IaDesc->Desc.FirstInterface;
+  for (Index = 0; Index < UsbIa->IaDesc->Desc.InterfaceCount; Index++) {
+    if (InterfaceNumber == Intrf) {
+      return UsbIa->IaDesc->Interfaces[Index];
+    }
+
+    Intrf++;
+  }
+
+  DEBUG ((DEBUG_ERROR, "UsbAssocFindInterface: interface 0x%x does not belong to this association\n", InterfaceNumber));
+  return NULL;
+}
+
+/**
+  Get the USB Interface Association Descriptor from the current USB configuration.
+
+  @param[in] This              A pointer to the EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL instance.
+  @param[out] Descriptor       A pointer to the caller allocated USB Interface Association Descriptor.
+
+  @retval EFI_SUCCESS           The descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER Descriptor is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbIaGetAssociationDescriptor (
+  IN  EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL  *This,
+  OUT EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR  *Descriptor
+  )
+{
+  USB_ASSOCIATION  *UsbIa;
+  EFI_TPL          OldTpl;
+  EFI_STATUS       Status;
+
+  OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+  Status = EFI_SUCCESS;
+
+  ASSERT (Descriptor != NULL);
+  if (Descriptor == NULL) {
+    Status = EFI_INVALID_PARAMETER;
+    goto ON_EXIT;
+  }
+
+  UsbIa = USB_ASSOCIATION_FROM_USBIA (This);
+  CopyMem (Descriptor, &(UsbIa->IaDesc->Desc), sizeof (EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR));
+
+ON_EXIT:
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+/**
+  Retrieve the details of the requested interface that belongs to USB association.
+
+  @param[in] This              A pointer to the EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL instance.
+  @param[in] InterfaceNumber   Interface number.
+  @param[out] UsbIo            A pointer to the caller allocated UsbIo protocol.
+  @param[out] SettingsCount    A pointer to the caller allocated number of settings for this interface.
+
+  @retval EFI_SUCCESS           Output parameters were updated successfully.
+  @retval EFI_INVALID_PARAMETER UsbIo or SettuntsCount is NULL.
+  @retval EFI_NOT_FOUND         Interface does not belong to this interface association.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbIaGetAssociateSettings (
+  IN  EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL  *This,
+  IN  UINT8                                     InterfaceNumber,
+  OUT EFI_USB_IO_PROTOCOL                       **UsbIo,
+  OUT UINTN                                     *SettingsCount
+  )
+{
+  USB_ASSOCIATION     *UsbIa;
+  EFI_TPL             OldTpl;
+  UINT8               Index;
+  EFI_STATUS          Status;
+  USB_DEVICE          *Device;
+  USB_INTERFACE_DESC  *IfDesc;
+
+  OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+  ASSERT ((UsbIo != NULL) && (SettingsCount != NULL));
+  if ((UsbIo == NULL) || (SettingsCount == NULL)) {
+    Status =  EFI_INVALID_PARAMETER;
+    goto  ON_EXIT;
+  }
+
+  UsbIa = USB_ASSOCIATION_FROM_USBIA (This);
+
+  IfDesc = UsbAssocFindInterface (UsbIa, InterfaceNumber);
+  if (IfDesc == NULL) {
+    Status = EFI_NOT_FOUND;
+    goto ON_EXIT;
+  }
+
+  *SettingsCount = IfDesc->NumOfSetting - 1;
+
+  //
+  // Find UsbIo protocol for this interface
+  //
+  Device = UsbIa->Device;
+  Status = EFI_NOT_FOUND;
+
+  for (Index = 0; Index < Device->NumOfInterface; Index++) {
+    if (Device->Interfaces[Index]->IfSetting->Desc.InterfaceNumber == InterfaceNumber) {
+      *UsbIo = &Device->Interfaces[Index]->UsbIo;
+      Status = EFI_SUCCESS;
+      break;
+    }
+  }
+
+ON_EXIT:
+  gBS->RestoreTPL (OldTpl);
+  return Status;
+}
+
+EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL  mUsbIaProtocol = {
+  UsbIaGetAssociationDescriptor,
+  UsbIaGetAssociateSettings
+};
diff --git a/MdeModulePkg/Include/Protocol/UsbAssociationIo.h b/MdeModulePkg/Include/Protocol/UsbAssociationIo.h
new file mode 100644
index 0000000000..2835dab5c0
--- /dev/null
+++ b/MdeModulePkg/Include/Protocol/UsbAssociationIo.h
@@ -0,0 +1,81 @@
+/** @file
+  EFI Usb Interface Association protocol.
+
+  This protocol is used by USB drivers, running in the EFI boot services
+  environment to access USB devices that implement their configurations
+  using interface association: USB cameras, USB audio devices, USB modems, etc.
+
+  Copyright (c) 2025, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __USB_ASSOCIATION_IO_H__
+#define __USB_ASSOCIATION_IO_H__
+
+#include <IndustryStandard/Usb.h>
+#include <Protocol/UsbIo.h>
+
+typedef USB_INTERFACE_ASSOCIATION_DESCRIPTOR EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR;
+
+//
+// Global ID for the USB IAD Protocol
+//
+#define EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL_GUID \
+  { \
+    0xf4279fb1, 0xef1e, 0x4346, { 0xab, 0x32, 0x3f, 0xe3, 0x86, 0xee, 0xb4, 0x52 } \
+  }
+
+typedef struct _EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL;
+
+/**
+  Get the USB Interface Association Descriptor from the current USB configuration.
+
+  @param[in]  This              A pointer to the EFI_USB_IA_PROTOCOL instance.
+  @param[out]  Descriptor       A pointer to the caller allocated USB Interface Association Descriptor.
+
+  @retval EFI_SUCCESS           The descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER Descriptor is NULL.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_IA_GET_ASSOCIATION_DESCRIPTOR)(
+  IN  EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL  *This,
+  OUT EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR  *Descriptor
+  );
+
+/**
+  Retrieve the details of the requested interface that belongs to USB association.
+
+  @param[in] This              A pointer to the EFI_USB_IA_PROTOCOL instance.
+  @param[in] InterfaceNumber   Interface number.
+  @param[out] UsbIo            A pointer to the caller allocated UsbIo protocol.
+  @param[out] SettingsCount    A pointer to the caller allocated number of settings for this interface.
+
+  @retval EFI_SUCCESS           Output parameters were updated successfully.
+  @retval EFI_INVALID_PARAMETER UsbIo or SettuntsCount is NULL.
+  @retval EFI_NOT_FOUND         Interface does not belong to this interface association.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_IA_GET_ASSOCIATE_SETTINGS)(
+  IN  EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL  *This,
+  IN  UINT8                                     InterfaceNumber,
+  OUT EFI_USB_IO_PROTOCOL                       **UsbIo,
+  OUT UINTN                                     *SettingsCount
+  );
+
+/**
+  USB interface association protocol functions.
+
+**/
+struct _EDKII_USB_INTERFACE_ASSOCIATION_PROTOCOL {
+  EDKII_USB_IA_GET_ASSOCIATION_DESCRIPTOR    UsbIaGetAssociationDescriptor;
+  EDKII_USB_IA_GET_ASSOCIATE_SETTINGS        UsbIaGetAssociateSettings;
+};
+
+extern EFI_GUID  gEdkiiUsbInterfaceAssociationProtocolGuid;
+
+#endif
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index b9bc7041f2..8bf118161c 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -10,6 +10,7 @@
 # Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
 # Copyright (c) Microsoft Corporation.<BR>
 # Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.<BR>
+# Copyright (C) 2025 American Megatrends International, LLC. All rights reserved.<BR>
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 ##
@@ -735,6 +736,9 @@
   ## Include/Protocol/PlatformBootManager.h
   gEdkiiPlatformBootManagerProtocolGuid = { 0xaa17add4, 0x756c, 0x460d, { 0x94, 0xb8, 0x43, 0x88, 0xd7, 0xfb, 0x3e, 0x59 } }
 
+  ## Include/Protocol/UsbAssociationIo.h
+  gEdkiiUsbInterfaceAssociationProtocolGuid = { 0xf4279fb1, 0xef1e, 0x4346, { 0xab, 0x32, 0x3f, 0xe3, 0x86, 0xee, 0xb4, 0x52 }}
+
 #
 # [Error.gEfiMdeModulePkgTokenSpaceGuid]
 #   0x80000001 | Invalid value provided.

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* 回复: [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association
  2025-01-03 21:39 [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association olegi via groups.io
@ 2025-01-04  2:10 ` gaoliming via groups.io
  2025-01-04  2:46   ` [edk2-devel] " olegi via groups.io
  0 siblings, 1 reply; 5+ messages in thread
From: gaoliming via groups.io @ 2025-01-04  2:10 UTC (permalink / raw)
  To: devel, olegi

[-- Attachment #1: Type: text/plain, Size: 1617 bytes --]

Seemly, this protocol is only used in UsbDxeBus module. If so, this protocol can be defined as the internal protocol in UsbDxeBus module.

 

Thanks

Liming

发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 olegi via groups.io
发送时间: 2025年1月4日 5:39
收件人: devel@edk2.groups.io
主题: [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association

 

USB Interface Association is a group of UsbIo that implement a USB function. UEFI device driver manages multiple UsbIo instances. Examples of such devices are: USB camera, USB serial, USB network, etc.

Current approach for supporting these devices is to respond on UsbIo installation and analyze if the current UsbIo belongs to the USB association. This algorithm is based on assumptions that may not be correct for different device configurations. Having USB association protocol that reports its associates (UsbIo) simplifies the USB device driver.

 

For the USB configurations that implement USB association the UsbDxeBus driver will:

*	create USB association device
*	install device path
*	install USB association IO protocol

Request to create edk2-staging/usb_iad branch, patch is attached.





-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#120950): https://edk2.groups.io/g/devel/message/120950
Mute This Topic: https://groups.io/mt/110417824/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



[-- Attachment #2: Type: text/html, Size: 8242 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [edk2-devel] 回复: [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association
  2025-01-04  2:10 ` 回复: " gaoliming via groups.io
@ 2025-01-04  2:46   ` olegi via groups.io
  2025-01-04  9:06     ` 回复: " gaoliming via groups.io
  0 siblings, 1 reply; 5+ messages in thread
From: olegi via groups.io @ 2025-01-04  2:46 UTC (permalink / raw)
  To: gaoliming, devel

[-- Attachment #1: Type: text/plain, Size: 595 bytes --]

Sorry I was not clear. This protocol is consumed by USB Device Drivers that follow UEFI driver model. Protocol returns the UsbIo instances that belong to the USB association, these UsbIo's are consumed by the USB Device Driver.


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#120951): https://edk2.groups.io/g/devel/message/120951
Mute This Topic: https://groups.io/mt/110417824/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



[-- Attachment #2: Type: text/html, Size: 1018 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* 回复: [edk2-devel] 回复: [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association
  2025-01-04  2:46   ` [edk2-devel] " olegi via groups.io
@ 2025-01-04  9:06     ` gaoliming via groups.io
  2025-01-06 21:53       ` [edk2-devel] " olegi via groups.io
  0 siblings, 1 reply; 5+ messages in thread
From: gaoliming via groups.io @ 2025-01-04  9:06 UTC (permalink / raw)
  To: devel, olegi

[-- Attachment #1: Type: text/plain, Size: 1070 bytes --]

I don’t see the consumer code in the attached patch. Could you provide the sample code for this new protocol?

 

Thanks

Liming

发件人: devel@edk2.groups.io <devel@edk2.groups.io> 代表 olegi via groups.io
发送时间: 2025年1月4日 10:46
收件人: gaoliming <gaoliming@byosoft.com.cn>; devel@edk2.groups.io
主题: Re: [edk2-devel] 回复: [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association

 

Sorry I was not clear. This protocol is consumed by USB Device Drivers that follow UEFI driver model. Protocol returns the UsbIo instances that belong to the USB association, these UsbIo's are consumed by the USB Device Driver.





-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#120952): https://edk2.groups.io/g/devel/message/120952
Mute This Topic: https://groups.io/mt/110420476/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



[-- Attachment #2: Type: text/html, Size: 4811 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [edk2-devel] 回复: [edk2-devel] 回复: [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association
  2025-01-04  9:06     ` 回复: " gaoliming via groups.io
@ 2025-01-06 21:53       ` olegi via groups.io
  0 siblings, 0 replies; 5+ messages in thread
From: olegi via groups.io @ 2025-01-06 21:53 UTC (permalink / raw)
  To: gaoliming, devel


[-- Attachment #1.1: Type: text/plain, Size: 1035 bytes --]

Attaching the USB Association IO consumer code sample.  This is the prototype of the USB video class code driver prototype, it illustrates the usage of USB Association IO protocol.

Usb bus driver in MdeModulePkg implements this protocol (implementation is also in the  patch). The definition is in MdePkg so that it can be implemented by non-edk2 USB bus.
Usb association is a part of USB specification, USB Association IO protocol should be eventually added to UEFI specification.
There are additional comments by Michael Kinney (@mdkinney) in https://github.com/tianocore/edk2/pull/10558

Please take a look and let me know how to proceed.

Thanks,
Oleg


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#120965): https://edk2.groups.io/g/devel/message/120965
Mute This Topic: https://groups.io/mt/110420476/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



[-- Attachment #1.2: Type: text/html, Size: 1673 bytes --]

[-- Attachment #2: diff.patch --]
[-- Type: application/octet-stream, Size: 67109 bytes --]

diff --git a/MdeModulePkg/Application/UsbVideoDxe/ComponentName.c b/MdeModulePkg/Application/UsbVideoDxe/ComponentName.c
new file mode 100644
index 0000000000..48b26e7ab7
--- /dev/null
+++ b/MdeModulePkg/Application/UsbVideoDxe/ComponentName.c
@@ -0,0 +1,298 @@
+/** @file
+
+  UEFI Component Name(2) protocol implementation for Usb Video driver.
+
+Copyright (c) 2024, American Megatrends LLC.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiLib.h>
+#include <PiDxe.h>
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **ControllerName
+  );
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUsbVideoComponentName = {
+  UsbVideoComponentNameGetDriverName,
+  UsbVideoComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gUsbVideoComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)UsbVideoComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)UsbVideoComponentNameGetControllerName,
+  "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  gUsbVideoDriverNameTable[] = {
+  { "eng;en", L"Usb Video Driver" },
+  { NULL,     NULL              }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           gUsbVideoDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gUsbVideoComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.c b/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.c
new file mode 100644
index 0000000000..57d53153e6
--- /dev/null
+++ b/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.c
@@ -0,0 +1,222 @@
+/** @file
+
+  Usb Video driver provides services for Usb video device
+
+  (C) Copyright 2011-2024, American Megatrends International LLC<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Protocol/UsbAssociationIo.h>
+#include "UsbVideoDxe.h"
+
+//
+// USB Keyboard Driver Global Variables
+//
+EFI_DRIVER_BINDING_PROTOCOL  gUsbVideoDriverBinding = {
+  UsbVideoDriverBindingSupported,
+  UsbVideoDriverBindingStart,
+  UsbVideoDriverBindingStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+/**
+  Check whether USB video driver supports this device.
+
+  @param  This                   The USB video driver binding protocol.
+  @param  Controller             The controller handle to check.
+  @param  RemainingDevicePath    The remaining device path.
+
+  @retval EFI_SUCCESS            The driver supports this controller.
+  @retval other                  This device isn't supported.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                               Status;
+  EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL   *UsbIaIo;
+  EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR  Desc;
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiUsbInterfaceAssociationProtocolGuid,
+                  (VOID **)&UsbIaIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = UsbIaIo->UsbIaGetAssociationDescriptor (UsbIaIo, &Desc);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  gBS->CloseProtocol (
+        Controller,
+        &gEfiUsbInterfaceAssociationProtocolGuid,
+        This->DriverBindingHandle,
+        Controller
+        );
+
+  if ((Desc.FunctionClass == USB_VIDEO_CLASS) && (Desc.FunctionSubclass == USB_VIDEO_INTERFACE_COLLECTION_SUBCLASS)) {
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Starts the USB video device with this driver.
+
+  @param  This                   The USB video driver binding instance.
+  @param  Controller             Handle of device to bind driver to.
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child
+                                 device to start.
+
+  @retval EFI_SUCCESS            The controller is controlled by the usb video driver.
+  @retval EFI_UNSUPPORTED        No interrupt endpoint can be found.
+  @retval Other                  This controller cannot be started.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                                Status;
+  EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL    *UsbIaIo;
+  EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR  AssocDesc;
+  EFI_USB_INTERFACE_DESCRIPTOR              IntrfDesc;
+  EFI_USB_IO_PROTOCOL                       *UsbIo;
+  UINT8                                     IfNum;
+  UINTN                                     SettingCount;
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiUsbInterfaceAssociationProtocolGuid,
+                  (VOID **)&UsbIaIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = UsbIaIo->UsbIaGetAssociationDescriptor (UsbIaIo, &AssocDesc);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  for (IfNum = AssocDesc.FirstInterface; IfNum < (AssocDesc.FirstInterface + AssocDesc.InterfaceCount); IfNum++) {
+    Status = UsbIaIo->UsbIaGetAssociateSettings (UsbIaIo, IfNum, &UsbIo, &SettingCount);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IntrfDesc);
+    ASSERT_EFI_ERROR (Status);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    if (IntrfDesc.InterfaceClass == USB_VIDEO_CLASS) {
+      if (IntrfDesc.InterfaceSubClass == USB_VIDEOCONTROL_SUBCLASS) {
+        // found video control UsbIo, store it in the device for future use
+        // TBD
+        //
+      }
+      if (IntrfDesc.InterfaceSubClass == USB_VIDEOSTREAMING_SUBCLASS) {
+        // found video streaming UsbIo, store it in the device for future use
+        // TBD
+        //
+      }
+    }
+  }
+  if (IfNum == (AssocDesc.FirstInterface + AssocDesc.InterfaceCount)) {
+    gBS->CloseProtocol (
+          Controller,
+          &gEfiUsbInterfaceAssociationProtocolGuid,
+          This->DriverBindingHandle,
+          Controller
+          );
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Stops the USB video device handled by this driver.
+
+  @param  This                   The USB video driver binding protocol.
+  @param  Controller             The controller to release.
+  @param  NumberOfChildren       The number of handles in ChildHandleBuffer.
+  @param  ChildHandleBuffer      The array of child handle.
+
+  @retval EFI_SUCCESS            The device was stopped.
+  @retval EFI_UNSUPPORTED        Simple Text In Protocol or Simple Text In Ex Protocol
+                                 is not installed on Controller.
+  @retval EFI_DEVICE_ERROR       The device could not be stopped due to a device error.
+  @retval Others                 Fail to uninstall protocols attached on the device.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   Controller,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  )
+{
+  gBS->CloseProtocol (
+        Controller,
+        &gEfiUsbInterfaceAssociationProtocolGuid,
+        This->DriverBindingHandle,
+        Controller
+        );
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  The USB video driver entry pointer.
+
+  @param ImageHandle       The driver image handle.
+  @param SystemTable       The system table.
+
+  @return EFI_SUCCESS      The component name protocol is installed.
+  @return Others           Failed to init the usb driver.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoDxeEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gUsbVideoDriverBinding,
+           ImageHandle,
+           &gUsbVideoComponentName,
+           &gUsbVideoComponentName2
+           );
+}
diff --git a/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.h b/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.h
new file mode 100644
index 0000000000..e3779e691b
--- /dev/null
+++ b/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.h
@@ -0,0 +1,102 @@
+/** @file
+  Header file for USB Video Driver's Data Structures.
+
+Copyright (c) 2025, American Megatrends LLC.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _EFI_USB_VIDEO_H_
+#define _EFI_USB_VIDEO_H_
+
+#include <Uefi.h>
+#include <Protocol/DriverBinding.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <PiDxe.h>
+
+#define USB_VIDEO_CLASS                          0xE
+#define USB_VIDEO_INTERFACE_COLLECTION_SUBCLASS  0x3
+#define USB_VIDEOCONTROL_SUBCLASS                0x1
+#define USB_VIDEOSTREAMING_SUBCLASS              0x2
+
+
+typedef struct _USB_VIDEO_DEVICE {
+  EFI_USB_IO_PROTOCOL *VideoCtlIo;
+} USB_VIDEO_DEVICE;
+
+extern EFI_COMPONENT_NAME_PROTOCOL   gUsbVideoComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL  gUsbVideoComponentName2;
+
+//
+// Functions of Driver Binding Protocol
+//
+
+/**
+  Check whether USB video driver supports this device.
+
+  @param  This                   The USB video driver binding protocol.
+  @param  Controller             The controller handle to check.
+  @param  RemainingDevicePath    The remaining device path.
+
+  @retval EFI_SUCCESS            The driver supports this controller.
+  @retval other                  This device isn't supported.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  Starts the USB video device with this driver.
+
+  @param  This                   The USB video driver binding instance.
+  @param  Controller             Handle of device to bind driver to.
+  @param  RemainingDevicePath    Optional parameter use to pick a specific child
+                                 device to start.
+
+  @retval EFI_SUCCESS            The controller is controlled by the usb video driver.
+  @retval EFI_UNSUPPORTED        No interrupt endpoint can be found.
+  @retval Other                  This controller cannot be started.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  Stops the USB video device handled by this driver.
+
+  @param  This                   The USB video driver binding protocol.
+  @param  Controller             The controller to release.
+  @param  NumberOfChildren       The number of handles in ChildHandleBuffer.
+  @param  ChildHandleBuffer      The array of child handle.
+
+  @retval EFI_SUCCESS            The device was stopped.
+  @retval EFI_UNSUPPORTED        Simple Text In Protocol or Simple Text In Ex Protocol
+                                 is not installed on Controller.
+  @retval EFI_DEVICE_ERROR       The device could not be stopped due to a device error.
+  @retval Others                 Fail to uninstall protocols attached on the device.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbVideoDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   Controller,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  );
+
+
+#endif
diff --git a/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.inf b/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.inf
new file mode 100644
index 0000000000..cfa7fac901
--- /dev/null
+++ b/MdeModulePkg/Application/UsbVideoDxe/UsbVideoDxe.inf
@@ -0,0 +1,44 @@
+## @file
+# Usb Video driver provides services for Usb video device
+#
+# Copyright (c) 2025, American Megatrends LLC. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION     = 0x00010005
+  BASE_NAME       = UsbVideoDxe
+  FILE_GUID       = 4134A1FD-D9DB-405E-9876-7E58DB3B7908
+  MODULE_TYPE     = UEFI_DRIVER
+  VERSION_STRING  = 1.0
+  ENTRY_POINT     = UsbVideoDxeEntryPoint
+    
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+
+[Sources]
+  UsbVideoDxe.c
+  UsbVideoDxe.h
+  ComponentName.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  MemoryAllocationLib
+  DevicePathLib
+  UefiLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+  DebugLib
+  ReportStatusCodeLib
+
+[Protocols]
+  gEfiUsbIoProtocolGuid
+  gEfiUsbInterfaceAssociationProtocolGuid
+    
\ No newline at end of file
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h
index 21a24218fc..f734442737 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h
@@ -3,6 +3,7 @@
     Usb Bus Driver Binding and Bus IO Protocol.
 
 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -15,6 +16,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Protocol/Usb2HostController.h>
 #include <Protocol/UsbHostController.h>
 #include <Protocol/UsbIo.h>
+#include <Protocol/UsbAssociationIo.h>
 #include <Protocol/DevicePath.h>
 
 #include <Library/BaseLib.h>
@@ -29,10 +31,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 
 #include <IndustryStandard/Usb.h>
 
-typedef struct _USB_DEVICE     USB_DEVICE;
-typedef struct _USB_INTERFACE  USB_INTERFACE;
-typedef struct _USB_BUS        USB_BUS;
-typedef struct _USB_HUB_API    USB_HUB_API;
+typedef struct _USB_DEVICE       USB_DEVICE;
+typedef struct _USB_INTERFACE    USB_INTERFACE;
+typedef struct _USB_BUS          USB_BUS;
+typedef struct _USB_HUB_API      USB_HUB_API;
+typedef struct _USB_ASSOCIATION  USB_ASSOCIATION;
 
 #include "UsbUtility.h"
 #include "UsbDesc.h"
@@ -132,8 +135,9 @@ typedef struct _USB_HUB_API    USB_HUB_API;
 //
 #define  USB_BUS_TPL  TPL_NOTIFY
 
-#define  USB_INTERFACE_SIGNATURE  SIGNATURE_32 ('U', 'S', 'B', 'I')
-#define  USB_BUS_SIGNATURE        SIGNATURE_32 ('U', 'S', 'B', 'B')
+#define  USB_INTERFACE_SIGNATURE    SIGNATURE_32 ('U', 'S', 'B', 'I')
+#define  USB_BUS_SIGNATURE          SIGNATURE_32 ('U', 'S', 'B', 'B')
+#define  USB_ASSOCIATION_SIGNATURE  SIGNATURE_32 ('U', 'S', 'B', 'A')
 
 #define USB_BIT(a)                 ((UINTN)(1 << (a)))
 #define USB_BIT_IS_SET(Data, Bit)  ((BOOLEAN)(((Data) & (Bit)) == (Bit)))
@@ -141,6 +145,9 @@ typedef struct _USB_HUB_API    USB_HUB_API;
 #define USB_INTERFACE_FROM_USBIO(a) \
           CR(a, USB_INTERFACE, UsbIo, USB_INTERFACE_SIGNATURE)
 
+#define USB_ASSOCIATION_FROM_USBIA(a) \
+          CR(a, USB_ASSOCIATION, UsbIaProtocol, USB_ASSOCIATION_SIGNATURE)
+
 #define USB_BUS_FROM_THIS(a) \
           CR(a, USB_BUS, BusId, USB_BUS_SIGNATURE)
 
@@ -176,6 +183,9 @@ struct _USB_DEVICE {
   UINT16                                LangId[USB_MAX_LANG_ID];
   UINT16                                TotalLangId;
 
+  UINT8                                 NumOfAssociation;
+  USB_ASSOCIATION                       *Associations[USB_MAX_ASSOCIATION];
+
   UINT8                                 NumOfInterface;
   USB_INTERFACE                         *Interfaces[USB_MAX_INTERFACE];
 
@@ -230,6 +240,23 @@ struct _USB_INTERFACE {
   UINT8                       MaxSpeed;
 };
 
+//
+// Stands for a function implemented using interface association
+//
+struct _USB_ASSOCIATION {
+  UINTN                             Signature;
+  USB_DEVICE                        *Device;
+  USB_INTERFACE_ASSOCIATION_DESC    *IaDesc;
+
+  //
+  // Handles and protocols
+  //
+  EFI_HANDLE                              Handle;
+  EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL  UsbIaProtocol;
+  EFI_DEVICE_PATH_PROTOCOL                *DevicePath;
+  BOOLEAN                                 IsManaged;
+};
+
 //
 // Stands for the current USB Bus
 //
@@ -751,9 +778,10 @@ UsbBusControllerDriverStop (
   IN EFI_HANDLE                   *ChildHandleBuffer
   );
 
-extern EFI_USB_IO_PROTOCOL           mUsbIoProtocol;
-extern EFI_DRIVER_BINDING_PROTOCOL   mUsbBusDriverBinding;
-extern EFI_COMPONENT_NAME_PROTOCOL   mUsbBusComponentName;
-extern EFI_COMPONENT_NAME2_PROTOCOL  mUsbBusComponentName2;
+extern EFI_USB_IO_PROTOCOL                     mUsbIoProtocol;
+extern EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL  mUsbIaProtocol;
+extern EFI_DRIVER_BINDING_PROTOCOL             mUsbBusDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL             mUsbBusComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL            mUsbBusComponentName2;
 
 #endif
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
index dd85894346..fdb40f9110 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
@@ -2,6 +2,7 @@
 #  The Usb Bus Dxe driver is used to enumerate and manage all attached usb devices.
 #
 #  Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -40,6 +41,7 @@
   UsbUtility.c
   UsbDesc.h
   UsbBus.h
+  UsbIntfAssoc.c
 
 [Packages]
   MdePkg/MdePkg.dec
@@ -58,6 +60,7 @@
 
 [Protocols]
   gEfiUsbIoProtocolGuid                         ## BY_START
+  gEfiUsbInterfaceAssociationProtocolGuid
   ## TO_START
   ## BY_START
   gEfiDevicePathProtocolGuid
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c
index 8b078e7e49..f96df11e99 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c
@@ -3,6 +3,7 @@
     Manage Usb Descriptor List
 
 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -46,6 +47,24 @@ UsbFreeInterfaceDesc (
   FreePool (Setting);
 }
 
+/**
+  Free the interface association descriptor
+
+  @param  Iad               The interface association descriptor to free.
+
+**/
+VOID
+UsbFreeInterfaceAssociationDesc (
+  IN USB_INTERFACE_ASSOCIATION_DESC  *Iad
+  )
+{
+  if (Iad->Interfaces != NULL) {
+    FreePool (Iad->Interfaces);
+  }
+
+  FreePool (Iad);
+}
+
 /**
   Free a configuration descriptor with its interface
   descriptors. It may be initialized partially.
@@ -167,6 +186,11 @@ UsbCreateDesc (
       CtrlLen = sizeof (USB_ENDPOINT_DESC);
       break;
 
+    case USB_DESC_TYPE_INTERFACE_ASSOCIATION:
+      DescLen = sizeof (EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR);
+      CtrlLen = sizeof (USB_INTERFACE_ASSOCIATION_DESC);
+      break;
+
     default:
       ASSERT (FALSE);
       return NULL;
@@ -231,6 +255,7 @@ UsbCreateDesc (
     return NULL;
   }
 
+  ASSERT (CtrlLen >= DescLen);
   CopyMem (Desc, Head, (UINTN)DescLen);
 
   *Consumed = Offset;
@@ -319,6 +344,85 @@ ON_ERROR:
   return NULL;
 }
 
+/**
+  Parse an interface association and its interfaces.
+
+  @param  DescBuf               The buffer of raw interface association descriptor.
+  @param  Len                   The length of the raw descriptor buffer.
+  @param  Config                The device configuration buffer.
+  @param  Consumed              The number of raw descriptor consumed.
+
+  @return The created interface association descriptor or NULL if failed.
+
+**/
+USB_INTERFACE_ASSOCIATION_DESC *
+UsbParseInterfaceAssociationDesc (
+  IN UINT8            *DescBuf,
+  IN UINTN            Len,
+  IN USB_CONFIG_DESC  *Config,
+  OUT UINTN           *Consumed
+  )
+{
+  USB_INTERFACE_ASSOCIATION_DESC  *Iad;
+  UINT8                           Index;
+  UINT8                           IfIndex;
+  UINT8                           i;
+  UINTN                           NumIf;
+  UINTN                           Used;
+
+  *Consumed = 0;
+  Iad       = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_INTERFACE_ASSOCIATION, &Used);
+
+  if (Iad == NULL) {
+    return NULL;
+  }
+
+  //
+  // Create an array to hold the association's interfaces
+  //
+  NumIf = Iad->Desc.InterfaceCount;
+
+  DEBUG ((
+    DEBUG_INFO,
+    "UsbParseInterfaceAssociationDesc: interface association has %d interfaces\n",
+    (UINT32)NumIf
+    ));
+
+  ASSERT (NumIf > 0);
+  if (NumIf == 0) {
+    DEBUG ((DEBUG_ERROR, "UsbParseInterfaceAssociationDesc: no interfaces are reported for this association.\n"));
+    goto ON_ERROR;
+  }
+
+  Iad->Interfaces = AllocateZeroPool (sizeof (USB_INTERFACE_DESC *) * NumIf);
+
+  //
+  // Populate interfaces for this association
+  //
+  IfIndex = 0;
+  for (Index = Iad->Desc.FirstInterface; Index < (Iad->Desc.FirstInterface + Iad->Desc.InterfaceCount); Index++) {
+    for (i = 0; i < Config->Desc.NumInterfaces; i++) {
+      if (Index == Config->Interfaces[i]->Settings[0]->Desc.InterfaceNumber) {
+        break;
+      }
+    }
+
+    if (i == Config->Desc.NumInterfaces) {
+      DEBUG ((DEBUG_ERROR, "UsbParseInterfaceAssociationDesc: interface %d not found.\n", Index));
+      goto ON_ERROR;
+    }
+
+    Iad->Interfaces[IfIndex++] = Config->Interfaces[i];
+  }
+
+  *Consumed = Used;
+  return Iad;
+
+ON_ERROR:
+  UsbFreeInterfaceAssociationDesc (Iad);
+  return NULL;
+}
+
 /**
   Parse the configuration descriptor and its interfaces.
 
@@ -334,16 +438,22 @@ UsbParseConfigDesc (
   IN UINTN  Len
   )
 {
-  USB_CONFIG_DESC        *Config;
-  USB_INTERFACE_SETTING  *Setting;
-  USB_INTERFACE_DESC     *Interface;
-  UINTN                  Index;
-  UINTN                  NumIf;
-  UINTN                  Consumed;
+  USB_CONFIG_DESC                 *Config;
+  USB_INTERFACE_SETTING           *Setting;
+  USB_INTERFACE_DESC              *Interface;
+  UINTN                           Index;
+  UINTN                           NumIf;
+  UINTN                           Consumed;
+  UINT8                           *Buffer;
+  UINTN                           Length;
+  USB_INTERFACE_ASSOCIATION_DESC  *Iad;
 
   ASSERT (DescBuf != NULL);
 
-  Config = UsbCreateDesc (DescBuf, Len, USB_DESC_TYPE_CONFIG, &Consumed);
+  Buffer = DescBuf;
+  Length = Len;
+
+  Config = UsbCreateDesc (Buffer, Length, USB_DESC_TYPE_CONFIG, &Consumed);
 
   if (Config == NULL) {
     return NULL;
@@ -384,15 +494,15 @@ UsbParseConfigDesc (
   // setting 1|interface 1, setting 0|interface 2, setting 0|. Check
   // USB2.0 spec, page 267.
   //
-  DescBuf += Consumed;
-  Len     -= Consumed;
+  Buffer += Consumed;
+  Length -= Consumed;
 
   //
   // Make allowances for devices that return extra data at the
   // end of their config descriptors
   //
-  while (Len >= sizeof (EFI_USB_INTERFACE_DESCRIPTOR)) {
-    Setting = UsbParseInterfaceDesc (DescBuf, Len, &Consumed);
+  while (Length >= sizeof (EFI_USB_INTERFACE_DESCRIPTOR)) {
+    Setting = UsbParseInterfaceDesc (Buffer, Length, &Consumed);
 
     if (Setting == NULL) {
       DEBUG ((DEBUG_ERROR, "UsbParseConfigDesc: warning: failed to get interface setting, stop parsing now.\n"));
@@ -416,8 +526,36 @@ UsbParseConfigDesc (
     Interface->Settings[Interface->NumOfSetting] = Setting;
     Interface->NumOfSetting++;
 
-    DescBuf += Consumed;
-    Len     -= Consumed;
+    Buffer += Consumed;
+    Length -= Consumed;
+  }
+
+  //
+  // Process interface association descriptors.
+  //
+  Buffer = DescBuf;
+  Length = Len;
+
+  while (Length >= sizeof (EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR)) {
+    Iad = UsbParseInterfaceAssociationDesc (Buffer, Length, Config, &Consumed);
+
+    if (Iad == NULL) {
+      break;
+    }
+
+    //
+    // Insert the association descriptor to the corresponding set.
+    //
+    if (Config->NumOfIads >= USB_MAX_ASSOCIATION) {
+      DEBUG ((DEBUG_ERROR, "UsbParseConfigDesc: too many interface association descriptors in this configuration.\n"));
+      goto ON_ERROR;
+    }
+
+    Config->Iads[Config->NumOfIads] = Iad;
+    Config->NumOfIads++;
+
+    Buffer += Consumed;
+    Length -= Consumed;
   }
 
   return Config;
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h
index ce205e706d..03f38c7afa 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h
@@ -3,6 +3,7 @@
     Manage Usb Descriptor List
 
 Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -11,6 +12,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #define _USB_DESCRIPTOR_H_
 
 #define USB_MAX_INTERFACE_SETTING  256
+#define USB_MAX_ASSOCIATION        16
 
 //
 // The RequestType in EFI_USB_DEVICE_REQUEST is composed of
@@ -62,8 +64,15 @@ typedef struct {
 } USB_INTERFACE_DESC;
 
 typedef struct {
-  EFI_USB_CONFIG_DESCRIPTOR    Desc;
-  USB_INTERFACE_DESC           **Interfaces;
+  EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR    Desc;
+  USB_INTERFACE_DESC                          **Interfaces;
+} USB_INTERFACE_ASSOCIATION_DESC;
+
+typedef struct {
+  EFI_USB_CONFIG_DESCRIPTOR         Desc;
+  USB_INTERFACE_ASSOCIATION_DESC    *Iads[USB_MAX_ASSOCIATION];
+  UINT8                             NumOfIads;
+  USB_INTERFACE_DESC                **Interfaces;
 } USB_CONFIG_DESC;
 
 typedef struct {
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
index b3a40639f2..64786603c3 100644
--- a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbEnumer.c
@@ -3,6 +3,7 @@
     Usb bus enumeration support.
 
 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -41,6 +42,41 @@ UsbGetEndpointDesc (
   return NULL;
 }
 
+/**
+  Check if given Usb interface is a part of Usb association.
+
+  @param[in]  Device    The device may have the interface association descriptor.
+  @param[in]  IfDesc    The interface descriptor to check for.
+  @param[out] IfAssoc   The USB association device pointer.
+
+  @retval EFI_SUCCESS   IfDesc is found within associations, IfAssoc has valid pointer.
+  @retval EFI_NOT_FOUND IfDesc does no belong to any association.
+
+**/
+EFI_STATUS
+GetInterfaceAssociation (
+  IN USB_DEVICE          *Device,
+  IN USB_INTERFACE_DESC  *IfDesc,
+  OUT USB_ASSOCIATION    **IfAssoc
+  )
+{
+  UINTN            Index;
+  UINTN            i;
+  USB_ASSOCIATION  *Ia;
+
+  for (Index = 0; Index < Device->NumOfAssociation; Index++) {
+    Ia = Device->Associations[Index];
+    for (i = 0; i < Ia->IaDesc->Desc.InterfaceCount; i++) {
+      if (IfDesc->Settings[0]->Desc.InterfaceNumber == Ia->IaDesc->Interfaces[i]->Settings[0]->Desc.InterfaceNumber) {
+        *IfAssoc = Ia;
+        return EFI_SUCCESS;
+      }
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
 /**
   Free the resource used by USB interface.
 
@@ -54,9 +90,25 @@ UsbFreeInterface (
   IN USB_INTERFACE  *UsbIf
   )
 {
-  EFI_STATUS  Status;
+  EFI_STATUS       Status;
+  USB_ASSOCIATION  *UsbIa;
 
-  UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+  Status = GetInterfaceAssociation (UsbIf->Device, UsbIf->IfDesc, &UsbIa);
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // Close USB Interface Association Protocol by Child
+    //
+    Status = gBS->CloseProtocol (
+                    UsbIa->Handle,
+                    &gEfiUsbInterfaceAssociationProtocolGuid,
+                    mUsbBusDriverBinding.DriverBindingHandle,
+                    UsbIf->Handle
+                    );
+    DEBUG ((DEBUG_INFO, "UsbFreeInterface: close IAD protocol by child, %r\n", Status));
+  } else {
+    UsbCloseHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+  }
 
   Status = gBS->UninstallMultipleProtocolInterfaces (
                   UsbIf->Handle,
@@ -73,7 +125,66 @@ UsbFreeInterface (
 
     FreePool (UsbIf);
   } else {
-    UsbOpenHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+    Status = GetInterfaceAssociation (UsbIf->Device, UsbIf->IfDesc, &UsbIa);
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // Reopen USB Interface Assiciation Protocol by Child
+      //
+      Status = gBS->OpenProtocol (
+                      UsbIa->Handle,
+                      &gEfiUsbInterfaceAssociationProtocolGuid,
+                      (VOID **)&UsbIa,
+                      mUsbBusDriverBinding.DriverBindingHandle,
+                      UsbIf->Handle,
+                      EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                      );
+      DEBUG ((DEBUG_INFO, "UsbFreeInterface: reopen IAD for child, Status = %r\n", Status));
+    } else {
+      //
+      // Reopen USB Host Controller Protocol by Child
+      //
+      Status = UsbOpenHostProtoByChild (UsbIf->Device->Bus, UsbIf->Handle);
+      DEBUG ((DEBUG_INFO, "UsbFreeInterface: reopen host controller for child, Status = %r\n", Status));
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Free the resource used by USB association.
+
+  @param  UsbIa                 The USB association to free.
+
+  @retval EFI_ACCESS_DENIED     The Usb association resource is still occupied.
+  @retval EFI_SUCCESS           The Usb association resource is freed.
+
+**/
+EFI_STATUS
+UsbFreeAssociation (
+  IN USB_ASSOCIATION  *UsbIa
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+                  UsbIa->Handle,
+                  &gEfiDevicePathProtocolGuid,
+                  UsbIa->DevicePath,
+                  &gEfiUsbInterfaceAssociationProtocolGuid,
+                  &UsbIa->UsbIaProtocol,
+                  NULL
+                  );
+
+  if (!EFI_ERROR (Status)) {
+    if (UsbIa->DevicePath != NULL) {
+      FreePool (UsbIa->DevicePath);
+    }
+
+    FreePool (UsbIa);
+  } else {
+    UsbOpenHostProtoByChild (UsbIa->Device->Bus, UsbIa->Handle);
   }
 
   return Status;
@@ -82,6 +193,7 @@ UsbFreeInterface (
 /**
   Create an interface for the descriptor IfDesc. Each
   device's configuration can have several interfaces.
+  Interface may belong to interface association.
 
   @param  Device                The device has the interface descriptor.
   @param  IfDesc                The interface descriptor.
@@ -95,10 +207,12 @@ UsbCreateInterface (
   IN USB_INTERFACE_DESC  *IfDesc
   )
 {
-  USB_DEVICE_PATH  UsbNode;
-  USB_INTERFACE    *UsbIf;
-  USB_INTERFACE    *HubIf;
-  EFI_STATUS       Status;
+  USB_DEVICE_PATH           UsbNode;
+  USB_INTERFACE             *UsbIf;
+  USB_INTERFACE             *HubIf;
+  USB_ASSOCIATION           *UsbIa;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePathProtocol;
+  EFI_STATUS                Status;
 
   UsbIf = AllocateZeroPool (sizeof (USB_INTERFACE));
 
@@ -128,10 +242,17 @@ UsbCreateInterface (
 
   SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));
 
-  HubIf = Device->ParentIf;
-  ASSERT (HubIf != NULL);
+  Status = GetInterfaceAssociation (Device, IfDesc, &UsbIa);
+
+  if (!EFI_ERROR (Status)) {
+    DevicePathProtocol = UsbIa->DevicePath;
+  } else {
+    HubIf = Device->ParentIf;
+    ASSERT (HubIf != NULL);
+    DevicePathProtocol = HubIf->DevicePath;
+  }
 
-  UsbIf->DevicePath = AppendDevicePathNode (HubIf->DevicePath, &UsbNode.Header);
+  UsbIf->DevicePath = AppendDevicePathNode (DevicePathProtocol, &UsbNode.Header);
 
   if (UsbIf->DevicePath == NULL) {
     DEBUG ((DEBUG_ERROR, "UsbCreateInterface: failed to create device path\n"));
@@ -154,10 +275,28 @@ UsbCreateInterface (
     goto ON_ERROR;
   }
 
-  //
-  // Open USB Host Controller Protocol by Child
-  //
-  Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle);
+  Status = GetInterfaceAssociation (Device, IfDesc, &UsbIa);
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // Open USB Interface Assiciation Protocol by Child
+    //
+    Status = gBS->OpenProtocol (
+                    UsbIa->Handle,
+                    &gEfiUsbInterfaceAssociationProtocolGuid,
+                    (VOID **)&UsbIa,
+                    mUsbBusDriverBinding.DriverBindingHandle,
+                    UsbIf->Handle,
+                    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                    );
+    DEBUG ((DEBUG_INFO, "UsbCreateInterface: open IAD for child, Status = %r\n", Status));
+  } else {
+    //
+    // Open USB Host Controller Protocol by Child
+    //
+    Status = UsbOpenHostProtoByChild (Device->Bus, UsbIf->Handle);
+    DEBUG ((DEBUG_INFO, "UsbCreateInterface: open host controller for child, Status = %r\n", Status));
+  }
 
   if (EFI_ERROR (Status)) {
     gBS->UninstallMultipleProtocolInterfaces (
@@ -184,6 +323,112 @@ ON_ERROR:
   return NULL;
 }
 
+/**
+  Create an interface association instance and install protocols to manage it.
+
+  @param  Device        The Usb device that has the interface association.
+  @param  Index         The interface association index within Device.
+  @param  IfAssocDesc   The interface association descriptor.
+
+  @return The created USB interface association, or NULL.
+
+**/
+USB_ASSOCIATION *
+UsbCreateAssociation (
+  IN USB_DEVICE                      *Device,
+  IN UINT8                           Index,
+  IN USB_INTERFACE_ASSOCIATION_DESC  *IfAssocDesc
+  )
+{
+  USB_DEVICE_PATH  UsbNode;
+  USB_ASSOCIATION  *UsbAssoc;
+  EFI_STATUS       Status;
+
+  UsbAssoc = AllocateZeroPool (sizeof (USB_ASSOCIATION));
+
+  if (UsbAssoc == NULL) {
+    return NULL;
+  }
+
+  UsbAssoc->Signature = USB_ASSOCIATION_SIGNATURE;
+  UsbAssoc->Device    = Device;
+  UsbAssoc->IaDesc    = IfAssocDesc;
+
+  CopyMem (
+    &(UsbAssoc->UsbIaProtocol),
+    &mUsbIaProtocol,
+    sizeof (EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL)
+    );
+
+  //
+  // Install USB association protocols
+  //
+  // Device path protocol for the association has the USB node similar to the
+  // one installed for USB interface.
+  //
+
+  UsbNode.Header.Type      = MESSAGING_DEVICE_PATH;
+  UsbNode.Header.SubType   = MSG_USB_DP;
+  UsbNode.ParentPortNumber = Device->ParentPort;
+  UsbNode.InterfaceNumber  = Index;
+
+  SetDevicePathNodeLength (&UsbNode.Header, sizeof (UsbNode));
+
+  ASSERT (Device->ParentIf != NULL);
+
+  UsbAssoc->DevicePath = AppendDevicePathNode (Device->ParentIf->DevicePath, &UsbNode.Header);
+
+  if (UsbAssoc->DevicePath == NULL) {
+    DEBUG ((DEBUG_ERROR, "UsbCreateAssociation: failed to create device path\n"));
+
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &UsbAssoc->Handle,
+                  &gEfiDevicePathProtocolGuid,
+                  UsbAssoc->DevicePath,
+                  &gEfiUsbInterfaceAssociationProtocolGuid,
+                  &UsbAssoc->UsbIaProtocol,
+                  NULL
+                  );
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "UsbCreateAssociation: failed to install UsbIad - %r\n", Status));
+    goto ON_ERROR;
+  }
+
+  //
+  // Open USB Host Controller Protocol by Child
+  //
+  Status = UsbOpenHostProtoByChild (Device->Bus, UsbAssoc->Handle);
+
+  if (EFI_ERROR (Status)) {
+    gBS->UninstallMultipleProtocolInterfaces (
+           UsbAssoc->Handle,
+           &gEfiDevicePathProtocolGuid,
+           UsbAssoc->DevicePath,
+           &gEfiUsbInterfaceAssociationProtocolGuid,
+           &UsbAssoc->UsbIaProtocol,
+           NULL
+           );
+
+    DEBUG ((DEBUG_ERROR, "UsbCreateAssociation: failed to open host for child - %r\n", Status));
+    goto ON_ERROR;
+  }
+
+  return UsbAssoc;
+
+ON_ERROR:
+  if (UsbAssoc->DevicePath != NULL) {
+    FreePool (UsbAssoc->DevicePath);
+  }
+
+  FreePool (UsbAssoc);
+  return NULL;
+}
+
 /**
   Free the resource used by this USB device.
 
@@ -236,6 +481,41 @@ UsbCreateDevice (
   return Device;
 }
 
+/**
+  Connect USB controller at TPL_CALLBACK
+
+  This function is called in both UsbIoControlTransfer and
+  the timer callback in hub enumeration. So, at least it is
+  called at TPL_CALLBACK. Some driver sitting on USB has
+  twisted TPL used. It should be no problem for us to connect
+  or disconnect at CALLBACK.
+
+  @param  Handle        Controller handle to be connected
+
+**/
+EFI_STATUS
+UsbConnectController (
+  EFI_HANDLE  Handle
+  )
+{
+  EFI_STATUS  Status;
+  EFI_TPL     OldTpl;
+
+  OldTpl = UsbGetCurrentTpl ();
+  DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL before connect is %d, %p\n", (UINT32)OldTpl, Handle));
+
+  gBS->RestoreTPL (TPL_CALLBACK);
+
+  Status = gBS->ConnectController (Handle, NULL, NULL, TRUE);
+
+  DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl ()));
+  ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
+
+  gBS->RaiseTPL (OldTpl);
+
+  return Status;
+}
+
 /**
   Connect the USB interface with its driver. EFI USB bus will
   create a USB interface for each separate interface descriptor.
@@ -252,7 +532,6 @@ UsbConnectDriver (
   )
 {
   EFI_STATUS  Status;
-  EFI_TPL     OldTpl;
 
   Status = EFI_SUCCESS;
 
@@ -264,30 +543,12 @@ UsbConnectDriver (
     DEBUG ((DEBUG_INFO, "UsbConnectDriver: found a hub device\n"));
     Status = mUsbHubApi.Init (UsbIf);
   } else {
-    //
-    // This function is called in both UsbIoControlTransfer and
-    // the timer callback in hub enumeration. So, at least it is
-    // called at TPL_CALLBACK. Some driver sitting on USB has
-    // twisted TPL used. It should be no problem for us to connect
-    // or disconnect at CALLBACK.
-    //
-
     //
     // Only recursively wanted usb child device
     //
     if (UsbBusIsWantedUsbIO (UsbIf->Device->Bus, UsbIf)) {
-      OldTpl = UsbGetCurrentTpl ();
-      DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL before connect is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));
-
-      gBS->RestoreTPL (TPL_CALLBACK);
-
-      Status           = gBS->ConnectController (UsbIf->Handle, NULL, NULL, TRUE);
+      Status           = UsbConnectController (UsbIf->Handle);
       UsbIf->IsManaged = (BOOLEAN) !EFI_ERROR (Status);
-
-      DEBUG ((DEBUG_INFO, "UsbConnectDriver: TPL after connect is %d\n", (UINT32)UsbGetCurrentTpl ()));
-      ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
-
-      gBS->RaiseTPL (OldTpl);
     }
   }
 
@@ -378,6 +639,7 @@ UsbSelectConfig (
   USB_INTERFACE       *UsbIf;
   EFI_STATUS          Status;
   UINT8               Index;
+  USB_ASSOCIATION     *UsbIa;
 
   //
   // Locate the active config, then set the device's pointer
@@ -393,7 +655,7 @@ UsbSelectConfig (
     }
   }
 
-  if (Index == DevDesc->Desc.NumConfigurations) {
+  if ((ConfigDesc == NULL) || (Index == DevDesc->Desc.NumConfigurations)) {
     return EFI_NOT_FOUND;
   }
 
@@ -406,6 +668,19 @@ UsbSelectConfig (
     Device->Address
     ));
 
+  //
+  // Create interfaces for each USB interface association descriptor.
+  //
+  Device->NumOfAssociation = ConfigDesc->NumOfIads;
+
+  for (Index = 0; Index < ConfigDesc->NumOfIads; Index++) {
+    DEBUG ((DEBUG_INFO, "UsbSelectConfig: process IAD %d\n", Index));
+
+    UsbIa = UsbCreateAssociation (Device, Index, ConfigDesc->Iads[Index]);
+    ASSERT (Index < USB_MAX_ASSOCIATION);
+    Device->Associations[Index] = UsbIa;
+  }
+
   //
   // Create interfaces for each USB interface descriptor.
   //
@@ -448,9 +723,55 @@ UsbSelectConfig (
 
   Device->NumOfInterface = Index;
 
+  //
+  // Connect association device drivers. Connection may fail if if device driver has been already
+  // started for any UsbIo that belongs to the association.
+  //
+  for (Index = 0; Index < Device->NumOfAssociation; Index++) {
+    Status                                 = gBS->ConnectController (Device->Associations[Index]->Handle, NULL, NULL, TRUE);
+    Device->Associations[Index]->IsManaged = (BOOLEAN) !EFI_ERROR (Status);
+
+    DEBUG ((DEBUG_INFO, "UsbSelectConfig: association driver connect: %r\n", Status));
+  }
+
   return EFI_SUCCESS;
 }
 
+/**
+  Disconnect USB controller at TPL_CALLBACK
+
+  This function is called in both UsbIoControlTransfer and
+  the timer callback in hub enumeration. So, at least it is
+  called at TPL_CALLBACK. Some driver sitting on USB has
+  twisted TPL used. It should be no problem for us to connect
+  or disconnect at CALLBACK.
+
+  @param  Handle        Controller handle to be disconnected
+
+**/
+EFI_STATUS
+UsbDisconnectController (
+  EFI_HANDLE  Handle
+  )
+{
+  EFI_TPL     OldTpl;
+  EFI_STATUS  Status;
+
+  OldTpl = UsbGetCurrentTpl ();
+  DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: old TPL is %d, %p\n", (UINT32)OldTpl, Handle));
+
+  gBS->RestoreTPL (TPL_CALLBACK);
+
+  Status = gBS->DisconnectController (Handle, NULL, NULL);
+
+  DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl (), Status));
+  ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
+
+  gBS->RaiseTPL (OldTpl);
+
+  return Status;
+}
+
 /**
   Disconnect the USB interface with its driver.
 
@@ -462,7 +783,6 @@ UsbDisconnectDriver (
   IN USB_INTERFACE  *UsbIf
   )
 {
-  EFI_TPL     OldTpl;
   EFI_STATUS  Status;
 
   //
@@ -473,27 +793,11 @@ UsbDisconnectDriver (
   if (UsbIf->IsHub) {
     Status = UsbIf->HubApi->Release (UsbIf);
   } else if (UsbIf->IsManaged) {
-    //
-    // This function is called in both UsbIoControlTransfer and
-    // the timer callback in hub enumeration. So, at least it is
-    // called at TPL_CALLBACK. Some driver sitting on USB has
-    // twisted TPL used. It should be no problem for us to connect
-    // or disconnect at CALLBACK.
-    //
-    OldTpl = UsbGetCurrentTpl ();
-    DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: old TPL is %d, %p\n", (UINT32)OldTpl, UsbIf->Handle));
+    Status = UsbDisconnectController (UsbIf->Handle);
 
-    gBS->RestoreTPL (TPL_CALLBACK);
-
-    Status = gBS->DisconnectController (UsbIf->Handle, NULL, NULL);
     if (!EFI_ERROR (Status)) {
       UsbIf->IsManaged = FALSE;
     }
-
-    DEBUG ((DEBUG_INFO, "UsbDisconnectDriver: TPL after disconnect is %d, %d\n", (UINT32)UsbGetCurrentTpl (), Status));
-    ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
-
-    gBS->RaiseTPL (OldTpl);
   }
 
   return Status;
@@ -510,10 +814,11 @@ UsbRemoveConfig (
   IN USB_DEVICE  *Device
   )
 {
-  USB_INTERFACE  *UsbIf;
-  UINTN          Index;
-  EFI_STATUS     Status;
-  EFI_STATUS     ReturnStatus;
+  USB_INTERFACE    *UsbIf;
+  USB_ASSOCIATION  *UsbIa;
+  UINTN            Index;
+  EFI_STATUS       Status;
+  EFI_STATUS       ReturnStatus;
 
   //
   // Remove each interface of the device
@@ -543,6 +848,35 @@ UsbRemoveConfig (
   }
 
   Device->ActiveConfig = NULL;
+
+  if (EFI_ERROR (ReturnStatus)) {
+    return ReturnStatus;
+  }
+
+  // ReturnStatus is EFI_SUCCESS
+
+  //
+  // Remove each interface association
+  //
+  for (Index = 0; Index < Device->NumOfAssociation; Index++) {
+    UsbIa = Device->Associations[Index];
+
+    Status = UsbDisconnectController (UsbIa->Handle);
+    if (!EFI_ERROR (Status)) {
+      Status = UsbFreeAssociation (UsbIa);
+      DEBUG ((DEBUG_INFO, "UsbRemoveConfig: free association: %r\n", Status));
+      if (EFI_ERROR (Status)) {
+        UsbConnectController (UsbIa->Handle);
+      }
+    }
+
+    if (!EFI_ERROR (Status)) {
+      Device->Associations[Index] = NULL;
+    } else {
+      ReturnStatus = Status;
+    }
+  }
+
   return ReturnStatus;
 }
 
diff --git a/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbIntfAssoc.c b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbIntfAssoc.c
new file mode 100644
index 0000000000..87218705b8
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbBusDxe/UsbIntfAssoc.c
@@ -0,0 +1,153 @@
+/** @file
+
+  Usb Interface Association Protocol implementation.
+
+Copyright (c) 2025, American Megatrends International, LLC. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "UsbBus.h"
+
+/**
+  Get the interface from interface association.
+
+  @param[in]  UsbIa   A pointer to the interface association data.
+  @param[in]  InterfaceNumber Interface to look for inside the association.
+
+  @retval A pointer to the interface data
+  @retval NULL  Interface is not found in the association.
+
+**/
+USB_INTERFACE_DESC *
+UsbAssocFindInterface (
+  IN USB_ASSOCIATION  *UsbIa,
+  IN UINT8            InterfaceNumber
+  )
+{
+  UINT8  Index;
+  UINT8  Intrf;
+
+  Intrf = UsbIa->IaDesc->Desc.FirstInterface;
+  for (Index = 0; Index < UsbIa->IaDesc->Desc.InterfaceCount; Index++) {
+    if (InterfaceNumber == Intrf) {
+      return UsbIa->IaDesc->Interfaces[Index];
+    }
+
+    Intrf++;
+  }
+
+  DEBUG ((DEBUG_ERROR, "UsbAssocFindInterface: interface 0x%x does not belong to this association\n", InterfaceNumber));
+  return NULL;
+}
+
+/**
+  Get the USB Interface Association Descriptor from the current USB configuration.
+
+  @param[in] This              A pointer to the EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL instance.
+  @param[out] Descriptor       A pointer to the caller allocated USB Interface Association Descriptor.
+
+  @retval EFI_SUCCESS           The descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER Descriptor is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbIaGetAssociationDescriptor (
+  IN  EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL    *This,
+  OUT EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR  *Descriptor
+  )
+{
+  USB_ASSOCIATION  *UsbIa;
+  EFI_TPL          OldTpl;
+  EFI_STATUS       Status;
+
+  OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+  Status = EFI_SUCCESS;
+
+  ASSERT (Descriptor != NULL);
+  if (Descriptor == NULL) {
+    Status = EFI_INVALID_PARAMETER;
+    goto ON_EXIT;
+  }
+
+  UsbIa = USB_ASSOCIATION_FROM_USBIA (This);
+  CopyMem (Descriptor, &(UsbIa->IaDesc->Desc), sizeof (EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR));
+
+ON_EXIT:
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+/**
+  Retrieve the details of the requested interface that belongs to USB association.
+
+  @param[in] This              A pointer to the EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL instance.
+  @param[in] InterfaceNumber   Interface number.
+  @param[out] UsbIo            A pointer to the caller allocated UsbIo protocol.
+  @param[out] SettingsCount    A pointer to the caller allocated number of settings for this interface.
+
+  @retval EFI_SUCCESS           Output parameters were updated successfully.
+  @retval EFI_INVALID_PARAMETER UsbIo or SettuntsCount is NULL.
+  @retval EFI_NOT_FOUND         Interface does not belong to this interface association.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbIaGetAssociateSettings (
+  IN  EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL  *This,
+  IN  UINT8                                   InterfaceNumber,
+  OUT EFI_USB_IO_PROTOCOL                     **UsbIo,
+  OUT UINTN                                   *SettingsCount
+  )
+{
+  USB_ASSOCIATION     *UsbIa;
+  EFI_TPL             OldTpl;
+  UINT8               Index;
+  EFI_STATUS          Status;
+  USB_DEVICE          *Device;
+  USB_INTERFACE_DESC  *IfDesc;
+
+  OldTpl = gBS->RaiseTPL (USB_BUS_TPL);
+
+  ASSERT ((UsbIo != NULL) && (SettingsCount != NULL));
+  if ((UsbIo == NULL) || (SettingsCount == NULL)) {
+    Status =  EFI_INVALID_PARAMETER;
+    goto  ON_EXIT;
+  }
+
+  UsbIa = USB_ASSOCIATION_FROM_USBIA (This);
+
+  IfDesc = UsbAssocFindInterface (UsbIa, InterfaceNumber);
+  if (IfDesc == NULL) {
+    Status = EFI_NOT_FOUND;
+    goto ON_EXIT;
+  }
+
+  *SettingsCount = IfDesc->NumOfSetting - 1;
+
+  //
+  // Find UsbIo protocol for this interface
+  //
+  Device = UsbIa->Device;
+  Status = EFI_NOT_FOUND;
+
+  for (Index = 0; Index < Device->NumOfInterface; Index++) {
+    if (Device->Interfaces[Index]->IfSetting->Desc.InterfaceNumber == InterfaceNumber) {
+      *UsbIo = &Device->Interfaces[Index]->UsbIo;
+      Status = EFI_SUCCESS;
+      break;
+    }
+  }
+
+ON_EXIT:
+  gBS->RestoreTPL (OldTpl);
+  return Status;
+}
+
+EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL  mUsbIaProtocol = {
+  UsbIaGetAssociationDescriptor,
+  UsbIaGetAssociateSettings
+};
diff --git a/MdePkg/Include/Protocol/UsbAssociationIo.h b/MdePkg/Include/Protocol/UsbAssociationIo.h
new file mode 100644
index 0000000000..8ce66207c9
--- /dev/null
+++ b/MdePkg/Include/Protocol/UsbAssociationIo.h
@@ -0,0 +1,81 @@
+/** @file
+  EFI Usb Interface Association protocol.
+
+  This protocol is used by USB drivers, running in the EFI boot services
+  environment to access USB devices that implement their configurations
+  using interface association: USB cameras, USB audio devices, USB modems, etc.
+
+  Copyright (c) 2025, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __USB_ASSOCIATION_IO_H__
+#define __USB_ASSOCIATION_IO_H__
+
+#include <IndustryStandard/Usb.h>
+#include <Protocol/UsbIo.h>
+
+typedef USB_INTERFACE_ASSOCIATION_DESCRIPTOR  EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR;
+
+//
+// Global ID for the USB IAD Protocol
+//
+#define EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL_GUID \
+  { \
+    0xf4279fb1, 0xef1e, 0x4346, { 0xab, 0x32, 0x3f, 0xe3, 0x86, 0xee, 0xb4, 0x52 } \
+  }
+
+typedef struct _EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL;
+
+/**
+  Get the USB Interface Association Descriptor from the current USB configuration.
+
+  @param[in]  This              A pointer to the EFI_USB_IA_PROTOCOL instance.
+  @param[out]  Descriptor       A pointer to the caller allocated USB Interface Association Descriptor.
+
+  @retval EFI_SUCCESS           The descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER Descriptor is NULL.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_USB_IA_GET_ASSOCIATION_DESCRIPTOR)(
+  IN  EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL    *This,
+  OUT EFI_USB_INTERFACE_ASSOCIATION_DESCRIPTOR  *Descriptor
+  );
+
+/**
+  Retrieve the details of the requested interface that belongs to USB association.
+
+  @param[in] This              A pointer to the EFI_USB_IA_PROTOCOL instance.
+  @param[in] InterfaceNumber   Interface number.
+  @param[out] UsbIo            A pointer to the caller allocated UsbIo protocol.
+  @param[out] SettingsCount    A pointer to the caller allocated number of settings for this interface.
+
+  @retval EFI_SUCCESS           Output parameters were updated successfully.
+  @retval EFI_INVALID_PARAMETER UsbIo or SettuntsCount is NULL.
+  @retval EFI_NOT_FOUND         Interface does not belong to this interface association.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_USB_IA_GET_ASSOCIATE_SETTINGS)(
+  IN  EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL  *This,
+  IN  UINT8                                   InterfaceNumber,
+  OUT EFI_USB_IO_PROTOCOL                     **UsbIo,
+  OUT UINTN                                   *SettingsCount
+  );
+
+/**
+  USB interface association protocol functions.
+
+**/
+struct _EFI_USB_INTERFACE_ASSOCIATION_PROTOCOL {
+  EFI_USB_IA_GET_ASSOCIATION_DESCRIPTOR  UsbIaGetAssociationDescriptor;
+  EFI_USB_IA_GET_ASSOCIATE_SETTINGS      UsbIaGetAssociateSettings;
+};
+
+extern EFI_GUID  gEfiUsbInterfaceAssociationProtocolGuid;
+
+#endif
diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
index f4a60e5cc7..3fc30c23b9 100644
--- a/MdePkg/MdePkg.dec
+++ b/MdePkg/MdePkg.dec
@@ -1991,6 +1991,9 @@
   ## Include/Protocol/ShellDynamicCommand.h
   gEfiShellDynamicCommandProtocolGuid  = { 0x3c7200e9, 0x005f, 0x4ea4, {0x87, 0xde, 0xa3, 0xdf, 0xac, 0x8a, 0x27, 0xc3 }}
 
+  ## Include/Protocol/UsbAssociationIo.h
+  gEfiUsbInterfaceAssociationProtocolGuid = { 0xf4279fb1, 0xef1e, 0x4346, { 0xab, 0x32, 0x3f, 0xe3, 0x86, 0xee, 0xb4, 0x52 }}
+
 #
 # [Error.gEfiMdePkgTokenSpaceGuid]
 #   0x80000001 | Invalid value provided.

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2025-01-06 21:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-03 21:39 [edk2-devel] [staging/usb_iad] MdeModulePkg UsbBusDxe: Add support for USB Interface Association olegi via groups.io
2025-01-04  2:10 ` 回复: " gaoliming via groups.io
2025-01-04  2:46   ` [edk2-devel] " olegi via groups.io
2025-01-04  9:06     ` 回复: " gaoliming via groups.io
2025-01-06 21:53       ` [edk2-devel] " olegi via groups.io

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox