字符驱动
将字符设备抽象为文件,应用程序可以像读写文件一样操作字符设备。字符驱动在文件系统中创建设备文件夹来表示字符设备。
- 字符设备:以字符为单位进行输入和输出的设备
- 设备号:用于标识字符设备
- 设备结点:/dev目录下的特殊文件,与设备文件一一对应。
文件操作函数:通过file_operations结构体定义的一组函数指针,用于处理对设备文件的操作
字符设备结构体cdev:用于表示和管理设备的结构体,其包含设备号,文件操作函数等信息,通过cdev_init和cdev_add函数与字符设备关联。
设备文件的创建与删除:通过class_create和device_create函数可以在/dev目录下创 建设备文件,并通过device_destroy和class_destroy 函数进行删除。
内核缓冲区和用户空间数据交换:驱动程序可以使用copy_to_user和copy_from_use r 函数在内核缓冲区和用户空间之间传输数据
字符驱动注册方法1
#include <linux/module.h> // 包含模块相关函数的头文件
#include <linux/fs.h> // 包含文件系统相关函数的头文件
#include <linux/uaccess.h> // 包含用户空间数据访问函数的头文件
#include <linux/cdev.h> //包含字符设备头文件
#define DEVICE_NAME “mydevice” // 设备名称
static dev_t dev_num; //分配的设备号
struct cdev my_cdev; //字符设备指针
int major; //主设备号
int minor; //次设备号
入口函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| static int __init mydevice_init(void) { int ret; // 在这里执行驱动程序的初始化操作
/*注册字符设备驱动程序 第一个参数是一个指向dev_t类型的指针 第二个参数为次设备号起始值 第三个参数为次设备数量 第三个参数为设备名称,可以在/proc/devices中找到*/ ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME); if (ret < 0) { printk(KERN_ALERT "Failed to allocate device number: %d\n", ret); return ret; } major=MAJOR(dev_num); //返回主设备号 minor=MINOR(dev_num); //返回次设备号 printk(KERN_INFO "major number: %d\n",major); printk(KERN_INFO "minor number: %d\n",minor);
my_cdev.owner = THIS_MODULE;
/*初始化字符设备结构体 第一个参数指向要初始化的struct cdev结构体的指针 第二个参数指向包含设备操作函数的struct file_operations 结构体的指针*/ cdev_init(&my_cdev,&fops);
/*将字符设备添加到内核 第一个参数为指向要添加的struct cdev结构体的指针 第二个参数是要添加的设备号 第三个参数是要添加的设备数量*/ cdev_add(&my_cdev,dev_num,1);
printk(KERN_INFO "Device registered successfully.\n"); return 0; }
|
出口函数
1 2 3 4 5 6 7 8 9 10 11
| static void __exit mydevice_exit(void) { // 在这里执行驱动程序的清理操作 //删除字符设备 cdev_del(&my_cdev); /*注销字符设备驱动程序 主设备号 字符设备名称*/ unregister_chrdev(0, DEVICE_NAME); printk(KERN_INFO "Device unregistered.\n"); }
|
定义fops结构体
1 2 3 4 5 6 7
| static struct file_operations fops = { .owner = THIS_MODULE, .open = device_open, .release = device_release, .read = device_read, .write = device_write, };
|
设备操作函数
1 2 3 4 5 6 7 8
| static int device_open(struct inode *inode, struct file *file) { // 在这里处理设备打开的操作 printk(KERN_INFO "This is device_open.\n"); return 0; }
...
|
字符驱动注册方法2
方法1似乎很原始,参数都要自己配置
1 2 3 4 5 6
| major = register_chrdev(0, DEVICE_NAME, &fops)
hello_class = class_create(THIS_MODULE, "hello_class"); device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello");
|
第一个参数可指定主设备号,若为0,则让系统自动分配
自动创建结点
包含头文件并声明变量
1 2 3 4
| #include <linux/device.h>
static struct class *my_class; static struct device *my_device;
|
创建设备类
1 2 3 4 5 6
| my_class = class_create(THIS_MODULE, "my_class");
if (IS_ERR(my_class)) { pr_err("Failed to create class\n"); return PTR_ERR(my_class); }
|
创建设备节点并关联到设备类
1 2 3 4 5 6 7
| my_device = device_create(my_class, NULL, MKDEV(major, minor), NULL, "my_device");
if (IS_ERR(my_device)) { pr_err("Failed to create device\n"); class_destroy(my_class); return PTR_ERR(my_device); }
|