概述

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

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

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


PCI NIC设备的注册

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

    每一个设备驱动程序会注册一个pci_device_id
实例的向量(即一系列的pci_device_id 实例),这个向量包含了该驱动程序所能处理的设备的ID。
    下面是设备驱动程序的注册和删除的函数:
  1. int __pci_register_driver(struct pci_driver *drv, struct module *owner, const char *mod_name)
  2. void pci_unregister_driver(struct pci_driver *drv)
  3. 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内容:

  1. #define INTEL_8255X_ETHERNET_DEVICE(device_id, ich) {\
  2. PCI_VENDOR_ID_INTEL, device_id, PCI_ANY_ID, PCI_ANY_ID, \
  3. PCI_CLASS_NETWORK_ETHERNET << 8, 0xFFFF00, ich }
  4.  
  5. /****************************************************************************************/
  6. #define DEFINE_PCI_DEVICE_TABLE(_table) \
  7. const struct pci_device_id _table[] __devinitconst
  8. /***************************************************************************************/
  9.  
  10. static DEFINE_PCI_DEVICE_TABLE(e100_id_table) = {
  11. INTEL_8255X_ETHERNET_DEVICE(0x1029, 0),
  12. INTEL_8255X_ETHERNET_DEVICE(0x1030, 0),
  13. INTEL_8255X_ETHERNET_DEVICE(0x1031, 3),
  14. INTEL_8255X_ETHERNET_DEVICE(0x1032, 3),
  15. INTEL_8255X_ETHERNET_DEVICE(0x1033, 3),
  16. INTEL_8255X_ETHERNET_DEVICE(0x1034, 3),
  17. INTEL_8255X_ETHERNET_DEVICE(0x1038, 3),
  18. INTEL_8255X_ETHERNET_DEVICE(0x1039, 4),
  19. INTEL_8255X_ETHERNET_DEVICE(0x103A, 4),
  20. INTEL_8255X_ETHERNET_DEVICE(0x103B, 4),
  21. INTEL_8255X_ETHERNET_DEVICE(0x103C, 4),
  22. INTEL_8255X_ETHERNET_DEVICE(0x103D, 4),
  23. INTEL_8255X_ETHERNET_DEVICE(0x103E, 4),
  24. INTEL_8255X_ETHERNET_DEVICE(0x1050, 5),
  25. INTEL_8255X_ETHERNET_DEVICE(0x1051, 5),
  26. INTEL_8255X_ETHERNET_DEVICE(0x1052, 5),
  27. INTEL_8255X_ETHERNET_DEVICE(0x1053, 5),
  28. INTEL_8255X_ETHERNET_DEVICE(0x1054, 5),
  29. INTEL_8255X_ETHERNET_DEVICE(0x1055, 5),
  30. INTEL_8255X_ETHERNET_DEVICE(0x1056, 5),
  31. INTEL_8255X_ETHERNET_DEVICE(0x1057, 5),
  32. INTEL_8255X_ETHERNET_DEVICE(0x1059, 0),
  33. INTEL_8255X_ETHERNET_DEVICE(0x1064, 6),
  34. INTEL_8255X_ETHERNET_DEVICE(0x1065, 6),
  35. INTEL_8255X_ETHERNET_DEVICE(0x1066, 6),
  36. INTEL_8255X_ETHERNET_DEVICE(0x1067, 6),
  37. INTEL_8255X_ETHERNET_DEVICE(0x1068, 6),
  38. INTEL_8255X_ETHERNET_DEVICE(0x1069, 6),
  39. INTEL_8255X_ETHERNET_DEVICE(0x106A, 6),
  40. INTEL_8255X_ETHERNET_DEVICE(0x106B, 6),
  41. INTEL_8255X_ETHERNET_DEVICE(0x1091, 7),
  42. INTEL_8255X_ETHERNET_DEVICE(0x1092, 7),
  43. INTEL_8255X_ETHERNET_DEVICE(0x1093, 7),
  44. INTEL_8255X_ETHERNET_DEVICE(0x1094, 7),
  45. INTEL_8255X_ETHERNET_DEVICE(0x1095, 7),
  46. INTEL_8255X_ETHERNET_DEVICE(0x10fe, 7),
  47. INTEL_8255X_ETHERNET_DEVICE(0x1209, 0),
  48. INTEL_8255X_ETHERNET_DEVICE(0x1229, 0),
  49. INTEL_8255X_ETHERNET_DEVICE(0x2449, 2),
  50. INTEL_8255X_ETHERNET_DEVICE(0x2459, 2),
  51. INTEL_8255X_ETHERNET_DEVICE(0x245D, 2),
  52. INTEL_8255X_ETHERNET_DEVICE(0x27DC, 7),
  53. { 0, }
  54. };

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

  1. static struct pci_driver e100_driver = {
  2. .name = DRV_NAME,
  3. .id_table = e100_id_table,
  4. .probe = e100_probe,
  5. .remove = __devexit_p(e100_remove),
  6. #ifdef CONFIG_PM
  7. /* Power Management hooks */
  8. .suspend = e100_suspend,
  9. .resume = e100_resume,
  10. #endif
  11. .shutdown = e100_shutdown,
  12. .err_handler = &e100_err_handler,
  13. };
  14. static int __init e100_init_module(void)
  15. {
  16. if (((1 << debug) - 1) & NETIF_MSG_DRV) {
  17. pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
  18. pr_info("%s\n", DRV_COPYRIGHT);
  19. }
  20. return pci_register_driver(&e100_driver);
  21. }
  22. static void __exit e100_cleanup_module(void)
  23. {
  24. pci_unregister_driver(&e100_driver);
  25. }
  26. module_init(e100_init_module);
  27. module_exit(e100_cleanup_module);

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

  1. #define DRV_NAME "e100"
  2. static int __devinit e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  3. {
  4. struct net_device *netdev;
  5. struct nic *nic;
  6. int err;
  7. if (!(netdev = alloc_etherdev(sizeof(struct nic)))) {
  8. if (((1 << debug) - 1) & NETIF_MSG_PROBE)
  9. pr_err("Etherdev alloc failed, aborting\n");
  10. return -ENOMEM;
  11. }
  12. ……
  13. ……
  14. }

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. json获取元素数量

    var keleyijson={"plug1":"myslider","plug2":"zonemenu"} funct ...

  2. MVC ---- 如何使用Predicate以及如何自定定义Predicate委托

    微软公司提供只能返回bool值,接受一个参数的委托类型(Predicate). //Predicate委托 public static class PredicateDemo{ //内置方法 publ ...

  3. 【转】Windows Server 2008 R2怎样设置自动登陆

    Windows Server 2008 R2是一款服务器操作系统,提升了虚拟化.系统管理弹性.网络存取方式,以及信息安全等领域的应用,Windows Server 2008 R2也是第一个只提供64位 ...

  4. poj 1523 SPF 无向图求割点

    SPF Description Consider the two networks shown below. Assuming that data moves around these network ...

  5. highcharts PHP中使用

    官网 https://www.hcharts.cn/demo/highcharts html <div id="container" style="min-widt ...

  6. 运行gulp提示:Task function must be specified

    问题出在gulp版本上,以下是gulp3  VS  gulp4的区别: gulp4最大的变化是不能像以前那样传递一个依赖的任务列表. gulp3中,如果有一个任务A.B和C的列表,你想在一个序列中运行 ...

  7. 《剑指offer》第三十四题(二叉树中和为某一值的路径)

    // 面试题34:二叉树中和为某一值的路径 // 题目:输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所 // 有路径.从树的根结点开始往下一直到叶结点所经过的结点形成一条路径. #i ...

  8. 文件路径、File协议、FTP协议、Http协议之间简单的区别

    File协议主要用于访问本地计算机中的文件,就如同在Windows资源管理器中打开文件一样,基本的格式如下:file:///文件路径. FTP是文件传输协议,可以用于互联网上.例如,你有一个网站,放在 ...

  9. 用EL時(el-api.jar,el-ri.jar ),要設isELIgnored="false"

    用EL時(el-api.jar,el-ri.jar ),要設isELIgnored="false" 否则jstl标签不显示. 加上 <%@page isELIgnored="false ...

  10. 3-8《Ruby元编程》第二章对象模型

    <Ruby元编程> 第二章 对象模型 类定义揭秘inside class definitions: class关键字更像一个作用域操作符,核心作用是可以在里面随时定义方法. [].meth ...