Linux on the D-Link DNS-320L
I recently purchased a D-Link DNS-320L, hoping that it would be similar enough to the DNS-320 to get Linux up and running. Turned out that I was wrong. The DNS-320L features a different, but similar CPU and is tighter controller by an additional microcontroller.
A few days ago I started exploring the DNS320L's capabilities, added Serial port and started hacking around. I tried to boot various ARM Linux kernels, but the only one I got output from was the Arch kernel - and it told me that my platform was not supported. Fortunately, D-Link has just recently released the GPL Sources for the DNS-320L.
From the Sources I could read that the 88F6702A CPU used in this little box is very similar to the 88F6291A CPU used in a few other NAS boxes. Therefore, I started patching and hacking around the Linux kernel.
It took me quite some days to figure out that one can set the mach-id in u-boot using
setenv arcNumber 4746
After that and adding a few hacks to the Linux kernel, I got Linux-3.1.10 up and running in a minimal configuration with limited device support.
Be aware that this guide is not for beginners! Everything you do you do on your own risk. Soldering pins to the board and toying around with mainline Linux will certainly void your warranty!
Adding Serial Port
There is an unpopulated Serial header near the power jack on the board. You need to solder pins to the header, the pinout is similar to the DNS320 (very good page by Jamie Lentin describing his work on the DNS320/325).
Hacking around the GPIO registers
The Marvell Kirkwood chips have up to 50 GPIO pins tha can be used for various purposes. Unfortunately, due to the lack of documentation, it is often hard to tell which GPIO performs what function.
By chance I found a forum post recommending the use of mv_shell to read special registers. This works fine on the Marvell kernel, but does not on mainline kernels.
What I did was read the GPIO registers, toggle a LED or press a button and read the GPIO registers again. Here is some examplary output from the command
rr 0x10100
in mv_shell:
HD0 RED LED OFF: 00010100 : 81000000 00010104 : 380fffff 00010108 : 00000000 0001010c : 00000000 00010110 : b9000000 00010114 : b90c4f30
Then I toggled the red LED of HD0 and read the registers again:
HD0 RED LED ON: 00010100 : 81400000 GPIO Data Out Register 00010104 : 380fffff GPIO Data Out Enable Control Register 00010108 : 00000000 GPIO Blink Enable Register 0001010c : 00000000 GPIO Data In Polarity Register 00010110 : b9400000 GPIO Data In Register 00010114 : b94c4f30 GPIO Interrupt Cause Register
and voila: One of the bits at 0x10110 changed! By examining the bit number you can get the GPIO number, and in this case this corresponds to GPIO 22. I added the relevant register descriptions as well. For the higher GPIO numbers, the base starts at 0x101400 instead of 0x10100.
The MCU
D-Link put a small Microcontroller on /dev/ttyS1 at 115200 baud. The responsibility of this Weltrend MCU is to supervise the system, to act as a RTC and to wake up the device if required as well as to control the fan. Lorenzo Martignoni succeeded in writing a basic fan control daemon and reverse engineered parts of the protocol. Find his daemon here: https://github.com/martignlo/DNS-320L
I took the same approach (install strace and run up_send_daemon and up_read_daemon on the original firmware to gather commands) and found some more commands:
- Set DeviceReady, automatically toggles power LED to ON
- Turn Power LED to blink/on/off
- Send DeviceShutdown command
- Read/Write RTC
- Get/Set WOL Status
- Get/Set RTC Alarm
- …
The power up / shutdown procedure of the device is as follows:
- Upon startup, the OS sends DeviceReady to the MCU
- the MCU stops blinking the power LED and does NOT immediately power down on power button press
- Instead, long pressing the power button is registered again by the MCU and the Power LED starts blinking
- A GPIO is pulled low that can be read by Linux and which triggers the shutdown process
- Then again, DeviceShutdown is sent to the MCU to cut power
As you can see, it is quite a complex setup just to power up and shut down the machine.
Working so far
- Boot from SATA Disk
- Ethernet
- USB
- Serial
- Fan Speed Control (needs userspace daemon)
- All LEDs
- All Buttons (but power button can only be read as GPIO, does not produce a key event)
- Real Time Clock (needs userspace daemon)
- Correct Power Down (needs userspace daemon)
- WOL Status (needs userspace daemon)
- Automatic Power Recovery (needs userspace daemon)
Still Todo/WiP
- Resume from RTC Alarm
Installation - Arch/Debian/etc.
Currently, there is no way around a serial console! The stock firmware does not feature fw_setenv and setting U-Boot variables inside my Arch build corrupts the environment and resets it to defaults!
Patch Download
You can find the latest Linux kernel patch in my Hg repository: www.aboehler.at/hg/linux-dns320l
Compilation
Get yourself a Linux 3.11 kernel source from kernel.org. Then, apply the patch from the linux-3.11 subfolder of my mercurial repository. Compile/Cross-Compile your kernel and you're ready to go (on Arch, I installed arm-linux-gnueabi-gcc-stage2 from the AUR as a cross compiler).
After compilation, you need to set the arcNumber to 4746 in U-Boot (as mentioned above) and run the kernel, e.g. via tftp.
Alternatively, you can grab a 3.12 kernel (linux-next at this time) and put the DeviceTree dts file to arch/arm/boot/dts. Then follow the build instructions on the very bottom of Jamie Lentin's site: http://jamie.lentin.co.uk/devices/dlink-dns325/keeping-original-firmware/.
If you are on Arch Linux, you can just fetch my PKGBUILD and the patches from my hg repository.
System Daemon
For the system to be able to control the fan, power up and shutdown properly as well as to configure the RTC, daemon software is provided by D-Link. Based on work by Lorenzo Martignoni, I reverse engineered most of the commands and implemented a first version daemon. It's written in C and provided as-is.
It was developed and tested on Arch, so a systemd unit is provided as well. Additionally, a PKGBUILD is available to compile a package. Read the Readme.txt file in the distribution. You can find the sourcecode in my Hg repository: www.aboehler.at/hg/dns320l-daemon
Setting up U-BOOT
Using serial console at 115200 baud you can access U-Boot environment during power up. Press “Space” and “1” to get to a prompt.
I added the following lines to quickly be able to toggle between Linux and Stock firmware. My kernel sits on an ext2 partition on the first harddisk, the root filesystem is an ex4 partition at /dev/sda2.
setenv arcNumber 4746 setenv mainlineLinux yes setenv bootargs_stock 'console=ttyS0,115200 root=/dev/ram' setenv bootargs_linux 'console=ttyS0,115200 root=/dev/sda2' setenv bootcmd_linux 'setenv bootargs $(bootargs_linux); ide reset; ext2load ide 0:1 0xa00000 /uImage; bootm 0xa00000' setenv bootcmd_stock 'setenv bootargs $(bootargs_stock); nand read.e 0xa00000 0x100000 0x300000;nand read.e 0xf00000 0x600000 0x300000;bootm 0xa00000 0xf00000' setenv to_stock 'setenv mainlineLinux no; setenv bootcmd \'run bootcmd_stock\'; saveenv; reset' setenv to_linux 'setenv mainlineLinux yes; setenv bootcmd \'run bootcmd_linux\'; saveenv; reset'
Afterwards, you can switch between Linux and Stock simply by running
run to_linux
resp.
run to_stock
Installation - OpenWrt
I created a patchset for OpenWrt, which can be installed to the internal NAND. This allows for the harddisks to spin down more often and more efficiently, since the root filesystem is not on the harddisk. To compile your own OpenWrt, download the patch from my hg and apply it to a trunk checkout of OpenWrt. The patch was created against revision 39746 of OpenWrt.
To install OpenWrt, put the files openwrt-kirkwood-dns320l-uImage and openwrt-kirkwood-dns320l-jffs2-nand-2048-128k.img in your computer's TFTP root directory. Attach serial console and issue the following commands in your uboot environment:
setenv bootargs_openwrt 'console=ttyS0,115200 earlyprintk root=/dev/mtdblock2 rootfstype=jffs2' setenv bootcmd_openwrt 'setenv bootargs $(bootargs_openwrt); nand read.e 0xa00000 0x100000 0x300000; bootm 0x300000' setenv to_openwrt 'setenv mainlineLinux yes; setenv bootcmd \'run bootcmd_openwrt\'; saveenv; reset' rw 0x6400000 0xffff 0x200000 tftpboot 0x6400000 openwrt-dns320l-uImage nand erase 0x100000 0x400000 nand write.e 0x6400000 0x100000 0x400000 rw 0x6400000 0xffff 0x200000 tftpboot 0x6400000 openwrt-dns320l-jffs2.img nand erase 0x500000 0xfb00000 nand write.e 0x6400000 0x500000 0x200000
Now you should be able to boot into your new OpenWrt installation by issuing a simple 'to_openwrt' in your U-Boot.
A Package for dns320l-daemon, which is necessary for fan control and power button, will follow soon.