I had tinkered with OpenWrt on my TP-Link Archer C59 (v1) AC1350 (US) Router, but decided it’s not for me. I appreciate all the work put into that project, but I didn’t like the experience and wanted to restore my router to it’s original stock OEM firmware.

This is where I made a mistake. I uploaded an incorrect firmware file to restore things and rendered my router unusable or “bricked” (not permanently, thankfully). It would not boot into a state where I could use it. The admin web UI was inaccessible and only one LED light was on. I put it in a closet for a few months before trying to restore it. I don’t remember much about exactly what firmware file I uploaded to break things.

I decided I needed to open things up and see if there were any debugging abilities on the board. There were four unpopulated pins near a JP1 label. With some research I determined that this was a serial port I could connect to via a UART device. I (poorly) soldered four header pins to the board.

Photo of the top of a TP-Link Archer C59 AC1350 circuit board. In the middle of the photo four header pins have been soldered to the board
Photo of the top of a TP-Link Archer C59 AC1350 circuit board. In the middle of the photo four header pins have been soldered to the board

Using a video on YouTube and a multimeter I figured out which pin was GND. Notes for a similar board online helped me figure out which pin was TX, RX, and VCC, and I confirmed how to wire the UART cable to the pins. I read that the UART cable I have is 3.3V, which seemed most likely what my router’s board required. I wired up the pins and was able to get data over the serial connection.

Photo of the top of a TP-Link Archer C59 AC1350 circuit board with three out of the four UART wires connected to three out of the four available soldered serial port headers
Photo of the top of a TP-Link Archer C59 AC1350 circuit board with three out of the four UART wires connected to three out of the four available soldered serial port headers

I used minicom to connect to the device.

sudo minicom -b 115200 -D /dev/ttyUSB0

I could see that U-Boot was loading! I could also see an error. Bad Magic Number.

U-Boot 1.1.4 (Jul 16 2016 - 18:05:34)

ap151 - Dragonfly 1.0

DRAM:  128 MB
Top of RAM usable for U-Boot at: 88000000
Reserving 342k for U-Boot at: 87fa8000
Reserving 32832k for malloc() at: 85f98000
Reserving 44 Bytes for Board Info at: 85f97fd4
Reserving 36 Bytes for Global Data at: 85f97fb0
Reserving 128k for boot params() at: 85f77fb0
Stack Pointer at: 85f77f98
Now running in RAM - U-Boot at: 87fa8000
Flash Manuf Id 0xef, DeviceId0 0x40, DeviceId1 0x18
flash size 16MB, sector count = 256
Flash: 16 MB
Using default environment

In:    serial
Out:   serial
Err:   serial
Net:   ath_gmac_enet_initialize...
No valid address in Flash. Using fixed address
No valid address in Flash. Using fixed address
ath_gmac_enet_initialize: reset mask:c02200
Dragonfly----> S27 PHY *
: cfg1 0x80000000 cfg2 0x7114
eth0: 00:03:7f:09:0b:ad
athrs27_phy_setup ATHR_PHY_CONTROL 4 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 4 :10
eth0 up
ATHRS27: resetting s27
ATHRS27: s27 reset done
: cfg1 0x800c0000 cfg2 0x7214
eth1: 00:03:7f:09:0b:ad
athrs27_phy_setup ATHR_PHY_CONTROL 0 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 0 :50
athrs27_phy_setup ATHR_PHY_CONTROL 1 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 1 :50
athrs27_phy_setup ATHR_PHY_CONTROL 2 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 2 :50
athrs27_phy_setup ATHR_PHY_CONTROL 3 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 3 :50
eth1 up
eth0, eth1
Setting 0x181162c0 to 0x82
Autobooting in 1 seconds
## Booting image at 9f020000 ...
Bad Magic Number
ath>

Running tftpboot with no arguments from the UART console revealed some helpful information.

ath> tftpboot
Trying eth0
eth0 link down
FAIL
Trying eth1
enet1 port0 up
dup 1 speed 1000
*** Warning: no boot file name; using '0200A8C0.img'
Using eth1 device
TFTP from server 192.168.0.10; our IP address is 192.168.0.2
Filename '0200A8C0.img'.

It seems that the router’s U-Boot shell has a client IP of 192.168.0.2 (statically assigned, because there’s no DHCP server in play here) and is looking for a tftp server at 192.168.0.10 and a boot image of 0200A8C0.img.

