Chapter 7. Talking To Device Files

Talking to Device Files (writes and IOCTLs)}


This is not always enough. Imagine you had a serial port connected to a modem (even if you have an internal modem, it is still implemented from the CPU's perspective as a serial port connected to a modem, so you don't have to tax your imagination too hard). The natural thing to do would be to use the device file to write things to the modem (either modem commands or data to be sent through the phone line) and read things from the modem (either responses for commands or the data received through the phone line). However, this leaves open the question of what to do when you need to talk to the serial port itself, for example to send the rate at which data is sent and received.

The answer in Unix is to use a special function called ioctl (short for Input Output ConTroL). Every device can have its own ioctl commands, which can be read ioctl's (to send information from a process to the kernel), write ioctl's (to return information to a process), [1] both or neither. The ioctl function is called with three parameters: the file descriptor of the appropriate device file, the ioctl number, and a parameter, which is of type long so you can use a cast to use it to pass anything. [2]

The ioctl number encodes the major device number, the type of the ioctl, the command, and the type of the parameter. This ioctl number is usually created by a macro call (_IO, _IOR, _IOW or _IOWR --- depending on the type) in a header file. This header file should then be included both by the programs which will use ioctl (so they can generate the appropriate ioctl's) and by the kernel module (so it can understand it). In the example below, the header file is chardev.h and the program which uses it is ioctl.c.

If you want to use ioctls in your own kernel modules, it is best to receive an official ioctl assignment, so if you accidentally get somebody else's ioctls, or if they get yours, you'll know something is wrong. For more information, consult the kernel source tree at Documentation/ioctl-number.txt.

Example 7-1. chardev.c

/*  chardev.c - Create an input/output character device


#include <linux/modversions.h>

/* For character devices */

/* The character device definitions are here */
#include <linux/fs.h>

/* A wrapper which does next to nothing at
 * at present, but may help for compatibility
 * with future versions of Linux */
#include <linux/wrapper.h>

/* Our own ioctl numbers */
#include "chardev.h"

 * here if necessary. */

#include <asm/uaccess.h>  /* for get_user and put_user */

#define SUCCESS 0


 * /proc/devices */
#define DEVICE_NAME "char_dev"

#define BUF_LEN 80

/* Is the device open right now? Used to prevent 
 * concurent access into the same device */
static int Device_Open = 0;

/* The message the device will give when asked */
static char Message[BUF_LEN];

/* How far did the process reading the message get? 
 * Useful if the message is larger than the size of the 
 * buffer we get to fill in device_read. */
static char *Message_Ptr;

/* This function is called whenever a process attempts 
 * to open the device file */
static int device_open(struct inode *inode, 
                       struct file *file)
#ifdef DEBUG

  /* We don't want to talk to two processes at the 
   * same time */
  if (Device_Open)
    return -EBUSY;

   * more careful here, because one process might have 
   * checked Device_Open right before the other one 
   * tried to increment it. However, we're in the 
   * kernel, so we're protected against context switches.
   * This is NOT the right attitude to take, because we
   * SMP in a later chapter.


  /* Initialize the message */
  Message_Ptr = Message;


  return SUCCESS;

/* This function is called when a process closes the 
 * device file. It doesn't have a return value because 
 * it cannot fail. Regardless of what else happens, you 
 * device file could be impossible to close).
static int device_release(struct inode *inode, 
                          struct file *file)
static void device_release(struct inode *inode, 
                           struct file *file)
#ifdef DEBUG
  /* We're now ready for our next caller */
  Device_Open --;


  return 0;

/* This function is called whenever a process which 
 * has already opened the device file attempts to 
 * read from it. */
static ssize_t device_read(
    struct file *file,
    size_t length,     /* The length of the buffer */
    loff_t *offset) /* offset to the file */
static int device_read(
    struct inode *inode,
    struct file *file,
    int length)     /* The length of the buffer 
                     * (mustn't write beyond that!) */
  /* Number of bytes actually written to the buffer */
  int bytes_read = 0;

#ifdef DEBUG

   * (which signifies end of file) */
  if (*Message_Ptr == 0)
    return 0;

  /* Actually put the data into the buffer */
  while (length && *Message_Ptr)  {

    /* Because the buffer is in the user data segment, 
     * not the kernel data segment, assignment wouldn't 
     * work. Instead, we have to use put_user which 
     * copies data from the kernel data segment to the 
     * user data segment. */
    length --;
    bytes_read ++;

#ifdef DEBUG

   /* Read functions are supposed to return the number 
    * of bytes actually inserted into the buffer */
  return bytes_read;

/* This function is called when somebody tries to 
 * write into our device file. */ 
static ssize_t device_write(struct file *file,
                            const char *buffer,
                            size_t length,
                            loff_t *offset)
static int device_write(struct inode *inode,
                        struct file *file,
                        const char *buffer,
                        int length)
  int i;

#ifdef DEBUG
    file, buffer, length);

  for(i=0; i<length && i<BUF_LEN; i++)
    get_user(Message[i], buffer+i);
    Message[i] = get_user(buffer+i);

  Message_Ptr = Message;

  return i;

/* This function is called whenever a process tries to 
 * parameters (additional to the inode and file 
 * structures, which all device functions get): the number
 * of the ioctl called and the parameter given to the 
 * ioctl function.
 * is returned to the calling process), the ioctl call 
 * returns the output of this function.
int device_ioctl(
    struct inode *inode,
    struct file *file,
    unsigned long ioctl_param) /* The parameter to it */
  int i;
  char *temp;
  char ch;

  /* Switch according to the ioctl called */
  switch (ioctl_num) {
    case IOCTL_SET_MSG:
       * and set that to be the device's message. */ 

      temp = (char *) ioctl_param;
      /* Find the length of the message */
      get_user(ch, temp);
      for (i=0; ch && i<BUF_LEN; i++, temp++)
        get_user(ch, temp);
      for (i=0; get_user(temp) && i<BUF_LEN; i++, temp++)

      /* Don't reinvent the wheel - call device_write */

    case IOCTL_GET_MSG:
      /* Give the current message to the calling 
       * process - the parameter we got is a pointer, 
       * fill it. */
      /* Warning - we assume here the buffer length is 
       * 100. If it's less than that we might overflow 
       * the buffer, causing the process to core dump. 
       * The reason we only allow up to 99 characters is 
       * that the NULL which terminates the string also 
       * needs room. */

       * will be properly terminated */

      /* This ioctl is both input (ioctl_param) and 
       * output (the return value of this function) */
      return Message[ioctl_param];

  return SUCCESS;


/* This structure will hold the functions to be called 
 * when a process does something to the device we 
 * created. Since a pointer to this structure is kept in 
 * the devices table, it can't be local to
 * init_module. NULL is for unimplemented functions. */
struct file_operations Fops = {
  NULL,   /* seek */
  NULL,   /* readdir */
  NULL,   /* select */
  device_ioctl,   /* ioctl */
  NULL,   /* mmap */
  NULL,  /* flush */
  device_release  /* a.k.a. close */

/* Initialize the module - Register the character device */
int init_module()
  int ret_val;

  /* Register the character device (atleast try) */
  ret_val = module_register_chrdev(MAJOR_NUM, 

  /* Negative values signify an error */
  if (ret_val < 0) {
            "Sorry, registering the character device ",
    return ret_val;

          "Registeration is a success", 
  printk ("file you'll use.\n");

  return 0;

void cleanup_module()
  int ret;

  /* Unregister the device */
  ret = module_unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
  /* If there's an error, report it */ 
  if (ret < 0)

Example 7-2. chardev.h

/*  chardev.h - the header file with the ioctl definitions.
 *  The declarations here have to be in a header file, because
 *  they need to be known both to the kernel module
 *  (in chardev.c) and the process calling ioctl (ioctl.c)

#ifndef CHARDEV_H
#define CHARDEV_H

#include <linux/ioctl.h>

/* The major device number. We can't rely on dynamic 
 * registration any more, because ioctls need to know 
 * it. */
#define MAJOR_NUM 100

/* Set the message of the device driver */
#define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *)
/* _IOR means that we're creating an ioctl command 
 * number for passing information from a user process
 * to the kernel module. 
 * The first arguments, MAJOR_NUM, is the major device 
 * number we're using.
 * The second argument is the number of the command 
 * (there could be several with different meanings).
 * The third argument is the type we want to get from 
 * the process to the kernel.

/* Get the message of the device driver */
#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)
 /* This IOCTL is used for output, to get the message 
  * of the device driver. However, we still need the 
  * buffer to place the message in to be input, 
  * as it is allocated by the process.

/* Get the n'th byte of the message */
 /* The IOCTL is used for both input and output. It 
  * receives from the user a number, n, and returns 
  * Message[n]. */

/* The name of the device file */
#define DEVICE_FILE_NAME "char_dev"


Example 7-3. ioctl.c

/*  ioctl.c - the process to use ioctl's to control the kernel module
 *  Until now we could have used cat for input and output.  But now
 *  we need to do ioctl's, which require writing our own process. 

/* device specifics, such as ioctl numbers and the 
 * major device file. */
#include "chardev.h"    

#include <fcntl.h>      /* open */ 
#include <unistd.h>     /* exit */
#include <sys/ioctl.h>  /* ioctl */

/* Functions for the ioctl calls */

ioctl_set_msg(int file_desc, char *message)
  int ret_val;

  ret_val = ioctl(file_desc, IOCTL_SET_MSG, message);

  if (ret_val < 0) {
    printf ("ioctl_set_msg failed:%d\n", ret_val);

ioctl_get_msg(int file_desc)
  int ret_val;
  char message[100]; 

  /* Warning - this is dangerous because we don't tell 
   * the kernel how far it's allowed to write, so it 
   * might overflow the buffer. In a real production 
   * program, we would have used two ioctls - one to tell
   * the kernel the buffer length and another to give 
   * it the buffer to fill
  ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);

  if (ret_val < 0) {
    printf ("ioctl_get_msg failed:%d\n", ret_val);

  printf("get_msg message:%s\n", message);

ioctl_get_nth_byte(int file_desc)
  int i;
  char c;

  printf("get_nth_byte message:");

  i = 0;
  while (c != 0) {
    c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);

    if (c < 0) {
      "ioctl_get_nth_byte failed at the %d'th byte:\n", i);


/* Main - Call the ioctl functions */
  int file_desc, ret_val;
  char *msg = "Message passed by ioctl\n";

  file_desc = open(DEVICE_FILE_NAME, 0);
  if (file_desc < 0) {
    printf ("Can't open device file: %s\n", 

  ioctl_set_msg(file_desc, msg);




Notice that here the roles of read and write are reversed again, so in ioctl's read is to send information to the kernel and write is to receive information from the kernel.


This isn't exact. You won't be able to pass a structure, for example, through an ioctl --- but you will be able to pass a pointer to the structure.