【C语言】linux内核pci_enable_device函数和_PCI_NOP宏

2024-03-22 08:12

本文主要是介绍【C语言】linux内核pci_enable_device函数和_PCI_NOP宏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

pci_enable_device

一、注释

static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
{struct pci_dev *bridge;int err;int i, bars = 0;/** 此时电源状态可能是未知的,可能是由于新启动或者设备移除调用。* 因此获取当前的电源状态,这样像 MSI 消息写入这样的操作会按预期行为* (比如,如果设备在 enable 时确实位于 D0 状态)。*/if (dev->pm_cap) {u16 pmcsr;pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);}// 如果设备已经启用,只增加引用计数,然后直接返回if (atomic_inc_return(&dev->enable_cnt) > 1)return 0;        /* 已经启用 */// 查找上游桥设备,如果存在则启用它bridge = pci_upstream_bridge(dev);if (bridge)pci_enable_bridge(bridge);// 只跳过和 SR-IOV 相关的资源for (i = 0; i <= PCI_ROM_RESOURCE; i++)if (dev->resource[i].flags & flags)bars |= (1 << i);for (i = PCI_BRIDGE_RESOURCES; i < DEVICE_COUNT_RESOURCE; i++)if (dev->resource[i].flags & flags)bars |= (1 << i);// 实际启用 PCI 设备err = do_pci_enable_device(dev, bars);if (err < 0)atomic_dec(&dev->enable_cnt); // 如果启用失败,则减少引用计数return err;
}/*** pci_enable_device - 在驱动程序使用设备前初始化设备。* @dev: 需要初始化的 PCI 设备**  在驱动程序使用设备前初始化设备。要求底层代码去启用 I/O 和内存。*  如果设备已经进入休眠,则将其唤醒。注意,该函数可能会失败。**  注意如果我们反复调用这个函数,我们不会真正多次启用设备(我们只是增加计数)。*/
int pci_enable_device(struct pci_dev *dev)
{return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
}
EXPORT_SYMBOL(pci_enable_device);

在这段代码中,`pci_enable_device_flags` 是一个静态函数,它接受两个参数:一个指向 PCI 设备的指针和一个标志集合。函数的目的是启用指定的 PCI 设备,但首先会检查设备的电源管理(PM)能力和当前的电源状态,并递增设备的启用计数。如果设备已经启用,函数会立即返回。然后函数检查是否有上游桥设备,若存在则递归地启用桥设备。接下来,它会确定需要启用的资源并最终调用 do_pci_enable_device 函数。

pci_enable_device 函数封装了 pci_enable_device_flags 函数,用于启用 PCI 设备的 I/O 和内存资源。该函数导出了符号 pci_enable_device,使得其他内核模块可以通过符号名引用该函数。 

二、讲解

这段代码是Linux内核中用于启动PCI设备的一部分。下面将对这段代码的关键部分进行讲解。

static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)

这是一个静态函数,其目的是用给定的标志(`flags`)来启动一个PCI设备(`dev`)。

    struct pci_dev *bridge;int err;int i, bars = 0;

定义了指向PCI设备的桥接器的指针`bridge`,一个整型错误码`err`以及两个整型变量`i`和`bars`,其中`bars`用于跟踪需要启用的设备资源条目(BARs)。

    if (dev->pm_cap) {

检查设备(dev)是否支持电源管理(PM)功能。

        u16 pmcsr;pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);}

如果支持,就从设备的配置空间中读取电源管理控制状态寄存器(PMCSR),并更新设备当前的电源状态。

    if (atomic_inc_return(&dev->enable_cnt) > 1)return 0;        /* already enabled */

使用原子操作增加设备使能计数器`enable_cnt`,如果这不是第一次使能(计数大于1),则表示设备已经使能,函数直接返回0。

    bridge = pci_upstream_bridge(dev);if (bridge)pci_enable_bridge(bridge);

找到设备的上游桥接器(bridge),如果存在,就启用这个桥接器。

    for (i = 0; i <= PCI_ROM_RESOURCE; i++)...for (i = PCI_BRIDGE_RESOURCES; i < DEVICE_COUNT_RESOURCE; i++)...

这两个循环检查设备的所有资源条目,确定哪些BAR需要被启用,这基于给定的标志和资源的类型。

    err = do_pci_enable_device(dev, bars);if (err < 0)atomic_dec(&dev->enable_cnt);return err;
}

调用`do_pci_enable_device`来实际启用这些资源。如果有错误发生,减少使能计数器并返回错误码。

int pci_enable_device(struct pci_dev *dev)
{return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);
}
EXPORT_SYMBOL(pci_enable_device);

这是一个外部可见的函数,用默认的标志(内存和I/O资源)来启用一个PCI设备。

整体而言,这段代码是驱动程序在使用PCI设备之前,对设备进行初始化和启用操作的过程。它确保设备可以响应内存和I/O操作,以及从低功耗状态中唤醒设备。

本函数在drivers\pci\pci.c中定义。

_PCI_NOP宏

一、注释

