Recent Linux on the D-Link DIR-685
(Last boot 2021-04-12 with the v5.10 kernel, using OpenWRT as rootfs)

D-Link DIR-685

D-Link DIR-685 booting a mainline kernel with prompt in the little display. The touchkeys on the right provide "enter" but that is the only useful input to the terminal without any GUI tools.

I have moved the basic use and set-up of the device to the corresponding OpenWrt DIR-685 device page where it is properly maintained by the community. This page is just my own musings and tricks, and a bit of history.

Here is my latest factory image for the router, this is based on mainline OpenWrt as of 2021-04-12 plus some minor patches, it fixes the RTL8366RB switch so the router can be used as a router.

There is a datasheet from D-Link.


This is missing:

Getting into it

You need to remove four screws on the bottom and then there are plastic snap locks around the edges, I open these with a credit card or a letter knife.

Circuit board photos

Identified electronics:

Connecting a UART

J3 on the top is obviously the serial port. Helpfully there is even a header. There is some documentation I found online on the layout of the serial port:


  o o o _ o
  | | |   |
  | | |   RX
  | | VCC
  | GND

The serial port configuration should be: 19200 baud, 8 bits, no parity, no flow control. After soldering a RS232 interface accordingly, the console works like a charm!

Getting OpenWRT up on it


  1. Plug the DIR-685 into your computer ethernet using one of the LAN ports on the back. Set your computer to obtain network from DHCP.
  2. Turn on the router. The router should boot and assign an IP number to your computer. Check what IP you get: I got
  3. Check that you can contact the router: ping
  4. Start a browser and browse to
  5. Log in as "Admin", blank password.
  6. Select "Tools" from the top row pane
  7. Select "Firmware on the left pane
  8. Select "Browse" in the "FIRWMARE UPGRADE" box
  9. Select the openwrt-gemini-dlink_dir-685-squashfs-factory.bin file from the OpenWrt build or from another trusted source
  10. Hit "Upload"
  11. The router says "The F/W is updating..." also in the little display.
  12. Next thing that happens is that OpenWrt boots the kernel from flash.
  13. Note that INSTALLATION IS NOT FINISHED YET if you just reboot the router here, it will be bricked.
  14. Wait for the following text on the console (this can take a few minutes):
        jffs2_build_filesystem(): erasing all blocks after the end marker...
        jffs2: notice: (1645) jffs2_build_xattr_subsystem: complete building xattr subsystem, 0 of xdatum (0 unchecked, 0 orphan).

    This means the flash is set up and ready to be used.
  15. Configure networking so you get SSH access to the host
  16. Upload the openwrt-gemini-dlink_dir-685-squashfs-sysupgrade.bin image to the routers /tmp directory. If your networking is set up properly it should be just:
    scp openwrt-gemini-dlink_dir-685-squashfs-sysupgrade.bin root@
  17. Issue:
    sysupgrade /tmp/openwrt-gemini-dlink_dir-685-squashfs-sysupgrade.bin
  18. The DIR-685 will flash the firmware upgrade and reboot
  19. After this you can proceed with full configuration etc.

The firmware can also be reflashed from the RedBoot derivative "Boot Menu" that comes up if you have a serial console like this:

  1. Select "Y: Upgrade Kernel" by pressing "Y"
  2. Select TFTP or serial download (see elsewhere for how that works) Download the openwrt-gemini-dlink_dir-685-squashfs-factory.bin file using TFTP, you need to rename it because the boot loader cannot handle this long filename. I just name it openwrt
  3. The boot loader will erase and flash the image looking something like this:
    Erase flash (0x30040000): Size=32768000 ........
    Program flash (0x30040000): Size=6750212 ........
    Reboot to start the new firmware and it should come up.

After first boot the dmesg displays this stuff on the console. DO NOT POWER OFF THE DEVICE WHILE THIS IS GOING ON!

