1.应用函数
在用户空间,使用ioctl系统调用来控制设备,原型如下:
int ioctl(int fd,unsigned long cmd,...)
fd: 要控制的设备文件描述符
fd: 要控制的设备文件描述符
cmd: 发送给设备的控制命令
…: 第3个参数是可选的参数,存在与否是依赖于控制命令(第 2 个参数 )。
2.驱动函数
当应用程序使用ioctl系统调用时,驱动程序将由如下函数来响应:
- 2.6.36 之前的内核
long (ioctl) (struct inode node ,struct file* filp, unsigned int cmd,unsigned long arg) - 2.6.36之后的内核
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg)
参数cmd: 通过应用函数ioctl传递下来的命令
3.控制操作
3.1 定义命令
命令从其实质而言就是一个整数, 但为了让这个整数具备更好的可读性,我们通常会把这个整数分为几个段:类型(8位),序号,参数传送方向,参数长度。
- Type(类型/幻数): 表明这是属于哪个设备的命令。
- Number(序号),用来区分同一设备的不同命令。
- Direction:参数传送的方向,可能的值是 _IOC_NONE(没有数据传输),_IOC_READ, _IOC_WRITE(向设备写入参数)
- Size: 参数长度
此外,Linux系统提供了下面的宏来帮助定义命令: - _IO(type,nr):不带参数的命令
- _IOR(type,nr,datatype):从设备中读参数的命令
- _IOW(type,nr,datatype):向设备写入参数的命令
eg.
#define MEM_MAGIC ‘m’ //定义幻数
#define MEM_SET _IOW(MEM_MAGIC, 0, int)
3.2实现操作
unlocked_ioctl函数的实现通常是根据命令执行的一个switch语句。但是,当命令号不能匹配任何一个设备所支持的命令时,返回-EINVAL.
编程模型:
Switch cmd
Case 命令A:
//执行A对应的操作
Case 命令B:
//执行B对应的操作
Default:
// return -EINVAL
4.代码
mem_ctl.c
1 #include<sys/types.h> 2 #include<sys/stat.h> 3 #include<fcntl.h> 4 #include<sys/ioctl.h> 5 #include "memdev.h" 6 7 int main() 8 { 9 int fd; 10 11 fd = open("/dev/memdev0", O_RDWR);//可读可写打开文件 12 13 ioctl(fd, MEM_SET, 115200);//第一个参数fd,第二个是我们要发送的命令,第三个是要传入的参数 14 15 ioctl(fd, MEM_RESTART);//重启 16 17 close(fd); 18 }
memdev.h
1 #ifndef MEM_DEV_H 2 #define MEM_DEV_H 3 4 #define MEM_MAGIC 'm' //定义一个幻数,而长度正好和ASC码长度一样为8位,所以这里定义个字符 5 #define MEM_RESTART _IO(MEM_MAGIC, 0) //第一个命令是重启的命令,重启的命令不带参数 第一命令这里序号定义成0 6 #define MEM_SET _IOW(MEM_MAGIC, 1, int) //设置参数命令,第2个命令,类型为int 7 8 9 10 #endif
memdev.c
1 #include <linux/module.h> 2 #include <linux/types.h> 3 #include <linux/fs.h> 4 #include <linux/errno.h> 5 #include <linux/init.h> 6 #include <linux/cdev.h> 7 #include <asm/uaccess.h> 8 #include <linux/slab.h> 9 #include "memdev.h" 10 11 12 int dev1_registers[5]; 13 int dev2_registers[5]; 14 15 struct cdev mdev; 16 dev_t devno; 17 18 static int mem_open(struct inode *inode, struct file *filep) 19 { 20 int num = MINOR(inode->i_rdev);//Secondary equipment number 21 22 if (num==0) 23 filep->private_data = dev1_registers; 24 else if(num == 1) 25 filep->private_data = dev2_registers; 26 else 27 return -ENODEV; 28 29 return 0; 30 31 } 32 33 static int mem_release(struct inode *inode, struct file *filep) 34 { 35 return 0; 36 } 37 38 static loff_t mem_llseek(struct file *filep, loff_t offset, int base_addr ) 39 { 40 loff_t newpos; 41 42 switch(base_addr) { 43 case SEEK_SET: 44 newpos = offset; 45 break; 46 47 case SEEK_CUR: 48 newpos = filep->f_pos + offset; 49 break; 50 51 case SEEK_END: 52 newpos = 5*sizeof(int)-1 + offset; 53 break; 54 55 default: 56 return -EINVAL; 57 } 58 if((newpos<0) || (newpos>5*sizeof(int))) 59 return -EINVAL; 60 61 filep->f_pos = newpos; 62 return newpos; 63 64 } 65 66 static ssize_t mem_read(struct file *filep, char __user *buf, size_t size, loff_t *ppos) 67 { 68 unsigned long p = *ppos; 69 unsigned int count = size; 70 int ret = 0; 71 int *register_addr = filep->private_data; 72 73 if(p >= 5*sizeof(int)) 74 return 0; 75 if(count > 5*sizeof(int) - p) 76 count = 5*sizeof(int) - p; 77 78 if (copy_to_user(buf, register_addr+p, count)) 79 { 80 ret = -EFAULT; 81 } 82 else 83 { 84 ppos += count; 85 ret = count; 86 } 87 return ret; 88 } 89 90 static ssize_t mem_write(struct file *filep, const char __user *buf, size_t size, loff_t *ppos) 91 { 92 unsigned long p = *ppos; 93 unsigned int count = size; 94 int ret = 0; 95 int *register_addr = filep->private_data; 96 97 if (p >= 5*sizeof(int)) 98 return 0; 99 if (count > 5*sizeof(int) - p) 100 count = 5*sizeof(int) - p; 101 102 if (copy_from_user(register_addr + p, buf, count)) 103 ret = -EFAULT; 104 else 105 { 106 *ppos += count; 107 ret = count; 108 } 109 return ret; 110 } 111 112 static long mem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 113 { 114 115 switch(cmd) 116 { 117 case MEM_RESTART: //传入的是重启的命令 118 printk(KERN_WARNING"restart device! ");//这里通过虚拟设备来模拟 119 return 0; 120 case MEM_SET: //传入的是设置参数命令 121 printk(KERN_WARNING"arg is : %d ", arg);//打印出传入的参数 122 return 0; 123 default: 124 return -EINVAL;//其他的打印错误 125 126 } 127 128 return 0; 129 } 130 131 static const struct file_operations mem_fops = 132 { 133 .llseek = mem_llseek, 134 .read = mem_read, 135 .write = mem_write, 136 .open = mem_open, 137 .release = mem_release, 138 .unlocked_ioctl = mem_ioctl 139 }; 140 141 static int memdev_init(void) 142 { 143 cdev_init(&mdev,&mem_fops); 144 alloc_chrdev_region(&devno, 0, 2, "memdev"); 145 cdev_add(&mdev,devno,2); 146 147 return 0; 148 } 149 150 static void memdev_exit(void) 151 { 152 cdev_del(&mdev); 153 unregister_chrdev_region(devno, 2); 154 } 155 156 module_init(memdev_init); 157 module_exit(memdev_exit);