Friday 31 August 2012

example of char device driver

--------------------
 char_dev.c
--------------------

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h> // required for various structures related to files liked fops.
#include <asm/uaccess.h> // required for copy_from and copy_to user functions
#include <linux/semaphore.h>
#include <linux/cdev.h>
static int Major;
dev_t dev_no,dev;
struct device
{
char array[100];
struct semaphore sem;
}char_dev;

int open(struct inode *inode, struct file *filp)
 {
        printk(KERN_INFO "Inside open \n");
        if(down_interruptible(&char_dev.sem)) {
        printk(KERN_INFO " could not hold semaphore");
        return -1;
  }
 return 0;
}

int release(struct inode *inode, struct file *filp)
 {
        printk (KERN_INFO "Inside close \n");
        printk(KERN_INFO "Releasing semaphore");
        up(&char_dev.sem);
        return 0;
}

ssize_t read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
       unsigned long ret;
       printk("Inside read \n");
       ret = copy_to_user(buff, char_dev.array, count);
       return ret;
}

ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp)
{
       unsigned long ret;
       printk(KERN_INFO "Inside write \n");
       ret = copy_from_user(char_dev.array, buff, count);
       return count;
}

struct file_operations fops = {
 read:  read,
 write:  write,
 open:   open,
 release: release
};

struct cdev *kernel_cdev;

int char_dev_init (void)
{
      int ret;
      kernel_cdev = cdev_alloc();
      kernel_cdev->ops = &fops;
      kernel_cdev->owner = THIS_MODULE;
      printk (" Inside init module\n");
      ret = alloc_chrdev_region( &dev_no , 0, 1,"chr_arr_dev");
      if (ret < 0) {
      printk("Major number allocation is failed\n");
      return ret;
 }

Major = MAJOR(dev_no);
dev = MKDEV(Major,0);
sema_init(&char_dev.sem,1);
printk (" The major number for your device is %d\n", Major);
ret = cdev_add( kernel_cdev,dev,1);
if(ret < 0 )
{
     printk(KERN_INFO "Unable to allocate cdev");
     return ret;
}
     return 0;
}

void char_dev_cleanup(void)
{
     printk(KERN_INFO " Inside cleanup_module\n");
     cdev_del(kernel_cdev);
     unregister_chrdev_region(Major, 1);
}

MODULE_LICENSE("GPL");
module_init(char_dev_init);
module_exit(char_dev_cleanup);
-------------
Makefile :
-------------

ifneq ($(KERNELRELEASE),)
   obj-m := char_dev.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

----------------
creating a node:
----------------
mknod /dev/char_dev c 250 0



User Application :-
-------------------
main.c
------------------

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

main ( )
 {
        int i,fd;
        char ch, write_buf[100], read_buf[100];
        fd = open("/dev/char_dev", O_RDWR);
        if (fd == -1)
        {
                printf("Error in opening file \n");
                exit(-1);
        }
        printf ("Press r to read from device or w to write the device ");
        scanf ("%c", &ch);

        switch (ch) {
                case 'w':
                       printf (" Enter the data to be written into device");
                        scanf (" %[^\n]", write_buf);
                        write(fd, write_buf, sizeof(write_buf));
                        break;
                case 'r':
                        read(fd, read_buf, sizeof(read_buf));
                        printf ("The data in the device is %s\n", read_buf);
                        break;
                default:
                        printf("Wrong choice \n");
                        break;
        }
        close(fd);
}
------------------------------
Compiling User Application :
------------------------------
$gcc -o main main.c

#./main
Press r to read from device or w to write the device : w
Enter the data to be written into device  : American Megatrends Inc.

#./main
Press r to read from device or w to write the device : r
The data in the device is : American Megatrends Inc.

