Wednesday, March 31, 2010

Linux and uClinux rc file and inittab file

On a linux system inittab file, rc scripts and relavant config files in /etc play key roles at system startup. Inittab file is read and parsed by init process, to decide what level the system will run at and which processes are started at bootup and at normal operation. Then the rc script will be run at that level.

There are system V and BSD styles to the arrangement of inittab, rc scripts and config files. And there are differences between Linux distributions. But the key point is, the system will startup at a predefined level most likely defined in inittab, and rc scripts will be run for that level to spawn all the necessary processes.

In uClinux there are inittab and rc file as well. But there are no run levels. An embedded system usually runs at just one level. So be carefull, uClinux inittab and rc are different from that of standard Linux.

In uClinux, rc is executed BEFORE inittab! rc file basically contains commands to prepare the system environment such mount /var, make directories, startup network etc. Inittab can be used to startup programs and services, such as serial terminals, IP servers, etc.

Something about the terminal. The line for terminal is usually like this:

ttyS0:vt100:/bin/agetty 9600 ttyS0

agetty is an excutable binary in /bin directory. It opens a tty port, prompt a login name and invokes /bin/login command. The above is the simplest format of an agetty command. It actually has quite a few options to change its behaviors. For example this one:

agetty -L 9600 ttyS1 vt100

This will force the line to be a local line with no need for carrier detect. This can be useful when you have a locally attached terminal where the serial line does not set the carrier detect signal. Try this if your terminal just sleeps instead of giving you a password: prompt.

Another thing is that agetty will optionally display the contents of /etc/issue. You can use a -f to display an alternative file, or you can use -i to ignore any issue file, not to display the contents of /etc/issue (or other) before writing the login prompt.

One more note, if there is no agetty in inittab, and you choose init with "enable console shell" when you configure core applications, then the uClinux will start without login at all.

Friday, March 26, 2010

MJPG-streamer, a light weight video streaming server for embedded system

A short post, about a light weight video streaming server. I've been looking around for a small but robust realtime video streamer that can stream video from a camera over IP network to a standalone viewer or browser. And of course it has to be able to run smooth on a resource constrained embedded system such as mini2440(ARM9 + 64M ram + 128M flash).

mjpg-streamer looks like a good fit. It's an open source project at http://sourceforge.net/projects/mjpg-streamer/. And it's compiled for mini2440 at http://code.google.com/p/mjpg-streamer-mini2440/.

So I installed the binary for mini2440, it works pretty good. It works with a cheap USB camera, it works with a CMOS camera too. Only thing is, the frame rate is not high enough, 5 frames/sec, with 1-2 seconds delays. CMOS camera is obviously better than USB camera regarding to frames rate and delays. I haven't tried a camera with some compression capability yet, but it's supposed to have better result.

But it's pretty good overall. There are a few things I want to do with it (too busy now to do it now) in the near future:
1. A better camera
2. Make it part of the rootfs
3. Look into the code to see how it works with the camera driver.
4. Some modifications, such as embedded data within the pictures.

How come a ROM based ROMFS image can run from within RAM?

Background: On a Coldfire uC5272 platform, a ROM based ROMFS image(linux.bin + romfs) supports XIP(execution in place), meaning the image can be burnt into Flash and boot and run from within ROM, with only the .data and .bss section moved into RAM by second bootloader(crt0_rom.S for 2.4.x kernel).

The image I built just works fine. But I decided to go a bit further. What's gonna happen if I run the image from within RAM? It's not supposed to be able to boot.

To my supprise, when I set uCbootloader environment varible "RAMIMAGE=yes" and type "go" command, the image get copied into ram, boot and run with staring address at 0x0020000, exactly as "RAMIMAGE=no"(which means boot from ROM at 0x10c20000).

Here is my explanation. For uCbootloader, when we type "go" to boot, the loader will fetch the first 32bytes of the image (the .bvec section of linux.bin) as the SP and IP content, and execute from the address pointed to by IP (the entry point of second bootloader). When the image get copied into RAM starting at 0x0020000, and when we "go" from there, the first 32 bytes starting from 0x0020000 are fetched into SP and IP. But, here is the point, the content of IP is still pointed to the entry address of second bootloader in ROM, so the execution is still go back to ROM.

To prove it I erase the Flash so there is no image in ROM at all. Then I download the ROM based image into RAM. Now I "goram" at 0x0020000, system just stop there without booting up, because the address pointed by IP is empty this time.

Conclusion, in a uClinux image, once generated, all the addresses of symbols (global varibles, functions, etc) are arranged and fixed. As long as the execution can jump to the right address, it will execute normally.

Wednesday, March 24, 2010

How to make putty private key authenticated

Once in a while I need to access my ubuntu9.10 at home from my Windows laptop. This is pretty easy. Once I have a ssh client (putty) installed on my Windows, open a NAT for port 22 on my router, I can login my account by username and password.

This is very insecure as everyone know, a username/passwd authentication in clear text is easy for attackers too. So I changed the authentication to RSA public/private key authentication. Here is a link that gives a pretty good explanation about how to do that. https://help.ubuntu.com/community/SSH/OpenSSH/Keys. But it's talking about clients running linux.

