首先提纲挈领的总结下基于pci的驱动原理,摘自Understanding Linux Network Internals
原文配合三张图来讲解,这里就不附图了,相关范例的说明也加以删除
When device driver is loaded, it registers with the PCI layer by callingpci_register_driver and
providing its instance of pci_driver. Thepci_driver structure includes a vector with the IDs of those
PCI devices it can drive. The PCI layer then uses that table to see what devices match in its list of detected
PCI devices. It thus creates the driver's device list . In addition, for each matching
device, the PCI layer invokes the probe function provided by the matching driver in itspci_driver
structure. The probe function creates and registers the associated network device.
在init初始化时会调用sata驱动的初始化函数
static int __init sil24_init(void)
{ return pci_register_driver(&sil24_pci_driver);
}
其中
static struct pci_driver sil24_pci_driver = {.name = DRV_NAME,.id_table = sil24_pci_tbl,.probe = sil24_init_one,.remove = ata_pci_remove_one, #ifdef CONFIG_PM.suspend = ata_pci_device_suspend,.resume = sil24_pci_device_resume, #endif
static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) {extern int __MARKER__sil24_cmd_block_is_sized_wrongly;static int printed_version;struct ata_port_info pi = sil24_port_info[ent->driver_data];const struct ata_port_info *ppi[] = { &pi, NULL };void __iomem * const *iomap;struct ata_host *host;int rc;u32 tmp;printk(KERN_INFO"probe start here\n");/* cause link error if sil24_cmd_block is sized wrongly */if (sizeof(union sil24_cmd_block) != PAGE_SIZE)__MARKER__sil24_cmd_block_is_sized_wrongly = 1;if (!printed_version++)dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");/* acquire resources */rc = pcim_enable_device(pdev);if (rc)return rc;rc = pcim_iomap_regions(pdev,(1 << SIL24_HOST_BAR) | (1 << SIL24_PORT_BAR),DRV_NAME);if (rc)return rc;iomap = pcim_iomap_table(pdev);/* apply workaround for completion IRQ loss on PCI-X errata */if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) {tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL);if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))dev_printk(KERN_INFO, &pdev->dev,"Applying completion IRQ loss on PCI-X ""errata fix\n");elsepi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;}/* allocate and fill host */host = ata_host_alloc_pinfo(&pdev->dev, ppi,SIL24_FLAG2NPORTS(ppi[0]->flags));if (!host)return -ENOMEM;host->iomap = iomap;printk(KERN_INFO,"assign iomap here:%0#10lx %0#10lx\n",pci_resource_start(pdev,0),pci_resource_start(pdev,2));/* configure and activate the device */if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));if (rc) {rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));if (rc) {dev_printk(KERN_ERR, &pdev->dev,"64-bit DMA enable failed\n");return rc;}}} else {rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));if (rc) {dev_printk(KERN_ERR, &pdev->dev,"32-bit DMA enable failed\n");return rc;}rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));if (rc) {dev_printk(KERN_ERR, &pdev->dev,"32-bit consistent DMA enable failed\n");return rc;}}/* Set max read request size to 4096. This slightly increases* write throughput for pci-e variants.*/pcie_set_readrq(pdev, 4096);sil24_init_controller(host);if (sata_sil24_msi && !pci_enable_msi(pdev)) {dev_printk(KERN_INFO, &pdev->dev, "Using MSI\n");pci_intx(pdev, 0);}pci_set_master(pdev);return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED,&sil24_sht); }
/** pci_register_driver must be a macro so that KBUILD_MODNAME can be expanded*/ #define pci_register_driver(driver) \__pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
};
pci_register_driver实际是一个宏,最终的实现是
该driver是静态赋值初始化完成的,之后就进入一个复杂的内核pci功能调用线:
__pci_register_driver--->driver_register-->bus_add_driver-->driver_attach-->__driver_attach-->driver_probe_device-->really_probe-->dev->bus->probe(dev)
在静态赋值时probe方法初始化为sil24_init_one。
probe函数需要实现的功能如下:
This function should enable the hardware, allocate the net_device structure, and initialize and register the new device(from ULNI)
static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{extern int __MARKER__sil24_cmd_block_is_sized_wrongly;static int printed_version;struct ata_port_info pi = sil24_port_info[ent->driver_data];const struct ata_port_info *ppi[] = { &pi, NULL };void __iomem * const *iomap;struct ata_host *host;int rc;u32 tmp;printk(KERN_INFO"probe start here\n");/* cause link error if sil24_cmd_block is sized wrongly */if (sizeof(union sil24_cmd_block) != PAGE_SIZE)__MARKER__sil24_cmd_block_is_sized_wrongly = 1;if (!printed_version++)dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");/* acquire resources */rc = pcim_enable_device(pdev);if (rc)return rc;rc = pcim_iomap_regions(pdev,(1 << SIL24_HOST_BAR) | (1 << SIL24_PORT_BAR),DRV_NAME);if (rc)return rc;iomap = pcim_iomap_table(pdev);/* apply workaround for completion IRQ loss on PCI-X errata */if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) {tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL);if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL))dev_printk(KERN_INFO, &pdev->dev,"Applying completion IRQ loss on PCI-X ""errata fix\n");elsepi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC;}/* allocate and fill host */host = ata_host_alloc_pinfo(&pdev->dev, ppi,SIL24_FLAG2NPORTS(ppi[0]->flags));if (!host)return -ENOMEM;host->iomap = iomap;printk(KERN_INFO,"assign iomap here:%0#10lx %0#10lx\n",pci_resource_start(pdev,0),pci_resource_start(pdev,2));/* configure and activate the device */if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));if (rc) {rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));if (rc) {dev_printk(KERN_ERR, &pdev->dev,"64-bit DMA enable failed\n");return rc;}}} else {rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));if (rc) {dev_printk(KERN_ERR, &pdev->dev,"32-bit DMA enable failed\n");return rc;}rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));if (rc) {dev_printk(KERN_ERR, &pdev->dev,"32-bit consistent DMA enable failed\n");return rc;}}/* Set max read request size to 4096. This slightly increases* write throughput for pci-e variants.*/pcie_set_readrq(pdev, 4096);sil24_init_controller(host);if (sata_sil24_msi && !pci_enable_msi(pdev)) {dev_printk(KERN_INFO, &pdev->dev, "Using MSI\n");pci_intx(pdev, 0);}pci_set_master(pdev);return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED,&sil24_sht);
}
sil24_init_one完成设备的初始化实际工作:进行PCI的MEMORY/IO窗口映射,分配IRQ,激活设备等等。
linux的驱动开发有两部分组成:设置实现以及内核匹配。
设备实现是实际的设备相关的功能函数,集中体现在ops数组
static struct ata_port_operations sil24_ops = {.inherits = &sata_pmp_port_ops,.qc_defer = sil24_qc_defer,.qc_prep = sil24_qc_prep,.qc_issue = sil24_qc_issue,.qc_fill_rtf = sil24_qc_fill_rtf,.freeze = sil24_freeze,.thaw = sil24_thaw,.softreset = sil24_softreset,.hardreset = sil24_hardreset,.pmp_softreset = sil24_softreset,.pmp_hardreset = sil24_pmp_hardreset,.error_handler = sil24_error_handler,.post_internal_cmd = sil24_post_internal_cmd,.dev_config = sil24_dev_config,.scr_read = sil24_scr_read,.scr_write = sil24_scr_write,.pmp_attach = sil24_pmp_attach,.pmp_detach = sil24_pmp_detach,.port_start = sil24_port_start,
#ifdef CONFIG_PM.port_resume = sil24_port_resume,
#endif
};
内核匹配指的是在合适的时机用内核框架搭好的入口传入设备初始化及ops数组
这款sata控制器芯片的ops是通过下面的定义传入内核的
static const struct ata_port_info sil24_port_info[] = {/* sil_3124 */{.flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) |SIL24_FLAG_PCIX_IRQ_WOC,.pio_mask = ATA_PIO4,.mwdma_mask = ATA_MWDMA2,.udma_mask = ATA_UDMA5,.port_ops = &sil24_ops,},/* sil_3132 */{.flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2),.pio_mask = ATA_PIO4,.mwdma_mask = ATA_MWDMA2,.udma_mask = ATA_UDMA5,.port_ops = &sil24_ops,},/* sil_3131/sil_3531 */{.flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1),.pio_mask = ATA_PIO4,.mwdma_mask = ATA_MWDMA2,.udma_mask = ATA_UDMA5,.port_ops = &sil24_ops,},
};
在初始化设备时,会调用 host = ata_host_alloc_pinfo(&pdev->dev, ppi, SIL24_FLAG2NPORTS(ppi[0]->flags));
将芯片的控制函数ops穿入内核
struct ata_host *ata_host_alloc_pinfo(struct device *dev,const struct ata_port_info * const * ppi,int n_ports)
{const struct ata_port_info *pi;struct ata_host *host;int i, j;host = ata_host_alloc(dev, n_ports);if (!host)return NULL;for (i = 0, j = 0, pi = NULL; i < host->n_ports; i++) {struct ata_port *ap = host->ports[i];if (ppi[j])pi = ppi[j++];ap->pio_mask = pi->pio_mask;ap->mwdma_mask = pi->mwdma_mask;ap->udma_mask = pi->udma_mask;ap->flags |= pi->flags;ap->link.flags |= pi->link_flags;ap->ops = pi->port_ops;if (!host->ops && (pi->port_ops != &ata_dummy_port_ops))host->ops = pi->port_ops;}return host;
}
中断处理由sil24_init_one在最后结束的时候调用return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED, &sil24_sht);传入内核