Monday, June 7, 2010

The Linux filesystem

The first thing that most new users shifting from Windows will find
confusing is navigating the Linux filesystem. The Linux filesystem
does things a lot more differently than the Windows filesystem.
This article explains the differences and takes you through the
layout of the Linux filesystem.

For starters, there is only a single hierarchal directory structure.
Everything starts from the root directory, represented by '/', and then
expands into sub-directories. Where DOS/Windows had various partitions and
then directories under those partitions, Linux places all the partitions
under the root directory by 'mounting' them under specific directories.
Closest to root under Windows would be c:.

Under Windows, the various partitions are detected at boot and assigned a
drive letter. Under Linux, unless you mount a partition or a device, the
system does not know of the existence of that partition or device. This
might not seem to be the easiest way to provide access to your partitions
or devices but it offers great flexibility.

This kind of layout, known as the unified filesystem, does offer several
advantages over the approach that Windows uses. Let's take the example of
the /usr directory. This directory off the root directory contains most of
the system executables. With the Linux filesystem, you can choose to mount
it off another partition or even off another machine over the network. The
underlying system will not know the difference because /usr appears to be
a local directory that is part of the local directory structure! How many
times have you wished to move around executables and data under Windows,
only to run into registry and system errors? Try moving c:windowssystem
to another partition or drive.

Another point likely to confuse newbies is the use of the frontslash '/'
instead of the backslash '' as in DOS/Windows. So c:windowssystem would
be /c/windows/system. Well, Linux is not going against convention here.
Unix has been around a lot longer than Windows and was the standard a lot
before Windows was. Rather, DOS took the different path, using '/' for
command-line options and '' as the directory separator.

To liven up matters even more, Linux also chooses to be case sensitive.
What this means that the case, whether in capitals or not, of the
characters becomes very important. So this is not the same as THIS or ThIs
for that matter. This one feature probably causes the most problems for
newbies.

We now move on to the layout or the directory structure of the Linux
filesystem. Given below is the result of a 'ls -p' in the root directory.

bin/ dev/ home/ lost+found/ proc/ sbin/ usr/
boot/ etc/ lib/ mnt/ root/ tmp/ var/

/sbin - This directory contains all the binaries that are essential to the
working of the system. These include system administration as well as
maintenance and hardware configuration programs. Find lilo, fdisk, init,
ifconfig etc here. These are the essential programs that are required by
all the users. Another directory that contains system binaries is /usr/sbin.
This directory contains other binaries of use to the system administrator.
This is where you will find the network daemons for your system along with
other binaries that only the system administrator has access to, but which are
not required for system maintenance, repair etc.

/bin - In contrast to /sbin, the bin directory contains several useful
commands that are used by both the system administrator as well as
non-privileged users. This directory usually contains the shells like
bash, csh etc. as well as much used commands like cp, mv, rm, cat, ls.
There also is /usr/bin, which contains other user binaries. These binaries
on the other hand are not essential for the user. The binaries in /bin
however, a user cannot do without.

/boot - This directory contains the system.map file as well as the Linux
kernel. Lilo places the boot sector backups in this directory.

/dev - This is a very interesting directory that highlights one important
characteristic of the Linux filesystem - everything is a file or a
directory. Look through this directory and you should see hda1, hda2 etc,
which represent the various partitions on the first master drive of the
system. /dev/cdrom and /dev/fd0 represent your CDROM drive and your floppy
drive. This may seem strange but it will make sense if you compare the
characteristics of files to that of your hardware. Both can be read from
and written to. Take /dev/dsp, for instance. This file represents your
speaker device. So any data written to this file will be re-directed to
your speaker. Try 'cat /etc/lilo.conf > /dev/dsp' and you should hear some
sound on the speaker. That's the sound of your lilo.conf file! Similarly,
sending data to and reading from /dev/ttyS0 ( COM 1 ) will allow you to
communicate with a device attached there - your modem.

/etc - This directory contains all the configuration files for your system.
Your lilo.conf file lies in this directory as does hosts, resolv.conf and
fstab. Under this directory will be X11 sub-directory which contains the
configuration files for X. More importantly, the /etc/rc.d directory
contains the system startup scripts. This is a good directory to backup
often. It will definitely save you a lot of re-configuration later if you
re-install or lose your current installation.

/home - Linux is a multi-user environment so each user is also assigned a
specific directory which is accessible only to them and the system
administrator. These are the user home directories, which can be found
under /home/username. This directory also contains the user specific
settings for programs like IRC, X etc.

/lib - This contains all the shared libraries that are required by system
programs. Windows equivalent to a shared library would be a DLL file.

/lost+found - Linux should always go through a proper shutdown. Sometimes
your system might crash or a power failure might take the machine down.
Either way, at the next boot, a lengthy filesystem check using fsck will
be done. Fsck will go through the system and try to recover any corrupt
files that it finds. The result of this recovery operation will be placed
in this directory. The files recovered are not likely to be complete or
make much sense but there always is a chance that something worthwhile is
recovered.

/mnt - This is a generic mount point under which you mount your filesystems
or devices. Mounting is the process by which you make a filesystem
available to the system. After mounting your files will be accessible
under the mount-point. This directory usually contains mount points or
sub-directories where you mount your floppy and your CD. You can also
create additional mount-points here if you want. There is no limitation to
creating a mount-point anywhere on your system but convention says that
you do not litter your file system with mount-points.

/opt - This directory contains all the software and add-on packages that
are not part of the default installation. Generally you will find KDE and
StarOffice here. Again, this directory is not used very often as it's
mostly a standard in Unix installations.

/proc - This is a special directory on your system. We have a more detailed
article on this one here.

/root - We talked about user home directories earlier and well this one is
the home directory of the user root. This is not to be confused with the
system root, which is directory at the highest level in the filesystem.

/tmp - This directory contains mostly files that are required temporarily.
Many programs use this to create lock files and for temporary storage of
data. On some systems, this directory is cleared out at boot or at
shutdown.

/usr - This is one of the most important directories in the system as it
contains all the user binaries. X and its supporting libraries can be
found here. User programs like telnet, ftp etc are also placed here.
/usr/doc contains useful system documentation. /usr/src/linux contains the
source code for the Linux kernel.

/var - This directory contains spooling data like mail and also the output
from the printer daemon. The system logs are also kept here in
/var/log/messages. You will also find the database for BIND in /var/named
and for NIS in /var/yp.

This was a short and basic look at the Linux filesystem. You do need to
have at least this basic knowledge of the layout of the filesystem to
fully utilize its potential. One good place to read about the filesystem
is this detailed document at www.pathname.com/fhs/1.2/fsstnd-toc.html that
specifies the standard structure of the Linux filesystem.

Linux Filesystem Hierarchy Standard
Exploring /proc
Filesystems HOWTO

Thursday, May 6, 2010

Excellent Downloadable eBooks To Teach Yourself Linux


So you have heard of all the advantages and geeky babble about how Linux is better and you have finally decided to try it? Just one thing, you don’t know an awful lot about Linux to get you started. How about some free downloadable ebooks to teach yourself Linux, that you can download today? Would that help?

Free – you ask? Yes, free. Welcome to the world of Linux where things are free both as in free speech and also as in free beer (mostly)!

If you are starting out on your journey towards Linux awesomeness, here are a few free downloadable ebooks to teach yourself Linux that should help you along nicely:

Newbie’s Getting Started Guide to Linux

(Download)

MakeUseOf’s very own Newbie’s Guide to Linux, tells you how to choose a distribution and then teaches you how to perform a basic Linux install. You can then use the guide to familiarize yourself with the Linux desktop and some basic commands.

Stefan did a great job in keeping it simple and to the point, the way beginners want it. Also don’t forget to check out our other MakeUseOf Manuals.

Introduction to Linux – A Hands on Guide

(Download)

Takes you from the absolute basics to basics. This hands on guide tells you everything right from logging in, basic file management, backup techniques up to basics of networking. It is what you need if you are having difficulty figuring out how to get to that resume file you saved just now. The guide explains Linux file structure and introduces to basic commands and text editors as well.

GNU/Linux Command line tools Summary

(Download)

One important aspect of working in Linux is that you have to familiar with the command line. This book shows you how to use the command line in Linux to your advantage. Apart from the ins and outs of the shell, this book also introduces various commands and the situations where you would use them. There are chapters that deal with specific tasks and list various commands you can use to achieve the task. If you can study online, there is another excellent manual you can refer to.

Ubuntu Pocket Guide and Reference

(Download)

Ubuntu is one of the most popular distributions, new users look up to when trying out Linux. If it is Ubuntu specific information that you are after then you should definitely check out Ubuntu Pocket Guide and Reference. The guide takes you from installing and configuring Ubuntu to adding and managing software and securing your system. A must read book if you use Ubuntu.

Rute User’s Tutorial and Exposition

(Download)

This one is not for the faint hearted! There is enough Linux juice in this book to keep even the intermediate to advanced users interested. The book begins humbly by presenting the basic commands and tools, however before you know it, you are learning everything from regular expressions to shell scripting to C programming to networking.

There is plenty of great material out there if you are trying to learn Linux, similar to these downloadable ebooks to teach yourself everything you ever wanted to know about Linux. If you have read a book or a tutorial that you found particularly useful, feel free to tell us about it in the comments below.

Wednesday, April 28, 2010

Current and Future Developments in Embedded Mobile Linux

An increasing number of manufacturers are adopting Linux for their mobile phones since it enables them to use the same operating system on a large range of different products. It also gives them greater flexibility in differentiating devices, without being locked into proprietary schemes.

Mobile phones running Linux include
- Motorola A760, A768, A780, and E680
- Panasonic P901i
- NEC N901ic
- Samsung SCH-i519
- Telepong
- Wildseed
- ROAD S101 devices

