7
votes

We have an embedded device that connects to the PC via USB, and it has multiple virtual serial ports (CDC-ACM).

We have this working on Windows. On the embedded device, we have multiple CDC-ACM interfaces. The USB descriptors declare it as a composite device (class=0xEF, sub-class=2, protocol=1), and it has an "Interface Association Descriptor" for each virtual serial port. On Windows, we use an INF file that installs usbser.sys for each CDC-ACM control interface (MI_00, MI_02, etc).

However, as we've found, this method doesn't seem to work for Mac. I've found that I can get it to work for the Mac and Linux, by changing it to a "Communications" class (class=2, sub-class=0, protocol=0), and removing the IADs. (For Linux, testing with Ubuntu, I found that this worked with the Ubuntu Linux kernel 2.6.35-28 or newer. With earlier kernels, only the first serial port worked.) But then, this method doesn't work for Windows.

What method can be used to make a USB device with multiple virtual serial ports, that works on Windows, Mac, and Linux? I think I'd prefer a solution that uses the CDC-ACM standard as much as possible, and avoids the write-your-own-drivers option as much as possible.

3

3 Answers

3
votes

The one way I can think of off top of my head would be the device presents itself as an USB hub with multiple separate single-serial-port devices attached to it. This isn't pretty but very bulletproof.

2
votes

As Apple's drivers don't support composite CDC devices, I'd suggest either making your device reconfigure somehow and making your alternate descriptors plain CDC, or sticking with the composite and using a third party driver (my company makes CDC ACM drivers for OS X which will probably support your device).

It may also be possible to force the issue with a codeless kext.

2
votes

One solution that I've found, which I think could work (subject to further testing on Windows):

Make the device enumerate in the way that works for the Mac:

  • Make it "Communications" class (class=2, sub-class=0, protocol=0), not composite device.
  • Remove the IADs.

The device should "just work" on Mac and recent Linux, in this configuration. (For Linux, testing with Ubuntu, I found that this worked with the Ubuntu Linux kernel 2.6.35-28 or newer. With earlier kernels, only the first serial port worked.)

Then, for Windows, modify the INF file for the device, to explicitly load the composite device driver usbccgp.sys. I'm a novice with Windows INF files, but here are the relevant snippets from what I could figure out so far:

[CCGPDriverInstall.NT]
Include=usb.inf
Needs=Composite.Dev.NT
AddReg=CCGPDriverInstall.AddReg

[CCGPDriverInstall.NT.Services]
Include=usb.inf
Needs=Composite.Dev.NT.Services

[CCGPDriverInstall.AddReg]
HKR,,EnumeratorClass, 0x00000001,02,00,00

...

[DeviceList]
%DESCRIPTION_DEVICE%=CCGPDriverInstall, USB\VID_FFFF&PID_0001
%DESCRIPTION_AT%=DriverInstall, USB\VID_FFFF&PID_0001&MI_00 
%DESCRIPTION_MENU%=DriverInstall, USB\VID_FFFF&PID_0001&MI_02 

[DeviceList.NTamd64] 
%DESCRIPTION_DEVICE%=CCGPDriverInstall, USB\VID_FFFF&PID_0001
%DESCRIPTION_AT%=DriverInstall, USB\VID_FFFF&PID_0001&MI_00 
%DESCRIPTION_MENU%=DriverInstall, USB\VID_FFFF&PID_0001&MI_02 

[DeviceList.NTx86...1]
%DESCRIPTION_DEVICE%=CCGPDriverInstall, USB\VID_FFFF&PID_0001
%DESCRIPTION_AT%=DriverInstall, USB\VID_FFFF&PID_0001&MI_00 
%DESCRIPTION_MENU%=DriverInstall, USB\VID_FFFF&PID_0001&MI_02 

With the INF file explicitly loading the usbccgp.sys driver, both USB serial ports worked for me on Windows XP SP3 32-bit.

I have done only limited testing so far, so I'd be interested to hear how well this works, or not, for others.