首先提纲挈领的总结下基于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.
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"); else pi.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)
};
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");
else
pi.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);
}
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
};
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,
},
};
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;
}