字符设备驱动 架构分析
2009-12-28 21:37
381 查看
转自 http://linux.chinaunix.net/bbs/viewthread.php?tid=1027719 xpl
的文章:
好长时间没怎么看书了,最近把字符设备驱动部分又复习了一下,写个笔记.
Char Device Driver
相关数据结构:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct file_operations *fops;
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
#define CHRDEV_MAJOR_HASH_SIZE 255
下面本文通过一下三个方面以及他们的关联来描述字符设备驱动:
1. 字符驱动模型
2. 字符设备的设备号
3. 文件系统中对字符设备文件的访问
1. 字符驱动模型
每个字符驱动由一个 cdev 结构来表示.
在设备驱动模型(device driver model)中, 使用 (kobject mapping domain) 来记录字符设备驱动.
这是由 struct kobj_map 结构来表示的. 它内嵌了255个struct probe指针数组
kobj_map由全局变量 cdev_map 引用: static struct kobj_map *cdev_map;
相关函数说明:
cdev_alloc() 用来创建一个cdev的对象
cdev_add() 用来将cdev对象添加到驱动模型中,其主要是通过kobj_map()来实现的.
kobj_map() 会创建一个probe对象,然后将其插入cdev_map中的某一项中,并关联probe->data 指向 cdev
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
根据设备号,在cdev_map中查找其cdev对象内嵌的kobject. (probe->data->kobj),返回的是cdev的kobject
2. 字符设备的设备号
字符设备的主,次设备号的分配:
全局数组 chrdevs 包含了255(CHRDEV_MAJOR_HASH_SIZE 的值)个 struct char_device_struct的元素.
每一个对应一个相应的主设备号.
如果分配了一个设备号,就会创建一个 struct char_device_struct 的对象,并将其添加到 chrdevs 中.
这样,通过chrdevs数组,我们就可以知道分配了哪些设备号.
相关函数:
register_chrdev_region( ) 分配指定的设备号范围
alloc_chrdev_region( ) 动态分配设备范围
他们都主要是通过调用函数__register_chrdev_region() 来实现的
要注意,这两个函数仅仅是注册设备号! 如果要和cdev关联起来,还要调用cdev_add()
register_chrdev( ) 申请指定的设备号,并且将其注册到字符设备驱动模型中.
它所做的事情为:
1. 注册设备号, 通过调用 __register_chrdev_region() 来实现
2. 分配一个cdev, 通过调用 cdev_alloc() 来实现
3. 将cdev添加到驱动模型中, 这一步将设备号和驱动关联了起来. 通过调用 cdev_add() 来实现
4. 将第一步中创建的 struct char_device_struct 对象的 cdev 指向第二步中分配的cdev. 由于register_chrdev()是老的接口,这一步在新的接口中并不需要.
3. 文件系统中对字符设备文件的访问
对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev, 如果i_cdev为 NULL ,则说明该设备文件没有被打开.
由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个链表
首先,系统调用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的相应操作.
------------------------------------------------------------------------------------------------------------
这篇文章里好多东西看不懂。不过上面红色那一行解决了我的一个疑问:
ldd3 p58讲:
struct file_operations *f_op 是 struct file 里的一个重要结构。
struct file_operations *f_op
与文件相关的操作,内核在执行open操作时对这个指针赋值
,以后需要处理这些操作时就读取这个指针。
这样就搞明白了用户定义的file_operations 结构如何和file结果里的file_operations关联起来的。 用户设置了cdev里的file_operations后,以后打开设备时,内核自动把cdev里的file_operations结构赋值给file对象里的file_operations结构。
至于上面说的第四步,在内核里找到了相关的代码:
linux/fs/char_dev.c
[/code]
的文章:
好长时间没怎么看书了,最近把字符设备驱动部分又复习了一下,写个笔记.
Char Device Driver
相关数据结构:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct file_operations *fops;
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
#define CHRDEV_MAJOR_HASH_SIZE 255
下面本文通过一下三个方面以及他们的关联来描述字符设备驱动:
1. 字符驱动模型
2. 字符设备的设备号
3. 文件系统中对字符设备文件的访问
1. 字符驱动模型
每个字符驱动由一个 cdev 结构来表示.
在设备驱动模型(device driver model)中, 使用 (kobject mapping domain) 来记录字符设备驱动.
这是由 struct kobj_map 结构来表示的. 它内嵌了255个struct probe指针数组
kobj_map由全局变量 cdev_map 引用: static struct kobj_map *cdev_map;
相关函数说明:
cdev_alloc() 用来创建一个cdev的对象
cdev_add() 用来将cdev对象添加到驱动模型中,其主要是通过kobj_map()来实现的.
kobj_map() 会创建一个probe对象,然后将其插入cdev_map中的某一项中,并关联probe->data 指向 cdev
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
根据设备号,在cdev_map中查找其cdev对象内嵌的kobject. (probe->data->kobj),返回的是cdev的kobject
2. 字符设备的设备号
字符设备的主,次设备号的分配:
全局数组 chrdevs 包含了255(CHRDEV_MAJOR_HASH_SIZE 的值)个 struct char_device_struct的元素.
每一个对应一个相应的主设备号.
如果分配了一个设备号,就会创建一个 struct char_device_struct 的对象,并将其添加到 chrdevs 中.
这样,通过chrdevs数组,我们就可以知道分配了哪些设备号.
相关函数:
register_chrdev_region( ) 分配指定的设备号范围
alloc_chrdev_region( ) 动态分配设备范围
他们都主要是通过调用函数__register_chrdev_region() 来实现的
要注意,这两个函数仅仅是注册设备号! 如果要和cdev关联起来,还要调用cdev_add()
register_chrdev( ) 申请指定的设备号,并且将其注册到字符设备驱动模型中.
它所做的事情为:
1. 注册设备号, 通过调用 __register_chrdev_region() 来实现
2. 分配一个cdev, 通过调用 cdev_alloc() 来实现
3. 将cdev添加到驱动模型中, 这一步将设备号和驱动关联了起来. 通过调用 cdev_add() 来实现
4. 将第一步中创建的 struct char_device_struct 对象的 cdev 指向第二步中分配的cdev. 由于register_chrdev()是老的接口,这一步在新的接口中并不需要.
3. 文件系统中对字符设备文件的访问
对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev, 如果i_cdev为 NULL ,则说明该设备文件没有被打开.
由于多个设备可以共用同一个驱动程序.所以,通过字符设备的inode 中的i_devices 和 cdev中的list组成一个链表
首先,系统调用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的相应操作.
------------------------------------------------------------------------------------------------------------
这篇文章里好多东西看不懂。不过上面红色那一行解决了我的一个疑问:
ldd3 p58讲:
struct file_operations *f_op 是 struct file 里的一个重要结构。
struct file_operations *f_op
与文件相关的操作,内核在执行open操作时对这个指针赋值
,以后需要处理这些操作时就读取这个指针。
这样就搞明白了用户定义的file_operations 结构如何和file结果里的file_operations关联起来的。 用户设置了cdev里的file_operations后,以后打开设备时,内核自动把cdev里的file_operations结构赋值给file对象里的file_operations结构。
至于上面说的第四步,在内核里找到了相关的代码:
linux/fs/char_dev.c
254 int chrdev_open(struct inode * inode, struct file * filp) 255 { 256 struct cdev *p; 257 struct cdev *new = NULL; 258 int ret = 0; 259 260 spin_lock(&cdev_lock); 261 p = inode->i_cdev; 262 if (!p) { 263 struct kobject *kobj; 264 int idx; 265 spin_unlock(&cdev_lock); 266 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); 267 if (!kobj) 268 return -ENXIO; 269 new = container_of(kobj, struct cdev, kobj); 270 spin_lock(&cdev_lock); 271 p = inode->i_cdev; 272 if (!p) { 273 inode->i_cdev = p = new; 274 inode->i_cindex = idx; 275 list_add(&inode->i_devices, &p->list); 276 new = NULL; 277 } else if (!cdev_get(p)) 278 ret = -ENXIO; 279 } else if (!cdev_get(p)) 280 ret = -ENXIO; 281 spin_unlock(&cdev_lock); 282 cdev_put(new); 283 if (ret) 284 return ret; 285 filp->f_op = fops_get(p->ops); 286 if (!filp->f_op) { 287 cdev_put(p); 288 return -ENXIO; 289 } 290 if (filp->f_op->open) { 291 lock_kernel(); 292 ret = filp->f_op->open(inode,filp); 293 unlock_kernel(); 294 } 295 if (ret) 296 cdev_put(p); 297 return ret; 298 }
[/code]
相关文章推荐
- 字符设备驱动 架构分析
- 字符设备驱动 架构分析
- 字符设备驱动 架构分析
- 字符设备驱动 架构分析
- 字符设备驱动 架构分析
- 字符设备驱动 架构分析
- 字符设备驱动 架构分析
- 字符设备驱动 架构分析
- [转载]Linux设备驱动之I2C架构分析 - linux设备驱动 - Linux内核学习
- linux设备模型之uart驱动架构分析
- Linux设备模型之tty驱动架构分析
- 字符设备驱动之笔记-misc设备驱动分析
- 11-S3C2440驱动学习(七)嵌入式linux-字符设备的另一种写法及RTC驱动程序分析和字符设备驱动框架总结
- Linux设备模型之tty驱动架构分析
- Linux设备模型之tty驱动架构分析 .
- Linux设备模型之tty驱动架构分析
- linux设备模型之uart驱动架构分析
- 设备驱动学习之字符设备驱动内核代码分析(一)——设备号申请接口
- linux设备模型之uart驱动架构分析
- 字符设备驱动代码完整分析