world leader in high performance signal processing
Trace: » ioctls

Device Driver IOCTL interface

These interfaces are used to provide access into the device kernel space. Normally setup and performance data is managed with ioctl functions.

The IOCTL interface provides a wide range of kernel interface options. It is used to control characteristics of the driver (and in fact any other global kernel space variables).

The basic use of an IOCTL call involved some kind of a command switch with an associated optional data item. There is a convention applied to the way the command switch is formulated.

Currently all IOCTL calls are serialized using the Big Kernel Lock. This means only one IOCTL can be in progress at one time. More of an issue with multi CPU systems.

IOCTL User Space

The user space interface is: int ioctl(int fd , int cmd , var * data )

An example:

  ioctl(fd, TCSETA, tp);
 

IOCTL - Kernel Space

In Kernel space the most common way to implement an IOCTL function is to use a large switch statement.

The kernel function servicing ioctl's is called sys_ioctl in the file linux-2.6.x/fs/ioctl.c.

Question What do the lock_kernel() / unlock_kernel() functions do ?

  static int tty_ioctl(struct inode * inode, struct file * file,
                       unsigned int cmd, unsigned long arg)     
  {                                                             
 
       switch (cmd) {                                           
                  case TIOCSTI:                                 
                          return tiocsti(tty, p);               
                  case TIOCGWINSZ:                              
                          return tiocgwinsz(tty, p);            
                  case TIOCSWINSZ:                              
                           return tiocswinsz(tty, real_tty, p); 
 
        }                                                       
  }

IOCTL Commands

The commands used to trigger the various functions must be well defined and understood by both the kernel code and the user application. These definitions are normally held in an include file shared with user space.

Handling User Space Data

The user space data address must be copied into kernel space. The copy_to_user and copy_from_user macros can be used.

IOCTL Example code

This is a code example for implementing an IOCTL interface. The DATA array for a device will be written, read and exchanged.

The header file is set up first

  /* file name samp.h for both user and kernel space */                    
  #include <asm/ioctl.h>                                                   
 
  /* magic number for SCMD IOCTL operations */                             
  #define SCMD_MAGIC 's'                                                   
 
  #define SCMD_IOCGETD  _IOR(SCMD_MAGIC, 1 , char *) //get driver data     
  #define SCMD_IOCSETD  _IOW(SCMD_MAGIC, 2 , char *) //set driver data     
  #define SCMD_IOCXCHD  _IOWR(SCMD_MAGIC,3 , char *) //exchange driver data
 

IOCTL Addition to Driver

Next a section of the driver is added.

  int scmd_ioctl(struct inode* inode , struct file* filp ,                 
                                  unsigned int cmd , unsigned long  arg ) {
     int ret;                                                              
     int count;                                                            
     char temp[SCMD_SIZE];                                                 
     scmd_dev_t * dev;                                                     
 
     count = SCMD_SIZE;                                                    
     dev = (scmd_dev_t *) filp->private_data;                              
 
     switch (cmd) {                                                        
        case SCMD_IOCGETD:                                                 
           if(copy_to_user((unsigned char *)arg, dev->data, count))        
               return -EFAULT;                                             
           return 0;                                                       
 
        case SCMD_IOCSETD:                                                 
           if (copy_from_user(dev->data, (unsigned char *)arg, count))     
               return -EFAULT;                                             
           return 0;                                                       
 
        case SCMD_IOCXCHD:                                                 
           memcopy( (unsigned char *)&temp,                                
                        (unsigned char *)dev->data, count);                
 
           if (copy_from_user((unsigned char *)dev->data,                  
                              (unsigned char *)arg, count))                
               return -EFAULT;                                             
 
           if (copy_to_user((unsigned char *)arg,                          
                              (unsigned char *)&temp, count))              
               return -EFAULT;                                             
 
           return 0;                                                       
 
        default:                                                           
           return -ENOTTY;                                                 
  /*         return -EINVAL; another return option */                      
     }                                                                     
  }

IOCTL Fops Table

The fops table needs to be set up in the driver init function

    scmd_driver_fops.write = scmd_write;               
    // set up the ioctl function                       
    scmd_driver_fops.ioctl = scmd_ioctl;               
 
    printk(" scmd_ioctl module set up ret = %d\n",ret);
 

User code

A typical user code file

  // test_ioctl.c simple test for ioctl             
  // cc -O2 -o test_ioctl test_ioctl.c              
  #include <stdio.h>                                
  #include <fcntl.h>                                
  #include <unistd.h>                               
  #include "scmd_ioctl.h"                           
  #define SCMD_DEVICE "/dev/scmd"                   
 
  int main () {                                     
    int fd;                                         
    int ret;                                        
    int i;                                          
    char scmd_data[SCMD_SIZE];                      
 
    fd = open(SCMD_DEVICE,O_RDWR);                  
    if ( fd < 0 ) {                                 
      printf(" failed to open %s \n",SCMD_DEVICE);  
      exit (1);                                     
    }                                               
    ret = ioctl(fd,SCMD_IOCGETD,&scmd_data);        
    if ( ret < 0 ) {                                
      printf(" failed to GETD on %s\n",SCMD_DEVICE);
      exit(1);                                      
    }                                               
    printf("initial data ...\n");                   
    for (i = 0 ; i < 8 ; i++ ) {                    
      printf("data[%d] %x     data[%d] %x\n",       
             i,scmd_data[i],                        
             i+8,scmd_data[i+8]);                   
    }                                               
    close(fd);                                      
    return 0;                                       
  }