# USB Ethernet and TFTP on the station A USB Ethernet adapter granted to this machine over WebUSB puts it on your local network, or on a cable straight to a board. With it the machine can fetch files from a TFTP server, and serve files to a board or a host over TFTP: an STM32F7 SOM's U-Boot fetches its kernel from this machine and writes it into NOR (section 6). Measured on our bench in Chrome on a Linux host, with a TP-Link USB 10/100 LAN: cabled to the bench LAN on 2026-10-06, and straight to an STM32F7 SOM's RJ-45 on 2026-10-08. Not yet tried: an adapter granted from a Mac, and any other adapter. ## 1. What you need - **Developer mode on** for this machine (its Settings). The site then runs the HardwareJS in this image's `/boot`, which handles the adapter. The site's own release refused it when last measured (2026-09-30), with `bad CDC descriptors`. - **A kernel with the USB Ethernet drivers.** Check with `ls /sys/bus/usb/drivers/cdc_ether`; if the directory is not there, this image's kernel does not have them yet. - **An adapter whose driver is built in:** CDC-ECM (`cdc_ether`), CDC-NCM (`cdc_ncm`), DM9601 (`dm9601`) or RTL8153 in ECM mode (`r8153_ecm`). The one we tried is the TP-Link USB 10/100 LAN (`2357:0602`), which binds through `cdc_ether`. Adapters that need `r8152`, `asix` or `ax88179_178a` do not bind (`linux#137`). - **A cable to your network**, and a free static address on it: DHCP does not work in the browser (section 7). ## 2. Let the browser have the adapter (Linux host) The host's own kernel binds the adapter as a network interface, and Chrome then cannot claim it. As root, once: # /etc/udev/rules.d/70-brintos-webusb.rules SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="2357", ATTRS{idProduct}=="0602", MODE="0660", GROUP="plugdev", TAG+="uaccess" ACTION=="bind", SUBSYSTEM=="usb", DRIVER=="cdc_ether", ATTRS{idVendor}=="2357", ATTRS{idProduct}=="0602", RUN+="/bin/sh -c 'echo -n %k > /sys/bus/usb/drivers/cdc_ether/unbind'" `udevadm control --reload`, then replug. Use your adapter's IDs from `lsusb`. If `lsusb -t` shows the host bound it with another driver (`r8152`, say), unbind that one instead. The host loses the adapter as its own interface while the rule is installed. On a Mac this has not been tried. ## 3. Attach it and bring it up In the machine's **USB** tab, **Attach USB device**, pick the adapter. Wait about ten seconds, go back to the **Console** tab, then: lsusb # the adapter's ID dmesg | grep -E 'cdc_ether|cdc_ncm|dm9601' | tail -n 3 ip link set eth1 up ip addr add
/24 dev eth1 ip route get # must name eth1 `eth0` is the machine's own virtual network card, which the site provides and relays through the browser. It is not a path to your local network, so leave it alone and put the address on the adapter. If the adapter is not `eth1`, find it by its MAC address: grep -l /sys/class/net/*/address The carrier shows a few seconds after `ip link set … up`: read c < /sys/class/net/eth1/carrier; echo $c # 1 with the cable in ## 4. Fetch a file (TFTP client) tftp -m binary -c get /tmp/ curl --tftp-blksize 8192 -o /tmp/ tftp:/// - `-m binary` matters: `tftp`'s default mode is netascii, which is for text. - `curl` is the faster one, because it asks for larger blocks and the cost through WebUSB is per block (section 8). `tftp` here cannot ask. - Use IP addresses. Nothing configures a name server on the adapter. - **Fetch into `/tmp`.** It is the only memory-backed place for files on a running machine. Everywhere else is the machine's own filesystem, which is slow to write (section 8), and `/var/tmp` points into `/var/volatile`, which a running machine does not mount. Files in `/tmp` are gone after a restart and are not part of a snapshot. Check what arrived with `sha256sum /tmp/`. ## 5. Serve files (TFTP server) The server is `in.tftpd-hpa`. It serves `/var/lib/tftpboot`, and reads its options from `/etc/default/tftpd-hpa`. Writing a large file into `/var/lib/tftpboot` is slow: it is the machine's own filesystem, about 17 kB/s, and the 5.8 MB kernel took 337 s. So keep the file in `/tmp` and put a link to it in `/var/lib/tftpboot`: curl --tftp-blksize 8192 -o /tmp/ tftp:/// # or any way into /tmp ln -s /tmp/ /var/lib/tftpboot/ ls -l /var/lib/tftpboot/ # the link is there /etc/init.d/tftpd-hpa start grep ':0045 00000000:0000' /proc/net/udp # one line: listening on port 69 `ln -s` may print `ln: …: Unknown error 517` (`glibc#14`) and still make the link, which is why the `ls -l` is there. A file the image already carries needs no copy and no new link: the STM32F7 SOM's U-Boot and kernel are already served as `stm32f7/u-boot.bin` and `stm32f7/rootfs.uImage`, through the image's own link `/var/lib/tftpboot/stm32f7` (section 6). Then, from a host or a board on the network, fetch `` from this machine's address, e.g. on a Linux host: curl -o tftp://
/ - Serving through a link was checked across the cable: a host on the LAN fetched U-Boot (119 KB) linked from `/tmp` byte-for-byte, in 6 s. - `/tmp` is empty after a restart: fetch the files into it again. - The options are `-l -u root -m /etc/tftpd-hpa.remap /var/lib/tftpboot`. There is no `-s`, because its per-request `chroot()` fails in the browser (`glibc#14`); the remap rule gives a name without a leading `/` the same directory instead. `-u root` is there because this kernel cannot drop privileges (`brintos/linux#19`). - inetutils' `in.tftpd` is on the image too, but it serves only under an inetd, and inetd here handles one request and then stops (`brintos/linux#21`). ## 6. A board's kernel, over a straight cable What a station uses the adapter for: an Emcraft STM32F7 SOM's U-Boot fetches its kernel from this machine over TFTP and writes it into NOR with its `update` macro. Cable the adapter straight to the board's RJ-45 (on the SOM-BSB-EXT); both ends auto-MDIX, and nothing else is on the cable. The kernel ships with this image, as `/usr/share/brintos-som/stm32f7/rootfs.uImage`, with its record in `rootfs.uImage.PROVENANCE` beside it. The TFTP root reaches that directory through a link, `/var/lib/tftpboot/stm32f7`, so the board asks for `stm32f7/rootfs.uImage` and nothing has to be copied or linked first. On this machine, with the adapter attached (section 3): ip link set eth1 up ip addr add 192.168.7.1/24 dev eth1 /etc/init.d/tftpd-hpa start At the board's `STM32F7-SOM>` prompt (its console is `som-console`; the flashing guide's section 6.2 says how to stop at the prompt): printenv flashaddr # where update will write setenv ipaddr 192.168.7.2 setenv serverip 192.168.7.1 setenv image stm32f7/rootfs.uImage run update crc32 ${flashaddr} ${kernelsize} # must end ==> 87504267, the crc32: of the PROVENANCE reset # boots the kernel just written - `update` ends with `saveenv`, so the addresses above are stored with the kernel. On a board whose environment has been erased, `flashaddr` is `60020000`, this U-Boot's default. - This U-Boot has no `ping` command. To test the cable from U-Boot without writing anything, fetch into RAM and check it: `tftp ${loadaddr} stm32f7/rootfs.uImage`, then `crc32 ${loadaddr} ${filesize}`. U-Boot prints `link UP (100/Half)` first when the cable is good. - Wake the prompt with Ctrl-C, not Enter. A bare Enter at the U-Boot prompt repeats the last command, and after `run update` that is a second write. Measured on 2026-10-09, headless Chrome in Developer mode, a TP-Link USB 10/100 LAN straight to an STM32F7-SOM on a SOM-BSB-EXT: | What | Time | |---|---| | `tftp ${loadaddr} stm32f7/rootfs.uImage`: load the kernel image (5,785,356 B) to DRAM, five loads | 29.1–34.7 s | | `run update` (the load, 45 sectors erased, the copy, `saveenv`), three runs | 64.1–70.4 s | U-Boot negotiates larger blocks than `tftp`'s 512: its progress marks count about 3,950 blocks, about 1,468 B each. Of `run update`, 35.0 s is the board's own erase, copy and `saveenv`, the same from any server: the same `run update` from a TFTP server on our bench LAN took 38 s. The flashing guide's §6.4 splits the load into the wait for the first block and the blocks themselves. With the kernel running, the board pinged this machine and this machine pinged the board, three of three each way. ## 7. What does not work - **DHCP.** With packet sockets in the kernel, `udhcpc` never returned in the browser, so they are left out and a DHCP client fails at once (`linux#81`). Give the adapter a static address. - **`eth0` to your local network** — see section 3. - **`cat` or `cp` of a file not yet read** may never return (Ctrl-C gets the shell back) or print `glibc-wasm32: RESTART-SENTINEL-LEAK val=517 nr=40 …` instead of the file (`glibc#14`). Read with `sed -n p ` and copy with `dd`, which do not go through the call that leaks. - **`ln -s`** may print `Unknown error 517` (`glibc#14`) and still make the link. - **Not tried:** two adapters at once. ## 8. How fast it is The board's 5.8 MB kernel (`rootfs.uImage`), from a TFTP server on the same LAN, and, in the last row, from this machine over a straight cable. "Host" is the Linux PC running Chrome; on its own, through its 1 Gb network card, it fetches the file in about a second. The adapter's 100 Mbit/s is not what limits this machine: its best rate below is about 5 Mbit/s. | Transfer | Block size | Time | Throughput | |---|---|---|---| | this machine ← server, `tftp` | 512 B (default) | 123–132 s | 44–47 kB/s | | this machine ← server, `curl --tftp-blksize` | 1,468 B | 35 s | 164 kB/s | | this machine ← server, `curl --tftp-blksize` | 8,192 B | 11 s | 517 kB/s | | host ← this machine's server | 512 B (default) | 75–84 s | 69–77 kB/s | | host ← this machine's server | 1,468 B | 29 s | 198 kB/s | | host ← this machine's server | 8,192 B | 9.4 s | 615 kB/s | | a board's U-Boot ← this machine's server, straight cable | about 1,468 B (U-Boot's) | 29.1–34.7 s | 167–199 kB/s | | a board's U-Boot ← the host's own server, the same adapter and cable, no machine | about 1,468 B (U-Boot's) | 5.4–10.3 s | 562–1,071 kB/s | The first transfer is no slower than the next. For a large file the cost is per TFTP block, about 7 to 16 ms each through WebUSB, so larger blocks are what makes it fast; a small file costs a few seconds whatever its size (U-Boot's 119 KB took 5 to 7 s). Writing into the machine's own filesystem runs at about 17 kB/s.