驱动开发之字符设备驱动(第七天)

Linux系统中根据驱动程序实现的模拟框架将设备的驱动分为了三大类:

一:字符设备驱动

设备对数据的处理是按照字节流的形式进行的,可以支持随机访问,也可以不支持随机访问,因为数据流量通常不是很大,所以一般没有页高速缓存linux内核实现磁盘缓存。它主要用来减少对磁盘的I/O操作。具体地讲,是通过把磁盘中的数据缓存到物理内存中,把对磁盘的访问变为对物理内存的访问)。典型的字符设备有串口(串行接口)、键盘、帧缓存设备等。以串口为例,串口对收发的数据长度没有具体要求,可以是任意多个字节;串口也不支持lseek操作,即不能定位到一个具体的位置进行读写,因为串口按顺序发送或接受数据;串口的数据通常保存在一个较小的FIFO中,并且不会重复利用FIFO中的数据。帧缓存设备(就是我们通常说的显卡)也是一个字符设备,但它可以进行随机访问,这样我们就能修改某个具体位置的帧缓存数据,从而改变屏幕上的某些确定像素点的颜色。

1.1 字符设备驱动基础

使用如下命令可以看到很多设备文件及其相关的信息  : ls -l /dev

注:设备文件会比普通文件多出两个数字,这两个数字分别是主设备号和次设备号。这两个号是设备在内核中的身份或标志,是内核区分不同设备的唯一信息。通常内核用主设备号区别一类设备,次设备号用于区分同一类设备单不同个体或不同分区。而路径名则是用户层用于区别设备信息的。

mkmod命令(make node):创建了一个节点,所以设备文件有时又叫做设备节点。

在Linux系统中,一个节点代表一个文件,创建一个文件最主要的根本工作就是分配一个新的节点(存在于磁盘上的节点,之后会看到位于内存中的节点inode),包含节点号的分配(节点号在一个文件系统中是唯一的,可以以此来区别不同的文件),然后初始化好这个新节点(包含文件模式、访问时间、用户ID、组ID等元数据信息,如果是设备文件还要初始化好设备号),再将这个初始化好的节点写入磁盘。还需要在文件所在的目录下添加一个目录项,目录项中包含了前面分配的节点号和文件的名字,然后写入磁盘。存在于磁盘上的这个节点用一个结构封装。

1.2字符设备驱动框架

实现一个字符设备驱动,最重要的就是构造一个cdev对象,并让cdev同设备号和设备的操作方法集合相关联,然后将该cdev结构对象添加到内核的cdev_map散列表中

根据inode中的设备号——>找到cdev——>根据cdev——>找到关联的操作方法集合——>调用驱动所提供的操作方法来完成对设备的具体操作

cdev 和file_operations之间的调用关系可用下图来表法

第一步:在驱动中注册设备号

①:静态注册设备号

        静态申请设备号(register_chrdev_region)

int register_chrdev_region(dev_t from, unsigned count, const char *name);
/* 参数:
    dev_t from - 要申请的设备号(起始)
    unsigned count - 要申请的设备号数量
    const char *name - 设备名
   返回值:
    成功:0
    失败:负数(绝对值是错误码)*/

②:动态注册设备号

        动态申请设备号(alloc_chrdev_region)

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
/* 参数:
    dev_t *dev - 用于保存分配到的第一个设备号(起始)
    unsigned baseminor - 起始次设备号
    unsigned count - 要分配设备号的数量
    const char *name - 设备名
   返回值:
    成功:0
    失败:负数(绝对值是错误码)*/

第二步:构造并添加cdev结构对象

①:定义一个struct cdev类型的全局变量vsdev

static struct cdev vsdev;

②:定义一个struct file_operations类型的全局变量vser_ops

static struct file_operations vser_ops = {.owner = THIS_MODULE,};

相关代码如下:

struct file_operations 
{
  struct module *owner;  
    /* 模块拥有者,一般为 THIS——MODULE */
  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  
    /* 从设备中读取数据,成功时返回读取的字节数,出错返回负值(绝对值是错误码) */
  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);   
    /* 向设备发送数据,成功时该函数返回写入字节数。若为被实现,用户调层用write()时系统将返回 -EINVAL*/
  int (*mmap) (struct file *, struct vm_area_struct *);  
    /* 将设备内存映射内核空间进程内存中,若未实现,用户层调用 mmap()系统将返回 -ENODEV */
  long (*unlocked_ioctl)(struct file *filp, unsigned int cmd, unsigned long arg);  
    /* 提供设备相关控制命令(读写设备参数、状态,控制设备进行读写...)的实现,当调用成功时返回一个非负值 */
  int (*open) (struct inode *, struct file *);  
    /* 打开设备 */
  int (*release) (struct inode *, struct file *);  
    /* 关闭设备 */
  int (*flush) (struct file *, fl_owner_t id);  
    /* 刷新设备 */
  loff_t (*llseek) (struct file *, loff_t, int);  
    /* 用来修改文件读写位置,并将新位置返回,出错时返回一个负值 */
  int (*fasync) (int, struct file *, int);  
    /* 通知设备 FASYNC 标志发生变化 */
  unsigned int (*poll) (struct file *, struct poll_table_struct *);  
    /* POLL机制,用于询问设备是否可以被非阻塞地立即读写。当询问的条件未被触发时,用户空间进行select()和poll()系统调用将引起进程阻塞 */
  ...
};

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值