Original URL: http://www.cnblogs.com/geneil/archive/2011/12/04/2275372.html
In addition to the ability to read and write devices, most drivers also require the ability to control hardware.
First, in user space, using the IOCTL system call to control the device , the prototype is as follows:
int ioctl (int fd,unsigned long cmd,...);
/*
FD: File descriptor
CMD: Control command
...: Optional parameter: Insert *ARGP, specific content depends on cmd
*/
The user program simply tells the driver what it wants to do with the command code, as to how to interpret these commands and how to implement them, which is what the driver does .
Second, drive the IOCTL method :
Int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
/*
The inode and FILP two pointers correspond to the file descriptor FD passed by the application, as is the argument for passing the open method.
CMD is passed directly to the driver without modification by the user space
ARG is optional.
*/
In the driver implementation of the IOCTL function in vivo, there is actually a switch {case} structure, each case corresponding to a command code, to do some corresponding action. How to implement these operations, this is every programmer's own thing, because the devices are specific. The key is how to organize the command code, because in the IOCTL command code is the only way to contact the user program commands and driver support.
In the Linux kernel, this defines a command code :
____________________________________
| Device Type | Serial number | Direction | Data Size |
|----------|--------|------|-------- |
| 8 bit | 8 bit | 2 bit |8~14 bit|
|----------|--------|------|-------- |
In this way, a command becomes an integer-like command code. However, the command code is very non-intuitive, so Linux kernel provides a number of macros, these macros can be generated according to the easy-to-understand string command code, or from the command code to get some user-understandable string to indicate the command corresponding device type, device serial number, data transmission direction and transmission size.
1. Define the command:
The kernel provides some macros to help define the command:
NR is ordinal, datatype is a data type, such as int
_io (Type, NR)//command with no parameters
_ior (Type, NR, datatype)//read data from drive
_iow (Type, NR, datatype)//write data to drive
_IOWR (TYPE,NR, datatype)//bidirectional transmission
Examples of defining commands:
#define Mem_ioc_magic ' m '//definition type
#define Mem_iocset _iow (mem_ioc_magic,0,int)
#define Mem_iocgqset _ior (mem_ioc_magic, 1, int)
2. Implement the command:
Define the command, the next step is to implement the IOCTL function, the implementation of the IOCTL includes three technical links:
1) return value;
The implementation of the IOCTL function is a switch statement executed according to the command, but usually returns-einval (illegal parameter) when the command does not match the command supported by any one device;
2) parameter use;
The user uses the INT ioctl (int fd,unsinged long cmd,...) When,... is the parameter to be passed;
Pass arg in int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
If Arg is an integer , it can be used directly;
In the case of pointers , we have to make sure that the user address is valid, so it needs to be checked correctly before use.
Having a check-up inside, not requiring testing :
Copy_from_user
Copy_to_user
Get_user
Put_user
need to detect :
__get_user
__put_user
Detection function ACCESS_OK ():
static inline int Access_ok (int type, const void *ADDR, unsigned long size)
/*
Type: is Verify_read or verify_write used to indicate whether to read user memory or write user memory;
Addr: Is the user memory address to be manipulated;
Size: Is the length of the operation. If the IOCTL needs to read an integer from the user space, the size parameter equals sizeof (int);
Return value: Access_ok Returns a Boolean value: 1, which is successful (access no problem); 0, is the failure, the IOCTL returns-efault;
*/
3) command operation;
Switch (CMD)
{
Case
... ...
}
Third, theIOCTL example analysis :
(1)memdev.h:
View Code
/*MEM Device Description Structure Body */
struct MEM_DEV
{
Char *data;
unsigned long size;
};
/* Define MAGIC number */
#define Memdev_ioc_magic ' K '
/* Define the command */
#define Memdev_iocprint _io (memdev_ioc_magic, 1)
#define Memdev_iocgetdata _ior (memdev_ioc_magic, 2, int)
#define Memdev_iocsetdata _iow (memdev_ioc_magic, 3, int)
#define MEMDEV_IOC_MAXNR 3
#endif/* _memdev_h_ */
(2)memdev.c:(Driver)
View Code
static int mem_major = Memdev_major;
Module_param (mem_major, int, s_irugo);
struct Mem_dev *mem_devp; /* Device Structural body pointer */
struct Cdev Cdev;
/* File Open function */
int Mem_open (struct inode *inode, struct file *filp)
{
struct Mem_dev *dev;
/* Get the secondary device number */
int num = MINOR (Inode->i_rdev);
if (num >= memdev_nr_devs)
Return-enodev;
dev = &mem_devp[num];
/* Assign the device description structure pointer to the file private data pointer */
Filp->private_data = Dev;
return 0;
}
/* File deallocation function */
int mem_release (struct inode *inode, struct file *filp)
{
return 0;
}
/*io Operation */
int Memdev_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err = 0;
int ret = 0;
int ioarg = 0;
/* Check the validity of the command */
if (_ioc_type (cmd)! = memdev_ioc_magic)
Return-einval;
if (_ioc_nr (cmd) > Memdev_ioc_maxnr)
Return-einval;
/* Check whether the parameter space can be accessed according to the command type */
if (_ioc_dir (cmd) & _ioc_read)
Err =!ACCESS_OK (Verify_write, (void *) arg, _ioc_size (cmd));
else if (_ioc_dir (cmd) & _ioc_write)
Err =!ACCESS_OK (Verify_read, (void *) arg, _ioc_size (cmd));
if (ERR)
Return-efault;
/* Perform the appropriate action according to the command */
Switch (CMD) {
/* Print Current device information */
Case Memdev_iocprint:
PRINTK ("<---CMD memdev_iocprint done--->\n\n");
Break
/* Get Parameters */
Case Memdev_iocgetdata:
Ioarg = 1101;
ret = __put_user (ioarg, (int *) arg);
Break
/* Set Parameters */
Case Memdev_iocsetdata:
ret = __get_user (ioarg, (int *) arg);
PRINTK ("<---in Kernel memdev_iocsetdata ioarg =%d--->\n\n", ioarg);
Break
Default
Return-einval;
}
return ret;
}
/* File operation structure */
static const struct File_operations Mem_fops =
{
. Owner = This_module,
. open = Mem_open,
. Release = Mem_release,
. IOCTL = Memdev_ioctl,
};
/* Device driver module load function */
static int memdev_init (void)
{
int result;
int i;
dev_t Devno = MKDEV (mem_major, 0);
/* Static application device number */
if (mem_major)
result = Register_chrdev_region (Devno, 2, "Memdev");
else/* Dynamically assign device number */
{
result = Alloc_chrdev_region (&devno, 0, 2, "Memdev");
Mem_major = Major (Devno);
}
if (Result < 0)
return result;
/* Initialize the CDEV structure */
Cdev_init (&cdev, &mem_fops);
Cdev.owner = This_module;
Cdev.ops = &mem_fops;
/* Register character device */
Cdev_add (&cdev, MKDEV (mem_major, 0), Memdev_nr_devs);
/* Allocate memory for device description structure */
MEM_DEVP = Kmalloc (Memdev_nr_devs * sizeof (struct mem_dev), gfp_kernel);
if (!MEM_DEVP)/* Request failed */
{
result =-Enomem;
Goto Fail_malloc;
}
memset (MEM_DEVP, 0, sizeof (struct mem_dev));
/* Allocate memory for device */
for (i=0; i < Memdev_nr_devs; i++)
{
Mem_devp[i].size = memdev_size;
Mem_devp[i].data = Kmalloc (memdev_size, Gfp_kernel);
memset (mem_devp[i].data, 0, memdev_size);
}
return 0;
Fail_malloc:
Unregister_chrdev_region (Devno, 1);
return result;
}
/* Module Unload function */
static void Memdev_exit (void)
{
Cdev_del (&cdev); /* Unregister the device */
Kfree (MEM_DEVP); /* release device structure in vivo */
Unregister_chrdev_region (MKDEV (mem_major, 0), 2); /* Release the device number */
}
Module_author ("David Xie");
Module_license ("GPL");
Module_init (Memdev_init);
Module_exit (Memdev_exit);
(3) app-ioctl.c(application)
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "memdev.h"/* contains the command definition */
int main ()
{
int fd = 0;
int cmd;
int arg = 0;
Char buf[4096];
/* Open the device file */
FD = open ("/dev/memdev0", O_RDWR);
if (FD < 0)
{
printf ("Open Dev Mem0 error!\n");
return-1;
}
/* Invoke Command Memdev_iocprint */
printf ("<---call memdev_iocprint--->\n");
cmd = Memdev_iocprint;
if (IOCTL (FD, CMD, &arg) < 0)
{
printf ("Call cmd memdev_iocprint fail\n");
return-1;
}
/* Invoke Command Memdev_iocsetdata */
printf ("<---call memdev_iocsetdata--->\n");
cmd = Memdev_iocsetdata;
arg = 2007;
if (IOCTL (FD, CMD, &arg) < 0)
{
printf ("Call cmd memdev_iocsetdata fail\n");
return-1;
}
/* Invoke Command Memdev_iocgetdata */
printf ("<---call memdev_iocgetdata--->\n");
cmd = Memdev_iocgetdata;
if (IOCTL (FD, CMD, &arg) < 0)
{
printf ("Call cmd memdev_iocgetdata fail\n");
return-1;
}
printf ("<---in User Space memdev_iocgetdata Get Data is%d--->\n\n", arg);
Close (FD);
return 0;
}
"Turn" the control of the IOCTL for Linux device drivers