linuxPci驱动获取指定设备bus、device以及devfn数据方式

  在vxworks系统中,调用pciFindDevice()函数可以直接获取到指定设备的bus、deviceNo以及devfn数据信息。相对于linux系统,vxworks编写驱动相对简单一些。

  linux系统下bus、deviceNo以及devfn数据由驱动内部函数使用 (编写驱动过程中这些数据几乎用不到),并且没有提供明确的接口,需要我们自己分析驱动函数调用这些数据的方式。

首先在Terminal输入: lspci -vmmD;
这里写图片描述
  我们看到设备信息第一行Slot:0000表示设备域,02表示bus信息,05表示deviceNo信息,0表示devfn信息;

  了解到要得到数据信息后,开始追踪Pci相关文件,首先跟踪到linux/pci.h头文件找到
    #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
    #define PCI_FUNC(devfn) ((devfn) & 0x07),

  根据定义名称我们知道 PCI_SLOT表示PCI槽,PCI_FUNC表示PCI函数信息(或者称为描述信息),我们得到了deviceNo和devfn的转换方法。下面继续跟踪找到struct pci_dev结构体,结构体内找到了unsigned int devfn; , 到这里可以确定devfn和deviceNo就是用这个变量转换过来的。

  现在还需要得到bus号,在pci_dev结构体中没有定义bus变量,说明我们的设备bus号应该不能直接获取到
继续检查pci_dev结构体发现struct pci_bus bus; / bus this device is on /,看到这个注释明白了我们要找的设备bus号在pci_bus结构体内 , 跟踪 pci_bus结构体 找到unsigned char number; / bus number */。

  现在我们知道这些数据的获取方式,想要获取到数据需要用到struct pci_dev *dev指针。Pci入口函数static int __init pci_probe(struct pci_dev *dev, const struct pci_device_id *id)第一参数就是我们要用到的指针数据,结合上一篇的Pci驱动实例来描述具体实现方式,下面贴上代码:


#define DevName 	"test"
#define ClassName 	"class_test"
#define VendorID 	0xFA01
#define DeviceID   	0x1234

unsigned char bus;//增加bus号定义
unsigned int deviceNo;
unsigned int devfn;

struct class    *mem_class;
struct Pci_Test
{
	struct cdev 	_cdev;
	dev_t    	dev;
	char 		msi_enabled;
}*pci_test;


static int Test_open(struct inode *inode,struct file *filp)
{
	return 0;
}

static int Test_release(struct inode *inode,struct file *filp)
{
	return 0;
}


static struct file_operations test_fops = {
.owner = THIS_MODULE,
//.ioctl = Test_ioctl,
.open = Test_open,
.release = Test_release,
};

//字符驱动
static init_chrdev(struct Pci_Test *test_ptr)
{
	int result = alloc_chrdev_region(&test_ptr->dev, 0, 2, DevName);
	if (result < 0)
	{
		printk("Err:failed in alloc_chrdev_region!\n");
		return result;
	}
	
	mem_class = class_create(THIS_MODULE,ClassName);// /dev/ create devfile 
    	if (IS_ERR(mem_class))
    	{
		printk("Err:failed in creating class!\n");
  	}
	device_create(mem_class,NULL,test_ptr->dev,NULL,DevName);

	cdev_init(&test_ptr->_cdev,&test_fops);
	test_ptr->_cdev.owner = THIS_MODULE;
	test_ptr->_cdev.ops = &test_fops;//Create Dev and file_operations Connected
	result = cdev_add(&test_ptr->_cdev,test_ptr->dev,1);
	return result;
}

//PCI驱动入口函数   在这个函数中增加bus、device和devfn数据获取方法
static int __init pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
	int rc = 0;
	pci_test = dev;
    	pci_set_drvdata(dev, pci_test);
    //在这里创建字符设备驱动
	rc = init_chrdev(pci_test); 
    	if (rc) {
        	dev_err(&dev->dev, "init_chrdev() failed\n");
        	return -1;
    	}

	rc = pci_enable_device(dev);
    	if (rc) {
        	dev_err(&dev->dev, "pci_enable_device() failed\n");
       		return -1;
    	} 

	rc = pci_request_regions(dev, DevName);
    	if (rc) {
        	dev_err(&dev->dev, "pci_request_regions() failed\n");
        	return -1;
    	}

    	pci_set_master(dev);
    	rc = pci_enable_msi(dev);
   	if (rc) {
        	dev_info(&dev->dev, "pci_enable_msi() failed\n");
        	pci_test->msi_enabled = 0;
    	} else {
        	dev_info(&dev->dev, "pci_enable_msi() successful\n");
       	 	pci_test->msi_enabled = 1;
   	}

