Linux教程

linux下pci总线的驱动模型

本文主要是介绍linux下pci总线的驱动模型,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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" );
这篇关于linux下pci总线的驱动模型的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!