Control of character devices
1. Character Device control theory
1.1 Effects
Most drivers require the ability to control equipment in addition to the ability to provide read and write equipment. For example: Change the baud rate
1.2 Application Interface
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: Device file descriptor to control
CMD: control commands sent to the device
...: The third parameter is an optional argument that exists or not depends on the control command (the second argument)
1.3 Device Drive Method
2. Character Device control implementation
2.1 Defining commands
The order is essentially an integer, but to make the integer more readable, we usually divide the integer into segments, type (8 bits), ordinal, parameter transfer direction, parameter length
Type (Types/magic number): The command that indicates which device this belongs to
number (ordinal): Different commands used to differentiate the same device
direction: The direction of the parameter transfer, the possible value is _ioc_none(no data transfer),_ioc_read, _ioc_write(read and write parameters to the device)
Size: Parameter length
The Linux system provides the following macros to help define the command:
* _io (type, nr): command without parameters
*_ior (type, nr, datatype): command to read parameters from the device
*_iow (type, nr, datatype) : command to write parameters to the device
For example:
#define Mem_magic ' m '//define magic number
#define Mem_set _iow (mem_magic, 0, int)
2.2 Implementing Device methods
The implementation of the UNLOCKED_IOCTL function is usually a switch statement executed according to the command. However, when the command number does not match the command supported by any one device, the -einval is returned
Programming Model:
Switch cmd:
Case command A:
//perform the corresponding operation of a
Case Command B:
//perform the corresponding operation of B
Defaule:
Return-einval;
Instance code analysis to implement the IOCTL:
The memdev.c here is still on the basis of the previous article.
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h > #include <linux/init.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <linux/ slab.h> #include "memdev.h" int dev1_registers[5];int dev2_registers[5];struct Cdev cdev; dev_t devno;/* File Open function */int mem_open (struct inode *inode, struct file *filp) {/* Get secondary device number */int num = MINOR (inode-> ; i_rdev); if (num==0) filp->private_data = dev1_registers; else if (num = = 1) filp->private_data = dev2_registers; else Return-enodev; Invalid secondary device number return 0; }/* file Release function */int mem_release (struct inode *inode, struct file *filp) {return 0;} /* Read function */static ssize_t mem_read (struct file *filp, char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos; unsigned int count = size; int ret = 0; int *register_addr = filp->private_data; /* Obtain the device's register base address */* To determine if the read position is valid */if (P >= 5*sizeof (int)) return 0; if (Count > 5*sizeof (int)-P) Count = 5*sizeof (int)-p; /* Read data to User space */if (Copy_to_user (buf, Register_addr+p, Count)) {ret =-efault; } else {*ppos + = count; ret = count; } return ret;} /* Write function */static ssize_t mem_write (struct file *filp, const char __user *buf, size_t size, loff_t *ppos) {unsigned long p = *ppos; unsigned int count = size; int ret = 0; int *register_addr = filp->private_data; /* Get the device's register address */* parse and get valid write length */if (P >= 5*sizeof (int)) return 0; if (Count > 5*sizeof (int)-P) Count = 5*sizeof (int)-p; /* Write data from user space */if (Copy_from_user (Register_addr + p, buf, count)) ret =-efault; else {*ppos + = count; ret = count; } return ret;} /* Seek file Locator function */static loff_t mem_llseek (struct file *filp, loff_t offset, int whence) {loff_t newpos; Switch (whence) {case seek_set:newpos = offset; Break Case Seek_cur:newpos = Filp->f_pos + offset; Break Case Seek_end: Newpos = 5*sizeof (int)-1 + offset; Break Default:return-einval; } if ((newpos<0) | | | (newpos>5*sizeof (int))) Return-einval; Filp->f_pos = Newpos; return newpos;} <span style= "color: #ff0000;" >//Device control function long mem_ioctl (struct file *filp, unsigned int cmd, unsigned long arg) {switch (cmd) {case Mem_restart://If passed in here is the reboot command PRINTK (kern_warning "Restart device!\n");//Here is a virtual device to simulate return 0;case Mem_set://If the Set parameter command is passed PRINTK (kern_ WARNING "Arg is:%d\n", arg);//print out the passed parameter return 0;default:return-einval;//other printing error}return 0;} </span>/* file operation structure Body */static const struct file_operations mem_fops ={. Llseek = Mem_llseek,. Read = Mem_read,. Write = Mem_write,. Open = Mem_open,. Release = Mem_release, <span style= "color: #ff0000;" >.unlocked_ioctl = mem_ioctl,</span>};/* device driver module load function */static int memdev_init (void) {/* Initialize CDEV structure */Cdev_init (& Amp;cdev, &mem_fops); /* Register character device */alloc_chrdev_region (&DEVNO, 0, 2, "Memdev"); Cdev_Add (&cdev, Devno, 2); PRINTK (kern_warning "Hello memdev!\n"); PRINTK ("Hello memdev!\n");} /* Module unload function */static void memdev_exit (void) {Cdev_del (&cdev); /* Unregister device */Unregister_chrdev_region (DEVNO, 2); /* Release Device number */PRINTK (kern_warning "Exit memdev!\n");} Module_license ("GPL"); Module_init (Memdev_init); Module_exit (Memdev_exit);Memdev.h
#define Mem_magic ' m '//defines a magic number, and the length is exactly the same as the ASC code length of 8 bits, so this defines a character # # Mem_restart _io (mem_magic, 0) //The first command is a reboot command, Reboot command without parameters First command here the ordinal is defined as 0#define mem_set _iow (mem_magic, 1, int)//Set parameter command ordinal 1, type int
Test the application Mem_ctl.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h># Include "Memdev.h" int main () {int fd;fd = open ("/dev/memdev0", O_RDWR);//readable writable Open File ioctl (FD, Mem_set, 115200);//first parameter FD, The second one is the command we want to send, the third is the parameter to be passed to the IOCTL (FD, Mem_restart);//restart Close (FD);
Makefile
Obj-m: = Memdev.okdir: =/home/kernel/linux-ok6410all:make-c $ (kdir) m=$ (PWD) modules cross_compile=arm-linux-arch= Armclean:rm-f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
Here the IOCTL function prototype can view the kernel source Fs.h (/include/linux directory) file
Then this time use static compile mem_ctl.c file Arm-linux-gcc-static-o memctl (here with static compilation above the blog said)
As the entire Rootfs directory is mounted, the files generated here are synced to my development Board
Install the device driver module, and then Cat/proc/device view the device driver's main device number
Then create the device file so that our app can access the device via this device file (of course, our device is a virtual character device, but it's perfectly formed)
Mknod memdev0 C 252 0 (Execute the command)
Memdevo is the name we give to the character device, 252 is the device number of the character device,
Here you can see that the/dev directory will generate memdev0 this device file, run the MEM_CLT application
The control of the IOCTL character device is over!
Control technology of IOCTL---character device