//在这里增加获取bus、deviceNo和devfn数据的方法
bus = dev->bus->number;
deviceNo = (((dev->devfn) >> 3 ) && 0x1f);
devfn = ((dev->devfn) && 0x07);

return rc;
}

static void __exit pci_remove(struct pci_dev *dev)
{
    	if (0 != mem_class)
    	{
		device_destroy(mem_class,pci_test->dev);
		class_destroy(mem_class);
		mem_class = 0;
    	}

	pci_test = pci_get_drvdata(dev);
    	cdev_del(&pci_test->_cdev);
    	unregister_chrdev_region(pci_test->dev, 1);
   	pci_disable_device(dev);

    	if(pci_test) {
        	if(pci_test->msi_enabled) {
            		pci_disable_msi(dev);
            		pci_test->msi_enabled = 0;
        		}
    	}
 
    	pci_release_regions(dev);
}

static struct pci_device_id pci_ids[] = {
    { PCI_DEVICE( VendorID, DeviceID) },
    { 0 }
};

static struct pci_driver driver_ops = {
    .name = DevName,
    .id_table = pci_ids,
    .probe = pci_probe,
    .remove = pci_remove,
};
//驱动模块入口函数
static int Test_init_module(void)
{
	int rc = 0;
	pci_test = kzalloc(sizeof(struct Pci_Test), GFP_KERNEL);
	//配对设备以及注册PCI驱动,如果找到对应设备调用PCI入口函数
	rc = pci_register_driver(&driver_ops);
    	if (rc) {
       		printk(KERN_ALERT  ": PCI driver registration failed\n");
    	}
	
	return rc;
}

static void Test_exit_module(void)
{
	pci_unregister_driver(&driver_ops);
	kfree(pci_test);
}
module_init(Test_init_module);
module_exit(Test_exit_module);
MODULE_AUTHOR(DevName);
MODULE_LICENSE("GPL");

上一篇Pci驱动调用字符设备驱动方式:http://blog.csdn.net/a29562268/article/details/78446178!

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 EP 端驱动代码中,可以通过以下步骤实现向 PCIe RC 侧发送 hot reset 请求: 1. 首先需要获取 PCIe 设备PCI 设备 ID,可以使用 `pci_get_device()` 函数获取。 2. 然后需要获取 PCIe 设备所在的 PCIe 总线编号和 PCIe 设备编号,可以使用 `pci_read_config_byte()` 函数来读取 PCIe 设备的配置空间中的 `bus` 和 `devfn` 寄存器。 3. 接下来,需要向 PCIe RC 侧发送 hot reset 请求。可以使用 `pcie_capability_clear_and_set_word()` 函数来设置 PCIe 设备的 `Device Control` 寄存器中的 `Secondary Bus Reset` 位,以触发 hot reset 请求。 4. 最后,需要等待一段时间,让 hot reset 请求完成。可以通过读取 `Device Control` 寄存器中的 `Secondary Bus Reset` 位来判断 hot reset 请求是否完成。 以下是一个示例代码片段,用于在 EP 端向 PCIe RC 侧发送 hot reset 请求: ```c /* 获取 PCI 设备 ID */ struct pci_dev *pdev = pci_get_device(PCI_VENDOR_ID, PCI_DEVICE_ID, NULL); /* 获取 PCIe 总线编号和 PCIe 设备编号 */ u8 bus = pci_read_config_byte(pdev, PCI_BUS); u8 devfn = pci_read_config_byte(pdev, PCI_DEVFN); /* 向 PCIe RC 侧发送 hot reset 请求 */ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RESET_LT, PCI_EXP_DEVCTL_RESET_LT); /* 等待 hot reset 请求完成 */ while (pcie_capability_read_word(pdev, PCI_EXP_DEVCTL) & PCI_EXP_DEVCTL_RESET_LT) msleep(100); ``` 需要注意的是,hot reset 请求可能会影响 PCIe 设备的状态和性能,因此在实际应用中需要慎重考虑是否需要发送 hot reset 请求。此外,hot reset 请求也可能会影响 PCIe RC 侧连接的其他 PCIe 设备,因此需要确保在发送 hot reset 请求前已经做好了相关的准备工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坤昱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值