Linux Kernel Driver Program: 9_Procfs_Device_driver

Linux Kernel Driver Program: 9_Procfs_Device_driver

neelkanth_surekha#cat Read\ me 

This directory contains three files.
1.Driver
2.Make File
3.User Application

Driver is running on kernel space
Makefile is used to build the kernel linux device driver
User application will running on the user space and communicate to driver on kernel space.

neelkanth_surekha#cat driver.c 

/* 
 * Operating system segregates virtual memory into kernel space and user space.  Kernel space 
 * is strictly reserved for running the kernel, kernel extensions, and most device drivers. 
 * In contrast, user space is the memory area where all user mode applications work and this 
 * memory can be swapped out when necessary. 
 */

#if 0
############################################################################################
There are many ways to Communicate between the User space and Kernel Space, they are:
############################################################################################
1. IOCTL
2. Procfs <---------------------
3. Sysfs
4. Configfs
5. Debugfs
6. Sysctl
7. UDP Sockets
8. Netlink Sockets

##################################################################################
##################################################################################
The proc entry can also be used to pass data to the kernel by writing into the 
kernel, so there can be two kinds of proc entries.

An entry that only reads data from the kernel space.
An entry that reads as well as writes data into and from kernel space.
##################################################################################
##################################################################################
#endif

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h>                 //kmalloc()
#include<linux/uaccess.h>              //copy_to/from_user()
#include <linux/ioctl.h>
#include<linux/proc_fs.h>

#define WR_VALUE _IOW('a','a',int32_t*)
#define RD_VALUE _IOR('a','b',int32_t*)

int32_t value = 0;
char dummy_array[20] = "dummy proc file\n";
static int len = 1;


dev_t dev = 0;
static struct class *dev_class;
static struct cdev dummy_cdev;

static int __init dummy_driver_init(void);
static void __exit dummy_driver_exit(void);
/*************** Driver Fuctions **********************/
static int dummy_open(struct inode *inode, struct file *file);
static int dummy_release(struct inode *inode, struct file *file);
static ssize_t dummy_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
static ssize_t dummy_write(struct file *filp, const char *buf, size_t len, loff_t * off);
static long dummy_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

/***************** Procfs Functions *******************/
static int open_proc(struct inode *inode, struct file *file);
static int release_proc(struct inode *inode, struct file *file);
static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length,loff_t * offset);
static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t * off);

static struct file_operations fops =
{
    .owner          = THIS_MODULE,
    .read           = dummy_read,
    .write          = dummy_write,
    .open           = dummy_open,
    .unlocked_ioctl = dummy_ioctl,
    .release        = dummy_release,
};

static struct file_operations proc_fops = {
    .open = open_proc,
    .read = read_proc,
    .write = write_proc,
    .release = release_proc
};

static int open_proc(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "proc file opend.....\t");
    return 0;
}

static int release_proc(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "proc file released.....\n");
    return 0;
}

static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length,loff_t * offset)
{
    printk(KERN_INFO "proc file read.....\n");
    if(len)
        len=0;
    else{
        len=1;
        return 0;
    }
    copy_to_user(buffer,dummy_array,20);

    return length;;
}

static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t * off)
{
    printk(KERN_INFO "proc file wrote.....\n");
    copy_from_user(dummy_array,buff,len);
    return len;
}

static int dummy_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "Device File Opened...!!!\n");
    return 0;
}

static int dummy_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "Device File Closed...!!!\n");
    return 0;
}

static ssize_t dummy_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
    printk(KERN_INFO "Readfunction\n");
    return 0;
}
static ssize_t dummy_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
    printk(KERN_INFO "Write Function\n");
    return 0;
}

static long dummy_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch(cmd) {
        case WR_VALUE:
            copy_from_user(&value ,(int32_t*) arg, sizeof(value));
            printk(KERN_INFO "Value = %d\n", value);
            break;
        case RD_VALUE:
            copy_to_user((int32_t*) arg, &value, sizeof(value));
            break;
    }
    return 0;
}


static int __init dummy_driver_init(void)
{
    /*Allocating Major number*/
    if((alloc_chrdev_region(&dev, 0, 1, "dummy_Dev")) <0){
        printk(KERN_INFO "Cannot allocate major number\n");
        return -1;
    }
    printk(KERN_INFO "Major = %d Minor = %d \n", MAJOR(dev), MINOR(dev));

    /*Creating cdev structure*/
    cdev_init(&dummy_cdev, &fops);
    dummy_cdev.owner = THIS_MODULE;
    dummy_cdev.ops = &fops;

    /*Adding character device to the system*/
    if((cdev_add(&dummy_cdev, dev, 1)) < 0){
        printk(KERN_INFO "Cannot add the device to the system\n");
        goto r_class;
    }

    /*Creating struct class*/
    if((dev_class = class_create(THIS_MODULE, "dummy_class")) == NULL){
        printk(KERN_INFO "Cannot create the struct class\n");
        goto r_class;
    }

    /*Creating device*/
    if((device_create(dev_class, NULL, dev, NULL, "dummy_device")) == NULL){
        printk(KERN_INFO "Cannot create the Device 1\n");
        goto r_device;
    }

    /*Creating Proc entry*/
    /* dummy_proc is the file created in proc file system */
    proc_create("dummy_proc", 0666, NULL, &proc_fops);

    printk(KERN_INFO "Device Driver Insert...Done!!!\n");
    return 0;

r_device:
    class_destroy(dev_class);
r_class:
    unregister_chrdev_region(dev,1);
    return -1;
}

void __exit dummy_driver_exit(void)
{
    remove_proc_entry("dummy_proc",NULL);
    device_destroy(dev_class,dev);
    class_destroy(dev_class);
    cdev_del(&dummy_cdev);
    unregister_chrdev_region(dev, 1);
    printk(KERN_INFO "Device Driver Remove...Done!!!\n");
}

module_init(dummy_driver_init);
module_exit(dummy_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Neelkanth Reddy <www.neelkanth.13@gmail.com>");
MODULE_DESCRIPTION("A simple device driver");
MODULE_VERSION("0:1.0");

neelkanth_surekha#cat Makefile 

obj-m += driver.o

KDIR = /lib/modules/$(shell uname -r)/build


#e.g. : make -C /lib/modules/4.4.0-93-generic/build  M=/home/neelkanth_surekha/Device_Driver/8_IOCTL_Driver_code modules
all:
make -C $(KDIR)  M=$(shell pwd) modules

clean:
make -C $(KDIR)  M=$(shell pwd) clean