You are on page 1of 21

1. A Simple Introduction to Device Drivers under Linux 2. Tour the Linux generic SCSI driver 3.

High-Level Device-Driver Programming

http://140.120.7.20/LinuxRef/LDD/LinuxDeviceDrivers-intro.ht ml /dev/hello_world: A Simple Introduction to Device Drivers under Linux (Source Origin)
by Valerie Henson 07/05/2007 Since the misty days of yore, the first step in learning a new programming language has been writing a program that prints "Hello, world!" (See the Hello World Collection for a list of more than 300 "Hello, world!" examples.) In this article, we will use the same approach to learn how to write simple Linux kernel modules and device drivers. We will learn how to print "Hello, world!" from a kernel module three different ways: printk(), a /proc file, and a device in /dev.

Preparation: Installing Kernel Module Compilation Requirements


For the purposes of this article, a kernel module is a piece of kernel code that can be dynamically loaded and unloaded from the running kernel. Because it runs as part of the kernel and needs to interact closely with it, a kernel module cannot be compiled in a vacuum. It needs, at minimum, the kernel headers and configuration for the kernel it will be loaded into. Compiling a module also requires a set of development tools, such as a compiler. For simplicity, we will briefly describe how to install the requirements to build a kernel module using Debian, Fedora, and the "vanilla" Linux kernel in tarball form. In all cases, you must compile your module against the source for the running kernel (the kernel executing on your system when you load the module into your kernel). A note on kernel source location, permissions, and privileges: the kernel source customarily used to be located in /usr/src/linux and owned by root. Nowadays, it is recommended that the kernel source be located in a home directory and owned by a non-root user. The commands in this article are all run as a non-root user, using sudo to temporarily gain root privileges only when necessary. To setup sudo, see the sudo(8), visudo(8), and sudoers(5) main pages. Alternatively, become root, and run all the commands as root if desired. Either way, you will need root access to follow the instructions in this article. Preparation for Compiling Kernel Modules Under Debian The module-assistant package for Debian installs packages and configures the system to build out-of-kernel modules. Install it with:
$ sudo apt-get install module-assistant

That's it; you can now compile kernel modules. For further reading, the Debian Linux Kernel Handbook has an in-depth discussion on kernel-related tasks in Debian.

Fedora Kernel Source and Configuration The kernel-devel package for Fedora has a package that includes all the necessary kernel headers and tools to build an out-of-kernel module for a Fedora-shipped kernel. Install it with:
$ sudo yum install kernel-devel

Again, that's all it takes; you can now compile kernel modules. Related documentation can be found in the Fedora release notes. Vanilla Kernel Source and Configuration If you choose to use the vanilla Linux kernel source, you must configure, compile, install, and reboot into your new vanilla kernel. This is definitely not the easy route and this article will only cover the very basics of working with vanilla kernel source. The canonical Linux source code is hosted at http://kernel.org. The most recent stable release is linked to from the front page. Download the full source release, not the patch. For example, the current stable release is located at http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.21.5.tar.bz2. For faster download, find the closest mirror from the list at http://kernel.org/mirrors/, and download from there. The easiest way to get the source is using wget in continue mode. HTTP is rarely blocked, and if your download is interrupted, it will continue where it left off.
$ wget -c "http://kernel.org/pub/linux/kernel/v2.6/linux-<version>.tar.bz2"

Unpack the kernel source:


$ tar xjvf linux-<version>.tar.bz2

Now your kernel is located in linux-<version>/. Change directory into your kernel and configure it:
$ cd linux-<version> $ make menuconfig

A number of really nice make targets exist to automatically build and install a kernel in many forms: Debian package, RPM package, gzipped tar, etc. Ask the make system for help to list them all:
$ make help

A target that will work on almost every distro is:


$ make tar-pkg

When finished building, install your new kernel with:


$ sudo tar -C / -xvf linux-<version>.tar

Then create a symbolic link to the source tree in the standard location:
$ sudo ln -s <location of top-level source directory> /lib/modules/'uname -r'/build

Now the kernel source is ready for compiling external modules. Reboot into your new kernel before loading modules compiled against this source tree.

"Hello, World!" Using printk()


For our first module, we'll start with a module that uses the kernel message facility, printk(), to print "Hello, world!". printk() is basically printf() for the kernel. The output of printk() is printed to the kernel message buffer and copied to /var/log/messages (with minor variations depending on how syslogd is configured). Download the hello_printk module tarball and extract it:
$ tar xzvf hello_printk.tar.gz

This contains two files: Makefile, which contains instructions for building the module, and hello_printk.c, the module source file. First, we'll briefly review the Makefile.
obj-m := hello_printk.o

obj-m is a list of what kernel modules to build. The .o and other objects will be automatically built from the corresponding .c file (no need to list the source files explicitly).
KDIR := /lib/modules/$(shell uname -r)/build

KDIR is the location of the kernel source. The current standard is to link to the associated source tree from the directory containing the compiled modules.
PWD := $(shell pwd)

