1bce5c2eaSStephen Hemminger // SPDX-License-Identifier: GPL-2.0 2ccb86a69SMichael S. Tsirkin /* uio_pci_generic - generic UIO driver for PCI 2.3 devices 3ccb86a69SMichael S. Tsirkin * 4ccb86a69SMichael S. Tsirkin * Copyright (C) 2009 Red Hat, Inc. 5ccb86a69SMichael S. Tsirkin * Author: Michael S. Tsirkin <mst@redhat.com> 6ccb86a69SMichael S. Tsirkin * 7ccb86a69SMichael S. Tsirkin * Since the driver does not declare any device ids, you must allocate 8ccb86a69SMichael S. Tsirkin * id and bind the device to the driver yourself. For example: 9ccb86a69SMichael S. Tsirkin * 10ccb86a69SMichael S. Tsirkin * # echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id 11ccb86a69SMichael S. Tsirkin * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind 12ccb86a69SMichael S. Tsirkin * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind 13ccb86a69SMichael S. Tsirkin * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver 14ccb86a69SMichael S. Tsirkin * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic 15ccb86a69SMichael S. Tsirkin * 16ccb86a69SMichael S. Tsirkin * Driver won't bind to devices which do not support the Interrupt Disable Bit 17ccb86a69SMichael S. Tsirkin * in the command register. All devices compliant to PCI 2.3 (circa 2002) and 18ccb86a69SMichael S. Tsirkin * all compliant PCI Express devices should support this bit. 19ccb86a69SMichael S. Tsirkin */ 20ccb86a69SMichael S. Tsirkin 21ccb86a69SMichael S. Tsirkin #include <linux/device.h> 22ccb86a69SMichael S. Tsirkin #include <linux/module.h> 23ccb86a69SMichael S. Tsirkin #include <linux/pci.h> 245a0e3ad6STejun Heo #include <linux/slab.h> 25ccb86a69SMichael S. Tsirkin #include <linux/uio_driver.h> 26ccb86a69SMichael S. Tsirkin 27ccb86a69SMichael S. Tsirkin #define DRIVER_VERSION "0.01.0" 28ccb86a69SMichael S. Tsirkin #define DRIVER_AUTHOR "Michael S. Tsirkin <mst@redhat.com>" 29ccb86a69SMichael S. Tsirkin #define DRIVER_DESC "Generic UIO driver for PCI 2.3 devices" 30ccb86a69SMichael S. Tsirkin 31ccb86a69SMichael S. Tsirkin struct uio_pci_generic_dev { 32ccb86a69SMichael S. Tsirkin struct uio_info info; 33ccb86a69SMichael S. Tsirkin struct pci_dev *pdev; 34ccb86a69SMichael S. Tsirkin }; 35ccb86a69SMichael S. Tsirkin 36ccb86a69SMichael S. Tsirkin static inline struct uio_pci_generic_dev * 37ccb86a69SMichael S. Tsirkin to_uio_pci_generic_dev(struct uio_info *info) 38ccb86a69SMichael S. Tsirkin { 39ccb86a69SMichael S. Tsirkin return container_of(info, struct uio_pci_generic_dev, info); 40ccb86a69SMichael S. Tsirkin } 41ccb86a69SMichael S. Tsirkin 42*865a11f9SVenkatesh Srinivas static int release(struct uio_info *info, struct inode *inode) 43*865a11f9SVenkatesh Srinivas { 44*865a11f9SVenkatesh Srinivas struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info); 45*865a11f9SVenkatesh Srinivas 46*865a11f9SVenkatesh Srinivas /* 47*865a11f9SVenkatesh Srinivas * This driver is insecure when used with devices doing DMA, but some 48*865a11f9SVenkatesh Srinivas * people (mis)use it with such devices. 49*865a11f9SVenkatesh Srinivas * Let's at least make sure DMA isn't left enabled after the userspace 50*865a11f9SVenkatesh Srinivas * driver closes the fd. 51*865a11f9SVenkatesh Srinivas * Note that there's a non-zero chance doing this will wedge the device 52*865a11f9SVenkatesh Srinivas * at least until reset. 53*865a11f9SVenkatesh Srinivas */ 54*865a11f9SVenkatesh Srinivas pci_clear_master(gdev->pdev); 55*865a11f9SVenkatesh Srinivas return 0; 56*865a11f9SVenkatesh Srinivas } 57*865a11f9SVenkatesh Srinivas 58ccb86a69SMichael S. Tsirkin /* Interrupt handler. Read/modify/write the command register to disable 59ccb86a69SMichael S. Tsirkin * the interrupt. */ 60ccb86a69SMichael S. Tsirkin static irqreturn_t irqhandler(int irq, struct uio_info *info) 61ccb86a69SMichael S. Tsirkin { 62ccb86a69SMichael S. Tsirkin struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info); 63ccb86a69SMichael S. Tsirkin 642502dbdfSJan Kiszka if (!pci_check_and_mask_intx(gdev->pdev)) 652502dbdfSJan Kiszka return IRQ_NONE; 66ccb86a69SMichael S. Tsirkin 67ccb86a69SMichael S. Tsirkin /* UIO core will signal the user process. */ 682502dbdfSJan Kiszka return IRQ_HANDLED; 69ccb86a69SMichael S. Tsirkin } 70ccb86a69SMichael S. Tsirkin 71b17b75bbSBill Pemberton static int probe(struct pci_dev *pdev, 72ccb86a69SMichael S. Tsirkin const struct pci_device_id *id) 73ccb86a69SMichael S. Tsirkin { 74ccb86a69SMichael S. Tsirkin struct uio_pci_generic_dev *gdev; 75ccb86a69SMichael S. Tsirkin int err; 76ccb86a69SMichael S. Tsirkin 77ccb86a69SMichael S. Tsirkin err = pci_enable_device(pdev); 78ccb86a69SMichael S. Tsirkin if (err) { 79ccb86a69SMichael S. Tsirkin dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n", 80ccb86a69SMichael S. Tsirkin __func__, err); 81ccb86a69SMichael S. Tsirkin return err; 82ccb86a69SMichael S. Tsirkin } 83ccb86a69SMichael S. Tsirkin 84acec09e6SJim Harris if (pdev->irq && !pci_intx_mask_supported(pdev)) { 852502dbdfSJan Kiszka err = -ENODEV; 86ccb86a69SMichael S. Tsirkin goto err_verify; 872502dbdfSJan Kiszka } 88ccb86a69SMichael S. Tsirkin 89ccb86a69SMichael S. Tsirkin gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL); 90ccb86a69SMichael S. Tsirkin if (!gdev) { 91ccb86a69SMichael S. Tsirkin err = -ENOMEM; 92ccb86a69SMichael S. Tsirkin goto err_alloc; 93ccb86a69SMichael S. Tsirkin } 94ccb86a69SMichael S. Tsirkin 95ccb86a69SMichael S. Tsirkin gdev->info.name = "uio_pci_generic"; 96ccb86a69SMichael S. Tsirkin gdev->info.version = DRIVER_VERSION; 97*865a11f9SVenkatesh Srinivas gdev->info.release = release; 98acec09e6SJim Harris gdev->pdev = pdev; 99acec09e6SJim Harris if (pdev->irq) { 100ccb86a69SMichael S. Tsirkin gdev->info.irq = pdev->irq; 101ccb86a69SMichael S. Tsirkin gdev->info.irq_flags = IRQF_SHARED; 102ccb86a69SMichael S. Tsirkin gdev->info.handler = irqhandler; 103acec09e6SJim Harris } else { 104acec09e6SJim Harris dev_warn(&pdev->dev, "No IRQ assigned to device: " 105acec09e6SJim Harris "no support for interrupts?\n"); 106acec09e6SJim Harris } 107ccb86a69SMichael S. Tsirkin 108c4277e9eSAlexey Khoroshilov err = uio_register_device(&pdev->dev, &gdev->info); 109c4277e9eSAlexey Khoroshilov if (err) 110ccb86a69SMichael S. Tsirkin goto err_register; 111ccb86a69SMichael S. Tsirkin pci_set_drvdata(pdev, gdev); 112ccb86a69SMichael S. Tsirkin 113ccb86a69SMichael S. Tsirkin return 0; 114ccb86a69SMichael S. Tsirkin err_register: 115ccb86a69SMichael S. Tsirkin kfree(gdev); 116ccb86a69SMichael S. Tsirkin err_alloc: 117ccb86a69SMichael S. Tsirkin err_verify: 118ccb86a69SMichael S. Tsirkin pci_disable_device(pdev); 119ccb86a69SMichael S. Tsirkin return err; 120ccb86a69SMichael S. Tsirkin } 121ccb86a69SMichael S. Tsirkin 122ccb86a69SMichael S. Tsirkin static void remove(struct pci_dev *pdev) 123ccb86a69SMichael S. Tsirkin { 124ccb86a69SMichael S. Tsirkin struct uio_pci_generic_dev *gdev = pci_get_drvdata(pdev); 125ccb86a69SMichael S. Tsirkin 126ccb86a69SMichael S. Tsirkin uio_unregister_device(&gdev->info); 127ccb86a69SMichael S. Tsirkin pci_disable_device(pdev); 128ccb86a69SMichael S. Tsirkin kfree(gdev); 129ccb86a69SMichael S. Tsirkin } 130ccb86a69SMichael S. Tsirkin 131cd437398SPeter Huewe static struct pci_driver uio_pci_driver = { 132ccb86a69SMichael S. Tsirkin .name = "uio_pci_generic", 133ccb86a69SMichael S. Tsirkin .id_table = NULL, /* only dynamic id's */ 134ccb86a69SMichael S. Tsirkin .probe = probe, 135ccb86a69SMichael S. Tsirkin .remove = remove, 136ccb86a69SMichael S. Tsirkin }; 137ccb86a69SMichael S. Tsirkin 138cd437398SPeter Huewe module_pci_driver(uio_pci_driver); 139ccb86a69SMichael S. Tsirkin MODULE_VERSION(DRIVER_VERSION); 140ccb86a69SMichael S. Tsirkin MODULE_LICENSE("GPL v2"); 141ccb86a69SMichael S. Tsirkin MODULE_AUTHOR(DRIVER_AUTHOR); 142ccb86a69SMichael S. Tsirkin MODULE_DESCRIPTION(DRIVER_DESC); 143