概要性了解Linux的总线设备驱动

news2025/1/10 23:42:08

引言

假如我们的板子上有很多LED,有时候需要操作这个LED,有时候需要操作另一个LED,我们希望代码可以清晰地组织在一起,方便扩展,同时自动为这些具体的设备生成对应的设备文件以供用户空间使用。

在Linux中,可以通过总线设备驱动编程的方法来实现这个需求。

这种方法将驱动程序划分为三个部分:总线 (Bus)、设备 (Device) 和 驱动 (Driver)。

  • 设备: 代表具体的硬件资源,例如一个 LED,同时描述了它的相关硬件信息(如寄存器地址、IRQ 等)。
  • 驱动: 实现了操作该设备的具体逻辑代码。
  • 总线: 负责在设备和驱动之间建立匹配关系,并调用驱动程序对设备进行初始化。

匹配过程可以大致理解为:

  1. 设备代码: 告诉总线有哪些硬件资源需要被驱动。
  2. 总线程序: 根据设备的信息,去查找注册过的驱动程序中是否有能够匹配的驱动。
  3. 驱动程序: 被总线调用后完成设备的初始化,并通过内核 API 为用户空间创建对应的设备文件(如 /dev/led0),这样这些设备就可以被用户访问和使用了。

这种设计具有模块化、可扩展和动态加载的优点,适用于开发复杂的嵌入式系统。

总线设备驱动的相关基础知识

总线设备驱动编程方法是 Linux 驱动模型的重要组成部分。总线、设备和驱动是 Linux 设备模型的核心三大概念。以下是详细价绍:

1. 总线设备驱动的三个基本概念

总线设备驱动编程方法将系统中的硬件划分为三部分:总线 (Bus)设备 (Device)驱动 (Driver),它们通过 Linux 的设备模型进行抽象和管理。

  • 总线 (Bus): 描述一类硬件连接标准(如 I2C、SPI、PCI 等),负责在设备和驱动之间建立匹配关系。
  • 设备 (Device): 表示挂载在某条总线上的具体硬件设备,通常由总线代码创建。
  • 驱动 (Driver): 用于操作某种特定设备的代码实现。

2. 总线设备驱动的结构

在 Linux 驱动模型中,设备和驱动通过总线进行联系,主要通过以下方式实现:

  • struct bus_type: 定义总线类型并实现其操作函数。
  • struct device: 定义具体的硬件设备。
  • struct device_driver: 定义设备驱动。
  • 匹配机制: 设备和驱动通过名字或其他标识符进行匹配,比如使用 id_tableof_match_table

3. 总线设备驱动的实现步骤

以下是实现总线设备驱动的一般步骤:

1. 注册总线

实现和注册总线类型:

static struct bus_type my_bus_type = {
    .name = "my_bus",
    .match = my_bus_match, // 匹配函数
    .probe = my_bus_probe, // 设备绑定驱动时调用
    .remove = my_bus_remove, // 解绑时调用
};
bus_register(&my_bus_type);

2. 注册设备

创建并注册设备:

static struct device my_device = {
    .init_name = "my_device",
    .bus = &my_bus_type,
};
device_register(&my_device);

3. 注册驱动

创建并注册驱动:

static struct device_driver my_driver = {
    .name = "my_device", // 必须与设备名字一致
    .bus = &my_bus_type,
};
driver_register(&my_driver);

4. 匹配设备和驱动

设备和驱动通过总线的 match 函数匹配,一旦匹配成功,就会调用 probe 函数进行初始化。

4. 设备树支持

如果硬件通过设备树描述,总线和设备的关联通常由 of_match_tableacpi_match_table 提供,匹配更加自动化。

5. 总线设备驱动的优点

  • 模块化设计: 硬件抽象更清晰,设备和驱动可以独立开发。
  • 动态管理: 支持动态加载和卸载设备及驱动。
  • 通用性强: 可以适配不同的总线类型。

