Sie sind auf Seite 1von 129

Compiling and booting Linux

Cross-compiling the kernel

Cross-compiling the kernel


When you compile a Linux kernel for another CPU architecture Much faster than compiling natively, when the target system is much slower than your GNU/Linux workstation. Much easier as development tools for your GNU/Linux workstation are much easier to find. To make the difference with a native compiler, cross-compiler executables are prefixed by the name of the target system, architecture and sometimes library. Examples: mips-linux-gcc m68k-linux-uclibc-gcc arm-linux-gnueabi-gcc

Specifying cross-compilation
The CPU architecture and cross-compiler prefix are defined through the ARCH and CROSS_COMPILE variables in the toplevel Makefile. The Makefile defines CC = $(CROSS_COMPILE)gcc See comments in Makefile for details The easiest solution is to modify the Makefile. Example, ARM platform, cross-compiler: arm-linux-gcc ARCH ?= arm CROSS_COMPILE ?= arm-linuxOther solutions
Pass ARCH and CROSS_COMPILE on the make command line Define ARCH and CROSS_COMPILE as environment variables Don't forget to have the values properly set at all steps, otherwise the kernel configuration and build system gets confused

Configuring the kernel


make xconfig Same as in native compiling The set of available options will be different Don't forget to set the right board / machine type!

Ready-made config files


assabet_defconfig integrator_defconfig mainstone_defconfig badge4_defconfig iq31244_defconfig mx1ads_defconfig bast_defconfig iq80321_defconfig neponset_defconfig cerfcube_defconfig iq80331_defconfig netwinder_defconfig clps7500_defconfig iq80332_defconfig omap_h2_1610_defconfig ebsa110_defconfig ixdp2400_defconfig omnimeter_defconfig edb7211_defconfig ixdp2401_defconfig pleb_defconfig enp2611_defconfig ixdp2800_defconfig pxa255-idp_defconfig ep80219_defconfig ixdp2801_defconfig rpc_defconfig epxa10db_defconfig ixp4xx_defconfig s3c2410_defconfig footbridge_defconfig jornada720_defconfig shannon_defconfig fortunet_defconfig lart_defconfig shark_defconfig h3600_defconfig lpd7a400_defconfig simpad_defconfig h7201_defconfig lpd7a404_defconfig smdk2410_defconfig h7202_defconfig lubbock_defconfig versatile_defconfig hackkit_defconfig lusl7200_defconfig

arch/arm/configs example

Using ready-made config files


Default configuration files available for many boards / machines! Check if one exists in arch/<arch>/configs/ for your target. Example: if you found an acme_defconfig file, you can run: make acme_defconfig Using arch/<arch>/configs/ is a very good good way of releasing a default configuration file for a group of users or developers. Like all make commands, you must run make <machine>_defconfig in the toplevel source directory.

Building the kernel


Run make Copy arch/<arch>/boot/zImage to the target storage You can customize arch/<arch>/boot/install.sh so that make install does this automatically for you. make INSTALL_MOD_PATH=<dir>/ modules_install and copy <dir>/lib/modules/ to /lib/modules/ on the target storage.

ROOT FILE SYSTEM (RFS)

General purpose toolbox: BusyBox


http://www.busybox.net/ Most Unix command line utilities within a single executable! It even includes a web server! Sizes less than < 500 KB (statically compiled with uClibc) or less than 1 MB (statically compiled with glibc). Easy to configure which features to include. The best choice for Initramfs / initrd with complex scripts Small and medium size embedded systems See http://www-128.ibm.com/developerworks/linux/library/l-busybox/ for a nice introduction.

