概述

    内核的PCI子系统(即PCI层)提供了不同设备一些通用的功能,以便简化各种设备驱动程序。
    PCI层重要结构体如下:
pci_device_id
    设备标识,根据PCI标志定义的ID,而不是Linux本地的。

pci_dev
    类似于网络设备的net_device。每个PCI会被分配一个net_dev实例。

pci_driver
    PCI层和设备驱动程序之间的接口。主要由一些函数指针组成。如下所示:
struct pci_driver {
struct list_head node;
char *name; //驱动程序名字
const struct pci_device_id *id_table; /* ID向量,内核用于把设备关联到此驱动程序 */
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
};


PCI NIC设备的注册

    PCI设备由 pci_device_id (的成员共同)唯一标识。
struct pci_device_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/ //通常 vendor, device就足以标识设备
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID *///很少用到
__u32 class, class_mask; /* (class,subclass,prog-if) triplet *///设备所属的类,如network类
kernel_ulong_t driver_data; /* Data private to the driver *///不属于PCI标识部分,而是驱动私有参数
};

    每一个设备驱动程序会注册一个pci_device_id
实例的向量(即一系列的pci_device_id 实例),这个向量包含了该驱动程序所能处理的设备的ID。
    下面是设备驱动程序的注册和删除的函数:
int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name)
void pci_unregister_driver(struct pci_driver *drv)
pci_module_init()//在一些驱动程序上作为__pci_register_driver别名

    内核根据设备ID查询设备的驱动程序,这是一种探测机制。探测机制有两种方式:静态和动态。
 静态:

    给定一个PCI的ID,内核根据该ID从id_table向量查询出对应的驱动程序

动态:
    根据用户手动配置的ID,比较少用到,要求内核编译时支持热插拔。

电源管理和网络唤醒

    PCI的电源管理事件有pci_driver的suspend和resume进行。这两个函数分别负责PCI状态的保存和恢复。如果遇到NIC的的情况还需要分别进行下面步骤:

    suspend:停止设备出口队列,使得该设备无法再传输:
    resume:重启出口队列,是设备可以继续传输。

    网络唤醒功能允许NIC在接收到某种特殊帧是唤醒系统。这个功能通常是被禁用的,但是此功能可以用pci_enable_wake打开或关上。关于这部分我发现linux-3.12.36里好像没有这个函数了,可能有使用了其它网络唤醒方法,留着以后再补充了。


PCI NIC驱动程序注册范例

以Intel PRO/100 Ethernet驱动程序说明NIC设备驱动程序的注册,源文件为drivers/net/e100.c。

初始化pci_device_id内容:

#define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\
PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \
PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich } /****************************************************************************************/
#define DEFINE_PCI_DEVICE_TABLE(_table) \
const struct pci_device_id _table[] __devinitconst
/***************************************************************************************/ static DEFINE_PCI_DEVICE_TABLE(e100_id_table) = {
INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1030, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1031, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1032, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1033, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1034, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1038, 3),
INTEL_8255X_ETHERNET_DEVICE(0x1039, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103A, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103B, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103C, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103D, 4),
INTEL_8255X_ETHERNET_DEVICE(0x103E, 4),
INTEL_8255X_ETHERNET_DEVICE(0x1050, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1051, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1052, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1053, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1054, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1055, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1056, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1057, 5),
INTEL_8255X_ETHERNET_DEVICE(0x1059, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1064, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1065, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1066, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1067, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1068, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1069, 6),
INTEL_8255X_ETHERNET_DEVICE(0x106A, 6),
INTEL_8255X_ETHERNET_DEVICE(0x106B, 6),
INTEL_8255X_ETHERNET_DEVICE(0x1091, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1092, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1093, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1094, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1095, 7),
INTEL_8255X_ETHERNET_DEVICE(0x10fe, 7),
INTEL_8255X_ETHERNET_DEVICE(0x1209, 0),
INTEL_8255X_ETHERNET_DEVICE(0x1229, 0),
INTEL_8255X_ETHERNET_DEVICE(0x2449, 2),
INTEL_8255X_ETHERNET_DEVICE(0x2459, 2),
INTEL_8255X_ETHERNET_DEVICE(0x245D, 2),
INTEL_8255X_ETHERNET_DEVICE(0x27DC, 7),
{ 0, }
};

