Linux字符设备驱动模型小记

news/2024/7/24 2:21:54 标签: linux

基于file_operations结构的字符设备驱动模型

file_operations是内核提供用来操作文件的操作集合,我们示例中只使用到了下面四个函数,因此示例中的hello_drv只实现了这四个函数。

truct file_operations {
    ...
    struct module *owner;
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ...
}

实现了file_operations数据结构后,然后通过module_init/xxx_initcall等方式让kernel在启动阶段自动调用驱动函数。

常见的注册设备驱动的方式有以下两种:

1.使用alloc_chrdev_region+cdev注册设备驱动

1.) 调用alloc_chrdev_region获取主设备号。

intalloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);

让内核分配给我们一个尚未使用的主设备号,不是由我们自己指定的,四个参数的意义如下:

dev :alloc_chrdev_region函数向内核申请下来的设备号
baseminor :次设备号的起始
count: 申请次设备号的个数
name :执行 cat /proc/devices显示的名称

示例:

alloc_chrdev_region(&devno, 0, 1, NIM_DEVICE_NAME);

2.) 调用cdev_init或者cdev_alloc,将cdev和file_operations关联起来,需要先实现file_operations的各个成员 -- 操作函数。

struct cdev*cdev_alloc(void);

示例:

m_hello_cdev = cdev_alloc();
    if (m_hello_cdev == NULL) {
        printk("hello cdev alloc fail\n");
        return 0;
    }

    m_hello_cdev->ops = &hello_fops;
    m_hello->owner = hello_fops.owner;
kobject_set_name(&m_hello_cdev->kobj, "%s", "hello");
voidcdev_init(struct cdev *, const struct file_operations *);

示例:

cdev_init(&mxl241_nim_dev.cdev, &mxl241_nim_fops);

3.) 调用cdev_add函数,将cdev和主设备号关联起来。

intcdev_add(struct cdev *, dev_t, unsigned);

示例:

ret=cdev_add(&mxl241_nim_dev.cdev, devno, 1);

将上述步骤封装在函数中,通过module_init/xxx_initcall让kernel在启动阶段自动调用该驱动函数。

2. 使用register_chrdev注册设备驱动。

1.) 调用register_chrdev获取主设备号。

intregister_chrdev(unsigned int major, const char *name,const structfile_operations *fops)
参数说明:
major,表示当前设备的主设备号,范围是1~254。可以自己指定,也可以设置为0让内核自动分配。犹如学号。
name,表示当前设备驱动的名字,犹如名字。
fops,是file_operations结构体指针。
返回值:
major如果设置为0,则返回自动分配的主设备号;
如果设置为指定的主设备号,成功则返回值为0,失败返回负数。

示例,获取主设备号,并指定设备名称。

major =register_chrdev(0, "hello", &hello_drv); 

2.) 调用class_create与device_create来创建设备节点。

内核中定义了structclass结构体,顾名思义,一个structclass结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

class_create与device_create匹配使用。

示例,将设备注册到“/dev/hello”。

hello_class = class_create(THIS_MODULE, "hello_class");
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */

上述步骤封装在函数中,通过module_init/xxx_initcall让kernel在启动阶段自动调用该驱动函数。

基于platform_driver结构的字符设备驱动模型

platform是一条虚拟的总线。设备用platform_device表示,驱动用platform_driver进行注册,Linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的)。

pltform机制本身使用并不复杂,由两部分组成:platform_device和platform_driver。通过platform机制开发底层驱动的大致流程为:定义platform_deive->注册platform_device->定义platform_driver->注册platform_driver。

platform_device通常情况下,kernel会从dts中匹配,并展开自动生成。dts文件最终在linux内核中会转化成platform_device:

dts -> dtb -> device_node -> platform_device

platform_driver的基本形态如下

static struct platform_driver plt_hello_driver = {
    .driver =
        {
            .name           = "plt_hello",
            .owner          = THIS_MODULE,
            .of_match_table = plt_hello_of_match,
        },

    .probe  = plt_hello_probe,
    .remove = plt_hello_remove,
};
PS:MODULE_DEVICE_TABLE(类型, ID表);
设备树ID表
类型:of

该宏定义用来指定dts中的设备表,示例如下:

static const struct of_device_id plt_hello_of_match[] = {
    {
        .compatible = "tech, plt_hello",
    },
    {},
};
MODULE_DEVICE_TABLE(of, plt_hello_of_match);

在.probe函数中实现驱动的方式与前述file_operations结构比较相似,也是通过cdev_alloc/cdev_init与cdev_add创建设备,然后再通过class_create/device_create创建总线及设备。

1. 使用module_platform_driver注册设备驱动

例子,从dts获取主设备号:

ret = of_get_major_minor(pdev->dev.of_node, &m_hello_dev_t, 0, 1, "hello");
    if (ret < 0) {
        pr_err("unable to get major and minor for char devive\n");
        return ret;
    }

