Now we have understood like what is device driver, basic components of device drivers ,functionality of device driver. As we have in almost every driver have basic functions support like open, close , read and write. Besides of these generic function every driver may have their own particular functionality exposed to user space applications. Like driver for display devices may have function to control the brightness and resolution. Driver for camera sensor may have functions to control the image resolutions ,starting captures of images and shutting down it.
So there should be some mechanism to call or initiate these functionality from user space. One way of doing this is by using of syscalls . But problem with syscalls was that syscalls are limited in number and with available syscalls we cannot cover all the functionality of every devices available in this world. And the other problem is to remember all the syscalls and there uses.
To solve this problem Linux provide us a generic call which can be used to control any device which is called IOCTL or input output control. As the name suggest by using of ioctl we can write to the device as well as we can read from the device also.
Now let’s understand the mystery that how a single generic call can fulfill the requirement of controlling all the devices. for that lets understand ioctl call.
following is the syntax of ioctl call
long ioctl(struct file *f, unsigned int cmd, unsigned long arg);
from above declaration we can understand that command and arguments are the main component for ioctl.
command :- command specifies the operation that need to be performed on the devices.
arguments :- these are the parameter which actually required to fulfill the command or we can say these are the configuration parameter if we are trying to configuring the device. If there is need to send more than one argument we can put all argument in a structure and pointer to that structure is passed as an argument. Argument is taken as long integer in kernel space and processed accordingly.
IOCTL implementation in driver
ioctl function needs to be implemented in driver and an function pointer is initialized with it as we do in case of open(), close() etc. In implementation of ioctl we generally use switch case statement ,each case belongs to commends expected from the user space. lets understand it with below example
static char ker_buf; //driver local buffer
static int a;
static int dev_open(struct inode *inod, struct file *fil);
static ssize_t dev_read(struct file *filep,char *buf,size_t len,loff_t *off);
static ssize_t dev_write(struct file *flip,const char *buff,size_t len,loff_t *off);
static int dev_release(struct inode *inod,struct file *fil);
//structure containing device operation
static long my_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
static struct file_operations fops=
.read=dev_read, //pointer to device read function
.write=dev_write, //pointer to device write function
.open=dev_open, //pointer to device open function
.release=dev_release, //pointer to device release function
.unlocked_ioctl=my_ioctl, //ioctl added by me
static int hello_init(void) //init function to be called at the time of insmod
printk(KERN_ALERT “device registration failed.”);
printk(KERN_ALERT “device registred\n”);
static void hello_exit(void) //exit function to be called at the time of rmmod
In the above example we can see that we have implemented my_ioctl function to handle the ioctl commend from user space . As you can see we have defined three case:-
QUERY_GET_VALUE:- to read the value of variable a from user space.
QUERY_SET_VALUE:- to set the value of a from user space.
QUERY_CLEAR_VALUE:- to set the value of a to 0.
there are the command which user application can send to our driver. So in a conclusion we can say that we need to define the case for every possible command we are expecting to receive from user space.
after defining my_ioctl () we need to initialize the unlocked_ioctl function pointer with the address of my_ioctl.
IOCTL header file
these ioctls command and their arguments are defined in some header file and that header file is included in both user space application and driver.
#define QUERY_GET_VALUE _IOR(‘q’, 1, int *)
#define QUERY_CLEAR_VALUE _IO(‘q’, 2)
#define QUERY_SET_VALUE _IOW(‘q’, 3, int *)
In the above function definition _IOR, _IOW, _IOWR define the direction of operation.
_IOR:- it indicates the direction from driver to application or we can say it’s a read operation.
_IOW:- it indicates the direction from application to driver or we can say it’s a write operation.
User space application
Below is the application code in which we invoke the ioctl with command and argument.
printf(“value written is = %d\n”,a);
printf(“lets read value from device\n”);
printf(“value of a is =%d\n”,a);