niedziela, 26 maja 2013

custom kernel + rootfs

Custom firmware


The current test firmware version supports 16MB ram / 4MB flash units only. Following features are available:

- Based on Linux version
- rt2800usb wifi network support (Ralink idVendor=148f, idProduct=3070)
- PixArt cmos usb camera (idVendor=1d0f, idProduct=1801)
- jffs2 writable root filesystem (2MB)
- jffs2 writeble user/data filesystem (~500KB)
- experimental software spi bus + sd/mmc, slow but enough to save camera snapshots, stream mp3 files etc.
- v4l camera interface
- mathopd http server (with cgi support)
- simple login subsystem (users account / passwords)
- inetd
- telnetd
- ftpd
- userspace binary to controll horizontal/vertical stepper motors

The complete camera firmware is divided into 3 images:

  •  linux kernel image

md5sum: d4433c1c74a45650602ef3dfcb86df5a  zImage

  • root filesystem

md5sum: da562a3fc82655ea4d25d4ceadff1865  romjffs2.img

  • user filesystem (empty/pre-allocated fs mounted over /data, useful if you haven't done the sd/mmc mod yet)

md5sum: 4d4fe44522508b09381ddb39ad9a61f8  userfsjffs2.img

Warning: Proceed at your own risk, ONLY if you are 100% sure that you have a valid backup and know how to perform the restore operation. There are many variants of those so-called ipcam/foscam clone cameras and not all are compatible. Also, this firmware is an early test version only, I can NOT be held responsible if something breaks, melts, burns as a result of using it !

So delete existing flash partitions (except the Image 0), setup a DHCP server on your host machine and upload each new image using the following commands:

Kernel image:

ft 1 linux 0x7F020000 0x00008000 -acx

Root fs:

ft 2 root 0x7F170000 0x7F170000 -f

User fs:

ft 3 userfs 0x7F380000 0x7F380000 -f

Instead of using dhcp/tftp "ft" you can use "fx" command and upload via serial cable - it's much slower (helps in case you don't have dhcp/tftp utilities)

When uploading individual images, one should see messages like below:

bootloader > ft 2 root 0x7F170000 0x7F170000 -f
Wait for auto-negotiation complete...
100MB - Full
IP Address. . . . . . . . . . . . :
Subnet Mask . . . . . . . . . . . :
Default Gateway . . . . . . . . . :
Waiting for download ...
TFTP client:
Download OK, file size:2097152       

Flash programming ... 

When all files are successfully uploaded, the bootloader image table should look like this:

bootloader > ls
Image: 0 name:BOOT INFO base:0x7F010000 size:0x00000048 exec:0x7F010000 -f
Image: 1 name:linux base:0x7F020000 size:0x0013B358 exec:0x00008000 -acx
Image: 2 name:root base:0x7F170000 size:0x00200000 exec:0x7F170000 -f
Image: 3 name:userfs base:0x7F380000 size:0x00070000 exec:0x7F380000 -f

If that's the case, issue the "boot" command, hopefully after a while on the console the boot messages should be visible:

usb 1-1: new full speed USB device using nuc700-ohci and address 2
usb 1-1: New USB device found, idVendor=1d0f, idProduct=1801
usb 1-1: New USB device strings: Mfr=16, Product=96, SerialNumber=0
usb 1-1: Product: USB2.0_Camera
usb 1-1: Manufacturer: PixArt Imaging Inc.
uvcvideo: Found UVC 1.00 device USB2.0_Camera (1d0f:1801)
VFS: Mounted root (jffs2 filesystem) on device 31:2.
devtmpfs: mounted
Freeing init memory: 76K
input: USB2.0_Camera as /devices/platform/nuc700-ohci/usb1/1-1/1-1:1.0/input/input0
usb 1-2: new full speed USB device using nuc700-ohci and address 3
usb 1-2: New USB device found, idVendor=148f, idProduct=3070
usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-2: Product: 802.11 n WLAN
usb 1-2: Manufacturer: Ralink
usb 1-2: SerialNumber: 1.0
starting userspace: custom_cam  kernel v1.0, rootfs v1.0

At boot time both eth0 and wlan0 are enabled with these ip parameters:

 / $ ifconfig
eth0      Link encap:Ethernet  HWaddr 00:02:AC:55:88:A8 
          inet addr:  Bcast:  Mask:
          RX packets:1 errors:0 dropped:1 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:16
          RX bytes:60 (60.0 B)  TX bytes:0 (0.0 B)

