Implementing a web server in a single printf() call

A guy just forwarded a joke that most of us will already know Jeff Dean Facts (also here and here). Everytime I read that list, this part stands out:

Jeff Dean once implemented a web server in a single printf() call. Other engineers added thousands of lines of explanatory comments but still don’t understand exactly how it works. Today that program is the front-end to Google Search.

It is really possible to implement a web server using a single printf call, but I haven’t found anyone doing it. So this time after reading the list, I decided to implement it. So here is the code, a pure single printf call, without any extra variables or macros (don’t worry, I will explain how to this code works)

#include <stdio.h>

int main(int argc, char *argv[])
  ((((unsigned long int)0x4005c8 + 12) >> 16) & 0xffff), 
  0, 0x00000000006007D8 + 2, 
  (((unsigned long int)0x4005c8 + 12) & 0xffff)-
  ((((unsigned long int)0x4005c8 + 12) >> 16) & 0xffff), 
  0, 0x00000000006007D8 );

This code only works on a Linux AMD64 bit system, with a particular compiler (gcc version 4.8.2 (Debian 4.8.2-16) ) And to compile it:

gcc -g web1.c -O webserver

As some of you may have guessed: I cheated by using a special format string . That code may not run on your machine because I have hardcoded two addresses.

The following version is a little bit more user friendly (easier to change), but you are still going to need to change 2 values: FUNCTION_ADDR and DESTADDR which I will explain later:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define FUNCTION_ADDR ((uint64_t)0x4005c8 + 12)
#define DESTADDR 0x00000000006007D8
#define a (FUNCTION_ADDR & 0xffff)
#define b ((FUNCTION_ADDR >> 16) & 0xffff)

int main(int argc, char *argv[])
	, b, 0, DESTADDR + 2, a-b, 0, DESTADDR );

I will explain how the code works through a series of short C codes. The first one is a code that will explain how that we can start another code without function call. See this simple code:

#include <stdlib.h>
#include <stdio.h>

#define ADDR 0x00000000600720

void hello()
        printf("hello world\n");

int main(int argc, char *argv[])
        (*((unsigned long int*)ADDR))= (unsigned long int)hello;

You can compile it, but it many not run on your system. You need to do these steps:

1. Compile the code:

gcc run-finalizer.c -o run-finalizer

2. Examine the address of fini_array

objdump -h -j .fini_array run-finalizer

And find the VMA of it:

run-finalizer:     file format elf64-x86-64
Idx Name          Size      VMA               LMA               File off  Algn
 18 .fini_array   00000008  0000000000600720  0000000000600720  00000720  2**3
                  CONTENTS, ALLOC, LOAD, DATA

Note that you need a recent GCC to do this, older version of gcc uses different mechanism of storing finalizers.

3. Change the value of ADDR on the code to the correct address

4. Compile the code again

5. Run it

and now you will see “hello world” printed to your screen. How does this work exactly?:

According to Chapter 11 of Linux Standard Base Core Specification 3.1

This section holds an array of function pointers that contributes to a single termination array for the executable or shared object containing the section.

We are overwriting the array so that our hello function is called instead of the default handler. If you are trying to compile the webserver code, the value of ADDR is obtained the same way (using objdump).

Ok, now we know how to execute a function by overriding a certain address, we need to know how we can overwrite an address using printf. You can find many tutorials on how to exploit format string bugs, but I will try give a short explanation.

The printf function has this feature that enables us to know how many characters has been printed using the “%n” format:

