Linux驱动开发入门与实践(一)
2015-04-08 16:04
288 查看
最近这一段时间,把之前学的linux基础的东西捡了回来,正式开始接触驱动了。接触后发现底层的东西还是相当的繁琐,内核里的宏定义数不胜数,结构体,指针更是扑面而来。驱动主要分为三部分,分别是字符设备、块设备以及网络接口设备。这里先总结下简单字符设备驱动相关的东西。
参考资料:1.《Linux驱动开发入门与实践》 郑强
2.《国嵌培训资料以及视频》
参考资料1个人觉得不是很适合拿来上手,虽然讲的点多,但是却不细致。而2讲的点少,但比较细也比较浅显。
字符设备驱动:
字符设备理解起来可以和块设备进行区分理解。
字符=只能一个一个字节读写
块=可以从任意位置读取一定长度数据的设备,不必按照先后顺序。
如果对应上实物的话:字符设备有:鼠标、键盘、串口、控制台···
块设备有:SD卡、硬盘、磁盘、U盘···
Linux具体是怎样去操作设备驱动的?
在Linux系统中,每个字符设备或者块设备都在 /dev目录下有对应一个设备文件。Linux对设备进行操控,本质上就是通过这些文件来操作的。这样相当于有了一套标准,程序员便可以撇开设备的差异化从而按照标准进行编程。
区分一个现成的Linux系统当前的设备文件的属性,只需要cd /dev 然后 ls -l
出现以下条目:
crw-rw----+ 1 root root 14, 12 12-21 22:56 adsp
第一个字符c 就表示char 字符型设备 ,b则表示block 块设备。而5、6字段分别表示主设备号和次设备号。
为什么需要主设备号和次设备号呢?
主设备号用来区分不同类型的设备,如USB设备和串口设备。次设备号则是用来区分某一类型设备中不同的子设备。如串口设备不止一种,所以通过此设备号进行区分。
主设备号和次设备号的表示(具体代码实现)
Linux中用dev_t 类型来表示设备号。其实它本质上就是一个无符号长整型,
typedef u_long dev_t
u_long 在32位机里为4个字节 ,在64位里为8字节。由于自己学的是ARM,以32位机为例。其中高12位表示主设备号,低20位表示次设备号。
设备号的获得与申请方式:
主要有2种,第一种为静态申请,第二种自然就是动态申请。采用的方法不同,前期的基础工作也就不同。
先说说静态申请:静态申请毫无疑问需要程序员自己给分配一个设备号,那么怎样判断一个设备号是否可用呢?或者说当前系统里没有被其他设备占用呢?
方法:可以读取 /proc/devices 文件来获得设备号。
指令如下:cat /proc/devices
知道当前系统占用的设备号后,比如要需要设置的设备号为200,那么怎么构建该设备号呢?
Linux系统采用MKDEV来实现,其中
dev_t devno=MKDEV(ma,mi);其中ma为主设备号,mi为次设备号。
构建完设备号之后,还需要进行申请,静态申请的方法为:
int register_chrdev_region(dev_t from, unsigned count , const char *name); 头文件:<fs/char_dev.c>
from 是要分配设备号范围起始值,count表示需要申请设备号的个数。 name则是设备名称,注意不能超过64字节。
动态分配:静态分配由于人为因素,很可能导致冲突,所以Linux自己给自动分配一个未使用的设备号则更加有利。
动态申请不需要自己构建设备号,调用函数:
int alloc_chrdev_region(&dev_t *dev,unsigned baseminor,unsigned count, const char *name)
成功后返回的设备号保存在dev指向的dev_t 类型的变量里。baseminor 为次设备号的起始号,count为子设备数 name为设备名称。
申请完后,要将字符设备注册到系统中,才能使用。
cdev 用来描述字符设备。
struct cdev {
struct kobject kobj;
struct module *owner;/*指向包含该结构的模块的指针,用于引用计数*/
const struct file_operations *ops;/*指向字符设备操作函数集的指针*/
struct list_head list; /*该结构将使用该驱动的字符设备连成一个链表*/
dev_t dev; /*该字符设备的起始设备号*/
unsigned int count;/*使用该字符设备驱动的设备数量*/
};
kobj结构用于内核管理字符设备,驱动开发人员一般不使用。
ops是指向file_operations 操作函数结构体指针。
list为双向链表,用于将其他结构体连接成一个双向链表,其连接到inode结构体i_devices成员。而i_devices也是一个list_head结构。这样使得cdev结构与inode结点组成一个双向链表。
文件系统中对字符设备文件的访问
对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev, 如果i_cdev为 NULL ,则说明该设备文件没有被打开.
由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个双向链表。inode表示/dev下的设备文件。每一个字符设备在/dev下都有一个设备文件,打开设备文件就相当于打开相应的字符设备。例如应用程序打开设备文件A,那么系统就会产生一个inode结点,这样可以通过inode结点的i_cdev字段找到cdev字符结构体。
![](http://hi.csdn.net/attachment/201103/16/0_1300268876in3w.gif)
首先,系统调用open打开一个字符设备的时候, 通过一系列调用,最终会执行到 chrdev_open.
(最终是通过调用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 这一系列的调用过程,本文暂不讨论)
int chrdev_open(struct inode * inode, struct file * filp)
chrdev_open()所做的事情可以概括如下:
1. 根据设备号(inode->i_rdev), 在字符设备驱动模型中查找对应的驱动程序, 这通过kobj_lookup() 来实现, kobj_lookup()会返回对应驱动程序cdev的kobject.
2. 设置inode->i_cdev , 指向找到的cdev.
3. 将inode添加到cdev->list的链表中.
4. 使用cdev的ops 设置file对象的f_op
5. 如果ops中定义了open方法,则调用该open方法
6. 返回.
执行完 chrdev_open()之后,file对象的f_op指向cdev的ops,因而之后对设备进行的read, write等操作,就会执行cdev的相应操作.
参考资料:1.《Linux驱动开发入门与实践》 郑强
2.《国嵌培训资料以及视频》
参考资料1个人觉得不是很适合拿来上手,虽然讲的点多,但是却不细致。而2讲的点少,但比较细也比较浅显。
字符设备驱动:
字符设备理解起来可以和块设备进行区分理解。
字符=只能一个一个字节读写
块=可以从任意位置读取一定长度数据的设备,不必按照先后顺序。
如果对应上实物的话:字符设备有:鼠标、键盘、串口、控制台···
块设备有:SD卡、硬盘、磁盘、U盘···
Linux具体是怎样去操作设备驱动的?
在Linux系统中,每个字符设备或者块设备都在 /dev目录下有对应一个设备文件。Linux对设备进行操控,本质上就是通过这些文件来操作的。这样相当于有了一套标准,程序员便可以撇开设备的差异化从而按照标准进行编程。
区分一个现成的Linux系统当前的设备文件的属性,只需要cd /dev 然后 ls -l
出现以下条目:
crw-rw----+ 1 root root 14, 12 12-21 22:56 adsp
第一个字符c 就表示char 字符型设备 ,b则表示block 块设备。而5、6字段分别表示主设备号和次设备号。
为什么需要主设备号和次设备号呢?
主设备号用来区分不同类型的设备,如USB设备和串口设备。次设备号则是用来区分某一类型设备中不同的子设备。如串口设备不止一种,所以通过此设备号进行区分。
主设备号和次设备号的表示(具体代码实现)
Linux中用dev_t 类型来表示设备号。其实它本质上就是一个无符号长整型,
typedef u_long dev_t
u_long 在32位机里为4个字节 ,在64位里为8字节。由于自己学的是ARM,以32位机为例。其中高12位表示主设备号,低20位表示次设备号。
设备号的获得与申请方式:
主要有2种,第一种为静态申请,第二种自然就是动态申请。采用的方法不同,前期的基础工作也就不同。
先说说静态申请:静态申请毫无疑问需要程序员自己给分配一个设备号,那么怎样判断一个设备号是否可用呢?或者说当前系统里没有被其他设备占用呢?
方法:可以读取 /proc/devices 文件来获得设备号。
指令如下:cat /proc/devices
知道当前系统占用的设备号后,比如要需要设置的设备号为200,那么怎么构建该设备号呢?
Linux系统采用MKDEV来实现,其中
dev_t devno=MKDEV(ma,mi);其中ma为主设备号,mi为次设备号。
构建完设备号之后,还需要进行申请,静态申请的方法为:
int register_chrdev_region(dev_t from, unsigned count , const char *name); 头文件:<fs/char_dev.c>
from 是要分配设备号范围起始值,count表示需要申请设备号的个数。 name则是设备名称,注意不能超过64字节。
动态分配:静态分配由于人为因素,很可能导致冲突,所以Linux自己给自动分配一个未使用的设备号则更加有利。
动态申请不需要自己构建设备号,调用函数:
int alloc_chrdev_region(&dev_t *dev,unsigned baseminor,unsigned count, const char *name)
成功后返回的设备号保存在dev指向的dev_t 类型的变量里。baseminor 为次设备号的起始号,count为子设备数 name为设备名称。
申请完后,要将字符设备注册到系统中,才能使用。
cdev 用来描述字符设备。
struct cdev {
struct kobject kobj;
struct module *owner;/*指向包含该结构的模块的指针,用于引用计数*/
const struct file_operations *ops;/*指向字符设备操作函数集的指针*/
struct list_head list; /*该结构将使用该驱动的字符设备连成一个链表*/
dev_t dev; /*该字符设备的起始设备号*/
unsigned int count;/*使用该字符设备驱动的设备数量*/
};
kobj结构用于内核管理字符设备,驱动开发人员一般不使用。
ops是指向file_operations 操作函数结构体指针。
list为双向链表,用于将其他结构体连接成一个双向链表,其连接到inode结构体i_devices成员。而i_devices也是一个list_head结构。这样使得cdev结构与inode结点组成一个双向链表。
文件系统中对字符设备文件的访问
对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev, 如果i_cdev为 NULL ,则说明该设备文件没有被打开.
由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个双向链表。inode表示/dev下的设备文件。每一个字符设备在/dev下都有一个设备文件,打开设备文件就相当于打开相应的字符设备。例如应用程序打开设备文件A,那么系统就会产生一个inode结点,这样可以通过inode结点的i_cdev字段找到cdev字符结构体。
![](http://hi.csdn.net/attachment/201103/16/0_1300268876in3w.gif)
首先,系统调用open打开一个字符设备的时候, 通过一系列调用,最终会执行到 chrdev_open.
(最终是通过调用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 这一系列的调用过程,本文暂不讨论)
int chrdev_open(struct inode * inode, struct file * filp)
chrdev_open()所做的事情可以概括如下:
1. 根据设备号(inode->i_rdev), 在字符设备驱动模型中查找对应的驱动程序, 这通过kobj_lookup() 来实现, kobj_lookup()会返回对应驱动程序cdev的kobject.
2. 设置inode->i_cdev , 指向找到的cdev.
3. 将inode添加到cdev->list的链表中.
4. 使用cdev的ops 设置file对象的f_op
5. 如果ops中定义了open方法,则调用该open方法
6. 返回.
执行完 chrdev_open()之后,file对象的f_op指向cdev的ops,因而之后对设备进行的read, write等操作,就会执行cdev的相应操作.
相关文章推荐
- TCP/IP 动手实践1-windows驱动开发入门
- linux2.6 驱动开发入门:第一个驱动程序
- linux驱动开发重点关注内容--摘自《嵌入式Linux驱动模板精讲与项目实践》
- Linux环境下字符设备驱动开发入门
- linux驱动开发内核模块编译——Makefile入门
- Hello的驱动示例-linux驱动开发入门
- 测试驱动开发实践-入门篇
- Linux驱动开发入门——基本知识简介
- Linux USB Host 驱动开发入门
- linux2.6驱动开发入门:常见问题
- linux设备驱动开发范例,linux驱动例子,Linux设备驱动详解范例example 宋宝华版的光盘驱动源码,实测编译通过入门学习用
- Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建
- linux驱动开发之framebuffer应用编程实践(一)
- 测试驱动开发实践——入门篇
- linux2.6驱动开发入门:基本流程
- 最大化利用内核资源进行Linux驱动开发--摘自《嵌入式Linux驱动模板精讲与项目实践》
- linux驱动开发重点关注内容--摘自《嵌入式Linux驱动模板精讲与项目实践》
- Linux驱动开发1-内核入门之hello模块
- [ARM-LInux开发]linux设备驱动makefile入门解析
- Linux 驱动开发之内核模块开发 (二)—— 内核模块编译 Makefile 入门