1、对于驱动的理解和解释
(1)、字符设备和块设备都可以体现为“一切都是文件”的思想,可以使用open()、read()、write()、close()等函数进行访问。
(2)、驱动的编写要体现“驱动融合入内核中的思想”,其实就是驱动必须遵循内核提供的接口规范进行编写,即驱动对硬件操作的函数,和内核系统调用函数之间有映射关系。当应用程序中系统调用open()、read()、write()等函数调用时,内核找到驱动对应的映射函数进行对硬件的操作。
2、工作流程
模块加载时,自己定义一个结构体,在这个结构体中,调用内核cdev结构体,然后用内核提供“一组函数”对设备进行申请、注册。
3、编写字符驱动的步骤
(1)、认识内核提供2个结构体:cdev 和 file_operations
struct cdev
{
struct module *owner; //所属模块
struct file_operation *ops;//文件操作结构体
dev_t dev;//设备号
}
struct file_operations
{
struct module *owner;
函数指针;
.......
}
(2)、自己编写file_operations结构体中成员函数的具体实现
一般为 xxx_read()、xxx_write()、xxx_close()等。
(3)、自己定义一个file_operations实例,将具体成员函数的入口地址付给函数指针
struct file_operations xxx_ops
{
.owner = This_module;
.read = xxx_read;
.write = xxx_write;
.close = xxx_close;
......
}
4、字符驱动模块加载和卸载模板
认识:内核中提供了用于操作cdev结构体的一组函数,这组函数是:
void cdev_init(struct cdev *,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 *);
// 设备结构体(自己定义的)
struct xxx_dev_t
{
struct cdev cdev;
....
}
// 设备驱动模块加载函数
static int __init xxx_init(void)
{
....
cdev_init(&xxx_dev.cdev,&xxx_fops);// 初始化cdev,将自己定义的结构体指针和内核结构体指针关联起来(这样子,内核就指向驱动的xxx_open等函数了)
xxx_dev.cdev.owner = THIS_MODULE;
if(xxx_major)
{
register_chrdev_region(xxx_dev_no,1,DEV_NAME);
}
else
{
alloc_chrdev_region(&xxx_dev_no,0,1,DEV_NAME);
}
ret = cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//注册设备
......
}
// 设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
unregister_chrdev_region(xxx_dev_no,1);//释放占用的设备号
cdev_del(&xxx_dev.cdev);//注销设备
.....
}