summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@suren.me>2023-05-25 22:41:04 +0200
committerSuren A. Chilingaryan <csa@suren.me>2023-05-25 22:41:04 +0200
commit6f4af841f6fdd099b97d071ae64c8be60f809456 (patch)
treed4f9a18b38e1ce3cfc0a5336215d5ce3afe830d2
downloadpcidev-6f4af841f6fdd099b97d071ae64c8be60f809456.tar.gz
pcidev-6f4af841f6fdd099b97d071ae64c8be60f809456.tar.bz2
pcidev-6f4af841f6fdd099b97d071ae64c8be60f809456.tar.xz
pcidev-6f4af841f6fdd099b97d071ae64c8be60f809456.zip
A sample event engine for pcitool (not requiring any PCIe hardware). Initial (barely tested and intended only as an example) release
-rw-r--r--.gitignore55
-rw-r--r--CMakeLists.txt86
-rw-r--r--README19
-rw-r--r--apps/CMakeLists.txt13
-rw-r--r--apps/grab.c63
-rw-r--r--cmake/version.cmake12
-rw-r--r--dma.c450
-rw-r--r--dma.h71
-rw-r--r--dma_private.h46
-rw-r--r--env.c17
-rw-r--r--env.h24
-rw-r--r--events.c592
-rw-r--r--events.h24
-rw-r--r--model.c78
-rw-r--r--model.h13
-rw-r--r--pcidev.h32
-rw-r--r--pcidev.pc10
-rw-r--r--pcidev.pc.in10
-rw-r--r--pcidev.spec57
-rw-r--r--pcidev.spec.in57
-rw-r--r--private.h135
-rw-r--r--registers.c103
-rw-r--r--registers.h36
-rw-r--r--version.h.in12
-rw-r--r--xml/CMakeLists.txt8
-rw-r--r--xml/pcidev.xml4
-rw-r--r--xml/pcidev/registers.xml11
27 files changed, 2038 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4030bbe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,55 @@
+*.tar.bz2
+*.cmd
+pciDriver.ko
+pciDriver.mod.c
+pci.d
+tools.d
+modules.order
+Module.symvers
+./pci
+.tmp_versions
+cli.d
+ipecamera.d
+pci.d
+tools.d
+*.d
+CMakeCache.txt
+CMakeFiles
+cmake_install.cmake
+Makefile
+*.so.*
+*.so
+*.a
+*.o
+*.mod
+install_manifest.txt
+apps/xilinx
+apps/pio_test
+apps/compare_to_value
+apps/heb_strip_bad_values
+*.out
+apps/check_counter
+apps/lorenzo_ipedma_test
+pcitool.pc
+config.h
+pcitool/pci
+version.h
+Doxyfile
+html
+pcilib/build.h
+build.h
+build
+pcipywrap.py
+pcipywrapPYTHON_wrap.c
+apps/test_multithread
+pcitool.spec
+misc/dkms.conf
+CPackConfig.cmake
+CPackSourceConfig.cmake
+_CPack_Packages
+pcilib_api.service
+pcilib_html.service
+pcilib.conf
+pcilib.sysconfig
+.spec
+.pc
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..1ed3e40
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,86 @@
+project(pcidev C)
+
+set(RELEASE "0")
+set(PCIDEV_VERSION "0.0.1")
+set(PCIDEV_ABI_VERSION "0")
+
+cmake_minimum_required(VERSION 2.6)
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
+
+add_definitions("-fPIC --std=gnu99 -Wall -O2 -gdwarf-2 -g3 -fno-omit-frame-pointer")
+
+find_package(PkgConfig REQUIRED)
+find_package(Threads REQUIRED)
+
+pkg_check_modules(PCILIB pcitool>=0.2 REQUIRED)
+exec_program("pkg-config --variable=plugindir pcitool" OUTPUT_VARIABLE PCILIB_PLUGIN_DIR)
+exec_program("pkg-config --variable=datadir pcitool" OUTPUT_VARIABLE PCILIB_DATA_DIR)
+exec_program("pkg-config --variable=modeldir pcitool" OUTPUT_VARIABLE PCILIB_MODEL_DIR)
+
+
+include(cmake/version.cmake)
+VERSION_TO_VARS(${PCIDEV_VERSION} PCIDEV_VERSION_MAJOR PCIDEV_VERSION_MINOR PCIDEV_VERSION_MICRO)
+
+include(GNUInstallDirs)
+
+add_subdirectory(xml)
+#add_subdirectory(apps)
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${UFODECODE_INCLUDE_DIRS}
+ ${PCILIB_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${UFODECODE_LIBRARY_DIRS}
+ ${PCILIB_LIBRARY_DIRS}
+)
+
+#set(HEADERS ${HEADERS} model.h registers.h events.h env.h private.h pcidev.h version.h)
+#add_library(pcidev SHARED model.c registers.c events.c env.c)
+
+set(HEADERS ${HEADERS} model.h registers.h dma.h events.h env.h dma_private.h private.h pcidev.h version.h)
+add_library(pcidev SHARED model.c registers.c dma.c events.c env.c)
+
+target_link_libraries(pcidev ${PCILIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${UFODECODE_LIBRARIES} )
+
+install(FILES pcidev.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+
+install(TARGETS pcidev
+ DESTINATION ${PCILIB_PLUGIN_DIR}
+)
+
+set(TARNAME "pcidev")
+set(PACKAGE_VERSION ${PCIDEV_VERSION})
+set(PACKAGE_NAME "${TARNAME}")
+set(PACKAGE_TARNAME "${TARNAME}")
+set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
+set(PACKAGE_BUGREPORT "http://ufo.kit.edu/ufo/newticket")
+
+set(CPACK_SOURCE_GENERATOR "TBZ2")
+set(CPACK_PACKAGE_CONTACT "Suren A. Chilingaryan <csa@suren.me>")
+if (${RELEASE} GREATER 0)
+ set(CPACK_PACKAGE_VERSION "${PACKAGE_VERSION}.${RELEASE}")
+else (${RELEASE} GREATER 0)
+ set(CPACK_PACKAGE_VERSION "${PACKAGE_VERSION}")
+endif (${RELEASE} GREATER 0)
+set(CPACK_SOURCE_IGNORE_FILES "/.bzr/;CMakeFiles;_CPack_Packages;cmake_install.cmake;CPack.*.cmake;CMakeCache.txt;install_manifest.txt;config.h$;.pc$;Makefile;.tar.bz2$;~$;${CPACK_SOURCE_IGNORE_FILES}")
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
+include(CPack)
+
+add_custom_target(dist_clean COMMAND ${CMAKE_MAKE_PROGRAM} clean WORKING_DIRECTORY ${CMAKE_CURRENT_DIR})
+add_custom_target(dist DEPENDS dist_clean COMMAND ${CMAKE_MAKE_PROGRAM} package_source)
+
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pcidev.pc.in ${CMAKE_CURRENT_BINARY_DIR}/pcidev.pc)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pcidev.spec.in ${CMAKE_CURRENT_BINARY_DIR}/pcidev.spec)
+
+install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/pcidev.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
+)
diff --git a/README b/README
new file mode 100644
index 0000000..df394f0
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+Sample event engine for pcilib driver platform
+ - This driver does not require real PCIe hardware and produces dummy data
+ * Register values are random
+ * DMA continuously return current buffer context with anyway modifying it (so it is either 0 or random content)
+ - The pciDriver should be loaded in 'dummy' mode with the following options
+ modprobe pciDriver DUMMY_DEVICE=1
+ - The driver implements all 3 APIs define by pcilib
+ * Register protocol generating random numbers (and ignoring 'set' values). The registers are defined via included xml definitions and by DMA engine
+ * DMA engine just prvoiding unmodified buffers in the memory as fast as possible
+ * Event engine building a histogram over pre-defined integration period and returning either this histograms or raw data via rawdata-callback mechanism.
+ The access to raw data via standard getdata mechanism is not implemented as it would inflict significant performance penalty. As well, get_next_event API
+ call is not implemented and is expected to be implemented (with substantial performance penalities) in pcitool via general-purpose implementation relying
+ on available 'stream' call. This is general-purpose implementation is not enabled yet, but is available and just need testing.
+ - The implementation of histograming is single-threaded and not optimized. Some thinking should be put here for real applications to ensure that
+ * It is fast enough to process expected data stream. Likely multi-processing will be needed here and can be implemented either with OpenMP (question of efficiacy)
+ or using dedicated threads (see ipecamera event engine for an example of trully multiprocessed event engine).
+ * If still processing was too slow for some period of time, the system can drop some data and recover operation (in provided example, it will misbehave after
+ losing the data for a first time)
+
diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt
new file mode 100644
index 0000000..761bb2b
--- /dev/null
+++ b/apps/CMakeLists.txt
@@ -0,0 +1,13 @@
+include_directories(
+ ${CMAKE_SOURCE_DIR}
+ ${PCILIB_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${CMAKE_BINARY_DIR}
+ ${PCILIB_LIBRARY_DIRS}
+)
+
+
+add_executable(grab grab.c)
+target_link_libraries(grab ${PCILIB_LIBRARIES} ipecamera)
diff --git a/apps/grab.c b/apps/grab.c
new file mode 100644
index 0000000..b891e2f
--- /dev/null
+++ b/apps/grab.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pcilib.h>
+#include <pcilib/error.h>
+
+#include <ipecamera.h>
+
+void log_error(void *arg, const char *file, int line, pcilib_log_priority_t prio, const char *format, va_list ap) {
+ vprintf(format, ap);
+ printf("\n");
+
+ if (prio == PCILIB_LOG_ERROR) {
+ printf("Exiting at [%s:%u]\n\n", file, line);
+ exit(-1);
+ }
+}
+
+
+int main() {
+ int err;
+ pcilib_event_id_t evid;
+ ipecamera_event_info_t info;
+ ipecamera_t *ipecamera;
+ size_t size;
+ void *data;
+ FILE *f;
+
+ pcilib_set_logger(PCILIB_LOG_WARNING, &log_error, NULL);
+
+ pcilib_t *pcilib = pcilib_open("/dev/fpga0", "ipecamera");
+ if (!pcilib) pcilib_error("Error opening device");
+
+ ipecamera = (ipecamera_t*)pcilib_get_event_engine(pcilib);
+ if (!ipecamera) pcilib_error("Failed to get ipecamera event engine");
+
+ err = ipecamera_set_buffer_size(ipecamera, 8);
+ if (err) pcilib_error("Error (%i) setting buffer size", err);
+
+ err = pcilib_start(pcilib, PCILIB_EVENTS_ALL, PCILIB_EVENT_FLAGS_DEFAULT);
+ if (err) pcilib_error("Error (%i) starting event engine", err);
+
+ err = pcilib_trigger(pcilib, PCILIB_EVENT0, 0, NULL);
+ if (err) pcilib_error("Error (%i) triggering event", err);
+
+ err = pcilib_get_next_event(pcilib, 100000, &evid, sizeof(info), (pcilib_event_info_t*)&info);
+ if (err) pcilib_error("Error (%i) while waiting for event", err);
+
+ data = pcilib_get_data(pcilib, evid, PCILIB_EVENT_DATA, &size);
+ if (!data) pcilib_error("Error getting event data");
+
+ printf("Writting %zu bytes to /dev/null\n", size);
+ f = fopen("/dev/null", "w");
+ if (f) {
+ fwrite(data, 1, size, f);
+ fclose(f);
+ }
+
+ err = pcilib_return_data(pcilib, evid, PCILIB_EVENT_DATA, data);
+ if (err) pcilib_error("Error returning data, data is possibly corrupted");
+
+ pcilib_stop(pcilib, PCILIB_EVENT_FLAGS_DEFAULT);
+}
diff --git a/cmake/version.cmake b/cmake/version.cmake
new file mode 100644
index 0000000..9023aef
--- /dev/null
+++ b/cmake/version.cmake
@@ -0,0 +1,12 @@
+SET(VERSION_REGEX "[0-9]+\\.[0-9]+\\.[0-9]+")
+
+MACRO(VERSION_TO_VARS version major minor patch)
+ IF(${version} MATCHES ${VERSION_REGEX})
+ STRING(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" ${major} "${version}")
+ STRING(REGEX REPLACE "^[0-9]+\\.([0-9])+\\.[0-9]+" "\\1" ${minor} "${version}")
+ STRING(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1" ${patch} "${version}")
+ ELSE(${version} MATCHES ${VERSION_REGEX})
+ MESSAGE("MACRO(VERSION_TO_VARS ${version} ${major} ${minor} ${patch}")
+ MESSAGE(FATAL_ERROR "Problem parsing version string, I can't parse it properly.")
+ ENDIF(${version} MATCHES ${VERSION_REGEX})
+ENDMACRO(VERSION_TO_VARS)
diff --git a/dma.c b/dma.c
new file mode 100644
index 0000000..94ff203
--- /dev/null
+++ b/dma.c
@@ -0,0 +1,450 @@
+#define _PCIDEV_DMA_C
+#define _BSD_SOURCE
+#define _DEFAULT_SOURCE
+#define _POSIX_C_SOURCE 199309L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sched.h>
+#include <time.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include <pcilib.h>
+#include <pcilib/kmem.h>
+#include <pcilib/error.h>
+#include <pcilib/debug.h>
+
+#include "dma.h"
+#include "dma_private.h"
+
+ // We will use get_block_ba for real bus-mapped buffers
+#define pcilib_kmem_get_block_addr pcilib_kmem_get_block_pa
+
+
+pcilib_dma_context_t *pcidev_dma_init(pcilib_t *pcilib, const char *model, const void *arg) {
+ pcilib_register_value_t version_value;
+
+ pcidev_dma_t *ctx = malloc(sizeof(pcidev_dma_t));
+
+ if (ctx) {
+ memset(ctx, 0, sizeof(pcidev_dma_t));
+ ctx->dmactx.pcilib = pcilib;
+
+ if (!pcilib_read_register(ctx->dmactx.pcilib, "dmaconf", "dma_timeout", &version_value))
+ ctx->version = version_value;
+ else
+ ctx->version = PCILIB_VERSION;
+
+
+ pcilib_info("Sample DMA engine, version %lu", ctx->version);
+
+ }
+
+ return (pcilib_dma_context_t*)ctx;
+}
+
+void pcidev_dma_free(pcilib_dma_context_t *vctx) {
+ pcidev_dma_t *ctx = (pcidev_dma_t*)vctx;
+
+ if (ctx) {
+ pcidev_dma_stop(vctx, PCILIB_DMA_ENGINE_ALL, PCILIB_DMA_FLAGS_DEFAULT);
+ free(ctx);
+ }
+}
+
+static void pcidev_dma_disable(pcidev_dma_t *ctx) {
+ // We calling this function before cleaning memory to ensure no DMA write operations into this memory might be performed after return from this function
+ return;
+}
+
+static void pcidev_dma_enable(pcidev_dma_t *ctx) {
+ // Sequence to enable DMA engine and start streaming data. Memory should be ready and configured
+ return;
+}
+
+static size_t pcidev_dma_find_buffer_by_bus_addr(pcidev_dma_t *ctx, uintptr_t bus_addr) {
+ size_t i;
+
+ for (i = 0; i < ctx->ring_size; i++) {
+ uintptr_t buf_addr = pcilib_kmem_get_block_addr(ctx->dmactx.pcilib, ctx->pages, i);
+
+ if (bus_addr == buf_addr)
+ return i;
+ }
+
+ return (size_t)-1;
+}
+
+
+int pcidev_dma_start(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma_flags_t flags) {
+ pcidev_dma_t *ctx = (pcidev_dma_t*)vctx;
+
+ pcidev_dma_desc_t *hw;
+
+ int preserve = 0;
+ pcilib_kmem_flags_t kflags;
+ pcilib_kmem_reuse_state_t reuse_desc, reuse_pages;
+ pcilib_kmem_handle_t *desc = NULL;
+ pcilib_kmem_handle_t *pages = NULL;
+
+ pcilib_register_value_t value;
+
+ uintptr_t dma_region = 0;
+
+ // Support single bank for now. We can support multiple banks with little modifications, e.g. bank number can be mapped to network port...
+ if (dma == PCILIB_DMA_ENGINE_INVALID) return 0;
+ else if (dma > 1) return PCILIB_ERROR_INVALID_BANK;
+
+ if (!ctx->started) ctx->started = 1;
+
+ if (flags&PCILIB_DMA_FLAG_PERSISTENT) ctx->preserve = 1;
+
+ if (ctx->pages) return 0;
+
+ // Get DMA configuration
+ if (!pcilib_read_register(ctx->dmactx.pcilib, "dmaconf", "dma_timeout", &value))
+ ctx->dma_timeout = value;
+ else
+ ctx->dma_timeout = PCIDEV_DMA_TIMEOUT;
+
+ if (!pcilib_read_register(ctx->dmactx.pcilib, "dmaconf", "dma_page_size", &value)) {
+ if (value % PCIDEV_PAGE_SIZE) {
+ pcilib_error("Invalid DMA page size (%lu) is configured", value);
+ return PCILIB_ERROR_INVALID_ARGUMENT;
+ }
+
+ ctx->page_size = value;
+ } else
+ ctx->page_size = PCIDEV_PAGE_SIZE;
+
+ if ((!pcilib_read_register(ctx->dmactx.pcilib, "dmaconf", "dma_pages", &value))&&(value > 0))
+ ctx->ring_size = value;
+ else
+ ctx->ring_size = PCIDEV_DMA_PAGES;
+
+ if (!pcilib_read_register(ctx->dmactx.pcilib, "dmaconf", "dma_region_low", &value)) {
+ dma_region = value;
+ if (!pcilib_read_register(ctx->dmactx.pcilib, "dmaconf", "dma_region_low", &value))
+ dma_region |= ((uintptr_t)value)<<32;
+ }
+
+ if (!pcilib_read_register(ctx->dmactx.pcilib, "dmaconf", "dma_flags", &value))
+ ctx->dma_flags = value;
+ else
+ ctx->dma_flags = 0;
+
+
+ // Allocate/map shared memory. There is two structures: 'desc' is descriptor which normally would be controlled by DMA engine to inform about its operations and 'pages' with actual data.
+ kflags = PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_EXCLUSIVE|PCILIB_KMEM_FLAG_HARDWARE|(ctx->preserve?PCILIB_KMEM_FLAG_PERSISTENT:0);
+
+ desc = pcilib_alloc_kernel_memory(ctx->dmactx.pcilib, PCILIB_KMEM_TYPE_CONSISTENT, 1, PCIDEV_DMA_DESCRIPTOR_SIZE, PCIDEV_DMA_DESCRIPTOR_ALIGNMENT, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_RING, 0x00), kflags);
+ if (dma_region)
+ pages = pcilib_alloc_kernel_memory(ctx->dmactx.pcilib, PCILIB_KMEM_TYPE_REGION_C2S, ctx->ring_size, ctx->page_size, dma_region, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_PAGES, 0x00), kflags);
+ else
+ pages = pcilib_alloc_kernel_memory(ctx->dmactx.pcilib, PCILIB_KMEM_TYPE_DMA_C2S_PAGE, ctx->ring_size, ctx->page_size, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_PAGES, 0x00), kflags);
+
+ if (!desc||!pages) {
+ if (pages) pcilib_free_kernel_memory(ctx->dmactx.pcilib, pages, PCILIB_KMEM_FLAG_REUSE);
+ if (desc) pcilib_free_kernel_memory(ctx->dmactx.pcilib, desc, PCILIB_KMEM_FLAG_REUSE);
+ pcilib_error("Can't allocate required kernel memory for PCIDEV DMA engine (%lu pages of %lu bytes + %lu byte descriptor)", ctx->ring_size, ctx->page_size, (unsigned long)PCIDEV_DMA_DESCRIPTOR_SIZE);
+ return PCILIB_ERROR_MEMORY;
+ }
+ reuse_desc = pcilib_kmem_is_reused(ctx->dmactx.pcilib, desc);
+ reuse_pages = pcilib_kmem_is_reused(ctx->dmactx.pcilib, pages);
+
+ hw = (pcidev_dma_desc_t*)pcilib_kmem_get_ua(ctx->dmactx.pcilib, desc);
+
+ // Try to get status of DMA engine if shared memory already initialized and check for consistency. Re-initialize or return the error if the memory is inconsistent.
+ if ((reuse_pages & PCILIB_KMEM_REUSE_PARTIAL)||(reuse_desc & PCILIB_KMEM_REUSE_PARTIAL)) {
+ pcidev_dma_disable(ctx);
+
+ pcilib_free_kernel_memory(ctx->dmactx.pcilib, pages, PCILIB_KMEM_FLAG_REUSE);
+ pcilib_free_kernel_memory(ctx->dmactx.pcilib, desc, PCILIB_KMEM_FLAG_REUSE);
+
+ if (((flags&PCILIB_DMA_FLAG_STOP) == 0)||(dma_region)) {
+ pcilib_error("Inconsistent DMA buffers are found (buffers are only partially re-used). This is very wrong, please stop DMA engine and correct configuration...");
+ return PCILIB_ERROR_INVALID_STATE;
+ }
+
+ pcilib_warning("Inconsistent DMA buffers are found (buffers are only partially re-used), reinitializing...");
+ desc = pcilib_alloc_kernel_memory(ctx->dmactx.pcilib, PCILIB_KMEM_TYPE_CONSISTENT, 1, PCIDEV_DMA_DESCRIPTOR_SIZE, PCIDEV_DMA_DESCRIPTOR_ALIGNMENT, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_RING, 0x00), kflags|PCILIB_KMEM_FLAG_MASS);
+ pages = pcilib_alloc_kernel_memory(ctx->dmactx.pcilib, PCILIB_KMEM_TYPE_DMA_C2S_PAGE, ctx->ring_size, ctx->page_size, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_PAGES, 0x00), kflags|PCILIB_KMEM_FLAG_MASS);
+
+ if (!desc||!pages) {
+ if (pages) pcilib_free_kernel_memory(ctx->dmactx.pcilib, pages, PCILIB_KMEM_FLAG_REUSE);
+ if (desc) pcilib_free_kernel_memory(ctx->dmactx.pcilib, desc, PCILIB_KMEM_FLAG_REUSE);
+ return PCILIB_ERROR_MEMORY;
+ }
+
+ hw = (pcidev_dma_desc_t*)pcilib_kmem_get_ua(ctx->dmactx.pcilib, desc);
+ } else if (reuse_desc != reuse_pages) {
+ pcilib_warning("Inconsistent DMA buffers (modes of ring and page buffers does not match), reinitializing....");
+ } else if (reuse_desc & PCILIB_KMEM_REUSE_REUSED) {
+ if ((reuse_desc & PCILIB_KMEM_REUSE_PERSISTENT) == 0) pcilib_warning("Lost DMA buffers are found (non-persistent mode), reinitializing...");
+ else if ((reuse_desc & PCILIB_KMEM_REUSE_HARDWARE) == 0) pcilib_warning("Lost DMA buffers are found (missing HW reference), reinitializing...");
+ else {
+ if (hw->page_count != ctx->ring_size)
+ pcilib_warning("Inconsistent DMA buffers are found (Number of allocated buffers (%lu) does not match current request (%lu)), reinitializing...", hw->page_count, PCIDEV_DMA_PAGES);
+ else
+ preserve = 1;
+ }
+ }
+
+ // get page size if default size was used
+ if (!ctx->page_size)
+ ctx->page_size = pcilib_kmem_get_block_size(ctx->dmactx.pcilib, pages, 0);
+
+ if (preserve) {
+ ctx->reused = 1;
+ ctx->preserve = 1;
+ } else {
+ ctx->reused = 0;
+
+ pcidev_dma_disable(ctx);
+ pcidev_dma_enable(ctx);
+
+ ctx->last_read = ctx->ring_size - 1;
+
+ // Normally this should be maintained by hardware
+ hw->page_count = ctx->ring_size;
+ hw->last_read_addr = pcilib_kmem_get_block_addr(ctx->dmactx.pcilib, pages, ctx->ring_size - 1);
+ hw->last_write_addr = pcilib_kmem_get_block_addr(ctx->dmactx.pcilib, pages, ctx->ring_size - 2);
+ }
+
+ ctx->desc = desc;
+ ctx->pages = pages;
+
+ ctx->last_read = pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_read_addr);
+
+ return 0;
+}
+
+int pcidev_dma_stop(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma_flags_t flags) {
+ pcilib_kmem_flags_t kflags;
+
+ pcidev_dma_t *ctx = (pcidev_dma_t*)vctx;
+
+ if (!ctx->started) return 0;
+
+ if ((dma != PCILIB_DMA_ENGINE_INVALID)&&(dma > 1)) return PCILIB_ERROR_INVALID_BANK;
+
+ // ignoring previous setting if flag specified
+ if (flags&PCILIB_DMA_FLAG_PERSISTENT) {
+ ctx->preserve = 0;
+ }
+
+ if (ctx->preserve) {
+ kflags = PCILIB_KMEM_FLAG_REUSE;
+ } else {
+ kflags = PCILIB_KMEM_FLAG_HARDWARE|PCILIB_KMEM_FLAG_PERSISTENT;
+
+ ctx->started = 0;
+
+ pcidev_dma_disable(ctx);
+ }
+
+ // Clean buffers
+ if (ctx->desc) {
+ pcilib_free_kernel_memory(ctx->dmactx.pcilib, ctx->desc, kflags);
+ ctx->desc = NULL;
+ }
+
+ if (ctx->pages) {
+ pcilib_free_kernel_memory(ctx->dmactx.pcilib, ctx->pages, kflags);
+ ctx->pages = NULL;
+ }
+
+ return 0;
+}
+
+
+int pcidev_dma_get_status(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma_engine_status_t *status, size_t n_buffers, pcilib_dma_buffer_status_t *buffers) {
+ size_t i;
+ pcidev_dma_t *ctx = (pcidev_dma_t*)vctx;
+
+ pcidev_dma_desc_t *hw = (pcidev_dma_desc_t*)pcilib_kmem_get_ua(ctx->dmactx.pcilib, ctx->desc);
+
+ if (!status) return -1;
+
+ pcilib_debug(DMA, "Current DMA status - last read: %4u, last_read_addr: %4u (0x%x), last_write_addr: %4lu (0x%lx)", ctx->last_read,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_read_addr), hw->last_read_addr,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_write_addr), hw->last_write_addr
+ );
+
+
+ status->started = ctx->started;
+ status->ring_size = ctx->ring_size;
+ status->buffer_size = ctx->page_size;
+ status->written_buffers = 0;
+ status->written_bytes = 0;
+
+ // For simplicity, we keep last_read here, and fix in the end
+ status->ring_tail = ctx->last_read;
+
+ status->ring_head = pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_write_addr);
+
+ if (status->ring_head == (size_t)-1) {
+ if (hw->last_write_addr) {
+ pcilib_warning("DMA is in unknown state, last_written_addr does not correspond any of available buffers");
+ return PCILIB_ERROR_FAILED;
+ }
+ status->ring_head = 0;
+ status->ring_tail = 0;
+ }
+
+ if (n_buffers > ctx->ring_size) n_buffers = ctx->ring_size;
+
+ if (buffers)
+ memset(buffers, 0, n_buffers * sizeof(pcilib_dma_buffer_status_t));
+
+ if (status->ring_head >= status->ring_tail) {
+ for (i = status->ring_tail + 1; i <= status->ring_head; i++) {
+ status->written_buffers++;
+ status->written_bytes += ctx->page_size;
+
+ if ((buffers)&&(i < n_buffers)) {
+ buffers[i].used = 1;
+ buffers[i].size = ctx->page_size;
+ buffers[i].first = 1;
+ buffers[i].last = 1;
+ }
+ }
+ } else {
+ for (i = 0; i <= status->ring_head; i++) {
+ status->written_buffers++;
+ status->written_bytes += ctx->page_size;
+
+ if ((buffers)&&(i < n_buffers)) {
+ buffers[i].used = 1;
+ buffers[i].size = ctx->page_size;
+ buffers[i].first = 1;
+ buffers[i].last = 1;
+ }
+ }
+
+ for (i = status->ring_tail + 1; i < status->ring_size; i++) {
+ status->written_buffers++;
+ status->written_bytes += ctx->page_size;
+
+ if ((buffers)&&(i < n_buffers)) {
+ buffers[i].used = 1;
+ buffers[i].size = ctx->page_size;
+ buffers[i].first = 1;
+ buffers[i].last = 1;
+ }
+ }
+ }
+
+ // We actually keep last_read in the ring_tail, so need to increase
+ if (status->ring_tail != status->ring_head) {
+ status->ring_tail++;
+ if (status->ring_tail == status->ring_size) status->ring_tail = 0;
+ }
+
+ return 0;
+}
+
+
+int pcidev_dma_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, pcilib_dma_callback_t cb, void *cbattr) {
+ int err, ret = PCILIB_STREAMING_REQ_PACKET;
+
+ pcilib_timeout_t wait = 0;
+ struct timeval start, cur;
+
+ pcilib_dma_flags_t packet_flags = PCILIB_DMA_FLAG_EOP;
+
+ size_t nodata_sleep;
+ struct timespec sleep_ts = {0};
+
+ size_t cur_read;
+
+ pcidev_dma_t *ctx = (pcidev_dma_t*)vctx;
+
+ // We auto-start if not started yet (then, we will also stop DMA on finishing the app)
+ err = pcidev_dma_start(vctx, dma, PCILIB_DMA_FLAGS_DEFAULT);
+ if (err) return err;
+
+ pcidev_dma_desc_t *hw = (pcidev_dma_desc_t*)pcilib_kmem_get_ua(ctx->dmactx.pcilib, ctx->desc);
+
+
+ switch (sched_getscheduler(0)) {
+ case SCHED_FIFO:
+ case SCHED_RR:
+ if (ctx->dma_flags&PCIDEV_DMA_FLAG_NOSLEEP)
+ nodata_sleep = 0;
+ else
+ nodata_sleep = PCIDEV_DMA_NODATA_SLEEP;
+ break;
+ default:
+ pcilib_info_once("Streaming DMA data using non real-time thread (may cause extra CPU load)", errno);
+ nodata_sleep = 0;
+ }
+
+ do {
+ switch (ret&PCILIB_STREAMING_TIMEOUT_MASK) {
+ case PCILIB_STREAMING_CONTINUE:
+ wait = ctx->dma_timeout;
+ break;
+ case PCILIB_STREAMING_WAIT:
+ wait = (timeout > ctx->dma_timeout)?timeout:ctx->dma_timeout;
+ break;
+ }
+
+ pcilib_debug(DMA, "Waiting for data in %4u - last_read: %4u, last_read_addr: %4u (0x%08x), last_written: %4u (0x%08x)", ctx->last_read + 1, ctx->last_read,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_read_addr), hw->last_read_addr,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_write_addr), hw->last_write_addr
+ );
+
+ // Wait for data. In this example we always have data, but normally DMA engine will maintain last_write_addr...
+ gettimeofday(&start, NULL);
+ memcpy(&cur, &start, sizeof(struct timeval));
+ while (((hw->last_write_addr == 0)||(hw->last_write_addr == hw->last_read_addr))&&((wait == PCILIB_TIMEOUT_INFINITE)||(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < wait))) {
+ if (nodata_sleep) {
+ sleep_ts.tv_nsec = nodata_sleep;
+ nanosleep(&sleep_ts, NULL);
+ }
+
+ gettimeofday(&cur, NULL);
+ }
+
+ // Failing out if we exited on timeout
+ if ((hw->last_write_addr == 0)||(hw->last_write_addr == hw->last_read_addr)) {
+ return (ret&PCILIB_STREAMING_FAIL)?PCILIB_ERROR_TIMEOUT:0;
+ }
+
+ // Getting next page to read
+ cur_read = ctx->last_read + 1;
+ if (cur_read == ctx->ring_size) cur_read = 0;
+
+ pcilib_debug(DMA, "Got buffer %4u - last read: %4u, last_read_addr: %4u (0x%x), last_written: %4u (0x%x)", cur_read, ctx->last_read,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_read_addr), hw->last_read_addr,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_write_addr), hw->last_write_addr
+ );
+
+ // In real case, we would need to sync to ensure that DMA transfer is finished and cashes are coherent
+ //if ((ctx->dma_flags&PCIDEV_DMA_FLAG_NOSYNC) == 0)
+ // pcilib_kmem_sync_block(ctx->dmactx.pcilib, ctx->pages, PCILIB_KMEM_SYNC_FROMDEVICE, cur_read);
+
+ void *buf = (void*)pcilib_kmem_get_block_ua(ctx->dmactx.pcilib, ctx->pages, cur_read);
+ ret = cb(cbattr, packet_flags, ctx->page_size, buf);
+ if (ret < 0) return -ret;
+
+ // Implementing a real DMA engine, here we might need to put buffer back into the DMA queue
+ hw->last_read_addr = pcilib_kmem_get_block_addr(ctx->dmactx.pcilib, ctx->pages, cur_read);
+ hw->last_write_addr = pcilib_kmem_get_block_addr(ctx->dmactx.pcilib, ctx->pages, ctx->last_read);
+
+ ctx->last_read = cur_read;
+
+ pcilib_debug(DMA, "Buffer returned %4u - last read: %4u, last_read_addr: %4u (0x%x), last_written: %4u (0x%x)", cur_read, ctx->last_read,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_read_addr), hw->last_read_addr,
+ pcidev_dma_find_buffer_by_bus_addr(ctx, hw->last_write_addr), hw->last_write_addr
+ );
+
+
+ } while (ret);
+
+ return 0;
+}
diff --git a/dma.h b/dma.h
new file mode 100644
index 0000000..c39e371
--- /dev/null
+++ b/dma.h
@@ -0,0 +1,71 @@
+#ifndef _PCIDEV_DMA_H
+#define _PCIDEV_DMA_H
+
+#include <stdio.h>
+#include <pcilib.h>
+#include <pcilib/version.h>
+#include <pcilib/dma.h>
+
+#include "version.h"
+
+#define PCIDEV_PAGE_SIZE 4096l /**< page size */
+#define PCIDEV_DMA_PAGES 512l /**< number of DMA pages in the ring buffer to allocate */
+#define PCIDEV_DMA_TIMEOUT 100000l /**< us, overrides PCILIB_DMA_TIMEOUT (actual hardware timeout is 50ms according to Lorenzo) */
+
+pcilib_dma_context_t *pcidev_dma_init(pcilib_t *ctx, const char *model, const void *arg);
+void pcidev_dma_free(pcilib_dma_context_t *vctx);
+
+int pcidev_dma_get_status(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma_engine_status_t *status, size_t n_buffers, pcilib_dma_buffer_status_t *buffers);
+
+int pcidev_dma_start(pcilib_dma_context_t *ctx, pcilib_dma_engine_t dma, pcilib_dma_flags_t flags);
+int pcidev_dma_stop(pcilib_dma_context_t *ctx, pcilib_dma_engine_t dma, pcilib_dma_flags_t flags);
+
+int pcidev_dma_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, pcilib_dma_callback_t cb, void *cbattr);
+double pcidev_dma_benchmark(pcilib_dma_context_t *vctx, pcilib_dma_engine_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction);
+
+# ifdef _PCIDEV_MODEL_C
+static const pcilib_dma_api_description_t pcidev_dma_api = {
+ PCILIB_VERSION,
+ pcidev_dma_init,
+ pcidev_dma_free,
+ pcidev_dma_get_status,
+ NULL,
+ NULL,
+ NULL,
+ pcidev_dma_start,
+ pcidev_dma_stop,
+ NULL,
+ pcidev_dma_stream_read,
+ NULL
+};
+
+static const pcilib_dma_engine_description_t pcidev_dma_engines[] = {
+ { 0, PCILIB_DMA_TYPE_PACKET, PCILIB_DMA_FROM_DEVICE, 32, "dma", NULL },
+ { 0 }
+};
+
+static const pcilib_register_bank_description_t pcidev_dma_banks[] = {
+ { PCILIB_REGISTER_BANK_DMACONF, PCILIB_REGISTER_PROTOCOL_SOFTWARE, PCILIB_BAR_NOBAR, 0, 0, 32, 0x1000, PCILIB_HOST_ENDIAN, PCILIB_HOST_ENDIAN, "0x%lx", "dmaconf", "DMA Configuration"},
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }
+};
+
+static const pcilib_register_description_t pcidev_dma_registers[] = {
+ {0x0000, 0, 32, PCILIB_VERSION, 0x00000000, PCILIB_REGISTER_R , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMACONF, "dma_version", "Version of DMA engine"},
+ {0x0004, 0, 32, PCIDEV_DMA_TIMEOUT, 0x00000000, PCILIB_REGISTER_RW , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMACONF, "dma_timeout", "Default DMA timeout"},
+ {0x0008, 0, 32, PCIDEV_DMA_PAGES, 0x00000000, PCILIB_REGISTER_RW , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMACONF, "dma_pages", "Number of buffers in DMA page ring"},
+ {0x000C, 0, 32, PCIDEV_PAGE_SIZE, 0x00000000, PCILIB_REGISTER_RW , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMACONF, "dma_page_size", "Size of a page in DMA page ring (multiple of 4K)"},
+ {0x0010, 0, 32, 0, 0x00000000, PCILIB_REGISTER_RW , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMACONF, "dma_region_low", "Low bits of static DMA I/O region"},
+ {0x0014, 0, 32, 0, 0x00000000, PCILIB_REGISTER_RW , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMACONF, "dma_region_hi", "High bits of static DMA I/O region"},
+ {0x0020, 0, 32, 0, 0xFFFFFFFF, PCILIB_REGISTER_RW , PCILIB_REGISTER_STANDARD, PCILIB_REGISTER_BANK_DMACONF, "dma_flags", "DMA Control Register"},
+ {0x0020, 0, 1, 0, 0xFFFFFFFF, PCILIB_REGISTER_RW , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMACONF, "dma_nosync", "Do not synchronize DMA pages"},
+ {0x0020, 1, 1, 0, 0xFFFFFFFF, PCILIB_REGISTER_RW , PCILIB_REGISTER_BITS, PCILIB_REGISTER_BANK_DMACONF, "dma_nosleep", "Do not sleep while there is no data"},
+ {0, 0, 0, 0, 0x00000000, 0, 0, 0, NULL, NULL}
+};
+
+static const pcilib_dma_description_t pcidev_dma =
+ { &pcidev_dma_api, pcidev_dma_banks, pcidev_dma_registers, pcidev_dma_engines, NULL, NULL, "pcidev", "Dummy DMA sample" };
+
+# endif /* _PCIDEV_MODEL_C */
+
+
+#endif /* _PCIDEV_DMA_H */
diff --git a/dma_private.h b/dma_private.h
new file mode 100644
index 0000000..53e951b
--- /dev/null
+++ b/dma_private.h
@@ -0,0 +1,46 @@
+#ifndef _PCIDEV_DMA_PRIVATE_H
+#define _PCIDEV_DMA_PRIVATE_H
+
+#include <pcilib/dma.h>
+
+#define PCIDEV_DMA_DESCRIPTOR_SIZE 128
+#define PCIDEV_DMA_DESCRIPTOR_ALIGNMENT 64
+
+#define PCIDEV_DMA_FLAG_NOSYNC 0x01 /**< Do not call kernel space for page synchronization */
+#define PCIDEV_DMA_FLAG_NOSLEEP 0x02 /**< Do not sleep in the loop while waiting for the data */
+#define PCIDEV_DMA_NODATA_SLEEP 100 /**< To keep CPU free, in nanoseconds */
+
+typedef uint32_t reg_t;
+typedef struct pcidev_dma_s pcidev_dma_t;
+typedef struct pcidev_dma_desc_s pcidev_dma_desc_t;
+
+struct pcidev_dma_desc_s {
+ uint32_t page_count;
+ volatile uintptr_t last_write_addr;
+ volatile uintptr_t last_read_addr;
+};
+
+struct pcidev_dma_s {
+ pcilib_dma_context_t dmactx;
+
+ pcilib_kmem_handle_t *desc; /**< in-memory status descriptor written by DMA engine upon operation progess */
+ pcilib_kmem_handle_t *pages; /**< collection of memory-locked pages for DMA operation */
+
+ pcilib_dma_engine_t rdma;
+
+ uint32_t version; /**< hardware revision */
+
+ int started; /**< indicates that DMA buffers are initialized and reading is allowed */
+ int writting; /**< indicates that we are in middle of writting packet */
+ int reused; /**< indicates that DMA was found intialized, buffers were reused, and no additional initialization is needed */
+ int preserve; /**< indicates that DMA should not be stopped during clean-up */
+
+ uint32_t dma_flags; /**< Various operation flags, see PCIDEV_DMA_FLAG_* */
+ size_t dma_timeout; /**< DMA timeout, PCIDEV_DMA_TIMEOUT is used by default */
+ size_t dma_pages; /**< Number of DMA pages in ring buffer to allocate */
+
+ size_t ring_size, page_size; /**< Number of pages in ring buffer and the size of a single DMA page */
+ size_t last_read;
+};
+
+#endif /* _PCIDEV_DMA_PRIVATE_H */
diff --git a/env.c b/env.c
new file mode 100644
index 0000000..a1ebbce
--- /dev/null
+++ b/env.c
@@ -0,0 +1,17 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "env.h"
+
+
+static const char *env_cache[PCIDEV_MAX_ENV] = {0};
+
+const char *pcidev_getenv(pcidev_env_t env, const char *var) {
+ if (!env_cache[env]) {
+ const char *var_env = getenv(var);
+ env_cache[env] = var_env?var_env:(void*)-1;
+ return var_env;
+ }
+
+ return (env_cache[env] == (void*)-1)?NULL:env_cache[env];
+}
+
diff --git a/env.h b/env.h
new file mode 100644
index 0000000..7ab3d52
--- /dev/null
+++ b/env.h
@@ -0,0 +1,24 @@
+#ifndef _PCIDEV_ENV_H
+#define _PCIDEV_ENV_H
+
+typedef enum {
+ PCIDEV_DEBUG_RAW_FRAMES_ENV,
+ PCIDEV_DEBUG_BROKEN_FRAMES_ENV,
+ PCIDEV_DEBUG_RAW_PACKETS_ENV,
+ PCIDEV_DEBUG_HARDWARE_ENV,
+ PCIDEV_DEBUG_FRAME_HEADERS_ENV,
+ PCIDEV_DEBUG_API_ENV,
+ PCIDEV_MAX_ENV
+} pcidev_env_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *pcidev_getenv(pcidev_env_t env, const char *var);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIDEV_ENV_H */
diff --git a/events.c b/events.c
new file mode 100644
index 0000000..1500a24
--- /dev/null
+++ b/events.c
@@ -0,0 +1,592 @@
+#define _PCIDEV_IMAGE_C
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include <pcilib.h>
+#include <pcilib/tools.h>
+#include <pcilib/error.h>
+#include <pcilib/event.h>
+#include <pcilib/cpu.h>
+#include <pcilib/timing.h>
+
+#include "private.h"
+#include "model.h"
+#include "events.h"
+
+
+#define FIND_REG(var, bank, name) \
+ ctx->var = pcilib_find_register(pcilib, bank, name); \
+ if (ctx->var == PCILIB_REGISTER_INVALID) { \
+ err = PCILIB_ERROR_NOTFOUND; \
+ pcilib_error("Unable to find a %s register", name); \
+ }
+
+
+#define GET_REG(reg, var) \
+ if (!err) { \
+ err = pcilib_read_register_by_id(pcilib, ctx->reg, &var); \
+ if (err) { \
+ pcilib_error("Error reading %s register", model_info->registers[ctx->reg].name); \
+ } \
+ }
+
+#define SET_REG(reg, val) \
+ if (!err) { \
+ err = pcilib_write_register_by_id(pcilib, ctx->reg, val); \
+ if (err) { \
+ pcilib_error("Error writting %s register", model_info->registers[ctx->reg].name); \
+ } \
+ }
+
+#define CHECK_REG(reg, check) \
+ if (!err) { \
+ err = pcilib_read_register_by_id(pcilib, ctx->reg, &value); \
+ if (err) { \
+ pcilib_error("Error reading %s register", model_info->registers[ctx->reg].name); \
+ } \
+ if (value != check) { \
+ pcilib_error("Unexpected value (0x%lx) of register %s", value, model_info->registers[ctx->reg].name); \
+ err = PCILIB_ERROR_INVALID_DATA; \
+ } \
+ }
+
+#define CHECK_STATUS()
+ //CHECK_REG(status_reg, PCIDEV_GET_EXPECTED_STATUS(ctx))
+
+#define CHECK_VALUE(value, val) \
+ if ((!err)&&(value != val)) { \
+ pcilib_error("Unexpected value (0x%x) in data stream (0x%x is expected)", value, val); \
+ err = PCILIB_ERROR_INVALID_DATA; \
+ }
+
+#define CHECK_FLAG(flag, check, ...) \
+ if ((!err)&&(!(check))) { \
+ pcilib_error("Unexpected value (0x%x) of " flag, __VA_ARGS__); \
+ err = PCILIB_ERROR_INVALID_DATA; \
+ }
+
+#define LOCK(lock_name) \
+ err = pcilib_try_lock(ctx->lock_name##_lock); \
+ if (err) { \
+ pcilib_error("IPECamera is busy"); \
+ return PCILIB_ERROR_BUSY; \
+ } \
+ ctx->lock_name##_locked = 1;
+
+#define UNLOCK(lock_name) \
+ if (ctx->lock_name##_locked) { \
+ pcilib_unlock(ctx->lock_name##_lock); \
+ ctx->lock_name##_locked = 0; \
+ }
+
+
+pcilib_context_t *pcidev_init(pcilib_t *pcilib) {
+ int err = 0;
+
+ pcidev_t *ctx = malloc(sizeof(pcidev_t));
+
+ if (ctx) {
+ memset(ctx, 0, sizeof(pcidev_t));
+
+ ctx->run_lock = pcilib_get_lock(pcilib, PCILIB_LOCK_FLAGS_DEFAULT, "pcidev");
+ ctx->stream_lock = pcilib_get_lock(pcilib, PCILIB_LOCK_FLAGS_DEFAULT, "pcidev/stream");
+ ctx->trigger_lock = pcilib_get_lock(pcilib, PCILIB_LOCK_FLAGS_DEFAULT, "pcidev/trigger");
+
+ if (!ctx->run_lock||!ctx->stream_lock||!ctx->trigger_lock) {
+ free(ctx);
+ pcilib_error("Failed to initialize locks to protect pcidev operation");
+ return NULL;
+ }
+
+ ctx->buffer_size = PCIDEV_DEFAULT_BUFFER_SIZE;
+ ctx->rdma = PCILIB_DMA_ENGINE_INVALID;
+
+ if (err) {
+ free(ctx);
+ return NULL;
+ }
+ }
+
+ return (pcilib_context_t*)ctx;
+}
+
+void pcidev_free(pcilib_context_t *vctx) {
+ if (vctx) {
+ pcidev_t *ctx = (pcidev_t*)vctx;
+ pcidev_stop(vctx, PCILIB_EVENT_FLAGS_DEFAULT);
+
+ if (ctx->trigger_lock)
+ pcilib_return_lock(vctx->pcilib, PCILIB_LOCK_FLAGS_DEFAULT, ctx->trigger_lock);
+
+ if (ctx->stream_lock)
+ pcilib_return_lock(vctx->pcilib, PCILIB_LOCK_FLAGS_DEFAULT, ctx->stream_lock);
+
+ if (ctx->run_lock)
+ pcilib_return_lock(vctx->pcilib, PCILIB_LOCK_FLAGS_DEFAULT, ctx->run_lock);
+
+ free(ctx);
+ }
+}
+
+pcilib_dma_context_t *pcidev_init_dma(pcilib_context_t *vctx) {
+ const pcilib_model_description_t *model_info = pcilib_get_model_description(vctx->pcilib);
+ if ((!model_info->dma)||(!model_info->dma->api)||(!model_info->dma->api->init)) {
+ pcilib_error("The DMA engine is not configured in model");
+ return NULL;
+ }
+
+ return model_info->dma->api->init(vctx->pcilib, "pcidev", NULL);
+}
+
+
+int pcidev_set_buffer_size(pcidev_t *ctx, int size) {
+ if (ctx->started) {
+ pcilib_error("Can't change buffer size while grabbing");
+ return PCILIB_ERROR_INVALID_REQUEST;
+ }
+
+ if (size < 2) {
+ pcilib_error("The buffer size is too small");
+ return PCILIB_ERROR_INVALID_REQUEST;
+ }
+
+ if ((size^(size-1)) < size) {
+ pcilib_error("The buffer size is not power of 2");
+ }
+
+ ctx->buffer_size = size;
+
+ return 0;
+}
+
+int pcidev_start(pcilib_context_t *vctx, pcilib_event_t event_mask, pcilib_event_flags_t flags) {
+ int i;
+ int err = 0;
+
+ pcidev_t *ctx = (pcidev_t*)vctx;
+// pcilib_register_value_t value;
+
+ if (!ctx) {
+ pcilib_error("pcidev event engine is not initialized");
+ return PCILIB_ERROR_NOTINITIALIZED;
+ }
+
+ if (ctx->started) {
+ pcilib_error("pcidev event grabbing is already started");
+ return PCILIB_ERROR_INVALID_REQUEST;
+ }
+
+ LOCK(run);
+
+ pcidev_debug(API, "pcidev: starting");
+
+ ctx->event_id = 0;
+ ctx->buffer_pos = 0;
+ ctx->process_data = (flags&PCILIB_EVENT_FLAG_RAW_DATA_ONLY)?0:1;
+ ctx->event_size = PCIDEV_EVENT_SIZE * sizeof(size_t);
+
+ memset(&ctx->eio_timestamp, 0, sizeof(struct timeval));
+
+ ctx->buffer = malloc(ctx->event_size * ctx->buffer_size);
+ if (!ctx->buffer) {
+ pcidev_stop(vctx, PCILIB_EVENT_FLAGS_DEFAULT);
+ pcilib_error("Unable to allocate ring buffer (%lu bytes)", ctx->event_size * ctx->buffer_size);
+ return PCILIB_ERROR_MEMORY;
+ }
+
+ ctx->event = (pcidev_event_t*)malloc(ctx->buffer_size * sizeof(pcidev_event_t));
+ if (!ctx->event) {
+ pcidev_stop(vctx, PCILIB_EVENT_FLAGS_DEFAULT);
+ pcilib_error("Unable to allocate event-info buffer");
+ return PCILIB_ERROR_MEMORY;
+ }
+
+ memset(ctx->event, 0, ctx->buffer_size * sizeof(pcidev_event_t));
+
+ for (i = 0; i < ctx->buffer_size; i++) {
+ err = pthread_rwlock_init(&ctx->event[i].mutex, NULL);
+ if (err) break;
+ }
+
+ ctx->event_mutex_destroy = i;
+
+ if (!err) {
+ ctx->rdma = pcilib_find_dma_by_addr(vctx->pcilib, PCILIB_DMA_FROM_DEVICE, PCIDEV_DMA_ADDRESS);
+ if (ctx->rdma == PCILIB_DMA_ENGINE_INVALID) {
+ err = PCILIB_ERROR_NOTFOUND;
+ pcilib_error("The C2S channel of DMA Engine (%u) is not found", PCIDEV_DMA_ADDRESS);
+ } else {
+ err = pcilib_start_dma(vctx->pcilib, ctx->rdma, PCILIB_DMA_FLAGS_DEFAULT);
+ if (err) {
+ ctx->rdma = PCILIB_DMA_ENGINE_INVALID;
+ pcilib_error("Failed to initialize C2S channel of DMA Engine (%u)", PCIDEV_DMA_ADDRESS);
+ }
+ }
+ }
+
+ if (err) {
+ pcidev_stop(vctx, PCILIB_EVENT_FLAGS_DEFAULT);
+ return err;
+ }
+
+ if (vctx->params.autostop.duration) {
+ gettimeofday(&ctx->autostop.timestamp, NULL);
+ ctx->autostop.timestamp.tv_usec += vctx->params.autostop.duration % 1000000;
+ if (ctx->autostop.timestamp.tv_usec > 999999) {
+ ctx->autostop.timestamp.tv_sec += 1 + vctx->params.autostop.duration / 1000000;
+ ctx->autostop.timestamp.tv_usec -= 1000000;
+ } else {
+ ctx->autostop.timestamp.tv_sec += vctx->params.autostop.duration / 1000000;
+ }
+ }
+
+ if (vctx->params.autostop.max_events) {
+ ctx->autostop.evid = vctx->params.autostop.max_events;
+ }
+
+ ctx->started = 1;
+
+ // Technically here we need to start DAQ and processing threads. DAQ thread will pull raw data from DMA and distribute it between processing threads...
+ // Current example doesn't spawn threads, but expects that enough buffering is performed by hardware and we can just allow user pull the data directly from hardware...
+ // So, here pcidev_stream combines functions of 3 threads: DAQ thread pulling and buffering raw data, processing threads generating event data, and stream function feading processed event data to user callback
+
+ pcidev_debug(API, "pcidev: started");
+
+ return 0;
+}
+
+
+int pcidev_stop(pcilib_context_t *vctx, pcilib_event_flags_t flags) {
+ int i;
+ pcidev_t *ctx = (pcidev_t*)vctx;
+
+ if (!ctx) {
+ pcilib_error("pcidev event engine is not initialized");
+ return PCILIB_ERROR_NOTINITIALIZED;
+ }
+
+ ctx->run_streamer = 0;
+ if (flags&PCILIB_EVENT_FLAG_STOP_ONLY) return 0;
+
+ pcidev_debug(API, "pcidev: stopping");
+
+ if (ctx->started) {
+ // Here we would also normally wait untill all spawned threads are terminated
+
+ while (ctx->streaming) {
+ usleep(PCIDEV_NOEVENT_SLEEP);
+ }
+ }
+
+ if (ctx->event_mutex_destroy) {
+ for (i = 0; i < ctx->event_mutex_destroy; i++) {
+ pthread_rwlock_destroy(&ctx->event[i].mutex);
+ }
+ ctx->event_mutex_destroy = 0;
+ }
+
+ if (ctx->rdma != PCILIB_DMA_ENGINE_INVALID) {
+ pcilib_stop_dma(vctx->pcilib, ctx->rdma, PCILIB_DMA_FLAGS_DEFAULT);
+ ctx->rdma = PCILIB_DMA_ENGINE_INVALID;
+ }
+
+
+ if (ctx->event) {
+ free(ctx->event);
+ ctx->event = NULL;
+ }
+
+ if (ctx->buffer) {
+ free(ctx->buffer);
+ ctx->buffer = NULL;
+ }
+
+ memset(&ctx->autostop, 0, sizeof(pcidev_autostop_t));
+ memset(&ctx->eio_timestamp, 0, sizeof(struct timeval));
+
+ ctx->event_id = 0;
+ ctx->buffer_pos = 0;
+ ctx->started = 0;
+
+ pcidev_debug(API, "pcidev: stopped");
+ UNLOCK(run);
+
+ return 0;
+}
+
+int pcidev_reset(pcilib_context_t *vctx) {
+ pcidev_t *ctx = (pcidev_t*)vctx;
+
+ if (!ctx) {
+ pcilib_error("pcidev event engine is not initialized");
+ return PCILIB_ERROR_NOTINITIALIZED;
+ }
+
+ // Technically we need to stop event generation, reset device, drop all pending data on DMA channels, restart device
+
+ pcidev_debug(API, "pcidev: reset done");
+ return 0;
+}
+
+
+int pcidev_trigger(pcilib_context_t *vctx, pcilib_event_t event, size_t trigger_size, void *trigger_data) {
+ return PCILIB_ERROR_NOTSUPPORTED;
+}
+
+
+typedef struct {
+ pcidev_t *pcidev;
+ pcilib_event_callback_t event_cb;
+ void *event_cb_ctx;
+} pcidev_data_callback_user_t;
+
+static int pcidev_data_callback(void *user, pcilib_dma_flags_t flags, size_t bufsize, void *buf) {
+ int i;
+ int res;
+ int eoi = 0; //**< end-of-integration flag */
+
+ struct timeval packet_timestamp; //**< hardware timestamp of the packet (set by FPGA) */
+ static unsigned long packet_id = 0;
+
+ pcidev_data_callback_user_t *datacb_ctx = (pcidev_data_callback_user_t*)user;
+
+ pcidev_t *ctx = datacb_ctx->pcidev;
+ pcilib_event_callback_t event_callback = datacb_ctx->event_cb;
+ void *event_callback_ctx = datacb_ctx->event_cb_ctx;
+
+ packet_id++;
+ pcidev_debug_buffer(RAW_PACKETS, bufsize, buf, PCILIB_DEBUG_BUFFER_MKDIR, "event%4lu/packet%9lu", ctx->event_id, packet_id);
+
+ // Packet analysis may come here
+ // This sample illustrates time-based events (e.g. histograms). Instead, we also can have size based events when we receive data until expected size is reached (e.g. from camera)
+ // We can check packet consistency here (e.g. verify magic number, packet seq number, and check CRC/MD5), return negative error code on the error
+ // If we deal with multi-packet events and started to receive data not from the beginning of the event, here we would skip data until new event begins by returning PCILIB_STREAMING_CONTINUE
+ // If we deal with multi-packet events and the event data is not yet complete, we would buffer it and return PCILIB_STREAMING_REQ_FRAGMENT
+
+
+ // We need to get the actual packet time-stamp here. The demo uses host timestamp instead
+ gettimeofday(&packet_timestamp, NULL);
+
+ // Check if integration time is over.
+ if (pcilib_timecmp(&packet_timestamp, &ctx->eio_timestamp) > 0) {
+ // initialize timing on a first packet
+ if (ctx->eio_timestamp.tv_sec == 0) {
+ gettimeofday(&ctx->eio_timestamp, NULL);
+ pcilib_add_timeout(&ctx->eio_timestamp, PCIDEV_INTEGRATION_PERIOD);
+ } else
+ eoi = 1;
+ }
+
+ if ((!ctx->run_streamer)
+ ||((ctx->event_id == ctx->autostop.evid)&&(ctx->event_id))
+ ||(pcilib_check_deadline(&ctx->autostop.timestamp, 0))
+ ) {
+ ctx->run_streamer = 0;
+ return PCILIB_STREAMING_STOP;
+ }
+
+ if (eoi) {
+ pcidev_event_info_t info;
+
+ ctx->event[ctx->buffer_pos].event.info.type = PCILIB_EVENT0;
+ ctx->event[ctx->buffer_pos].event.info.flags = 0;
+
+ memcpy(&info, ctx->event + ctx->buffer_pos, sizeof(pcidev_event_info_t));
+
+ if (event_callback) {
+ res = event_callback(ctx->event_id + 1, (pcilib_event_info_t*)&info, event_callback_ctx);
+ if (res <= 0) {
+ ctx->run_streamer = 0;
+ if (res < 0) return -res;
+ return PCILIB_STREAMING_STOP;
+ }
+ }
+
+
+ ctx->buffer_pos = (++ctx->event_id) % ctx->buffer_size;
+
+ // Compute next period. We don't handle case when the processing is not fast enough and we get behind... We should in real driver, it often happens.
+ pcilib_add_timeout(&ctx->eio_timestamp, PCIDEV_INTEGRATION_PERIOD);
+ }
+
+ if (ctx->process_data) {
+ if (eoi) {
+ memset(ctx->buffer + ctx->buffer_pos * ctx->event_size, 0, ctx->event_size);
+ }
+
+ for (i = 0; i < bufsize; i++) {
+ ((size_t*)ctx->buffer)[((uint8_t*)buf)[i]]++;
+ }
+ }
+
+ if (ctx->pcictx.params.rawdata.callback) {
+ res = ctx->pcictx.params.rawdata.callback(ctx->event_id, (pcilib_event_info_t*)(ctx->event + ctx->buffer_pos), (eoi?PCILIB_EVENT_FLAG_EOF:PCILIB_EVENT_FLAGS_DEFAULT), bufsize, buf, ctx->pcictx.params.rawdata.user);
+ if (res <= 0) {
+ if (res < 0) return res;
+ return PCILIB_STREAMING_STOP;
+ }
+ }
+
+ return PCILIB_STREAMING_REQ_FRAGMENT;
+}
+
+
+
+int pcidev_stream(pcilib_context_t *vctx, pcilib_event_callback_t callback, void *user) {
+ int err = 0;
+ int do_stop = 0;
+
+ pcidev_t *ctx = (pcidev_t*)vctx;
+
+ if (!ctx) {
+ pcilib_error("pcidev event engine is not initialized");
+ return PCILIB_ERROR_NOTINITIALIZED;
+ }
+
+ pcidev_debug(API, "pcidev: start streaming");
+
+ pcidev_data_callback_user_t datacb_ctx;
+ datacb_ctx.pcidev = ctx;
+ datacb_ctx.event_cb = callback;
+ datacb_ctx.event_cb_ctx = user;
+
+ LOCK(stream);
+
+ ctx->streaming = 1;
+ ctx->run_streamer = 1;
+
+ if (!ctx->started) {
+ err = pcidev_start(vctx, PCILIB_EVENTS_ALL, PCILIB_EVENT_FLAGS_DEFAULT);
+ if (err) {
+ ctx->streaming = 0;
+ return err;
+ }
+
+ do_stop = 1;
+ }
+
+ while (ctx->run_streamer) {
+ err = pcilib_stream_dma(ctx->pcictx.pcilib, ctx->rdma, 0, 0, PCILIB_DMA_FLAG_MULTIPACKET, PCIDEV_DMA_TIMEOUT, &pcidev_data_callback, &datacb_ctx);
+ if (err) {
+ if (err == PCILIB_ERROR_TIMEOUT) {
+ if (pcilib_check_deadline(&ctx->autostop.timestamp, 0)) {
+ ctx->run_streamer = 0;
+ break;
+ }
+ usleep(PCIDEV_NOEVENT_SLEEP);
+ } else pcilib_error("DMA error while reading pcidev events, error: %i", err);
+ }
+ }
+
+ ctx->run_streamer = 0;
+ ctx->streaming = 0;
+
+ UNLOCK(stream);
+
+ pcidev_debug(API, "pcidev: streaming finished");
+
+ if (do_stop) {
+ pcidev_stop(vctx, PCILIB_EVENT_FLAGS_DEFAULT);
+ }
+
+ return err;
+}
+
+static int pcidev_resolve_event_id(pcidev_t *ctx, pcilib_event_id_t evid) {
+ pcilib_event_id_t diff;
+
+ if (evid > ctx->event_id) {
+ diff = (((pcilib_event_id_t)-1) - ctx->event_id) + evid;
+ if (diff >= ctx->buffer_size) return -1;
+ } else {
+ diff = ctx->event_id - evid;
+ if (diff >= ctx->buffer_size) return -1;
+ }
+
+ return (evid - 1) % ctx->buffer_size;
+}
+
+int pcidev_get_data(pcilib_context_t *vctx, pcilib_event_id_t event_id, pcilib_event_data_type_t data_type, size_t arg_size, void *arg, size_t *size, void **ret) {
+ int buf_ptr;
+ pcidev_t *ctx = (pcidev_t*)vctx;
+
+ void *data = *ret;
+
+ if (!ctx) {
+ pcilib_error("pcidev event engine is not initialized");
+ return PCILIB_ERROR_NOTINITIALIZED;
+ }
+
+ pcidev_debug(API, "pcidev: get_data");
+
+ buf_ptr = pcidev_resolve_event_id(ctx, event_id);
+ if (buf_ptr < 0) {
+ pcidev_debug(HARDWARE, "The data of the requested event %zu has been meanwhile overwritten", event_id);
+ return PCILIB_ERROR_OVERWRITTEN;
+ }
+
+ switch ((pcidev_data_type_t)data_type) {
+ case PCIDEV_RAW_DATA:
+ pcilib_error("The raw data is not buffered for the performance reasons and can only be accessed via rawdata-callback mechanism");
+ return PCILIB_ERROR_NOTSUPPORTED;
+ case PCIDEV_STANDARD_DATA:
+ // We will lock the data for non-raw data to prevent ocasional overwritting.
+ pthread_rwlock_rdlock(&ctx->event[buf_ptr].mutex);
+
+ // Check if data is still not overwritten
+ if (pcidev_resolve_event_id(ctx, event_id) < 0) {
+ pthread_rwlock_unlock(&ctx->event[buf_ptr].mutex);
+ pcidev_debug(HARDWARE, "The data of the requested event %zu has been meanwhile overwritten", event_id);
+ return PCILIB_ERROR_OVERWRITTEN;
+ }
+
+ if (data) {
+ if ((!size)||(*size < ctx->event_size)) {
+ pthread_rwlock_unlock(&ctx->event[buf_ptr].mutex);
+ pcilib_warning("The size of event data is too big (%zu bytes) for user supplied buffer (%zu bytes)", ctx->event_size, (size?*size:0));
+ return PCILIB_ERROR_TOOBIG;
+ }
+ memcpy(data, ctx->buffer + buf_ptr * ctx->event_size, ctx->event_size);
+ pthread_rwlock_unlock(&ctx->event[buf_ptr].mutex);
+ *size = ctx->event_size;
+ return 0;
+ }
+
+ if (size) *size = ctx->event_size;
+ *ret = ctx->buffer + buf_ptr * ctx->event_size;
+ return 0;
+ default:
+ pcilib_error("Unknown data type (%li) is requested", data_type);
+ return PCILIB_ERROR_INVALID_REQUEST;
+ }
+}
+
+int pcidev_return_data(pcilib_context_t *vctx, pcilib_event_id_t event_id, pcilib_event_data_type_t data_type, void *data) {
+ pcidev_t *ctx = (pcidev_t*)vctx;
+
+ if (!ctx) {
+ pcilib_error("pcidev event engine is not initialized");
+ return PCILIB_ERROR_NOTINITIALIZED;
+
+ }
+
+ if ((pcidev_data_type_t)data_type == PCIDEV_RAW_DATA) {
+ return PCILIB_ERROR_NOTSUPPORTED;
+ } else {
+ int buf_ptr = (event_id - 1) % ctx->buffer_size;
+ pthread_rwlock_unlock(&ctx->event[buf_ptr].mutex);
+ }
+
+ pcidev_debug(API, "pcidev: return_data");
+
+ return 0;
+}
diff --git a/events.h b/events.h
new file mode 100644
index 0000000..5d09d74
--- /dev/null
+++ b/events.h
@@ -0,0 +1,24 @@
+#ifndef _PCIDEV_EVENTS_H
+#define _PCIDEV_EVENTS_H
+
+#include <stdio.h>
+
+#include <pcilib.h>
+#include "pcidev.h"
+
+pcilib_context_t *pcidev_init(pcilib_t *pcilib);
+void pcidev_free(pcilib_context_t *ctx);
+
+pcilib_dma_context_t *pcidev_init_dma(pcilib_context_t *ctx);
+
+int pcidev_reset(pcilib_context_t *ctx);
+int pcidev_start(pcilib_context_t *ctx, pcilib_event_t event_mask, pcilib_event_flags_t flags);
+int pcidev_stop(pcilib_context_t *ctx, pcilib_event_flags_t flags);
+int pcidev_trigger(pcilib_context_t *ctx, pcilib_event_t event, size_t trigger_size, void *trigger_data);
+int pcidev_stream(pcilib_context_t *vctx, pcilib_event_callback_t callback, void *user);
+//int pcidev_next_event(pcilib_context_t *vctx, pcilib_timeout_t timeout, pcilib_event_id_t *evid, size_t info_size, pcilib_event_info_t *info);
+
+int pcidev_get_data(pcilib_context_t *ctx, pcilib_event_id_t event_id, pcilib_event_data_type_t data_type, size_t arg_size, void *arg, size_t *size, void **buf);
+int pcidev_return_data(pcilib_context_t *ctx, pcilib_event_id_t event_id, pcilib_event_data_type_t data_type, void *data);
+
+#endif /* _PCIDEV_EVENTS_H */
diff --git a/model.c b/model.c
new file mode 100644
index 0000000..28391a2
--- /dev/null
+++ b/model.c
@@ -0,0 +1,78 @@
+#define _PCIDEV_MODEL_C
+
+#include <stdio.h>
+#include <strings.h>
+
+#include <pcilib.h>
+#include <pcilib/model.h>
+
+#include "version.h"
+
+#include "registers.h"
+#include "dma.h"
+#include "events.h"
+#include "model.h"
+
+
+
+
+static const pcilib_event_description_t pcidev_events[] = {
+ {PCILIB_EVENT0, "new_event", ""},
+ {0, NULL, NULL}
+};
+
+static const pcilib_event_data_type_description_t pcidev_data_types[] = {
+ {PCIDEV_RAW_DATA, PCILIB_EVENT0, "raw", "raw data from device" },
+ {PCIDEV_STANDARD_DATA, PCILIB_EVENT0, "std", "processed data" },
+ {0, 0, NULL, NULL}
+};
+
+
+pcilib_event_api_description_t pcidev_event_api = {
+ PCIDEV_VERSION,
+
+ pcidev_init,
+ pcidev_free,
+
+ pcidev_init_dma,
+
+ pcidev_reset,
+ pcidev_start,
+ pcidev_stop,
+ pcidev_trigger,
+
+ pcidev_stream,
+ NULL,
+ pcidev_get_data,
+ pcidev_return_data
+};
+
+
+static const pcilib_model_description_t pcidev_models[] = {{
+ PCILIB_EVENT_INTERFACE_VERSION,
+ &pcidev_event_api,
+ &pcidev_dma,
+ NULL, // Registers are defined in XML
+ NULL, // Banks are defined in XML
+ &pcidev_protocols,
+ NULL, // Register ranges are defined in XML
+ NULL,
+ NULL,
+ pcidev_events,
+ pcidev_data_types,
+ "pcidev",
+ "Sample Plugin"
+}, { 0 }};
+
+
+const pcilib_model_description_t *pcilib_get_event_model(pcilib_t *pcilib, unsigned short vendor_id, unsigned short device_id, const char *model) {
+ // Enumeration call
+ if ((!vendor_id)&&(!device_id)&&(!model)) {
+ return pcidev_models;
+ }
+
+ if ((model)&&(strcasecmp(model, "pcidev")))
+ return NULL;
+
+ return &pcidev_models[0];
+}
diff --git a/model.h b/model.h
new file mode 100644
index 0000000..131d748
--- /dev/null
+++ b/model.h
@@ -0,0 +1,13 @@
+#ifndef _PCIDEV_MODEL_H
+#define _PCIDEV_MODEL_H
+
+#include <stdio.h>
+#include <pcilib/model.h>
+
+//#define PCIDEV_DEBUG
+
+#define PCIDEV_DMA_ADDRESS 0 /**< Address of DMA engine to use for communication */
+
+const pcilib_model_description_t *pcilib_get_event_model(pcilib_t *pcilib, unsigned short vendor_id, unsigned short device_id, const char *model);
+
+#endif /* _PCIDEV_MODEL_H */
diff --git a/pcidev.h b/pcidev.h
new file mode 100644
index 0000000..55700c2
--- /dev/null
+++ b/pcidev.h
@@ -0,0 +1,32 @@
+#ifndef _PCIDEV_H
+#define _PCIDEV_H
+
+typedef struct pcidev_s pcidev_t;
+
+typedef struct {
+ unsigned int size;
+} pcidev_image_dimensions_t;
+
+typedef enum {
+ PCIDEV_STANDARD_DATA = 0,
+ PCIDEV_RAW_DATA = 1,
+} pcidev_data_type_t;
+
+
+typedef struct {
+ pcilib_event_info_t info;
+} pcidev_event_info_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int pcidev_set_buffer_size(pcidev_t *ctx, int size);
+pcilib_event_id_t pcidev_get_last_event_id(pcidev_t *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _PCIDEV_H */
diff --git a/pcidev.pc b/pcidev.pc
new file mode 100644
index 0000000..d94c94c
--- /dev/null
+++ b/pcidev.pc
@@ -0,0 +1,10 @@
+prefix=/usr/local
+exec_prefix=/usr/local/bin
+libdir=/usr/local/lib/pcilib
+includedir=/usr/local/include
+
+Name: pcidev
+Description: Sample event engine for pcilib
+Version: 0.0.1
+Libs: -L/usr/local/lib/pcilib -lpcidev
+Cflags: -I/usr/local/include
diff --git a/pcidev.pc.in b/pcidev.pc.in
new file mode 100644
index 0000000..0da4ca0
--- /dev/null
+++ b/pcidev.pc.in
@@ -0,0 +1,10 @@
+prefix=${CMAKE_INSTALL_PREFIX}
+exec_prefix=${CMAKE_INSTALL_FULL_BINDIR}
+libdir=${PCILIB_PLUGIN_DIR}
+includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}
+
+Name: ${TARNAME}
+Description: Sample event engine for pcilib
+Version: ${PACKAGE_VERSION}
+Libs: -L${PCILIB_PLUGIN_DIR} -lpcidev
+Cflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR}
diff --git a/pcidev.spec b/pcidev.spec
new file mode 100644
index 0000000..665bcb3
--- /dev/null
+++ b/pcidev.spec
@@ -0,0 +1,57 @@
+Summary: Sample plugin for pcitool
+Name: pcidev
+Version: 0.0.1
+Release: csa
+License: GPL-3.0
+Group: Development/Libraries
+Source: pcidev-0.0.1.tar.bz2
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+URL: http://darksoft.org
+Prefix: %{_prefix}
+Docdir: %{_docdir}
+Requires: pcilib >= 0.2.7
+BuildRequires: uthash
+BuildRequires: libpcilib-devel
+BuildRequires: pkg-config libtool cmake
+Vendor: Institute for Data Processing and Electronics, KIT
+Packager: Suren A. Chilingaryan <csa@suren.me>
+
+%description
+This package provides a sample event engine for the pcilib platform.
+
+%package devel
+Summary: Sample plugin for pcitool
+Group: Development/Libraries
+Requires: pcidev = %{version}
+Requires: libpcilib-devel
+
+%description devel
+Development files provide access to some non-standard features of the event engine.
+
+%prep
+%setup -q
+
+%build
+cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=%{_libdir} -DCMAKE_INSTALL_BINDIR=%{_bindir} -DCMAKE_INSTALL_DATADIR=%{_datadir} -DCMAKE_INSTALL_DATAROOTDIR=%{_datadir} -DCMAKE_INSTALL_INCLUDEDIR=%{_includedir} .
+
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-, root, root)
+/usr/local/lib/pcilib/libpcidev.so
+
+%files -n pcidev-devel
+%defattr(-, root, root)
+%{_includedir}/*
+%{_libdir}/pkgconfig/*.pc
+
+%changelog
+* Fri Mar 4 2016 Suren A. Chilingaryan <csa@suren.me> - 0.0.1
+- Added spec file to the sources
diff --git a/pcidev.spec.in b/pcidev.spec.in
new file mode 100644
index 0000000..afe79c1
--- /dev/null
+++ b/pcidev.spec.in
@@ -0,0 +1,57 @@
+Summary: Sample plugin for pcitool
+Name: ${PACKAGE_NAME}
+Version: ${CPACK_PACKAGE_VERSION}
+Release: csa
+License: GPL-3.0
+Group: Development/Libraries
+Source: ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+URL: http://darksoft.org
+Prefix: %{_prefix}
+Docdir: %{_docdir}
+Requires: pcilib >= 0.2.7
+BuildRequires: uthash
+BuildRequires: libpcilib-devel
+BuildRequires: pkg-config libtool cmake
+Vendor: Institute for Data Processing and Electronics, KIT
+Packager: Suren A. Chilingaryan <csa@suren.me>
+
+%description
+This package provides a sample event engine for the pcilib platform.
+
+%package devel
+Summary: Sample plugin for pcitool
+Group: Development/Libraries
+Requires: ${PACKAGE_NAME} = %{version}
+Requires: libpcilib-devel
+
+%description devel
+Development files provide access to some non-standard features of the event engine.
+
+%prep
+%setup -q
+
+%build
+cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=%{_libdir} -DCMAKE_INSTALL_BINDIR=%{_bindir} -DCMAKE_INSTALL_DATADIR=%{_datadir} -DCMAKE_INSTALL_DATAROOTDIR=%{_datadir} -DCMAKE_INSTALL_INCLUDEDIR=%{_includedir} .
+
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-, root, root)
+${PCILIB_PLUGIN_DIR}/lib${PACKAGE_NAME}.so
+
+%files -n ${PACKAGE_NAME}-devel
+%defattr(-, root, root)
+%{_includedir}/*
+%{_libdir}/pkgconfig/*.pc
+
+%changelog
+* Fri Mar 4 2016 Suren A. Chilingaryan <csa@suren.me> - ${CPACK_PACKAGE_VERSION}
+- Added spec file to the sources
diff --git a/private.h b/private.h
new file mode 100644
index 0000000..3c3c9da
--- /dev/null
+++ b/private.h
@@ -0,0 +1,135 @@
+#ifndef _PCIDEV_PRIVATE_H
+#define _PCIDEV_PRIVATE_H
+
+#include <pthread.h>
+#include <pcilib/model.h>
+#include <pcilib/debug.h>
+#include <pcilib/locking.h>
+#include "events.h"
+#include "pcidev.h"
+#include "env.h"
+
+#define PCIDEV_DEBUG
+
+#ifdef PCIDEV_DEBUG
+# define PCIDEV_DEBUG_RAW_FRAMES //**< Store all raw frames */
+# define IPECAMERA_DEBUG_BROKEN_FRAMES //**< Store broken frames in the specified directory */
+# define PCIDEV_DEBUG_RAW_PACKETS //**< Store all raw packets read from DMA grouped in frames */
+# define PCIDEV_DEBUG_HARDWARE //**< Produce various debugging information about pcidev operation */
+# define IPECAMERA_DEBUG_FRAME_HEADERS //**< Print frame headers & footers */
+# define PCIDEV_DEBUG_API //**< Debug IPECamera API calls */
+#endif /* PCIDEV_DEBUG */
+
+#define PCIDEV_DEFAULT_BUFFER_SIZE 256 //**< number of buffers in a ring buffer, should be power of 2 */
+#define PCIDEV_INTEGRATION_PERIOD 5000000 //**< the duration of integration period, in microseconds */
+#define PCIDEV_EVENT_SIZE 256 //**< the size of event data in bytes, always 256 in this example */
+#define PCIDEV_NOEVENT_SLEEP 100 //**< Sleep while polling for a new frame in reader */
+#define PCIDEV_DMA_TIMEOUT 50000 //**< Default DMA timeout in microseconds */
+
+
+#ifdef PCIDEV_DEBUG_RAW_FRAMES
+# define PCIDEV_DEBUG_RAW_FRAMES_MESSAGE(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_message (#function, __FILE__, __LINE__, __VA_ARGS__); }
+# define PCIDEV_DEBUG_RAW_FRAMES_BUFFER(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_data_buffer (#function, __VA_ARGS__); }
+#else /* PCIDEV_DEBUG_RAW_FRAMES */
+# define PCIDEV_DEBUG_RAW_FRAMES_MESSAGE(function, ...)
+# define PCIDEV_DEBUG_RAW_FRAMES_BUFFER(function, ...)
+#endif /* PCIDEV_DEBUG_RAW_FRAMES */
+
+#ifdef IPECAMERA_DEBUG_BROKEN_FRAMES
+# define IPECAMERA_DEBUG_BROKEN_FRAMES_MESSAGE(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_message (#function, __FILE__, __LINE__, __VA_ARGS__); }
+# define IPECAMERA_DEBUG_BROKEN_FRAMES_BUFFER(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_data_buffer (#function, __VA_ARGS__); }
+#else /* IPECAMERA_DEBUG_BROKEN_FRAMES */
+# define IPECAMERA_DEBUG_BROKEN_FRAMES_MESSAGE(function, ...)
+# define IPECAMERA_DEBUG_BROKEN_FRAMES_BUFFER(function, ...)
+#endif /* IPECAMERA_DEBUG_BROKEN_FRAMES */
+
+#ifdef PCIDEV_DEBUG_RAW_PACKETS
+# define PCIDEV_DEBUG_RAW_PACKETS_MESSAGE(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_message (#function, __FILE__, __LINE__, __VA_ARGS__); }
+# define PCIDEV_DEBUG_RAW_PACKETS_BUFFER(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_data_buffer (#function, __VA_ARGS__); }
+#else /* PCIDEV_DEBUG_RAW_PACKETS */
+# define PCIDEV_DEBUG_RAW_PACKETS_MESSAGE(function, ...)
+# define PCIDEV_DEBUG_RAW_PACKETS_BUFFER(function, ...)
+#endif /* PCIDEV_DEBUG_RAW_PACKETS */
+
+#ifdef PCIDEV_DEBUG_HARDWARE
+# define PCIDEV_DEBUG_HARDWARE_MESSAGE(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_message (#function, __FILE__, __LINE__, __VA_ARGS__); }
+# define PCIDEV_DEBUG_HARDWARE_BUFFER(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_data_buffer (#function, __VA_ARGS__); }
+#else /* PCIDEV_DEBUG_HARDWARE */
+# define PCIDEV_DEBUG_HARDWARE_MESSAGE(function, ...)
+# define PCIDEV_DEBUG_HARDWARE_BUFFER(function, ...)
+#endif /* PCIDEV_DEBUG_HARDWARE */
+
+#ifdef PCIDEV_DEBUG_FRAME_HEADERS
+# define PCIDEV_DEBUG_FRAME_HEADERS_MESSAGE(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_message (#function, __FILE__, __LINE__, __VA_ARGS__); }
+# define PCIDEV_DEBUG_FRAME_HEADERS_BUFFER(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_data_buffer (#function, __VA_ARGS__); }
+#else /* PCIDEV_DEBUG_RAW_FRAMES */
+# define PCIDEV_DEBUG_FRAME_HEADERS_MESSAGE(function, ...)
+# define PCIDEV_DEBUG_FRAME_HEADERS_BUFFER(function, ...)
+#endif /* PCIDEV_DEBUG_RAW_FRAMES */
+
+#ifdef PCIDEV_DEBUG_API
+# define PCIDEV_DEBUG_API_MESSAGE(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_message (#function, __FILE__, __LINE__, __VA_ARGS__); }
+# define PCIDEV_DEBUG_API_BUFFER(function, ...) if (pcidev_getenv(function##_ENV, #function)) { pcilib_debug_data_buffer (#function, __VA_ARGS__); }
+#else /* PCIDEV_DEBUG_API */
+# define PCIDEV_DEBUG_API_MESSAGE(function, ...)
+# define PCIDEV_DEBUG_API_BUFFER(function, ...)
+#endif /* PCIDEV_DEBUG_API */
+
+
+#define pcidev_debug(function, ...) \
+ PCIDEV_DEBUG_##function##_MESSAGE(PCIDEV_DEBUG_##function, PCILIB_LOG_DEFAULT, __VA_ARGS__)
+
+#define pcidev_debug_buffer(function, ...) \
+ PCIDEV_DEBUG_##function##_BUFFER(PCIDEV_DEBUG_##function, __VA_ARGS__)
+
+
+typedef uint32_t pcidev_payload_t;
+
+
+typedef struct {
+ pcilib_event_id_t evid;
+ struct timeval timestamp;
+} pcidev_autostop_t;
+
+typedef struct {
+ pcidev_event_info_t event; /**< this structure is overwritten by the reader thread, we need a copy */
+ pthread_rwlock_t mutex; /**< this mutex protects reconstructed buffers only, the raw data, event_info, etc. will be overwritten by reader thread anyway */
+} pcidev_event_t;
+
+struct pcidev_s {
+ pcilib_context_t pcictx;
+
+ pcilib_lock_t *run_lock; /**< Lock protecting global camera operation */
+ pcilib_lock_t *stream_lock; /**< Lock protecting stream/next_frame operations */
+ pcilib_lock_t *trigger_lock; /**< Lock protecting stream/next_frame operations */
+ int run_locked; /**< Flag indicating if camera is currently locked */
+ int stream_locked; /**< Flag indicating if camera is currently locked */
+ int trigger_locked; /**< Flag indicating if camera is currently locked */
+
+ char *data;
+ size_t size;
+
+ volatile pcilib_event_id_t event_id;
+ volatile pcilib_event_id_t preproc_id;
+ pcilib_event_id_t reported_id;
+
+ pcilib_dma_engine_t rdma;
+
+ int started; /**< Camera is in grabbing mode (start function is called) */
+ int streaming; /**< Camera is in streaming mode (we are within stream call) */
+ int process_data; /**< Indicates if some processing of the data is required, otherwise only rawdata_callback will be called */
+ volatile int run_streamer; /**< Indicates request to stop streaming events and can be set by reader_thread upon exit or by user request */
+
+ pcidev_autostop_t autostop;
+ struct timeval eio_timestamp;
+
+ size_t buffer_size; /**< How many images to store */
+ size_t buffer_pos; /**< Current image offset in the buffer, due to synchronization reasons should not be used outside of reader_thread */
+ size_t event_size; /**< Size of raw event data in bytes */
+
+ void *buffer; /**< Ring buffer for event data. Here we consider only a single type of events */
+ pcidev_event_t *event; /**< Description of events. */
+ int event_mutex_destroy;
+};
+
+#endif /* _PCIDEV_PRIVATE_H */
diff --git a/registers.c b/registers.c
new file mode 100644
index 0000000..1c7ce2d
--- /dev/null
+++ b/registers.c
@@ -0,0 +1,103 @@
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+#define _PCIDEV_MODEL_C
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <pcilib.h>
+#include <pcilib/tools.h>
+#include <pcilib/error.h>
+#include <pcilib/locking.h>
+#include <pcilib/model.h>
+#include <pcilib/datacpy.h>
+
+#include "registers.h"
+#include "private.h"
+
+
+
+typedef struct pcidev_register_context_s pcidev_register_context_t;
+
+struct pcidev_register_context_s {
+ pcilib_register_bank_context_t bank_ctx; /**< the bank context associated with the software registers */
+ pcilib_lock_t *lock; /**< the lock to serialize access through GPIO */
+};
+
+
+pcilib_register_bank_context_t* pcidev_regfile_open(pcilib_t *ctx, pcilib_register_bank_t bank, const char* model, const void *args) {
+ pcidev_register_context_t *bank_ctx;
+
+ bank_ctx = calloc(1, sizeof(pcidev_register_context_t));
+ if (!bank_ctx) {
+ pcilib_error("Memory allocation for bank context has failed");
+ return NULL;
+ }
+
+ bank_ctx->lock = pcilib_get_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, "registers");
+ if (!bank_ctx->lock) {
+ pcidev_regfile_close(ctx, (pcilib_register_bank_context_t*)bank_ctx);
+ pcilib_error("Failed to initialize a lock to protect CMOSIS register bank");
+ return NULL;
+ }
+
+ srandom(1);
+
+ return (pcilib_register_bank_context_t*)bank_ctx;
+}
+
+void pcidev_regfile_close(pcilib_t *ctx, pcilib_register_bank_context_t *reg_bank_ctx) {
+ pcidev_register_context_t *bank_ctx = (pcidev_register_context_t*)reg_bank_ctx;
+
+ if (bank_ctx->lock)
+ pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, bank_ctx->lock);
+ free(bank_ctx);
+}
+
+
+int pcidev_register_read(pcilib_t *ctx, pcilib_register_bank_context_t *reg_bank_ctx, pcilib_register_addr_t addr, pcilib_register_value_t *value) {
+ int err;
+ uint32_t val;
+
+ pcidev_register_context_t *bank_ctx = (pcidev_register_context_t*)reg_bank_ctx;
+ const pcilib_register_bank_description_t *bank = reg_bank_ctx->bank;
+
+
+retry:
+ err = pcilib_lock(bank_ctx->lock);
+ if (err) {
+ pcilib_error("Error (%i) obtaining a lock to serialize access to registers ", err);
+ return err;
+ }
+
+ val = random();
+
+ pcilib_unlock(bank_ctx->lock);
+
+ *value = val;
+
+ return 0;
+}
+
+int pcidev_register_write(pcilib_t *ctx, pcilib_register_bank_context_t *reg_bank_ctx, pcilib_register_addr_t addr, pcilib_register_value_t value) {
+ int err;
+
+ pcidev_register_context_t *bank_ctx = (pcidev_register_context_t*)reg_bank_ctx;
+ const pcilib_register_bank_description_t *bank = reg_bank_ctx->bank;
+
+retry:
+ err = pcilib_lock(bank_ctx->lock);
+ if (err) {
+ pcilib_error("Error (%i) obtaining a lock to serialize access to CMOSIS registers ", err);
+ return err;
+ }
+
+ pcilib_unlock(bank_ctx->lock);
+
+ return 0;
+}
+
+
+
diff --git a/registers.h b/registers.h
new file mode 100644
index 0000000..d98a3fe
--- /dev/null
+++ b/registers.h
@@ -0,0 +1,36 @@
+#ifndef _PCIDEV_CMOSIS_H
+#define _PCIDEV_CMOSIS_H
+
+#include <pcilib/bank.h>
+
+#include "version.h"
+
+enum ipecamera_protocol_s {
+ PCIDEV_PROTOCOL_DEFAULT = PCILIB_REGISTER_PROTOCOL0,
+};
+
+pcilib_register_bank_context_t* pcidev_regfile_open(pcilib_t *ctx, pcilib_register_bank_t bank, const char* model, const void *args);
+void pcidev_regfile_close(pcilib_t *ctx, pcilib_register_bank_context_t *reg_bank_ctx);
+int pcidev_register_read(pcilib_t *ctx, pcilib_register_bank_context_t *bank, pcilib_register_addr_t addr, pcilib_register_value_t *value);
+int pcidev_register_write(pcilib_t *ctx, pcilib_register_bank_context_t *bank, pcilib_register_addr_t addr, pcilib_register_value_t value);
+
+
+# ifdef _PCIDEV_MODEL_C
+static const pcilib_register_protocol_api_description_t pcidev_protocol_api =
+ { PCIDEV_VERSION, pcidev_regfile_open, pcidev_regfile_close, NULL, pcidev_register_read, pcidev_register_write };
+
+static const pcilib_register_protocol_description_t pcidev_protocols[] = {
+ {PCIDEV_PROTOCOL_DEFAULT, &pcidev_protocol_api, NULL, NULL, "pcidev", "Dummy register protocol sample"},
+ { 0 }
+};
+
+static const pcilib_register_bank_description_t pcidev_banks[] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL }
+};
+
+static const pcilib_register_range_t pcidev_ranges[] = {
+ {0, 0, 0, 0}
+};
+# endif /* _PCIDEV_MODEL_C */
+
+#endif /* _PCIDEV_CMOSIS_H */
diff --git a/version.h.in b/version.h.in
new file mode 100644
index 0000000..16dbac7
--- /dev/null
+++ b/version.h.in
@@ -0,0 +1,12 @@
+#ifndef _PCIDEV_VERSION_H
+#define _PCIDEV_VERSION_H
+
+#include <pcilib/version.h>
+
+#define PCIDEV_VERSION_MAJOR ${PCIDEV_VERSION_MAJOR}
+#define PCIDEV_VERSION_MINOR ${PCIDEV_VERSION_MINOR}
+#define PCIDEV_VERSION_MICRO ${PCIDEV_VERSION_MICRO}
+
+#define PCIDEV_VERSION PCILIB_MAKE_VERSION(PCIDEV_VERSION_MAJOR, PCIDEV_VERSION_MINOR, PCIDEV_VERSION_MICRO)
+
+#endif /* _PCIDEV_VERSION_H */
diff --git a/xml/CMakeLists.txt b/xml/CMakeLists.txt
new file mode 100644
index 0000000..4e15949
--- /dev/null
+++ b/xml/CMakeLists.txt
@@ -0,0 +1,8 @@
+
+install(FILES pcidev.xml
+ DESTINATION ${PCILIB_DATA_DIR}
+)
+
+install(DIRECTORY pcidev
+ DESTINATION ${PCILIB_MODEL_DIR}
+)
diff --git a/xml/pcidev.xml b/xml/pcidev.xml
new file mode 100644
index 0000000..2a819e8
--- /dev/null
+++ b/xml/pcidev.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<devices xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <device vendor="0000" device="0000" model="pcidev"/>
+</devices>
diff --git a/xml/pcidev/registers.xml b/xml/pcidev/registers.xml
new file mode 100644
index 0000000..3735921
--- /dev/null
+++ b/xml/pcidev/registers.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <bank size="0x0200" protocol="pcidev" read_address="0x0" write_address="0x0" word_size="32" endianess="little" format="0x%lx" name="pcidev" description="Sample Registers">
+ <register address="0x00" offset="0" size="32" default="0" rwmask="0" mode="RW" name="testreg">
+ <field offset="0" size="8" mode="R" name="firmware_version"/>
+ <field offset="8" size="1" mode="R" name="firmware_bitmode"/>
+ <field offset="12" size="2" mode="R" name="adc_resolution"/>
+ <field offset="16" size="2" mode="R" name="output_mode"/>
+ </register>
+ </bank>
+</model>