常见的符合Linux标准的总线设备驱动

符合Linux标准的总线设备驱动被称为标准总线设备驱动 ,以下是常见的 标准总线设备驱动 以及它们的介绍:


1. Platform 总线设备驱动

简介
  • Platform 总线 是一个虚拟的、与硬件无关的总线,主要用于管理 片上系统 (SoC) 上的设备。
  • 适合处理没有明确硬件总线(如 I2C、SPI 等)约束的设备,比如板载 LED、GPIO、按键等。
组成
  • 设备:用 struct platform_device 描述,表示具体硬件资源。
  • 驱动:用 struct platform_driver 描述,实现对硬件的具体操作。
设备与驱动的匹配
  • 匹配基于设备名称(platform_device.nameplatform_driver.name)。
  • 注册函数:platform_device_register()platform_driver_register()
特点
  • 简单易用,适用于自定义硬件资源的管理。
  • 常用于嵌入式开发,特别是 SoC 内部的设备。
示例
// 注册一个设备
static struct platform_device my_device = {
    .name = "my_platform_device",
    .id = -1,
};
platform_device_register(&my_device);

// 编写对应的驱动
static int my_probe(struct platform_device *pdev) {
    printk("Device probed: %s\n", pdev->name);
    return 0;
}

static struct platform_driver my_driver = {
    .driver = {
        .name = "my_platform_device",
    },
    .probe = my_probe,
};
platform_driver_register(&my_driver);

2. I2C 总线设备驱动

简介
  • I2C 总线 是一种串行总线,用于连接低速设备(如 EEPROM、传感器、RTC 等)。
  • Linux 内核为 I2C 总线提供了完整的驱动框架。
组成
  • 设备:用 struct i2c_client 描述,表示连接到 I2C 总线的设备。
  • 驱动:用 struct i2c_driver 描述,实现设备的操作逻辑。
  • 总线:用 struct i2c_adapter 描述,表示 I2C 控制器。
设备与驱动的匹配
  • 匹配基于设备名称(i2c_client.namei2c_driver.id_table)。
  • 注册函数:i2c_new_device()i2c_add_driver()
特点
  • 适合驱动所有通过 I2C 总线连接的设备。
  • 支持通过设备树或平台数据描述设备。
示例
// 定义 I2C 驱动
static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {
    printk("I2C device probed: %s\n", client->name);
    return 0;
}

static struct i2c_driver my_driver = {
    .driver = {
        .name = "my_i2c_device",
    },
    .probe = my_i2c_probe,
    .id_table = (struct i2c_device_id[]) {
        { "my_i2c_device", 0 },
        { }
    },
};
i2c_add_driver(&my_driver);

3. SPI 总线设备驱动

简介
  • SPI 总线 是一种高速的全双工串行总线,用于连接设备如 SPI 闪存、显示屏、传感器等。
  • Linux 内核为 SPI 总线提供了通用的驱动框架。
组成
  • 设备:用 struct spi_device 描述,表示挂载到 SPI 总线的设备。
  • 驱动:用 struct spi_driver 描述,实现设备的操作逻辑。
  • 总线:用 struct spi_master 描述,表示 SPI 控制器。
设备与驱动的匹配
  • 匹配基于设备名称(spi_device.namespi_driver.driver.name)。
  • 注册函数:spi_register_device()spi_register_driver()
特点
  • 提供对 SPI 总线的完整支持,适合驱动各种 SPI 外设。
  • 支持通过设备树或平台数据描述设备。
示例
// 定义 SPI 驱动
static int my_spi_probe(struct spi_device *spi) {
    printk("SPI device probed: %s\n", spi->modalias);
    return 0;
}

static struct spi_driver my_driver = {
    .driver = {
        .name = "my_spi_device",
    },
    .probe = my_spi_probe,
};
spi_register_driver(&my_driver);

4. USB 驱动

简介
  • USB 总线 是一种常见的高速串行总线,用于连接外设如键盘、鼠标、存储设备等。
  • Linux 提供了完整的 USB 驱动模型。
组成
  • 设备:用 struct usb_device 描述,表示 USB 总线上的设备。
  • 驱动:用 struct usb_driver 描述,实现对设备的操作逻辑。
  • 总线:USB 控制器实现了 USB 总线管理。
设备与驱动的匹配
  • 匹配基于设备的 Vendor ID 和 Product ID。
  • 注册函数:usb_register_driver()
特点
  • 支持所有标准 USB 设备协议。
  • 提供 USB 核心模块,用于设备的管理和匹配。
示例
// 定义 USB 驱动
static int my_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) {
    printk("USB device probed\n");
    return 0;
}

static struct usb_device_id my_usb_table[] = {
    { USB_DEVICE(0x1234, 0x5678) },
    { }
};

static struct usb_driver my_driver = {
    .name = "my_usb_device",
    .probe = my_usb_probe,
    .id_table = my_usb_table,
};
usb_register_driver(&my_driver);

5. PCI 驱动

简介
  • PCI 总线 是一种高速并行总线,用于连接设备如网卡、显卡等。
  • Linux 内核为 PCI 提供了标准的驱动框架。
组成
  • 设备:用 struct pci_dev 描述,表示 PCI 总线上的设备。
  • 驱动:用 struct pci_driver 描述,实现设备的操作逻辑。
设备与驱动的匹配
  • 匹配基于设备的 Vendor ID 和 Device ID。
  • 注册函数:pci_register_driver()
特点
  • 支持标准的 PCI 协议,适合驱动各种 PCI 设备。
  • 提供内核接口简化驱动开发。
示例
// 定义 PCI 驱动
static int my_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) {
    printk("PCI device probed\n");
    return 0;
}

static struct pci_device_id my_pci_table[] = {
    { PCI_DEVICE(0x1234, 0x5678) },
    { }
};

static struct pci_driver my_driver = {
    .name = "my_pci_device",
    .id_table = my_pci_table,
    .probe = my_pci_probe,
};
pci_register_driver(&my_driver);

总结

这些标准总线设备驱动都依赖 Linux 内核的驱动模型,统一了设备与驱动的管理方式,极大地简化了驱动开发工作:

  • Platform 总线设备驱动:用于片上资源。
  • I2C 和 SPI 驱动:用于串行总线设备。
  • USB 驱动:用于外部高速设备。
  • PCI 驱动:用于高速并行设备。

它们的核心思想相同,只是针对不同的总线和设备提供了不同的实现。

Linux源码中提供的与I2C有关的总线设备驱动有什么用?

问:Linux源码中提供的与I2C有关的总线设备驱动有什么用,比如drivers\nfc\nfcmrvl\i2c.c,我实际在驱动一个I2C时还是得去写驱动程序呀?我能不能借助这些文件填个I2C的设备信息就完成了I2C设备的驱动?

答:Linux 源代码中像 drivers/nfc/nfcmrvl/i2c.c 这样的文件,确实是与 I2C 驱动相关的代码。它们的作用是为某些具体的 I2C 设备提供驱动程序实现。但在实际开发中,是否需要完全自行编写驱动程序,或者能否借助现有代码和框架,取决于具体设备和使用场景。以下是详细的说明:


1. 源代码中的 I2C 驱动文件的作用

drivers/nfc/nfcmrvl/i2c.c 这样的文件,是为特定的 I2C 外设 提供驱动实现。例如:

  • nfcmrvl 是一个 NFC 控制器的驱动程序,i2c.c 实现了通过 I2C 总线与硬件交互的功能。
  • 它的核心作用是:
    • 提供 probe 函数来初始化设备,配置寄存器。
    • 实现设备的操作逻辑(如读写数据、状态监控)。
    • 与 Linux 内核的 I2C 子系统协作,通过注册 i2c_driver 和设备匹配。

