设备控制理论
1.设备控制的作用
大部分驱动程序除了需要提供读写设备的能力外,还需要具备控制设备的能力。比如改变波特率。
2.设备控制的函数
在用户空间使用ioctl系统调用函数来控制设备。
int ioclt(int fd, unsigned long cmd, ...)
fd:要控制的设备文件描述符
cmd:发送给设备的命令
...:第三个参数是可选的参数,存在与否是依赖于控制命令
当应用程序使用ioclt系统调用时,驱动程序将由如下函数来响应:
1. 2.6.36以前的内核
long (*ioclt)(struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)
2. 2.6.36以后的内核
long (*unlocked_ioctl)(struct file *filp, unsigned int cmd, unsigned long arg)
参数cmd:通过应用程序ioctl传递下来的命令
设备控制的实现
1.定义命令
命令从其实质而言就是一个整数,但为了让这个整数具备更好的可读性,我们通常会把这个整数划分为几个段:类型(8位),序号,参数传递方向,参数长度
type(类型/幻数):表明这是属于哪个设备的命令。
number(序号):用来区分同一个设备的不同命令。
direction:参数传递的方向,可以是_IOC_NONE(没有数据传输),_IOC_READ(从设备读取数据),_IOC_WRITE(向设备中写入数据)
size:参数长度
Linux系统提供了下面的宏来帮助定义命令:
_IO(type, number):不带参数的命令
_IOR(type, number, datatype):从设备中读取参数的命令
_IOW(type, number, datatype):向设备中写入参数的命令
示例:
#define MEM_MAGIC ‘m’
#define MEM_SET _IOW(MEM_MAGIC, 0, int)
2.操作实现
unlocked_ioclt函数的现实通常是根据命令执行的一个switch语句。但是当命令不能匹配任何一个设备所支持的命令是,返回-EINVAL。
编程模型:
switch(cmd){
case A:
//执行A对应的操作
case B:
//执行B对应的操作
default:
//return -EINVAL
}
memdev.h
//定义命令 #define MEM_MAGIC 'M' //设备幻数 #define MEM_RST _IO(MEM_MAGIC, 0) //重启命令 #define MEM_SET _IOW(MEM_MAGIC, 1, int) //设置命令
memdev.c
#include <linux/init.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <asm/uaccess.h> #include "memdev.h" //全局变量 struct cdev memdev; //字符设备 dev_t devnum; //设备编号 int register0[5]; //设备0寄存器 int register1[5]; //设备1寄存器 //定位设备 loff_t mem_lseek(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)) + offset; break; default: newpos = 0; break; } //移动位置 filp->f_pos = newpos; return newpos; } //读取设备 ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){ //获取地址 int *base; base = filp->private_data + *ppos; //读取数据 copy_to_user(buf, base, size); //移动位置 filp->f_pos += size; return size; } //写入设备 ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos){ //获取地址 int *base; base = filp->private_data + *ppos; //写入数据 copy_from_user(base, buf, size); //移动位置 filp->f_pos += size; return size; }
//控制设备 long mem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){ //处理命令 switch(cmd){ case MEM_RST: //重启命令 printk("reset device\n"); break; case MEM_SET: //设置命令 printk("set arg is %d\n", arg); break; default: //错误命名 return -EINVAL; } return 0; } //打开设备 int mem_open(struct inode *node, struct file *filp){ //获取次设备号 int secnum; secnum = MINOR(node->i_rdev); //设置设备地址 if(secnum == 0){ filp->private_data = register0; } if(secnum == 1){ filp->private_data = register1; } return 0; } //关闭设备 int mem_colse(struct inode *node, struct file *filp){ return 0; } //设备方法 struct file_operations memfops = { .llseek = mem_lseek, .read = mem_read, .write = mem_write, .unlocked_ioctl = mem_ioctl, .open = mem_open, .release = mem_colse }; //驱动注册 static int memdev_init(){ //注册设备结构 cdev_init(&memdev, &memfops); //注册主设备号 alloc_chrdev_region(&devnum, 0, 2, "memdev"); //添加设备结构 cdev_add(&memdev, devnum, 2); return 0; } //驱动注销 static void memdev_exit(){ //注销设备结构 cdev_del(&memdev); //注销主设备号 unregister_chrdev_region(devnum, 2); } //驱动声明 MODULE_LICENSE("GPL"); MODULE_AUTHOR("D"); MODULE_DESCRIPTION("memdev"); MODULE_VERSION("v1.0"); module_init(memdev_init); module_exit(memdev_exit);
contorl.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include "memdev.h" int main(int argc, char **argv){ //打开设备 int fd; fd = open("/dev/memdev0", O_RDWR); //设置设备 ioctl(fd, MEM_SET, 115200); //重启设备 ioctl(fd, MEM_RST); //关闭设备 close(fd); return 0; }