implementing a simple char device in linux

Upload: hai-hung

Post on 09-Apr-2018

222 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/7/2019 Implementing a Simple Char Device in Linux

    1/5

    Implementing a Simple Char Device in Linux

    Written by Thong D. NguyenBy Ranjeet Mishra

    Device

    For the purpose of this article, let's consider a device to be a virtual represention, withinLinux, of hardware that one would like to drive by using a piece of software. In the Linuxworld, devices are implemented in the form of modules. By using modules, we canprovide device functionality that can be accessed from userspace.

    A userspace entry point to a device is provided by a file node in the /dev directory. Aswe know, most of the things in Linux world are represented in the form of files. We cando [ ls -l ] on any device file, which will report the device type - character or block device,as well as its major number and minor number.

    The type of device indicates the way data is written to a device. For a character device,it's done serially, byte by byte, and for a block device (e.g., hard disk) in the form ofchunks of bytes - just as the name suggests.

    The major number is assigned at the time of registering the device (using some module)and the kernel uses it to differentiate between various devices. The minor number is

    used by the device driver programmer to access different functions in the same device.Looking at the number of files in the /dev directory, one might think that a very largenumber of devices are up and running in the system, but only few might be actuallypresent and running. This can be seen by executing [ cat /proc/devices ]. (One can thensee the major numbers and names of devices that are passed at the time ofregistering.)

    Modules

    Every device requires a module. Information about the currently loaded modules can be

    extracted from the kernel through [ cat /proc/modules ]. A module is nothing more thanan object file that can be linked into a running kernel; to accomplish this, Linux providesthe [ insmod ] utility. As an example, let's say that my module's object file iscalled my_dev.o ; we can link it to the kernel using [ insmod my_dev.o ]. If insmod issuccessful we can see our module's entry using [ cat /proc/modules ], or [ lsmod ]. Wecan remove the module using the rmmod utility, which takes the object file name as anargument.

  • 8/7/2019 Implementing a Simple Char Device in Linux

    2/5

    Writing a Module to register a Char device

    First of all, we should know the basics of generating a module object file. The moduleuses kernel space functions and since the whole kernel code is written insidethe __KERNEL__ directive we need to define it at time of compiling, or in our sourcecode. We need to define the MODULE directive before anything else because Modulefunctions are defined inside it. In order to link our module with the kernel, the version ofthe running kernel should match the version which the module is compiled with, or[insmod ] will reject the request. This means that we must include the [include] directorypresent in the Linux source code of the appropriate version. Again, if my module file iscalled my_dev.c, a sample compiler instruction could be [ gcc -D__KERNEL__ -I/usr/src/linux.2.6.7/linux/include -c my_dev.c ]. A -D is used to define any directivesymbol. Here we need to define __KERNEL__, since without this kernel-specific contentwon't be available to us.

    The two basic functions for module operations are module_init and module_exit . Theinsmod utility loads the module and calls the function passed to module_init, and rmmodremoves the module and calls function passed to module_exit. So inside module_init,we can do whatever we wish using our kernel API. For registering the char device, thekernel provides register_chrdev which takes three arguments, namely: the majornumber, the char string (which gives a tag name to the device), and the file operationsstruct address which defines all the stuff we would like to do with our char device. structfile_operations is defined in $(KERNELDIR)/linux/include/fs.h which declares thefunction pointers for basic operations like open, read, write, release, etc. One needs toimplement whatever functions are necessary for the device. Finally, inside the function

    passed to module_exit, we should free the resources using unregister_chrdev whichwill be called when we do rmmod .

    Below is the code listing where the device is nothing but an 80 byte chunk of memory.

    Playing with the char device

    Load the device using [ insmod my_dev.o ]. Look for the entry through /proc/modulesand /proc/devices. Create a file node in /dev directory using [ mknod /dev/my_device c222 0 ]. Look inside the code, we have given the major number as 222. You might thinkthat this number may clash with some other device - well, that's correct, but I have

    checked whether this number is already occupied by some other device. One could usedynamic allocation of the major number; for that we have to pass 0 as the argument.

    Now we can read the data in the device using [ cat /dev/my_device ] and can write toour device using [ echo "something" > /dev/my_device ]. We can also write full-fledgeduserspace code to access our device using standard system calls of open, read, write,close, etc. Sample code is presented below.

    ------------------------------------------- /* Sample code to access our char device */

    #include

  • 8/7/2019 Implementing a Simple Char Device in Linux

    3/5

    #include#include#include#include

    int main(){

    int fd=0,ret=0;char buff[80]="";

    fd=open("/dev/my_device",O_RDONLY);

    printf("fd :%d\n",fd);

    ret=read(fd,buff,10);buff[ret]='\0';

    printf("buff: %s ;length: %d bytes\n",buff,ret);close(fd);

    }

    -------------------------------------------Output fd: 3buff: hi from kernel ;length: 14 bytes-------------------------------------------

    Conclusion

    [ Note: a tarball containing all the code in this article can be downloaded:http://linuxgazette.net/125/misc/mishra/my_dev.tgz]

    In this article I have tried to show how to use the kernel functions to register a characterdevice, and how to invoke it from userspace. There are many issues that have not beentouched upon here, such as the concurrency problem where we need to provide asemaphore for the device to do mutual exclusion as more than one process may try toaccess it. I will try to cover these issues in my future articles.

    my_dev.c: #include#include#include"my_dev.h"

    MODULE_AUTHOR("ranjmis");MODULE_DESCRIPTION("A simple char device");

  • 8/7/2019 Implementing a Simple Char Device in Linux

    4/5

    static int r_init(void);static void r_cleanup(void);

    module_init(r_init);module_exit(r_cleanup);

    static int r_init(void){printk("hi\n");if(register_chrdev(222,"my_device",&my_fops)){

    printk("failed to register");}

    return 0;}static void r_cleanup(void){printk("bye\n");unregister_chrdev(222,"my_device");return ;}

    my_dev.h: /*

    * my device header file*/

    #ifndef _MY_DEVICE_H#define _MY_DEVICE_H

    #include #include #include #include #include #include

    char my_data[80]="hi from kernel"; /* our device */

    int my_open(struct inode *inode,struct file *filep);int my_release(struct inode *inode,struct file *filep);ssize_t my_read(struct file *filep,char *buff,size_tcount,loff_t *offp );

  • 8/7/2019 Implementing a Simple Char Device in Linux

    5/5

    ssize_t my_write(struct file *filep,const char *buff,size_tcount,loff_t *offp );

    struct file_operations my_fops={open: my_open,read: my_read,write: my_write,release:my_release,

    };

    int my_open(struct inode *inode,struct file *filep){

    /*MOD_INC_USE_COUNT;*/ /* increments usage count of module*/

    return 0;}

    int my_release(struct inode *inode,struct file *filep){

    /*MOD_DEC_USE_COUNT;*/ /* decrements usage count of module*/

    return 0;}ssize_t my_read(struct file *filep,char *buff,size_tcount,loff_t *offp )

    {/* function to copy kernel space buffer to user space*/if ( copy_to_user(buff,my_data,strlen(my_data)) != 0 )

    printk( "Kernel -> userspace copy failed!\n" );return strlen(my_data);

    }ssize_t my_write(struct file *filep,const char *buff,size_tcount,loff_t *offp ){

    /* function to copy user space buffer to kernel space*/

    if ( copy_from_user(my_data,buff,count) != 0 )printk( "Userspace -> kernel copy failed!\n" );return 0;

    }#endif