Device Drivers Basics Char : Putting it all together

Now let’s put whatever we have learnt so far into writing a basic char device driver. Here is the code of a basic char driver for you, Finally.

unsigned long copy_to_user (void* destination_address, const void* source_address, unsigned long num_of_bytes_to_copy);

/* Your First Driver – Basic Char Driver

** Author: LLK

** Kernel version: 2.6

*/

#include <linux/module.h>

#include <linux/fs.h>

#include <asm/uaccess.h>  // for copy_to_user & copy_from_user.

#include <linux/init.h>

#include <linux/sched.h>

#include <linux/types.h>

#include <linux/cdev.h>

/* Initial Declarations */

static int                 char_device_id;

#define CHAR_DEVICE_NAME   ”llk_chrdev”

#define MAX_LENGTH         4000

#define MY_MAJOR                         254

static char                char_device_buf[MAX_LENGTH];

/**

 * Just declare your module’s initialize and cleanup (exit) functions

 * And register them with module_init and module_exit.

 * Remember again , these function names can be anything but their signature

 * should be exactly same as specified below.

 * */

static int init_char_device(void);

static void exit_char_device(void);

module_init(init_char_device);

module_exit(exit_char_device);

/**

 * Next, identify all those functions that are to be operated upon device.

 * Just declare those functions.

* */

static int char_device_open(struct inode *inode, struct file  *file);

static int char_device_release(struct inode *inode, struct file *file);

static int char_device_read(struct file *file, char *buf, size_t lbuf,

                                                          loff_t *ppos);

static int char_device_write(struct file *file, const char *buf, size_t lbuf,

                                                            loff_t *ppos);

static loff_t char_device_lseek(struct file *file, loff_t offset, int orig);

/* Next define struct file_operations structure, You will populate the

 * members of this structure by assigning the address of the above functions */

static struct file_operations char_device_file_ops =

{

.owner = THIS_MODULE,

.read = char_device_read,

.write = char_device_write,

.open = char_device_open,

.release = char_device_release,

.llseek = char_device_lseek

};

/**

 * The initialization routine.

 *

 * After deciding all the driver functions, we need to register all those.

 * To do that it first fills the file operations fops structure with

 * addresses of all those functions.

 * Once we populate the file operations structure, we need to

 * register with Linux kernel. Once we register with the Linux Kernel the

 * kernel gives back a driver id which is stored in char_device_id.

 * Registration of char device is done in module’s initialization routine.

 *

 * We have already defined the name of our device as “llk_chrdev”.

 *

 * */

static int init_char_device(void)

{

                int i,retVal;

retVal = register_chrdev(MY_MAJOR, CHAR_DEVICE_NAME, fops);

                /*Register our character Device*/

                if(retVal < 0 ) {

                                printk(“Error registering device driver\n”);

                                return ret;

                }

                char_device_buf = { 0 };

                return 0;

}

/**

 * This module’s unregistration routine.

 * */

static void exit_char_device(void)

{

                printk(“\n Module removed”);

                unregister_chrdev(MY_MAJOR, CHAR_DEVICE_NAME);

}

/** Provide Definitions to all the functions that will be operated on the

  * device by the device driver.

*/

/**

* This should be the first function in the sequence. It is called when a      * user wants to use this device and has called the open function.

 *

 * The function will keep a count of how many people

 * tried to open it and increments it each time

 * this function is called

 *

 * The function prints out two pieces of information

 * 1. Number of times open() was called on this device

 * 2. Number of processes accessing this device right now

 *

 * Return value

 *            Always returns SUCCESS

 * */

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

{

                static int counter = 0;

                counter++;

                printk(“Number of times open() was called: %d\n”, counter);

                printk(“Process id of the current process: %d\n”, current->pid );

                return 0;

}

/**

 * This function is called when the user program uses close() function

 *

 * The function decrements the number of processes currently

 * using this device. This should be done because if there are no

 * users of a driver for a long time, the kernel will unload

 * the driver from the memory.

 *

 * */

static int char_device_release(struct inode *inode,

                                            struct file *file)

{

                return 0;

}

/**

 * This function is called when the user calls read on this device.

 * User reads from the device ‘file’ to a user space buffer ‘buf’.

 * Parameters,

 *            buf  = Address of buffer that we are getting from user space.

 *            ppos = present position

 *            bufsize = size of the user space buffer

 *            file = device file that driver will read

 * This Function will read a block of data from device to the user space

 * buffer and returns the number of bytes(characters) read.

 * */

static int char_device_read(struct file* file, char* buf, size_t bufsize,

                                                    loff_t* ppos)

