2007-11-28
by heyl

/*
* linux/fs/devices.c
*
* (C) 1993 Matthias Urlichs -- collected common code and tables.
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Added kerneld support: Jacques Gelinas and Bjorn Ekwall
* (changed to kmod)
*/
看到这儿注释,不难理解这里为啥不搭边...

这里是char dev的天下?想起了block_dev 的相关结构,倒是和这个有点类似.
struct device_struct {
const char * name;
struct file_operations * fops;
};
和blkdev是不同的,存储的是name和fops.
static struct {
const char *name;
struct block_device_operations *bdops;
} blkdevs[MAX_BLKDEV];

static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
static struct device_struct chrdevs[MAX_CHRDEV];

int get_device_list(char * page) : 获取char dev 和block dev的列表,极尽简单.

static struct file_operations * get_chrfops(unsigned int major, unsigned int minor) : 从char获取file ops, char dev
直接面对file,真是素面朝天的做法,比起block dev的七拐八拐直接多了.这个函数无非就是如果是tty照顾到了kmod. 参考major.h,里边是
所有已知设备的major number.

chardev的注册函数,真是直白,我们需要这样子的代码.
int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
int unregister_chrdev(unsigned int major, const char * name)


/*
* Called every time a character special file is opened
*/
int chrdev_open(struct inode * inode, struct file * filp)
{
int ret = -ENODEV;

filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if (filp->f_op) {
ret = 0;
if (filp->f_op->open != NULL) {
lock_kernel();
ret = filp->f_op->open(inode,filp);
unlock_kernel();
}
}
return ret;
}

/*
* Dummy default file-operations: the only thing this does
* is contain the open that then fills in the correct operations
* depending on the special file...
*/
static struct file_operations def_chr_fops = {
open: chrdev_open,
};

def注册到inode的i_fops,
通denty_open这个file ops转移到filep->f_ops.
struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
{
struct file * f;
.....
f->f_op = fops_get(inode->i_fop);
}

接下来就是一个很知名的函数了:
void init_special_inode(struct inode *inode, umode_t mode, int rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = to_kdev_t(rdev);
} else if (S_ISBLK(mode)) { /*block dev 有两个dev和之对应*/
inode->i_fop = &def_blk_fops;
inode->i_rdev = to_kdev_t(rdev); /*kdev,一个int, 系统中所有dev的统一handler*/
inode->i_bdev = bdget(rdev); /* block_device pointer */
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops; /*sock使用另外的soket标准接口和文件系统相区别*/
else
printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
}

搜索init_special_inode的调用者,我们知道不是所有的文件系统都支持设备文件的, 比如vfat就免谈了.


剩下的函数看看名字好了.
const char * kdevname(kdev_t dev)
const char * cdevname(kdev_t dev)
static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
static struct file_operations bad_sock_fops = {
open: sock_no_open
};

不想我们从这里得窥char device的一角, chardev 直接实现一个file ops给文件系统,倒是省心了不少. 比block dev干净许多啊.