This page describes how to use the usbd_framework to produce a USB Composite Device Driver, which appears as two USB Functions on host.
Details about the USB can be found in the USB specification 2.0, respectively.
The Composite driver is based on USB Device Framework Architecture.
Software Architecture Using the AT91 USB Framework
The Device descriptor of a composite device is very basic according to the definition in section 9.6.1, Universal Serial Bus Specification, Revision 2.0, but some of the fields may have different values based on if there is multi-interface function in the device: bDeviceClass, bDeviceSubClass, bDeviceProtocol set to zero for a device without multi-interface function inside; bDeviceClass, bDeviceSubClass, bDeviceProtocol set to EFH, 02H, 01H for a device with multi-interface function inside, such as CDC or Audio class function, as shown below:
// Composite Device Descriptor const USBDeviceDescriptor deviceDescriptor = { sizeof(USBDeviceDescriptor), USBGenericDescriptor_DEVICE, USBDeviceDescriptor_USB2_00, #if defined(usb_HIDMSD) 0x00, 0x00, 0x00, #else 0xEF,// MI 0x02,// 0x01,// #endif BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), COMPOSITEDDriverDescriptors_VENDORID, COMPOSITEDDriverDescriptors_PRODUCTID, COMPOSITEDDriverDescriptors_RELEASE, 0, // No string descriptor for manufacturer 1, // Index of product string descriptor is #1 0, // No string descriptor for serial number 1 // Device has 1 possible configuration };
Note that the Vendor ID is a special value attributed by the USB-IF organization. The product ID can be chosen freely by the vendor. The HID and MSD class functions are all single-interface functions, so the corresponding fields are set to zeros.
It's just standard one as defined in section 9.6.3, Universal Serial Bus Specification, Revision 2.0, with wTotalLength set to the total length of all standard and function specific descriptors followed, bNumInterface set to the summary number of interfaces of all functions used.
// Composite Configuration Descriptor { sizeof(USBConfigurationDescriptor), USBGenericDescriptor_CONFIGURATION, sizeof(CompositeDriverConfigurationDescriptors), COMPOSITEDDriverDescriptors_NUMINTERFACE, 1, // This is configuration #1 0, // No string descriptor for this configuration BOARD_USB_BMATTRIBUTES, USBConfigurationDescriptor_POWER(100) },
When the Configuration descriptor is requested by the host (by using the GET_DESCRIPTOR command), the device must also send all the related descriptors, i.e. IAD, Interface, Endpoint and Class-specific descriptors. It is convenient to create a single structure to hold all this data, for sending everything in one trunk.
See Interface Association Descriptor.
All these descriptors are almost the same as their definitions in a single USB device, except the interface index related or endpoint addresses related settings. Please refer to the following projects:
Several descriptors can be commented with a String Descriptor. The latter is completely optional and does not have any effect on the detection of the device by the operating system. Whether or not to include them is entirely up to the programmer.
There is one exception to this rule when using the MSD class. According to the specification, there must be a Serial Number string. It must contains at least 12 characters, and these character must only be either letters (a-z, A-Z) or numbers (0-9). This cause no problem for the driver in practice, but this is a strict requirement for certification. Also please remember that string descriptors use the Unicode format.
The composite itself does not define any special requests, but the device functions do.
Each function in the device has its own requests handler. The device invokes one of the function requests handler first, when the return value of the handler indicates the request is handled by the device function, then the device request handler return directly, but if it is not handled, the device request handler calls next function request handler to check it. It will be forwarded to the standard request handler if there is no valid handler for this request.
In the device function request handlers, wIndex of the request is checked first to confirm that the request is accepted by the function (interface). Then check if it is a standard request and handle it in two different way.
General Request Handler for Composite Device
For class-specific request, refer to their related documents:
Notifications are supported only if CDC serial port device function is included, please refer to USB Device CDC Serial Driver.
The function in the device may need some callbacks to handle the USB events, such as configured or changed interface.
For a HID Keyboard, the device function should start reporting to host through interrupt endpoint as soon as the device is configured, so USBDDriverCallbacks_ConfigurationChanged() should be re-implement to monitor the configured status of the device, and then forward the event to the device function to handle it. For more details, see USB Device HID Keyboard Driver.
The USB Audio Speaker defines alternative interfaces to control the USB bandwidth, so there is a callback defined to handle the event of AT91 USB Device Framework. The function is USBDDriverCallbacks_InterfaceSettingChanged(), which should be re-implemented to replace the original one. AUDDFunctionCallbacks_InterfaceSettingChanged() is invoked and then let the Audio Device Function to handle the event. For more details, see USB Device Audio Speaker Driver.
HID MSD Composite Driver Architecture
Device descriptors:
typedef struct { // Standard configuration descriptor. USBConfigurationDescriptor configuration; // --- HID USBInterfaceDescriptor hidInterface; HIDDescriptor hid; USBEndpointDescriptor hidInterruptIn; USBEndpointDescriptor hidInterruptOut; // --- MSD // Mass storage interface descriptor. USBInterfaceDescriptor msdInterface; // Bulk-out endpoint descriptor. USBEndpointDescriptor msdBulkOut; // Bulk-in endpoint descriptor. USBEndpointDescriptor msdBulkIn; } __attribute__ ((packed)) CompositeDriverConfigurationDescriptors;
HID Audio Composite Device Architecture
Device descriptors:
typedef struct { // Standard configuration descriptor. USBConfigurationDescriptor configuration; // --- HID USBInterfaceDescriptor hidInterface; HIDDescriptor hid; USBEndpointDescriptor hidInterruptIn; USBEndpointDescriptor hidInterruptOut; // --- AUDIO // IAD 1 USBInterfaceAssociationDescriptor audIAD; // Audio control interface. USBInterfaceDescriptor audInterface; // Descriptors for the audio control interface. AUDDSpeakerDriverAudioControlDescriptors audControl; // -- AUDIO out // Streaming out interface descriptor (with no endpoint, required). USBInterfaceDescriptor audStreamingOutNoIsochronous; // Streaming out interface descriptor. USBInterfaceDescriptor audStreamingOut; // Audio class descriptor for the streaming out interface. AUDStreamingInterfaceDescriptor audStreamingOutClass; // Stream format descriptor. AUDFormatTypeOneDescriptor1 audStreamingOutFormatType; // Streaming out endpoint descriptor. AUDEndpointDescriptor audStreamingOutEndpoint; // Audio class descriptor for the streaming out endpoint. AUDDataEndpointDescriptor audStreamingOutDataEndpoint; } __attribute__ ((packed)) CompositeDriverConfigurationDescriptors;
CDC HID Composite Device Architecture
Device descriptors:
typedef struct { // Standard configuration descriptor. USBConfigurationDescriptor configuration; // --- CDC 0 // IAD 0 USBInterfaceAssociationDescriptor cdcIAD0; // Communication interface descriptor USBInterfaceDescriptor cdcCommunication0; // CDC header functional descriptor. CDCHeaderDescriptor cdcHeader0; // CDC call management functional descriptor. CDCCallManagementDescriptor cdcCallManagement0; // CDC abstract control management functional descriptor. CDCAbstractControlManagementDescriptor cdcAbstractControlManagement0; // CDC union functional descriptor (with one slave interface). CDCUnionDescriptor cdcUnion0; // Notification endpoint descriptor. USBEndpointDescriptor cdcNotification0; // Data interface descriptor. USBInterfaceDescriptor cdcData0; // Data OUT endpoint descriptor. USBEndpointDescriptor cdcDataOut0; // Data IN endpoint descriptor. USBEndpointDescriptor cdcDataIn0; // --- HID USBInterfaceDescriptor hidInterface; HIDDescriptor hid; USBEndpointDescriptor hidInterruptIn; USBEndpointDescriptor hidInterruptOut; } __attribute__ ((packed)) CompositeDriverConfigurationDescriptors;
CDC MSD Composite Device Architecture
Device descriptors:
typedef struct { // Standard configuration descriptor. USBConfigurationDescriptor configuration; // --- CDC 0 // IAD 0 USBInterfaceAssociationDescriptor cdcIAD0; // Communication interface descriptor USBInterfaceDescriptor cdcCommunication0; // CDC header functional descriptor. CDCHeaderDescriptor cdcHeader0; // CDC call management functional descriptor. CDCCallManagementDescriptor cdcCallManagement0; // CDC abstract control management functional descriptor. CDCAbstractControlManagementDescriptor cdcAbstractControlManag // CDC union functional descriptor (with one slave interface). CDCUnionDescriptor cdcUnion0; // Notification endpoint descriptor. USBEndpointDescriptor cdcNotification0; // Data interface descriptor. USBInterfaceDescriptor cdcData0; // Data OUT endpoint descriptor. USBEndpointDescriptor cdcDataOut0; // Data IN endpoint descriptor. USBEndpointDescriptor cdcDataIn0; // --- MSD // Mass storage interface descriptor. USBInterfaceDescriptor msdInterface; // Bulk-out endpoint descriptor. USBEndpointDescriptor msdBulkOut; // Bulk-in endpoint descriptor. USBEndpointDescriptor msdBulkIn; } __attribute__ ((packed)) CompositeDriverConfigurationDescriptors;
CDC Audio Composite Device Architecture
Device descriptors:
typedef struct { // Standard configuration descriptor. USBConfigurationDescriptor configuration; // --- CDC 0 // IAD 0 USBInterfaceAssociationDescriptor cdcIAD0; // Communication interface descriptor USBInterfaceDescriptor cdcCommunication0; // CDC header functional descriptor. CDCHeaderDescriptor cdcHeader0; // CDC call management functional descriptor. CDCCallManagementDescriptor cdcCallManagement0; // CDC abstract control management functional descriptor. CDCAbstractControlManagementDescriptor cdcAbstractControlManagement0; // CDC union functional descriptor (with one slave interface). CDCUnionDescriptor cdcUnion0; // Notification endpoint descriptor. USBEndpointDescriptor cdcNotification0; // Data interface descriptor. USBInterfaceDescriptor cdcData0; // Data OUT endpoint descriptor. USBEndpointDescriptor cdcDataOut0; // Data IN endpoint descriptor. USBEndpointDescriptor cdcDataIn0; // --- AUDIO // IAD 1 USBInterfaceAssociationDescriptor audIAD; // Audio control interface. USBInterfaceDescriptor audInterface; // Descriptors for the audio control interface. AUDDSpeakerDriverAudioControlDescriptors audControl; // -- AUDIO out // Streaming out interface descriptor (with no endpoint, required). USBInterfaceDescriptor audStreamingOutNoIsochronous; // Streaming out interface descriptor. USBInterfaceDescriptor audStreamingOut; // Audio class descriptor for the streaming out interface. AUDStreamingInterfaceDescriptor audStreamingOutClass; // Stream format descriptor. AUDFormatTypeOneDescriptor1 audStreamingOutFormatType; // Streaming out endpoint descriptor. AUDEndpointDescriptor audStreamingOutEndpoint; // Audio class descriptor for the streaming out endpoint. AUDDataEndpointDescriptor audStreamingOutDataEndpoint; } __attribute__ ((packed)) CompositeDriverConfigurationDescriptors;
Dual CDC Composite Device Architecture
Device descriptors:
typedef struct { // Standard configuration descriptor. USBConfigurationDescriptor configuration; // --- CDC 0 // IAD 0 USBInterfaceAssociationDescriptor cdcIAD0; // Communication interface descriptor USBInterfaceDescriptor cdcCommunication0; // CDC header functional descriptor. CDCHeaderDescriptor cdcHeader0; // CDC call management functional descriptor. CDCCallManagementDescriptor cdcCallManagement0; // CDC abstract control management functional descriptor. CDCAbstractControlManagementDescriptor cdcAbstractControlManagement0; // CDC union functional descriptor (with one slave interface). CDCUnionDescriptor cdcUnion0; // Notification endpoint descriptor. USBEndpointDescriptor cdcNotification0; // Data interface descriptor. USBInterfaceDescriptor cdcData0; // Data OUT endpoint descriptor. USBEndpointDescriptor cdcDataOut0; // Data IN endpoint descriptor. USBEndpointDescriptor cdcDataIn0; // --- CDC 1 // IAD 1 USBInterfaceAssociationDescriptor cdcIAD1; // Communication interface descriptor USBInterfaceDescriptor cdcCommunication1; // CDC header functional descriptor. CDCHeaderDescriptor cdcHeader1; // CDC call management functional descriptor. CDCCallManagementDescriptor cdcCallManagement1; // CDC abstract control management functional descriptor. CDCAbstractControlManagementDescriptor cdcAbstractControlManagement1; // CDC union functional descriptor (with one slave interface). CDCUnionDescriptor cdcUnion1; // Notification endpoint descriptor. USBEndpointDescriptor cdcNotification1; // Data interface descriptor. USBInterfaceDescriptor cdcData1; // Data OUT endpoint descriptor. USBEndpointDescriptor cdcDataOut1; // Data IN endpoint descriptor. USBEndpointDescriptor cdcDataIn1; } __attribute__ ((packed)) CompositeDriverConfigurationDescriptors;
Normally the main application code can be divided into four parts:
This part initializes all peripherals and drivers, depending on the functions which are included. Normally there are following general initializations:
This part includes the necessary handler for Vbus detect, the media handler for MSD device function, USART handler for CDC serial port, and the SSC handler for Audio device function if needed. One extra handler may be needed for the timer or PIT or SYSTICK.
Some of the device function requires callback handler to execute the request from host, such as the mute state changed by the host, or data received or sent, so that another transfer can be started.
All driver function is non-blocked function, and many of them is called in the main loop, to receive data from the host, to scan the input keys and send report to host (for HID), to run the state machine (for MSD).
The device functions are generally supported by Microsoft Windows, but for composite device, some patches are needed. All the composite devices above are tested under windows XP (SP3) and works fine. For CDC serial port, additional windows driver file (.inf) is required.
See Windows Driver Update For Composite.
In order to make windows recognize the CDC serial device correctly, it is necessary to write a .inf file. Please refer to USB Device CDC Serial Driver for detailed information. Only one thing should be modified to match the composite device function installation.
For composite devices, the hardware ID is made up of the Vender ID, the Product ID and (optionally) the Device Release Number and the start interface number of the function. Those values are extracted from the device descriptors provided during the enumeration phase, the following is the example of the modification for the dual-port CDC serial:
[AtmelMfg] %USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6119&MI_00 ; 1st COM device %USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6119&MI_02 ; 2nd COM device
When a new device is plugged in for the first time, Windows looks for an appropriate specific or generic driver to use it. The composite device will be recognized as ˇ°USB Composite Deviceˇ±. Then Windows search the driver for the functions inside the composite device.
Please refer to the driver function related application notes for more details. The final installation file is as following:
[Version] Signature="$Chicago$" Class=Ports ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} Provider=%ATMEL% DriverVer=09/12/2006,1.1.1.5 [DestinationDirs] DefaultDestDir=12 [Manufacturer] %ATMEL%=AtmelMfg [AtmelMfg] %USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6127&MI_00 %USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6128&MI_00 %USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6129&MI_00 %USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6119&MI_00 %USBtoSerialConverter%=USBtoSer.Install,USB\VID_03EB&PID_6119&MI_02 [USBtoSer.Install] include=mdmcpq.inf CopyFiles=FakeModemCopyFileSection AddReg=USBtoSer.AddReg [USBtoSer.AddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,usbser.sys HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" [USBtoSer.Install.Services] AddService=usbser,0x00000002,USBtoSer.AddService [USBtoSer.AddService] DisplayName=%USBSer% ServiceType=1 StartType=3 ErrorControl=1 ServiceBinary=%12%\usbser.sys [Strings] ATMEL="ATMEL Corp." USBtoSerialConverter="AT91 USB to Serial Converter" USBSer="USB Serial Driver"