5

I am trying to use an LTE modem connected to an Android 7 device over an OTG cable. The kernel recognizes the device and registers it with cdc_ether, but I am unable to use the connection from the device. Is this because it is subsequently mounted as USB storage?

The device does not show up as a device in the Android UI / status bar.

If I disable MTP support, then the device wont register cdc_ether at all.

dmesg:

[10946.408785] usb 1-1.3: new high-speed USB device number 21 using msm_hsusb_host
[10946.525287] usb 1-1.3: New USB device found, idVendor=19d2, idProduct=1225
[10946.525306] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[10946.525316] usb 1-1.3: Product: ZTE Mobile Broadband
[10946.525325] usb 1-1.3: Manufacturer: ZTE,Incorporated
[10946.525335] usb 1-1.3: SerialNumber: MF8610ZTED000000
[10946.529662] usb-storage 1-1.3:1.0: USB Mass Storage device detected
[10946.532702] scsi host19: usb-storage 1-1.3:1.0
[10947.538579] scsi 19:0:0:0: CD-ROM            ZTE      USB SCSI CD-ROM  2.31 PQ: 0 ANSI: 2
[10952.740595] usb 1-1.3: USB disconnect, device number 21
[10953.087891] usb 1-1.3: new high-speed USB device number 22 using msm_hsusb_host
[10953.232955] usb 1-1.3: New USB device found, idVendor=19d2, idProduct=1405
[10953.232969] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[10953.232977] usb 1-1.3: Product: ZTE Mobile Broadband
[10953.232984] usb 1-1.3: Manufacturer: ZTE,Incorporated
[10953.232991] usb 1-1.3: SerialNumber: MF8610ZTED000000
[10953.260856] cdc_ether 1-1.3:1.0 usb0: register 'cdc_ether' at usb-msm_hsusb_host-1.3, CDC Ethernet Device, 36:4b:50:b7:ef:da
[10953.262322] usb-storage 1-1.3:1.2: USB Mass Storage device detected
[10953.262652] scsi host20: usb-storage 1-1.3:1.2
[10954.261139] scsi 20:0:0:0: CD-ROM            ZTE      USB SCSI CD-ROM  2.31 PQ: 0 ANSI: 2

dmesg with MTP disabled:

[10664.987934] usb 1-1.3: new high-speed USB device number 19 using msm_hsusb_host
[10665.105272] usb 1-1.3: New USB device found, idVendor=19d2, idProduct=1225
[10665.105291] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[10665.105301] usb 1-1.3: Product: ZTE Mobile Broadband
[10665.105310] usb 1-1.3: Manufacturer: ZTE,Incorporated
[10665.105320] usb 1-1.3: SerialNumber: MF8610ZTED000000
[10665.110339] usb-storage 1-1.3:1.0: USB Mass Storage device detected
[10665.111320] scsi host17: usb-storage 1-1.3:1.0
[10666.110748] scsi 17:0:0:0: CD-ROM            ZTE      USB SCSI CD-ROM  2.31 PQ: 0 ANSI: 2
[10671.223090] usb 1-1.3: USB disconnect, device number 19
[10671.407859] msm_otg 78db000.usb: OTG runtime idle
[10671.407887] msm_otg 78db000.usb: OTG runtime suspend

ifconfig:

TB-8504F:/ # ifconfig                                                                                                                  
wlan0     Link encap:Ethernet  HWaddr 40:a1:08:36:5b:0d
          inet addr:192.168.1.133  Bcast:192.168.1.255  Mask:255.255.255.0 
          inet6 addr: 2605:a601:ab2b:9900:b19e:4f2e:5d28:5fa9/64 Scope: Global
          inet6 addr: fe80::42a1:8ff:fe36:5b0d/64 Scope: Link
          inet6 addr: 2605:a601:ab2b:9900:42a1:8ff:fe36:5b0d/64 Scope: Global
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:27906 errors:0 dropped:4 overruns:0 frame:0 
          TX packets:17795 errors:0 dropped:0 overruns:0 carrier:0 
          collisions:0 txqueuelen:1000 
          RX bytes:14342222 TX bytes:8697917 

dummy0    Link encap:Ethernet  HWaddr c6:b9:c8:82:8f:7e
          inet6 addr: fe80::c4b9:c8ff:fe82:8f7e/64 Scope: Link
          UP BROADCAST RUNNING NOARP  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0 
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0 
          collisions:0 txqueuelen:0 
          RX bytes:0 TX bytes:210 

p2p0      Link encap:Ethernet  HWaddr 42:a1:08:36:5b:0d
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0 
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 
          collisions:0 txqueuelen:1000 
          RX bytes:0 TX bytes:0 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0 
          inet6 addr: ::1/128 Scope: Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0 
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 
          collisions:0 txqueuelen:0 
          RX bytes:0 TX bytes:0 

ip l:

255|TB-8504F:/ # ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/ether c6:b9:c8:82:8f:7e brd ff:ff:ff:ff:ff:ff
3: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default 
    link/sit 0.0.0.0 brd 0.0.0.0
20: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
    link/ether 40:a1:08:36:5b:0d brd ff:ff:ff:ff:ff:ff
21: p2p0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN mode DORMANT group default qlen 1000
    link/ether 42:a1:08:36:5b:0d brd ff:ff:ff:ff:ff:ff
31: usb0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 36:4b:50:b7:ef:da brd ff:ff:ff:ff:ff:ff

The device does show up as usb0 with the MAC address that was listed in dmesg.

7
  • but I am unable to use the connection from the device How did you try using it? Android won't configure it (unless a custom configuration is added to some of init's .rc files), you have to do it manually. Does it appear as a network interface (check using ip l or ifconfig)? Commented Jun 4, 2020 at 16:32
  • thank you for the response - i added the requested logs - do you have an example of how to add a custom entry to init.rc files? I can modify the boot partition on this device
    – dazza5000
    Commented Jun 4, 2020 at 17:02
  • 1
    I think usb0 is the interface. You can confirm though by removing the dongle. You can configure this interface using ip commands (set up, set IP, add route etc.). But Android framework won't be aware of this interface, so some APIs would report device offline. Android doesn't configure and manage hardware resources directly but through vendor provided HALs. So if you want to add a new hardware resource, you need to run its HAL as a native service. Android framework would interact with that. In simple words it requires rebuilding a custom ROM. Commented Jun 4, 2020 at 17:36
  • I have the source for this device and have been building the ROM / modifying it to support a quectel modem. Thank you so much for your information! usb0 is the interface - it disappears when I remove it
    – dazza5000
    Commented Jun 4, 2020 at 18:09
  • Development related questions are off-topic here. // Android provides limited support for Ethernet. See this, this, this and this. If only Mass Storage device appears and not the Ethernet, your dongle must be a flip flop device which needs USB_ModeSwitch. Commented Jun 4, 2020 at 22:00

1 Answer 1

12

There is a long list of Ethernet related questions but none has a comprehensive answer covering all aspects. I'm generalizing your question in order to share my knowledge on this.

This is what you need to do in order to make Ethernet work on Android:

  • Make sure OTG support is available
  • Kernel must be built with Ethernet (and USB Ethernet) support
  • Handle USB mode switch and kernel module loading (if applicable)
  • Make Android framework do network configuration or do it manually

Note: Everything described below requires a rooted device, or at least the one with unlocked bootloader.
You should be familiar with commandline interface.


OTG SUPPORT

Your device must be able to operate in USB host mode.EthernetService is started only if device supports feature USB host (android.hardware.usb.host) or Ethernet (android.hardware.ethernet). You may also need to use a powered USB hub if Android's USB power supply is not enough for connected device. Related question:

KERNEL CONFIGURATION

In order to use Ethernet over USB (adapters or modem-like devices) kernel must be built with CONFIG_USB_USBNET and other configurations like USB_NET_CDCETHER, USB_NET_HUAWEI_CDC_NCM, USB_NET_CDC_MBIM etc. depending on the type of connected device and the protocol it talks. Related questions:

USB MODE SWITCH AND LOADING KERNEL MODULE

Many USB network devices are multi-mode or flip flop devices. They appear as USB Mass Storage device (also called ZeroCD mode) when inserted and need to be switched to Ethernet/PPP mode. USB_ModeSwitch is a Linux tool commonly used for this purpose. See some details here how it works. You need to build this tool for your device, or may download this binary for aarch64. Get device database from here.

In order to automatically switch mode whenever the device is connected to Android, we need to listen to kernel USB uevents, either through hotplug helper or a userspace daemon (like udev on Linux and ueventd on Android). Additionally the kernel module can also be loaded/unloaded automatically. I'm defining an init service here to achieve this, you can do it manually too.