PWD is the current working directory and the location of our module source files.
default: $(MAKE) -C $(KDIR) M=$(PWD) modules

default is the default make target; that is, make will execute the rules for this target unless it is told to build another target instead. The rule here says to run make with a working directory of the directory containing the kernel source and compile only the modules in the $(PWD) (local) directory. This allows us to use all the rules for compiling modules defined in the main kernel source tree. Now, let's run through the code in hello_printk.c.
#include <linux/init.h> #include <linux/module.h>

This includes the header files provided by the kernel that are required for all modules. They include things like the definition of the module_init() macro, which we will see later on.
static int __init hello_init(void) { printk("Hello, world!\n"); return 0; }

This is the module initialization function, which is run when the module is first loaded. The __init keyword tells the kernel that this code will only be run once, when the module is loaded. The printk() line writes the string "Hello, world!" to the kernel message buffer. The format of printk() arguments is, in most cases, identical to that of printf(3).
module_init(hello_init);

The module_init() macro tells the kernel which function to run when the module first starts up. Everything else that happens inside a kernel module is a consequence of what is set up in the module initialization function.
static void __exit hello_exit(void) { printk("Goodbye, world!\n"); } module_exit(hello_exit);

Similarly, the exit function is run once, upon module unloading, and the module_exit() macro identifies the exit function. The __exit keyword tells the kernel that this code will only be executed once, on module unloading.
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Valerie Henson <val@nmt.edu>"); MODULE_DESCRIPTION("\"Hello, world!\" minimal module"); MODULE_VERSION("printk");

MODULE_LICENSE() informs the kernel what license the module source code is under, which affects which symbols (functions, variables, etc.) it may access in the main kernel. A GPLv2 licensed module (like this one) can access all the symbols. Certain module licenses will taint the kernel, indicating that non-open or untrusted code has been loaded. Modules without a MODULE_LICENSE() tag are assumed to be non-GPLv2 and will result in tainting the kernel. Most kernel developers will ignore bug reports from tainted kernels because they do not have access to all the source code, which makes debugging much more difficult. The rest of the MODULE_*() macros provide useful identifying information about the module in a standard format. /dev/hello_world: A Simple Introduction to Device Drivers under Linux Now, to compile and run the code. Change into the directory and build the module:
$ cd hello_printk $ make

Then, load the module, using insmod, and check that it printed its message, using dmesg, a program that prints out the kernel message buffer:
$ sudo insmod ./hello_printk.ko $ dmesg | tail

You should see "Hello, world!" in the output from dmesg. Now unload the module, using rmmod, and check for the exit message:
$ sudo rmmod hello_printk $ dmesg | tail

You have successfully compiled and installed a kernel module!

Hello, World! Using /proc


One of the easiest and most popular ways to communicate between the kernel and user programs is via

a file in the /proc file system. /proc is a pseudo-file system, where reads from files return data manufactured by the kernel, and data written to files is read and handled by the kernel. Before /proc, all user-kernel communication had to happen through a system call. Using a system call meant choosing between finding a system call that already behaved the way you needed (often not possible), creating a new system call (requiring global changes to the kernel, using up a system call number, and generally frowned upon), or using the catch-all ioctl() system call, which requires the creation of a special file that the ioctl() operates on (complex and frequently buggy, and very much frowned upon). /proc provides a simple, predefined way to pass data between the kernel and userspace with just enough framework to be useful, but still enough freedom that kernel modules can do what they need. For our purposes, we want a file in /proc that will return "Hello, world!" when read. We'll use /proc/hello_world. Download and extract the hello_proc module tarball. We'll run through the code in hello_proc.c.
#include <linux/init.h> #include <linux/module.h> #include <linux/proc_fs.h>

This time, we add the header file for procfs, which includes support for registering with the /proc file system. The next function will be called when a process calls read() on the /proc file we will create. It is simpler than a completely generic read() system call implementation because we only allow the "Hello, world!" string to be read all at once.
static int hello_read_proc(char *buffer, char **start, off_t offset, int size, int *eof, void *data) {

The arguments to this function deserve an explicit explanation. buffer is a pointer to a kernel buffer where we write the output of the read(). start is used for more complex /proc files; we ignore it here. offset tells us where to begin reading inside the "file"; we only allow an offset of 0 for simplicity. size is the size of the buffer in bytes; we must check that we don't write past the end of the buffer accidentally. eof is a short cut for indicating EOF (end of file) rather than the usual method of calling read() again and getting 0 bytes back. data is again for more complex /proc files and ignored here. Now, for the body of the function:
char *hello_str = "Hello, world!\n"; int len = strlen(hello_str); /* Don't include the null byte. */ /* * We only support reading the whole string at once. */ if</ (size < len) return< -EINVAL; /* * If file position is non-zero, then assume the string has * been read and indicate there is no more data to be read. */ if (offset != 0) return 0; /*

* We know the buffer is big enough to hold the string. */ strcpy(buffer, hello_str); /* * Signal EOF. */ *eof = 1; return len; }

