NetConsole: A U-Boot Prompt Over UDP
The serial console on a Thingino camera is usually there. It is just on the inside: pads or an unpopulated header on the PCB, behind a sealed case. Getting to it means opening the camera, and a camera already installed means getting it off the wall or the ceiling first.
That is the real cost of a failed flash or one wrong environment variable. The console is not impossible to reach, it is merely behind a ladder, a screwdriver and a set of test hook clips.
NetConsole removes the hard work. U-Boot brings the Ethernet MAC up early to
support tftpboot, and NetConsole reuses that to carry stdin, stdout and
stderr as UDP datagrams. The result is a full interactive bootloader prompt on a
camera you never have to touch.
Available on the ciao branch since 1 September 2026. Nothing to patch
separately: build from ciao and enable one option.
Enabling It
NetConsole is off by default and opt-in per camera. One line in the camera defconfig:
BR2_PACKAGE_THINGINO_UBOOT_NETCONSOLE=y
The build derives the rest from flags the board already sets: wired Ethernet
gets the plain redirect, a board with only a USB OTG port gets the USB variant
described below. Cameras that do not opt in build a byte-identical stock
bootloader and keep the stock one-second bootdelay.
What Gets Enabled
That one symbol pulls in three U-Boot config options:
| Symbol | Purpose |
|---|---|
CONFIG_NETCONSOLE |
the console-over-UDP driver |
CONFIG_CONSOLE_MUX |
device lists, so serial is kept alongside nc rather than replaced |
CONFIG_PREBOOT |
allows redirecting the console before the autoboot window |
Plus the environment that makes it reachable:
preboot=setenv stdout serial,nc;setenv stderr serial,nc;setenv stdin serial,nc
bootdelay=5
ipaddr=192.168.1.10
Two details are worth understanding rather than copying.
The redirect must happen in preboot. By the time bootcmd runs,
console_init_r has already rebuilt stdin/stdout/stderr from the real
devices and the abort window has closed. preboot is the only hook early
enough.
CONFIG_CONSOLE_MUX is not optional. The mux only calls a device’s read
function after it has signalled that a character is waiting, so NetConsole’s
unbounded read is never entered on a dead line. A bare stdin=nc without the mux can hang
forever on a dead network – and a hang you can only clear by opening the case
is exactly what NetConsole exists to avoid.
Using It
The client ships with U-Boot:
gcc -o tools/ncb tools/ncb.c # once
tools/netconsole 255.255.255.255 6666 # broadcast
Aimed at the board’s ipaddr instead, a session looks like this:
$ ./tools/netconsole 192.168.1.10
Board out port: 6666
Board in port: 6666
NOTE: the interrupt signal (normally ^C) has been remapped to ^T
ncb is /usr/bin/ncb
KEY: ###### Press Ctrl-C now to interrupt boot... loading in 5s ######
isvp_t31# <INTERRUPT>
isvp_t31# version
build: thingino_T31X
Thingino U-Boot for Ingenic 2013.07 (Aug 31 2026 - 00:46:07 CEST)
mipsel-linux-gcc.br_real (Buildroot -g2c96a67d-dirty) 16.2.0
GNU ld (GNU Binutils) 2.45.1
isvp_t31#
The client remaps its own interrupt key to Ctrl-T so that Ctrl-C stays free to travel to U-Boot.
Do not wait for the KEY: ###### Press Ctrl-C now ... line. It is printed
once, and on a cold boot it is often on the wire before the link has finished
coming up, so it may never reach you even though the window behind it is open.
Start pressing Ctrl-C as soon as you trigger the reboot and keep going. One
keypress landing inside the window is enough; the extra ones are harmless.
Broadcast Or Unicast
Output is always broadcast. Input can be sent either way, and the choice matters more than it looks.
Unicast (netconsole 192.168.1.10 6666) requires ipaddr to be on
your subnet and an ARP entry to be resolvable in both directions.
Broadcast (netconsole 255.255.255.255 6666) needs no ARP entry, no
matching subnet, and no cooperation from the switch. It is the recommended
default.
Two things follow from that.
Broadcast can show characters twice. Your keystroke is broadcast to the segment, so your listener sees it alongside the board’s echo. It is cosmetic; nothing is sent to the board twice.
Unicast input can freeze while output keeps flowing. On at least one board/switch combination, the switch’s dynamic unicast FDB entry for an idle camera expires, and unicast stops being delivered to that port until the camera transmits again. Broadcast floods regardless, so output survives and input does not. The counters told the tale: every frame the camera sent reached the switch, but the frames addressed to it never left, because the forwarding entry was gone.
If a unicast session goes deaf, you do not need to restart anything:
ping -b 255.255.255.255
Every probe is broadcast, so it reaches the board regardless; the board’s reply
re-teaches the switch, and the session resumes. A freshly started arping <ipaddr> works too,
but goes deaf once it switches to unicast probes, so the broadcast ping is the one to leave running.
Cameras Without An Ethernet Port
Boards with WiFi and a USB OTG port have no MAC for U-Boot to use, and U-Boot has no WiFi driver. They are still reachable through a USB-Ethernet dongle.
No patch is required. The 2013.07 tree already builds the DWC2 host controller
and the ASIX drivers for every board but one, and board_usb_init() already
puts the OTG port in host mode. Only the sequencing is new:
preboot=usb start;setenv ethact ${nc_ethact};setenv stdout serial,nc;...
nc_ethact=asx0
usb start enumerates the dongle and registers it as an Ethernet device.
setenv ethact then selects it, which is required: the on-chip MAC is
registered as device 0 on every board, including boards with no PHY fitted, so
without it NetConsole talks to an interface with nothing attached.
nc_ethact defaults to asx0 (ASIX AX88772); set it to axg0 for an AX88179.
Because usb start runs inside preboot, enumeration and link-up finish
before the countdown starts, so USB costs the abort window nothing. Measured on
a Wyze Cam v3 with an AX88772C, the prompt was caught on every boot at
bootdelay 2 and above.
Driver Fixes Behind This
NetConsole was the first workload to exercise the jz4775 Ethernet driver properly, and it exposed roughly a dozen defects that also affected any other network use in U-Boot:
- A speed mask defined as zero, so selecting 10Mb/s set nothing.
- DMA descriptors and buffers without cache-line alignment, sharing lines with data written through the cached alias.
- A four-deep transmit ring the driver could not keep in sync with the DMA engine’s pointer, collapsed to a single descriptor.
- A transmit FIFO never flushed, so the first send after every init timed out.
- Latched CSR5 status never cleared, so suspended engines ignored poll demands.
jz_halt()enabling the transmitter it meant to disable.- Console output emitted from inside the init of the interface carrying it, causing the driver to interrupt itself.
These are in ciao as patches 0007-0010 for the 2013.07 tree.
Tested On
Two cameras, both Ingenic T31X, covering the wired and USB paths:
| Camera | Network path | What was verified |
|---|---|---|
vanhua_fjz_t31x_gc4653_eth (T31X, GC4653, PoE) |
on-chip MAC, wired | Ctrl-C into an interactive prompt at both 10M and 100M, clean transmit over a 12 minute window, no duplicate packets, serial still usable alongside |
wyze_cam3_t31x_gc2053_rtl8189ftv (T31X, GC2053, WiFi + USB OTG) |
ASIX AX88772C dongle | same prompt over the dongle, printenv ethact reporting asx0, both broadcast and unicast input, bootdelay swept across 18 reboots |
The unicast freeze was observed on a MikroTik CRS112 (QCA-8511). It is a property of the switch’s forwarding table rather than of either camera, so treat it as something to expect on any switch, not as a fault of these two.
Limitations And Gotchas
It is an unauthenticated shell. With ncip unset the board accepts console
input from any host on the segment, so during the countdown any host on that segment
can interrupt boot and reach a U-Boot prompt with full access to flash and the
environment. Use it on trusted networks, or pin one client with
fw_setenv ncip <host>.
Output stops at bootm. eth_halt() runs before the kernel starts. For
kernel messages, use Linux-side netconsole instead. Anything printed before
main_loop (SPL, DDR init, the banner) is also out of reach, because the
network is not up that early.
Check your neighbour table before blaming the firmware. A stale static ARP
entry pointing the camera’s IP at a different MAC silently swallows every
unicast packet while broadcast keeps working. The symptom is indistinguishable
from a firmware bug; the fix is ip neigh del.
A saved environment wins over compiled-in defaults. A camera that only had
its bootloader reflashed keeps its old environment, so preboot and
bootdelay may still be the previous values. Check with fw_printenv before
concluding the build is wrong.