Composite Device User's Guide
Composite Device User's Guide
Contents
1 Overview
This document describes steps to implement the 1 Overview .................................................................. 1
composite device based on the USB stack.
2 Introduction............................................................... 2
The USB Stack provides two composite device 3 Setup ........................................................................ 2
demos, hid+audio and msc+cdc. However, 4 USB Composite Device Structures ........................... 2
users can create composite devices to fit their 5 USB descriptor functions .......................................... 5
needs. This document is a detailed step-by-step 6 USB Stack Configurations ........................................ 7
guide to create a customizable composite
7 Application template ................................................. 8
device.
8 CDC+CDC Composite device example .................. 11
9 Revision history ...................................................... 20
2 Introduction
The composite device combines multiple independent functionalities by unifying independent
functionality code into one example. For example, the single functionality code for CDC is
provided in the CDC example and the single functionality code for MSC is provided in the MSC
example. Creating the CDC+MSC composite device example requires combining the CDC
example code and MSC example code into a single example.
Composite device descriptors are combined from the single-function device descriptors. There are
two single-function devices and each device has only one interface descriptor in its configuration
descriptor. If the composite device is combined using two single function devices, the interface
descriptor of each device should be merged into the composite device configuration descriptor.
Implementing a composite device involves combining the descriptors and the functionality of the
single function devices.
3 Setup
Before developing the composite device, the user needs to:
1. Decide how many classes to include in this composite device.
2. Decide which types of classes to include in this composite device, for example, HID +
AUDIO, HID + HID, etc.
3. Prepare the device descriptor depending on use case. Particularly, the IAD should be used
for AUDIO/VIDEO class, (see
www.usb.org/developers/docs/whitepapers/iadclasscode_r10.pdf)
4. Ensure that the functionality of the single function device code is valid.
The variable “count” holds the number of classes included in the composite device. Because
composite device MSD+CDC includes two classes, the value of variable "count" is 2.
The type of “class” is usb_class_struct_t. See the next section for more information.
4.2 usb_class_struct_t
This structure is required for the composite device and provides information about each class.
This is an example for the composite device MSD + CDC:
static usb_class_struct_t usb_dec_class[USB_MSD_CDC_CLASS_MAX] =
{
{
. type = USB_CLASS_CDC,
. interfaces = USB_DESC_CONFIGURATION(2, usb_cdc_if),
},
{
. type = USB_CLASS_MSC,
. interfaces = USB_DESC_CONFIGURATION(1, usb_msd_if),
},
};
Type represents the type of each class included in the composite device. For example, the type of
MSD class is USB_CLASS_MSC.
Interfaces include detailed interface information about the class, including interface count, ep
count, ep type and ep direction. See next section for more information. Number “2” means that the
CDC class has two interfaces. The interface list is “usb_cdc_if”. Number “1” means that the MSD
class has one interface. The interface list is “usb_msd_if”. See next section for more information.
4.3 usb_interfaces_struct_t
This structure is required for the composite device and provides information about each class.
Prototype:
typedef struct _usb_interfaces_struct
{
uint8_t count;
usb_if_struct_t* interface;
Description:
count: interface numbers for each class.
interface: interface information list.
4.4 usb_if_struct_t
This structure is required for the composite device and provides information about each interface.
Prototype:
typedef struct _usb_if_struct
{
uint8_t index;
usb_endpoints_t endpoints;
} usb_if_struct_t;
Description:
index: interface index in the interface descriptor.
endpoints: ep information struct.
Number “0” holds the index of the CDC class control interface. In other words, in the interface
descriptor, the interface number is 0.
Number “1” means that the ep number of this interface is 1.
“cic_ep” is ep detail information structure. See Section 4.6 for more information.
MSD:
static usb_if_struct_t usb_msd_if[1] = {
USB_DESC_INTERFACE(2, 2, msd_ep),
};
In USB_DESC_INTERFACE(2, 2, msd_ep):
The first number “2” holds the index of the MSD class control interface. In other words, in the
interface descriptor, the interface number is 2.
The second number “2” means that the ep number of this interface is 2.
4.5 usb_endpoints_t
This structure is required for the composite device and provides ep information for each interface.
4.6 usb_ep_struct_t
This structure is required for the composite device and provides ep information.
This is an example for the composite device MSD + CDC:
This is CDC class control interface endpoint information.
usb_ep_struct_t cic_ep[CIC_ENDP_COUNT] = {
#if CIC_NOTIF_ELEM_SUPPORT
{
.ep_num = 5,
.type = USB_INTERRUPT_PIPE,
.direction = USB_SEND,
.size = 16
}
#endif
};
5.2 USB_Desc_Get_Descriptor
This function provides all standard descriptors, such as descriptor for device and class. All
composite devices must implement USB_STRING_DESCRIPTOR and
USB_STANDARD_DESCRIPTOR. If the class has a class-specific descriptor, it needs to be
added to this function. For example, for HID class, the USB_REPORT_DESCRIPTOR needs to be
added.
USB_Desc_Get_Descriptor is provided in the example when a composite device is HID + HID.
Omit part can be a reference in the USB Stack.
5.3 USB_Desc_Get_Entity
This function provides information about a class driver. Any composite equipment must
implement the USB_COMPOSITE_INFO and the USB_CLASS_INTERFACE_INDEX_INFO.
For Audio class, add the USB_AUDIO_UNITS.
For MSC, add the USB_MSC_LBA_INFO.
For PHDC, add the USB_PHDC_QOS_INFO.
This is an example for the composite device HID + HID.
uint8_t USB_Desc_Get_Entity(uint32_t handle, entity_type type, uint32_t * object)
{
switch (type)
{
case USB_CLASS_INFO:
break;
case USB_CLASS_INTERFACE_INDEX_INFO:
*object = 0xff;
if (handle ==
(uint32_t)g_composite_device.hid_generic.app_handle)
{
*object = (uint32_t)HID_GENERIC_INTERFACE_INDEX;
break;
}
Note:
USB_CLASS_INTERFACE_INDEX_INFO get the index of class
arrange. It is the position of usb_class_struct_t array for a class.
5.4 USB_Set_Configation
This is a most common configuration and the USB_Set_Configation can be empty. For a
composite device, if you complete this function, you should determine which class the handle
needs.
This is an example for the two CDC devices:
uint8_t USB_Set_Configation( cdc_handle_t handle, uint8_t config )
{
for(i = 0; i < usb_composite_info.count; i++)
{
switch(usb_composite_info.class[i].type)
{
case USB_CLASS_CDC:
if (handle == g_composite_device.cdc_vcom1.cdc_handle)
usb_dec_class[i].interfaces = usb_cdc_configuration[config - 1];
else if (handle == g_composite_device.cdc_vcom2.cdc_handle)
usb_dec_class[i].interfaces = usb_cdc2_configuration[config - 1];
break;
default:
break;
}
}
return USB_OK;
}
7 Application template
The main difference between the composite devices and other devices is application. Designing a
composite device application is to design a composite device demo.
Prototype:
typedef struct composite_device_struct
{
composite_handle_t composite_device;
hid_keyboard_struct_t hid_keyboard;
hid_mouse_struct_t hid_mouse;
composite_config_struct_t composite_device_config_callback;
class_config_struct_t composite_device_config_list[COMPOSITE_CFG_MAX];
}composite_device_struct_t;
2.
g_composite_device.composite_device_config_callback.count = 2;
g_composite_device.composite_device_config_callback.class_app_callback =
g_composite_device.composite_device_config_list;
3. Call USB_Composite_Init
USB_Composite_Init(CONTROLLER_ID,
&g_composite_device.composite_device_config_callback,
&g_composite_device.composite_device);
/* cdc1 endpoints definition: interface1 has one endpoint, interface2 has two endpoints*/
usb_ep_struct_t cic_ep[CIC_ENDP_COUNT] = {
#if CIC_NOTIF_ELEM_SUPPORT
{
CIC_NOTIF_ENDPOINT,
USB_INTERRUPT_PIPE,
USB_SEND,
CIC_NOTIF_ENDP_PACKET_SIZE
}
#endif
};
usb_ep_struct_t dic_ep[DIC_ENDP_COUNT] = {
#if DATA_CLASS_SUPPORT
{
DIC_BULK_IN_ENDPOINT,
USB_BULK_PIPE,
USB_SEND,
DIC_BULK_IN_ENDP_PACKET_SIZE
},
{
DIC_BULK_OUT_ENDPOINT,
USB_BULK_PIPE,
USB_RECV,
DIC_BULK_OUT_ENDP_PACKET_SIZE
/* cdc2 endpoints definition: interface1 has one endpoint, interface2 has two endpoints */
usb_ep_struct_t cic_ep2[CIC_ENDP_COUNT] = {
#if CIC_NOTIF_ELEM_SUPPORT
{
CIC2_NOTIF_ENDPOINT,
USB_INTERRUPT_PIPE,
USB_SEND,
CIC_NOTIF_ENDP_PACKET_SIZE
}
#endif
};
usb_ep_struct_t dic_ep2[DIC_ENDP_COUNT] = {
#if DATA_CLASS_SUPPORT
{
DIC2_BULK_IN_ENDPOINT,
USB_BULK_PIPE,
USB_SEND,
DIC_BULK_IN_ENDP_PACKET_SIZE
},
{
DIC2_BULK_OUT_ENDPOINT,
USB_BULK_PIPE,
USB_RECV,
DIC_BULK_OUT_ENDP_PACKET_SIZE
}
8.2.1 USB_Desc_Get_Descriptor
/* string descriptor get from usb_all_languages_t g_languages */
/* other descriptor get from g_std_descriptors array */
uint8_t USB_Desc_Get_Descriptor(…)
{
if (type == USB_STRING_DESCRIPTOR)
{
if(index == 0)
{
*descriptor = (uint8_t *)g_languages.languages_supported_string;
*size = g_languages.languages_supported_size;
}
else
{
uint8_t lang_id=0;
uint8_t lang_index=USB_MAX_LANGUAGES_SUPPORTED;
for(;lang_id< USB_MAX_LANGUAGES_SUPPORTED;lang_id++)
{
if(index == g_languages.usb_language[lang_id].language_id)
{
if(str_num < USB_MAX_STRING_DESCRIPTORS)
{
lang_index=str_num;
}
break;
}
}
*descriptor =
(uint8_t *)g_languages.usb_language[lang_id].lang_desc[str_num];
*size =
g_languages.usb_language[lang_id].lang_desc_size[lang_index];
}
}
else if (type < USB_MAX_STD_DESCRIPTORS+1)
{
*descriptor = (uint8_t *)g_std_descriptors [type];
if(*descriptor == NULL)
{
return USBERR_INVALID_REQ_TYPE;
}
8.2.2 USB_Desc_Get_Entity
/* USB_CLASS_INTERFACE_INDEX_INFO: Return different index based on different handle */
/* USB_COMPOSITE_INFO: return usb_composite_info defined in section 1*/
uint8_t USB_Desc_Get_Entity(uint32_t handle, entity_type type, uint32_t * object)
{
switch(type)
{
case USB_CLASS_INFO:
break;
case USB_CLASS_INTERFACE_INDEX_INFO:
*object = 0xff;
if (handle == (uint32_t)g_composite_device.cdc_vcom)
{
*object = (uint32_t)CDC_VCOM_INTERFACE_INDEX;
break;
}
else if (handle == (uint32_t)g_composite_device.msc_disk.app_handle)
{
*object = (uint32_t)MSC_DISK_INTERFACE_INDEX;
break;
}
break;
case USB_COMPOSITE_INFO:
*object = (uint32_t)&usb_composite_info;
break;
default :
break;
}/* End Switch */
return USB_OK;
}
8.2.3 USB_Set_Configation
/* set different class configure based on different handle. */
uint8_t USB_Set_Configation( cdc_handle_t handle, uint8_t config )
{
for(i = 0; i < usb_composite_info.count; i++)
{
switch(usb_composite_info.class[i].type)
{
case USB_CLASS_CDC:
if (handle == g_composite_device.cdc_vcom1.cdc_handle)
usb_dec_class[i].interfaces = usb_cdc_configuration[config - 1];
else if (handle == g_composite_device.cdc_vcom2.cdc_handle)
usb_dec_class[i].interfaces = usb_cdc2_configuration[config - 1];
/* cdc_struct_t is as follow. */
/* It contain variables for one cdc class. */
typedef struct _cdc_variable_struct
{
cdc_handle_t cdc_handle;
uint8_t g_curr_recv_buf[DATA_BUFF_SIZE];
uint8_t g_curr_send_buf[DATA_BUFF_SIZE];
uint8_t g_recv_size;
uint8_t g_send_size;
bool start_app;
bool start_transactions;
uint8_t out_endpoint;
uint8_t in_endpoint;
}cdc_struct_t;
2.
/* call cdc_vcom_preinit by parameter cdc_struct_t */
5.
g_composite_device.composite_device_config_callback.count = 2;
g_composite_device.composite_device_config_callback.class_app_callback =
g_composite_device.composite_device_config_list;
This table summarizes revisions to this document since the release of the previous version
Table 1 Revision History
Revision number Date Substantive changes