Recent Linux on the D-Link DIR-685
(Last boot 2017-09-17 with a patched v4.14-rc1 kernel)

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.

The D-Link DIR-685 is some kind of high-profile product from D-Link, it is labelled a Xtreme N Storage Router whatever that means. It is based solely around the Gemini platform, and ARMv4 Faraday silicon design, and has the following features:

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!

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 with rootfs

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.

Kernel TODO

Userspace TODO

Getting RT2880 wireless going

This needs kernel config CONFIG_RT2800_PCI to begin with.