• -------------------------------------------------------------------------------- 原文:How To Write Linux PCI Drivers 作者:Martin Mares on 07-Feb-2000 位置:Documentation/pci.txt 编译:dqzhangp@263...
     --------------------------------------------------------------------------------

    原文:How To Write Linux PCI Drivers
    作者:Martin Mares <mj@ucw.cz> on 07-Feb-2000
    位置:Documentation/pci.txt
    编译:dqzhangp@263.net

    0. PCI驱动程序的架构
    有两种类型的PCI驱动程序,新型的驱动程序,它把多数的设备探测工作留给了PCI层, and support online insertion and removal of devices [thus supporting PCI, hot-pluggable PCI and CardBus in single driver])。还有一种PCI驱动程序,他自己完成所有的探测工作。

    当驱动程序发现相应的设备,需要完成下面的功能:

    激活设备
    对设备配置空间进行存取
    发现资源,如地址、中断号等
    分配这些资源
    与设备进行通讯
    1. 新型驱动程序
    新型的设备驱动程序在初始化的时候调用函数pci_register_driver,此函数的参数是一个struct pci_driver类型的指针。struct pci_driver包括以下一些项:

    name:驱动的名字
    id_table:指向驱动程序感兴趣的设备的设备ID列表。很多设备驱动程序需要使用 MODULE_DEVICE_TABLE(pci,...)输出此表。如果设为NULL则感兴趣的设备是系统识别的所有设备。
    probe:一个探测函数的指针,用来探测所有与ID表中匹配的设备。This function gets passed a pointer to the pci_dev structure representing the device and also which entry in the ID table did the device match。驱动程序接受设备的时候返回0,否则返回一个错误代码。This function al4矗绲刂贰⒅卸虾诺�
    分配这些资源
    与设备进行通讯
    1. 新型驱动程序
    新型的设备驱动程序在初始化的时候调用函数pci_register_driver,此函数的参数是一个struct pci_driver类型的指针。struct pci_driver包括以下一些项:

    name:驱动的名字
    id_table:指向驱动程序感兴趣的设备的设备ID列表。很多设备驱动程序需要使用 MODULE_DEVICE_TABLE(pci,...)输出此表。如果设为NULL则感兴趣的设备是系统识别的所有设备。
    probe:一个探测函数的指针,用来探测所有与ID表中匹配的设备。This function gets passed a pointer to the pci_dev structure representing the device and also which entry in the ID table did the device match。驱动程序接受设备的时候返回0,否则返回一个错误代码。This function always gets called from process context, so it can sleep.
    remove:一个函数指针,当一个驱动程序所驱动的一个设备被卸掉的时候被调用。This function always gets called from process context, so it can sleep.
    save_state:在挂起前存储设备状态。
    suspend 把设备置于低电状态。
    resume 把设备从低电状态唤醒。
    enable_wake 是设备能够从低电状态产生唤醒事件。
    ID列表是一个以全零入口结尾的struct pci_device_id类型的数组。struct pci_device_id:

    vendor, device Vendor and device ID to match (or PCI_ANY_ID)
    subvendor, Subsystem vendor and device ID to match (or PCI_ANY_ID) subdevice
    class, Device class to match. The class_mask tells which bits
    class_mask of the class are honored during the comparison.
    driver_data Data private to the driver.
    驱动程序退出的时候调用函数pci_unregister_driver(),PCI层自动调用remove钩子函数。请把相应的函数设�%Aways gets called from process context, so it can sleep.
    remove:一个函数指针,当一个驱动程序所驱动的一个设备被卸掉的时候被调用。This function always gets called from process context, so it can sleep.
    save_state:在挂起前存储设备状态。
    suspend 把设备置于低电状态。
    resume 把设备从低电状态唤醒。
    enable_wake 是设备能够从低电状态产生唤醒事件。
    ID列表是一个以全零入口结尾的struct pci_device_id类型的数组。struct pci_device_id:

    vendor, device Vendor and device ID to match (or PCI_ANY_ID)
    subvendor, Subsystem vendor and device ID to match (or PCI_ANY_ID) subdevice
    class, Device class to match. The class_mask tells which bits
    class_mask of the class are honored during the comparison.
    driver_data Data private to the driver.
    驱动程序退出的时候调用函数pci_unregister_driver(),PCI层自动调用remove钩子函数。请把相应的函数设定为初始化或者清除类型。相应的宏在头文件include/linux/init.h中定义:

    __init:初始化代码。驱动程序初始化完成后从内存中清除。
    __exit:Exit code。如果此驱动不是模块的方式,则代码被忽略。
    __devinit:Device initialization code. 如果内核没有被配置为CONFIG_HOTPLUG,即不支持热插拔,则与__init相同,否则就是一个普通的函数。
    __devexit:与 __exit一样。
    技巧:

    函数module_init()和module_exit() 应当分别标记为 __init和exit。
    结构pci_driver不能标记为__init和exit。
    ID table array 应当标记为 __devinitdata。
    函数probe() 和 remove() 应当标记为 __devinit 和 exit。
    If you are sure the driver is not a hotplug driver then use only __init/exit __initdata/exitdata.
    Pointers to functions marked as __devexit must be created using __devexit_p(function_name). That will generat8为初始化或者清除类型。相应的宏在头文件include/linux/init.h中定义:

    __init:初始化代码。驱动程序初始化完成后从内存中清除。
    __exit:Exit code。如果此驱动不是模块的方式,则代码被忽略。
    __devinit:Device initialization code. 如果内核没有被配置为CONFIG_HOTPLUG,即不支持热插拔,则与__init相同,否则就是一个普通的函数。
    __devexit:与 __exit一样。
    技巧:

    函数module_init()和module_exit() 应当分别标记为 __init和exit。
    结构pci_driver不能标记为__init和exit。
    ID table array 应当标记为 __devinitdata。
    函数probe() 和 remove() 应当标记为 __devinit 和 exit。
    If you are sure the driver is not a hotplug driver then use only __init/exit __initdata/exitdata.
    Pointers to functions marked as __devexit must be created using __devexit_p(function_name). That will generate the function name or NULL if the __devexit function will be discarded.
    2. 如何手动发现PCI设备(老的方式)
    PCI驱动程序不使用函数pci_register_driver()查找PCI设备,可以使用下面的方法:

    通过生产商或设备ID号:
    struct pci_dev *dev = NULL;
    while (dev = pci_find_device(VENDOR_ID, DEVICE_ID, dev))
    configure_device(dev);

    通过种类ID号搜索:
    pci_find_class(CLASS_ID, dev)

    Searching by both vendor/device and subsystem vendor/device ID:
    pci_find_subsys(VENDOR_ID, DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev).

    利用常数PCI_ANY_ID替代VENDOR_ID或者DEVICE_ID,这种方法用来搜索特定厂商的所有设备。
    有时候需要根据比较复杂的规则决定要查找的设备,这可以通过遍历发现的所有设备,根据每个设备的具体情况作出选择。
    struct pci_dev *dev;
    pci_for_each_dev(dev) {
    ... do ane the function name or NULL if the __devexit function will be discarded.
    2. 如何手动发现PCI设备(老的方式)
    PCI驱动程序不使用函数pci_register_driver()查找PCI设备,可以使用下面的方法:

    通过生产商或设备ID号:
    struct pci_dev *dev = NULL;
    while (dev = pci_find_device(VENDOR_ID, DEVICE_ID, dev))
    configure_device(dev);

    通过种类ID号搜索:
    pci_find_class(CLASS_ID, dev)

    Searching by both vendor/device and subsystem vendor/device ID:
    pci_find_subsys(VENDOR_ID, DEVICE_ID, SUBSYS_VENDOR_ID, SUBSYS_DEVICE_ID, dev).

    利用常数PCI_ANY_ID替代VENDOR_ID或者DEVICE_ID,这种方法用来搜索特定厂商的所有设备。
    有时候需要根据比较复杂的规则决定要查找的设备,这可以通过遍历发现的所有设备,根据每个设备的具体情况作出选择。
    struct pci_dev *dev;
    pci_for_each_dev(dev) {
    ... do anything you want with dev ...
    }

    为了与旧的内核的设备排序兼容,可以使用函数pci_for_each_dev_reverse(dev)以反方向遍历设备列表。
    3. 激活设备
    在使用发现的设备之前,必须调用函数pci_enable_device()激活它,该函数使得设备的I/O口和存储区可用,在必要时分派丢失的资源,或者用来唤醒处于挂起状态的设备。

    调用函数pci_set_master()使得设备处于bus mastering模式,此函数设置PCI_COMMAND寄存器的bus master位。同时 fixes the latency timer value if it's set to something bogus by the BIOS。所谓bus mastering模式,就是直接与总线上的其他设备通讯, 而不必经过CPU,又叫做first-party DMA。

    如果要使用PCI的Memory-Write-Invalidate transaction,调用函数pci_set_mwi()。This enables bit PCI_COMMAND bit for Mem-Wr-Inval and also ensures thatything you want with dev ...
    }

    为了与旧的内核的设备排序兼容,可以使用函数pci_for_each_dev_reverse(dev)以反方向遍历设备列表。
    3. 激活设备
    在使用发现的设备之前,必须调用函数pci_enable_device()激活它,该函数使得设备的I/O口和存储区可用,在必要时分派丢失的资源,或者用来唤醒处于挂起状态的设备。

    调用函数pci_set_master()使得设备处于bus mastering模式,此函数设置PCI_COMMAND寄存器的bus master位。同时 fixes the latency timer value if it's set to something bogus by the BIOS。所谓bus mastering模式,就是直接与总线上的其他设备通讯, 而不必经过CPU,又叫做first-party DMA。

    如果要使用PCI的Memory-Write-Invalidate transaction,调用函数pci_set_mwi()。This enables bit PCI_COMMAND bit for Mem-Wr-Inval and also ensures that the cache line size register is set correctly. 一定要检查函数pci_set_mwi()的返回值,因为不是所有的架构都支持Memory-Write-Invalidate。

    4. 如何访问PCI设备的配置空间
    可以使用函数pci_(read|write)_config_(byte|word|dword)访问一个设备的配置空间,此设备用struct pci_dev * 来表示。所有这些函数调用成功是的返回值都为0,如果失败则返回一个错误代码:PCIBIOS_... ,这个错误代码可以利用函数pcibios_strerror转换成一个字符串。Most drivers expect that accesses to valid PCI devices don't fail.

    If you access fields in the standard portion of the config header, please use symbolic names of locations and bits declared in <linux/pci.h>.

    If you need to access Extended PCI Capability registers, just call pci_find_capability() for the particular capability and it will find the corresponding register block for you. 

    5.地址空间与中断
    Memory and port addresses and interrupt numbers should NOT the cache line size register is set correctly. 一定要检查函数pci_set_mwi()的返回值,因为不是所有的架构都支持Memory-Write-Invalidate。

    4. 如何访问PCI设备的配置空间
    可以使用函数pci_(read|write)_config_(byte|word|dword)访问一个设备的配置空间,此设备用struct pci_dev * 来表示。所有这些函数调用成功是的返回值都为0,如果失败则返回一个错误代码:PCIBIOS_... ,这个错误代码可以利用函数pcibios_strerror转换成一个字符串。Most drivers expect that accesses to valid PCI devices don't fail.

    If you access fields in the standard portion of the config header, please use symbolic names of locations and bits declared in <linux/pci.h>.

    If you need to access Extended PCI Capability registers, just call pci_find_capability() for the particular capability and it will find the corresponding register block for you. 

    5.地址空间与中断
    Memory and port addresses and interrupt numbers should NOT be read from the config space. You should use the values in the pci_dev structure as they might have been remapped by the kernel.

    See Documentation/IO-mapping.txt for how to access device memory.

    You still need to call request_region() for I/O regions and request_mem_region() for memory regions to make sure nobody else is using the same device.

    All interrupt handlers should be registered with SA_SHIRQ and use the devid to map IRQs to devices (remember that all PCI interrupts are shared). 

    6.其它相关的函数
    pci_find_slot() Find pci_dev corresponding to given bus and slot numbers.

    pci_set_power_state() Set PCI Power Management state (0=D0 ... 3=D3)

    pci_find_capability() Find specified capability in device's capability list.

    pci_module_init() Inline helper function for ensuring correct

    pci_driver initialization and error handling.

    pci_resource_start() Returns bus start address for a given PCI region

    pci_resource_end() Returns bus end address for a given PCI region

    pci_resource_len() Returns the byte length of a PCI region

    pci_set_drvdata() Set private driver data pointer for a pci_dev

    pci_get_drvdata() Return private driver data pointer for a pci_dev

    pci_set_mwi() Enable Memory-Write-Invbe read from the config space. You should use the values in the pci_dev structure as they might have been remapped by the kernel.

    See Documentation/IO-mapping.txt for how to access device memory.

    You still need to call request_region() for I/O regions and request_mem_region() for memory regions to make sure nobody else is using the same device.

    All interrupt handlers should be registered with SA_SHIRQ and use the devid to map IRQs to devices (remember that all PCI interrupts are shared). 

    6.其它相关的函数
    pci_find_slot() Find pci_dev corresponding to given bus and slot numbers.

    pci_set_power_state() Set PCI Power Management state (0=D0 ... 3=D3)

    pci_find_capability() Find specified capability in device's capability list.

    pci_module_init() Inline helper function for ensuring correct

    pci_driver initialization and error handling.

    pci_resource_start() Returns bus start address for a given PCI region

    pci_resource_end() Returns bus end address for a given PCI region

    pci_resource_len() Returns the byte length of a PCI region

    pci_set_drvdata() Set private driver data pointer for a pci_dev

    pci_get_drvdata() Return private driver data pointer for a pci_dev

    pci_set_mwi() Enable Memory-Write-Invalidate transactions.

    pci_clear_mwi() Disable Memory-Write-Invalidate transactions.

    7. Miscellaneous hints
    When displaying PCI slot names to the user (for example when a driver wants to tell the user what card has it found), please use pci_dev->slot_name

    for this purpose. Always refer to the PCI devices by a pointer to the pci_dev structure.

    All PCI layer functions use this identification and it's the only reasonable one. Don't use bus/slot/function numbers except for very special purposes -- on systems with multiple primary buses their semantics can be pretty complex.

    If you're going to use PCI bus mastering DMA, take a look at Documentation/DMA-mapping.txt. 

    参考资料
    linux/include/linux/pci.h
    linux/Documentation/power/pci.txt
    对ISA总线DMA的实现
    linux/Documentation/DMA-mapping.txt

     
    展开全文
  • comedi是linux的一个关于板卡驱动的开源社区,其项目在github上更新维护,社区内的包含linux上国内外各大板卡厂商常用板卡的驱动源文件,这些驱动文件均由热心程序员测试上传,这些驱动不是官方提供,但linux上和硬件打...
  • lspci是列出所有的硬件信息,包括已经安装...如果要确认有没有安装驱动,就需要通过lsmod命令来看,当然lsmod命令只能显示编译linux内核时选中为“M”的驱动程序,最靠谱的还是dmesg来查看该设备的驱动有没有安装,d...
  • Linux平台PCIe驱动编写 2019-03-21 10:45:33
    以前文章分析了PCIe整个系统知识,包括如何扫描PCIE树,这篇文章讲解一下当拿到一个PCIe设备时如何编写驱动程序。编写驱动程序在应用程序中编写,同样可以在内核层编写驱动。 从应用层编写驱动主要是使用pcilib库和...
  • linux pci/pcie驱动 2018-01-02 09:38:06
    在pci驱动中pci调用pci_scan_device扫描每个设备的每个功能,当发现该功能存在时(通过读设备的vendor及product ID确定),就为该设备功能建立一个完整的pci_dev(通过pci_setup_device 完成),并将该设备功能加入到...
  •  GPIO驱动Linux驱动开发中最基础、但却是很常用、很重要的驱动。比如你要点亮一个LED灯、键盘扫描、输出高低电平等等。而Linux内核的强大之处在于对最底层的GPIO硬件操作层的基础上封装了一些统一的GPIO操作接口...
  •  本文主要对的具体方法进行描述,并给出了如何将驱动程序编译进内核,以使驱动模块静态加载的方法。    1 PCI9054简介  PCI总线协议一般需要繁琐的逻辑验证和时序分析工作,而且开发周期较长,因此,更多的...
  • NI采集卡 linux驱动 2020-05-30 23:32:41
    NI-DAQmx18.1 linux驱动,我的内核是3.10和3.18,均可用
  • 前一段时间开发了一个linux下的PCI数据采集板卡驱动,在驱动调试时,一直使用insmod 动态的加载驱动模块(ko文件). 现在驱动基本开发结束,要提交给客户,使用insmod加载时,对客户来说不太好,比较麻烦,所以不能...
  • linux 编译安装驱动有两种,动态加载与静态加载 动态加载 一,编译,在指点内核树下编译,生成.o文件或.ko文件 二,将生成的.o或.ko文件拷到相应目录,一般是/lib/module/kernel下面 三,用insmod命令加载,用...
  • 来源:... 分类: LINUX ...一.PC机上Ubuntu9.10系统下 ...1. 驱动程序源码及Makefile如下: #------源程序------##include linux/module.h> #include linux/init.h> s
  • 1、系统日志。 嵌入式系统多是直接dmesg一下,看有没有设备关键字相关的出错信息(通用系统可检查/var/log/messages文件)。 2、lsmod: 已加载的模块。检查模块加载列表中有没有相关设备的模块。...
  • 随着通用处理器和嵌入式技术的迅猛发展,越来越多的电子设备需要由处理器控制。...同时,基于Linux内核的嵌入式操作系统应用势头强劲,开发基于Linux的设备驱动程序,具有很强的实用性和可移植性[2]。  1 PCI总
  • linux网卡驱动程序的编译与安装 powered by KindGeorge http://kindgeorge.at.3322.org 一般来说,目前新版的 Linux 预设可以支持的网络卡芯片组数量已经很完备了,很多网络卡芯片都已经被支持,例如RLT 8139 芯片 ...
  • 刚毕业的小菜鸟,师傅让我装双系统,在好不容易装好Linux后,网卡驱动一直装不上,无奈啊,查资料啊,个人感觉不错的一篇文章就贴出来分享。  linux网卡驱动程序的编译与安装(转)  分类: Linux ...
  • 知识背景:菜鸟阶段,一心学习linux驱动开发,只是现在还什么都不会。。 特别信仰 “工欲善其事,必先利其器”。 环境:ubuntu 12.04 arm-linux-gcc step1: 安装eclipse相信你已经会了,嵌入式开发下载 c/...
  • pc104接口Linux驱动程序 2020-06-05 23:30:17
    pc104接口的FPGA板卡驱动程序,与Linux系统的接口以及板卡初始化程序
  • 硬件平台 Kintex ®-7 family of FPGAs Intel X86 软件平台 Linux 4.15.0-36-generic #39~16.04.1-Ubuntu Xilinx xapp1052 运行界面 转载于:https://www.cnblogs.com/chiang-tech/p/9948782.html
1 2 3 4 5 ... 20
收藏数 2,625
精华内容 1,050