linux下的pci驱动关心两个参数
vid(厂商ID) 和did(设备ID)
进一步通过lspci -n
详细的解释如下
zw@zw-pc:~$ lspci -nn |grep NVI 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation Device [10de:1c03] (rev a1) 01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:10f1] (rev a1)
输出值 | 含义 | 详细解释 |
---|---|---|
"01:00.0" 和 “01::00.1” | 以 ”bus:slot.func“ 格式来唯一标识一个 PCI 功能设备 | 唯一定位一个 PCI 设备的虚拟功能,可以是一个物理设备,也可以是一个多功能设备的功能设备,一个多功能设备可以最多有8个功能。 总线号(bus): 从系统中的256条总线中选择一条,0--255。 设备号(slot): 在一条给定的总线上选择32个设备中的一个。0--31。功能号(func): 选择多功能设备中的某一个功能,有八种功能,0--7。 PCI规范规定,功能0是必须实现的。 |
”0300“ | PCI 设备类型 | 指 PCI 设备的类型,来自不同厂商的同一类设备的类型码可以是相同的。 |
“10de” | 供应商识别字段(Vendor ID) | 该字段用一标明设备的制造者。一个有效的供应商标识由 PCI SIG 来分配,以保证它的唯一性。Intel 的 ID 为 0x8086,Nvidia 的 ID 为 0x10de |
“1c03” | 设备识别字段(Device ID) | 用以标明特定的设备,具体代码由供应商来分配。本例中表示的是 GPU 图形卡的设备 ID。 |
“a1” | 版本识别字段(Revision ID) | 用来指定一个设备特有的版本识别代码,其值由供应商提供 |
在PCIE配置空间里,0x10开始后面有6个32位的BAR寄存器,BAR寄存器中存储的数据是表示PCIE设备在PCIE地址空间中的基地址,注意这里不是表示PCIE设备内存在CPU内存中的映射地址,关于这两者的关系以及两者如何转换后面会有介绍。
其中Type0 Header最多有6个BAR,而Type1 Header最多有两个BAR。这就意味着,对于Endpoint来说,最多可以拥有6个不同的地址空间。但是实际应用中基本上不会用到6个,通常1~3个BAR比较常见。
主要注意的是,如果某个设备的BAR没有被全部使用,则对应的BAR应被硬件全被设置为0,并且告知软件这些BAR是不可以操作的。对于被使用的BAR来说,其部分低比特位是不可以被软件操作的,只有其高比特位才可以被软件操作。而这些不可操作的低比特决定了当前BAR支持的操作类型和可申请的地址空间的大小。
一旦BAR的值确定了(Have been programmed),其指定范围内的当前设备中的内部寄存器(或内部存储空间)就可以被访问了。当该设备确认某一个请求(Request)中的地址在自己的BAR的范围内,便会接受这请求。
驱动的框架模型
#define BAR_NUM 6 struct xxx_dev { struct pci_dev *pci_dev; .... }; static int get_map_bars ( struct xxx_dev *priv, struct pci_dev *dev ) { int i; for ( i = 0; i < BAR_NUM; i++ ) { unsigned long bar_start = pci_resource_start ( dev, i ); priv->bar_length[i] = pci_resource_len ( dev, i ); if ( !priv->bar_length[i] ) { priv->bar[i] = NULL; continue; } priv->bar[i] = pci_iomap ( dev, i, 0 ); if ( !priv->bar[i] ) { dev_err ( &dev->dev, "could not map BAR[%d]", i ); return -1; } else { drv_printk ( "BAR[%d] mapped to 0x%p, length %lu \r\n", i, priv->bar[i], ( long unsigned int ) priv->bar_length[i] ); } } xxx_regs = priv->bar[0];//根据自己的设备来定 return 0; } static int pcie_scan_bars ( struct xxx_dev *priv, struct pci_dev *dev ) { int i; for ( i = 0; i < BAR_NUM; i++ ) { unsigned long bar_start = pci_resource_start ( dev, i ); unsigned long bar_end = pci_resource_end ( dev, i ); unsigned long bar_flags = pci_resource_flags ( dev, i ); priv->bar_length[i] = pci_resource_len ( dev, i ); drv_printk ( "BAR[%d] 0x%08lx-0x%08lx flags 0x%08lx, length %d \r\n", i, bar_start, bar_end, bar_flags, ( int ) priv->bar_length[i] ); } return 0; } static int pcie_xxx_probe ( struct pci_dev *dev, const struct pci_device_id *id ) { struct xxx_dev *priv; int ret; printk ( "%s enter.\n", __func__ ); /* Allocate private data */ priv = devm_kzalloc ( &dev->dev, sizeof ( struct xxx_dev ), GFP_KERNEL ); if ( !priv ) { dev_err ( &dev->dev, "Unable to allocate device private data\n" ); return -ENOMEM; } priv->pci_dev = pci_dev_get ( dev ); .... pci_set_drvdata ( dev, priv ); ret = pci_enable_device ( dev ); if ( ret ) { goto out_deregister; } pci_set_master ( dev ); pci_read_config_byte ( dev, PCI_REVISION_ID, &priv->revision ); pci_read_config_byte ( dev, PCI_INTERRUPT_PIN, &priv->irq_pin ); pci_read_config_byte ( dev, PCI_INTERRUPT_LINE, &priv->irq_line ); drv_printk ( "irq pin: %d\n", priv->irq_pin ); drv_printk ( "irq line: %d\n", priv->irq_line ); drv_printk ( "revision: %d\n", priv->revision ); pcie_scan_bars ( priv, dev ); get_map_bars ( priv, dev );//获取基地址 .... return ret; } static int pcie_xxx_remove ( struct pci_dev *dev ) { struct xxx_dev *priv = pci_get_drvdata ( dev ); printk ( "%s enter.\n", __func__ ); .... printk ( "pcie_fpga_remove success.\n" ); return 0; } static struct pci_device_id pcie_xxx_ids[] = { { PCI_DEVICE ( xxx_VID, xxx_DID ) }, { 0 } }; static struct pci_driver pcie_xxx_driver = { .name = "fsl, pcie_xxx", .id_table = pcie_xxx_ids, .probe = pcie_xxx_probe, .remove = pcie_xxx_remove, }; static int __init pcie_xxx_init ( void ) { int rc = 0; rc = pci_register_driver ( &pcie_xxx_driver ); if ( rc ) { printk ( " PCI driver registration failed\n" ); } return rc; } static void __exit pcie_xxx_exit ( void ) { pci_unregister_driver ( &pcie_xxx_driver ); } module_init ( pcie_xxx_init ); module_exit ( pcie_xxx_exit ); MODULE_LICENSE ( "GPL" ); MODULE_AUTHOR ( "tccxy <xxx@xxx.com>" ); MODULE_DESCRIPTION ( "xxx driver" );