Sunday, February 21, 2010

A Linux tool for downloading kernel and rootfs to mini2440

The manufacturer of mini2440 provides a MS Windows tool DNW.exe for downloading kernel image and root file system into the board. It works great but I'd like to have a similar tool on my dev Linux box so that I don't have to switch back and forth. Fortunately core_rui wrote a small command line program doing exactly the job. (Even though I prefer a program with GUI, but this is good enough for now).

Its usage is simple:
dnw2 suppervivi-128M
dnw2 zImage_T35
dnw2 root_qtopia_128M

Here is the source code:

/* dnw2 linux main file. This depends on libusb.
*
* Author: Fox
* License: GPL
*
*/
#include <stdio.h>
#include <usb.h>
#include <errno.h>
#include <sys>
#include <fcntl.h>
#include <unistd.h>

#define QQ2440_SECBULK_IDVENDOR 0x5345
#define QQ2440_SECBULK_IDPRODUCT 0x1234

struct usb_dev_handle * open_port()
{
struct usb_bus *busses, *bus;

usb_init();
usb_find_busses();
usb_find_devices();

busses = usb_get_busses();
for(bus=busses;bus;bus=bus->next)
{
struct usb_device *dev;
for(dev=bus->devices;dev;dev=dev->next)
{
if( QQ2440_SECBULK_IDVENDOR==dev->descriptor.idVendor
&& QQ2440_SECBULK_IDPRODUCT==dev->descriptor.idProduct)
{
printf("Target usb device found!n");
struct usb_dev_handle *hdev = usb_open(dev);
if(!hdev)
{
perror("Cannot open device");
}
else
{
if(0!=usb_claim_interface(hdev, 0))
{
perror("Cannot claim interface");
usb_close(hdev);
hdev = NULL;
}
}
return hdev;
}
}
}

printf("Target usb device not found!n");

return NULL;
}

void usage()
{
printf("Usage: dnw2 nn");
}

unsigned char* prepare_write_buf(char *filename, unsigned int *len)
{
unsigned char *write_buf = NULL;
struct stat fs;

int fd = open(filename, O_RDONLY);
if(-1==fd)
{
perror("Cannot open file");
return NULL;
}
if(-1==fstat(fd, &fs))
{
perror("Cannot get file size");
goto error;
}
write_buf = (unsigned char*)malloc(fs.st_size+10);
if(NULL==write_buf)
{
perror("malloc failed");
goto error;
}

if(fs.st_size != read(fd, write_buf+8, fs.st_size))
{
perror("Reading file failed");
goto error;
}

printf("Filename : %sn", filename);
printf("Filesize : %d bytesn", fs.st_size);

*((u_int32_t*)write_buf) = 0x30000000; //download address
*((u_int32_t*)write_buf+1) = fs.st_size + 10; //download size;

*len = fs.st_size + 10;
return write_buf;

error:
if(fd!=-1) close(fd);
if(NULL!=write_buf) free(write_buf);
fs.st_size = 0;
return NULL;

}

int main(int argc, char *argv[])
{
if(2!=argc)
{
usage();
return 1;
}

struct usb_dev_handle *hdev = open_port();
if(!hdev)
{
return 1;
}

unsigned int len = 0;
unsigned char* write_buf = prepare_write_buf(argv[1], &len);
if(NULL==write_buf) return 1;

unsigned int remain = len;
unsigned int towrite;
printf("Writing data ...n");
while(remain)
{
towrite = remain>512 ? 512 : remain;
if(towrite != usb_bulk_write(hdev, 0x03, write_buf+(len-remain), towrite, 3000))
{
perror("usb_bulk_write failed");
break;
}
remain-=towrite;
printf("r%d%t %d bytes ", (len-remain)*100/len, len-remain);
fflush(stdout);
}
if(0==remain) printf("Done!n");
return 0;
}

To compile: gcc dnw2.c -o dnw2 -lusb
dnw2 can be placed in /usr/bin for ease of use.

Thursday, February 18, 2010

uClinux memory layout

georges@menie.org gave a good explanation of uClinux memory layout here:

http://www.menie.org/georges/DragonEngine/memory-map.html

Some thing I want to add, how is the image.bin (which is the executable image we usually download to the hardware board to execute) generated.

