Enabling USB Client feature in Torizon

I’m using Torizon with a Colibri iMX7D 1GB on an Iris dev board (And one of my own board designs). I need to enable the USB client driver so that my customer can use the USB board in VCOM, RNDIS or mass storage mode. It seems that by default the USB client feature is disabled in Torizon as no USB device appears at all in Windows when I connect the USB cable.

When TEZI is running instead of Torizon an RNDIS driver is loaded for the USB port allowing me to access the device using 192.168.1.1, so I know the USB port and cable do work.

When running regular embedded Linux on the same Colibri module (i.e. not Torizon) I can make the USB port do what I want by generating a g1.schema file and putting it in /etc/usbg folder. I tried simply using the same g1.schema file and putting it in the same place on Torizon, but it has no effect there.

My older products which use WinCE on the T20 also use the USB port in client mode but the customer has to choose between RNDIS and VCOM mode and a reboot is required to switch from one to the other. I really like that with embedded Linux I can make the USB port behave as a composite device using the g1.schema so that RNDIS and VCOM are active at the same time. I’d like to use this composite USB device feature with Torizon as well.

Greetings @MikeS,

I did a quick test with this. So if I generate a schema from scratch as documented here: USB Device Mode (Linux)

With this I can connect my Toradex module to my PC and I saw it was enumerated as a USB device. So it would seem the functionality is there and working. It would appear there’s no service running that automatically imports the schema from /etc/usbg.

However there’s no such service on our latest embedded linux image either. What version of our Embedded Linux BSP were you running where this works?

Either way though as I said the USB device/gadget feature does work on Torizon but you need to manually generate the schema as shown in the article I linked. On my side I’ll see if this is intended or not.

Best Regards,
Jeremias

Hi @jeremias.tx,

It was some time ago when I worked on this with Embedded Linux. I have a screenshot of the Linux distribution booting which shows the following:

The Angstrom Distribution colibri-imx7-eemc
Angstrom v2017.12 - Kernel
Colibri-iMX7-2MMC_LXDE-Image 2.8b6.184 20190401

I tried to follow the process on the page “USB Device Mode (Linux)” while running Torizon. The first few steps seem to go as expected if I switch to root first using “sudo -i”. When I create a new folder called g2 in /sys/kernel/config/usb_gadget/ then a set of folders are generated in there as expected. The first few steps to set-up strings go well but then the first problem happens with this command from the example on that page,

echo "ci_hdrc.0" > g2/UDC

which replies with,

echo: write error: No such device

Then this command,

gadget-export g3 /etc/usbg/g3.schema

replies with,

gadget-export: command not found

Also, there is no file /lib/systemd/system/usbg.service which I can view or edit. It feels like some of the components or packages that I need to follow the example on that page are not included in Torizon.

Mike

This makes more sense our 2.8 release is fairly old these days. The “USB Device Mode (Linux)” article was probably written for this release and is outdated for our more recent 5.X releases.

Please notice in the article that it has you run this command from outside of the g2 directory. As the article shows you’re running this from the /sys/kernel/config/usb_gadget/ directory. This command worked for me if executed from the right directory.

One thing that is outdated is that there is no gadget-export or gadget-import command in the newer releases. However these commands are only to export/import an already existing schema. The commands the article had you do prior is what creates the schema. In this case you would only need export the schema if you want to transfer it to another system. Once you’ve ran the commands previous to gadget-export you’ve already configured the module as a USB device.

As I said in my previous post there’s no service present in the latest Embedded Linux images either. This is another thing that appears to have been outdated.

In summary you can still configure the module as USB device however the article itself seems to be fairly outdated. There’s no gadget-import/export command the the service seems to be missing as well. For now you need to manually create a schema, by following the commands in the article prior to the command where it has you run gadget-export. But as I said this command is not needed to configure the device, it is only needed if you want to generate schema file to transfer to other devices.

Best Regards,
Jeremias

Thanks @jeremias.tx, I had a chance to look at this today.

I found that it did work as you described and when I entered the command echo "ci_hdrc.1" > g1/UDC a USB device would immediately appear in the Device Manager on my PC. The old page on the Toradex website refers to ci.hdrc.0 but I realized that it has to be c1_hrc.1 on Torizon.

