Commit 71585b03 authored by Lucas Russo's avatar Lucas Russo

kernel/*: restructure kernel folder

Now, the PCIe driver is located in foreign/pcie-driver
parent 6ddc301d
# Set your cross compile prefix with CROSS_COMPILE variable
CROSS_COMPILE ?=
CMDSEP = ;
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
LD = $(CROSS_COMPILE)ld
OBJDUMP = $(CROSS_COMPILE)objdump
OBJCOPY = $(CROSS_COMPILE)objcopy
SIZE = $(CROSS_COMPILE)size
MAKE = make
DRIVER_DIR = drivers/pcie
LIB_DIR = lib/pcie
all: kernel_driver lib_driver
.PHONY: kernel_driver lib_driver install uninstall clean
kernel_driver:
$(MAKE) -C $(DRIVER_DIR) all
# $(MAKE) -C $(DRIVER_DIR) install
lib_driver:
$(MAKE) -C $(LIB_DIR) all
# $(MAKE) -C $(LIB_DIR) install
clean:
$(MAKE) -C $(DRIVER_DIR) clean
$(MAKE) -C $(LIB_DIR) clean
install:
$(MAKE) -C $(DRIVER_DIR) install
$(MAKE) -C $(LIB_DIR) install
uninstall:
$(MAKE) -C $(DRIVER_DIR) uninstall
$(MAKE) -C $(LIB_DIR) uninstall
==========================================================
Folder containing all of the Beam Position Monitor software.
==========================================================
1. PCIE driver for FPGA device
1.1. Overview
This driver is based on original driver for the PCIe SG DMA project
placed at the opencores.org
As of now, the changes are mostly reworked Makefiles to fit into the
directory structure at the OHWR repo; also, with present makefiles
it's easier to cross-compile code (e.g. using buildroot).
It consists of two parts:
- kernel driver at driver/pcie
- c/c++ library at lib/pcie
All relevant includes are in include/pcie folder.
1.2. INSTALL
To compile and install kernel driver go to driver/pcie and issue 'make && make install'
To install library go to lib/pcie and type 'make && make install'
1.3. TODO
- review and commit original test units
- port to PowerPC
- any changes that will be required
# Main Makefile for the pciDriver
#helpful in case of buildroot crosscompiling
ROOTDIR :=
CURDIR := $(shell pwd)
export SRCDIR := $(CURDIR)/src
all:
$(Q)$(MAKE) -C $(SRCDIR)
install:
$(Q)$(MAKE) -C $(SRCDIR) install
@echo "INSTALL 60-udev_fpga.rules"
-$(Q)install -m 644 etc/udev/rules.d/60-udev_fpga.rules $(ROOTDIR)/etc/udev/rules.d/
uninstall:
$(Q)$(MAKE) -C $(SRCDIR) uninstall
@echo "UNINSTALL 60-udev_fpga.rules"
-$(Q)rm -f $(ROOTDIR)/etc/udev/rules.d/60-udev_fpga.rules
clean:
@echo "Cleaning..."
-$(Q)$(MAKE) -C $(SRCDIR) clean
# Permissions for the fpga devices
KERNEL=="fpga*", OWNER="root", GROUP="users", MODE="660"
#!/bin/bash
#add relevant overrides to make use of buildroot toolchain
BUILDROOT_DIR="/home/adrian/praca/buildroot/buildroot"
KERNELDIR="$BUILDROOT_DIR/output/build/linux-3.6.8"
INSTALLDIR="$BUILDROOT_DIR/output/target"
export ARCH=x86_64
export CROSS_COMPILE=x86_64-linux-
export PATH="$PATH:$BUILDROOT_DIR/output/host/usr/bin"
EXTRA_CFLAGS="-D VERBOSE"
cd src && make KERNELDIR=$KERNELDIR
cd ..
echo "Copying to $INSTALLDIR"
cp src/pciDriver.ko $INSTALLDIR/opt/
cp etc/udev/rules.d/60-udev_fpga.rules $INSTALLDIR/etc/udev/rules.d/
echo "DONE"
#makefile symbolic link
pciDriver.h
#editor backup files
*~
#Kbuild files
*.o
*.ko
*.mod.c
Module.symvers
modules.order
.tmp_versions/
.*.cmd
obj-m := pciDriver.o
pciDriver-objs := base.o int.o umem.o kmem.o sysfs.o ioctl.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
INSTALLDIR ?= /lib/modules/$(shell uname -r)/extra
INSTALLHDRDIR ?= /usr/include/pciDriver/driver
PWD := $(shell pwd)
default: dolink
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
dolink:
@ln -sf ../../../include/pcie/driver/pciDriver.h
install:
@mkdir -p $(INSTALLDIR)
@echo "INSTALL $(INSTALLDIR)/pciDriver.ko"
@install -m 755 pciDriver.ko $(INSTALLDIR)
@echo "INSTALL $(INSTALLHDRDIR)/pciDriver.h"
@mkdir -p $(INSTALLHDRDIR)
@install -m 644 pciDriver.h $(INSTALLHDRDIR)
uninstall:
@echo "UNINSTALL $(INSTALLDIR)/pciDriver.ko"
@rm -f $(INSTALLDIR)/pciDriver.ko
@echo "UNINSTALL $(INSTALLHDRDIR)/pciDriver.h"
@rm -rf /usr/include/pciDriver/driver
clean:
rm -f *.o *.ko *.mod.c .*.cmd Module.symvers modules.order
rm -rf .tmp_versions
This diff is collapsed.
#ifndef _PCIDRIVER_BASE_H
#define _PCIDRIVER_BASE_H
#include "sysfs.h"
/**
*
* This file contains prototypes and data structures for internal use of the pciDriver.
*
*
*/
/* 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 */
static struct pci_driver pcidriver_driver;
static int pcidriver_probe(struct pci_dev *pdev, const struct pci_device_id *id);
static void 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 <kernel-source>/Documentation/pci.txt
*
*/
DEFINE_PCI_DEVICE_TABLE(pcidriver_ids) = {
{ PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_ML605_DEVICE_ID ) }, // PCI-E Xilinx ML605
{ PCI_DEVICE(PCIE_XILINX_VENDOR_ID, PCIE_KC705_DEV_ID)}, // PCIe Xilinx KC705
{ PCI_DEVICE(PCIE_XILINX_VENDOR_ID, PCIE_AMC_DEV_ID)}, // PCIe Creotech AMC
{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, pci_board_info *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;
/* Sysfs attributes */
static DEVICE_ATTR(mmap_mode, (S_IRUGO | S_IWUGO), pcidriver_show_mmap_mode, pcidriver_store_mmap_mode);
static DEVICE_ATTR(mmap_area, (S_IRUGO | S_IWUGO), 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, S_IWUGO, NULL, pcidriver_store_kmem_alloc);
static DEVICE_ATTR(kmem_free, S_IWUGO, NULL, pcidriver_store_kmem_free);
static DEVICE_ATTR(umappings, S_IRUGO, pcidriver_show_umappings, NULL);
static DEVICE_ATTR(umem_unmap, S_IWUGO, 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
#ifndef _PCIDRIVER_COMMON_H
#define _PCIDRIVER_COMMON_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;
struct list_head list;
dma_addr_t dma_handle;
unsigned long cpua;
unsigned long size;
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 {
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 */
atomic_t kmem_count; /* id for next kmem entry */
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 */
} pcidriver_privdata_t;
#define PCIE_XILINX_VENDOR_ID 0x10ee
/* Identifies the PCI-E Xilinx ML605 */
#define PCIE_ML605_DEVICE_ID 0x6014
/* Identfifies the PCIe Xilinx KC705 */
#define PCIE_KC705_DEV_ID 0x7021
/* Identfifies the PCIe Xilinx Artix AMC board */
#define PCIE_AMC_DEV_ID 0x7014
/*************************************************************************/
/* 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
/**
*
* @file compat.h
* @author Michael Stapelberg
* @date 2009-04-05
* @brief Contains compatibility definitions for the different linux kernel versions to avoid
* putting ifdefs all over the driver code.
*
*/
#ifndef _COMPAT_H
#define _COMPAT_H
/* dev_name is the wrapper one needs to use to access what was formerly called
* bus_id in struct device. However, before 2.6.26, direct access was necessary,
* so we provide our own version. */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
static inline const char *dev_name(struct device *dev) {
return dev->bus_id;
}
#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
#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)
#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
#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
/* 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 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)
#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
/* 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
/* 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
}
#endif
/*******************************/
/* Configuration of the driver */
/*******************************/
/* Debug messages */
#define DEBUG
/* Enable/disable IRQ handling */
#define ENABLE_IRQ
/* The name of the module */
#define MODNAME "pciDriver"
/* Major number is allocated dynamically */
/* Minor number */
#define MINORNR 0
/* Node name of the char device */
#define NODENAME "fpga"
#define NODENAMEFMT "fpga%d"
/* Maximum number of devices. This is the default maximum
* number of boards per microTCA.4 crate */
#define MAXDEVICES 12
/**
*
* @file int.c
* @author Guillermo Marcus
* @date 2009-04-05
* @brief Contains the interrupt handler.
*
*/
/*
* 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 <linux/version.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <stdbool.h>
#include "config.h"
#include "compat.h"
#include "pciDriver.h"
#include "common.h"
#include "int.h"
/*
* The ID between IRQ_SOURCE in irq_outstanding and the actual source is arbitrary.
* Therefore, be careful when communicating with multiple implementations.
*/
/* 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)
#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 */
#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)
/**
*
* If IRQ-handling is enabled, this function will be called from pcidriver_probe
* to initialize the IRQ handling (maps the BARs)
*
*/
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;
}
/* 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;
}
/**
*
* Frees/cleans up the data structures, called from pcidriver_remove()
*
*/
void pcidriver_remove_irq(pcidriver_privdata_t *privdata)
{
/* Release the IRQ handler */
if (privdata->irq_enabled != 0)
free_irq(privdata->pdev->irq, privdata);
pcidriver_irq_unmap_bars(privdata);
}
/**
*
* Unmaps the BARs and releases them
*
*/
void pcidriver_irq_unmap_bars(pcidriver_privdata_t *privdata)
{
int i;
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);
}
}
/**
*
* Acknowledge the interrupt by ACKing the interrupt generator.
*
* @returns true if the channel was acknowledged and the interrupt handler is done
*
*/
static bool check_acknowlegde_channel(pcidriver_privdata_t *privdata, int interrupt,
int channel, volatile unsigned int *bar)
{
if (!(bar[ABB_INT_STAT] & interrupt))
return false;
bar[ABB_INT_ENABLE] &= !interrupt;
if (interrupt == ABB_INT_IG)
bar[ABB_IG_CTRL] = ABB_IG_ACK;
/* Wake up the waiting loop in ioctl.c:ioctl_wait_interrupt() */
atomic_inc(&(privdata->irq_outstanding[channel]));
wake_up_interruptible(&(privdata->irq_queues[channel]));
return true;
}
/**
*
* 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)
{
/* Disable this thing, as it is irrelevant in our case
volatile unsigned int *bar;
*/
/* TODO: add subvendor / subsystem ids */
/* FIXME: guillermo: which ones? all? */
/* Test if we have to handle this interrupt */
/* if ((privdata->pdev->vendor != PCIEABB_VENDOR_ID) ||
(privdata->pdev->device != PCIEABB_DEVICE_ID))
return false;
*/
/* Acknowledge the device */
/* this is for ABB / wenxue DMA engine */
/*
bar = privdata->bars_kmapped[0];
mod_info_dbg("interrupt registers. ISR: %x, IER: %x\n", bar[ABB_INT_STAT], bar[ABB_INT_ENABLE]);
if (check_acknowlegde_channel(privdata, ABB_INT_CH0, ABB_IRQ_CH0, bar))
return true;
if (check_acknowlegde_channel(privdata, ABB_INT_CH1, ABB_IRQ_CH1, bar))
return true;
if (check_acknowlegde_channel(privdata, ABB_INT_IG, ABB_IRQ_IG, bar))
return true;
if (check_acknowlegde_channel(privdata, ABB_INT_CH0_TIMEOUT, ABB_IRQ_CH0, bar))
return true;
if (check_acknowlegde_channel(privdata, ABB_INT_CH1_TIMEOUT, ABB_IRQ_CH1, bar))
return true;
mod_info_dbg("err: interrupt registers. ISR: %x, IER: %x\n", bar[ ABB_INT_STAT ], bar[ ABB_INT_ENABLE ] );
*/
return false;
}
/**
*
* 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;
}
#if !defined(_PCIDRIVER_INT_H) && defined(ENABLE_IRQ)
#define _PCIDRIVER_INT_H
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
This diff is collapsed.
long pcidriver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
/**
*
* @file kmem.c
* @brief This file contains all functions dealing with kernel memory.
* @author Guillermo Marcus
* @date 2009-04-05
*
*/
#include <linux/version.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#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 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
/**
*
* Allocates new kernel memory including the corresponding management structure, makes
* it available via sysfs if possible.
*
*/
int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle)
{
pcidriver_kmem_entry_t *kmem_entry;
void *retptr;
/* 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;
kmem_entry->size = kmem_handle->size;
kmem_handle->handle_id = kmem_entry->id;
/* 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.
*/
retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) );
if (retptr == NULL)
goto kmem_alloc_mem_fail;
kmem_entry->cpua = (unsigned long)retptr;
kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
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;
kmem_alloc_mem_fail:
kfree(kmem_entry);
kmem_alloc_entry_fail:
return -ENOMEM;
}
/**
*
* Called via sysfs, frees kernel memory and the corresponding management structure
*
*/
int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle )
{
pcidriver_kmem_entry_t *kmem_entry;
/* 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 */
return pcidriver_kmem_free_entry(privdata, kmem_entry);
}
/**
*
* Called when cleaning up, frees all kernel memory and their corresponding management structure
*
*/
int pcidriver_kmem_free_all(pcidriver_privdata_t *privdata)
{
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);
pcidriver_kmem_free_entry(privdata, kmem_entry); /* spin lock inside! */
}
return 0;
}
/**
*
* Synchronize memory to/from the device (or in both directions).
*
*/
int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync )
{
pcidriver_kmem_entry_t *kmem_entry;
/* Find the associated kmem_entry for this buffer */
if ((kmem_entry = pcidriver_kmem_find_entry(privdata, &(kmem_sync->handle))) == NULL)
return -EINVAL; /* kmem_handle is not valid */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
switch (kmem_sync->dir) {
case PCIDRIVER_DMA_TODEVICE:
pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE );
break;
case PCIDRIVER_DMA_FROMDEVICE:
pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_FROMDEVICE );
break;
case PCIDRIVER_DMA_BIDIRECTIONAL:
pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_BIDIRECTIONAL );
pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_BIDIRECTIONAL );
break;
default:
return -EINVAL; /* wrong direction parameter */
}
#else
switch (kmem_sync->dir) {
case PCIDRIVER_DMA_TODEVICE:
pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE );
break;
case PCIDRIVER_DMA_FROMDEVICE:
pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_FROMDEVICE );
break;
case PCIDRIVER_DMA_BIDIRECTIONAL:
pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_BIDIRECTIONAL );
break;
default:
return -EINVAL; /* wrong direction parameter */
}
#endif
return 0; /* success */
}
/**
*
* Free the given kmem_entry and its memory.
*
*/
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;i<end;i++) {
struct page *kpage = pfn_to_page(i);
ClearPageReserved(kpage);
}
#endif
#endif
/* Release DMA memory */
pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle );
/* Remove the kmem list entry */
spin_lock( &(privdata->kmemlist_lock) );
list_del( &(kmem_entry->list) );
spin_unlock( &(privdata->kmemlist_lock) );
/* Release kmem_entry memory */
kfree(kmem_entry);
return 0;
}
/**
*
* Find the corresponding kmem_entry for the given kmem_handle.
*
*/
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;
/* 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);
if (entry->dma_handle == kmem_handle->pa) {
result = entry;
break;
}
}
spin_unlock(&(privdata->kmemlist_lock));
return result;
}
/**
*
* find the corresponding kmem_entry for the given id.
*
*/
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;
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;
}
}
spin_unlock(&(privdata->kmemlist_lock));
return result;
}
/**
*
* mmap() kernel memory to userspace.
*
*/
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 */
spin_lock(&(privdata->kmemlist_lock));
if (list_empty(&(privdata->kmem_list))) {
spin_unlock(&(privdata->kmemlist_lock));
mod_info("Trying to mmap a kernel memory buffer without creating it first!\n");
return -EFAULT;
}
kmem_entry = list_entry(privdata->kmem_list.prev, pcidriver_kmem_entry_t, list);
spin_unlock(&(privdata->kmemlist_lock));
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;
}
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",
(long unsigned int)virt_to_phys((void*)kmem_entry->cpua),
page_to_pfn(virt_to_page((void*)kmem_entry->cpua)));
ret = remap_pfn_range_cpua_compat(
vma,
vma->vm_start,
kmem_entry->cpua,
kmem_entry->size,
vma->vm_page_prot );
if (ret) {
mod_info("kmem remap failed: %d (%lx)\n", ret,kmem_entry->cpua);
return -EAGAIN;
}
return ret;
}
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( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync );
int pcidriver_kmem_free_all( pcidriver_privdata_t *privdata );
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle );
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id( pcidriver_privdata_t *privdata, int id );
int pcidriver_kmem_free_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry );
/**
*
* @file sysfs.c
* @brief This file contains the functions providing the SysFS-interface.
* @author Guillermo Marcus
* @date 2010-03-01
*
*/
#include <linux/version.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/kernel.h>
#include "compat.h"
#include "config.h"
#include "pciDriver.h"
#include "common.h"
#include "umem.h"
#include "kmem.h"
#include "sysfs.h"
static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry);
static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry);
/**
*
* 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 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
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;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)
/* owner was removed in 2.6.36 */
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
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)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
class_device_remove_file(privdata->class_dev, sysfs_attr);
kfree(sysfs_attr->attr.name);
#endif
}
static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
// pcidriver_privdata_t *privdata = (pcidriver_privdata_t *)cls->class_data;
/* 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);
return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id);
#else
return 0;
#endif
}
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
}
#ifdef ENABLE_IRQ
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)
{
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])) );
return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
}
#endif
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)
{
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;
return strlen(buf);
}
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)
{
pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
int temp = -1;
sscanf(buf, "%d", &temp);
if ((temp >= PCIDRIVER_BAR0) && (temp <= PCIDRIVER_BAR5))
privdata->mmap_area = temp;
return strlen(buf);
}
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)
{
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);
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;
/* 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;
pcidriver_kmem_free_entry(privdata, kmem_entry );
err:
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;
/* 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);
/* 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 );
}
spin_unlock(&(privdata->kmemlist_lock));
/* 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;
/* 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 );
/* 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));
}
spin_unlock( &(privdata->umemlist_lock) );
/* 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;
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;
pcidriver_umem_sgunmap(privdata, umem_entry);
err:
return strlen(buf);
}
#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
/* 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
This diff is collapsed.
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 );
#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 <linux/ioctl.h>
/* 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
#define PCIDRIVER_DMA_FROMDEVICE 2
/* 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
/* Types */
typedef struct {
unsigned long pa;
unsigned long size;
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 {
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;
typedef struct {
unsigned short vendor_id;
unsigned short device_id;
unsigned short bus;
unsigned short slot;
unsigned short devfn;
unsigned char interrupt_pin;
unsigned char interrupt_line;
unsigned int irq;
unsigned long bar_start[6];
unsigned long bar_length[6];
} pci_board_info;
/* 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
* <linux-src>/Documentation/ioctl-number.txt.
* previous SHL -> 'S' definition, conflicts with several devices,
* so I changed it to be pci -> 'p', in the range 0xA0-AF
*/
#define PCIDRIVER_IOC_MAGIC 'p'
#define PCIDRIVER_IOC_BASE 0xB0
#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 _IOW( 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, pci_board_info * )
/* Clear interrupt queues */
#define PCIDRIVER_IOC_CLEAR_IOQ _IO( PCIDRIVER_IOC_MAGIC, PCIDRIVER_IOC_BASE + 13 )
#endif
#ifndef PCIDRIVER_EXCEPTION_H_
#define PCIDRIVER_EXCEPTION_H_
/********************************************************************
*
* October 10th, 2006
* Guillermo Marcus - Universitaet Mannheim
*
* $Revision: 1.3 $
* $Date: 2008-01-11 10:06:09 $
*
*******************************************************************/
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.2 2007-02-09 17:01:28 marcus
* Added Exception as part of the standard exception hierarchy.
* Simplified description handling, now using an array.
*
* Revision 1.1 2006/10/13 17:18:33 marcus
* Implemented and tested most of C++ interface.
*
*******************************************************************/
#include <exception>
namespace pciDriver {
class Exception : public std::exception {
protected:
int type;
public:
enum Type {
UNKNOWN,
DEVICE_NOT_FOUND,
INVALID_BAR,
INTERNAL_ERROR,
OPEN_FAILED,
NOT_OPEN,
MMAP_FAILED,
ALLOC_FAILED,
SGMAP_FAILED,
INTERRUPT_FAILED
};
static const char* descriptions[];
Exception(Type t) : type(t) {}
inline int getType() { return type; }
char const *toString() { return descriptions[type]; }
virtual const char* what() const throw() { return descriptions[type]; }
};
}
#endif /*PCIDRIVER_EXCEPTION_H_*/
#ifndef KERNELMEMORY_H_
#define KERNELMEMORY_H_
/********************************************************************
*
* October 10th, 2006
* Guillermo Marcus - Universitaet Mannheim
*
* $Revision: 1.1 $
* $Date: 2006-10-13 17:18:34 $
*
*******************************************************************/
#include "PciDevice.h"
namespace pciDriver {
class KernelMemory {
friend class PciDevice;
protected:
unsigned long pa;
unsigned long size;
int handle_id;
void *mem;
PciDevice *device;
KernelMemory(PciDevice& device, unsigned int size);
public:
~KernelMemory();
/**
*
* @returns the physical address of the kernel memory.
*
*/
inline unsigned long getPhysicalAddress() { return pa; }
/**
*
* @returns the size of the kernel memory.
*
*/
inline unsigned long getSize() { return size; }
/**
*
* @returns the pointer to the memory.
*
*/
inline void *getBuffer() { return mem; }
enum sync_dir {
BIDIRECTIONAL = 0,
TO_DEVICE = 1,
FROM_DEVICE = 2
};
void sync(sync_dir dir);
};
}
#endif /*KERNELMEMORY_H_*/
#ifndef PD_PCIDEVICE_H_
#define PD_PCIDEVICE_H_
/********************************************************************
*
* October 10th, 2006
* Guillermo Marcus - Universitaet Mannheim
*
* $Revision: 1.5 $
* $Date: 2008-01-24 14:21:36 $
*
*******************************************************************/
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.4 2008-01-11 10:14:21 marcus
* Added intSource to the interrupt wait function.
*
* Revision 1.3 2006/11/17 18:51:08 marcus
* Support both types of SG-lists at runtime.
*
* Revision 1.2 2006/11/17 16:51:05 marcus
* Added page info values, and functions to get the bus and slot of a device,
* required for compatibility with the uelib.
*
* Revision 1.1 2006/10/13 17:18:34 marcus
* Implemented and tested most of C++ interface.
*
*******************************************************************/
#include <pthread.h>
namespace pciDriver {
// Forward references
class KernelMemory;
class UserMemory;
class PciDevice {
private:
unsigned int pagesize;
unsigned int pageshift;
unsigned int pagemask;
protected:
int handle;
int device;
char name[50];
pthread_mutex_t mmap_mutex;
public:
PciDevice(int number);
~PciDevice();
void open();
void close();
int getHandle();
unsigned short getBus();
unsigned short getSlot();
KernelMemory& allocKernelMemory( unsigned int size );
UserMemory& mapUserMemory( void *mem, unsigned int size, bool merged );
inline UserMemory& mapUserMemory( void *mem, unsigned int size )
{ return mapUserMemory(mem,size,true); }
inline void mmap_lock() { pthread_mutex_lock( &mmap_mutex ); }
inline void mmap_unlock() { pthread_mutex_unlock( &mmap_mutex ); }
void waitForInterrupt(unsigned int int_id);
void clearInterruptQueue(unsigned int int_id);
unsigned int getBARsize(unsigned int bar);
void *mapBAR(unsigned int bar);
void unmapBAR(unsigned int bar, void *ptr);
unsigned char readConfigByte(unsigned int addr);
unsigned short readConfigWord(unsigned int addr);
unsigned int readConfigDWord(unsigned int addr);
void writeConfigByte(unsigned int addr, unsigned char val);
void writeConfigWord(unsigned int addr, unsigned short val);
void writeConfigDWord(unsigned int addr, unsigned int val);
};
}
#endif /*PD_PCIDEVICE_H_*/
#ifndef USERMEMORY_H_
#define USERMEMORY_H_
/********************************************************************
*
* October 10th, 2006
* Guillermo Marcus - Universitaet Mannheim
*
* $Revision: 1.2 $
* $Date: 2006-11-17 18:50:17 $
*
*******************************************************************/
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.1 2006/10/13 17:18:34 marcus
* Implemented and tested most of C++ interface.
*
*******************************************************************/
#include "PciDevice.h"
namespace pciDriver {
class UserMemory {
friend class PciDevice;
private:
struct sg_entry {
unsigned long addr;
unsigned long size;
};
protected:
unsigned long vma;
unsigned long size;
int handle_id;
PciDevice *device;
int nents;
struct sg_entry *sg;
UserMemory(PciDevice& device, void *mem, unsigned int size, bool merged );
public:
~UserMemory();
enum sync_dir {
BIDIRECTIONAL = 0,
TO_DEVICE = 1,
FROM_DEVICE = 2
};
void sync(sync_dir dir);
inline unsigned int getSGcount() { return nents; }
inline unsigned long getSGentryAddress(unsigned int entry ) { return sg[entry].addr; }
inline unsigned long getSGentrySize(unsigned int entry ) { return sg[entry].size; }
};
}
#endif /*USERMEMORY_H_*/
#ifndef LIBPCIDRIVER_H_
#define LIBPCIDRIVER_H_
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.6 2008-01-11 10:07:53 marcus
* Removed unused int code.
* Modified int wait call to include the intSource to wait for. (experimental for cocurrent interrupts support).
*
* Revision 1.5 2007/02/09 16:59:52 marcus
* Added interrupt descriptor and related function.
*
* Revision 1.4 2006/11/13 16:44:51 marcus
* Added direction values as define's.
*
* Revision 1.3 2006/10/16 16:57:43 marcus
* Added nice comment at the start.
*
*******************************************************************/
#include <pthread.h>
/* Both APIs are in a single header */
/***********************************************************************************
* This is the C API
***********************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
/* Data types */
typedef struct {
int handle; /* PCI device handle */
int device; /* Device ID number */
char name[50]; /* Device Name (node) used */
pthread_mutex_t mmap_mutex; /* Mmap mutex used by the device */
} pd_device_t;
/* All Data types are redefined in the C API, even if they match the native driver interface */
typedef struct {
unsigned long pa;
unsigned long size;
void *mem;
int handle_id;
pd_device_t *pci_handle;
} pd_kmem_t;
typedef struct {
unsigned long addr;
unsigned long size;
} pd_umem_sgentry_t;
typedef struct {
unsigned long vma;
unsigned long size;
int handle_id;
int nents;
pd_umem_sgentry_t *sg;
pd_device_t *pci_handle;
} pd_umem_t;
/* Direction of a Sync operation */
#define PD_DIR_BIDIRECTIONAL 0
#define PD_DIR_TODEVICE 1
#define PD_DIR_FROMDEVICE 2
int pd_open( int dev, pd_device_t *pci_handle, char *dev_entry );
int pd_close( pd_device_t *pci_handle );
/* Kernel Memory Functions */
void *pd_allocKernelMemory( pd_device_t *pci_handle, unsigned int size, pd_kmem_t *kmem_handle );
int pd_freeKernelMemory( pd_kmem_t *kmem_handle );
/* User Memory Functions */
int pd_mapUserMemory( pd_device_t *pci_handle, void *mem, unsigned int size, pd_umem_t *umem_handle );
int pd_unmapUserMemory( pd_umem_t *umem_handle );
/* Sync Functions */
int pd_syncKernelMemory( pd_kmem_t *kmem_handle, int dir );
int pd_syncUserMemory( pd_umem_t *umem_handle, int dir );
/* Interrupt Function */
int pd_waitForInterrupt(pd_device_t *pci_handle , unsigned int int_id );
int pd_clearInterruptQueue(pd_device_t *pci_handle , unsigned int int_id );
/* PCI Functions */
int pd_getID( pd_device_t *pci_handle );
int pd_getBARsize( pd_device_t *pci_handle, unsigned int bar );
void *pd_mapBAR( pd_device_t *pci_handle, unsigned int bar );
int pd_unmapBAR( pd_device_t *pci_handle, unsigned int bar, void *ptr );
unsigned char pd_readConfigByte( pd_device_t *pci_handle, unsigned int addr );
unsigned short pd_readConfigWord( pd_device_t *pci_handle, unsigned int addr );
unsigned int pd_readConfigDWord( pd_device_t *pci_handle, unsigned int addr );
int pd_writeConfigByte( pd_device_t *pci_handle, unsigned int addr, unsigned char val );
int pd_writeConfigWord( pd_device_t *pci_handle, unsigned int addr, unsigned short val );
int pd_writeConfigDWord( pd_device_t *pci_handle, unsigned int addr, unsigned int val );
#ifdef __cplusplus
}
#endif
/***********************************************************************************
* This is the C++ API
***********************************************************************************/
#ifdef __cplusplus
#include "Exception.h"
#include "PciDevice.h"
#include "KernelMemory.h"
#include "UserMemory.h"
#include "pciDriver_compat.h"
#endif
#endif /*LIBPCIDRIVER_H_*/
#ifndef PCIDRIVER_COMPAT_H_
#define PCIDRIVER_COMPAT_H_
/***********************************************************************************
* C++ API compatible with older pciDriver interfaces
* Do not use for new designs!
*
* $Revision: 1.3 $
* $Date: 2006-11-17 18:53:23 $
*
***********************************************************************************/
/***********************************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.2 2006/11/13 12:29:49 marcus
* Backup commit. Compatibility section still requires testing.
*
* Revision 1.1 2006/10/10 14:46:49 marcus
* Initial commit of the new pciDriver for kernel 2.6
*
***********************************************************************************/
// Some needed forward references
namespace pciDriver {
class KernelMemory;
class UserMemory;
class PciDevice;
}
#ifndef KMEM_H
#define KMEM_H
/** This class is the main interface to allocate kernel memory
* with the driver. It performes ioctl calls to allocate and
* free the memory and mapps it into the user space.
*/
class KMem {
public:
/** The standard constructor.
* Initialises data.
*/
KMem();
/** The standard destructor.
* Ensure data is properly released.
*/
~KMem();
/** Constructor which initialises with allocated kernel memory.
* @param handle The device driver handle
* @param order The memory size in powers of 2
*/
KMem(int handle, int order);
/** Allocate kernel memory.
* @param handle The device driver handle
* @param order The memory size in powers of 2
* @return Integer 1 on success, 0 else.
*/
int Alloc(int handle, int order);
/** Free the kernel memory
* @return Integer 1 on success, 0 else.
*/
int Free(void);
/** Get the physical address of the allocated buffer
* @return The physical address of the buffer
*/
unsigned long GetPhysicalAddress(void);
/** Get the buffer address
* @return The virtual address of the buffer
*/
unsigned int *GetBuffer(void);
void Sync(void);
protected:
pciDriver::KernelMemory *km;
};
#endif /* KMEM_H */
#ifndef MEMORYPAGELIST_H
#define MEMORYPAGELIST_H
class MemoryPageList {
public:
/// The standard constructor
MemoryPageList();
/// The standard destructor
~MemoryPageList();
/** A constructor which builds up the pagelist for a
* given buffer.
* @param handle The device driver handle
* @param buffer Pointer which contains the virtual address of the buffer
* @param size The buffer size in byte
*/
MemoryPageList(int handle, unsigned int *buffer, unsigned int size);
/** Lock AND build up the list of buffer pages.
* @param handle The device driver handle
* @param buffer Pointer which contains the virtual address of the buffer
* @param size The buffer size in byte
* @return True on success, else false
*/
bool LockBuffer(int handle, unsigned int *buffer, unsigned int size);
/** Unlock the buffer
* @return True on success, else false
*/
bool UnlockBuffer(void);
/** Check if this page list is accociated to a buffer.
* @return True if this object contains a page list, else false
*/
bool IsUsed(void);
/** Get the size of the pagelist.
* @return The number of pages, 0 if no pagelist exists
*/
unsigned int GetNumberOfPages(void);
/** returns the physical addresses of the pages.
@param index The number of the buffer page
@return The physical address or 0 if the pagelist is not in use
*/
unsigned int GetPhysicalAddress(unsigned int index);
/// Access to the physical addresses of the pages.
unsigned int operator[] (unsigned int index);
/// Get the offset in the first page
unsigned int GetFirstPageOffset(void);
void Sync(void);
protected:
pciDriver::UserMemory *um;
int pagesize;
int pageshift;
};
#endif /* MEMORYPAGELIST_H */
#ifndef PCIDEVICE_H
#define PCIDEVICE_H
// --------x----------------------------x-----------------------------------x--
/** Class PciDevice
* This class represents a PCI device. It can open and close the device
* driver and get the user memory mapped PCI areas.
*/
class PciDevice {
public:
/** Standard constructor
Initialises data.*/
PciDevice();
/** Standard destructor
Initialises data.*/
~PciDevice();
/** Open the driver to accesss a device. Initialise data.
* @param deviceNr The logical number of the device to open.
* @return Integer 1 on success.
*/
int Open(unsigned int deviceNr);
/** Close the device driver. */
int Close(void);
/** Get access to a PCI memory bar which is mapped in user memory.
@return The virtual pointer to the PCI memory bar.
*/
volatile unsigned int *GetBarAccess(unsigned int barNr);
/** Checks if the driver is open.
* @return True if the driver is open else false.
*/
bool IsOpen(void);
unsigned char ReadConfigByte(unsigned int address);
unsigned short ReadConfigWord(unsigned int address);
unsigned int ReadConfigDWord(unsigned int address);
void WriteConfigByte(unsigned int address, unsigned char val);
void WriteConfigWord(unsigned int address, unsigned short val);
void WriteConfigDWord(unsigned int address, unsigned int val);
unsigned int GetBus(void);
unsigned int GetSlot(void);
unsigned short GetVendorId(void);
unsigned short GetDeviceId(void);
/** Performs a cast operation to an int to get easy access to the
* device handle.
*/
operator int();
static int GetNumberOfDevices(void);
protected:
pciDriver::PciDevice *dev;
int dev_number;
void *bar[6];
};
#endif /* PCIDEVICE_H */
#endif /*PCIDRIVER_COMPAT_H_*/
#editor backup files
*~
#makefile generated
*.o
*.so
*.a
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.1 2006/10/13 17:18:32 marcus
* Implemented and tested most of C++ interface.
*
*******************************************************************/
#include "Exception.h"
using namespace pciDriver;
const char* Exception::descriptions[] = {
"Unknown Exception",
"Device Not Found",
"Invalid BAR",
"Internal Error",
"Open failed",
"Not Open",
"Mmap failed",
"Alloc failed",
"SGmap failed",
"Interrupt failed"
};
/**
*
* @file KernelMemory.cpp
* @author Guillermo Marcus
* @date 2009-04-05
* @brief KernelMemory class implementation.
*
*/
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.3 2007-02-09 17:02:38 marcus
* Modified Exception handling, made simpler and more standard.
*
* Revision 1.2 2006/10/30 19:39:50 marcus
* Renamed variable to avoid confusions.
*
* Revision 1.1 2006/10/13 17:18:32 marcus
* Implemented and tested most of C++ interface.
*
*******************************************************************/
#include "KernelMemory.h"
#include "Exception.h"
#include "driver/pciDriver.h"
#include <sys/ioctl.h>
#include <sys/mman.h>
using namespace pciDriver;
/**
*
* Constructor of a KernelMemory object, allocates kernel memory of the specified
* size and mmaps it.
*
* @param size How much memory to allocate
*
*/
KernelMemory::KernelMemory(PciDevice& dev, unsigned int size)
{
void *m_ptr;
kmem_handle_t kh;
int dev_handle;
dev_handle = dev.getHandle();
this->device = &dev;
this->size = size;
/* Allocate */
kh.size = size;
if (ioctl(dev_handle, PCIDRIVER_IOC_KMEM_ALLOC, &kh) != 0)
throw Exception(Exception::ALLOC_FAILED);
handle_id = kh.handle_id;
pa = kh.pa;
/* Mmap */
/* This is not fully safe, as a separate process can still open the device independently.
* That will use a separate mutex and the race condition can arise.
* Posible fix: Do not allow the driver for mutliple openings of a device */
device->mmap_lock();
if (ioctl(dev_handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_KMEM) != 0)
goto pd_allockm_err;
m_ptr = mmap( 0, size, PROT_WRITE | PROT_READ, MAP_SHARED, dev_handle, 0 );
if ((m_ptr == MAP_FAILED) || (m_ptr == NULL))
goto pd_allockm_err;
this->mem = m_ptr;
device->mmap_unlock();
/* Success, Object created successfully */
return;
/* On error, unlock, deallocate buffer and throw an exception */
pd_allockm_err:
device->mmap_unlock();
ioctl(dev_handle, PCIDRIVER_IOC_KMEM_FREE, &kh);
throw Exception(Exception::ALLOC_FAILED);
}
/**
*
* Destructor of KernelMemory, unmaps the memory and frees it.
*
*/
KernelMemory::~KernelMemory()
{
kmem_handle_t kh;
/* Unmap */
munmap(this->mem, this->size);
/* Free buffer */
kh.handle_id = handle_id;
kh.size = size;
kh.pa = pa;
if (ioctl(device->getHandle(), PCIDRIVER_IOC_KMEM_FREE, &kh) != 0)
throw Exception(Exception::INTERNAL_ERROR);
}
/**
*
* Syncs the kernel memory to the device.
*
*/
void KernelMemory::sync(sync_dir dir)
{
kmem_sync_t ks;
ks.handle.handle_id = handle_id;
ks.handle.pa = pa;
ks.handle.size = size;
/* We assume (C++ API) dir === (Driver API) dir */
ks.dir = dir;
if (ioctl(device->getHandle(), PCIDRIVER_IOC_KMEM_SYNC, &ks) != 0)
throw Exception(Exception::INTERNAL_ERROR);
}
# Makefile for the pciDriver library
CC = gcc
CXX = g++
CFLAGS += -O2 -g -fPIC
CXXFLAGS += -O2 -g -fPIC
# Source files in this directory
SRC = $(wildcard *.cpp)
SRCC = $(wildcard *.c)
# Corresponding object file
OBJ = $(SRC:.cpp=.o)
OBJ += $(SRCC:.c=.o)
LIB ?= libpcidriver.so
LIBSTATIC ?= libpcidriver.a
INCDIR =../../include/pcie
INCDIR +=../../include/pcie/lib
INC = $(INCDIR)/%.h
LDINC += -L $(realpath .)
CFLAGS += $(addprefix -I, $(INCDIR))
CXXFLAGS += $(addprefix -I,i $(INCDIR))
###############################################################
# Target definitions
.PHONY: all clean
all: $(LIB) $(LIBSTATIC)
# Target for dynamic the library
$(LIB): $(OBJ)
@echo "## Creating dynamic library ##"
@echo -e "LD [.so] \t$@"
$(Q)$(CXX) -shared $(LDINC) $(LDFLAGS) $(CXXFLAGS) -o $@ $^
# Target for static the library
$(LIBSTATIC): $(OBJ)
@echo "## Creating static library ##"
@echo -e "LD [.a] \t$@"
$(Q)ar -r $(LIBSTATIC) $^
$(Q)ar -s $(LIBSTATIC)
%.o: %.cpp $(INC)
@echo -e "CC \t$<"
$(CXX) $(CXXFLAGS) -c -o $@ $<
%.o: %.c $(INC)
@echo -e "CC \t$<"
$(CC) $(CFLAGS) -c -o $@ $<
install:
@echo "INSTALL libpcidriver.so"
-$(Q)install -m 755 $(LIB) /usr/lib/
@echo "INSTALL libpcidriver.a"
-$(Q)install -m 755 $(LIBSTATIC) /usr/lib/
@echo "INSTALL /usr/include/pciDriver/lib"
-$(Q)mkdir -p /usr/include/pciDriver/lib
-$(Q)install -D -m 644 ../../include/pcie/lib/*.h /usr/include/pciDriver/lib
uninstall:
@echo "UNINSTALL libpcidriver.so"
-$(Q)rm -f /usr/lib/libpcidriver.so
@echo "UNINSTALL libpcidriver.a"
-$(Q)rm -f /usr/lib/libpcidriver.a
@echo "UNINSTALL /usr/include/pciDriver/lib"
-$(Q)rm -rf /usr/include/pciDriver/lib
-$(Q)rmdir /usr/include/pciDriver
clean:
@echo -e "CLEAN \t$(shell pwd)"
-$(Q)rm -f $(LIB) $(LIBSTATIC)
-$(Q)rm -f $(OBJ)
/**
*
* @file PciDevice.cpp
* @author Guillermo Marcus
* @date 2009-04-05
* @brief Represents the PCI device.
*
*/
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.4 2008-01-11 10:15:59 marcus
* Added intSource to the Wait for Interrupt call.
*
* Revision 1.3 2007/02/09 17:02:39 marcus
* Modified Exception handling, made simpler and more standard.
*
* Revision 1.2 2006/11/17 18:59:57 marcus
* Fixed offset when mmaping a BAR with a not page-aligned address.
* Added support for SGlist types at runtime.
*
* Revision 1.1 2006/10/13 17:18:31 marcus
* Implemented and tested most of C++ interface.
*
*******************************************************************/
#include "driver/pciDriver.h"
#include "PciDevice.h"
#include "Exception.h"
#include "KernelMemory.h"
#include "UserMemory.h"
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
using namespace pciDriver;
/**
*
* Construtor for the PciDevice. Checks if the specified device exists and initializes
* pagemask, pageshift and the mmap_mutex.
*
* @param number Number of the device, e.g. 0 for /dev/fpga0
*
*/
PciDevice::PciDevice(int number)
{
struct stat tmp_stat;
unsigned int temp;
device = number;
snprintf(name, sizeof(name), "/dev/fpga%d", number);
if (stat(name, &tmp_stat) < 0)
throw Exception( Exception::DEVICE_NOT_FOUND );
pthread_mutex_init(&mmap_mutex, NULL);
handle = -1;
pagesize = getpagesize();
// set pagemask and pageshift
for( pagemask=0, pageshift=0, temp = pagesize; temp != 1; pageshift++ ) {
temp = (temp >> 1);
pagemask = (pagemask << 1)+1;
}
}
/**
*
* Destructor of PciDevice. Closes the device if it is opened and destroys the mmap_mutex.
*
*/
PciDevice::~PciDevice()
{
// Close device if open
if (handle > 0)
this->close();
pthread_mutex_destroy(&mmap_mutex);
}
/**
*
* Gets the file handle.
*
* @returns file handle of the opened PCI device.
*
*/
int PciDevice::getHandle()
{
if (handle == -1)
throw Exception(Exception::NOT_OPEN);
return handle;
}
/**
*
* Opens the PCI device.
*
*/
void PciDevice::open()
{
int ret;
/* Check if the device is already opened and exit if yes */
if (handle != -1)
return;
if ((ret = ::open(name, O_RDWR)) < 0)
throw Exception( Exception::OPEN_FAILED );
handle = ret;
}
/**
*
* Close the PCI device.
*
*/
void PciDevice::close()
{
// do nothing, pass silently if closing a non-opened device.
if (handle != -1)
::close(handle);
handle = -1;
}
/**
*
* Allocates kernel memory of the specified size.
*
* @param size How much memory to allocate
* @returns A KernelMemory object
* @see KernelMemory
*
*/
KernelMemory& PciDevice::allocKernelMemory(unsigned int size)
{
KernelMemory *km = new KernelMemory(*this, size);
return *km;
}
/**
*
* Maps user memory of the specified size.
*
* @returns A UserMemory object
* @see UserMemory
*
*/
UserMemory& PciDevice::mapUserMemory(void *mem, unsigned int size, bool merged)
{
UserMemory *um = new UserMemory(*this, mem, size, merged);
return *um;
}
/**
*
* Waits for an interrupt.
*
*/
void PciDevice::waitForInterrupt(unsigned int int_id)
{
if (handle == -1)
throw Exception(Exception::NOT_OPEN);
if (ioctl(handle, PCIDRIVER_IOC_WAITI, int_id) != 0)
throw Exception(Exception::INTERRUPT_FAILED);
}
/**
*
* Clears the interrupt queue.
*
*/
void PciDevice::clearInterruptQueue(unsigned int int_id)
{
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
if (ioctl(handle, PCIDRIVER_IOC_CLEAR_IOQ, int_id) != 0)
throw Exception(Exception::INTERNAL_ERROR);
}
/**
*
* Gets the size of a BAR.
*
* @returns the size of the given BAR
*
*/
unsigned int PciDevice::getBARsize(unsigned int bar)
{
pci_board_info info;
unsigned int id;
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
if (bar > 5)
throw Exception( Exception::INVALID_BAR );
if (ioctl(handle, PCIDRIVER_IOC_PCI_INFO, &info) != 0)
throw Exception( Exception::INTERNAL_ERROR );
return info.bar_length[ bar ];
}
/**
*
* Gets the bus ID of the PCI device
*
*/
unsigned short PciDevice::getBus()
{
pci_board_info info;
if (handle == -1)
throw Exception(Exception::NOT_OPEN);
if (ioctl(handle, PCIDRIVER_IOC_PCI_INFO, &info) != 0)
throw Exception(Exception::INTERNAL_ERROR);
return info.bus;
}
/**
*
* Gets the slot of the PCI device
*
*/
unsigned short PciDevice::getSlot()
{
pci_board_info info;
if (handle == -1)
throw Exception(Exception::NOT_OPEN);
if (ioctl(handle, PCIDRIVER_IOC_PCI_INFO, &info) != 0)
throw Exception(Exception::INTERNAL_ERROR);
return info.slot;
}
/**
*
* Map the specified BAR.
*
* @param bar Which BAR to map (1-5).
* @returns A pointer to the mapped bar.
*
*/
void *PciDevice::mapBAR(unsigned int bar)
{
void *mem;
pci_board_info info;
if (handle == -1)
throw Exception(Exception::NOT_OPEN);
if (bar > 5)
throw Exception(Exception::INVALID_BAR);
if (ioctl(handle, PCIDRIVER_IOC_PCI_INFO, &info) != 0)
return NULL;
/* Mmap */
/* This is not fully safe, as a separate process can still open the device independently.
* That will use a separate mutex and the race condition can arise.
* Posible fix: Do not allow the driver for mutliple openings of a device */
mmap_lock();
if (ioctl(handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_PCI) != 0)
throw Exception(Exception::INTERNAL_ERROR);
if (ioctl( handle, PCIDRIVER_IOC_MMAP_AREA, PCIDRIVER_BAR0+bar) != 0)
throw Exception(Exception::INTERNAL_ERROR);
mem = mmap(0, info.bar_length[bar], PROT_WRITE | PROT_READ, MAP_SHARED, handle, 0);
mmap_unlock();
if ((mem == MAP_FAILED) || (mem == NULL))
throw Exception(Exception::MMAP_FAILED);
return mem;
}
/**
*
* Unmap the specified bar.
*
*/
void PciDevice::unmapBAR(unsigned int bar, void *ptr)
{
pci_board_info info;
if (handle == -1)
throw Exception(Exception::NOT_OPEN);
if (bar > 5)
throw Exception(Exception::INVALID_BAR);
if (ioctl(handle, PCIDRIVER_IOC_PCI_INFO, &info) != 0)
throw Exception(Exception::INVALID_BAR);
munmap(ptr, info.bar_length[bar]);
}
unsigned char PciDevice::readConfigByte(unsigned int addr)
{
pci_cfg_cmd cmd;
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
cmd.addr = addr;
cmd.size = PCIDRIVER_PCI_CFG_SZ_BYTE;
ioctl( handle, PCIDRIVER_IOC_PCI_CFG_RD, &cmd );
return cmd.val.byte;
}
unsigned short PciDevice::readConfigWord(unsigned int addr)
{
pci_cfg_cmd cmd;
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
cmd.addr = addr;
cmd.size = PCIDRIVER_PCI_CFG_SZ_WORD;
ioctl( handle, PCIDRIVER_IOC_PCI_CFG_RD, &cmd );
return cmd.val.word;
}
unsigned int PciDevice::readConfigDWord(unsigned int addr)
{
pci_cfg_cmd cmd;
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
cmd.addr = addr;
cmd.size = PCIDRIVER_PCI_CFG_SZ_DWORD;
ioctl( handle, PCIDRIVER_IOC_PCI_CFG_RD, &cmd );
return cmd.val.dword;
}
void PciDevice::writeConfigByte(unsigned int addr, unsigned char val)
{
pci_cfg_cmd cmd;
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
cmd.addr = addr;
cmd.size = PCIDRIVER_PCI_CFG_SZ_BYTE;
cmd.val.byte = val;
ioctl( handle, PCIDRIVER_IOC_PCI_CFG_WR, &cmd );
return;
}
void PciDevice::writeConfigWord(unsigned int addr, unsigned short val)
{
pci_cfg_cmd cmd;
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
cmd.addr = addr;
cmd.size = PCIDRIVER_PCI_CFG_SZ_WORD;
cmd.val.word = val;
ioctl( handle, PCIDRIVER_IOC_PCI_CFG_WR, &cmd );
return;
}
void PciDevice::writeConfigDWord(unsigned int addr, unsigned int val)
{
pci_cfg_cmd cmd;
if (handle == -1)
throw Exception( Exception::NOT_OPEN );
cmd.addr = addr;
cmd.size = PCIDRIVER_PCI_CFG_SZ_DWORD;
cmd.val.dword = val;
ioctl( handle, PCIDRIVER_IOC_PCI_CFG_WR, &cmd );
return;
}
/**
*
* @file UserMemory.cpp
* @author Guillermo Marcus
* @date 2009-04-05
* @brief UserMemory class
*
*/
/*******************************************************************
* Change History:
*
* $Log: not supported by cvs2svn $
* Revision 1.3 2007-02-09 17:02:38 marcus
* Modified Exception handling, made simpler and more standard.
*
* Revision 1.2 2006/11/17 18:57:27 marcus
* Added SGlist type support at runtime.
*
* Revision 1.1 2006/10/13 17:18:33 marcus
* Implemented and tested most of C++ interface.
*
*******************************************************************/
#include "UserMemory.h"
#include "Exception.h"
#include "driver/pciDriver.h"
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
using namespace pciDriver;
/**
*
* Constructor of UserMemory. Allocates a scatter/gather list and receives it
* from kernel space.
*
*/
UserMemory::UserMemory(PciDevice& dev, void *mem, unsigned int size, bool merged)
{
int i;
umem_handle_t uh;
umem_sglist_t sgl;
int dev_handle;
dev_handle = dev.getHandle();
this->device = &dev;
this->vma = reinterpret_cast<unsigned long>(mem);
this->size = size;
uh.vma = reinterpret_cast<unsigned long>(mem);
uh.size = size;
/* Lock and Map the memory to their pages */
if (ioctl(dev_handle, PCIDRIVER_IOC_UMEM_SGMAP, &uh) != 0)
throw Exception( Exception::SGMAP_FAILED );
this->handle_id = uh.handle_id;
/* Obtain the scatter/gather list for this memory */
sgl.handle_id = uh.handle_id;
sgl.type = ((merged) ? PCIDRIVER_SG_MERGED : PCIDRIVER_SG_NONMERGED);
sgl.nents = (size / getpagesize()) + 2;
sgl.sg = new umem_sgentry_t[ sgl.nents ];
if (ioctl(dev_handle, PCIDRIVER_IOC_UMEM_SGGET, &sgl) != 0) {
ioctl( dev_handle, PCIDRIVER_IOC_UMEM_SGUNMAP, &uh );
delete [] sgl.sg;
throw Exception( Exception::SGMAP_FAILED );
}
/* Copy the Scatter / Gather list to our structures */
this->nents = sgl.nents;
this->sg = new struct sg_entry[ sgl.nents ];
for(i = 0; i < sgl.nents; i++) {
this->sg[i].addr = sgl.sg[i].addr;
this->sg[i].size = sgl.sg[i].size;
}
/* Temp SG list for the driver is no longer needed */
delete [] sgl.sg;
}
/**
*
* Destructor of UserMemory. Deletes the scatter/gather list and unmaps it
* in kernel.
*
*/
UserMemory::~UserMemory()
{
umem_handle_t uh;
delete [] this->sg;
uh.handle_id = handle_id;
uh.vma = vma;
uh.size = size;
if (ioctl(device->getHandle(), PCIDRIVER_IOC_UMEM_SGUNMAP, &uh) != 0)
throw Exception(Exception::INTERNAL_ERROR);
}
/**
*
* Syncs the user memory from/to the device.
*
*/
void UserMemory::sync(sync_dir dir)
{
umem_handle_t uh;
/* We assume (C++ API) dir === (Driver API) dir */
uh.handle_id = handle_id;
uh.vma = vma;
uh.size = size;
uh.dir = dir;
if (ioctl(device->getHandle(), PCIDRIVER_IOC_UMEM_SYNC, &uh) != 0)
throw Exception( Exception::INTERNAL_ERROR );
}
This diff is collapsed.
This diff is collapsed.
###############################################################
#
# Change History:
#
# Revision 2.0 2013/03/28 abyszuk
# Moved to OHWR repo
#
# Revision 1.4 2006/11/17 18:56:44 marcus
# Added testCompatInterface.
#
# Revision 1.3 2006/10/30 19:40:32 marcus
# Added testPCIeBoard.
#
# Revision 1.2 2006/10/16 16:57:17 marcus
# Added testCinterface.
#
# Revision 1.1 2006/10/13 17:18:35 marcus
# Implemented and tested most of C++ interface.
#
###############################################################
# Makefile for the pciDriver test programs
CC = gcc
CXX = g++
CFLAGS += -O2 -g
CXXFLAGS += -O2 -g
INCDIR = ../../include/pcie
LDINC += $(addprefix -L ,$(LIBDIR))
LDFLAGS += -lpcidriver -lpthread
BINDIR = bin
CFLAGS += $(addprefix -I, $(INCDIR))
CXXFLAGS += $(addprefix -I,i $(INCDIR))
BINARIES = testCppInterface \
testDMA \
testPciDriver \
testPciDriverMod \
testCinterface \
benchmarkDevice
###############################################################
# Target definitions
.PHONY: all dirs clean
all: dirs $(BINARIES)
dirs:
$(Q)mkdir -p $(BINDIR)
# Relate all exec names to it exec in the bin dir
$(BINARIES) : % : $(BINDIR)/% ;
# Target for each exec from the object file
$(BINDIR)/%: %.cpp
@echo -e "LD \t$@"
$(Q)$(CXX) $(LDINC) $(CXXFLAGS) -o $@ $< $(LDFLAGS)
$(BINDIR)/testCinterface: testCinterface.c
@echo -e "LD \t$@"
$(Q)$(CC) $(LDINC) $(CFLAGS) -o $@ $< $(LDFLAGS)
$(BINDIR)/testPciDriver: testPciDriver.c
@echo -e "LD \t$@"
$(Q)$(CC) $(LDINC) $(CFLAGS) -o $@ $< $(LDFLAGS)
$(BINDIR)/testPciDriverMod: testPciDriverMod.c
@echo -e "LD \t$@"
$(Q)$(CC) $(LDINC) $(CFLAGS) -o $@ $< $(LDFLAGS)
clean:
@echo -e "CLEAN \t$(shell pwd)"
-$(Q)rm -f $(addprefix $(BINDIR)/,$(BINARIES))
-$(Q)rm -f $(BINDIR)/testCinterface
-$(Q)rm -f $(BINDIR)/testPciDriver
#include "lib/pciDriver.h"
#include <iostream>
#include <iomanip>
#include <limits>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
class BDA {
public:
uint32_t pa_h;
uint32_t pa_l;
uint32_t ha_h;
uint32_t ha_l;
uint32_t length;
uint32_t control;
uint32_t next_bda_h;
uint32_t next_bda_l;
uint32_t status;
void write(volatile uint32_t *base) {
base[0] = pa_h;
base[1] = pa_l;
base[2] = ha_h;
base[3] = ha_l;
base[4] = next_bda_h;
base[5] = next_bda_l;
base[6] = length;
base[7] = control; // control is written at the end, starts DMA
}
void reset(volatile uint32_t *base) {
base[7] = 0x0200000A;
}
inline void wait_finish(volatile uint32_t *base) {
//check for END bit
do {
status = base[8];
} while(!(status & 0x1));
}
};
void testDevice(int i);
void testDirectIO(pciDriver::PciDevice *dev, size_t total_size);
void testDMA(pciDriver::PciDevice *dev, size_t total_size);
void testDMAKernelMemory(uint32_t *bar0, uint32_t *bar2,
pciDriver::KernelMemory *km, const size_t buf_size,
const size_t test_len);
int main()
{
testDevice(0);
return 0;
}
void testDevice(int i)
{
pciDriver::PciDevice *dev;
//Total transfer data count for each test
const unsigned int dma_total_size = std::numeric_limits<unsigned int>::max();
const unsigned int dio_total_size = pow(10,9);
try {
std::cout << "Trying device " << i << " ... ";
dev = new pciDriver::PciDevice(i);
std::cout << "found" << std::endl;
// Open device
dev->open();
testDirectIO(dev, dio_total_size);
testDMA(dev, dma_total_size);
// Close device
dev->close();
delete dev;
} catch (pciDriver::Exception& e) {
std::cout << "failed: " << e.toString() << std::endl;
return;
}
}
void testDirectIO(pciDriver::PciDevice *dev,
size_t total_size)
{
uint32_t *bar2;
unsigned int bar2size;
time_t time_start;
time_t time_end;
double time_diff;
size_t bytes_sent;
try {
std::cout << "\n### Starting DirectIO test ###" << std::endl;
// Map BARs
bar2 = static_cast<uint32_t *>(dev->mapBAR(2));
// Get BAR sizes
bar2size = dev->getBARsize(2);
unsigned int bar2len = bar2size/sizeof(uint32_t);
uint32_t *buf = new uint32_t[bar2len];
const unsigned int size2mbyte = total_size/pow(2, 20);
std::cout << "Total transfer size: " << size2mbyte << " MBytes" << std::endl;
//start measurement
std::cout << "[Write test]" << std::endl;
time(&time_start);
for(bytes_sent = 0; bytes_sent < total_size; bytes_sent += bar2size) {
memcpy(bar2, buf, bar2size);
}
time(&time_end);
time_diff = difftime(time_end, time_start);;
std::cout << "Write time: " << std::fixed << std::setprecision(2) <<
time_diff << " seconds" << std::endl;
std::cout << "Write speed: " << std::fixed <<
(bytes_sent/time_diff)/pow(2,20) << " [MB/s]" << std::endl;
std::cout << "[Read test]" << std::endl;
time(&time_start);
for(bytes_sent = 0; bytes_sent < total_size; bytes_sent += bar2size) {
memcpy(buf, bar2, bar2size);
}
time(&time_end);
time_diff = difftime(time_end, time_start);;
std::cout << "Read time: " << std::fixed << std::setprecision(2) <<
time_diff << " seconds" << std::endl;
std::cout << "Read speed: " << std::fixed <<
(bytes_sent/time_diff)/pow(2,20) << " [MB/s]" << std::endl;
delete[] buf;
dev->unmapBAR(2,bar2);
} catch(pciDriver::Exception& e) {
std::cout << "Exception: " << e.toString() << std::endl;
}
}
void testDMA(pciDriver::PciDevice *dev,
size_t total_size)
{
pciDriver::KernelMemory *km;
uint32_t *bar0, *bar2;
//buffer sizes for DMA transactions
const size_t base_size = pow(2, 10); //1KByte
const size_t top_size = pow(2, 22);; //4MBytes
try {
// Map BARs
bar0 = static_cast<uint32_t *>(dev->mapBAR(0));
bar2 = static_cast<uint32_t *>(dev->mapBAR(2));
std::cout << "\n### Starting DMA test ###" << std::endl;
const unsigned int size2mbyte = total_size/pow(2, 20);
std::cout << "Total transfer size: " << size2mbyte << " MBytes" << std::endl;
for (size_t buffer_size = base_size; buffer_size <= top_size; buffer_size <<= 1) {
const unsigned int dma_length = buffer_size/pow(2, 10);
std::cout << "## DMA length: " << dma_length << " KB" << std::endl;
// Create buffers
km = &dev->allocKernelMemory(buffer_size);
// Test DDR SDRAM memory
testDMAKernelMemory(bar0, bar2, km, buffer_size, total_size);
// Delete buffer descriptors
delete km;
}
// Unmap BARs
dev->unmapBAR(0,bar0);
dev->unmapBAR(2,bar2);
} catch(pciDriver::Exception& e) {
std::cout << "Exception: " << e.toString() << std::endl;
}
}
void testDMAKernelMemory(
uint32_t *bar0,
uint32_t *bar2,
pciDriver::KernelMemory *km,
const size_t buf_size,
const size_t test_len)
{
BDA dma;
const unsigned int BASE_DMA_UP = (0x2C >> 2);
uint32_t * const us_engine = bar0 + BASE_DMA_UP;
const unsigned int BASE_DMA_DOWN = (0x50 >> 2);
uint32_t * const ds_engine = bar0 + BASE_DMA_DOWN;
const unsigned int bar_no = 2;
time_t time_start;
time_t time_end;
double time_diff;
size_t bytes_sent;
uint32_t *ptr = static_cast<uint32_t *>(km->getBuffer());
uint64_t ha = km->getPhysicalAddress();
uint64_t pa = 0x0;
// Setup a DMA transfer
dma.pa_h = (pa >> 32);
dma.pa_l = pa;
dma.ha_h = (ha >> 32);
dma.ha_l = ha;
dma.length = buf_size;
dma.control = 0x03008000 | (bar_no << 16);
dma.next_bda_h = 0x0;
dma.next_bda_l = 0x0;
std::cout << "[Write test]" << std::endl;
time(&time_start);
for(bytes_sent = 0; bytes_sent < test_len; bytes_sent += buf_size) {
dma.reset(ds_engine);
dma.write(ds_engine);
dma.wait_finish(ds_engine);
}
time(&time_end);
time_diff = difftime(time_end, time_start);;
std::cout << "Write time: " << std::fixed << std::setprecision(2) <<
time_diff << " seconds" << std::endl;
std::cout << "Write speed: " << std::fixed << std::setprecision(2) <<
(bytes_sent/time_diff)/pow(2,20) << " [MB/s]" << std::endl;
std::cout << "[Read test]" << std::endl;
time(&time_start);
for(bytes_sent = 0; bytes_sent < test_len; bytes_sent += buf_size) {
dma.reset(us_engine);
dma.write(us_engine);
dma.wait_finish(us_engine);
}
time(&time_end);
time_diff = difftime(time_end, time_start);;
std::cout << "Read time: " << std::fixed << std::setprecision(2) <<
time_diff << " seconds" << std::endl;
std::cout << "Read speed: " << std::fixed << std::setprecision(2) <<
(bytes_sent/time_diff)/pow(2,20) << " [MB/s]\n" << std::endl;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment