Hi,欢迎来到中国嵌入式培训高端品牌 - 在线365bet<北京总部官网>,专注嵌入式工程师培养13年!
当前位置: > 嵌入式学院 > 嵌入式学习 > 讲师博文 > Linux字符设备驱动模型之字符设备初始化
Linux字符设备驱动模型之字符设备初始化
时间:2017-05-24作者:华清远见

因为Linux字符设备驱动主要依赖于struct cdev结构,原型为:

所以我们需要对所使用到的结构成员进行配置,驱动开发所使用到的结构成员分别为:【unsigned int count;】、【dev_t dev;】、【const struct file_operations *ops;】。

开发流程如下图:

一、字符设备驱动初始化

1、分配设备cdev

Cdev变量的定义可以采用静态和动态两种方法进行分配。静态方法直接分配内存,而动态方法随机分配内存。

静态分配cdev:

定义:struct cdev mdev;

mdev即代表相应的字符设备空间地址。

动态分配cdev:

定义:struct cdev *pdev = cdev_alloc();

pdev即代表对于的字符设备的空间地址。

2、初始化设备cdev

Linux内核中,字符设备struct cdev的初始化使用cdev_init。其原型如下:

参数:

cdev:待初始化的struct cdev结构

fops:设备对应的操作函数集

由原型所要求的参数可知,需要初始化一个字符设备,必须根据申请一个struct cdev结构的空间,然后对其成员进行配置。即实现设备的操作函数集、为设备申请设备号(包括主设备号和次设备号)、指定此类型的字符设备有多少个相同设备。

3、注册设备cdev

Linux内核中字符设备的注册使用cdev_add函数来进行完成注册。其原型如下:

其相关参数定义为:

p:待添加到内核中的字符设备结构,即为struct cdev。

dev:设备号

count:该类设备的设备数量,各个设备的区别体现为从设备号。

在开发驱动时,当确定了字符设备的结构,主设备号和从设备号、设备的数量,就可以使用cdev_add函数将相应的字符设备添加到Linux内核驱动中进行注册。

4、硬件初始化

关于硬件的初始化就简单了。直接根据所需要操作的字符设备,阅读器Datasheet,然后根据Datasheet进行硬件的配置即可。

二、设备操作和驱动操作映射

从上图基本可知,在用户空间的每一种硬件设备操作函数,在内核空间通用有一个映射操作函数实现。

•int (*open) (struct inode *, struct file *):打开设备,响应open系统

•int (*release) (struct inode *, struct file *):关闭设备,响应close系统调用

•loff_t (*llseek) (struct file *, loff_t, int):重定位读写指针,响应lseek系统调用

•ssize_t (*read) (struct file *, char __user *, size_t, loff_t *):从设备读取数据,响应read系统调用

•ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *):向设备写入数据,响应write系统调用

从以上函数指针操作方法中可以提取出两个重要的参数成员,即为struct file和struct inode。

【struct file】:在Linux系统中,每一个打开的文件,在内核中都会对应的关联一个struct file结构体,它由内核在打开文件时创建,在文件关闭后释放。

其非常重要的成员有:

loff_t f_pos /*文件读写指针*/

struct file_operations *f_op /*该文件所对应的操作*/

【struct inode】:每一个存在于文件系统里面的文件都会关联一个inode 结构,该结构主要用来记录文件物理上的信息。因此, 它和代表打开文件的file结构是不同的。一个文件没有被打开时不会关联file结构,但是却会关联一个inode 结构。

在struct inode结构中,dev_t i_rdev尤其重要,表示设备号。

三、struct file_operations结构常用设备操作解析

1.open设备操作

根据原型可知,open设备函数方法是在进行开始启用操作设备时的初始化工作,与用户空间的open函数对应,通常情况下,open函数实现:

(1)表明次设备号

(2)启用设备

当然,如果在启用设备时,不需要任何的准备操作或者初始化操作,那么open函数可以为空函数,不实现。

2.release设备操作

release函数方法的作用正好和open函数方法的作用相反,它在设备关闭时用到,与用户空间的close对应。通常用来进行关闭设备的实现。如果所操作的设备在关闭时不需要其他操作,那么使其为空函数即可。

3.read设备操作

read方法主要完成两件事:

(1)访问硬件操作,从设备中读取数据。

(2)将从设备端读取到的数据返回给用户空间的应用程序read函数。

其read方法的具体原型可分析如下:

ssize_t (*read) (struct file *filp, char __user *buff, size_t count, loff_t *offp)

参数分析:

filp:与字符设备文件关联的file结构指针, 由内核创建。

buff : 从设备读取到的数据,需要保存到的位置。由read系统调用提供该参数。

count: 请求传输的数据量,由read系统调用提供该参数。

offp: 文件的读写位置,由内核从file结构中取出后,传递进来。

那么实际上在这里就存在了一个问题,从内核空间如何将数据返回给用户空间???

在Linux内核中,为从内核空间将数据返回到用户空间提供了函数方法,即为:copy_to_user。其原型如下:

之所以采用copy_to_user函数的原因是,buff参数来源于用户空间的指针,这类指针在内核空间中不能直接被内核代码直接引用,所以必须采用拷贝的方式进行数据的传递。具体的操作过程如下图表示:

4.write设备操作

write函数方法也主要完成两件事:

(1)从应用程序提供的地址中取出数据到内核空间。

(2)访问硬件设备,将数据写入到设备中。

write函数方法直接对应于用户空间的write函数。

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *)

write函数方法的参数基本上和read函数方法类似,在此不再做解释。

那么实际上在此也存在一个问题,如何将数据从用户空间取出到内核空间使用???

实际上Linux内核中也提供了相应的方法函数,将用户空间的数据拷贝到内核空间进行使用,其方法为:copy_from_user。原型如下:

四、设备驱动注销

当内核中不再需要使用一个字符设备时,我们可以在内核中将相应的设备驱动程序进行卸载。那么就需要对其字符设备进行注销。Linux内核中为我们提供了cdev_del函数来完成字符设备的注销。其原型如下:


发表评论

全国咨询电话:400-706-1880,双休日及节假日请致电值班手机:15010390966

在线咨询: 曹老师QQ(619366077), 余老师QQ(2657985593), 李老师QQ(2814652411), 徐老师QQ(1462495461)

企业培训洽谈专线:010-82600901,院校合作洽谈专线:010-82600350,在线咨询:QQ(248856300)

Copyright 2004-2017 华清远见教育集团 版权所有 ,沪ICP备10038863号,京公海网安备110108001117号