The Sharp Zaurus PDAs have been leading in the area of handheld Linux devices for many years.

Additional Resources

You can find more information about embedded Linux and Linux for mobile devices, including the latest on Linux gadgets and software applications here.

Comparison with Other Embedded Operating Systems

Compared with other embedded operating systems, like QNX, Windows CE, Embedded NT, or Palm OS, Embedded Linux has the advantages of being Open Source, stable and well supported, having a small footprint (2MB), and requiring no royalty payments.

Embedded Mobile Linux Processors and Vendors

Processor Architectures used with Embedded/Mobile Linux

Besides the ARM processors, embedded Linux has been adapted to a variety of processors, including
- IBM PowerPC embedded processors
- Tensilica’s Xtensa microprocessors
- Intel’s processors for wireless devices
- Freescale's PowerPC and PowerQuicc processors

Embedded Linux Vendors

Organizations developing embedded/mobile Linux products include
-µClinux
- RTLinux
- LynuxWorks
- Wind River
- MontaVista, which has released Mobilinux, the first version of its Linux operating system specifically designed and optimized for mobile phones and wireless devices.

Mobile Linux vs. Standard (x86-based) Linux

The methodical design of Linux, a UNIX-based operating system, made it possible to adapt it to a wide range of computing platforms. Originally developed for Intel 386 processors and their successors, Linux was soon ported to DEC Alpha processor architectures and runs today on many other widely adopted CPUs. Of particular interest in this context are the ARM based architectures, as many embedded systems and mobile devices are powered by ARM processors. Being able to extract the core functionality of Linux and minimize its footprint made it possible to adopt Linux as an open standard for small and inexpensive devices.

Embedded Linux for Mobile Devices

The first operating systems for mobile phones and other mobile consumer electronic devices were custom developed, which means they were expensive to develop and maintain, as all hardware drivers and interfaces had to be written from scratch in a low level programming language.

Soon developers started to look for higher-level approaches that would facilitate re-use of software components. A typical installation of embedded Linux requires only about two megabytes, which was therefore a good candidate for use as operating system of resource limited devices. Furthermore, Linux is Open Source and therefore well suited as basis for standards.

A group of companies interested in the development of Linux products formed The Embedded Linux Consortium (ELC) in order to promote Linux and develop standards for the embedded computing markets. Standards are also developed for managing power consumption of devices, designing user interfaces, and real-time operation of embedded Linux software. One of the results of this effort is the Embedded Linux Consortium Platform Specification (ELCPS).

Power Combination: Mobile Linux and Mobile Java

While Linux is evolving into a major standard for mobile device operating systems, Java is becoming a standard at the software application level. The J2ME/MIDP specifications have been adopted by all major mobile phone manufacturers, which enables software developers to write applications that can be run, without modifications, on all such devices that adhere to these specifications. The MIDP (Mobile Information Device Profile) is comprised of a set of Java APIs, that provides a J2ME (Java 2 Micro Edition) runtime environment for mobile information devices. It standardizes functions such as user interface design, persistent storage, and networking.

Introduction to Embedded Mobile Linux

To understand the exciting, relatively new, mobile Linux technology, you need background knowledge on embedded Linux, real-time Linux and handheld Linux.

What is Embedded Linux

Linux for mobile phones and other mobile devices is part of the field of "Embedded Linux". "Embedded" refers to the concept of integrating a computational system (a small computer) into a machine or device, other than an ordinary computer, in order to make such machines smarter and more flexible. That is, the primary function of such a machine is something other than being a computer. For example, a specialized computer could be embedded in a car to control ignition, fuel injection, anti-lock braking, and the many other functions of a modern car.

Real-Time is Important

For such applications it is important that the computer system responds quickly and reliably to any tasks given to it, that is, it needs to be run by a "real-time" operating system.

What is a Mobile Computing Device

Mobile simply means portable, easy to be moved about. Several technologies help to make a computing device mobile. First, the size has be small, preferably handheld. It should be powered with rechargeable batteries, and wireless technologies for transmitting and exchanging data have become important as well.

Hot Stuff - Embedded Computer Systems That are Also Mobile

Although an embedded system can be part of a stationary device (e.g., a home security system), and not all mobile devices have embedded or real-time components (e.g., a personal digital assistant or PDA), the greatest interest right now is in embedded computer systems in mobile devices, such as mobile phones.

Friday, March 19, 2010

Embedded System Testing (Software & Hardware)

Embedded systems is gaining importance with increasing adoption of 16 and 32-bit processors across a wide variety of electronic products. As consumer expectation from these systems grow, manufacturers are challenged with the following factors before testing this to perfect for market release:

  • Real time responses
  • Separate host (target) systems from development environments
  • Lack of standardization in deployment architectures
  • Lack of established interfaces to systems under testing
  • Stringent Fail-Safe requirements
  • Extremely high cost of isolating and fixing defects

Calsoft Labs provides end-to-end testing services for embedded software & hardware across a gadgets/devices, Real Time Operating Systems (RTOS), development platforms, and programming languages. Our services include embedded software & hardware testing strategy and code-level testing, as well as coverage analysis, functional testing, stress testing, code review, debugging, and code maintenance.

Calsoft labs’ well defined Testing Services practice covering the entire spectrum from semiconductor, firmware, middleware/ protocols and system & application level testing. Our robust test automation framework is suitable to address testing challenges for the embedded systems of industries like Networking, Communications, Storage, Consumer Electronics & Multimedia, Industrial Automation, Computer Hardware & Peripherals and Automotive Electronics.

The testing services spectrum covers entire value chain including:

  • System Level Testing
  • Application Testing
  • Middleware Testing
  • BSP & Driver testing
  • Embedded Hardware Design

Tuesday, March 9, 2010

Embedded Internet

The embedded Internet is bringing transformative changes to the embedded world. The era of intelligent connectivity is dawning. And the industry is about to hit the fast-forward button.¹

We have watched the Internet grow from its early beginnings to a global human network, breaking down old boundaries, stimulating new usage models, and unleashing opportunities for building businesses and growing revenue on a global scale.

Now the Internet is evolving again, to the embedded space. How big will it become? Intel Vice President Doug Davis cites the IDC prediction of 15 billion intelligent, connected devices by the year 2015¹.

Extending the power of Internet connectivity to a virtually limitless variety of embedded devices, with many communicating machine-to-machine without human intervention, has more far-reaching implications than any single technologist can imagine.

The important question is: how many of these breakthrough solutions will you create?

Even one billion of anything is an astounding number. But when you start to think about 15 billion intelligent devices1 connected to each other, you realize that our industry is on the threshold of something new.

If Internet-connected PCs and phones were transformative, imagine what happens when the Internet connects cars, home media phones, digital signs and shopping carts, mobile medical diagnostic tools, factory robots and intelligent wind turbines.

-Read the full article

At Intel we are working with the embedded computing and communications ecosystem, and with end customers, to envision the innovative possibilities and capture unprecedented opportunities for industry growth.

Driven by breakthroughs in microarchitecture and process technology, the same Intel® architecture that is at the heart of the majority of today’s Internet applications can now deliver scalable intelligence and connectivity to billions of new intelligent, connected devices.

Building on our 30 years of embedded industry experience, Intel is delivering the platforms you need today—based on products whose specifications range from milliwatts of power consumption to petaflops of performance—all based on a single, familiar and proven software architecture.

Newer and even more visionary embedded applications are yet to come. Their implications will be vast—for the industry, and for your future.


"Gantz, John. "The Embedded Internet: Methodology and Findings." IDC. January 2009."

Wednesday, March 3, 2010

How to write a Network Driver in Linux

Linux Network Interface Driver

This article explains how to write a network interface driver in Linux . There are many network

interface cards available in market. I have taken the Realtek chip driver 8139too.c to explain code snippets. This driver is implemented in linux/drivers/net/8139too.c file. I have chosen 8139too.c

driver because hardware specifications for Realtek chips are available free and you can download or read them online .

See References section for download links to Realtek Manuals RTL8139D_DataSheet.pdf , RTL8139_ProgrammersGuide.pdf. I suggest you, first read device manuals and then read this article for better understanding. RTL8139_ProgrammersGuide.pdf explains how the reception and transmission happen in Realtek 8139 chip where as RTL8139D_DataSheet.pdf explains register’s details. This article explains the driver from linux 2..6.26 kernel.

I assume that reader is familiar with Linux kernel and PCI devices. Though this article explains

8139too.c driver , writing any other network interface driver is similar except for the hardware specific functionalities, which change.

This article is divided into 5 parts and I wish to present each part every week to facilitate short, quick and informative reading.

1) Overview

2) Initialization

3) Packet handling ( reception and transmission)

4) Status and Control

5) Uninitialization

Overview

In Linux, network drivers have different properties than other drivers like char drivers and block drivers. Char and block drivers have major and minor number concept . VFS identifies these drivers using their major and minor numbers and these drivers have files created in /dev directory with their major and minor numbers . But network drivers do not have any major or minor number concept and no files created in /dev directory. Network drivers are identified in the kernel with its interface descriptor block ,struct net_device (defined in linux/include/linux/netdevice.h). net_device objects of all network drivers are put into a global liked list and accessed by the kernel whenever it needs. An application cannot access the driver directly and it should go through the system calls like socket .

As you know, any device can be interfaced with CPU using any bus like PCI , USB, Firewire etc .

Network interface card also can be interfaced with CPU using any of the above said buses . Network interface card can be inbuilt into mother board or inserted into any bus slot. In this article we assume that network interface card is a PCI device. When a packet arrives, network interface card sees the destination MAC address of the packet and puts the packet in input data buffer(RxRing) if the destination MAC address matches with its MAC address , raises an interrupt and continue receiving the packets. If Driver wants to send a packet it puts the packet in output buffer(TxBuffer) . Network interface card takes the packet from TxBuffer and puts that in its controller’s FIFO buffer and try to send the packet. Once the packet is sent network interface card raises an interrupt and writes status of the interrupt in its status registers. Any Network interface driver is responsible for activities in the following areas:

