From 6f4af841f6fdd099b97d071ae64c8be60f809456 Mon Sep 17 00:00:00 2001 From: "Suren A. Chilingaryan" Date: Thu, 25 May 2023 22:41:04 +0200 Subject: A sample event engine for pcitool (not requiring any PCIe hardware). Initial (barely tested and intended only as an example) release --- .gitignore | 55 +++++ CMakeLists.txt | 86 +++++++ README | 19 ++ apps/CMakeLists.txt | 13 ++ apps/grab.c | 63 +++++ cmake/version.cmake | 12 + dma.c | 450 +++++++++++++++++++++++++++++++++++ dma.h | 71 ++++++ dma_private.h | 46 ++++ env.c | 17 ++ env.h | 24 ++ events.c | 592 +++++++++++++++++++++++++++++++++++++++++++++++ events.h | 24 ++ model.c | 78 +++++++ model.h | 13 ++ pcidev.h | 32 +++ pcidev.pc | 10 + pcidev.pc.in | 10 + pcidev.spec | 57 +++++ pcidev.spec.in | 57 +++++ private.h | 135 +++++++++++ registers.c | 103 +++++++++ registers.h | 36 +++ version.h.in | 12 + xml/CMakeLists.txt | 8 + xml/pcidev.xml | 4 + xml/pcidev/registers.xml | 11 + 27 files changed, 2038 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README create mode 100644 apps/CMakeLists.txt create mode 100644 apps/grab.c create mode 100644 cmake/version.cmake create mode 100644 dma.c create mode 100644 dma.h create mode 100644 dma_private.h create mode 100644 env.c create mode 100644 env.h create mode 100644 events.c create mode 100644 events.h create mode 100644 model.c create mode 100644 model.h create mode 100644 pcidev.h create mode 100644 pcidev.pc create mode 100644 pcidev.pc.in create mode 100644 pcidev.spec create mode 100644 pcidev.spec.in create mode 100644 private.h create mode 100644 registers.c create mode 100644 registers.h create mode 100644 version.h.in create mode 100644 xml/CMakeLists.txt create mode 100644 xml/pcidev.xml create mode 100644 xml/pcidev/registers.xml 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 ") +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 +#include + +#include +#include + +#include + +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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 + +#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 +#include +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 + +#include +#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 +#include + +#include +#include + +#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 +#include + +//#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 + +%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 - 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 + +%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 - ${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 +#include +#include +#include +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 + +#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 + +#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 @@ + + + + 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 @@ + + + + + + + + + + + -- cgit v1.2.3