LinuxDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


/dev/hello_world: A Simple Introduction to Device Drivers under Linux
Pages: 1, 2, 3

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 <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h><
#include <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;
}

Pages: 1, 2, 3

Next Pagearrow




Linux Online Certification

Linux/Unix System Administration Certificate Series
Linux/Unix System Administration Certificate Series — This course series targets both beginning and intermediate Linux/Unix users who want to acquire advanced system administration skills, and to back those skills up with a Certificate from the University of Illinois Office of Continuing Education.

Enroll today!


Linux Resources
  • Linux Online
  • The Linux FAQ
  • linux.java.net
  • Linux Kernel Archives
  • Kernel Traffic
  • DistroWatch.com


  • Sponsored by: