字符设备控制

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 }
mem_ctl

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.h

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);
memdev.c
原文地址:https://www.cnblogs.com/boyiliushui/p/6665178.html