BusyBox commands!
[, [[, addgroup, adduser, adjtimex, ar, arp, arping, ash, awk, basename, bbconfig, bbsh, brctl, bunzip2, busybox, bzcat, bzip2, cal, cat, catv, chat, chattr, chcon, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt, chvt, cksum, clear, cmp, comm, cp, cpio, crond, crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt, delgroup, deluser, depmod, devfsd, df, dhcprelay, diff, dirname, dmesg, dnsd, dos2unix, dpkg, dpkg_deb, du, dumpkmap, dumpleases, e2fsck, echo, ed, egrep, eject, env, envdir, envuidgid, ether_wake, expand, expr, fakeidentd, false, fbset, fbsplash, fdflush, fdformat, fdisk, fetchmail, fgrep, find, findfs, fold, free, freeramdisk, fsck, fsck_minix, ftpget, ftpput, fuser, getenforce, getopt, getsebool, getty, grep, gunzip, gzip, halt, hd, hdparm, head, hexdump, hostid, hostname, httpd, hush, hwclock, id, ifconfig, ifdown, ifenslave, ifup, inetd, init, inotifyd, insmod, install, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd, lash, last, length, less, linux32, linux64, linuxrc, ln, load_policy, loadfont, loadkmap, logger, login, logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lzmacat, makedevs, man, matchpathcon, md5sum, mdev, mesg, microcom, mkdir, mke2fs, mkfifo, mkfs_minix, mknod, mkswap, mktemp, modprobe, more, mount, mountpoint, msh, mt, mv, nameif, nc, netstat, nice, nmeter, nohup, nslookup, od, openvt, parse, passwd, patch, pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill, poweroff, printenv, printf, ps, pscan, pwd, raidautorun, rdate, rdev, readahead, readlink, readprofile, realpath, reboot, renice, reset, resize, restorecon, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run_parts, runcon, runlevel, runsv, runsvdir, rx, script, sed, selinuxenabled, sendmail, seq, sestatus, setarch, setconsole, setenforce, setfiles, setfont, setkeycodes, setlogcons, setsebool, setsid, setuidgid, sh, sha1sum, showkey, slattach, sleep, softlimit, sort, split, start_stop_daemon, stat, strings, stty, su, sulogin, sum, sv, svlogd, swapoff, swapon, switch_root, sync, sysctl, syslogd, tac, tail, tar, taskset, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, top, touch, tr, traceroute, true, tty, ttysize, tune2fs, udhcpc, udhcpd, udpsvd, umount, uname, uncompress, unexpand, uniq, unix2dos, unlzma, unzip, uptime, usleep, uudecode, uuencode, vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs, yes, zcat, zcip

Commands available in BusyBox 1.13

Applet highlight - BusyBox vi


If you are using BusyBox, adding vi supports only adds 20K. (built with shared libraries, using uClibc). You can select which exact features to compile in. Users hardly realize that they are using a lightweight vi version! Tip: you can learn vi on the desktop, by running the vimtutor command.

Configuring BusyBox
Get the latest stable sources from http://busybox.net Configure BusyBox (creates a .config file): make defconfig Good to begin with BusyBox. Configures BusyBox with all options for regular users. make allnoconfig Unselects all options. Good to configure only what you need. make xconfig (graphical) or make menuconfig (text) Same configuration interfaces as the ones used by the Linux kernel.

BusyBox make xconfig


You can choose: the commands to compile, and even the command options and features that you need!

Compiling BusyBox
Set the cross-compiler prefix in the configuration interface: BusyBox Settings -> Build Options -> Cross Compiler prefix Example: arm-linuxSet the installation directory in the configuration interface: BusyBox Settings -> Installation Options -> BusyBox installation prefix Add the cross-compiler path to the PATH environment variable: export PATH=/usr/local/arm/3.3.2/bin:$PATH Compile BusyBox: make Install it (this creates a Unix directory structure symbolic links to the busybox executable): make install

Alternative to BusyBox: embutils


http://www.fefe.de/embutils/ From the creator of diet libc A similar set of tiny utilities for embedded systems. Version 0.19 (Aug. 2008): 90 common commands are implemented. Can only be built statically with diet libc! Compared to BusyBox: Much less momentum, user and developer base. Still misses key commands and features (ifconfig, for example) But can achieve smaller size than BusyBox on standalone executables.

Boot loaders: A brief on UBOOT & porting attempt

Bootloaders
The bootloader is a piece of code responsible for
Basic hardware initialization Loading of an application binary, usually an operating system kernel, from flash storage, from the network, or from another type of non-volatile storage. Possibly uncompression of the application binary Execution of the application

Besides these basic functions, most bootloaders provide a shell with various commands implementing different operations.
Loading of data from storage or network, memory inspection, hardware diagnostics and testing, etc.

Bootloaders on x86 (1)


The x86 processors are typically bundled on a board with a nonvolatile memory containing a program, the BIOS. This program gets executed by the CPU after reset, and is responsible for basic hardware initialization and loading of a small piece of code from non-volatile storage.
This piece of code is usually the first 512 bytes of an hard disk

This piece of code is usually a 1st stage bootloader, which will load the full bootloader itself. The bootloader can then offer all its features. It typically understands filesystem formats so that the kernel file can be loaded directly from a normal filesystem.

Bootloaders on x86 (2)

GRUB, Grand Unified Bootloader, the most powerful one. http://www.gnu.org/software/grub/ Can read many filesystem formats to load the kernel image and the configuration, provides a powerful shell with various commands, can load kernel images over the network, etc. LILO, the original Linux Loader http://freshmeat.net/projects/lilo/ Syslinux, for network and removable media booting http://syslinux.zytor.com

Bootloaders on embedded architectures (1)


On embedded architectures, the low-level booting process is very CPU and board dependent Some boards have a NOR flash from which the CPU starts executing instructions after reset. In that case, the bootloader must directly be flashed inside the NOR at the proper location Some CPUs have an integrated bootcode in ROM that automatically loads a small portion of a DataFlash or NAND flash, usually to a static RAM. In that case, a minimal first stage bootloader is required, that will load the main bootloader (BootROM on AT91SAM CPUs, Steppingstone on S3C24xx CPUs, etc.). The bootloader on embedded architectures starts right after CPU reset, so it must initialize all the devices, including the memory controller in order to access the DRAM. As the boot process is very CPU and board dependent, refer to the vendor documentation.

Bootloaders on embedded architectures (2)


We will focus on the generic part, the main bootloader, offering the most important features. There are several open-source generic bootloaders. Here are the most popular ones:
U-Boot, the universal bootloader by Denx The most used on ARM, also used on PPC, MIPS, x86, m68k, NIOS, etc. The de-facto standard nowadays. We will study it in detail. http://www.denx.de/wiki/U-Boot Barebox, a new architecture-neutral bootloader, written as a successor of U-Boot. Better design, better code, active development, but doesn't yet have as much hardware support as U-Boot. http://www.barebox.org

There are also a lot of other open-source or proprietary bootloaders, often architecture-specific
RedBoot, Yaboot, PMON, etc.

The U-boot bootloader

The U-boot boot loader

U-Boot
U-Boot is a typical free software project Freely available at http://www.denx.de/wiki/U-Boot Documentation available at http://www.denx.de/wiki/UBoot/Documentation The latest development source code is available in a Git repository: http://git.denx.de/cgi-bin/gitweb.cgi?p=uboot.git;a=summary Development and discussions happen around an open mailinglist http://lists.denx.de/pipermail/u-boot/ Since the end of 2008, it follows a fixed-interval release schedule. Every two months, a new version is released. Versions are named YYYY.MM.

Compiling U-Boot (1)


Get the source code from the website, and uncompress it The include/configs/ directory contains one configuration file for each supported board
It defines the CPU type, the peripherals and their configuration, the memory mapping, the U-Boot features that should be compiled in, etc. It is a simple .h file that sets pre-processor constants. See the README file for the documentation of these constants.

Assuming that your board is already supported by U-Boot, there should be one file corresponding to your board, for example include/configs/omap2420h4.h.

Compiling U-Boot (2)

U-Boot must be configured before being compiled


make BOARDNAME_config Where BOARDNAME is the name of the configuration file in include/configs/, without the .h

Make sure that the cross-compiler is available in PATH


export PATH=/usr/local/uclibc-0.9.29-2/arm/bin/:$PATH

Compile U-Boot, by specifying the cross-compiler prefix. Example, if your cross-compiler executable is arm-linux-gcc: make CROSS_COMPILE=arm-linux-

Installing U-Boot
U-Boot must usually be installed in flash memory to be executed by the hardware. Depending on the hardware, the installation of U-Boot is done in a different way:
The board provides some kind of specific boot monitor, which allows to flash the second stage bootloader. In this case, refer to the board documentation and tools U-Boot is already installed, and can be used to flash a new version of U-Boot. However, be careful: if the new version of U-Boot doesn't work, the board is unusable The board provides a JTAG interface, which allows to write to the flash memory remotely, without any system running on the board. It also allows to rescue a board if the bootloader doesn't work.

U-boot prompt
Connect the target to the host through a serial console Power-up the board. On the serial console, you will see something like: U-Boot 1.1.2 (Aug 3 2004 - 17:31:20) RAM Configuration: Bank #0: 00000000 8 MB Flash: 2 MB In: serial Out: serial Err: serial u-boot # The U-Boot shell offers a set of commands. We will study the most important ones, see the documentation for a complete reference or the help command.

Information commands
U-Boot> flinfo DataFlash:AT45DB021 Nb pages: 1024 Page Size: 264 Size= 270336 bytes Logical address: 0xC0000000 Area 0: C0000000 to C0001FFF (RO) Bootstrap Area 1: C0002000 to C0003FFF Environment Area 2: C0004000 to C0041FFF (RO) U-Boot U-Boot> nand info Device 0: NAND 256MiB 3,3V 8-bit, sector size 128 KiB U-Boot> version U-Boot 2009.08 (Nov 15 2009 - 14:48:35)

Flash information

NAND flash information U-Boot information

Can vary from one board to the other (according to the U-Boot compile configuration)

Environment variables (1)


U-Boot can be configured through environment variables, which affect the behavior of the different commands. See the documentation for the complete list of environment variables. The printenv command also to display all variables or one :
u-boot # printenv baudrate=19200 ethaddr=00:40:95:36:35:33 netmask=255.255.255.0 ipaddr=10.0.0.11 serverip=10.0.0.1 stdin=serial stdout=serial stderr=serial u-boot # printenv serverip serverip=10.0.0.2

Network configuration

Environment variables (2)


The value of the environment variables can be changed using the setenv command : u-boot # setenv serverip 10.0.0.2 Environment variable changes can be stored to flash using the saveenv command. The location in flash is defined at compile time in the U-Boot configuration file. You can even create small scripts stored in environment variables: setenv mmc-boot 'mmc init 0; if fatload mmc 0 80000000 boot.ini; then source; else if fatload mmc 0 80000000 uImage; then run mmc-bootargs; bootm; fi; fi' You can then execute the script: run mmc-boot

Transferring files to the target


U-Boot is mostly used to load and boot a kernel image, but it also allows to change the kernel image and the root filesystem stored in flash. Files must be exchanged between the target and the development workstation. This is possible :
Through the network if the target has an Ethernet connection, and U-Boot contains a driver for the Ethernet chip. If so, the TFTP protocol can be used to exchange files Through the serial line if no Ethernet connection is available.

Target U-Boot TFTP client Ethernet connection

Host TFTP server

Configuring and testing tftp


On GNU/Linux systems based on Debian: Ubuntu, Knoppix Install the tftpd-hpa package (tftp server): apt-get install tftpd-hpa Copy files to the root directory of the tftp server. Example: cp arch/arm/boot/uImage /var/lib/tftpboot To test the server, install a tftp client on your workstation: apt-get install tftp-hpa Use it to download a file (-4 to force the use of IPv4) tftp -4 localhost > get uImage

U-boot mkimage
The kernel image that U-Boot loads and boots must be prepared, so that an U-Boot specific header is added in front of the image This is done with a tool that comes in U-Boot, mkimage Debian / Ubuntu: just install the uboot-mkimage package. Or, compile it by yourself: simply configure U-Boot for any board of any architecture and compile it. Then install mkimage: cp tools/mkimage /usr/local/bin/ The special target uImage of the kernel Makefile can then be used to generate a kernel image suitable for U-Boot.

Flashing a kernel image


Compile your kernel and generate the U-Boot header running make uImage Copy the kernel image to the directory exported by the TFTP server On the board, in U-Boot, download the kernel image to memory : u-boot # tftp 8000 uImage Unprotect NOR flash u-boot # protect off 1:0-4 Erase NOR flash u-boot # erase 1:0-4 Copy to NOR flash (0x01000000: first sector) u-boot # cp.b ${fileaddr} 1000000 ${filesize} Restore NOR flash sector protection: u-boot # protect on 1:0-4 See our practical labs for details handling NAND flash.

boot commands
Specify kernel boot parameters: Continues on u-boot # setenv bootargs mem=64M \ console=ttyS0,115200 line the same init=/sbin/init \ root=/dev/mtdblock0 Execute the kernel from a given physical address (RAM or flash): bootm 0x01030000

OMAP boot components


At least of OMAP3 and OMAP4
Found in ROM Executed from ROM at boot

Rom code

X-loader

Found in NAND or in MMC Copied to the CPU's internal RAM. Initializes the external DRAM controller + other devices Found in NAND or in MMC (u-boot.bin file) Copied to DRAM Can also initialize some devices Found in NAND, MMC, USB, network, serial Copied to DRAM (depending on U-boot's features)

U-boot

Linux kernel

Traditional booting sequence


Bootloader
- Executed by the hardware at a fixed location in ROM / Flash - Initializes support for the device where the kernel image is found (local storage, network, removable media) - Loads the kernel image in RAM - Executes the kernel image (with a specified command line)

Kernel
- Uncompresses itself - Initializes the kernel core and statically compiled drivers (needed to access the root filesystem) - Mounts the root filesystem (specified by the root kernel parameter) - Executes the first userspace program (specified by the init kernel parameter)

First userspace program


- Configures userspace and starts up system services

Kernel command line parameters


The Linux kernel can be given parameters at boot time Kernel command line arguments are part of the bootloader configuration settings. They are copied to RAM by the bootloader, to a location where the kernel expects them. Useful to modify the behavior of the kernel at boot time, without having to recompile it. Useful to perform advanced kernel and driver initialization, without having to use complex user-space scripts.

Kernel command line example


HP iPAQ h2200 PDA booting example: root=/dev/ram0 \ ramdisk) rw \ filesystem mounting mode init=/linuxrc \ console=ttyS0,115200n8 \ console=tty0 \ (framebuffer) ramdisk_size=8192 \ cachepolicy=writethrough Root filesystem (first Root First userspace program Console (serial) Other console Misc parameters...

Hundreds of command line parameters described on Documentation/kernel-parameters.txt

Drawbacks
Assumption that all device drivers needed to mount the root filesystem (storage and filesystem drivers) are statically compiled inside the kernel. Assumption can be correct for most embedded systems, where the hardware is known and the kernel can be fine-tuned for the system. Assumption is mostly wrong for desktop and servers, since a single kernel image should support a wide range of devices and filesystems
More flexibility was needed Modules have this flexibility, but they are not available before mounting the root filesystem Need to handle complex setups (RAID, NFS, etc.)

Solution
A solution is to include a small temporary root filesystem with modules, in the kernel itself. This small filesystem is called the initramfs. This initramfs is a gzipped cpio archive of this basic root filesystem
A gzipped cpio archive is a kind of zip file, with a much simpler format

The initramfs scripts will detect the hardware, load the corresponding kernel modules, and mount the real root filesystem. Finally the initramfs scripts will run the init application in the real root filesystem and the system can boot as usual. The initramfs technique completely replaces init ramdisks (initrds). Initrds were used in Linux 2.4, but are no longer needed.

Booting sequence with initramfs


Bootloader
Executed by the hardware at a fixed location in ROM / Flash Initializes support for the device where the images are found (local storage, network, removable media) Loads the kernel image in RAM Executes the kernel image (with a specified command line)

unchanged

Kernel
- Uncompresses itself - Initializes the kernel core and statically compiled drivers - Uncompresses an initramfs cpio archive (if existing, in the kernel image or copied to memory by the bootloader) and extracts it to the kernel file cache (no mounting, no filesystem). - If found in the initramfs, executes the first userspace program: /init

Userspace:/init script (what follows is just a typical scenario)


- Runs userspace commands to configure the device (such as network setup, mounting /proc and /sys...) - Mounts a new root filesystem. Switch to it (switch_root ) - Runs /sbin/init

Userspace:/sbin/init
- Runs commands to configure the device (if not done yet in the initramfs) - Starts up system services (daemons, servers) and user programs

Initramfs features and advantages


Root filesystem directly embedded in the kernel image, or copied to RAM by the bootloader, simple solution. Just a plain compressed cpio archive extracted in the file cache. Neither needs a block nor a filesystem driver. Simpler to mount complex filesystems from flexible userspace scripts rather than from rigid kernel code. More complexity moved out to user-space! Possible to add non GPL files (firmware, proprietary drivers) in the filesystem. This is not linking, just file aggregation (not considered as a derived work by the GPL).

How to populate an initramfs


Using CONFIG_INITRAMFS_SOURCE in kernel configuration (General Setup section) Either give an existing cpio archive (file name ending with .cpio) Or give a directory to be archived. Any other regular file will be taken as a text specification file (see next page).
see Documentation/filesystems/ramfs-rootfs-initramfs.txt and Documentation/early-userspace/README in kernel sources. See also http://www.linuxdevices.com/articles/AT4017834659.html for a nice overview of initramfs (by Rob Landley).

Initramfs specification file example


major minor dir /dev 755 0 0 nod /dev/console 644 0 0 c 5 1 nod /dev/loop0 644 0 0 b 7 0 dir /bin 755 1000 1000 file /bin/busybox /stuff/initramfs/busybox 755 0 0 slink /bin/sh busybox 777 0 0 dir /proc 755 0 0 dir /sys 755 0 0 dir /mnt 755 0 0 file /init /stuff/initramfs/init.sh 755 0 0 No need for root user access! permissions

user id group id

Summary
For embedded systems, two interesting solutions No initramfs: all needed drivers are included inside the kernel, and the final root filesystem is mounted directly Everything inside the initramfs

Enabling Target Server with gdbserver

Copy the gdb5.3.tar.gz in /temp Extract and get in folder Run the following command
./configure --target=arm-linux --prefix=/usr/local/arm-gdb

make

make install

Major steps

Move to GDB source directory to compile for ARM.


Cd gdb7.X

Export the path


export PATH=$PATH:/usr/local/arm-gdb/bin

Run the following command


./configure --target=arm-linux --host=arm-linux

Major steps

Now move to
cd /temp/gdb7.x/gdb/gdbserver Type the following command
make CC=/usr/local/arm/4.3.2/bin/armlinux-gcc

Testing code

WAP a bug based code Compile the code Download the code on board via zmodem Run the code It should hit segmentation fault

Major steps

Set the PC with


ifconfig eth0 192.168.1.111

Move to following
cd /usr/local/arm-gdb/bin Copy the buggy code exe to this path

Run command on board


./gdbserver 192.168.1.111:1234 test.exe

Major steps

On PC run
./arm-linux-gdb

(gdb) target remote 192.168.100.50:1234 (gdb) symbol-file <exefile> (gdb) list

The Linux Device Driver

Introduction to Device Driver Introduction to LKML/ Modules Writing Sample Modules Linux Device Drivers and Types Character Device Driver: A close look A close look to related Data Structures A skeleton of device driver w.r.t types An example driver demo Testing few drivers via applications

Linux Device Driver


A piece of code driving h/w (resident to kernel generally) If you have to write a In linux driver you need to learn Loadable kernel modules thoroughly The LKMs are key to write any Linux DD hence, I would say every one to get the same. Sample code will guide you understand the internals We will be writing out driver on Ram disk
Need not test your code always with a device Saves time for beginners Gives a room to experiment without tampering any device

Linux/drivers
Largest amount of code in the kernel tree (~1.5M). device, bus, platform and general directories. drivers/char n_tty.c is the default line discipline. drivers/block elevator.c, genhd.c, linear.c, ll_rw_blk.c, raidN.c. drivers/net specific drivers and general routines Space.c and net_init.c. drivers/scsi scsi_*.c files are generic; sd.c (disk), sr.c (CD-ROM), st.c (tape), sg.c (generic). General:
cdrom, ide, isdn, parport, pcmcia, pnp, sound, telephony, video.

Buses i2c, pci, sbus, usb. Platforms acorn, macintosh, s390, sgi.

Introduction to Linux Loadable Kernel Modules-1

If you want to add code to a Linux kernel, the most basic way to do that is to add some source files to the kernel source tree and recompile the kernel. In fact, the kernel configuration process consists mainly of choosing which files to include in the kernel to be compiled. But you can also add code to the Linux kernel while it is running. A chunk of code that you add in this way is called a loadable kernel module.

Introduction to Linux Loadable Kernel Modules-2

These modules can do lots of things, but they typically are one of two things: 1) Device drivers; 2) File System drivers; The kernel isolates certain functions, including these, especially well so they don't have to be intricately wired into the rest of the kernel.

