抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

字符驱动

将字符设备抽象为文件,应用程序可以像读写文件一样操作字符设备。字符驱动在文件系统中创建设备文件夹来表示字符设备。

  • 字符设备:以字符为单位进行输入和输出的设备
  • 设备号:用于标识字符设备
  • 设备结点:/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);
}

评论