linux内核源码分析之设备驱动
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核源码分析之设备驱动相关的知识,希望对你有一定的参考价值。
目录
一、I/O 体系结构
与外设的通信通常称之为 输入输出 ,一般都缩写为 I/O 实现外设的 I/O 时,内核必须处理 3 个可能出现问题的领域- 必须根据具体的设备类型和模型,使用各种方法对硬件寻址
- 内核必须向用户应用程序和系统工具提供访问各种设备的方法
- 用户空间需要知道内核中有哪些设备可用
二、与外设的交互
1、I/O端口:
在这种情况下,内核发送数据给 I/O 控制器。数据的目标设备通过唯一的端口号标识,数据被传输到设备进行处理。
每种处理器类型实现端口访问的方式都不同。因此,内核必须提供一个适当的抽象层。诸如outb (写一个字节)、outw (写一个字)、 inb (读取一个字节)之类的命令2、I/O内存映射
处理器提供了对I/O 端口进行内存 映射的选项,将特定外设的端口地址映射到普通内存中,可以像处理普通内存那样操作外设。诸如 PCI 之类的系统总线通常也是通过 I/O 地址映射进行寻址的。 内核提供了一个小的抽象层,主 要包括ioremap 和 iounmap 命令3、轮询和中断
轮询( polling )重复询问设备数据是否可用,如果可用,则处理器取回数据。 这样做比较浪费资源,从而会影响重要 任务的执行。 中断是更好的备选方案。每个 CPU 都提供了 中断线 ( interrupt line),可由各个系统设备共享。每个中断通过一个唯一的号码标识,内核对使用的 系统就不再需要频繁检查是否有新的数据可用。三、驱动分类
1、字符设备、块设备和其他设备wolfgang@meitner> ls -l /dev/sda,b /dev/ttyS0,1
brw-r-----1 root disk 8, 0 2008-02-21 21:06 /dev/sda
brw-r-----1 root disk 8, 16 2008-02-21 21:06 /dev/sdb
crw-rw----1 root uucp 4, 64 2007-09-21 21:12 ttyS0
crw-rw----1 root uucp 4, 65 2007-09-21 21:12 ttyS1
- 访问权限前的字母是b或c,分别表示块设备和字符设备
- 设备文件没有文件长度,而增加了另外的两个值,分别是主设备号和从设备号。
四、注册
1、设备数据库 每个字符设备都表示为struct cdev的一个实例。struct kobj_map
struct probe
struct probe *next;
dev_t dev;//设备号
unsigned long range;//设备号的范围
struct module *owner;//提供设备驱动程序的模块
kobj_probe_t *get;//指向可以返回与设备关联的kobject实例
int (*lock)(dev_t, void *);
void *data;//字符设备指向 struct cdev实例;块设备 指向struct genhd实例
*probes[255];
struct mutex *lock;
;
2、字符设备范围数据库
管理为驱动程序分配的设备号范围,确保指定的范围不与现存的范围重叠。
static struct char_device_struct
struct char_device_struct *next;
unsigned int major;//主设备号
unsigned int baseminor;//baseminor是包含minorct个从设备号的连续范围中最小的从设备号
int minorct;
char name[64];//标识
struct cdev *cdev; /* will die */
*chrdevs[CHRDEV_MAJOR_HASH_SIZE];
3、设备号
1)动态静态设备号申请
int register_chrdev_region(dev_t from, unsigned count, const char *name)
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name);
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
在获取了设备号范围之后,需要将设备添加到字符设备数据库,以激活设备。在cdev_add成功返回后,设备进入活动状态。
2)动态创建设备文件
每当内核检测到一个设备时,都会创建一个内核对象kobject
。该对象借助于
sysfs 文件系统导出到用户层
。内核还向用户空间发送一个热插拔消息。
udevd是一个守护进程,允许从用户层动态创建设备文件。
如果在系统启动期间发现新设备,或在运行期间有新设备接入(如USB
存储棒),内核产生的热 插拔消息包含了驱动程序为设备分配的主从设备号。udevd
守护进程所需完成的所有工作,就是监听 这些消息。在注册新设备时,会在/dev
中创建对应的项,接下来就可以从用户层访问该设备了。
五、与文件系统关联
1、 inode中的设备文件成员
虚拟文件系统中的每个文件都关联到一个inode,用于管理文件属性。inode与驱动程序有关的成员如下:
struct inode
...
dev_t i_rdev; //标识与一个设备文件关联的设备
...
umode_t i_mode;
...
struct file_operations *i_fop; //一组函数指针的集合
...
union
...
struct block_device *i_bdev;
struct cdev *i_cdev;
;
...
;
2,标准文件操作
在打开一个设备文件时,各种文件系统的实现会调用init_special_inode函数,为块设备或字 符设备文件创建一个inode。void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
inode->i_mode = mode;
if (S_ISCHR(mode))
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
else if (S_ISBLK(mode))
inode->i_fop = &def_blk_fops;
inode->i_rdev = rdev;
else if (S_ISFIFO(mode))
inode->i_fop = &pipefifo_fops;
else if (S_ISSOCK(mode))
; /* leave it no_open_fops */
else
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
" inode %s:%lu\\n", mode, inode->i_sb->s_id,
inode->i_ino);
EXPORT_SYMBOL(init_special_inode);
3,字符设备的标准操作
struct file_operations def_chr_fops =
.open = chrdev_open,
;
chrdev_open函数的主要任务就是向该结构填入适用于已打开设备的函数指针, 使得能够在设备文件上执行有意义的操作,并最终能够操作设备自身。
4、字符设备的表示
struct cdev
struct kobject kobj; //一个嵌入在该结构中的内核对象,它用于该数据结构的一般管理
struct module *owner; //指向驱动程序的模块
const struct file_operations *ops; //硬件通信的具体操作
struct list_head list;
dev_t dev; //设备号
unsigned int count;
;
六、资源管理
为使得各种不同的驱动程序彼此互不干扰,有必要事先为驱动程序分配端口和I/O内存范围。这确保几种设备驱动程序不会试图访问同样的资源。struct resource
resource_size_t start; //start和end类型为unsigned long,指定了一个一般性的
区域
resource_size_t end;
const char *name;
unsigned long flags; //用于更准确地描述资源及其当前状态
struct resource *parent, *sibling, *child; //父子兄弟之间关系
;
资源分配它连续地扫描现存的资源,将新资源添加到正确的位置,或发现与已经分配区域的冲突。完成所述工作,需要遍历兄弟结点的链表。如果 所需的资源区域是空闲的,则插入新的resource实例,这样就完成了资源的分配。如果该区域不是空 闲的,则分配失败。
//资源请求
static struct resource * request_resource(struct resource *root,
struct resource *new);
//资源释放
void release_resource(struct resource *old)
七、结构体总结
上述介绍的结构体在内核中的使用如下
内核启动start_kernel到kobj_map注册
(内核免费课程链接:https://ke.qq.com/course/4032547?flowToken=1042391)
以上是关于linux内核源码分析之设备驱动的主要内容,如果未能解决你的问题,请参考以下文章