Basic Steps to registering character device driver
1) Create the object of structure named as dev_t which contains two things as major number and minor number.
2) Call function alloc_chardev_region() which is use to create device file by allocating major number and minor number implicitely.
3) If we call this function then there is no need to create device file explicitely.
4) At the time of removing device driver we have to call unregister_chardev_region() function.
5) Compile that driver and insert into kernel using insmod command.
6) We can check the list of devices which are loaded currently by the kernel by opening the file /proc/devices.
7) Major number and minor number gets display in our log file as /var/log/syslog.
--------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Device Driver Program:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Program Name: driver-1.c
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This header file required for all kernel module.
#include<linux/module.h>
// This header file is required for __init() and __exit() function.
#include<linux/init.h>
// This header file is required for KERN_INFO, KERN_ALERT macros.
#include<linux/kernel.h>
// This header file required for dev_t typedef.
#include<linux/types.h>
// This header file is required for alloc_chrdev_region() and unregister_chrdev_region() function.
#include<linux/fs.h>
// This header file is required for format_dev_t() function.
#include<linux/kdev_t.h>
// structure which contains major number and minor number.
static dev_t mydev;
// Use for debugging
static char buffer[64];
MODULE_AUTHOR("Sunil Bhagwat");
MODULE_LICENSE("GPL");
static __init chardrv_in(void)
{
printk(KERN_INFO "Character driver being loaded.\n");
// It is used to register our device with kernel // Parameters are : // 1. Address of dev_t structure // 2. Starting range of device number // 3. Number of device numbers we want. // 4. Name of device which sill appeare in /proc/devices
alloc_chrdev_region(&mydev, 0, 1, "Demo Driver");
printk(KERN_INFO "Information of our driver is %s \n", format_dev_t(buffer, mydev));
return 0;
}
static void __exit chardrv_out(void)
{
printk(KERN_INFO "Character driver being unloaded. \n");
// It is used to unregister our device with kernel // Parameters are : // 1. Address of dev_t structure // 2. Number of device numbers we want.
unregister_chrdev_region(mydev, 1);
}
module_init(chardrv_in);
module_exit(chardrv_out);
1) Create the object of structure named as dev_t which contains two things as major number and minor number.
2) Call function alloc_chardev_region() which is use to create device file by allocating major number and minor number implicitely.
3) If we call this function then there is no need to create device file explicitely.
4) At the time of removing device driver we have to call unregister_chardev_region() function.
5) Compile that driver and insert into kernel using insmod command.
6) We can check the list of devices which are loaded currently by the kernel by opening the file /proc/devices.
7) Major number and minor number gets display in our log file as /var/log/syslog.
--------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Device Driver Program:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Program Name: driver-1.c
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This header file required for all kernel module.
#include<linux/module.h>
// This header file is required for __init() and __exit() function.
#include<linux/init.h>
// This header file is required for KERN_INFO, KERN_ALERT macros.
#include<linux/kernel.h>
// This header file required for dev_t typedef.
#include<linux/types.h>
// This header file is required for alloc_chrdev_region() and unregister_chrdev_region() function.
#include<linux/fs.h>
// This header file is required for format_dev_t() function.
#include<linux/kdev_t.h>
// structure which contains major number and minor number.
static dev_t mydev;
// Use for debugging
static char buffer[64];
MODULE_AUTHOR("Sunil Bhagwat");
MODULE_LICENSE("GPL");
static __init chardrv_in(void)
{
printk(KERN_INFO "Character driver being loaded.\n");
// It is used to register our device with kernel // Parameters are : // 1. Address of dev_t structure // 2. Starting range of device number // 3. Number of device numbers we want. // 4. Name of device which sill appeare in /proc/devices
printk(KERN_INFO "Information of our driver is %s \n", format_dev_t(buffer, mydev));
return 0;
}
static void __exit chardrv_out(void)
{
printk(KERN_INFO "Character driver being unloaded. \n");
// It is used to unregister our device with kernel // Parameters are : // 1. Address of dev_t structure // 2. Number of device numbers we want.
}
module_init(chardrv_in);
module_exit(chardrv_out);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Makefile
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
obj-m += driver-1.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Steps to execute this driver program.
1) Open the terminal and compile this driver-1.c program using "make" command.
"make"
2) After this type terminal in "cat /proc/devices" for open the file and see the driver driver name. e.g. Demo Driver
"cat /proc/devices "
3) Open the "syslog" file by using command "cat /var/log/syslog" or "dmesg" and see
the printk() function output.
-------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Program Name:- driver-2.c
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This header file is required for __init() and __exit() function.
#include<linux/init.h>
// This header file required for load LKM into kernel.
#include<linux/module.h>
// This header file is required for KERN_INFO, KERN_ALERT macros.
#include<linux/kernel.h>
// This header file is required for alloc_chrdev_region() and unregister_chrdev_region() function.
#include<linux/fs.h>
// This header file is r
equired for the copy to user function.
#include<asm/uaccess.h>
// // This header file is use to support the kernel Driver Model
#include<linux/device.h>
// The device will appear at /dev/char using this value.
#define DEVICE_NAME "My Driver"
// The device class -- this is a character device driver.
#define CLASS_NAME "MY DEVICE DRIVER"
// The license type -- this affects available functionality.
MODULE_LICENSE("GPL");
// The author -- visible when you use modinfo.
MODULE_AUTHOR("Sunil Bhagwat");
MODULE_DESCRIPTION("My Demo Device Driver");
// A version number to inform users.
MODULE_VERSION("0.1");
// Stores the device number -- determined automatically.
static int majorNumber;
// Memory for the string that is passed from userspace.
static char message[250] = {0};
// Used to remember the size of the string stored.
static short size_of_message;
// Counts the number of times the device is opened.
static int numberOpens = 0;
// The device-driver class struct pointer.
static struct class* charClass = NULL;
static struct device* charDevice = NULL;
// The prototype functions for the character driver -- must come before the struct definition.
static int dev_open(struct inode *, struct file *);
static int dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);
// Initialize file_operations structure.
static struct file_operations fops =
{
.open = dev_open,
.read = dev_read,
.write = dev_write,
.release = dev_release,
};
// Driver initialization function.
static int __init char_init(void)
{
printk(KERN_INFO "MyDriver : Driver loaded sucessfully \n");
//Allocate a major number for the device.
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
// If there is a problem in major number allocation .
if(majorNumber < 0)
{
printk(KERN_ALERT "MyDriver : failed to register a major number \n");
return majorNumber;
}
printk(KERN_INFO "MyDriver : reistered correctly with major number %d \n", majorNumber);
// Register the device class.
charClass = class_create(THIS_MODULE, CLASS_NAME);
if(IS_ERR(charClass))
{
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device \n");
return PTR_ERR(charClass);
}
printk(KERN_INFO "MyDriver : device class registered correctly \n");
// Register the device driver
charDevice = device_create(charClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if(IS_ERR(charDevice))
{
class_destroy(charClass);
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device \n");
return PTR_ERR(charDevice);
}
printk(KERN_INFO "MyDriver : device class created correctly \n");
return 0;
}
// Driver cleanup function.
static void __exit char_exit(void)
{
// remove the device.
device_destroy(charClass, MKDEV(majorNumber, 0));
// unregister the device class.
class_unregister(charClass);
// remove the device class.
class_destroy(charClass);
// unregister the major number.
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_INFO "MyDevice : Goodbye from our driver! \n");
}
// Function which gets called when we open the device.
static int dev_open(struct inode *inodep, struct file *filep)
{
numberOpens++;
printk(KERN_INFO "MyDriver : Device has been opened %d time(s) \n", numberOpens);
return 0;
}
// Function is called whenever device is being read from user space i.e. data is
/*
filep : A pointer to a file object (defined in linux/fs.h)
buffer: The pointer to the buffer to which this function writes the data
len : The length of the buffer
offset: The offset if required
*/
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset)
{
int error_count = 0;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success.
error_count = copy_to_user(buffer, message, size_of_message);
if(error_count == 0)
{
// if true then have success
printk(KERN_INFO "MyDriver : Sent %d characters to the user \n", size_of_message);
return (size_of_message = 0); // clear the position to the start and return 0
}
else
{
printk(KERN_INFO "MyDriver : Failed to send %d characters to the user \n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14).
}
}
//This function is called whenever the device is being written to from user space i.e.
// data is sent to the device from the user. The data is copied to the message[] array in this
/*
filep: A pointer to a file object
buffer: The buffer to that contains the string to write to the device
len: The length of the array of data that is being passed in the const char buffer
offset: The offset if required
*/
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset)
{
sprintf(message, "%s(%d letters)", buffer, len); // appending received string with its length.
size_of_message = strlen(message);
// store the length of the stored message.
printk(KERN_INFO "MyDriver : Received %d characters from the user \n",len);
return len;
}
// The device release function that is called whenever the device is closed/released by the user space program
/*
inodep: A pointer to an inode object (defined in linux/fs.h)
filep: A pointer to a file object (defined in linux/fs.h)
*/
static int dev_release(struct inode *inodep, struct file *filep)
{
printk(KERN_INFO "MyDriver : Device successfully closed \n");
return 0;
}
module_init(char_init);
module_exit(char_exit);
--------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Makefile
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
obj-m += driver-2.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
--------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Program Name :- DemoDriver.c
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This program is use to perform the operation on device driver file such open, read, write and
// release as like actual device.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>
#define BUFFER_LENGTH 256 // The buffer length (crude but fine)
static char receive[BUFFER_LENGTH]; // The receive buffer from the LKM
int main(){
int ret, fd;
char stringToSend[BUFFER_LENGTH];
printf("Starting device test code example...\n");
fd = open("/dev/Marvellous Driver 1", O_RDWR); // Open the device with read/write access
if (fd < 0){
perror("Failed to open the device...");
return errno;
}
printf("Type in a short string to send to the kernel module:\n");
scanf("%[^\n]%*c", stringToSend); // Read in a string (with spaces)
printf("Writing message to the device [%s].\n", stringToSend);
ret = write(fd, stringToSend, strlen(stringToSend)); // Send the string to the LKM
if (ret < 0){
perror("Failed to write the message to the device.");
return errno;
}
printf("Press ENTER to read back from the device...\n");
getchar();
printf("Reading from the device...\n");
ret = read(fd, receive, BUFFER_LENGTH); // Read the response from the LKM
if (ret < 0){
perror("Failed to read the message from the device.");
return errno;
}
printf("The received message is: [%s]\n", receive);
printf("End of the program\n");
return 0;
}
--------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Steps to execute this device driver program.
1) Run "make" utility to create .ko file.
2) Insert device driver in kernel image by using insmod command.
"sudo insmod driver-2.ko"
3) Check whether our driver is successfully loaded or not by opening the file /proc/devices.
" cat /proc/devices"
4) After this compile our "DemoDriver.c" file using gcc command and create executable file.
" gcc DemoDriver.c -o DemoDriver "
5) Execute that executable file by using following command.
" sudo ./DemoDriver "
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////