Linux驱动开发—在自己总线下注册设备和驱动

news2024/9/23 17:12:32

书接上回:Linux驱动开发—创建总线,创建属性文件-CSDN博客

创建完总线,就可以进行本次实验了

文章目录

    • 前备知识
      • 如何引用导出的符号
    • 在总线下注册设备
      • device_register 函数解析
      • 使用示例
        • 关键点:
      • 实验结果
    • 在总线下注册驱动
      • driver_register 函数解析
      • 使用示例
      • 实验结果
    • 总线,设备,设备驱动 三者完整代码
    • 加载设备和加载驱动没有先后顺序
      • 1. 设备与驱动的动态匹配
      • 2. 总线的 `match` 机制
      • 3. 延迟匹配

前备知识

在 Linux 内核编程中,EXPORT_SYMBOL_GPL 用于将符号(例如变量、函数)导出到内核符号表中,使其可以被其他内核模块或驱动程序引用和使用。具体来说:

  • EXPORT_SYMBOL_GPL(symbol_name):将指定的符号(如结构体、函数等)导出,并且只允许那些以 GPL 许可证发布的模块使用该符号。这意味着该符号只能被 GPL 兼容的模块引用和使用。

导出符号的机制使得一个模块可以将某些符号公开给其他模块使用,而不需要将所有的实现都暴露出来。这对于模块化开发和代码复用非常有用。

如何引用导出的符号

如果一个符号通过 EXPORT_SYMBOL_GPL 导出,在另一个模块中可以直接使用这个符号,而无需显式地声明或定义它。为了使用导出的符号:

  1. 确保使用该符号的模块也使用 GPL 兼容的许可证。
  2. 在使用符号的模块中包含相关的头文件(如果符号在头文件中声明了)。
  3. 直接在代码中使用这个符号,编译器会自动解析并链接到导出的符号。

在总线下注册设备

内核中,已经提供了一个API供开发者来调用,具体为device_register

device_register 函数解析

device_register 是 Linux 内核中用于注册设备的函数。它将一个设备结构体 (struct device) 注册到系统中,使其成为内核设备模型的一部分。设备模型用于管理和组织设备与驱动程序的关系,并提供设备的层次结构和电源管理等功能。

主要功能包括

  1. 初始化设备:
    • 调用 device_initialize 函数来初始化设备结构体。这包括为设备分配引用计数、初始化设备锁、设置设备状态等。
  2. 添加设备:
    • 调用 device_add 函数将设备添加到内核设备模型中。device_add 会将设备插入到设备树(或设备层次结构)中,并执行设备的其他初始化操作,如为设备创建 sysfs 节点、触发 uevent 以通知用户空间有新设备加入等。
  3. 错误处理:
    • 如果在设备添加过程中发生错误,device_register 会执行适当的清理操作,以确保没有资源泄漏。

函数定义:

int device_register(struct device *dev);

参数说明:

dev: 这是一个指向 struct device 结构体的指针,表示要注册的设备。该结构体包含了设备的基本信息,如设备名称、设备所属的总线、父设备、设备类等。

使用示例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>
// my_bus 模块中的bus_type 已经导出到了内核符号表,在这里可以直接调用
extern struct bus_type my_bus_type;
void device_release(struct device *dev){
    printk("This is device release");
}
struct device mydev = {
    .init_name = "my_device",
    .bus = &my_bus_type,
    .release = device_release,
    .devt = ((255<<20|0)),
};
static int __init my_bus_device_init(void)
{
    int ret;
    // 往系统中注册一个设备结构体
    device_register(&mydev);
    return ret;
}
static void __exit my_bus_device_exit(void)
{
    device_unregister(&mydev);

}
module_init(my_bus_device_init);
module_exit(my_bus_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of bus registration");
关键点:

device结构体中包括了众多字段,一般只需要关注:名称,所属于的总线,释放函数,设备号…

其中所属的总线,上文曾提到注册总线的方法,总线模块中已经包含了bus_type, 设备类中没必要再写一遍,可以导出bus_type内核符号,让设备模块进行调用即可。

导出方式EXPORT_SYMBOL_GPL

//注册总线的关键结构体
struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_bus_match,
    .probe = my_bus_probe,
};
EXPORT_SYMBOL_GPL(my_bus_type);

