一、字符设备驱动结构
1. cdev结构体
在Linux内核中,使用cdev
结构体来描述一个字符设备
struct cdev {
struct kobject kobj; //内嵌kobject对象
struct module *owner; //所属的模块
const struct file_operations *ops; //该设备的文件操作结构体
struct list_head list;
dev_t dev; //设备号
unsigned int count;
};
cdev相关的操作
cdev_init
: 初始化cdev的函数,实际上就是将cdev
和file_operation
进行关联
void cdev_init(struct cdev *cdev, struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops; /* 将传入的文件操作结构体指针赋值给 cdev 的 ops*/
}
cdev_alloc
: 动态申请一个cdev
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
cdev_add/cdev_del
: 向内核中添加/删除cdev,即对设备的注册和注销。通常发生在加载/卸载模块时
设备号
cdev结构体的dev_t定义了设备号,前12bit代表主设备号,后20bit代表次设备号。同一驱动可支持多个同类设备,因此同一类设备一般使用相同的主设备号,次设备号从0开始用来描述驱动的不同设备序号
MKDEV(int major, int minor) //组成一个设备号
MAJOR(dev_t dev) //获取主设备号
MINOR(dev_t dev) //获取次设备号
在调用cdev_add函数注册设备之前,需要先申请设备号
//已知设备号时使用,直接申请
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);
在调用cdev_del函数注销设备前,需要释放设备号
void unregister_chrdev_region(dev_t from, unsigned count);
2. file_operation结构体
应用程序调用的open/read/write
等函数,最终时调用的对应设备的file_operation
结构体中的对应函数,所以驱动程序设计的主题内容就是实现file_operation
结构体中的成员函数
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 (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long