Note: There is an Android app PPP Widget (by the developer of USB_ModeSwitch, I have no affiliation) which handles mode switching automatically and needs "no kernel driver modules, the 'driver' implementation is based on the Android USB host API". You might be interested in that too.

# /system/etc/init/custom.rc

# kernel hotplug or uevent daemon service
service cust.udevd /system/sbin/busybox uevent /system/sbin/udev.sh
    seclabel u:r:magisk:s0
    disabled
    writepid /dev/cpuset/system-background/tasks

# set kernel hotplug helper or start uevent daemon on boot
on property:sys.boot_completed=1
    #write /proc/sys/kernel/hotplug /system/sbin/udev.sh
    start cust.udevd

* In case of hotplug you need to define custom SELinux policies to let kernel make changes (see this answer for details).

#!/system/bin/sh

# /system/sbin/udev.sh script is executed from kernel hotplug or uevent daemon

# set PATH where you placed binaries
export PATH=/system/bin

# save log
exec >>/dev/udev.log 2>&1

# don't execute multiple instances
exec 200<>/dev/udev.lock
flock 200

VID="12d1"          # USB vendor ID of a Huawei devcie
PID_UMS="1f01"      # product ID in ZeroCD mode
PID_ETH="14db"      # product ID in Ethernet mode
MODULE="cdc_ether"  # kernel module for USB Ethernet
IFACE="usb0"        # Ethernet interface name

matches() {
    [ -e "/sys/$DEVPATH/$1" ] || return 1
    [ "$(cat "/sys/$DEVPATH/$1")" = "$2" ] || return 1
    return 0
}

# check if a new USB device is added or removed
if [ "$SUBSYSTEM" = "usb" ]
then
    # check if a USB device is added, then match VID and PID for mode switching
    # also device must belong to UMS class: https://www.usb.org/defined-class-codes#anchor_BaseClass08h
    if [ "$ACTION" = "add" ] && echo "$PRODUCT" | grep -q "$VID/$PID_UMS/" &&
        matches bInterfaceClass 08 && matches bInterfaceNumber 00
    then
        echo "Switching USB mode..."

        # USB mode switching of flip flop devices (USB modems, routers etc.)
        # usb_modeswitch_dispatcher needs /system/sbin/usb_modeswitch binary and configuration files in /etc
        # so you need to modify the hard-coded paths in source code as per your requirement
        usb_modeswitch_dispatcher --switch-mode "$(basename "$DEVPATH")"
    fi

    # match VID and PID for module loading
    # modprobe should be built with the hard-coded path to where you place modules e.g. /system/lib
    if echo "$PRODUCT" | grep -q "$VID/$PID_ETH/"
    then
        if [ "$ACTION" = "add" ] && ! grep -q "^$MODULE " /proc/modules
        then
            echo "Loading $MODULE module..."
            modprobe "$MODULE"

        elif [ "$ACTION" = "remove" ] && grep -q "^$MODULE " /proc/modules
        then
            echo "Removing $MODULE module..."
            modprobe -r "$MODULE"
        fi
    fi
fi

# on network interface event
if [ "$SUBSYSTEM" = "net" ] && [ "$INTERFACE" = "$IFACE" ]
then
    if [ "$ACTION" = "add" ]
    then
        echo "Starting cust.eth_config service..."
        #start cust.eth_config    # uncomment if you want to do manual network configuration
    fi

    if [ "$ACTION" = "remove" ]
    then
        echo "Stopping cust.eth_config service..."
        #stop cust.eth_config    # uncomment if you want to do manual network configuration
    fi
fi

NETWORK CONFIGURATION

Android framework has a hard-coded name for Ethernet interface (default is eth0, eth1, ...). Whenever an Ethernet interface appears, its name is matched with the hard-coded value. Renaming interface afterwards doesn't work because only kernel provided interface name is tracked.

