Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

composite CDC ACM + RNDIS devices #469

Closed
pavelandr opened this issue Feb 7, 2023 · 1 comment
Closed

composite CDC ACM + RNDIS devices #469

pavelandr opened this issue Feb 7, 2023 · 1 comment

Comments

@pavelandr
Copy link

pavelandr commented Feb 7, 2023

Hello. Great package!

SETUP DESCRIPTION
I encountered the next problem in the CdcAcmSerialDriver:

for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbInterface = mDevice.getInterface(i);
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) {
if(controlInterfaceCount == mPortNumber) {
mControlIndex = i;
mControlInterface = usbInterface;
}
controlInterfaceCount++;
}
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
if(dataInterfaceCount == mPortNumber) {
mDataInterface = usbInterface;
}
dataInterfaceCount++;
}
}

The loop itterates over the interfaces and select the "mPortNumber" ACM DATA interface it encouters.
It is not a problem if there are even quantity of ACM CONTROL and DATA interfaces, but it does in other cases.

I use the package to communicate with DJI remote control serial port.
It has Serial module, RNDIS (Remote Network Driver Interface Specification) module and a Mass Storage module.
The UsbDevice of this remote control has the next 6 interfaces:
1.

UsbInterface[
mId=0,
mAlternateSetting=0,
mName=RNDIS Communications Control,
mClass=224,mSubclass=1,
mProtocol=3,
mEndpoints=[
UsbEndpoint[mAddress=130,mAttributes=3,mMaxPacketSize=8,mInterval=9]]

UsbInterface[
mId=1,
mAlternateSetting=0,
mName=RNDIS Ethernet Data,mClass=10,
mSubclass=0,
mProtocol=0,
mEndpoints=[
UsbEndpoint[mAddress=129,mAttributes=2,mMaxPacketSize=512,mInterval=0]
UsbEndpoint[mAddress=8,mAttributes=2,mMaxPacketSize=512,mInterval=0]]

UsbInterface[
mId=2,
mAlternateSetting=0,
mName=Mass Storage,
mClass=8,
mSubclass=6,
mProtocol=80,
mEndpoints=[
UsbEndpoint[mAddress=131,mAttributes=2,mMaxPacketSize=512,mInterval=0]
UsbEndpoint[mAddress=9,mAttributes=2,mMaxPacketSize=512,mInterval=1]]

UsbInterface[
mId=3,
mAlternateSetting=0,
mName=null,
mClass=255,
mSubclass=67,
mProtocol=1,
mEndpoints=[
UsbEndpoint[mAddress=132,mAttributes=2,mMaxPacketSize=512,mInterval=0]
UsbEndpoint[mAddress=10,mAttributes=2,mMaxPacketSize=512,mInterval=0]]

UsbInterface[
mId=4,
mAlternateSetting=0,
mName=CDC Abstract Control Model (ACM),
mClass=2,
mSubclass=2,
mProtocol=1,
mEndpoints=[
UsbEndpoint[mAddress=134,mAttributes=3,mMaxPacketSize=10,mInterval=9]]

UsbInterface[
mId=5,
mAlternateSetting=0,
mName=CDC ACM Data,
mClass=10,
mSubclass=0,
mProtocol=0,
mEndpoints=[
UsbEndpoint[mAddress=133,mAttributes=2,mMaxPacketSize=512,mInterval=0]
UsbEndpoint[mAddress=11,mAttributes=2,mMaxPacketSize=512,mInterval=0]]]]

THE PROBLEM
So where is the problem?
During the interface scanning in code:

for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbInterface = mDevice.getInterface(i);
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) {
if(controlInterfaceCount == mPortNumber) {
mControlIndex = i;
mControlInterface = usbInterface;
}
controlInterfaceCount++;
}
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
if(dataInterfaceCount == mPortNumber) {
mDataInterface = usbInterface;
}
dataInterfaceCount++;
}
}

The CdcAcmSerialDriver recognize interface 5 as the only ACM control interface (mControlInterface).
But also recognize two ACM data interfaces (inteface 2 and interface 6).
So it regonize 1 Control Interface and 2 Data interfaces.
It automatically selects the first data interface in the list ,interface 2.
But the interface 2 is the DATA interface of CONTROL interface 1 (RNDIS module) and not of CONTROL interface 5.

THE WORKAROUND
In my work around I build my own CdcAcmSerialDriver that inherit from CdcAcmSerialDriver and rewrote the openInterface().
My assumption that the DATA interface will always be numarted after the CONTROL interface.
So I changed the line:


to:

if(mControlInterface != null && usbInterface.id == mControlInterface.id+1)

or in Kotlin:

   mControlInterface?.let {
       if (usbInterface.id == it.id+1)
       ...
    }
@kai-morich
Copy link
Collaborator

I only have devices where the control interface comes before the data interface, but I could not find any hint in the USB statndard that they have to be ordered like that.
The USB standard has 'Interface Association Descriptors' (IAD) to group interfaces, but looks like these are filtered out on lower layer and are not accessible to the library.
According to the linux kernel, RNDIS is a variant of CDC ACM, so might be an option to skip data interfaces if rndis control interface USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3) is found

@kai-morich kai-morich changed the title Multiple ACM Data Interfaces composite CDC ACM + RNDIS devices Feb 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants