当前位置: 首页 > news >正文

如何编写Linux PCI设备驱动器 之一

如何编写Linux PCI设备驱动器 之一

  • PCI寻址
  • PCI驱动器使用的API
    • pci_register_driver()
      • pci_driver结构
      • pci_device_id结构
  • 如何查找PCI设备
  • 存取PCI配置空间
    • 读配置空间APIs
    • 写配置空间APIs
    • where的常量值
      • 共用部分
      • 类型0
      • 类型1

PCI总线通过使用比ISA更高的时钟速率来实现更好的性能;它是时钟运行在 25 或 33 MHz,并且最近还部署了 66 MHz 甚至 133 MHz 实施方案。而且,它配备了32位数据总线,并已扩展了64位包含在规范中。

PCI总线是一种独立于平台的计算机总线,这是 PCI 的一个特别重要的特性。PC 世界一直由处理器特定的接口标准主导。目前 PCI广泛用于不同的平台,比如,X86、ARM、Alpha、PowerPC,以及其他一些平台。

PCI 设备是无跳线的。与大多数较旧的外设不同,它在启动时,自动配置。驱动程序编写者必须关注 PCI自动检测能力。 设备驱动程序必须能够访问设备中的配置信息,以完成初始化。

PCI寻址

每个 PCI 外设由总线号、设备号和功能号来标识。
PCI 规范允许单个系统承载多达 256 条总线,但是由于256总线对于许多大型系统来说是不够的,Linux现在支持PCI域。

  • 每个 PCI 域最多可以承载 256 条总线。
  • 每个总线最多可容纳 32 人设备。
  • 每个设备最多具有8个功能。

所以,每个功能可以通过一个16 位地址进行标识。Linux驱动程序不需要处理这些二进制地址,因为,驱动器是通过使用称为 pci_dev 的特定数据结构,来对设备进行操作。

在单个系统中,通过桥将多个总线连接在一起。桥是一种专用 PCI 外设,它将两个总线连接起来。

PCI系统的总体布局就像一棵树,其中每条总线都连接到上层总线,直至树根处的总线 0。

lspci可以显示 PCI 外设的16位硬件地址。这些存储在 struct pci_dev结构对象中。

PCI设备的sysfs显示就使用了这种寻址方案,但添加PCI 域信息。

当显示硬件地址时,它可以显示为

  • 两个值,一个 8 位总线号,一个 8 位设备和功能号。
  • 三个值,总线、设备和功能。
  • 四个值,域、总线、设备和功能。

使用lspci显示在系统中的PCI设备,lspci显示总线号、设备号和功能号。

~$ lspci | cut -d ":" -f1-2
00:00.0 Host bridge
00:01.0 ISA bridge
00:01.1 IDE interface
00:02.0 VGA compatible controller
00:03.0 Ethernet controller
00:04.0 System peripheral
00:05.0 Multimedia audio controller
00:06.0 USB controller
00:07.0 Bridge
00:0b.0 USB controller
00:0d.0 SATA controller

使用tree显示在系统中的PCI设备,tree显示域号,总线号、设备号和功能号。

~$ tree /sys/bus/pci/devices/
/sys/bus/pci/devices/
├── 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0
├── 0000:00:01.0 -> ../../../devices/pci0000:00/0000:00:01.0
├── 0000:00:01.1 -> ../../../devices/pci0000:00/0000:00:01.1
├── 0000:00:02.0 -> ../../../devices/pci0000:00/0000:00:02.0
├── 0000:00:03.0 -> ../../../devices/pci0000:00/0000:00:03.0
├── 0000:00:04.0 -> ../../../devices/pci0000:00/0000:00:04.0
├── 0000:00:05.0 -> ../../../devices/pci0000:00/0000:00:05.0
├── 0000:00:06.0 -> ../../../devices/pci0000:00/0000:00:06.0
├── 0000:00:07.0 -> ../../../devices/pci0000:00/0000:00:07.0
├── 0000:00:0b.0 -> ../../../devices/pci0000:00/0000:00:0b.0
└── 0000:00:0d.0 -> ../../../devices/pci0000:00/0000:00:0d.0

PCI驱动器使用的API

通过 pci_register_driver(),PCI 驱动程序发现系统中的 PCI 设备。当PCI通用代码发现新设备时,将通知具有匹配“描述”的驱动程序。

pci_register_driver() 将大部分设备探测工作留给 PCI 层,并支持设备热插拔。 pci_register_driver() 调用需要传入函数指针表,和驱动程序的高级结构。

一旦驱动程序知道PCI设备,并得到控制权,则,驱动程序通常需要执行以下初始化:

  • 启用设备
  • 请求MMIO/IOP资源
  • 设置 DMA 掩码大小,包括连续和流 DMA
  • 分配并初始化共享控制数据, 调用pci_allocate_coherent()函数分配数据空间
  • 访问设备配置空间(如果需要)
  • 登记IRQ处理程序, 调用request_irq()
  • 初始化非 PCI, 即特定芯片部分
  • 启用 DMA/处理引擎

pci_register_driver()

PCI 设备驱动程序在初始化期间调用 pci_register_driver(),参数是驱动程序的结构指针:
该函数的词法:
int pci_register_driver(struct pci_driver *drv)

pci_driver结构

struct pci_driver {const char              *name;const struct pci_device_id *id_table;int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);void (*remove)(struct pci_dev *dev);int (*suspend)(struct pci_dev *dev, pm_message_t state);int (*resume)(struct pci_dev *dev);void (*shutdown)(struct pci_dev *dev);int (*sriov_configure)(struct pci_dev *dev, int num_vfs);int (*sriov_set_msix_vec_count)(struct pci_dev *vf, int msix_vec_count);u32 (*sriov_get_vf_total_msix)(struct pci_dev *pf);const struct pci_error_handlers *err_handler;const struct attribute_group **groups;const struct attribute_group **dev_groups;struct device_driver    driver;struct pci_dynids       dynids;bool driver_managed_dma;
};
结构成员名称意义
name驱动器名称
id_table驱动程序支持设备的ID表的指针。大多数驱动程序应使用 MODULE_DEVICE_TABLE (pci,…) 导出此表。
probe在对现有设备执行 pci_register_driver() ,或稍后插入新设备时。对于与 ID 表匹配,且尚未被其他驱动程序“拥有”的所有 PCI 设备,将调用此探测函数。对于ID表中的条目与设备匹配的每个设备,此函数都会传递一个“struct pci_dev *”。当驱动程序选择获取设备的“所有权”时,探测函数返回零,否则返回错误代码(负数)。探测函数总是从进程上下文中调用,因此它可以休眠。
remove :无论是在驱动程序注销期间,还是手动将其从热插拔插槽中拔出,只要删除该驱动程序, 都会调用remove()函数。删除函数总是从进程上下文中调用,因此它可以休眠。
suspend将设备置于低功耗状态
resume将设备从低功耗状态唤醒。
shutdown挂接到reboot_notifier_list (kernel/sys.c)。目的是停止任何空闲的DMA操作。对于启用LAN唤醒 (NIC) ,或在重新启动之前,更改设备的电源状态非常有用。
sriov_configure可选的驱动程序回调函数,借助sysfs “sriov_numvfs”文件, 启用VF 数量的配置 。
sriov_set_msix_vec_countPF 驱动程序回调函数,用于更改 VF 上的 MSI-X 矢量数量。通过 sysfs“sriov_vf_msix_count”触发。这将更改 VF 消息控制寄存器中的 MSI-X 表大小。
sriov_get_vf_total_msixPF 驱动程序回调以获取可分发到 VF 的 MSI-X 矢量总数。
err_handler
groupssysfs 属性组。
dev_groups一旦设备绑定到驱动程序,该设备就会创建。dev_groups就是附加到该设备的属性。
driver驱动器模型结构
dynids动态添加的设备 ID 列表。
driver_managed_dma设备驱动程序不使用内核 DMA API 进行 DMA。对于大多数设备驱动程序来说,只要所有 DMA 都是通过内核 DMA API 处理的,就无需关心此标志。对于一些特殊的驱动程序,例如 VFIO 驱动程序,它们知道如何自己管理 DMA 并设置此标志,以便 IOMMU 层允许它们设置和管理自己的 I/O 地址空间。

pci_device_id结构

struct pci_device_id {__u32 vendor, device;__u32 subvendor, subdevice;__u32 class, class_mask;kernel_ulong_t driver_data;__u32 override_only;
};
结构中成员名称意义
vendor供应商ID
device设备ID
subvendor子系统供应商ID
subdevice子系统设备ID
class设备类、子类和“接口”。大多数驱动程序不需要指定 class/class_mask,因为供应商/设备通常就足够了。
class_mask限制比较类字段的哪些子字段。
driver_data驱动程序私有的数据。大多数驱动程序不需要使用 driver_data 字段。最佳实践是使用 driver_data 作为等效设备类型的静态列表的索引,而不是将其用作指针。
override_only仅当 dev->driver_override 是该驱动程序时才匹配。

如何查找PCI设备

  1. 按供应商和设备 ID 搜索
struct pci_dev *dev = NULL;
while (dev = pci_get_device(VENDOR_ID, DEVICE_ID, dev))configure_device(dev);
  1. 按类ID搜索
pci_get_class(CLASS_ID, dev)
  1. 按供应商/设备和子系统供应商/设备ID进行搜索
pci_get_subsys(VENDOR_ID,DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev)

存取PCI配置空间

读配置空间APIs

int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn,  int where, u8 *val);
int pci_bus_read_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 *val);
int pci_bus_read_config_dword(struct pci_bus *bus, unsigned int devfn,int where, u32 *val);

写配置空间APIs

int pci_bus_write_config_byte(struct pci_bus *bus, unsigned int devfn,int where, u8 val);
int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 val);
int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 val);

where的常量值

linux/pci_regs.h包含所有配置空间位置的常量定义。

共用部分

#define PCI_VENDOR_ID           0x00    /* 16 bits */
#define PCI_DEVICE_ID           0x02    /* 16 bits */
#define PCI_COMMAND             0x04    /* 16 bits */#define PCI_STATUS              0x06    /* 16 bits */
#define PCI_CLASS_REVISION      0x08    /* High 24 bits are class, low 8 revision */
#define PCI_REVISION_ID         0x08    /* Revision ID */#define PCI_CACHE_LINE_SIZE     0x0c    /* 8 bits */
#define PCI_LATENCY_TIMER       0x0d    /* 8 bits */
#define PCI_HEADER_TYPE         0x0e    /* 8 bits */
#define PCI_BIST                0x0f    /* 8 bits */#define PCI_BASE_ADDRESS_0      0x10    /* 32 bits */
#define PCI_BASE_ADDRESS_1      0x14    /* 32 bits [htype 0,1 only] */

类型0

/* Header type 0 (normal devices) */
#define PCI_BASE_ADDRESS_2      0x18    /* 32 bits [htype 0 only] */
#define PCI_BASE_ADDRESS_3      0x1c    /* 32 bits */
#define PCI_BASE_ADDRESS_4      0x20    /* 32 bits */
#define PCI_BASE_ADDRESS_5      0x24    /* 32 bits */#define PCI_CARDBUS_CIS         0x28
#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
#define PCI_SUBSYSTEM_ID        0x2e
#define PCI_ROM_ADDRESS         0x30    /* Bits 31..11 are address, 10..1 reserved */#define PCI_CAPABILITY_LIST     0x34    /* Offset of first capability list entry *//* 0x35-0x3b are reserved */#define PCI_INTERRUPT_LINE      0x3c    /* 8 bits */
#define PCI_INTERRUPT_PIN       0x3d    /* 8 bits */
#define PCI_MIN_GNT             0x3e    /* 8 bits */
#define PCI_MAX_LAT             0x3f    /* 8 bits */

类型1

/* Header type 1 (PCI-to-PCI bridges) */
#define PCI_PRIMARY_BUS         0x18    /* Primary bus number */
#define PCI_SECONDARY_BUS       0x19    /* Secondary bus number */
#define PCI_SUBORDINATE_BUS     0x1a    /* Highest bus number behind the bridge */
#define PCI_SEC_LATENCY_TIMER   0x1b    /* Latency timer for secondary interface */
#define PCI_IO_BASE             0x1c    /* I/O range behind the bridge */
#define PCI_IO_LIMIT            0x1d
#define PCI_SEC_STATUS          0x1e    /* Secondary status register, only bit 14 used */
#define PCI_MEMORY_BASE         0x20    /* Memory range behind */
#define PCI_MEMORY_LIMIT        0x22
#define PCI_PREF_MEMORY_BASE    0x24    /* Prefetchable memory range behind */
#define PCI_PREF_MEMORY_LIMIT   0x26
#define PCI_PREF_BASE_UPPER32   0x28    /* Upper half of prefetchable memory range */
#define PCI_PREF_LIMIT_UPPER32  0x2c
#define PCI_IO_BASE_UPPER16     0x30    /* Upper half of I/O addresses */
#define PCI_IO_LIMIT_UPPER16    0x32
#define PCI_ROM_ADDRESS1        0x38    /* Same as PCI_ROM_ADDRESS, but for htype 1 */
#define PCI_BRIDGE_CONTROL      0x3e

相关文章:

如何编写Linux PCI设备驱动器 之一

如何编写Linux PCI设备驱动器 之一 PCI寻址PCI驱动器使用的APIpci_register_driver()pci_driver结构pci_device_id结构 如何查找PCI设备存取PCI配置空间读配置空间APIs写配置空间APIswhere的常量值共用部分类型0类型1 PCI总线通过使用比ISA更高的时钟速率来实现更好的性能&…...

梯度弥散问题及解决方法

梯度弥散问题及解决方法 简要阐述梯度弥散发生的原因以及现象针对不同发生原因有什么解决方案1. 使用ReLU及其变体激活函数2. 权重初始化3. 批量归一化(Batch Normalization)4. 残差连接(Residual Connections)5. 梯度裁剪(Gradient Clipping)简要阐述梯度弥散发生的原因…...

Python中pickle文件操作及案例-学习篇

一、简介 Pickle 算是Python的一种数据序列化方法,它能够将对象转换为字节流,进而可以保存到文件中或通过网络传输给其他Python程序。这种方式非常适合快速简便地保存复杂的数据结构,例如列表、字典、自定义对象等。 二、pickle文件的读写 …...

微服务日常总结

1.当我们在开发中,需要连接多个库时,可以在yml中进行配置。 当在查询的时候,跨库时,需要通过DS 注解来指定,需要yml配置需要保持一致。 2. 当我们想把数据存入到clob类型中,需要再字段 的占位符后面加上j…...

C和C++内存管理

C和C内存管理 (一)C/C内存分布(二)C语言动态内存管理(三)c内存管理(3.1)new/delete操作内置类型(3.2)new和delete操作自定义类型 (四)…...

axios取消请求

1.使用CancelToken: class RequestHttp {service: AxiosInstance;public constructor(config: AxiosRequestConfig) {// 实例化axiosthis.service axios.create(config);/*** description 请求拦截器* 客户端发送请求 -> [请求拦截器] -> 服务器*/this.service.interce…...

阿里中间件——diamond

一、前言 最近工作不忙闲来无事,仔细分析了公司整个项目架构,发现用到了很多阿里巴巴集团开源的框架,今天要介绍的是中间件diamond. 二、diamond学习笔记 1、diamond简介 diamond是一个管理持久配置(持久配置是指配置数据会持久化…...

pyenv -- 一款macos下开源的多版本python环境安装管理工具 国内加速版安装 + 项目venv虚拟环境 pip加速 使用与总结

一个比较方便实用的python多版本环境安装管理工具, 阿里云加速版本 pyenv安装方法: 直接克隆本下面到你的本地目录,然后设置环境变量即可 git clone https://gitee.com/tekintian/pyenv.git ~/.pyenv 环境变量配置 在~/.bash_profile 或者 .zshrc 中增加环境变量 export …...

VitePress 自定义 CSS 指南

VitePress 是一款基于 Vite 和 Vue 3 的静态网站生成器,专为文档编写而设计。尽管 VitePress 提供了丰富的默认主题,但在某些情况下,我们可能需要对其进行更深入的定制以满足特定的视觉需求。本文将详细介绍如何通过覆盖根级别的 CSS 变量来自…...

【舍入,取整,取小数,取余数丨Excel 函数】

数学函数 1、Round函数 Roundup函数 Rounddown函数 取整:(Int /Trunc)其他舍入函数: 2、Mod函数用Mod函数提取小数用Mod函数 分奇偶通过身份证号码判断性别 1、Round函数 Roundup函数 Rounddown函数 Round(数字,保留几位小数)(四…...

无线信道中ph和ph^2的场景

使用 p h ph ph的情况: Rayleigh 分布的随机变量可以通过两个独立且相同分布的零均值、高斯分布的随机变量表示。设两个高斯随机变量为 X ∼ N ( 0 , σ 2 ) X \sim \mathcal{N}(0, \sigma^2) X∼N(0,σ2)和 Y ∼ N ( 0 , σ 2 ) Y \sim \mathcal{N}(0, \sigma^2)…...

HCIA--实验五:静态路由综合实验

静态路由综合实验 一、实验内容: 1.需求/目的: 在ensp模拟器中使用四个路由器,并且在路由器上创建loopback接口,相当于连接了一台主机,通过配置静态路由的方式实现全网通。 二、实验过程 1.道具: 4个…...

不同vlan之间的通信方法

1.通过路由器的物理接口 1.给PC1,PC2配置IP地址,网关2.进入交换机配置vlan,交换机所有口都配置access口并绑定vlan3.配置路由器,进入路由器的两个接口配置网关IP和掩码缺点:成本高,每增加一个vlan就需要一个物理端口和…...

java后端框架

框架就是对技术的封装。 本篇博客小博主首先对以后我们要学习的框架进行简单概述,使大家对框架有一定的基本概念。 一.mybatis mybatis就是对jdbc(数据库连接)进行封装,避免了jdbc中手动设置参数,手动映射结果的操作。…...

如何在Word中插入复选框

如何在Word中插入复选框:详细教程与技巧 在Word中插入复选框是一项非常实用的技巧,尤其是在制作问卷调查、待办事项清单、交互式表单或文档中需要用户进行选择时,复选框不仅能提高文档的功能性,还能显得更加专业。本文将详细讲解…...

Android 源码中jni项目 加载so目录小结

Android 源码中jni项目 加载so目录小结 文章目录 Android 源码中jni项目 加载so目录小结一、前言二、so目录验证测试1、jni so文件错误报错(1)报错1 - 未找到so文件:(2)报错2 - so文件中未找到native方法: …...

24/9/6算法笔记 kaggle 房屋价格

预测模型主要分为两大类: 回归模型:当你的目标变量是连续的数值时,你会使用回归模型进行预测。回归模型试图找到输入特征和连续输出之间的关联。一些常见的回归模型包括: 线性回归(Linear Regression)岭回归…...

【MA35D1】buildroot 编译使用经验

文章目录 芯片介绍Buildroot开发Linux实践环境搭建代码获取编译执行步骤(仅适用于我公司产品) 后续有需要更改的输出文件目录 芯片介绍 NuMicro MA35D1系列为一颗异核同构的多核心微处理器,适用于高端 Edge IIoT Gateway。它是基于双核 64 位…...

排查 MyBatis XML 配置中的 IF 语句与传值名称不匹配的 Bug

文章目录 本文档只是为了留档方便以后工作运维,或者给同事分享文档内容比较简陋命令也不是特别全,不适合小白观看,如有不懂可以私信,上班期间都是在得 前言,在改一个bug得时候发现一个有意思得问题,就是myb…...

数字证书与公钥基础设施

关注这个证书的其他相关笔记:NISP 一级 —— 考证笔记合集-CSDN博客 0x01:数字证书 数字证书是由第三方可信机构(一般是证书服务器)颁发的数字证书,可以证明身份的可信度。 数字证书具有以下特点以及性质&#xff1a…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋&#xff0c;无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话&#xff0c;配置.bahs_profile后也能解决上下翻页这些&#xff0c;但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...

Unity-ECS详解

今天我们来了解Unity最先进的技术——ECS架构&#xff08;EntityComponentSystem&#xff09;。 Unity官方下有源码&#xff0c;我们下载源码后来学习。 ECS 与OOP&#xff08;Object-Oriented Programming&#xff09;对应&#xff0c;ECS是一种完全不同的编程范式与数据架构…...