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

173 篇文章 1 订阅
67 篇文章 0 订阅

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配置空间的访问时,这些函数会直接返回一个“功能不支持”的错误码。

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

109702008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值