Saturday, November 10, 2012

Simple Linux "char" Device Driver

Here is the code snippet of a tiny "character" device driver - which reverses the character string specified by the user

File : my_driver.c

#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <asm/uaccess.h>


MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Custom Tiny Device Driver");
MODULE_AUTHOR("Giridhar Bhujanga");


static char  msg[100]     = {0};
static short readPos      =  0;
static int   access_count =  0;
static int   MAJOR_NUMBER = 91;

static int device_open    ( struct inode * , struct file *);
static int device_release ( struct inode * , struct file *);

static ssize_t device_read  ( struct file * , char *,       size_t , loff_t *);
static ssize_t device_write ( struct file * , const char *, size_t , loff_t *);

/**********************/
/* callback functions */
/**********************/

static struct file_operations file_opts = 
{
      .read = device_read,
      .open = device_open,
     .write = device_write,
   .release = device_release
};


/************************************/
/* called when the module is loaded */
/************************************/

int init_module(void)
{
   int t = register_chrdev(MAJOR_NUMBER, "myDevice", &file_opts);
   if(t<0) 
   {
      printk(KERN_ALERT "Device registration Failed !\n");
   }
   else
   {
      printk(KERN_ALERT "Device successfully registered !\n");
   }

   return t;
}



/**************************************/
/* called when the module is unloaded */
/**************************************/

void cleanup_module(void)
{
   unregister_chrdev(91, "myDevice");
}



/*************************************************************/
/* called when 'open' system call is done on the device file */
/*************************************************************/

static int device_open(struct inode *inodep, struct file *filep)
{
   access_count++;
   printk(KERN_ALERT "Device opened %d time(s)\n", access_count);
   return 0;
}


/*************************************************************/
/* called when 'read' system call is done on the device file */
/*************************************************************/

static ssize_t device_read(struct file *filep, char *buff, size_t len, loff_t *off)
{
   short count = 0;
   while(len && (msg[readPos]!=0))
   {
      put_user(msg[readPos],buff++); /* copy byte from kernel space to user space */
      count++;
      len--;
      readPos++;
   }
   return count;
}


/**************************************************************/
/* called when 'write' system call is done on the device file */
/**************************************************************/

static ssize_t device_write(struct file *filep, const char *buff, size_t len, loff_t *off)
{
   short ind = len -1;
   short count = 0;
   memset(msg,0,100);
   readPos = 0;
   while(len >0) 
   {
      msg[count++] = buff[ind--];
      len--;
   }

   return count;
}


/**************************************************************/
/* called when 'close' system call is done on the device file */
/**************************************************************/

static int device_release(struct inode *inodep, struct file *filep)
{
   printk(KERN_ALERT "Device closed !\n");
   return 0;
}


File : Makefile

obj-m := my_driver.o
KVERSION := $(shell uname -r)
KDIR := /lib/modules/$(shell uname -r)/build/
PWD := $(shell pwd)
INC := -I/usr/src/kernels/2.6.18-194.el5-x86_64/include/ -I/usr/include -I/usr/lib/x86_64-redhat-linux4E/include/

all:
 $(MAKE) $(INC) -C $(KDIR) M=$(PWD) modules

clean:
 $(MAKE) $(INC) -C $(KDIR) M=$(PWD) clean


Output of "make" command

[root@RHEL-55 tutorial-4]# make
make -I/usr/src/kernels/2.6.18-194.el5-x86_64/include/ -I/usr/include -I/usr/lib/x86_64-redhat-linux4E/include/ -C /lib/modules/2.6.18-194.el5/build/ M=/root/kernel-programs/tutorial-4 modules
make[1]: Entering directory `/usr/src/kernels/2.6.18-194.el5-x86_64'
  CC [M]  /root/kernel-programs/tutorial-4/my_driver.o
  Building modules, stage 2.
  MODPOST
  CC      /root/kernel-programs/tutorial-4/my_driver.mod.o
  LD [M]  /root/kernel-programs/tutorial-4/my_driver.ko
make[1]: Leaving directory `/usr/src/kernels/2.6.18-194.el5-x86_64'
[root@RHEL-55 tutorial-4]# 

Create the device file through the command "mknod"


Create:

# mknod /dev/myDevice c 91 1

Set permissions:

# chmod a+r+w /dev/myDevice


Current list of files after the command : make

[root@RHEL-55 tutorial-4]# ls -l
total 364
-rw-r--r-- 1 root root    331 Nov 10 15:27 Makefile
-rw-r--r-- 1 root root      0 Nov 10 15:34 Module.markers
-rw-r--r-- 1 root root      0 Nov 10 15:34 Module.symvers
-rw-r--r-- 1 root root   3095 Nov 10 16:05 my_driver.c
-rw-r--r-- 1 root root 164963 Nov 10 16:38 my_driver.ko
-rw-r--r-- 1 root root    794 Nov 10 16:38 my_driver.mod.c
-rw-r--r-- 1 root root  80936 Nov 10 16:38 my_driver.mod.o
-rw-r--r-- 1 root root  85776 Nov 10 16:38 my_driver.o
-rwxr-xr-x 1 root root   7697 Nov 10 16:29 verify
-rw-r--r-- 1 root root    433 Nov 10 16:29 verify.c
[root@RHEL-55 tutorial-4]# 

Install the kernel module

# insmod my_driver.ko 

Verify : " tail -f /var/log/messages" "

Nov 10 16:40:23 RHEL-55 kernel: Device successfully registered !

Verify if the driver is working correctly


Feed a string to the driver:

# echo "hello world" > /dev/myDevice

"tail -f /var/log/messages":

Nov 10 16:43:13 RHEL-55 kernel: Device opened 1 time(s)
Nov 10 16:43:13 RHEL-55 kernel: Device closed !

It should reverse the string which it gets:

# cat /dev/myDevice

dlrow olleh

"tail -f /var/log/messages":

Nov 10 16:45:27 RHEL-55 kernel: Device opened 2 time(s)
Nov 10 16:45:27 RHEL-55 kernel: Device closed !


C program to verify if our driver is working correctly verify.c

#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>

int main(int argc, char *argv[])
{
   assert(argc > 1);
   char buff[100];
   char i = 0;
   
   memset(buff,0,100);

   printf("\nInput: %s\n", argv[1]);

   int fp = open("/dev/myDevice", O_RDWR);

   write(fp, argv[1], strlen(argv[1]));

   while(read(fp, &buff[i++], 1));

   printf("\nReversed by the driver: \n\n\t %s \n\n", buff);

   return 0;
}


Run the verify program


[root@RHEL-55 tutorial-4]# gcc verify.c -o verify
[root@RHEL-55 tutorial-4]# 
[root@RHEL-55 tutorial-4]# ./verify "holy cow"

Input: holy cow

Reversed by the driver: 

  woc yloh 

[root@RHEL-55 tutorial-4]# 


No comments :

Post a Comment