2003
I wrote the following article back in 2003/2004, but didn't publish it then (see my old posting in comp.arch.embedded about "Using ZyXEL/Netgear/... router as ARM7 development platform"). Maybe it's still useful to someone, so now (2006/2007) I finally decided to put it online anyway. I've been told that the ATEN trick also works on much newer devices...
Intention
My original intention was to try and learn developing for the ARM family of microcontroller cores, especially ARM7TDMI. I could have bought a development board for hundreds of EUR, but soon found that there are consumer products available off the shelves that provide almost the same functionality for less money.
After some searching and looking at pictures that people took from inside their devices, it appeared that a large number of DSL routers and WLAN access points are built around ARM7 or ARM9 cores. Many use chips from Samsung (e.g. S3C4510 with ARM7TDMI embedded) and Conexant (e.g. CX82100 with ARM940T; or CX84200 with ARM7TDMI, formerly made by ADMtek, called ADM5106). The Samsung chips are best documented, while information about Conexant is rare.
Another thing to look for is the availability of some method to upload and run selfmade code on the device. Often the boards in the routers provide the ARM standard JTAG connector (2x7 pin header). But from documentation available on the net, I learned that ZyXEL routers come loaded with a "BootExtension" that can be used to up- and download firmware via serial console port, and even to inspect and modify RAM contents using simple AT commands (like those AT commands used to control Hayes modems).
Finally I bought a Netgear RT311 from eBay (EUR 16!). It is almost identical to ZyXEL Prestige 310, and very similar to devices like Netgear RT314, MR314, and other ZyXEL Prestige. The most important technical specs (from my point of view) are:
- Samsung S3C4510X01 controller, running at 50 MHz
- 1 MByte flash memory (1x Intel TE28F800B3B, 16x512 kBit)
The firmware knows about the TE28F160C3B (16 MBit == 2 MByte), but I can't see on the board if A19 is connected (maybe even A20 or A21 are). Probably it is possible to replace the flash chip with a bigger one. - 4 MByte SDRAM (2x EliteMT M12L16161C)
There are to similar places unpopulated, not even resistors and capacitors are present, but it is probably possible to add another two SDRAM chips there. That would result in 8 MByte memory. - 1 RS232 console port
- 1 Ethernet WAN (10 MBps) via RTL8019AS MAC+PHY
- 1 Ethernet LAN (10/100 MBps) using S3C4510's MAC and LSI 80225/B PHY
- 1 JTAG 2x7 pin header (unpopulated)
- Software: ZyNOS BootBase, BootExtension, HTP and RAS
This is a list of devices that probably use ZyNOS BootExtension as well; (please send corrections and updates, especially I'd like to know which ones have a serial console port or at least are prepared to have one)
- ZyXEL Prestige 100, 128, 202; D-Link DI-106; Netgear RT328, RH348
- ZyXEL Prestige 310, Netgear RT311, Teledat T-DSL Router
- ZyXEL Prestige 314, Netgear RT314, Teledat 400
- ZyXEL Prestige 316, Netgear MR314
- Other Netgear: FR314, RP114, RP614, RP344
The ZyNOS ("ZyXEL Network OS"?) firmware in these routers is based on Kadak AMX RTOS, the same OS that was basis for PalmOS up to version 4.x. I don't know if that's still the case for newer devices that don't use an ARM core anymore.
Accessing the router
Using the console port
Too lazy to build a JTAG cable, I tried the serial console port first. Use a normal serial cable (not a Nullmodem cable) to connect it to your PC. The initial baud rate can be configured permanently to any standard rate you want up to 115200 bps; so you might need several tries until you get a message after power-on similar to this one:
Bootbase Version: V2.04 | 2/7/2001 18:08:22 RAM: Size = 4096 Kbytes DRAM POST: Testing: 4096K OK FLASH: Intel 8M RAS Version: V3.25(M.00) | 5/4/2001 11:44:42 Press any key to enter debug mode within 3 seconds. .......
Unless you hit a key, the RAS code boots and the router just works as it was meant to. But IF you hit a key, you're in debug mode. That is, you're talking to ZyXEL's "BootExtension" code. Try typing the command "ATHE" and you get this output:
======= Debug Command Listing ======= AT just answer OK ATHE print help ATBAx change baudrate. 1:38.4k, 2:19.2k, 3:9.6k 4:57.6k 5:115.2k ATENx,(y) set BootExtension Debug Flag (y=password) ATSE show the seed of password generator ATTI(h,m,s) change system time to hour:min:sec or show current time ATDA(y,m,d) change system date to year/month/day or show current date ATDS dump RAS stack ATDT dump Boot Module Common Area ATDUx,y dump memory contents from address x for length y ATRBx display the 8-bit value of address x ATRWx display the 16-bit value of address x ATRLx display the 32-bit value of address x ATGO(x) run program at addr x or boot router ATGR boot router ATGT run Hardware Test Program ATRTw,x,y(,z) RAM test level w, from address x to y (z iterations) ATSH dump manufacturer related data in ROM ATDOx,y download from address x for length y to PC via XMODEM ATTD download router configuration to PC via XMODEM ATUR upload router firmware to flash ROM ATLC upload router configuration file to flash ROM ATXSx xmodem select: x=0: CRC mode(default); x=1: checksum mode OK
Now the above list does mention a lot of useful commands, but I thought there were commands to write to RAM? And what is this obscure "DebugFlag"?
At least the ATDU command allowed me to dump the whole RAM contents and then disassemble them (using arm-objdump). Locating the code that processes the ATEN and ATSE commands took me some time, but, well, learning ARM assembly language was one of my goals when I bought the device ;-)
The DebugFlag and how to change it
The ATENx[,y] command obviously wants a "password" y. If you used it without a password, it always sets the DebugFlag to zero, regardless of the value x. "Officially", the password shall be a hex value, derived from the output of the ATSE command. Now after understanding the code, it became clear that ATSE initializes an internal variable (the "seed") from the ARM's TCNT0 timer value, and outputs this value together with the last three octets of the device's (LAN) MAC address.
Further digging in the code revealed how to compute the password from the ATSE output. Only the first three octets (the "seed") and least significant 3 bits of the (LAN) MAC address are important:
unsigned long password(unsigned long seed, unsigned char last_mac_octet) { unsigned long b = seed & 0x00FFFFFF; unsigned long r = ROR(b + 0x10F0A563, last_mac_octet & 7); return r^b; }
But this is still utterly complex. Just don't use ATSE, and the seed will be just zero after booting. Then, only the 3 least significant bits of the (LAN) MAC address determine the password. You can gather the MAC address from the ATSH output. It is always the same unless you change the router...
MAC Address of LAN port | Password y for ATENx,y |
---|---|
...0 or ...8 | 10F0A563 |
...1 or ...9 | 887852B1 |
...2 or ...A | C43C2958 |
...3 or ...B | 621E14AC |
...4 or ...C | 310F0A56 |
...5 or ...D | 1887852B |
...6 or ...E | 8C43C295 |
...7 or ... F | C621E14A |
New possibilities if DebugFlag is set
On my RT311, I can now enable the DebugFlag using
ATEN1,1887852B
And now when the DebugFlag is set, the ATHE instantly show some more possibilities (only the new ones listed here):
ATWBx,y write address x with 8-bit value y ATWWx,y write address x with 16-bit value y ATWLx,y write address x with 32-bit value y AT%Tx Enable Hardware Test Program at boot up ATBTx block0 write enable (1=enable, other=disable) ATWEa(,b,c,d) write MAC addr, Country code, EngDbgFlag, FeatureBit to flash ROM ATCUx write Country code to flash ROM ATCB copy from FLASH ROM to working buffer ATCL clear working buffer ATSB save working buffer to FLASH ROM ATBU dump manufacturer related data in working buffer ATWMx set MAC address in working buffer ATCOx set country code in working buffer ATFLx set EngDebugFlag in working buffer ATSTx set ROMRAS address in working buffer ATSYx set system type in working buffer ATVDx set vendor name in working buffer ATPNx set product name in working buffer ATFEx,y,... set feature bits in working buffer ATMP check & dump memMapTab ATUPx,y upload to RAM address x for length y from PC via XMODEM ATUXx(,y) xmodem upload from flash block x to y ATERx,y erase flash rom from block x to y ATWFx,y,z copy data from addr x to flash addr y, length z ATLOa,b,c,d Int/Trap Log Cmd OK
Booting uCLinux
Please note: The following text is slightly out-of-date. In 2.4.24-uc0, a patch from me was integrated that adds better support for the router. A description is here in uclinux CVS. It adds the "Support ZyXEL BootExtension" configuration option which you should check!
Build the kernel
First, you need the arm-elf toolchain, i.e. the gcc and binutils compiled for the ARM target. I succeeded with prebuilt arm-elf-tools-20030314.sh from http://www.uclinux.org/pub/uClinux/arm-elf-tools/.
For the kernel, take a plain Linux kernel (I tried 2.4.22) and apply the matching uCLinux patch - available from http://www.uclinux.org/pub/uClinux/uClinux-2.4.x/.
To build a kernel for the router, a few minor changes have to be made in the kernel source tree. First, uncomment these lines in the top-level Makefile to declare that you're building a kernel for the "armnommu" architecture, and that you're using a cross-compiler:
ARCH := armnommu CROSS_COMPILE = arm-elf-
We're going to build a "SNDS-100" kernel. That is the name of a well-documented development board that has the S3C4510 processor onboard. But a few definitions need to be changed to make it run on the router, because the router is a little different from the SNDS-100:
- RAM and Flash size are different.
In the arch/armnommu/config.in, modify the memory settings in the section if [ "$CONFIG_BOARD_SNDS100" = "y" ]:define_hex DRAM_BASE 0x00000000 define_hex DRAM_SIZE 0x00400000 define_hex FLASH_MEM_BASE 0x02000000 define_hex FLASH_SIZE 0x00100000
- We want to upload and start the kernel at a specific
address in RAM.
In the arch/armnommu/Makefile, modify the address in the section ifeq ($(CONFIG_BOARD_SNDS100),y):TEXTADDR = 0x00020000
- You may change the baud rate of initial console to something other
than 19200 in drivers/char/serial_samsung.c, at the top of
serial_console_setup:
int baud = 115200;
Note: To build a compressed image (zImage), more changes are required because the Makefiles in arch/armnommu/boot/compressed aren't properly prepared to produce big-endian code. Therefore we stay with an uncompressed image for now...
After making the above changes, go ahead and configure the kernel using "make menuconfig" or "make xconfig". For the first try, use the following settings:
Code maturity options: [X] Prompt for development and/or incomplete code/drivers [ ] Prompt for obsolete code/drivers Loadable module support: [ ] Enable loadable module support System type: (Samsung) ARM system type [X] Generate big endian code [ ] Set flash/sdram size and base addr (RAM) Kernel executes from (S3C4510-SNDS100) Board Implementation Character devices: [X] Samsung serial port support [X] Support for console on Samsung serial port
Do a "make clean dep Image". The result hopefully is a file arch/armnommu/boot/Image. This is the kernel (uncompressed) that can now be uploaded to the router. Determine its size in hex, so we can issue the correct upload command at the router later.
Now boot the router, enter Debug mode and enable the DebugFlag (see above). Issue the ATUP command to upload the kernel at address 0x20000. Assuming the Image file size is 429540 bytes, i.e. 0x68DE4 bytes in hex, the command would be this:
ATUP20000,68DE4
Once the Xmodem upload is complete, make sure you have the serial speed (baud rate) set to 19200 bps (e.g. by issuing the command ATBA2) (or whatever is configured in drivers/char/serial_samsung.c), and start the kernel at 0x20000:
ATBA2 Now, console speed will be changed to 19200 bps OK ATGO20000 Linux version 2.4.22-uc0 (kawk@n5) (gcc version 2.95.3 20010315 (release)(ColdFire patches - 20010318 from http://fiddes.net/coldfire/)(uClinux XIP and shared lib patches from http://www.snapgear.com/)) #1 Sun Dec 21 15:08:45 CET 2003 Processor: Samsung S3C4510B revision 6 Architecture: SNDS100 On node 0 totalpages: 1024 zone(0): 0 pages. zone(1): 1024 pages. zone(2): 0 pages. Kernel command line: root=/dev/rom0 Calibrating delay loop... 49.86 BogoMIPS Memory: 4MB = 4MB total Memory: 3372KB available (365K code, 143K data, 28K init) Dentry cache hash table entries: 512 (order: 0, 4096 bytes) Inode cache hash table entries: 512 (order: 0, 4096 bytes) Mount cache hash table entries: 512 (order: 0, 4096 bytes) Buffer cache hash table entries: 1024 (order: 0, 4096 bytes) Page-cache hash table entries: 1024 (order: 0, 4096 bytes) POSIX conformance testing by UNIFIX Linux NET4.0 for Linux 2.4 Based upon Swansea University Computer Society NET3.039 Starting kswapd Samsung S3C4510 Serial driver version 0.9 (2001-12-27) with no serial options enabled ttyS00 at 0x3ffd000 (irq = 5) is a S3C4510B ttyS01 at 0x3ffe000 (irq = 7) is a S3C4510B Kernel panic: VFS: Unable to mount root fs on 00:00
Big Endian vs. Little Endian
My router is set to run in Big Endian mode (BE). This introduces a lot of problems, because support for Big Endian targets is still work in progress in uClinux for armnommu. Some of my patches to get this completely working have been integrated in uClinux 2.4 CVS in January 2004, especially regarding the checksumming code in these files:
arch/armnommu/lib/csumpartial.S arch/armnommu/lib/csumpartialcopy.S arch/armnommu/lib/csumpartialcopygeneric.S arch/armnommu/lib/csumpartialcopyuser.S include/asm-armnommu/checksum.h
I'm yet working on patches for the s3c4510 ethernet driver - only two lines need change for Big Endian, but activation of the PHY also needs to be integrated and that'll take time until I have a clean patch:
(s3c4510.c) FD_ptr->Reserved = (Padding | CRCMode | FrameDataPtrInc | WA00 | MACTxIntEn); (s3c4510.h) gBDMARXCON = BRxDIE | BRxEn | BRxMAINC | BRxBRST | BRxNLIE | BRxNOIE | BRxSTSKO | BRxWA10,
Network devices
The LSI80225 PHY for the LAN interface, driven by the S3C4510X's internal EMAC, has to explictly enabled by driving I/O pin #6 low (IOPMOD[6]=1 and IOPDATA[6]=0). Then, the drivers/net/s3c4510.c with small fixes for Big Endian mode will just run fine. Haven't done any stress tests however...
The second (WAN) network interface uses a RTL8019AS MAC with integrated PHY. This one has to be enabled by driving I/O pin #2 low (IOPMOD[2]=1 and IOPDATA[2]=0). Its interrupt is connected to the S3C4510X's external interrupt line #3; the setting IOPCON[19:15]=11101 is usable.
The RTL8019AS is connected in external I/O bank 1 using a bus width of 16 bit (EXTDBWTH[22:23]=10). If I/O base address was set to 0x3F00000 (REFEXTCON[9:0]=0x3F0), the registers appear starting at 0x3F045C0.
To match the big endian host, the MSB of the RTL8019AS data bus is connected to the LSB of the S3C4510X data bus. Therefore the RTL8019AS cannot be accessed with the S3C4510 bus width set to 8 bit (the LSB of RTL8019AS data, e.g. register contents, wouldn't be visible to the S3C4510).
In 16 bit mode however, the (byte-wide) registers appear in address space on half-word boundaries, not byte boundaries, and the mapping in the ne.c / 8390.c driver code has to be tweaked. The address on S3C4510 bus appears at the RTL8019AS shifted right by one; i.e. if you want to read a value from 0x2E0, you have to access 0x5C0 instead.
The LEDs
The TEST LED is controlled by I/O pin #1. It lights when IOPMOD[1]=1 and IOPDATA[1]=0. The other LEDs are probably hardwired to the respective output pins from the RTL8019AS, power supply, and WAN PHY.
Make a root filesystem etc.
This is on my to-do list. In the meantime, I decoded the various structures used by the BootExtension - see the following section. Here the intention was to become able to construct a valid firmware image, without ZyNOS RasCode but including my own kernel instead. The presented information should be sufficient to achieve this now.ZyNOS BootExtension data
Memory map
The flash is visible in the ARM7's address space at 0x02000000 and also at 0x06000000. You can get a directory of its contents (as well as the proposed destinations in RAM) using the ATMP command:ROMIO image start at 06008000 code version: code start: 00000100 code length: D6B84 memMapTab: 15 entries, start = 02021000, checksum = B001 $RAM Section: 0: BootExt(RAMBOOT), start=00000100, len=17F00 1: BootData(RAM), start=00018000, len=8000 2: HTPCode(RAMCODE), start=00020000, len=10000 3: HTPData(RAM), start=0003c000, len=14000 4: RasCode(RAMCODE), start=00020000, len=1E0000 5: RasData(RAM), start=00200000, len=200000 $ROM Section: 6: BootBas(ROMIMG), start=02000000, len=4000 7: DbgArea(ROMIMG), start=02004000, len=2000 8: RomDir2(ROMDIR), start=02006000, len=2000 9: BootExt(ROMIMG), start=02008030, len=10FD0 10: HTPCode(ROMBIN), start=02019000, len=8000 (Compressed) Version: HTP_p310 V 0.10, start: 02019030 Length: 828C, Checksum: 0A93 Compressed Length: 4AD3, Checksum: EA0E 11: MemMapT(ROMMAP), start=02021000, len=C00 12: termcap(ROMIMG), start=02021c00, len=400 13: RomDefa(ROMIMG), start=02022000, len=2000 14: RasCode(ROMBIN), start=02024000, len=DC000 (Compressed) Version: RAS p310, start: 02024030 Length: 16BA58, Checksum: 6849 Compressed Length: BAB84, Checksum: 097A $USER Section: ...
The first two sections in $ROM, i.e. BootBas and DbgArea, cannot be changed with the standard upload commands. ATLC targets the 2x8kByte DbgArea+RomDir2 at offset 0x4000, and ATUR puts the received data into flash starting at offset 0x8000.
- ATLC uploads 16kByte configuration data ("romfile0.bin" in firmware upgrade packages) into the DbgArea and RomDir2
- ATUR stores the router firmware received via Xmodem (including BootExt, HTPCode, and the actual application RasCode) into flash, starting at offset 0x8000
Data structures
Firmware images, named "ROMIO image" in the output of ATMP, always begin with a 0x30 bytes header. It contains the information described in the following table. Note that numbers are stored big-endian, i.e. the most significant byte appears at the lowest address!Offset | Size[bytes] | Meaning |
---|---|---|
0 | 4 | Start address of boot code in RAM |
4 | 2 | All zero (0) |
6 | 3 | Three ascii chars "SIG" |
9 | 1 | The value 0x03(?) |
10 | 4 | Size[bytes] of data following this header |
14 | 4 | All zero (0) |
18 | 1 | Flags (0x40?) |
20 | 2 | Expected checksum of data following this header |
22 | 2 | All zero (0) |
24 | 15 | ASCIIZ Code version |
39 | 4 | Absolute location of memMapTab memory map table |
43 | 5 | All zero (0) |
The memory map, which is parsed e.g. when you issue the ATMP command, consists of a 0x18 bytes header, followed by the table entries (each 0x18 bytes), and ASCII text for the "$USER" section (exactly what ATMP prints at the end following the $USER title). The header looks like this:
Offset | Size[bytes] | Meaning |
---|---|---|
0 | 2 | Number of memMapTab entries following |
2 | 4 | Location of start of $USER section |
6 | 4 | Location behind end of $USER section |
10 | 2 | Expected checksum of the memMapTab data following the header, including $USER section |
12 | 12 | All zero (0) |
Each entry in the memMapTab describes an "object" somewhere in memory:
Offset | Size[bytes] | Meaning |
---|---|---|
0 | 1 | Object type |
1 | 8 | ASCIIZ Object name (e.g. BootBas) |
9 | 1 | Zero (0) |
10 | 4 | Object start address in memory |
16 | 4 | Object size(bytes) |
20 | 3 | All zero (0) |
23 | 1 | Value 0x01 for DbgArea, 0x02 for RomDir2, 0x03 for BootExt - 0 otherwise(?) |
When MSB is set in the type byte, this means the entry is within the $RAM section, otherwise it is for $ROM. This is a complete list of types as ATMP displays them:
Type | Section | Name (as ATMP says) |
---|---|---|
1 | $ROM | ROMIMG |
4 | $ROM | ROMBIN |
5 | $ROM | ROMDIR |
7 | $ROM | ROMMAP |
128 | $RAM | RAM |
129 | $RAM | RAMCODE |
130 | $RAM | RAMBOOT |
Where ROMBIN entries point to, you'll find another 0x30 bytes header describing the data immediately following that header, as shown below. The Size given in the memMapTab for these entries specifies the space reserved for the data, which may be more than the actual size as specified in the header there:
Offset | Size[bytes] | Meaning |
---|---|---|
0 | 6 | All zero (0) |
6 | 3 | Three ascii chars "SIG" |
9 | 1 | Object type (0x04 for ROMBIN) |
10 | 4 | Size[bytes] of uncompressed data |
14 | 4 | Size[bytes] of compressed data |
18 | 1 | Flags (0xE0?) |
20 | 2 | Expected checksum of uncompressed data |
22 | 2 | Expected checksum of compressed data |
24 | 15 | ASCIIZ Code version |
39 | 9 | All zero (0) |
If the flags have bit 7 set (0x80), this means the following data is stored in compressed form. Compressed data is stored as contiguous chunks of LZW encoded data; each starting with a halfword (16 bit) specifying the size of the chunk when uncompressed (2048 except for the last chunk), followed by a halfword specifying the size of the compressed data in the chunk, and finally followed by the compressed data itself. If you concatenate the data parts of all chunks, you can decompress them using the LZW demo code from Mark Nelson. And that code also would allow you to compress your own data to build your own firmware...
Note for the future: I took a look at firmware of a newer device, the ZyAIR B-2000 V.2. The firmware still uses the same structure with HTPCode and RasCode and memMapTable etc., albeit compressed data there is not any longer stored LZW encoded as described above, but simply compressed with bzip2. You can run bunzip2 immediately on the data following the SIG header to get the uncompressed code.
Finally, while looking at all the data, I found the info at the end of the BootBase area that defines some of the info displayed by the ATSH command:
Offset (hex) | Size[bytes] | Meaning |
---|---|---|
0x3F72 | 21 | SNMP MIB level & OID |
0x3F87 | 1 | Zero (0) |
0x3F88 | 8 | Values 0x00020000 and 0x00100000(?) |
0x3F90 | 32 | ASCIIZ Vendor name |
0x3FB0 | 32 | ASCIIZ Product Model |
0x3FD0 | 4 | RAS ROM address |
0x3FD4 | 2 | Zero (0) |
0x3FD6 | 1 | 0x05 (System type? Flash type?) |
0x3FD7 | 1 | Zero (0) |
0x3FD8 | 22 | Other feature bits |
0x3FF6 | 1 | Main feature bits |
0x3FF7 | 1 | Zero (0) |
0x3FF8 | 6 | MAC Address |
0x3FFE | 1 | Default Country Code |
0x3FFF | 1 | BootExtension DebugFlag |
Computing checksums
The following code yields correct checksums as used in the ROMIO, ROMBIN and memMapTab structures (might be written prettier, but this is similar to what the BootExtension code actually does):
long compute_checksum(long length) { int n; long checked; unsigned long sum; for(sum=0, checked = 0; checked < (length&~1); checked+=2) { sum += GET_NEXT_BYTE << 8; sum += GET_NEXT_BYTE; if(sum > 0xFFFF) sum = (sum+1)&0xFFFF; }; if(length&1) { sum += GET_NEXT_BYTE << 8; if(sum > 0xFFFF) sum = (sum+1)&0xFFFF; }; return sum; }
Hardware notes
2-pin header J4
This connects I/O "p0" of S3C4510 to ground. When connected, the router at startup doesn't ask to press "any" key to enter Debug Mode, but to press "ESC" key. More important is that it starts HTPCode (that is the Hardware Test Program) afterwards instead of RasCode (the router application).
2x7-pin header JP1
That's a standard ARM JTAG header. This is its pinout:
JTAG signal | Pin No. | Pin No. | JTAG signal |
---|---|---|---|
TVcc (3.3V) | 1 | 2 | GND |
nTRST | 3 | 4 | GND |
TDI | 5 | 6 | GND |
TMS | 7 | 8 | GND |
TCK | 9 | 10 | GND |
TDO | 11 | 12 | nRESET (not connected!) |
TVcc | 13 | 14 | GND |
As in the SNDS100 reference board circuit, nRESET at the JTAG header isn't connected to anything, but the nTRST causes both nTRST and nRESET to be asserted. Thus if you own a router without RESET button, you could add one yourself (short nTRST and GND).
Unfortunately this means that asserting nTRST too long would cause a full system reset. I ran into problems with this when I tried the openwince-jtag tools. The armtool from Erwin Authried (midori Linux distribution) works better and I was able to upload an Image to the router with it (3 times faster than with a 115200 bps serial Xmodem upload), but it ended up having the bytes reversed (again: little vs. big endian hassles...)
2x6-pin header S1
This probably could be populated with a switch to change the wiring on the LAN port, to emulate a cross-connected Ethernet cable.
I/O pin assignments
These have been mentioned in the text above already, but I'll summarize them in the following table. IOPMOD and IOPDATA registers are used to access the I/O pins.
I/O Pin | Direction | Meaning |
---|---|---|
0 | In | Enable HTP at bootup (connected to header S4) |
1 | Out | When low, TEST LED is lit |
2 | Out | 0 = Enable WAN interface (RTL8019AS) |
6 | Out | 0 = Enable LAN PHY (LSI80225) |