这些驱动文件是为具体硬件提供的参考实现,如果你的硬件设备刚好是类似的设备,可能可以直接使用或稍加改动就能满足需求。


2. 是否需要自己写驱动程序?

这取决于你的硬件设备是否已经有现成的驱动支持,或者是否可以使用通用驱动。

(1) 硬件有现成驱动支持
  • 如果你的 I2C 设备已经有厂商提供的驱动,或者 Linux 内核中已经有现成的驱动(比如像 drivers/nfc/nfcmrvl/i2c.c),你只需要:

    • 注册设备:通过设备树(Device Tree)或平台数据将设备信息传递给驱动。
    • 加载驱动:在内核中启用或加载相应的模块。
  • 示例:如果你使用的设备是一个支持 I2C 接口的 EEPROM 芯片(比如 AT24 系列),Linux 内核中已经有通用的 at24 驱动,你只需要在设备树中描述硬件信息:

    i2c1: i2c@1 {
        eeprom@50 {
            compatible = "atmel,24c02";
            reg = <0x50>;
        };
    };
    

    然后加载 at24 模块即可完成设备驱动,无需自己编写驱动程序。


(2) 硬件没有现成驱动支持
  • 如果你的设备是定制的,或者没有现成驱动,就需要自己编写驱动程序。
  • 不过,Linux 内核已经为 I2C 总线提供了完整的框架:
    • 你只需实现设备的特定逻辑(如寄存器配置、数据读写等)。
    • 借助内核的 I2C 框架,可以大幅简化驱动开发。

示例代码框架:

static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    // 设备初始化
    printk(KERN_INFO "Probing I2C device: %s\n", client->name);
    return 0;
}

static int my_i2c_remove(struct i2c_client *client)
{
    // 设备卸载
    printk(KERN_INFO "Removing I2C device: %s\n", client->name);
    return 0;
}

static const struct i2c_device_id my_i2c_id[] = {
    { "my_device", 0 },
    { }
};

static struct i2c_driver my_i2c_driver = {
    .driver = {
        .name = "my_device",
    },
    .probe = my_i2c_probe,
    .remove = my_i2c_remove,
    .id_table = my_i2c_id,
};

module_i2c_driver(my_i2c_driver);

3. 能否只填设备信息完成 I2C 设备驱动?

在某些情况下,确实可以,具体情况如下:

(1) 使用通用驱动
  • 如果设备只需要简单的读写操作(如 EEPROM、GPIO 扩展芯片等),Linux 已提供一些通用驱动,可以通过设备树或平台代码传递设备信息。
  • 示例:
    • at24 驱动支持 I2C EEPROM。
    • gpio-mcp23s08 驱动支持 I2C GPIO 扩展芯片。

你只需提供设备树信息,无需自己编写驱动程序。

(2) 自定义硬件逻辑
  • 如果硬件逻辑很复杂,比如需要进行协议栈解析、状态管理等,那么仅靠设备信息无法满足需求,仍需编写驱动程序。

4. 借助现有代码的可能性

如果现有驱动(如 drivers/nfc/nfcmrvl/i2c.c)与目标设备逻辑类似,可以:

  1. 参考现有代码:根据目标设备特性修改现有代码(如修改设备初始化、寄存器操作等)。
  2. 作为模板实现新驱动:通过复用现有驱动框架,减少开发工作量。

小结

  • Linux 源代码中 drivers/.../i2c.c 文件是为具体的 I2C 设备提供的驱动实现,它们遵循总线设备驱动模型,主要作用是控制对应硬件。
  • 如果你的硬件设备已经有现成驱动,可以直接使用或稍作配置完成驱动加载。
  • 如果没有现成驱动,可以借助 I2C 框架编写自己的驱动程序。
  • 在某些简单设备场景下,只需填充设备信息(通过设备树或平台代码)即可完成设备驱动。

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

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

相关文章

ue5玩家角色添加武器。切换武器位置,手上武器放到背上。演示一下人体插槽和武器的连接。仅仅演示,实际项目不是这么用的

把第一人称资源包导进来 这就是我们枪的骨骼网格体 我们找到这个骨骼 右手添加插槽 取个名字 因为武器上也有动画&#xff0c;所有武器单独写个蓝图类 新建一个蓝图类 BP_Weapon 把枪的蓝图拖到人的静态网格体下&#xff0c;成为一个部分 选中BP_Weapon的父类套接字…

微信小程序防止重复点击事件

直接写在app.wpy里面&#xff0c;全局可以调用 // 防止重复点击事件preventActive(fn) {const self this;if (this.globalData.PageActive) {this.globalData.PageActive false;if (fn) fn();setTimeout(() > {self.globalData.PageActive true;}, 3000); //设置该时间内…

Docker入门之docker基本命令

Docker入门之docker基本命令 官方网站&#xff1a;https://www.docker.com/ 1. 拉取官方镜像并创建容器&#xff08;以redis为例&#xff09; 拉取官方镜像 docker pull redis# 如果不需要添加到自定义网络使用这个命令&#xff0c;如需要&#xff0c;直接看第二步 docker r…

SQL Server中可以通过扩展事件来自动抓取阻塞

在SQL Server中可以通过扩展事件来自动抓取阻塞&#xff0c;以下是详细流程&#xff1a; 开启阻塞跟踪配置&#xff1a; • 执行以下SQL语句来启用相关配置&#xff1a; EXEC sp_configureshow advanced options, 1; RECONFIGURE; EXEC sp_configure blocked process thresh…

【VBA】【EXCEL】将某列内容横向粘贴到指定行

Sub CopyRowToColumn()On Error GoTo ErrorHandler 添加错误处理Application.ScreenUpdating FalseApplication.Calculation xlCalculationManualApplication.EnableEvents False 禁用事件处理Dim lastCol As LongDim lastRow As LongDim i As Long, colCount As LongDim …

基于机器学习的故障诊断(入门向)

一、原始信号的特征提取 1.EMD经验模态分解的作用 信号分析&#xff1a;EMD可以将信号分解为多个IMFs&#xff0c;每个IMF代表信号中的一个特定频率和幅度调制的成分。这使得EMD能够提供对信号的时频特征进行分析的能力&#xff08;特征提取用到的&#xff09;。信号去噪&…

多台PC共用同一套鼠标键盘

当环境中有多个桌面 pc 需要操作的时候&#xff0c;在 多台 pc 之间切换会造成很多的不方便 可以通过远程进行连接&#xff0c;但是有一个更好的方案是让多台机器之间共用同一套键盘鼠标 常用的解决方案 synergy 和 sharemouse&#xff0c;通过移动光标在不同的 pc 间切换 s…

[免费]微信小程序(高校就业)招聘系统(Springboot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序(高校就业)招聘系统(Springboot后端Vue管理端)&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序(高校就业)招聘系统(Springboot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…

Midjourney 应用:框架总结

Midjourney 应用&#xff1a;框架总结 官方的模板很简单&#xff0c;分成四个部分&#xff1a; 主体细节 & 背景风格、媒介、艺术家参数 我的总结 其实按照官方模板写&#xff0c;你已经能超过 90% 的初学者&#xff0c;但根据我的实验&#xff0c;我细化了他们的模板的…

【Maui】导航栏样式调整

前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移动和桌面应用。 使用 .NET MAUI&#xff0c;可从单个共享代码库开发可在 Android、iOS、macOS 和 Windows 上运行的应用。 .NET MAUI 是一款开放源代码应用&#xff0c;是 X…

uniapp 微信小程序内嵌h5实时通信

描述&#xff1a; 小程序webview内嵌的h5需要向小程序实时发送消息&#xff0c;有人说postMessage可以实现&#xff0c;所以试验一下&#xff0c;结果是实现不了实时&#xff0c;只能在特定时机后退、组件销毁、分享时小程序才能接收到信息&#xff08;小程序为了安全等考虑做了…

腾讯云AI代码助手编程挑战赛-厨房助手之AI大厨

腾讯云AI代码助手编程挑战赛-厨房助手之AI大厨 作品简介 身处当今如火箭般迅猛发展的互联网时代&#xff0c;智能聊天助手已然化身成为提升用户体验的关键利器&#xff0c;全方位渗透至人们的数字生活。 紧紧跟随着这股汹涌澎湃的时代浪潮&#xff0c;我毅然投身于极具挑战性…

网易云音乐登录两部手机:IP属地归属何方?

在数字化生活日益普及的今天&#xff0c;音乐平台成为了我们日常娱乐不可或缺的一部分。网易云音乐&#xff0c;作为众多音乐爱好者的首选&#xff0c;其丰富的音乐资源和个性化的推荐算法深受用户喜爱。然而&#xff0c;随着多设备登录成为常态&#xff0c;一个问题也随之浮现…

[工具]git克隆远程仓库到本地快速操作流程

一、新建空目录 二、初始化本地仓库 git init 初始化成功后&#xff0c;会在当前目录生成一个.git的目录。 三、关联远程仓库 git remote add origin <URL>这一步让本地仓库与远程仓库进行关联&#xff0c;origin是远程仓库的别名&#xff0c;可以自定义。 四、克隆…

如何在 Ubuntu 22.04 上集成 Collabora Online 教程

简介 在本教程中&#xff0c;我们将详细讲解如何在 Ubuntu 22.04 操作系统上安装 Collabora Online。 Collabora Online 是一个基于 LibreOffice 技术的开源办公套件。它提供了许多功能&#xff0c;其中最有用的一个功能是 Collabora 提供了 Word 文档、电子表格、演示文稿等…

Linux的内核空间中的日志打印函数printk的详解;如果设置`printk` 函数的默认日志级别和是否输出到终端控制台

引言 首先&#xff0c;要知道&#xff0c;内核空间是没有printf函数的&#xff0c;printf函数是是用户空间的标准 I/O 函数&#xff0c;而不是内核空间中的。 所以在运行于内核空间的程序中(比如驱动程序)&#xff0c;是不能使用printf函数的&#xff0c;但有时候我们又需要打…

Python编程实例-特征向量与特征值编程实现

特征向量与特征值编程实现 文章目录 特征向量与特征值编程实现1、什么是特征向量2、特征向量背后的直觉3、为什么特征向量很重要?4、如何计算特征向量?4、特征向量Python实现5、可视化特征向量6、总结线性代数是许多高级数学概念的基石,广泛应用于数据科学、机器学习、计算机…

202-01-06 Unity 使用 Tip1 —— UnityHub 模块卸载重装

文章目录 1 卸载模块2 更新配置文件3 重启 UnityHub 起因&#xff1a; ​ WebGL 平台打包程序报错&#xff0c;懒得修复了&#xff0c;因此粗暴地删了重装。但是 UnityHub 不支持卸载模块&#xff0c;因此手动配置。 1 卸载模块 ​ 以 Unity 6000.0.26f1c1 为例&#xff0c;其…

Git的简单介绍与如何安装Git

文章目录 前言一、初始git1.git是什么2.为什么要使用git(出现的问题)3.git是如何解决问题的 二、git的安装与卸载1.centos系统2.ubuntu系统3.windows 三、搭建git本地环境1.创建git本地仓库2.配置用户信息 总结 前言 本文简单引入git的相关内容。 一、初始git 1.git是什么 g…

Linux 进程入门:带你走进操作系统的核心地带(1)

&#x1f31f; 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。&#x1f31f; &#x1f6a9;用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 在 Linux 操作系…