Linux Device Driver Tutorial Part 8 – I/O Control in Linux IOCTL()

This article is a continuation of the  Series on Linux Device Driver, and carries on the discussion on character drivers and their implementation.This is the Part 8 of Linux device driver tutorial. Now we will discuss IOCTL Tutorial in Linux.

Introduction

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. 
There are many ways to Communicate between the User space and Kernel Space, they are:
  • IOCTL
  • Procfs
  • Sysfs
  • Configfs
  • Debugfs
  • Sysctl
  • UDP Sockets
  • Netlink Sockets

In this tutorial we will see IOCTL.

IOCTL Tutorial in Linux

IOCTL

IOCTL is referred as Input and Output Control, which is used to talking to device drivers. This system call, available in most driver categories.  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.

Some real time applications of ioctl is Ejecting the media from a “cd” drive, to change the Baud Rate of Serial port, Adjust the Volume, Reading or Writing device registers, etc. We already have write and read function in our device driver. But it is not enough for all cases.

Steps involved in IOCTL

There are some steps involved to use IOCTL.

  • Create IOCTL command in driver
  • Write IOCTL function in driver
  • Create IOCTL command in User space application
  • Use IOCTL system call in User space

Create IOCTL Command in Driver

To implement a new ioctl command we need to follow the following steps.

1. Define the ioctl code

#define "ioctl name" __IOX("magic number","command number","argument type")

where IOX can be :
“IO”: an ioctl with no parameters
“IOW”: an ioctl with write parameters (copy_from_user)
“IOR”: an ioctl with read parameters (copy_to_user)
“IOWR”: an ioctl with both write and read parameters

  • 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 .This is used to differentiate the commands from one another.
  • The last is the type of data.

2. Add the header file linux/ioctl.h to make use of the above mentioned calls.

Example:

Write IOCTL function in driver

The next step is to implement the ioctl call we defined in to the corresponding driver. We need to add the ioctl function which has the prototype.

Where
<file>   : 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 defined above. We need to use the same commands in switch statement which is defined above.

Then need to inform the kernel that the ioctl calls are implemented in the function “etx_ioctl”. This is done by making the fops pointer “unlocked_ioctl” to point to “etx_ioctl” as shown below.

Example:

Now we need to call the new ioctl command from a user application.

Create IOCTL command in User space application

Just define the ioctl command like how we defined in driver.

Example:

Use IOCTL system call in User space

Include the header file <sys/ioctl.h>.Now we need to call the new ioctl command from a user application.

long ioctl( "file descriptor","ioctl command","Arguments");

<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.

Example:

Now we will see the complete driver and application.

Device Driver Source Code

In this example we only implemented IOCTL. In this driver, i defined one variable (int32_t value). Using ioctl command we can read or change the variable. So other functions like open, close, read, write, We simply left empty. Just go through the code below.

driver.c

Makefile:

Application Source Code

This application is used to write the value to the driver. Then read the value again.

test_app.c

Building Driver and Application

  • Build the driver by using Makefile (sudo make)
  • Use below line in terminal to compile the user space application.

gcc -o test_app test_app.c

Execution (Output)

As of now, we have driver.ko and test_app. Now we will see the output.

  • Load the driver using sudo insmod driver.ko
  • Run the application (sudo ./test_app)

*********************************
*******WWW.EmbeTronicX.com*******

Opening Driver
Enter the Value to send

  • Enter the value to pass

23456
Writing Value to Driver
Reading Value from Driver
Value is 23456
Closing Driver

  • Now check the value using dmesg

Device File Opened...!!!
Value = 23456
Device File Closed...!!!

  • Our value 23456 was passed to the kernel and it was updated.

This is the simple example using ioctl in driver. If you want to send multiple arguments, put those variables into structure and pass the structure.

In our next tutorial we will see other userspace and kernel space communication methods.

Download our new Android app. You can learn all Embedded Tutorials from your Android Phone easily.

Click Here to Download App!