Initialization

Packet Reception

Packet Transmission

Status and Control

Uninitialization

Initialization

The initialization part of a network driver has the responsibility of initializing the driver and hardware. The following are the some of the responsibilities of initialization part of the driver.

Registering with the Linux low level bus interface subsystem

Allocating interface descriptor block (net_device) ,device specific structure and initializing media

specific fields

Getting device specific structure object pointer

Enabling Network interface card

Getting the Device resources (Memory mapped or port mapped I/O register map)

Getting device MAC address

Initialization of device methods in the net_device

Registering net_device object with the kernel

Registering the interrupt handler (ISR)

Allocating Rxring and Txring

Initializing the hardware (network interface card)

Start the network interface’s transmit Queue

2.1) Registering with the Linux low level bus interface subsystem

Linux provides low level interface to access bus. For PCI devices PCI subsystem is provided. This

provides functions for accessing PCI devices. For USB devices USB subsystem is provided. The PCI subsystem in the kernel provides all the generic functions that are used in common by various PCI device drivers to access a PCI device. First step in initialization part of the driver should be registering with PCI subsystem. Once registration is over PCI subsystem will notify the driver whenever the device is found on the bus by calling probe function of the driver. The following code snippet shows registration with PCI subsystem. This code would be written in initialization routine of the driver module.

static struct pci_device_id rtl8139_pci_tbl[] = {

{0×10ec, 0×8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },

{}, /*Null terminated entry*/

}

static struct pci_driver rtl8139_pci_driver = {

.name = DRV_NAME,

.id_table = rtl8139_pci_tbl,

.probe = rtl8139_init_one,

.remove = __devexit_p(rtl8139_remove_one),

#ifdef CONFIG_PM

.suspend = rtl8139_suspend,

.resume = rtl8139_resume,

#endif /* CONFIG_PM */

};

static int __init rtl8139_init_module (void)

{

return pci_register_driver(&rtl8139_pci_driver);

}

To register with the PCI subsystem,the driver should create a struct pci_driver object , fill in the

object and call pci_register_driver function. pci_register_driver function takes one parameter , pointer to

an object of type struct pci_driver . The following are some of the fields of pci_driver:

‘name’ field is name of this driver,

‘probe’ field is a call back function which is called whenever your network controller is found on the PCI bus. It’s prototype is:

int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);

‘id_table’ field is struct pci_device_id pointer which contains the device information

that you are writing driver for. struct pci_device_id has the following definition:

struct pci_device_id {

__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/

__u32 subvendor, subdevice; /* Subsystem ID’s or PCI_ANY_ID */

__u32 class, class_mask; /* (class,subclass,prog­if) triplet */

kernel_ulong_t driver_data; /* Data private to the driver */

};

‘vendor’ field is vendor of the any PCI device(in this case network interface card ), ‘device ‘ field is product or device id of the device , subvendor is subvendor of the device , ‘class’ is class of the device (see PCI specification for different types classes ), driver_data is a private field that can be used by driver.

‘remove’ is a call back function which is called whenever network interface card is removed or

when the driver module is unloaded. Prototype of this function is:

void (*remove) (struct pci_dev *dev)

’suspend ‘ is a call back function which is called whenever device is to be suspended and should not process any more packets. Prototype of this function is:

int (*suspend) (struct pci_dev *dev, pm_message_t state);

‘resume’ is a call back function which is called by the PCI layer whenever suspended device wants to be woken up. Prototype of this function is:

int (*resume) (struct pci_dev *dev);

Once the driver creates pci_driver object it is registered with the PCI subsystem using

pci_register_driver function. Whenever our device (network interface card) is found on the bus, PCI subsystem calls driver’s ‘probe’ function. probe function receives two parameters. One is pointer to

struct pci_dev object and second is pointer to struct pci_device_id object . Each PCI device is an object of type struct pci_dev . The following is the 8139too.c probe function.

static int __devinit rtl8139_init_one (struct pci_dev *pdev, onst struct pci_device_id *ent)

c

{

struct net_device *dev = NULL;

struct rtl8139_private *tp;

}

2.2) Allocating interface descriptor block (net_device) ,device specific structure and

initializing media specific fields

Each interface is an object of type struct net_device , called interface descriptor block. Interface

can represent a specific media like Ethernet,Token ring and fddi etc. Interface descriptor block is a

combination of device specific fields and media specific fields . the following function allocates

interface descriptor block struct net_device.

dev = alloc_netdev(sizeof(*tp),”eth%d”,ether_setup);

This function creates struct net_device object ,allocates space for device specific structure

s name to this interface as ‘eth%d’ (%d

struct rtl8139_private (first argument) and assign

specifies eth0 for first interface ,eth1 for second interface etc)(second argument).

struct rtl8139_private is a device specific structure .There will be an object of this structure

for each interface found on the network interface card. Each driver will define its own device specific

structure. struct rtl8139_private definition can be found in 8139too.c file.

Realtek 8139 driver is a Ethernet driver. So we need to initialize Ethernet media specific fields of

n to

struct net_device object . alloc_netdev function calls ether_setup (third argument) functio

initialize the Ethernet media specific fields.

ether_setup is a kernel function which initialize some of the ethernet specific fields . tr_setup function

can be used to initialize token ring device fields. fddi_setup function can be used for fddi devices.

These all three tasks , allocating net_device , allocating space for device specific structure and initializing

media specific fields can also be performed by calling alloc_etherdev media specific function. The

following code shows that.

dev = alloc_etherdev(sizeof(*tp));

This allocates net_device object ,gives its name as eth%d and initialize ethernet media specific

fields.

For token ring devices you have alloc_trdev and for fddi devices you have alloc_fddidev functions .

If you want more about struct net_device you can refer Understanding Linux network internals

book.

2.3 Getting device specific structure object pointer

Memory allocated for the device specific object will be pointed by the ‘priv’ field of the net_device

object. This can be stored into a local variable using netdev_priv function. Direct accessing of ‘priv’

field is discouraged.

tp = netdev_priv(dev);

This will store the pointer to device specific structure into ‘tp’ local pointer variable.

2.4 Enabling Network Interface Card

At the initialization time , network interface card will be in idle state . we need to enable the network

interface card by setting enable bit in the command register of the device configuration space. This can

be done by calling pci_enable_device function.

rc = pci_enable_device(pdev);

if(rc)

goto err_out;

2.5 Getting the Device resources (Memory mapped or Port mapped register map )

To talk to the network interface card , network controller provides some registers. Driver has to map

these registers into processor address space so that read/write operations by the driverwill be made on

system memory addresses directly. PCI devices can provide two types of memory mapping, one is

memory mapped I/O and second is port mapped I/ . O

Details of each register will be explained in the device specification. At the time of device enumeration,

base address of the registers, some flags and total size of these registers are stored in ‘resources’ field of

the struct pci_dev object. Network interface driver needs to get these details and store them in

device specific structure to be used later. The following code snippet shows this:

pio_start = pci_resource_start (pdev, 0);

pio_end = pci_resource_end (pdev, 0);

pio_flags = pci_resource_flags (pdev, 0);

pio_len = pci_resource_len (pdev, 0);

mmio_start = pci_resource_start (pdev, 1);

mmio_end = pci_resource_end (pdev, 1);

mmio_flags = pci_resource_flags (pdev, 1);

mmio_len = pci_resource_len (pdev, 1);

Each PCI device can support upto six base addresses like base address 0,base address 1 etc . One for

memory mapped ,one for port mapped and etc .For each base address an object of type struct

resource is created and filled with base address ,size and flags . Driver has to read each struct

resource object and get the base address and size and flags..

To get the base address of a device you can use pci_resource_start macro. This macro takes two

parameters , one is pci_dev object of the device that you want base address and another is base address

register number. Base address register number can be one of 0­5 numbers.

To get the end of base address you can use pci_resource_end macro,to get size of the registers you can

use pci_resource_len macro.

To get flags of the device you can use pci_resource_flags macro. Flags can be any of the following.

IORESOURCE_IO /* Resource type */

IORESOURCE_MEM

IORESOURCE_IRQ

IORESOURCE_DMA

IORESOURCE_PREFETCH

IORESOURCE_READONLY

IORESOURCE_CACHEABLE

IORESOURCE_RANGELENGTH

IORESOURCE_SHADOWABLE

IORESOURCE_SIZEALIGN /* size indicates alignment */

IORESOURCE_STARTAL IGN /* start field is alignment */

IORESOURCE_DISABLED

IORESOURCE_UNSET

IORESOURCE_AUTO

IORESOURCE_BUSY /* Driver has marked this resource busy */

As a driver developer you need to know only IORESOURCE_IO, IORESOURCE_MEM and

IORESOURCE_BUSY flags. IORESOURCE_IO flags tel s that this base address is port mapped ,

l

IORESOURCE_MEM tells that this base address is memory mapped and IORESOURCE_BUSY tells

that this resource is reserved by this driver. If resources are reserved, another driver cannot use these

resources.

Once you got the device resources you need to check them for what type of mapping they are. The

following code shows that :

/* make sure PCI base addr 0 is PIO */