Terminology
Loadable kernel modules are often called just kernel modules or just modules, but those are rather misleading terms because there are lots of kinds of modules in the world and various pieces built into the base kernel can easily be called modules. We use the term loadable Some people think of LKMs as outside of the kernel. They speak of LKMs communicating with the kernel. This is a mistake; LKMs (when loaded) are very much part of the kernel. The correct term for the part of the kernel that is bound into the image that you boot, i.e. all of the kernel except the LKMs, is "base kernel. LKMs communicate with the base kernel.

History of Loadable Kernel Modules


LKMs did not exist in Linux in the beginning. Anything we use an LKM for today was built into the base kernel at kernel build time instead. LKMs have been around at least since Linux 1.2 (1995). Device drivers and such were always quite modular, though. When LKMs were invented, only a small amount of work was needed on these modules to make them buildable as LKMs. However, it had to be done on each and every one, so it took some time. Since about 2000, virtually everything that makes sense as an LKM has at least had the option of being an LKM.

Cases: For Loadable Kernel Modules -1

You often have a choice between putting a module into the kernel by loading it as an LKM or binding it into the base kernel. LKMs have a lot of advantages over binding into the base kernel and I recommend them wherever possible.

One advantage is that you don't have to rebuild your kernel as often. This saves you time and spares you the possibility of introducing an error in rebuilding and reinstalling the base kernel. Once you have a working base kernel, it is good to leave it untouched as long as possible.