[   25.636854] mount_root: jffs2 not ready yet, using temporary tmpfs overlay
[   74.996487] jffs2_scan_eraseblock(): End of filesystem marker found at 0x0
[   75.126315] jffs2_build_filesystem(): unlocking the mtd device...
[   75.126340] done.
[   75.256101] jffs2_build_filesystem(): erasing all blocks after the end marker...
[  189.309087] jffs2: notice: (1645) jffs2_build_xattr_subsystem: complete building xattr subsystem, 0 of xdatum (0 unchecked, 0 orphan).
[  191.353679] overlayfs: upper fs does not support tmpfile.

After this df -Tm should display this:

Filesystem           Type       1M-blocks      Used Available Use% Mounted on
/dev/root            squashfs           4         4         0 100% /rom
tmpfs                tmpfs             60         0        60   0% /tmp
tmpfs                tmpfs             60         0        60   0% /tmp/root
tmpfs                tmpfs              1         0         1   0% /dev
/dev/mtdblock4       jffs2             25         1        24   4% /overlay
overlayfs:/overlay   overlay           25         1        24   4% /

Quick Install of Hard Disk Boot partition with Busybox or OpenWRT

It is possible to prepare a SATA hard disk and use that to boot OpenWRT and set the device up as a router and NAS without any soldering or other trickery. All you need to know is how to use OpenWRT.

Booting a kernel

Compiling the kernel

To compile the kernel you first need a ARMv4 toolchain to build it with, then you need to configure and compile the kernel, and possibly also attach an initramfs.

I use a special gemini.mak makefile that I put in the root of the kernel directory, edit the makefile to select the right DTB then I type:

  make -f gemini.mak config && make -f gemini.mak build
And this will build the whole kernel, attaching an initramfs and a DTB file, and put the resulting zImage in $HOME as well as in /var/lib/tftpboot if it is writeable. You will need the rootfs-gemini.cpio rootfs for this to succeed.

Pre-built kernel images

These can be downloaded and booted using the method described above.

Analyzing the vendor code

This bootlog from the unmodified firmware shows some kernel init and flash partitioning etc. A modified busybox binary was needed to get the dmesg out. It shows us that the kernel shipping in my router was compiled on march 25, 2010.

The vendor tree has a custom GPIO userspace access driver in boards/wrgns01/apps/nas_gpio_access/nas_gpio_access.c exposing GPIO in /proc/nas_gpio/gpio looking like this:

  # cd nas_gpio/
  # ls
  # cd gpio/
  # ls
  outen_pa_7   outen_pa_14  outen_pb_7   outen_pb_14  data_pa_13   data_pa_11
  outen_pa_8   outen_pa_16  outen_pb_8   outen_pb_16  data_pb_6    data_pa_12
  outen_pa_11  outen_pa_18  outen_pb_11  outen_pb_18  data_pa_7
  outen_pa_12  outen_pa_6   outen_pb_12  outen_pb_6   data_pa_8

Inspecting the driver we see that for ports A and B it creates all the same output enable and data input/output files. So this means GPIO lines 6, 7, 8, 11, 12, 13, 14, 16, 18 are used on either port A or port B, and line 13 is only used as input.

There is further a userspace program for GPIO and LED control in boards/wrgns01/apps/fresetd.c where the use of different lines can be deduced. This illustrates that the blue and orange HD LEDs cannot be on at the same time.

After reverse-engineering the following is deducted:

18 is still unknown, we don't know which port it is used on or for what.

Some were found inspecting the vendor code that contains boards/wrgns01/apps/rtl8366/rtl8366rb a kernel module for controlling a RealTek RTL8366RB switch using GPIO.

A compiled kernel module named switch.ko is in the directory and disassebling it hints that it is doing control of the switch over GPIO in response to custom ioctl() calls from userspace. armv4l-objdump -d switch.ko |less yields things like this:

