Startup on the iMote2

Introduction

When you finish your final application, you'll likely want it to start up when you turn on your iMote2 and automatically load any needed kernel modules. This page describes how to install kernel modules so they will be automatically loaded, how to automatically make devices in /dev, and how to automatically start programs when the iMote2 boots up.

The Linux Boot Process

For more information on how the Linux boot procedure works, particularly on embedded Linux systems, the Building Embedded Linux Systems book is an excellent reference.

When you turn on a Linux machine, the processor, just like the AVR microcontroller we used earlier in the quarter, begins executing instructions from a specific location in memory. On desktop PCs, this location contains BIOS, which knows how to load and execute the master boot record from a hard disk. On an embedded processor like the iMote2, the nonvolatile flash storage is mapped into the memory space, so the iMote can actually begin executing code directly out of flash. The bootloader (called blob) is loaded from the start of flash memory.

The bootloader is responsible for initializing the system, setting the special function registers to a good default state (the bootloader is responsible for setting the LED pins as outputs and turning on the blue LED when you switch on the power, for example) and then decompressing and loading the Linux kernel.

The Linux kernel sets itself up, and then tries to mount the root filesystem and looks for the init program which is responsible for starting the rest of the system. (If you look at the list of running processes, there will always be one called init and it always has a process ID of 1.) It looks for this script in a few hardcoded places, such as /init, /bin/init, and /sbin/init. The Linux kernel only defines what happens up until the point that the init script is started; what the init script actually does is dependent on the details of the particular Linux distribution.

The init script looks at /etc/inittab, which allows it to be configured to the needs of a particular system. The inittab file on your iMote2 contains the following:

::sysinit:/etc/init.d/rcS
::askfirst:/sbin/getty 115200 ttyS2
::restart:/sbin/init
::shutdown:/bin/umount -a -r

The first line says that at startup "sysinit", the /etc/init.d/rcS script should be executed. (This script, in turn, executes all of the scripts it finds in /etc/rcS.d.) The second line says that a console should be started on the serial port, but only after displaying a message that says "Press ENTER to activate this console." This is the console that you use if you connect the debug board to the serial port. The third line re-runs the init script if init is restarted, and the last line remounts all of the filesystems to read-only at shutdown.

Running Scripts at Startup

When init started up, it ran /etc/init.d/rcS, which executed all of the scripts in /etc/rcS.d. Each of these scripts starts with a capital S and a number indicating in what sequence the scripts should be run (they are simply executed in alphanumeric order). If you want to add processes to be started when your iMote2 starts up, you should add scripts to the rcS directory. Remember that they are executed in order, so you probably want your script executed after everything else has been started up. Also note that the next script will not be executed until the previous one finishes; it is good practice to make sure that your scripts spawn processes in the background and then exit. See the man pages for nohup and exec, and use the & character to run processes in the background.

It is also important to remember that processes started automatically are not attached to a console to be able to print any output from printf or similar calls. You should redirect your program's output to /dev/null or to an appropriate log file, or the system will kill your process when the output buffer gets full.

Loading kernel modules

At startup, one of the scripts in rcS.d looks at the /etc/modules file and executes modprobe for every module listed. modprobe is similar to insmod (in fact, it calls insmod to do its work) but it also checks module dependencies and makes sure that any modules that a particular module depends on are loaded first. (Calling modprobe on snd-soc-superbird, for example, also loads in the entire ALSA sound system, the drivers for the AK4642 codec, drivers for ALSA's timers, and so on, before actually loading the SuperBird sound driver.)

In order for modprobe to work, it expects two things. One is that modules will be installed in /lib/modules/2.6.28_r1.0/kernel. The other is that a file called /lib/modules/2.6.28_r1.0/modules.dep exists, which specifies the dependencies between modules.

To install a module to the system, you just need to place it in the proper directory in /lib/modules/2.6.28_r1.0/kernel (most drivers should go in the drivers subdirectory.) You can then update the modules.dep file by running depmod, which looks at the symbol tables of all of the modules, figures out what symbols they export and which they depend on, and produces the dependency tree in the modules.dep file.

Once you have done this, you can now load your module by running modprobe yourmodule, and this can be done automatically at startup by adding your module's name to /etc/modules.

Automatically making devices in /dev

Most of the devices in /dev are created from the /etc/device_table file at startup. However, this approach expects the devices to have fixed major and minor numbers, so this approach won't work for the drivers you've written since they dynamically choose device numbers. Linux normally uses a program like hotplug or udev to handle such dynamic devices, but it adds a lot of overhead and is not set up on the iMote2.

You can, however, write a startup script that does essentially the same thing that you do manually when you want to add a device: look at /proc/devices, find the major device number, and execute mknod. The following sequence of commands, for example, will create a device called /dev/superbird for the superbird device:

sbnum=`cat /proc/devices | grep -E "superbird$" | awk '{print $1}'`
mknod /dev/superbird c $sbnum 0

The first line looks in /proc/devices for a line ending with the string "superbird". It then takes the first item on the line, which is the major device number, and sticks it in a variable called $sbnum. The second line then executes mknod, creating /dev/superbird as a character device, with the major device number stored in $sbnum and a minor device number of 0.

If you do this in a script that's in rcS.d (making sure it's executed after the modules are loaded) the device will be automatically created at startup.

A script that searches /proc/devices and calls mknod with the major number it finds is pre-installed on the iMote2 and is called makedev. It's arguments are as follows:

makedev <name of device in /proc/devices> <minor number> /dev/<device filename>

You can use this script, or you may write your own.