wlan0     Link encap:Ethernet  HWaddr 00:E0:4C:B4:61:01 
          inet addr:  Bcast:  Mask:
          RX packets:3 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:397 (397.0 B)  TX bytes:423 (423.0 B)

 The wlan0 interface will try to associate with a wireless network (WPA) sid="wifi1" using password="secret123". All the network init can be adjusted by editing /etc/ and /etc/wpa_supplicant.conf files. Pay attention to the non-standard network mask  (heh, yes that's my home setup).

If all went correctly, your camera system can be accesed by:

  • telnet / ftp - there're two initial accounts (root and pwoz, password is 1q2w3e for both users)
  • web browser - default URL: - a test page to move the camera to a desired direction and get the image snapshot. (it's intended for test only, don't use it too much - every snapshot is saved to jffs filesystem so it might put additional strain on the flash chip)
To control the stepper motors directly, use "/bin/mygpio" binary, for example: "/bin/mygpio -r 10" to move 10 horizontal steps to the right (clockwise direction).

czwartek, 14 marca 2013

IP-Cam / Cool Cam (Foscam clone) hacking - custom firmware (draft version)



The foscam-like pan/tilt camera is a nice little gadet, for little money one can get an interesting linux-based embedded platform.

Hardware features:

- ARM7TDMI CPU clocked at 80 MHz
- 16 MB RAM
- 4 MB Flash
- Ralink 3072 WiFi module (rt2x00 compatible)
- 10/100 MBps Ethernet
- Two unipolar stepper motors (Horizontal/Vertical)
- PixelArt USB camera (640x480)
- 10 IR LEDs used for (a kind of) night vision
- Realtek audio chip

There are already a lot of sites covering the same subject so there's no point in reinventing the wheel. What makes this project different is the attempt to implement:

  • Stepper motors control
  • MMC/SD card over software-emulated SPI bus


  • No MMU 
The CPU ARM7TDMI doesn't have a MMU unit (memory management unit) - so the normal/real Linux operating system can't be used, the only solution is "uClinux" - which has some further implications like for instance: no virtual memory, all processes share the same linear address space, no memory protection, various issues with mmap(), no fork() function etc.

  • It's slow...
The rough estimate is only about ~39 BogoMIPS. In other words: no mp3 playback, motion detection will have to be clever and simple.

Recommended reading:

There's a very useful forum dedicated to foscam-like devices, I strongly recommend reading it, especially the below thread:

Tool-chain & Kernel 3.x  (even though I'm using 2.6.x - but the basic rules are the same),204.0.html

Also, in order to change the firmware to a custom one a soldering iron is needed, the procedure is described here:

Base Kernel:

This firmware project is based on Linux Kernel version 2.6.38, ported to ARM7 by Wan ZongShun, who kindly made ARM-specific files available here:;a=summary

Some hacks and ideas were also "borrowed" from the forum user "robert-qfh", his git repository:

In addition to the above, some additional changes were required to support the platform hardware features, below you can find the result of my efforts.

flat.h - patch to get rid of nasty/random "bad relloc" errors in userspace apps


change line 10 to:

#define flat_reloc_valid(reloc, size)           ((reloc) <= (size)+0x1000)


WiFi driver rt2x00 - apparently the hardware encryption is broken and should be disabled


The internal USB-WiFi module identifies itself as a Ralink 3070 802.11n device, dmesg output:

usb 1-2: New USB device found, idVendor=148f, idProduct=3070
usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-2: Product: 802.11 n WLAN
usb 1-2: Manufacturer: Ralink
usb 1-2: SerialNumber: 1.0

Normally, it should work well with the default rt2x00 drivers bundled with the 2.6.38 kernel wireless section, but as it usually happens, some additional tweaks are needed to make it stable.

Disable power saving:

     * Disable powersaving as default on PCI devices.
    //if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev))
        rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;


By default powersaving is disabled only for PCI devices, comment out the above "if" statement to disable it completely

Disable hardware encryption:

With hardware encryption enabled (default state) the wifi can be successfully initialised, associated with an AP, but it's unstable - missing ARPs, slow pings etc.

To disable hw_crypt: edit rt2800usb.c file:

set the following parameter:

static int modparam_nohwcrypt = 1;

and/or (to be really sure):

comment out that statement:

    if (!modparam_nohwcrypt)
        __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);

In addition, you may also try playing with the TX queue size, it affects both performance and stability (google for the infamous "missed TX status report").

static const struct data_queue_desc rt2800usb_queue_tx = {
    .entry_num        = 128,
    .data_size        = AGGREGATION_SIZE,
    .desc_size        = TXINFO_DESC_SIZE + TXWI_DESC_SIZE,
    .priv_size        = sizeof(struct queue_entry_priv_usb),

By default the .entry_num is 128 which IMHO is bit too high for a slow system - it seems there is much less unnecessary overhead when max queue length is reduced to 8/16.

Also, one thing worth mentioning with regards to disabled hw encryption. Basically it means that all encryption must be handled be the main CPU which has a big negative impact on the overall system performance when WPA/WPA2 method is used. For this reason the CCMP encryption should not be used (it would eat all spare CPU cycles, instead use the TKIP both for group and pairwise packets. It can be enforced in the wpa_supplicant.conf file:


Stepper motors control from within userspace application


Stepper motors are connected to the CPU GPIO space via two additional ICs:

- ULN2803 (8 x Darlington transistor array) it's used to switch on/off an individual coil, each motor has 4 coils.

- 74HCT259 - Configured to act as an addressable latch, three address lines A0, A1, A2 are used to select the desired output logical state (low/high). Apparently, the LE (latch enable) pin is hardwired to GND which is far from being perfect as it may introduce some transient "glitches" when address lines are changed.

CPU -> 74HCT259 mapping:

GPIO PORT5 output register (0xFFF83058):

bit2 = A0
bit3 = A1
bit4 = A2
bit5 = D (data pin)

I'm using the following code, it's just for testing purposes. In long term it should be cleaned and rewritten as a kernel module.

#define inpw(port) (*((volatile unsigned int *)port))
#define outpw(port,val) (*((volatile unsigned int *)port)=(val))

//set stepper & led GPIO cfg
void cfgGPIO()
outpw(0xFFF83050, inpw(0xFFF83050) & ~0xFF0); //PORT5 GPIO: 2,3,4,5
outpw(0xFFF83054, inpw(0xFFF83054) | 0x3C); //PORT5 2,3,4,5 output

outpw(0xFFF83040,  inpw(0xFFF83040) & ~((1<<20) + (1<<21)) ); //  led port4 gpio
outpw(0xFFF83044,  inpw(0xFFF83044) | 0x400); //PORT4 output 4

// count - number of steps
// h_or_v - select vertical or horizontal motor
// dir - select direction, 0 - normal, 1 - reverse (left/right, up/down)
int step(int count,int h_or_v, int dir)
unsigned long port = 0xFFF83058, ledport=0xFFF83048;

unsigned long tab1[] = {0,4,8,12,16,20,24,28};

unsigned long tabV[] = {0,4,8,12};
unsigned long tabH[] = {16,20,24,28};
unsigned long *tabptr;

int l=0,k;
unsigned int tempVal;

        if (h_or_v == 0) tabptr = tabH ; //select horizontal motor
        else tabptr = tabV;//vertical motor

//clear all outputs

                 tempVal = (inpw(port) & ~60) | tab1[l];
                 outpw(port, tempVal); //only address bits are set here, D always low


                //outpw  don't touch other bits (they might be used by the SPI/MMC hack - both are using PORT5)
                // get PORT5 val; clear bits 2,3,4,5 - "and NOT 60";  set the latch address according to tab pre-defined values using "OR"

                if(dir==0) tempVal = ( inpw(port) & ~60) | tabptr[l];
                else tempVal = ( inpw(port) & ~60) | tabptr[3-l]; //reverse gear

                     outpw(port, tempVal | 32); // 32 = PORT5.5 = 74HCT249 D pin
                        outpw(ledport, 0x0); //led on - REVISE !
                        usleep(10000); //10 ms - REVISE !
                outpw(port, tempVal ); //D pin LOW;
                outpw(ledport, 0x400); //led off - REVISE !
                usleep(5000); //5 ms - REVISE !
//clear all outputs, very important - don't skip this step !!! (coils might overheat)
                tempVal = (inpw(port) & ~60) | tab1[l];
                outpw(port, tempVal); //only address bits are set here, D always low

As indicated in the code above, it's crucially important to always switch off all coils ! If from whatever reason this step is omitted the affected stepper motor will become very hot and eventually the internal coil(s) might be damaged (then  the current flow can be increased even more - leading to a potentially hazardous scenario !)

SD/MMC storage

It's possible to implement SD/MMC card storage by emulating software SPI bus connected to GPIO pins. The easiest way is to use pins originally connected to horizontal / vertical edge position sensors, no need to mess with tiny smd soldering points - just cut & connect.

more details soon...

SPI-over-GPIO - the out-of-the-box version works but is unacceptably slow (<30 KB/s raw)

An optimised solution was needed (direct I/O pin access, bypassing linux general gpio locking)

more details comming soon...

MMC/SD block driver - by default crashes the filesystem if  even a single write error should occur

Retry routine added to the default MMC block driver

more details comming soon...


Camera sensor access


The default V4L driver in 2.6.x no longer supports memory mapping operations with no-mmu CPU, fortunately there is a patch to bring back mmap capabilities.

more details soon...