-
-
Notifications
You must be signed in to change notification settings - Fork 2
Combining multiple USB Functions — IAD
You can combine multiple USB Functions using an Interface Association Descriptor. Each IAD will result in a separate device being created. To use an IAD, just put the IAD before the first interface of each function and define the class, subclass and protocol in the IAD instead of the device descriptor.
To tell the host to look for IADs, set the device descriptors class, subclass and protocol to 0xEF
, 0x02
and 0x01
.
For IADs, the class and subclass need to be completely defined, especially for CDC devices. CDC devices allow to defer the subclass down to the management interface definition, but that won't do with IADs. Instead you'll have to fully specify the subclass and protocol you will use.
Example of a combined device offering ethernet emulation (NCM) and a Virtual COM Port (CDC)
typedef struct {
unsigned char Length;
unsigned char DescriptorType;
unsigned char FirstInterface;
unsigned char InterfaceCount;
unsigned char Class;
unsigned char SubClass;
unsigned char Protocol;
unsigned char strFunction;
} USB_FUNC_IAD;
static const USB_DESCRIPTOR_DEVICE DeviceDescriptor = {
.Length = 18,
.Type = 0x01,
.USBVersion = 0x0200,
.DeviceClass = 0xEF,
.DeviceSubClass = 0x02,
.DeviceProtocol = 0x01,
.MaxPacketSize = 64,
.VendorID = 0x16C0,
.ProductID = 0x088B,
.DeviceVersion = 0x0100,
.strManufacturer = 1,
.strProduct = 2,
.strSerialNumber = 3,
.Configurations = 1};
static const USB_DESCRIPTOR_CONFIG ConfigDescriptor = {
.Length = 9,
.Type = 0x02,
.TotalLength = 155,
.Interfaces = 4,
.ConfigurationID = 1,
.strConfiguration = 0,
.Attributes = (1 << 7),
.MaxPower = 50};
static const USB_FUNC_IAD NCM_IAD = {
.Length = 8,
.DescriptorType = 0x0B,
.FirstInterface = 0,
.InterfaceCount = 2,
.Class = 0x02,
.SubClass = 0x00,
.Protocol = 0x00,
.strFunction = 0};
static const USB_DESCRIPTOR_INTERFACE NCMInterface = {
.Length = 9,
.Type = 0x04,
.InterfaceID = 0,
.AlternateID = 0,
.Endpoints = 1,
.Class = 0x02,
.SubClass = 0x0D,
.Protocol = 0x00,
.strInterface = 4};
static const USB_DESC_FUNC_HEADER NCMFuncHeader = {
.Length = 5,
.Type = CS_INTERFACE,
.SubType = FUNC_HEADER,
.CDCVersion = 0x0110};
static const USB_DESC_FUNC_UNION1 NCMFuncUnion = {
.Length = 5,
.Type = CS_INTERFACE,
.SubType = FUNC_UNION,
.ControlInterface = 0,
.SubInterface0 = 1};
static const USB_DESC_FUNC_ECM NCMFuncETH = {
.Length = 13,
.Type = CS_INTERFACE,
.SubType = FUNC_ECM,
.MaxSegmentSize = 1514,
.strMacAddress = 20};
static const USB_DESC_FUNC_NCM NCMFuncNCM = {
.Length = 6,
.Type = CS_INTERFACE,
.SubType = FUNC_NCM,
.NcmVersion = 0x0100,
.NetworkCapabilities = 0b10000};
static const USB_DESCRIPTOR_INTERFACE DataInterfaces[2] = {
{
.Length = 9,
.Type = 0x04,
.InterfaceID = 1,
.AlternateID = 0,
.Endpoints = 0,
.Class = 0x0A,
.SubClass = 0x00,
.Protocol = 0x01,
.strInterface = 0,
},
{.Length = 9,
.Type = 0x04,
.InterfaceID = 1,
.AlternateID = 1,
.Endpoints = 2,
.Class = 0x0A,
.SubClass = 0x00,
.Protocol = 0x01,
.strInterface = 0}};
static const USB_DESCRIPTOR_ENDPOINT NCM_Endpoints[3] = {
{.Length = 7,
.Type = 0x05,
.Address = (1 << 7) | 1,
.Attributes = 0x03,
.MaxPacketSize = 16,
.Interval = 50},
{.Length = 7,
.Type = 0x05,
.Address = 2,
.Attributes = 0x02,
.MaxPacketSize = 64,
.Interval = 1},
{.Length = 7,
.Type = 0x05,
.Address = (1 << 7) | 2,
.Attributes = 0x02,
.MaxPacketSize = 64,
.Interval = 0}};
static const USB_FUNC_IAD CDC_IAD = {
.Length = 8,
.DescriptorType = 0x0B,
.FirstInterface = 2,
.InterfaceCount = 2,
.Class = 0x02,
.SubClass = 0x00,
.Protocol = 0x00,
.strFunction = 0};
static const USB_DESCRIPTOR_INTERFACE CDCInterfaces[2] = {
{.Length = 9,
.Type = 0x04,
.InterfaceID = 2,
.AlternateID = 0,
.Endpoints = 1,
.Class = 0x02,
.SubClass = 0x02,
.Protocol = 0x01,
.strInterface = 0},
{.Length = 9,
.Type = 0x04,
.InterfaceID = 3,
.AlternateID = 0,
.Endpoints = 2,
.Class = 0x0A,
.SubClass = 0x00,
.Protocol = 0x00,
.strInterface = 0}};
static const USB_DESC_FUNC_HEADER CDCFuncHeader = {
.Length = 5,
.Type = 0x24,
.SubType = 0x00,
.CDCVersion = 0x0110};
static const USB_DESC_FUNC_ACM CDCFuncACM = {
.Length = 4,
.Type = 0x24,
.SubType = 0x02,
.Capabilities = (1 << 1)};
static const USB_DESC_FUNC_UNION1 CDCFuncUnion = {
.Length = 5,
.Type = 0x24,
.SubType = 0x06,
.ControlInterface = 2,
.SubInterface0 = 3};
static const USB_DESCRIPTOR_ENDPOINT CDCEndpoints[3] = {
{.Length = 7,
.Type = 0x05,
.Address = (1 << 7) | 3,
.Attributes = 0x03,
.MaxPacketSize = 8,
.Interval = 50},
{.Length = 7,
.Type = 0x05,
.Address = 4,
.Attributes = 0x02,
.MaxPacketSize = 64,
.Interval = 0},
{.Length = 7,
.Type = 0x05,
.Address = (1 << 7) | 4,
.Attributes = 0x02,
.MaxPacketSize = 64,
.Interval = 0}};
char *USB_GetConfigDescriptor(short *length) {
if (ConfigurationBuffer[0] == 0) {
short offset = 0;
AddToDescriptor(&ConfigDescriptor, &offset);
AddToDescriptor(&NCM_IAD, &offset);
AddToDescriptor(&NCMInterface, &offset);
AddToDescriptor(&NCMFuncHeader, &offset);
AddToDescriptor(&NCMFuncUnion, &offset);
AddToDescriptor(&NCMFuncETH, &offset);
AddToDescriptor(&NCMFuncNCM, &offset);
AddToDescriptor(&NCM_Endpoints[0], &offset);
AddToDescriptor(&DataInterfaces[0], &offset);
AddToDescriptor(&DataInterfaces[1], &offset);
AddToDescriptor(&NCM_Endpoints[1], &offset);
AddToDescriptor(&NCM_Endpoints[2], &offset);
AddToDescriptor(&CDC_IAD, &offset);
AddToDescriptor(&CDCInterfaces[0], &offset);
AddToDescriptor(&CDCFuncHeader, &offset);
AddToDescriptor(&CDCFuncACM, &offset);
AddToDescriptor(&CDCFuncUnion, &offset);
AddToDescriptor(&CDCEndpoints[0], &offset);
AddToDescriptor(&CDCInterfaces[1], &offset);
AddToDescriptor(&CDCEndpoints[1], &offset);
AddToDescriptor(&CDCEndpoints[2], &offset);
}
*length = sizeof(ConfigurationBuffer);
return ConfigurationBuffer;
}