Next, we need to register with the /proc subsystem in our module initialization function.
static int __init hello_init(void) { /* * Create an entry in /proc named "hello_world" that calls * hello_read_proc() when the file is read. */ if (create_proc_read_entry("hello_world", 0, NULL, hello_read_proc, NULL) == 0) { printk(KERN_ERR "Unable to register \"Hello, world!\" proc file\n"); return -ENOMEM; } } return 0;

module_init(hello_init);

And unregister when the module unloads (if we didn't do this, when a process attempted to read /proc/hello_world, the /proc file system would try to execute a function that no longer existed and the kernel would panic).
static void __exit hello_exit(void) { remove_proc_entry("hello_world", NULL); } module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Valerie Henson <val@nmt.edu>"); MODULE_DESCRIPTION("\"Hello, world!\" minimal module"); MODULE_VERSION("proc");

Then, we're ready to compile and load the module:


$ cd hello_proc $ make $ sudo insmod ./hello_proc.ko

Now, there is a file named /proc/hello_world that will produce "Hello, world!" when read:

$ cat /proc/hello_world Hello, world!

You can create many more /proc files from the same driver, add routines to allow writing to /proc files, create directories full of /proc files, and more. For anything more complicated than this driver, it is easier and safer to use the seq_file helper routines when writing /proc interface routines. For further reading, see Driver porting: The seq_file interface.

Hello, World! Using /dev/hello_world


Now we will implement "Hello, world!" using a device file in /dev, /dev/hello_world. Back in the old days, a device file was a special file created by running a crufty old shell script named MAKEDEV which called the mknod command to create every possible file in /dev, regardless of whether the associated device driver would ever run on that system. The next iteration, devfs, created /dev files when they were first accessed, which led to many interesting locking problems and wasteful attempts to open device files to see if the associated device existed. The current version of /dev support is called udev, since it creates /dev links with a userspace program. When kernel modules register devices, they appear in the sysfs file system, mounted on /sys. A userspace program, udev, notices changes in /sys and dynamically creates /dev entries according to a set of rules usually located in /etc/udev/. Download the hello world module tarball. We'll go through hello_dev.c.
#include #include #include #include <linux/fs.h> <linux/init.h> <linux/miscdevice.h>< <linux/module.h>

#include <asm/uaccess.h>

As we can see from looking at the necessary header files, creating a device requires quite a bit more kernel support than our previous methods. fs.h includes the definitions for a file operations structure, which we must fill out and attach to our /dev file. miscdevice.h includes support for registering a miscellaneous device file. asm/uaccess.h includes functions for testing whether we can read or write to userspace memory without violating permissions. hello_read() is the function called when a process calls read() on /dev/hello. It writes "Hello, world!" to the buffer passed in the read() call.
static ssize_t hello_read(struct file * file, char * buf, size_t count, loff_t *ppos) { char *hello_str = "Hello, world!\n"; int len = strlen(hello_str); /* Don't include the null byte. */ /* * We only support reading the whole string at once. */ if (count < len) return -EINVAL; /* * If file position is non-zero, then assume the string has * been read and indicate there is no more data to be read. */ if (*ppos != 0)

return 0; /* * Besides copying the string to the user provided buffer, * this function also checks that the user has permission to * write to the buffer, that it is mapped, etc. */ if (copy_to_user(buf, hello_str, len)) return -EINVAL; /* * Tell the user how much data we wrote. */ *ppos = len; } return len;

/dev/hello_world: A Simple Introduction to Device Drivers under Linux Next, we create the file operations struct defining what actions to take when the file is accessed. The only file operation we care about is read.
static const struct file_operations hello_fops = { .owner = THIS_MODULE, .read = hello_read, };

Now, create the structure containing the information needed to register a miscellaneous device with the kernel.
static struct miscdevice hello_dev = { /* * We don't care what minor number we end up with, so tell the * kernel to just pick one. */ MISC_DYNAMIC_MINOR, /* * Name ourselves /dev/hello. */ "hello", /* * What functions to call when a program performs file * operations on the device. */ &hello_fops };

As usual, we register the device in the module's initialization function.


static int __init hello_init(void) { int ret; /* * Create the "hello" device in the /sys/class/misc directory. * Udev will automatically create the /dev/hello device using

* the default rules. */ ret = misc_register(&hello_dev); if (ret) printk(KERN_ERR "Unable to register \"Hello, world!\" misc device\n"); } return ret;

module_init(hello_init);

And remember to unregister the device in the exit function.


static void __exit hello_exit(void) { misc_deregister(&hello_dev); } module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Valerie Henson <val@nmt.edu>"); MODULE_DESCRIPTION("\"Hello, world!\" minimal module"); MODULE_VERSION("dev");

Compile and load the module:


$ cd hello_dev $ make $ sudo insmod ./hello_dev.ko

Now there is a device named /dev/hello that will produce "Hello, world!" when read by root:
$ sudo cat /dev/hello Hello, world!

But we can't read it as a regular user:


$ cat /dev/hello cat: /dev/hello: Permission denied $ ls -l /dev/hello crw-rw---- 1 root root 10, 61 2007-06-20 14:31 /dev/hello

