#ifndef _LINUX_CDEV_H
#define _LINUX_CDEV_H
#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>
struct file_operations;
struct inode;
struct module;
struct cdev {
struct kobject kobj;//内嵌的kobject对象
struct module *owner;//所属模块
const struct file_operations *ops;//文件操作结构体,代表一个打开的文件,它由内核在open时创建,并传递给在该文件进行操作的所有函数。
struct list_head list;
dev_t dev;//设备号,长度为32位,其中高12为主设备号,低20位为次设备号
unsigned int count;
};
void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);
void cd_forget(struct inode *);
extern struct backing_dev_info directly_mappable_cdev_bdi;
#endif
可以使用下列宏从dev_t中获得主次设备号:MAJOR(dev_t dev); MINOR(dev_t dev);
也可以使用下列宏通过主次设备号生成dev_t:MKDEV(int major,int minor);
struct node结构在内部表示文件,和file结构不同,后者表示打开的文件描述符。对于单个文件,可能会有很多表示打开的文件描述符的file结构,但它们都指向单个inode结构。node里边和我们驱动程序有用的字段只有两个:
dev_t i_rdev; //对表示设备文件的inode结构,该字段包含了真正的设备编号
struct cdev *i_cdev; //是表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向struct cdev结构的指针。
可以使用下边两个宏从inode中获得主设备号和此设备号:
(1)内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里,散列表中的每一个元素是一个 char_device_struct 结构;
(2)register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev() 三个函数都会调用一个共用的 __register_chrdev_region() 函数来注册一组设备编号范围(即一个 char_device_struct 结构);
(3)使用他们注册字符设备都需要手动创建设备节点。
可以使用下边两个宏从inode中获得主设备号和此设备号:
unsigned int iminor(struct inode *inode); unsigned int imajor(struct inode *inode);
(1)内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里,散列表中的每一个元素是一个 char_device_struct 结构;
(2)register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev() 三个函数都会调用一个共用的 __register_chrdev_region() 函数来注册一组设备编号范围(即一个 char_device_struct 结构);
(3)使用他们注册字符设备都需要手动创建设备节点。
542 void cdev_init(struct cdev *cdev, const struct file_operations *fops)
543 {
544 memset(cdev, 0, sizeof *cdev);
545 INIT_LIST_HEAD(&cdev->list);
546 kobject_init(&cdev->kobj, &ktype_cdev_default);
547 cdev->ops = fops;
548 }
472 int cdev_add(struct cdev *p, dev_t dev, unsigned count)
473 {
474 p->dev = dev;
475 p->count = count;
/*内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。
kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,
通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。*/
476 return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
477 }
/*自动创建设备文件*/
myclass = class_create(THIS_MODULE,"test_char"); /*在sys下创建类目录/sys/class/test_char*/
device_create(myclass, NULL, MKDEV(mem_major,0), NULL, "memdev0");//设备名为memdev0
/* 手动创建设备文件*/
"mknod /dev/memdev0 c 150 0"命令创建"/dev/globalmem"字符设备节点
int chrdev_open(struct inode * inode, struct file * filp) //path: fs/char_dev.c
/*
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的相应操作.
*/