Using this I was able to enable an ACM device which appears as a COM port in Windows.

I was also able to enable an RNDIS device, but Windows doesn’t recognize the device and won’t load a driver. When TEZI is running Windows does recognize the USB device as RNDIS and loads a default driver which works, so I plan to look at the USB VID, PID, class and subclass used by TEZI to see if I can create the same behavior on Torizon. If you could help me with the details of setting up RNDIS to save me from having to dig too deeply then that would be greatly appreciated.

The USB configuration is not persistent, so it needs to be set-up again after a reboot. I understand why that is as we are not exporting a schema and storing it somewhere like we did with the older Embedded Linux images. I’ll need to figure out how to automate the process of setting up the USB after every boot, it seems to be need sudo access to do it which makes automating it a little tricky. If you have a suggestion about how to make the USB config permanent, or at least how to run a script to automate then that would be appreciated too.

With regards to the RNDIS device perhaps the issue is network configuration? On Tezi we apply some configurations for dhcp and the usb RNDIS network interface to have proper IP address and everything. The process is somewhat similar to what is described in the latter half of this article: Enabling USB RNDIS Support - ARM9 Based Platforms - Critical Link Support

You can see the corresponding configuration files in Tezi as a reference.

With regards to making the USB configuration persistent. This is a bit tricky and troublesome without a schema importing tool. However you can create a systemd start-up service that can be used to run a script to perform the steps to setup your RNDIS device. Since systemd is run with sudo/root privileges it should be able to perform the needed steps. More details on systemd services here: How to Autorun Application at the Start up in Linux

Best Regards,
Jeremias

Hi @jeremias.tx,

I have made some good progress with this. If I set the USB class of the RNDIS gadget to 0xEF, the subclass to 4 and the protocol to 1 then Windows recognizes that as an RNDIS device and loads the correct driver without needing an INF file. These are the same class and subclass values that we used years ago to get RNDIS working between Windows CE and Windows 10 in this discussion:
RNDIS on Windows 10 to connect to CE7 running on T20 - Technical Support - Toradex Community

If I then set a static IP address of 192.168.11.1 on the Colibri and a static address of 192.168.11.2 on the PC, and set the subnet to 255.255.255.0 on both, then I can connect from the PC to the Colibri via USB using a command like ssh torizon@192.168.11.1

This is really close to what I need (We can set aside the persistence part of it for now). The last part is getting Linux and Windows to negotiate IP addresses for each other instead of having me assign them statically, as using a static address like this in Windows won’t work when I deploy this to my end users. The article you linked to talks about enabling DHCP on Linux to solve this, but I don’t where where to start to try to do that with Torizon.

Mike

Hi @jeremias.tx,

More progress! I have realized that the RNDIS connection is actually working now that I am using the correct class and subclass and part of the problem I have been having is that my Windows PC does not route connections to the correct network interface when using RNDIS. This is a problem I have seen before with RNDIS on Windows CE, and it’s a problem that seems to affect some PCs and not others. This problem also seems to change over time on the same PC.

For example, if I have the Ethernet cable to the Colibri disconnected and the USB cable connected with RNDIS enabled then I can connect to the Colibri via RNIS if I specify its hostname like this:

ssh torizon@colibri-imx7-emmc-06372679

SSH is using IPv6 to connect over RNDIS when I use the hostname like this. But the connection fails if I try to use the IPv6 address of the module explicitly like this:

ssh torizon@fe80::acd1:b6ff:feba:6777

However, if I specify the interface number for the RNDIS connection as shown below then it does work!

ssh torizon@fe80::acd1:b6ff:feba:6777%17

The same thing goes for IPv4. When I assign a static IPv4 address to the RNDIS connection on the module then I can’t use that address from the PC unless I tell Windows which network interface to use. For example, this command does not work: ping 192.168.11.1

I don’t know how to tell ping to use network interface number 17 with an IPv4 address, but I do know how to do that from C# code running on the PC. My C# code can successfully connect to 192.168.11.1 because it knows to select the network interface with “NDIS” in its name. Interestingly, after connecting from C# it seems that Windows remembers this routing and now ping 192.168.11.1 does work from the command line.