#include <stdio.h>
int main(){
        int count;
        printf("AB%n", &count);
        printf("\n%d characters printed\n", count);

You will see that the output is:

2 characters printed

Of course we can put any address to the count pointer to overwrite that address. But to overide an address with a large value we need to print a large amount of text. Fortunately there is another format string “%hn” that works on short instead of int. We can overwrite the value 2 bytes at a time to form the 4 byte value that we want.

Lets try to use two printf calls to put a¡ value that we want (in this case the pointer to function “hello”) to the fini_array:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define FUNCTION_ADDR ((uint64_t)hello)
#define DESTADDR 0x0000000000600948

void hello()
	printf("\n\n\n\nhello world\n\n");

int main(int argc, char *argv[])
	short a= FUNCTION_ADDR & 0xffff;
	short b = (FUNCTION_ADDR >> 16) & 0xffff;
	printf("a = %04x b = %04x\n", a, b)
        uint64_t *p = (uint64_t*)DESTADDR;
        printf("before: %08lx\n", *p);
	printf("%*c%hn", b, 0, DESTADDR + 2 );
        printf("after1: %08lx\n", *p); 
	printf("%*c%hn", a, 0, DESTADDR);
        printf("after2: %08lx\n", *p);
	return 0;

The important lines are:

	short a= FUNCTION_ADDR & 0xffff;
	short b = (FUNCTION_ADDR >> 16) & 0xffff;
	printf("%*c%hn", b, 0, DESTADDR + 2 );
	printf("%*c%hn", a, 0, DESTADDR);

The a and b are just halves of the function address, we can construct a string of length a and b to be given to printf, but I chose to use the “%*” formatting which will control the length of the output through parameter.

For example, this code:

   printf("%*c", 10, 'A');

Will print 9 spaces followed by A, so in total, 10 characters will be printed.

If we want to use just one printf, we need to take account that b bytes have been printed, and we need to print another b-a bytes (the counter is accumulative).

  printf("%*c%hn%*c%hn", b, 0, DESTADDR + 2, b-a, 0, DESTADDR );

Currently we are using the “hello” function to call, but we can call any function (or any address). I have written a shellcode that acts as a web server that just prints “Hello world”. This is the shell code that I made:

unsigned char hello[] = 

If we remove the function hello and insert that shell code, that code will be called.

That code is just a string, so we can append it to the “%*c%hn%*c%hn” format string. This string is unnamed, so we will need to find the address after we compile it. To obtain the address, we need to compile the code, then disassemble it:

objdump -d webserver

00000000004004fd <main>:
  4004fd:	55                   	push   %rbp
  4004fe:	48 89 e5             	mov    %rsp,%rbp
  400501:	48 83 ec 20          	sub    $0x20,%rsp
  400505:	89 7d fc             	mov    %edi,-0x4(%rbp)
  400508:	48 89 75 f0          	mov    %rsi,-0x10(%rbp)
  40050c:	c7 04 24 d8 07 60 00 	movl   $0x6007d8,(%rsp)
  400513:	41 b9 00 00 00 00    	mov    $0x0,%r9d
  400519:	41 b8 94 05 00 00    	mov    $0x594,%r8d
  40051f:	b9 da 07 60 00       	mov    $0x6007da,%ecx
  400524:	ba 00 00 00 00       	mov    $0x0,%edx
  400529:	be 40 00 00 00       	mov    $0x40,%esi
  40052e:	bf c8 05 40 00       	mov    $0x4005c8,%edi
  400533:	b8 00 00 00 00       	mov    $0x0,%eax
  400538:	e8 a3 fe ff ff       	callq  4003e0 <printf@plt>
  40053d:	c9                   	leaveq 
  40053e:	c3                   	retq   
  40053f:	90                   	nop

We only need to care about this line:

mov    $0x4005c8,%edi

That is the address that we need in:

#define FUNCTION_ADDR ((uint64_t)0x4005c8 + 12)

The +12 is needed because our shell code starts after the string “%*c%hn%*c%hn” which is 12 characters long.

If you are curious about the shell code, it was created from the following C code.


int main(int argc, char *argv[])
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in serv_addr;
	bzero((char *)&serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        serv_addr.sin_port = htons(8080);
	bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
	listen(sockfd, 5);
	while (1) {
		int cfd  = accept(sockfd, 0, 0);
		char *s = "HTTP/1.0 200\r\nContent-type:text/html\r\n\r\n<h1>Hello world!</h1>"; 
		if (fork()==0) {
			write(cfd, s, strlen(s));
			shutdown(cfd, SHUT_RDWR);

	return 0;

I have done an extra effort (although it is not really necessary in this case) to remove all NUL character from the shell code (since I couldn’t find one for X86-64 in the Shellcodes database).

Jeff Dean once implemented a web server in a single printf() call. Other engineers added thousands of lines of explanatory comments but still don’t understand exactly how it works. Today that program is the front-end to Google Search.

It is left as an exercise for the reader to scale the web server to able to handle Google search load.

Source codes for this post is available at

For people who thinks that this is useless: yes it is useless. I just happen to like this challenge, and it has refreshed my memory and knowledge for the following topics: shell code writing (haven’t done this in years), AMD64 assembly (calling convention, preserved registers, etc), syscalls, objdump, fini_array (last time I checked, gcc still used .dtors), printf format exploiting, gdb tricks (like writing memory block to file), and low level socket code (I have been using boost’s for the past few years).

Update: Ubuntu adds a security feature that provides a read-only relocation table area in the final ELF. To be able to run the examples in ubuntu, add this in the command line when compiling



gcc -Wl,-z,norelro test.c

Raspberry Pi for Out of Band Linux PC management

Just a day before I left to Indonesia for my brother’s wedding, I got worried about my headless Linux PC server: it may freeze when I left it. It happened before because of kernel panic and hardware error, it can happen again. I want to be able to reset the PC in case of errors and to power it down in case the error was not recoverable (for example last year my disk drive went bad).

Just a note before reading this: in case you just want to turn on or off a PC using raspberry PI: just use wake on LAN (WOL) to turn on your PC and SSH access to turn it off. Wake on LAN works most of the time, but it can not handle a PC that is not responding.

I soldered 2 optocouplers that I have (4N25) to a small perfboard with a pin header. Then I use solderless breadboard cables to connect the board to Raspberry Pi, and to the PC power and reset button (so I can manually turn on/off using the power/reset button on the PC).


Watch carefully about the + and – on the motherboard (PW is for power, and RES is for reset, note the polarity is important for the optocoupler, on the picture above: Red goes to + and Black goes to -):

I could just have used one optocoupler to connect to the power button (the reset is not really necessary, because we can turn off and on the PC again to reset), but I just want to use the extra 4N25 that I have (it’s really cheap, 5.5 baht or around 17 cents USD).

To reset the PC, I just set the GPIO pin to high for about one second, then set it low again. To power up the PC, I set the GPIO pin to high for about 5 seconds, and set it low again (the same can be used to forcefully power down the PC).

Resetting and powering the PC is easy, the next task is to know what happened if the PC crashed. To do this, I need a serial connection. If my PC has a serial port and I have a USB to serial cable, then everything will be much easier, but since I don’t have a USB to serial cable, and my PC doesn’t have a serial port on the back, it gets a bit complicated.

I still have a small board based on MAX3232CPE to convert from 3.3V serial to 5.0 V, so I plugged that board to Raspberry Pi and connected it directly to the PC motherboard. This page helped me in finding the pin names (I only need to connect RX, TX and GND).


On the Raspberry side, I need to set up so that it will not use the serial port for kernel output and log in. You can follow the guide here.

On the PC side, I need to activate serial output in three places: to accept login (getty), to get the kernel output (kernel parameters in grub), and in grub itself (to show the boot selection dialog). This guide for Debian works for me, but I was not able to see the GRUB output on screen when I connected my screen (I can only see the output on my serial console, but this was not a problem for me).

I experimented a little bit with SGABios Hoping that I would be able to see BIOS output from my serial port. It didn’t work as expected. I can not see the initial BIOS screen, and I can not send a key to enter BIOS setting, but If I connect a keyboard and press a button to enter the BIOS, I can see the BIOS menu via serial port and I can interact with it.

Here are the steps that I tried to get the BIOS serial output: I downloaded the BIOS for my motherboard (an AWARD BIOS). Then on a windows machine, I modified the BIOS using CBROM cbrom bios.bin /isa sgabios.bin. Then I flashed the BIOS from Linux using Flashrom.

I didn’t solve the BIOS problem due to time constraint. There are several solutions that I can think of to solve this: one is to use CoreBoot (but unfortunately my motherboard is not supported by coreboot), another one is to try to do more hacking on the BIOS (maybe removing the VGA ROM to force the output to serial port) and the other one is to simulate a keypress to enter BIOS. The first two methods may not be portable across BIOS, but the last one should be portable. The key simulation can be done by simulating a PS2 device (using bitbanging on Raspberry Pi), or USB HID device. A super simple USB HID device can be made by using V-USB library (you can see this as an example).

Just a few hours before I left, I have an idea to connect a temperature sensor just to see if the temperature around the PC case is too high. We are entering the summer here in Chiang Mai and the outside temperature is getting higher every day (from November to beginning of February, the temperature was around 8-20 Celcius, and now it is around 17-37 Celcius). It was quite easy to add the temperature sensor, I just use the guide and driver from adafruit. Next time I may add an infrared LED on the Raspberry Pi to turn on the Air Conditioner when it gets really hot.

Having everything setup: nothing happened while I was away. The PC was running nicely (and I can access the PC via SSH and the serial console).

RFID based toy/game for toddlers


Inspired by this toy fromm LeapFrog that we got for free on a yard sale, I made this toy for my son:


This is a simple toy, he can pick a card from this set of alphabet cards:


And put it above this device:


And then the alphabet will be shown on the screen:


the alphabet is received by Raspberry Pi via bluetooth and displayed through HDMI:


Why wireless? I want to have distance between the device and the TV screen. I could have just used cables (I can even plug the RFID reader directly to Raspberry Pi), but it is not toddler-safe. My son would occasionally run to the TV screen to point at something, and I don’t want him to trip on the wires.

The implementation is quite simple. The cards are actually RFID cards (50 cards for $11.47), and it is read using this cheap 9.9 USD RFID reader. To make the card looks good, my wife prints the letters of the alphabet to a sticker paper and sticks them to the cards.


And to make the data available to the Raspberry Pi, I used the same Bluetooth module as the one I used in my previous post (you can find a similar one here). The baud rate for the RFID reader is 9600 bps, so we need to set the same baud rate for the Bluetooth module.


For the power source, I could have used AA batteries, but I have this USB powerbank (that also acts as a USB/Wifi router) that I don’t use very often:


I didn’t do any soldering for this project, I used a breadboard


And in case you are wondering, I just use this device (5v to 3.3v serial converter) to connect the USB power to breadboard (just because I don’t want to solder anything, and this device fits nicely):



For the software part, I wrote a small python script that uses pygame.

To prepare the raspberry pi to run the app, you need to install these packages:

sudo apt-get install python-pygame python-serial bluez-utils sox

Then find the device bluetooth address using:

hcitool scan

Create a file named “pincodes” to enable automatic pairing:

echo "DEVICEADDRESS PIN" >> /var/lib/bluetooth/YOURMACHINEADDRESS/pincodes

The default device PIN is 1234. For example, this is what I do in my laptop:

echo "00:12:03:09:17:55 1234 >> /var/lib/bluetooth/E0\:B9\:A5\:45\:15\:1B/pincodes

And for the serial connection, create /etc/bluetooth/rfcomm.conf file:

# RFCOMM configuration file.

rfcomm0 {
	# Automatically bind the device at startup
	bind yes;

	# Bluetooth address of the device

	# RFCOMM channel for the connection
	channel	1;

You can checkout the source code at github:

git clone git://

I don’t have a license to redistribute the wav files for the alphabet sound that I own, but fortunately you can find a collection of wav files from Voxeo site: (download

To be usable in pygame, you need to convert the format to raw 44.1Khz WAV using sox:

cd rfid-abc
wget -c
unzip -d original
cd original
for i in *.wav; do sox $i -r 44100 -e un ../$i; done
cd ..

And to run it:


Oh wait, you need to edit the card id mapping in map.txt, in case you didn’t touch the file the app will store unknown card ids to “unknown.txt”.

Future improvements

The software is still very simple. I am planning to make it multilingual (my son needs to know Indonesian, English and Thai), and more interesting (for example: the computer can ask “find me the letter C” or it can be changed into a spelling game).

Adding Bluetooth Serial Port to Asus RT-N16

I am running DebWrt on my Asus RT-N16 and it works well. The only problem that I have is: in case I misconfigure something and the device is inaccessible via the network, I need to open the case then connect a serial port to fix it. Because the configuration is in USB, I don’t have to open the case very often, in most cases, I can unplug the USB disk, mount it in my Linux machine, try to fix the configuration, plug the USB again, restart the router, and hope that my fix works. Either way, both are such a hassle.

I could have added a serial port just like my DIR-300 mod, but I think it’s not the best solution. Because I still need to bring down my router, find my serial cable, plug it in and connect to it. I wish that the device has a Bluetooth capability, so I can connect to it (via Bluetooth serial port profile), fix any problem that it has, and without moving or plugging anything, and hopefully I don’t even need to restart the router and wait for it to boot.

So I bought a 7.32 USD bluetooth module from Aliexpress and installed it on my RT-N16. Some of you may think that it is a bad idea because Bluetooth interferes (somewhat) with WIFI, but I don’t plan to keep constant connection via Bluetooth, and when I do make the connection, the data that I am transferring is very small (maybe just several kilobytes per minutes). So far in my testing, when connected via Bluetooth, I didn’t notice any speed difference in WIFI transfer speed (even when transferring large files via WIFI) and typing furiously from my Bluetooth terminal. Asus RTN16 only supports 2.4 GHz, but If your router supports 5GHz, I think you should use that band to eliminate any possible interference.

Asus RT-N16 has a serial header ready to be connected (it even has labels on it, GND, RX, TX, VCC):


But before plugging in the module, I need to set the speed of the Bluetooth serial module to 115200, because the default speed is 9600. To set up the Bluetooth serial module, we need to connect it to a computer via serial port (I am using Bus Pirate for this).

My version of Bluetooth module is H-C07, and for this version, the device doesn’t use \r or \n to terminate command, it just uses time out to end a command (a complete command must be received within few hundreds millisecond). Typing very-very quickly in your terminal won’t work, so just copy and paste the command from your text editor. The command needed to set the Bluetooth module to 115200 is “AT+BAUD8”. These Bluetooth modules usually don’t come with documentation, so you need to look on the internet for your specific version.


One of the nice thing about Bluetooth is: it is accessible from non-PC devices. After connecting the cables, I can connect to my router using Android Bluetooth SPP

Using Bluetooth SPP as console on Android

One thing to note: the Bluetooth module needs time to initialize, so it can not be used for accessing the bootloader. To restart the router, I need to plug and unplug the power cable. So the Bluetooth module will lose its connection when I restart the device. By the time the Bluetooth module is ready, the bootloader has already passed and you will be in the middle of Linux booting.

If you really want to use the Bluetooth module to access the bootloader, you will either need a separate power source for the Bluetooth module, or make a special reset button for the router (that doesn’t involve unplugging and plugging the device, and doesn’t cut power to the Bluetooth module).

LocalBar: Install signed BAR files directly from PlayBook

I’ve reverse engineered the protocol used by blackberry-deploy to install apps file (BAR file) into the playbook. Then I made an app to Install signed BAR files directly from the playbook itself. You can find my work here:

  • I am using https://localhost method. To put it simply: it works like other desktop installers that connect via network or USB, it sends commands to an HTTP service in the playbook. The only difference is that it works through the playbook itself.
  • It is possible that in the future RIM may block requests from localhost
  • I don’t have time to develop nice GUI for this, so I just use the basic GUI API that is accessible using NDK. For example: in the NDK there is a “login dialog” but no “password dialog”, so for the password dialog I use the “login dialog” that shows the “user” field (which I don’t need).
  • This works on OS 1.0.7 and on 2.0 (developer beta)
  • With this you can sort of OTA install through the playbook. From your PlayBook Just go to a website that has some bar files (for example this forum) , download it using the built in playbook browser, then run LocalBar to install the downloaded bar files.

EZ430-Chronos OTP

After wanting the EZ430 Chronos watch for a long time, I finally ordered one on Febuary 20th from TI eStore, and I got the watch on February 24th (Tax Free). So this is another stuff in my long list of “things to hack”.

I had a good idea to use my Ez430 Chronos as OTP generator for Google 2 factor authentication. Before my long weekend, I did my research on Thursday (24 February) and that time no one had implemented it. So I wrote a small modification to OpenChronos, and just before I finished my implementation on Sunday (I was quite busy during the long weekend helping to move our company’s office), I looked at Chronos Wiki again to find some links to the chronos documentation, and found out that Huan Truong has just implemented his version of OTP by modifying OpenChronos.

After learning that in his version the clock function doesn’t work yet (in his readme it says “THIS FIRMWARE CURRENTLY HAS A YET-TO-IMPLEMENT CLOCK FUNCTIONALITY, SO IT WONT DISPLAY TIME PROPERLY”), I decided to continue my implementation. My implementation doesn’t change the time logic so you can still use the stock Control Center provided by TI (Huan Troung changed the OpenChronos code to use epoch implementation, and he modified the control center) . Instead of replacing all algorithms to use timestamp, I use a simple mktime implementation to convert existing year/month/date data to unix timestamp.

After flashing the image to the watch, a new menu is added to the second line after “rFbSL”, it will show a heart icon and first 2 digits of the OTP (I will never buy a heart monitor for this watch so I use that icon just to show that I am in OTP mode). Pressing the “#” key for a few seconds will show the remaining 4 digits. Just for your information, enabling CONFIG_OTP adds 2914 bytes to the code size.

So here is my version of Google OTP (If many people are interested, I can put it in github):

I am too lazy to implement the “make config’, just edit otp.h with your key, and fill in the timezone offset (+N from UTC). You can get the key from base32 encoded string using codegen script that I made, for example:

bash$ python pf xwqy lomvz wu 33f

You can use make config to set your secret key in base32 (that means you can just copy paste from the auth code presented by Google), and you can set the timezone offset.

New Adventures

My last post was about 6 months ago. Now I am back with some new adventures. The first one is Jonathan, my first baby:


And the next one is BeagleBoard-xM from John Nicholls.


About a month ago I found a promotion and got this free MSP430 USB development tool:


It got me interested in MSP430 in general and bought some LaunchPad (only 4.30 usd each). My first project is to control the plug so i can plug and unplug BeagleBoard-xM through PC (so I can control it remotely via SSH). With this, I should be able to work on BeagleBoard remotely (like when I am in my room holding my baby boy).

LaunchPad MSP430

And I have updated the CNS21XX code in my gitorious repo with the latest head. Hopefully I can put the code to SVN HEAD in the near future.

CNS21XX port completed

About six months ago, Stefan Bethke donated me some money to buy a device from dealextreme so I can port FreeBSD to that device (you can see the pictures here). This device uses ARM Cavium Econa CNS21XX (formerly known as STR8132). Within few days I have completed the driver for serial port, interrupt controller, EHCI/OHCI. Then I stopped working on it, three months later I continued and finished the network driver, then I stopped again.

The last part that wasn’t finished was the SPI controller and the SPI flash driver, so this weekend I spent some time to finish it. So now, I can say that the port is finished, all drivers have been written for the device. With SPI flash support, I can now write the kernel to the device, and boot it from there (I don’t need to boot from network anymore).

Actually, I am not really finished yet, since I still need to reformat the code according to the FreeBSD standard, and there might still be bugs in my code, so I invite everyone that has this device to try it out. There is also a feature in the network driver that is not implemented yet (multicast filtering), because the datasheet is not very clear ( I would be very happy if someone could help me to complete this, wait now I suddenly understand the documentation).

For the bootloader, I am still using the default boot loader. This bootloader will load the kernel from memory 0x600000, and since I can’t change the bootloader configuration in this particular device, I modified the kernel configuration to match this. The latest code can be accessed at

To do initial boot, you will need a serial port. You will need to put your kernel on your tftp server. Hit any key during boot, and type:

setenv serverip
setenv ipaddr
tftpboot 0x600000 kernel.bin
go 0x600000

and to make it permanent:

dd if=kernel.gz.tramp.bin of=/dev/flash/spi0 obs=4k conv=osync seek=96

Please note that the block size is 4k, and 96 means the offset is 0x60000 (96*4096) which will be mapped to 0x600000 by the bootloader. If you are brave, you can just compile the image and dd using the default Linux, but I don’t recommend this, since you may have different hardware (especially SPI flash chip).

Another news: I have completed the driver for ThinLinx Hot-e NAND using NAND2 framework. I also completed the SPI part and support for the flash SPI (read-only).

D-LINK DIR-300 Serial Port and SD mod

The latest progress of my freeBSD port for CNS21XX and ThinkLink Hot-e was three weeks ago. The CNS21XX network driver and Hot-e network driver was completed. I haven’t touched anything since then because I had to work on weekends at the office. This weekend, I could have continued coding, but I don’t feel like coding, so I did a hardware project: adding serial port and SD card slot to my D-LINK DIR-300 that I bought April last year.

This is not a difficult project, I already added SD/MMC card to my WRT54GL about 2,5 years ago. The difference is that there isn’t much guide about the hardware part (which GPIO pins to solder), and the software part (how to activate the drivers). So here is a short guide to anyone who needs it. Note that I already installed OpenWRT Kamikaze using the guide from: OpenWRT site.
Continue reading “D-LINK DIR-300 Serial Port and SD mod”

ThinLinx Hot-e and CNS21XX

I am still fixing the Cavium Econa CNS11XX network driver with the guide from Pyun YongHyeon. He is guiding to make the network driver more robust. Unfortunately, we still don’t know why the driver is slower than the Linux version. The port is currently accessible through FreeBSD CVS at:

I asked in the freebsd-arm mailing list if anyone would like to donate me a CNS21XX device, and Stefan Bethke immediately offered me to buy one for me. I bought the device from dealextreme with the money donated to me. It took 9 days until it arrived.

Meanwhile John Nicolls from ThinkLinx sent me a Hot-e, a device based on AT91SAM9G20. I told him that I have received mr Stefan offer, but he said I can work on it anytime I want it. Since the Hot-e arrived earlier, I have managed to get it to boot. At first i was going to use the work from Sylvestre Gallon on at91sam9621(mailing list archive), but it turns out that it is not usable yet.

I have fixed the clock computation in at91_pmc.c, and currently writing a new timer driver (at91_pit.c), because the system timer device (at91_st.c) no longer exists in AT91SAM9G20. Currently the timer device is still not working properly, but I think I will be able to get it work this week.

When the CNS211XX LAN device finally arrived, I stopped the work for Hot-e for a while to test the new device. I bought a CA-42 cable to connect to it, but I can’t send anything to the device. I thought that the device was faulty, but turns out that the cable is faulty. I was disappointed because usually I used that type of cable (it is cheap only 132 baht or 4 usd). Fortunately I still have one MAX3232, and I can make my own cable.

I think porting CNS21XX will not be so difficult. I made some small adjustment for the serial port driver to make it show something. Memory mapping is different compared to CNS11XX, but that can easily be adjusted. The Interrupt controller is different, so I need to rewrite some parts. After it works, EHCI/OHCI was working fine. The network driver will need major adjustment (may be I will just write a new driver for this one), and the device doesn’t use CFI for Flash, it uses SPI, so I will need to write a driver for SPI bus.

For CNS11XX and CNS21XX You can see my progress in this wiki page: