Friday, September 23, 2016

Connecting BeagleBone to newer Android Nexus phones (like Nexus 5X and Nexus 6P) via USB Ethernet

Update: Huh, I just discovered that this exists: Settings > developer options > networking > select USB configuration > RNDIS (USB ethernet).  But it doesn't seem to work.  So I suspect the RNDIS option there doesn't actually do anything.

The BeagleBone comes with a neat feature: when you plug it into your computer's USB port, the BeagleBone pretends it is a USB hub connected to several things: a serial port, USB storage and an ethernet adapter.  Thus you can point your PC's browser to and pull up the BeagleBone's built-in webserver, login via /dev/ttyACM0, or look through the files on its boot partition.

This all happens thanks to the g_multi Linux kernel module, which is one of several modules that allow devices with USB OTG ports to pretend to have things like storage, serial ports, ethernet and even MIDI ports.  g_multi aggregates a bunch of those together and presents them all to the host under a virtual USB hub.

This works great when connecting the BeagleBone to my Linux workstation over USB, and it also partly works when I connect the BeagleBone to my (stock) Nexus 5X: I can attach the BeagleBone to the phone using this VicTsing OTG adapter, and the BeagleBone will power itself from the phone and let me browse its USB storage.

I haven't had any success with the serial port, unfortunately.  I tried a bunch of different Android serial apps, and the phone seems to realize that the BeagleBone is offering a serial port, but I can't get any data between the devices.

Ethernet: if you want to talk from your Android phone to your beaglebone via TCP/IP over the USB cable, the first thing you'll have to do is, sadly, turn on airplane mode so that cellular and wifi data are disabled.  If one of those interfaces is active, pings and HTTP requests no longer make it to the BeagleBone.

Here's the other issue: recent phones like the Nexus 6P have a smaller set of USB Ethernet drivers enabled in the stock distro.  So with the BeagleBone in its default configuration, the Nexus 5X sees the ethernet interface just fine, but the Nexus 6P doesn't.

Fortunately, the g_multi can pretend to be one of two different *types* of USB Ethernet device.  The default is RNDIS, which apparently is a common thing on Windows, and is one of the drivers that got removed on the Nexus 6P.  The other is CDC, which is apparently a cleaner standard that manufacturers of USB Ethernet adapters can follow (so that each manufacturer doesn't have to invent their own driver interface).

Annoyingly, I can't find docs on g_multi's module parameters anywhere.  But I did manage to switch my BeagleBone from RNDIS to CDC ethernet with this change to /opt/scripts/boot/

As you can see, I just commented out the block of code that decides how to load g_multi, and instead I load the standalone g_cdc CDC Ethernet driver.  (This means you'll no longer get the USB storage and serial interfaces):

 # Added these two lines:  
 modprobe g_cdc ${g_network}  
 # And commented out this big block:  
 ##g_multi: Do we have image file?  
 #if [ -f ${usb_image_file} ] ; then  
 #    modprobe g_multi file=${usb_image_file} cdrom=0 ro=0 stall=0 removable=1 nofua=1 ${g_network} || true  
 #    usb0="enable"  
 #    ttyGS0="enable"  
 #    #g_multi: Do we have a non-rootfs "fat" partition?  
 #    unset root_drive  
 #    root_drive="$(cat /proc/cmdline | sed 's/ /\n/g' | grep root=UUID= | awk -F 'root=' '{print $2}' || true)"  
 #    if [ ! "x${root_drive}" = "x" ] ; then  
 #        root_drive="$(/sbin/findfs ${root_drive} || true)"  
 #    else  
 #        root_drive="$(cat /proc/cmdline | sed 's/ /\n/g' | grep root= | awk -F 'root=' '{print $2}' || true)"  
 #    fi  
 #    if [ "x${root_drive}" = "x/dev/mmcblk0p1" ] || [ "x${root_drive}" = "x/dev/mmcblk1p1" ] ; then  
 #        #g_ether: Do we have udhcpd/dnsmasq?  
 #        if [ -f /usr/sbin/udhcpd ] || [ -f /usr/sbin/dnsmasq ] ; then  
 #            modprobe g_ether ${g_network} || true  
 #            usb0="enable"  
 #        else  
 #            #g_serial: As a last resort...  
 #            modprobe g_serial || true  
 #            ttyGS0="enable"  
 #        fi  
 #    else  
 #        boot_drive="${root_drive%?}1"  
 #        modprobe g_multi file=${boot_drive} cdrom=0 ro=0 stall=0 removable=1 nofua=1 ${g_network} || true  
 #        usb0="enable"  
 #        ttyGS0="enable"  
 #    fi  

Be careful not to comment too much: you still want the "if [ "x${usb0}" = "xenable" ] " block below to be active so that the network interface gets set up correctly.

You might want to do all this while booted from a microSD card so that if you screw it up and can't access the BeagleBone over USB anymore, you can just pop out the card and fix from your PC.

1 comment:

Anonymous said...

When I'm not booting off of an sd_card, my /opt/scripts/boot/ file looks like this:

#If image file is found, use it for g_multi
if [ -f ${usb_image_file} ] ; then
modprobe g_multi file=${usb_image_file} cdrom=0 ro=0 stall=0 removable=1 nofua=1 ${g_network} || true
#In a single partition setup, dont load g_multi, as we could trash the linux file system...
elif [ "x${root_drive}" = "x/dev/mmcblk0p1" ] || [ "x${root_drive}" = "x/dev/mmcblk1p1" ] ; then
if [ -f /usr/sbin/udhcpd ] || [ -f /usr/sbin/dnsmasq ] ; then
#Make sure (# CONFIG_USB_ETH_EEM is not set), otherwise this shows up as "usb0" instead of ethX on host pc..
modprobe g_ether ${g_network} || true
modprobe g_serial || true
modprobe g_multi file=${boot_drive} cdrom=0 ro=0 stall=0 removable=1 nofua=1 ${g_network} || true