{

                int maxbytes;     /* number of bytes from ppos to MAX_LENGTH */

                int bytes_to_read;  /* number of bytes to read */

                int nbytes;       /* number of bytes actually read */

                /* Maximum number of bytes that can be read from the device */

                maxbytes = MAX_LENGTH – *ppos;

                if( maxbytes > bufsize )

bytes_to_read = bufsize;

                else

bytes_to_read = maxbytes;

                if( bytes_to_read == 0 )

{

                                printk(“Reached end of device\n”);

                                return -ENOSPC; /* Causes read() to return EOF */

                }

                /* copy_to_user returns bytes that couldn’t be read  */

       * subtracting this with bytes_to_read gives exactly */

       * how many bytes are read */

                nbytes = bytes_to_read –

                                 copy_to_user( buf, /* to */

                                                       char_device_buf + *ppos, /* from */

                                                       bytes_to_do ); /* how many bytes */

                /* increase the ppos with the number of bytes that are read */

       * in order to update the current reading positiong */

                *ppos += nbytes;

                return nbytes;

}

/**

 * This function is called when the user calls write on this device.

 * It writes into the device ‘file’ from user space buffer named ‘buf’

 * starting from ‘ppos’ and upto ‘bufsize’ bytes.

 * parameters,

 *            buf  = buffer

 *            file = file to write into

 *            bufsize = size of the user space buffer

 *            ppos = present position

 * The function writes a block of data into the device from the user space

 * buffer and returs the number of characters(bytes) written.

 * */

static int char_device_write(struct file* file, const char* buf,

size_t bufsize, loff_t* ppos)

{

                int nbytes;        /*Number of bytes actually written to the device */

                int bytes_to_write; /* Number of bytes to write */

                int maxbytes;    /* Maximum number of bytes that can be written */

                maxbytes = MAX_LENGTH – *ppos;

                if( maxbytes > lbuf )

bytes_to_write = bufsize;

                else

bytes_to_write = maxbytes;

                if( bytes_to_write == 0 )

{

                                printk(“Reached end of device\n”);

                                return -ENOSPC; /* Returns EOF at write() */

                }

nbytes = bytes_to_write –

                         copy_from_user( char_device_buf + *ppos, /* to */

                                                                       buf, /* from */

                                                                       bytes_to_do ); /* how many bytes */

*ppos += nbytes;

return nbytes;

}

/**

 * This function is called when lseek() is called on the device.

 * It sets the current read / write position or the offset of the device.

 * The ‘ppos’ is the current read/write position of the device.

 * This function sets the ‘ppos’ depending on the value in the third

 * argument, ‘whence’.

 *

 * if whence  = SEEK_SET

 *            ppos = offset

 * if whence  = SEEK_CUR

 *            ppos = ppos + offset

 * if whence  = SEEK_END

 *            ppos = MAX_LENGTH + offset

 *

 * returns the new position

 * */

static loff_t char_device_lseek(struct file *file, loff_t offset, int whence)

{

                loff_t new_pos=0;

                switch( whence )

                {

                case    SEEK_SET:

                                new_pos = offset;

                                break;

                case    SEEK_CUR:

                                new_pos = file->f_pos + offset;

                                break;

                case   SEEK_END:

                                new_pos = MAX_LENGTH + offset;

                                break;

                }

                if( new_pos > MAX_LENGTH )

new_pos = MAX_LENGTH;

                if( new_pos < 0 )

new_pos = 0;

                file->f_pos = new_pos;

                return new_pos;

}

MODULE_AUTHOR(“LLK”);

MODULE_DESCRIPTION(” Basic Character Device Driver”);

/* End of code */

Fig. 5.10 Your basic char driver.

In this driver you can see that we have passed a number 254 in the register_chrdev() routine. You will build this driver and give it to the client to run. If at the client site, the number 254 is occupied by some other driver , this call will fail. To handle this situation, pass 0 (zero) in the register_chrdev() as a major number. If you pass 0 as a major number to register_chrdev(), it will allocate a free , available major number and return it to you. Now once you know the major number of your driver, then you can create a device file that interacts with this driver.

So, in the above program you change the major number,

#define MY_MAJOR  0

static int init_char_device(void)

{

                int i,retVal;

 

retVal = register_chrdev(MY_MAJOR, CHAR_DEVICE_NAME, fops);       

                /*Register our character Device*/

               

                if(retVal < 0 )

   {

                        printk(“Error registering device driver\n”);

                                    return ret;

                }

              

               printk(“The major number allocated is %d\n”,retVal);                               

               char_device_buf = { 0 };

               return 0;

}

Use dmesg to get the major number allocated.

 #dmsg

 Once you know which major number has been allocated to your driver, you can use ‘mknod’ command to create the device file that interacts with this driver to operate the actual device.

Assume, the major number allocated to your driver is 250.

# mknod /dev/my_device c 250 0

Read More Post