Skip to content

Commit

Permalink
MdeModulePkg|UsbBusDxe: support USB Interface Association
Browse files Browse the repository at this point in the history
USB Interface Association is a group of UsbIo instances 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 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:
- creates USB association device
- installs device path
- installs USB association IO protocol

Signed-off-by: Oleg Ilyasov <[email protected]>
  • Loading branch information
olegilyasov committed Dec 21, 2024
1 parent 79a64e7 commit 4c2d6f1
Show file tree
Hide file tree
Showing 8 changed files with 826 additions and 78 deletions.
41 changes: 35 additions & 6 deletions MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
**/
Expand All @@ -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>
Expand All @@ -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"
Expand Down Expand Up @@ -132,15 +135,19 @@ 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)))

#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)

Expand Down Expand Up @@ -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];

Expand Down Expand Up @@ -230,6 +240,24 @@ 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_IA_PROTOCOL UsbIaProtocol;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
BOOLEAN IsManaged;
};

//
// Stands for the current USB Bus
//
Expand Down Expand Up @@ -752,6 +780,7 @@ UsbBusControllerDriverStop (
);

extern EFI_USB_IO_PROTOCOL mUsbIoProtocol;
extern EDKII_USB_IA_PROTOCOL mUsbIaProtocol;
extern EFI_DRIVER_BINDING_PROTOCOL mUsbBusDriverBinding;
extern EFI_COMPONENT_NAME_PROTOCOL mUsbBusComponentName;
extern EFI_COMPONENT_NAME2_PROTOCOL mUsbBusComponentName2;
Expand Down
5 changes: 4 additions & 1 deletion MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down Expand Up @@ -40,10 +41,11 @@
UsbUtility.c
UsbDesc.h
UsbBus.h
UsbIntfAssoc.c

[Packages]
MdePkg/MdePkg.dec

MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
MemoryAllocationLib
Expand All @@ -58,6 +60,7 @@

[Protocols]
gEfiUsbIoProtocolGuid ## BY_START
gEdkiiUsbIadProtocolGuid
## TO_START
## BY_START
gEfiDevicePathProtocolGuid
Expand Down
160 changes: 147 additions & 13 deletions MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
**/
Expand Down Expand Up @@ -46,6 +47,23 @@ 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.
Expand Down Expand Up @@ -167,6 +185,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;
Expand Down Expand Up @@ -231,6 +254,7 @@ UsbCreateDesc (
return NULL;
}

ASSERT (CtrlLen >= DescLen);
CopyMem (Desc, Head, (UINTN)DescLen);

*Consumed = Offset;
Expand Down Expand Up @@ -319,6 +343,82 @@ UsbParseInterfaceDesc (
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.
Expand All @@ -334,16 +434,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;

Check failure

Code scanning / CodeQL

Comparison of narrow type with wide type in loop condition High

Comparison between
Index
of type UINT8 and
... + ...
of wider type int.

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;
Expand Down Expand Up @@ -384,15 +490,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"));
Expand All @@ -416,8 +522,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;
Expand Down
13 changes: 11 additions & 2 deletions MdeModulePkg/Bus/Usb/UsbBusDxe/UsbDesc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
**/
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 4c2d6f1

Please sign in to comment.