In order for the windows client putty to work, these extra steps are needed:
1. On ubuntu (the ssh server), copy the newly generated public key id_rsa.pub to authorized_keys, and uncomment the "AuthorizedKeysFile %h/.ssh/authorized_keys" in sshd_config. Of course restart sshd by "/etc/init.d/ssh restart"

2. Copy the private key, id_rsa to windows, load it into puttygen and save it as private key in .ppk format. (You need to download puttygen.exe from putty website)

3. Change your session to use private key authentication, by loading the private key into your session's configuration. Don't forget to save your session's configuration!

And the next time you login, you will be asked for a passphrase for your private key. After that, you're logged in private key authenticated. And you feel secured!!!

Friday, March 19, 2010

Linux zigbee checkout

Linux zigbee is an open source project implementing ieee802.15.4 standard and partially zigbee stack in kernel. Its source code can be downloaded by "git clone git://linux-zigbee.git.sourceforge.net/gitroot/linux-zigbee/linux-zigbee", according to http://sourceforge.net/projects/linux-zigbee/develop

As you can see, the project is managed by git, a revision control system used by some open source project.

I'm new to git so I download the source code by following the command, everything is fine. But then when I browse the source code, I found that my local repository doesn't have certain files and directories which exist when I browse them online from here: http://linux-zigbee.git.sourceforge.net/git/gitweb.cgi?p=linux-zigbee/kernel;a=tree;h=d6fac23360241750fcae3d9e2f1012af79934c48;hb=d6fac23360241750fcae3d9e2f1012af79934c48

After a little digging out I realize what's checked out in my local directory is the master branch while the one has more files and directories is devel branch. Some commands can be used to catch up the latest updates from the devel branch:

$git branch -r --->shows all the remote branches git is tracking
origin/HEAD -> origin/master
origin/devel
origin/devel-wpan-phy
origin/master
You can see there are three branches with the HEAD pointed to origin/master.

$git branch --->shows existing branches, with the current branch highlighted with an asterisk.
master
* origin/devel
I have 2 branched with origin/devel as the current one. (I have switched to origin/devel already, otherwise the asterisk will be with master)

$git checkout -b origin/devel --->switch to origin/devel branch, and bring in all the latest code from devel.

With the last command my current branch is switched from master to devel. And I have all the latest updates from devel in my local directory, such as macieee802154, drivers for atmel tranceiver, cc2420, etc.

From here, the following commands can be used to synchronize local repository with remote:
$git pull
or
$git fetch

Tuesday, March 2, 2010

How is romfs moved above .bss in uClinux?

Platform: Coldfire uc5272 dev board running uClinux 2.4.x

For a ram based image, the .bss section doesn't exist in the image and has to be created by the second boot loader before kernel starts to run. Since the .bss section will be created right after .data section, taking part of the romfs' position, the romfs has to be moved up to make room before .bss is created and initialized.

The assembly file crt0_ram.S acts as the second bootloader. It takes over control from bootloader and do the actual moving of romfs. The following code snapshot is where the moving happens.

85 #ifdef CONFIG_ROMFS_FS
86 /*
87 * Move ROM filesystem above bss :-)
88 */
89 lea.l _sbss, %a0 /* Get start of bss */
90 lea.l _ebss, %a1 /* Set up destination */
91 move.l %a0, %a2 /* Copy of bss start */
92
93 move.l 8(%a0), %d0 /* Get size of ROMFS */
94 addq.l #8, %d0 /* Allow for rounding */
95 and.l #0xfffffffc, %d0 /* Whole words */
96
97 add.l %d0, %a0 /* Copy from end */
98 add.l %d0, %a1 /* Copy from end */
99 move.l %a1, _ramstart /* Set start of ram */
100
101 _copy_romfs:
102 move.l -(%a0), %d0 /* Copy dword */
103 move.l %d0, -(%a1)
104 cmp.l %a0, %a2 /* Check if at end */
105 bne _copy_romfs
106 #endif

The third longword in the romfs image is the size of itself in bytes. So after line 93, register d0 (%d0 hereafter) has the length of the whole romfs. But you may wonder why the length is added a 8 at line 94? and then the last two binary digits are zeroed out at line 95?

The comments explain it but may be too brief to get a full understanding.

As we know, when we align a memory boundary, we align it to the nearest longword position, ie, an address that is divisible by 4. By adding 4 to the address and then zeroed out the last 2 binary digits, which could be 0,1,2,3, we got the nearest longword position.

For example, when we align 0x0101, we hope it is aligned at 0x0104. 0x0101 + 4 = 0x0105, then 0x0105 AND 0xfffc = 0x0104. (it's equal to minus 1).

Then why adding 8 instead of 4 in the snapshot? Because at line 102, 103, the address is minus 4 first before a longword is moved. So we need %a0 and %a1 to point to 1 longword above the actual address.

So there may be up to 4 bytes (if the boundary is already aligned, then it's 4 bytes) that are not part of romfs but get moved. But it doesn't matter, because these extra bytes are from free memory.