[国嵌攻略][116][字符设备控制技术]

Posted 盛夏夜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[国嵌攻略][116][字符设备控制技术]相关的知识,希望对你有一定的参考价值。

设备控制理论

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

 

以上是关于[国嵌攻略][116][字符设备控制技术]的主要内容,如果未能解决你的问题,请参考以下文章

[国嵌攻略][155][I2C用户态驱动设计]

[国嵌攻略][117][LED驱动程序设计]

[国嵌攻略][147][简单块设备驱动设计]

[国嵌攻略][162][USB协议分析]

[国嵌攻略][156][I2C自编设备驱动设计]

[国嵌攻略][133][网卡驱动架构分析]