引用方式:extern struct bus_type my_bus_type;

因此,一定要先加载总线模块,再加载设备模块,才能调用成功

实验结果

在这里插入图片描述

注:在sys目录查看某些文件,需要全路径,才能查看。

在总线下注册驱动

内核中,已经提供了一个API供开发者来调用,具体为driver_register

driver_register 函数解析

它是内核设备驱动模型中的核心函数之一,用于将驱动程序与内核设备模型关联起来,并将其挂载到对应的总线上。

函数定义

int driver_register(struct device_driver *drv);

主要功能:

1.检查参数有效性

  • 检查传入的 drv 是否有效,如果 drvNULL 或者它的某些关键字段(如 namebus)未被正确设置,driver_register 会返回错误。

2.初始化驱动程序结构体

  • 初始化 drv->kobj,它是用于内核对象模型 (kobject) 管理的内核对象。这一步是为了将驱动程序挂载到内核的 sysfs 文件系统中,便于用户空间和内核空间的交互。

3.将驱动程序添加到对应总线

  • 通过调用 bus_add_driver 函数,将驱动程序添加到它所属的总线(drv->bus)上。每个总线都有一组已注册的驱动程序,这一步将驱动程序加入到总线的驱动程序列表中。

4.触发设备与驱动的匹配

  • 在驱动程序被成功添加到总线后,内核会尝试匹配总线上已经存在的设备与新注册的驱动程序。如果匹配成功,则调用驱动程序的 probe 函数来初始化设备。

参数说明:

传入的参数为struct device_driver,主要字段有 名称,probe函数,remove函数,所在的总线…

使用示例

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>
// my_bus 模块中的bus_type 已经导出到了内核符号表,在这里可以直接调用
extern struct bus_type my_bus_type;


static int my_drv_probe (struct device *dev){
    printk("This is my driver probe function\n");
    return 0;
};
static int my_drv_remove (struct device *dev){
    printk("This is my driver remove function\n");
    return 0;
}

struct  device_driver my_device_drv =  {
    .name = "my_device", //与设备名称保持一致。 因为需要根据名称进行匹配。
    .bus = &my_bus_type,
    .probe = my_drv_probe,
    .remove = my_drv_remove,
};
static int __init my_bus_drv_init(void)
{
    int ret;
    ret = driver_register(&my_device_drv);
    return ret;
}

static void __exit my_bus_drv_exit(void)

{
    driver_unregister(&my_device_drv);
}
module_init(my_bus_drv_init);
module_exit(my_bus_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marxist");
MODULE_DESCRIPTION("A simple example of bus drv registration");

实验结果

在这里插入图片描述

看到指向设备的符号链接,这表明该驱动已经与设备成功匹配并绑定。

总线,设备,设备驱动 三者完整代码

总线:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>
//进行名称匹配
static int my_bus_match (struct device *dev, struct device_driver *drv){
    printk("match success\n");
    return  (strcmp(dev_name(dev),drv->name)==0);
}
static int my_bus_probe(struct device *dev){
    printk("new device probe success\n");
    struct device_driver *drv = dev->driver;    
    if(drv->probe){
        drv->probe(dev);
    }
    return 0;
}

//注册总线的关键结构体
struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_bus_match,
    .probe = my_bus_probe,
};
EXPORT_SYMBOL_GPL(my_bus_type);
// 在总线目录下创建属性文件 与 kobject 创建属性文件 类似
static ssize_t my_bus_show(struct bus_type *bus, char *buf) {
    return sprintf(buf, "Hello from the bus attribute!\n");
}

static ssize_t my_bus_store(struct bus_type *bus, const char *buf, size_t count) {
    pr_info("Received from userspace: %s\n", buf);
    return count;
}
static BUS_ATTR(my_bus_attr, 0664, my_bus_show, my_bus_store);
static int __init my_bus_init(void)
{
    int ret;
    // 注册总线
    ret = bus_register(&my_bus_type);
    if (ret)
        return ret;

    // 添加属性文件到总线
    ret = bus_create_file(&my_bus_type, &bus_attr_my_bus_attr);
    if (ret)
        bus_unregister(&my_bus_type);

    return ret;
}

static void __exit my_bus_exit(void)

{
       // 移除属性文件
    bus_remove_file(&my_bus_type, &bus_attr_my_bus_attr);
    bus_unregister(&my_bus_type);
}
module_init(my_bus_init);
module_exit(my_bus_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of bus registration");

设备

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>
// my_bus 模块中的bus_type 已经导出到了内核符号表,在这里可以直接调用
extern struct bus_type my_bus_type;

void device_release(struct device *dev){
    printk("This is device release");
}
struct device mydev = {
    .init_name = "my_device",
    .bus = &my_bus_type,
    .release = device_release,
    .devt = ((255<<20|0)),
};

static int __init my_bus_device_init(void)
{
    int ret;
    // 往系统中注册一个设备结构体
    device_register(&mydev);
    return ret;
}

static void __exit my_bus_device_exit(void)

{
    device_unregister(&mydev);

}
module_init(my_bus_device_init);
module_exit(my_bus_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of bus registration");

驱动

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/device.h>
// my_bus 模块中的bus_type 已经导出到了内核符号表,在这里可以直接调用
extern struct bus_type my_bus_type;


static int my_drv_probe (struct device *dev){
    printk("This is my driver probe function\n");
    return 0;
};
static int my_drv_remove (struct device *dev){
    printk("This is my driver remove function\n");
    return 0;
}

struct  device_driver my_device_drv =  {
    .name = "my_device", //与设备名称保持一致。 因为需要根据名称进行匹配。
    .bus = &my_bus_type,
    .probe = my_drv_probe,
    .remove = my_drv_remove,
};
static int __init my_bus_drv_init(void)
{
    int ret;
    ret = driver_register(&my_device_drv);
    return ret;
}

static void __exit my_bus_drv_exit(void)

{
    driver_unregister(&my_device_drv);
}
module_init(my_bus_drv_init);
module_exit(my_bus_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marxist");
MODULE_DESCRIPTION("A simple example of bus drv registration");

注:先加载设备 或者先加载驱动都可以

加载设备和加载驱动没有先后顺序

在 Linux 内核中,设备和驱动的加载顺序可以任意(即设备可以先加载,驱动也可以先加载),这得益于内核设备驱动模型的设计灵活性

1. 设备与驱动的动态匹配

无论是设备还是驱动,内核都会在合适的时机进行匹配和绑定:

  • 设备先加载
    • 如果设备先加载,内核会将设备添加到设备列表中并挂载到对应的总线下。
    • 当驱动稍后加载时,内核会遍历总线上的所有设备,尝试匹配这些设备和新加载的驱动程序。如果匹配成功,会调用驱动的 probe 函数来初始化设备。
  • 驱动先加载
    • 如果驱动先加载,内核会将驱动添加到驱动列表中并挂载到对应的总线下。
    • 当设备稍后加载时,内核会遍历总线上的所有驱动程序,尝试匹配这些驱动程序和新加载的设备。如果匹配成功,会同样调用驱动的 probe 函数来初始化设备。

2. 总线的 match 机制

每个总线都有自己的 match 机制,通常由总线的 match 函数实现(例如,I2C 总线、SPI 总线等)。这个 match 函数负责根据设备和驱动的属性(如名字、ID等)进行匹配。一旦匹配成功,设备和驱动就会绑定,并调用 probe 函数。

这种 match 机制的灵活性保证了不论设备和驱动的加载顺序,匹配和绑定都可以在任何一方加载完成后自动完成。

3. 延迟匹配

如果在设备加载时,尚无与之匹配的驱动程序,设备会处于未绑定状态,等待驱动程序的加载。同样地,如果驱动程序加载时,尚无与之匹配的设备,驱动程序也会处于未绑定状态,直到设备加载并匹配成功。这种机制被称为“延迟匹配”,确保设备和驱动可以在合适的时间进行匹配。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2084653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Excel 常用操作

Excel 常用操作 定位单元格显示该行的序列号范围选中指定行列的单元格常用快捷键在A列中拿到30000个序号升序数据 定位单元格 在该输入框输入想要定位的列号与行号&#xff0c;回车即可定位成功 显示该行的序列号 在一个单元格中输入ROW()函数&#xff0c;回车&#xff0c;就显…

识别热带赤道波动的艺术

识别热带赤道波动的艺术 什么是赤道波动 天气尺度(时间) to 行星尺度(空间)&#xff0c;纬向传播的低纬扰动 波动的周期从几天到几周 狭窄的定义&#xff1a; 线性波动理论的波解 kelvin waves (KW)equatorial rossby waves (ER)inertio-gravity waves (IG)mixed rossby g…

【SQL】餐馆营业额七日均线数据

目录 题目 分析 代码 题目 表: Customer ------------------------ | Column Name | Type | ------------------------ | customer_id | int | | name | varchar | | visited_on | date | | amount | int | -----------------------…

金风科技巴西风电装备制造基地正式投运

巴西巴伊亚州当地时间8月27日&#xff0c;金风科技巴西风电装备制造基地投运仪式成功举办。巴西联邦政府矿业能源部&#xff08;Ministrio de Minas e Energia&#xff09;部长Alexandre Silveira、巴西巴伊亚州州长Jernimo Rodrigues、中国驻里约热内卢总领事田敏、金风科技总…

Java生成任意长度随机字符串(数字+字母)

根据对于uuid随机获取其index的值组成字符串。生成的字符串越长&#xff0c;唯一性越高。 int i 10;String uuid UUID.randomUUID().toString().replace("-", "");StringBuilder randomStr new StringBuilder();Random random new Random();for (int j…

宠物医院管理平台的设计与实现---附源码93796

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

人体存在雷达感应传感器,跌倒、人员存在探测,卫生间秒变智能空间

在这个科技日新月异的时代&#xff0c;智能家居已不再是遥不可及的梦想&#xff0c;而是悄然融入了我们生活的每一个角落。今天&#xff0c;就让我们一起探索一项革新性的技术——人体雷达感应传感器&#xff0c;在卫生间这一私密而重要的空间里&#xff0c;如何以非凡的智慧&a…

6个岗位抢1个人,百万年薪抢毕业生?大厂打响AI人才战

“24岁毕业时年薪50万元&#xff0c;到了30岁大概能升到P7(注&#xff1a;职级名称&#xff09;&#xff0c;那时就能年薪百万了。” 从上海交大硕士毕业后&#xff0c;出生于2000年的赵宏在今年入职腾讯&#xff0c;担任AI算法工程师&#xff0c;成为AI风口下第一批就业的年轻…

第二证券:a股为什么总在3000点徘徊?涨不上去的原因有哪些?

首要&#xff0c;要了解A股3000点指的是大盘指数&#xff0c;首要是反映A股的股价走势。一般情况下&#xff0c;上证指数表明的便是大盘指数&#xff0c;上证指数3000点&#xff0c;也便是大盘指数3000点。 指数常常环绕3000点徜徉&#xff0c;造成这种情况的原因有&#xff1…

USB3.2 摘录(12)

系列文章目录 USB3.2 摘录&#xff08;一&#xff09; USB3.2 摘录&#xff08;二&#xff09; USB3.2 摘录&#xff08;三&#xff09; USB3.2 摘录&#xff08;四&#xff09; USB3.2 摘录&#xff08;五&#xff09; USB3.2 摘录&#xff08;六&#xff09; USB3.2 摘录&…

揭秘2024下半年大学生必考证时间表!

2024下半年&#xff0c;大学生考证时间表已公布&#xff0c;为各位学子规划学术和职业发展提供了明确的时间节点。 以下是值得重点关注的几类考试及其相关时间安排&#xff1a; 一、外语类考试安排&#xff0c;英语四六级考试&#xff0c;报名时间预计在9月&#xff0c;而笔试…

C语言 | Leetcode C语言题解之第381题O(1)时间插入、删除和获取随机元素-允许重复

题目&#xff1a; 题解&#xff1a; #define DYNAMIC_ARRAY_SIZE 128typedef struct {int *data;int size, capacity; } dynamic_array_t; dynamic_array_t *dynamic_array_init() {dynamic_array_t *da malloc(sizeof(dynamic_array_t));da->size 0, da->capacity D…

支付宝开放平台-开发者社区——AI 日报 「8 月 29 日」

1 3分钟千人被裁&#xff0c;IBM中国大败退 量子位&#xff5c;阅读原文 该公司首席执行官 Arvind Krishna早前就表示&#xff0c;公司将在未来五年内&#xff0c;用人工智能取代8000个岗位工作。早在上一轮AI浪潮之初&#xff0c;IBM就已经实现了智能问答系统&#xff0c;成…

基因生物行业大数据中心之间高效数据备份怎么去实现

随着高通量测序技术的迅猛发展&#xff0c;基因数据的积累正以惊人的速度增长。预计到2025年&#xff0c;全球的测序能力将达到Zb的规模。面对海量的数据&#xff0c;传统的数据备份方法已经显得不够用&#xff0c;难以满足跨地区备份和灾难恢复的需求。因此&#xff0c;基因生…

SpringCloudGateway网关技术

一、认识网关 二、网关服务初始化 1、新建网关module、引入依赖 <dependencies><!--common&#xff0c;自己的通用工具类--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0<…

亲测好用,ChatGPT 3.5/4.0新手使用手册,最全论文指令手册~ 【2024年9月 更新】

本以为遥遥领先的GPT早就普及了&#xff0c;但小伙伴寻找使用的热度一直高居不下&#xff0c;其实现在很简单了&#xff01; 国产大模型快200家了&#xff0c;还有很多成熟的国内AI产品&#xff0c;跟官网一样使用&#xff0c;还更加好用~ ① 3.5 大多数场景是够用的&#xff…

不良信息公示

系统应用于对各类不良信息进行公示 1、背景 针对目前各类打工人的工作生活&#xff0c;所遇仲裁无门&#xff0c;投诉无果的现象&#xff0c;以及各类公司有恃无恐的态度作为背景&#xff0c;特此对开源项目进行修改&#xff0c;诞生了此套系统 2、系统信息收集 为保障小人…

大模型时代,算法工程师的黄金时代

在大模型时代&#xff0c;算法工程师的角色已经超越了传统的编程和算法优化&#xff0c;他们成为了推动技术革新和业务发展的关键力量。作为一名算法工程师&#xff0c;我深刻地感受到这个时代对我们的新要求和期待。回想起我刚刚踏入这个领域时&#xff0c;深度学习还只是少数…

【多视图聚类】Reconsidering Representation Alignment for Multi-view Clustering

Reconsidering Representation Alignment for Multi-view Clustering CVPR 2021 0.论文摘要和信息 摘要 对齐视图表示的分布是当今用于深度多视图聚类的最先进模型的核心组件。然而&#xff0c;我们发现了nävely对齐表示分布的几个缺点。我们证明了这些缺点既导致表示空间…

07-问题-如何在Python中检查字符串是否为有效的标识符?

问题-如何在Python中检查字符串是否为有效的标识符&#xff1f; 规则 在Python中&#xff0c;标识符用来表示变量、函数、类、模块或其他对象的名字&#xff0c;并且需要遵循以下规则&#xff1a;可以由字母&#xff08;包括大小写&#xff09;、数字和下划线组成。不能以数字…