创建hello设备并关联file_operation(hello_fops)。

 m_hello_cdev = cdev_alloc();
    if (m_hello_cdev == NULL) {
        HELLO_PRF(" hello cdev alloc fail\n");
        return 0;
    }

    m_hello_cdev->ops   = &hello_fops;
    m_hello_cdev->owner = hello_fops.owner;
    kobject_set_name(&m_hello_cdev->kobj, "%s", "hello");
    if (cdev_add(m_hello_cdev, m_hello_dev_t, 1) < 0) {
        HELLO_PRF(" hello cdev add fail\n");
        goto FAIL;
    }

创建class/device

m_hello_class = class_create(THIS_MODULE, "hello");
  if (m_hello_class == NULL) {
      HELLO_PRF(" hello create class fail\n");
      goto FAIL;
  }

m_hello_device = device_create(m_hello_class, NULL, m_hello_dev_t, NULL, "hello0");
if (m_hello_device == NULL) {
    HELLO_PRF(" hello create device fail\n");
    goto FAIL;
}

2. 使用module_init/xxx_initcall来注册设备驱动

也可以通过上面的接口自己控制设备驱动的调用顺序。只需要在设备驱动中

通过platform_driver_register接口来注册platform_driver接口即可。

示例:

static struct platform_driver hello_driver = {
    .probe   = hello_probe,
    .remove  = hello_remove,
    .suspend = hello_suspend,
    .resume  = hello_resume,
    .driver =
        {
            .name           = "plt-hello",
            .owner          = THIS_MODULE,
            .of_match_table = plt_hello_dt_ids,
        },
};

static int __init hello_init(void)
{
    int ret = 0;

    ret = platform_driver_register(&hello_driver);

    return ret;
}

module_init(hello_init);

http://www.niftyadmin.cn/n/146696.html

相关文章

【MySQL】MySQL的优化(一)

目录 查看SQL执行频率 定位低效率执行SQL 定位低效率执行SQL-慢查询日志 定位低效率执行SQL-show processlist 查看SQL执行频率 MySQL 客户端连接成功后&#xff0c;通过 show [session|global] status 命令可以查看服务器状态信息。通 过查看状态信息可以查看对当…

PaddleDetection目标检测数据准备——VOC数据集和COCO数据集

目标检测数据说明 目标检测的数据比分类复杂&#xff0c;一张图像中&#xff0c;需要标记出各个目标区域的位置和类别。 一般的目标区域位置用一个矩形框来表示&#xff0c;一般用以下3种方式表达&#xff1a; 表达方式说明x1,y1,x2,y2(x1,y1)为左上角坐标&#xff0c;(x2,y…

Web APIs五、BOM操作浏览器

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

23种设计模式-责任链模式(Android开发实际应用场景介绍)

什么是责任链模式 责任链模式是一种行为型设计模式&#xff0c;它的核心思想是将请求从一系列处理者中传递&#xff0c;直到其中一个处理者能够处理它为止。在这个过程中&#xff0c;请求可以被任何一个处理者处理&#xff0c;也可以被拒绝&#xff0c;直到有一个处理者能够处…

28个案例问题分析---24---xxljob控制台不打印日志排查--xxljob问题

背景介绍 本文是对xxljob的不打印调度日志与控制台日志的排查过程 问题一&#xff1a;调度日志为空 问题2 admin权限启动jar包后&#xff0c;xxljob控制台将不会打印日志。root权限启动的话&#xff0c;xxljob控制台将会打印日志。 问题排查 问题一 出现问题一的可能有两…

该如何选择IT培训机构?

IT培训行业由来已久&#xff0c;存在的首要条件是有市场需求&#xff0c;尤其是对于没有更好渠道方式掌握IT技术的小伙伴。当然也有很多质疑甚至诋毁的声音。个人认为大可不必&#xff0c;根据23年职教经验总结&#xff1a;如果你没有需求可以直接略过&#xff0c;让给真正有需…

Nebius Welcome Round (Div. 1 + Div. 2, rated, t-shirts!)【A-C】

文章目录A. Lame King【简单模拟、贪心】B.Vaccination【模拟、贪心】C.Pull Your Luck【规律题、余数、找周期】链接传送门A. Lame King【简单模拟、贪心】 分析 首先符号并不重要&#xff0c;我们可以放到第一象限里进行考虑&#xff0c;不妨设x > y,记向x正方向移动为A&…

开心消消乐题解

题目描述&#xff1a; 给定一个 N 行 M 列的二维矩阵&#xff0c;矩阵中每个位置的数字取值为 0 或 1&#xff0c;矩阵示例如&#xff1a; 1 1 0 0 0 0 0 1 0 0 1 1 1 1 1 1 现需要将矩阵中所有的 1 进行反转为 0&#xff0c;规则如下&#xff1a; 当点击一个 1 时&#xf…