在模块的初始化和卸载接口中完成PCI设备驱动程序的注册和注销:

static struct pci_driver e100_driver = {
.name = DRV_NAME,
.id_table = e100_id_table,
.probe = e100_probe,
.remove = __devexit_p(e100_remove),
#ifdef CONFIG_PM
/* Power Management hooks */
.suspend = e100_suspend,
.resume = e100_resume,
#endif
.shutdown = e100_shutdown,
.err_handler = &e100_err_handler,
};
static int __init e100_init_module(void)
{
if (((1 << debug) - 1) & NETIF_MSG_DRV) {
pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
pr_info("%s\n", DRV_COPYRIGHT);
}
return pci_register_driver(&e100_driver);
}
static void __exit e100_cleanup_module(void)
{
pci_unregister_driver(&e100_driver);
}
module_init(e100_init_module);
module_exit(e100_cleanup_module);

其中的一些函数指针原型:

#define DRV_NAME        "e100"
static int __devinit e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *netdev;
struct nic *nic;
int err;
if (!(netdev = alloc_etherdev(sizeof(struct nic)))) {
if (((1 << debug) - 1) & NETIF_MSG_PROBE)
pr_err("Etherdev alloc failed, aborting\n");
return -ENOMEM;
}
……
……
}

PCI子系统总览

(a)在系统引导时,会建立一个数据库,把每个总线都关联到一份已侦测到而使用该总线的设备列表。PCI总线的描述符处理其他参数外,还包括一个已侦测PCI设备的列表。

(b)当驱动程序被加载,调用pci_register_driver注册pci_driver到PCI层时,PCI会使用pci_driver结构中的PCI设备ID参数id_table与已侦测到的PCI设备列表匹配,若匹配到就会建立该驱动程序的设备列表。对于每个匹配到的设备,PCI层会调用相匹配的驱动程序中的pci_driver结构中的probe函数,建立并注册相关联的网络设备。

/proc/pci文件包含了已注册的PCI设备的信息。pciutils套件中的lspci命令会输出有关本地PCI设备的信息,其中有些信息取自/sys。












深入理解Linux网络技术内幕——PCI层和网络接口卡的更多相关文章

  1. 深入理解linux网络技术内幕读书笔记(三)--用户空间与内核的接口

    Table of Contents 1 概论 1.1 procfs (/proc 文件系统) 1.1.1 编程接口 1.2 sysctl (/proc/sys目录) 1.2.1 编程接口 1.3 sy ...

  2. 深入理解linux网络技术内幕读书笔记(六)--PCI层与网络接口卡

    Table of Contents 1 本章涉及的数据结构 1.1 pci_device_id结构 1.2 pci_dev结构 1.3 pci_driver结构 2 PCI NIC设备驱动程序的注册 ...

  3. 《深入理解Linux网络技术内幕》阅读笔记 --- 邻居子系统

    1.封包从L3至L2的传送过程如下所示: 本地主机的路由子系统选择L3目的地址(下一个跃点). 根据路由表,如果下一个跃点在同一个网络中,邻居层就把目的L3地址解析为跃点的L2地址.这个关联会被放入缓 ...

  4. 深入理解Linux网络技术内幕——网络设备初始化

    概述    内核的初始化过程过程中,与网络相关的工作如下所示:     内核引导时执行start_kernel,start_kernel结束之前会调用rest_init,rest_init初始化内核线 ...

  5. 深入理解linux网络技术内幕读书笔记(五)--网络设备初始化

    Table of Contents 1 简介 2 系统初始化概论 2.1 引导期间选项 2.2 中断和定时器 2.3 初始化函数 3 设备注册和初始化 3.1 硬件初始化 3.2 软件初始化 3.3 ...

  6. 《深入理解Linux网络技术内幕》阅读笔记 --- 路由基本概念

    一.路由的基本概念 1.一条路由就是一组参数,这些参数存储了往一个给定目的地转发流量所需的信息,而一条路由所需的最少的参数集合为:(1)目的网络,(2)出口设备,(3)下一跳网关 2.路由中的相关术语 ...

  7. 深入理解linux网络技术内幕读书笔记(十)--帧的接收

    Table of Contents 1 概述 1.1 帧接收的中断处理 2 设备的开启与关闭 3 队列 4 通知内核帧已接收:NAPI和netif_rx 4.1 NAPI简介 4.1.1 NAPI优点 ...

  8. 深入理解linux网络技术内幕读书笔记(九)--中断与网络驱动程序

    Table of Contents 1 接收到帧时通知驱动程序 1.1 轮询 1.2 中断 2 中断处理程序 3 抢占功能 4 下半部函数 4.1 内核2.4版本以后的下半部函数: 引入软IRQ 5 ...

  9. 深入理解linux网络技术内幕读书笔记(八)--设备注册与初始化

    Table of Contents 1 设备注册之时 2 设备除名之时 3 分配net_device结构 4 NIC注册和除名架构 4.1 注册 4.2 除名 5 设备初始化 6 设备类型初始化: x ...