if (!(pio_flags & IORESOURCE_IO)) {

dev_err(&pdev­>dev, “region #0 not a PIO resource, aborting\n”);

rc = ­ENODEV;

goto err_out;

}

/* make sure PCI base addr 1 is MMIO */

if (!(mmio_flags & IORESOURCE_MEM)) {

dev_err(&pdev­>dev, “region #1 not an MMIO resource, aborting\n”);

rc = ­ENODEV;

goto err_out;

}

After checking the resources you have to reserve the resources by calling pci_request_regions function.

This function reserves the resources and returns error if they are already reserved by another driver.

rc = pci_request_regions (pdev, DRV_NAME);

if (rc)

goto err_out;

Some PCI devices have the capability of bus mastering . We need to enable it by calling pci_set_master

function. This function sets the bus mastering bit of the command register in the device configuration

space.

/* enable PCI bus­mastering */

pci_set_master(pdev);

Device resources have to be remapped into the kernel address space so that page tables will be created

for the registers. For the Port mapped I/O this is done using ioport_map and for memory mapped I/O

this is done using pci_iomap function. After remapping you need to store base address in the

‘base_addr’ field of net_device object and also store base address and resource length in the device

specific object to be used later.

#ifdef USE_IO_OPS

ioaddr = ioport_map(pio_start, pio_len);

if (!ioaddr) {

dev_err(&pdev­>dev, “cannot map PIO, aborting\n”);

rc = ­EIO;

goto err_out;

}

dev­>base_addr = pio_start;

tp­>mmio_addr = ioaddr;

tp­>regs_len = pio_len;

#else

/* ioremap MMIO region */

ioaddr = pci_iomap(pdev, 1, 0);

if (ioaddr == NULL) {

dev_err(&pdev­>dev, “cannot remap MMIO, aborting\n”);

rc = ­EIO;

goto err_out;

}

dev­>base_addr = (long) ioaddr;

tp­>mmio_addr = ioaddr;

tp­>regs_len = mmio_len;

#endif /* USE_IO_OPS */

Once you got resources you need to reset the controller chip. This can be done by setting reset bit of the

command register of the controller.(see the device specification)

/* Soft reset the chip. */

RTL_W8 (ChipCmd, CmdReset);

/* Check that the chip has finished the reset. */

for (i = 1000; i > 0; i­­) {

barrier();

if ((RTL_R8 (ChipCmd) & CmdReset) == 0)

break;

udelay (10);

}

RTL_W8 macro definition is:

#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg));

2.6 Getting device MAC address

Driver has to read the MAC address stored in the ROM of the network interface card . To access ROM

of the network interface card , some of the registers of the memory mapped or port mapped I/O registers

are used. This details can be found in the device specification.8139too driver calls read_eeprom local

function to read the MAC address from the ROM of the controller. This MAC address is stored in the

‘dev_addr’ and ‘perm_addr’ fields of net_device object.

addr_len = read_eeprom (ioaddr, 0, 8) == 0×8129 ? 8 : 6;

for (i = 0; i <>

((__le16 *) (dev­>dev_addr))[i] = cpu_to_le16(read_eeprom (ioaddr, i + 7, addr_len));

memcpy(dev­>perm_addr, dev­>dev_addr, dev­>addr_len);

2.7 Filling device methods in the net_device

Driver has to provide it’s functionalities to the kernel through the device methods of struct

net_device . These are the operations that can be performed on the network interface. Some of the

function pointers of net_device structure are left blank and some are filled by ‘ether_setup’ function

called at the time of net_device object allocation.

Driver should fill some basic fundamenta l operations and can leave optional operations. Fundamental

methods are those that are needed to be able to use the interface; optional methods implement more

advanced functionalities that are not strictly required.

/* The Rtl8139­specific entries in the device structure. */

dev­>open = rtl8139_open;

dev­>hard_start_xmit = rtl8139_start_xmit;

netif_napi_add(dev, &tp­>napi, rtl8139_poll, 64);

dev­>stop = rtl8139_close;

dev­>get_stats = rtl8139_get_stats;

dev­>set_multicast_list = rtl8139_set_rx_mode;

dev­>do_ioctl = netdev_ioctl;

dev­>ethtool_ops = &rtl8139_ethtool_ops;

dev­>tx_timeout = rtl8139_tx_timeout;

dev­>watchdog_timeo = TX_TIMEOUT;

#ifdef CONFIG_NET_POLL_CONTROLLER

dev­>poll_controller = rtl8139_poll_controller;

#endif

‘open ‘ function opens the interface and is called whenever user configures the interface using any

utilities like ifconfig or ip . This function explained later in detail.

‘hard_start_xmit’ function is called whenever kernel wants to send a packet . This function is

explained later in detail.

‘rtl8139_poll ‘ is called whenever there is an incoming packet to be processed. This function is

explained later in detail.

’stop’ function stops the interface and is called whenever interface is brought down. This function

is explained later in detail.

‘get_stats’ is called whenever an application needs to know statistics of the interface . This

function is explained later in detail.

’set_multicast_list’ is an optional method and is called when the multicast list for the device

changes and when the flags change . We are not going to explain this function.

‘do_ioctl’ is interface specific function . This is an optional routine and we are not going to

explain.

‘ethtool_ops’ is a pointer to a structure of type struct ethtool_ops. This structure contains some

function pointers that are used by the ethtool tool. This we are not going to explain.

‘poll_controller’ is called whenever kernel wants to poll for the device status. This function

simply calls drivers interrupt routine to check if the device has anything to say.

After this some of the net_device and device specific object fields are filled .

dev­>features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;

dev­>irq = pdev­>irq;

/* tp zeroed and aligned in alloc_etherdev */

tp­>mmio_addr = ioaddr;

‘features’ field of net_device object tells the capabilities of the driver like it support scatter gather

I/O ,check sum can be calculated in hardware and can DMA to high memory etc.’irq’ field of net_device

object contains irq number of this controller.

2.8 Registering net_device object with kernel

Driver has to register with the kernel by giving net_device object . All net_device objects of all

interfaces are put into linked lists and accessed by the kernel whenever it needs. Network driver can

register with the kernel using register_netdev function.

i = register_netdev(dev);

register_netdev function takes a completed net_device object and adds it to the kernel interfaces.

0 is returned on success and a negative error no code is returned on a failure to set up the device, or if

the name is a duplicate.

struct net_device object is stored in struct pci_dev object so that it can be accessed later.

This is done with pci_set_drvdata function.

pci_set_drvdata(pdev , dev);

} /*end of rtl8139_init_one function */

/*start of open function */

static int rtl8139_open (struct net_device *dev)

{

open function is called by the kernel whenever this network interface is configured by admin using any

user space utilities like ifconfig or ip .When ifconfig is used to assign an address to the interface, it

performs two tasks. First, it assigns the address by means of ioctl(SIOCSIFADDR) (Socket I/O Control

Set Interface Address). Then it sets the IFF_UP bit in dev­>flag by means of

ioctl(SIOCSIFFLAGS) (Socket I/O Control Set Interface Flags) to turn the interface on.

open function receives net_device object as its parameter. Driver should get the device specific

object which is stored in the ‘priv’ field of net_device object at the time of net_device object

allocation. This can be done by calling netdev_priv inline function.

struct rtl8139_private *tp = netdev_priv(dev);

/* get registers base address in a local variable */

void __iomem *ioaddr = tp­>mmio_addr;

2.9 Registering Interrupt handler (ISR)

Whenever a packet is received or a packet is sent an interrupt is raised by the network controller. Driver

needs to register an interrupt handler(ISR ) and this handler is called whenever controller raises an

interrupt. Driver can register the interrupt handler either in driver’s init routine or in the open function .

Driver registers with interrupt handler using request_irq routine.

retval = request_irq (dev­>irq, rtl8139_interrupt, IRQF_SHARED, dev­>name, dev);

if (retval)

return retval;

request_irq routine takes five parameters. First parameter is irq number of the device , second parameter

is interrupt handler (ISR) ,Third parameter is irqflags,fourth parameter is device name and last parameter

is dev_id. ‘dev_id’ feild can be of any object pointer ,is necessary if irqflags is IRQF_SHARED and

used at the time of freeing the ISR using free_irq routine. Generally this field will be a pointer to

net_device instance and is used at the time of interrupt handler execution. Interrupt handler has the

following prototype:

irqreturn_t (*irq_handler_t)(in t, void *)

and irqflags can be any of the following.

* IRQF_DISABLED ­ keep irqs disabled when calling the action handler

* IRQF_SAMPLE_RANDOM ­ irq is used to feed the random generator

* IRQF_SHARED ­ allow sharing the irq among several devices

* IRQF_PROBE_SHARED ­ set by callers when they expect sharing mismatches to

occur

* IRQF_TIMER ­ Flag to mark this interrupt as timer interrupt

* IRQF_PERCPU ­ Interrupt is per cpu

* IRQF_NOBALANCING ­ lag to exclude this interrupt from irq balancing

F

* IRQF_IRQPOLL ­ Interrupt is used for polling (only the interrupt that is registered first

in an shared interrupt is considered for performance reasons)

request_irq routine allocates interrupt resources and enables the interrupt line and IRQ handling. From

the point this call is made driver handler function may be invoked. Since driver handler function must

clear any interrupt the board raises, driver must take care both to initialize the hardware and to set up

the interrupt handler in the right order.

We will describe functionalities of interrupt handler(ISR) later in this article.

2.10 Allocating Rxring and Txbuffer

Whenever network controller receives a packet it puts that in a receive buffer called RxRing and raises an

interrupt and continue to receive next packet. Driver puts outgoing packets in Txbuffer and network

controller takes the packets from the Txbuffer , sends them out of the wire and raises interrupt. We will

describe this process later in detail. Driver allocates Rxring and Txbuffer using dma_alloc_coherent

routine. If Memory allocated for the buffers ,the by dma_free_coherent routine returns virtual address

of the buffers .

tp­>tx_bufs = dma_alloc_coherent(&tp­>pci_dev­>dev,TX_BUF_TOT_LEN,

&tp­>tx_bufs_dma, GFP_KERNEL);

tp­>rx_ring = dma_alloc_coherent(&tp­>pci_dev­>dev, X_BUF_TOT_LEN, R

&tp­>rx_ring_dma, GFP_KERNEL);

if (tp­>tx_bufs == NULL || tp­>rx_ring == NULL) {

free_irq(dev­>irq, dev);

if (tp­>tx_bufs)

dma_free_coherent(&tp­>pci_dev­>dev,TX_BUF_TOT_LEN,

tp­>tx_bufs, tp­>tx_bufs_dma);

if (tp­>rx_ring)

dma_free_coherent(&tp­>pci_dev­>dev,RX_BUF_TOT_LEN,

tp­>rx_ring, tp­>rx_ring_dma);

return ­ENOMEM;

}

dma_alloc_coherent routine takes four parameters. First is generic device struct device object ,

second is length of the buffer ,third is an output param of type dma_addr_t which is filled with physical

address (bus address) of the allocated memory and fifth is GFP flag which tells how the memory should

be allocated..This function returns virtual address of the memory allocated.

2.11 Initialize the hardware (network interface card)

Network driver has to initialize network controller at the time of open function is called. Controller

initialization includes, resetting the chip , restoring MAC address in the chip register enabling ,

reception(rx) and transmission by setting rxenable and txenable bits in command register of the

controller,setting transfer thresholds , initializing transmission descriptors and reception descriptor of the

controller with the physical addresses of the Txbuffer and RxRing,setting what type of packets to be

received by setting in RxConfig and TxConfig registers, and enabling interrupts by setting Interrupt

Mask Register(IMR) .

1 Soft reset the chip

RTL_W8 (ChipCmd, CmdReset);

/* Check that the chip has finished the reset. */

for (i = 1000; i > 0; i­­) {

barrier();

if ((RTL_R8 (ChipCmd) & CmdReset) == 0)

break;

udelay (10);

}

2 Restore the MAC address

RTL_W32_F (MAC0 + 0, le32_to_cpu (*(__le32 *) (dev­>dev_addr + 0)));

RTL_W32_F (MAC0 + 4, le16_to_cpu (*(__le16 *) (dev­>dev_addr + 4)));

3 Enable transmission and reception

RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb);

4 Set type of packets to be received

RTL_W32 (RxConfig, tp­>rx_config);

RTL_W32 (TxConfig, rtl8139_tx_config);

5 initialize reception buffer descriptor(RxBuf) with RxRing DMA address(bus address).This s

i

where physical address used.

RTL_W32_F (RxBuf, tp­>rx_ring_dma);

6 Initialize transmission descriptors with Txbuffer DMA address (bus address)

for (i = 0; i <>

RTL_W32_F (TxAddr0 + (i * 4), tp­>tx_bufs_dma + (tp­>tx_buf[i] ­ tp­>tx_bufs));

7 Enable all known interrupts by setting the interrupt mask register

RTL_W16 (IntrMask, rtl8139_intr_mask);

2.12 Start the network interface’s transmit Queue

The open function should also start the interface’s transmit queue (allowing it to accept packets for

transmission) once it is ready to start sending data. Driver should call kernel function netif_start_queue

to start queue.

netif_start_queue (dev);

netif_start_queue takes net_device object as its parameter and returns nothing. This function simply

sets a bit in ’state’ field of net_device object that allows upper layers to call the device

hard_start_xmit function .

}/ * end of open function */

3 Packet handling

Packet handling is a task of performing transmission and reception of packets. Packet handling is most

important task of any network interface driver. in kernel discussions, transmission refers only to sending

frames outward, whereas reception refers to frames coming in.

Before going to see how transmission and reception happen we will see the role of interrupts in network

drivers. Kernel can use two main techniques for exchanging data: pol ing and interrupts. There is also an

l

option of combination of these two techniques.

Polling is a technique where the kernel constantly keeps checking whether the device has anything to

say. It can do that by continually reading a memory register on the device, for instance, or returning to

check it when a timer expires.

Interrupts is another technique technique of exchanging data. Here the device driver, on behalf of the

kernel, instructs the device to generate a hardware interrupt when specific events occur. The kernel,

interrupted from its other activities, will then invoke a handler registered by the driver to take care of the

device’s needs. Interrupts can be raised by the device when frame is received and when a frame is

transmitted .. If this is the reception of a frame, the handler queues the frame somewhere and notifies the

kernel about it and if it is transmission the handler updates its status.

The code that takes care of an input frame is split into two parts: first the driver

copies the frame into an

input queue accessible by the kernel, and then the kernel processes it (usually passing it to a handler

dedicated to the associated protocol such as IP). The first part is executed in interrupt context and second

part is executed in the bottom half ..Second part may interrupted by the first because interrupt context

has the higher priority than bottom half . More about bottom halves can be read in Understanding Linux

Kernel and Understanding Linux network internals books.

Multiple Frames also can be processed During an Interrupt. This approach is used by quite a few Linux

device drivers. When an interrupt is notified and the driver handler is executed, the latter keeps

downloading frames and queuing them to the kernel input queue, up to a maximum number of frames

Timer­Driven Interrupts technique is an enhancement to the previous ones. Instead of having the device

asynchronously notify the driver about frame receptions, the driver instructs the device to generate an

interrupt at regular intervals. The handler will then check if any frames have arrived since the previous

interrupt, and handles all of them in one shot.

Combination of all above said techniques is also possible . A good combination would use the interrupt

technique under low load and switch to the timer­driven interrupt under high load.

Pros and cons of all above said and more detail description of above said techniques can be found in

Understanding Linux Network Internals book by Christian Benvenuti.

3.1 Packet Reception

When a packet arrives into the network interface card , network controller checks the destination MAC

address of the packet. if it matches with its MAC address or if it is broadcast address network interface

card copies the packet into receive buffer(Rxring) and raises an interrupt.

Receive buffer(Rxrinf) is a block of I/O memory allocated by the river . Network interface card will

d

have a receive buffer descriptor register and a receive buffer status register. Driver has to write Physical

address of the receive buffer allocated into the receive buffer descriptor register. This process is specific

to hardware and is provided in the device manual. Some devices provide some receive block

descriptors,a structure,that contains status of the each packet , buffer pointer and size of packets etc.

Driver has to allocate a buffer and physical address of this is put in the buffer pointer .

When a packet is received by the network interface card, it adds a packet header before the packet , puts

the packet in receive buffer(Rxring) and raises interrupt. This is specific to Realtek 8139 chips and see

device specification.

When the interrupt is raised , driver interrupt handler(ISR) gets called. Driver interrupt handler has to

check the interrupt status register(ISR) of the device what the interrupt is raised for and has to take

appropriate action. In the case of reception ,reception bit of interrupt status register is set. Now we will

see step by step what interrupt handler does:

3.1.1 Interrupt handler of the driver:

Interrupt handler is called when a packet is received or transmitted. Kernel will send two arguments to

this function . One is irq number and other is dev_id which is sent as last parameter of request_irq

function at the time of interrupt handler registration. Generally this is a pointer to net_device object.

As i said, driver has to check status register of the network interface card and should take action

accordingly. In the case of packet reception , driver should schedule a bottom half and return from

interrupt handler. The next step of processing of packet is done by the kernel in the bottom half. In the

bottom half, kernel calls driver’s poll function to do the later processing of packet.

static irqreturn_t rtl8139_interrupt (int irq, void *dev_id)

{

1 Get net_device object from the dev_id

struct net_device *dev = (struct net_device *) dev_id;

2 Get device specific structure object from the net_device. This is stored in the ‘priv’ field of

net_device. Use netdev_priv inline function to get pointer to that and store registers base

address into a local variable ‘ioaddr’ .

struct rtl8139_private *tp = netdev_priv(dev);

void __iomem *ioaddr = tp­>mmio_addr;

3 Get interrupt status register (ISR) of the controller into a local variable

status = RTL_R16 (IntrStatus);

4 Irq numbers in PCI devices can be shared by many devices. So many devices might have been

registered interrupt handler on the same irq number . Driver has to confirm that interrupt has

been raised by its interface card . This can be done by checking interrupt status register for

any pending interrupts by the interface card.

/* shared irq? */

if (unlikely((status & rtl8139_intr_mask) == 0))

goto out;

5 Check if the device is present or not (hot pluggable) or if there is major problem.

if (unlikely(status == 0xFFFF))

goto out;

6 Driver has to acknowledge the interrupts by clearing appropriate bits in the interrupt status

register (ISR)

ackstat = status & ~(RxAckBits | TxErr);

if (ackstat)

RTL_W16 (IntrStatus, ackstat);

7 As i said , receive packets are processed by poll function vector in the bottom handler.

Network uses softirq NET_ RX_SOFTIRQ bottom half for input packets. Driver should

schedule the bottom half and finish the interrupt handler. netif_rx_schedule is used to enable

NET_RX_SOFTIRQ bottom half. This functions takes two parameters, one is net_device

object and other is an object of type struct napi_struct . This structure has the

following definition.

struct napi_struct {

/* The poll_list must only be managed by the entity which

* changes the state of the NAPI_STATE_SCHED bit. This means

* whoever atomically sets that bit can add this napi_struct

* to the per­cpu poll_list, and whoever clears that bit

* can remove from the list right before clearing the bit.

*/

struct list_head poll_list;

unsigned long state;

int weight;

int (*poll)(struct napi_struct *, int);

#ifdef CONFIG_NETPOLL

spinlock_t poll_lock;

int poll_owner;

struct net_device *dev;

struct list_head dev_list;

#endif

};

This object is filled at the time of function pointers assignment, using netif_napi_add

function.

The following code checks status of the interrupt and calls netif_rx_schedule to

enable the bottom half. netif_rx_schdule function first tests if poll needs to be scheduled

using netif_rx_schedule_prep, is scheduled only if network interface up and next calls

__netif_rx_schedule to schedule poll .

if (status & RxAckBits)

netif_rx_schedule(dev, &tp­>napi);

8 return from the interrupt handler. (we will see transmission part of interrupt handler later in

Packet transmission section )

return IRQ_RETVAL(handled);

} /* end of interrupt handler routine */

3.1.2 NET_RX_SOFTIRQ softirq calls driver’s poll method

Kernel calls poll method of the driver in the NET_RX_SOFTIRQ softirq bottom half to process the

input packet . This softirq was scheduled in interrupt handler routine.

poll method receives two parameters, one is pointer to struct napi_struct object and second is

‘budget’. napi_struct object is the object which was created at the time of initialization of methods in

the net_device object .’budget’ field is the maximum number of packets the kernel can accept at this

time or we can say that the ‘budget’ value is a maximum number of packets that the current CPU can

receive from all interfaces.

The following is the 8139too.c driver poll method. It checks for the interrupt status register and calls

rtl8139_rx local function to do the rest of the process.

static int rtl8139_poll(struct napi_struct *napi, int budget)

{

1 Get device specific structure object. ‘napi’ is a field of type stuct napi_struct in device

specific structure struct rtl8139_private. If we know the address of ‘napi’ field of

struct rtl8139_private we can find the starting address of the struct

rtl8139_private object using container_of function.

struct rtl8139_private *tp = container_of(napi, struct rtl8139_private, napi);

2 Get net_device object from the struct rtl8139_private object and store register

base address into a local variable.

struct net_device *dev = tp­>dev;

void __iomem *ioaddr = tp­>mmio_addr;

3 Check if it was the receive interrupt and call rtl8139_rx local function. rtl8139_rx returns

number of packets received. This function is explained below.

if (likely(RTL_R16(IntrStatus) & RxAckB its))

work_done += rtl8139_rx(dev, tp, budget);

4 If number of packets received is less than budget , re­enable receive interrupts by setting the

interrupts mask regsiter(IMR) and all __netif_rx_complete function to turn of polling.

c

RTL_W16_F(IntrMask, rtl8139 _intr_mask);

__netif_rx_complete(dev, napi);

__netif_rx_complete function removes this interface from the polling list.

5 Return number of packets received

return work_done;

} /*end of rtl8139_poll method*/

A received packet is put into a structure struct sk_buff called socket buffer. This structure is the

main encapsulation of a packet and contains pointers to point different layer headers in the packet and

pointers to input ,output interfaces . More about this structure can be read in Understanding Linux

network internals book.

Driver has to take the packet from the RxRing , copy that into sk_buff object and give it to the kernel.

rtl8139_rx local function will do that. This function receives three parameters , one is pointer to

net_device object ,second is pointer to device specific object struct rtl8139_private and last is

‘budget’.

static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, int budget)

{

void __iomem *ioaddr = tp­>mmio_addr;

int received = 0;

1 Get pointer to RxRing

unsigned char *rx_ring = tp­>rx_ring;

2 Get current packet offeset (see device specification)

unsigned int cur_rx = tp­>cur_rx;

3 Now the driver should run in a loop and take the each packet from the RxRing . The loop

should run until three conditions are satisfied . Three conditions are network interface should be

up, received packets should be less than the ‘budget’ and the RxRing is not empty.

while (netif_running(dev) && received <>

&& (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {

struct sk_buf *skb;

4 Get the packet offset into the RxRing

u32 ring_offset = cur_rx % RX_BUF_LEN;

5 Get the packet header ( See device Manual) and find packet size. Note that packet size is

receive packet size minus 4( 4 is CRC)

/* read size+status of next frame from DMA ring buffer */

rx_status = le32_to_cpu (*(__le32 *) (rx_ring + ring_offset));

rx_size = rx_status >> 16; /*receive packet size from packet header*/

pkt_size = rx_size – 4; /* packet size */

6 Allocate struct sk_buff object.

skb = dev_alloc_skb (pkt_size + 2);

7 Copy packet into sk_buff . This will copy packet into ‘data’ field of sk_buff

skb_copy_to_linear_data (skb, &rx_ring[ring_offset + 4], pkt_size);

8 Set ‘protocol’ field of sk_buff to appropriate packet type. This is ‘type’ of ethernet

frame. Call eth_type_trans to get the packet type. For token ting devices you can use

tr_type_trans function.

skb­>protocol = eth_type_trans ( skb, dev);

9 Update statistics

tp­>stats.rx_bytes += pkt_size;

tp­>stats.rx_packets++;

10 Give the skb to kernel

netif_receive_skb (skb);

received ++;

} /* end of while*/

tp­>cu_rx = cur_rx;

return received;

}

netif_running inline function tests if the interface is up and running. It checks if

__LINK_STATE_START bit of ’status’ field of et_device object is set. dev_alloc_skb function

n

allocates memory for sk_buff from the cache and fills some the fields. netif_receive_skb is

main receive data processing function.

3.2 Packet Transmission

When the kernel has packets to send out of the interface ,it calls driver’s hard_start_xmit method.

hard_start_xmit function receives two parameters ,one is sk_buff of the packet to be transmitted and

another is net_device object .

sk_buff of the trasmitted packet is filled by the upper layers. ‘data’ field of sk_buff contains

packet to be sent. Driver should extract packet from the sk_buff and put that into TxBuffers. Then

driver should write length of packet and threshold in the Transmission descriptor status register of the

device . Then the device takes the packet from the Txbuffers and sends it.

Now we will describe rtl8139_start_xmit(hard_start_xmit) function of 8139too driver.

static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)

{

struct rtl8139_private *tp = netdev_priv(dev);

void __iomem ioaddr = tp­>mmio_addr; /*register base address */

unsigned int len = skb­>len; /* length of the packet */

1 Calculate the next Tx descriptor entry

entry = tp­>cur_tx % NUM_TX_DESC;

2 Copy the packet into TxBuffer

skb_copy_and_csum_dev(skb, tp­>tx_buf[entry]);

3 Free the socket buffer sk_buff

dev_kfree_skb(skb);

4 Writer length of packet and threshold in Transmission descripor status register

RTL_W32_F (TxStatus0 + entry * sizeof (u32)),

(

tp­>tx_flag | max(len, (unsigned int)ETH_ZLEN));

} /* end of tl8139_start_xmit */

Network interface card will take the packet from the TxBuffer and puts that in it’s FIFO. Once the FIFO

reached threshold value set by the driver it sends the packet. After sending the packet network interface

card will raise an interrupt. Driver’s interrupt handler will be called.

Driver’s interrupt handler should check why the interrupt has occurred and if it is transmission interrupt it

updates its statistics. The following code shows how rtl8139too driver will handle the transmission

interrupt.

static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance)

{

struct net_device *dev = (struct net_device *) dev_instance;

struct rtl8139_private *tp = netdev_priv(dev);

void __iomem *ioaddr = tp­>mmio_addr;

status = status = RTL_R16 (IntrStatus);

1 Check if the interrupt is transmission interrupt and call rtl8139_tx_interrupt local

function.

if (status & (TxOK | TxErr)) {

rtl8139_tx_interrupt (dev, tp, ioaddr);

if (status & TxErr)

RTL_W16 (IntrStatus, TxErr);

} /* end of rtl8139_interrupt */

static void rtl8139_tx_interrupt (struct net_device *dev, struct rtl8139_private *tp,

void __iomem *ioaddr)

{

1 Read transmission descriptor status register and see what the status of the packet.

txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32)));

