From eb88dc19b8729fea5dc740e80f6f9d44791570fe Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Wed, 2 Mar 2016 15:34:01 +0100 Subject: Resolution of the user-space BAR addresses --- CMakeLists.txt | 2 +- driver/Makefile | 14 ++++++++++---- driver/base.c | 28 ++++++++++++++++++++++++---- driver/base.h | 3 +++ driver/common.h | 30 ++++++++++++++++-------------- driver/rdma.c | 23 ++++++++++++++++++++++- driver/rdma.h | 3 --- 7 files changed, 76 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd889f0..33be2fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ if (NOT DISABLE_PYTHON) execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "import site; print site.PREFIXES[-1]" OUTPUT_VARIABLE PYTHON_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) file (TO_CMAKE_PATH "${PYTHON_PREFIX}" PYTHON_PREFIX) - execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "import site; print site.getsitepackages()[-1]" OUTPUT_VARIABLE PYTHON_SITE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "import site; print site.getsitepackages()[0]" OUTPUT_VARIABLE PYTHON_SITE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) file (TO_CMAKE_PATH "${PYTHON_SITE_DIR}" PYTHON_SITE_DIR) string (REGEX REPLACE "^${PYTHON_PREFIX}/" "${CMAKE_INSTALL_PREFIX}/" PYTHON_SITE_DIR "${PYTHON_SITE_DIR}") diff --git a/driver/Makefile b/driver/Makefile index 0a860bf..8d8ada8 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -4,7 +4,8 @@ obj-m := pciDriver.o pciDriver-objs := base.o int.o umem.o kmem.o sysfs.o ioctl.o compat.o rdma.o KERNELDIR ?= /lib/modules/$(shell uname -r)/build -INSTALLDIR ?= /lib/modules/$(shell uname -r)/kernel/extra +INSTALLDIR ?= /lib/modules/$(shell uname -r)/extra +MAININSTALLDIR ?= /lib/modules/$(shell uname -r)/kernel/extra PWD := $(shell pwd) EXTRA_CFLAGS += -I$(M)/.. @@ -53,13 +54,18 @@ install: @mkdir -p $(INSTALLDIR) @echo "INSTALL $(INSTALLDIR)/pciDriver.ko" @install -m 755 pciDriver.ko $(INSTALLDIR) - @echo "INSTALL /usr/include/pciDriver/driver/pciDriver.h" - @mkdir -p /usr/include/pciDriver/driver - @install -m 644 pciDriver.h /usr/include/pciDriver/driver + @echo "INSTALL $(INSTALLDIR)/pciDriver.symvers" + @install -m 644 Module.symvers $(INSTALLDIR)/pciDriver.symvers +# @echo "INSTALL /usr/include/pciDriver/driver/pciDriver.h" +# @mkdir -p /usr/include/pciDriver/driver +# @install -m 644 pciDriver.h /usr/include/pciDriver/driver uninstall: @echo "UNINSTALL $(INSTALLDIR)/pciDriver.ko" @rm -f $(INSTALLDIR)/pciDriver.ko + @rm -f $(MAININSTALLDIR)/pciDriver.ko + @rm -f $(INSTALLDIR)/pciDriver.symvers + @rm -f $(MAININSTALLDIR)/pciDriver.symvers @echo "UNINSTALL /usr/include/pciDriver/driver/pciDriver.h" @rm -rf /usr/include/pciDriver/driver diff --git a/driver/base.c b/driver/base.c index 4e55dda..dfd82c6 100644 --- a/driver/base.c +++ b/driver/base.c @@ -211,7 +211,7 @@ MODULE_LICENSE("GPL v2"); static struct class_compat *pcidriver_class; #ifdef PCIDRIVER_DUMMY_DEVICE -pcidriver_privdata_t *pcidriver_privdata = NULL; +pcidriver_privdata_t *pcidriver_dummydata = NULL; #endif /* PCIDRIVER_DUMMY_DEVICE */ /** @@ -225,6 +225,8 @@ static int __init pcidriver_init(void) /* Initialize the device count */ atomic_set(&pcidriver_deviceCount, 0); + + memset(pcidriver_privdata, 0, sizeof(pcidriver_privdata)); /* Allocate character device region dynamically */ if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) { @@ -377,6 +379,8 @@ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_devi err = -ENOMEM; goto probe_nomem; } + + privdata->devid = devid; INIT_LIST_HEAD(&(privdata->kmem_list)); spin_lock_init(&(privdata->kmemlist_lock)); @@ -387,7 +391,7 @@ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_devi atomic_set(&privdata->umem_count, 0); #ifdef PCIDRIVER_DUMMY_DEVICE - pcidriver_privdata = privdata; + pcidriver_dummydata = privdata; #else /* PCIDRIVER_DUMMY_DEVICE */ pci_set_drvdata(pdev, privdata); privdata->pdev = pdev; @@ -443,6 +447,8 @@ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_devi goto probe_cdevadd_fail; } + pcidriver_privdata[devid] = privdata; + return 0; probe_device_create_fail: @@ -473,13 +479,16 @@ static void __devexit pcidriver_remove(struct pci_dev *pdev) pcidriver_privdata_t *privdata; #ifdef PCIDRIVER_DUMMY_DEVICE - privdata = pcidriver_privdata; - pcidriver_privdata = NULL; + privdata = pcidriver_dummydata; + pcidriver_dummydata = NULL; #else /* PCIDRIVER_DUMMY_DEVICE */ /* Get private data from the device */ privdata = pci_get_drvdata(pdev); #endif /* PCIDRIVER_DUMMY_DEVICE */ + // Theoretically we should lock here and when using... + pcidriver_privdata[privdata->devid] = NULL; + /* Removing sysfs attributes from class device */ #define sysfs_attr(name) do { \ class_device_remove_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)); \ @@ -729,3 +738,14 @@ int pcidriver_mmap_pci(pcidriver_privdata_t *privdata, struct vm_area_struct *vm return 0; /* success */ #endif /* PCIDRIVER_DUMMY_DEVICE */ } + +pcidriver_privdata_t *pcidriver_get_privdata(int devid) { + if (devid >= MAXDEVICES) + return NULL; + + return pcidriver_privdata[devid]; +} + +void pcidriver_put_privdata(pcidriver_privdata_t *privdata) { + +} diff --git a/driver/base.h b/driver/base.h index 5b977fa..dce865f 100644 --- a/driver/base.h +++ b/driver/base.h @@ -69,6 +69,9 @@ static dev_t pcidriver_devt; /* Number of devices allocated */ static atomic_t pcidriver_deviceCount; +/* Private data for probed devices */ +static pcidriver_privdata_t* pcidriver_privdata[MAXDEVICES]; + /* Sysfs attributes */ static DEVICE_ATTR(mmap_mode, 0664, pcidriver_show_mmap_mode, pcidriver_store_mmap_mode); static DEVICE_ATTR(mmap_area, 0664, pcidriver_show_mmap_area, pcidriver_store_mmap_area); diff --git a/driver/common.h b/driver/common.h index 6bc1329..a83fd5e 100644 --- a/driver/common.h +++ b/driver/common.h @@ -36,30 +36,28 @@ typedef struct { struct list_head list; unsigned int nr_pages; /* number of pages for this user memeory area */ struct page **pages; /* list of pointers to the pages */ - unsigned int nents; /* actual entries in the scatter/gatter list (NOT nents for the map function, but the result) */ + unsigned int nents; /* actual entries in the scatter/gatter list (NOT nents for the map function, but the result) */ struct scatterlist *sg; /* list of sg entries */ struct class_device_attribute sysfs_attr; /* initialized when adding the entry */ } pcidriver_umem_entry_t; /* Hold the driver private data */ typedef struct { - dev_t devno; /* device number (major and minor) */ + int devid; /* the device id */ + dev_t devno; /* device number (major and minor) */ struct pci_dev *pdev; /* PCI device */ - struct class_device *class_dev; /* Class device */ - struct cdev cdev; /* char device struct */ - int mmap_mode; /* current mmap mode */ - int mmap_area; /* current PCI mmap area */ + struct class_device *class_dev; /* Class device */ + struct cdev cdev; /* char device struct */ + int mmap_mode; /* current mmap mode */ + int mmap_area; /* current PCI mmap area */ #ifdef ENABLE_IRQ - int irq_enabled; /* Non-zero if IRQ is enabled */ - int irq_count; /* Just an IRQ counter */ - - wait_queue_head_t irq_queues[ PCIDRIVER_INT_MAXSOURCES ]; - /* One queue per interrupt source */ - atomic_t irq_outstanding[ PCIDRIVER_INT_MAXSOURCES ]; - /* Outstanding interrupts per queue */ - volatile unsigned int *bars_kmapped[6]; /* PCI BARs mmapped in kernel space */ + int irq_enabled; /* Non-zero if IRQ is enabled */ + int irq_count; /* Just an IRQ counter */ + wait_queue_head_t irq_queues[ PCIDRIVER_INT_MAXSOURCES ]; /* One queue per interrupt source */ + atomic_t irq_outstanding[ PCIDRIVER_INT_MAXSOURCES ]; /* Outstanding interrupts per queue */ + volatile unsigned int *bars_kmapped[6]; /* PCI BARs mmapped in kernel space */ #endif spinlock_t kmemlist_lock; /* Spinlock to lock kmem list operations */ @@ -81,6 +79,10 @@ typedef struct { void pcidriver_module_get(pcidriver_privdata_t *privdata); void pcidriver_module_put(pcidriver_privdata_t *privdata); +pcidriver_privdata_t *pcidriver_get_privdata(int devid); +void pcidriver_put_privdata(pcidriver_privdata_t *privdata); + + /*************************************************************************/ /* Some nice defines that make code more readable */ /* This is to print nice info in the log */ diff --git a/driver/rdma.c b/driver/rdma.c index 22a4a5e..78c6d69 100644 --- a/driver/rdma.c +++ b/driver/rdma.c @@ -7,7 +7,12 @@ #include #include #include +#include +#include "config.h" +#include "compat.h" +#include "pciDriver.h" +#include "common.h" #include "rdma.h" static unsigned long pcidriver_follow_pte(struct mm_struct *mm, unsigned long address) @@ -42,12 +47,28 @@ static unsigned long pcidriver_follow_pte(struct mm_struct *mm, unsigned long ad } unsigned long pcidriver_resolve_bar(unsigned long address) { + int dev, bar; unsigned long pfn; address = (address >> PAGE_SHIFT) << PAGE_SHIFT; pfn = pcidriver_follow_pte(current->mm, address); - return pfn; + for (dev = 0; dev < MAXDEVICES; dev++) + { + pcidriver_privdata_t *privdata = pcidriver_get_privdata(dev); + if (!privdata) continue; + + for (bar = 0; bar < 6; bar++) + { + unsigned long start = pci_resource_start(privdata->pdev, bar); + unsigned long end = start + pci_resource_len(privdata->pdev, bar); + if ((pfn >= start)&&(pfn < end)) + return pfn; + } + pcidriver_put_privdata(privdata); + } + + return 0; } EXPORT_SYMBOL(pcidriver_resolve_bar); diff --git a/driver/rdma.h b/driver/rdma.h index 4aeda78..cfe9c83 100644 --- a/driver/rdma.h +++ b/driver/rdma.h @@ -1,9 +1,6 @@ #ifndef _PCIDRIVER_RDMA_H #define _PCIDRIVER_RDMA_H -#include -#include - extern unsigned long pcidriver_resolve_bar(unsigned long address); #endif /* _PCIDRIVER_RDMA_H */ -- cgit v1.2.3 From 01e857cca352e73243d00b62a0c248a35cea6b71 Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Wed, 2 Mar 2016 16:03:57 +0100 Subject: Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04) --- driver/base.c | 789 ++++++++++++++++------------------------ driver/base.h | 8 +- driver/common.h | 110 +++--- driver/compat.c | 23 +- driver/compat.h | 198 ++-------- driver/int.c | 226 ++++++------ driver/ioctl.c | 512 +++++++++++++------------- driver/ioctl.h | 4 - driver/kmem.c | 1017 ++++++++++++++++++++++++---------------------------- driver/pciDriver.h | 114 ++---- driver/rdma.c | 20 +- driver/sysfs.c | 293 +++++++-------- driver/umem.c | 625 +++++++++++++++----------------- 13 files changed, 1724 insertions(+), 2215 deletions(-) diff --git a/driver/base.c b/driver/base.c index dfd82c6..220f1f3 100644 --- a/driver/base.c +++ b/driver/base.c @@ -5,144 +5,9 @@ * @date 2009-04-05 * @brief Contains the main code which connects all the different parts and does * basic driver tasks like initialization. - * - * This is a full rewrite of the pciDriver. - * New default is to support kernel 2.6, using kernel 2.6 APIs. - * */ -/* - * Change History: - * - * $Log: not supported by cvs2svn $ - * Revision 1.13 2008-05-30 11:38:15 marcus - * Added patches for kernel 2.6.24 - * - * Revision 1.12 2008-01-24 14:21:36 marcus - * Added a CLEAR_INTERRUPT_QUEUE ioctl. - * Added a sysfs attribute to show the outstanding IRQ queues. - * - * Revision 1.11 2008-01-24 12:53:11 marcus - * Corrected wait_event condition in waiti_ioctl. Improved the loop too. - * - * Revision 1.10 2008-01-14 10:39:39 marcus - * Set some messages as debug instead of normal. - * - * Revision 1.9 2008-01-11 10:18:28 marcus - * Modified interrupt mechanism. Added atomic functions and queues, to address race conditions. Removed unused interrupt code. - * - * Revision 1.8 2007-07-17 13:15:55 marcus - * Removed Tasklets. - * Using newest map for the ABB interrupts. - * - * Revision 1.7 2007-07-06 15:56:04 marcus - * Change default status for OLD_REGISTERS to not defined. - * - * Revision 1.6 2007-07-05 15:29:59 marcus - * Corrected issue with the bar mapping for interrupt handling. - * Added support up to kernel 2.6.20 - * - * Revision 1.5 2007-05-29 07:50:18 marcus - * Split code into 2 files. May get merged in the future again.... - * - * Revision 1.4 2007/03/01 17:47:34 marcus - * Fixed bug when the kernel memory was less than one page, it was not locked properly, recalling an old mapping issue in this case. - * - * Revision 1.3 2007/03/01 17:01:22 marcus - * comment fix (again). - * - * Revision 1.2 2007/03/01 17:00:25 marcus - * Changed some comment in the log. - * - * Revision 1.1 2007/03/01 16:57:43 marcus - * Divided driver file to ease the interrupt hooks for the user of the driver. - * Modified Makefile accordingly. - * - * From pciDriver.c: - * Revision 1.11 2006/12/11 16:15:43 marcus - * Fixed kernel buffer mmapping, and driver crash when application crashes. - * Buffer memory is now marked reserved during allocation, and mmaped with - * remap_xx_range. - * - * Revision 1.10 2006/11/21 09:50:49 marcus - * Added PROGRAPE4 vendor/device IDs. - * - * Revision 1.9 2006/11/17 18:47:36 marcus - * Removed MERGE_SGENTRIES flag, now it is selected at runtime with 'type'. - * Removed noncached in non-prefetchable areas, to allow the use of MTRRs. - * - * Revision 1.8 2006/11/17 16:41:21 marcus - * Added slot number to the PCI info IOctl. - * - * Revision 1.7 2006/11/13 12:30:34 marcus - * Added a IOctl call, to confiure the interrupt response. (testing pending). - * Basic interrupts are now supported, using a Tasklet and Completions. - * - * Revision 1.6 2006/11/08 21:30:02 marcus - * Added changes after compile tests in kernel 2.6.16 - * - * Revision 1.5 2006/10/31 07:57:38 marcus - * Improved the pfn calculation in nopage(), to deal with some possible border - * conditions. It was really no issue, because they are normally page-aligned - * anyway, but to be on the safe side. - * - * Revision 1.4 2006/10/30 19:37:40 marcus - * Solved bug on kernel memory not mapping properly. - * - * Revision 1.3 2006/10/18 11:19:20 marcus - * Added kernel 2.6.8 support based on comments from Joern Adamczewski (GSI). - * - * Revision 1.2 2006/10/18 11:04:15 marcus - * Bus Master is only activated when we detect a specific board. - * - * Revision 1.1 2006/10/10 14:46:51 marcus - * Initial commit of the new pciDriver for kernel 2.6 - * - * Revision 1.9 2006/10/05 11:30:46 marcus - * Prerelease. Added bus and devfn to pciInfo for compatibility. - * - * Revision 1.8 2006/09/25 16:51:07 marcus - * Added PCI config IOctls, and implemented basic mmap functions. - * - * Revision 1.7 2006/09/20 11:12:41 marcus - * Added Merge SG entries - * - * Revision 1.6 2006/09/19 17:22:18 marcus - * backup commit. - * - * Revision 1.5 2006/09/18 17:13:11 marcus - * backup commit. - * - * Revision 1.4 2006/09/15 15:44:41 marcus - * backup commit. - * - * Revision 1.3 2006/08/15 11:40:02 marcus - * backup commit. - * - * Revision 1.2 2006/08/12 18:28:42 marcus - * Sync with the laptop - * - * Revision 1.1 2006/08/11 15:30:46 marcus - * Sync with the laptop - * - */ - -#include -/* Check macros and kernel version first */ -#ifndef KERNEL_VERSION -#error "No KERNEL_VERSION macro! Stopping." -#endif - -#ifndef LINUX_VERSION_CODE -#error "No LINUX_VERSION_CODE macro! Stopping." -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) -#error "This driver has been tested only for Kernel 2.6.8 or above." -#endif - -/* Required includes */ #include #include #include @@ -166,32 +31,15 @@ #include "../pcilib/version.h" -/* Configuration for the driver (what should be compiled in, module name, etc...) */ #include "config.h" - -/* Compatibility functions/definitions (provides functions which are not available on older kernels) */ #include "compat.h" - -/* External interface for the driver */ #include "pciDriver.h" - -/* Internal definitions for all parts (prototypes, data, macros) */ #include "common.h" - -/* Internal definitions for the base part */ #include "base.h" - -/* Internal definitions of the IRQ handling part */ #include "int.h" - -/* Internal definitions for kernel memory */ #include "kmem.h" - -/* Internal definitions for user space memory */ #include "umem.h" - #include "ioctl.h" - #include "build.h" /*************************************************************************/ @@ -221,54 +69,54 @@ pcidriver_privdata_t *pcidriver_dummydata = NULL; */ static int __init pcidriver_init(void) { - int err = 0; - - /* Initialize the device count */ - atomic_set(&pcidriver_deviceCount, 0); - - memset(pcidriver_privdata, 0, sizeof(pcidriver_privdata)); - - /* Allocate character device region dynamically */ - if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) { - mod_info("Couldn't allocate chrdev region. Module not loaded.\n"); - goto init_alloc_fail; - } - mod_info("Major %d allocated to nodename '%s'\n", MAJOR(pcidriver_devt), NODENAME); - - /* Register driver class */ - pcidriver_class = class_create(THIS_MODULE, NODENAME); - - if (IS_ERR(pcidriver_class)) { - mod_info("No sysfs support. Module not loaded.\n"); - goto init_class_fail; - } - - /* Register PCI driver. This function returns the number of devices on some - * systems, therefore check for errors as < 0. */ + int err = 0; + + /* Initialize the device count */ + atomic_set(&pcidriver_deviceCount, 0); + + memset(pcidriver_privdata, 0, sizeof(pcidriver_privdata)); + + /* Allocate character device region dynamically */ + if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) { + mod_info("Couldn't allocate chrdev region. Module not loaded.\n"); + goto init_alloc_fail; + } + mod_info("Major %d allocated to nodename '%s'\n", MAJOR(pcidriver_devt), NODENAME); + + /* Register driver class */ + pcidriver_class = class_create(THIS_MODULE, NODENAME); + + if (IS_ERR(pcidriver_class)) { + mod_info("No sysfs support. Module not loaded.\n"); + goto init_class_fail; + } + + /* Register PCI driver. This function returns the number of devices on some + * systems, therefore check for errors as < 0. */ #ifdef PCIDRIVER_DUMMY_DEVICE - if ((err = pcidriver_probe(NULL, NULL)) < 0) { + if ((err = pcidriver_probe(NULL, NULL)) < 0) { #else /* PCIDRIVER_DUMMY_DEVICE */ - if ((err = pci_register_driver(&pcidriver_driver)) < 0) { + if ((err = pci_register_driver(&pcidriver_driver)) < 0) { #endif /* PCIDRIVER_DUMMY_DEVICE */ - mod_info("Couldn't register PCI driver. Module not loaded.\n"); - goto init_pcireg_fail; - } + mod_info("Couldn't register PCI driver. Module not loaded.\n"); + goto init_pcireg_fail; + } - mod_info("pcidriver %u.%u.%u loaded\n", PCILIB_VERSION_GET_MAJOR(PCILIB_VERSION), PCILIB_VERSION_GET_MINOR(PCILIB_VERSION), PCILIB_VERSION_GET_MICRO(PCILIB_VERSION)); - mod_info("%s\n", PCIDRIVER_BUILD); - mod_info("%s\n", PCIDRIVER_REVISION); - if (strlen(PCIDRIVER_CHANGES)) { - mod_info("Extra changes - %s\n", PCIDRIVER_CHANGES); - } + mod_info("pcidriver %u.%u.%u loaded\n", PCILIB_VERSION_GET_MAJOR(PCILIB_VERSION), PCILIB_VERSION_GET_MINOR(PCILIB_VERSION), PCILIB_VERSION_GET_MICRO(PCILIB_VERSION)); + mod_info("%s\n", PCIDRIVER_BUILD); + mod_info("%s\n", PCIDRIVER_REVISION); + if (strlen(PCIDRIVER_CHANGES)) { + mod_info("Extra changes - %s\n", PCIDRIVER_CHANGES); + } - return 0; + return 0; init_pcireg_fail: - class_destroy(pcidriver_class); + class_destroy(pcidriver_class); init_class_fail: - unregister_chrdev_region(pcidriver_devt, MAXDEVICES); + unregister_chrdev_region(pcidriver_devt, MAXDEVICES); init_alloc_fail: - return err; + return err; } /** @@ -279,17 +127,17 @@ init_alloc_fail: static void pcidriver_exit(void) { #ifdef PCIDRIVER_DUMMY_DEVICE - pcidriver_remove(NULL); + pcidriver_remove(NULL); #else - pci_unregister_driver(&pcidriver_driver); + pci_unregister_driver(&pcidriver_driver); #endif /* PCIDRIVER_DUMMY_DEVICE */ - unregister_chrdev_region(pcidriver_devt, MAXDEVICES); + unregister_chrdev_region(pcidriver_devt, MAXDEVICES); - if (pcidriver_class != NULL) - class_destroy(pcidriver_class); + if (pcidriver_class != NULL) + class_destroy(pcidriver_class); - mod_info("Module unloaded\n"); + mod_info("Module unloaded\n"); } /*************************************************************************/ @@ -303,10 +151,10 @@ static void pcidriver_exit(void) */ #ifndef PCIDRIVER_DUMMY_DEVICE static struct pci_driver pcidriver_driver = { - .name = MODNAME, - .id_table = pcidriver_ids, - .probe = pcidriver_probe, - .remove = pcidriver_remove, + .name = MODNAME, + .id_table = pcidriver_ids, + .probe = pcidriver_probe, + .remove = pcidriver_remove, }; #endif /* ! PCIDRIVER_DUMMY_DEVICE */ @@ -318,155 +166,155 @@ static struct pci_driver pcidriver_driver = { */ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int err = 0; - int devno; - pcidriver_privdata_t *privdata; - int devid; - - /* At the moment there is no difference between these boards here, other than - * printing a different message in the log. - * - * However, there is some difference in the interrupt handling functions. - */ + int err = 0; + int devno; + pcidriver_privdata_t *privdata; + int devid; + + /* At the moment there is no difference between these boards here, other than + * printing a different message in the log. + * + * However, there is some difference in the interrupt handling functions. + */ #ifdef PCIDRIVER_DUMMY_DEVICE - mod_info("Emulated device\n"); + mod_info("Emulated device\n"); #else /* PCIDRIVER_DUMMY_DEVICE */ - if (id->vendor == PCIE_XILINX_VENDOR_ID) { - if (id->device == PCIE_ML605_DEVICE_ID) { - mod_info("Found ML605 board at %s\n", dev_name(&pdev->dev)); - } else if (id->device == PCIE_IPECAMERA_DEVICE_ID) { - mod_info("Found IPE Camera at %s\n", dev_name(&pdev->dev)); - } else if (id->device == PCIE_KAPTURE_DEVICE_ID) { - mod_info("Found KAPTURE board at %s\n", dev_name(&pdev->dev)); - } else { - mod_info("Found unknown Xilinx device (%x) at %s\n", id->device, dev_name(&pdev->dev)); - } - } else { - /* It is something else */ - mod_info("Found unknown board (%x:%x) at %s\n", id->vendor, id->device, dev_name(&pdev->dev)); - } - - /* Enable the device */ - if ((err = pci_enable_device(pdev)) != 0) { - mod_info("Couldn't enable device\n"); - goto probe_pcien_fail; - } - - /* Bus master & dma */ - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err < 0) { - printk(KERN_ERR "pci_set_dma_mask failed\n"); - goto probe_dma_fail; - } - - /* Set Memory-Write-Invalidate support */ - if ((err = pci_set_mwi(pdev)) != 0) - mod_info("MWI not supported. Continue without enabling MWI.\n"); + if (id->vendor == PCIE_XILINX_VENDOR_ID) { + if (id->device == PCIE_ML605_DEVICE_ID) { + mod_info("Found ML605 board at %s\n", dev_name(&pdev->dev)); + } else if (id->device == PCIE_IPECAMERA_DEVICE_ID) { + mod_info("Found IPE Camera at %s\n", dev_name(&pdev->dev)); + } else if (id->device == PCIE_KAPTURE_DEVICE_ID) { + mod_info("Found KAPTURE board at %s\n", dev_name(&pdev->dev)); + } else { + mod_info("Found unknown Xilinx device (%x) at %s\n", id->device, dev_name(&pdev->dev)); + } + } else { + /* It is something else */ + mod_info("Found unknown board (%x:%x) at %s\n", id->vendor, id->device, dev_name(&pdev->dev)); + } + + /* Enable the device */ + if ((err = pci_enable_device(pdev)) != 0) { + mod_info("Couldn't enable device\n"); + goto probe_pcien_fail; + } + + /* Bus master & dma */ + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err < 0) { + printk(KERN_ERR "pci_set_dma_mask failed\n"); + goto probe_dma_fail; + } + + /* Set Memory-Write-Invalidate support */ + if ((err = pci_set_mwi(pdev)) != 0) + mod_info("MWI not supported. Continue without enabling MWI.\n"); #endif /* PCIDRIVER_DUMMY_DEVICE */ - /* Get / Increment the device id */ - devid = atomic_inc_return(&pcidriver_deviceCount) - 1; - if (devid >= MAXDEVICES) { - mod_info("Maximum number of devices reached! Increase MAXDEVICES.\n"); - err = -ENOMSG; - goto probe_maxdevices_fail; - } - - /* Allocate and initialize the private data for this device */ - if ((privdata = kcalloc(1, sizeof(*privdata), GFP_KERNEL)) == NULL) { - err = -ENOMEM; - goto probe_nomem; - } - - privdata->devid = devid; - - INIT_LIST_HEAD(&(privdata->kmem_list)); - spin_lock_init(&(privdata->kmemlist_lock)); - atomic_set(&privdata->kmem_count, 0); - - INIT_LIST_HEAD(&(privdata->umem_list)); - spin_lock_init(&(privdata->umemlist_lock)); - atomic_set(&privdata->umem_count, 0); + /* Get / Increment the device id */ + devid = atomic_inc_return(&pcidriver_deviceCount) - 1; + if (devid >= MAXDEVICES) { + mod_info("Maximum number of devices reached! Increase MAXDEVICES.\n"); + err = -ENOMSG; + goto probe_maxdevices_fail; + } + + /* Allocate and initialize the private data for this device */ + if ((privdata = kcalloc(1, sizeof(*privdata), GFP_KERNEL)) == NULL) { + err = -ENOMEM; + goto probe_nomem; + } + + privdata->devid = devid; + + INIT_LIST_HEAD(&(privdata->kmem_list)); + spin_lock_init(&(privdata->kmemlist_lock)); + atomic_set(&privdata->kmem_count, 0); + + INIT_LIST_HEAD(&(privdata->umem_list)); + spin_lock_init(&(privdata->umemlist_lock)); + atomic_set(&privdata->umem_count, 0); #ifdef PCIDRIVER_DUMMY_DEVICE - pcidriver_dummydata = privdata; + pcidriver_dummydata = privdata; #else /* PCIDRIVER_DUMMY_DEVICE */ - pci_set_drvdata(pdev, privdata); - privdata->pdev = pdev; + pci_set_drvdata(pdev, privdata); + privdata->pdev = pdev; #endif /* PCIDRIVER_DUMMY_DEVICE */ - /* Device add to sysfs */ - devno = MKDEV(MAJOR(pcidriver_devt), MINOR(pcidriver_devt) + devid); - privdata->devno = devno; + /* Device add to sysfs */ + devno = MKDEV(MAJOR(pcidriver_devt), MINOR(pcidriver_devt) + devid); + privdata->devno = devno; - /* FIXME: some error checking missing here */ + /* FIXME: some error checking missing here */ #ifdef PCIDRIVER_DUMMY_DEVICE - privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, NULL, NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata); + privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, NULL, NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata); #else /* PCIDRIVER_DUMMY_DEVICE */ - privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, &(pdev->dev), NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata); + privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, &(pdev->dev), NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata); #endif /* PCIDRIVER_DUMMY_DEVICE */ - class_set_devdata( privdata->class_dev, privdata ); - mod_info("Device /dev/%s%d added\n",NODENAME,MINOR(pcidriver_devt) + devid); + class_set_devdata( privdata->class_dev, privdata ); + mod_info("Device /dev/%s%d added\n",NODENAME,MINOR(pcidriver_devt) + devid); #ifndef PCIDRIVER_DUMMY_DEVICE - /* Setup mmaped BARs into kernel space */ - if ((err = pcidriver_probe_irq(privdata)) != 0) - goto probe_irq_probe_fail; + /* Setup mmaped BARs into kernel space */ + if ((err = pcidriver_probe_irq(privdata)) != 0) + goto probe_irq_probe_fail; #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - /* Populate sysfs attributes for the class device */ - /* TODO: correct errorhandling. ewww. must remove the files in reversed order :-( */ - #define sysfs_attr(name) do { \ + /* Populate sysfs attributes for the class device */ + /* TODO: correct errorhandling. ewww. must remove the files in reversed order :-( */ +#define sysfs_attr(name) do { \ if (class_device_create_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)) != 0) \ goto probe_device_create_fail; \ } while (0) - #ifdef ENABLE_IRQ - sysfs_attr(irq_count); - sysfs_attr(irq_queues); - #endif - - sysfs_attr(mmap_mode); - sysfs_attr(mmap_area); - sysfs_attr(kmem_count); - sysfs_attr(kmem_alloc); - sysfs_attr(kmem_free); - sysfs_attr(kbuffers); - sysfs_attr(umappings); - sysfs_attr(umem_unmap); - #undef sysfs_attr - - /* Register character device */ - cdev_init( &(privdata->cdev), &pcidriver_fops ); - privdata->cdev.owner = THIS_MODULE; - privdata->cdev.ops = &pcidriver_fops; - err = cdev_add( &privdata->cdev, devno, 1 ); - if (err) { - mod_info( "Couldn't add character device.\n" ); - goto probe_cdevadd_fail; - } - - pcidriver_privdata[devid] = privdata; - - return 0; +#ifdef ENABLE_IRQ + sysfs_attr(irq_count); + sysfs_attr(irq_queues); +#endif + + sysfs_attr(mmap_mode); + sysfs_attr(mmap_area); + sysfs_attr(kmem_count); + sysfs_attr(kmem_alloc); + sysfs_attr(kmem_free); + sysfs_attr(kbuffers); + sysfs_attr(umappings); + sysfs_attr(umem_unmap); +#undef sysfs_attr + + /* Register character device */ + cdev_init( &(privdata->cdev), &pcidriver_fops ); + privdata->cdev.owner = THIS_MODULE; + privdata->cdev.ops = &pcidriver_fops; + err = cdev_add( &privdata->cdev, devno, 1 ); + if (err) { + mod_info( "Couldn't add character device.\n" ); + goto probe_cdevadd_fail; + } + + pcidriver_privdata[devid] = privdata; + + return 0; probe_device_create_fail: probe_cdevadd_fail: #ifndef PCIDRIVER_DUMMY_DEVICE probe_irq_probe_fail: - pcidriver_irq_unmap_bars(privdata); + pcidriver_irq_unmap_bars(privdata); #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - kfree(privdata); + kfree(privdata); probe_nomem: - atomic_dec(&pcidriver_deviceCount); + atomic_dec(&pcidriver_deviceCount); probe_maxdevices_fail: #ifndef PCIDRIVER_DUMMY_DEVICE probe_dma_fail: - pci_disable_device(pdev); + pci_disable_device(pdev); probe_pcien_fail: #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - return err; + return err; } /** @@ -476,62 +324,62 @@ probe_pcien_fail: */ static void __devexit pcidriver_remove(struct pci_dev *pdev) { - pcidriver_privdata_t *privdata; + pcidriver_privdata_t *privdata; #ifdef PCIDRIVER_DUMMY_DEVICE - privdata = pcidriver_dummydata; - pcidriver_dummydata = NULL; + privdata = pcidriver_dummydata; + pcidriver_dummydata = NULL; #else /* PCIDRIVER_DUMMY_DEVICE */ - /* Get private data from the device */ - privdata = pci_get_drvdata(pdev); + /* Get private data from the device */ + privdata = pci_get_drvdata(pdev); #endif /* PCIDRIVER_DUMMY_DEVICE */ - // Theoretically we should lock here and when using... - pcidriver_privdata[privdata->devid] = NULL; + // Theoretically we should lock here and when using... + pcidriver_privdata[privdata->devid] = NULL; - /* Removing sysfs attributes from class device */ - #define sysfs_attr(name) do { \ + /* Removing sysfs attributes from class device */ +#define sysfs_attr(name) do { \ class_device_remove_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)); \ } while (0) - #ifdef ENABLE_IRQ - sysfs_attr(irq_count); - sysfs_attr(irq_queues); - #endif - - sysfs_attr(mmap_mode); - sysfs_attr(mmap_area); - sysfs_attr(kmem_count); - sysfs_attr(kmem_alloc); - sysfs_attr(kmem_free); - sysfs_attr(kbuffers); - sysfs_attr(umappings); - sysfs_attr(umem_unmap); - #undef sysfs_attr - - /* Free all allocated kmem buffers before leaving */ - pcidriver_kmem_free_all( privdata ); +#ifdef ENABLE_IRQ + sysfs_attr(irq_count); + sysfs_attr(irq_queues); +#endif + + sysfs_attr(mmap_mode); + sysfs_attr(mmap_area); + sysfs_attr(kmem_count); + sysfs_attr(kmem_alloc); + sysfs_attr(kmem_free); + sysfs_attr(kbuffers); + sysfs_attr(umappings); + sysfs_attr(umem_unmap); +#undef sysfs_attr + + /* Free all allocated kmem buffers before leaving */ + pcidriver_kmem_free_all( privdata ); #ifndef PCIDRIVER_DUMMY_DEVICE # ifdef ENABLE_IRQ - pcidriver_remove_irq(privdata); + pcidriver_remove_irq(privdata); # endif #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - /* Removing Character device */ - cdev_del(&(privdata->cdev)); + /* Removing Character device */ + cdev_del(&(privdata->cdev)); - /* Removing the device from sysfs */ - class_device_destroy(pcidriver_class, privdata->devno); + /* Removing the device from sysfs */ + class_device_destroy(pcidriver_class, privdata->devno); - /* Releasing privdata */ - kfree(privdata); + /* Releasing privdata */ + kfree(privdata); #ifdef PCIDRIVER_DUMMY_DEVICE - mod_info("Device at " NODENAMEFMT " removed\n", 0); + mod_info("Device at " NODENAMEFMT " removed\n", 0); #else /* PCIDRIVER_DUMMY_DEVICE */ - /* Disabling PCI device */ - pci_disable_device(pdev); - mod_info("Device at %s removed\n", dev_name(&pdev->dev)); + /* Disabling PCI device */ + pci_disable_device(pdev); + mod_info("Device at %s removed\n", dev_name(&pdev->dev)); #endif /* PCIDRIVER_DUMMY_DEVICE */ } @@ -550,15 +398,11 @@ static void __devexit pcidriver_remove(struct pci_dev *pdev) * */ static struct file_operations pcidriver_fops = { - .owner = THIS_MODULE, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) - .ioctl = pcidriver_ioctl, -#else - .unlocked_ioctl = pcidriver_ioctl, -#endif - .mmap = pcidriver_mmap, - .open = pcidriver_open, - .release = pcidriver_release, + .owner = THIS_MODULE, + .unlocked_ioctl = pcidriver_ioctl, + .mmap = pcidriver_mmap, + .open = pcidriver_open, + .release = pcidriver_release, }; void pcidriver_module_get(pcidriver_privdata_t *privdata) { @@ -568,8 +412,8 @@ void pcidriver_module_get(pcidriver_privdata_t *privdata) { void pcidriver_module_put(pcidriver_privdata_t *privdata) { if (atomic_add_negative(-1, &(privdata->refs))) { - atomic_inc(&(privdata->refs)); - mod_info("Reference counting error..."); + atomic_inc(&(privdata->refs)); + mod_info("Reference counting error..."); } else { // mod_info("Unref: %i\n", atomic_read(&(privdata->refs))); } @@ -583,15 +427,15 @@ void pcidriver_module_put(pcidriver_privdata_t *privdata) { */ int pcidriver_open(struct inode *inode, struct file *filp) { - pcidriver_privdata_t *privdata; + pcidriver_privdata_t *privdata; - /* Set the private data area for the file */ - privdata = container_of( inode->i_cdev, pcidriver_privdata_t, cdev); - filp->private_data = privdata; + /* Set the private data area for the file */ + privdata = container_of( inode->i_cdev, pcidriver_privdata_t, cdev); + filp->private_data = privdata; - pcidriver_module_get(privdata); + pcidriver_module_get(privdata); - return 0; + return 0; } /** @@ -602,14 +446,14 @@ int pcidriver_open(struct inode *inode, struct file *filp) */ int pcidriver_release(struct inode *inode, struct file *filp) { - pcidriver_privdata_t *privdata; + pcidriver_privdata_t *privdata; - /* Get the private data area */ - privdata = filp->private_data; + /* Get the private data area */ + privdata = filp->private_data; - pcidriver_module_put(privdata); + pcidriver_module_put(privdata); - return 0; + return 0; } /** @@ -623,42 +467,54 @@ int pcidriver_release(struct inode *inode, struct file *filp) */ int pcidriver_mmap(struct file *filp, struct vm_area_struct *vma) { - pcidriver_privdata_t *privdata; - int ret = 0, bar; - - mod_info_dbg("Entering mmap\n"); - - /* Get the private data area */ - privdata = filp->private_data; - - /* Check the current mmap mode */ - switch (privdata->mmap_mode) { - case PCIDRIVER_MMAP_PCI: - /* Mmap a PCI region */ - switch (privdata->mmap_area) { - case PCIDRIVER_BAR0: bar = 0; break; - case PCIDRIVER_BAR1: bar = 1; break; - case PCIDRIVER_BAR2: bar = 2; break; - case PCIDRIVER_BAR3: bar = 3; break; - case PCIDRIVER_BAR4: bar = 4; break; - case PCIDRIVER_BAR5: bar = 5; break; - default: - mod_info("Attempted to mmap a PCI area with the wrong mmap_area value: %d\n",privdata->mmap_area); - return -EINVAL; /* invalid parameter */ - break; - } - ret = pcidriver_mmap_pci(privdata, vma, bar); - break; - case PCIDRIVER_MMAP_KMEM: - /* mmap a Kernel buffer */ - ret = pcidriver_mmap_kmem(privdata, vma); - break; - default: - mod_info( "Invalid mmap_mode value (%d)\n",privdata->mmap_mode ); - return -EINVAL; /* Invalid parameter (mode) */ - } - - return ret; + pcidriver_privdata_t *privdata; + int ret = 0, bar; + + mod_info_dbg("Entering mmap\n"); + + /* Get the private data area */ + privdata = filp->private_data; + + /* Check the current mmap mode */ + switch (privdata->mmap_mode) { + case PCIDRIVER_MMAP_PCI: + /* Mmap a PCI region */ + switch (privdata->mmap_area) { + case PCIDRIVER_BAR0: + bar = 0; + break; + case PCIDRIVER_BAR1: + bar = 1; + break; + case PCIDRIVER_BAR2: + bar = 2; + break; + case PCIDRIVER_BAR3: + bar = 3; + break; + case PCIDRIVER_BAR4: + bar = 4; + break; + case PCIDRIVER_BAR5: + bar = 5; + break; + default: + mod_info("Attempted to mmap a PCI area with the wrong mmap_area value: %d\n",privdata->mmap_area); + return -EINVAL; /* invalid parameter */ + break; + } + ret = pcidriver_mmap_pci(privdata, vma, bar); + break; + case PCIDRIVER_MMAP_KMEM: + /* mmap a Kernel buffer */ + ret = pcidriver_mmap_kmem(privdata, vma); + break; + default: + mod_info( "Invalid mmap_mode value (%d)\n",privdata->mmap_mode ); + return -EINVAL; /* Invalid parameter (mode) */ + } + + return ret; } /*************************************************************************/ @@ -666,76 +522,65 @@ int pcidriver_mmap(struct file *filp, struct vm_area_struct *vma) int pcidriver_mmap_pci(pcidriver_privdata_t *privdata, struct vm_area_struct *vmap, int bar) { #ifdef PCIDRIVER_DUMMY_DEVICE - return -ENXIO; + return -ENXIO; #else /* PCIDRIVER_DUMMY_DEVICE */ - int ret = 0; - unsigned long bar_addr; - unsigned long bar_length, vma_size; - unsigned long bar_flags; + int ret = 0; + unsigned long bar_addr; + unsigned long bar_length, vma_size; + unsigned long bar_flags; - mod_info_dbg("Entering mmap_pci\n"); + mod_info_dbg("Entering mmap_pci\n"); - /* Get info of the BAR to be mapped */ - bar_addr = pci_resource_start(privdata->pdev, bar); - bar_length = pci_resource_len(privdata->pdev, bar); - bar_flags = pci_resource_flags(privdata->pdev, bar); + /* Get info of the BAR to be mapped */ + bar_addr = pci_resource_start(privdata->pdev, bar); + bar_length = pci_resource_len(privdata->pdev, bar); + bar_flags = pci_resource_flags(privdata->pdev, bar); - /* Check sizes */ - vma_size = (vmap->vm_end - vmap->vm_start); + /* Check sizes */ + vma_size = (vmap->vm_end - vmap->vm_start); - if ((vma_size != bar_length) && - ((bar_length < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { - mod_info( "mmap size is not correct! bar: %lu - vma: %lu\n", bar_length, vma_size ); - return -EINVAL; - } + if ((vma_size != bar_length) && + ((bar_length < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { + mod_info( "mmap size is not correct! bar: %lu - vma: %lu\n", bar_length, vma_size ); + return -EINVAL; + } - if (bar_flags & IORESOURCE_IO) { - /* Unlikely case, we will mmap a IO region */ + if (bar_flags & IORESOURCE_IO) { + /* Unlikely case, we will mmap a IO region */ - /* IO regions are never cacheable */ -#ifdef pgprot_noncached - vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); -#endif + /* IO regions are never cacheable */ + vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); - /* Map the BAR */ - ret = io_remap_pfn_range_compat( - vmap, - vmap->vm_start, - bar_addr, - bar_length, - vmap->vm_page_prot); - } else { - /* Normal case, mmap a memory region */ - - /* Ensure this VMA is non-cached, if it is not flaged as prefetchable. - * If it is prefetchable, caching is allowed and will give better performance. - * This should be set properly by the BIOS, but we want to be sure. */ - /* adapted from drivers/char/mem.c, mmap function. */ -#ifdef pgprot_noncached -/* Setting noncached disables MTRR registers, and we want to use them. - * So we take this code out. This can lead to caching problems if and only if - * the System BIOS set something wrong. Check LDDv3, page 425. - */ -// if (!(bar_flags & IORESOURCE_PREFETCH)) + /* Map the BAR */ + ret = io_remap_pfn_range_compat(vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot); + } else { + /* Normal case, mmap a memory region */ + + /* Ensure this VMA is non-cached, if it is not flaged as prefetchable. + * If it is prefetchable, caching is allowed and will give better performance. + * This should be set properly by the BIOS, but we want to be sure. */ + /* adapted from drivers/char/mem.c, mmap function. */ + + /* Setting noncached disables MTRR registers, and we want to use them. + * So we take this code out. This can lead to caching problems if and only if + * the System BIOS set something wrong. Check LDDv3, page 425. + */ + +// if (!(bar_flags & IORESOURCE_PREFETCH)) // vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); -#endif - /* Map the BAR */ - ret = remap_pfn_range_compat( - vmap, - vmap->vm_start, - bar_addr, - bar_length, - vmap->vm_page_prot); - } - - if (ret) { - mod_info("remap_pfn_range failed\n"); - return -EAGAIN; - } - - return 0; /* success */ + + /* Map the BAR */ + ret = remap_pfn_range_compat(vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot); + } + + if (ret) { + mod_info("remap_pfn_range failed\n"); + return -EAGAIN; + } + + return 0; /* success */ #endif /* PCIDRIVER_DUMMY_DEVICE */ } diff --git a/driver/base.h b/driver/base.h index dce865f..8556480 100644 --- a/driver/base.h +++ b/driver/base.h @@ -47,10 +47,10 @@ static void pcidriver_exit(void); */ static const __devinitdata struct pci_device_id pcidriver_ids[] = { - { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_ML605_DEVICE_ID ) }, // PCI-E Xilinx ML605 - { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_IPECAMERA_DEVICE_ID ) }, // PCI-E IPE Camera - { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_KAPTURE_DEVICE_ID ) }, // PCI-E KAPTURE board for HEB - {0,0,0,0}, + { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_ML605_DEVICE_ID ) }, // PCI-E Xilinx ML605 + { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_IPECAMERA_DEVICE_ID ) }, // PCI-E IPE Camera + { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_KAPTURE_DEVICE_ID ) }, // PCI-E KAPTURE board for HEB + {0,0,0,0}, }; /* prototypes for internal driver functions */ diff --git a/driver/common.h b/driver/common.h index a83fd5e..48b2769 100644 --- a/driver/common.h +++ b/driver/common.h @@ -9,70 +9,70 @@ /* Define an entry in the kmem list (this list is per device) */ /* This list keeps references to the allocated kernel buffers */ typedef struct { - int id; - enum dma_data_direction direction; - - struct list_head list; - dma_addr_t dma_handle; - unsigned long cpua; - unsigned long size; - unsigned long type; - unsigned long align; - - unsigned long use; - unsigned long item; - - spinlock_t lock; - unsigned long mode; - unsigned long refs; - - struct class_device_attribute sysfs_attr; /* initialized when adding the entry */ + int id; + enum dma_data_direction direction; + + struct list_head list; + dma_addr_t dma_handle; + unsigned long cpua; + unsigned long size; + unsigned long type; + unsigned long align; + + unsigned long use; + unsigned long item; + + spinlock_t lock; + unsigned long mode; + unsigned long refs; + + struct class_device_attribute sysfs_attr; /* initialized when adding the entry */ } pcidriver_kmem_entry_t; /* Define an entry in the umem list (this list is per device) */ /* This list keeps references to the SG lists for each mapped userspace region */ typedef struct { - int id; - struct list_head list; - unsigned int nr_pages; /* number of pages for this user memeory area */ - struct page **pages; /* list of pointers to the pages */ - unsigned int nents; /* actual entries in the scatter/gatter list (NOT nents for the map function, but the result) */ - struct scatterlist *sg; /* list of sg entries */ - struct class_device_attribute sysfs_attr; /* initialized when adding the entry */ + int id; + struct list_head list; + unsigned int nr_pages; /* number of pages for this user memeory area */ + struct page **pages; /* list of pointers to the pages */ + unsigned int nents; /* actual entries in the scatter/gatter list (NOT nents for the map function, but the result) */ + struct scatterlist *sg; /* list of sg entries */ + struct class_device_attribute sysfs_attr; /* initialized when adding the entry */ } pcidriver_umem_entry_t; /* Hold the driver private data */ typedef struct { - int devid; /* the device id */ - dev_t devno; /* device number (major and minor) */ - struct pci_dev *pdev; /* PCI device */ - struct class_device *class_dev; /* Class device */ - struct cdev cdev; /* char device struct */ - int mmap_mode; /* current mmap mode */ - int mmap_area; /* current PCI mmap area */ + int devid; /* the device id */ + dev_t devno; /* device number (major and minor) */ + struct pci_dev *pdev; /* PCI device */ + struct class_device *class_dev; /* Class device */ + struct cdev cdev; /* char device struct */ + int mmap_mode; /* current mmap mode */ + int mmap_area; /* current PCI mmap area */ #ifdef ENABLE_IRQ - int irq_enabled; /* Non-zero if IRQ is enabled */ - int irq_count; /* Just an IRQ counter */ + int irq_enabled; /* Non-zero if IRQ is enabled */ + int irq_count; /* Just an IRQ counter */ - wait_queue_head_t irq_queues[ PCIDRIVER_INT_MAXSOURCES ]; /* One queue per interrupt source */ - atomic_t irq_outstanding[ PCIDRIVER_INT_MAXSOURCES ]; /* Outstanding interrupts per queue */ - volatile unsigned int *bars_kmapped[6]; /* PCI BARs mmapped in kernel space */ + wait_queue_head_t irq_queues[ PCIDRIVER_INT_MAXSOURCES ]; /* One queue per interrupt source */ + atomic_t irq_outstanding[ PCIDRIVER_INT_MAXSOURCES ]; /* Outstanding interrupts per queue */ + volatile unsigned int *bars_kmapped[6]; /* PCI BARs mmapped in kernel space */ #endif - - spinlock_t kmemlist_lock; /* Spinlock to lock kmem list operations */ - struct list_head kmem_list; /* List of 'kmem_list_entry's associated with this device */ - pcidriver_kmem_entry_t *kmem_last_sync; /* Last accessed kmem entry */ - atomic_t kmem_count; /* id for next kmem entry */ - - int kmem_cur_id; /* Currently selected kmem buffer, for mmap */ - - spinlock_t umemlist_lock; /* Spinlock to lock umem list operations */ - struct list_head umem_list; /* List of 'umem_list_entry's associated with this device */ - atomic_t umem_count; /* id for next umem entry */ - - int msi_mode; /* Flag specifying if interrupt have been initialized in MSI mode */ - atomic_t refs; /* Reference counter */ + + spinlock_t kmemlist_lock; /* Spinlock to lock kmem list operations */ + struct list_head kmem_list; /* List of 'kmem_list_entry's associated with this device */ + pcidriver_kmem_entry_t *kmem_last_sync; /* Last accessed kmem entry */ + atomic_t kmem_count; /* id for next kmem entry */ + + int kmem_cur_id; /* Currently selected kmem buffer, for mmap */ + + spinlock_t umemlist_lock; /* Spinlock to lock umem list operations */ + struct list_head umem_list; /* List of 'umem_list_entry's associated with this device */ + atomic_t umem_count; /* id for next umem entry */ + + int msi_mode; /* Flag specifying if interrupt have been initialized in MSI mode */ + atomic_t refs; /* Reference counter */ } pcidriver_privdata_t; @@ -88,17 +88,17 @@ void pcidriver_put_privdata(pcidriver_privdata_t *privdata); /* This is to print nice info in the log */ #ifdef DEBUG - #define mod_info( args... ) \ +#define mod_info( args... ) \ do { printk( KERN_INFO "%s - %s : ", MODNAME , __FUNCTION__ );\ printk( args ); } while(0) - #define mod_info_dbg( args... ) \ +#define mod_info_dbg( args... ) \ do { printk( KERN_INFO "%s - %s : ", MODNAME , __FUNCTION__ );\ printk( args ); } while(0) #else - #define mod_info( args... ) \ +#define mod_info( args... ) \ do { printk( KERN_INFO "%s: ", MODNAME );\ printk( args ); } while(0) - #define mod_info_dbg( args... ) +#define mod_info_dbg( args... ) #endif #define mod_crit( args... ) \ diff --git a/driver/compat.c b/driver/compat.c index a632781..f28f527 100644 --- a/driver/compat.c +++ b/driver/compat.c @@ -2,25 +2,24 @@ int pcidriver_pcie_get_mps(struct pci_dev *dev) { - u16 ctl; + u16 ctl; - pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl); + pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl); - return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); + return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); } int pcidriver_pcie_set_mps(struct pci_dev *dev, int mps) { - u16 v; + u16 v; - if (mps < 128 || mps > 4096 || !is_power_of_2(mps)) - return -EINVAL; + if (mps < 128 || mps > 4096 || !is_power_of_2(mps)) + return -EINVAL; - v = ffs(mps) - 8; - if (v > dev->pcie_mpss) - return -EINVAL; - v <<= 5; + v = ffs(mps) - 8; + if (v > dev->pcie_mpss) + return -EINVAL; + v <<= 5; - return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_PAYLOAD, v); + return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, v); } diff --git a/driver/compat.h b/driver/compat.h index 5232a22..a8a2cf0 100644 --- a/driver/compat.h +++ b/driver/compat.h @@ -10,196 +10,72 @@ #ifndef _COMPAT_H #define _COMPAT_H -/* -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) -# error "Linux 3.0 and latter are supported" -#endif -*/ +#include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) -# define __devinit -# define __devexit -# define __devinitdata +/* Check macros and kernel version first */ +#ifndef KERNEL_VERSION +# error "No KERNEL_VERSION macro! Stopping." #endif -/* dev_name is the wrapper one needs to use to access what was formerly called - * bus_id in struct device. However, before 2.6.27, direct access was necessary, - * so we provide our own version. */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) -static inline const char *dev_name(struct device *dev) { - return dev->bus_id; -} +#ifndef LINUX_VERSION_CODE +# error "No LINUX_VERSION_CODE macro! Stopping." #endif -/* SetPageLocked disappeared in v2.6.27 */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) - #define compat_lock_page SetPageLocked - #define compat_unlock_page ClearPageLocked -#else - /* in v2.6.28, __set_page_locked and __clear_page_locked was introduced */ - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) - #define compat_lock_page __set_page_locked - #define compat_unlock_page __clear_page_locked - #else - /* However, in v2.6.27 itself, neither of them is there, so - * we need to use our own function fiddling with bits inside - * the page struct :-\ */ - static inline void compat_lock_page(struct page *page) { - __set_bit(PG_locked, &page->flags); - } - - static inline void compat_unlock_page(struct page *page) { - __clear_bit(PG_locked, &page->flags); - } - #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) +# error "Linux 3.2 and latter are supported" #endif -/* Before 2.6.13, simple_class was the standard interface. Nowadays, it's just called class */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) - - #define class_compat class_simple - - /* These functions are redirected to their old corresponding functions */ - #define class_create(module, name) class_simple_create(module, name) - #define class_destroy(type) class_simple_destroy(type) - #define class_device_destroy(unused, devno) class_simple_device_remove(devno) - #define class_device_create(type, unused, devno, devpointer, nameformat, minor, unused) \ - class_simple_device_add(type, devno, devpointer, nameformat, minor) - #define class_set_devdata(classdev, privdata) classdev->class_data = privdata - #define DEVICE_ATTR_COMPAT - #define sysfs_attr_def_name(name) class_device_attr_##name - #define sysfs_attr_def_pointer privdata->class_dev - #define SYSFS_GET_FUNCTION(name) ssize_t name(struct class_device *cls, char *buf) - #define SYSFS_SET_FUNCTION(name) ssize_t name(struct class_device *cls, const char *buf, size_t count) - #define SYSFS_GET_PRIVDATA (pcidriver_privdata_t*)cls->class_data - -#else - -/* In 2.6.26, device.h was changed quite significantly. Luckily, it only affected - type/function names, for the most part. */ -//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) - #define class_device_attribute device_attribute - #define CLASS_DEVICE_ATTR DEVICE_ATTR - #define class_device device - #define class_data dev -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) - #define class_device_create(type, parent, devno, devpointer, nameformat, minor, privdata) \ - device_create(type, parent, devno, privdata, nameformat, minor) -#else - #define class_device_create(type, parent, devno, devpointer, nameformat, minor, unused) \ - device_create(type, parent, devno, nameformat, minor) +/* VM_RESERVED is removed in 3.7-rc1 */ +#ifndef VM_RESERVED +# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) #endif - #define class_device_create_file device_create_file - #define class_device_remove_file device_remove_file - #define class_device_destroy device_destroy - #define DEVICE_ATTR_COMPAT struct device_attribute *attr, - #define class_set_devdata dev_set_drvdata - - #define sysfs_attr_def_name(name) dev_attr_##name - #define sysfs_attr_def_pointer privdata->class_dev - #define SYSFS_GET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, char *buf) - #define SYSFS_SET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) - #define SYSFS_GET_PRIVDATA dev_get_drvdata(dev) - -//#endif -#define class_compat class +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) +# define __devinit +# define __devexit +# define __devinitdata #endif -/* The arguments of IRQ handlers have been changed in 2.6.19. It's very likely that - int irq will disappear somewhen in the future (current is 2.6.29), too. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) - #define IRQ_HANDLER_FUNC(name) irqreturn_t name(int irq, void *dev_id) -#else - #define IRQ_HANDLER_FUNC(name) irqreturn_t name(int irq, void *dev_id, struct pt_regs *regs) -#endif +#define compat_lock_page __set_page_locked +#define compat_unlock_page __clear_page_locked -/* atomic_inc_return appeared in 2.6.9, at least in CERN scientific linux, provide - compatibility wrapper for older kernels */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) -static int atomic_inc_return(atomic_t *variable) { - atomic_inc(variable); - return atomic_read(variable); -} -#endif -/* sg_set_page is available starting at 2.6.24 */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +#define class_device_attribute device_attribute +#define CLASS_DEVICE_ATTR DEVICE_ATTR +#define class_device device +#define class_data dev +#define class_device_create(type, parent, devno, devpointer, nameformat, minor, privdata) \ + device_create(type, parent, devno, privdata, nameformat, minor) +#define class_device_create_file device_create_file +#define class_device_remove_file device_remove_file +#define class_device_destroy device_destroy +#define DEVICE_ATTR_COMPAT struct device_attribute *attr, +#define class_set_devdata dev_set_drvdata + +#define sysfs_attr_def_name(name) dev_attr_##name +#define sysfs_attr_def_pointer privdata->class_dev +#define SYSFS_GET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, char *buf) +#define SYSFS_SET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +#define SYSFS_GET_PRIVDATA dev_get_drvdata(dev) -#define sg_set_page(sg, set_page, set_length, set_offset) do { \ - (sg)->page = set_page; \ - (sg)->length = set_length; \ - (sg)->offset = set_offset; \ -} while (0) +#define class_compat class -#endif -/* Before 2.6.20, disable was not an atomic counter, so this check was needed */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -#define pci_disable_device(pdev) do { \ - if (pdev->is_enabled) \ - pci_disable_device(pdev); \ -} while (0) -#endif +#define IRQ_HANDLER_FUNC(name) irqreturn_t name(int irq, void *dev_id) -/* Before 2.6.24, scatter/gather lists did not need to be initialized */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) - #define sg_init_table(sg, nr_pages) -#endif +#define request_irq(irq, irq_handler, modname, privdata) request_irq(irq, irq_handler, IRQF_SHARED, modname, privdata) -/* SA_SHIRQ was renamed to IRQF_SHARED in 2.6.24 */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) - #define request_irq(irq, irq_handler, modname, privdata) request_irq(irq, irq_handler, IRQF_SHARED, modname, privdata) -#else - #define request_irq(irq, irq_handler, modname, privdata) request_irq(irq, irq_handler, SA_SHIRQ, modname, privdata) -#endif -/* In 2.6.13, io_remap_page_range was removed in favor for io_remap_pfn_range which works on - more platforms and allows more memory space */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) #define io_remap_pfn_range_compat(vmap, vm_start, bar_addr, bar_length, vm_page_prot) \ io_remap_pfn_range(vmap, vm_start, (bar_addr >> PAGE_SHIFT), bar_length, vm_page_prot) -#else -#define io_remap_pfn_range_compat(vmap, vm_start, bar_addr, bar_length, vm_page_prot) \ - io_remap_page_range(vmap, vm_start, bar_addr, bar_length, vm_page_prot) -#endif -/* In 2.6.10, remap_pfn_range was introduced, see io_remap_pfn_range_compat */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) #define remap_pfn_range_compat(vmap, vm_start, bar_addr, bar_length, vm_page_prot) \ remap_pfn_range(vmap, vm_start, (bar_addr >> PAGE_SHIFT), bar_length, vm_page_prot) #define remap_pfn_range_cpua_compat(vmap, vm_start, cpua, size, vm_page_prot) \ remap_pfn_range(vmap, vm_start, page_to_pfn(virt_to_page((void*)cpua)), size, vm_page_prot) -#else -#define remap_pfn_range_compat(vmap, vm_start, bar_addr, bar_length, vm_page_prot) \ - remap_page_range(vmap, vm_start, bar_addr, bar_length, vm_page_prot) - -#define remap_pfn_range_cpua_compat(vmap, vm_start, cpua, size, vm_page_prot) \ - remap_page_range(vmap, vm_start, virt_to_phys((void*)cpua), size, vm_page_prot) -#endif - -/** - * Go over the pages of the kmem buffer, and mark them as reserved. - * This is needed, otherwise mmaping the kernel memory to user space - * will fail silently (mmaping /dev/null) when using remap_xx_range. - */ -static inline void set_pages_reserved_compat(unsigned long cpua, unsigned long size) -{ - /* Starting in 2.6.15, the PG_RESERVED bit was removed. - See also http://lwn.net/Articles/161204/ */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) - struct page *page, *last_page; - - page = virt_to_page(cpua); - last_page = virt_to_page(cpua + size - 1); - - for (; page <= last_page; page++) - SetPageReserved(page); -#endif -} int pcidriver_pcie_get_mps(struct pci_dev *dev); int pcidriver_pcie_set_mps(struct pci_dev *dev, int mps); diff --git a/driver/int.c b/driver/int.c index 5dbcb7f..f78c6ec 100644 --- a/driver/int.c +++ b/driver/int.c @@ -9,7 +9,7 @@ /* * Change History: - * + * * $Log: not supported by cvs2svn $ * Revision 1.7 2008-01-11 10:18:28 marcus * Modified interrupt mechanism. Added atomic functions and queues, to address race conditions. Removed unused interrupt code. @@ -60,7 +60,7 @@ /* * The ID between IRQ_SOURCE in irq_outstanding and the actual source is arbitrary. - * Therefore, be careful when communicating with multiple implementations. + * Therefore, be careful when communicating with multiple implementations. */ /* IRQ_SOURCES */ @@ -92,90 +92,90 @@ */ int pcidriver_probe_irq(pcidriver_privdata_t *privdata) { - unsigned char int_pin, int_line; - unsigned long bar_addr, bar_len, bar_flags; - int i; - int err; - - for (i = 0; i < 6; i++) - privdata->bars_kmapped[i] = NULL; - - for (i = 0; i < 6; i++) { - bar_addr = pci_resource_start(privdata->pdev, i); - bar_len = pci_resource_len(privdata->pdev, i); - bar_flags = pci_resource_flags(privdata->pdev, i); - - /* check if it is a valid BAR, skip if not */ - if ((bar_addr == 0) || (bar_len == 0)) - continue; - - /* Skip IO regions (map only mem regions) */ - if (bar_flags & IORESOURCE_IO) - continue; - - /* Check if the region is available */ - if ((err = pci_request_region(privdata->pdev, i, NULL)) != 0) { - mod_info( "Failed to request BAR memory region.\n" ); - return err; - } - - /* Map it into kernel space. */ - /* For x86 this is just a dereference of the pointer, but that is - * not portable. So we need to do the portable way. Thanks Joern! - */ - - /* respect the cacheable-bility of the region */ - if (bar_flags & IORESOURCE_PREFETCH) - privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len); - else - privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len); - - /* check for error */ - if (privdata->bars_kmapped[i] == NULL) { - mod_info( "Failed to remap BAR%d into kernel space.\n", i ); - return -EIO; - } - } - - /* Initialize the interrupt handler for this device */ - /* Initialize the wait queues */ - for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) { - init_waitqueue_head(&(privdata->irq_queues[i])); - atomic_set(&(privdata->irq_outstanding[i]), 0); - } - - /* Initialize the irq config */ - if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) { - /* continue without interrupts */ - int_pin = 0; - mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n"); - } - - /* Disable interrupts and activate them if everything can be set up properly */ - privdata->irq_enabled = 0; - - if (int_pin == 0) - return 0; - - if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &int_line)) != 0) { - mod_info("Error getting the interrupt line. Disabling interrupts for this device\n"); - return 0; - } - - /* Enable interrupts using MSI mode */ - if (!pci_enable_msi(privdata->pdev)) - privdata->msi_mode = 1; - - /* register interrupt handler */ - if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, MODNAME, privdata)) != 0) { - mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n"); - return 0; - } - - privdata->irq_enabled = 1; - mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq ); - - return 0; + unsigned char int_pin, int_line; + unsigned long bar_addr, bar_len, bar_flags; + int i; + int err; + + for (i = 0; i < 6; i++) + privdata->bars_kmapped[i] = NULL; + + for (i = 0; i < 6; i++) { + bar_addr = pci_resource_start(privdata->pdev, i); + bar_len = pci_resource_len(privdata->pdev, i); + bar_flags = pci_resource_flags(privdata->pdev, i); + + /* check if it is a valid BAR, skip if not */ + if ((bar_addr == 0) || (bar_len == 0)) + continue; + + /* Skip IO regions (map only mem regions) */ + if (bar_flags & IORESOURCE_IO) + continue; + + /* Check if the region is available */ + if ((err = pci_request_region(privdata->pdev, i, NULL)) != 0) { + mod_info( "Failed to request BAR memory region.\n" ); + return err; + } + + /* Map it into kernel space. */ + /* For x86 this is just a dereference of the pointer, but that is + * not portable. So we need to do the portable way. Thanks Joern! + */ + + /* respect the cacheable-bility of the region */ + if (bar_flags & IORESOURCE_PREFETCH) + privdata->bars_kmapped[i] = ioremap(bar_addr, bar_len); + else + privdata->bars_kmapped[i] = ioremap_nocache(bar_addr, bar_len); + + /* check for error */ + if (privdata->bars_kmapped[i] == NULL) { + mod_info( "Failed to remap BAR%d into kernel space.\n", i ); + return -EIO; + } + } + + /* Initialize the interrupt handler for this device */ + /* Initialize the wait queues */ + for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) { + init_waitqueue_head(&(privdata->irq_queues[i])); + atomic_set(&(privdata->irq_outstanding[i]), 0); + } + + /* Initialize the irq config */ + if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &int_pin)) != 0) { + /* continue without interrupts */ + int_pin = 0; + mod_info("Error getting the interrupt pin. Disabling interrupts for this device\n"); + } + + /* Disable interrupts and activate them if everything can be set up properly */ + privdata->irq_enabled = 0; + + if (int_pin == 0) + return 0; + + if ((err = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &int_line)) != 0) { + mod_info("Error getting the interrupt line. Disabling interrupts for this device\n"); + return 0; + } + + /* Enable interrupts using MSI mode */ + if (!pci_enable_msi(privdata->pdev)) + privdata->msi_mode = 1; + + /* register interrupt handler */ + if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, MODNAME, privdata)) != 0) { + mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n"); + return 0; + } + + privdata->irq_enabled = 1; + mod_info("Registered Interrupt Handler at pin %i, line %i, IRQ %i\n", int_pin, int_line, privdata->pdev->irq ); + + return 0; } /** @@ -185,16 +185,16 @@ int pcidriver_probe_irq(pcidriver_privdata_t *privdata) */ void pcidriver_remove_irq(pcidriver_privdata_t *privdata) { - /* Release the IRQ handler */ - if (privdata->irq_enabled != 0) - free_irq(privdata->pdev->irq, privdata); - - if (privdata->msi_mode) { - pci_disable_msi(privdata->pdev); - privdata->msi_mode = 0; - } - - pcidriver_irq_unmap_bars(privdata); + /* Release the IRQ handler */ + if (privdata->irq_enabled != 0) + free_irq(privdata->pdev->irq, privdata); + + if (privdata->msi_mode) { + pci_disable_msi(privdata->pdev); + privdata->msi_mode = 0; + } + + pcidriver_irq_unmap_bars(privdata); } /** @@ -204,15 +204,15 @@ void pcidriver_remove_irq(pcidriver_privdata_t *privdata) */ void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata) { - int i; + int i; - for (i = 0; i < 6; i++) { - if (privdata->bars_kmapped[i] == NULL) - continue; + for (i = 0; i < 6; i++) { + if (privdata->bars_kmapped[i] == NULL) + continue; - iounmap((void*)privdata->bars_kmapped[i]); - pci_release_region(privdata->pdev, i); - } + iounmap((void*)privdata->bars_kmapped[i]); + pci_release_region(privdata->pdev, i); + } } /** @@ -227,15 +227,15 @@ void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata) */ static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata) { - int channel = 0; + int channel = 0; // volatile unsigned int *bar; // bar = privdata->bars_kmapped[0]; // mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]); - atomic_inc(&(privdata->irq_outstanding[channel])); - wake_up_interruptible(&(privdata->irq_queues[channel])); - - return true; + atomic_inc(&(privdata->irq_outstanding[channel])); + wake_up_interruptible(&(privdata->irq_queues[channel])); + + return true; } /** @@ -248,11 +248,11 @@ static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata) */ IRQ_HANDLER_FUNC(pcidriver_irq_handler) { - pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id; + pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id; - if (!pcidriver_irq_acknowledge(privdata)) - return IRQ_NONE; + if (!pcidriver_irq_acknowledge(privdata)) + return IRQ_NONE; - privdata->irq_count++; - return IRQ_HANDLED; + privdata->irq_count++; + return IRQ_HANDLED; } diff --git a/driver/ioctl.c b/driver/ioctl.c index f957561..4fef5f9 100644 --- a/driver/ioctl.c +++ b/driver/ioctl.c @@ -60,13 +60,13 @@ */ static int ioctl_mmap_mode(pcidriver_privdata_t *privdata, unsigned long arg) { - if ((arg != PCIDRIVER_MMAP_PCI) && (arg != PCIDRIVER_MMAP_KMEM)) - return -EINVAL; + if ((arg != PCIDRIVER_MMAP_PCI) && (arg != PCIDRIVER_MMAP_KMEM)) + return -EINVAL; - /* change the mode */ - privdata->mmap_mode = arg; + /* change the mode */ + privdata->mmap_mode = arg; - return 0; + return 0; } /** @@ -76,14 +76,14 @@ static int ioctl_mmap_mode(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_mmap_area(pcidriver_privdata_t *privdata, unsigned long arg) { - /* validate input */ - if ((arg < PCIDRIVER_BAR0) || (arg > PCIDRIVER_BAR5)) - return -EINVAL; + /* validate input */ + if ((arg < PCIDRIVER_BAR0) || (arg > PCIDRIVER_BAR5)) + return -EINVAL; - /* change the PCI area to mmap */ - privdata->mmap_area = arg; + /* change the PCI area to mmap */ + privdata->mmap_area = arg; - return 0; + return 0; } /** @@ -97,45 +97,45 @@ static int ioctl_mmap_area(pcidriver_privdata_t *privdata, unsigned long arg) static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned int cmd, unsigned long arg) { #ifdef PCIDRIVER_DUMMY_DEVICE - return -ENXIO; + return -ENXIO; #else /* PCIDRIVER_DUMMY_DEVICE */ - int ret; - READ_FROM_USER(pci_cfg_cmd, pci_cmd); - - if (cmd == PCIDRIVER_IOC_PCI_CFG_RD) { - switch (pci_cmd.size) { - case PCIDRIVER_PCI_CFG_SZ_BYTE: - ret = pci_read_config_byte( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.byte) ); - break; - case PCIDRIVER_PCI_CFG_SZ_WORD: - ret = pci_read_config_word( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.word) ); - break; - case PCIDRIVER_PCI_CFG_SZ_DWORD: - ret = pci_read_config_dword( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.dword) ); - break; - default: - return -EINVAL; /* Wrong size setting */ - } - } else { - switch (pci_cmd.size) { - case PCIDRIVER_PCI_CFG_SZ_BYTE: - ret = pci_write_config_byte( privdata->pdev, pci_cmd.addr, pci_cmd.val.byte ); - break; - case PCIDRIVER_PCI_CFG_SZ_WORD: - ret = pci_write_config_word( privdata->pdev, pci_cmd.addr, pci_cmd.val.word ); - break; - case PCIDRIVER_PCI_CFG_SZ_DWORD: - ret = pci_write_config_dword( privdata->pdev, pci_cmd.addr, pci_cmd.val.dword ); - break; - default: - return -EINVAL; /* Wrong size setting */ - break; - } - } - - WRITE_TO_USER(pci_cfg_cmd, pci_cmd); - - return 0; + int ret; + READ_FROM_USER(pci_cfg_cmd, pci_cmd); + + if (cmd == PCIDRIVER_IOC_PCI_CFG_RD) { + switch (pci_cmd.size) { + case PCIDRIVER_PCI_CFG_SZ_BYTE: + ret = pci_read_config_byte( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.byte) ); + break; + case PCIDRIVER_PCI_CFG_SZ_WORD: + ret = pci_read_config_word( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.word) ); + break; + case PCIDRIVER_PCI_CFG_SZ_DWORD: + ret = pci_read_config_dword( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.dword) ); + break; + default: + return -EINVAL; /* Wrong size setting */ + } + } else { + switch (pci_cmd.size) { + case PCIDRIVER_PCI_CFG_SZ_BYTE: + ret = pci_write_config_byte( privdata->pdev, pci_cmd.addr, pci_cmd.val.byte ); + break; + case PCIDRIVER_PCI_CFG_SZ_WORD: + ret = pci_write_config_word( privdata->pdev, pci_cmd.addr, pci_cmd.val.word ); + break; + case PCIDRIVER_PCI_CFG_SZ_DWORD: + ret = pci_write_config_dword( privdata->pdev, pci_cmd.addr, pci_cmd.val.dword ); + break; + default: + return -EINVAL; /* Wrong size setting */ + break; + } + } + + WRITE_TO_USER(pci_cfg_cmd, pci_cmd); + + return 0; #endif /* PCIDRIVER_DUMMY_DEVICE */ } @@ -148,40 +148,40 @@ static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned */ static int ioctl_pci_info(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; + int ret; #ifdef PCIDRIVER_DUMMY_DEVICE - READ_FROM_USER(pcilib_board_info_t, pci_info); - memset(&pci_info, 0, sizeof(pci_info)); - WRITE_TO_USER(pcilib_board_info_t, pci_info); + READ_FROM_USER(pcilib_board_info_t, pci_info); + memset(&pci_info, 0, sizeof(pci_info)); + WRITE_TO_USER(pcilib_board_info_t, pci_info); #else /* PCIDRIVER_DUMMY_DEVICE */ - int bar; + int bar; - READ_FROM_USER(pcilib_board_info_t, pci_info); + READ_FROM_USER(pcilib_board_info_t, pci_info); - pci_info.vendor_id = privdata->pdev->vendor; - pci_info.device_id = privdata->pdev->device; - pci_info.bus = privdata->pdev->bus->number; - pci_info.slot = PCI_SLOT(privdata->pdev->devfn); - pci_info.devfn = privdata->pdev->devfn; - pci_info.func = PCI_FUNC(privdata->pdev->devfn); + pci_info.vendor_id = privdata->pdev->vendor; + pci_info.device_id = privdata->pdev->device; + pci_info.bus = privdata->pdev->bus->number; + pci_info.slot = PCI_SLOT(privdata->pdev->devfn); + pci_info.devfn = privdata->pdev->devfn; + pci_info.func = PCI_FUNC(privdata->pdev->devfn); - if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &(pci_info.interrupt_pin))) != 0) - return ret; + if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &(pci_info.interrupt_pin))) != 0) + return ret; - if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &(pci_info.interrupt_line))) != 0) - return ret; + if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &(pci_info.interrupt_line))) != 0) + return ret; - for (bar = 0; bar < 6; bar++) { - pci_info.bar_start[bar] = pci_resource_start(privdata->pdev, bar); - pci_info.bar_length[bar] = pci_resource_len(privdata->pdev, bar); - pci_info.bar_flags[bar] = pci_resource_flags(privdata->pdev, bar); - } + for (bar = 0; bar < 6; bar++) { + pci_info.bar_start[bar] = pci_resource_start(privdata->pdev, bar); + pci_info.bar_length[bar] = pci_resource_len(privdata->pdev, bar); + pci_info.bar_flags[bar] = pci_resource_flags(privdata->pdev, bar); + } - WRITE_TO_USER(pcilib_board_info_t, pci_info); + WRITE_TO_USER(pcilib_board_info_t, pci_info); #endif /* PCIDRIVER_DUMMY_DEVICE */ - return 0; + return 0; } /** @@ -193,13 +193,13 @@ static int ioctl_pci_info(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_kmem_alloc(pcidriver_privdata_t *privdata, unsigned long arg) { - int err, ret; + int err, ret; - READ_FROM_USER(kmem_handle_t, khandle); - err = pcidriver_kmem_alloc(privdata, &khandle); - WRITE_TO_USER(kmem_handle_t, khandle); + READ_FROM_USER(kmem_handle_t, khandle); + err = pcidriver_kmem_alloc(privdata, &khandle); + WRITE_TO_USER(kmem_handle_t, khandle); - return err; + return err; } /** @@ -211,13 +211,13 @@ static int ioctl_kmem_alloc(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_kmem_free(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - READ_FROM_USER(kmem_handle_t, khandle); + int ret; + READ_FROM_USER(kmem_handle_t, khandle); - if ((ret = pcidriver_kmem_free(privdata, &khandle)) != 0) - return ret; + if ((ret = pcidriver_kmem_free(privdata, &khandle)) != 0) + return ret; - return 0; + return 0; } /** @@ -229,15 +229,15 @@ static int ioctl_kmem_free(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_kmem_sync(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - READ_FROM_USER(kmem_sync_t, ksync); - - if ((ret = pcidriver_kmem_sync(privdata, &ksync)) != 0) - return ret; - - WRITE_TO_USER(kmem_sync_t, ksync); - - return 0; + int ret; + READ_FROM_USER(kmem_sync_t, ksync); + + if ((ret = pcidriver_kmem_sync(privdata, &ksync)) != 0) + return ret; + + WRITE_TO_USER(kmem_sync_t, ksync); + + return 0; } /* @@ -249,15 +249,15 @@ static int ioctl_kmem_sync(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_umem_sgmap(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - READ_FROM_USER(umem_handle_t, uhandle); + int ret; + READ_FROM_USER(umem_handle_t, uhandle); - if ((ret = pcidriver_umem_sgmap(privdata, &uhandle)) != 0) - return ret; + if ((ret = pcidriver_umem_sgmap(privdata, &uhandle)) != 0) + return ret; - WRITE_TO_USER(umem_handle_t, uhandle); + WRITE_TO_USER(umem_handle_t, uhandle); - return 0; + return 0; } /** @@ -269,19 +269,19 @@ static int ioctl_umem_sgmap(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_umem_sgunmap(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - pcidriver_umem_entry_t *umem_entry; - READ_FROM_USER(umem_handle_t, uhandle); + int ret; + pcidriver_umem_entry_t *umem_entry; + READ_FROM_USER(umem_handle_t, uhandle); - /* Find the associated umem_entry for this buffer, - * return -EINVAL if the specified handle id is invalid */ - if ((umem_entry = pcidriver_umem_find_entry_id(privdata, uhandle.handle_id)) == NULL) - return -EINVAL; + /* Find the associated umem_entry for this buffer, + * return -EINVAL if the specified handle id is invalid */ + if ((umem_entry = pcidriver_umem_find_entry_id(privdata, uhandle.handle_id)) == NULL) + return -EINVAL; - if ((ret = pcidriver_umem_sgunmap(privdata, umem_entry)) != 0) - return ret; + if ((ret = pcidriver_umem_sgunmap(privdata, umem_entry)) != 0) + return ret; - return 0; + return 0; } /** @@ -293,35 +293,35 @@ static int ioctl_umem_sgunmap(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_umem_sgget(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - READ_FROM_USER(umem_sglist_t, usglist); + int ret; + READ_FROM_USER(umem_sglist_t, usglist); - /* The umem_sglist_t has a pointer to the scatter/gather list itself which - * needs to be copied separately. The number of elements is stored in ->nents. - * As the list can get very big, we need to use vmalloc. */ - if ((usglist.sg = vmalloc(usglist.nents * sizeof(umem_sgentry_t))) == NULL) - return -ENOMEM; + /* The umem_sglist_t has a pointer to the scatter/gather list itself which + * needs to be copied separately. The number of elements is stored in ->nents. + * As the list can get very big, we need to use vmalloc. */ + if ((usglist.sg = vmalloc(usglist.nents * sizeof(umem_sgentry_t))) == NULL) + return -ENOMEM; - /* copy array to kernel structure */ - ret = copy_from_user(usglist.sg, ((umem_sglist_t *)arg)->sg, (usglist.nents)*sizeof(umem_sgentry_t)); - if (ret) return -EFAULT; + /* copy array to kernel structure */ + ret = copy_from_user(usglist.sg, ((umem_sglist_t *)arg)->sg, (usglist.nents)*sizeof(umem_sgentry_t)); + if (ret) return -EFAULT; - if ((ret = pcidriver_umem_sgget(privdata, &usglist)) != 0) - return ret; + if ((ret = pcidriver_umem_sgget(privdata, &usglist)) != 0) + return ret; - /* write data to user space */ - ret = copy_to_user(((umem_sglist_t *)arg)->sg, usglist.sg, (usglist.nents)*sizeof(umem_sgentry_t)); - if (ret) return -EFAULT; + /* write data to user space */ + ret = copy_to_user(((umem_sglist_t *)arg)->sg, usglist.sg, (usglist.nents)*sizeof(umem_sgentry_t)); + if (ret) return -EFAULT; - /* free array memory */ - vfree(usglist.sg); + /* free array memory */ + vfree(usglist.sg); - /* restore sg pointer to vma address in user space before copying */ - usglist.sg = ((umem_sglist_t *)arg)->sg; + /* restore sg pointer to vma address in user space before copying */ + usglist.sg = ((umem_sglist_t *)arg)->sg; - WRITE_TO_USER(umem_sglist_t, usglist); + WRITE_TO_USER(umem_sglist_t, usglist); - return 0; + return 0; } /** @@ -333,10 +333,10 @@ static int ioctl_umem_sgget(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_umem_sync(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - READ_FROM_USER(umem_handle_t, uhandle); + int ret; + READ_FROM_USER(umem_handle_t, uhandle); - return pcidriver_umem_sync( privdata, &uhandle ); + return pcidriver_umem_sync( privdata, &uhandle ); } /** @@ -349,47 +349,47 @@ static int ioctl_umem_sync(pcidriver_privdata_t *privdata, unsigned long arg) static int ioctl_wait_interrupt(pcidriver_privdata_t *privdata, unsigned long arg) { #ifdef ENABLE_IRQ - int ret; - unsigned long timeout; - unsigned int irq_source; - unsigned long temp = 0; + int ret; + unsigned long timeout; + unsigned int irq_source; + unsigned long temp = 0; - READ_FROM_USER(interrupt_wait_t, irq_handle); + READ_FROM_USER(interrupt_wait_t, irq_handle); - irq_source = irq_handle.source; + irq_source = irq_handle.source; - if (irq_source >= PCIDRIVER_INT_MAXSOURCES) - return -EFAULT; /* User tried to overrun the IRQ_SOURCES array */ + if (irq_source >= PCIDRIVER_INT_MAXSOURCES) + return -EFAULT; /* User tried to overrun the IRQ_SOURCES array */ - timeout = jiffies + (irq_handle.timeout * HZ / 1000000); + timeout = jiffies + (irq_handle.timeout * HZ / 1000000); - /* Thanks to Joern for the correction and tips! */ - /* done this way to avoid wrong behaviour (endless loop) of the compiler in AMD platforms */ - do { - /* We wait here with an interruptible timeout. This will be interrupted + /* Thanks to Joern for the correction and tips! */ + /* done this way to avoid wrong behaviour (endless loop) of the compiler in AMD platforms */ + do { + /* We wait here with an interruptible timeout. This will be interrupted * by int.c:check_acknowledge_channel() as soon as in interrupt for * the specified source arrives. */ - wait_event_interruptible_timeout( (privdata->irq_queues[irq_source]), (atomic_read(&(privdata->irq_outstanding[irq_source])) > 0), (10*HZ/1000) ); - - if (atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source])) ) - atomic_inc( &(privdata->irq_outstanding[irq_source]) ); - else - temp = 1; - } while ((!temp)&&(jiffies < timeout)); - - if ((temp)&&(irq_handle.count)) { - while (!atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source]))) temp++; - atomic_inc( &(privdata->irq_outstanding[irq_source]) ); - } - - irq_handle.count = temp; - - WRITE_TO_USER(interrupt_wait_t, irq_handle); - - return 0; + wait_event_interruptible_timeout( (privdata->irq_queues[irq_source]), (atomic_read(&(privdata->irq_outstanding[irq_source])) > 0), (10*HZ/1000) ); + + if (atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source])) ) + atomic_inc( &(privdata->irq_outstanding[irq_source]) ); + else + temp = 1; + } while ((!temp)&&(jiffies < timeout)); + + if ((temp)&&(irq_handle.count)) { + while (!atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source]))) temp++; + atomic_inc( &(privdata->irq_outstanding[irq_source]) ); + } + + irq_handle.count = temp; + + WRITE_TO_USER(interrupt_wait_t, irq_handle); + + return 0; #else - mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n"); - return -EFAULT; + mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n"); + return -EFAULT; #endif } @@ -404,18 +404,18 @@ static int ioctl_wait_interrupt(pcidriver_privdata_t *privdata, unsigned long ar static int ioctl_clear_ioq(pcidriver_privdata_t *privdata, unsigned long arg) { #ifdef ENABLE_IRQ - unsigned int irq_source; + unsigned int irq_source; - if (arg >= PCIDRIVER_INT_MAXSOURCES) - return -EFAULT; + if (arg >= PCIDRIVER_INT_MAXSOURCES) + return -EFAULT; - irq_source = arg; - atomic_set(&(privdata->irq_outstanding[irq_source]), 0); + irq_source = arg; + atomic_set(&(privdata->irq_outstanding[irq_source]), 0); - return 0; + return 0; #else - mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n"); - return -EFAULT; + mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n"); + return -EFAULT; #endif } @@ -429,18 +429,18 @@ static int ioctl_clear_ioq(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_version(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - pcilib_driver_version_t info; + int ret; + pcilib_driver_version_t info; - info = (pcilib_driver_version_t) { - .version = PCILIB_VERSION, - .interface = PCIDRIVER_INTERFACE_VERSION, - .ioctls = PCIDRIVER_IOC_MAX + 1 - }; + info = (pcilib_driver_version_t) { + .version = PCILIB_VERSION, + .interface = PCIDRIVER_INTERFACE_VERSION, + .ioctls = PCIDRIVER_IOC_MAX + 1 + }; - WRITE_TO_USER(pcilib_driver_version_t, info); + WRITE_TO_USER(pcilib_driver_version_t, info); - return 0; + return 0; } /** @@ -452,24 +452,24 @@ static int ioctl_version(pcidriver_privdata_t *privdata, unsigned long arg) */ static int ioctl_device_state(pcidriver_privdata_t *privdata, unsigned long arg) { - int ret; - pcilib_device_state_t info; + int ret; + pcilib_device_state_t info; #ifdef PCIDRIVER_DUMMY_DEVICE - memset(&info, 0, sizeof(info)); + memset(&info, 0, sizeof(info)); #else /* PCIDRIVER_DUMMY_DEVICE */ - info = (pcilib_device_state_t) { - .iommu = iommu_present(privdata->pdev->dev.bus), - .mps = pcidriver_pcie_get_mps(privdata->pdev), - .readrq = pcie_get_readrq(privdata->pdev), - .dma_mask = privdata->pdev->dma_mask - }; + info = (pcilib_device_state_t) { + .iommu = iommu_present(privdata->pdev->dev.bus), + .mps = pcidriver_pcie_get_mps(privdata->pdev), + .readrq = pcie_get_readrq(privdata->pdev), + .dma_mask = privdata->pdev->dma_mask + }; #endif /* PCIDRIVER_DUMMY_DEVICE */ - WRITE_TO_USER(pcilib_device_state_t, info); + WRITE_TO_USER(pcilib_device_state_t, info); - return 0; + return 0; } @@ -483,19 +483,19 @@ static int ioctl_device_state(pcidriver_privdata_t *privdata, unsigned long arg) static int ioctl_set_dma_mask(pcidriver_privdata_t *privdata, unsigned long arg) { #ifndef PCIDRIVER_DUMMY_DEVICE - int err; + int err; - if ((arg < 24) || (arg > 64)) - return -EINVAL; + if ((arg < 24) || (arg > 64)) + return -EINVAL; - err = pci_set_dma_mask(privdata->pdev, DMA_BIT_MASK(arg)); - if (err < 0) { - printk(KERN_ERR "pci_set_dma_mask(%lu) failed\n", arg); - return err; - } + err = pci_set_dma_mask(privdata->pdev, DMA_BIT_MASK(arg)); + if (err < 0) { + printk(KERN_ERR "pci_set_dma_mask(%lu) failed\n", arg); + return err; + } #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - - return 0; + + return 0; } /** @@ -508,19 +508,19 @@ static int ioctl_set_dma_mask(pcidriver_privdata_t *privdata, unsigned long arg) static int ioctl_set_mps(pcidriver_privdata_t *privdata, unsigned long arg) { #ifndef PCIDRIVER_DUMMY_DEVICE - int err; + int err; - if ((arg != 128) && (arg != 256) && (arg != 512)) - return -EINVAL; + if ((arg != 128) && (arg != 256) && (arg != 512)) + return -EINVAL; - err = pcidriver_pcie_set_mps(privdata->pdev, arg); - if (err < 0) { - printk(KERN_ERR "pcie_set_mps(%lu) failed\n", arg); - return err; - } + err = pcidriver_pcie_set_mps(privdata->pdev, arg); + if (err < 0) { + printk(KERN_ERR "pcie_set_mps(%lu) failed\n", arg); + return err; + } #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - - return 0; + + return 0; } @@ -533,69 +533,65 @@ static int ioctl_set_mps(pcidriver_privdata_t *privdata, unsigned long arg) * @returns -EFAULT when an invalid memory pointer is passed * */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) -int pcidriver_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) -#else long pcidriver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -#endif { - pcidriver_privdata_t *privdata = filp->private_data; + pcidriver_privdata_t *privdata = filp->private_data; + + /* Select the appropiate command */ + switch (cmd) { + case PCIDRIVER_IOC_MMAP_MODE: + return ioctl_mmap_mode(privdata, arg); - /* Select the appropiate command */ - switch (cmd) { - case PCIDRIVER_IOC_MMAP_MODE: - return ioctl_mmap_mode(privdata, arg); + case PCIDRIVER_IOC_MMAP_AREA: + return ioctl_mmap_area(privdata, arg); - case PCIDRIVER_IOC_MMAP_AREA: - return ioctl_mmap_area(privdata, arg); + case PCIDRIVER_IOC_PCI_CFG_RD: + case PCIDRIVER_IOC_PCI_CFG_WR: + return ioctl_pci_config_read_write(privdata, cmd, arg); - case PCIDRIVER_IOC_PCI_CFG_RD: - case PCIDRIVER_IOC_PCI_CFG_WR: - return ioctl_pci_config_read_write(privdata, cmd, arg); + case PCIDRIVER_IOC_PCI_INFO: + return ioctl_pci_info(privdata, arg); - case PCIDRIVER_IOC_PCI_INFO: - return ioctl_pci_info(privdata, arg); + case PCIDRIVER_IOC_KMEM_ALLOC: + return ioctl_kmem_alloc(privdata, arg); - case PCIDRIVER_IOC_KMEM_ALLOC: - return ioctl_kmem_alloc(privdata, arg); + case PCIDRIVER_IOC_KMEM_FREE: + return ioctl_kmem_free(privdata, arg); - case PCIDRIVER_IOC_KMEM_FREE: - return ioctl_kmem_free(privdata, arg); + case PCIDRIVER_IOC_KMEM_SYNC: + return ioctl_kmem_sync(privdata, arg); - case PCIDRIVER_IOC_KMEM_SYNC: - return ioctl_kmem_sync(privdata, arg); + case PCIDRIVER_IOC_UMEM_SGMAP: + return ioctl_umem_sgmap(privdata, arg); - case PCIDRIVER_IOC_UMEM_SGMAP: - return ioctl_umem_sgmap(privdata, arg); + case PCIDRIVER_IOC_UMEM_SGUNMAP: + return ioctl_umem_sgunmap(privdata, arg); - case PCIDRIVER_IOC_UMEM_SGUNMAP: - return ioctl_umem_sgunmap(privdata, arg); + case PCIDRIVER_IOC_UMEM_SGGET: + return ioctl_umem_sgget(privdata, arg); - case PCIDRIVER_IOC_UMEM_SGGET: - return ioctl_umem_sgget(privdata, arg); + case PCIDRIVER_IOC_UMEM_SYNC: + return ioctl_umem_sync(privdata, arg); - case PCIDRIVER_IOC_UMEM_SYNC: - return ioctl_umem_sync(privdata, arg); + case PCIDRIVER_IOC_WAITI: + return ioctl_wait_interrupt(privdata, arg); - case PCIDRIVER_IOC_WAITI: - return ioctl_wait_interrupt(privdata, arg); + case PCIDRIVER_IOC_CLEAR_IOQ: + return ioctl_clear_ioq(privdata, arg); - case PCIDRIVER_IOC_CLEAR_IOQ: - return ioctl_clear_ioq(privdata, arg); - - case PCIDRIVER_IOC_VERSION: - return ioctl_version(privdata, arg); + case PCIDRIVER_IOC_VERSION: + return ioctl_version(privdata, arg); - case PCIDRIVER_IOC_DEVICE_STATE: - return ioctl_device_state(privdata, arg); + case PCIDRIVER_IOC_DEVICE_STATE: + return ioctl_device_state(privdata, arg); - case PCIDRIVER_IOC_DMA_MASK: - return ioctl_set_dma_mask(privdata, arg); + case PCIDRIVER_IOC_DMA_MASK: + return ioctl_set_dma_mask(privdata, arg); - case PCIDRIVER_IOC_MPS: - return ioctl_set_mps(privdata, arg); + case PCIDRIVER_IOC_MPS: + return ioctl_set_mps(privdata, arg); - default: - return -EINVAL; - } + default: + return -EINVAL; + } } diff --git a/driver/ioctl.h b/driver/ioctl.h index 696fb8b..e989f95 100644 --- a/driver/ioctl.h +++ b/driver/ioctl.h @@ -1,5 +1 @@ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) -int pcidriver_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -#else long pcidriver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -#endif diff --git a/driver/kmem.c b/driver/kmem.c index 9bc1eb7..e3b0a97 100644 --- a/driver/kmem.c +++ b/driver/kmem.c @@ -24,10 +24,6 @@ #include "kmem.h" /* prototypes for kernel memory */ #include "sysfs.h" /* prototypes for sysfs */ -/* VM_RESERVED is removed in 3.7-rc1 */ -#ifndef VM_RESERVED -# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) -#endif /** * @@ -37,270 +33,268 @@ */ int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle) { - int flags; - pcidriver_kmem_entry_t *kmem_entry; - void *retptr; - - if (kmem_handle->flags&KMEM_FLAG_REUSE) { - kmem_entry = pcidriver_kmem_find_entry_use(privdata, kmem_handle->use, kmem_handle->item); - if (kmem_entry) { - unsigned long flags = kmem_handle->flags; - - if (flags&KMEM_FLAG_TRY) { - kmem_handle->type = kmem_entry->type; - kmem_handle->size = kmem_entry->size; - kmem_handle->align = kmem_entry->align; - } else { - if (kmem_handle->type != kmem_entry->type) { - mod_info("Invalid type of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->type, kmem_handle->type); - kmem_handle->type = kmem_entry->type; - return -EINVAL; - } - - if (((kmem_handle->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_PAGE)&&(kmem_handle->size == 0)) { - kmem_handle->size = kmem_entry->size; - } else if (kmem_handle->size != kmem_entry->size) { - mod_info("Invalid size of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->size, kmem_handle->size); - kmem_handle->size = kmem_entry->size; - return -EINVAL; - } - - if (kmem_handle->align != kmem_entry->align) { - mod_info("Invalid alignment of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->align, kmem_handle->align); - kmem_handle->align = kmem_entry->align; - return -EINVAL; - } - - if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?1:0) != ((flags&KMEM_FLAG_EXCLUSIVE)?1:0)) { - mod_info("Invalid mode of reusable kmem_entry\n"); - kmem_handle->flags = (kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?KMEM_FLAG_EXCLUSIVE:0; - return -EINVAL; - } - } - - - if ((kmem_entry->mode&KMEM_MODE_COUNT)==KMEM_MODE_COUNT) { - mod_info("Reuse counter of kmem_entry is overflown"); - return -EBUSY; - } - - - kmem_handle->handle_id = kmem_entry->id; - kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle); - kmem_handle->pa = virt_to_phys((void*)kmem_entry->cpua); - - kmem_handle->flags = KMEM_FLAG_REUSED; - if (kmem_entry->refs&KMEM_REF_HW) kmem_handle->flags |= KMEM_FLAG_REUSED_HW; - if (kmem_entry->mode&KMEM_MODE_PERSISTENT) kmem_handle->flags |= KMEM_FLAG_REUSED_PERSISTENT; - - kmem_entry->mode += 1; - if (flags&KMEM_FLAG_HW) { - if ((kmem_entry->refs&KMEM_REF_HW)==0) - pcidriver_module_get(privdata); - - kmem_entry->refs |= KMEM_REF_HW; - } - if (flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT; - - privdata->kmem_cur_id = kmem_entry->id; - - return 0; - } - - if (kmem_handle->flags&KMEM_FLAG_TRY) return -ENOENT; - } - - /* First, allocate zeroed memory for the kmem_entry */ - if ((kmem_entry = kcalloc(1, sizeof(pcidriver_kmem_entry_t), GFP_KERNEL)) == NULL) - goto kmem_alloc_entry_fail; - - /* Initialize the kmem_entry */ - kmem_entry->id = atomic_inc_return(&privdata->kmem_count) - 1; - privdata->kmem_cur_id = kmem_entry->id; - kmem_handle->handle_id = kmem_entry->id; - - kmem_entry->use = kmem_handle->use; - kmem_entry->item = kmem_handle->item; - kmem_entry->type = kmem_handle->type; - kmem_entry->align = kmem_handle->align; - kmem_entry->direction = PCI_DMA_NONE; - - /* Initialize sysfs if possible */ - if (pcidriver_sysfs_initialize_kmem(privdata, kmem_entry->id, &(kmem_entry->sysfs_attr)) != 0) - goto kmem_alloc_mem_fail; - - /* ...and allocate the DMA memory */ - /* note this is a memory pair, referencing the same area: the cpu address (cpua) - * and the PCI bus address (pa). The CPU and PCI addresses may not be the same. - * The CPU sees only CPU addresses, while the device sees only PCI addresses. - * CPU address is used for the mmap (internal to the driver), and - * PCI address is the address passed to the DMA Controller in the device. - */ - switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) { - case PCILIB_KMEM_TYPE_CONSISTENT: + int flags; + pcidriver_kmem_entry_t *kmem_entry; + void *retptr; + + if (kmem_handle->flags&KMEM_FLAG_REUSE) { + kmem_entry = pcidriver_kmem_find_entry_use(privdata, kmem_handle->use, kmem_handle->item); + if (kmem_entry) { + unsigned long flags = kmem_handle->flags; + + if (flags&KMEM_FLAG_TRY) { + kmem_handle->type = kmem_entry->type; + kmem_handle->size = kmem_entry->size; + kmem_handle->align = kmem_entry->align; + } else { + if (kmem_handle->type != kmem_entry->type) { + mod_info("Invalid type of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->type, kmem_handle->type); + kmem_handle->type = kmem_entry->type; + return -EINVAL; + } + + if (((kmem_handle->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_PAGE)&&(kmem_handle->size == 0)) { + kmem_handle->size = kmem_entry->size; + } else if (kmem_handle->size != kmem_entry->size) { + mod_info("Invalid size of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->size, kmem_handle->size); + kmem_handle->size = kmem_entry->size; + return -EINVAL; + } + + if (kmem_handle->align != kmem_entry->align) { + mod_info("Invalid alignment of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->align, kmem_handle->align); + kmem_handle->align = kmem_entry->align; + return -EINVAL; + } + + if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?1:0) != ((flags&KMEM_FLAG_EXCLUSIVE)?1:0)) { + mod_info("Invalid mode of reusable kmem_entry\n"); + kmem_handle->flags = (kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?KMEM_FLAG_EXCLUSIVE:0; + return -EINVAL; + } + } + + + if ((kmem_entry->mode&KMEM_MODE_COUNT)==KMEM_MODE_COUNT) { + mod_info("Reuse counter of kmem_entry is overflown"); + return -EBUSY; + } + + + kmem_handle->handle_id = kmem_entry->id; + kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle); + kmem_handle->pa = virt_to_phys((void*)kmem_entry->cpua); + + kmem_handle->flags = KMEM_FLAG_REUSED; + if (kmem_entry->refs&KMEM_REF_HW) kmem_handle->flags |= KMEM_FLAG_REUSED_HW; + if (kmem_entry->mode&KMEM_MODE_PERSISTENT) kmem_handle->flags |= KMEM_FLAG_REUSED_PERSISTENT; + + kmem_entry->mode += 1; + if (flags&KMEM_FLAG_HW) { + if ((kmem_entry->refs&KMEM_REF_HW)==0) + pcidriver_module_get(privdata); + + kmem_entry->refs |= KMEM_REF_HW; + } + if (flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT; + + privdata->kmem_cur_id = kmem_entry->id; + + return 0; + } + + if (kmem_handle->flags&KMEM_FLAG_TRY) return -ENOENT; + } + + /* First, allocate zeroed memory for the kmem_entry */ + if ((kmem_entry = kcalloc(1, sizeof(pcidriver_kmem_entry_t), GFP_KERNEL)) == NULL) + goto kmem_alloc_entry_fail; + + /* Initialize the kmem_entry */ + kmem_entry->id = atomic_inc_return(&privdata->kmem_count) - 1; + privdata->kmem_cur_id = kmem_entry->id; + kmem_handle->handle_id = kmem_entry->id; + + kmem_entry->use = kmem_handle->use; + kmem_entry->item = kmem_handle->item; + kmem_entry->type = kmem_handle->type; + kmem_entry->align = kmem_handle->align; + kmem_entry->direction = PCI_DMA_NONE; + + /* Initialize sysfs if possible */ + if (pcidriver_sysfs_initialize_kmem(privdata, kmem_entry->id, &(kmem_entry->sysfs_attr)) != 0) + goto kmem_alloc_mem_fail; + + /* ...and allocate the DMA memory */ + /* note this is a memory pair, referencing the same area: the cpu address (cpua) + * and the PCI bus address (pa). The CPU and PCI addresses may not be the same. + * The CPU sees only CPU addresses, while the device sees only PCI addresses. + * CPU address is used for the mmap (internal to the driver), and + * PCI address is the address passed to the DMA Controller in the device. + */ + switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) { + case PCILIB_KMEM_TYPE_CONSISTENT: #ifdef PCIDRIVER_DUMMY_DEVICE - retptr = kmalloc( kmem_handle->size, GFP_KERNEL); + retptr = kmalloc( kmem_handle->size, GFP_KERNEL); #else /* PCIDRIVER_DUMMY_DEVICE */ - retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) ); + retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) ); #endif /* PCIDRIVER_DUMMY_DEVICE */ - break; - case PCILIB_KMEM_TYPE_REGION: - retptr = ioremap(kmem_handle->pa, kmem_handle->size); - kmem_entry->dma_handle = kmem_handle->pa; - if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_S2C) { - kmem_entry->direction = PCI_DMA_TODEVICE; - } else if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_C2S) { - kmem_entry->direction = PCI_DMA_FROMDEVICE; - } - break; - case PCILIB_KMEM_TYPE_PAGE: - flags = GFP_KERNEL; - - if (kmem_handle->size == 0) - kmem_handle->size = PAGE_SIZE; - else if (kmem_handle->size%PAGE_SIZE) - goto kmem_alloc_mem_fail; - else - flags |= __GFP_COMP; - - retptr = (void*)__get_free_pages(flags, get_order(kmem_handle->size)); - kmem_entry->dma_handle = 0; - - if (retptr) { + break; + case PCILIB_KMEM_TYPE_REGION: + retptr = ioremap(kmem_handle->pa, kmem_handle->size); + kmem_entry->dma_handle = kmem_handle->pa; + if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_S2C) { + kmem_entry->direction = PCI_DMA_TODEVICE; + } else if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_C2S) { + kmem_entry->direction = PCI_DMA_FROMDEVICE; + } + break; + case PCILIB_KMEM_TYPE_PAGE: + flags = GFP_KERNEL; + + if (kmem_handle->size == 0) + kmem_handle->size = PAGE_SIZE; + else if (kmem_handle->size%PAGE_SIZE) + goto kmem_alloc_mem_fail; + else + flags |= __GFP_COMP; + + retptr = (void*)__get_free_pages(flags, get_order(kmem_handle->size)); + kmem_entry->dma_handle = 0; + + if (retptr) { #ifndef PCIDRIVER_DUMMY_DEVICE - if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) { - kmem_entry->direction = PCI_DMA_TODEVICE; - kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) { - free_pages((unsigned long)retptr, get_order(kmem_handle->size)); - goto kmem_alloc_mem_fail; - } - } else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) { - kmem_entry->direction = PCI_DMA_FROMDEVICE; - kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) { - free_pages((unsigned long)retptr, get_order(kmem_handle->size)); - goto kmem_alloc_mem_fail; - - } - } + if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) { + kmem_entry->direction = PCI_DMA_TODEVICE; + kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) { + free_pages((unsigned long)retptr, get_order(kmem_handle->size)); + goto kmem_alloc_mem_fail; + } + } else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) { + kmem_entry->direction = PCI_DMA_FROMDEVICE; + kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) { + free_pages((unsigned long)retptr, get_order(kmem_handle->size)); + goto kmem_alloc_mem_fail; + + } + } #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - } - - break; - default: - goto kmem_alloc_mem_fail; - } - - - if (retptr == NULL) - goto kmem_alloc_mem_fail; - - kmem_entry->size = kmem_handle->size; - kmem_entry->cpua = (unsigned long)retptr; - kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle); - kmem_handle->pa = virt_to_phys(retptr); - - kmem_entry->mode = 1; - if (kmem_handle->flags&KMEM_FLAG_REUSE) { - kmem_entry->mode |= KMEM_MODE_REUSABLE; - if (kmem_handle->flags&KMEM_FLAG_EXCLUSIVE) kmem_entry->mode |= KMEM_MODE_EXCLUSIVE; - if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT; - } - - kmem_entry->refs = 0; - if (kmem_handle->flags&KMEM_FLAG_HW) { - pcidriver_module_get(privdata); - - kmem_entry->refs |= KMEM_REF_HW; - } - - kmem_handle->flags = 0; - - set_pages_reserved_compat(kmem_entry->cpua, kmem_entry->size); - - /* Add the kmem_entry to the list of the device */ - spin_lock( &(privdata->kmemlist_lock) ); - list_add_tail( &(kmem_entry->list), &(privdata->kmem_list) ); - spin_unlock( &(privdata->kmemlist_lock) ); - - return 0; + } + + break; + default: + goto kmem_alloc_mem_fail; + } + + + if (retptr == NULL) + goto kmem_alloc_mem_fail; + + kmem_entry->size = kmem_handle->size; + kmem_entry->cpua = (unsigned long)retptr; + kmem_handle->ba = (unsigned long)(kmem_entry->dma_handle); + kmem_handle->pa = virt_to_phys(retptr); + + kmem_entry->mode = 1; + if (kmem_handle->flags&KMEM_FLAG_REUSE) { + kmem_entry->mode |= KMEM_MODE_REUSABLE; + if (kmem_handle->flags&KMEM_FLAG_EXCLUSIVE) kmem_entry->mode |= KMEM_MODE_EXCLUSIVE; + if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT; + } + + kmem_entry->refs = 0; + if (kmem_handle->flags&KMEM_FLAG_HW) { + pcidriver_module_get(privdata); + + kmem_entry->refs |= KMEM_REF_HW; + } + + kmem_handle->flags = 0; + + /* Add the kmem_entry to the list of the device */ + spin_lock( &(privdata->kmemlist_lock) ); + list_add_tail( &(kmem_entry->list), &(privdata->kmem_list) ); + spin_unlock( &(privdata->kmemlist_lock) ); + + return 0; kmem_alloc_mem_fail: - kfree(kmem_entry); + kfree(kmem_entry); kmem_alloc_entry_fail: - return -ENOMEM; + return -ENOMEM; } static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle, pcidriver_kmem_entry_t *kmem_entry) { - if ((kmem_handle->flags & KMEM_FLAG_FORCE) == 0) { - if (kmem_entry->mode&KMEM_MODE_COUNT) - kmem_entry->mode -= 1; - - if (kmem_handle->flags&KMEM_FLAG_HW) { - if (kmem_entry->refs&KMEM_REF_HW) - pcidriver_module_put(privdata); - - kmem_entry->refs &= ~KMEM_REF_HW; - } - - if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) - kmem_entry->mode &= ~KMEM_MODE_PERSISTENT; - - if (kmem_handle->flags&KMEM_FLAG_REUSE) - return 0; - - if (kmem_entry->refs) { - kmem_entry->mode += 1; - mod_info("can't free referenced kmem_entry, refs = %lx\n", kmem_entry->refs); - return -EBUSY; - } - - if (kmem_entry->mode & KMEM_MODE_PERSISTENT) { - kmem_entry->mode += 1; - mod_info("can't free persistent kmem_entry\n"); - return -EBUSY; - } - - if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0)) - return 0; - } else { - if (kmem_entry->refs&KMEM_REF_HW) - pcidriver_module_put(privdata); - - while (!atomic_add_negative(-1, &(privdata->refs))) pcidriver_module_put(privdata); - atomic_inc(&(privdata->refs)); - - } - - return 1; + if ((kmem_handle->flags & KMEM_FLAG_FORCE) == 0) { + if (kmem_entry->mode&KMEM_MODE_COUNT) + kmem_entry->mode -= 1; + + if (kmem_handle->flags&KMEM_FLAG_HW) { + if (kmem_entry->refs&KMEM_REF_HW) + pcidriver_module_put(privdata); + + kmem_entry->refs &= ~KMEM_REF_HW; + } + + if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) + kmem_entry->mode &= ~KMEM_MODE_PERSISTENT; + + if (kmem_handle->flags&KMEM_FLAG_REUSE) + return 0; + + if (kmem_entry->refs) { + kmem_entry->mode += 1; + mod_info("can't free referenced kmem_entry, refs = %lx\n", kmem_entry->refs); + return -EBUSY; + } + + if (kmem_entry->mode & KMEM_MODE_PERSISTENT) { + kmem_entry->mode += 1; + mod_info("can't free persistent kmem_entry\n"); + return -EBUSY; + } + + if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0)) + return 0; + } else { + if (kmem_entry->refs&KMEM_REF_HW) + pcidriver_module_put(privdata); + + while (!atomic_add_negative(-1, &(privdata->refs))) pcidriver_module_put(privdata); + atomic_inc(&(privdata->refs)); + + } + + return 1; } static int pcidriver_kmem_free_use(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle) { - int err; - int failed = 0; - struct list_head *ptr, *next; - pcidriver_kmem_entry_t *kmem_entry; - - /* iterate safely over the entries and delete them */ - list_for_each_safe(ptr, next, &(privdata->kmem_list)) { - kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list); - if (kmem_entry->use == kmem_handle->use) { - err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry); - if (err > 0) - pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */ - else - failed = 1; - } - } - - if (failed) { - mod_info("Some kmem_entries for use %lx are still referenced\n", kmem_handle->use); - return -EBUSY; - } - - return 0; + int err; + int failed = 0; + struct list_head *ptr, *next; + pcidriver_kmem_entry_t *kmem_entry; + + /* iterate safely over the entries and delete them */ + list_for_each_safe(ptr, next, &(privdata->kmem_list)) { + kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list); + if (kmem_entry->use == kmem_handle->use) { + err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry); + if (err > 0) + pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */ + else + failed = 1; + } + } + + if (failed) { + mod_info("Some kmem_entries for use %lx are still referenced\n", kmem_handle->use); + return -EBUSY; + } + + return 0; } /** @@ -310,24 +304,24 @@ static int pcidriver_kmem_free_use(pcidriver_privdata_t *privdata, kmem_handle_t */ int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle ) { - int err; - pcidriver_kmem_entry_t *kmem_entry; - - if (kmem_handle->flags&KMEM_FLAG_MASS) { - kmem_handle->flags &= ~KMEM_FLAG_MASS; - return pcidriver_kmem_free_use(privdata, kmem_handle); - } - - /* Find the associated kmem_entry for this buffer */ - if ((kmem_entry = pcidriver_kmem_find_entry(privdata, kmem_handle)) == NULL) - return -EINVAL; /* kmem_handle is not valid */ - - err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry); - - if (err > 0) - return pcidriver_kmem_free_entry(privdata, kmem_entry); - - return err; + int err; + pcidriver_kmem_entry_t *kmem_entry; + + if (kmem_handle->flags&KMEM_FLAG_MASS) { + kmem_handle->flags &= ~KMEM_FLAG_MASS; + return pcidriver_kmem_free_use(privdata, kmem_handle); + } + + /* Find the associated kmem_entry for this buffer */ + if ((kmem_entry = pcidriver_kmem_find_entry(privdata, kmem_handle)) == NULL) + return -EINVAL; /* kmem_handle is not valid */ + + err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry); + + if (err > 0) + return pcidriver_kmem_free_entry(privdata, kmem_entry); + + return err; } /** @@ -338,24 +332,24 @@ int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_han int pcidriver_kmem_free_all(pcidriver_privdata_t *privdata) { // int failed = 0; - struct list_head *ptr, *next; - pcidriver_kmem_entry_t *kmem_entry; - - /* iterate safely over the entries and delete them */ - list_for_each_safe(ptr, next, &(privdata->kmem_list)) { - kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list); - /*if (kmem_entry->refs) - failed = 1; - else*/ - pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */ - } -/* - if (failed) { - mod_info("Some kmem_entries are still referenced\n"); - return -EBUSY; - } -*/ - return 0; + struct list_head *ptr, *next; + pcidriver_kmem_entry_t *kmem_entry; + + /* iterate safely over the entries and delete them */ + list_for_each_safe(ptr, next, &(privdata->kmem_list)) { + kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list); + /*if (kmem_entry->refs) + failed = 1; + else*/ + pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */ + } + /* + if (failed) { + mod_info("Some kmem_entries are still referenced\n"); + return -EBUSY; + } + */ + return 0; } @@ -366,43 +360,27 @@ int pcidriver_kmem_free_all(pcidriver_privdata_t *privdata) */ int pcidriver_kmem_sync_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry, int direction) { - if (kmem_entry->direction == PCI_DMA_NONE) - return -EINVAL; + if (kmem_entry->direction == PCI_DMA_NONE) + return -EINVAL; #ifndef PCIDRIVER_DUMMY_DEVICE -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) - switch (direction) { - case PCILIB_KMEM_SYNC_TODEVICE: - pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); - break; - case PCILIB_KMEM_SYNC_FROMDEVICE: - pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); - break; - case PCILIB_KMEM_SYNC_BIDIRECTIONAL: - pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); - pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); - break; - default: - return -EINVAL; /* wrong direction parameter */ - } -#else - switch (direction) { - case PCILIB_KMEM_SYNC_TODEVICE: - pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); - break; - case PCILIB_KMEM_SYNC_FROMDEVICE: - pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); - break; - case PCILIB_KMEM_SYNC_BIDIRECTIONAL: - pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); - break; - default: - return -EINVAL; /* wrong direction parameter */ - } -#endif + switch (direction) { + case PCILIB_KMEM_SYNC_TODEVICE: + pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); + break; + case PCILIB_KMEM_SYNC_FROMDEVICE: + pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); + break; + case PCILIB_KMEM_SYNC_BIDIRECTIONAL: + pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); + pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction ); + break; + default: + return -EINVAL; /* wrong direction parameter */ + } #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - return 0; /* success */ + return 0; /* success */ } /** @@ -412,41 +390,41 @@ int pcidriver_kmem_sync_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_en */ int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync ) { - pcidriver_kmem_entry_t *kmem_entry = NULL; - - /* - * This is a shortcut to quickly find a next item in big multi-page kernel buffers - */ - spin_lock(&(privdata->kmemlist_lock)); - if (privdata->kmem_last_sync) { - if (privdata->kmem_last_sync->id == kmem_sync->handle.handle_id) - kmem_entry = privdata->kmem_last_sync; - else { - privdata->kmem_last_sync = container_of(privdata->kmem_last_sync->list.next, pcidriver_kmem_entry_t, list); - - if (privdata->kmem_last_sync) { - if (privdata->kmem_last_sync->id == kmem_sync->handle.handle_id) - kmem_entry = privdata->kmem_last_sync; - else - privdata->kmem_last_sync = NULL; - } - } - } - spin_unlock(&(privdata->kmemlist_lock)); - - /* - * If not found go the standard way - */ - if (!kmem_entry) { - if ((kmem_entry = pcidriver_kmem_find_entry(privdata, &(kmem_sync->handle))) == NULL) - return -EINVAL; /* kmem_handle is not valid */ - - spin_lock(&(privdata->kmemlist_lock)); - privdata->kmem_last_sync = kmem_entry; - spin_unlock(&(privdata->kmemlist_lock)); - } - - return pcidriver_kmem_sync_entry(privdata, kmem_entry, kmem_sync->dir); + pcidriver_kmem_entry_t *kmem_entry = NULL; + + /* + * This is a shortcut to quickly find a next item in big multi-page kernel buffers + */ + spin_lock(&(privdata->kmemlist_lock)); + if (privdata->kmem_last_sync) { + if (privdata->kmem_last_sync->id == kmem_sync->handle.handle_id) + kmem_entry = privdata->kmem_last_sync; + else { + privdata->kmem_last_sync = container_of(privdata->kmem_last_sync->list.next, pcidriver_kmem_entry_t, list); + + if (privdata->kmem_last_sync) { + if (privdata->kmem_last_sync->id == kmem_sync->handle.handle_id) + kmem_entry = privdata->kmem_last_sync; + else + privdata->kmem_last_sync = NULL; + } + } + } + spin_unlock(&(privdata->kmemlist_lock)); + + /* + * If not found go the standard way + */ + if (!kmem_entry) { + if ((kmem_entry = pcidriver_kmem_find_entry(privdata, &(kmem_sync->handle))) == NULL) + return -EINVAL; /* kmem_handle is not valid */ + + spin_lock(&(privdata->kmemlist_lock)); + privdata->kmem_last_sync = kmem_entry; + spin_unlock(&(privdata->kmemlist_lock)); + } + + return pcidriver_kmem_sync_entry(privdata, kmem_entry, kmem_sync->dir); } /** @@ -456,75 +434,46 @@ int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync */ int pcidriver_kmem_free_entry(pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry) { - pcidriver_sysfs_remove(privdata, &(kmem_entry->sysfs_attr)); - - /* Go over the pages of the kmem buffer, and mark them as not reserved */ -#if 0 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) - /* - * This code is DISABLED. - * Apparently, it is not needed to unreserve them. Doing so here - * hangs the machine. Why? - * - * Uhm.. see links: - * - * http://lwn.net/Articles/161204/ - * http://lists.openfabrics.org/pipermail/general/2007-March/034101.html - * - * I insist, this should be enabled, but doing so hangs the machine. - * Literature supports the point, and there is even a similar problem (see link) - * But this is not the case. It seems right to me. but obviously is not. - * - * Anyway, this goes away in kernel >=2.6.15. - */ - unsigned long start = __pa(kmem_entry->cpua) >> PAGE_SHIFT; - unsigned long end = __pa(kmem_entry->cpua + kmem_entry->size) >> PAGE_SHIFT; - unsigned long i; - for(i=start;itype&PCILIB_KMEM_TYPE_MASK) { - case PCILIB_KMEM_TYPE_CONSISTENT: + pcidriver_sysfs_remove(privdata, &(kmem_entry->sysfs_attr)); + + /* Release DMA memory */ + switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) { + case PCILIB_KMEM_TYPE_CONSISTENT: #ifdef PCIDRIVER_DUMMY_DEVICE - kfree((void*)(kmem_entry->cpua)); + kfree((void*)(kmem_entry->cpua)); #else /* PCIDRIVER_DUMMY_DEVICE */ - pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle ); + pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle ); #endif /* PCIDRIVER_DUMMY_DEVICE */ - break; - case PCILIB_KMEM_TYPE_REGION: - iounmap((void *)(kmem_entry->cpua)); - break; - case PCILIB_KMEM_TYPE_PAGE: + break; + case PCILIB_KMEM_TYPE_REGION: + iounmap((void *)(kmem_entry->cpua)); + break; + case PCILIB_KMEM_TYPE_PAGE: #ifndef PCIDRIVER_DUMMY_DEVICE - if (kmem_entry->dma_handle) { - if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) { - pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE); - } else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) { - pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_FROMDEVICE); - } - } + if (kmem_entry->dma_handle) { + if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) { + pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE); + } else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) { + pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_FROMDEVICE); + } + } #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - free_pages((unsigned long)kmem_entry->cpua, get_order(kmem_entry->size)); - break; - } + free_pages((unsigned long)kmem_entry->cpua, get_order(kmem_entry->size)); + break; + } - /* Remove the kmem list entry */ - spin_lock( &(privdata->kmemlist_lock) ); - if (privdata->kmem_last_sync == kmem_entry) - privdata->kmem_last_sync = NULL; - list_del( &(kmem_entry->list) ); - spin_unlock( &(privdata->kmemlist_lock) ); + /* Remove the kmem list entry */ + spin_lock( &(privdata->kmemlist_lock) ); + if (privdata->kmem_last_sync == kmem_entry) + privdata->kmem_last_sync = NULL; + list_del( &(kmem_entry->list) ); + spin_unlock( &(privdata->kmemlist_lock) ); - /* Release kmem_entry memory */ - kfree(kmem_entry); + /* Release kmem_entry memory */ + kfree(kmem_entry); - return 0; + return 0; } /** @@ -534,23 +483,23 @@ int pcidriver_kmem_free_entry(pcidriver_privdata_t *privdata, pcidriver_kmem_ent */ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle) { - struct list_head *ptr; - pcidriver_kmem_entry_t *entry, *result = NULL; + struct list_head *ptr; + pcidriver_kmem_entry_t *entry, *result = NULL; - /* should I implement it better using the handle_id? */ + /* should I implement it better using the handle_id? */ - spin_lock(&(privdata->kmemlist_lock)); - list_for_each(ptr, &(privdata->kmem_list)) { - entry = list_entry(ptr, pcidriver_kmem_entry_t, list); + spin_lock(&(privdata->kmemlist_lock)); + list_for_each(ptr, &(privdata->kmem_list)) { + entry = list_entry(ptr, pcidriver_kmem_entry_t, list); - if (entry->id == kmem_handle->handle_id) { - result = entry; - break; - } - } + if (entry->id == kmem_handle->handle_id) { + result = entry; + break; + } + } - spin_unlock(&(privdata->kmemlist_lock)); - return result; + spin_unlock(&(privdata->kmemlist_lock)); + return result; } /** @@ -560,21 +509,21 @@ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry(pcidriver_privdata_t *privdata */ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id(pcidriver_privdata_t *privdata, int id) { - struct list_head *ptr; - pcidriver_kmem_entry_t *entry, *result = NULL; + struct list_head *ptr; + pcidriver_kmem_entry_t *entry, *result = NULL; - spin_lock(&(privdata->kmemlist_lock)); - list_for_each(ptr, &(privdata->kmem_list)) { - entry = list_entry(ptr, pcidriver_kmem_entry_t, list); + spin_lock(&(privdata->kmemlist_lock)); + list_for_each(ptr, &(privdata->kmem_list)) { + entry = list_entry(ptr, pcidriver_kmem_entry_t, list); - if (entry->id == id) { - result = entry; - break; - } - } + if (entry->id == id) { + result = entry; + break; + } + } - spin_unlock(&(privdata->kmemlist_lock)); - return result; + spin_unlock(&(privdata->kmemlist_lock)); + return result; } /** @@ -584,21 +533,21 @@ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id(pcidriver_privdata_t *privd */ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_use(pcidriver_privdata_t *privdata, unsigned long use, unsigned long item) { - struct list_head *ptr; - pcidriver_kmem_entry_t *entry, *result = NULL; + struct list_head *ptr; + pcidriver_kmem_entry_t *entry, *result = NULL; - spin_lock(&(privdata->kmemlist_lock)); - list_for_each(ptr, &(privdata->kmem_list)) { - entry = list_entry(ptr, pcidriver_kmem_entry_t, list); + spin_lock(&(privdata->kmemlist_lock)); + list_for_each(ptr, &(privdata->kmem_list)) { + entry = list_entry(ptr, pcidriver_kmem_entry_t, list); - if ((entry->use == use)&&(entry->item == item)&&(entry->mode&KMEM_MODE_REUSABLE)) { - result = entry; - break; - } - } + if ((entry->use == use)&&(entry->item == item)&&(entry->mode&KMEM_MODE_REUSABLE)) { + result = entry; + break; + } + } - spin_unlock(&(privdata->kmemlist_lock)); - return result; + spin_unlock(&(privdata->kmemlist_lock)); + return result; } @@ -606,18 +555,18 @@ void pcidriver_kmem_mmap_close(struct vm_area_struct *vma) { unsigned long vma_size; pcidriver_kmem_entry_t *kmem_entry = (pcidriver_kmem_entry_t*)vma->vm_private_data; if (kmem_entry) { -/* - if (kmem_entry->id == 0) { - mod_info("refs: %p %p %lx\n", vma, vma->vm_private_data, kmem_entry->refs); - mod_info("kmem_size: %lu vma_size: %lu, s: %lx, e: %lx\n", kmem_entry->size, (vma->vm_end - vma->vm_start), vma->vm_start, vma->vm_end); - } -*/ - - vma_size = (vma->vm_end - vma->vm_start); - - if (kmem_entry->refs&KMEM_REF_COUNT) { - kmem_entry->refs -= vma_size / PAGE_SIZE; - } + /* + if (kmem_entry->id == 0) { + mod_info("refs: %p %p %lx\n", vma, vma->vm_private_data, kmem_entry->refs); + mod_info("kmem_size: %lu vma_size: %lu, s: %lx, e: %lx\n", kmem_entry->size, (vma->vm_end - vma->vm_start), vma->vm_start, vma->vm_end); + } + */ + + vma_size = (vma->vm_end - vma->vm_start); + + if (kmem_entry->refs&KMEM_REF_COUNT) { + kmem_entry->refs -= vma_size / PAGE_SIZE; + } } } @@ -632,78 +581,66 @@ static struct vm_operations_struct pcidriver_kmem_mmap_ops = { */ int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *vma) { - unsigned long vma_size; - pcidriver_kmem_entry_t *kmem_entry; - int ret; - - mod_info_dbg("Entering mmap_kmem\n"); - - /* FIXME: Is this really right? Always just the latest one? Can't we identify one? */ - /* Get latest entry on the kmem_list */ - kmem_entry = pcidriver_kmem_find_entry_id(privdata, privdata->kmem_cur_id); - if (!kmem_entry) { - mod_info("Trying to mmap a kernel memory buffer without creating it first!\n"); - return -EFAULT; - } - - mod_info_dbg("Got kmem_entry with id: %d\n", kmem_entry->id); - - /* Check sizes */ - vma_size = (vma->vm_end - vma->vm_start); - - if ((vma_size > kmem_entry->size) && - ((kmem_entry->size < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { - mod_info("kem_entry size(%lu) and vma size do not match(%lu)\n", kmem_entry->size, vma_size); - return -EINVAL; - } - - /* reference counting */ - if ((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)&&(kmem_entry->refs&KMEM_REF_COUNT)) { - mod_info("can't make second mmaping for exclusive kmem_entry\n"); - return -EBUSY; - } - if (((kmem_entry->refs&KMEM_REF_COUNT) + (vma_size / PAGE_SIZE)) > KMEM_REF_COUNT) { - mod_info("maximal amount of references is reached by kmem_entry\n"); - return -EBUSY; - } - - kmem_entry->refs += vma_size / PAGE_SIZE; - - vma->vm_flags |= (VM_RESERVED); - -#ifdef pgprot_noncached - // This is coherent memory, so it must not be cached. - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -#endif - - mod_info_dbg("Mapping address %08lx / PFN %08lx\n", - virt_to_phys((void*)kmem_entry->cpua), - page_to_pfn(virt_to_page((void*)kmem_entry->cpua))); - - if ((kmem_entry->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) { - ret = remap_pfn_range_compat( - vma, - vma->vm_start, - kmem_entry->dma_handle, - (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, - vma->vm_page_prot); - } else { - ret = remap_pfn_range_cpua_compat( - vma, - vma->vm_start, - kmem_entry->cpua, - (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, - vma->vm_page_prot ); - } - - if (ret) { - mod_info("kmem remap failed: %d (%lx)\n", ret,kmem_entry->cpua); - kmem_entry->refs -= 1; - return -EAGAIN; - } - - vma->vm_ops = &pcidriver_kmem_mmap_ops; - vma->vm_private_data = (void*)kmem_entry; - - return ret; + unsigned long vma_size; + pcidriver_kmem_entry_t *kmem_entry; + int ret; + + mod_info_dbg("Entering mmap_kmem\n"); + + /* FIXME: Is this really right? Always just the latest one? Can't we identify one? */ + /* Get latest entry on the kmem_list */ + kmem_entry = pcidriver_kmem_find_entry_id(privdata, privdata->kmem_cur_id); + if (!kmem_entry) { + mod_info("Trying to mmap a kernel memory buffer without creating it first!\n"); + return -EFAULT; + } + + mod_info_dbg("Got kmem_entry with id: %d\n", kmem_entry->id); + + /* Check sizes */ + vma_size = (vma->vm_end - vma->vm_start); + + if ((vma_size > kmem_entry->size) && + ((kmem_entry->size < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { + mod_info("kem_entry size(%lu) and vma size do not match(%lu)\n", kmem_entry->size, vma_size); + return -EINVAL; + } + + /* reference counting */ + if ((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)&&(kmem_entry->refs&KMEM_REF_COUNT)) { + mod_info("can't make second mmaping for exclusive kmem_entry\n"); + return -EBUSY; + } + if (((kmem_entry->refs&KMEM_REF_COUNT) + (vma_size / PAGE_SIZE)) > KMEM_REF_COUNT) { + mod_info("maximal amount of references is reached by kmem_entry\n"); + return -EBUSY; + } + + kmem_entry->refs += vma_size / PAGE_SIZE; + + vma->vm_flags |= (VM_RESERVED); + + // This is coherent memory, so it must not be cached. + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + mod_info_dbg("Mapping address %08lx / PFN %08lx\n", + virt_to_phys((void*)kmem_entry->cpua), + page_to_pfn(virt_to_page((void*)kmem_entry->cpua))); + + if ((kmem_entry->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) { + ret = remap_pfn_range_compat(vma, vma->vm_start, kmem_entry->dma_handle, (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); + } else { + ret = remap_pfn_range_cpua_compat(vma, vma->vm_start, kmem_entry->cpua, (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); + } + + if (ret) { + mod_info("kmem remap failed: %d (%lx)\n", ret,kmem_entry->cpua); + kmem_entry->refs -= 1; + return -EAGAIN; + } + + vma->vm_ops = &pcidriver_kmem_mmap_ops; + vma->vm_private_data = (void*)kmem_entry; + + return ret; } diff --git a/driver/pciDriver.h b/driver/pciDriver.h index 371bd88..3a231bd 100644 --- a/driver/pciDriver.h +++ b/driver/pciDriver.h @@ -1,60 +1,6 @@ #ifndef PCIDRIVER_H_ #define PCIDRIVER_H_ -/** - * This is a full rewrite of the pciDriver. - * New default is to support kernel 2.6, using kernel 2.6 APIs. - * - * This header defines the interface to the outside world. - * - * $Revision: 1.6 $ - * $Date: 2008-01-24 14:21:36 $ - * - */ - -/* - * Change History: - * - * $Log: not supported by cvs2svn $ - * Revision 1.5 2008-01-11 10:15:14 marcus - * Removed unused interrupt code. - * Added intSource to the wait interrupt call. - * - * Revision 1.4 2006/11/17 18:44:42 marcus - * Type of SG list can now be selected at runtime. Added type to sglist. - * - * Revision 1.3 2006/11/17 16:23:02 marcus - * Added slot number to the PCI info IOctl. - * - * Revision 1.2 2006/11/13 12:29:09 marcus - * Added a IOctl call, to confiure the interrupt response. (testing pending). - * Basic interrupts are now supported. - * - * Revision 1.1 2006/10/10 14:46:52 marcus - * Initial commit of the new pciDriver for kernel 2.6 - * - * Revision 1.7 2006/10/06 15:18:06 marcus - * Updated PCI info and PCI cmd - * - * Revision 1.6 2006/09/25 16:51:07 marcus - * Added PCI config IOctls, and implemented basic mmap functions. - * - * Revision 1.5 2006/09/18 17:13:12 marcus - * backup commit. - * - * Revision 1.4 2006/09/15 15:44:41 marcus - * backup commit. - * - * Revision 1.3 2006/08/15 11:40:02 marcus - * backup commit. - * - * Revision 1.2 2006/08/12 18:28:42 marcus - * Sync with the laptop - * - * Revision 1.1 2006/08/11 15:30:46 marcus - * Sync with the laptop - * - */ #include @@ -152,39 +98,39 @@ typedef struct { } pcilib_board_info_t; typedef struct { - unsigned long type; - unsigned long pa; - unsigned long ba; - unsigned long size; - unsigned long align; - unsigned long use; - unsigned long item; - int flags; - int handle_id; + unsigned long type; + unsigned long pa; + unsigned long ba; + unsigned long size; + unsigned long align; + unsigned long use; + unsigned long item; + int flags; + int handle_id; } kmem_handle_t; typedef struct { - unsigned long addr; - unsigned long size; + unsigned long addr; + unsigned long size; } umem_sgentry_t; typedef struct { - int handle_id; - int type; - int nents; - umem_sgentry_t *sg; + int handle_id; + int type; + int nents; + umem_sgentry_t *sg; } umem_sglist_t; typedef struct { - unsigned long vma; - unsigned long size; - int handle_id; - int dir; + unsigned long vma; + unsigned long size; + int handle_id; + int dir; } umem_handle_t; typedef struct { - kmem_handle_t handle; - int dir; + kmem_handle_t handle; + int dir; } kmem_sync_t; typedef struct { @@ -194,21 +140,21 @@ typedef struct { } interrupt_wait_t; typedef struct { - int size; - int addr; - union { - unsigned char byte; - unsigned short word; - unsigned int dword; /* not strict C, but if not can have problems */ - } val; + int size; + int addr; + union { + unsigned char byte; + unsigned short word; + unsigned int dword; /* not strict C, but if not can have problems */ + } val; } pci_cfg_cmd; /* ioctl interface */ /* See documentation for a detailed usage explanation */ -/* +/* * one of the problems of ioctl, is that requires a type definition. - * This type is only 8-bits wide, and half-documented in + * This type is only 8-bits wide, and half-documented in * /Documentation/ioctl-number.txt. * previous SHL -> 'S' definition, conflicts with several devices, * so I changed it to be pci -> 'p', in the range 0xA0-BF diff --git a/driver/rdma.c b/driver/rdma.c index 78c6d69..c08eef9 100644 --- a/driver/rdma.c +++ b/driver/rdma.c @@ -22,26 +22,26 @@ static unsigned long pcidriver_follow_pte(struct mm_struct *mm, unsigned long ad pmd_t *pmd; pte_t *pte; - spinlock_t *ptl; + spinlock_t *ptl; unsigned long pfn = 0; pgd = pgd_offset(mm, address); - if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) - return 0; - + if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) + return 0; + pud = pud_offset(pgd, address); - if (pud_none(*pud) || unlikely(pud_bad(*pud))) - return 0; + if (pud_none(*pud) || unlikely(pud_bad(*pud))) + return 0; pmd = pmd_offset(pud, address); - if (pmd_none(*pmd)) - return 0; + if (pmd_none(*pmd)) + return 0; pte = pte_offset_map_lock(mm, pmd, address, &ptl); if (!pte_none(*pte)) - pfn = (pte_pfn(*pte) << PAGE_SHIFT); - pte_unmap_unlock(pte, ptl); + pfn = (pte_pfn(*pte) << PAGE_SHIFT); + pte_unmap_unlock(pte, ptl); return pfn; } diff --git a/driver/sysfs.c b/driver/sysfs.c index abc1c8a..19865fc 100644 --- a/driver/sysfs.c +++ b/driver/sysfs.c @@ -35,46 +35,40 @@ static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry); * */ static int _pcidriver_sysfs_initialize(pcidriver_privdata_t *privdata, - int id, - struct class_device_attribute *sysfs_attr, - const char *fmtstring, - SYSFS_GET_FUNCTION((*callback))) + int id, + struct class_device_attribute *sysfs_attr, + const char *fmtstring, + SYSFS_GET_FUNCTION((*callback))) { - /* sysfs attributes for kmem buffers don’t make sense before 2.6.13, as - we have no mmap support before */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) - char namebuffer[16]; + /* sysfs attributes for kmem buffers don’t make sense before 2.6.13, as + we have no mmap support before */ + char namebuffer[16]; - /* allocate space for the name of the attribute */ - snprintf(namebuffer, sizeof(namebuffer), fmtstring, id); + /* allocate space for the name of the attribute */ + snprintf(namebuffer, sizeof(namebuffer), fmtstring, id); - if ((sysfs_attr->attr.name = kstrdup(namebuffer, GFP_KERNEL)) == NULL) - return -ENOMEM; + if ((sysfs_attr->attr.name = kstrdup(namebuffer, GFP_KERNEL)) == NULL) + return -ENOMEM; - sysfs_attr->attr.mode = S_IRUGO; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) - // DS: Shall we lock now while accessing driver data structures??? - sysfs_attr->attr.owner = THIS_MODULE; -#endif - sysfs_attr->show = callback; - sysfs_attr->store = NULL; - - /* name and add attribute */ - if (class_device_create_file(privdata->class_dev, sysfs_attr) != 0) - return -ENXIO; /* Device not configured. Not the really best choice, but hm. */ -#endif + sysfs_attr->attr.mode = S_IRUGO; + sysfs_attr->show = callback; + sysfs_attr->store = NULL; - return 0; + /* name and add attribute */ + if (class_device_create_file(privdata->class_dev, sysfs_attr) != 0) + return -ENXIO; /* Device not configured. Not the really best choice, but hm. */ + + return 0; } int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr) { - return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "kbuf%d", pcidriver_show_kmem_entry); + return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "kbuf%d", pcidriver_show_kmem_entry); } int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr) { - return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "umem%d", pcidriver_show_umem_entry); + return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "umem%d", pcidriver_show_umem_entry); } /** @@ -84,225 +78,210 @@ int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, stru */ void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct class_device_attribute *sysfs_attr) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) - class_device_remove_file(privdata->class_dev, sysfs_attr); - kfree(sysfs_attr->attr.name); -#endif + class_device_remove_file(privdata->class_dev, sysfs_attr); + kfree(sysfs_attr->attr.name); } static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - - /* As we can be sure that attr.name contains a filename which we - * created (see _pcidriver_sysfs_initialize), we do not need to have - * sanity checks but can directly call simple_strtol() */ - int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10); - pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id); - if (entry) { - unsigned long addr = entry->cpua; - unsigned long dma_addr = entry->dma_handle; - - if (entry->size >= 16) { - pcidriver_kmem_sync_entry(privdata, entry, PCILIB_KMEM_SYNC_FROMDEVICE); - return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\ndata: %8x %8x %8x %8x\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode, *(u32*)(entry->cpua), *(u32*)(entry->cpua + 4), *(u32*)(entry->cpua + 8), *(u32*)(entry->cpua + 12)); - } else - return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode); - } else - return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id); -#else - return 0; -#endif + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + + /* As we can be sure that attr.name contains a filename which we + * created (see _pcidriver_sysfs_initialize), we do not need to have + * sanity checks but can directly call simple_strtol() */ + int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10); + pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id); + if (entry) { + unsigned long addr = entry->cpua; + unsigned long dma_addr = entry->dma_handle; + + if (entry->size >= 16) { + pcidriver_kmem_sync_entry(privdata, entry, PCILIB_KMEM_SYNC_FROMDEVICE); + return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\ndata: %8x %8x %8x %8x\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode, *(u32*)(entry->cpua), *(u32*)(entry->cpua + 4), *(u32*)(entry->cpua + 8), *(u32*)(entry->cpua + 12)); + } else + return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode); + } else + return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id); } static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) -#if 0 - pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)cls->class_data; - - return snprintf(buf, PAGE_SIZE, "I am in the umem_entry show function, class_device_kobj_name: %s\n", cls->kobj.name); -#endif - return 0; -#else - return 0; -#endif + return 0; } #ifdef ENABLE_IRQ SYSFS_GET_FUNCTION(pcidriver_show_irq_count) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - return snprintf(buf, PAGE_SIZE, "%d\n", privdata->irq_count); + return snprintf(buf, PAGE_SIZE, "%d\n", privdata->irq_count); } SYSFS_GET_FUNCTION(pcidriver_show_irq_queues) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - int i, offset; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + int i, offset; - /* output will be truncated to PAGE_SIZE */ - offset = snprintf(buf, PAGE_SIZE, "Queue\tOutstanding IRQs\n"); - for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) - offset += snprintf(buf+offset, PAGE_SIZE-offset, "%d\t%d\n", i, atomic_read(&(privdata->irq_outstanding[i])) ); + /* output will be truncated to PAGE_SIZE */ + offset = snprintf(buf, PAGE_SIZE, "Queue\tOutstanding IRQs\n"); + for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++) + offset += snprintf(buf+offset, PAGE_SIZE-offset, "%d\t%d\n", i, atomic_read(&(privdata->irq_outstanding[i])) ); - return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); + return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); } #endif SYSFS_GET_FUNCTION(pcidriver_show_mmap_mode) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_mode); + return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_mode); } SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - int mode = -1; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + int mode = -1; - /* Set the mmap-mode if it is either PCIDRIVER_MMAP_PCI or PCIDRIVER_MMAP_KMEM */ - if (sscanf(buf, "%d", &mode) == 1 && - (mode == PCIDRIVER_MMAP_PCI || mode == PCIDRIVER_MMAP_KMEM)) - privdata->mmap_mode = mode; + /* Set the mmap-mode if it is either PCIDRIVER_MMAP_PCI or PCIDRIVER_MMAP_KMEM */ + if (sscanf(buf, "%d", &mode) == 1 && + (mode == PCIDRIVER_MMAP_PCI || mode == PCIDRIVER_MMAP_KMEM)) + privdata->mmap_mode = mode; - return strlen(buf); + return strlen(buf); } SYSFS_GET_FUNCTION(pcidriver_show_mmap_area) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_area); + return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_area); } SYSFS_SET_FUNCTION(pcidriver_store_mmap_area) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - int temp = -1; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + int temp = -1; - sscanf(buf, "%d", &temp); + sscanf(buf, "%d", &temp); - if ((temp >= PCIDRIVER_BAR0) && (temp <= PCIDRIVER_BAR5)) - privdata->mmap_area = temp; + if ((temp >= PCIDRIVER_BAR0) && (temp <= PCIDRIVER_BAR5)) + privdata->mmap_area = temp; - return strlen(buf); + return strlen(buf); } SYSFS_GET_FUNCTION(pcidriver_show_kmem_count) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&(privdata->kmem_count))); + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&(privdata->kmem_count))); } SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - kmem_handle_t kmem_handle; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + kmem_handle_t kmem_handle; - /* FIXME: guillermo: is validation of parsing an unsigned int enough? */ - if (sscanf(buf, "%lu", &kmem_handle.size) == 1) - pcidriver_kmem_alloc(privdata, &kmem_handle); + /* FIXME: guillermo: is validation of parsing an unsigned int enough? */ + if (sscanf(buf, "%lu", &kmem_handle.size) == 1) + pcidriver_kmem_alloc(privdata, &kmem_handle); - return strlen(buf); + return strlen(buf); } SYSFS_SET_FUNCTION(pcidriver_store_kmem_free) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - unsigned int id; - pcidriver_kmem_entry_t *kmem_entry; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + unsigned int id; + pcidriver_kmem_entry_t *kmem_entry; - /* Parse the ID of the kernel memory to be freed, check bounds */ - if (sscanf(buf, "%u", &id) != 1 || - (id >= atomic_read(&(privdata->kmem_count)))) - goto err; + /* Parse the ID of the kernel memory to be freed, check bounds */ + if (sscanf(buf, "%u", &id) != 1 || + (id >= atomic_read(&(privdata->kmem_count)))) + goto err; - if ((kmem_entry = pcidriver_kmem_find_entry_id(privdata,id)) == NULL) - goto err; + if ((kmem_entry = pcidriver_kmem_find_entry_id(privdata,id)) == NULL) + goto err; - pcidriver_kmem_free_entry(privdata, kmem_entry ); + pcidriver_kmem_free_entry(privdata, kmem_entry ); err: - return strlen(buf); + return strlen(buf); } SYSFS_GET_FUNCTION(pcidriver_show_kbuffers) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - int offset = 0; - struct list_head *ptr; - pcidriver_kmem_entry_t *entry; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + int offset = 0; + struct list_head *ptr; + pcidriver_kmem_entry_t *entry; - /* print the header */ - offset += snprintf(buf, PAGE_SIZE, "kbuf#\tcpu addr\tsize\n"); + /* print the header */ + offset += snprintf(buf, PAGE_SIZE, "kbuf#\tcpu addr\tsize\n"); - spin_lock(&(privdata->kmemlist_lock)); - list_for_each(ptr, &(privdata->kmem_list)) { - entry = list_entry(ptr, pcidriver_kmem_entry_t, list); + spin_lock(&(privdata->kmemlist_lock)); + list_for_each(ptr, &(privdata->kmem_list)) { + entry = list_entry(ptr, pcidriver_kmem_entry_t, list); - /* print entry info */ - if (offset > PAGE_SIZE) { - spin_unlock( &(privdata->kmemlist_lock) ); - return PAGE_SIZE; - } + /* print entry info */ + if (offset > PAGE_SIZE) { + spin_unlock( &(privdata->kmemlist_lock) ); + return PAGE_SIZE; + } - offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%08lx\t%lu\n", entry->id, (unsigned long)(entry->dma_handle), entry->size ); - } + offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%08lx\t%lu\n", entry->id, (unsigned long)(entry->dma_handle), entry->size ); + } - spin_unlock(&(privdata->kmemlist_lock)); + spin_unlock(&(privdata->kmemlist_lock)); - /* output will be truncated to PAGE_SIZE */ - return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); + /* output will be truncated to PAGE_SIZE */ + return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); } SYSFS_GET_FUNCTION(pcidriver_show_umappings) { - int offset = 0; - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - struct list_head *ptr; - pcidriver_umem_entry_t *entry; + int offset = 0; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + struct list_head *ptr; + pcidriver_umem_entry_t *entry; - /* print the header */ - offset += snprintf(buf, PAGE_SIZE, "umap#\tn_pages\tsg_ents\n"); + /* print the header */ + offset += snprintf(buf, PAGE_SIZE, "umap#\tn_pages\tsg_ents\n"); - spin_lock( &(privdata->umemlist_lock) ); - list_for_each( ptr, &(privdata->umem_list) ) { - entry = list_entry(ptr, pcidriver_umem_entry_t, list ); + spin_lock( &(privdata->umemlist_lock) ); + list_for_each( ptr, &(privdata->umem_list) ) { + entry = list_entry(ptr, pcidriver_umem_entry_t, list ); - /* print entry info */ - if (offset > PAGE_SIZE) { - spin_unlock( &(privdata->umemlist_lock) ); - return PAGE_SIZE; - } + /* print entry info */ + if (offset > PAGE_SIZE) { + spin_unlock( &(privdata->umemlist_lock) ); + return PAGE_SIZE; + } - offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%lu\t%lu\n", entry->id, - (unsigned long)(entry->nr_pages), (unsigned long)(entry->nents)); - } + offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%lu\t%lu\n", entry->id, + (unsigned long)(entry->nr_pages), (unsigned long)(entry->nents)); + } - spin_unlock( &(privdata->umemlist_lock) ); + spin_unlock( &(privdata->umemlist_lock) ); - /* output will be truncated to PAGE_SIZE */ - return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); + /* output will be truncated to PAGE_SIZE */ + return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); } SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap) { - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - pcidriver_umem_entry_t *umem_entry; - unsigned int id; + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + pcidriver_umem_entry_t *umem_entry; + unsigned int id; - if (sscanf(buf, "%u", &id) != 1 || - (id >= atomic_read(&(privdata->umem_count)))) - goto err; + if (sscanf(buf, "%u", &id) != 1 || + (id >= atomic_read(&(privdata->umem_count)))) + goto err; - if ((umem_entry = pcidriver_umem_find_entry_id(privdata, id)) == NULL) - goto err; + if ((umem_entry = pcidriver_umem_find_entry_id(privdata, id)) == NULL) + goto err; - pcidriver_umem_sgunmap(privdata, umem_entry); + pcidriver_umem_sgunmap(privdata, umem_entry); err: - return strlen(buf); + return strlen(buf); } diff --git a/driver/umem.c b/driver/umem.c index 7a8dcc1..bb9af1e 100644 --- a/driver/umem.c +++ b/driver/umem.c @@ -32,153 +32,153 @@ */ int pcidriver_umem_sgmap(pcidriver_privdata_t *privdata, umem_handle_t *umem_handle) { - int i, res, nr_pages; - struct page **pages; - struct scatterlist *sg = NULL; - pcidriver_umem_entry_t *umem_entry; - unsigned int nents; - unsigned long count,offset,length; - - /* - * We do some checks first. Then, the following is necessary to create a - * Scatter/Gather list from a user memory area: - * - Determine the number of pages - * - Get the pages for the memory area - * - Lock them. - * - Create a scatter/gather list of the pages - * - Map the list from memory to PCI bus addresses - * - * Then, we: - * - Create an entry on the umem list of the device, to cache the mapping. - * - Create a sysfs attribute that gives easy access to the SG list - */ - - /* zero-size?? */ - if (umem_handle->size == 0) - return -EINVAL; - - /* Direction is better ignoring during mapping. */ - /* We assume bidirectional buffers always, except when sync'ing */ - - /* calculate the number of pages */ - nr_pages = ((umem_handle->vma & ~PAGE_MASK) + umem_handle->size + ~PAGE_MASK) >> PAGE_SHIFT; - - mod_info_dbg("nr_pages computed: %u\n", nr_pages); - - /* Allocate space for the page information */ - /* This can be very big, so we use vmalloc */ - if ((pages = vmalloc(nr_pages * sizeof(*pages))) == NULL) - return -ENOMEM; - - mod_info_dbg("allocated space for the pages.\n"); - - /* Allocate space for the scatterlist */ - /* We do not know how many entries will be, but the maximum is nr_pages. */ - /* This can be very big, so we use vmalloc */ - if ((sg = vmalloc(nr_pages * sizeof(*sg))) == NULL) - goto umem_sgmap_pages; - - sg_init_table(sg, nr_pages); - - mod_info_dbg("allocated space for the SG list.\n"); - - /* Get the page information */ - down_read(¤t->mm->mmap_sem); - res = get_user_pages( - current, - current->mm, - umem_handle->vma, - nr_pages, - 1, - 0, /* do not force, FIXME: shall I? */ - pages, - NULL ); - up_read(¤t->mm->mmap_sem); - - /* Error, not all pages mapped */ - if (res < (int)nr_pages) { - mod_info("Could not map all user pages (%d of %d)\n", res, nr_pages); - /* If only some pages could be mapped, we release those. If a real - * error occured, we set nr_pages to 0 */ - nr_pages = (res > 0 ? res : 0); - goto umem_sgmap_unmap; - } - - mod_info_dbg("Got the pages (%d).\n", res); - - /* Lock the pages, then populate the SG list with the pages */ - /* page0 is different */ - if ( !PageReserved(pages[0]) ) - compat_lock_page(pages[0]); - - offset = (umem_handle->vma & ~PAGE_MASK); - length = (umem_handle->size > (PAGE_SIZE-offset) ? (PAGE_SIZE-offset) : umem_handle->size); - - sg_set_page(&sg[0], pages[0], length, offset); - - count = umem_handle->size - length; - for(i=1;i PAGE_SIZE) ? PAGE_SIZE : count), 0); - count -= sg[i].length; - } - - /* Use the page list to populate the SG list */ - /* SG entries may be merged, res is the number of used entries */ - /* We have originally nr_pages entries in the sg list */ - if ((nents = pci_map_sg(privdata->pdev, sg, nr_pages, PCI_DMA_BIDIRECTIONAL)) == 0) - goto umem_sgmap_unmap; - - mod_info_dbg("Mapped SG list (%d entries).\n", nents); - - /* Add an entry to the umem_list of the device, and update the handle with the id */ - /* Allocate space for the new umem entry */ - if ((umem_entry = kmalloc(sizeof(*umem_entry), GFP_KERNEL)) == NULL) - goto umem_sgmap_entry; - - /* Fill entry to be added to the umem list */ - umem_entry->id = atomic_inc_return(&privdata->umem_count) - 1; - umem_entry->nr_pages = nr_pages; /* Will be needed when unmapping */ - umem_entry->pages = pages; - umem_entry->nents = nents; - umem_entry->sg = sg; - - if (pcidriver_sysfs_initialize_umem(privdata, umem_entry->id, &(umem_entry->sysfs_attr)) != 0) - goto umem_sgmap_name_fail; - - /* Add entry to the umem list */ - spin_lock( &(privdata->umemlist_lock) ); - list_add_tail( &(umem_entry->list), &(privdata->umem_list) ); - spin_unlock( &(privdata->umemlist_lock) ); - - /* Update the Handle with the Handle ID of the entry */ - umem_handle->handle_id = umem_entry->id; - - return 0; + int i, res, nr_pages; + struct page **pages; + struct scatterlist *sg = NULL; + pcidriver_umem_entry_t *umem_entry; + unsigned int nents; + unsigned long count,offset,length; + + /* + * We do some checks first. Then, the following is necessary to create a + * Scatter/Gather list from a user memory area: + * - Determine the number of pages + * - Get the pages for the memory area + * - Lock them. + * - Create a scatter/gather list of the pages + * - Map the list from memory to PCI bus addresses + * + * Then, we: + * - Create an entry on the umem list of the device, to cache the mapping. + * - Create a sysfs attribute that gives easy access to the SG list + */ + + /* zero-size?? */ + if (umem_handle->size == 0) + return -EINVAL; + + /* Direction is better ignoring during mapping. */ + /* We assume bidirectional buffers always, except when sync'ing */ + + /* calculate the number of pages */ + nr_pages = ((umem_handle->vma & ~PAGE_MASK) + umem_handle->size + ~PAGE_MASK) >> PAGE_SHIFT; + + mod_info_dbg("nr_pages computed: %u\n", nr_pages); + + /* Allocate space for the page information */ + /* This can be very big, so we use vmalloc */ + if ((pages = vmalloc(nr_pages * sizeof(*pages))) == NULL) + return -ENOMEM; + + mod_info_dbg("allocated space for the pages.\n"); + + /* Allocate space for the scatterlist */ + /* We do not know how many entries will be, but the maximum is nr_pages. */ + /* This can be very big, so we use vmalloc */ + if ((sg = vmalloc(nr_pages * sizeof(*sg))) == NULL) + goto umem_sgmap_pages; + + sg_init_table(sg, nr_pages); + + mod_info_dbg("allocated space for the SG list.\n"); + + /* Get the page information */ + down_read(¤t->mm->mmap_sem); + res = get_user_pages( + current, + current->mm, + umem_handle->vma, + nr_pages, + 1, + 0, /* do not force, FIXME: shall I? */ + pages, + NULL ); + up_read(¤t->mm->mmap_sem); + + /* Error, not all pages mapped */ + if (res < (int)nr_pages) { + mod_info("Could not map all user pages (%d of %d)\n", res, nr_pages); + /* If only some pages could be mapped, we release those. If a real + * error occured, we set nr_pages to 0 */ + nr_pages = (res > 0 ? res : 0); + goto umem_sgmap_unmap; + } + + mod_info_dbg("Got the pages (%d).\n", res); + + /* Lock the pages, then populate the SG list with the pages */ + /* page0 is different */ + if ( !PageReserved(pages[0]) ) + compat_lock_page(pages[0]); + + offset = (umem_handle->vma & ~PAGE_MASK); + length = (umem_handle->size > (PAGE_SIZE-offset) ? (PAGE_SIZE-offset) : umem_handle->size); + + sg_set_page(&sg[0], pages[0], length, offset); + + count = umem_handle->size - length; + for(i=1; i PAGE_SIZE) ? PAGE_SIZE : count), 0); + count -= sg[i].length; + } + + /* Use the page list to populate the SG list */ + /* SG entries may be merged, res is the number of used entries */ + /* We have originally nr_pages entries in the sg list */ + if ((nents = pci_map_sg(privdata->pdev, sg, nr_pages, PCI_DMA_BIDIRECTIONAL)) == 0) + goto umem_sgmap_unmap; + + mod_info_dbg("Mapped SG list (%d entries).\n", nents); + + /* Add an entry to the umem_list of the device, and update the handle with the id */ + /* Allocate space for the new umem entry */ + if ((umem_entry = kmalloc(sizeof(*umem_entry), GFP_KERNEL)) == NULL) + goto umem_sgmap_entry; + + /* Fill entry to be added to the umem list */ + umem_entry->id = atomic_inc_return(&privdata->umem_count) - 1; + umem_entry->nr_pages = nr_pages; /* Will be needed when unmapping */ + umem_entry->pages = pages; + umem_entry->nents = nents; + umem_entry->sg = sg; + + if (pcidriver_sysfs_initialize_umem(privdata, umem_entry->id, &(umem_entry->sysfs_attr)) != 0) + goto umem_sgmap_name_fail; + + /* Add entry to the umem list */ + spin_lock( &(privdata->umemlist_lock) ); + list_add_tail( &(umem_entry->list), &(privdata->umem_list) ); + spin_unlock( &(privdata->umemlist_lock) ); + + /* Update the Handle with the Handle ID of the entry */ + umem_handle->handle_id = umem_entry->id; + + return 0; umem_sgmap_name_fail: - kfree(umem_entry); + kfree(umem_entry); umem_sgmap_entry: - pci_unmap_sg( privdata->pdev, sg, nr_pages, PCI_DMA_BIDIRECTIONAL ); + pci_unmap_sg( privdata->pdev, sg, nr_pages, PCI_DMA_BIDIRECTIONAL ); umem_sgmap_unmap: - /* release pages */ - if (nr_pages > 0) { - for(i=0;i 0) { + for(i=0; isysfs_attr)); - - /* Unmap user memory */ - pci_unmap_sg( privdata->pdev, umem_entry->sg, umem_entry->nr_pages, PCI_DMA_BIDIRECTIONAL ); - - /* Release the pages */ - if (umem_entry->nr_pages > 0) { - for(i=0;i<(umem_entry->nr_pages);i++) { - /* Mark pages as Dirty and unlock it */ - if ( !PageReserved( umem_entry->pages[i] )) { - SetPageDirty( umem_entry->pages[i] ); - compat_unlock_page(umem_entry->pages[i]); - } - /* and release it from the cache */ - page_cache_release( umem_entry->pages[i] ); - } - } - - /* Remove the umem list entry */ - spin_lock( &(privdata->umemlist_lock) ); - list_del( &(umem_entry->list) ); - spin_unlock( &(privdata->umemlist_lock) ); - - /* Release SG list and page list memory */ - /* These two are in the vm area of the kernel */ - vfree(umem_entry->pages); - vfree(umem_entry->sg); - - /* Release umem_entry memory */ - kfree(umem_entry); - - return 0; + int i; + pcidriver_sysfs_remove(privdata, &(umem_entry->sysfs_attr)); + + /* Unmap user memory */ + pci_unmap_sg( privdata->pdev, umem_entry->sg, umem_entry->nr_pages, PCI_DMA_BIDIRECTIONAL ); + + /* Release the pages */ + if (umem_entry->nr_pages > 0) { + for(i=0; i<(umem_entry->nr_pages); i++) { + /* Mark pages as Dirty and unlock it */ + if ( !PageReserved( umem_entry->pages[i] )) { + SetPageDirty( umem_entry->pages[i] ); + compat_unlock_page(umem_entry->pages[i]); + } + /* and release it from the cache */ + page_cache_release( umem_entry->pages[i] ); + } + } + + /* Remove the umem list entry */ + spin_lock( &(privdata->umemlist_lock) ); + list_del( &(umem_entry->list) ); + spin_unlock( &(privdata->umemlist_lock) ); + + /* Release SG list and page list memory */ + /* These two are in the vm area of the kernel */ + vfree(umem_entry->pages); + vfree(umem_entry->sg); + + /* Release umem_entry memory */ + kfree(umem_entry); + + return 0; } /** @@ -231,16 +231,16 @@ int pcidriver_umem_sgunmap(pcidriver_privdata_t *privdata, pcidriver_umem_entry_ */ int pcidriver_umem_sgunmap_all(pcidriver_privdata_t *privdata) { - struct list_head *ptr, *next; - pcidriver_umem_entry_t *umem_entry; + struct list_head *ptr, *next; + pcidriver_umem_entry_t *umem_entry; - /* iterate safely over the entries and delete them */ - list_for_each_safe( ptr, next, &(privdata->umem_list) ) { - umem_entry = list_entry(ptr, pcidriver_umem_entry_t, list ); - pcidriver_umem_sgunmap( privdata, umem_entry ); /* spin lock inside! */ - } + /* iterate safely over the entries and delete them */ + list_for_each_safe( ptr, next, &(privdata->umem_list) ) { + umem_entry = list_entry(ptr, pcidriver_umem_entry_t, list ); + pcidriver_umem_sgunmap( privdata, umem_entry ); /* spin lock inside! */ + } - return 0; + return 0; } /** @@ -250,117 +250,68 @@ int pcidriver_umem_sgunmap_all(pcidriver_privdata_t *privdata) */ int pcidriver_umem_sgget(pcidriver_privdata_t *privdata, umem_sglist_t *umem_sglist) { - int i; - pcidriver_umem_entry_t *umem_entry; - struct scatterlist *sg; - int idx = 0; - dma_addr_t cur_addr; - unsigned int cur_size; - - /* Find the associated umem_entry for this buffer */ - umem_entry = pcidriver_umem_find_entry_id( privdata, umem_sglist->handle_id ); - if (umem_entry == NULL) - return -EINVAL; /* umem_handle is not valid */ - - /* Check if passed SG list is enough */ - if (umem_sglist->nents < umem_entry->nents) - return -EINVAL; /* sg has not enough entries */ - - /* Copy the SG list to the user format */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) - if (umem_sglist->type == PCIDRIVER_SG_MERGED) { - for_each_sg(umem_entry->sg, sg, umem_entry->nents, i ) { - if (i==0) { - umem_sglist->sg[0].addr = sg_dma_address( sg ); - umem_sglist->sg[0].size = sg_dma_len( sg ); - idx = 0; - } - else { - cur_addr = sg_dma_address( sg ); - cur_size = sg_dma_len( sg ); - - /* Check if entry fits after current entry */ - if (cur_addr == (umem_sglist->sg[idx].addr + umem_sglist->sg[idx].size)) { - umem_sglist->sg[idx].size += cur_size; - continue; - } - - /* Skip if the entry is zero-length (yes, it can happen.... at the end of the list) */ - if (cur_size == 0) - continue; - - /* None of the above, add new entry */ - idx++; - umem_sglist->sg[idx].addr = cur_addr; - umem_sglist->sg[idx].size = cur_size; - } - } - /* Set the used size of the SG list */ - umem_sglist->nents = idx+1; - } else { - for_each_sg(umem_entry->sg, sg, umem_entry->nents, i ) { - mod_info("entry: %d\n",i); - umem_sglist->sg[i].addr = sg_dma_address( sg ); - umem_sglist->sg[i].size = sg_dma_len( sg ); - } - - /* Set the used size of the SG list */ - /* Check if the last one is zero-length */ - if ( umem_sglist->sg[ umem_entry->nents - 1].size == 0) - umem_sglist->nents = umem_entry->nents -1; - else - umem_sglist->nents = umem_entry->nents; - } -#else - if (umem_sglist->type == PCIDRIVER_SG_MERGED) { - /* Merge entries that are contiguous into a single entry */ - /* Non-optimal but fast for most cases */ - /* First one always true */ - sg=umem_entry->sg; - umem_sglist->sg[0].addr = sg_dma_address( sg ); - umem_sglist->sg[0].size = sg_dma_len( sg ); - sg++; - idx = 0; - - /* Iterate over the SG entries */ - for(i=1; i< umem_entry->nents; i++, sg++ ) { - cur_addr = sg_dma_address( sg ); - cur_size = sg_dma_len( sg ); - - /* Check if entry fits after current entry */ - if (cur_addr == (umem_sglist->sg[idx].addr + umem_sglist->sg[idx].size)) { - umem_sglist->sg[idx].size += cur_size; - continue; - } - - /* Skip if the entry is zero-length (yes, it can happen.... at the end of the list) */ - if (cur_size == 0) - continue; - - /* None of the above, add new entry */ - idx++; - umem_sglist->sg[idx].addr = cur_addr; - umem_sglist->sg[idx].size = cur_size; - } - /* Set the used size of the SG list */ - umem_sglist->nents = idx+1; - } else { - /* Assume pci_map_sg made a good job (ehem..) and just copy it. - * actually, now I assume it just gives them plainly to me. */ - for(i=0, sg=umem_entry->sg ; i< umem_entry->nents; i++, sg++ ) { - umem_sglist->sg[i].addr = sg_dma_address( sg ); - umem_sglist->sg[i].size = sg_dma_len( sg ); - } - /* Set the used size of the SG list */ - /* Check if the last one is zero-length */ - if ( umem_sglist->sg[ umem_entry->nents - 1].size == 0) - umem_sglist->nents = umem_entry->nents -1; - else - umem_sglist->nents = umem_entry->nents; - } -#endif - - return 0; + int i; + pcidriver_umem_entry_t *umem_entry; + struct scatterlist *sg; + int idx = 0; + dma_addr_t cur_addr; + unsigned int cur_size; + + /* Find the associated umem_entry for this buffer */ + umem_entry = pcidriver_umem_find_entry_id( privdata, umem_sglist->handle_id ); + if (umem_entry == NULL) + return -EINVAL; /* umem_handle is not valid */ + + /* Check if passed SG list is enough */ + if (umem_sglist->nents < umem_entry->nents) + return -EINVAL; /* sg has not enough entries */ + + /* Copy the SG list to the user format */ + if (umem_sglist->type == PCIDRIVER_SG_MERGED) { + for_each_sg(umem_entry->sg, sg, umem_entry->nents, i ) { + if (i==0) { + umem_sglist->sg[0].addr = sg_dma_address( sg ); + umem_sglist->sg[0].size = sg_dma_len( sg ); + idx = 0; + } + else { + cur_addr = sg_dma_address( sg ); + cur_size = sg_dma_len( sg ); + + /* Check if entry fits after current entry */ + if (cur_addr == (umem_sglist->sg[idx].addr + umem_sglist->sg[idx].size)) { + umem_sglist->sg[idx].size += cur_size; + continue; + } + + /* Skip if the entry is zero-length (yes, it can happen.... at the end of the list) */ + if (cur_size == 0) + continue; + + /* None of the above, add new entry */ + idx++; + umem_sglist->sg[idx].addr = cur_addr; + umem_sglist->sg[idx].size = cur_size; + } + } + /* Set the used size of the SG list */ + umem_sglist->nents = idx+1; + } else { + for_each_sg(umem_entry->sg, sg, umem_entry->nents, i ) { + mod_info("entry: %d\n",i); + umem_sglist->sg[i].addr = sg_dma_address( sg ); + umem_sglist->sg[i].size = sg_dma_len( sg ); + } + + /* Set the used size of the SG list */ + /* Check if the last one is zero-length */ + if ( umem_sglist->sg[ umem_entry->nents - 1].size == 0) + umem_sglist->nents = umem_entry->nents -1; + else + umem_sglist->nents = umem_entry->nents; + } + + return 0; } /** @@ -370,45 +321,29 @@ int pcidriver_umem_sgget(pcidriver_privdata_t *privdata, umem_sglist_t *umem_sgl */ int pcidriver_umem_sync( pcidriver_privdata_t *privdata, umem_handle_t *umem_handle ) { - pcidriver_umem_entry_t *umem_entry; - - /* Find the associated umem_entry for this buffer */ - umem_entry = pcidriver_umem_find_entry_id( privdata, umem_handle->handle_id ); - if (umem_entry == NULL) - return -EINVAL; /* umem_handle is not valid */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) - switch (umem_handle->dir) { - case PCIDRIVER_DMA_TODEVICE: - pci_dma_sync_sg_for_device( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_TODEVICE ); - break; - case PCIDRIVER_DMA_FROMDEVICE: - pci_dma_sync_sg_for_cpu( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_FROMDEVICE ); - break; - case PCIDRIVER_DMA_BIDIRECTIONAL: - pci_dma_sync_sg_for_device( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL ); - pci_dma_sync_sg_for_cpu( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL ); - break; - default: - return -EINVAL; /* wrong direction parameter */ - } -#else - switch (umem_handle->dir) { - case PCIDRIVER_DMA_TODEVICE: - pci_dma_sync_sg( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_TODEVICE ); - break; - case PCIDRIVER_DMA_FROMDEVICE: - pci_dma_sync_sg( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_FROMDEVICE ); - break; - case PCIDRIVER_DMA_BIDIRECTIONAL: - pci_dma_sync_sg( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL ); - break; - default: - return -EINVAL; /* wrong direction parameter */ - } -#endif - - return 0; + pcidriver_umem_entry_t *umem_entry; + + /* Find the associated umem_entry for this buffer */ + umem_entry = pcidriver_umem_find_entry_id( privdata, umem_handle->handle_id ); + if (umem_entry == NULL) + return -EINVAL; /* umem_handle is not valid */ + + switch (umem_handle->dir) { + case PCIDRIVER_DMA_TODEVICE: + pci_dma_sync_sg_for_device( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_TODEVICE ); + break; + case PCIDRIVER_DMA_FROMDEVICE: + pci_dma_sync_sg_for_cpu( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_FROMDEVICE ); + break; + case PCIDRIVER_DMA_BIDIRECTIONAL: + pci_dma_sync_sg_for_device( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL ); + pci_dma_sync_sg_for_cpu( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL ); + break; + default: + return -EINVAL; /* wrong direction parameter */ + } + + return 0; } /* @@ -420,19 +355,19 @@ int pcidriver_umem_sync( pcidriver_privdata_t *privdata, umem_handle_t *umem_han */ pcidriver_umem_entry_t *pcidriver_umem_find_entry_id(pcidriver_privdata_t *privdata, int id) { - struct list_head *ptr; - pcidriver_umem_entry_t *entry; + struct list_head *ptr; + pcidriver_umem_entry_t *entry; - spin_lock(&(privdata->umemlist_lock)); - list_for_each(ptr, &(privdata->umem_list)) { - entry = list_entry(ptr, pcidriver_umem_entry_t, list ); + spin_lock(&(privdata->umemlist_lock)); + list_for_each(ptr, &(privdata->umem_list)) { + entry = list_entry(ptr, pcidriver_umem_entry_t, list ); - if (entry->id == id) { - spin_unlock( &(privdata->umemlist_lock) ); - return entry; - } - } + if (entry->id == id) { + spin_unlock( &(privdata->umemlist_lock) ); + return entry; + } + } - spin_unlock(&(privdata->umemlist_lock)); - return NULL; + spin_unlock(&(privdata->umemlist_lock)); + return NULL; } -- cgit v1.2.3 From 1120e8745ccd3e512fe2016c9e5092fcd378490a Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Wed, 2 Mar 2016 19:37:30 +0100 Subject: Restructure driver headers --- driver/Makefile | 18 ++- driver/base.c | 437 ++++++++++++----------------------------------------- driver/base.h | 93 ++---------- driver/common.h | 110 -------------- driver/compat.c | 25 --- driver/compat.h | 43 ------ driver/config.h | 34 +++-- driver/debug.h | 25 +++ driver/dev.c | 196 ++++++++++++++++++++++++ driver/dev.h | 52 +++++++ driver/int.c | 133 +++++----------- driver/int.h | 3 +- driver/ioctl.c | 25 +-- driver/ioctl.h | 179 +++++++++++++++++++++- driver/kmem.c | 11 +- driver/kmem.h | 36 +++++ driver/pciDriver.h | 191 ----------------------- driver/pcibus.c | 25 +++ driver/pcibus.h | 7 + driver/pcidriver.h | 11 ++ driver/rdma.c | 6 +- driver/rdma.h | 1 - driver/sysfs.c | 247 ++++++++++++++++++------------ driver/sysfs.h | 30 ++-- driver/umem.c | 15 +- driver/umem.h | 21 +++ pcilib/pci.h | 2 +- pcilib/pcilib.h | 6 +- 28 files changed, 901 insertions(+), 1081 deletions(-) delete mode 100644 driver/common.h delete mode 100644 driver/compat.c create mode 100644 driver/debug.h create mode 100644 driver/dev.c create mode 100644 driver/dev.h delete mode 100644 driver/pciDriver.h create mode 100644 driver/pcibus.c create mode 100644 driver/pcibus.h create mode 100644 driver/pcidriver.h diff --git a/driver/Makefile b/driver/Makefile index 8d8ada8..a55e7c7 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -1,11 +1,12 @@ CONFIG_MODULE_SIG=n obj-m := pciDriver.o -pciDriver-objs := base.o int.o umem.o kmem.o sysfs.o ioctl.o compat.o rdma.o +pciDriver-objs := base.o dev.o int.o umem.o kmem.o sysfs.o ioctl.o pcibus.o rdma.o KERNELDIR ?= /lib/modules/$(shell uname -r)/build INSTALLDIR ?= /lib/modules/$(shell uname -r)/extra MAININSTALLDIR ?= /lib/modules/$(shell uname -r)/kernel/extra +HEADERDIR ?= /lib/modules/$(shell uname -r)/source/include PWD := $(shell pwd) EXTRA_CFLAGS += -I$(M)/.. @@ -56,9 +57,11 @@ install: @install -m 755 pciDriver.ko $(INSTALLDIR) @echo "INSTALL $(INSTALLDIR)/pciDriver.symvers" @install -m 644 Module.symvers $(INSTALLDIR)/pciDriver.symvers -# @echo "INSTALL /usr/include/pciDriver/driver/pciDriver.h" -# @mkdir -p /usr/include/pciDriver/driver -# @install -m 644 pciDriver.h /usr/include/pciDriver/driver + @echo "INSTALL $(HEADERDIR)/linux/pcidriver.h" + @install -m 644 pcidriver.h $(HEADERDIR)/linux/ + @echo "INSTALL /usr/include/linux/pcidriver.h" + @mkdir -p /usr/include/linux + @install -m 644 ioctl.h /usr/include/linux/pcidriver.h uninstall: @echo "UNINSTALL $(INSTALLDIR)/pciDriver.ko" @@ -66,8 +69,11 @@ uninstall: @rm -f $(MAININSTALLDIR)/pciDriver.ko @rm -f $(INSTALLDIR)/pciDriver.symvers @rm -f $(MAININSTALLDIR)/pciDriver.symvers - @echo "UNINSTALL /usr/include/pciDriver/driver/pciDriver.h" - @rm -rf /usr/include/pciDriver/driver + @echo "UNINSTALL /usr/include/linux/pcidriver.h" + @rm -rf /usr/include/pciDriver/ + @rm -rf /usr/include/linux/pcidriver.h + @echo "UNINSTALL $(HEADERDIR)/linux/pcidriver.h" + @rm -rf $(HEADERDIR)/linux/pcidriver.h clean: rm -rf *.o *.ko *.mod.c .*.o.cmd .*.o.tmp .*.ko.cmd .*.o *.symvers modules.order .tmp_versions diff --git a/driver/base.c b/driver/base.c index 220f1f3..8bfbed6 100644 --- a/driver/base.c +++ b/driver/base.c @@ -1,13 +1,3 @@ -/** - * - * @file base.c - * @author Guillermo Marcus - * @date 2009-04-05 - * @brief Contains the main code which connects all the different parts and does - * basic driver tasks like initialization. - */ - - #include #include #include @@ -30,139 +20,66 @@ #include #include "../pcilib/version.h" - -#include "config.h" -#include "compat.h" -#include "pciDriver.h" -#include "common.h" -#include "base.h" -#include "int.h" -#include "kmem.h" -#include "umem.h" -#include "ioctl.h" #include "build.h" +#include "base.h" -/*************************************************************************/ -/* Module device table associated with this driver */ -MODULE_DEVICE_TABLE(pci, pcidriver_ids); -/* Module init and exit points */ -module_init(pcidriver_init); -module_exit(pcidriver_exit); /* Module info */ MODULE_AUTHOR("Guillermo Marcus"); MODULE_DESCRIPTION("Simple PCI Driver"); MODULE_LICENSE("GPL v2"); -/* Module class */ -static struct class_compat *pcidriver_class; - -#ifdef PCIDRIVER_DUMMY_DEVICE -pcidriver_privdata_t *pcidriver_dummydata = NULL; -#endif /* PCIDRIVER_DUMMY_DEVICE */ - -/** - * - * Called when loading the driver +/* + * This is the table of PCI devices handled by this driver by default + * If you want to add devices dynamically to this list, do: * + * echo "vendor device" > /sys/bus/pci/drivers/pciDriver/new_id + * where vendor and device are in hex, without leading '0x'. */ -static int __init pcidriver_init(void) -{ - int err = 0; - - /* Initialize the device count */ - atomic_set(&pcidriver_deviceCount, 0); - memset(pcidriver_privdata, 0, sizeof(pcidriver_privdata)); - - /* Allocate character device region dynamically */ - if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) { - mod_info("Couldn't allocate chrdev region. Module not loaded.\n"); - goto init_alloc_fail; - } - mod_info("Major %d allocated to nodename '%s'\n", MAJOR(pcidriver_devt), NODENAME); +static const __devinitdata struct pci_device_id pcidriver_ids[] = { + { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_ML605_DEVICE_ID ) }, // PCI-E Xilinx ML605 + { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_IPECAMERA_DEVICE_ID ) }, // PCI-E IPE Camera + { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_KAPTURE_DEVICE_ID ) }, // PCI-E KAPTURE board for HEB + {0,0,0,0}, +}; - /* Register driver class */ - pcidriver_class = class_create(THIS_MODULE, NODENAME); +MODULE_DEVICE_TABLE(pci, pcidriver_ids); - if (IS_ERR(pcidriver_class)) { - mod_info("No sysfs support. Module not loaded.\n"); - goto init_class_fail; - } +/* Module class */ +static struct class *pcidriver_class; - /* Register PCI driver. This function returns the number of devices on some - * systems, therefore check for errors as < 0. */ #ifdef PCIDRIVER_DUMMY_DEVICE - if ((err = pcidriver_probe(NULL, NULL)) < 0) { +pcidriver_privdata_t *pcidriver_dummydata = NULL; #else /* PCIDRIVER_DUMMY_DEVICE */ - if ((err = pci_register_driver(&pcidriver_driver)) < 0) { +static struct pci_driver pcidriver_driver; #endif /* PCIDRIVER_DUMMY_DEVICE */ - mod_info("Couldn't register PCI driver. Module not loaded.\n"); - goto init_pcireg_fail; - } - - mod_info("pcidriver %u.%u.%u loaded\n", PCILIB_VERSION_GET_MAJOR(PCILIB_VERSION), PCILIB_VERSION_GET_MINOR(PCILIB_VERSION), PCILIB_VERSION_GET_MICRO(PCILIB_VERSION)); - mod_info("%s\n", PCIDRIVER_BUILD); - mod_info("%s\n", PCIDRIVER_REVISION); - if (strlen(PCIDRIVER_CHANGES)) { - mod_info("Extra changes - %s\n", PCIDRIVER_CHANGES); - } - return 0; +/* Hold the allocated major & minor numbers */ +static dev_t pcidriver_devt; -init_pcireg_fail: - class_destroy(pcidriver_class); -init_class_fail: - unregister_chrdev_region(pcidriver_devt, MAXDEVICES); -init_alloc_fail: - return err; -} +/* Number of devices allocated */ +static atomic_t pcidriver_deviceCount; -/** - * - * Called when unloading the driver - * - */ -static void pcidriver_exit(void) -{ -#ifdef PCIDRIVER_DUMMY_DEVICE - pcidriver_remove(NULL); -#else - pci_unregister_driver(&pcidriver_driver); -#endif /* PCIDRIVER_DUMMY_DEVICE */ +/* Private data for probed devices */ +static pcidriver_privdata_t* pcidriver_privdata[MAXDEVICES]; - unregister_chrdev_region(pcidriver_devt, MAXDEVICES); - if (pcidriver_class != NULL) - class_destroy(pcidriver_class); +pcidriver_privdata_t *pcidriver_get_privdata(int devid) { + if (devid >= MAXDEVICES) + return NULL; - mod_info("Module unloaded\n"); + return pcidriver_privdata[devid]; } -/*************************************************************************/ -/* Driver functions */ +void pcidriver_put_privdata(pcidriver_privdata_t *privdata) { -/** - * - * This struct defines the PCI entry points. - * Will be registered at module init. - * - */ -#ifndef PCIDRIVER_DUMMY_DEVICE -static struct pci_driver pcidriver_driver = { - .name = MODNAME, - .id_table = pcidriver_ids, - .probe = pcidriver_probe, - .remove = pcidriver_remove, -}; -#endif /* ! PCIDRIVER_DUMMY_DEVICE */ +} /** - * * This function is called when installing the driver for a device * @param pdev Pointer to the PCI device - * */ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -250,12 +167,8 @@ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_devi privdata->devno = devno; /* FIXME: some error checking missing here */ -#ifdef PCIDRIVER_DUMMY_DEVICE - privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, NULL, NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata); -#else /* PCIDRIVER_DUMMY_DEVICE */ - privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, &(pdev->dev), NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata); -#endif /* PCIDRIVER_DUMMY_DEVICE */ - class_set_devdata( privdata->class_dev, privdata ); + privdata->class_dev = device_create(pcidriver_class, NULL, devno, privdata, NODENAMEFMT, MINOR(pcidriver_devt) + devid); + dev_set_drvdata(privdata->class_dev, privdata); mod_info("Device /dev/%s%d added\n",NODENAME,MINOR(pcidriver_devt) + devid); #ifndef PCIDRIVER_DUMMY_DEVICE @@ -264,31 +177,14 @@ static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_devi goto probe_irq_probe_fail; #endif /* ! PCIDRIVER_DUMMY_DEVICE */ - /* Populate sysfs attributes for the class device */ /* TODO: correct errorhandling. ewww. must remove the files in reversed order :-( */ -#define sysfs_attr(name) do { \ - if (class_device_create_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)) != 0) \ - goto probe_device_create_fail; \ - } while (0) -#ifdef ENABLE_IRQ - sysfs_attr(irq_count); - sysfs_attr(irq_queues); -#endif - - sysfs_attr(mmap_mode); - sysfs_attr(mmap_area); - sysfs_attr(kmem_count); - sysfs_attr(kmem_alloc); - sysfs_attr(kmem_free); - sysfs_attr(kbuffers); - sysfs_attr(umappings); - sysfs_attr(umem_unmap); -#undef sysfs_attr + if (pcidriver_create_sysfs_attributes(privdata) != 0) + goto probe_device_create_fail; /* Register character device */ - cdev_init( &(privdata->cdev), &pcidriver_fops ); + cdev_init(&(privdata->cdev), pcidriver_get_fops()); privdata->cdev.owner = THIS_MODULE; - privdata->cdev.ops = &pcidriver_fops; + privdata->cdev.ops = pcidriver_get_fops(); err = cdev_add( &privdata->cdev, devno, 1 ); if (err) { mod_info( "Couldn't add character device.\n" ); @@ -338,23 +234,7 @@ static void __devexit pcidriver_remove(struct pci_dev *pdev) pcidriver_privdata[privdata->devid] = NULL; /* Removing sysfs attributes from class device */ -#define sysfs_attr(name) do { \ - class_device_remove_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)); \ - } while (0) -#ifdef ENABLE_IRQ - sysfs_attr(irq_count); - sysfs_attr(irq_queues); -#endif - - sysfs_attr(mmap_mode); - sysfs_attr(mmap_area); - sysfs_attr(kmem_count); - sysfs_attr(kmem_alloc); - sysfs_attr(kmem_free); - sysfs_attr(kbuffers); - sysfs_attr(umappings); - sysfs_attr(umem_unmap); -#undef sysfs_attr + pcidriver_remove_sysfs_attributes(privdata); /* Free all allocated kmem buffers before leaving */ pcidriver_kmem_free_all( privdata ); @@ -369,7 +249,7 @@ static void __devexit pcidriver_remove(struct pci_dev *pdev) cdev_del(&(privdata->cdev)); /* Removing the device from sysfs */ - class_device_destroy(pcidriver_class, privdata->devno); + device_destroy(pcidriver_class, privdata->devno); /* Releasing privdata */ kfree(privdata); @@ -384,213 +264,82 @@ static void __devexit pcidriver_remove(struct pci_dev *pdev) } -/*************************************************************************/ -/* File operations */ -/*************************************************************************/ - -/** - * This struct defines the file operation entry points. - * - * @see pcidriver_ioctl - * @see pcidriver_mmap - * @see pcidriver_open - * @see pcidriver_release - * - */ -static struct file_operations pcidriver_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = pcidriver_ioctl, - .mmap = pcidriver_mmap, - .open = pcidriver_open, - .release = pcidriver_release, +#ifndef PCIDRIVER_DUMMY_DEVICE +static struct pci_driver pcidriver_driver = { + .name = MODNAME, + .id_table = pcidriver_ids, + .probe = pcidriver_probe, + .remove = pcidriver_remove, }; +#endif /* ! PCIDRIVER_DUMMY_DEVICE */ -void pcidriver_module_get(pcidriver_privdata_t *privdata) { - atomic_inc(&(privdata->refs)); -// mod_info("Ref: %i\n", atomic_read(&(privdata->refs))); -} - -void pcidriver_module_put(pcidriver_privdata_t *privdata) { - if (atomic_add_negative(-1, &(privdata->refs))) { - atomic_inc(&(privdata->refs)); - mod_info("Reference counting error..."); - } else { -// mod_info("Unref: %i\n", atomic_read(&(privdata->refs))); - } -} - -/** - * - * Called when an application open()s a /dev/fpga*, attaches the private data - * with the file pointer. - * - */ -int pcidriver_open(struct inode *inode, struct file *filp) +static int __init pcidriver_init(void) { - pcidriver_privdata_t *privdata; - - /* Set the private data area for the file */ - privdata = container_of( inode->i_cdev, pcidriver_privdata_t, cdev); - filp->private_data = privdata; - - pcidriver_module_get(privdata); - - return 0; -} + int err = 0; -/** - * - * Called when the application close()s the file descriptor. Does nothing at - * the moment. - * - */ -int pcidriver_release(struct inode *inode, struct file *filp) -{ - pcidriver_privdata_t *privdata; + /* Initialize the device count */ + atomic_set(&pcidriver_deviceCount, 0); - /* Get the private data area */ - privdata = filp->private_data; + memset(pcidriver_privdata, 0, sizeof(pcidriver_privdata)); - pcidriver_module_put(privdata); + /* Allocate character device region dynamically */ + if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) { + mod_info("Couldn't allocate chrdev region. Module not loaded.\n"); + goto init_alloc_fail; + } + mod_info("Major %d allocated to nodename '%s'\n", MAJOR(pcidriver_devt), NODENAME); - return 0; -} + /* Register driver class */ + pcidriver_class = class_create(THIS_MODULE, NODENAME); -/** - * - * This function is the entry point for mmap() and calls either pcidriver_mmap_pci - * or pcidriver_mmap_kmem - * - * @see pcidriver_mmap_pci - * @see pcidriver_mmap_kmem - * - */ -int pcidriver_mmap(struct file *filp, struct vm_area_struct *vma) -{ - pcidriver_privdata_t *privdata; - int ret = 0, bar; - - mod_info_dbg("Entering mmap\n"); - - /* Get the private data area */ - privdata = filp->private_data; - - /* Check the current mmap mode */ - switch (privdata->mmap_mode) { - case PCIDRIVER_MMAP_PCI: - /* Mmap a PCI region */ - switch (privdata->mmap_area) { - case PCIDRIVER_BAR0: - bar = 0; - break; - case PCIDRIVER_BAR1: - bar = 1; - break; - case PCIDRIVER_BAR2: - bar = 2; - break; - case PCIDRIVER_BAR3: - bar = 3; - break; - case PCIDRIVER_BAR4: - bar = 4; - break; - case PCIDRIVER_BAR5: - bar = 5; - break; - default: - mod_info("Attempted to mmap a PCI area with the wrong mmap_area value: %d\n",privdata->mmap_area); - return -EINVAL; /* invalid parameter */ - break; - } - ret = pcidriver_mmap_pci(privdata, vma, bar); - break; - case PCIDRIVER_MMAP_KMEM: - /* mmap a Kernel buffer */ - ret = pcidriver_mmap_kmem(privdata, vma); - break; - default: - mod_info( "Invalid mmap_mode value (%d)\n",privdata->mmap_mode ); - return -EINVAL; /* Invalid parameter (mode) */ + if (IS_ERR(pcidriver_class)) { + mod_info("No sysfs support. Module not loaded.\n"); + goto init_class_fail; } - return ret; -} - -/*************************************************************************/ -/* Internal driver functions */ -int pcidriver_mmap_pci(pcidriver_privdata_t *privdata, struct vm_area_struct *vmap, int bar) -{ + /* Register PCI driver. This function returns the number of devices on some + * systems, therefore check for errors as < 0. */ #ifdef PCIDRIVER_DUMMY_DEVICE - return -ENXIO; + if ((err = pcidriver_probe(NULL, NULL)) < 0) { #else /* PCIDRIVER_DUMMY_DEVICE */ - int ret = 0; - unsigned long bar_addr; - unsigned long bar_length, vma_size; - unsigned long bar_flags; - - mod_info_dbg("Entering mmap_pci\n"); - - - /* Get info of the BAR to be mapped */ - bar_addr = pci_resource_start(privdata->pdev, bar); - bar_length = pci_resource_len(privdata->pdev, bar); - bar_flags = pci_resource_flags(privdata->pdev, bar); - - /* Check sizes */ - vma_size = (vmap->vm_end - vmap->vm_start); - - if ((vma_size != bar_length) && - ((bar_length < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { - mod_info( "mmap size is not correct! bar: %lu - vma: %lu\n", bar_length, vma_size ); - return -EINVAL; + if ((err = pci_register_driver(&pcidriver_driver)) < 0) { +#endif /* PCIDRIVER_DUMMY_DEVICE */ + mod_info("Couldn't register PCI driver. Module not loaded.\n"); + goto init_pcireg_fail; } - if (bar_flags & IORESOURCE_IO) { - /* Unlikely case, we will mmap a IO region */ - - /* IO regions are never cacheable */ - vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); - - /* Map the BAR */ - ret = io_remap_pfn_range_compat(vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot); - } else { - /* Normal case, mmap a memory region */ - - /* Ensure this VMA is non-cached, if it is not flaged as prefetchable. - * If it is prefetchable, caching is allowed and will give better performance. - * This should be set properly by the BIOS, but we want to be sure. */ - /* adapted from drivers/char/mem.c, mmap function. */ - - /* Setting noncached disables MTRR registers, and we want to use them. - * So we take this code out. This can lead to caching problems if and only if - * the System BIOS set something wrong. Check LDDv3, page 425. - */ - -// if (!(bar_flags & IORESOURCE_PREFETCH)) -// vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); - - - /* Map the BAR */ - ret = remap_pfn_range_compat(vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot); + mod_info("pcidriver %u.%u.%u loaded\n", PCILIB_VERSION_GET_MAJOR(PCILIB_VERSION), PCILIB_VERSION_GET_MINOR(PCILIB_VERSION), PCILIB_VERSION_GET_MICRO(PCILIB_VERSION)); + mod_info("%s\n", PCIDRIVER_BUILD); + mod_info("%s\n", PCIDRIVER_REVISION); + if (strlen(PCIDRIVER_CHANGES)) { + mod_info("Extra changes - %s\n", PCIDRIVER_CHANGES); } - if (ret) { - mod_info("remap_pfn_range failed\n"); - return -EAGAIN; - } + return 0; - return 0; /* success */ -#endif /* PCIDRIVER_DUMMY_DEVICE */ +init_pcireg_fail: + class_destroy(pcidriver_class); +init_class_fail: + unregister_chrdev_region(pcidriver_devt, MAXDEVICES); +init_alloc_fail: + return err; } -pcidriver_privdata_t *pcidriver_get_privdata(int devid) { - if (devid >= MAXDEVICES) - return NULL; +static void pcidriver_exit(void) +{ +#ifdef PCIDRIVER_DUMMY_DEVICE + pcidriver_remove(NULL); +#else + pci_unregister_driver(&pcidriver_driver); +#endif /* PCIDRIVER_DUMMY_DEVICE */ - return pcidriver_privdata[devid]; -} + unregister_chrdev_region(pcidriver_devt, MAXDEVICES); -void pcidriver_put_privdata(pcidriver_privdata_t *privdata) { + if (pcidriver_class != NULL) + class_destroy(pcidriver_class); + mod_info("Module unloaded\n"); } + +module_init(pcidriver_init); +module_exit(pcidriver_exit); diff --git a/driver/base.h b/driver/base.h index 8556480..0203808 100644 --- a/driver/base.h +++ b/driver/base.h @@ -1,90 +1,15 @@ #ifndef _PCIDRIVER_BASE_H #define _PCIDRIVER_BASE_H +#include "config.h" +#include "compat.h" +#include "debug.h" +#include "pcibus.h" #include "sysfs.h" +#include "dev.h" +#include "int.h" -/** - * - * This file contains prototypes and data structures for internal use of the pciDriver. - * - * - */ +pcidriver_privdata_t *pcidriver_get_privdata(int devid); +void pcidriver_put_privdata(pcidriver_privdata_t *privdata); -/* prototypes for file_operations */ -static struct file_operations pcidriver_fops; -int pcidriver_mmap( struct file *filp, struct vm_area_struct *vmap ); -int pcidriver_open(struct inode *inode, struct file *filp ); -int pcidriver_release(struct inode *inode, struct file *filp); - -/* prototypes for device operations */ -#ifndef PCIDRIVER_DUMMY_DEVICE -static struct pci_driver pcidriver_driver; -#endif /* ! PCIDRIVER_DUMMY_DEVICE */ -static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_device_id *id); -static void __devexit pcidriver_remove(struct pci_dev *pdev); - - - -/* prototypes for module operations */ -static int __init pcidriver_init(void); -static void pcidriver_exit(void); - -/* - * This is the table of PCI devices handled by this driver by default - * If you want to add devices dynamically to this list, do: - * - * echo "vendor device" > /sys/bus/pci/drivers/pciDriver/new_id - * where vendor and device are in hex, without leading '0x'. - * - * The IDs themselves can be found in common.h - * - * For more info, see /Documentation/pci.txt - * - * __devinitdata is applied because the kernel does not need those - * tables any more after boot is finished on systems which don't - * support hotplug. - * - */ - -static const __devinitdata struct pci_device_id pcidriver_ids[] = { - { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_ML605_DEVICE_ID ) }, // PCI-E Xilinx ML605 - { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_IPECAMERA_DEVICE_ID ) }, // PCI-E IPE Camera - { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_KAPTURE_DEVICE_ID ) }, // PCI-E KAPTURE board for HEB - {0,0,0,0}, -}; - -/* prototypes for internal driver functions */ -int pcidriver_pci_read( pcidriver_privdata_t *privdata, pci_cfg_cmd *pci_cmd ); -int pcidriver_pci_write( pcidriver_privdata_t *privdata, pci_cfg_cmd *pci_cmd ); -int pcidriver_pci_info( pcidriver_privdata_t *privdata, pcilib_board_info_t *pci_info ); - -int pcidriver_mmap_pci( pcidriver_privdata_t *privdata, struct vm_area_struct *vmap , int bar ); -int pcidriver_mmap_kmem( pcidriver_privdata_t *privdata, struct vm_area_struct *vmap ); - -/*************************************************************************/ -/* Static data */ -/* Hold the allocated major & minor numbers */ -static dev_t pcidriver_devt; - -/* Number of devices allocated */ -static atomic_t pcidriver_deviceCount; - -/* Private data for probed devices */ -static pcidriver_privdata_t* pcidriver_privdata[MAXDEVICES]; - -/* Sysfs attributes */ -static DEVICE_ATTR(mmap_mode, 0664, pcidriver_show_mmap_mode, pcidriver_store_mmap_mode); -static DEVICE_ATTR(mmap_area, 0664, pcidriver_show_mmap_area, pcidriver_store_mmap_area); -static DEVICE_ATTR(kmem_count, S_IRUGO, pcidriver_show_kmem_count, NULL); -static DEVICE_ATTR(kbuffers, S_IRUGO, pcidriver_show_kbuffers, NULL); -static DEVICE_ATTR(kmem_alloc, 0220, NULL, pcidriver_store_kmem_alloc); -static DEVICE_ATTR(kmem_free, 0220, NULL, pcidriver_store_kmem_free); -static DEVICE_ATTR(umappings, S_IRUGO, pcidriver_show_umappings, NULL); -static DEVICE_ATTR(umem_unmap, 0220, NULL, pcidriver_store_umem_unmap); - -#ifdef ENABLE_IRQ -static DEVICE_ATTR(irq_count, S_IRUGO, pcidriver_show_irq_count, NULL); -static DEVICE_ATTR(irq_queues, S_IRUGO, pcidriver_show_irq_queues, NULL); -#endif - -#endif +#endif /* _PCIDRIVER_BASE_H */ diff --git a/driver/common.h b/driver/common.h deleted file mode 100644 index 48b2769..0000000 --- a/driver/common.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef _PCIDRIVER_COMMON_H -#define _PCIDRIVER_COMMON_H - -#include "../pcilib/kmem.h" -/*************************************************************************/ -/* Private data types and structures */ - - -/* Define an entry in the kmem list (this list is per device) */ -/* This list keeps references to the allocated kernel buffers */ -typedef struct { - int id; - enum dma_data_direction direction; - - struct list_head list; - dma_addr_t dma_handle; - unsigned long cpua; - unsigned long size; - unsigned long type; - unsigned long align; - - unsigned long use; - unsigned long item; - - spinlock_t lock; - unsigned long mode; - unsigned long refs; - - struct class_device_attribute sysfs_attr; /* initialized when adding the entry */ -} pcidriver_kmem_entry_t; - -/* Define an entry in the umem list (this list is per device) */ -/* This list keeps references to the SG lists for each mapped userspace region */ -typedef struct { - int id; - struct list_head list; - unsigned int nr_pages; /* number of pages for this user memeory area */ - struct page **pages; /* list of pointers to the pages */ - unsigned int nents; /* actual entries in the scatter/gatter list (NOT nents for the map function, but the result) */ - struct scatterlist *sg; /* list of sg entries */ - struct class_device_attribute sysfs_attr; /* initialized when adding the entry */ -} pcidriver_umem_entry_t; - -/* Hold the driver private data */ -typedef struct { - int devid; /* the device id */ - dev_t devno; /* device number (major and minor) */ - struct pci_dev *pdev; /* PCI device */ - struct class_device *class_dev; /* Class device */ - struct cdev cdev; /* char device struct */ - int mmap_mode; /* current mmap mode */ - int mmap_area; /* current PCI mmap area */ - -#ifdef ENABLE_IRQ - int irq_enabled; /* Non-zero if IRQ is enabled */ - int irq_count; /* Just an IRQ counter */ - - wait_queue_head_t irq_queues[ PCIDRIVER_INT_MAXSOURCES ]; /* One queue per interrupt source */ - atomic_t irq_outstanding[ PCIDRIVER_INT_MAXSOURCES ]; /* Outstanding interrupts per queue */ - volatile unsigned int *bars_kmapped[6]; /* PCI BARs mmapped in kernel space */ -#endif - - spinlock_t kmemlist_lock; /* Spinlock to lock kmem list operations */ - struct list_head kmem_list; /* List of 'kmem_list_entry's associated with this device */ - pcidriver_kmem_entry_t *kmem_last_sync; /* Last accessed kmem entry */ - atomic_t kmem_count; /* id for next kmem entry */ - - int kmem_cur_id; /* Currently selected kmem buffer, for mmap */ - - spinlock_t umemlist_lock; /* Spinlock to lock umem list operations */ - struct list_head umem_list; /* List of 'umem_list_entry's associated with this device */ - atomic_t umem_count; /* id for next umem entry */ - - int msi_mode; /* Flag specifying if interrupt have been initialized in MSI mode */ - atomic_t refs; /* Reference counter */ -} pcidriver_privdata_t; - - -void pcidriver_module_get(pcidriver_privdata_t *privdata); -void pcidriver_module_put(pcidriver_privdata_t *privdata); - -pcidriver_privdata_t *pcidriver_get_privdata(int devid); -void pcidriver_put_privdata(pcidriver_privdata_t *privdata); - - -/*************************************************************************/ -/* Some nice defines that make code more readable */ -/* This is to print nice info in the log */ - -#ifdef DEBUG -#define mod_info( args... ) \ - do { printk( KERN_INFO "%s - %s : ", MODNAME , __FUNCTION__ );\ - printk( args ); } while(0) -#define mod_info_dbg( args... ) \ - do { printk( KERN_INFO "%s - %s : ", MODNAME , __FUNCTION__ );\ - printk( args ); } while(0) -#else -#define mod_info( args... ) \ - do { printk( KERN_INFO "%s: ", MODNAME );\ - printk( args ); } while(0) -#define mod_info_dbg( args... ) -#endif - -#define mod_crit( args... ) \ - do { printk( KERN_CRIT "%s: ", MODNAME );\ - printk( args ); } while(0) - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -#endif diff --git a/driver/compat.c b/driver/compat.c deleted file mode 100644 index f28f527..0000000 --- a/driver/compat.c +++ /dev/null @@ -1,25 +0,0 @@ -#include - -int pcidriver_pcie_get_mps(struct pci_dev *dev) -{ - u16 ctl; - - pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl); - - return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); -} - -int pcidriver_pcie_set_mps(struct pci_dev *dev, int mps) -{ - u16 v; - - if (mps < 128 || mps > 4096 || !is_power_of_2(mps)) - return -EINVAL; - - v = ffs(mps) - 8; - if (v > dev->pcie_mpss) - return -EINVAL; - v <<= 5; - - return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, v); -} diff --git a/driver/compat.h b/driver/compat.h index a8a2cf0..24cc5a9 100644 --- a/driver/compat.h +++ b/driver/compat.h @@ -37,47 +37,4 @@ # define __devinitdata #endif -#define compat_lock_page __set_page_locked -#define compat_unlock_page __clear_page_locked - - -#define class_device_attribute device_attribute -#define CLASS_DEVICE_ATTR DEVICE_ATTR -#define class_device device -#define class_data dev -#define class_device_create(type, parent, devno, devpointer, nameformat, minor, privdata) \ - device_create(type, parent, devno, privdata, nameformat, minor) -#define class_device_create_file device_create_file -#define class_device_remove_file device_remove_file -#define class_device_destroy device_destroy -#define DEVICE_ATTR_COMPAT struct device_attribute *attr, -#define class_set_devdata dev_set_drvdata - -#define sysfs_attr_def_name(name) dev_attr_##name -#define sysfs_attr_def_pointer privdata->class_dev -#define SYSFS_GET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, char *buf) -#define SYSFS_SET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -#define SYSFS_GET_PRIVDATA dev_get_drvdata(dev) - -#define class_compat class - - -#define IRQ_HANDLER_FUNC(name) irqreturn_t name(int irq, void *dev_id) - -#define request_irq(irq, irq_handler, modname, privdata) request_irq(irq, irq_handler, IRQF_SHARED, modname, privdata) - - -#define io_remap_pfn_range_compat(vmap, vm_start, bar_addr, bar_length, vm_page_prot) \ - io_remap_pfn_range(vmap, vm_start, (bar_addr >> PAGE_SHIFT), bar_length, vm_page_prot) - -#define remap_pfn_range_compat(vmap, vm_start, bar_addr, bar_length, vm_page_prot) \ - remap_pfn_range(vmap, vm_start, (bar_addr >> PAGE_SHIFT), bar_length, vm_page_prot) - -#define remap_pfn_range_cpua_compat(vmap, vm_start, cpua, size, vm_page_prot) \ - remap_pfn_range(vmap, vm_start, page_to_pfn(virt_to_page((void*)cpua)), size, vm_page_prot) - - -int pcidriver_pcie_get_mps(struct pci_dev *dev); -int pcidriver_pcie_set_mps(struct pci_dev *dev, int mps); - #endif diff --git a/driver/config.h b/driver/config.h index c217afd..abf8011 100644 --- a/driver/config.h +++ b/driver/config.h @@ -1,6 +1,5 @@ -/*******************************/ -/* Configuration of the driver */ -/*******************************/ +#ifndef _PCIDRIVER_CONFIG_H +#define _PCIDRIVER_CONFIG_H /* Debug messages */ //#define DEBUG @@ -8,16 +7,29 @@ /* Enable/disable IRQ handling */ #define ENABLE_IRQ -/* The name of the module */ -#define MODNAME "pciDriver" +/* Maximum number of interrupt sources */ +#define PCIDRIVER_INT_MAXSOURCES 16 + +/* Maximum number of devices*/ +#define MAXDEVICES 4 -/* Major number is allocated dynamically */ /* Minor number */ -#define MINORNR 0 +#define MINORNR 0 + +/* The name of the module */ +#define MODNAME "pciDriver" /* Node name of the char device */ -#define NODENAME "fpga" -#define NODENAMEFMT "fpga%d" +#define NODENAME "fpga" +#define NODENAMEFMT "fpga%d" -/* Maximum number of devices*/ -#define MAXDEVICES 4 +/* Identifies the PCI-E Xilinx ML605 */ +#define PCIE_XILINX_VENDOR_ID 0x10ee +#define PCIE_ML605_DEVICE_ID 0x6024 + +/* Identifies the PCI-E IPE Hardware */ +#define PCIE_IPECAMERA_DEVICE_ID 0x6081 +#define PCIE_KAPTURE_DEVICE_ID 0x6028 + + +#endif /* _PCIDRIVER_CONFIG_H */ diff --git a/driver/debug.h b/driver/debug.h new file mode 100644 index 0000000..b30e81d --- /dev/null +++ b/driver/debug.h @@ -0,0 +1,25 @@ +#ifndef _PCIDRIVER_DEBUG_H +#define _PCIDRIVER_DEBUG_H + +#include "config.h" + +#ifdef DEBUG +#define mod_info( args... ) \ + do { printk( KERN_INFO "%s - %s : ", MODNAME , __FUNCTION__ );\ + printk( args ); } while(0) +#define mod_info_dbg( args... ) \ + do { printk( KERN_INFO "%s - %s : ", MODNAME , __FUNCTION__ );\ + printk( args ); } while(0) +#else +#define mod_info( args... ) \ + do { printk( KERN_INFO "%s: ", MODNAME );\ + printk( args ); } while(0) +#define mod_info_dbg( args... ) +#endif + +#define mod_crit( args... ) \ + do { printk( KERN_CRIT "%s: ", MODNAME );\ + printk( args ); } while(0) + + +#endif /* _PCIDRIVER_DEBUG_H */ diff --git a/driver/dev.c b/driver/dev.c new file mode 100644 index 0000000..2a047a4 --- /dev/null +++ b/driver/dev.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base.h" + + +/** + * + * Called when an application open()s a /dev/fpga*, attaches the private data + * with the file pointer. + * + */ +static int pcidriver_open(struct inode *inode, struct file *filp) +{ + pcidriver_privdata_t *privdata; + + /* Set the private data area for the file */ + privdata = container_of( inode->i_cdev, pcidriver_privdata_t, cdev); + filp->private_data = privdata; + + pcidriver_module_get(privdata); + + return 0; +} + +/** + * + * Called when the application close()s the file descriptor. Does nothing at + * the moment. + * + */ +static int pcidriver_release(struct inode *inode, struct file *filp) +{ + pcidriver_privdata_t *privdata; + + /* Get the private data area */ + privdata = filp->private_data; + + pcidriver_module_put(privdata); + + return 0; +} + + +/*************************************************************************/ +/* Internal driver functions */ +static int pcidriver_mmap_bar(pcidriver_privdata_t *privdata, struct vm_area_struct *vmap, int bar) +{ +#ifdef PCIDRIVER_DUMMY_DEVICE + return -ENXIO; +#else /* PCIDRIVER_DUMMY_DEVICE */ + int ret = 0; + unsigned long bar_addr; + unsigned long bar_length, vma_size; + unsigned long bar_flags; + + mod_info_dbg("Entering mmap_pci\n"); + + + /* Get info of the BAR to be mapped */ + bar_addr = pci_resource_start(privdata->pdev, bar); + bar_length = pci_resource_len(privdata->pdev, bar); + bar_flags = pci_resource_flags(privdata->pdev, bar); + + /* Check sizes */ + vma_size = (vmap->vm_end - vmap->vm_start); + + if ((vma_size != bar_length) && + ((bar_length < PAGE_SIZE) && (vma_size != PAGE_SIZE))) { + mod_info( "mmap size is not correct! bar: %lu - vma: %lu\n", bar_length, vma_size ); + return -EINVAL; + } + + if (bar_flags & IORESOURCE_IO) { + /* Unlikely case, we will mmap a IO region */ + + /* IO regions are never cacheable */ + vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); + + /* Map the BAR */ + ret = io_remap_pfn_range(vmap, vmap->vm_start, (bar_addr >> PAGE_SHIFT), bar_length, vmap->vm_page_prot); + } else { + /* Normal case, mmap a memory region */ + + /* Ensure this VMA is non-cached, if it is not flaged as prefetchable. + * If it is prefetchable, caching is allowed and will give better performance. + * This should be set properly by the BIOS, but we want to be sure. */ + /* adapted from drivers/char/mem.c, mmap function. */ + + /* Setting noncached disables MTRR registers, and we want to use them. + * So we take this code out. This can lead to caching problems if and only if + * the System BIOS set something wrong. Check LDDv3, page 425. + */ + +// if (!(bar_flags & IORESOURCE_PREFETCH)) +// vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); + + + /* Map the BAR */ + ret = remap_pfn_range(vmap, vmap->vm_start, (bar_addr >> PAGE_SHIFT), bar_length, vmap->vm_page_prot); + } + + if (ret) { + mod_info("remap_pfn_range failed\n"); + return -EAGAIN; + } + + return 0; /* success */ +#endif /* PCIDRIVER_DUMMY_DEVICE */ +} + +/** + * + * This function is the entry point for mmap() and calls either pcidriver_mmap_bar + * or pcidriver_mmap_kmem + * + * @see pcidriver_mmap_bar + * @see pcidriver_mmap_kmem + * + */ +static int pcidriver_mmap(struct file *filp, struct vm_area_struct *vma) +{ + pcidriver_privdata_t *privdata; + int ret = 0, bar; + + mod_info_dbg("Entering mmap\n"); + + /* Get the private data area */ + privdata = filp->private_data; + + /* Check the current mmap mode */ + switch (privdata->mmap_mode) { + case PCIDRIVER_MMAP_PCI: + bar = privdata->mmap_area; + if ((bar < 0)||(bar > 5)) { + mod_info("Attempted to mmap a PCI area with the wrong mmap_area value: %d\n",privdata->mmap_area); + return -EINVAL; + } + ret = pcidriver_mmap_bar(privdata, vma, bar); + break; + case PCIDRIVER_MMAP_KMEM: + ret = pcidriver_mmap_kmem(privdata, vma); + break; + default: + mod_info( "Invalid mmap_mode value (%d)\n",privdata->mmap_mode ); + return -EINVAL; /* Invalid parameter (mode) */ + } + + return ret; +} + +static struct file_operations pcidriver_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = pcidriver_ioctl, + .mmap = pcidriver_mmap, + .open = pcidriver_open, + .release = pcidriver_release, +}; + +const struct file_operations *pcidriver_get_fops(void) +{ + return &pcidriver_fops; +} + + +void pcidriver_module_get(pcidriver_privdata_t *privdata) { + atomic_inc(&(privdata->refs)); +// mod_info("Ref: %i\n", atomic_read(&(privdata->refs))); +} + +void pcidriver_module_put(pcidriver_privdata_t *privdata) { + if (atomic_add_negative(-1, &(privdata->refs))) { + atomic_inc(&(privdata->refs)); + mod_info("Reference counting error..."); + } else { +// mod_info("Unref: %i\n", atomic_read(&(privdata->refs))); + } +} diff --git a/driver/dev.h b/driver/dev.h new file mode 100644 index 0000000..5e95365 --- /dev/null +++ b/driver/dev.h @@ -0,0 +1,52 @@ +#ifndef _PCIDRIVER_DEV_H +#define _PCIDRIVER_DEV_H + +typedef struct pcidriver_privdata_s pcidriver_privdata_t; + +#include "kmem.h" +#include "umem.h" + + +/* Hold the driver private data */ +struct pcidriver_privdata_s { + int devid; /* the device id */ + dev_t devno; /* device number (major and minor) */ + struct pci_dev *pdev; /* PCI device */ + struct device *class_dev; /* Class device */ + struct cdev cdev; /* char device struct */ + int mmap_mode; /* current mmap mode */ + int mmap_area; /* current PCI mmap area */ + +#ifdef ENABLE_IRQ + int irq_enabled; /* Non-zero if IRQ is enabled */ + int irq_count; /* Just an IRQ counter */ + + wait_queue_head_t irq_queues[ PCIDRIVER_INT_MAXSOURCES ]; /* One queue per interrupt source */ + atomic_t irq_outstanding[ PCIDRIVER_INT_MAXSOURCES ]; /* Outstanding interrupts per queue */ + volatile unsigned int *bars_kmapped[6]; /* PCI BARs mmapped in kernel space */ +#endif + + spinlock_t kmemlist_lock; /* Spinlock to lock kmem list operations */ + struct list_head kmem_list; /* List of 'kmem_list_entry's associated with this device */ + pcidriver_kmem_entry_t *kmem_last_sync; /* Last accessed kmem entry */ + atomic_t kmem_count; /* id for next kmem entry */ + + int kmem_cur_id; /* Currently selected kmem buffer, for mmap */ + + spinlock_t umemlist_lock; /* Spinlock to lock umem list operations */ + struct list_head umem_list; /* List of 'umem_list_entry's associated with this device */ + atomic_t umem_count; /* id for next umem entry */ + + int msi_mode; /* Flag specifying if interrupt have been initialized in MSI mode */ + atomic_t refs; /* Reference counter */ +}; + +const struct file_operations *pcidriver_get_fops(void); + +void pcidriver_module_get(pcidriver_privdata_t *privdata); +void pcidriver_module_put(pcidriver_privdata_t *privdata); + +long pcidriver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +#endif /* _PCIDRIVER_DEV_H */ + diff --git a/driver/int.c b/driver/int.c index f78c6ec..4bbfb26 100644 --- a/driver/int.c +++ b/driver/int.c @@ -7,36 +7,6 @@ * */ -/* - * Change History: - * - * $Log: not supported by cvs2svn $ - * Revision 1.7 2008-01-11 10:18:28 marcus - * Modified interrupt mechanism. Added atomic functions and queues, to address race conditions. Removed unused interrupt code. - * - * Revision 1.6 2007-11-04 20:58:22 marcus - * Added interrupt generator acknowledge. - * Fixed wrong operator. - * - * Revision 1.5 2007-10-31 15:42:21 marcus - * Added IG ack for testing, may be removed later. - * - * Revision 1.4 2007-07-17 13:15:56 marcus - * Removed Tasklets. - * Using newest map for the ABB interrupts. - * - * Revision 1.3 2007-07-05 15:30:30 marcus - * Added support for both register maps of the ABB. - * - * Revision 1.2 2007-05-29 07:50:18 marcus - * Split code into 2 files. May get merged in the future again.... - * - * Revision 1.1 2007/03/01 16:57:43 marcus - * Divided driver file to ease the interrupt hooks for the user of the driver. - * Modified Makefile accordingly. - * - */ - #include #include #include @@ -48,41 +18,47 @@ #include #include -#include "config.h" +#include "base.h" -#include "compat.h" - -#include "pciDriver.h" +/** + * + * Acknowledges the receival of an interrupt to the card. + * + * @returns true if the card was acknowledget + * @returns false if the interrupt was not for one of our cards + * + * @see check_acknowlegde_channel + * + */ +static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata) +{ + int channel = 0; -#include "common.h" + atomic_inc(&(privdata->irq_outstanding[channel])); + wake_up_interruptible(&(privdata->irq_queues[channel])); -#include "int.h" + return true; +} -/* - * The ID between IRQ_SOURCE in irq_outstanding and the actual source is arbitrary. - * Therefore, be careful when communicating with multiple implementations. +/** + * + * Handles IRQs. At the moment, this acknowledges the card that this IRQ + * was received and then increases the driver's IRQ counter. + * + * @see pcidriver_irq_acknowledge + * */ +static irqreturn_t pcidriver_irq_handler(int irq, void *dev_id) +{ + pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id; -/* IRQ_SOURCES */ -#define ABB_IRQ_CH0 0 -#define ABB_IRQ_CH1 1 -#define ABB_IRQ_IG 2 - -/* See ABB user’s guide, register definitions (3.1) */ -#define ABB_INT_ENABLE (0x0010 >> 2) -#define ABB_INT_STAT (0x0008 >> 2) + if (!pcidriver_irq_acknowledge(privdata)) + return IRQ_NONE; -#define ABB_INT_CH1_TIMEOUT (1 << 4) -#define ABB_INT_CH0_TIMEOUT (1 << 5) -#define ABB_INT_IG (1 << 2) -#define ABB_INT_CH0 (1 << 1) /* downstream */ -#define ABB_INT_CH1 (1) /* upstream */ + privdata->irq_count++; + return IRQ_HANDLED; +} -#define ABB_CH0_CTRL (108 >> 2) -#define ABB_CH1_CTRL (72 >> 2) -#define ABB_CH_RESET (0x0201000A) -#define ABB_IG_CTRL (0x0080 >> 2) -#define ABB_IG_ACK (0x00F0) /** * @@ -167,7 +143,7 @@ int pcidriver_probe_irq(pcidriver_privdata_t *privdata) privdata->msi_mode = 1; /* register interrupt handler */ - if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, MODNAME, privdata)) != 0) { + if ((err = request_irq(privdata->pdev->irq, pcidriver_irq_handler, IRQF_SHARED, MODNAME, privdata)) != 0) { mod_info("Error registering the interrupt handler. Disabling interrupts for this device\n"); return 0; } @@ -215,44 +191,3 @@ void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata) } } -/** - * - * Acknowledges the receival of an interrupt to the card. - * - * @returns true if the card was acknowledget - * @returns false if the interrupt was not for one of our cards - * - * @see check_acknowlegde_channel - * - */ -static bool pcidriver_irq_acknowledge(pcidriver_privdata_t *privdata) -{ - int channel = 0; -// volatile unsigned int *bar; -// bar = privdata->bars_kmapped[0]; -// mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]); - - atomic_inc(&(privdata->irq_outstanding[channel])); - wake_up_interruptible(&(privdata->irq_queues[channel])); - - return true; -} - -/** - * - * Handles IRQs. At the moment, this acknowledges the card that this IRQ - * was received and then increases the driver's IRQ counter. - * - * @see pcidriver_irq_acknowledge - * - */ -IRQ_HANDLER_FUNC(pcidriver_irq_handler) -{ - pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)dev_id; - - if (!pcidriver_irq_acknowledge(privdata)) - return IRQ_NONE; - - privdata->irq_count++; - return IRQ_HANDLED; -} diff --git a/driver/int.h b/driver/int.h index 4a834c7..42fa474 100644 --- a/driver/int.h +++ b/driver/int.h @@ -4,6 +4,5 @@ int pcidriver_probe_irq(pcidriver_privdata_t *privdata); void pcidriver_remove_irq(pcidriver_privdata_t *privdata); void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata); -IRQ_HANDLER_FUNC(pcidriver_irq_handler); -#endif +#endif /* _PCIDRIVER_INT_H */ diff --git a/driver/ioctl.c b/driver/ioctl.c index 4fef5f9..f852f2c 100644 --- a/driver/ioctl.c +++ b/driver/ioctl.c @@ -32,13 +32,7 @@ #include "../pcilib/version.h" -#include "config.h" /* Configuration for the driver */ -#include "compat.h" /* Compatibility functions/definitions */ -#include "pciDriver.h" /* External interface for the driver */ -#include "common.h" /* Internal definitions for all parts */ -#include "kmem.h" /* Internal definitions for kernel memory */ -#include "umem.h" /* Internal definitions for user space memory */ -#include "ioctl.h" /* Internal definitions for the ioctl part */ +#include "base.h" /** Declares a variable of the given type with the given name and copies it from userspace */ #define READ_FROM_USER(type, name) \ @@ -87,12 +81,7 @@ static int ioctl_mmap_area(pcidriver_privdata_t *privdata, unsigned long arg) } /** - * * Reads/writes a byte/word/dword of the device's PCI config. - * - * @see pcidriver_pci_read - * @see pcidriver_pci_write - * */ static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned int cmd, unsigned long arg) { @@ -103,7 +92,7 @@ static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned READ_FROM_USER(pci_cfg_cmd, pci_cmd); if (cmd == PCIDRIVER_IOC_PCI_CFG_RD) { - switch (pci_cmd.size) { + switch (pci_cmd.size) { case PCIDRIVER_PCI_CFG_SZ_BYTE: ret = pci_read_config_byte( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.byte) ); break; @@ -115,9 +104,9 @@ static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned break; default: return -EINVAL; /* Wrong size setting */ - } + } } else { - switch (pci_cmd.size) { + switch (pci_cmd.size) { case PCIDRIVER_PCI_CFG_SZ_BYTE: ret = pci_write_config_byte( privdata->pdev, pci_cmd.addr, pci_cmd.val.byte ); break; @@ -130,7 +119,7 @@ static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned default: return -EINVAL; /* Wrong size setting */ break; - } + } } WRITE_TO_USER(pci_cfg_cmd, pci_cmd); @@ -140,11 +129,7 @@ static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned } /** - * * Gets the PCI information for the device. - * - * @see pcidriver_pci_info - * */ static int ioctl_pci_info(pcidriver_privdata_t *privdata, unsigned long arg) { diff --git a/driver/ioctl.h b/driver/ioctl.h index e989f95..a102092 100644 --- a/driver/ioctl.h +++ b/driver/ioctl.h @@ -1 +1,178 @@ -long pcidriver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#ifndef _PCIDRIVER_IOCTL_H_ +#define _PCIDRIVER_IOCTL_H_ + +#include + +#define PCIDRIVER_INTERFACE_VERSION 2 /**< Driver API version, only the pcilib with the same driver interface version is allowed */ + +/* Possible values for ioctl commands */ + +/* PCI mmap areas */ +#define PCIDRIVER_BAR0 0 +#define PCIDRIVER_BAR1 1 +#define PCIDRIVER_BAR2 2 +#define PCIDRIVER_BAR3 3 +#define PCIDRIVER_BAR4 4 +#define PCIDRIVER_BAR5 5 + +/* mmap mode of the device */ +#define PCIDRIVER_MMAP_PCI 0 +#define PCIDRIVER_MMAP_KMEM 1 + +/* Direction of a DMA operation */ +#define PCIDRIVER_DMA_BIDIRECTIONAL 0 +#define PCIDRIVER_DMA_TODEVICE 1 // PCILIB_KMEM_SYNC_TODEVICE +#define PCIDRIVER_DMA_FROMDEVICE 2 // PCILIB_KMEM_SYNC_FROMDEVICE + +/* Possible sizes in a PCI command */ +#define PCIDRIVER_PCI_CFG_SZ_BYTE 1 +#define PCIDRIVER_PCI_CFG_SZ_WORD 2 +#define PCIDRIVER_PCI_CFG_SZ_DWORD 3 + +/* Possible types of SG lists */ +#define PCIDRIVER_SG_NONMERGED 0 +#define PCIDRIVER_SG_MERGED 1 + +#define KMEM_REF_HW 0x80000000 /**< Special reference to indicate hardware access */ +#define KMEM_REF_COUNT 0x0FFFFFFF /**< Mask of reference counter (mmap/munmap), couting in mmaped memory pages */ + +#define KMEM_MODE_REUSABLE 0x80000000 /**< Indicates reusable buffer */ +#define KMEM_MODE_EXCLUSIVE 0x40000000 /**< Only a single process is allowed to mmap the buffer */ +#define KMEM_MODE_PERSISTENT 0x20000000 /**< Persistent mode instructs kmem_free to preserve buffer in memory */ +#define KMEM_MODE_COUNT 0x0FFFFFFF /**< Mask of reuse counter (alloc/free) */ + +#define KMEM_FLAG_REUSE PCILIB_KMEM_FLAG_REUSE /**< Try to reuse existing buffer with the same use & item */ +#define KMEM_FLAG_EXCLUSIVE PCILIB_KMEM_FLAG_EXCLUSIVE /**< Allow only a single application accessing a specified use & item */ +#define KMEM_FLAG_PERSISTENT PCILIB_KMEM_FLAG_PERSISTENT /**< Sets persistent mode */ +#define KMEM_FLAG_HW PCILIB_KMEM_FLAG_HARDWARE /**< The buffer may be accessed by hardware, the hardware access will not occur any more if passed to _free function */ +#define KMEM_FLAG_FORCE PCILIB_KMEM_FLAG_FORCE /**< Force memory cleanup even if references are present */ +#define KMEM_FLAG_MASS PCILIB_KMEM_FLAG_MASS /**< Apply to all buffers of selected use */ +#define KMEM_FLAG_TRY PCILIB_KMEM_FLAG_TRY /**< Do not allocate buffers, try to reuse and fail if not possible */ + +#define KMEM_FLAG_REUSED PCILIB_KMEM_FLAG_REUSE /**< Indicates if buffer with specified use & item was already allocated and reused */ +#define KMEM_FLAG_REUSED_PERSISTENT PCILIB_KMEM_FLAG_PERSISTENT /**< Indicates that reused buffer was persistent before the call */ +#define KMEM_FLAG_REUSED_HW PCILIB_KMEM_FLAG_HARDWARE /**< Indicates that reused buffer had a HW reference before the call */ + +/* Types */ + +typedef struct { + unsigned long version; /**< pcilib version */ + unsigned long interface; /**< driver interface version */ + unsigned long ioctls; /**< number of supporterd ioctls */ + unsigned long reserved[5]; /**< reserved for the future use */ +} pcilib_driver_version_t; + +typedef struct { + int iommu; /**< Specifies if IOMMU is enabled or disabled */ + int mps; /**< PCIe maximum payload size */ + int readrq; /**< PCIe read request size */ + unsigned long dma_mask; /**< DMA mask */ +} pcilib_device_state_t; + +typedef struct { + unsigned short vendor_id; + unsigned short device_id; + unsigned short bus; + unsigned short slot; + unsigned short func; + unsigned short devfn; + unsigned char interrupt_pin; + unsigned char interrupt_line; + unsigned int irq; + unsigned long bar_start[6]; + unsigned long bar_length[6]; + unsigned long bar_flags[6]; +} pcilib_board_info_t; + +typedef struct { + unsigned long type; + unsigned long pa; + unsigned long ba; + unsigned long size; + unsigned long align; + unsigned long use; + unsigned long item; + int flags; + int handle_id; +} kmem_handle_t; + +typedef struct { + unsigned long addr; + unsigned long size; +} umem_sgentry_t; + +typedef struct { + int handle_id; + int type; + int nents; + umem_sgentry_t *sg; +} umem_sglist_t; + +typedef struct { + unsigned long vma; + unsigned long size; + int handle_id; + int dir; +} umem_handle_t; + +typedef struct { + kmem_handle_t handle; + int dir; +} kmem_sync_t; + +typedef struct { + unsigned long count; + unsigned long timeout; // microseconds + unsigned int source; +} interrupt_wait_t; + +typedef struct { + int size; + int addr; + union { + unsigned char byte; + unsigned short word; + unsigned int dword; /* not strict C, but if not can have problems */ + } val; +} pci_cfg_cmd; + +/* ioctl interface */ +/* See documentation for a detailed usage explanation */ + +/* + * one of the problems of ioctl, is that requires a type definition. + * This type is only 8-bits wide, and half-documented in + * /Documentation/ioctl-number.txt. + * previous SHL -> 'S' definition, conflicts with several devices, + * so I changed it to be pci -> 'p', in the range 0xA0-BF + */ +#define PCIDRIVER_IOC_MAGIC 'p' +#define PCIDRIVER_IOC_BASE 0xA0 + +#define PCIDRIVER_IOC_MMAP_MODE _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 0 ) +#define PCIDRIVER_IOC_MMAP_AREA _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 1 ) +#define PCIDRIVER_IOC_KMEM_ALLOC _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 2, kmem_handle_t * ) +#define PCIDRIVER_IOC_KMEM_FREE _IOW ( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 3, kmem_handle_t * ) +#define PCIDRIVER_IOC_KMEM_SYNC _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 4, kmem_sync_t * ) +#define PCIDRIVER_IOC_UMEM_SGMAP _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 5, umem_handle_t * ) +#define PCIDRIVER_IOC_UMEM_SGUNMAP _IOW( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 6, umem_handle_t * ) +#define PCIDRIVER_IOC_UMEM_SGGET _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 7, umem_sglist_t * ) +#define PCIDRIVER_IOC_UMEM_SYNC _IOW( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 8, umem_handle_t * ) +#define PCIDRIVER_IOC_WAITI _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 9 ) + +/* And now, the methods to access the PCI configuration area */ +#define PCIDRIVER_IOC_PCI_CFG_RD _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 10, pci_cfg_cmd * ) +#define PCIDRIVER_IOC_PCI_CFG_WR _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 11, pci_cfg_cmd * ) +#define PCIDRIVER_IOC_PCI_INFO _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 12, pcilib_board_info_t * ) + +/* Clear interrupt queues */ +#define PCIDRIVER_IOC_CLEAR_IOQ _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 13 ) + +#define PCIDRIVER_IOC_VERSION _IOR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 14, pcilib_driver_version_t * ) +#define PCIDRIVER_IOC_DEVICE_STATE _IOR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 15, pcilib_device_state_t * ) +#define PCIDRIVER_IOC_DMA_MASK _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 16) +#define PCIDRIVER_IOC_MPS _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 17) + +#define PCIDRIVER_IOC_MAX 17 + +#endif /* _PCIDRIVER_IOCTL_H */ diff --git a/driver/kmem.c b/driver/kmem.c index e3b0a97..47d1929 100644 --- a/driver/kmem.c +++ b/driver/kmem.c @@ -17,12 +17,7 @@ #include #include -#include "config.h" /* compile-time configuration */ -#include "compat.h" /* compatibility definitions for older linux */ -#include "pciDriver.h" /* external interface for the driver */ -#include "common.h" /* internal definitions for all parts */ -#include "kmem.h" /* prototypes for kernel memory */ -#include "sysfs.h" /* prototypes for sysfs */ +#include "base.h" /** @@ -628,9 +623,9 @@ int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *v page_to_pfn(virt_to_page((void*)kmem_entry->cpua))); if ((kmem_entry->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) { - ret = remap_pfn_range_compat(vma, vma->vm_start, kmem_entry->dma_handle, (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); + ret = remap_pfn_range(vma, vma->vm_start, (kmem_entry->dma_handle >> PAGE_SHIFT), (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); } else { - ret = remap_pfn_range_cpua_compat(vma, vma->vm_start, kmem_entry->cpua, (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); + ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page((void*)(kmem_entry->cpua))), (vma_size < kmem_entry->size)?vma_size:kmem_entry->size, vma->vm_page_prot); } if (ret) { diff --git a/driver/kmem.h b/driver/kmem.h index 503620e..e793bd6 100644 --- a/driver/kmem.h +++ b/driver/kmem.h @@ -1,3 +1,35 @@ +#ifndef _PCIDRIVER_KMEM_H +#define _PCIDRIVER_KMEM_H + +#include + +#include "../pcilib/kmem.h" +#include "ioctl.h" + +/* Define an entry in the kmem list (this list is per device) */ +/* This list keeps references to the allocated kernel buffers */ +typedef struct { + int id; + enum dma_data_direction direction; + + struct list_head list; + dma_addr_t dma_handle; + unsigned long cpua; + unsigned long size; + unsigned long type; + unsigned long align; + + unsigned long use; + unsigned long item; + + spinlock_t lock; + unsigned long mode; + unsigned long refs; + + struct device_attribute sysfs_attr; /* initialized when adding the entry */ +} pcidriver_kmem_entry_t; + + int pcidriver_kmem_alloc( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle ); int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle ); int pcidriver_kmem_sync_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry, int direction ); @@ -7,3 +39,7 @@ pcidriver_kmem_entry_t *pcidriver_kmem_find_entry( pcidriver_privdata_t *privdat pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id( pcidriver_privdata_t *privdata, int id ); pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_use(pcidriver_privdata_t *privdata, unsigned long use, unsigned long item); int pcidriver_kmem_free_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry ); + +int pcidriver_mmap_kmem( pcidriver_privdata_t *privdata, struct vm_area_struct *vmap ); + +#endif /* _PCIDRIVER_KMEM_H */ diff --git a/driver/pciDriver.h b/driver/pciDriver.h deleted file mode 100644 index 3a231bd..0000000 --- a/driver/pciDriver.h +++ /dev/null @@ -1,191 +0,0 @@ -#ifndef PCIDRIVER_H_ -#define PCIDRIVER_H_ - - -#include - -#define PCIDRIVER_INTERFACE_VERSION 2 /**< Driver API version, only the pcilib with the same driver interface version is allowed */ - -/* Identifies the PCI-E Xilinx ML605 */ -#define PCIE_XILINX_VENDOR_ID 0x10ee -#define PCIE_ML605_DEVICE_ID 0x6024 - -/* Identifies the PCI-E IPE Hardware */ -#define PCIE_IPECAMERA_DEVICE_ID 0x6081 -#define PCIE_KAPTURE_DEVICE_ID 0x6028 - - -/* Possible values for ioctl commands */ - -/* PCI mmap areas */ -#define PCIDRIVER_BAR0 0 -#define PCIDRIVER_BAR1 1 -#define PCIDRIVER_BAR2 2 -#define PCIDRIVER_BAR3 3 -#define PCIDRIVER_BAR4 4 -#define PCIDRIVER_BAR5 5 - -/* mmap mode of the device */ -#define PCIDRIVER_MMAP_PCI 0 -#define PCIDRIVER_MMAP_KMEM 1 - -/* Direction of a DMA operation */ -#define PCIDRIVER_DMA_BIDIRECTIONAL 0 -#define PCIDRIVER_DMA_TODEVICE 1//PCILIB_KMEM_SYNC_TODEVICE -#define PCIDRIVER_DMA_FROMDEVICE 2//PCILIB_KMEM_SYNC_FROMDEVICE - -/* Possible sizes in a PCI command */ -#define PCIDRIVER_PCI_CFG_SZ_BYTE 1 -#define PCIDRIVER_PCI_CFG_SZ_WORD 2 -#define PCIDRIVER_PCI_CFG_SZ_DWORD 3 - -/* Possible types of SG lists */ -#define PCIDRIVER_SG_NONMERGED 0 -#define PCIDRIVER_SG_MERGED 1 - -/* Maximum number of interrupt sources */ -#define PCIDRIVER_INT_MAXSOURCES 16 - -#define KMEM_REF_HW 0x80000000 /**< Special reference to indicate hardware access */ -#define KMEM_REF_COUNT 0x0FFFFFFF /**< Mask of reference counter (mmap/munmap), couting in mmaped memory pages */ - -#define KMEM_MODE_REUSABLE 0x80000000 /**< Indicates reusable buffer */ -#define KMEM_MODE_EXCLUSIVE 0x40000000 /**< Only a single process is allowed to mmap the buffer */ -#define KMEM_MODE_PERSISTENT 0x20000000 /**< Persistent mode instructs kmem_free to preserve buffer in memory */ -#define KMEM_MODE_COUNT 0x0FFFFFFF /**< Mask of reuse counter (alloc/free) */ - -#define KMEM_FLAG_REUSE PCILIB_KMEM_FLAG_REUSE /**< Try to reuse existing buffer with the same use & item */ -#define KMEM_FLAG_EXCLUSIVE PCILIB_KMEM_FLAG_EXCLUSIVE /**< Allow only a single application accessing a specified use & item */ -#define KMEM_FLAG_PERSISTENT PCILIB_KMEM_FLAG_PERSISTENT /**< Sets persistent mode */ -#define KMEM_FLAG_HW PCILIB_KMEM_FLAG_HARDWARE /**< The buffer may be accessed by hardware, the hardware access will not occur any more if passed to _free function */ -#define KMEM_FLAG_FORCE PCILIB_KMEM_FLAG_FORCE /**< Force memory cleanup even if references are present */ -#define KMEM_FLAG_MASS PCILIB_KMEM_FLAG_MASS /**< Apply to all buffers of selected use */ -#define KMEM_FLAG_TRY PCILIB_KMEM_FLAG_TRY /**< Do not allocate buffers, try to reuse and fail if not possible */ - -#define KMEM_FLAG_REUSED PCILIB_KMEM_FLAG_REUSE /**< Indicates if buffer with specified use & item was already allocated and reused */ -#define KMEM_FLAG_REUSED_PERSISTENT PCILIB_KMEM_FLAG_PERSISTENT /**< Indicates that reused buffer was persistent before the call */ -#define KMEM_FLAG_REUSED_HW PCILIB_KMEM_FLAG_HARDWARE /**< Indicates that reused buffer had a HW reference before the call */ - -/* Types */ - -typedef struct { - unsigned long version; /**< pcilib version */ - unsigned long interface; /**< driver interface version */ - unsigned long ioctls; /**< number of supporterd ioctls */ - unsigned long reserved[5]; /**< reserved for the future use */ -} pcilib_driver_version_t; - -typedef struct { - int iommu; /**< Specifies if IOMMU is enabled or disabled */ - int mps; /**< PCIe maximum payload size */ - int readrq; /**< PCIe read request size */ - unsigned long dma_mask; /**< DMA mask */ -} pcilib_device_state_t; - -typedef struct { - unsigned short vendor_id; - unsigned short device_id; - unsigned short bus; - unsigned short slot; - unsigned short func; - unsigned short devfn; - unsigned char interrupt_pin; - unsigned char interrupt_line; - unsigned int irq; - unsigned long bar_start[6]; - unsigned long bar_length[6]; - unsigned long bar_flags[6]; -} pcilib_board_info_t; - -typedef struct { - unsigned long type; - unsigned long pa; - unsigned long ba; - unsigned long size; - unsigned long align; - unsigned long use; - unsigned long item; - int flags; - int handle_id; -} kmem_handle_t; - -typedef struct { - unsigned long addr; - unsigned long size; -} umem_sgentry_t; - -typedef struct { - int handle_id; - int type; - int nents; - umem_sgentry_t *sg; -} umem_sglist_t; - -typedef struct { - unsigned long vma; - unsigned long size; - int handle_id; - int dir; -} umem_handle_t; - -typedef struct { - kmem_handle_t handle; - int dir; -} kmem_sync_t; - -typedef struct { - unsigned long count; - unsigned long timeout; // microseconds - unsigned int source; -} interrupt_wait_t; - -typedef struct { - int size; - int addr; - union { - unsigned char byte; - unsigned short word; - unsigned int dword; /* not strict C, but if not can have problems */ - } val; -} pci_cfg_cmd; - -/* ioctl interface */ -/* See documentation for a detailed usage explanation */ - -/* - * one of the problems of ioctl, is that requires a type definition. - * This type is only 8-bits wide, and half-documented in - * /Documentation/ioctl-number.txt. - * previous SHL -> 'S' definition, conflicts with several devices, - * so I changed it to be pci -> 'p', in the range 0xA0-BF - */ -#define PCIDRIVER_IOC_MAGIC 'p' -#define PCIDRIVER_IOC_BASE 0xA0 - -#define PCIDRIVER_IOC_MMAP_MODE _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 0 ) -#define PCIDRIVER_IOC_MMAP_AREA _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 1 ) -#define PCIDRIVER_IOC_KMEM_ALLOC _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 2, kmem_handle_t * ) -#define PCIDRIVER_IOC_KMEM_FREE _IOW ( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 3, kmem_handle_t * ) -#define PCIDRIVER_IOC_KMEM_SYNC _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 4, kmem_sync_t * ) -#define PCIDRIVER_IOC_UMEM_SGMAP _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 5, umem_handle_t * ) -#define PCIDRIVER_IOC_UMEM_SGUNMAP _IOW( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 6, umem_handle_t * ) -#define PCIDRIVER_IOC_UMEM_SGGET _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 7, umem_sglist_t * ) -#define PCIDRIVER_IOC_UMEM_SYNC _IOW( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 8, umem_handle_t * ) -#define PCIDRIVER_IOC_WAITI _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 9 ) - -/* And now, the methods to access the PCI configuration area */ -#define PCIDRIVER_IOC_PCI_CFG_RD _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 10, pci_cfg_cmd * ) -#define PCIDRIVER_IOC_PCI_CFG_WR _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 11, pci_cfg_cmd * ) -#define PCIDRIVER_IOC_PCI_INFO _IOWR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 12, pcilib_board_info_t * ) - -/* Clear interrupt queues */ -#define PCIDRIVER_IOC_CLEAR_IOQ _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 13 ) - -#define PCIDRIVER_IOC_VERSION _IOR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 14, pcilib_driver_version_t * ) -#define PCIDRIVER_IOC_DEVICE_STATE _IOR( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 15, pcilib_device_state_t * ) -#define PCIDRIVER_IOC_DMA_MASK _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 16) -#define PCIDRIVER_IOC_MPS _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 17) - -#define PCIDRIVER_IOC_MAX 17 - -#endif diff --git a/driver/pcibus.c b/driver/pcibus.c new file mode 100644 index 0000000..f28f527 --- /dev/null +++ b/driver/pcibus.c @@ -0,0 +1,25 @@ +#include + +int pcidriver_pcie_get_mps(struct pci_dev *dev) +{ + u16 ctl; + + pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl); + + return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5); +} + +int pcidriver_pcie_set_mps(struct pci_dev *dev, int mps) +{ + u16 v; + + if (mps < 128 || mps > 4096 || !is_power_of_2(mps)) + return -EINVAL; + + v = ffs(mps) - 8; + if (v > dev->pcie_mpss) + return -EINVAL; + v <<= 5; + + return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, v); +} diff --git a/driver/pcibus.h b/driver/pcibus.h new file mode 100644 index 0000000..8e977ec --- /dev/null +++ b/driver/pcibus.h @@ -0,0 +1,7 @@ +#ifndef _PCIDRIVER_PCIBUS_H +#define _PCIDRIVER_PCIBUS_H + +int pcidriver_pcie_get_mps(struct pci_dev *dev); +int pcidriver_pcie_set_mps(struct pci_dev *dev, int mps); + +#endif /* _PCIDRIVER_PCIBUS_H */ diff --git a/driver/pcidriver.h b/driver/pcidriver.h new file mode 100644 index 0000000..d64c80a --- /dev/null +++ b/driver/pcidriver.h @@ -0,0 +1,11 @@ +#ifndef _PCIDRIVER_H +#define _PCIDRIVER_H + +/** + * Evaluates if the supplied user-space address is actually BAR mapping. + * @param[in] address - the user-space address + * @return - the hardware address of BAR or 0 if the \p address is not BAR mapping + */ +extern unsigned long pcidriver_resolve_bar(unsigned long address); + +#endif /* _PCIDRIVER_H */ \ No newline at end of file diff --git a/driver/rdma.c b/driver/rdma.c index c08eef9..b1d939a 100644 --- a/driver/rdma.c +++ b/driver/rdma.c @@ -9,11 +9,7 @@ #include #include -#include "config.h" -#include "compat.h" -#include "pciDriver.h" -#include "common.h" -#include "rdma.h" +#include "base.h" static unsigned long pcidriver_follow_pte(struct mm_struct *mm, unsigned long address) { diff --git a/driver/rdma.h b/driver/rdma.h index cfe9c83..813406d 100644 --- a/driver/rdma.h +++ b/driver/rdma.h @@ -1,6 +1,5 @@ #ifndef _PCIDRIVER_RDMA_H #define _PCIDRIVER_RDMA_H -extern unsigned long pcidriver_resolve_bar(unsigned long address); #endif /* _PCIDRIVER_RDMA_H */ diff --git a/driver/sysfs.c b/driver/sysfs.c index 19865fc..d0fd870 100644 --- a/driver/sysfs.c +++ b/driver/sysfs.c @@ -18,106 +18,32 @@ #include #include -#include "compat.h" -#include "config.h" -#include "pciDriver.h" -#include "common.h" -#include "umem.h" -#include "kmem.h" -#include "sysfs.h" +#include "base.h" -static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry); -static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry); +#define SYSFS_GET_PRIVDATA dev_get_drvdata(dev) +#define SYSFS_GET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, char *buf) +#define SYSFS_SET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -/** - * - * Initializes the sysfs attributes for an kmem/umem-entry - * - */ -static int _pcidriver_sysfs_initialize(pcidriver_privdata_t *privdata, - int id, - struct class_device_attribute *sysfs_attr, - const char *fmtstring, - SYSFS_GET_FUNCTION((*callback))) -{ - /* sysfs attributes for kmem buffers don’t make sense before 2.6.13, as - we have no mmap support before */ - char namebuffer[16]; - - /* allocate space for the name of the attribute */ - snprintf(namebuffer, sizeof(namebuffer), fmtstring, id); - - if ((sysfs_attr->attr.name = kstrdup(namebuffer, GFP_KERNEL)) == NULL) - return -ENOMEM; +#define SYSFS_ATTR_NAME(name) (dev_attr_##name) - sysfs_attr->attr.mode = S_IRUGO; - sysfs_attr->show = callback; - sysfs_attr->store = NULL; +#define SYSFS_ATTR_CREATE(name) do { \ + int err = device_create_file(privdata->class_dev, &SYSFS_ATTR_NAME(name)); \ + if (err != 0) return err; \ + } while (0) - /* name and add attribute */ - if (class_device_create_file(privdata->class_dev, sysfs_attr) != 0) - return -ENXIO; /* Device not configured. Not the really best choice, but hm. */ - - return 0; -} - -int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr) -{ - return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "kbuf%d", pcidriver_show_kmem_entry); -} - -int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr) -{ - return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "umem%d", pcidriver_show_umem_entry); -} - -/** - * - * Removes the file from sysfs and frees the allocated (kstrdup()) memory. - * - */ -void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct class_device_attribute *sysfs_attr) -{ - class_device_remove_file(privdata->class_dev, sysfs_attr); - kfree(sysfs_attr->attr.name); -} - -static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry) -{ - pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; - - /* As we can be sure that attr.name contains a filename which we - * created (see _pcidriver_sysfs_initialize), we do not need to have - * sanity checks but can directly call simple_strtol() */ - int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10); - pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id); - if (entry) { - unsigned long addr = entry->cpua; - unsigned long dma_addr = entry->dma_handle; - - if (entry->size >= 16) { - pcidriver_kmem_sync_entry(privdata, entry, PCILIB_KMEM_SYNC_FROMDEVICE); - return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\ndata: %8x %8x %8x %8x\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode, *(u32*)(entry->cpua), *(u32*)(entry->cpua + 4), *(u32*)(entry->cpua + 8), *(u32*)(entry->cpua + 12)); - } else - return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode); - } else - return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id); -} - -static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry) -{ - return 0; -} +#define SYSFS_ATTR_REMOVE(name) do { \ + device_remove_file(privdata->class_dev, &SYSFS_ATTR_NAME(name)); \ + } while (0) #ifdef ENABLE_IRQ -SYSFS_GET_FUNCTION(pcidriver_show_irq_count) +static SYSFS_GET_FUNCTION(pcidriver_show_irq_count) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; return snprintf(buf, PAGE_SIZE, "%d\n", privdata->irq_count); } -SYSFS_GET_FUNCTION(pcidriver_show_irq_queues) +static SYSFS_GET_FUNCTION(pcidriver_show_irq_queues) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; int i, offset; @@ -131,14 +57,14 @@ SYSFS_GET_FUNCTION(pcidriver_show_irq_queues) } #endif -SYSFS_GET_FUNCTION(pcidriver_show_mmap_mode) +static SYSFS_GET_FUNCTION(pcidriver_show_mmap_mode) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_mode); } -SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode) +static SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; int mode = -1; @@ -151,14 +77,14 @@ SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode) return strlen(buf); } -SYSFS_GET_FUNCTION(pcidriver_show_mmap_area) +static SYSFS_GET_FUNCTION(pcidriver_show_mmap_area) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_area); } -SYSFS_SET_FUNCTION(pcidriver_store_mmap_area) +static SYSFS_SET_FUNCTION(pcidriver_store_mmap_area) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; int temp = -1; @@ -171,14 +97,14 @@ SYSFS_SET_FUNCTION(pcidriver_store_mmap_area) return strlen(buf); } -SYSFS_GET_FUNCTION(pcidriver_show_kmem_count) +static SYSFS_GET_FUNCTION(pcidriver_show_kmem_count) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&(privdata->kmem_count))); } -SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc) +static SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; kmem_handle_t kmem_handle; @@ -190,7 +116,7 @@ SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc) return strlen(buf); } -SYSFS_SET_FUNCTION(pcidriver_store_kmem_free) +static SYSFS_SET_FUNCTION(pcidriver_store_kmem_free) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; unsigned int id; @@ -209,7 +135,7 @@ err: return strlen(buf); } -SYSFS_GET_FUNCTION(pcidriver_show_kbuffers) +static SYSFS_GET_FUNCTION(pcidriver_show_kbuffers) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; int offset = 0; @@ -238,7 +164,7 @@ SYSFS_GET_FUNCTION(pcidriver_show_kbuffers) return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); } -SYSFS_GET_FUNCTION(pcidriver_show_umappings) +static SYSFS_GET_FUNCTION(pcidriver_show_umappings) { int offset = 0; pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; @@ -268,7 +194,7 @@ SYSFS_GET_FUNCTION(pcidriver_show_umappings) return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1); } -SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap) +static SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap) { pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; pcidriver_umem_entry_t *umem_entry; @@ -285,3 +211,128 @@ SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap) err: return strlen(buf); } + +static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry) +{ + pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA; + + /* As we can be sure that attr.name contains a filename which we + * created (see _pcidriver_sysfs_initialize), we do not need to have + * sanity checks but can directly call simple_strtol() */ + int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10); + pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id); + if (entry) { + unsigned long addr = entry->cpua; + unsigned long dma_addr = entry->dma_handle; + + if (entry->size >= 16) { + pcidriver_kmem_sync_entry(privdata, entry, PCILIB_KMEM_SYNC_FROMDEVICE); + return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\ndata: %8x %8x %8x %8x\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode, *(u32*)(entry->cpua), *(u32*)(entry->cpua + 4), *(u32*)(entry->cpua + 8), *(u32*)(entry->cpua + 12)); + } else + return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode); + } else + return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id); +} + +static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry) +{ + return 0; +} + + +#ifdef ENABLE_IRQ +static DEVICE_ATTR(irq_count, S_IRUGO, pcidriver_show_irq_count, NULL); +static DEVICE_ATTR(irq_queues, S_IRUGO, pcidriver_show_irq_queues, NULL); +#endif + +static DEVICE_ATTR(mmap_mode, 0664, pcidriver_show_mmap_mode, pcidriver_store_mmap_mode); +static DEVICE_ATTR(mmap_area, 0664, pcidriver_show_mmap_area, pcidriver_store_mmap_area); +static DEVICE_ATTR(kmem_count, 0444, pcidriver_show_kmem_count, NULL); +static DEVICE_ATTR(kbuffers, 0444, pcidriver_show_kbuffers, NULL); +static DEVICE_ATTR(kmem_alloc, 0220, NULL, pcidriver_store_kmem_alloc); +static DEVICE_ATTR(kmem_free, 0220, NULL, pcidriver_store_kmem_free); +static DEVICE_ATTR(umappings, 0444, pcidriver_show_umappings, NULL); +static DEVICE_ATTR(umem_unmap, 0220, NULL, pcidriver_store_umem_unmap); + +int pcidriver_create_sysfs_attributes(pcidriver_privdata_t *privdata) { +#ifdef ENABLE_IRQ + SYSFS_ATTR_CREATE(irq_count); + SYSFS_ATTR_CREATE(irq_queues); +#endif + + SYSFS_ATTR_CREATE(mmap_mode); + SYSFS_ATTR_CREATE(mmap_area); + SYSFS_ATTR_CREATE(kmem_count); + SYSFS_ATTR_CREATE(kmem_alloc); + SYSFS_ATTR_CREATE(kmem_free); + SYSFS_ATTR_CREATE(kbuffers); + SYSFS_ATTR_CREATE(umappings); + SYSFS_ATTR_CREATE(umem_unmap); + + return 0; +} + +void pcidriver_remove_sysfs_attributes(pcidriver_privdata_t *privdata) { +#ifdef ENABLE_IRQ + SYSFS_ATTR_REMOVE(irq_count); + SYSFS_ATTR_REMOVE(irq_queues); +#endif + + SYSFS_ATTR_REMOVE(mmap_mode); + SYSFS_ATTR_REMOVE(mmap_area); + SYSFS_ATTR_REMOVE(kmem_count); + SYSFS_ATTR_REMOVE(kmem_alloc); + SYSFS_ATTR_REMOVE(kmem_free); + SYSFS_ATTR_REMOVE(kbuffers); + SYSFS_ATTR_REMOVE(umappings); + SYSFS_ATTR_REMOVE(umem_unmap); +} + +/** + * + * Removes the file from sysfs and frees the allocated (kstrdup()) memory. + * + */ +void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct device_attribute *sysfs_attr) +{ + device_remove_file(privdata->class_dev, sysfs_attr); + kfree(sysfs_attr->attr.name); +} + +/** + * + * Initializes the sysfs attributes for an kmem/umem-entry + * + */ +static int _pcidriver_sysfs_initialize(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr, const char *fmtstring, SYSFS_GET_FUNCTION((*callback))) +{ + /* sysfs attributes for kmem buffers don’t make sense before 2.6.13, as + we have no mmap support before */ + char namebuffer[16]; + + /* allocate space for the name of the attribute */ + snprintf(namebuffer, sizeof(namebuffer), fmtstring, id); + + if ((sysfs_attr->attr.name = kstrdup(namebuffer, GFP_KERNEL)) == NULL) + return -ENOMEM; + + sysfs_attr->attr.mode = S_IRUGO; + sysfs_attr->show = callback; + sysfs_attr->store = NULL; + + /* name and add attribute */ + if (device_create_file(privdata->class_dev, sysfs_attr) != 0) + return -ENXIO; /* Device not configured. Not the really best choice, but hm. */ + + return 0; +} + +int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr) +{ + return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "kbuf%d", pcidriver_show_kmem_entry); +} + +int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr) +{ + return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "umem%d", pcidriver_show_umem_entry); +} diff --git a/driver/sysfs.h b/driver/sysfs.h index 4c413f0..8de518b 100644 --- a/driver/sysfs.h +++ b/driver/sysfs.h @@ -1,23 +1,15 @@ #ifndef _PCIDRIVER_SYSFS_H #define _PCIDRIVER_SYSFS_H -int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr); -int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct class_device_attribute *sysfs_attr); -void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct class_device_attribute *sysfs_attr); -#ifdef ENABLE_IRQ -SYSFS_GET_FUNCTION(pcidriver_show_irq_count); -SYSFS_GET_FUNCTION(pcidriver_show_irq_queues); -#endif +#include -/* prototypes for sysfs operations */ -SYSFS_GET_FUNCTION(pcidriver_show_mmap_mode); -SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode); -SYSFS_GET_FUNCTION(pcidriver_show_mmap_area); -SYSFS_SET_FUNCTION(pcidriver_store_mmap_area); -SYSFS_GET_FUNCTION(pcidriver_show_kmem_count); -SYSFS_GET_FUNCTION(pcidriver_show_kbuffers); -SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc); -SYSFS_SET_FUNCTION(pcidriver_store_kmem_free); -SYSFS_GET_FUNCTION(pcidriver_show_umappings); -SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap); -#endif +#include "dev.h" + +int pcidriver_create_sysfs_attributes(pcidriver_privdata_t *privdata); +void pcidriver_remove_sysfs_attributes(pcidriver_privdata_t *privdata); + +int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr); +int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr); +void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct device_attribute *sysfs_attr); + +#endif /* _PCIDRIVER_SYSFS_H */ diff --git a/driver/umem.c b/driver/umem.c index bb9af1e..d8be358 100644 --- a/driver/umem.c +++ b/driver/umem.c @@ -18,12 +18,7 @@ #include #include -#include "config.h" /* compile-time configuration */ -#include "compat.h" /* compatibility definitions for older linux */ -#include "pciDriver.h" /* external interface for the driver */ -#include "common.h" /* internal definitions for all parts */ -#include "umem.h" /* prototypes for kernel memory */ -#include "sysfs.h" /* prototypes for sysfs */ +#include "base.h" /** * @@ -109,7 +104,7 @@ int pcidriver_umem_sgmap(pcidriver_privdata_t *privdata, umem_handle_t *umem_han /* Lock the pages, then populate the SG list with the pages */ /* page0 is different */ if ( !PageReserved(pages[0]) ) - compat_lock_page(pages[0]); + __set_page_locked(pages[0]); offset = (umem_handle->vma & ~PAGE_MASK); length = (umem_handle->size > (PAGE_SIZE-offset) ? (PAGE_SIZE-offset) : umem_handle->size); @@ -120,7 +115,7 @@ int pcidriver_umem_sgmap(pcidriver_privdata_t *privdata, umem_handle_t *umem_han for(i=1; i PAGE_SIZE) ? PAGE_SIZE : count), 0); @@ -169,7 +164,7 @@ umem_sgmap_unmap: if (nr_pages > 0) { for(i=0; ipages[i] )) { SetPageDirty( umem_entry->pages[i] ); - compat_unlock_page(umem_entry->pages[i]); + __clear_page_locked(umem_entry->pages[i]); } /* and release it from the cache */ page_cache_release( umem_entry->pages[i] ); diff --git a/driver/umem.h b/driver/umem.h index d16c466..d504ecb 100644 --- a/driver/umem.h +++ b/driver/umem.h @@ -1,5 +1,26 @@ +#ifndef _PCIDRIVER_UMEM_H +#define _PCIDRIVER_UMEM_H + +#include + +#include "ioctl.h" + +/* Define an entry in the umem list (this list is per device) */ +/* This list keeps references to the SG lists for each mapped userspace region */ +typedef struct { + int id; + struct list_head list; + unsigned int nr_pages; /* number of pages for this user memeory area */ + struct page **pages; /* list of pointers to the pages */ + unsigned int nents; /* actual entries in the scatter/gatter list (NOT nents for the map function, but the result) */ + struct scatterlist *sg; /* list of sg entries */ + struct device_attribute sysfs_attr; /* initialized when adding the entry */ +} pcidriver_umem_entry_t; + int pcidriver_umem_sgmap( pcidriver_privdata_t *privdata, umem_handle_t *umem_handle ); int pcidriver_umem_sgunmap( pcidriver_privdata_t *privdata, pcidriver_umem_entry_t *umem_entry ); int pcidriver_umem_sgget( pcidriver_privdata_t *privdata, umem_sglist_t *umem_sglist ); int pcidriver_umem_sync( pcidriver_privdata_t *privdata, umem_handle_t *umem_handle ); pcidriver_umem_entry_t *pcidriver_umem_find_entry_id( pcidriver_privdata_t *privdata, int id ); + +#endif /* _PCIDRIVER_UMEM_H */ diff --git a/pcilib/pci.h b/pcilib/pci.h index 172a6fc..bfd3796 100644 --- a/pcilib/pci.h +++ b/pcilib/pci.h @@ -18,7 +18,7 @@ #include #include "linux-3.10.h" -#include "driver/pciDriver.h" +#include "driver/ioctl.h" #include "timing.h" #include "cpu.h" diff --git a/pcilib/pcilib.h b/pcilib/pcilib.h index f1c0dd7..b82e517 100644 --- a/pcilib/pcilib.h +++ b/pcilib/pcilib.h @@ -1,5 +1,5 @@ -#ifndef _PCITOOL_PCILIB_H -#define _PCITOOL_PCILIB_H +#ifndef _PCILIB_H +#define _PCILIB_H #include #include @@ -1331,4 +1331,4 @@ int pcilib_convert_value_type(pcilib_t *ctx, pcilib_value_t *val, pcilib_value_t } #endif -#endif /* _PCITOOL_PCILIB_H */ +#endif /* _PCILIB_H */ -- cgit v1.2.3 From 7e39a6c8ae5c3f95b3b2895c4ce8d858c7ad3b79 Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Wed, 2 Mar 2016 20:05:25 +0100 Subject: Minor change of logic in pcidriver_kmem_free_check() resulting in less complains while de-referencing the shared non-persistent kernel memory --- driver/kmem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/driver/kmem.c b/driver/kmem.c index 47d1929..522f3bc 100644 --- a/driver/kmem.c +++ b/driver/kmem.c @@ -239,6 +239,9 @@ static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle if (kmem_handle->flags&KMEM_FLAG_REUSE) return 0; + if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0)) + return 0; + if (kmem_entry->refs) { kmem_entry->mode += 1; mod_info("can't free referenced kmem_entry, refs = %lx\n", kmem_entry->refs); @@ -251,8 +254,6 @@ static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle return -EBUSY; } - if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0)) - return 0; } else { if (kmem_entry->refs&KMEM_REF_HW) pcidriver_module_put(privdata); -- cgit v1.2.3 From 1e53d2d17e611586030e3ff3e9ab87e5bb753c9b Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Thu, 3 Mar 2016 00:10:09 +0100 Subject: Fix CMakeLists.txt script for Python3 --- CMakeLists.txt | 4 ++-- xml/test/test_prop4.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c2d45b..d3a8166 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,9 +50,9 @@ if (NOT DISABLE_PYTHON) # execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "from sysconfig import get_path; print get_path('platlib')" OUTPUT_VARIABLE PYTHON_INSTALL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "import site; print site.PREFIXES[-1]" OUTPUT_VARIABLE PYTHON_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "import site; print (site.PREFIXES[-1])" OUTPUT_VARIABLE PYTHON_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) file (TO_CMAKE_PATH "${PYTHON_PREFIX}" PYTHON_PREFIX) - execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "import site; print site.getsitepackages()[0]" OUTPUT_VARIABLE PYTHON_SITE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process (COMMAND ${PYTHON_EXECUTABLE} -c "import site; print (site.getsitepackages()[0])" OUTPUT_VARIABLE PYTHON_SITE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) file (TO_CMAKE_PATH "${PYTHON_SITE_DIR}" PYTHON_SITE_DIR) string (REGEX REPLACE "^${PYTHON_PREFIX}/" "${CMAKE_INSTALL_PREFIX}/" PYTHON_SITE_DIR "${PYTHON_SITE_DIR}") diff --git a/xml/test/test_prop4.py b/xml/test/test_prop4.py index a7e0269..30e9ece 100644 --- a/xml/test/test_prop4.py +++ b/xml/test/test_prop4.py @@ -10,7 +10,6 @@ def read_from_register(ctx, value): read_from_register.counter += 1 for i in range (0, 5): time.sleep(0.1) - print(cur) out = ctx.get_property('/test/prop3') / 2 ctx.unlock('test_prop4') -- cgit v1.2.3 From 195c28f3074486165b6e0935362810f8a1fb9531 Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Thu, 3 Mar 2016 00:50:59 +0100 Subject: Make Python problems non-fatal --- pcilib/pci.c | 19 +++++++++---------- pcilib/py.c | 36 ++++++++++++++++++++++++++---------- pcilib/py.h | 1 + views/transform.c | 8 +++++--- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/pcilib/pci.c b/pcilib/pci.c index ec40c95..c9cd1d2 100644 --- a/pcilib/pci.c +++ b/pcilib/pci.c @@ -147,9 +147,8 @@ pcilib_t *pcilib_open(const char *device, const char *model) { err = pcilib_init_py(ctx); if (err) { - pcilib_error("Error (%i) initializing python subsystem", err); - pcilib_close(ctx); - return NULL; + pcilib_warning("Error (%i) initializing python subsystem", err); + pcilib_free_py(ctx); } ctx->alloc_reg = PCILIB_DEFAULT_REGISTER_SPACE; @@ -191,22 +190,22 @@ pcilib_t *pcilib_open(const char *device, const char *model) { if (!ctx->model) ctx->model = strdup(model?model:"pci"); - + err = pcilib_py_add_script_dir(ctx, NULL); if (err) { - pcilib_error("Error (%i) add script path to python path", err); - pcilib_close(ctx); - return NULL; + pcilib_warning("Error (%i) add script path to python path", err); + pcilib_free_py(ctx); + err = 0; } - - + + xmlerr = pcilib_init_xml(ctx, ctx->model); if ((xmlerr)&&(xmlerr != PCILIB_ERROR_NOTFOUND)) { pcilib_error("Error (%i) initializing XML subsystem for model %s", xmlerr, ctx->model); pcilib_close(ctx); return NULL; } - + // We have found neither standard model nor XML if ((err)&&(xmlerr)) { diff --git a/pcilib/py.c b/pcilib/py.c index 9e4ca90..833532e 100644 --- a/pcilib/py.c +++ b/pcilib/py.c @@ -135,19 +135,19 @@ int pcilib_init_py(pcilib_t *ctx) { ctx->py->main_module = PyImport_AddModule("__parser__"); if (!ctx->py->main_module) { - pcilib_python_error("Error importing python parser"); + pcilib_python_warning("Error importing python parser"); return PCILIB_ERROR_FAILED; } ctx->py->global_dict = PyModule_GetDict(ctx->py->main_module); if (!ctx->py->global_dict) { - pcilib_python_error("Error locating global python dictionary"); + pcilib_python_warning("Error locating global python dictionary"); return PCILIB_ERROR_FAILED; } PyObject *pywrap = PyImport_ImportModule(PCILIB_PYTHON_WRAPPER); if (!pywrap) { - pcilib_python_error("Error importing pcilib python wrapper"); + pcilib_python_warning("Error importing pcilib python wrapper"); return PCILIB_ERROR_FAILED; } @@ -158,7 +158,7 @@ int pcilib_init_py(pcilib_t *ctx) { Py_XDECREF(mod_name); if (!ctx->py->pcilib_pywrap) { - pcilib_python_error("Error initializing python wrapper"); + pcilib_python_warning("Error initializing python wrapper"); return PCILIB_ERROR_FAILED; } #endif /* HAVE_PYTHON */ @@ -173,6 +173,8 @@ int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) { PyObject *pydict, *pystr, *pyret = NULL; char *script_dir; + if (!ctx->py) return 0; + const char *model_dir = getenv("PCILIB_MODEL_DIR"); if (!model_dir) model_dir = PCILIB_MODEL_DIR; @@ -188,13 +190,13 @@ int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) { pypath = PySys_GetObject("path"); if (!pypath) { - pcilib_python_error("Can't get python path"); + pcilib_python_warning("Can't get python path"); return PCILIB_ERROR_FAILED; } pynewdir = PyUnicode_FromString(script_dir); if (!pynewdir) { - pcilib_python_error("Can't create python string"); + pcilib_python_warning("Can't create python string"); return PCILIB_ERROR_MEMORY; } @@ -224,7 +226,7 @@ int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) { Py_DECREF(pynewdir); if (err) { - pcilib_python_error("Can't add directory (%s) to python path", script_dir); + pcilib_python_warning("Can't add directory (%s) to python path", script_dir); return err; } #endif /* HAVE_PYTHON */ @@ -267,6 +269,7 @@ int pcilib_py_load_script(pcilib_t *ctx, const char *script_name) { PyObject* pymodule; pcilib_script_t *module = NULL; + if (!ctx->py) return 0; char *module_name = strdupa(script_name); if (!module_name) return PCILIB_ERROR_MEMORY; @@ -304,7 +307,12 @@ int pcilib_py_get_transform_script_properties(pcilib_t *ctx, const char *script_ PyObject *dict; PyObject *pystr; pcilib_script_t *module; - + + if (!ctx->py) { + if (mode_ret) *mode_ret = mode; + return 0; + } + HASH_FIND_STR(ctx->py->script_hash, script_name, module); if(!module) { @@ -343,7 +351,9 @@ pcilib_py_object *pcilib_get_value_as_pyobject(pcilib_t* ctx, pcilib_value_t *va long ival; double fval; - + + if (!ctx->py) return NULL; + gstate = PyGILState_Ensure(); switch(val->type) { case PCILIB_TYPE_LONG: @@ -383,7 +393,9 @@ int pcilib_set_value_from_pyobject(pcilib_t* ctx, pcilib_value_t *val, pcilib_py int err = 0; PyObject *pyval = (PyObject*)pval; PyGILState_STATE gstate; - + + if (!ctx->py) return PCILIB_ERROR_NOTINITIALIZED; + gstate = PyGILState_Ensure(); if (PyLong_Check(pyval)) { err = pcilib_set_value_from_int(ctx, val, PyLong_AsLong(pyval)); @@ -536,6 +548,8 @@ int pcilib_py_eval_string(pcilib_t *ctx, const char *codestr, pcilib_value_t *va char *code; PyObject* obj; + if (!ctx->py) return PCILIB_ERROR_NOTINITIALIZED; + code = pcilib_py_parse_string(ctx, codestr, value); if (!code) { pcilib_error("Failed to parse registers in the code: %s", codestr); @@ -572,6 +586,8 @@ int pcilib_py_eval_func(pcilib_t *ctx, const char *script_name, const char *func PyObject *pyval = NULL, *pyret; pcilib_script_t *module = NULL; + if (!ctx->py) return PCILIB_ERROR_NOTINITIALIZED; + HASH_FIND_STR(ctx->py->script_hash, script_name, module); if (!module) { diff --git a/pcilib/py.h b/pcilib/py.h index c372a09..128fbb6 100644 --- a/pcilib/py.h +++ b/pcilib/py.h @@ -5,6 +5,7 @@ #include #define pcilib_python_error(...) pcilib_log_python_error(__FILE__, __LINE__, PCILIB_LOG_DEFAULT, PCILIB_LOG_ERROR, __VA_ARGS__) +#define pcilib_python_warning(...) pcilib_log_python_error(__FILE__, __LINE__, PCILIB_LOG_DEFAULT, PCILIB_LOG_WARNING, __VA_ARGS__) typedef struct pcilib_py_s pcilib_py_t; typedef void pcilib_py_object; diff --git a/views/transform.c b/views/transform.c index 25f30d1..3aa3b2b 100644 --- a/views/transform.c +++ b/views/transform.c @@ -18,7 +18,7 @@ static pcilib_view_context_t * pcilib_transform_view_init(pcilib_t *ctx, pcilib_ pcilib_view_context_t *view_ctx; pcilib_transform_view_description_t *v = (pcilib_transform_view_description_t*)(ctx->views[view]); - if(v->script) { + if (v->script) { pcilib_access_mode_t mode = 0; err = pcilib_py_load_script(ctx, v->script); @@ -39,11 +39,13 @@ static pcilib_view_context_t * pcilib_transform_view_init(pcilib_t *ctx, pcilib_ if (!v->read_from_reg) v->read_from_reg = "read_from_register"; if (!v->write_to_reg) v->write_to_reg = "write_to_register"; + } else if (!ctx->py) { + v->base.mode &= (~PCILIB_REGISTER_RW); } - + view_ctx = (pcilib_view_context_t*)malloc(sizeof(pcilib_view_context_t)); if (view_ctx) memset(view_ctx, 0, sizeof(pcilib_view_context_t)); - + return view_ctx; } -- cgit v1.2.3