18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* uio_pci_generic - generic UIO driver for PCI 2.3 devices 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2009 Red Hat, Inc. 58c2ecf20Sopenharmony_ci * Author: Michael S. Tsirkin <mst@redhat.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Since the driver does not declare any device ids, you must allocate 88c2ecf20Sopenharmony_ci * id and bind the device to the driver yourself. For example: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * # echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id 118c2ecf20Sopenharmony_ci * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind 128c2ecf20Sopenharmony_ci * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind 138c2ecf20Sopenharmony_ci * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver 148c2ecf20Sopenharmony_ci * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Driver won't bind to devices which do not support the Interrupt Disable Bit 178c2ecf20Sopenharmony_ci * in the command register. All devices compliant to PCI 2.3 (circa 2002) and 188c2ecf20Sopenharmony_ci * all compliant PCI Express devices should support this bit. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/device.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/pci.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/uio_driver.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define DRIVER_VERSION "0.01.0" 288c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Michael S. Tsirkin <mst@redhat.com>" 298c2ecf20Sopenharmony_ci#define DRIVER_DESC "Generic UIO driver for PCI 2.3 devices" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct uio_pci_generic_dev { 328c2ecf20Sopenharmony_ci struct uio_info info; 338c2ecf20Sopenharmony_ci struct pci_dev *pdev; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline struct uio_pci_generic_dev * 378c2ecf20Sopenharmony_cito_uio_pci_generic_dev(struct uio_info *info) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci return container_of(info, struct uio_pci_generic_dev, info); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int release(struct uio_info *info, struct inode *inode) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* 478c2ecf20Sopenharmony_ci * This driver is insecure when used with devices doing DMA, but some 488c2ecf20Sopenharmony_ci * people (mis)use it with such devices. 498c2ecf20Sopenharmony_ci * Let's at least make sure DMA isn't left enabled after the userspace 508c2ecf20Sopenharmony_ci * driver closes the fd. 518c2ecf20Sopenharmony_ci * Note that there's a non-zero chance doing this will wedge the device 528c2ecf20Sopenharmony_ci * at least until reset. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci pci_clear_master(gdev->pdev); 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Interrupt handler. Read/modify/write the command register to disable 598c2ecf20Sopenharmony_ci * the interrupt. */ 608c2ecf20Sopenharmony_cistatic irqreturn_t irqhandler(int irq, struct uio_info *info) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (!pci_check_and_mask_intx(gdev->pdev)) 658c2ecf20Sopenharmony_ci return IRQ_NONE; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* UIO core will signal the user process. */ 688c2ecf20Sopenharmony_ci return IRQ_HANDLED; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int probe(struct pci_dev *pdev, 728c2ecf20Sopenharmony_ci const struct pci_device_id *id) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct uio_pci_generic_dev *gdev; 758c2ecf20Sopenharmony_ci int err; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 788c2ecf20Sopenharmony_ci if (err) { 798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n", 808c2ecf20Sopenharmony_ci __func__, err); 818c2ecf20Sopenharmony_ci return err; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (pdev->irq && !pci_intx_mask_supported(pdev)) { 858c2ecf20Sopenharmony_ci err = -ENODEV; 868c2ecf20Sopenharmony_ci goto err_verify; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL); 908c2ecf20Sopenharmony_ci if (!gdev) { 918c2ecf20Sopenharmony_ci err = -ENOMEM; 928c2ecf20Sopenharmony_ci goto err_alloc; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci gdev->info.name = "uio_pci_generic"; 968c2ecf20Sopenharmony_ci gdev->info.version = DRIVER_VERSION; 978c2ecf20Sopenharmony_ci gdev->info.release = release; 988c2ecf20Sopenharmony_ci gdev->pdev = pdev; 998c2ecf20Sopenharmony_ci if (pdev->irq) { 1008c2ecf20Sopenharmony_ci gdev->info.irq = pdev->irq; 1018c2ecf20Sopenharmony_ci gdev->info.irq_flags = IRQF_SHARED; 1028c2ecf20Sopenharmony_ci gdev->info.handler = irqhandler; 1038c2ecf20Sopenharmony_ci } else { 1048c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "No IRQ assigned to device: " 1058c2ecf20Sopenharmony_ci "no support for interrupts?\n"); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci err = uio_register_device(&pdev->dev, &gdev->info); 1098c2ecf20Sopenharmony_ci if (err) 1108c2ecf20Sopenharmony_ci goto err_register; 1118c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, gdev); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_cierr_register: 1158c2ecf20Sopenharmony_ci kfree(gdev); 1168c2ecf20Sopenharmony_cierr_alloc: 1178c2ecf20Sopenharmony_cierr_verify: 1188c2ecf20Sopenharmony_ci pci_disable_device(pdev); 1198c2ecf20Sopenharmony_ci return err; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void remove(struct pci_dev *pdev) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct uio_pci_generic_dev *gdev = pci_get_drvdata(pdev); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci uio_unregister_device(&gdev->info); 1278c2ecf20Sopenharmony_ci pci_disable_device(pdev); 1288c2ecf20Sopenharmony_ci kfree(gdev); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct pci_driver uio_pci_driver = { 1328c2ecf20Sopenharmony_ci .name = "uio_pci_generic", 1338c2ecf20Sopenharmony_ci .id_table = NULL, /* only dynamic id's */ 1348c2ecf20Sopenharmony_ci .probe = probe, 1358c2ecf20Sopenharmony_ci .remove = remove, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cimodule_pci_driver(uio_pci_driver); 1398c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 1408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1418c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 1428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 143