2 Increment error statistics if there are any problems in the transmission

if (txstatus & (TxOutOfWindow | TxAborted)) {

tp­>stats.tx_errors++;

if (txstatus & TxAborted) {

tp­>stats.tx_aborted_errors++;

RTL_W32 (TxConfig, TxClearAbt);

RTL_W16 (IntrStatus, TxErr);

}

if (txstatus & TxCarrierLost)

tp­>stats.tx_carrier_errors++;

if (txstatus & TxOutOfWindow)

tp­>stats.tx_window_errors++;

}

3 Increment statistics of successful transmitted packets

else {

tp­>stats.collisions += (txstatus >> 24) & 15;

tp­>stats.tx_bytes += txstatus & 0×7ff;

tp­>stats.tx_packets++;

}

4 start the transmission queue allowing kernel to call driver’s hard_start_xmit method

again.

netif_wake_queue (dev);

}/* End of rtl8139_tx_interrupt */

4 Status and Control

4.1 When kernel wants to stop interface it calls stop method of driver

When kernel wants to stop interface it calls stop function of driver. This is called ,for example, when the

interface is brought down by using any utilities like ifconfig. Responsibility of this function is would be

exactly opposite to what we have done in open method. Some of the responsibilities include freeing

receive and transmission buffers , freeing irq and stopping transmission queue etc.

