Linux驱动开发入门与实践(一)

来源:互联网 发布:湖南软件职业学院专业 编辑:程序博客网 时间:2024/06/05 14:49

    最近这一段时间,把之前学的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字符结构体。

 

 首先,系统调用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的相应操作.



0 0