This is what happens with the default udev rule, which says that when a miscellaneous device appears, create a file named /dev/<device name> and give it permissions 0660 (owner and group have read-write access, everyone else has no access). We would really like instead for the device be readable by regular users and have a link to it named /dev/hello_world. In order to do this, we'll write a udev rule. The udev rule has to do two things: create a symbolic link and change the permissions on device to make world readable. The rule that accomplishes this is:
KERNEL=="hello", SYMLINK+="hello_world", MODE="0444"

We'll break the rule down into parts and explain each part.

KERNEL=="hello" says to execute the rest of the rule when a device with a name the same as this string (the == operator means "comparison") appears in /sys. The hello device appeared when we called misc_register() with a structure containing the device name "hello". See the result for yourself in /sys:
$ ls -d /sys/class/misc/hello/ /sys/class/misc/hello/

SYMLINK+="hello_world" says to add (the += operator means append) hello_world to the list of symbolic links that should be created when the device appears. In our case, we know this is the only symbolic link in the list, but other devices may have multiple udev rules that create multiple different symbolic links, so it is good practice add to the list instead of assigning to it. MODE="0444" says to set the permissions of the original device file to the 0444 mode, which allows owner, group, and world all to read the file. In general, it is very important to use the correct operator (==, +=, or =), or unexpected things will happen. Now that we understand what the rule does, let's install it in the /etc/udev directory. Udev rules files are arranged in much the same manner as the System V init scripts in /etc/init.d/. Udev executes every script the udev rules directory, /etc/udev/rules.d, in alphabetical/numerical order. Like System V init scripts, the files in the /etc/udev/rules.d directory are usually symbolic links to the real rules files, with the symbolic links named so that the rules will be executed in the correct order. Copy the hello.rules file from the hello_dev directory into the /etc/udev/ directory and create a link to it that will be executed before any other rules file:
$ sudo cp hello.rules /etc/udev/ $ sudo ln -s ../hello.rules /etc/udev/rules.d/010_hello.rules

Now, reload the hello world driver and look at the new /dev entries:
$ sudo rmmod hello_dev $ sudo insmod ./hello_dev.ko $ ls -l /dev/hello* cr--r--r-- 1 root root 10, 61 2007-06-19 21:21 /dev/hello lrwxrwxrwx 1 root root 5 2007-06-19 21:21 /dev/hello_world -> hello

Now we have /dev/hello_world! Finally, check that you can read the "Hello, world!" devices as a normal user:
$ cat /dev/hello_world Hello, world! $ cat /dev/hello Hello, world!

For more details on writing udev rules, see Writing udev rules, by Daniel Drake. Valerie Henson is a Linux consultant specializing in file systems, and maintainer of the TCP/IP Drinking Game. Return to LinuxDevCenter.com.

Tour the Linux generic SCSI driver (Source Origin)


Dive into the Linux generic SCSI driver API and surface with a usage example Level: Intermediate Mao Tao (taomaoy@cn.ibm.com), Software Engineer, IBM 25 Feb 2009 Computers control and transfer data to SCSI devices via SCSI commands. In this article, the author introduces some of the SCSI commands and methods of executing SCSI commands when using SCSI API in Linux. He provides background on the SCSI client/server model and the storage SCSI command. Next, he explains the Linux generic SCSI driver API and offers an example of using a system that focuses on executing the inquiry command using the generic driver. The SCSI client/server model During the communication between a host and storage, a host generally acts as a SCSI initiator. In computer storage, the SCSI initiator is the endpoint that initiates a SCSI session, meaning it sends a SCSI command. The storage often acts as a SCSI target which receives and processes SCSI commands. The SCSI target waits for the initiator's commands and then provides required input/output data transfers. The target usually provides the initiators one or more logical unit numbers (LUN). In computer storage, a LUN is simply the number assigned to a logical unit. A logical unit is a SCSI protocol entity, the only one which may be addressed by the actual I/O operations. Each SCSI target provides one or more logical units; it does not perform I/O as itself, but on behalf of a specific logical unit. In a storage area, a LUN often represents a SCSI disk on which a host can perform a read and write operation. Figure 1 shows how the SCSI client/server model works. Figure 1. The SCSI client/server model

The initiator first sends commands to the target which decodes the command and then may request data from the initiator or send data to the initiator. After that, the target sends the status to the initiator. If the status is bad, then the initiator sends request sense command to the target. The target returns the sense data to indicate what went wrong. Now let's focus on storage-related SCSI commands.

Storage-related SCSI commands Storage-related SCSI commands are defined mainly in the SCSI Architecture Model (SAM), SCSI Primary Commands (SPC), and SCSI Block Commands (SBC): SAM defines the SCSI systems model, the functional partitioning of the SCSI standard set, and the requirements applicable to all SCSI implementations and implementation standards. SPC defines behaviors that are common to all SCSI device models. SBC defines the command set extensions to facilitate operation of SCSI direct-access block devices. Each SCSI command is described by a Command Descriptor Block (CDB) which defines the operations to be performed by the SCSI device. The SCSI commands involve data commands which are used for transferring data from or to SCSI devices and non-data commands which request or set the configure parameters of a SCSI device. In Table 1, you can see the most commonly used commands. Table 1. Most commonly used SCSI commands Command Description Inquiry Request general information of the target device. Test/Unit/Ready Check whether the target device is ready for the transfer operation. READ Transfer data from SCSI target device. WRITE Transfer data to SCSI target device. Request Sense Request the sense data of the last command. Read Capacity Request the storage capacity information. All SCSI commands should begin with an operation code as the first byte. This indicates what operation it represents. All SCSI commands should also contain a control byte. This is typically the last byte of the command, used for vendor-specific information and other uses. Now on to the generic SCSI driver. The Linux generic SCSI driver SCSI devices under Linux are often named to help the user identify the device. For example, the first SCSI CD-ROM is /dev/scd0. SCSI disks are labeled /dev/sda, /dev/sdb, /dev/sdc, etc. Once device initialization is complete, the Linux SCSI disk driver interfaces (sd) only send SCSI READ and WRITE commands. These SCSI devices may also have generic names or interfaces, such as /dev/sg0, /dev/sg1 or /dev/sga, /dev/sgb, etc. With these generic driver interfaces, you can directly send SCSI commands to SCSI devices, bypassing a file system which is normally created on a SCSI disk and mounted under a directory. In Figure 2, you can see how different applications communicate with SCSI devices. Figure 2. The myriad ways of communicating with a SCSI device

With Linux generic driver interface, you can build applications which can send more kinds of SCSI commands to SCSI devices. In other words, you have a choice. To determine which SCSI device stands for which sg interface, you can use the sg_map command (within Debian sg3-utils package) to list the maps: With Red Hat/Fedora or Debian family, sg3_utils should be installed. Let's take a look at how a typical SCSI system call command would be performed.
[root@taomaoy ~]# sg_map -i /dev/sg0 /dev/sda ATA /dev/sg1 /dev/scd0 HL-DT-ST ST3160812AS RW/DVD GCC-4244N 3.AA 1.02

On kubuntu:
$ sudo sg_map -i /dev/sg0 /dev/scd0 /dev/sg1 /dev/scd1 /dev/sg2 /dev/sda /dev/sg3 /dev/sdb PIONEER SONY ATA ATA DVD-ROM DVD-126P DVD RW DRU-820A ST3808110AS ST3200827AS 1.02 1.0a 3.AA 3.AA

The typical SCSI generic driver commands SCSI generic driver supports many typical system calls for character device, such as open(), close(), read(), write, poll(), ioctl(). The procedure of sending SCSI commands to a specific SCSI device is also very simple: 1. 2. 3. 4. 5. Open the SCSI generic device file (such as sg1) to get the file descriptor of SCSI device. Prepare the SCSI command. Set related memory buffers. Call the ioctl() function to execute the SCSI command. Close the device file.

A typical ioctl() function could be written like this: ioctl(fd,SG_IO,p_io_hdr);. The ioctl() function here requires three parameters: 1. fd is the file descriptor of the device file. After the device file is successfully opened by calling open(), this parameter could be acquired. 2. SG_IO indicates that an sg_io_hdr object is handed as the third parameter of the ioctl() function and will return when the SCSI command is finished. 3. The p_io_hdr is a pointer to the sg_io_hdr object which contains the SCSI command and other settings. The most important data structure for SCSI generic driver is struct sg_io_hdr, defined in /usr/include/scsi/sg.h, contains information on how the SCSI command would be

performed. Listing 1 shows the definition of the structure. Listing 1. Definition of struct sg_io_hdr
typedef struct sg_io_hdr { int interface_id; /* [i] 'S' (required) */ int dxfer_direction; /* [i] */ unsigned char cmd_len; /* [i] */ unsigned char mx_sb_len; /* [i] */ unsigned short iovec_count; /* [i] */ unsigned int dxfer_len; /* [i] */ void * dxferp; /* [i], [*io] */ unsigned char * cmdp; /* [i], [*i] */ unsigned char * sbp; /* [i], [*o] */ unsigned int timeout; /* [i] unit: millisecs */ unsigned int flags; /* [i] */ int pack_id; /* [i->o] */ void * usr_ptr; /* [i->o] */ unsigned char status; /* [o] */ unsigned char masked_status; /* [o] */ unsigned char msg_status; /* [o] */ unsigned char sb_len_wr; /* [o] */ unsigned short host_status; /* [o] */ unsigned short driver_status; /* [o] */ int resid; /* [o] */ unsigned int duration; /* [o] */ unsigned int info; /* [o] */ } sg_io_hdr_t; /* 64 bytes long (on i386) */

Not all the fields in this structure are required, so only those that are commonly used are introduced here: interface_id: Should always be S. dxfer_direction: Used for data transfer direction, could be one of the following values: SG_DXFER_NONE: No data transfer is needed. For example, a SCSI Test Unit Ready command. SG_DXFER_TO_DEV: Data is transferred to device. A SCSI WRITE command. SG_DXFER_FROM_DEV: Data is transferred from device. A SCSI READ command. SG_DXFER_TO_FROM_DEV: Data is transferred both ways. SG_DXFER_UNKNOWN: The data transfer direction is unknown. cmd_len: The length in bytes of cmdp that points to the SCSI command. mx_sb_len: The maximum size that can be written back to the sbp when a sense_buffer is output. dxfer_len: The length of the user memory for data transfer. dxferp: A pointer to user memory of at least dxfer_len bytes in length for data transfer. cmdp: A pointer to the SCSI command to be executed. sbp: A pointer to sense buffer. timeout: Used to timeout the given command. status: SCSI status byte as defined by the SCSI standard.

In summary, when data is about to be transferred using this method, cmdp must point to SCSI CDB whose length is stored in cmd_len; sbp points to a user memory whose maximum length is mx_sb_len. In case an error occurs, the sense data would be written back to this place. dxferp points to a memory, the data would be transferred from or to the SCSI device depending on the dxfer_direction. Finally, let's look at an inquiry command and how it would execute using the generic driver. Example: Executing an inquiry command An inquiry command is the most common SCSI command that all SCSI devices implement. This command is used to request the basic information of the SCSI device and is often used as a ping operation to test to see if the SCSI device is online. Table 2 shows how the SCSI standard is defined. Table 2. The inquiry command format definition bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 byte 0 Operation code = 12h byte 1 LUN Reserved EVPD byte 2 Page code byte 3 Reserved byte 4 Allocation length byte 5 Control If the EVPD parameter bit (for enable vital product data) is zero and the Page Code parameter byte is zero, then the target will return the standard inquiry data. If the EVPD parameter is one, then the target would return vendor-specific data according to the page code fields. Listing 2 shows the source code clips of using the SCSI generic API. Let's first look at the examples of setting sg_io_hdr. Listing 2. Setting sg_io_hdr
struct sg_io_hdr * init_io_hdr() { struct sg_io_hdr * p_scsi_hdr = (struct sg_io_hdr *)malloc(sizeof(struct sg_io_hdr)); memset(p_scsi_hdr, 0, sizeof(struct sg_io_hdr)); if (p_scsi_hdr) { p_scsi_hdr->interface_id = 'S'; /* this is the only choice we have! */ /* this would put the LUN to 2nd byte of cdb*/ p_scsi_hdr->flags = SG_FLAG_LUN_INHIBIT; } return p_scsi_hdr; } void destroy_io_hdr(struct sg_io_hdr * p_hdr) { if (p_hdr) { free(p_hdr); } } void set_xfer_data(struct sg_io_hdr * p_hdr, void * data, unsigned int length) { if (p_hdr) { p_hdr->dxferp = data; p_hdr->dxfer_len = length; } }

void set_sense_data(struct sg_io_hdr * p_hdr, unsigned char * data, unsigned int length) { if (p_hdr) { p_hdr->sbp = data; p_hdr->mx_sb_len = length; } }

