The file CdcMscConfig.fth contains a USB configuration for a composite Communications Device Class (CDC) and Mass Storage Class (MSC) application. For CDC and MSC only applications see CdcConfig.fth and MscConfig.fth.
Running composite devices on Windows without using a custom driver for Windows can be problematic. Windows 7, Vista and XP SP3 are fine, but XP SP2 is not. For XP, either upgrade to SP3 or install hotfixes. It seems you need:
We installed SP3 for testing and had to copy the new version of usbser.sys manually from SP3.cab. Use Windows search and copy the new version to replace the one in Windows\System32\Drivers.
The trick for composite devices is to use an Interface Aggregation Descriptor (IAD) for each instance of any class with more than one interface, e.g. CDC. Read the source code for the gory details!
You also need to take care with the start up code for composite devices. USB device start up consists of three steps.
UDATA
section, and so will not be initialised by the default MPE
initialisation code. This code should also be part of the
USB reset action for the class or interface.InitUSB
.USBconnect
.Your start up code for a composite device then consists of
initA initB ... InitUSB USBConnect
Versions of this stack from November 2010 onwards include
in UsbEP0.fth the word startUSBsys
which performs
these operations.
Note that interface numbers must be a contiguous set starting at 0.
0 equ dbgUHW? \ -- flag
Set true to get debug information from the USB hardware
layer. Some of the debug output must be done using
polled serial drivers.
0 equ dbgCore? \ -- flag
Set true to get debug information from the USB core layer.
Some of the debug output must be done using polled
serial drivers.
0 equ UsbPowered? \ -- 0/1 ; bus powered?
Set to one if bus powered.
1 equ UsbDMA? \ -- 0/1 ; uses DMA?
Set to one if any endpoints other than EP0 use DMA. At the
moment we assume that EP0 does not use DMA.
#64 equ /maxpacket0 \ -- u
Maximum size of packets on endpoint 0.
#64 equ /maxpacket \ -- u
Maximum size of packets on endpoints other than 0.
#32 equ #EPs \ -- u
Number of endpoints we are are going to deal with. On a
32 bit CPU, there are 32 bits per word. We need separate
bits for IN and OUT, so there's a practical maximum here
of 32, and USB supports 16 endpoints, each with IN and
OUT capability. Endpoint 0 is always IN and OUT capable.
4 equ #USBifs \ -- u
Number of interfaces we can expand to eventually.
0 equ HID? \ -- flag
True if a HID device, e.g. mouse, is to be supported.
1 equ CDC? \ -- flag
True if a communications device class (CDC) is to be supported.
The most common of these is a USB serial port.
1 equ MSC? \ -- flag
True if a mass storage device is to be supported.
0 equ AUDIO? \ -- flag
True if an audio device is to be supported.
resetUIFs
Force the interface numbers to start at zero.
NextUIF: CdcCIf# \ -- b
Interface number of the CDC control interface.
NextUIF: CdcDIf# \ -- b
Interface number of the CDC data interface.
$81 equ CDCIinEP \ -- ep
Optional Interrupt IN event notification endpoint (CIF).
$85 equ CDCBinEP \ -- ep
CDC Bulk IN endpoint (DIF).
$05 equ CDCBoutEP \ -- ep
CDC Bulk OUT endpoint (DIF).
1 equ dbgCDC? \ -- flag
set non-zero to get debug information from the CDC driver.
CDCIinEP EP>mask equ CDCIinEPMask \ -- mask
Core layer bit mask for the CDC Interrupt IN endpoint.
Used to avoid run-time calculation.
CDCBinEP EP>mask equ CDCBinEPMask \ -- mask
Core layer bit mask for the CDC bulk IN endpoint.
Used to avoid run-time calculation.
CDCBoutEP EP>mask equ CDCBoutEPMask \ -- mask
Core layer bit mask for the CDC bulk OUT endpoint.
Used to avoid run-time calculation.
NextUIF: MscIf# \ -- b
Interface number of the mass storage class.
$82 equ MscBinEP \ -- ep
MSC Bulk IN endpoint.
$02 equ MscBoutEP \ -- ep
MSC Bulk OUT endpoint.
0 equ dbgMSC? \ -- flag
set non-zero to get debug information from the MSC driver.
MscBinEP EP>mask equ MscBinEPMask \ -- mask
Core layer bit mask for the MSC bulk IN endpoint.
Used to avoid run-time calculation.
MscBoutEP EP>mask equ MscBoutEPMask \ -- mask
Core layer bit mask for the MSC bulk OUT endpoint.
Used to avoid run-time calculation.
This section contains the USB descriptors. There is a descriptor for the device, for its configuration, and for various strings. There are various other descriptors, both general and class class specific. Their use is absurdly badly documented and the actual requirement as to which are required is often operating system dependent. If it works with Linux, it will (probably) work with everything else.
Strings are accessed using a "string index". Apart from zero, which has a predefined meaning to define the language, string indices (1..255) are defined by the application. Here the indices are the offset in bytes from the start of a string table. If you need more than 255 bytes of string descriptors, change the index to be the offset into a table of string descriptor addresses.
Create CfgDesc \ -- addr
The configuration descriptor has a primary configuration, three
interfaces, and five endpoints. If there is more than one
configuration, additional ones must follow the first one.
create StrDesc \ -- addr
String descriptor and following strings.
create DevDescDefault \ -- addr
The device descriptor for Windows and Linux.
create DevDescOSX \ -- addr
The device descriptor for OSX.
0 value DevDescReqLen \ -- u
Holds request length of first device descriptor after reset.
It is used to auto-select the device descriptor
:noname 0 to DevDescReqLen ; AtUSBreset
Performed at USB reset.
: DevDesc \ -- addr
Return the address of the device descriptor. This word uses
a kluge to provide operation on Windows, Linux, and versions
of OS X before 10.5.6 which recognises the IAD descriptor.
If the word unit#
is defined, it should return a
32 bit unit serial number. This is used by the USB code
to return a serial number to the host. If the word
unit#
has not been defined, a default fixed serial
number is returned.
#34 buffer: UTFbuf \ -- addr
Holds a string descriptor with up to 16 little-endian Unicode
16-bit characters.
: usbSerialNum \ -- caddr len
Returns the default device USB serial number if unit#
is not defined.
: usbSerialNum \ -- caddr len
Returns the default device USB serial number if unit#
is not defined.
: USBStr@ \ index -- caddr len
Return the USB string descriptor corresponding to the given
string index. By convention the following order is used:
0 |
language identifier - do not change |
1 |
serial number |
2 |
product |
3 |
manufacturer |
If you configure your products with serial numbers extracted from non-USB strings, e.g. MAC numbers, modify this word to perform the string extraction as required.