00000ad0 <_smi_start>:
    ad0:       e1a0c00d        mov     ip, sp
    ad4:       e92dd800        push    {fp, ip, lr, pc}
    ad8:       e24cb004        sub     fp, ip, #4      ; 0x4
    adc:       e3a01001        mov     r1, #1  ; 0x1
    ae0:       e3a00016        mov     r0, #22 ; 0x16
    ae4:       ebfffffe        bl      934 
    ae8:       e3a01001        mov     r1, #1  ; 0x1
    aec:       e3a00015        mov     r0, #21 ; 0x15
    af0:       ebfffffe        bl      934 
    af4:       e3a00015        mov     r0, #21 ; 0x15
    af8:       e3a01000        mov     r1, #0  ; 0x0
    afc:       ebfffffe        bl      a40 
    b00:       e3a00016        mov     r0, #22 ; 0x16
    b04:       e3a01001        mov     r1, #1  ; 0x1
    b08:       ebfffffe        bl      a40 

It includes some kind of interrupt handler named linkInterruptHandle, setting link status, gpio_set_dir_bit, gpio_read_bit, gpio_write_bit, rtl8366rb_setEthernetPHY etc. 0x16 is passed to gpio_read_bit and 0x15 and 0x16 is passed to gpio_write_bit, so it is reasonable to assume that GPIOs 0x15 (21) and 0x16 (22) are used for controlling MDC and MDIO for the RTL8366RB SMI interface.

SMI is not MDIO. It is not following the standard anything else than electrically, sadly. So it needs a separate implementation.

We find the RESET GPIO line and the interrupt line the same way. This is not very nice kernel design since the GPIO registers are mapped directly into this driver creating races etc.

In the vendor kernel tree there is some sort of driver in driver/net/sl351x_gmac.8366rb.c

The DD-WRT people have a driver dump here specifically for the 8366RB here.

RT2880F-based PCIe card

I also looked at the Ralink RT2880F mini PCI card. The source dump contains a binary kernel module rt2880_iNIC.ko but it does not contain the source code. iNIC spells out "intelligent network interface card", I guess the lowercase i is a tribute too all Apple stuff named like that or something. It has a curious Makefile in dir685.gpl.source.new0611/dir685/progs.priv/ralink_RT2880_iNIC/v1.1.8.3/build/module-v1.1.8.3 that indicates codenames for all projects Ralink have delivered this PCI card to:


This PCIe card looks like so in lspci:

00:0c.0 Class 0200: 1814:0801

The card also appears in Asus DSL-N13, Asus WL-127n, Alcatel Lucent Cellpipe 7130, Pegatron WL227N-5G Wireless Card, and the Billion BiPAC 7800N. I checked the source code for that device as well. It also just has a binary module.

The DD-WRT source tree contains a Makefile in drivers/net/wireless/Makefile that references:

#obj-$(CONFIG_RT2880v2_INIC_MII) += iNIC/mii/
#obj-$(CONFIG_RT2880v2_INIC_PCI) += iNIC/pci/

In some referenced linux-ramips_mt7620a/linux-3.10.17 the config symbol CONFIG_RT305x_INIC_MII is used to build this driver, maybe some MT7620 or RT5350 source tree accidentally contains this driver?

dir685.gpl.source.new0611/dir685/progs.priv/ralink_RT2880_iNIC/v1.1.8.3/build/module-v1.1.8.3.tmp_versions/rt2880_iNIC.mod reveals that we are looking for this source code set:


BINGO! there is the sourcecode in the middel of Canal+ r7oss project with open source code for the Canal+ Set Top Boxes. Thank you Canal+! Also an subset of it called ralink inic can be found in use by Vitalii Kovzik apparently working on the same thing.

Kernel TODO


Testing ethernet with the DSA switch

Recent versions of OpenWRT and the Linux kernel v5.10 should bring up the DSA switch and interfaces lan0, lan1, lan2, lan3 and wan automatically. You should be able to do things like ping on each lan0, lan1 (etc) port and see packets coming out/in on that very port.

The manual way to test an ethernet connected through a DSA switch is kind of idiomatic and involves bringing the CPU-facing eth0 and the lan0 switch port independently:

  ifconfig eth0 netmask up
  ifconfig lan0 up
  ifconfig lan1 up
  ifconfig lan2 up
  ifconfig lan3 up
  ifconfig wan up

Configuring a static IP in OpenWRT will handle the first automatically, the 5 other ports need to be set up manually for now.

Getting RT2880 wireless going

Because of missing driver support this device does not work yet.