Ioctl which stand for Input Output control is a system call used in linux to implement system calls which are not be available in the kernel by default.
The major use of this is in case of handling some specific operations of a device for which the kernel does not have a system call by default. For eg: Ejecting the media from a "cd" drive.An ioctl command is implemented to give the eject system call to the cd drive.
Note: The following code is only valid for 2.6 kernels below 2.6.36.
For versions above 2.6.36 refer to the post "Implementing ioctl call for kernel versions above 2.6.36
To implement a new ioctl command we need to follow the following steps.
1. Define the ioctl code in a header file and include the same in the application as well as the module.
The definition is done as follows
#define "ioctl name" __IOX("magic number","command number","argument type")
where IOX can be :
"IO": If the command neither reads any data from the user nor writes any data to the userspace.
"IOW": If the commands needs to write some to the kernel space.
"IOR": If the command needs to read some thing from the kernel space.
"IOWR": If the command does both read as well as write from the user
The Magic Number is a unique number or character that will differentiate our set of ioctl calls from the other ioctl calls. some times the major number for the device is used here.
Command Number is the number that is assigned to the ioctl .It is this number that is used to differentiate the commands from one another .
The last is the type of data that will be written in case of __IOW or read in case of __IOR or both read as well as write in case of __IOWR. In the case of _IO we need not pass any thing.
2. Add the header file linux/ioctl.h to make use of the above mentioned calls.
Let us call the ioctl that we will create as "IOCTL_HELLO" , hence the header file , ioctl_basic.h, would be
3. The next step is to implement the ioctl call we defined in to the corresponding driver. First we will need to #include the header file ioctl_basic.h
Then we need to add the ioctl function which has the prototype
Where
ionde :is the inode number of the file being worked on
filp : is the file pointer to the file that was passed by the application.
cmd : is the ioctl command that was called from the user space.
arg : are the arguments passed from the user space.
With in the function "ioctl" we need to implement all the commands that we define in the header file.As we saw each command is given a command number in the header file, the same number will be used in a switch case statement to implement the calls.
We will just add a print statement to see how the ioctl call works. so the function would look as follows
In the above function the ioctl command "IOCTL_HELLO" will have got the number 0,that we assigned in the header file ioctl_basic.h, The same number would be passed in the argument "cmd" when the ioctl call is made from the user space. Thus using "cmd" in the switch case makes sure that the correct command get executed for the ioctl call.
4. Next step is to inform the kernel that the ioctl calls are implmented in the function "our_ioctl". This is done by making the fops pointer "ioctl" to point to "our_ioctl" as shown below
Note: You can read about fops at "fops implementation"
Now we need to call the new ioctl command from a user application, to test its working.
The call to an ioctl in the user space i.e in an application looks as follows
file descriptor : This the open file on which the ioctl command needs to be
executed, which would generally be device files
ioctl command : ioctl command which is implemented to achieve the desired functionality
arguments: The arguments that needs to be passed to the ioctl command.
Thus is our case the call would look as below
We need to "#include" the header file "ioctl_basic.h" in the user space program too.
The following are the full codes for the header file,the module and the user space application.
Header file: ioctl_basic.h
Module: ioctl_basic.c
User space appliction: user_basic_ioctl.c
Now compile the module using the following makefile
Note : To know more about compiling a module refer to "Inserting into the kernel" and "user access"
The message that was printed in the output of dmesg command is the same message that we have incorporated in the ioctl call implementation in the driver, hence proving that the call from the user space was passed on to the kernel space successfully.
The major use of this is in case of handling some specific operations of a device for which the kernel does not have a system call by default. For eg: Ejecting the media from a "cd" drive.An ioctl command is implemented to give the eject system call to the cd drive.
Note: The following code is only valid for 2.6 kernels below 2.6.36.
For versions above 2.6.36 refer to the post "Implementing ioctl call for kernel versions above 2.6.36
To implement a new ioctl command we need to follow the following steps.
1. Define the ioctl code in a header file and include the same in the application as well as the module.
The definition is done as follows
#define "ioctl name" __IOX("magic number","command number","argument type")
where IOX can be :
"IO": If the command neither reads any data from the user nor writes any data to the userspace.
"IOW": If the commands needs to write some to the kernel space.
"IOR": If the command needs to read some thing from the kernel space.
"IOWR": If the command does both read as well as write from the user
The Magic Number is a unique number or character that will differentiate our set of ioctl calls from the other ioctl calls. some times the major number for the device is used here.
Command Number is the number that is assigned to the ioctl .It is this number that is used to differentiate the commands from one another .
The last is the type of data that will be written in case of __IOW or read in case of __IOR or both read as well as write in case of __IOWR. In the case of _IO we need not pass any thing.
2. Add the header file linux/ioctl.h to make use of the above mentioned calls.
Let us call the ioctl that we will create as "IOCTL_HELLO" , hence the header file , ioctl_basic.h, would be
3. The next step is to implement the ioctl call we defined in to the corresponding driver. First we will need to #include the header file ioctl_basic.h
Then we need to add the ioctl function which has the prototype
Where
ionde :is the inode number of the file being worked on
filp : is the file pointer to the file that was passed by the application.
cmd : is the ioctl command that was called from the user space.
arg : are the arguments passed from the user space.
With in the function "ioctl" we need to implement all the commands that we define in the header file.As we saw each command is given a command number in the header file, the same number will be used in a switch case statement to implement the calls.
We will just add a print statement to see how the ioctl call works. so the function would look as follows
In the above function the ioctl command "IOCTL_HELLO" will have got the number 0,that we assigned in the header file ioctl_basic.h, The same number would be passed in the argument "cmd" when the ioctl call is made from the user space. Thus using "cmd" in the switch case makes sure that the correct command get executed for the ioctl call.
4. Next step is to inform the kernel that the ioctl calls are implmented in the function "our_ioctl". This is done by making the fops pointer "ioctl" to point to "our_ioctl" as shown below
Note: You can read about fops at "fops implementation"
Now we need to call the new ioctl command from a user application, to test its working.
The call to an ioctl in the user space i.e in an application looks as follows
file descriptor : This the open file on which the ioctl command needs to be
executed, which would generally be device files
ioctl command : ioctl command which is implemented to achieve the desired functionality
arguments: The arguments that needs to be passed to the ioctl command.
Thus is our case the call would look as below
We need to "#include" the header file "ioctl_basic.h" in the user space program too.
The following are the full codes for the header file,the module and the user space application.
Header file: ioctl_basic.h
Module: ioctl_basic.c
User space appliction: user_basic_ioctl.c
Now compile the module using the following makefile
Note : To know more about compiling a module refer to "Inserting into the kernel" and "user access"
The message that was printed in the output of dmesg command is the same message that we have incorporated in the ioctl call implementation in the driver, hence proving that the call from the user space was passed on to the kernel space successfully.
Very good post ... Explained in a way that its very easy for anyone to understand ..Thankyou...
ReplyDeletewhat is "/dev/temp"?
ReplyDelete/dev/temp is the character device we create using the command mknod.
DeleteI see that the post uses a different name(/dev/char_arr) with mknod, will correct it.
Thank you
Thank you very much for this post ! It helps me a lot to understand how to implement ioctl !
ReplyDeleteglad it helped :-)
DeleteThen we need to add the ioctl function which has the prototype:
ReplyDeleteint ioctl_funcs (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
'ioctl_funcs' is a user defined function name, could be 'my_ioctl_funcs'.
yes we can use that name too.
Deletestruct file_operations fops = {
ReplyDeleteopen: open
ioctl: our_ioctl, //Mapping the ioctl function
release: release,
};
Don't you think, it is 'ioctl_funcs' and not 'our_ioctl' ? It is little confusing when u define ioctl_funcs() and assign 'our_ioctl' .
Thanks for pointing the error, have corrected it.
DeleteThis post was very helpful. Thanks!
ReplyDeleteThis comment has been removed by the author.
DeleteGlad it was helpful
Deletei get when compiling :
ReplyDeleteerror: truct file_operations has no member named ioctl; did you mean iopoll
ioctl: ioctl_funcs,
What is your kernel version, if its abofe 2.36, please use the code in the post https://tuxthink.blogspot.com/2012/12/implementing-ioctl-call-for-kernel.html
Delete