5 comments:

  1. Thanks for providing nice program. However, it may necessary to give procedure for successfully run the example.

    1. Compile char_drv.c driver with given MakeFile.
    2. Load char_drv in to kernel using # insmod char_drv.ko.
    3. Verify the driver is loaded using # dmesg
    [root@linux ldd1]# dmesg | tail -5
    [12904.294818] Inside close
    [12873.302846] Releasing semaphore
    [12904.294823] Releasing semaphore<6>[13023.776464] Inside cleanup_module
    [13710.280654] Inside init module
    [13710.280659] The major number for your device is 246
    [root@linux ldd1]#

    4. See the Major number of device (here is 246)
    5. Now create nod in /dev using # mknod /dev/char_dev c 246 0
    6. Verify the above node created in /dev using [root@linux ldd1]# ls -l /dev/char_dev
    crw-r--r--. 1 root root 247, 0 Mar 17 09:03 /dev/char_dev
    [root@linux ldd1]#

    7. compile user application program main.c using #gcc -o main main.c
    I have modified the main.c is below
    int main ( )
    {
    int lock, i,fd;
    char ch, write_buf[100], read_buf[100];
    fd = open("/dev/char_dev", O_RDWR);
    if (fd == -1)
    {
    printf("Error in opening file \n");
    exit(-1);
    }
    lock = 1;
    while(lock != 'q')
    {
    printf ("Press (w,r,q) w: write to mem; r-read from mem; q: Exit: ");
    scanf ("%c", &ch);

    switch (ch)
    {
    case 'w':
    printf ("Enter the data to be written into device: ");
    scanf (" %[^\n]", write_buf);
    write(fd, write_buf, sizeof(write_buf));
    break;

    case 'r':
    read(fd, read_buf, sizeof(read_buf));
    printf ("The data in the device is: %s\n", read_buf);
    break;
    case 'q':
    lock = 'q';
    printf ("\n Good bye...\n");
    break;

    default:
    printf("Wrong choice \n");
    break;
    } // switch
    } // while
    close(fd);
    return 0;
    } // main

    Finally run # ./main
    Enjoy....


    ReplyDelete
  2. Compiler warning during char_dev.c compilation:
    In function ‘copy_from_user.constprop.0’,
    inlined from ‘write’ at /home/linux/Project/ldd1/char_dev.c:53:7:
    /usr/src/kernels/3.7.9-104.fc17.i686.PAE/arch/x86/include/asm/uaccess_32.h:211:26: warning: call to ‘copy_from_user_overflow’ declared with attribute warning: copy_from_user() buffer size is not provably correct [enabled by default]

    To overcome the above warning I have modified the driver source code as follows.


    #include
    #include
    #include // required for various structures related to files liked fops.
    #include // required for copy_from and copy_to user functions
    #include
    #include

    // array size is fixed modified on 16.03.13
    #define MAX 100
    static int Major;
    dev_t dev_no,dev;

    struct device
    {
    char array[MAX];
    struct semaphore sem;
    }char_dev;

    int open(struct inode *inode, struct file *filp)
    {
    printk(KERN_INFO "Inside open \n");
    if(down_interruptible(&char_dev.sem))
    {
    printk(KERN_INFO " could not hold semaphore");
    return -1;
    }
    return 0;
    }

    int release(struct inode *inode, struct file *filp)
    {
    printk (KERN_INFO "Inside close \n");
    printk(KERN_INFO "Releasing semaphore");
    up(&char_dev.sem);
    return 0;
    }

    ssize_t read(struct file *filp, char *buff, size_t count, loff_t *offp)
    {
    unsigned long ret;
    printk("Inside read \n");
    ret = copy_to_user(buff, char_dev.array, count);
    return ret;
    }

    ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp)
    {
    unsigned long ret;
    printk(KERN_INFO "Inside write \n");
    if(count > MAX) // added this statement to avoid overflow warning (16.03.13)
    count = MAX;

    ret = copy_from_user(char_dev.array, buff, count);
    return count;
    }

    struct file_operations fops =
    {
    read: read,
    write: write,
    open: open,
    release: release
    };

    struct cdev *kernel_cdev;

    int char_dev_init (void)
    {
    int ret;
    kernel_cdev = cdev_alloc();
    kernel_cdev->ops = &fops;
    kernel_cdev->owner = THIS_MODULE;
    printk (" Inside init module\n");
    ret = alloc_chrdev_region( &dev_no , 0, 1,"chr_arr_dev");
    if (ret < 0)
    {
    printk("Major number allocation is failed\n");
    return ret;
    }

    Major = MAJOR(dev_no);
    dev = MKDEV(Major,0);
    sema_init(&char_dev.sem,1);
    printk (" The major number for your device is %d\n", Major);
    ret = cdev_add( kernel_cdev,dev,1);
    if(ret < 0 )
    {
    printk(KERN_INFO "Unable to allocate cdev");
    return ret;
    }
    return 0;
    }

    void char_dev_cleanup(void)
    {
    printk(KERN_INFO " Inside cleanup_module\n");
    cdev_del(kernel_cdev);
    unregister_chrdev_region(Major, 1);
    }

    MODULE_LICENSE("GPL");
    module_init(char_dev_init);
    module_exit(char_dev_cleanup);

    This source code gives clean compilation:

    ReplyDelete
  3. Hi.,

    I have tested the code which is given/last updated.,But I could find the some issue ie., continious printing, whicle reading the written text. And the captures a sgiven below, can u pls check this why this continious printing during the read operation.,,

    The logs are.,

    root@lcl:$> ls -l /dev/chr_dev
    ls: cannot access /dev/chr_dev: No such file or directory
    root@lcl:$> mknod /dev/chr_dev c 246 0
    root@lcl:$> ls -l /dev/chr_dev
    crw-r--r-- 1 root root 246, 0 Jan 29 04:22 /dev/chr_dev
    root@lcl:$> echo "HELLO.............UUUUUUUUUUUUUUU" > /dev/chr_dev; dmesg | tail -10
    Inside open
    Inside write
    Inside close
    Releasing semaphore
    root@lcl:$> cat > /dev/chr_dev; dmesg | tail -10
    root@lcl:$> cat /dev/chr_dev; dmesg | tail -10
    HELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopenHELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_init init_module write releasereadchar_dev_cleanupopen HELLO.............UUUUUUUUUUUUUUU
    O÷O÷``AEEUOdDtOdDtOdDt*OnXEt0OoaHt8Oo At=OdDtNOo(Xtcleanup_modulechar_dev_initinit_modulewritereleasereadchar_dev_cleanupopen^C
    ....................this is continues printing which is non stop till then we use ctrl+C znd ^z............
    Inside close
    Releasing semaphore
    root@lcl:$>

    ReplyDelete
  4. Hi, i have followed the complete steps but am getting error prints that is unable to open the file... Please advise

    ReplyDelete