I have an embedded Linux 3.19 system with a USB device interface. The device needs to expose three USB interfaces to the host: a virtual network interface (RNDIS or CDC ECM) and two virtual serial ports (CDC ACM). The device needs to cooperate with modern Windows (7+) and Linux (3.16+) hosts.
Seeing as Windows doesn't natively support CDC ECM, we decided to implement two USB configurations (this is a popular approach):
The intention is to let Windows use the first configuration with RNDIS, which is natively supported (Windows always selects first USB configuration); and let non-Windows hosts use the second configuration with CDC ECM.
I put together a script (based on a similar script by David Lechner): http://pastebin.com/VtAusEmf. Relevant part of the script is provided below (please follow the link to see the full script, it's rather large):
mkdir -p ${g}
echo "${usb_ver}" > ${g}/bcdUSB
echo "${dev_class}" > ${g}/bDeviceClass
echo "${vid}" > ${g}/idVendor
echo "${pid}" > ${g}/idProduct
mkdir ${g}/strings/0x409
echo "${mfg}" > ${g}/strings/0x409/manufacturer
echo "${prod}" > ${g}/strings/0x409/product
echo "${serial}" > ${g}/strings/0x409/serialnumber
mkdir ${g}/configs/c.1
echo "${attr}" > ${g}/configs/c.1/bmAttributes
echo "${pwr}" > ${g}/configs/c.1/MaxPower
mkdir ${g}/configs/c.1/strings/0x409
echo "${cfg1}" > ${g}/configs/c.1/strings/0x409/configuration
echo "1" > ${g}/os_desc/use
echo "${ms_vendor_code}" > ${g}/os_desc/b_vendor_code
echo "${ms_qw_sign}" > ${g}/os_desc/qw_sign
mkdir ${g}/functions/rndis.usb0
echo "${dev_mac}" > ${g}/functions/rndis.usb0/dev_addr
echo "${host_mac}" > ${g}/functions/rndis.usb0/host_addr
echo "${ms_compat_id}" > ${g}/functions/rndis.usb0/os_desc/interface.rndis/compatible_id
echo "${ms_subcompat_id}" > ${g}/functions/rndis.usb0/os_desc/interface.rndis/sub_compatible_id
mkdir ${g}/configs/c.2
echo "${attr}" > ${g}/configs/c.2/bmAttributes
echo "${pwr}" > ${g}/configs/c.2/MaxPower
mkdir ${g}/configs/c.2/strings/0x409
echo "${cfg2}" > ${g}/configs/c.2/strings/0x409/configuration
mkdir ${g}/functions/ecm.usb0
echo "${dev_mac}" > ${g}/functions/ecm.usb0/dev_addr
echo "${host_mac}" > ${g}/functions/ecm.usb0/host_addr
mkdir ${g}/functions/acm.GS0
mkdir ${g}/functions/acm.GS1
ln -s ${g}/configs/c.1 ${g}/os_desc
ln -s ${g}/functions/rndis.usb0 ${g}/configs/c.1
ln -s ${g}/functions/acm.GS0 ${g}/configs/c.1
ln -s ${g}/functions/acm.GS1 ${g}/configs/c.1
ln -s ${g}/functions/ecm.usb0 ${g}/configs/c.2
ln -s ${g}/functions/acm.GS0 ${g}/configs/c.2
ln -s ${g}/functions/acm.GS1 ${g}/configs/c.2
echo "${device}" > ${g}/UDC
The resulting Gadget configuration works fine with Linux hosts (second configuration gets selected, all three interfaces are available and working), but Windows hosts (tested with 8 and 10) only detect the RNDIS interface, ignoring the ACM interfaces. The RNDIS works well though.
If I disable the RNDIS interface, Windows hosts only detect the first ACM interface, ignoring the second one.
I suspect that Windows is incapable of handling composite USB devices properly. Is it so, or am I doing something wrong? If it is so, do I have to write my own .inf
file, specifying which class drivers need to be loaded?
RTFM helped. The requirements to composite USB devices are described in these articles:
Solution:
echo "0xEF" > ${g}/bDeviceClass
echo "0x02" > ${g}/bDeviceSubClass
echo "0x01" > ${g}/bDeviceProtocol