printenv reveals some variables defined on the router, which confirms the IP addresses used by tftpboot.

ath> printenv
bootargs=console=ttyS0,115200 board=AP151 rootfstype=squashfs init=/etc/preinit mtdparts=spi0.0:128k(u-boot),1152k(uImage),15040k(rootfs),64k@0xff0000(ART) mem=128M
bootcmd=bootm 0x9f020000
bootdelay=1
baudrate=115200
ethaddr=0x00:0xaa:0xbb:0xcc:0xdd:0xee
ipaddr=192.168.0.2
serverip=192.168.0.10
dir=
lu=tftp 0x80060000 ${dir}tuboot.bin&&erase 0x9f000000 +$filesize&&cp.b $fileaddr 0x9f000000 $filesize
lf=tftp 0x80060000 ${dir}ap151${bc}-jffs2&&erase 0x9f010000 +$filesize&&cp.b $fileaddr 0x9f010000 $filesize
lk=tftp 0x80060000 ${dir}vmlinux${bc}.lzma.uImage&&erase 0x9f300000 +$filesize&&cp.b $fileaddr 0x9f300000 $filesize
stdin=serial
stdout=serial
stderr=serial
ethact=eth0

Environment size: 681/65532 bytes

It seemed like the pieces were in place for booting the router via tftp. I wired a laptop to the router using an ethernet cable. I set a static IP on the ethernet interface of the laptop to match the IP the router expected for tftpboot.

ip address flush dev eth0
ip address add 192.168.0.10/24 dev eth0

I set up a dnsmasq tftp server on the laptop to handle tftp requests.

dnsmasq \
  -i "${device}" \
  --enable-tftp \
  --tftp-root=/tmp/tftp \
  --bootp-dynamic \
  --port=0 `# disable DNS. Not needed` \
  --no-daemon

Initially, I tried to tftpboot an OpenWrt bin image file I obtained for my router. My thinking was that I had left the router in a state where perhaps a bootloader for OpenWrt was installed, but I had improperly clobbered the boot image somehow, and so maybe tftpbooting to a recent OpenWRT image would fix things.

tftpboot 192.168.0.10 openwrt-24.10.0-ath79-generic-tplink_archer-c59-v1-squashfs-factory.bin

I saw the corresponding request logged by the dnsmasq server.

dnsmasq: started, version 2.90 DNS disabled
dnsmasq: compile time options: IPv6 GNU-getopt DBus no-UBus i18n IDN2 DHCP DHCPv6 no-Lua TFTP conntrack ipset nftset auth cryptohash DNSSEC loop-detect inotify dumpfile
dnsmasq-dhcp: DHCP, IP range 10.1.1.50 -- 10.1.1.100, lease time 1h
dnsmasq-tftp: TFTP root is /tmp/tftp
dnsmasq-tftp: sent /tmp/tftp/openwrt-24.10.0-ath79-generic-tplink_archer-c59-v1-squashfs-factory.bin to 192.168.0.2

Back on the router, I did see that the tftpboot did seem to work, and loaded the boot image.

ath> tftpboot 192.168.0.10 openwrt-24.10.0-ath79-generic-tplink_archer-c59-v1-squashfs-factory.bin
Trying eth0
eth0 link down
FAIL
Trying eth1
enet1 port2 up
dup 1 speed 1000
Using eth1 device
TFTP from server 192.168.0.10; our IP address is 192.168.0.2
Filename 'openwrt-24.10.0-ath79-generic-tplink_archer-c59-v1-squashfs-factory.bin'.
Load address: 0x192
Loading: #################################################################
         #################################################################
         ... (continued) ...

done
Bytes transferred = 7209888 (6e03a0 hex)

The image was loaded, but nothing was happening. Based on some Googling, and the output of printenv, it looked like the bootcmd was set to bootcmd=bootm 0x9f020000.

However, running that left the system in a state where it was hanging and stuck and had to be rebooted.

ath> bootm 0x9f0200000
## Booting image at f0200000 ...

I tried various mechanisms like go and bootm with various addresses, but could not seem to get this image to actually boot. I threw a lot of different commands at the wall to see if something would stick.

My lack of understanding about U-Boot and the format of these boot images was not helping here.