Cases: For Loadable Kernel Modules -2


Another advantage is that LKMs help you diagnose system problems. A bug in a device driver which is bound into the kernel can stop your system from booting at all and it can be really hard to tell which part of the base kernel caused the trouble. If the same device driver is an LKM, though, the base kernel is up and running before the device driver even gets loaded. If your system dies after the base kernel is up and running, it's an easy matter to track the problem down to the troublemaking device driver and just not load that device driver till you fix the problem.

What LKMs Can't Do

There is a tendency to think of LKMs like user space programs. They do share a lot of their properties, but LKMs are definitely not user space programs. They are part of the kernel. As such, they have free run of the system and can easily crash it. Security point of view LKMs are not a good way of implementing a secure kernel services?
They offer room for hackers often leading the programmer prove something stupid

A view : LKMs
LKMs

KAS RAM UAS

BKI

Making Loadable Kernel Modules An LKM lives in a single ELF object file (normally named like "serial.o"). You typically keep all your LKM object files in a particular directory (near your base kernel image makes sense). When you use the insmod program to insert an LKM into the kernel, you give the name of that object file.

LKM Utilities
The programs you need to load and unload and otherwise work with LKMs are in the package modutils. This package contains the following programs to help you use LKMs: Insmod Insert an LKM into the kernel. Rmmod Remove an LKM from the kernel. Depmod Determine interdependencies between LKMs. Ksyms Display symbols that are exported by the kernel for use by new LKMs. Lsmod List currently loaded LKMs. Modinfo Display contents of .modinfo section in an LKM object file. Modprobe Insert or remove an LKM or set of LKMs intelligently. For example, if you must load A before loading B, Modprobe will automatically load A when you tell it to load B.