Like George said:
The uClinux build system is building first the linux kernel, then the user applications and the root filesystem. The last step is usually to extract the text, data and init segments, to convert them in a flat binary file and to add the root file system at the end.

For Arcturus ColdFire uC5272, if you take a look at the Makefile at .../vendors/Arcturus/uC5272, you will find something like this:

$(CROSS_COMPILE)objcopy -O binary --remove-section=.ramvec\
--remove-section=.eram \
--set-section-flags=.romvec=CONTENTS,ALLOC,LOAD,READONLY,CODE \
$(ROOTDIR)/$(LINUXDIR)/linux $(IMAGEDIR)/linux.bin

cat $(IMAGEDIR)/linux.bin $(ROMFSIMG) > $(IMAGE)

Basically objcopy remove .ramvec, .eram sections, and set section flags of linux, and copy the rest into linux.bin. Then linux.bin and romfs.bin are cat'ed into image.bin file.

Tuesday, February 16, 2010

Open source IEEE802.15.4/Zigbee implementation

This is a summary of my recent research about IEEE802.15.4/Zigbee open source implementation.

1. Linux-Zigbee:
URL: http://sourceforge.net/apps/trac/linux-zigbee/

An implementation of IEEE802.15.4 standard and several protocols running on the top of IEEE 802.15.4 (like ZigBee) for Linux. It contains two parts, kernel protocol stack and userspace utilities.

It's a good candidate for embedded linux system functioning as a Zigbee coordinator, gateway, control center or similar devices. There are also efforts to put it into Freescale MC1322x Platform in a package. (MC1322x is basically one signal SoC with both RF and an ARM7 mcu)

2. Using the Freescale MC1322x Series ARM7 Processor with integrated 802.15.4 by Mariano Alvira:
URL: http://mc1322x.devl.org/

It's a good cand The purpose is to develop Open Source tools (namely Linux related) and hardware for the Freescale MC13224V processor.

This is interesting because it potentially makes it possible for Linux-Zigbee to work on MC1322V. Then we will have 802.15.4/Zigbee sensors running Linux (uClinux?)

3. Open-ZB:
URL: http://www.open-zb.net/
  • Implementation of beacon-enabled mode of the IEEE 802.15.4 protocol stack developed in nesC, under the TinyOS operating system v1.1.15 and version 2.0 (new) for the CrossBow MICAz and TelosB motes;
  • Implementation of the ZigBee Network Layer (including the IEEE 802.15.4) supporting the Cluster-tree topology, featuring our proposed mechanism to schedule de beacon transmission, the Time Division Beacon Scheduling
It's open source but not Linux. It's a good candidate for implementing 802.15.4 and Zigbee sensors.
(I know company who implement a Zigbee to Ethernet gateway using TinyOS too)

4. Zigbee for PIC18, PIC24 by Microchip
URL: http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en538049

It's proprietory but provides full source code. It's only for Microchip PIC series microcontrollers.

How to bring up WIFI connection on Embedded Linux

This is how I bring up the wlan connection up on a mini2440 dev board running Linux by using command lines.

The hardware board is a Sumsung ARM9 based dev board running Linux kernel 2.6.29. The wifi card is a TP-Link WN321G usb adapter.

First of all, I have to make sure the driver is compiled into the kernel. The driver for this adapter is "Ralink rt2501/rt73 (USB) support". This is what I chose when I configure the kernel before building it.

When the system boots up, I see something like this:

[root@FriendlyARM /]# usb 1-1: new full speed USB device using s3c2410-ohci and
address 2
usb 1-1: New USB device found, idVendor=148f, idProduct=2573
usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
usb 1-1: Product: 54M.USB.......
usb 1-1: Manufacturer: Ralink
usb 1-1: configuration #1 chosen from 1 choice
wmaster0 (rt73usb): not using net_device_ops yet
wlan0 (rt73usb): not using net_device_ops yet

This is enough to tell that the card is detected by the system.

