关于字符设备的基础知识:
对于字符驱动程序来说,首先要注册一个设备号,设备号包括主设备和次设备,一般来讲,主设备标志与设备相对应的驱动程序,而次设备则是被内核用来标志所指向是那个设备。内核除了知道次设备指向驱动所对应的设备外,对于次设备其它的一无所知。
如果事先就知道你要注册的设备号,你可以使用宏MKDEV( )来生成设备号,结构是dev_t, 以后就可以通过MAJOR( )和MINOR可以查看相应的主设备和次设备
MKDEV(int major, int minor);
MAJOR(dev_t dev);
MAJOR(dev_t dev);
定义好设备号后并且想让他工作的话就必须让内核知道,这里首先要了解几个函数和数据结构:
int register_chrdev_region(dev_t first , unsigned int count , char* name );
first 是你想注册的设备号,其中的minor一般为0,count为你设备号临近的设备号的总和,这里需要注意的是,如果count设置的比较大的话,可能使得设备号的major超出你开始分配的major number。name为设备的名称,将会出现在/proc/divices中。注册成功的话将会将会返回0。
一般而言,开发着一般都不能确定主设备应该选择什么,如果是这样的话,可以使用下个这个函数
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char* name);
这个函数可以动态分配主设备,count 和name与上面那个函数的含义一样,firstminor为需要使用的第一个次设备号,通常为0,使用这个函数后,dev为内核为你分配的设备号,可以通过上面的二个宏来获得主设备和次设备号。然而使用这个函数也有个缺点,即不能事先创建设备节点,也就是说不能在/dev显示出相应的设备。不过可以通过读取/proc/devices来创建。
下面是几个重要的数据结构file_operations, file, inode
file_operations是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。
struct file_operations {
struct module *owner; //防止模块还在被使用的时候被卸载
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//同步读
ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
......
}
file代表一个打开的文件,在执行file_operation中的open操作时被创建,这里需要注意的是与用户空间file指针的区别,一个在内核,而file指针在用户空间,由c库来定义。
struct file {
struct list_head f_list;
struct dentry *f_dentry;
struct vfsmount *f_vfsmnt;
struct file_operations *f_op; //这就是上面所说的file_oeprations
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode; //文件是否可读、可写
int f_error;
loff_t f_pos; //当前读写位置
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
unsigned long f_version;
void *f_security;
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
inode被内核用来代表一个文件,注意和file的区别,一个是代表文件,一个是代表打开的文件
inode包括很重要的二个成员:
dev_t i_rdev 设备文件的设备号
struct cdev *i_cdev 代表字符设备的数据结构
Inode结构是用来在内核内部表示文件的.同一个文件可以被打开好多次,所以可以对应很多struct file,但是只对应一个struct inode
好了,现在可以注册字符设备了
可以有两种方法注册
struct cdev *my_cdev=cdev_alloc();
my_cdev->ops=&my_fops;
或
void cdev_init(struct cdev *cdev,struct file_operations *fops);
注册完后还要通知内核一声,通过调用
int cdev_add(struct cdev *dev,dev_t num,unsigned int count);
count 一般是 1.
删除字符设备,调用
void cdev_del(struct cdev *dev);
参考资料
驱动读书笔记
Linux Device Driver 3rd Edition
阅读(370) | 评论(0) | 转发(0) |