I think this is just evidence of the weirdness of networking in Windows and is not really a problem with Torizon, because I see the same issues when using RNDIS with other operating systems like Windows CE. RNDIS on Torizon is now working as well as it did for me on Windows CE so I think I will move on to the next task of setting up RNDIS automatically at boot time.

@jeremias.tx enabling RNDIS at boot time using systemd turned out to be easy. I think using systemd to do this on Torizon is the right way to do it, rather than doing it from inside a container. I might come back to this later as I would like my app in the container to be able to make USB changes at run-time based on user inputs, but I’ll leave that for another time.

I used the guide on this page to run a shell script at start-up using systemd:
How to automatically execute shell script at startup boot on systemd Linux - Linux Tutorials - Learn Linux Configuration

I’ll share my script here in case it is useful for other people. I created the file usb-function.service in /etc/systemd/system with this content:

[Unit]
After=network.target

[Service]
ExecStart=/usr/local/bin/usb-function.sh

[Install]
WantedBy=default.target

And I created the file usb-function.sh in /usr/local/bin with this content:

#!/bin/bash

# Create and activate an RNDIS USB gadget
# Sets the USB class and subclass to work with Windows 10 without an INF file
# Sets a static IP address of 169.254.21.151 and a subnet of 255.255.0.0
# This IP address matches RNDIS on Windows CE for backwards compatibility

echo "usb-function.sh running" > /tmp/usb-function.log
date >> /tmp/usb-function.log

cd /sys/kernel/config/usb_gadget/
mkdir g1
cd g1
echo 0x1b67 > idVendor
cho 0x400c > idProduct
mkdir strings/0x409
cat /proc/device-tree/serial-number > strings/0x409/serialnumber
echo "Toradex" > strings/0x409/manufacturer
cat /proc/device-tree/model > strings/0x409/product
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "USB RNDIS" > configs/c.1/strings/0x409/configuration
mkdir functions/rndis.usb0
ln -s functions/rndis.usb0 configs/c.1
echo "EF" > functions/rndis.usb0/class
echo "04" > functions/rndis.usb0/subclass
echo "01" > functions/rndis.usb0/protocol
cd ..
echo "ci_hdrc.1" > g1/UDC
ifconfig usb0 169.254.21.151 netmask 255.255.0.0 up

ifconfig usb0 >> /tmp/usb-function.log

We can consider this question to be closed now as RNDIS is working in the way I need it to.

1 Like

Good to hear you were able to make progress on this, despite the weirdness from the Windows side of things.

The article you linked to talks about enabling DHCP on Linux to solve this, but I don’t where where to start to try to do that with Torizon.

You can configure a DHCP server that should be capable of allocating IP addresses and such. You just need to provide a conf file then you can start the DHCP server via the udhcpd command. As a reference you can see the udhcpd.conf file we use in Tezi located at /etc/udhcpd.conf. For more complex configuration I would suggest reviewing the manpage for udhcpd here:Ubuntu Manpage: udhcpd.conf - udhcp server configuration file

Best Regards,
Jeremias

@jeremias.tx I would like to follow up on your DHCP suggestion, but where can I find the file /etc/udhcpd.conf for TEZI? Is the TEZI source code available in a public repo that I can see? If so, can you send me the URL?
Mike

Unfortunately the Tezi source code is not publicly available at this time. However if you run Tezi and open a serial console to Tezi while it’s running you can see the configuration file in /etc/. The file in the latest Tezi looks like this:

# The start and end of the IP lease block
start           192.168.11.2
end             192.168.11.2

option subnet   255.255.255.0

max_leases      1

# USB RNDIS gadget
interface       usb0

# The location of the leases file
lease_file      /var/run/udhcpd.leases

It’s fairly simple but you may need something a bit more involved for your use-case. Which is why I reference the man page for the conf file, since there is quite a bit of different configuration options.

Best Regards,
Jeremias

@jeremias.tx using DHCP works very well. I used the configuration file from TEZI as-is and it just worked. I see at least two advantages of using DHCP:

  1. It resolves the confusion in Windows about which network interface to use
  2. It saves significant time when connecting the USB cable as Windows does not need to negotiate and then assign itself an IP address, it just immediately uses the IP address it is told to use

I like it!

That’s great to hear! I’m glad I was able to assist you with getting this worked out.