ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

linux下pci总线的驱动模型

2022-04-26 13:33:26  阅读:223  来源: 互联网

标签:BAR xxx 总线 dev pcie pci linux priv


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" );

标签:BAR,xxx,总线,dev,pcie,pci,linux,priv
来源: https://www.cnblogs.com/tccxy/p/16194393.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有