So you need to make this naming convention consistent between kernel and AOSP by modifying one of the both (if needed). Kernel provided name can be seen using ip tool (as in your case it's usb0). Use dumpsys or de-compile /system/framework/framework-res.apk using apktool to see the AOSP value.

~$ dumpsys ethernet
...
  Ethernet interface name filter: eth\d
...

As soon as an Ethernet interface appears, Android configures it automatically, NetworkMonitor validates the connectivity and ConnectivityService turns off WiFi and Mobile Data (if it's ON). Other services and components involved in configuration include UsbHostManager, EthernetTracker, EthernetNetworkFactory, IpClient.eth0, DhcpClient, DnsManager and Netd.

EthernetService was added in Android 5. Before that AOSP was patched to make Ethernet work (e.g. see this and this). Still stock Android provides no GUI settings for Ethernet, but some custom ROM developers and OEMs do (e.g. see this). EthernetManager class which is used to set and save manual IP configuration (to /data/misc/ethernet/ipconfig.txt) is hidden. Default is to use a hard-coded configuration (see using dumpsys ethernet under "IP Configurations:") or DHCP provided configuration.

MANUAL CONFIGURATION

You might want to do manual network configuration e.g. if:

  • Android framework doesn't configure the Ethernet interface (on older devices or due to interface name inconsistency).
  • You want to set static IP address or different DNS server.
  • You want to use Ethernet along with WiFi or Mobile Data, or want to share internet among any of these.

But in this case Android's Java network stack remains down, so some apps depending on Android APIs may not behave normally. For related details see Connecting to WiFi via ADB Shell.

# /system/etc/init/custom.rc

# Ethernet IP configuration service
service cust.eth_config /system/sbin/eth_config.sh
    seclabel u:r:magisk:s0
    disabled
    writepid /dev/cpuset/system-background/tasks

# clear routing and DNS
on property:init.svc.cust.eth_config=stopped
    exec u:r:magisk:s0 -- /system/sbin/eth_config.sh stop
#!/system/bin/sh

# /system/sbin/eth_config.sh script is executed from eth_config init service

# set PATH where you placed binaries
export PATH=/system/bin

IFACE=usb0                    # Ethernet interface name
DIR=/data/local/tmp/ethernet  # temporary directory
mkdir -p $DIR

# save log
exec >$DIR/eth_config.log 2>&1

if [ "$1" = stop ]
then
    echo "Clearing configuration..."
    ip ru del lookup main
    ip r f table main
    ndc resolver setnetdns 0 '' 0.0.0.0
    exit
fi

# destroy set network if any
ndc network default set 0

# turn WiFi and Mobile Data off
svc wifi disable
svc data disable

# set interfaces up
ip link set dev lo up
ip link set dev $IFACE up

# Android doesn't use main table by default
ip rule add lookup main

# set IP, route and DNS manually here
# or add any other IP/routing configuration
# or run a minimal DHCP client as follows

# create 'udhcpc' script
<<-'SCRIPT' cat >$DIR/udhcpc_default.script
#!/system/bin/sh

case $1 in
    bound|renew)
        echo "Setting IP address, gateway route and DNS for $interface..."
        ip address f dev $interface
        ip route f table main
        ip address add $ip/$mask dev $interface
        ip route add default via $router dev $interface
        ndc resolver setnetdns 0 '' $dns
    ;;
    *)
        echo "Ignoring $1"
    ;;
esac
SCRIPT

# start DHCP client to obtain IP from server
chmod 0755 $DIR/udhcpc_default.script
exec busybox udhcpc -v -f -i $IFACE -s $DIR/udhcpc_default.script

Do not forget to set proper permissions on .rc file and shell scripts. Once setup, Ethernet works as soon as you connect USB adapter.

6
  • 1
    @moderators merge it with some other similar question if needed. Commented Jun 8, 2020 at 18:42
  • chat.stackexchange.com/transcript/121034
    – alecxs
    Commented Mar 19, 2021 at 12:36
  • This seems precisely the problem that I am facing. But I cannot understand how the usbX interface is renamed as ethX... Could you please provide more details?
    – EM90
    Commented Mar 9, 2022 at 11:17
  • 2
    @EM90 I think I've already explained it well: "So you need to make this naming convention consistent between kernel and AOSP by modifying one of the both". In simple words: rebuild your kernel or the ROM. Or "you might want to do manual network configuration if Android framework doesn't configure the Ethernet interface due to interface name inconsistency". Commented Mar 9, 2022 at 12:36
  • 3
    +1 Thank you a lot for this great answer! Really a lot of specifics that are super hard to find anywhere else... If I didn't find the "Android doesn't use main table by default" remark, I would have likely wasted so much time on trying to make an RNDIS ethernet connection work!
    – gabtub
    Commented Sep 23, 2022 at 11:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .