Lab5 - Linux Device Driver (ver. 3) Interface Device Driver with Application Program

Implement Version 3 of hello1.c: Interface with Application Program

  • Try the sample device driver, you should build and install the driver, and then compile "hello" program to verify it works.
  • To create a sample device driver (version 3) follow the steps as below.
  • I wrote following documentations based on documentation of driver version 2 (Lab 4). So, first you have to understand driver version 2 notes to follow instructions on this page.

Step 1

  • First you need administrative privilege using following command.
  $ su -
  • First go to the directory examples where you create hello1.c.
  $ cd /usr/src/linux-2.6.20.1/drivers/char/examples
  • Then back up the hello1.c to the file hello1.v2.c.
   $ cp hello1.c hello1.v2.c
  • To see the content of examples folder use the following command.
$ ls –l examples

Snapshot 1

Step 2

  • Then edit the file hello1.c file to make it for driver version 3.
$ vi hello1.c

#include <linux/module.h>

#include <linux/fs.h>

#define HELLO_MAJOR 234

static int debug_enable = 0;

module_param(debug_enable, int, 0);

MODULE_PARM_DESC(debug_enable, "Enable module debug mode.");

struct file_operations hello_fops;

static int hello_open(struct inode *inode, struct file *file)

{

printk("hello_open: successful\n");

return 0;

}

static int hello_release(struct inode *inode, struct file *file)

{

printk("hello_release: successful\n");

return 0;

}

static ssize_t hello_read(struct file *file, char *buf, size_t count,loff_t *ptr)

{

printk("hello_read: returning zero bytes\n");

return 0;

}

static ssize_t hello_write(struct file *file, const char *buf,

size_t count, loff_t * ppos)

{

printk("hello_read: accepting zero bytes\n");

return 0;

}

static int hello_ioctl(struct inode *inode, struct file *file,

unsigned int cmd, unsigned long arg)

{

printk("hello_ioctl: cmd=%d, arg=%ld\n", cmd, arg);

return 0;

}

static int __init hello_init(void)

{

int ret;

printk("Hello Example Init - debug mode is %s\n",

debug_enable ? "enabled" : "disabled");

ret = register_chrdev(HELLO_MAJOR, "hello1", &hello_fops);

if (ret < 0) {

printk("Error registering hello device\n");

goto hello_fail1;

}

printk("Hello: registered module successfully!\n");

/* Init processing here... */

return 0;

hello_fail1:

return ret;

}

static void __exit hello_exit(void)

{

printk("Hello Example Exit\n");

}

struct file_operations hello_fops = {

owner: THIS_MODULE,

read: hello_read,

write: hello_write,

ioctl: hello_ioctl,

open: hello_open,

release: hello_release,

};

module_init(hello_init);

module_exit(hello_exit);

MODULE_AUTHOR("Henry Chang (v3)");

MODULE_DESCRIPTION("Hello World Example");

MODULE_LICENSE("GPL");

Note:

  1. From the top, we've had to add a new kernel header file to get the definitions for the file system operations.
  2. We've also defined a major number for our device driver. (Note to device driver authors: This is not the proper way to allocate a device driver major number. Refer to the Linux kernel documentation(.../Documentation/devices.txt) or one of the excellent texts on device drivers for guidance on the allocation of major device numbers. For this simple example, we simply choose one that we know isn't in use on our system.)
  3. Next we see definitions for four new functions, our open, close, read, and write methods. In keeping with good coding practices, we've adopted a consistent naming scheme that will not collide with any other subsystems in the kernel. Our new methods are called hello_open(), hello_release(), hello_read(), and hello_write(), respectively. For purposes of this simple exercise, they are do-nothing functions that simply print a message to the kernel log subsystem.
  4. Notice that we've also added a new function call to our hello_init() routine. This line registers our device driver with the kernel. With that registration call, we pass a structure containing pointers to the required methods. The kernel uses this structure, of type struct file_operations, to bind our specific device functions with the appropriate requests from the file system. When an application opens a device represented by our device driver and requests a read() operation, the file system associates that generic read() request with our module's hello_read() function.

Step 3

  • Then go to the directory
  $ cd /usr/src/linux-2.6.20.1
  • Then use following command to compile the module.
  $ make modules
CHK     include/linux/version.h
CHK     include/linux/utsrelease.h
CC [M]  drivers/char/examples/hello1.o
Building modules, stage 2.
MODPOST 1211 modules
CC      drivers/char/examples/hello1.mod.o
LD [M]  drivers/char/examples/hello1.ko

  • Then use the following command to install module in a kernel.
  $ make modules_install
  • The result is shown in following Snapshot 2.

Snapshot 2

Step 4

  • A device node is a special file type that represents a device by binding an installed device driver. It is Located in the /dev
  • It is created by the command mknod as below. For example, create a new file called /dev/hello1 that represents our device driver module.
$ mknod /dev/hello1 c 234 0
$ ls –l /dev/hello1

Snapshot 3

Step 5

  • Then go to the directory examples and create use-hello.c file.
$ cd /usr/src/linux-2.6.20.1/drivers/char/examples
$ vi use-hello.c

Snapshot 4

  • And edit the file use-hello.c with following program.
  • Error in this part of program in website link (Modified it as shown)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
int main(int argc, char **argv)
{
    /* Our file descriptor */
    int fd;
    int rc = 0;
    char *rd_buf[16];
 
    printf("%s: entered\n", argv[0]);
 
    /* Open the device */
    fd = open("/dev/hello1", O_RDWR);
    if ( fd == -1 ) {
        perror("open failed");
        rc = fd;
        exit(-1);
    }
    printf("%s: open: successful\n", argv[0]);
    /* Issue a read */
    rc = read(fd, rd_buf, 0);
    if ( rc == -1 ) {
        perror("read failed");
        close(fd);
        exit(-1);
    }
    printf("%s: read: returning %d bytes!\n", argv[0], rc);
 
    close(fd);
    return 0;
}

Step 6

  • Compile the program use-hello.c as below.
  $ gcc -o use-hello use-hello.c
  • Use modeprobe to add module hello1 using following command.
  $ modprobe hello1
  • Then execute our simple application program which use added module and show the result as shown below.
   $ ./use-hello

Snapshot 5

  • At the end remove module hello1 and then then again execute use-hello and see the difference in result.

Snapshot 6