[PATCH v4 2/4] uio_pci_generic: properly initialize PCI bars mappings towards UIO
From: Vlad Zolotarov
Date: Tue Oct 06 2015 - 12:18:03 EST
Signed-off-by: Vlad Zolotarov <vladz@xxxxxxxxxxxxxxxxxxxx>
---
drivers/uio/uio_pci_generic.c | 89 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c
index d0b508b..2c6e2b1 100644
--- a/drivers/uio/uio_pci_generic.c
+++ b/drivers/uio/uio_pci_generic.c
@@ -40,6 +40,75 @@ to_uio_pci_generic_dev(struct uio_info *info)
return container_of(info, struct uio_pci_generic_dev, info);
}
+/* Unmap previously ioremap'd resources */
+static void release_iomaps(struct uio_pci_generic_dev *gdev)
+{
+ int i;
+ struct uio_mem *mem = gdev->info.mem;
+
+ for (i = 0; i < MAX_UIO_MAPS; i++, mem++) {
+ if (mem->internal_addr) {
+ iounmap(mem->internal_addr);
+ mem->internal_addr = NULL;
+ }
+ }
+}
+
+static int setup_maps(struct pci_dev *pdev, struct uio_info *info)
+{
+ int i, m = 0, p = 0, err;
+ static const char * const bar_names[] = {
+ "BAR0", "BAR1", "BAR2", "BAR3", "BAR4", "BAR5",
+ };
+
+ for (i = 0; i < ARRAY_SIZE(bar_names); i++) {
+ unsigned long start = pci_resource_start(pdev, i);
+ unsigned long flags = pci_resource_flags(pdev, i);
+ unsigned long len = pci_resource_len(pdev, i);
+
+ if (start == 0 || len == 0)
+ continue;
+
+ if (flags & IORESOURCE_MEM) {
+ void __iomem *addr;
+
+ if (m >= MAX_UIO_MAPS)
+ continue;
+
+ addr = ioremap(start, len);
+ if (addr == NULL) {
+ err = -EINVAL;
+ goto fail;
+ }
+
+ info->mem[m].name = bar_names[i];
+ info->mem[m].addr = start;
+ info->mem[m].internal_addr = addr;
+ info->mem[m].size = len;
+ info->mem[m].memtype = UIO_MEM_PHYS;
+ ++m;
+ } else if (flags & IORESOURCE_IO) {
+ if (p >= MAX_UIO_PORT_REGIONS)
+ continue;
+
+ info->port[p].name = bar_names[i];
+ info->port[p].start = start;
+ info->port[p].size = len;
+ info->port[p].porttype = UIO_PORT_X86;
+ ++p;
+ }
+ }
+
+ return 0;
+fail:
+ for (i = 0; i < m; i++) {
+ iounmap(info->mem[i].internal_addr);
+ info->mem[i].internal_addr = NULL;
+ }
+
+ return err;
+}
+
/* Interrupt handler. Read/modify/write the command register to disable
* the interrupt. */
static irqreturn_t irqhandler(int irq, struct uio_info *info)
@@ -86,18 +155,35 @@ static int probe(struct pci_dev *pdev,
gdev->info.name = "uio_pci_generic";
gdev->info.version = DRIVER_VERSION;
+
+ err = pci_request_regions(pdev, "uio_pci_generic");
+ if (err != 0) {
+ dev_err(&pdev->dev, "Cannot request regions\n");
+ goto err_request_regions;
+ }
+
gdev->info.irq = pdev->irq;
gdev->info.irq_flags = IRQF_SHARED;
gdev->info.handler = irqhandler;
gdev->pdev = pdev;
+ /* remap resources */
+ err = setup_maps(pdev, &gdev->info);
+ if (err)
+ goto err_maps;
+
err = uio_register_device(&pdev->dev, &gdev->info);
if (err)
goto err_register;
pci_set_drvdata(pdev, gdev);
return 0;
+
err_register:
+ release_iomaps(gdev);
+err_maps:
+ pci_release_regions(pdev);
+err_request_regions:
kfree(gdev);
err_alloc:
err_verify:
@@ -110,8 +196,11 @@ static void remove(struct pci_dev *pdev)
struct uio_pci_generic_dev *gdev = pci_get_drvdata(pdev);
uio_unregister_device(&gdev->info);
+ release_iomaps(gdev);
+ pci_release_regions(pdev);
pci_disable_device(pdev);
kfree(gdev);
+ pci_set_drvdata(pdev, NULL);
}
static struct pci_driver uio_pci_driver = {
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/