stop takes struct net_device object as its parameter. The following code shows how 8139too.c

implements stop method.

static int rtl8139_close (struct net_device *dev)

{

struct rtl8139_private *tp = netdev_priv(dev);

void __iomem *ioaddr = tp­>mmio_addr;

unsigned long flags;

1 stop transmission queue. Once transmission queue is stopped kernel cannot send any more

packets to the driver

netif_stop_queue(dev);

2 Prevent poll function to be scheduled .

napi_disable(&tp­>napi);

3 Stop the chip’s Transmission and reception DMA processes . This can be done by writing 0 into

command register of device.

RTL_W8 (ChipCmd, 0);

4 Disable interrupts by clearing the interrupt mask register(IMR).

RTL_W16 (IntrMask, 0);

5 wait for pending IRQ handlers (on other CPUs) to be completed. This can be done using

synchronize_irq function.

synchronize_irq (dev­>irq);

6 Unregister the interrupt handler (ISR)

free_irq(dev­>irq,dev);

free_irq function removes an interrupt handler. The handler is removed and if the interrupt line

is no longer in use by any driver it is disabled. On a shared IRQ the driver must ensure the

interrupts are disabled by clearing interrupt mask register on the card it drives before calling

this function. This function does not return until any executing interrupts for this IRQ have

completed. This function must not be called from interrupt context. This function takes two

parameters one is irq line that is to be freed and second is ‘dev_id’ which is sent as last argument

to request_irq function.

7 Free receive(RxRing) and transmission(TxBuffer) buffers .

dma_free_coherent(&tp­>pci_dev­>dev,RX_BUF_TOT_LEN,

tp­>rx_ring, tp­>rx_ring_dma);

dma_free_coherent(&tp­>pci_dev­>dev,TX_BUF_TOT_LEN,

tp­>tx_bufs, tp­>tx_bufs_dma);

tp­>rx_ring = NULL;

tp­>tx_bufs = NULL;

return 0;

}