How To Insert And Remove LKMs


The basic programs for inserting and removing LKMs are insmod and rmmod. See their man pages for details. Inserting an LKM is conceptually easy: Just type, as superuser, a command like insmod generic_serial.o or /sbin/insmod (serial.o contains the device driver for serial ports (UARTs)). However, It would be misleading you if I said the command just works. It is very common, and rather maddening, for the command to fail either with a message about a module/kernel version mismatch or a pile of unresolved symbols. If it does work, though, the way to prove to yourself that you know what you're doing is to look at /proc/modules

A difficult insertion - 1
Now lets look at a more difficult insertion. If you try insmod msdos.o you will probably get a raft of error messages like: msdos.o: unresolved symbol fat_date_unix2dos msdos.o: unresolved symbol fat_add_cluster1 msdos.o: unresolved symbol fat_put_super ... This is because msdos.o contains external symbol references to the symbols mentioned and there are no such symbols exported by the kernel. To prove this, do a cat /proc/ksyms to list every symbol that is exported by the kernel (i.e. available for binding to LKMs). You will see that 'fat_date_unix2dos' is nowhere in the list.

A difficult insertion - 2
How do you get it into the list? By loading another LKM, one which defines those symbols and exports them. In this case, it is the LKM in the file fat.o. So do insmod fat.o and then see that "fat_date_unix2dos" is in /proc/ksyms. Now redo the insmod msdos.o and it works. Look at /proc/modules and see that both LKMs are loaded and one depends on the other: msdos 5632 0 (unused) fat 30400 0 [msdos] How did I know fat.o was the module I was missing? Just a little ingenuity. A more robust way to address this problem is to use depmod and modprobe instead of insmod

A difficult insertion -3
When your symbols look like "fat_date_unix2dos_R83fb36a1", the problem may be more complex than just getting prerequisite LKMs loaded. When the error message is "kernel/module version mismatch, Often, you need to pass parameters to the LKM when you insert it. For example, a device driver wants to know the address and IRQ of the device it is supposed to drive. Or the network driver wants to know how much diagnostic tracing you want it to do. Here is an example of that: insmod ne.o io=0x300 irq=11 Here, I am loading the device driver for my NE2000-like Ethernet adapter and telling it to drive the Ethernet adapter at IO address 0x300, which generates interrupts on IRQ 11.

A difficult insertion - 4 & finally Removing a Module


There are no standard parameters for LKMs and very few conventions. Each LKM author decides what parameters insmod will take for his LKM. To remove an LKM from the kernel, the command is like rmmod ne There is a command lsmod to list the currently loaded LKMs, but all it does is dump the contents of /proc/modules, with column headings, so you may just want to go to the horse's mouth and forget about lsmod.

/proc/modules -1
To see the presently loaded LKMs, do cat /proc/modules You see a line like
serial 24484 0
The left column is the name of the LKM, which is normally the name of the object file from which you loaded it, minus the ".o" suffix. The "24484" is the size in bytes of the LKM in memory. The "0" is the use count. It tells how many things presently depend on the LKM being loaded. Typical "things" are open devices or mounted fileystems. It is important because you cannot remove an LKM unless the use count is zero. The LKM itself maintains this count, but the module manager uses it to decide whether to permit an unload.

/proc/modules -2
There is an exception to the above description of the use count. You may see -1 in the use count column. What that means is that this LKM does not use usecounts to determine when it is OK to unload. Instead, the LKM has registered a subroutine that the module manager can call that will return an indication of whether or not it is OK to unload the LKM. In this case, the LKM ought to provide you with some custom interface, and some documentation, to determine when the LKM is free to be unloaded. Do not confuse use count with "dependencies", which are described as below here with another example, with more information: lp 5280 0 (unused) parport_pc 7552 1 parport 7600 1 [lp parport_pc]

/proc/modules -3
The stuff in square dependencies. brackets ("[lp parport_pc]") describes

