分类: LINUX
2010-07-02 15:46:26
LINUX内核用下面的数据结构管理多个字符设备。一个驱动可以动态的申请设备号或者定义一个静态的设备号。
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
一个字符设备的注册过程分为两步:
1 注册和分配一个设备号,如果驱动要指定设备号函数register_chrdev_region必须被调用,函数alloc_chrdev_region使内核选择这个号。
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);
2 当一个新的设备号被分配了那么此节点号就由baseminor和count定义了。这个主节点号被放到dev结构体里也就是说结构体cdev并没有要求和分配设备号。当设备号分配后需要激活把它加入到设备的数据库中去这个需要初始化一个cdev的实例通过函数cdev_init。接着调用cdev_add。
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
cdev结构体里的指针fops指向了实际设备的操作。Count表示有多少此节点号设备提供的。
static struct cdev dm642_cdev;
cdev_init(&video1394_cdev, &dm642_fops);
...
ret = cdev_add(&dm642_cdev, DM642_DEV, 16);
在cdev__dd返回成功后那么设备就被激活了。
在早先的内核中使用标准函数register_chrdev来注册一个块设备,只支持255个设备。
块设备的注册:
虚拟文件系统的每一个文件只链接一个管理文件属性的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;
};
...
};
为了唯一的识别设备和设备文件内存存储了文件类型在变量i_mode里把主设备和次设备号放在i_rdev里。i_fop收集了很多虚拟文件系统在块设备使用的信息比如open,read,write。Inode表示块设备和字符设备的分别是i_cdev和i_bdev。当一个设备被打开的时候各种文件系统唤醒函数init_special_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
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o)\n",mode);
}
底层的文件系统比须返回主次设备号除设备类型外。这个inode依赖设备类型提供给不同的文件系统。字符设备的初始化只有一个文件系统的选项组成。
struct file_operations def_chr_fops = {
.open = chrdev_open,
};
const struct file_operations def_blk_fops = {
.open = blkdev_open,
.release = blkdev_close,
.llseek = block_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write_nolock,
.mmap = generic_file_mmap,
.fsync = block_fsync,
.unlocked_ioctl = block_ioctl,
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
};
读写操作遵循内核的标准路径,内核产生cache自动用于块设备。对比字符设备块设备不能完全被上面的数据结构描述因为访问块设备不执行应答但是内核负责有效的管理缓存和请求链表。但是请求链表是块设备层管理的。
所有的字符设备都用结构体cdev表示:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
字符设备通过函数chrdev_open打开设备方式:Chrdev_open->fop_open
Cdev设备端的实例内核也通过cdev->ops访问设备定义的file_operations。