获取配置信息

// 打印配置空间信息
void skel_get_configs(struct pci_dev* dev) {

    uint8_t revisin_id;
    uint16_t vendor_id, device_id;
    uint32_t class_id;

    pci_read_config_word(dev, PCI_VENDOR_ID, &vendor_id);
    printk("vendorID = %x", vendor_id);

    pci_read_config_word(dev, PCI_DEVICE_ID, &device_id);
    printk("deviceID = %x", device_id);

    pci_read_config_word(dev, PCI_REVISION_ID, &revisin_id);
    printk("deviceID = %x", revisin_id);

    pci_read_config_word(dev, PCI_CLASS_REVISION, &class_id);
    printk("classID = %x", class_id);
}

pci_read_config_word:从PCI设备配置空间中读取特定寄存器的值,并将其存储到对应的变量中。

  • 供应商ID(vendor ID)

  • 设备ID(device ID)

  • 修订ID(revision ID):修订ID(Revision ID)是PCI设备的一个标识符,用于指示设备的版本或修订级别。每个PCI设备都有一个唯一的修订ID,它反映了硬件设计上的变化、错误修复或其他更改。通过读取修订ID,可以确定具体使用的是哪个版本或修订级别的设备。(每个PCI设备都有一个唯一的修订ID,它反映了硬件设计上的变化、错误修复或其他更改。通过读取修订ID,可以确定具体使用的是哪个版本或修订级别的设备。)

  • 类别ID(class ID):类别ID(Class ID)是PCI设备的一个标识符,用于指示设备所属的设备类别。每个PCI设备都有一个唯一的类别ID,它定义了该设备的功能和特征。(PCI规范将所有可能的设备分为多个不同的类别,每个类别都有一个独特的16位标识符。这些类别包括网络控制器、声卡、图形卡、存储控制器等等。通过读取和解析设备的类别ID,可以确定该设备所属的具体类别。)

设备中断函数

static irqreturn_t pci_mcard_interrupt(int irq, void* dev_id) {

    pci_card_t* pci_mcard = (pci_card_t*)dev_id;

    //打印中断号
    printk("irq = %d, pci_mcard_irq = %d\n", irq, pci_mcard->irq);
    return IRQ_HANDLED;
}
  • irq:中断号

  • dev_id:设备标识符

探测和初始化PCI设备

static int probe(struct pci_dev* dev, const struct pci_device_id* id) {

    int retval = 0;
    pci_card_t* pci_mcard;

    printk("probe func\n");

    // 设备使能
    if (pci_enable_device(dev)) {

        printk(KERN_ERR "IO Error.\n");
        return -EIO;
    }

    pci_mcard = kmalloc(sizeof(pci_card_t), GFP_KERNEL);
    if (!pci_mcard) {

        printk("In %s, kmalloc err!", __func__);
        return -ENOMEM;
    }

    // 设备中断号
    pci_mcard->irq = dev->irq;
    if (pci_mcard->irq < 0) {

        printk("IRQ is %d, it's invalid!\n", pci_mcard->irq);
        goto out_pci_mcard;
    }

    // 获取io内存相关信息
    pci_mcard->io = pci_resource_start(dev, 0);
    pci_mcard->range = pci_resource_end(dev, 0) - pci_mcard->io + 1;
    pci_mcard->flags = pci_resource_flags(dev, 0);
    printk("start %llx %lx %lx\n", pci_mcard->io, pci_mcard->range, pci_mcard->flags);
    printk("PCI base addr 0 is io%s.\n", (pci_mcard->flags & IORESOURCE_MEM) ? "mem" : "port");

    // 防止地址访问冲突 先申请
    retval = pci_request_regions(dev, "pci_module");
    if (retval) {

        printk("PCI request regions err!\n");
        goto out_pci_mcard;
    }

    // 在此映射
    pci_mcard->ioaddr = pci_ioremap_bar(dev, 0);
    if (!pci_mcard->ioaddr) {

        printk("ioremap err!\n");
        retval = -ENOMEM;
        goto out_regions;
    }

    retval = request_irq(pci_mcard->irq, pci_mcard_interrupt, IRQF_SHARED, "pci_module", pci_mcard);
    if (retval) {

        printk(KERN_ERR, "Can't get assigned IRQ %d.\n", pci_mcard->irq);
        goto out_iounmap;
    }

    pci_set_drvdata(dev, pci_mcard);
    skel_get_configs(dev);

    return 0;
out_iounmap:
    iounmap(pci_mcard->ioaddr);
out_regions:
    pci_release_regions(dev);
out_pci_mcard:
    kfree(pci_mcard);
    return retval;
}
  1. 开启设备使能:使用pci_enable_device()函数来启用PCI设备。如果无法启动成功,则返回-EIO。

  2. 分配并初始化pci_card_t结构体:使用kmalloc()函数为pci_card_t结构体分配内存,并进行相应的错误处理。

  3. 获取中断号和IO内存相关信息:从PCI设备结构体dev中获取中断号、IO起始地址、地址范围以及标志位信息,并打印相关信息。

  4. 申请IO区域资源:使用pci_request_regions()函数申请IO区域资源,防止地址访问冲突。如果申请失败,则进行错误处理。

  5. 映射IO地址空间:使用pci_ioremap_bar()函数将物理地址映射到虚拟地址空间,并进行相应的错误处理。

  6. 请求IRQ(中断请求):使用request_irq()函数请求IRQ,并将自定义的中断函数pci_mcard_interrupt与之关联。如果请求失败,则进行错误处理。

  7. 设置驱动函数:使用pci_set_drvdata()函数将pci_mcard指针保存在设备结构体dev的私有数据字段中,以便后续在其他驱动程序调用时可以方便地获取。

  8. 获取配置信息:调用skel_get_configs()函数来获取PCI设备的配置信息。

  9. 返回0表示探测成果,否则进行错误处理返回相应错误码。

移除PCI设备

static void remove(struct pci_dev* dev) {

    pci_card_t* pci_mcard = pci_get_drvdata(dev);
    free_irq(pci_mcard->irq, pci_mcard);
    iounmap(pci_mcard->ioaddr);
    pci_release_region(dev);
    kfree(pci_mcard);
    pci_disable_device(dev);
    printk(remove pci device ok.\n);
}
  1. 获取与 PCI 设备相关联的私有数据结构 pci_mcard,该结构包含了一些设备相关的信息。

  2. 使用 free_irq 函数释放之前注册的中断处理程序。

  3. 使用 iounmap 函数解除之前映射的 I/O 内存地址。

  4. 调用 pci_release_region 释放之前分配的 PCI 资源区域。

  5. 使用 kfree 释放私有数据结构内存。

  6. 使用 pci_disable_device 禁用 PCI 设备,防止进一步访问设备。

  7. 打印调试信息,说明成功移除了 PCI 设备。