diff options
Diffstat (limited to 'driver/base.c')
-rw-r--r-- | driver/base.c | 676 |
1 files changed, 0 insertions, 676 deletions
diff --git a/driver/base.c b/driver/base.c deleted file mode 100644 index 974224d..0000000 --- a/driver/base.c +++ /dev/null @@ -1,676 +0,0 @@ -/** - * - * @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. - * - * 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 <linux/version.h> - -/* 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 <linux/string.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/cdev.h> -#include <linux/sysfs.h> -#include <asm/atomic.h> -#include <linux/pagemap.h> -#include <linux/spinlock.h> -#include <linux/list.h> -#include <asm/scatterlist.h> -#include <linux/vmalloc.h> -#include <linux/stat.h> -#include <linux/interrupt.h> -#include <linux/wait.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" - -/*************************************************************************/ -/* 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; - -/** - * - * Called when loading the driver - * - */ -static int __init pcidriver_init(void) -{ - int err; - - /* Initialize the device count */ - atomic_set(&pcidriver_deviceCount, 0); - - /* 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. */ - if ((err = pci_register_driver(&pcidriver_driver)) < 0) { - mod_info("Couldn't register PCI driver. Module not loaded.\n"); - goto init_pcireg_fail; - } - - mod_info("Module loaded\n"); - - return 0; - -init_pcireg_fail: - class_destroy(pcidriver_class); -init_class_fail: - unregister_chrdev_region(pcidriver_devt, MAXDEVICES); -init_alloc_fail: - return err; -} - -/** - * - * Called when unloading the driver - * - */ -static void pcidriver_exit(void) -{ - pci_unregister_driver(&pcidriver_driver); - unregister_chrdev_region(pcidriver_devt, MAXDEVICES); - - if (pcidriver_class != NULL) - class_destroy(pcidriver_class); - - mod_info("Module unloaded\n"); -} - -/*************************************************************************/ -/* Driver functions */ - -/** - * - * This struct defines the PCI entry points. - * Will be registered at module init. - * - */ -static struct pci_driver pcidriver_driver = { - .name = MODNAME, - .id_table = pcidriver_ids, - .probe = pcidriver_probe, - .remove = pcidriver_remove, -}; - -/** - * - * 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) -{ - int err; - 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. - */ - 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 */ - if ((id->vendor == PCIE_XILINX_VENDOR_ID)&&(id->device == PCIE_IPECAMERA_DEVICE_ID)) { - 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"); - - /* 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; - } - - 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); - - pci_set_drvdata( pdev, privdata ); - privdata->pdev = pdev; - - /* Device add to sysfs */ - devno = MKDEV(MAJOR(pcidriver_devt), MINOR(pcidriver_devt) + devid); - privdata->devno = devno; - if (pcidriver_class != NULL) { - /* FIXME: some error checking missing here */ - privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, &(pdev->dev), NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata); - class_set_devdata( privdata->class_dev, privdata ); - mod_info("Device /dev/%s%d added\n",NODENAME,MINOR(pcidriver_devt) + devid); - } - - /* Setup mmaped BARs into kernel space */ - if ((err = pcidriver_probe_irq(privdata)) != 0) - goto probe_irq_probe_fail; - - /* 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; - } - - return 0; - -probe_device_create_fail: -probe_cdevadd_fail: -probe_irq_probe_fail: - pcidriver_irq_unmap_bars(privdata); - kfree(privdata); -probe_nomem: - atomic_dec(&pcidriver_deviceCount); -probe_maxdevices_fail: -probe_dma_fail: - pci_disable_device(pdev); -probe_pcien_fail: - return err; -} - -/** - * - * This function is called when disconnecting a device - * - */ -static void __devexit pcidriver_remove(struct pci_dev *pdev) -{ - pcidriver_privdata_t *privdata; - - /* Get private data from the device */ - privdata = pci_get_drvdata(pdev); - - /* 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 - pcidriver_remove_irq(privdata); -#endif - - /* Removing Character device */ - cdev_del(&(privdata->cdev)); - - /* Removing the device from sysfs */ - class_device_destroy(pcidriver_class, privdata->devno); - - /* Releasing privdata */ - kfree(privdata); - - /* Disabling PCI device */ - pci_disable_device(pdev); - - mod_info("Device at %s removed\n", dev_name(&pdev->dev)); -} - -/*************************************************************************/ -/* 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, -#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, -}; - -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) -{ - 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. - * - */ -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; -} - -/** - * - * 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) */ - } - - return ret; -} - -/*************************************************************************/ -/* Internal driver functions */ -int pcidriver_mmap_pci(pcidriver_privdata_t *privdata, struct vm_area_struct *vmap, int bar) -{ - 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 */ -#ifdef pgprot_noncached - vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot); -#endif - - /* 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)) -// 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 */ -} |