Eventually, I tried something much more rudimentary. I ran the stock recovery process for my router. I…

  • Turned the router off
  • Held down the reset button
  • While the reset button was still held down, I powered on the router

I could see via minicom that this resulted in a slightly different state. Without a serial connection, and only using LEDs to debug, this process previously seemed pointless. With the serial connection I could see much more helpful information.

TFTP from server 192.168.0.66; our IP address is 192.168.0.86
Filename 'tp_recovery.bin'.

This was interesting. I could see that the router’s reset sequence triggered a specific command for U-Boot.

A note on OpenWrt’s website, which previously seemed odd to me, now made much more sense.

Comment recovery: TFTP recovery filename: tp_recovery.bin (use factory image)

https://openwrt.org/toh/hwdata/tp-link/tp-link_archer_c59_v1

I downloaded the stock TP-Link firmware for my device, renamed it to tp_recovery.bin, and put it in my tftp server’s directory. I also changed the laptop’s IP address to correspond to what the recovery process seemed to need.

ip address flush dev eth0
ip address add 192.168.0.66/24 dev eth0

I restarted dnsmasq, used the reset boot sequence again, and…

eth0 link down
FAIL
Trying eth1
enet1 port2 up
dup 1 speed 1000
Using eth1 device
TFTP from server 192.168.0.66; our IP address is 192.168.0.86
Filename 'tp_recovery.bin'.
Load address: 0x80800000
Loading: #################################################################
         #################################################################
         ... (continued) ...
done
Bytes transferred = 11768817 (b393f1 hex)
Firmware recovery: FLASH_SIZE = 16 filesize = 0xb393f1.
Reading Partition Table from NVRAM ... OK
Parsing Partition Table ... OK
File Length:11768817
[Error]sysmgr_proinfo_buildStruct():  587 @ unknown id(hw_id), skip it.
[Error]sysmgr_proinfo_buildStruct():  587 @ unknown id(oem_id), skip it.
[Error]sysmgr_cfg_checkSupportList():  909 @ 00000000 NOT Match.
Firmwave supports, check OK.
Firmware Checking Passed.
######################################################################
######################################################################
##############################################
Done.
All Done!

U-Boot 1.1.4 (Jul 16 2016 - 18:05:34)

...

athrs27_phy_setup ATHR_PHY_CONTROL 2 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 2 :50
athrs27_phy_setup ATHR_PHY_CONTROL 3 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 3 :50
eth1 up
eth0, eth1
Setting 0x181162c0 to 0x82
Autobooting in 1 seconds
## Booting image at 9f020000 ...
   Image Name:   MIPS OpenWrt Linux-3.3.8
   Created:      2016-07-19   9:52:32 UTC
   Image Type:   MIPS Linux Multi-File Image (lzma compressed)
   Data Size:    1069313 Bytes =  1 MB
   Load Address: 80060000
   Entry Point:  80060000
   Contents:
   Image 0:  1069305 Bytes =  1 MB
   Verifying Checksum at 0x9f020040 ...OK
   Uncompressing Multi-File Image ... OK
No initrd
## Transferring control to Linux (at address 80060000) ...
## Giving linux memsize in bytes, 134217728

Starting kernel ...

[    0.000000] Linux version 3.3.8 (tplink@tplink) (gcc version 4.6.3 20120201 (prerelease) (Linaro GCC 4.6-2012.02) ) #1 Tue Jul 19 17:51:52 HKT 2016
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU revision is: 00019750 (MIPS 74Kc)
[    0.000000] SoC: Qualcomm Atheros QCA956X rev 0
[    0.000000] Clocks: CPU:775.000MHz, DDR:650.000MHz, AHB:258.333MHz, Ref:25.000MHz
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 08000000 @ 00000000 (usable)
[    0.000000] User-defined physical RAM map:
[    0.000000]  memory: 08000000 @ 00000000 (usable)
[    0.000000] Initrd not found or empty - disabling initrd
[    0.000000] Zone PFN ranges:

Voila. It booted a kernel! So, it seems like I must’ve had the stock TP-Link firmware after all, and just needed a stock recovery image.

Now running in RAM - U-Boot at: 87fa8000
Flash Manuf Id 0xef, DeviceId0 0x40, DeviceId1 0x18
flash size 16MB, sector count = 256
Flash: 16 MB
Using default environment

