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>  | 