/** 如果系统没有PCI,很显然这些函数会返回错误。定义这些函数为简单的内联函数,* 以避免在驱动程序中的复杂操作。*/
#define _PCI_NOP(o, s, t) \static inline int pci_##o##_config_##s(struct pci_dev *dev, \int where, t val) \{ return PCIBIOS_FUNC_NOT_SUPPORTED; } // 定义宏_PCI_NOP来构造内联函数,若PCI操作不被支持,则返回错误码#define _PCI_NOP_ALL(o, x)    _PCI_NOP(o, byte, u8 x) \ // 利用_PCI_NOP宏定义读写byte、word、dword三种操作_PCI_NOP(o, word, u16 x) \_PCI_NOP(o, dword, u32 x)_PCI_NOP_ALL(read, *) // 定义所有的read操作,如果系统无法执行PCI读取操作,它们将返回错误
_PCI_NOP_ALL(write,) // 定义所有的write操作,如果系统无法执行PCI写入操作,它们将返回错误

二、讲解

这段代码是用于操作PCI配置空间的宏定义和内联函数,适用于没有PCI支持的系统环境。在这种情况下,对PCI配置空间的读写操作将返回一个错误码,通常是因为PCI功能不被支持(`PCIBIOS_FUNC_NOT_SUPPORTED`)。
代码解释:
1. _PCI_NOP 宏定义: 它用于声明一个静态的内联函数,这个函数对应于PCI读或写操作,并返回一个错误码 PCIBIOS_FUNC_NOT_SUPPORTED。这个宏接收三个参数:操作类型 (o)、数据大小 (s) 和数据类型 (t)。具体来说:
    - o 表示操作类型,可以是 read 或 write。
    - s 表示数据的大小,可以是 byte(8位),`word`(16位)或 dword(32位)。
    - t 表示对应的数据类型,分别是 u8(unsigned 8-bit)、`u16` (unsigned 16-bit)、`u32` (unsigned 32-bit).
2. _PCI_NOP_ALL 宏定义: 它是一个帮助宏,用于针对读和写操作声明所有可能的大小的内联函数,即字节、字和双字。
3. _PCI_NOP_ALL(read, *) 和 _PCI_NOP_ALL(write,): 这两个宏分别为读和写操作创建了三个函数,即针对 byte、`word` 和 dword 大小的数据。对 read 操作的函数,传入变量 val 是一个指针(`*指明取地址),这是因为读操作需要一个指针来存放读到的值。对 write` 操作,`val` 是一个普通变量,因为写操作是将该值写入到PCI配置空间。
4. 函数 pci_##o##_config_##s: 这是一个构建函数名的宏,这里使用了宏的字符串化(##)来组合操作类型和数据大小,创建特定的函数名。例如,`pci_read_config_byte` 或 pci_write_config_dword。
总结一下,这段代码就是为不支持PCI操作的系统提供了一组空操作(no-operation, NOP)函数,以便在该系统的PCI驱动中使用,从而避免了在驱动代码中添加复杂的条件编译指令。当驱动尝试进行PCI配置空间的访问时,这些函数会直接返回一个“功能不支持”的错误码。

这篇关于【C语言】linux内核pci_enable_device函数和_PCI_NOP宏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/834901

相关文章

Linux搭建单机MySQL8.0.26版本的操作方法

《Linux搭建单机MySQL8.0.26版本的操作方法》:本文主要介绍Linux搭建单机MySQL8.0.26版本的操作方法,本文通过图文并茂的形式给大家讲解的非常详细,感兴趣的朋友一起看看吧... 目录概述环境信息数据库服务安装步骤下载前置依赖服务下载方式一:进入官网下载,并上传到宿主机中,适合离线环境

windows和Linux使用命令行计算文件的MD5值

《windows和Linux使用命令行计算文件的MD5值》在Windows和Linux系统中,您可以使用命令行(终端或命令提示符)来计算文件的MD5值,文章介绍了在Windows和Linux/macO... 目录在Windows上:在linux或MACOS上:总结在Windows上:可以使用certuti

Pandas中统计汇总可视化函数plot()的使用

《Pandas中统计汇总可视化函数plot()的使用》Pandas提供了许多强大的数据处理和分析功能,其中plot()函数就是其可视化功能的一个重要组成部分,本文主要介绍了Pandas中统计汇总可视化... 目录一、plot()函数简介二、plot()函数的基本用法三、plot()函数的参数详解四、使用pl

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

C语言中位操作的实际应用举例

《C语言中位操作的实际应用举例》:本文主要介绍C语言中位操作的实际应用,总结了位操作的使用场景,并指出了需要注意的问题,如可读性、平台依赖性和溢出风险,文中通过代码介绍的非常详细,需要的朋友可以参... 目录1. 嵌入式系统与硬件寄存器操作2. 网络协议解析3. 图像处理与颜色编码4. 高效处理布尔标志集合

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

C 语言中enum枚举的定义和使用小结

《C语言中enum枚举的定义和使用小结》在C语言里,enum(枚举)是一种用户自定义的数据类型,它能够让你创建一组具名的整数常量,下面我会从定义、使用、特性等方面详细介绍enum,感兴趣的朋友一起看... 目录1、引言2、基本定义3、定义枚举变量4、自定义枚举常量的值5、枚举与switch语句结合使用6、枚