Here, the modules lp and parport_pc both refer to addresses within module parport (via external symbols that parport exports). So lp and parport_pc are "dependent" on (and are "dependencies of" parport not a use count. You cannot unload an LKM that has dependencies. But you can remove those dependencies by unloading the dependent LKMs. The "(unused)" legend means the LKM has never been used.

What Happens When An LKM Loads -1


So you've successfully loaded an LKM, and verified that via /proc/modules. But how do you know it's working? That's up to the LKM, and varies according to what kind of LKM it is, but here are some of the more common actions of an LKM upon being loaded. The first thing a device driver LKM does after loading (which is what the module would do at boot time if it were bound into the base kernel) is usually to search the system for a device it knows how to drive. Just how it does this search varies from one driver to the next, and can usually be controlled by module parameters. But in any case, if the driver doesn't find any device it is capable of driving, it causes the load to fail.

What Happens When An LKM Loads -2


You can see that a device driver has registered itself in the file /proc/devices. You can see that the device driver is handling the device's interrupts in /proc/interrupts. A nice device driver issues kernel messages telling what devices it found and is prepared to drive. (Kernel messages in most systems end up on the console and in the file /var/log/messages). Some drivers, however, are silent. A network device (interface) driver works similarly, except that the LKM registers a device name of its choosing (e.g. eth0) rather than a major number. You can see the currently registered network device names in /proc/net/dev

Unresolved Symbols
The most common and most frustrating failure in loading an LKM is a bunch of error messages about unresolved symbols, like this: msdos.o: unresolved symbol fat_date_unix2dos msdos.o: unresolved symbol fat_add_cluster1 msdos.o: unresolved symbol fat_put_super ... There are actually a bunch of different problems that result in this symptom. In any case, you can get closer to the problem by looking at /proc/ksyms and confirming that the symbols in the message are indeed not in the list.

An LKM Must Match The Base Kernel


The designers of loadable kernel modules realized there would be a problem with having the kernel in multiple files, possibly distributed independently of one another. What if the LKM mydriver.o was written and compiled to work with the Linux 1.2.1 base kernel, and then someone tried to load it into a Linux 1.2.2 kernel? What if there was a change between 1.2.1 and 1.2.2 in the way a kernel subroutine that mydriver.o calls works? Problem is same for any version. To address this problem, the creators of LKMs endowed them with a kernel version number. The special .modinfo section of the mydriver.o object file say has "1.2.1" in it because it was compiled using header files from Linux 1.2.1. Try to load it into a 1.2.2 kernel and insmod notices the mismatch and fails, telling you you have a kernel version mismatch.

An LKM Must Match The Base Kernel

To ease this burden, insmod has a -f option that "forces" insmod to ignore the kernel version mismatch and insert the module anyway. As it is so unusual for there to be a significant difference between any two kernel versions, I recommend you always use -f. You will, however, still get a warning message about the mismatch. There's no way to shut that off. if you get the error message isn't one about mismatched kernel versions, but simply "unresolved symbol reference. Nothing will work, even a -f

How They Work -? Technical Details

insmod makes an init_module system call to load the LKM into kernel memory. Loading it is the easy. How does the kernel know to use it? The answer is that the init_module system call invokes the LKM's initialization routine as it loads the LKM. insmod passes to init_module the address of the subroutine in the LKM named init_module as its initialization routine.

If we set up init_module to call a kernel function that registers the subroutines that the LKM contains.

Modinfo
You can use the modinfo program to interpret the contents of the .modinfo section. What is in the .modinfo section and who uses it? insmod uses the .modinfo section for the following: It contains the kernel release number for which the module was built. I.e. of the kernel source tree whose header files were used in compiling the module. It describes the form of the LKM's parameters. insmod uses this information to format the parameters you supply on the insmod command line into data structure initial values, which insmod inserts into the LKM as it loads it.

A final Note:

Internal workings of the Linux kernel with respect to LKMs can be started looking at following codes. You should not need to know any of this in order to develop, build, and use LKMs. The code to handle LKMs is in the source files kernel/module.c in the Linux source tree. The kernel module loader lives in kernel/kmod.c.

Lets Try the following

Building modules

Parameter passing

Major MODULE_XXX macros

EXPORTING symbols

Various interdependency check

Application and device driver

Application and device driver

Linux Device Driver


Basics and types Making a device node Trying some operation on it Understanding Major/Minor number Registering a device driver Understanding the ways of defining services Related Data structure A sample skeleton of character device driver

CHAR DRIVERS
There are three types of device driver
Char (c) Block (b) Network

Linux Treats every device as a file and to interface the driver uses device nodes found in /dev historically Network device driver unlike char, and block uses a different approach for the same

Major and Minor Numbers -1

Char devices are accessed through names in the filesystem. Those names are called special files or device files or simply nodes of the filesystem tree; they are conventionally located in the /dev directory. Special files for char drivers are identified by a c in the first column of the output of ls l. Block devices appear in /dev as well, but they are identified by a b.
The focus of this session is on char devices, but much of the following information applies to block devices as well.

Major and Minor Numbers -2


The following listing shows a few devices as they appear on a typical system. Their major numbers are 1, 4, 7, and 10, while the minors are 1, 3, 5, 64, 65, and 129.


The major number identifies the driver associated with the device.

For example, /dev/null and /dev/zero ar e both managed by driver 1, whereas virtual consoles and serial terminals are managed by driver 4; similarly, both vcs1 and vcsa1 devices are managed by driver 7.
The kernel uses the major number at open time to dispatch execution to the appropriate driver. It is common for a driver to control several devices (as shown in the listing); The minor number provides a way for the driver to differ entiate among them.

The device file system


Version 2.4 of the kernel, though, introduced a new (optional) feature, the device file system or devfs. If this file system is used, management of device files is simplified and quite different; On the other hand, the new filesystem brings several uservisible incompatibilities, and as we are writing it has not yet been chosen as a default feature by system distributors.

Registering your driver


When devfs is not being used, adding a new driver to the system means assigning a major number to it. The assignment should be made at driver (module) initialization by calling the following function, defined in <linux/fs.h>
int register_chrdev (unsigned int major, const char *name, struct file_operations *fops);
The return value indicates success or failure of the operation. A negative return code signals an error; 0 or positive return code reports successful completion

Registering your driver


The major argument is the major number being requested, name is the name of your device, which will appear in /proc/devices, and fops is the pointer to an array of function pointers, used to invoke your drivers entry points can be understood when dealing with file operation The 2.0 kernel supported 128 devices; 2.2 and 2.4 increased that number to 256 (while reserving the values 0 and 255 for future uses). Minor numbers, too, are eight-bit quantities; they aren t passed to register_chr dev because, as stated, they are only used by the driver itself.
There is tremendous pressure from the developer community to increase the number of possible devices supported by the kernel; increasing device numbers to at least 16 bits is a stated goal for the 2.5/2.6 development series.

Registering your driver


Once the driver has been register ed in the kernel table, its operations are associated with the given major number.

Whenever an operation is performed on a character device file associated with that major number, the kernel finds and invokes the proper function from the file_operations structure.

For this reason, the pointer passed to register_chrdev should point to a global structure within the driver, not to one local to the modules initialization function.

Creating a Device node:


Giving a name via our driver communicates
The command to create a device node on a filesystem is mknod;

superuser privileges are required for this operation. The command takes three arguments in addition to the name of the file being created.
For example, the command

mknod /dev/scull0 c 254 0


creates a char device (c) whose major number is 254 and whose minor number is 0.
[

Minor numbers should be in the range 0 to 255 because, for historical reasons, they are sometimes stored in a single byte. There are sound reasons to extend the range of available minor numbers, but for the time being, the eight-bit limit is still in force

Dynamic Allocation of Major Numbers


Fortunately (or rather, thanks to someones ingenuity), you can request dynamic assignment of a major number. If the argument major is set to 0 when you call register_chrdev, the function selects a free number and returns it.

The major number returned is always positive, while negative return values are error codes.
Please note the behavior is slightly differ ent in the two cases: the function returns the allocated major number if the caller requests a dynamic number, but returns 0 (not the major number) when successfully registering a predefined major number.

Dynamic Allocation of Major Numbers


To load a driver using a dynamic major number, therefore, the invocation of insmod can be replaced by a simple script that after calling insmod reads /proc/devices in order to create the special file(s). A typical /pr oc/devices file looks like the following: Character devices:
1 mem 2 pty 3 ttyp 4 ttyS 6 lp 13 input 14 sound 21 sg 180 usb

Experimental Major Numbers

Major numbers in the ranges


60 to 63 120 to 127, and 240 to 254 are reserved for local and experimental use.

We should not assign it to any such major numbers to real device

Removing a driver from system


When a module is unloaded from the system, the major number must be released. This is accomplished with the following function, which you call from the modules cleanup function:
int unregister_chrdev (unsigned int major, const char *name);

The arguments are the major number being released and the name of the associated device. The kernel compares the name to the register ed name for that number, if any:
if they differ, -EINVAL is returned. The kernel also returns EINVAL if the major number is out of the allowed range.

File Operations
Lets look at the various operations a driver can perform on the devices it manages. An open device is identified internally by a file structure, and the kernel uses the file_operations structure to access the drivers functions. The structure, defined in <linux/fs.h>, is an array of function pointers. Each file is associated with its own set of functions (by including a field called f_op that points to a file_operations structure).

File Operations
The operations are mostly in charge of implementing the system calls and are thus named open, read, and so on. We can consider the file to be an object and the functions operating on it to be its methods, using object-oriented programming terminology to denote actions declared by an object to act on itself. This is the first sign of object-oriented programming we see in the Linux kernel. The file_operations structure has been slowly getting bigger as new functionality is added to the kernel. The addition of new operations can, of course, create portability problems for device drivers.

File Operations
Instantiations of the structure in each driver used to be declared using standard C syntax, and new operations were normally added to the end of the structure; A simple recompilation of the drivers would place a NULL value for that operation, thus selecting the default behavior, usually what you wanted. Since then, kernel developers have switched to a tagged initialization format that allows initialization of structure fields by name, thus circumventing most problems with changed data structures. The tagged initialization, however, is not standard C but a (useful) extension specific to the GNU compiler.

File Operations
The following list shows what operations appear in struct file_operations for the 2.4 series of kernels, in the order in which they appear.
loff_t (*llseek) (struct file *, loff_t, int);
The llseek method is used to change the current read/write position in a file, and the new position is returned as a (positive) return value. The loff_t is a long offset and is at least 64 bits wide even on 32-bit platforms. Errors are signaled by a negative return value. If the function is not specified for the driver, a seek relative to end-of-file fails, while other seeks succeed by modifying the position counter in the file structure (described in The file Structure).

File Operations
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
Used to retrieve data from the device. A null pointer in this position causes the read system call to fail with -EINVAL (Invalid argument). A non-negative return value represents the number of bytes successfully read (the return value is a signed size type, usually the native integer type for the target platform).

ssize_t (*write) (struct file *, const char *, size_t, loff_t *);


Sends data to the device. If missing, -EINVAL is retur ned to the program calling the write system call. The retur n value, if non-negative, repr esents the number of bytes successfully written.

File Operations
int (*readdir) (struct file *, void *, filldir_t); This field should be NULL for device files; it is used for reading directories, and is only useful to filesystems. unsigned int (*poll) (struct file *, struct poll_table_struct *); The poll method is the back end of two system calls, poll and select, both used to inquire if a device is readable or writable or in some special state. Either system call can block until a device becomes readable or writable.
If a driver doesnt define its poll method, the device is assumed to be both readable and writable, and in no special state. The return value is a bit mask describing the status of the device.

File Operations
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); The ioctl system call offers a way to issue device-specific commands. Additionally, a few ioctl commands are recognized by the kernel without referring to the fops table. If the device doesnt offer an ioctl entry point, the system call retur ns an error for any request that isnt predefined (-ENOTTY, No such ioctl for device). int (*mmap) (struct file *, struct vm_area_struct *); mmap is used to request a mapping of device memory to a processs address space. If the device doesnt implement this method, the mmap system call retur ns -ENODEV.

File Operations
int (*open) (struct inode *, struct file *); Though this is always the first operation performed on the device file, the driver is not required to declare a corresponding method. If this entry is NULL, opening the device always succeeds, but your driver isnt notified. int (*flush) (struct file *); The flush operation is invoked when a process closes its copy of a file descriptor for a device; it should execute (and wait for) any outstanding operations on the device. This must not be confused with the fsync operation requested by user programs. Currently, flush is used only in the network file system (NFS) code. If flush is NULL, it is simply not invoked.

File Operations
int (*release) (struct inode *, struct file *);
This operation is invoked when the file structure is being released. Like open, release can be missing.

int (*fsync) (struct inode *, struct dentry *, int);


This method is the back end of the fsync system call, which a user calls to flush any pending data. If not implemented in the driver, the system call returns -EINVAL.

Note:
Referer linux/fs.h data structure for a complete detail

Implementing the functionalities


The scull device driver implements only the most important device methods, and uses the tagged format to declare its file_operations structur e: struct file_operations my_fops = {
llseek: my_llseek, read: my_read, write: my_write, ioctl: my_ioctl, open: my_open, release: my_release,

};

Note: A tagged notation


This declaration uses the tagged structure initialization syntax, as we described earlier. This syntax is preferred because it makes drivers more portable across changes in the definitions of the structures, and arguably makes the code more compact and readable. Tagged initialization allows the reordering of structure members; In some cases, substantial performance improvements have been realized by placing frequently accessed members in the same hardware cache line.

The file Structure


The struct file, defined in <linux/fs.h>, is the second most important data structure used in device drivers. Note that a file has nothing to do with the FILEs of user-space programs. A FILE is defined in the C library and never appears in kernel code. A struct file, on the other hand, is a kernel structure that never appears in user programs. The file Structure represents an open file. (It is not specific to device drivers; every open file in the system has an associated struct file in kernel space.) It is created by the kernel on open and is passed to any function that operates on the file, until the last close. After all instances of the file are closed, the kernel releases the data structure. An open file is different from a disk file, represented by struct inode.

The file Structure


In the kernel sources, a pointer to struct file is usually called either file or filp (file pointer). Well consistently call the pointer filp to prevent ambiguities with the structure itself. Thus, file refers to the structure and filp to a pointer to the structure. The most important fields of struct file are shown here. As in the previous section

mode_t f_mode;
The file mode identifies the file as either readable or writable (or both), by means of the bits FMODE_READ and FMODE_WRITE.

The file Structure


loff_t f_pos;
The current reading or writing position. loff_t is a 64-bit value (long long in gcc terminology). The driver can read this value if it needs to know the current position in the file

unsigned int f_flags;


These are the file flags, such as O_RDONLY, O_NONBLOCK, and O_SYNC. A driver needs to check the flag for non blocking operation, while the other flags ar e seldom used. In particular, read/write permission should be checked using f_mode instead of f_flags. All the flags are defined in the header <linux/fcntl.h>.

The file Structure


struct file_operations *f_op;
The operations associated with the file.

void *private_data;
The open system call sets this pointer to NULL before calling the open method for the driver. The driver is free to make its own use of the field or to ignore it private_data is a useful resource for preserving state information across system calls

struct dentry *f_dentry;


The directory entry (dentry) structure associated with the file. Dentries are an optimization introduced in the 2.1 development series. Device driver writers normally need not concern themselves with dentry structures, other than to access the inode structure as filp->f_dentry->d_inode.

Data transfer between UAS/KAS


The code for read and write in scull needs to copy a whole segment of data to or from the user address space. This capability is offered by the following kernel functions, which copy an arbitrary array of bytes and sit at the heart of every read and write implementation: unsigned long copy_to_user (void *to, const void *from, unsigned long count); unsigned long copy_from_user (void *to, const void *from, unsigned long count); Although these functions behave like normal memcpy functions, a little extra care must be used when accessing user space from kernel code. The user pages being addressed might not be currently present in memory.

Data transfer between UAS/KAS


Another method
put_user (Char *Message_Ptr, char *buffer); get_user (Char *Message_Ptr, char *buffer);

Find more details at asm/uaccess.h

A sample character Driver: simple_char -1


/* kernel code needs to have this defined */ #define __KERNEL__ /* needed for versioned kernels (i.e. most kernels in current use) */ #ifdef MODVERSIONS #include <linux/modversions.h> #endif /* needed to make a module */ #ifdef MODULE #include <linux/module.h> #endif

A sample character Driver: simple_char -2


/* needed to deal w/ being a char device */ #include <linux/fs.h> /* needed to deal w/ userspace memory */ #include <asm/uaccess.h> /* to get the string functions */ #include <linux/string.h>

A sample character Driver: simple_char -3


MODULE_LICENSE ("GPL"); /* tells everybody who wrote the module */ MODULE_AUTHOR (AAA"); /* gives a brief description of what the module does */ MODULE_DESCRIPTION ("This module provides a simple example of a character device"); static int major_num = 0; /* default to 0 (dynamic), allowing override */ MODULE_PARM (major_num, "i"); MODULE_PARM_DESC (major_num, "The major number associated with this driver");

A sample character Driver: simple_char - 4


static char default_phrase[128] = "this is the boring default phrase because someone was too lazy to pass a parameter to insmod\n"; static char *phrase; static unsigned int phrase_length; MODULE_PARM (phrase, "s"); MODULE_PARM_DESC (phrase, "The phrase that will be sent back by the driver on read()");

A sample character Driver: simple_char - 5


/* the name of the device for /proc/devices (nothing special about NAME, the actual name gets set by the register_chrdev() call) */ #define NAME "simple_char" /* XXX */ #if 0 /* size of the "scratch" buffer */ #define SCRATCH_SIZE 50 /* the "scratch" area of the device */ static char scratch_area [SCRATCH_SIZE] = "bleh, this is the scratch message\n"; #endif

A sample character Driver: simple_char - 6


int simple_open(struct inode *inode, struct file *filp) { /* increment the usage count */ MOD_INC_USE_COUNT; return 0; } int simple_release(struct inode *inode, struct file *filp) { /* decrement the usage count */ MOD_DEC_USE_COUNT; return 0; }

A sample character Driver: simple_char - 7


#define EOF 0 ssize_t simple_read(struct file *filp, char *buff, size_t count, loff_t *offp) { int num_xfered = 0; /* return EOF if an offset beyond the buffer size is requested */ if (*offp >= phrase_length) { printk(KERN_DEBUG "simple_read(): EOF reached\n"); return num_xfered; } /* decide how much to actually send back (being careful not to read beyond the length of our buffer) */ num_xfered = (*offp + count < phrase_length ? count : phrase_length *offp);

A sample character Driver: simple_char - 8


/* we can't actually reference user-land memory directly, as it may be swapped out currently... as such, we have to use special functions to deal w/ that memory (ie, not memcpy) */ copy_to_user(buff, phrase + *offp, num_xfered); /* update the current position in the file */ *offp += num_xfered; /* send back the number of bytes we transfered to buff */ printk(KERN_DEBUG "simple_read(): num_xfered=%d\n", num_xfered); return num_xfered; }

A sample character Driver: simple_char - 9

/* this structure tells the kernel which operations are supported and how */ static struct file_operations simple_fops = { open: simple_open, release: simple_release, read: simple_read };

A sample character Driver: simple_char - 10

int init_module(void) { int result; /* set the owner field of simple_fops */ SET_MODULE_OWNER (&simple_fops); printk (KERN_DEBUG "registering simple_char\n"); result = register_chrdev( major_num, NAME, &simple_fops); if (result < 0) { printk (KERN_WARNING "simple_char: unsuccessful request of major #: %d\n", major_num); return result; /* report any problems during registration */

A sample character Driver: simple_char - 11

/* get and cache the length of the phrase */ if (phrase == NULL) phrase = default_phrase; phrase_length = strlen(phrase); /* if we're using dynamic allocation, find out which # we were assigned */ if (major_num == 0) major_num = result; return 0;

A sample character Driver: simple_char - 12

/* cleanup_module is called when the module is unloaded */ void cleanup_module(void) { int result; printk(KERN_DEBUG "unregistering simple_char\n"); /* we have to make sure and clean up after ourselves */ result = unregister_chrdev(major_num, NAME); if (result < 0){ printk(KERN_WARNING "simple_char: unregister_chrdev(%d, " NAME ") returned '%d'\n", major_num, result); } }

Cross compiling the driver


Compile the same code across the path of targets kernel source code

Use zmodem via minicom to download the target elf

Write a sample C application to test your driver.


Dont forget to cross compile and download and run on the board

Putting it together
Device driver in Linux is basically a file representation Device model of Linux is a new move in Linux 2.6 with sysfs Synchronization and KCP have improved for Multi core There is a plenty of room for New Device Vendors and instance of such devices too Backwards compatibility is a difficult task to achieve but a regular updates can ease this a bit Device software is the next generation requirement and Linux has a huge population in this

Das könnte Ihnen auch gefallen