Then the following commands are used to bring the connection up:
ifconfig eth0 down --->bring down the ethernet interface
ifconfig wlan0 up --->bring up the wifi interface
iwlist wlan0 scanning | grep ESSID ---> scan the essids of available wifi network (option)
iwconfig wlan0 key "ABCD..." --->this is the key for secured wifi network. I'm using a 128bit WEP, so the key is a 26 digits HEX. For text key, you can use:
iwconfig wlan0 key s:"text passwd"
iwconfig wlan0 essid "Home AP" --->set the wifi network to connect to
iwconfig wlan0 ap auto --->connect to the wifi network
ifconfig wlan0 192.168.1.15 ---->set the IP of the wifi interface. (don't have to do this if you are using dhcp)

After all of these, use ping to see if you are connected or not.

Looks like the WN321G is not very sensitive to signals. So if you are not connected and you are sure you have followed the commands, try a different location where signal is stronger.

These process can be intergrated into the system's startup scripts. It'll be in /etc/init.d/rcS file for mini2440.



Friday, February 12, 2010

Linux device driver

As we know, every device in a linux system is a file. A program read/write to a device through the file. How is this possible? It's made possible by device driver.

Device driver model found in Unix and Linux is a natural partitioning of functionality between application code and hardware or kernel devices related code. It isolates user's application from directly access to critical kernel data structure and hardware devices. It hides the operation details of particular hardwares. And a loadable device driver ease the embedded system development and field updates/upgrades.

Note: When we talk about device drivers, loadable kernel module (LKM), loadable module, and module, we usually refer to the same thing, a loadable kernel device driver module.

Device driver is running in the kernel space. It could be statically compiled into the kernel. This is done by selecting the specific driver when we configure the kernel in the build process. For example, if we want a system to boot from a NFS root system, we have to compile the network-related drivers such as TCP/IP, ethernet driver into the kernel image, so that they are available during system boot from a remote root file system.

In many cases device drivers are loadable modules. They are compiled into seperate binaries files (against the kernel on which it will be running), and can be installed/uninstalled after the kernel has booted. They can be loaded by startup scripts, can be "demand loaded" when needed, or can be loaded manually by user space commands.

The following are the mostly used commands:
lsmod ---> list the modules that are inserted into the kernel
insmod ---> insert a module into a running kernel, it takes the module's file name.
rmmod ---> removes a module from a running kernel
modprobe ---> most powerful tool, load/unload a module, togather with dependent modules, etc.

There are a few files that are working togather with loadable modules. They are:
/etc/modprobe.conf ---> configuration file for modprobe. It enables a system developer to associate devices with device drivers. For simple embedded system, modprobe.conf might be empty or might contains very few lines. The modprobe utility is compiled with a set of default rules that establish the defaults in the absence of a valid modprobe.conf.

../modules.dep ---> in the same location where modules are installed. It contains a list of all the modules that the kernel build system is configured for, along with dependency info for each. It's used by modprobe to decide dependencies.

Monday, February 8, 2010

uClinux flat file format

In order for a OS to execute a program, the OS kernel has to load the program into RAM (or partial of the program for XIP). The kernel loader needs to know how and where to load each segments of the program.

Where does this imformation come from? It comes from the file, which is in a format that the kernel loader supports and understands. When we build a program, the linker will put address location information in the file in the specific format, and these information is read by kernel loader to locate the program into RAM.

uClinux uses a Binary Flat format commonly known as BFLT. It is a relatively simple and lightweight executable format based on the original a.out format. It has seen two major revisions, version 2 and more recently version 4. Version 2 is used by the m68k-coff compilers and is still in use with the ARM elf2flt converter while version 4 is used with the m68k elf2flt converter. Like many open source projects worked on by many individuals, little features have creped into versions without the revision field changing. As a consequence what we detail in each version may not strictly be the case in all circumstances. Both the 2.0.x and 2.4.x kernels’ bFLT loaders in the CVS support both formats. Earlier kernels may need to have binfmt_flat.c and flat.c patched to support version 4, should version 4 binaries report the following message when loading

bFLT:not found.
bad magic/rev(4,need 2)

Each flat binary is preceded by a header of the structure shown below in listing 1. It starts with 4 ASCII bytes, “bFLT” or 0x62, 0x46, 0x4C, 0x54 which identifies the binary as conforming to the flat format. The next field designates the version number of the flat header. As mentioned there are two major versions, version 2 and version 4. Each version differs by the supported flags and the format of the relocations.

The next group of fields in the header specify the starting address of each segment relative to the start of the flat file. Most files start the .text segment at 0x40 (immediately after the end of the header). The data_start, data_end and bss_end fields specify the start or finish of the designated segments. With the absence of text_end and bss_start fields, it is assumed that the text segment comes first, followed immediately by the data segment. While the comments for the flat file header would suggest there is a bss segment somewhere in the flat file, this is not true. bss_end is used to represent the length of the bss segment, thus should be set to data_end + size of bss.



Figure 1 : Flat File Format

struct flat_hdr {
char magic[4];
unsigned long rev; /* version */
unsigned long entry; /* Offset of first executable instruction with text segment from beginning of file */
unsigned long data_start; /* Offset of data segment from beginning of file */
unsigned long data_end; /* Offset of end of data segment from beginning of file */
unsigned long bss_end; /* Offset of end of bss segment from beginning of file */

/* (It is assumed that data_end through bss_end forms the bss segment.) */

unsigned long stack_size; /* Size of stack, in bytes */
unsigned long reloc_start; /* Offset of relocation records from beginning of file */
unsigned long reloc_count; /* Number of relocation records */
unsigned long flags;
unsigned long filler[6]; /* Reserved, set to zero */
};
Listing 1 : The bFLT header structure extracted from flat.h

Following the segments’ start and end pointers comes the stack size field specified in bytes. This is normally set to 4096 by the m68k bFLT converters and can be changed by passing an argument (-s) to the elf2flt / coff2flt utility.

The next two fields specify the details of the relocations. Each relocation is a long (32 bits) with the relocation table following the data segment in the flat binary file. The relocation entries are different per bFLT version.

Version 2 specified a 2 bit type and 30 bit offset per relocation. This causes a headache with endianess problems. The 30 bit relocation is a pointer relative to zero where an absolute address is used. The type indicates whether the absolute address points to .text, .data or .bss.

#define FLAT_RELOC_TYPE_TEXT 0
#define FLAT_RELOC_TYPE_DATA 1
#define FLAT_RELOC_TYPE_BSS 2
struct flat_reloc {
#if defined(__BIG_ENDIAN_BITFIELD) /* bit fields, ugh ! */
unsigned long type : 2;
signed long offset : 30;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
signed long offset : 30;
unsigned long type : 2;
#endif
Listing 2 : Version 2 relocation structures - Not for use in new code.

This enables the flat loader to fix-up absolute addressing at runtime by jumping to the absolute address specified by the relocation entry and adding the loaded base address to the existing address.

Version 4 removed the need of specifying a relocation type. Each relocation entry simply contains a pointer to an absolute address in need of fix-up. As the bFLT loader can determine the length of the .text segment at runtime (data_start - entry) we can use this to determine what segment the relocation is for. If the absolute address before fix-up is less that the text length, we can safety assume the relocation is pointing to the text segment and this add the base address of this segment to the absolute address.

On the other hand if the absolute address before fix-up is greater than the text length, then the absolute address must be pointing to .data or .bss. As .bss always immediately follows the data segment there is no need to have a distinction, we just add the base address of the data segment and subtract the length of the text segment. We subtract the text segment as the absolute address in version 4 is relative to zero and not to the start of the data segment.

Now you could say we may take it one step further. As every absolute address is referenced to zero, we can simply add the base address of the text segment to each address needing fix-up. This would be true if the data segment immediately follows the text segment, but we now have complications of -msep-data where the text segment can be in ROM and the data segment in another location in RAM. Therefore we can no longer assume that the .data+.bss segment and text segment will immediately follow each other.

The last defined field in the header is the flags. This appeared briefly in some version 2 headers (Typically ARM) but was cemented in place with version 4. The flags are defined as follows

#define FLAT_FLAG_RAM 0x0001 /* load program entirely into RAM */
#define FLAT_FLAG_GOTPIC 0x0002 /* program is PIC with GOT */
#define FLAT_FLAG_GZIP 0x0004 /* all but the header is compressed */
Listing 3 : New flags defined in Version 4

Early version 2 binaries saw both the .text and .data segments loaded into RAM regardless. XIP (eXecute in Place) was later introduced allowing programs to execute from ROM with only the data segment being copied into RAM. In version 4, it is now assumed that each binary is loaded from ROM if GOTPIC is true and FLAT_FLAG_GZIP and FLAT_FLAG_RAM is false. A binary can be forced to load into RAM by forcing the FLAT_FLAG_RAM flag.

The FLAT_FLAG_GOTPIC informs the loader that a GOT (Global Offset Table) is present at the start of the data segment. This table includes offsets that need to be relocated at runtime and thus allows XIP to work. (Data is accessed through the GOT, thus relocations need not be made to the .text residing in ROM.) The GOT is terminated with a -1, followed immediately by the data segment.

The FLAT_FLAG_GZIP indicates the binary is compressed with the GZIP algorithm. The header is left untouched, but the .text, .data and relocations are compressed. Some bFLT loaders do not support GZIP and will report an error at loading.

Note: most of the info above is from http://www.beyondlogic.org/uClinux/bflt.htm

Friday, February 5, 2010

What's portmap and it's use

In a uClinux system, there is a program "portmap" in the /bin directory. And in the system start script rc file, sometimes you see a line like "/bin/portmap &". This means portmap will be started as part of the system startup. But what's portmap, and what's it for? In a uClinux system, when should it be included?

portmap is a server that converts RPC program numbers into DARPA protocol port numbers. It must be running in order to make RPC calls.

When an RPC server is started, it will tell portmap what port number it is listening to, and what RPC program numbers it is prepared to serve. When a client make a RPC call, by a program number, it will first contact portmap on the server machine to determine the port number where the RPC packets should be sent to.

In a embedded uClinux system, sometimes we want to mount a NFS file systme in the target board, and NFS service is based on RPC. That why we need to start portmap when we start up the system. (Of course you can start the server manually in a shell).

Tuesday, February 2, 2010

romfs - ROM file system

What is rom fs:

romfs is a space-efficient, small, READ-ONLY filesystem originally for Linux and used by some Linux based projects. It is a block-based filesystem, that means it makes use of block (or sector) accessible storage driver (like disks, CDs, ROM drives, RAM disk, etc).

romfs itself, although it may sound weird at first, cannot handle ROMs of any kind. It is just a file system, working over a block device, including ROM disk or RAM disk. In order for romfs to work on ROM or RAM, you need drivers to make them appear as disk (ROM disk or RAM disk).
Where romfs is used:
There are at least two main uses where romfs, despite its limitations, is almost perfect.

1. The first is normal Linux systems, where you want some of the files to take the least possible space, and you can't afford or need integrated compression. This is typical in some boot procedures. You can have your initial ram disks in romfs format, and even if you don't use it after the boot, its code is still very small to waste memory on. This is also the single case when compression is easily achievable, as the initial ramdisk loader recognizes gzip compressed images. Currently no romfs code supports compression.

2. The other possible use when you need to use a Linux kernel on a device which has significant amount of read-only memory, and so it makes little sense to have a writable filesystem on it. This is the case with many embedded systems. uClinux is this case.

What the other file system:
There other file systems that are widely used in embedded linux system, such as ext2, ext3, cramfs(RAM based file system), JFFS2, NFS(we can even put root file system on a NFS server), and pseudo file systems such as proc, sysfs etc. They have different advantages and are chosen according to the specific applicaton.

Monday, February 1, 2010

uClinux rc and inittab files

There are two files playing key roles in system startup, rc and inittab. They are referenced by the first user space process "init", which is spawned by the kernel afte completion of the boot process.

/etc/inittab contains system configurations and directives for each run level, as well as directives that apply to all run levels. For a standard Linux system, the entries in inittab will always like this:

id:runlevel:action:process

For example:
l1:1:wait:/etc/rc.d/rc 0 ----> at runlevel 1, wait until /etc/rc.d/rc 0 start and finish
x:5:respawn:/etc/X11/prefdm -nodaemon ----> start xdm at level 5

However, since uClinux has only one runlevel, the inittab usually is very simple. (An example shows there is no "action" field". Am I right?)

The rc file contains all the scripts to run at system startup. This is where different file system are mounted, network interfaces are configured, etc. We can add our own scripts to start specific applications.

It's simple once they are understood. But they can turn to be complex in a real live system I believe.