These functions are used for setting the sg_io_hdr object. Some of the fields point to user space memory; when the execution is finished, inquiry output data from the SCSI command is copied into the memory where dxferp points to. If there is an error and the sense data is required, the sense data would be copied to where sbp points to. The example for sending an inquiry command to a SCSI target is shown in Listing 3. Listing 3. Sending an inquiry command to a SCSI target
int execute_Inquiry(int fd, int page_code, int evpd, struct sg_io_hdr * p_hdr) { unsigned char cdb[6]; /* set the cdb format */ cdb[0] = 0x12; /*This is for Inquery*/ cdb[1] = evpd & 1; cdb[2] = page_code & 0xff; cdb[3] = 0; cdb[4] = 0xff; cdb[5] = 0; /*For control filed, just use 0 */ p_hdr->dxfer_direction = SG_DXFER_FROM_DEV; p_hdr->cmdp = cdb; p_hdr->cmd_len = 6; int ret = ioctl(fd, SG_IO, p_hdr); if (ret<0) { printf("Sending SCSI Command failed.\n"); close(fd); exit(1); } return p_hdr->status;

So the function first prepares the CDB according to the inquiry standard format and then calls the ioctl() function, handing file descriptor SG_IO, and the sg_io_hdr object; the return status is stored in the "status" field of the sg_io_hdr object. Now let's see how the application program uses this function to execute the inquiry command (Listing 4): Listing 4. Application program executes the inquiry command
unsigned char sense_buffer[SENSE_LEN]; unsigned char data_buffer[BLOCK_LEN*256]; void test_execute_Inquiry(char * path, int evpd, int page_code) {

struct sg_io_hdr * p_hdr = init_io_hdr(); set_xfer_data(p_hdr, data_buffer, BLOCK_LEN*256); set_sense_data(p_hdr, sense_buffer, SENSE_LEN); int status = 0; int fd = open(path, O_RDWR); if (fd>0) { status = execute_Inquiry(fd, page_code, evpd, p_hdr); printf("the return status is %d\n", status); if (status!=0) { show_sense_buffer(p_hdr); } else{ show_vendor(p_hdr); show_product(p_hdr); show_product_rev(p_hdr); } } else { printf("failed to open sg file %s\n", path); } close(fd); destroy_io_hdr(p_hdr); }

The process of sending the SCSI command here is quite simple. First the user space data buffer and sense buffer should be allocated and made to point to the sg_io_hdr object. Then, open the device driver and get the file descriptor. With these parameters, the SCSI command could be sent to the target device. The output from the SCSI target would then be copied to the user space buffers when the command is finished. Listing 5. Using parameters to send SCSI command to target device
void show_vendor(struct sg_io_hdr * hdr) { unsigned char * buffer = hdr->dxferp; int i; printf("vendor id:"); for (i=8; i<16; ++i) { putchar(buffer[i]); } putchar('\n'); } void show_product(struct sg_io_hdr * hdr) { unsigned char * buffer = hdr->dxferp; int i; printf("product id:"); for (i=16; i<32; ++i) { putchar(buffer[i]); } putchar('\n'); } void show_product_rev(struct sg_io_hdr * hdr) { unsigned char * buffer = hdr->dxferp; int i; printf("product ver:"); for (i=32; i<36; ++i) {

putchar(buffer[i]); } putchar('\n'); } int main(int argc, char * argv[]) { test_execute_Inquiry(argv[1], 0, 0); return EXIT_SUCCESS; }

The standard response of the SCSI Inquiry Command (Page Code and EVPD fields are all set to 0) is complicated. According to the standard, vendor ID extends from the 8th to the 15th byte, product ID from the 16th to the 31st byte, and product version from the 32nd to the 35th byte. This information could be retrieved to check whether the command has been successfully executed. After building this simple example, run it on /dev/sg0 which is normally the local hard disk. You should get the following:
[root@taomaoy scsi_test]# ./scsi_test /dev/sg0 the return status is 0 vendor id:ATA product id:ST3160812AS product ver:3.AA

The result is the same as the sg_map tool reports. Summary Linux provides a generic driver for SCSI devices and an application programming interface so users can build applications to send SCSI commands directly to SCSI devices. Users can manually make SCSI commands and set other related parameters in an sg_io_hdr object, then call ioctl() to execute their SCSI commands and get output from the same sg_io_hdr object. I hope this article has helped introduce you to mastering SCSI commands in your Linux programming efforts. Download Description Sample code Resources Learn Want more on Linux and the SCSI subsystem? Try these: "Anatomy of the Linux SCSI subsystem" (developerWorks, November 2007) introduces the Linux SCSI subsystem and discusses where this subsystem is going in the future. "Anatomy of Linux synchronization methods" (developerWorks, October 2007) outlines atomic synchronization operations (often used by SCSI drivers). "GCC hacks in the Linux kernel" (developerWorks, November 2008) introduces the GNU Compiler Collection suite; in it you'll find an example of range extension in use in a SCSI switch block (Listing 2). Name scsi_test.zip Size 3KB Download method HTTP

TC T10 SCSI Storage Interfaces is an excellent repository of knowledge on I/O interfaces, especially SCSI-3 and SAS. And here is a wonderful library on the Linux SCSI generic driver that includes release updates, backgrounder, features list, device driver downloads, utilities, and related Web sites. In the developerWorks Linux zone, find more resources for Linux developers (including developers who are new to Linux), and scan our most popular articles and tutorials. See all Linux tips and Linux tutorials on developerWorks. Stay current with developerWorks technical events and Webcasts. Get products and technologies With IBM trial software, available for download directly from developerWorks, build your next development project on Linux. Discuss Get involved in the developerWorks community through blogs, forums, podcasts, and spaces. About the author Mao Yuan Tao is a software engineer in IBM China System and Technology Lab (CSTL). His engineering background ranges from testing storage products to configuring SAN environments (including FC switches, back-end storage, and hosts) and even developing test tools.

High-Level Device-Driver Programming (Source Origin)


Submitted by Cameron Laird and Kathryn Soraiz on Mon, 12/01/2008 - 15:37. Tagged with Regular Expressions In September, "Regular Expressions" described several ways kernel programming has become more manageable: instead of being restricted to a narrow sliver of elite programmers, it's now feasible for nearly anyone who maintains his or her own Linux host. Much the same has happened with work on device drivers. While authorship of professional-caliber device drivers remains a specialized pursuit, it's now possible to be productive with device drivers recreationally, or at least on a basis other than full-time devotion.

Software for Easier Device Drivers


Most of the changes easing this work are familiar from larger trends in kernel programming mentioned earlier: standards for loadable modules, rationalization of device driver interfaces, the availability of virtual machines for testing, less expensive hardware, and so on. This week, we look at two general techniques that have proven more successful with device drivers than you might realize: programming

in userland and with high-level languages. Traditionally, device drivers have connected deep inside an operating system's kernel. In recent years, progressively better-defined interfaces have narrowed the ties that a device driver must have to a kernel, to the point that now it's even feasible to write device drivers "in userland," that is, without kernel rebinding, just like more normal applications. FUSD, from the Center for Embedded Networking Sensing (CENS), is the best example of this. "...CENS... is a major research enterprise focused on... applying this revolutionary technology to critical scientific and societal pursuits", according to the profile on its own Web site. "[T]his revolutionary technology" involves miniaturization and networking of sensors, to "foster ... a deeper understanding of the natural and built environments." CENS looks at all sorts of things in the world as opportunities for digital sensing; it's a group whose experience with device drivers is deep. FUSD, reminiscent in several regards of the FUSE that appeared in our September column, is the technical basis for several of these device drivers. As its manual describes, "FUSD is a Linux framework for proxying device file callbacks into user-space, allowing device files to be implemented by daemons instead of kernel code." Although this limits the performance of device drivers implemented with FUSD, its original co-author Jeremy Elson emphasizes that a user-space device driver has more capabilities than a conventional kernel-level driver, including long-running computations, branching into other device drivers, event-based processing, and retrieval of data from the filesystem. Most crucially for CENS, development of a device driver costs less. A FUSD device can't crash the host as is all-to-common with a kernel-level driver, "and is generally easier to code and debug, especially for people who are application developers," as the other FUSD co-author, Lewis Girod, wrote us. The CENS team can focus more on the science of, for example, the Acoustic ENSBox, and less on fussing with its enabling software. FUSD's advantages manifest in several other specific benefits. Girod illustrates, "An example of what FUSD is good for is a 'GPS driver.' ... [T]he 'GPSd' package ... is a user space daemon that acts as a gateway between a GPS unit connected by RS232 and a sockets protocol. Using FUSD you could implement the same RS232 GPS-reading protocol in user-space, but provide access to the features of the GPS via character devices. The advantage is that these devices are browseable and accessible from the shell, rather than using telnet or other special programs to talk to the socket. Another alternative would be to write the GPS driver in the kernel, but this is difficult because it isn't possible to access the device interface for the serial port from inside the kernel. ...Since FUSD devices are just device files, you can use the usual Unix permissions to control access. ...Another advantage of FUSD is the ability to customize the semantics of the character device, for example to allow notification of events via select() and to provide reads aligned along message boundaries without framing. This can simplify client code considerably, relative to stream socket protocols." The result is that FUSD saved CENS considerable time in connecting a variety of microcontroller boards to Linux hosts. Our explanation here neglects subtleties involved in getting FUSD's implementation right. Elson emphasized when he wrote us that, as straightforward as the idea of FUSD sounds, rendering it in

"industrial-strength code ... free of race conditions" came only after "many revisions". The continuing inspirations during this rework were "Linux's /proc filesystem" as a window to the status of the kernel, and "the QNX operation system," as a model for inter-process communication.

Unexpected Languages for Device Drivers


All this work brings still more benefits to FUSD's users. CENS open-sourced FUSD along with many of its other software assets. Among the small community of users outside CENS, at least a few develop in Java and Python. While a conventional kernel-level device driver essentially must be written in C (or perhaps assembler or C++), a FUSD device driver "can be made to work with anything that has access to the POSIX interface," as Girod observes. All the usual advantages of high-level languages immediately apply to work with device drivers. Brian Warner accomplished so much with a Python binding, in fact, that he contributed it back to CENS, where it's now maintained in the same CVS tree. FUSD isn't the only basis for "higher-level" device-driver development, though. Another documented example is Pat Thoyt's tclftd2xx. Future Technology Devices International Ltd. [FTDI] are British "specialists in converting legacy peripherals to Universal Serial Bus (USB)." Even though FTDI has done the hard work of packaging libraries for the hardware it supports, it still requires considerable expertise to make good use of those libraries. Thoyts needed to provide for "a general user [to] update the firmware on a device using the FTDI USB chipset." After a little experimentation, he settled on a graphical user interface (GUI) and API coded mostly in Tcl/Tk. As Thoyts wrote us, "In many ways this is a classic use of extensions in Tcl: I've added just enough [C code in a channel driver] to the Tcl interpreter to permit general use of the device." With that minimal extension, though, supported FTDI devices are now just as programmable at a high level in Tcl as FTDI made them in C. Is it worth the effort? Here we run into one of "Regular Expressions'" recurring themes: some of the best development stories can be publicized partially or not at all. Our own practice has yielded enough spectacular successes for scripting interfaces (one in particular was very much like tclftd2xx) to encourage us to write this column. In many specific cases, however, the details and especially measurements on the advantages of high-level programming must remain proprietary. What's certain in the public record is that Thoyts provides an open-source interface free for all of us to study and use for our own benefit.

Summary
The expressiveness and productivity of high-level languages can be available even for work in development of device drivers, generally regarded as one of the most specialized and demanding areas of programming. Kathryn and Cameron run their own consultancy, Phaseit, Inc., specializing in high-reliability and high-performance applications managed by high-level languages. They write about high-level languages and related topics in their "Regular Expressions" columns.

You might also like