mik3y / usb-serial-for-android

Android USB host serial driver library for CDC, FTDI, Arduino and other devices.
MIT License
4.82k stars 1.58k forks source link

composite CDC ACM + RNDIS devices #469

Closed pavelandr closed 1 year ago

pavelandr commented 1 year ago

Hello. Great package!

SETUP DESCRIPTION I encountered the next problem in the CdcAcmSerialDriver: https://github.com/mik3y/usb-serial-for-android/blob/6ffb666b33265876ae58727021627112d00b45e3/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java#L140-L155

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]]

2.

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]]

3.

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]]

4.

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]]

5.

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]]

6.

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: https://github.com/mik3y/usb-serial-for-android/blob/6ffb666b33265876ae58727021627112d00b45e3/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java#L140-L155

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: https://github.com/mik3y/usb-serial-for-android/blob/6ffb666b33265876ae58727021627112d00b45e3/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java#L150 to:

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

or in Kotlin:

   mControlInterface?.let {
       if (usbInterface.id == it.id+1)
       ...
    }
kai-morich commented 1 year ago

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