diff options
author | Suren A. Chilingaryan <csa@suren.me> | 2023-05-25 22:41:04 +0200 |
---|---|---|
committer | Suren A. Chilingaryan <csa@suren.me> | 2023-05-25 22:41:04 +0200 |
commit | 6f4af841f6fdd099b97d071ae64c8be60f809456 (patch) | |
tree | d4f9a18b38e1ce3cfc0a5336215d5ce3afe830d2 | |
download | pcidev-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-- | .gitignore | 55 | ||||
-rw-r--r-- | CMakeLists.txt | 86 | ||||
-rw-r--r-- | README | 19 | ||||
-rw-r--r-- | apps/CMakeLists.txt | 13 | ||||
-rw-r--r-- | apps/grab.c | 63 | ||||
-rw-r--r-- | cmake/version.cmake | 12 | ||||
-rw-r--r-- | dma.c | 450 | ||||
-rw-r--r-- | dma.h | 71 | ||||
-rw-r--r-- | dma_private.h | 46 | ||||
-rw-r--r-- | env.c | 17 | ||||
-rw-r--r-- | env.h | 24 | ||||
-rw-r--r-- | events.c | 592 | ||||
-rw-r--r-- | events.h | 24 | ||||
-rw-r--r-- | model.c | 78 | ||||
-rw-r--r-- | model.h | 13 | ||||
-rw-r--r-- | pcidev.h | 32 | ||||
-rw-r--r-- | pcidev.pc | 10 | ||||
-rw-r--r-- | pcidev.pc.in | 10 | ||||
-rw-r--r-- | pcidev.spec | 57 | ||||
-rw-r--r-- | pcidev.spec.in | 57 | ||||
-rw-r--r-- | private.h | 135 | ||||
-rw-r--r-- | registers.c | 103 | ||||
-rw-r--r-- | registers.h | 36 | ||||
-rw-r--r-- | version.h.in | 12 | ||||
-rw-r--r-- | xml/CMakeLists.txt | 8 | ||||
-rw-r--r-- | xml/pcidev.xml | 4 | ||||
-rw-r--r-- | xml/pcidev/registers.xml | 11 |
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 +) @@ -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) @@ -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; +} @@ -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 */ @@ -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]; +} + @@ -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 */ @@ -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]; +} @@ -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> |