When Application wants statistics of the interface, drivers’s get_stats

4.

method is called

Whenever an application needs to get statistics for the interface, get_stats method of driver is called. This

happens, for example, when ifconfig or netstat ­i is used by the user.

This function receives struct net_device object as its parameter and returns struct

net_device_stats object. Driver has to fill in the struct net_device_stats object with

interface statistics stored in device specific structure rtl8139_private and return it. The

following is the get_stats implementation of 8139too.c driver.

static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)

{

struct rtl8139_private *tp = netdev_priv(dev);

void __iomem *ioaddr = tp­>mmio_addr;

/*return statistics stored in ’stats’ field of device specific object*/

return &tp­>stats;

}

5 Un­initialization

5.1 remove function of pci_driver (pci remove function)

pci_driver ’s remove method is called whenever network interface card is removed or when the

driver module is unloaded. Functionalities of this driver includes unregistering the net_device with

the kernel and disabling the network interface card,freeing resources etc . remove function takes

pci_dev object as its parameter and returns nothing.

static void __devexit rtl8139_remove_one (struct pci_dev *pdev)

{

1 Get net_device object from pci_dev object. We have stored net_device object in

pci_dev object by calling pci_set_drvdata in probe method of the driver.

struct net_device *dev = pci_get_drvdata (pdev);

2 flush if there are any packets to be transmitted yet. This can be done using

flush_scheduled_work function. flush_scheduled_work function starts the work queue

rtl8139_thread ,that is created at the time of probe function.

flush_scheduled_work();

3 Unregister the net_device object with kernel

unregister_netdev (dev);

4 Free IO Resources and net_device object

__rtl8139_cleanup_dev (dev);

5 Disable the network interface card .

pci_disable_device (pdev);

}

static void __rtl8139_cleanup_dev (struct net_device *dev)

{

struct rtl8139_private *tp = netdev_priv(dev);

struct pci_dev *pdev;

pdev = tp­>pci_dev;

1 Remove kernel page tables for IO resources

#ifdef USE_IO_OPS

if (tp­>mmio_addr)

ioport_unmap (tp­>mmio_addr);

#else

if (tp­>mmio_addr)

pci_iounmap (pdev, tp­>mmio_addr);

#endif /* USE_IO_OPS */

2 Release reserved PCI I/O and memory resources. These resources were previously reserved by

pci_request_regions function. This function takes pci_dev object as its parameter.

pci_release_regions (pdev);

3 Free net_device object using free_netdev function. free_netdev function does the last stage of

destroying an allocated device interface. The reference to the device object is released.

free_netdev(dev);

}

5.2 Unregistering driver with the low level bus interface (PCI Subsystem)

Last step in the network driver development will be unregistering with low level bus interface ,in this

case PCI subsystem. Driver can unregister with PCI subsystem using pci_unregister_driver function.

This function takes pci_driver object as its parameter.

Unregistering will be done in driver module’s cleanup routine.

static void __exit rtl8139_cleanup_module (void)

{

pci_unregister_driver (&rtl8139_pci_driver);

}

References:

1) PCI Local Bus specification

2) Device specifications (RTL8139D_DataSheet.pdf , RTL8139_ProgrammersGuide.pdf)

3) Understanding Linux Network Internals by By Christian Benvenu ti

4) Linux source code

comments and suggestions can be sent to mohan@techveda.org

Evolution of GNU/Linux system..must read for Linux newbies

I would like to introduce you to a chronology of events that happened in the early 80’s and 90’s.

For Richard Stallman things started to look bad with the collapse of the free community at the Artificial Intelligence lab at MIT in the early 80’s, with modern era operating systems, none of them free softwares, were coming with a nondisclosure agreement which said, you are not allowed to share or change the software and if you want to get something changed, ask us to do it for you.

This sounded anti-social to the software-sharing community that had existed for many years at the MIT, who enjoyed and agreed sharing their programs with universities and companies. And to see or to change the source code of an unfamiliar program to create a new one, was quite common.

After loosing his community, Stallman always had the choice of joining the proprietary software world , writing code under nondisclosure agreements, which he believed divided the software society and a means for not helping a fellow hacker (“Someone who loves to program and enjoys being clever about it”) or quiting the computer field, which was rather an unpleasant thing to do as it would have wasted his skills as an operating system developer. Other way round was to built the community back by writing free programs again.

GNU Project

Now the idea was pretty clear, what was needed first is an operating system. With a free operating system, a community of cooperating hackers would be able to use a computer without starting to deprive his or her friend. He chose to make the system compatible with Unix so that it would be portable, and Unix users could easily switch to it. The name GNU was chosen for the project following a hacker tradition, as a recursive acronym for “GNU’s Not Unix”.

The GNU project started with an objective to create a “free software” society, here the term “free” is often misunderstood, it has nothing to do with price. It is about freedom. It is defined as:

  • You have the freedom to run the program, for any purpose.
  • You have the freedom to modify the program to suit your needs. (To make this freedom effective in practice, you must have access to the source code, since making changes in a program without having the source code is exceedingly difficult.)
  • You have the freedom to redistribute copies, either gratis or for a fee.
  • You have the freedom to distribute modified versions of the program, so that the community can benefit from your improvements.

After quitting his job at MIT in 1984 Stallman began writing the GNU software. First he began by writing a compiler from scratch, which is now popularly known as GCC and the GNU Emacs editor for writing and editing his programs.

Free Software Foundation

As users of Emacs were growing, more people were getting involved in the GNU project, and this forced Stallman to look for some funding. So in 1985 the Free Software Foundation(FSF) was created, a tax-exempt charity for free software development. Since then Free Software Foundation employees have written and maintained a number of GNU software packages, two notable ones are the C library and the shell.

Gradually more and more programs were added to the GNU system and most of them gained popularity as they could run on the Unix systems, and users began extending them and porting them to the various incompatible versions of Unix, and sometimes to other systems as well.

By 1990 the GNU system was almost complete, with a major missing link, the kernel, which actually does the job of managing the system resources. The decision was to implement the kernel as a collection of server processes running on top of Mach, a microkernel developed at Carnegie Mellon University and then at the University of Utah. This kernel named the GNU HURD (or “herd of gnus”) could run on top of Mach, and do the various jobs of the Unix kernel.

GNU/Linux System

In 1991, a student from Finland named Linus Torvalds developed a Unix-compatible kernel and called it Linux. And around1992, combining Linux with the not-quite-complete GNU system resulted in a complete free operating system, the GNU/Linux system. It is due to Linux that a version of the GNU system could be run today.

GPL (GNU General Public License)

All the softwares under the GNU project were distributed under the GPL, which says that you can copy and distribute exact copies of the program’s source code as you have received it. You can make changes or modify the program and again redistribute under the first mentioned condition, with clear notices of your changes and date of that change.

Linux Distributions

Many Linux distributions based on the GNU/Linux system are currently available both as free copies and commercial distributions. Most of these distributors add up there own features, targeting specific areas like Enterprise, Desktop, Multimedia etc, to the existing GNU system, to cater diverse user sections. Some noted ones are RedHat, Fedora (an open project by RedHat), Debian, Suse from Novell, Mandriva, Ubuntu, Sabayon, PCLinuxOS, SimplyMEPIS, Knoppix, Gentoo etc. All these distributions intend to target different set of users. So you, now have the options of choosing the distribution based on your intended use, like suse, ubuntu, PCLinuxOS for user friendliness, debian, fedora for development, RedHat for Enterprise and so on. Least to say programming would be delightful on all of them.

Where do I get Linux

Most of the Linux distributions are freely available for download from the Internet;

fedora from download.fedora.redhat.com

suse from novell.com

debian from debian.org etc

There are also other links from where you can pull down these distributions. And if you do not want to waste time downloading, buy them from people like OSDisc.com, LinuxCD.org etc, but I am sure you would definitely find one, among your colleagues.

Inspiring linux learning,

Friday, February 26, 2010

Tutorial: A simple embedded Linux system

Foreword -- This article begins a series of tutorials on embedded Linux system development contributed by noted ARM Linux kernel hackers Vincent Sanders and Daniel Silverstone. In the initial installment, the Simtec engineers describe how embedded devices differ from other computers, and how to build "the most basic system possible."



Papers in the series have been contributed to LinuxDevices by Simtec Electronics, a 20-year-old U.K.-based company specializing in embedded hardware and software services, with special expertise in ARM Linux.

Other papers in the series (will) include:
  • Building a simple embedded system (this paper)
  • Building an embedded Linux system with a web server
  • Building an embedded Linux web kiosk
  • Building an ARM-based embedded Linux web kiosk
  • Improving an embedded Linux system
  • Deploying embedded Linux systems