In:    serial
Out:   serial
Err:   serial
Net:   ath_gmac_enet_initialize...
No valid address in Flash. Using fixed address
No valid address in Flash. Using fixed address
ath_gmac_enet_initialize: reset mask:c02200
Dragonfly----> S27 PHY *
: cfg1 0x80000000 cfg2 0x7114
eth0: 00:03:7f:09:0b:ad
athrs27_phy_setup ATHR_PHY_CONTROL 4 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 4 :10
eth0 up
ATHRS27: resetting s27
ATHRS27: s27 reset done
: cfg1 0x800c0000 cfg2 0x7214
eth1: 00:03:7f:09:0b:ad
athrs27_phy_setup ATHR_PHY_CONTROL 0 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 0 :50
athrs27_phy_setup ATHR_PHY_CONTROL 1 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 1 :50
athrs27_phy_setup ATHR_PHY_CONTROL 2 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 2 :50
athrs27_phy_setup ATHR_PHY_CONTROL 3 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 3 :50
eth1 up
eth0, eth1
Setting 0x181162c0 to 0x82
Trying eth0
eth0 link down
FAIL
Trying eth1
enet1 port2 up
dup 1 speed 1000
Using eth1 device
TFTP from server 192.168.0.66; our IP address is 192.168.0.86
Filename 'tp_recovery.bin'.
Load address: 0x80800000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         ########################
done
Bytes transferred = 11768817 (b393f1 hex)
Firmware recovery: FLASH_SIZE = 16 filesize = 0xb393f1.
Reading Partition Table from NVRAM ... OK
Parsing Partition Table ... OK
File Length:11768817
[Error]sysmgr_proinfo_buildStruct():  587 @ unknown id(hw_id), skip it.
[Error]sysmgr_proinfo_buildStruct():  587 @ unknown id(oem_id), skip it.
[Error]sysmgr_cfg_checkSupportList():  909 @ 00000000 NOT Match.
Firmwave supports, check OK.
Firmware Checking Passed.
######################################################################
######################################################################
##############################################
Done.
All Done!

U-Boot 1.1.4 (Jul 16 2016 - 18:05:34)

ap151 - Dragonfly 1.0

DRAM:  128 MB
Top of RAM usable for U-Boot at: 88000000
Reserving 342k for U-Boot at: 87fa8000
Reserving 32832k for malloc() at: 85f98000
Reserving 44 Bytes for Board Info at: 85f97fd4
Reserving 36 Bytes for Global Data at: 85f97fb0
Reserving 128k for boot params() at: 85f77fb0
Stack Pointer at: 85f77f98
Now running in RAM - U-Boot at: 87fa8000
Flash Manuf Id 0xef, DeviceId0 0x40, DeviceId1 0x18
flash size 16MB, sector count = 256
Flash: 16 MB
Using default environment

In:    serial
Out:   serial
Err:   serial
Net:   ath_gmac_enet_initialize...
No valid address in Flash. Using fixed address
No valid address in Flash. Using fixed address
ath_gmac_enet_initialize: reset mask:c02200
Dragonfly----> S27 PHY *
: cfg1 0x80000000 cfg2 0x7114
eth0: 00:03:7f:09:0b:ad
athrs27_phy_setup ATHR_PHY_CONTROL 4 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 4 :10
eth0 up
ATHRS27: resetting s27
ATHRS27: s27 reset done
: cfg1 0x800c0000 cfg2 0x7214
eth1: 00:03:7f:09:0b:ad
athrs27_phy_setup ATHR_PHY_CONTROL 0 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 0 :50
athrs27_phy_setup ATHR_PHY_CONTROL 1 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 1 :50
athrs27_phy_setup ATHR_PHY_CONTROL 2 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 2 :50
athrs27_phy_setup ATHR_PHY_CONTROL 3 :1000
athrs27_phy_setup ATHR_PHY_SPEC_STAUS 3 :50
eth1 up
eth0, eth1
Setting 0x181162c0 to 0x82
Autobooting in 1 seconds
## Booting image at 9f020000 ...
   Image Name:   MIPS OpenWrt Linux-3.3.8
   Created:      2016-07-19   9:52:32 UTC
   Image Type:   MIPS Linux Multi-File Image (lzma compressed)
   Data Size:    1069313 Bytes =  1 MB
   Load Address: 80060000
   Entry Point:  80060000
   Contents:
   Image 0:  1069305 Bytes =  1 MB
   Verifying Checksum at 0x9f020040 ...OK
   Uncompressing Multi-File Image ... OK
No initrd
## Transferring control to Linux (at address 80060000) ...
## Giving linux memsize in bytes, 134217728

Starting kernel ...

[    0.000000] Linux version 3.3.8 (tplink@tplink) (gcc version 4.6.3 20120201 (prerelease) (Linaro GCC 4.6-2012.02) ) #1 Tue Jul 19 17:51:52 HKT 2016
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU revision is: 00019750 (MIPS 74Kc)
[    0.000000] SoC: Qualcomm Atheros QCA956X rev 0
[    0.000000] Clocks: CPU:775.000MHz, DDR:650.000MHz, AHB:258.333MHz, Ref:25.000MHz
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 08000000 @ 00000000 (usable)
[    0.000000] User-defined physical RAM map:
[    0.000000]  memory: 08000000 @ 00000000 (usable)
[    0.000000] Initrd not found or empty - disabling initrd
[    0.000000] Zone PFN ranges:
[    0.000000]   Normal   0x00000000 -> 0x00008000
[    0.000000] Movable zone start PFN for each node
[    0.000000] Early memory PFN ranges
[    0.000000]     0: 0x00000000 -> 0x00008000
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 32512
[    0.000000] Kernel command line:  console=ttyS0,115200 board=AP151 rootfstype=squashfs init=/etc/preinit mtdparts=spi0.0:128k(u-boot),1152k(uImage),15040k(rootfs),64k@0xff0000(ART) mem=128M rootfstype=squashfs,jffs2 nd
[    0.000000] PID hash table entries: 512 (order: -1, 2048 bytes)
...

After a few reboots, and setting up my router “normally” using the OEM process, it was back to life. I could even get a standard Linux shell over UART after hitting Enter after the kernel log messages.

BusyBox v1.19.4 (2016-07-16 17:16:48 HKT) built-in shell (ash)
Enter 'help' for a list of built-in commands.

     MM           NM                    MMMMMMM          M       M
   $MMMMM        MMMMM                MMMMMMMMMMM      MMM     MMM
  MMMMMMMM     MM MMMMM.              MMMMM:MMMMMM:   MMMM   MMMMM
MMMM= MMMMMM  MMM   MMMM       MMMMM   MMMM  MMMMMM   MMMM  MMMMM'
MMMM=  MMMMM MMMM    MM       MMMMM    MMMM    MMMM   MMMMNMMMMM
MMMM=   MMMM  MMMMM          MMMMM     MMMM    MMMM   MMMMMMMM
MMMM=   MMMM   MMMMMM       MMMMM      MMMM    MMMM   MMMMMMMMM
MMMM=   MMMM     MMMMM,    NMMMMMMMM   MMMM    MMMM   MMMMMMMMMMM
MMMM=   MMMM      MMMMMM   MMMMMMMM    MMMM    MMMM   MMMM  MMMMMM
MMMM=   MMMM   MM    MMMM    MMMM      MMMM    MMMM   MMMM    MMMM
MMMM$ ,MMMMM  MMMMM  MMMM    MMM       MMMM   MMMMM   MMMM    MMMM
  MMMMMMM:      MMMMMMM     M         MMMMMMMMMMMM  MMMMMMM MMMMMMM
    MMMMMM       MMMMN     M           MMMMMMMMM      MMMM    MMMM
     MMMM          M                    MMMMMMM        M       M
       M
 ---------------------------------------------------------------
   For those about to rock... (%C, %R)
 ---------------------------------------------------------------
root@Archer_C59:/#

I took the opportunity to log some information for my router in Linux.

root@Archer_C59:/# uname -a
Linux Archer_C59 3.3.8 #1 Tue Jul 19 17:51:52 HKT 2016 mips GNU/Linux
root@Archer_C59:/# cat /proc/cmdline
 console=ttyS0,115200 board=AP151 rootfstype=squashfs init=/etc/preinit mtdparts=spi0.0:128k(u-boot),1152k(uImage),15040k(rootfs),64k@0xff0000(ART) mem=128M rootfstype=squashfs,jffs2 noinitrd