随机推荐

  1. YOLOv3-darknet 内容解析

    目录 Yolov3-darknet 内容解析 多标签分类预测 跨尺度预测 网络结构改变 reference Yolov3-darknet 内容解析 YOLOv3是到目前为止,速度和精度最均衡的目标检测 ...

  2. Ubuntu 14.04 下 OF-Config安装

    参考: Github of-config configure.ac - configure file issue OF-Config安装 1.安装OvS v2.3.1: Releases $ tar ...

  3. 【Coursera】Technology :Fifth Week(2)

    The Ethernet Story Bob Metcalfe Bob 参与了 Xerox 研究项目,着手解决建造一个处处连接个人计算机的架构.当时,他们刚刚完成了 Internet 的开端 -具有 ...

  4. 03_Spark集群部署

    [安装前的环境准备] Hadoop:2.6.1Java:jdk-1.7.0Spark: spark-1.6.0-bin-hadoop2.6.tgzScala: scala-2.11.4.tgz虚拟机: ...

  5. 为什么我的电脑win系统没有便笺功能?为什么我在开始菜单里找不到便笺功能?

    有些网友表示,为什么我的电脑没有便笺功能?为什么我在开始菜单里找不到便笺功能? 从问题可以基本判断出来,这些网友使用的Win7版本有可能是买笔记本或者台式电脑时预装的Win7家庭普通版或者Win7精简 ...

  6. Jmeter Connect to Cassandra

    https://github.com/Netflix/CassJMeter/wiki https://stackoverflow.com/questions/40974407/connecting-c ...

  7. 微信小程序发起支付流程

    小程序调起支付API 需要参数 邮件中参数 API参数名 详细说明 APPID appid appid是微信小程序后台APP的唯一标识,在小程序后台申请小程序账号后,微信会自动分配对应的appid,用 ...

  8. MongoDB(课时22 过期索引)

    3.6.2 过期索引 在一些程序的站点会出现若干秒之后信息被删除的情况,例如:手机信息验证码,那么在MongoDB里面可以轻松实现过期索引.但这个时间往往不怎么准确. 范例:设置过期索引(实现过期索引 ...

  9. 使用R内置函数操作数据框

    我们已经学习了数据框的基础,这里回顾一下用于筛选数据框的内置函数.尽管数据框本质上是一个由向量构成的列表,由于各列长度相同,所以可以将其看作矩阵进行访问和操作.选择满足特定条件的行,需要为 [ ] 的 ...

  10. C++学习笔记(一)——一个字符串分割和统计的工具(TextUtils)

    第一讲先从一个实例开始——我们需要完成一个遍历文件并统计单词出现次数的任务.分解功能:首先,按行读取文件并舍弃可能的空行.其次,将每一行都按照空格划分单词.因为可能存在标点符号,我们还需要将标点符号都 ...