Enjoy . . . !


Simple Embedded Linux System
by Vincent Sanders and Daniel Silverstone


Introduction

Constructing an embedded system with Linux is often seen as a complex undertaking. This article is the first in a series which will show the fundamental aspects of constructing such systems and enable the reader to apply this knowledge to their specific situation.

This first article covers the construction of the most basic system possible, which will provide a command shell on the console. Along with the rest of the series, it assumes a basic understanding of a Linux-based operating system. While discussing concepts and general approaches, these concepts are demonstrated with extensive practical examples. All the practical examples are based upon a Debian- or Ubuntu-based distribution.

What is an embedded system?

The term "Embedded System" has been applied to such a large number of systems that its meaning has become somewhat ill-defined. The term has been applied to everything from 4-bit microcontroller systems to huge industrial control systems.

The context in which we are using the term here is to refer to systems where the user is limited to a specific range of interaction with a limited number of applications (typically one). Thus, from the whole spectrum of applications which a general purpose computer can run, a very narrow selection is made by the creator of the embedded system software.

It should be realized that the limits of interaction with a system may involve hardware as well as software. For example, if a system is limited to a keypad with only the digits 0 to 9, user interaction will be more constrained than if the user had access to a full 102-key keyboard.

In addition to the limiting of user interaction, there may also be limits on the system resources available. Such limits are typically imposed by a system's cost, size, or environment. However, wherever possible, these limits should be arrived at with as much knowledge of the system requirements as possible. Many projects fail unnecessarily because an arbitrary limit has been set which makes a workable solution unachievable. An example of this would be the selection of a system's main memory size before the application's memory requirements have been determined.

What do you want to achieve?

A project must have a clearly defined goal.

This may be viewed as a statement of the obvious, but it bears repeating as for some unfortunately inexplicable reason, embedded systems seem to suffer from poorly-defined goals.

An "embedded" project, like any other, should have a clear statement of what must be achieved to be declared a success. The project brief must contain all the requirements, as well as a list of "desirable properties." It is essential that the two should not be confused; e.g., if the product must fit inside a 100mm by 80mm enclosure, that is a requirement. However, a statement that the lowest cost should be achieved is a desirable item, whereas a fixed upper cost would be a requirement.

If information necessary to formulate a requirement is not known, then it should be kept as a "desirable item" couched in terms of that unknown information. It may be possible that once that information is determined, a requirement can be added.

It is, again, self-evident that any project plan must be flexible enough to cope with changes to requirements, but it must be appreciated that such changes may have a huge impact on the whole project and, indeed, may invalidate central decisions which have already been made.

General IT project management is outside the scope of this article. Fortunately there exist many good references on this topic.

Requirements which might be added to a project brief based on the assumptions of this article are:
  • The system software will be based upon a Linux kernel.
  • The system software will use standard Unix-like tools and layout.
The implications of these statements mean the chosen hardware should have a Linux kernel port available, and must have sufficient resources to run the chosen programs.

Another important consideration is what kind of OS the project warrants. For example, if you have a project requirement of in-field updates, then you may want to use a full OS with package management, such as Debian GNU/Linux or Fedora. Such a requirement would, however, imply a need for a non-flash-based storage medium such as a hard disc for storing the OS, as these kinds of systems are typically very large (even in minimal installations), and not designed with the constraints of flash-based storage in mind. However, given that additional wrinkle, using an extant operating system can reduce software development costs significantly.

Anatomy of a Linux-based system

Much has been written on how Linux-based systems are put together; however a brief review is in order, to ensure that basic concepts are understood.

To be strictly correct the term "Linux" refers only to the kernel. Various arguments have been made as to whether the kernel constitutes an operating system (OS) in its entirety, or whether the term should refer to the whole assemblage of software that makes up the system. We use the latter interpretation here.

The general steps when any modern computer is turned on or reset is:
  • The CPU (or designated boot CPU on multi-core/processor systems) initializes its internal hardware state, loads microcode etc.
  • The CPU commences execution of the initial boot code, e.g., the BIOS on x86 or the boot-loader on ARM.
  • The boot code loads and executes the kernel. However, it is worth noting that x86 systems generally use the BIOS to load an intermediate loader such as GRUB or syslinux, which then fetches and starts the kernel.
  • The kernel configures the hardware and executes the init process.
  • The init process executes other processes to get all the required software running.
The kernel's role in the system is to provide a generic interface to programs, and arbitrate access to resources. Each program running on the system is called a process. Each operates as if it were the only process running. The kernel completely insulates a program from the implementation details of physical memory layout, peripheral access, networking, etc.

The first process executed is special in that it is not expected to exit, and is expected to perform some basic housekeeping tasks to keep a system running. Except in very specific circumstances, this process is provided by a program named /sbin/init. The init process typically starts a shell script at boot to execute additional programs.

Some projects have chosen to run their primary application as the init process. While this is possible, it is not recommended, as such a program is exceptionally difficult to debug and control. A programming bug in the application halts the system, and there is no way to debug the issue.

One feature of almost all Unix-like systems is the shell, an interactive command parser. Most common shells have the Bourne shell syntax.

A simple beginning

We shall now consider creating a minimal system. The approach taken here requires no additional hardware beyond the host PC, and the absolute minimum of additional software.

As already mentioned, these examples assume a Debian or Ubuntu host system. To use the QEMU emulator for testing, the host system must be supported by QEMU as a target. An example where this might not be the case is where the target system is x86-64, which QEMU does not support.

To ease construction of the examples, we will use the kernel's initramfs support. An initramfs is a gzip-compressed cpio archive of a file system. It is unpacked into a RAM disk at kernel initialization. A slight difference to normal system start-up is that while the first process executed must still be called init, it must be in the root of the file system. We will use the /init script to create some symbolic links and device nodes before executing the more-typical /sbin/init program.

This example system will use a program called Busybox, which provides a large number of utilities in a single executable, including a shell and an init process. Busybox is used extensively to build embedded systems of many types.

The busybox-static package is required to obtain pre-built copy of the Busybox binary and the qemupackage is required to test the constructed images. These may be obtained by executing:

$ sudo apt-get install busybox-static qemu


As mentioned, our initramfs-based approach requires a small /init script. This configures some basic device nodes and directories, mounts the special /sys and /proc file systems, and starts the processing of hotplug events using mdev.

#!/bin/sh

# Create all the busybox symbolic links
/bin/busybox --install -s

# Create base directories
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir --mode=0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
mkdir -p /var/lock

# Mount essential filesystems
mount -t sysfs none /sys -onodev,noexec,nosuid
mount -t proc none /proc -onodev,noexec,nosuid

# Create essential filesystem nodes
mknod /dev/zero c 1 5
mknod /dev/null c 1 3

mknod /dev/tty c 5 0
mknod /dev/console c 5 1
mknod /dev/ptmx c 5 2

mknod /dev/tty0 c 4 0
mknod /dev/tty1 c 4 1

echo "/sbin/mdev" > /proc/sys/kernel/hotplug

echo "Creating devices"
/sbin/mdev -s

exec /sbin/init


To construct the cpio archive, the following commands should be executed in a shell. Note, however, that INITSCRIPT should be replaced with the location of the above script.

$ mkdir simple
$ cd simple
$ mkdir -p bin sbin usr/bin usr/sbin
$ cp /bin/busybox bin/busybox
$ ln -s busybox bin/sh
$ cp INITSCRIPT init
$ chmod a+x init
$ find . | cpio --quiet -o -H newc | gzip >../simple.gz
$ cd ..


To test the constructed image use a command like:

$ qemu -kernel /boot/vmlinuz-2.6.26-1-686 -initrd simple.gz \
            -append "root=/dev/ram" /dev/zero


This should present a QEMU window where the OS you just constructed boots and displays the message "Please press Enter to activate this console." Press enter and you should be presented with an interactive shell from which you can experiment with the commands Busybox provides. This environment is executing entirely from a RAM disc and is completely volatile. As such, any changes you make will not persist when the emulator is stopped.

Booting a real system

Starting the image under emulation proves the image ought to work on a real system, but there is no substitute for testing on real hardware. The syslinux package allows us to construct bootable systems for standard PCs on DOS-formatted storage.

A suitable medium should be chosen to boot from, e.g., a DOS-formatted floppy disk or a DOS-formatted USB stick. The DOS partition of the USB stick must be marked bootable. Some USB sticks might need repartitioning and reformatting with the Linux tools in order to work correctly.

The syslinux program should be run on the device /dev/fd0 for a floppy disk, or something similar to/dev/sdx1 for a USB stick. Care must be taken, as selecting the wrong device name might overwrite your host system's hard drive.

The target device should then be mounted and the kernel and the simple.gz file copied on.

The syslinux loader can be configured using a file called syslinux.cfg which would look something like:

default simple
timeout 100
prompt 1

label simple
 kernel vmlinuz
 append initrd=simple root=/dev/ram


The complete command sequence to perform these actions, substituting file locations as appropriate, is:

$ sudo syslinux -s /dev/sdd1
$ sudo mount -t vfat -o shortname=mixed  /dev/sdd1 /mnt/
$ cd /mnt
$ sudo cp /boot/vmlinuz-2.6.26-1-686 VMLINUZ
$ sudo cp simple.gz SIMPLE
$ sudo cp syslinux.cfg SYSLINUX.CFG
$ cd /mnt
$ sudo umount /mnt


The device may now be removed and booted on an appropriate PC. The PC should boot the image and present a prompt exactly the same way the emulator did.

What's next?

This first step, while simple, provides a complete OS, and demonstrates that constructing an embedded system can be a straightforward process.

The next step is to expand this simple example to encompass a specific application, which will be covered in the next article.