A new year is upon us, and a new version of slackware. I've decided to come back to a project long abandoned, which involves hacking the Sweex router. Although I've got replacement firmware working before, never with Slackware 11.0, so here goes.
I'm running on Slackware 11.0. I won't go into this, it's a pre-requisite I'm afraid.
First problem - USB->RS232 dongle. Plugged it in and it was recognised. Slackware puts it somewhere funny: /dev/tts/USB0 (or USB1...). Minicom, very nicely doesn't tell me that it couldn't open device /dev/ttyS0, and I sit there wondering if my serial level converter is broken. The place that sold me the converter is www.jabsplace.co.uk:
|
I can recommend the price, but not the device. Unfortunately the power LED only comes on for 5v operation, not 3.3v, so I saw nothing and immediately suspected wiring, or anything but minicom.
So now minicom correctly configured and looking at device /dev/tts/USB0, and I'm seeing the ADM5120 boot nicely. It won't respond to key strokes though. After scratching my head, and (again) getting out the multimeter I worked out it was hardware flow control. Turned it off and two-way communication can commense.
I created a cable for the level shifter, which consisted of an old motherboard-to-serial port ribbon cable (old motherboards used to do this before the time of standard back-panels), with the 9-way 'D' connector removed and the cables connedted to a small piece of stripboard with a 0.1 inch spaced 4-way socket:
|
Now I needed a firmware. I tried the Tom Wilms instructions again (12/1/2007) and they still work with an up-to-date buildroot version. I decided to mirror the instructions, and the equally important ADM5120 patch, just in case.
Now I'm remembering what a pig it was to XMODEM stuff onto the board. Today is no exception, because I'm using Slackware 11.0, which has minicom configured to use a program called 'sx' to send data. Unfortunately this program name is taken for something else (osx), and nothing happens... doh! It's OK, though because the lsz program (which sx is normally just a link to), also takes an -X argument to tell it which protocol to use. I decided I'd better take a break and investigate a way of automating the upload though.
Here is the script: adm_tx.py. You can run it once for usage instructions:
usage: adm_tx.py [options] <filename>
Waits for the ADM5120 to power up, then transfers the specified kernel
image to the DRAM/FLASH. adm_tx.py will try to remind you if you're writing to
FLASH ram and you've forgotten the CSYS header. It can also create a
temporary file adding/removing the CSYS header as it does so. You need to
1) Switch off the router, 2) Run this program then 3) Switch on the router.
options:
--version show program's version number and exit
-h, --help show this help message and exit
-d DEVICE, --device=DEVICE
Communication device (default: /dev/ttyS0)
-b, --burn 'Burn' to flash (default: load to SDRAM)
-f, --force Try to fix the image so it matches the action, i.e.
add/remove CSYS header as necessary. (default: No)
The program should dump you into an interactive console when it's done, and not a minicom in sight!
With that slight interruption out of the way, I now need to drive a 7-segment multiplexed display from the router. I figure I had better get the LEDs working first.
Now I need to add wget to the list of programs in my basic root filesystem. I went back into buildroot and typed 'make menuconfig'. This is the bit Tom missed out. After that, saving the new configuration and running 'make' resulted in wget being downloaded. Not sure I like all these build scripts that access the internet, but I suppose we have to put up with it while buildroot is the only thing which works.
Going back into linux/usr and re-mounting the root fs gives me wget. Sweet. Less sweet is that my hand-crafted configuration files have been overwritten in the (re) mounted root file system. This will quickly get irritating, so I decided I'd better detour again to make this easier.
Back from the detour I now have another utility which performs the steps documented by Tom on the mounted rootfs. Again run it for usage.
Now it's time to build the kernel+image again:
make vmlinuz
There is some re-linking and the romfs gets re-absorbed.
Now I want some network by default. According to the busybox documentation the inittab script is used to run commands on startup it seems. I'm happy with busybox init for what I'm doing, I just want to run a program (my embedded 'app') so this will do fine. I added two lines to /etc/inittab to setup the network:
null::sysinit:/sbin/ifconfig eth0 10.0.0.44 null::sysinit:/sbin/route add default gw 10.0.0.2
Fixed IP address. I don't see any point in giving embedded systems dhcp, since you can't find the buggers! Don't know if I'll need the default route, but never mind.
With networking up and running and wget, I can have a look at writing a C program but first I need to access those LEDs. Back to the adm5120 patch for the 2.6 kernel, and a look in adm5120_gpio.c shows a define:
#define LED_MINOR 151
Aha... So I need a device of minor number 151, that's a start. What about the major number? Nowhere to be seen, however the source file includes miscdevice.h, so I think this has the same major as misc (10). Now to put this into practise:
mknod usr/root/etc/gpio c 10 151 make vmlinuz ./adm_tx.py
Uploaded and it works fine. Now I've got the device I should be able to write something to it:
echo ff > /dev/gpio
With this, the LED went off.
Yup. Looks like I'm controlling the LEDs. It seems they default to output. I heard there's a file called led_app.c somewhere which I must find. Seems it's in a 35MB download... doh!
Had a look at led_app.c, but it's for the 2.4.32 Amilda driver. It uses ioctls. I'll need to change it to drive a 7-seg display, so I decided to go my own way.
OK, Now back to the driver. It looks kinda limiting so I adapted the driver so it writes to arbitrary addresses in the ADM5120 memory space so I can investigate things.
The driver now receives an 8-byte ioctl message containing two 32-bit values - the first the address, the second the value. I tested the error cases and it works, but I can't send unsigned long values using shell, so I'll need to write a C program. I also made the driver work on 386 systems for testing (in case you're wondering).
Here's my 'hello world' makefile:
all : main
main : main.c
mipsel-linux-gcc -o main main.c
clean :
rm -f main.o main
And here's the program, main.c:
#include <stdio.h>
int main()
{
printf("Hello world\n");
}
Setup apache on my slackware machine:
joe /etc/apache/httpd.conf (to change DocumentRoot) chmod 755 /etc/rc.d/rc.httpd /etc/rc.d/rc.httpd start
Apache running, now copied the compiled program over to the /tmp directory on the Sweex. It ran. So far so good, but it doesn't access the LED driver device..... yet. All this copying is OK, but is going to get annoying.
Although the display requires 12 lines, of four of them only one will ever be active at a time, so I can reduce this to 10 with one decoder chip. I decided this is what I'll do, and it'll also reduce the amount of wiring to the board. 10 easily accessible LED connections, and two (also easily accessible) button connections. I don't think I need anything to set the alarm - that'll be done from my laptop. I decided I'd better make a connector just in case I trashed the router and needed to swap it out for another one. I used a 20-way header, with 10 data lines on one side, 8 earth lines, one 15v and one 3.3v line on the other side. For the cable I used an old floppy drive ribbon cable, and spliced out all the cables so they'd be more flexible. I chopped off the wires I didn't need.
|
I used to have a very nice working Audioline RTT-1, timetrak clock from Argos. It did everything I needed, ran from mains, corrected itself from the MSF time signal had a radio and a nice loud alarm. Sadly, it went wrong, but lucky for me I kept it.
|
Note: No AM/PM dot needed, this is now 24H! There was a lot of electronics inside the thing, so it was a nice large size. I took out pretty much everything apart from the display support. I also had to remove a few pillars, thus reducing the structural strength, but I figured I'm not going to be throwing it around the room, so I'll be OK.
I wired up the 7-segment display control board. This uses a 4511, which was from school days (purchased in the 80s and still good). One downside: It uses old-fashioned 9s and 6s which don't have tails. Pity. I also used a bunch of BC338 NPN transistors which I bought in a batch off Ebay. They have 10k series base resistors, and 10k pull-ups. This drives the 4511 inputs. The 4511 segment anode output is limited by 1k series resistors and the common LED cathodes are sunk into an open-collector arrangement of another 5 BC338s, again with 10k base resistors. There's a spare transistor in the picture, which is for the alarm (when I wire up the buzzer, and augment the software)
|
I had to file out openings for the ethernet, switch, and power, and also get rid of the internal backup battery compartment.
|
Not much use for the volume or tuning controls, and with nowhere inside to mount them they had to go:
|
|
Finally, it almost looks good as new
|
The software now syncs to an NTP clock. I'm going to have to work on ways of syncing to alternate clocks if the one I'm currently using goes down. Re-sync is every 12 hours. This was really easy to achieve using ntpdate, which comes with buildroot in the ntp package. ntpd would have been nicer, but it gave a bus error when ran for some reason. I have the following script, called imaginatively 'setdate' to run it:
#!/bin/sh /usr/bin/ntpdate ntp2c.mcc.ac.uk /bin/sleep 40000
Worth noting here, that busybox sleep does not support the 'm', 'd' or 'h' suffix to the sleep period, contrary to the documentation. Only seconds works 40000 = 12 hours approx. You can get lists of local time servers from searches of the internet. Your ISP is very likely to have one (but mine didn't). You can also use rdate which comes with busybox for setting the time from the network, however it uses the 'rdate' protocol which seems to be frowned on by the ntp community. You will need to set your timezone for any of this to work correctly. You do this by putting the text GMT0BST in /etc/TZ if you're in the UK or whatever other piece of arcane crap some standards people have decided will represent your local timezone if not. Lash, the busybox shell does not support while loops, so I decided to let inittab do the loop. I added the following entries to /etc/inittab:
null::respawn:/sbin/setdate null::respawn:/sbin/timed
The first line will run setdate (which I put in /sbin) on boot, and when it quits (some 12 hours later) it will run it again (respawn it). timed.c is the simple program which updates the kernel module (and therefore the display) with the system time.
Here is the final version of the driver, which updates the display. I had to recompile the kernel with disabled pre-emptive scheduling for this to work without error, or I'd occasionally get hiccups in the display. I almost considered leaving these in for 'geek' value since they were quite interesting but...