diff --git a/.github/workflows/.dev.workflow.yml b/.github/workflows/.dev.workflow.yml index 2f09bfba..c11dfbde 100644 --- a/.github/workflows/.dev.workflow.yml +++ b/.github/workflows/.dev.workflow.yml @@ -3,91 +3,124 @@ name: Dev Workflow on: push: branches-ignore: - - xlink_upstream + [xlink_upstream] pull_request: branches-ignore: - - xlink_upstream + [xlink_upstream] jobs: build: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [macos-latest, windows-latest, ubuntu-latest] compiler: [default, clang] - libusb: [ON, OFF] - include: - - os: windows-latest - compiler: mingw - libusb: ON - - os: windows-latest - compiler: mingw - libusb: OFF + enable-libusb: [ON, OFF] + libusb-system: [ON, OFF] + exclude: + # libusb-system is only valid on macOS & Ubuntu + - os: windows-latest + libusb-system: ON + # clang is only valid on macOS & Ubuntu + - os: windows-latest + compiler: clang + # Prevent meaningless combos where libusb-system ON but libusb disabled + - enable-libusb: OFF + libusb-system: ON steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: 'recursive' - - name: Install libusb - if: matrix.os == 'macos-latest' && matrix.libusb == 'ON' + # Install system libusb only when requested AND on supported OSes + - name: Install libusb (macOS) + if: matrix.os == 'macos-latest' && matrix['libusb-system'] == 'ON' run: | brew install libusb - - name: Install libusb - if: matrix.os == 'ubuntu-latest' && matrix.libusb == 'ON' + - name: Install libusb (Ubuntu) + if: matrix.os == 'ubuntu-latest' && matrix['libusb-system'] == 'ON' run: | - sudo apt-get install libusb-1.0-0-dev libusb-1.0-0 + sudo apt-get update + sudo apt-get install -y libusb-1.0-0-dev libusb-1.0-0 - - name: Setup MinGW compiler - if: matrix.compiler == 'mingw' - run: | - choco install mingw - choco upgrade mingw - - - name: Install Clang on macOS - if: matrix.os == 'macos-latest' && matrix.compiler == 'clang' - run: | - brew install llvm - echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH - - - name: Install Clang on Ubuntu - if: matrix.os == 'ubuntu-latest' && matrix.compiler == 'clang' - run: | - sudo apt-get install clang - - - name: Install Clang on Windows - if: matrix.os == 'windows-latest' && matrix.compiler == 'clang' - run: | - choco install llvm - echo "C:\Program Files\LLVM\bin" >> $env:GITHUB_PATH - - - name: Install Ninja on macOS - if: matrix.os == 'macos-latest' - run: | - brew install ninja - - - name: Install Ninja on Ubuntu - if: matrix.os == 'ubuntu-latest' + # --- Local libusb when SYSTEM_LIBUSB=OFF but libusb is enabled --- + - name: Build & install local libusb (all OSes) + if: matrix['enable-libusb'] == 'ON' && matrix['libusb-system'] == 'OFF' + shell: bash run: | - sudo apt-get install ninja-build + set -euxo pipefail + # Where to install local libusb + PREFIX="${GITHUB_WORKSPACE}/.local/libusb" + mkdir -p "${PREFIX}" + + # Get libusb (shallow) + git clone --depth=1 https://github.com/luxonis/libusb.git --branch cmake-android-mainline + cmake -S libusb -B build-libusb \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ + -DWITH_UDEV=OFF \ + -DBUILD_SHARED_LIBS=ON + cmake --build build-libusb --config Release --parallel + cmake --build build-libusb --config Release --target install + + # Export usb-1.0_DIR for later CMake configure + # (CMake package config is placed here by libusb) + echo "USB1_DIR=${PREFIX}/lib/cmake/usb-1.0" >> "$GITHUB_ENV" - - name: Install Ninja on Windows - if: matrix.os == 'windows-latest' + - name: Setup MinGW compiler + if: matrix.compiler == 'mingw' run: | - choco install ninja - - - name: configure - if: matrix.compiler == 'default' - run: cmake . -Bbuild -DXLINK_BUILD_EXAMPLES=ON -DXLINK_BUILD_TESTS=ON -DXLINK_ENABLE_LIBUSB=${{ matrix.libusb }} - - - name: configure + choco install -y mingw + choco upgrade -y mingw + + # Configure (Debug) + - name: configure (Debug) - default/clang + if: matrix.compiler == 'default' || matrix.compiler == 'clang' + shell: bash + run: > + cmake . -Bbuild + -DXLINK_BUILD_EXAMPLES=ON + -DXLINK_BUILD_TESTS=ON + -DXLINK_ENABLE_LIBUSB=${{ matrix['enable-libusb'] }} + -DXLINK_LIBUSB_SYSTEM=${{ matrix['libusb-system'] }} + -D"usb-1.0_DIR=$USB1_DIR" + + - name: configure (Debug) - MinGW if: matrix.compiler == 'mingw' - run: cmake . -Bbuild -DXLINK_BUILD_EXAMPLES=ON -DXLINK_BUILD_TESTS=ON -DXLINK_ENABLE_LIBUSB=${{ matrix.libusb }} -G"MinGW Makefiles" - - - name: configure - if: matrix.compiler == 'clang' - run: cmake . -Bbuild -DXLINK_BUILD_EXAMPLES=ON -DXLINK_BUILD_TESTS=ON -DXLINK_ENABLE_LIBUSB=${{ matrix.libusb }} -G"Ninja" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ - - - name: build - run: cmake --build build --parallel \ No newline at end of file + shell: bash + run: > + cmake . -Bbuild + -DXLINK_BUILD_EXAMPLES=ON + -DXLINK_BUILD_TESTS=ON + -DXLINK_ENABLE_LIBUSB=${{ matrix['enable-libusb'] }} + -DXLINK_LIBUSB_SYSTEM=${{ matrix['libusb-system'] }} + -D"usb-1.0_DIR=$USB1_DIR" + -G "MinGW Makefiles" + + - name: build (Debug) + run: cmake --build build --parallel --config Debug + + - name: test (Debug) + shell: bash + run: ctest --test-dir build -C Debug --output-on-failure + + - name: reconfigure (Release) + shell: bash + run: > + cmake . -Bbuild + -DCMAKE_BUILD_TYPE=Release + -DXLINK_BUILD_EXAMPLES=ON + -DXLINK_BUILD_TESTS=ON + -DXLINK_ENABLE_LIBUSB=${{ matrix['enable-libusb'] }} + -DXLINK_LIBUSB_SYSTEM=${{ matrix['libusb-system'] }} + -D"usb-1.0_DIR=$USB1_DIR" + + - name: build (Release) + run: cmake --build build --parallel --config Release + + - name: test (Release) + shell: bash + run: ctest --test-dir build -C Release --output-on-failure diff --git a/.gitignore b/.gitignore index 5639fca8..c8479fa2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ build/ build*/ .cache* +#clangd +.cache/* + #git *.orig *_REMOTE_* diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f86b51e..d8e581b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,6 @@ cmake_minimum_required(VERSION 3.10) -include("cmake/HunterGate.cmake") -HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/9d9242b60d5236269f894efd3ddd60a9ca83dd7f.tar.gz" - SHA1 "16cc954aa723bccd16ea45fc91a858d0c5246376" - LOCAL # Local config for dependencies -) ### Constants set(TARGET_NAME "XLink") @@ -20,21 +14,14 @@ project(${TARGET_NAME} LANGUAGES C CXX) set(PROJECT_EXPORT_GROUP "${PROJECT_NAME}Targets") -# Set default installation directory -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Installation Directory" FORCE) -endif() - # Compile with USB protocol capabilities option(XLINK_ENABLE_LIBUSB "Enable USB protocol which requires libusb library" ON) # Build examples option(XLINK_BUILD_EXAMPLES "Build XLink examples" OFF) # Build tests option(XLINK_BUILD_TESTS "Build XLink tests" OFF) -# Debug option -set(XLINK_LIBUSB_LOCAL "" CACHE STRING "Path to local libub source to use instead of Hunter") -# Debug option -option(XLINK_LIBUSB_SYSTEM "Use system libusb library instead of Hunter" OFF) +# Install headers only (skip binary lib in install tree) +option(XLINK_INSTALL_PUBLIC_ONLY "Install only header interface target (XLinkPublic)" OFF) # Specify exporting all symbols on Windows if(WIN32 AND BUILD_SHARED_LIBS) @@ -65,6 +52,7 @@ get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) if(is_multi_config) set_target_properties(${TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d") endif() + # Define export header to support shared library on Windows include(GenerateExportHeader) generate_export_header(${TARGET_NAME} @@ -89,13 +77,12 @@ if(XLINK_ENABLE_LIBUSB) else() # Link to CMake libusb target_link_libraries(${TARGET_NAME} PRIVATE usb-1.0) - if(WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + if(WIN32 AND NOT MINGW) target_link_libraries(${TARGET_NAME} PRIVATE delayimp.lib) # workaround https://gitlab.kitware.com/cmake/cmake/-/issues/20022 target_link_options(${TARGET_NAME} PUBLIC "$:d>.dll>") endif() endif() - if(WIN32 AND NOT MINGW) target_link_libraries(${TARGET_NAME} PRIVATE Pathcch.lib) endif() @@ -157,15 +144,9 @@ target_compile_definitions(${TARGET_NAME} USE_TCP_IP ) -if (ENABLE_MYRIAD_NO_BOOT) - target_compile_definitions(${TARGET_NAME} - PRIVATE - NO_BOOT) -endif() -# Set C99 standard +# Set C99 and C++11 standard set_property(TARGET ${TARGET_NAME} PROPERTY C_STANDARD 99) -# Set compiler features (c++11), and disables extensions (g++11) set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD 11) set_property(TARGET ${TARGET_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${TARGET_NAME} PROPERTY CXX_EXTENSIONS OFF) @@ -220,17 +201,18 @@ endif() include(GNUInstallDirs) +# Handle public-only install option +set(_XLINK_INSTALL_TARGETS ${TARGET_PUBLIC_NAME}) +if(NOT XLINK_INSTALL_PUBLIC_ONLY) + list(APPEND _XLINK_INSTALL_TARGETS ${TARGET_NAME}) +endif() + # Export targets (capability to import current build directory) export(TARGETS ${TARGET_NAME} ${TARGET_PUBLIC_NAME} FILE "${PROJECT_NAME}Targets.cmake") # Dependencies file configure_file("cmake/${PROJECT_NAME}Dependencies.cmake" ${PROJECT_NAME}Dependencies.cmake COPYONLY) -# Configure config file (one for exporting build directory, one for installation) -if(${HUNTER_INSTALL_PREFIX}) - file(RELATIVE_PATH XLINK_DEPENDENCIES_INSTALLATION_PATH_REL "${CMAKE_CURRENT_BINARY_DIR}" "${HUNTER_INSTALL_PREFIX}") -endif() - configure_file(cmake/${PROJECT_NAME}Config.cmake.in ${PROJECT_NAME}Config.cmake @ONLY) # Config for installation @@ -239,7 +221,7 @@ configure_file(cmake/${PROJECT_NAME}Config.cmake.in _install/${PROJECT_NAME}Conf # Install targets install( - TARGETS ${TARGET_NAME} ${TARGET_PUBLIC_NAME} + TARGETS ${_XLINK_INSTALL_TARGETS} EXPORT ${PROJECT_EXPORT_GROUP} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" @@ -252,13 +234,6 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}Export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/XLink ) - -# Install Hunter dependencies -if(XLINK_ENABLE_LIBUSB) - if(NOT XLINK_LIBUSB_LOCAL AND NOT XLINK_LIBUSB_SYSTEM) - install(DIRECTORY "${HUNTER_INSTALL_PREFIX}/" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/dependencies") - endif() -endif() # Install export group (information about targets) install(EXPORT ${PROJECT_EXPORT_GROUP} DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake deleted file mode 100644 index 8bb4c5e6..00000000 --- a/cmake/Hunter/config.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# libusb without udev -hunter_config( - libusb-luxonis - VERSION "1.0.24-cmake" - URL "https://github.com/luxonis/libusb/archive/b7e4548958325b18feb73977163ad44398099534.tar.gz" - SHA1 "2d79573d57628fe56d2868d2f6ce756d40906cf4" - CMAKE_ARGS - WITH_UDEV=OFF - # Build shared libs by default to not cause licensing issues - BUILD_SHARED_LIBS=ON -) diff --git a/cmake/HunterGate.cmake b/cmake/HunterGate.cmake deleted file mode 100644 index 6d9cc240..00000000 --- a/cmake/HunterGate.cmake +++ /dev/null @@ -1,539 +0,0 @@ -# Copyright (c) 2013-2019, Ruslan Baratov -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# This is a gate file to Hunter package manager. -# Include this file using `include` command and add package you need, example: -# -# cmake_minimum_required(VERSION 3.2) -# -# include("cmake/HunterGate.cmake") -# HunterGate( -# URL "https://github.com/path/to/hunter/archive.tar.gz" -# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" -# ) -# -# project(MyProject) -# -# hunter_add_package(Foo) -# hunter_add_package(Boo COMPONENTS Bar Baz) -# -# Projects: -# * https://github.com/hunter-packages/gate/ -# * https://github.com/ruslo/hunter - -option(HUNTER_ENABLED "Enable Hunter package manager support" ON) - -if(HUNTER_ENABLED) - if(CMAKE_VERSION VERSION_LESS "3.2") - message( - FATAL_ERROR - "At least CMake version 3.2 required for Hunter dependency management." - " Update CMake or set HUNTER_ENABLED to OFF." - ) - endif() -endif() - -include(CMakeParseArguments) # cmake_parse_arguments - -option(HUNTER_STATUS_PRINT "Print working status" ON) -option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) -option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) - -set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors") - -function(hunter_gate_status_print) - if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) - foreach(print_message ${ARGV}) - message(STATUS "[hunter] ${print_message}") - endforeach() - endif() -endfunction() - -function(hunter_gate_status_debug) - if(HUNTER_STATUS_DEBUG) - foreach(print_message ${ARGV}) - string(TIMESTAMP timestamp) - message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") - endforeach() - endif() -endfunction() - -function(hunter_gate_error_page error_page) - message("------------------------------ ERROR ------------------------------") - message(" ${HUNTER_ERROR_PAGE}/${error_page}.html") - message("-------------------------------------------------------------------") - message("") - message(FATAL_ERROR "") -endfunction() - -function(hunter_gate_internal_error) - message("") - foreach(print_message ${ARGV}) - message("[hunter ** INTERNAL **] ${print_message}") - endforeach() - message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") - message("") - hunter_gate_error_page("error.internal") -endfunction() - -function(hunter_gate_fatal_error) - cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}") - if("${hunter_ERROR_PAGE}" STREQUAL "") - hunter_gate_internal_error("Expected ERROR_PAGE") - endif() - message("") - foreach(x ${hunter_UNPARSED_ARGUMENTS}) - message("[hunter ** FATAL ERROR **] ${x}") - endforeach() - message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") - message("") - hunter_gate_error_page("${hunter_ERROR_PAGE}") -endfunction() - -function(hunter_gate_user_error) - hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data") -endfunction() - -function(hunter_gate_self root version sha1 result) - string(COMPARE EQUAL "${root}" "" is_bad) - if(is_bad) - hunter_gate_internal_error("root is empty") - endif() - - string(COMPARE EQUAL "${version}" "" is_bad) - if(is_bad) - hunter_gate_internal_error("version is empty") - endif() - - string(COMPARE EQUAL "${sha1}" "" is_bad) - if(is_bad) - hunter_gate_internal_error("sha1 is empty") - endif() - - string(SUBSTRING "${sha1}" 0 7 archive_id) - - if(EXISTS "${root}/cmake/Hunter") - set(hunter_self "${root}") - else() - set( - hunter_self - "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" - ) - endif() - - set("${result}" "${hunter_self}" PARENT_SCOPE) -endfunction() - -# Set HUNTER_GATE_ROOT cmake variable to suitable value. -function(hunter_gate_detect_root) - # Check CMake variable - string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty) - if(not_empty) - set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) - hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") - return() - endif() - - # Check environment variable - string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty) - if(not_empty) - set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) - hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") - return() - endif() - - # Check HOME environment variable - string(COMPARE NOTEQUAL "$ENV{HOME}" "" result) - if(result) - set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) - hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") - return() - endif() - - # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) - if(WIN32) - string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result) - if(result) - set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) - hunter_gate_status_debug( - "HUNTER_ROOT set using SYSTEMDRIVE environment variable" - ) - return() - endif() - - string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result) - if(result) - set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) - hunter_gate_status_debug( - "HUNTER_ROOT set using USERPROFILE environment variable" - ) - return() - endif() - endif() - - hunter_gate_fatal_error( - "Can't detect HUNTER_ROOT" - ERROR_PAGE "error.detect.hunter.root" - ) -endfunction() - -function(hunter_gate_download dir) - string( - COMPARE - NOTEQUAL - "$ENV{HUNTER_DISABLE_AUTOINSTALL}" - "" - disable_autoinstall - ) - if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) - hunter_gate_fatal_error( - "Hunter not found in '${dir}'" - "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" - "Settings:" - " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" - " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" - ERROR_PAGE "error.run.install" - ) - endif() - string(COMPARE EQUAL "${dir}" "" is_bad) - if(is_bad) - hunter_gate_internal_error("Empty 'dir' argument") - endif() - - string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) - if(is_bad) - hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") - endif() - - string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) - if(is_bad) - hunter_gate_internal_error("HUNTER_GATE_URL empty") - endif() - - set(done_location "${dir}/DONE") - set(sha1_location "${dir}/SHA1") - - set(build_dir "${dir}/Build") - set(cmakelists "${dir}/CMakeLists.txt") - - hunter_gate_status_debug("Locking directory: ${dir}") - file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) - hunter_gate_status_debug("Lock done") - - if(EXISTS "${done_location}") - # while waiting for lock other instance can do all the job - hunter_gate_status_debug("File '${done_location}' found, skip install") - return() - endif() - - file(REMOVE_RECURSE "${build_dir}") - file(REMOVE_RECURSE "${cmakelists}") - - file(MAKE_DIRECTORY "${build_dir}") # check directory permissions - - # Disabling languages speeds up a little bit, reduces noise in the output - # and avoids path too long windows error - file( - WRITE - "${cmakelists}" - "cmake_minimum_required(VERSION 3.2)\n" - "project(HunterDownload LANGUAGES NONE)\n" - "include(ExternalProject)\n" - "ExternalProject_Add(\n" - " Hunter\n" - " URL\n" - " \"${HUNTER_GATE_URL}\"\n" - " URL_HASH\n" - " SHA1=${HUNTER_GATE_SHA1}\n" - " DOWNLOAD_DIR\n" - " \"${dir}\"\n" - " TLS_VERIFY\n" - " ${HUNTER_TLS_VERIFY}\n" - " SOURCE_DIR\n" - " \"${dir}/Unpacked\"\n" - " CONFIGURE_COMMAND\n" - " \"\"\n" - " BUILD_COMMAND\n" - " \"\"\n" - " INSTALL_COMMAND\n" - " \"\"\n" - ")\n" - ) - - if(HUNTER_STATUS_DEBUG) - set(logging_params "") - else() - set(logging_params OUTPUT_QUIET) - endif() - - hunter_gate_status_debug("Run generate") - - # Need to add toolchain file too. - # Otherwise on Visual Studio + MDD this will fail with error: - # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" - if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") - get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) - set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") - else() - # 'toolchain_arg' can't be empty - set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") - endif() - - string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) - if(no_make) - set(make_arg "") - else() - # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM - set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") - endif() - - execute_process( - COMMAND - "${CMAKE_COMMAND}" - "-H${dir}" - "-B${build_dir}" - "-G${CMAKE_GENERATOR}" - "${toolchain_arg}" - ${make_arg} - WORKING_DIRECTORY "${dir}" - RESULT_VARIABLE download_result - ${logging_params} - ) - - if(NOT download_result EQUAL 0) - hunter_gate_internal_error( - "Configure project failed." - "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}" - "In directory ${dir}" - ) - endif() - - hunter_gate_status_print( - "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" - " ${HUNTER_GATE_URL}" - " -> ${dir}" - ) - execute_process( - COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" - WORKING_DIRECTORY "${dir}" - RESULT_VARIABLE download_result - ${logging_params} - ) - - if(NOT download_result EQUAL 0) - hunter_gate_internal_error("Build project failed") - endif() - - file(REMOVE_RECURSE "${build_dir}") - file(REMOVE_RECURSE "${cmakelists}") - - file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") - file(WRITE "${done_location}" "DONE") - - hunter_gate_status_debug("Finished") -endfunction() - -# Must be a macro so master file 'cmake/Hunter' can -# apply all variables easily just by 'include' command -# (otherwise PARENT_SCOPE magic needed) -macro(HunterGate) - if(HUNTER_GATE_DONE) - # variable HUNTER_GATE_DONE set explicitly for external project - # (see `hunter_download`) - set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) - endif() - - # First HunterGate command will init Hunter, others will be ignored - get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) - - if(NOT HUNTER_ENABLED) - # Empty function to avoid error "unknown function" - function(hunter_add_package) - endfunction() - - set( - _hunter_gate_disabled_mode_dir - "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" - ) - if(EXISTS "${_hunter_gate_disabled_mode_dir}") - hunter_gate_status_debug( - "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" - ) - list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") - endif() - elseif(_hunter_gate_done) - hunter_gate_status_debug("Secondary HunterGate (use old settings)") - hunter_gate_self( - "${HUNTER_CACHED_ROOT}" - "${HUNTER_VERSION}" - "${HUNTER_SHA1}" - _hunter_self - ) - include("${_hunter_self}/cmake/Hunter") - else() - set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}") - - string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) - if(_have_project_name) - hunter_gate_fatal_error( - "Please set HunterGate *before* 'project' command. " - "Detected project: ${PROJECT_NAME}" - ERROR_PAGE "error.huntergate.before.project" - ) - endif() - - cmake_parse_arguments( - HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} - ) - - string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) - string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) - string( - COMPARE - NOTEQUAL - "${HUNTER_GATE_UNPARSED_ARGUMENTS}" - "" - _have_unparsed - ) - string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) - string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) - - if(_have_unparsed) - hunter_gate_user_error( - "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" - ) - endif() - if(_empty_sha1) - hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") - endif() - if(_empty_url) - hunter_gate_user_error("URL suboption of HunterGate is mandatory") - endif() - if(_have_global) - if(HUNTER_GATE_LOCAL) - hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") - endif() - if(_have_filepath) - hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") - endif() - endif() - if(HUNTER_GATE_LOCAL) - if(_have_global) - hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") - endif() - if(_have_filepath) - hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") - endif() - endif() - if(_have_filepath) - if(_have_global) - hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") - endif() - if(HUNTER_GATE_LOCAL) - hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") - endif() - endif() - - hunter_gate_detect_root() # set HUNTER_GATE_ROOT - - # Beautify path, fix probable problems with windows path slashes - get_filename_component( - HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE - ) - hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") - if(NOT HUNTER_ALLOW_SPACES_IN_PATH) - string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) - if(NOT _contain_spaces EQUAL -1) - hunter_gate_fatal_error( - "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." - "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" - "(Use at your own risk!)" - ERROR_PAGE "error.spaces.in.hunter.root" - ) - endif() - endif() - - string( - REGEX - MATCH - "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" - HUNTER_GATE_VERSION - "${HUNTER_GATE_URL}" - ) - string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) - if(_is_empty) - set(HUNTER_GATE_VERSION "unknown") - endif() - - hunter_gate_self( - "${HUNTER_GATE_ROOT}" - "${HUNTER_GATE_VERSION}" - "${HUNTER_GATE_SHA1}" - _hunter_self - ) - - set(_master_location "${_hunter_self}/cmake/Hunter") - if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter") - # Hunter downloaded manually (e.g. by 'git clone') - set(_unused "xxxxxxxxxx") - set(HUNTER_GATE_SHA1 "${_unused}") - set(HUNTER_GATE_VERSION "${_unused}") - else() - get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) - set(_done_location "${_archive_id_location}/DONE") - set(_sha1_location "${_archive_id_location}/SHA1") - - # Check Hunter already downloaded by HunterGate - if(NOT EXISTS "${_done_location}") - hunter_gate_download("${_archive_id_location}") - endif() - - if(NOT EXISTS "${_done_location}") - hunter_gate_internal_error("hunter_gate_download failed") - endif() - - if(NOT EXISTS "${_sha1_location}") - hunter_gate_internal_error("${_sha1_location} not found") - endif() - file(READ "${_sha1_location}" _sha1_value) - string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal) - if(NOT _is_equal) - hunter_gate_internal_error( - "Short SHA1 collision:" - " ${_sha1_value} (from ${_sha1_location})" - " ${HUNTER_GATE_SHA1} (HunterGate)" - ) - endif() - if(NOT EXISTS "${_master_location}") - hunter_gate_user_error( - "Master file not found:" - " ${_master_location}" - "try to update Hunter/HunterGate" - ) - endif() - endif() - include("${_master_location}") - set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) - endif() -endmacro() diff --git a/cmake/XLinkConfig.cmake.in b/cmake/XLinkConfig.cmake.in index 7eea9841..9dafb48b 100644 --- a/cmake/XLinkConfig.cmake.in +++ b/cmake/XLinkConfig.cmake.in @@ -5,6 +5,7 @@ set(XLINK_SHARED_LIBS @BUILD_SHARED_LIBS@) set(XLINK_ENABLE_LIBUSB @XLINK_ENABLE_LIBUSB@) set(XLINK_LIBUSB_LOCAL @XLINK_LIBUSB_LOCAL@) set(XLINK_LIBUSB_SYSTEM @XLINK_LIBUSB_SYSTEM@) +set(XLINK_INSTALL_PUBLIC_ONLY @XLINK_INSTALL_PUBLIC_ONLY@) # Specify that this is config mode (Called by find_package) set(CONFIG_MODE TRUE) diff --git a/cmake/XLinkDependencies.cmake b/cmake/XLinkDependencies.cmake index 1072d1d8..6c6af4af 100644 --- a/cmake/XLinkDependencies.cmake +++ b/cmake/XLinkDependencies.cmake @@ -5,19 +5,15 @@ if(CONFIG_MODE) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "BOTH") set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}/${_IMPORT_PREFIX}" ${CMAKE_PREFIX_PATH}) set(_QUIET "QUIET") -else() - # set(XLINK_SHARED_LIBS ${BUILD_SHARED_LIBS}) - if(XLINK_ENABLE_LIBUSB) - if(NOT XLINK_LIBUSB_LOCAL AND NOT XLINK_LIBUSB_SYSTEM) - hunter_add_package(libusb-luxonis) - endif() - endif() endif() # libusb -if(XLINK_ENABLE_LIBUSB) +if((NOT CONFIG_MODE OR (CONFIG_MODE AND NOT XLINK_INSTALL_PUBLIC_ONLY)) AND XLINK_ENABLE_LIBUSB) if(XLINK_LIBUSB_LOCAL) add_subdirectory("${XLINK_LIBUSB_LOCAL}" "${CMAKE_CURRENT_BINARY_DIR}/libusb" EXCLUDE_FROM_ALL) + elseif(XLINK_LIBUSB_SYSTEM) + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBUSB REQUIRED libusb-1.0) elseif(NOT XLINK_LIBUSB_SYSTEM) find_package(usb-1.0 ${_QUIET} CONFIG REQUIRED HINTS "${CMAKE_CURRENT_LIST_DIR}/libusb") endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 575c1563..2330b091 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,11 +31,29 @@ add_example(list_devices list_devices.cpp) # Search devices add_example(search_devices search_devices.cpp) +# Search devices +add_example(find_device find_device.cpp) + # Boot bootloader add_example(boot_bootloader boot_bootloader.cpp) # Boot firmware add_example(boot_firmware boot_firmware.cpp) +# Server example +add_example(xlink_server xlink_server.cpp) + +# Server example +add_example(xlink_server2 xlink_server2.cpp) # Boot firmware add_example(device_connect_reset device_connect_reset.cpp) + +# Local shared memory example +if (UNIX AND !APPLE) + add_example(xlink_server_local xlink_server_local.cpp) + add_example(xlink_client_local xlink_client_local.cpp) +endif (UNIX AND !APPLE) + +# EP example +add_example(xlink_usb_server xlink_usb_server.cpp) +add_example(xlink_usb_client xlink_usb_client.cpp) diff --git a/examples/boot_bootloader.cpp b/examples/boot_bootloader.cpp index 9fc8543a..60bbd553 100644 --- a/examples/boot_bootloader.cpp +++ b/examples/boot_bootloader.cpp @@ -27,7 +27,7 @@ int main(){ suitableDevice.platform = X_LINK_ANY_PLATFORM; suitableDevice.state = X_LINK_FLASH_BOOTED; - status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev); + status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev, XLINK_DEVICE_DEFAULT_SEARCH_TIMEOUT_MS); if(status != X_LINK_SUCCESS) throw std::runtime_error("Couldn't retrieve all connected devices"); if(numdev == 0){ diff --git a/examples/boot_firmware.cpp b/examples/boot_firmware.cpp index a7f4da8d..6f70662e 100644 --- a/examples/boot_firmware.cpp +++ b/examples/boot_firmware.cpp @@ -32,7 +32,7 @@ int main(int argc, const char** argv){ suitableDevice.platform = X_LINK_ANY_PLATFORM; suitableDevice.state = X_LINK_UNBOOTED; - status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev); + status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev, XLINK_DEVICE_DEFAULT_SEARCH_TIMEOUT_MS); if(status != X_LINK_SUCCESS) throw std::runtime_error("Couldn't retrieve all connected devices"); if(numdev == 0){ diff --git a/examples/find_device.cpp b/examples/find_device.cpp new file mode 100644 index 00000000..47598827 --- /dev/null +++ b/examples/find_device.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +XLinkGlobalHandler_t xlinkGlobalHandler = {}; + +int main(int argc, char* argv[]){ + + mvLogDefaultLevelSet(MVLOG_WARN); + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + throw std::runtime_error("Couldn't initialize XLink"); + } + + deviceDesc_t desc, dev; + desc.state = X_LINK_ANY_STATE; + desc.platform = X_LINK_ANY_PLATFORM; + desc.protocol = X_LINK_ANY_PROTOCOL; + desc.mxid[0] = 0; + + if(argc >= 2) { + strncpy(desc.name, argv[1], sizeof(desc.name)); + desc.nameHintOnly = true; + printf("Name: %s\n", argv[1]); + } + if(argc >= 3) { + strncpy(desc.mxid, argv[2], sizeof(desc.mxid)); + printf("ID: %s\n", argv[2]); + } + + // Use "name" as hint only, but might still change + status = XLinkFindFirstSuitableDevice(desc, &dev); + if(status != X_LINK_SUCCESS) { + printf("Couldnt find a device...\n"); + return -1; + } + + std::cout << "status: " << XLinkErrorToStr(dev.status); + std::cout << ", name: " << dev.name; + std::cout << ", mxid: " << dev.mxid; + std::cout << ", state: " << XLinkDeviceStateToStr(dev.state); + std::cout << ", protocol: " << XLinkProtocolToStr(dev.protocol); + std::cout << ", platform: " << XLinkPlatformToStr(dev.platform); + std::cout << std::endl; + +} \ No newline at end of file diff --git a/examples/list_devices.cpp b/examples/list_devices.cpp index f726818e..620d7d22 100644 --- a/examples/list_devices.cpp +++ b/examples/list_devices.cpp @@ -27,7 +27,7 @@ int main(){ suitableDevice.protocol = X_LINK_ANY_PROTOCOL; suitableDevice.platform = X_LINK_ANY_PLATFORM; - status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev); + status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev, XLINK_DEVICE_DEFAULT_SEARCH_TIMEOUT_MS); if(status != X_LINK_SUCCESS) throw std::runtime_error("Couldn't retrieve all connected devices"); // Print device details diff --git a/examples/xlink_client_local.cpp b/examples/xlink_client_local.cpp new file mode 100644 index 00000000..eb0f0e2a --- /dev/null +++ b/examples/xlink_client_local.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +const long MAXIMUM_SHM_SIZE = 4096; +const char *SHARED_MEMORY_NAME = "/xlink_shared_memory_b"; + +XLinkGlobalHandler_t xlinkGlobalHandler = {}; + +int main(int argc, const char** argv){ + xlinkGlobalHandler.protocol = X_LINK_TCP_IP_OR_LOCAL_SHDMEM; + + mvLogDefaultLevelSet(MVLOG_ERROR); + + printf("Initializing XLink...\n"); + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + printf("Initializing wasn't successful\n"); + return 1; + } + + XLinkHandler_t handler; + handler.devicePath = "127.0.0.1"; + handler.protocol = X_LINK_TCP_IP_OR_LOCAL_SHDMEM; + status = XLinkConnect(&handler); + if(X_LINK_SUCCESS != status) { + printf("Connecting wasn't successful\n"); + return 1; + } + + streamPacketDesc_t *packet; + + auto s = XLinkOpenStream(0, "test", 1024 * 1024); + assert(s != INVALID_STREAM_ID); + + // Read the data packet containing the FD + auto r = XLinkReadData(s, &packet); + assert(r == X_LINK_SUCCESS); + + void *sharedMemAddr; + long receivedFd = packet->fd; + if (receivedFd < 0) { + printf("Not a valid FD, data streamed through message\n"); + sharedMemAddr = packet->data; + } else { + // Map the shared memory + sharedMemAddr = mmap(NULL, MAXIMUM_SHM_SIZE, PROT_READ, MAP_SHARED, receivedFd, 0); + if (sharedMemAddr == MAP_FAILED) { + perror("mmap"); + return 1; + } + } + + // Read and print the message from shared memory + printf("Message from Process A: %s\n", static_cast(sharedMemAddr)); + + const char *normalMessage = "Normal message from Process B"; + auto w = XLinkWriteData(s, (uint8_t*)normalMessage, strlen(normalMessage) + 1); + assert(w == X_LINK_SUCCESS); + + const char *shmName = SHARED_MEMORY_NAME; + long shmFd = memfd_create(shmName, 0); + if (shmFd < 0) { + perror("shm_open"); + return 1; + } + + ftruncate(shmFd, MAXIMUM_SHM_SIZE); + + void *addr = mmap(NULL, MAXIMUM_SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shmFd, 0); + if (addr == MAP_FAILED) { + perror("mmap"); + close(shmFd); + shm_unlink(shmName); + return 1; + } + + // Write a message to the shared memory + const char *message = "Shared message from Process B!"; + memcpy(addr, message, strlen(message) + 1); + + // Send the FD through the XLinkWriteFd function + w = XLinkWriteFd(s, shmFd); + assert(w == X_LINK_SUCCESS); + + r = XLinkReadData(s, &packet); + assert(r == X_LINK_SUCCESS); + + printf("Message from Process A: %s\n", (char *)(packet->data)); + + if (receivedFd >= 0) { + munmap(sharedMemAddr, MAXIMUM_SHM_SIZE); + } + + munmap(addr, MAXIMUM_SHM_SIZE); + close(shmFd); + unlink(shmName); + + return 0; +} diff --git a/examples/xlink_server.cpp b/examples/xlink_server.cpp new file mode 100644 index 00000000..2a107d19 --- /dev/null +++ b/examples/xlink_server.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +XLinkGlobalHandler_t xlinkGlobalHandler = {}; + +int main(int argc, const char** argv){ + + xlinkGlobalHandler.protocol = X_LINK_TCP_IP; + + // Initialize and suppress XLink logs + mvLogDefaultLevelSet(MVLOG_ERROR); + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + throw std::runtime_error("Couldn't initialize XLink"); + } + + // loop through streams + constexpr static auto NUM_STREAMS = 16; + std::array threads; + for(int i = 0; i < NUM_STREAMS; i++){ + threads[i] = std::thread([i](){ + std::string name = "test_"; + auto s = XLinkOpenStream(0, (name + std::to_string(i)).c_str(), 1024); + assert(s != INVALID_STREAM_ID); + auto w = XLinkWriteData(s, (uint8_t*) &s, sizeof(s)); + assert(w == X_LINK_SUCCESS); + }); + } + for(auto& thread : threads){ + thread.join(); + } + + std::this_thread::sleep_for(std::chrono::seconds(3)); + std::cout << "All threads joined\n"; + + return 0; +} \ No newline at end of file diff --git a/examples/xlink_server2.cpp b/examples/xlink_server2.cpp new file mode 100644 index 00000000..15aa5331 --- /dev/null +++ b/examples/xlink_server2.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +XLinkGlobalHandler_t xlinkGlobalHandler = {}; + +int main(int argc, const char** argv){ + xlinkGlobalHandler.protocol = X_LINK_TCP_IP; + + printf("Initializing XLink...\n"); + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + return 0; + } + + XLinkHandler_t handler; + std::string serverIp{"127.0.0.1"}; + handler.devicePath = &serverIp[0]; + handler.protocol = X_LINK_TCP_IP; + status = XLinkServer(&handler, "xlinkserver", X_LINK_BOOTED, X_LINK_RVC3); + if(X_LINK_SUCCESS != status) { + return 0; + } + + return 0; +} \ No newline at end of file diff --git a/examples/xlink_server_local.cpp b/examples/xlink_server_local.cpp new file mode 100644 index 00000000..e2b4d64d --- /dev/null +++ b/examples/xlink_server_local.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +const long MAXIMUM_SHM_SIZE = 4096; +const char *SHARED_MEMORY_NAME = "/xlink_shared_memory_a"; + +XLinkGlobalHandler_t xlinkGlobalHandler = {}; + +int main(int argc, const char** argv){ + xlinkGlobalHandler.protocol = X_LINK_TCP_IP_OR_LOCAL_SHDMEM; + + mvLogDefaultLevelSet(MVLOG_ERROR); + + printf("Initializing XLink...\n"); + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + printf("Initializing wasn't successful\n"); + return 1; + } + + XLinkHandler_t handler; + handler.devicePath = "0.0.0.0"; + handler.protocol = X_LINK_TCP_IP_OR_LOCAL_SHDMEM; + status = XLinkServerOnly(&handler); + if(X_LINK_SUCCESS != status) { + printf("Connecting wasn't successful\n"); + return 1; + } + + auto s = XLinkOpenStream(0, "test", 1024 * 1024); + assert(s != INVALID_STREAM_ID); + + const char *shmName = SHARED_MEMORY_NAME; + long shmFd = memfd_create(shmName, 0); + if (shmFd < 0) { + perror("shm_open"); + return 1; + } + + ftruncate(shmFd, MAXIMUM_SHM_SIZE); + + void *addr = mmap(NULL, MAXIMUM_SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shmFd, 0); + if (addr == MAP_FAILED) { + perror("mmap"); + close(shmFd); + shm_unlink(shmName); + return 1; + } + + // Write a message to the shared memory + const char *message = "Shared message from Process A!"; + memcpy(addr, message, strlen(message) + 1); + + // Send the FD through the XLinkWriteFd function + auto w = XLinkWriteFd(s, shmFd); + assert(w == X_LINK_SUCCESS); + + streamPacketDesc_t *packet; + auto r = XLinkReadData(s, &packet); + assert(r == X_LINK_SUCCESS); + + printf("Message from Process B: %s\n", (char *)(packet->data)); + + // Read the data packet containing the FD + r = XLinkReadData(s, &packet); + assert(r == X_LINK_SUCCESS); + + void *sharedMemAddr; + long receivedFd = packet->fd; + if (receivedFd < 0) { + printf("Not a valid FD, data streamed through message\n"); + sharedMemAddr = packet->data; + } else { + // Map the shared memory + sharedMemAddr = mmap(NULL, MAXIMUM_SHM_SIZE, PROT_READ, MAP_SHARED, receivedFd, 0); + if (sharedMemAddr == MAP_FAILED) { + perror("mmap"); + return 1; + } + } + + // Read and print the message from shared memory + printf("Message from Process B: %s\n", static_cast(sharedMemAddr)); + + const char *normalMessage = "Normal message from Process A"; + w = XLinkWriteData(s, (uint8_t*)normalMessage, strlen(normalMessage) + 1); + assert(w == X_LINK_SUCCESS); + + if (receivedFd >= 0) { + munmap(sharedMemAddr, MAXIMUM_SHM_SIZE); + } + + munmap(addr, MAXIMUM_SHM_SIZE); + close(shmFd); + unlink(shmName); + + return 0; +} diff --git a/examples/xlink_usb_client.cpp b/examples/xlink_usb_client.cpp new file mode 100644 index 00000000..01b1d767 --- /dev/null +++ b/examples/xlink_usb_client.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +// Common constants +const uint8_t DUMMY_DATA[1024*128] = {}; + +int main(int argc, char** argv) { + XLinkGlobalHandler_t gHandler; + XLinkInitialize(&gHandler); + + mvLogDefaultLevelSet(MVLOG_DEBUG); + + deviceDesc_t deviceDesc; + strcpy(deviceDesc.name, "usbdev"); + deviceDesc.protocol = X_LINK_USB_EP; + + printf("Device name: %s\n", deviceDesc.name); + + XLinkHandler_t handler; + handler.devicePath = deviceDesc.name; + handler.protocol = deviceDesc.protocol; + auto connRet = XLinkConnect(&handler); + printf("Connection returned: %s\n", XLinkErrorToStr(connRet)); + if(connRet != X_LINK_SUCCESS) { + return -1; + } + + auto s = XLinkOpenStream(handler.linkId, "test_0", sizeof(DUMMY_DATA) * 2); + if(s == INVALID_STREAM_ID){ + printf("Open stream failed...\n"); + } else { + printf("Open stream OK - id: 0x%08X\n", s); + } + + auto w = XLinkWriteData(s, (uint8_t*) &s, sizeof(s)); + assert(w == X_LINK_SUCCESS); + + return 0; +} diff --git a/examples/xlink_usb_server.cpp b/examples/xlink_usb_server.cpp new file mode 100644 index 00000000..fe223960 --- /dev/null +++ b/examples/xlink_usb_server.cpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +// Common constants +constexpr static auto NUM_STREAMS = 16; +constexpr static auto NUM_PACKETS = 120; +const uint8_t DUMMY_DATA[1024*128] = {}; +XLinkGlobalHandler_t xlinkGlobalHandler = {}; + +// Server +// + +int main(int argc, const char** argv){ + + xlinkGlobalHandler.protocol = X_LINK_USB_EP; + + // Initialize and suppress XLink logs + mvLogDefaultLevelSet(MVLOG_DEBUG); + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + throw std::runtime_error("Couldn't initialize XLink"); + } + + XLinkHandler_t handler; + handler.devicePath = "/dev/usb-ffs/device"; + handler.protocol = X_LINK_USB_EP; + XLinkServerOnly(&handler); + + + // loop through streams + auto s = XLinkOpenStream(0, "test_0", sizeof(DUMMY_DATA) * 2); + assert(s != INVALID_STREAM_ID); + + // auto w = XLinkWriteData2(s, (uint8_t*) &s, sizeof(s/2), ((uint8_t*) &s) + sizeof(s/2), sizeof(s) - sizeof(s/2)); + // assert(w == X_LINK_SUCCESS); + + auto w = XLinkWriteData(s, (uint8_t*) &s, sizeof(s)); + assert(w == X_LINK_SUCCESS); + + + streamPacketDesc_t p; + w = XLinkReadMoveData(s, &p); + assert(w == X_LINK_SUCCESS); + XLinkDeallocateMoveData(p.data, p.length); + + return 0; +} diff --git a/include/XLink/XLink.h b/include/XLink/XLink.h index 0a77ea47..1774f387 100644 --- a/include/XLink/XLink.h +++ b/include/XLink/XLink.h @@ -10,6 +10,7 @@ #ifndef _XLINK_H #define _XLINK_H #include "XLinkPublicDefines.h" +#include "XLinkTime.h" #ifdef __cplusplus extern "C" @@ -28,6 +29,66 @@ extern "C" */ XLinkError_t XLinkInitialize(XLinkGlobalHandler_t* globalHandler); +/** + * @brief Initializes XLink Server and detached discovery service + * @param globalHandler[in] XLink global communication parameters + * @return Status code of the operation: X_LINK_SUCCESS (0) for success + */ +XLinkError_t XLinkServer(XLinkHandler_t* handler, const char* deviceId, XLinkDeviceState_t state, XLinkPlatform_t platform); + +/** + * @brief Initializes only XLink Server and not discovery service + * @param globalHandler[in] XLink global communication parameters + * @return Status code of the operation: X_LINK_SUCCESS (0) for success + */ +XLinkError_t XLinkServerOnly(XLinkHandler_t* handler); + +/** + * @brief Starts discovery service with given description of itself + * @param deviceId[in] Id to respond with + * @param state[in] State to respond with + * @param platform[in] Platform to respond with + * @return Status code of the operation: X_LINK_SUCCESS (0) for success + */ +XLinkError_t XLinkDiscoveryServiceStart(const char* deviceId, XLinkDeviceState_t state, XLinkPlatform_t platform); + +/** + * @brief Sets a callback when discovery services receives a reset request + * @param cb[in] Callback + */ +void XLinkDiscoveryServiceSetCallbackReset(void (*cb)()); + +/** + * @brief Checks whether or not a service is running + * @return True if service is running, false otherwise + */ +bool XLinkDiscoveryServiceIsRunning(); + +/** + * @brief Stops discovery service. Blocking operation for max 500ms + */ +void XLinkDiscoveryServiceStop(); + +/** + * @brief Detaches discovery service thread. Use when not intending to stop it manually + */ +void XLinkDiscoveryServiceDetach(); + +/** + * @brief Adds a callback for link down events + * @param cb[in] Callback function to be called + * @return Callback id, -1 for error + */ +int XLinkAddLinkDownCb(void (*cb)(linkId_t)); + +/** + * @brief Removes callback with given id + * + * @param cbId callback id retrieved by XLinkAddLinkDownCb function + * @return status, 0 ok else error + */ +int XLinkRemoveLinkDownCb(int cbId); + #ifndef __DEVICE__ /** @@ -64,12 +125,14 @@ XLinkError_t XLinkFindFirstSuitableDevice(const deviceDesc_t in_deviceRequiremen * @param[in,out] out_foundDevicesPtr - pointer to array with all found devices descriptions * @param[out] devicesArraySize - size of out_foundDevicesPtr * @param[out] out_foundDevicesCount - amount of found devices + * @param[in] timeoutMs - for how long to search for * @return Status code of the operation: X_LINK_SUCCESS (0) for success */ XLinkError_t XLinkFindAllSuitableDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t *out_foundDevicesPtr, const unsigned int devicesArraySize, - unsigned int *out_foundDevicesCount); + unsigned int *out_foundDevicesCount, + int timeoutMs); /** * @brief Returns all Myriad devices description which meets the requirements @@ -241,6 +304,43 @@ XLinkError_t XLinkCloseStream(streamId_t const streamId); */ XLinkError_t XLinkWriteData(streamId_t const streamId, const uint8_t* buffer, int size); +XLinkError_t XLinkWriteData_(streamId_t streamId, const uint8_t* buffer, int size, XLinkTimespec* outTSend); + +/** + * @brief Sends/Receives a message to Gate via USB + * @param[in] name - Device name/path + * @param[in] data - Data to be transmitted/collected + * @param[in] size - The data size + * @param[in] size - USB timeout + * @return Status code of the operation: X_LINK_SUCCESS (0) for success + */ +XLinkError_t XLinkGateWrite(const char *name, void *data, int size, int timeout); +XLinkError_t XLinkGateRead(const char *name, void *data, int size, int timeout); + +/** + * @brief Sends a package to initiate the writing of a file descriptor + * @warning Actual size of the written data is ALIGN_UP(size, 64) + * @param[in] streamId - stream link Id obtained from XLinkOpenStream call + * @param[in] buffer - FD to be transmitted + * @return Status code of the operation: X_LINK_SUCCESS (0) for success + */ +XLinkError_t XLinkWriteFd(streamId_t const streamId, const long fd); +XLinkError_t XLinkWriteFd_(streamId_t streamId, const long fd, XLinkTimespec* outTSend); +XLinkError_t XLinkWriteFdData(streamId_t streamId, const long fd, const uint8_t* dataBuffer, int dataSize); + + +/** + * @brief Sends a package to initiate the writing of data to a remote stream + * @warning Actual size of the written data is ALIGN_UP(size, 64) + * @param[in] streamId – stream link Id obtained from XLinkOpenStream call + * @param[in] buffer1 – data buffer to be transmitted + * @param[in] buffer1Size – size of the data to be transmitted + * @param[in] buffer2 – data buffer to be transmitted + * @param[in] buffer2Size – size of the data to be transmitted + * @return Status code of the operation: X_LINK_SUCCESS (0) for success + */ +XLinkError_t XLinkWriteData2(streamId_t streamId, const uint8_t* buffer1, int buffer1Size, const uint8_t* buffer2, int buffer2Size); + /** * @brief Sends a package to initiate the writing of data to a remote stream * @warning Actual size of the written data is ALIGN_UP(size, 64) diff --git a/include/XLink/XLinkDispatcher.h b/include/XLink/XLinkDispatcher.h index e0b993c0..5e5d235c 100644 --- a/include/XLink/XLinkDispatcher.h +++ b/include/XLink/XLinkDispatcher.h @@ -12,15 +12,15 @@ #include "XLinkPrivateDefines.h" #include "time.h" +#include "stdbool.h" #ifdef __cplusplus extern "C" { #endif -typedef int (*getRespFunction) (xLinkEvent_t*, - xLinkEvent_t*); +typedef int (*getRespFunction) (xLinkEvent_t*, xLinkEvent_t*, bool); typedef struct { - int (*eventSend) (xLinkEvent_t*); + int (*eventSend) (xLinkEvent_t*, XLinkTimespec*); int (*eventReceive) (xLinkEvent_t*); getRespFunction localGetResponse; getRespFunction remoteGetResponse; @@ -29,11 +29,14 @@ typedef struct { } DispatcherControlFunctions; XLinkError_t DispatcherInitialize(DispatcherControlFunctions *controlFunc); -XLinkError_t DispatcherStart(xLinkDeviceHandle_t *deviceHandle); +XLinkError_t DispatcherStart(xLinkDesc_t *deviceHandle); +XLinkError_t DispatcherStartServer(xLinkDesc_t *deviceHandle); +XLinkError_t DispatcherStartImpl(xLinkDesc_t *deviceHandle, bool server); int DispatcherClean(xLinkDeviceHandle_t *deviceHandle); int DispatcherDeviceFdDown(xLinkDeviceHandle_t *deviceHandle); xLinkEvent_t* DispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event); +xLinkEvent_t* DispatcherAddEvent_(xLinkEventOrigin_t origin, xLinkEvent_t *event, XLinkTimespec* outTime); int DispatcherWaitEventComplete(xLinkDeviceHandle_t *deviceHandle, unsigned int timeoutMs); int DispatcherWaitEventCompleteTimeout(xLinkDeviceHandle_t *deviceHandle, struct timespec abstime); @@ -50,4 +53,4 @@ int DispatcherServeEvent(eventId_t id, } #endif -#endif \ No newline at end of file +#endif diff --git a/include/XLink/XLinkDispatcherImpl.h b/include/XLink/XLinkDispatcherImpl.h index 6172909e..19e751af 100644 --- a/include/XLink/XLinkDispatcherImpl.h +++ b/include/XLink/XLinkDispatcherImpl.h @@ -6,13 +6,12 @@ #define _XLINKDISPATCHERIMPL_H #include "XLinkPrivateDefines.h" +#include -int dispatcherEventSend (xLinkEvent_t*); +int dispatcherEventSend (xLinkEvent_t*, XLinkTimespec* sendTime); int dispatcherEventReceive (xLinkEvent_t*); -int dispatcherLocalEventGetResponse (xLinkEvent_t*, - xLinkEvent_t*); -int dispatcherRemoteEventGetResponse (xLinkEvent_t*, - xLinkEvent_t*); +int dispatcherLocalEventGetResponse (xLinkEvent_t*, xLinkEvent_t*, bool); +int dispatcherRemoteEventGetResponse (xLinkEvent_t*, xLinkEvent_t*, bool); void dispatcherCloseLink (void* fd, int fullClose); void dispatcherCloseDeviceFd (xLinkDeviceHandle_t* deviceHandle); diff --git a/include/XLink/XLinkLog.h b/include/XLink/XLinkLog.h index b8b2195e..412e5986 100644 --- a/include/XLink/XLinkLog.h +++ b/include/XLink/XLinkLog.h @@ -29,6 +29,7 @@ extern "C" { #include #include #include +#include "XLinkExport.h" #include "XLinkExport.h" diff --git a/include/XLink/XLinkPlatform.h b/include/XLink/XLinkPlatform.h index c8c6574b..c3974a95 100644 --- a/include/XLink/XLinkPlatform.h +++ b/include/XLink/XLinkPlatform.h @@ -34,14 +34,17 @@ typedef enum { X_LINK_PLATFORM_DRIVER_NOT_LOADED = -128, X_LINK_PLATFORM_USB_DRIVER_NOT_LOADED = X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_USB_VSC, X_LINK_PLATFORM_TCP_IP_DRIVER_NOT_LOADED = X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_TCP_IP, + X_LINK_PLATFORM_LOCAL_SHDMEM_DRIVER_NOT_LOADED = X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_LOCAL_SHDMEM, + X_LINK_PLATFORM_TCP_IP_OR_LOCAL_SHDMEM_DRIVER_NOT_LOADED = X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_TCP_IP_OR_LOCAL_SHDMEM, X_LINK_PLATFORM_PCIE_DRIVER_NOT_LOADED = X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_PCIE, + X_LINK_PLATFORM_USB_EP_DRIVER_NOT_LOADED = X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_USB_EP, } xLinkPlatformErrorCode_t; // ------------------------------------ // Device management. Begin. // ------------------------------------ -xLinkPlatformErrorCode_t XLinkPlatformInit(void* options); +xLinkPlatformErrorCode_t XLinkPlatformInit(XLinkGlobalHandler_t* globalHandler); #ifndef __DEVICE__ /** @@ -49,7 +52,7 @@ xLinkPlatformErrorCode_t XLinkPlatformInit(void* options); */ xLinkPlatformErrorCode_t XLinkPlatformFindDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, unsigned sizeFoundDevices, - unsigned *out_amountOfFoundDevices); + unsigned *out_amountOfFoundDevices, int timeoutMs); xLinkPlatformErrorCode_t XLinkPlatformFindDevicesDynamic(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, unsigned sizeFoundDevices, unsigned *out_amountOfFoundDevices, int timeoutMs, bool (*cb)(deviceDesc_t*, unsigned int)); @@ -64,8 +67,10 @@ xLinkPlatformErrorCode_t XLinkPlatformFindArrayOfDevicesNames( xLinkPlatformErrorCode_t XLinkPlatformBootRemote(const deviceDesc_t* deviceDesc, const char* binaryPath); xLinkPlatformErrorCode_t XLinkPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length); xLinkPlatformErrorCode_t XLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, - XLinkProtocol_t protocol, void** fd); + XLinkProtocol_t *protocol, void** fd); xLinkPlatformErrorCode_t XLinkPlatformBootBootloader(const char* name, XLinkProtocol_t protocol); +xLinkPlatformErrorCode_t XLinkPlatformServer(const char* devPathRead, const char* devPathWrite, + XLinkProtocol_t *protocol, void** fd); UsbSpeed_t get_usb_speed(); const char* get_mx_serial(); @@ -84,7 +89,10 @@ xLinkPlatformErrorCode_t XLinkPlatformCloseRemote(xLinkDeviceHandle_t* deviceHan // ------------------------------------ int XLinkPlatformWrite(xLinkDeviceHandle_t *deviceHandle, void *data, int size); -int XLinkPlatformRead(xLinkDeviceHandle_t *deviceHandle, void *data, int size); +int XLinkPlatformWriteFd(xLinkDeviceHandle_t *deviceHandle, const long fd, void *data2, int size2); +int XLinkPlatformRead(xLinkDeviceHandle_t *deviceHandle, void *data, int size, long *fd); +int XLinkPlatformGateWrite(const char *name, void *data, int size, int timeout); +int XLinkPlatformGateRead(const char *name, void *data, int size, int timeout); void* XLinkPlatformAllocateData(uint32_t size, uint32_t alignment); void XLinkPlatformDeallocateData(void *ptr, uint32_t size, uint32_t alignment); @@ -99,18 +107,9 @@ void XLinkPlatformDeallocateData(void *ptr, uint32_t size, uint32_t alignment); // Helpers. Begin. // ------------------------------------ -#ifndef __DEVICE__ - int XLinkPlatformIsDescriptionValid(const deviceDesc_t *in_deviceDesc, const XLinkDeviceState_t state); char* XLinkPlatformErrorToStr(const xLinkPlatformErrorCode_t errorCode); -// for deprecated API -XLinkPlatform_t XLinkPlatformPidToPlatform(const int pid); -XLinkDeviceState_t XLinkPlatformPidToState(const int pid); -// for deprecated API - -#endif // __DEVICE__ - // ------------------------------------ // Helpers. End. // ------------------------------------ diff --git a/include/XLink/XLinkPrivateDefines.h b/include/XLink/XLinkPrivateDefines.h index f281578a..9b9e92f0 100644 --- a/include/XLink/XLinkPrivateDefines.h +++ b/include/XLink/XLinkPrivateDefines.h @@ -89,7 +89,8 @@ typedef enum XLINK_CLOSE_STREAM_REQ, XLINK_PING_REQ, XLINK_RESET_REQ, - XLINK_REQUEST_LAST, + + XLINK_STATIC_REQUEST_LAST, //note that is important to separate request and response XLINK_WRITE_RESP, XLINK_READ_RESP, @@ -98,20 +99,28 @@ typedef enum XLINK_CLOSE_STREAM_RESP, XLINK_PING_RESP, XLINK_RESET_RESP, - XLINK_RESP_LAST, + + XLINK_STATIC_RESP_LAST, /*X_LINK_IPC related events*/ IPC_WRITE_REQ, IPC_READ_REQ, IPC_CREATE_STREAM_REQ, IPC_CLOSE_STREAM_REQ, + // IPC_WRITE_RESP, IPC_READ_RESP, IPC_CREATE_STREAM_RESP, IPC_CLOSE_STREAM_RESP, + XLINK_READ_REL_SPEC_REQ, + XLINK_WRITE_FD_REQ, // only for the shared mem protocol + XLINK_REQUEST_LAST, + XLINK_READ_REL_SPEC_RESP, + XLINK_WRITE_FD_RESP, // only for the shared mem protocol + XLINK_RESP_LAST, } xLinkEventType_t; typedef enum @@ -120,11 +129,8 @@ typedef enum EVENT_REMOTE, } xLinkEventOrigin_t; -#ifndef __DEVICE__ #define MAX_LINKS 64 -#else -#define MAX_LINKS 1 -#endif +#define MAX_LINK_DOWN_CBS 64 #define MAX_EVENTS 64 #define MAX_SCHEDULERS MAX_LINKS @@ -159,6 +165,8 @@ typedef struct xLinkEvent_t { XLINK_ALIGN_TO_BOUNDARY(64) xLinkEventHeader_t header; xLinkDeviceHandle_t deviceHandle; void* data; + void* data2; + int data2Size; }xLinkEvent_t; #define XLINK_INIT_EVENT(event, in_streamId, in_type, in_size, in_data, in_deviceHandle) do { \ diff --git a/include/XLink/XLinkPrivateFields.h b/include/XLink/XLinkPrivateFields.h index 0e831374..441520c0 100644 --- a/include/XLink/XLinkPrivateFields.h +++ b/include/XLink/XLinkPrivateFields.h @@ -15,7 +15,7 @@ #define EXTRACT_STREAM_ID(streamId) ((streamId) & STREAM_ID_MASK) #define COMBINE_IDS(streamId, linkid) \ - streamId = streamId | ((linkid & LINK_ID_MASK) << LINK_ID_SHIFT); + streamId = streamId | (((uint32_t)linkid & LINK_ID_MASK) << LINK_ID_SHIFT); // ------------------------------------ // Global fields declaration. Begin. diff --git a/include/XLink/XLinkPublicDefines.h b/include/XLink/XLinkPublicDefines.h index d5f1f8e8..fd53d147 100644 --- a/include/XLink/XLinkPublicDefines.h +++ b/include/XLink/XLinkPublicDefines.h @@ -10,6 +10,7 @@ #ifndef _XLINKPUBLICDEFINES_H #define _XLINKPUBLICDEFINES_H #include +#include #include #include "XLinkTime.h" #ifdef __cplusplus @@ -27,6 +28,7 @@ extern "C" #endif #define XLINK_MAX_PACKETS_PER_STREAM 64 #define XLINK_NO_RW_TIMEOUT 0xFFFFFFFF +#define XLINK_DEVICE_DEFAULT_SEARCH_TIMEOUT_MS 500 typedef enum { @@ -53,6 +55,8 @@ typedef enum{ X_LINK_NOT_IMPLEMENTED, X_LINK_INIT_USB_ERROR, X_LINK_INIT_TCP_IP_ERROR, + X_LINK_INIT_LOCAL_SHDMEM_ERROR, + X_LINK_INIT_TCP_IP_OR_LOCAL_SHDMEM_ERROR, X_LINK_INIT_PCIE_ERROR, } XLinkError_t; @@ -62,6 +66,9 @@ typedef enum{ X_LINK_PCIE, X_LINK_IPC, X_LINK_TCP_IP, + X_LINK_LOCAL_SHDMEM, + X_LINK_TCP_IP_OR_LOCAL_SHDMEM, + X_LINK_USB_EP, X_LINK_NMB_OF_PROTOCOLS, X_LINK_ANY_PROTOCOL } XLinkProtocol_t; @@ -70,6 +77,8 @@ typedef enum{ X_LINK_ANY_PLATFORM = 0, X_LINK_MYRIAD_2 = 2450, X_LINK_MYRIAD_X = 2480, + X_LINK_RVC3 = 3000, + X_LINK_RVC4 = 4000, } XLinkPlatform_t; typedef enum{ @@ -97,6 +106,18 @@ typedef enum{ * The device has booted the flashed firmware/pipeline (e.g. in case of OAK POE devices in standalone mode). */ X_LINK_BOOTED_NON_EXCLUSIVE = X_LINK_FLASH_BOOTED, + /** + * The device is running Gate + */ + X_LINK_GATE, + /** + * The device is running Gate and already booted + */ + X_LINK_GATE_BOOTED, + /** + * The device is in setup mode + */ + X_LINK_GATE_SETUP } XLinkDeviceState_t; typedef enum{ @@ -127,6 +148,7 @@ typedef struct streamPacketDesc_t { uint8_t* data; uint32_t length; + int32_t fd; // file descriptor XLinkTimespec tRemoteSent; /// remote timestamp of when the packet was sent. Related to remote clock. Note: not directly related to local clock XLinkTimespec tReceived; /// local timestamp of when the packet was received. Related to local monotonic clock } streamPacketDesc_t; diff --git a/include/XLink/XLinkTime.h b/include/XLink/XLinkTime.h index 49666fa0..1349fa45 100644 --- a/include/XLink/XLinkTime.h +++ b/include/XLink/XLinkTime.h @@ -1,6 +1,7 @@ #ifndef _XLINK_TIME_H #define _XLINK_TIME_H +#include #ifdef __cplusplus extern "C" { @@ -16,4 +17,4 @@ void getMonotonicTimestamp(XLinkTimespec* ts); #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/src/pc/PlatformData.c b/src/pc/PlatformData.c index dc39c9a1..9fb2d56b 100644 --- a/src/pc/PlatformData.c +++ b/src/pc/PlatformData.c @@ -14,6 +14,7 @@ #include "usb_host.h" #include "pcie_host.h" #include "tcpip_host.h" +#include "local_memshd.h" #include "PlatformDeviceFd.h" #include "inttypes.h" @@ -35,6 +36,9 @@ #include #include #include +#include +#include +#include #endif #ifdef USE_LINK_JTAG @@ -52,20 +56,19 @@ #include #include "usb_host.h" +#endif /*USE_USB_VSC*/ extern int usbFdWrite; extern int usbFdRead; -#endif /*USE_USB_VSC*/ +extern int usbGateFdWrite; +extern int usbGateFdRead; // ------------------------------------ // Wrappers declaration. Begin. // ------------------------------------ static int pciePlatformRead(void *f, void *data, int size); -static int tcpipPlatformRead(void *fd, void *data, int size); - static int pciePlatformWrite(void *f, void *data, int size); -static int tcpipPlatformWrite(void *fd, void *data, int size); // ------------------------------------ // Wrappers declaration. End. @@ -86,7 +89,8 @@ int XLinkPlatformWrite(xLinkDeviceHandle_t *deviceHandle, void *data, int size) switch (deviceHandle->protocol) { case X_LINK_USB_VSC: case X_LINK_USB_CDC: - return usbPlatformWrite(deviceHandle->xLinkFD, data, size); + case X_LINK_USB_EP: + return usbPlatformWrite(deviceHandle->protocol, deviceHandle->xLinkFD, data, size); case X_LINK_PCIE: return pciePlatformWrite(deviceHandle->xLinkFD, data, size); @@ -94,12 +98,37 @@ int XLinkPlatformWrite(xLinkDeviceHandle_t *deviceHandle, void *data, int size) case X_LINK_TCP_IP: return tcpipPlatformWrite(deviceHandle->xLinkFD, data, size); +#if defined(__unix__) + case X_LINK_LOCAL_SHDMEM: + return shdmemPlatformWrite(deviceHandle->xLinkFD, data, size); +#endif + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: + mvLog(MVLOG_ERROR, "Failed to write with TCP_IP_OR_LOCAL_SHDMEM\n"); + default: return X_LINK_PLATFORM_INVALID_PARAMETERS; } } -int XLinkPlatformRead(xLinkDeviceHandle_t *deviceHandle, void *data, int size) +int XLinkPlatformWriteFd(xLinkDeviceHandle_t *deviceHandle, const long fd, void *data2, int size2) +{ + if(!XLinkIsProtocolInitialized(deviceHandle->protocol)) { + return X_LINK_PLATFORM_DRIVER_NOT_LOADED+deviceHandle->protocol; + } + + switch (deviceHandle->protocol) { +#if defined(__unix__) + case X_LINK_LOCAL_SHDMEM: + return shdmemPlatformWriteFd(deviceHandle->xLinkFD, fd, data2, size2); +#endif + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: + mvLog(MVLOG_ERROR, "Failed to write FD with TCP_IP_OR_LOCAL_SHDMEM\n"); + default: + return X_LINK_PLATFORM_INVALID_PARAMETERS; + } +} + +int XLinkPlatformRead(xLinkDeviceHandle_t *deviceHandle, void *data, int size, long *fd) { if(!XLinkIsProtocolInitialized(deviceHandle->protocol)) { return X_LINK_PLATFORM_DRIVER_NOT_LOADED+deviceHandle->protocol; @@ -108,19 +137,46 @@ int XLinkPlatformRead(xLinkDeviceHandle_t *deviceHandle, void *data, int size) switch (deviceHandle->protocol) { case X_LINK_USB_VSC: case X_LINK_USB_CDC: - return usbPlatformRead(deviceHandle->xLinkFD, data, size); + case X_LINK_USB_EP: + return usbPlatformRead(deviceHandle->protocol, deviceHandle->xLinkFD, data, size); case X_LINK_PCIE: return pciePlatformRead(deviceHandle->xLinkFD, data, size); case X_LINK_TCP_IP: return tcpipPlatformRead(deviceHandle->xLinkFD, data, size); - + +#if defined(__unix__) + case X_LINK_LOCAL_SHDMEM: + return shdmemPlatformRead(deviceHandle->xLinkFD, data, size, fd); +#endif + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: + mvLog(MVLOG_ERROR, "Failed to read with TCP_IP_OR_LOCAL_SHDMEM\n"); default: return X_LINK_PLATFORM_INVALID_PARAMETERS; } } +int XLinkPlatformGateWrite(const char *name, void *data, int size, int timeout) +{ + if(!XLinkIsProtocolInitialized(X_LINK_USB_EP)) { + return X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_USB_EP; + } + + return usbPlatformGateWrite(name, data, size, timeout); +} + +int XLinkPlatformGateRead(const char *name, void *data, int size, int timeout) +{ + if(!XLinkIsProtocolInitialized(X_LINK_USB_EP)) { + return X_LINK_PLATFORM_DRIVER_NOT_LOADED+X_LINK_USB_EP; + } + + return usbPlatformGateRead(name, data, size, timeout); +} + + + void* XLinkPlatformAllocateData(uint32_t size, uint32_t alignment) { void* ret = NULL; @@ -262,70 +318,7 @@ int pciePlatformRead(void *f, void *data, int size) #endif } -static int tcpipPlatformRead(void *fdKey, void *data, int size) -{ -#if defined(USE_TCP_IP) - int nread = 0; - - void* tmpsockfd = NULL; - if(getPlatformDeviceFdFromKey(fdKey, &tmpsockfd)){ - mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); - return -1; - } - TCPIP_SOCKET sock = (TCPIP_SOCKET) (uintptr_t) tmpsockfd; - while(nread < size) - { - int rc = recv(sock, &((char*)data)[nread], size - nread, 0); - if(rc <= 0) - { - return -1; - } - else - { - nread += rc; - } - } -#endif - return 0; -} - -static int tcpipPlatformWrite(void *fdKey, void *data, int size) -{ -#if defined(USE_TCP_IP) - int byteCount = 0; - - void* tmpsockfd = NULL; - if(getPlatformDeviceFdFromKey(fdKey, &tmpsockfd)){ - mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); - return -1; - } - TCPIP_SOCKET sock = (TCPIP_SOCKET) (uintptr_t) tmpsockfd; - - while(byteCount < size) - { - // Use send instead of write and ignore SIGPIPE - //rc = write((intptr_t)fd, &((char*)data)[byteCount], size - byteCount); - - int flags = 0; - #if defined(MSG_NOSIGNAL) - // Use flag NOSIGNAL on send call - flags = MSG_NOSIGNAL; - #endif - - int rc = send(sock, &((char*)data)[byteCount], size - byteCount, flags); - if(rc <= 0) - { - return -1; - } - else - { - byteCount += rc; - } - } -#endif - return 0; -} // ------------------------------------ // Wrappers implementation. End. diff --git a/src/pc/PlatformDeviceControl.c b/src/pc/PlatformDeviceControl.c index 1b1092f9..cce0bd63 100644 --- a/src/pc/PlatformDeviceControl.c +++ b/src/pc/PlatformDeviceControl.c @@ -10,6 +10,8 @@ #include "usb_host.h" #include "pcie_host.h" #include "tcpip_host.h" +#include "local_memshd.h" +#include "tcpip_memshd.h" #include "XLinkStringUtils.h" #include "PlatformDeviceFd.h" @@ -22,10 +24,12 @@ #include #include #include +#endif /*USE_USB_VSC*/ int usbFdWrite = -1; int usbFdRead = -1; -#endif /*USE_USB_VSC*/ +int usbGateFdWrite = -1; +int usbGateFdRead = -1; #include "XLinkPublicDefines.h" @@ -63,30 +67,22 @@ static const int statuswaittimeout = 5; // ------------------------------------ static int pciePlatformConnect(UNUSED const char *devPathRead, const char *devPathWrite, void **fd); -static int tcpipPlatformConnect(const char *devPathRead, const char *devPathWrite, void **fd); - static xLinkPlatformErrorCode_t usbPlatformBootBootloader(const char *name); static int pciePlatformBootBootloader(const char *name); -static xLinkPlatformErrorCode_t tcpipPlatformBootBootloader(const char *name); - static int pciePlatformClose(void *f); -static int tcpipPlatformClose(void *fd); - static int pciePlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length); -static int tcpipPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length); // ------------------------------------ // Wrappers declaration. End. // ------------------------------------ -void xlinkSetProtocolInitialized(const XLinkProtocol_t protocol, int initialized); - // ------------------------------------ // XLinkPlatform API implementation. Begin. // ------------------------------------ -xLinkPlatformErrorCode_t XLinkPlatformInit(void* options) +void xlinkSetProtocolInitialized(const XLinkProtocol_t protocol, int initialized); +xLinkPlatformErrorCode_t XLinkPlatformInit(XLinkGlobalHandler_t* globalHandler) { // Set that all protocols are initialized at first for(int i = 0; i < X_LINK_NMB_OF_PROTOCOLS; i++) { @@ -94,16 +90,25 @@ xLinkPlatformErrorCode_t XLinkPlatformInit(void* options) } // check for failed initialization; LIBUSB_SUCCESS = 0 - if (usbInitialize(options) != 0) { + if (usbInitialize(globalHandler->options) != 0) { xlinkSetProtocolInitialized(X_LINK_USB_VSC, 0); + xlinkSetProtocolInitialized(X_LINK_USB_EP, 0); + } + + // Initialize tcpip protocol if necessary + if(tcpip_initialize() != TCPIP_HOST_SUCCESS) { + xlinkSetProtocolInitialized(X_LINK_TCP_IP, 0); } - // TODO(themarpe) - move to tcpip_host - //tcpipInitialize(); -#if (defined(_WIN32) || defined(_WIN64)) && defined(USE_TCP_IP) - WSADATA wsa_data; - WSAStartup(MAKEWORD(2,2), &wsa_data); +#if defined(__unix__) + // Initialize the shared memory protocol if necessary + if (shdmem_initialize() != 0) { + xlinkSetProtocolInitialized(X_LINK_LOCAL_SHDMEM, 0); + } #endif + + xlinkSetProtocolInitialized(X_LINK_TCP_IP_OR_LOCAL_SHDMEM, 1); + return X_LINK_PLATFORM_SUCCESS; } @@ -174,21 +179,55 @@ xLinkPlatformErrorCode_t XLinkPlatformBootFirmware(const deviceDesc_t* deviceDes } -xLinkPlatformErrorCode_t XLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, XLinkProtocol_t protocol, void** fd) +xLinkPlatformErrorCode_t XLinkPlatformConnect(const char* devPathRead, const char* devPathWrite, XLinkProtocol_t *protocol, void** fd) { - if(!XLinkIsProtocolInitialized(protocol)) { - return X_LINK_PLATFORM_DRIVER_NOT_LOADED+protocol; + if(!XLinkIsProtocolInitialized(*protocol)) { + return X_LINK_PLATFORM_DRIVER_NOT_LOADED+*protocol; } - switch (protocol) { + + switch (*protocol) { case X_LINK_USB_VSC: case X_LINK_USB_CDC: - return usbPlatformConnect(devPathRead, devPathWrite, fd); + case X_LINK_USB_EP: + return usbPlatformConnect(*protocol, devPathRead, devPathWrite, fd); case X_LINK_PCIE: return pciePlatformConnect(devPathRead, devPathWrite, fd); case X_LINK_TCP_IP: return tcpipPlatformConnect(devPathRead, devPathWrite, fd); + + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: + return tcpipOrLocalShdmemPlatformConnect(protocol, devPathRead, devPathWrite, fd); + +#if defined(__unix__) + case X_LINK_LOCAL_SHDMEM: + return shdmemPlatformConnect(devPathRead, devPathWrite, fd); +#endif + + default: + return X_LINK_PLATFORM_INVALID_PARAMETERS; + } +} + +xLinkPlatformErrorCode_t XLinkPlatformServer(const char* devPathRead, const char* devPathWrite, XLinkProtocol_t *protocol, void** fd) +{ + switch (*protocol) { + case X_LINK_USB_VSC: + case X_LINK_USB_CDC: + case X_LINK_USB_EP: + return usbPlatformServer(devPathRead, devPathWrite, fd); + + case X_LINK_TCP_IP: + return tcpipPlatformServer(devPathRead, devPathWrite, fd, NULL); + + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: + return tcpipOrLocalShdmemPlatformServer(protocol, devPathRead, devPathWrite, fd); + +#if defined(__unix__) + case X_LINK_LOCAL_SHDMEM: + return shdmemPlatformServer(devPathRead, devPathWrite, fd, NULL); +#endif default: return X_LINK_PLATFORM_INVALID_PARAMETERS; @@ -230,13 +269,19 @@ xLinkPlatformErrorCode_t XLinkPlatformCloseRemote(xLinkDeviceHandle_t* deviceHan switch (deviceHandle->protocol) { case X_LINK_USB_VSC: case X_LINK_USB_CDC: - return usbPlatformClose(deviceHandle->xLinkFD); + case X_LINK_USB_EP: + return usbPlatformClose(deviceHandle->protocol, deviceHandle->xLinkFD); case X_LINK_PCIE: return pciePlatformClose(deviceHandle->xLinkFD); case X_LINK_TCP_IP: return tcpipPlatformClose(deviceHandle->xLinkFD); + +#if defined(__unix__) + case X_LINK_LOCAL_SHDMEM: + return shdmemPlatformClose(deviceHandle->xLinkFD); +#endif default: return X_LINK_PLATFORM_INVALID_PARAMETERS; @@ -292,82 +337,6 @@ int pciePlatformConnect(UNUSED const char *devPathRead, return pcie_init(devPathWrite, fd); } -// TODO add IPv6 to tcpipPlatformConnect() -int tcpipPlatformConnect(const char *devPathRead, const char *devPathWrite, void **fd) -{ -#if defined(USE_TCP_IP) - if (!devPathWrite || !fd) { - return X_LINK_PLATFORM_INVALID_PARAMETERS; - } - - TCPIP_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); - -#if (defined(_WIN32) || defined(_WIN64) ) - if(sock == INVALID_SOCKET) - { - return TCPIP_HOST_ERROR; - } -#else - if(sock < 0) - { - return TCPIP_HOST_ERROR; - } -#endif - - // Disable sigpipe reception on send - #if defined(SO_NOSIGPIPE) - const int set = 1; - setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(set)); - #endif - - struct sockaddr_in serv_addr = { 0 }; - - const size_t maxlen = 255; - size_t len = strnlen(devPathWrite, maxlen + 1); - if (len == 0 || len >= maxlen + 1) - return X_LINK_PLATFORM_INVALID_PARAMETERS; - char *const serv_ip = (char *)malloc(len + 1); - if (!serv_ip) - return X_LINK_PLATFORM_ERROR; - serv_ip[0] = 0; - // Parse port if specified, or use default - int port = TCPIP_LINK_SOCKET_PORT; - sscanf(devPathWrite, "%[^:]:%d", serv_ip, &port); - - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(port); - - int ret = inet_pton(AF_INET, serv_ip, &serv_addr.sin_addr); - free(serv_ip); - - if(ret <= 0) - { - tcpip_close_socket(sock); - return -1; - } - - int on = 1; - if(setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) - { - perror("setsockopt TCP_NODELAY"); - tcpip_close_socket(sock); - return -1; - } - - if(connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) - { - tcpip_close_socket(sock); - return -1; - } - - // Store the socket and create a "unique" key instead - // (as file descriptors are reused and can cause a clash with lookups between scheduler and link) - *fd = createPlatformDeviceFdKey((void*) (uintptr_t) sock); - -#endif - return 0; -} - xLinkPlatformErrorCode_t usbPlatformBootBootloader(const char *name) { @@ -380,10 +349,6 @@ int pciePlatformBootBootloader(const char *name) return -1; } -xLinkPlatformErrorCode_t tcpipPlatformBootBootloader(const char *name) -{ - return tcpip_boot_bootloader(name); -} static char* pciePlatformStateToStr(const pciePlatformState_t platformState) { @@ -417,43 +382,6 @@ int pciePlatformClose(void *f) return rc; } -int tcpipPlatformClose(void *fdKey) -{ -#if defined(USE_TCP_IP) - - int status = 0; - - void* tmpsockfd = NULL; - if(getPlatformDeviceFdFromKey(fdKey, &tmpsockfd)){ - mvLog(MVLOG_FATAL, "Cannot find file descriptor by key"); - return -1; - } - TCPIP_SOCKET sock = (TCPIP_SOCKET) (uintptr_t) tmpsockfd; - -#ifdef _WIN32 - status = shutdown(sock, SD_BOTH); - if (status == 0) { status = closesocket(sock); } -#else - if(sock != -1) - { - status = shutdown(sock, SHUT_RDWR); - if (status == 0) { status = close(sock); } - } -#endif - - if(destroyPlatformDeviceFdKey(fdKey)){ - mvLog(MVLOG_FATAL, "Cannot destroy file descriptor key"); - return -1; - } - - return status; - -#endif - return -1; -} - - - int pciePlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length){ // Temporary open fd to boot device and then close it int* pcieFd = NULL; @@ -470,11 +398,6 @@ int pciePlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmwar return rc; } -int tcpipPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length){ - // TCPIP doesn't support a boot mechanism - return -1; -} - // ------------------------------------ // Wrappers implementation. End. // ------------------------------------ diff --git a/src/pc/PlatformDeviceSearch.c b/src/pc/PlatformDeviceSearch.c index 99e11791..f029ef94 100644 --- a/src/pc/PlatformDeviceSearch.c +++ b/src/pc/PlatformDeviceSearch.c @@ -9,6 +9,7 @@ #include "usb_host.h" #include "pcie_host.h" #include "tcpip_host.h" +#include "local_memshd.h" #include "XLinkStringUtils.h" @@ -31,8 +32,14 @@ static xLinkPlatformErrorCode_t getPCIeDeviceName(int index, deviceDesc_t* out_foundDevice); static xLinkPlatformErrorCode_t getTcpIpDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, int sizeFoundDevices, - unsigned int *out_amountOfFoundDevices); + unsigned int *out_amountOfFoundDevices, + int timeoutMs); +#if defined(__unix__) +static xLinkPlatformErrorCode_t getLocalShdmemDevices(const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevices, int sizeFoundDevices, + unsigned int *out_amountOfFoundDevices); +#endif // ------------------------------------ // Helpers declaration. End. @@ -45,17 +52,19 @@ static xLinkPlatformErrorCode_t getTcpIpDevices(const deviceDesc_t in_deviceRequ xLinkPlatformErrorCode_t XLinkPlatformFindDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, unsigned sizeFoundDevices, - unsigned int *out_amountOfFoundDevices) { + unsigned int *out_amountOfFoundDevices, int timeoutMs) { memset(out_foundDevices, 0, sizeFoundDevices * sizeof(deviceDesc_t)); xLinkPlatformErrorCode_t USB_rc; xLinkPlatformErrorCode_t PCIe_rc; xLinkPlatformErrorCode_t TCPIP_rc; + xLinkPlatformErrorCode_t SHDMEM_rc; unsigned numFoundDevices = 0; *out_amountOfFoundDevices = 0; switch (in_deviceRequirements.protocol){ case X_LINK_USB_CDC: case X_LINK_USB_VSC: + case X_LINK_USB_EP: if(!XLinkIsProtocolInitialized(in_deviceRequirements.protocol)) { return X_LINK_PLATFORM_DRIVER_NOT_LOADED+in_deviceRequirements.protocol; } @@ -66,15 +75,21 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevices(const deviceDesc_t in_deviceRe case X_LINK_PCIE: return getPCIeDeviceName(0, state, in_deviceRequirements, out_foundDevice); */ - case X_LINK_TCP_IP: if(!XLinkIsProtocolInitialized(in_deviceRequirements.protocol)) { return X_LINK_PLATFORM_DRIVER_NOT_LOADED+in_deviceRequirements.protocol; } - return getTcpIpDevices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, out_amountOfFoundDevices); + return getTcpIpDevices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, out_amountOfFoundDevices, timeoutMs); - case X_LINK_ANY_PROTOCOL: +#if defined(__unix__) + case X_LINK_LOCAL_SHDMEM: + if(!XLinkIsProtocolInitialized(in_deviceRequirements.protocol)) { + return X_LINK_PLATFORM_DRIVER_NOT_LOADED+in_deviceRequirements.protocol; + } + return getLocalShdmemDevices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, out_amountOfFoundDevices); +#endif + case X_LINK_ANY_PROTOCOL: // If USB protocol is initialized if(XLinkIsProtocolInitialized(X_LINK_USB_VSC)) { // Find first correct USB Device @@ -91,7 +106,9 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevices(const deviceDesc_t in_deviceRe } - /* TODO(themarpe) - reenable PCIe + // TODO(themarpe) - reenable PCIe + (void) PCIe_rc; + /* if(XLinkIsProtocolInitialized(X_LINK_PCIE)) { numFoundDevices = 0; PCIe_rc = getPCIeDeviceName(0, state, in_deviceRequirements, out_foundDevice); @@ -106,10 +123,26 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevices(const deviceDesc_t in_deviceRe } */ + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: +#if defined(__unix__) + if(XLinkIsProtocolInitialized(X_LINK_LOCAL_SHDMEM)) { + numFoundDevices = 0; + SHDMEM_rc = getLocalShdmemDevices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, &numFoundDevices); + *out_amountOfFoundDevices += numFoundDevices; + out_foundDevices += numFoundDevices; + // Found enough devices, return + if (numFoundDevices >= sizeFoundDevices) { + return X_LINK_PLATFORM_SUCCESS; + } else { + sizeFoundDevices -= numFoundDevices; + } + } +#endif + // Try find TCPIP device if(XLinkIsProtocolInitialized(X_LINK_TCP_IP)) { numFoundDevices = 0; - TCPIP_rc = getTcpIpDevices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, &numFoundDevices); + TCPIP_rc = getTcpIpDevices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, &numFoundDevices, timeoutMs); *out_amountOfFoundDevices += numFoundDevices; out_foundDevices += numFoundDevices; // Found enough devices, return @@ -147,30 +180,14 @@ char* XLinkPlatformErrorToStr(const xLinkPlatformErrorCode_t errorCode) { case X_LINK_PLATFORM_TIMEOUT: return "X_LINK_PLATFORM_TIMEOUT"; case X_LINK_PLATFORM_USB_DRIVER_NOT_LOADED: return "X_LINK_PLATFORM_USB_DRIVER_NOT_LOADED"; case X_LINK_PLATFORM_TCP_IP_DRIVER_NOT_LOADED: return "X_LINK_PLATFORM_TCP_IP_DRIVER_NOT_LOADED"; + case X_LINK_PLATFORM_LOCAL_SHDMEM_DRIVER_NOT_LOADED: return "X_LINK_PLATFORM_LOCAL_SHDMEM_DRIVER_NOT_LOADED"; + case X_LINK_PLATFORM_TCP_IP_OR_LOCAL_SHDMEM_DRIVER_NOT_LOADED: return "X_LINK_PLATFORM_LOCAL_SHDMEM_DRIVER_NOT_LOADED"; case X_LINK_PLATFORM_PCIE_DRIVER_NOT_LOADED: return "X_LINK_PLATFORM_PCIE_DRIVER_NOT_LOADED"; case X_LINK_PLATFORM_INVALID_PARAMETERS: return "X_LINK_PLATFORM_INVALID_PARAMETERS"; default: return ""; } } -XLinkPlatform_t XLinkPlatformPidToPlatform(const int pid) { - switch (pid) { - case DEFAULT_UNBOOTPID_2150: return X_LINK_MYRIAD_2; - case DEFAULT_UNBOOTPID_2485: return X_LINK_MYRIAD_X; - default: return X_LINK_ANY_PLATFORM; - } -} - -XLinkDeviceState_t XLinkPlatformPidToState(const int pid) { - switch (pid) { - case DEFAULT_OPENPID: return X_LINK_BOOTED; - case DEFAULT_BOOTLOADER_PID: return X_LINK_BOOTLOADER; - case DEFAULT_FLASH_BOOTED_PID: return X_LINK_FLASH_BOOTED; - case AUTO_PID: return X_LINK_ANY_STATE; - default: return X_LINK_UNBOOTED; - } -} - // ------------------------------------ // XLinkPlatform API implementation. End. // ------------------------------------ @@ -286,6 +303,32 @@ xLinkPlatformErrorCode_t getPCIeDeviceName(int index, } xLinkPlatformErrorCode_t getTcpIpDevices(const deviceDesc_t in_deviceRequirements, + deviceDesc_t* out_foundDevices, int sizeFoundDevices, + unsigned int *out_amountOfFoundDevices, int timeoutMs) +{ + ASSERT_XLINK_PLATFORM(out_foundDevices); + ASSERT_XLINK_PLATFORM(out_amountOfFoundDevices); + if (in_deviceRequirements.platform == X_LINK_MYRIAD_2) { + /** + * No case with TCP IP devices on TCP_IP protocol + */ + return X_LINK_PLATFORM_ERROR; + } + + if(in_deviceRequirements.state == X_LINK_UNBOOTED) { + /** + * There is no condition where unbooted + * state device to be found using tcp/ip. + */ + return X_LINK_PLATFORM_DEVICE_NOT_FOUND; + } + + return tcpip_get_devices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, out_amountOfFoundDevices, timeoutMs); +} + + +#if defined(__unix__) +xLinkPlatformErrorCode_t getLocalShdmemDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, int sizeFoundDevices, unsigned int *out_amountOfFoundDevices) { @@ -306,8 +349,9 @@ xLinkPlatformErrorCode_t getTcpIpDevices(const deviceDesc_t in_deviceRequirement return X_LINK_PLATFORM_DEVICE_NOT_FOUND; } - return tcpip_get_devices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, out_amountOfFoundDevices); + return shdmemGetDevices(in_deviceRequirements, out_foundDevices, sizeFoundDevices, out_amountOfFoundDevices); } +#endif // ------------------------------------ // Helpers implementation. End. diff --git a/src/pc/PlatformDeviceSearchDynamic.cpp b/src/pc/PlatformDeviceSearchDynamic.cpp index fe4ac7b6..f3b10704 100644 --- a/src/pc/PlatformDeviceSearchDynamic.cpp +++ b/src/pc/PlatformDeviceSearchDynamic.cpp @@ -9,6 +9,7 @@ #include "usb_host.h" #include "pcie_host.h" #include "tcpip_host.h" +#include "local_memshd.h" #include "XLinkStringUtils.h" #include #include @@ -39,6 +40,7 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevicesDynamic(const deviceDesc_t in_d void* tcpip_ctx; bool usb_vsc_available = false; bool tcpip_available = false; + bool shdmem_available = false; if(XLinkIsProtocolInitialized(X_LINK_USB_VSC)) { usb_vsc_available = true; @@ -47,6 +49,10 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevicesDynamic(const deviceDesc_t in_d tcpip_available = true; } + if(XLinkIsProtocolInitialized(X_LINK_LOCAL_SHDMEM)) { + shdmem_available = true; + } + xLinkPlatformErrorCode_t status = X_LINK_PLATFORM_TIMEOUT; do { @@ -89,8 +95,6 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevicesDynamic(const deviceDesc_t in_d } } - - /* TODO(themarpe) - reenable PCIe if(XLinkIsProtocolInitialized(X_LINK_PCIE)) { numFoundDevices = 0; @@ -106,6 +110,7 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevicesDynamic(const deviceDesc_t in_d } */ + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: // Try find TCPIP device if(tcpip_available) { numFoundDevices = 0; @@ -133,7 +138,7 @@ xLinkPlatformErrorCode_t XLinkPlatformFindDevicesDynamic(const deviceDesc_t in_d { deviceDesc_t* devices = out_foundDevices; int write_index = 0; - for(int i = 0; i < *out_amountOfFoundDevices; i++){ + for(int i = 0; i < static_cast(*out_amountOfFoundDevices); i++){ bool duplicate = false; for(int j = i - 1; j >= 0; j--){ // Check if duplicate diff --git a/src/pc/ProtocolManager.cpp b/src/pc/ProtocolManager.cpp index 1bd4ec93..7e68f987 100644 --- a/src/pc/ProtocolManager.cpp +++ b/src/pc/ProtocolManager.cpp @@ -9,7 +9,7 @@ extern "C" void xlinkSetProtocolInitialized(const XLinkProtocol_t protocol, int } } -int XLinkIsProtocolInitialized(const XLinkProtocol_t protocol) { +extern "C" int XLinkIsProtocolInitialized(const XLinkProtocol_t protocol) { if(protocol >= 0 && protocol < X_LINK_NMB_OF_PROTOCOLS) { return protocolInitialized[protocol]; } diff --git a/src/pc/protocols/local_memshd.cpp b/src/pc/protocols/local_memshd.cpp new file mode 100644 index 00000000..74811f9f --- /dev/null +++ b/src/pc/protocols/local_memshd.cpp @@ -0,0 +1,250 @@ +/** + * @file local_memshd.c + * @brief Shared memory helper definitions +*/ + +#include "local_memshd.h" +#include "../PlatformDeviceFd.h" + +#define MVLOG_UNIT_NAME local_memshd +#include "XLinkLog.h" + +#if defined(__unix__) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int shdmem_initialize() { + mvLog(MVLOG_DEBUG, "Shared memory initialized\n"); + return X_LINK_SUCCESS; +} + +int shdmemPlatformConnect(const char *devPathRead, const char *devPathWrite, void **desc) { + const char *socketPath = devPathWrite; + + mvLog(MVLOG_DEBUG, "Shared memory connect invoked with socket path %s\n", socketPath); + + int socketFd = socket(AF_UNIX, SOCK_STREAM, 0); + if (socketFd < 0) { + mvLog(MVLOG_FATAL, "Socket creation failed"); + return X_LINK_ERROR; + } + + struct sockaddr_un sockAddr; + memset(&sockAddr, 0, sizeof(sockAddr)); + sockAddr.sun_family = AF_UNIX; + strcpy(sockAddr.sun_path, socketPath); + + if (connect(socketFd, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) < 0) { + mvLog(MVLOG_FATAL, "Socket connection failed"); + return X_LINK_ERROR; + } + + // Store the socket and create a "unique" key instead + // (as file descriptors are reused and can cause a clash with lookups between scheduler and link) + *desc = createPlatformDeviceFdKey((void*) (uintptr_t) socketFd); + + return X_LINK_SUCCESS; +} + +int shdmemPlatformServer(const char *devPathRead, const char *devPathWrite, void **desc, long *sockFd) { + const char *socketPath = devPathWrite; + mvLog(MVLOG_DEBUG, "Shared memory server invoked with socket path %s\n", socketPath); + + int socketFd = socket(AF_UNIX, SOCK_STREAM, 0); + if (socketFd < 0) { + mvLog(MVLOG_FATAL, "Socket creation failed"); + return X_LINK_ERROR; + } + + if (sockFd != nullptr) *sockFd = socketFd; + + struct sockaddr_un addrUn; + memset(&addrUn, 0, sizeof(addrUn)); + addrUn.sun_family = AF_UNIX; + strcpy(addrUn.sun_path, socketPath); + unlink(socketPath); + + if (bind(socketFd, (struct sockaddr *)&addrUn, sizeof(addrUn)) < 0) { + mvLog(MVLOG_FATAL, "Socket bind failed"); + return X_LINK_ERROR; + } + + listen(socketFd, 1); + mvLog(MVLOG_DEBUG, "Waiting for a connection...\n"); + int clientFd = accept(socketFd, NULL, NULL); + close(socketFd); + if (sockFd != nullptr) *sockFd = -1; + if (clientFd < 0) { + mvLog(MVLOG_FATAL, "Socket accept failed"); + return X_LINK_ERROR; + } + + + // Store the socket and create a "unique" key instead + // (as file descriptors are reused and can cause a clash with lookups between scheduler and link) + *desc = createPlatformDeviceFdKey((void*) (uintptr_t) clientFd); + + printf("XLink SHDMEM connected\n"); + return X_LINK_SUCCESS; + +} + +int shdmemPlatformClose(void **desc) { + long socketFd = 0; + if(getPlatformDeviceFdFromKey(desc, (void**)&socketFd)) { + mvLog(MVLOG_DEBUG, "Failed\n"); + return X_LINK_ERROR; + } + + close(socketFd); + + return X_LINK_SUCCESS; +} + +int shdmemPlatformRead(void *desc, void *data, int size, long *fd) { + long socketFd = 0; + if(getPlatformDeviceFdFromKey(desc, (void**)&socketFd)) { + mvLog(MVLOG_DEBUG, "Failed\n"); + return X_LINK_ERROR; + } + + struct msghdr msg = {}; + struct iovec iov; + if (data != NULL && size > 0) { + iov.iov_base = data; + iov.iov_len = size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + } + + char ancillaryElementBuffer[CMSG_SPACE(sizeof(long))]; + msg.msg_control = ancillaryElementBuffer; + msg.msg_controllen = sizeof(ancillaryElementBuffer); + + int bytes; + if(bytes = recvmsg(socketFd, &msg, MSG_WAITALL) < 0) { + mvLog(MVLOG_ERROR, "Failed to recieve message: %s", strerror(errno)); + return X_LINK_ERROR; + } + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + long recvFd = *((long*)CMSG_DATA(cmsg)); + mvLog(MVLOG_DEBUG, "We received ad FD: %d\n", recvFd); + + + /* We have recieved a FD */ + *fd = recvFd; + } + + return 0; +} + +int shdmemPlatformWrite(void *desc, void *data, int size) { + long socketFd = 0; + if(getPlatformDeviceFdFromKey(desc, (void**)&socketFd)) { + mvLog(MVLOG_ERROR, "Failed to get the socket FD\n"); + return X_LINK_ERROR; + } + + struct msghdr msg = {}; + struct iovec iov; + iov.iov_base = data; + iov.iov_len = size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + int bytes; + if(bytes = sendmsg(socketFd, &msg, 0) < 0) { + mvLog(MVLOG_ERROR, "Failed to send message: %s\n", strerror(errno)); + return X_LINK_ERROR; + } + + return 0; +} + +int shdmemPlatformWriteFd(void *desc, const long fd, void *data2, int size2) { + long socketFd = 0; + if(getPlatformDeviceFdFromKey(desc, (void**)&socketFd)) { + mvLog(MVLOG_ERROR, "Failed to get the socket FD\n"); + return X_LINK_ERROR; + } + + struct msghdr msg = {}; + struct iovec iov; + long buf[1] = {0}; // Buffer for single byte of data to send + if (data2 != NULL && size2 > 0) { + iov.iov_base = data2; + iov.iov_len = size2; + } else if (fd >= 0) { + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + } else { + return 0; + } + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char ancillaryElementBuffer[CMSG_SPACE(sizeof(long))]; + if (fd >= 0) { + msg.msg_control = ancillaryElementBuffer; + msg.msg_controllen = sizeof(ancillaryElementBuffer); + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(long)); + + *((long*)CMSG_DATA(cmsg)) = fd; + } + + int bytes; + if(bytes = sendmsg(socketFd, &msg, 0) < 0) { + mvLog(MVLOG_ERROR, "Failed to send message: %s", strerror(errno)); + return X_LINK_ERROR; + } + + return 0; +} + +int shdmemSetProtocol(XLinkProtocol_t *protocol, const char* devPathRead, const char* devPathWrite) { + devPathWrite = devPathRead = SHDMEM_DEFAULT_SOCKET; + *protocol = X_LINK_LOCAL_SHDMEM; + return X_LINK_SUCCESS; +} + + +xLinkPlatformErrorCode_t shdmemGetDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, int sizeFoundDevices, unsigned int *out_amountOfFoundDevices) { + if (access(SHDMEM_DEFAULT_SOCKET, F_OK) != 0) { + return X_LINK_PLATFORM_ERROR; + } + + // Status + out_foundDevices[0].status = X_LINK_SUCCESS; + // IP + memset(out_foundDevices[0].name, 0, sizeof(out_foundDevices[0].name)); + strncpy(out_foundDevices[0].name, SHDMEM_DEFAULT_SOCKET, sizeof(out_foundDevices[0].name)); + // MXID + memset(out_foundDevices[0].mxid, 0, sizeof(out_foundDevices[0].mxid)); + strncpy(out_foundDevices[0].mxid, in_deviceRequirements.mxid, sizeof(out_foundDevices[0].mxid)); + // Platform + out_foundDevices[0].platform = X_LINK_MYRIAD_X; + // Protocol + out_foundDevices[0].protocol = X_LINK_LOCAL_SHDMEM; + // State + out_foundDevices[0].state = X_LINK_BOOTED; + + *out_amountOfFoundDevices = 1; + + return X_LINK_PLATFORM_SUCCESS; +} + +#endif /* !defined(__unix__) */ diff --git a/src/pc/protocols/local_memshd.h b/src/pc/protocols/local_memshd.h new file mode 100644 index 00000000..866c6094 --- /dev/null +++ b/src/pc/protocols/local_memshd.h @@ -0,0 +1,46 @@ +/** + * @file local_memshd.h + * @brief Shared memory helper public header +*/ + +#ifndef LOCAL_MEMSHD_H +#define LOCAL_MEMSHD_H + +/* **************************************************************************/ +/* Include Files */ +/* **************************************************************************/ +#include "XLinkPlatform.h" +#include "XLinkPublicDefines.h" + + +#if defined(__unix__) + +#define SHDMEM_DEFAULT_SOCKET "/tmp/xlink.sock" + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @brief Initializes Shared Memory protocol +*/ +int shdmem_initialize(); + +int shdmemPlatformConnect(const char *devPathRead, const char *devPathWrite, void **desc); +int shdmemPlatformServer(const char *devPathRead, const char *devPathWrite, void **desc, long *sockFd); +int shdmemPlatformClose(void **desc); + +int shdmemPlatformRead(void *desc, void *data, int size, long *fd); +int shdmemPlatformWrite(void *desc, void *data, int size); +int shdmemPlatformWriteFd(void *desc, const long fd, void *data2, int size2); + +int shdmemSetProtocol(XLinkProtocol_t *protocol, const char* devPathRead, const char* devPathWrite); + +xLinkPlatformErrorCode_t shdmemGetDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, int sizeFoundDevices, unsigned int *out_amountOfFoundDevices); + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__unix__) */ + +#endif /* LOCAL_MEMSHD_H */ diff --git a/src/pc/protocols/tcpip_host.cpp b/src/pc/protocols/tcpip_host.cpp index ab2f8d8a..4e6baa12 100644 --- a/src/pc/protocols/tcpip_host.cpp +++ b/src/pc/protocols/tcpip_host.cpp @@ -3,17 +3,25 @@ * @brief TCP/IP helper definitions */ -/* **************************************************************************/ -/* Include Files */ -/* **************************************************************************/ -#include -#include -#include -#include - #include "tcpip_host.h" +#include "../PlatformDeviceFd.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MVLOG_UNIT_NAME tcpip_host +#include "XLinkLog.h" +// Windows specifics #if (defined(_WIN32) || defined(_WIN64)) #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0601 /* Windows 7. */ @@ -23,41 +31,117 @@ #include #pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "iphlpapi.lib") +#define tcpip_errno WSAGetLastError() +#define ERRNO_EAGAIN WSAETIMEDOUT + #else + +// *Unix specifics #include #include #include #include #include -#include #include #include -#include -#include #include -#include #include +#include +using TCPIP_SOCKET = int; +#define ERRNO_EAGAIN EAGAIN +#define tcpip_errno errno + #endif -#include +/* Host-to-device command list */ +typedef enum +{ + TCPIP_HOST_CMD_NO_COMMAND = 0, + TCPIP_HOST_CMD_DEVICE_DISCOVER = 1, + TCPIP_HOST_CMD_DEVICE_INFO = 2, + TCPIP_HOST_CMD_RESET = 3, + TCPIP_HOST_CMD_DEVICE_DISCOVERY_EX = 4, +} tcpipHostCommand_t; + +/* Device state */ +typedef enum +{ + TCPIP_HOST_STATE_INVALID = 0, + TCPIP_HOST_STATE_BOOTED = 1, + TCPIP_HOST_STATE_UNBOOTED = 2, + TCPIP_HOST_STATE_BOOTLOADER = 3, + TCPIP_HOST_STATE_FLASH_BOOTED = 4, + TCPIP_HOST_STATE_BOOTED_NON_EXCLUSIVE = TCPIP_HOST_STATE_FLASH_BOOTED, + TCPIP_HOST_STATE_GATE = 5, + TCPIP_HOST_STATE_GATE_BOOTED = 6, + TCPIP_HOST_STATE_GATE_SETUP = 7, +} tcpipHostDeviceState_t; + +/* Device protocol */ +typedef enum +{ + TCPIP_HOST_PROTOCOL_USB_VSC = 0, + TCPIP_HOST_PROTOCOL_USB_CDC = 1, + TCPIP_HOST_PROTOCOL_PCIE = 2, + TCPIP_HOST_PROTOCOL_IPC = 3, + TCPIP_HOST_PROTOCOL_TCP_IP = 4, +} tcpipHostDeviceProtocol_t; + +/* Device platform */ +typedef enum +{ + TCPIP_HOST_PLATFORM_INVALID = 0, + TCPIP_HOST_PLATFORM_MYRIAD_X = 2, + TCPIP_HOST_PLATFORM_RVC3 = 3, + TCPIP_HOST_PLATFORM_RVC4 = 4, +} tcpipHostDevicePlatform_t; +/* Device response payload */ +typedef struct +{ + tcpipHostCommand_t command; + char mxid[32]; + uint32_t state; +} tcpipHostDeviceDiscoveryResp_t; + +typedef struct +{ + tcpipHostCommand_t command; + char mxid[32]; + int32_t linkSpeed; + int32_t linkFullDuplex; + int32_t gpioBootMode; +} tcpipHostDeviceInformationResp_t; + +typedef struct +{ + tcpipHostCommand_t command; + char id[32]; + uint32_t state; + uint32_t protocol; + uint32_t platform; + uint16_t portHttp; + uint16_t portHttps; +} tcpipHostDeviceDiscoveryExResp_t; + +typedef struct +{ + tcpipHostCommand_t command; +} tcpipHostDeviceDiscoveryReq_t; + /* **************************************************************************/ /* Private Macro Definitions */ /* **************************************************************************/ // Debug, uncomment first line for some printing -//#define HAS_DEBUG +// #define HAS_DEBUG #undef HAS_DEBUG -#define BROADCAST_UDP_PORT 11491 +static constexpr const auto DEFAULT_DEVICE_DISCOVERY_PORT = 11491; +static constexpr const auto DEFAULT_DEVICE_DISCOVERY_POOL_TIMEOUT = std::chrono::milliseconds{500}; -#define MAX_IFACE_CHAR 64 -#define MAX_DEVICE_DISCOVERY_IFACE 10 - -#define MSEC_TO_USEC(x) (x * 1000) -#define DEVICE_RES_TIMEOUT_MSEC 20 - -static constexpr auto DEVICE_DISCOVERY_RES_TIMEOUT = std::chrono::milliseconds{500}; +constexpr int MSEC_TO_USEC(int x) { return x * 1000; } +static constexpr auto DEVICE_DISCOVERY_SOCKET_TIMEOUT = std::chrono::milliseconds{20}; #ifdef HAS_DEBUG #define DEBUG(...) do { printf(__VA_ARGS__); } while(0) @@ -66,19 +150,48 @@ static constexpr auto DEVICE_DISCOVERY_RES_TIMEOUT = std::chrono::milliseconds{5 #endif +static tcpipHostDeviceState_t tcpip_convert_device_state(XLinkDeviceState_t state) { + switch (state) { + case XLinkDeviceState_t::X_LINK_BOOTED: return TCPIP_HOST_STATE_BOOTED; + case XLinkDeviceState_t::X_LINK_UNBOOTED: return TCPIP_HOST_STATE_UNBOOTED; + case XLinkDeviceState_t::X_LINK_BOOTLOADER: return TCPIP_HOST_STATE_BOOTLOADER; + case XLinkDeviceState_t::X_LINK_BOOTED_NON_EXCLUSIVE: return TCPIP_HOST_STATE_BOOTED_NON_EXCLUSIVE; + case XLinkDeviceState_t::X_LINK_GATE: return TCPIP_HOST_STATE_GATE; + case XLinkDeviceState_t::X_LINK_GATE_BOOTED: return TCPIP_HOST_STATE_GATE_BOOTED; + case XLinkDeviceState_t::X_LINK_ANY_STATE: return TCPIP_HOST_STATE_INVALID; + } + return TCPIP_HOST_STATE_INVALID; +} + static XLinkDeviceState_t tcpip_convert_device_state(uint32_t state) { if(state == TCPIP_HOST_STATE_BOOTED) { return X_LINK_BOOTED; } + else if(state == TCPIP_HOST_STATE_UNBOOTED) + { + return X_LINK_UNBOOTED; + } else if(state == TCPIP_HOST_STATE_BOOTLOADER) { return X_LINK_BOOTLOADER; } - else if(state == TCPIP_HOST_STATE_FLASH_BOOTED) + else if(state == TCPIP_HOST_STATE_BOOTED_NON_EXCLUSIVE) + { + return X_LINK_BOOTED_NON_EXCLUSIVE; + } + else if(state == TCPIP_HOST_STATE_GATE) + { + return X_LINK_GATE; + } + else if(state == TCPIP_HOST_STATE_GATE_BOOTED) + { + return X_LINK_GATE_BOOTED; + } + else if(state == TCPIP_HOST_STATE_GATE_SETUP) { - return X_LINK_FLASH_BOOTED; + return X_LINK_GATE_SETUP; } else { @@ -86,25 +199,6 @@ static XLinkDeviceState_t tcpip_convert_device_state(uint32_t state) } } -/* Device protocol */ -typedef enum -{ - TCPIP_HOST_PROTOCOL_USB_VSC = 0, - TCPIP_HOST_PROTOCOL_USB_CDC = 1, - TCPIP_HOST_PROTOCOL_PCIE = 2, - TCPIP_HOST_PROTOCOL_IPC = 3, - TCPIP_HOST_PROTOCOL_TCP_IP = 4, -} tcpipHostDeviceProtocol_t; - -/* Device platform */ -typedef enum -{ - TCPIP_HOST_PLATFORM_INVALID = 0, - TCPIP_HOST_PLATFORM_MYRIAD_X = 2, - TCPIP_HOST_PLATFORM_RVC3 = 3, - TCPIP_HOST_PLATFORM_RVC4 = 4, -} tcpipHostDevicePlatform_t; - static XLinkProtocol_t tcpip_convert_device_protocol(uint32_t protocol) { switch (protocol) @@ -125,8 +219,8 @@ static XLinkPlatform_t tcpip_convert_device_platform(uint32_t platform) switch (platform) { case TCPIP_HOST_PLATFORM_MYRIAD_X: return X_LINK_MYRIAD_X; - // case TCPIP_HOST_PLATFORM_RVC3: return X_LINK_RVC3; - // case TCPIP_HOST_PLATFORM_RVC4: return X_LINK_RVC4; + case TCPIP_HOST_PLATFORM_RVC3: return X_LINK_RVC3; + case TCPIP_HOST_PLATFORM_RVC4: return X_LINK_RVC4; default: return X_LINK_ANY_PLATFORM; break; @@ -136,14 +230,46 @@ static XLinkPlatform_t tcpip_convert_device_platform(uint32_t platform) static tcpipHostDevicePlatform_t tcpip_convert_device_platform(XLinkPlatform_t platform) { switch (platform) { case X_LINK_MYRIAD_X: return TCPIP_HOST_PLATFORM_MYRIAD_X; - // case X_LINK_RVC3: return TCPIP_HOST_PLATFORM_RVC3; - // case X_LINK_RVC4: return TCPIP_HOST_PLATFORM_RVC4; + case X_LINK_RVC3: return TCPIP_HOST_PLATFORM_RVC3; + case X_LINK_RVC4: return TCPIP_HOST_PLATFORM_RVC4; case X_LINK_ANY_PLATFORM: return TCPIP_HOST_PLATFORM_INVALID; case X_LINK_MYRIAD_2: return TCPIP_HOST_PLATFORM_INVALID; } return TCPIP_HOST_PLATFORM_INVALID; } +static int tcpip_setsockopt(int __fd, int __level, int __optname, const void *__optval, socklen_t __optlen) { +#if (defined(_WIN32) || defined(_WIN64) ) + return setsockopt(__fd, __level, __optname, (const char*) __optval, __optlen); +#else + return setsockopt(__fd, __level, __optname, __optval, __optlen); +#endif +} + +// macOS bind_to_if helper +static int tcpip_bind_to_if(TCPIP_SOCKET sock, const char* ifname) { +#if defined(__APPLE__) + // macOS: bind to interface by index + unsigned int ifindex = if_nametoindex(ifname); + if (ifindex == 0) return -1; + if (setsockopt(sock, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)) < 0) { + return -1; + } + return 0; +#else + return -1; +#endif +} + +// macOS unbind_to_if helper +static void tcpip_unbind_if(TCPIP_SOCKET sock) { +#if defined(__APPLE__) + // Clear binding: index 0 means "any" + unsigned int ifindex = 0; + setsockopt(sock, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)); +#endif +} + static tcpipHostError_t tcpip_create_socket(TCPIP_SOCKET* out_sock, bool broadcast, int timeout_ms) { TCPIP_SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -179,25 +305,27 @@ static tcpipHostError_t tcpip_create_socket(TCPIP_SOCKET* out_sock, bool broadca #endif // Specify timeout + if(timeout_ms != 0) { #if (defined(_WIN32) || defined(_WIN64) ) - int read_timeout = timeout_ms; + int read_timeout = timeout_ms; #else - struct timeval read_timeout; - read_timeout.tv_sec = 0; - read_timeout.tv_usec = MSEC_TO_USEC(timeout_ms); + struct timeval read_timeout; + read_timeout.tv_sec = 0; + read_timeout.tv_usec = MSEC_TO_USEC(timeout_ms); #endif - if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &read_timeout, sizeof(read_timeout)) < 0) - { - return TCPIP_HOST_ERROR; + if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &read_timeout, sizeof(read_timeout)) < 0) + { + return TCPIP_HOST_ERROR; + } } *out_sock = sock; return TCPIP_HOST_SUCCESS; } -static tcpipHostError_t tcpip_create_socket_broadcast(TCPIP_SOCKET* out_sock) +static tcpipHostError_t tcpip_create_socket_broadcast(TCPIP_SOCKET* out_sock, std::chrono::milliseconds timeout = std::chrono::milliseconds(0)) { - return tcpip_create_socket(out_sock, true, DEVICE_RES_TIMEOUT_MSEC); + return tcpip_create_socket(out_sock, true, static_cast(timeout.count())); } @@ -227,7 +355,7 @@ static tcpipHostError_t tcpip_send_broadcast(TCPIP_SOCKET sock){ struct sockaddr_in broadcast = { 0 }; broadcast.sin_addr.s_addr = INADDR_BROADCAST; broadcast.sin_family = AF_INET; - broadcast.sin_port = htons(BROADCAST_UDP_PORT); + broadcast.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); for (DWORD i = 0; i < ipaddrtable->dwNumEntries; ++i) { // Get broadcast IP @@ -251,7 +379,7 @@ static tcpipHostError_t tcpip_send_broadcast(TCPIP_SOCKET sock){ struct sockaddr_in broadcast_addr = { 0 }; broadcast_addr.sin_addr.s_addr = INADDR_BROADCAST; broadcast_addr.sin_family = AF_INET; - broadcast_addr.sin_port = htons(BROADCAST_UDP_PORT); + broadcast_addr.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); tcpipHostCommand_t send_buffer = TCPIP_HOST_CMD_DEVICE_DISCOVER; sendto(sock, reinterpret_cast(&send_buffer), sizeof(send_buffer), 0, (struct sockaddr *) &broadcast_addr, sizeof(broadcast_addr)); @@ -297,15 +425,21 @@ static tcpipHostError_t tcpip_send_broadcast(TCPIP_SOCKET sock){ // send broadcast message struct sockaddr_in broadcast_addr; + memset(&broadcast_addr, 0, sizeof(broadcast_addr)); broadcast_addr.sin_family = family; broadcast_addr.sin_addr.s_addr = ip_broadcast.sin_addr.s_addr; - broadcast_addr.sin_port = htons(BROADCAST_UDP_PORT); + broadcast_addr.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); + // macOS - bind this socket to the specific interface + bool bound = (tcpip_bind_to_if(sock, ifa->ifa_name) == 0); tcpipHostCommand_t send_buffer = TCPIP_HOST_CMD_DEVICE_DISCOVER; if(sendto(sock, reinterpret_cast(&send_buffer), sizeof(send_buffer), 0, (struct sockaddr *) &broadcast_addr, sizeof(broadcast_addr)) < 0) { // Ignore if not successful. The devices on that interface won't be found } + if (bound) { + tcpip_unbind_if(sock); // remove binding so we can send on the next interface + } } else { DEBUG("Not up and running."); } @@ -322,7 +456,7 @@ static tcpipHostError_t tcpip_send_broadcast(TCPIP_SOCKET sock){ struct sockaddr_in broadcast_addr = { 0 }; broadcast_addr.sin_addr.s_addr = INADDR_BROADCAST; broadcast_addr.sin_family = AF_INET; - broadcast_addr.sin_port = htons(BROADCAST_UDP_PORT); + broadcast_addr.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); tcpipHostCommand_t send_buffer = TCPIP_HOST_CMD_DEVICE_DISCOVER; sendto(sock, reinterpret_cast(&send_buffer), sizeof(send_buffer), 0, (struct sockaddr *) &broadcast_addr, sizeof(broadcast_addr)); @@ -336,6 +470,20 @@ static tcpipHostError_t tcpip_send_broadcast(TCPIP_SOCKET sock){ /* **************************************************************************/ /* Public Function Definitions */ /* **************************************************************************/ + +tcpipHostError_t tcpip_initialize() { +#if (defined(_WIN32) || defined(_WIN64)) + WSADATA wsa_data; + int ret = WSAStartup(MAKEWORD(2,2), &wsa_data); + if(ret != 0) { + mvLog(MVLOG_FATAL, "Couldn't initialize Winsock DLL using WSAStartup function. (Return value: %d)", ret); + return TCPIP_HOST_ERROR; + } +#endif + + return TCPIP_HOST_SUCCESS; +} + tcpipHostError_t tcpip_close_socket(TCPIP_SOCKET sock) { #if (defined(_WIN32) || defined(_WIN64) ) @@ -405,8 +553,9 @@ xLinkPlatformErrorCode_t tcpip_perform_search(void* ctx, deviceDesc_t* devices, // TODO(themarpe) - Add IPv6 capabilities // send unicast device discovery struct sockaddr_in device_address; + memset(&device_address, 0, sizeof(device_address)); device_address.sin_family = AF_INET; - device_address.sin_port = htons(BROADCAST_UDP_PORT); + device_address.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); // Convert address to binary #if (defined(_WIN32) || defined(__USE_W32_SOCKETS)) && (_WIN32_WINNT <= 0x0501) @@ -498,7 +647,7 @@ xLinkPlatformErrorCode_t tcpip_perform_search(void* ctx, deviceDesc_t* devices, num_devices_match++; } } - } while(std::chrono::steady_clock::now() - t1 < DEVICE_DISCOVERY_RES_TIMEOUT); + } while(std::chrono::steady_clock::now() - t1 < std::chrono::milliseconds(XLINK_DEVICE_DEFAULT_SEARCH_TIMEOUT_MS)); // if at least one device matched, return OK otherwise return not found if(num_devices_match <= 0) @@ -524,12 +673,12 @@ xLinkPlatformErrorCode_t tcpip_close_search_context(void* ctx) // TODO(themarpe) - duplicate until further tested -xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* devices, size_t devices_size, unsigned int* device_count) +xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* devices, size_t devices_size, unsigned int* device_count, int timeout_ms) { // Name signifies ip in TCP_IP protocol case const char* target_ip = in_deviceRequirements.name; XLinkDeviceState_t target_state = in_deviceRequirements.state; - auto target_platform = in_deviceRequirements.platform; + XLinkPlatform_t target_platform = in_deviceRequirements.platform; const char* target_mxid = in_deviceRequirements.mxid; // Socket @@ -548,9 +697,10 @@ xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequireme if(target_mxid != NULL && strlen(target_mxid) > 0){ check_target_mxid = true; } + DEBUG("check target mxid = %d\n", check_target_mxid); // Create socket first (also capable of doing broadcasts) - if(tcpip_create_socket_broadcast(&sock) != TCPIP_HOST_SUCCESS){ + if(tcpip_create_socket_broadcast(&sock, DEVICE_DISCOVERY_SOCKET_TIMEOUT) != TCPIP_HOST_SUCCESS){ return X_LINK_PLATFORM_ERROR; } @@ -559,8 +709,9 @@ xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequireme // TODO(themarpe) - Add IPv6 capabilities // send unicast device discovery struct sockaddr_in device_address; + memset(&device_address, 0, sizeof(device_address)); device_address.sin_family = AF_INET; - device_address.sin_port = htons(BROADCAST_UDP_PORT); + device_address.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); // Convert address to binary #if (defined(_WIN32) || defined(__USE_W32_SOCKETS)) && (_WIN32_WINNT <= 0x0501) @@ -597,6 +748,7 @@ xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequireme } char ip_addr[INET_ADDRSTRLEN] = {0}; + // tcpipHostDeviceDiscoveryResp_t recv_buffer = {}; uint8_t recv_buffer[1500] = {}; struct sockaddr_in dev_addr; #if (defined(_WIN32) || defined(_WIN64) ) @@ -621,12 +773,14 @@ xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequireme tcpipHostDeviceDiscoveryResp_t* discovery = reinterpret_cast(recv_buffer); foundState = tcpip_convert_device_state(discovery->state); strncpy(bufferId, discovery->mxid, sizeof(bufferId)); - } else if (command == TCPIP_HOST_CMD_DEVICE_DISCOVERY_EX) { + } else if(command == TCPIP_HOST_CMD_DEVICE_DISCOVERY_EX) { tcpipHostDeviceDiscoveryExResp_t* discoveryEx = reinterpret_cast(recv_buffer); foundState = tcpip_convert_device_state(discoveryEx->state); strncpy(bufferId, discoveryEx->id, sizeof(bufferId)); protocol = tcpip_convert_device_protocol(discoveryEx->protocol); platform = tcpip_convert_device_platform(discoveryEx->platform); + // TODO(status) + platform = tcpip_convert_device_platform(discoveryEx->platform); port = discoveryEx->portHttp; } else { continue; @@ -635,7 +789,7 @@ xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequireme DEBUG("target_state: %d, foundState: %d\n", target_state, foundState); if(target_state != X_LINK_ANY_STATE && foundState != target_state) continue; // Check that platform matches - // DEBUG("target_platform: %d, platform: %d\n", target_platform, platform); + DEBUG("target_platform: %d, platform: %d\n", target_platform, platform); if(target_platform != X_LINK_ANY_PLATFORM && platform != target_platform) continue; DEBUG("target id: %s, found id: %s\n", target_mxid, bufferId); @@ -672,7 +826,7 @@ xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequireme num_devices_match++; } - } while(std::chrono::steady_clock::now() - t1 < DEVICE_DISCOVERY_RES_TIMEOUT); + } while(std::chrono::steady_clock::now() - t1 < std::chrono::milliseconds(timeout_ms)); tcpip_close_socket(sock); @@ -708,7 +862,6 @@ xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequireme } - xLinkPlatformErrorCode_t tcpip_boot_bootloader(const char* name){ if(name == NULL || name[0] == 0){ return X_LINK_PLATFORM_DEVICE_NOT_FOUND; @@ -723,8 +876,9 @@ xLinkPlatformErrorCode_t tcpip_boot_bootloader(const char* name){ // TODO(themarpe) - Add IPv6 capabilities // send unicast reboot to bootloader struct sockaddr_in device_address; + memset(&device_address, 0, sizeof(device_address)); device_address.sin_family = AF_INET; - device_address.sin_port = htons(BROADCAST_UDP_PORT); + device_address.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); // Convert address to binary #if (defined(_WIN32) || defined(__USE_W32_SOCKETS)) && (_WIN32_WINNT <= 0x0501) @@ -743,3 +897,504 @@ xLinkPlatformErrorCode_t tcpip_boot_bootloader(const char* name){ return X_LINK_PLATFORM_SUCCESS; } + + +int tcpipPlatformRead(void *fdKey, void *data, int size) +{ + int nread = 0; + + void* tmpsockfd = NULL; + if(getPlatformDeviceFdFromKey(fdKey, &tmpsockfd)){ + mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); + return -1; + } + TCPIP_SOCKET sock = (TCPIP_SOCKET) (uintptr_t) tmpsockfd; + + while(nread < size) + { + int rc = recv(sock, &((char*)data)[nread], size - nread, 0); + if(rc <= 0) + { + return -1; + } + else + { + nread += rc; +#if defined(TCP_QUICKACK) + const int enable = 1; + if(tcpip_setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, &enable, sizeof(enable)) < 0) + { + // Do not error out, as not portable + // Warning is not needed, as its issued once at the beginnning + // mvLog(MVLOG_WARN, "TCP_QUICKACK could not be enabled"); + } +#endif + } + } + return 0; +} + +int tcpipPlatformWrite(void *fdKey, void *data, int size) +{ + int byteCount = 0; + + void* tmpsockfd = NULL; + if(getPlatformDeviceFdFromKey(fdKey, &tmpsockfd)){ + mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); + return -1; + } + TCPIP_SOCKET sock = (TCPIP_SOCKET) (uintptr_t) tmpsockfd; + + while(byteCount < size) + { + // Use send instead of write and ignore SIGPIPE + //rc = write((intptr_t)fd, &((char*)data)[byteCount], size - byteCount); + + int flags = 0; + #if defined(MSG_NOSIGNAL) + // Use flag NOSIGNAL on send call + flags = MSG_NOSIGNAL; + #endif + + int rc = send(sock, &((char*)data)[byteCount], size - byteCount, flags); + if(rc <= 0) + { + return -1; + } + else + { + byteCount += rc; + } + } + + return 0; +} + +// TODO add IPv6 to tcpipPlatformConnect() +int tcpipPlatformServer(const char *devPathRead, const char *devPathWrite, void **fd, long *sockFd) +{ + TCPIP_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0) + { + mvLog(MVLOG_FATAL, "Couldn't open socket for server"); + tcpip_close_socket(sock); + return X_LINK_PLATFORM_ERROR; + } + + if (sockFd != nullptr) *sockFd = sock; + + int reuse_addr = 1; + int sc; + sc = tcpip_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(int)); + if(sc < 0) + { + mvLog(MVLOG_FATAL, "Couldn't set server socket options"); + tcpip_close_socket(sock); + return X_LINK_PLATFORM_ERROR; + } + + // Disable sigpipe reception on send + #if defined(SO_NOSIGPIPE) + const int set = 1; + setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(set)); + #endif + + // parse IP + :port + char ip[INET_ADDRSTRLEN + 16]; + strncpy(ip, "0.0.0.0", sizeof(ip) - 1); // ANY by default + int port = TCPIP_LINK_SOCKET_PORT; + sscanf(devPathWrite, "%16[^:]:%15d", ip, &port); + + struct sockaddr_in serv_addr = {0}, client = {0}; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + // Convert address to binary + #if (defined(_WIN32) || defined(__USE_W32_SOCKETS)) && (_WIN32_WINNT <= 0x0501) + serv_addr.sin_addr.s_addr = inet_addr(ip); // for XP + #else + inet_pton(AF_INET, ip, &serv_addr.sin_addr.s_addr); + #endif + serv_addr.sin_port = htons(port); + if(bind(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) + { + mvLog(MVLOG_FATAL, "Couldn't bind to server socket"); + perror("bind"); + tcpip_close_socket(sock); + return X_LINK_PLATFORM_ERROR; + } + + if(listen(sock, 1) < 0) + { + mvLog(MVLOG_FATAL, "Couldn't listen to server socket"); + tcpip_close_socket(sock); + return X_LINK_PLATFORM_ERROR; + } + +#if (defined(_WIN32) || defined(_WIN64) ) + using socklen_portable = int; +#else + using socklen_portable = socklen_t; +#endif + + socklen_portable len = (socklen_portable) sizeof(client); + int connfd = accept(sock, (struct sockaddr*)&client, &len); + // Regardless of return, close the listening socket + tcpip_close_socket(sock); + if (sockFd != nullptr) *sockFd = -1; + // Then check if connection was accepted succesfully + if(connfd < 0) + { + mvLog(MVLOG_FATAL, "Couldn't accept a connection to server socket"); + return X_LINK_PLATFORM_ERROR; + } + + // Disable Nagle to reduce latency on the accepted connection + int on = 1; + if(tcpip_setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + { + perror("setsockopt TCP_NODELAY (server)"); + tcpip_close_socket(connfd); + return X_LINK_PLATFORM_ERROR; + } + + // Store the socket and create a "unique" key instead + // (as file descriptors are reused and can cause a clash with lookups between scheduler and link) + *fd = createPlatformDeviceFdKey((void*) (uintptr_t) connfd); + + return 0; +} + +// TODO add IPv6 to tcpipPlatformConnect() +int tcpipPlatformConnect(const char *devPathRead, const char *devPathWrite, void **fd) +{ + if (!devPathWrite || !fd) { + return X_LINK_PLATFORM_INVALID_PARAMETERS; + } + + TCPIP_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); + +#if (defined(_WIN32) || defined(_WIN64) ) + if(sock == INVALID_SOCKET) + { + return TCPIP_HOST_ERROR; + } +#else + if(sock < 0) + { + return TCPIP_HOST_ERROR; + } +#endif + + // Disable sigpipe reception on send + #if defined(SO_NOSIGPIPE) + const int set = 1; + setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(set)); + #endif + + struct sockaddr_in serv_addr = { 0 }; + + const size_t maxlen = 255; + size_t len = strnlen(devPathWrite, maxlen + 1); + if (len == 0 || len >= maxlen + 1) + return X_LINK_PLATFORM_INVALID_PARAMETERS; + char *const serv_ip = (char *)malloc(len + 1); + if (!serv_ip) + return X_LINK_PLATFORM_ERROR; + serv_ip[0] = 0; + // Parse port if specified, or use default + int port = TCPIP_LINK_SOCKET_PORT; + sscanf(devPathWrite, "%[^:]:%d", serv_ip, &port); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + + int ret = inet_pton(AF_INET, serv_ip, &serv_addr.sin_addr); + free(serv_ip); + + if(ret <= 0) + { + tcpip_close_socket(sock); + return -1; + } + + int on = 1; + if(tcpip_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + { + perror("setsockopt TCP_NODELAY"); + tcpip_close_socket(sock); + return -1; + } + +#if defined(TCP_QUICKACK) + if(tcpip_setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, &on, sizeof(on)) < 0) + { + // Do not error out, as its not portable + mvLog(MVLOG_WARN, "TCP_QUICKACK could not be enabled"); + } +#endif + + if(connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + tcpip_close_socket(sock); + return -1; + } + + // Store the socket and create a "unique" key instead + // (as file descriptors are reused and can cause a clash with lookups between scheduler and link) + *fd = createPlatformDeviceFdKey((void*) (uintptr_t) sock); + + return 0; +} + + +xLinkPlatformErrorCode_t tcpipPlatformBootBootloader(const char *name) +{ + return tcpip_boot_bootloader(name); +} + + +int tcpipPlatformDeviceFdDown(void *fdKey) +{ + int status = 0; + + void* tmpsockfd = NULL; + if(getPlatformDeviceFdFromKey(fdKey, &tmpsockfd)){ + mvLog(MVLOG_FATAL, "Cannot find file descriptor by key"); + return -1; + } + TCPIP_SOCKET sock = (TCPIP_SOCKET) (uintptr_t) tmpsockfd; + +#ifdef _WIN32 + status = shutdown(sock, SD_BOTH); +#else + if(sock != -1) + { + status = shutdown(sock, SHUT_RDWR); + } +#endif + + return status; +} + +int tcpipPlatformClose(void *fdKey) +{ + int status = 0; + + void* tmpsockfd = NULL; + if(getPlatformDeviceFdFromKey(fdKey, &tmpsockfd)){ + mvLog(MVLOG_FATAL, "Cannot find file descriptor by key"); + return -1; + } + TCPIP_SOCKET sock = (TCPIP_SOCKET) (uintptr_t) tmpsockfd; + +#ifdef _WIN32 + status = shutdown(sock, SD_BOTH); + if (status == 0) { status = closesocket(sock); } +#else + if(sock != -1) + { + status = shutdown(sock, SHUT_RDWR); + if (status == 0) { status = close(sock); } + } +#endif + + if(destroyPlatformDeviceFdKey(fdKey)){ + mvLog(MVLOG_FATAL, "Cannot destroy file descriptor key"); + return -1; + } + + return status; +} + + +int tcpipPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length){ + // TCPIP doesn't support a boot mechanism + return -1; +} + +// Discovery Service +static std::thread serviceThread; +static std::mutex serviceMutex; +static std::atomic serviceRunning{false}; +static std::function serviceCallback = nullptr; + +void tcpip_set_discovery_service_reset_callback(void (*cb)()) { + std::unique_lock lock(serviceMutex); + serviceCallback = cb; +} + +xLinkPlatformErrorCode_t tcpip_start_discovery_service(const char* id, XLinkDeviceState_t state, XLinkPlatform_t platform) { + // Parse arguments first + auto deviceState = tcpip_convert_device_state(state); + auto devicePlatform = tcpip_convert_device_platform(platform); + if(deviceState == TCPIP_HOST_STATE_INVALID || devicePlatform == TCPIP_HOST_PLATFORM_INVALID) { + return X_LINK_PLATFORM_INVALID_PARAMETERS; + } + if(id == nullptr) { + return X_LINK_PLATFORM_INVALID_PARAMETERS; + } + std::string deviceId{id}; + auto resetCb = serviceCallback; + + // Lock and proceed to start the thread + std::unique_lock lock(serviceMutex); + + // The service must be stopped first + if(serviceRunning) { + return X_LINK_PLATFORM_ERROR; + } + if(serviceThread.joinable()) { + serviceThread.join(); + } + + // Start service + serviceRunning = true; + serviceThread = std::thread([deviceId, deviceState, devicePlatform, resetCb](){ + // Historical artifact + int gpioBootMode = 0x3; + + TCPIP_SOCKET sockfd; + if(tcpip_create_socket_broadcast(&sockfd, DEFAULT_DEVICE_DISCOVERY_POOL_TIMEOUT) < 0) + { + mvLog(MVLOG_FATAL, "Failure creating socket. Couldn't start device discovery service"); + return; + } + + struct sockaddr_in recv_addr; + recv_addr.sin_family = AF_INET; + recv_addr.sin_port = htons(DEFAULT_DEVICE_DISCOVERY_PORT); + recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + if(bind(sockfd, (struct sockaddr*) &recv_addr, sizeof(recv_addr)) < 0) + { + mvLog(MVLOG_FATAL, "Failure binding. Couldn't start device discovery service"); + tcpip_close_socket(sockfd); + return; + } + + while(serviceRunning) { + + // receive broadcast message from client + tcpipHostDeviceDiscoveryReq_t request; + struct sockaddr_in send_addr; + socklen_t socklen = sizeof(send_addr); + + #if (defined(_WIN32) || defined(_WIN64) ) + using PACKET_LEN = int; + #else + using PACKET_LEN = ssize_t; + #endif + + PACKET_LEN packetlen = 0; + if(( packetlen = recvfrom(sockfd, reinterpret_cast(&request), sizeof(request), 0, (struct sockaddr*) &send_addr, &socklen)) < 0){ + if(tcpip_errno != ERRNO_EAGAIN) { + mvLog(MVLOG_ERROR, "Device discovery service - Error recvform - %d\n", tcpip_errno); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + continue; + } + + // Debug + #ifdef HAS_DEBUG + DEBUG("Received packet, length: %zd data: ", packetlen); + for(ssize_t i = 0; i < packetlen; i++){ + DEBUG("%02X ", ((uint8_t*)&request)[i]); + } + DEBUG("\n"); + #endif + + // Parse Request + switch (request.command) { + case TCPIP_HOST_CMD_DEVICE_DISCOVER: { + mvLog(MVLOG_DEBUG, "Received device discovery request, sending back - mxid: %s, state: %u\n", deviceId.c_str(), (uint32_t) deviceState); + + if(devicePlatform == TCPIP_HOST_PLATFORM_MYRIAD_X) { + + // send back device discovery response + tcpipHostDeviceDiscoveryResp_t resp = {}; + resp.command = TCPIP_HOST_CMD_DEVICE_DISCOVER; + strncpy(resp.mxid, deviceId.c_str(), sizeof(resp.mxid)); + resp.state = deviceState; + + if(sendto(sockfd, reinterpret_cast(&resp), sizeof(resp), 0, (struct sockaddr*) &send_addr, sizeof(send_addr)) < 0) { + mvLog(MVLOG_ERROR, "Device discovery service - Error sendto...\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + } else { + // send back device discovery EX response + tcpipHostDeviceDiscoveryExResp_t resp = {}; + resp.command = TCPIP_HOST_CMD_DEVICE_DISCOVERY_EX; + resp.platform = devicePlatform; + resp.state = deviceState; + resp.protocol = TCPIP_HOST_PROTOCOL_TCP_IP; + strncpy(resp.id, deviceId.c_str(), sizeof(resp.id)); + + if(sendto(sockfd, reinterpret_cast(&resp), sizeof(resp), 0, (struct sockaddr*) &send_addr, sizeof(send_addr)) < 0) { + mvLog(MVLOG_ERROR, "Device discovery service - Error sendto...\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + } + + } break; + + case TCPIP_HOST_CMD_DEVICE_INFO: { + + mvLog(MVLOG_DEBUG, "Received device information request, sending back - mxid: %s, speed %d?, full duplex: %d?, boot mode: 0x%02X\n", deviceId.c_str(), 0,0, gpioBootMode); + + // send back device information response + tcpipHostDeviceInformationResp_t resp = {}; + resp.command = TCPIP_HOST_CMD_DEVICE_INFO; + strncpy(resp.mxid, deviceId.c_str(), sizeof(resp.mxid)); + // TODO(themarpe) - reimplement or drop this command + resp.linkSpeed = 0; + resp.linkFullDuplex = 0; + resp.gpioBootMode = gpioBootMode; + if(sendto(sockfd, reinterpret_cast(&resp), sizeof(resp), 0, (struct sockaddr*) &send_addr, sizeof(send_addr)) < 0) { + mvLog(MVLOG_ERROR, "Device discovery service - Error sendto...\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + } break; + + case TCPIP_HOST_CMD_RESET: { + if(resetCb) resetCb(); + } break; + + default: { + + mvLog(MVLOG_DEBUG, "Received invalid request, sending back no_command"); + + // send back device information response + tcpipHostCommand_t resp = TCPIP_HOST_CMD_NO_COMMAND; + if(sendto(sockfd, reinterpret_cast(&resp), sizeof(resp), 0, (struct sockaddr*) &send_addr, sizeof(send_addr)) < 0) { + mvLog(MVLOG_ERROR, "Device discovery service - Error sendto...\n"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + } break; + + } + } + }); + + return X_LINK_PLATFORM_SUCCESS; +} + +void tcpip_stop_discovery_service() { + std::unique_lock lock(serviceMutex); + serviceRunning = false; + serviceThread.join(); +} + +void tcpip_detach_discovery_service() { + std::unique_lock lock(serviceMutex); + serviceThread.detach(); +} + +bool tcpip_is_running_discovery_service() { + return serviceRunning; +} diff --git a/src/pc/protocols/tcpip_host.h b/src/pc/protocols/tcpip_host.h index 4733df6f..b468cac5 100644 --- a/src/pc/protocols/tcpip_host.h +++ b/src/pc/protocols/tcpip_host.h @@ -51,48 +51,15 @@ typedef enum TCPIP_INVALID_PARAMETERS = -5 } tcpipHostError_t; -/* Host-to-device command list */ -typedef enum -{ - TCPIP_HOST_CMD_NO_COMMAND = 0, - TCPIP_HOST_CMD_DEVICE_DISCOVER = 1, - TCPIP_HOST_CMD_DEVICE_INFO = 2, - TCPIP_HOST_CMD_RESET = 3, - TCPIP_HOST_CMD_DEVICE_DISCOVERY_EX = 4, -} tcpipHostCommand_t; - -/* Device state */ -typedef enum -{ - TCPIP_HOST_STATE_BOOTED = 1, - TCPIP_HOST_STATE_BOOTLOADER = 3, - TCPIP_HOST_STATE_FLASH_BOOTED = 4, -} tcpipHostDeviceState_t; - -/* Device response payload */ -typedef struct -{ - tcpipHostCommand_t command; - char mxid[32]; - uint32_t state; -} tcpipHostDeviceDiscoveryResp_t; - -/* Device response payload extended*/ -typedef struct -{ - tcpipHostCommand_t command; - char id[32]; - uint32_t state; - uint32_t protocol; - uint32_t platform; - uint16_t portHttp; - uint16_t portHttps; -} tcpipHostDeviceDiscoveryExResp_t; - /* **************************************************************************/ /* Public Function Declarations */ /* **************************************************************************/ +/** + * @brief Initializes TCP/IP protocol +*/ +tcpipHostError_t tcpip_initialize(); + /** * @brief Close socket * @@ -110,10 +77,11 @@ tcpipHostError_t tcpip_close_socket(TCPIP_SOCKET socket); * @param[in] devices_size Size of devices array * @param[out] device_count Total device IP address obtained * @param[in] target_ip Target IP address to be checked + * @param[in] timeout_ms Timeout in milliseconds * @retval TCPIP_HOST_ERROR Failed to get network interface informations * @retval TCPIP_HOST_SUCCESS Received all device IP address available */ -xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* devices, size_t devices_size, unsigned int* device_count); +xLinkPlatformErrorCode_t tcpip_get_devices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* devices, size_t devices_size, unsigned int* device_count, int timeout_ms); xLinkPlatformErrorCode_t tcpip_create_search_context(void** pctx, const deviceDesc_t in_deviceRequirements); xLinkPlatformErrorCode_t tcpip_perform_search(void* ctx, deviceDesc_t* devices, size_t devices_size, unsigned int* device_count); @@ -126,9 +94,23 @@ xLinkPlatformErrorCode_t tcpip_close_search_context(void* ctx); */ xLinkPlatformErrorCode_t tcpip_boot_bootloader(const char* name); +int tcpipPlatformRead(void *fd, void *data, int size); +int tcpipPlatformWrite(void *fd, void *data, int size); +int tcpipPlatformConnect(const char *devPathRead, const char *devPathWrite, void **fd); +int tcpipPlatformServer(const char *devPathRead, const char *devPathWrite, void **fd, long *sockFd); +xLinkPlatformErrorCode_t tcpipPlatformBootBootloader(const char *name); +int tcpipPlatformDeviceFdDown(void *fd); +int tcpipPlatformClose(void *fd); +int tcpipPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length); + +xLinkPlatformErrorCode_t tcpip_start_discovery_service(const char* id, XLinkDeviceState_t state, XLinkPlatform_t platform); +void tcpip_stop_discovery_service(); +void tcpip_detach_discovery_service(); +void tcpip_set_discovery_service_reset_callback(void (*cb)()); +bool tcpip_is_running_discovery_service(); #ifdef __cplusplus } #endif -#endif /* TCPIP_HOST_H */ \ No newline at end of file +#endif /* TCPIP_HOST_H */ diff --git a/src/pc/protocols/tcpip_memshd.cpp b/src/pc/protocols/tcpip_memshd.cpp new file mode 100644 index 00000000..b31a6c7a --- /dev/null +++ b/src/pc/protocols/tcpip_memshd.cpp @@ -0,0 +1,146 @@ +#include "tcpip_host.h" +#include "local_memshd.h" +#include "tcpip_memshd.h" + +#define MVLOG_UNIT_NAME tcpip_memshd +#include "XLinkLog.h" + +#include + +#include +#include +#include + +#include + +#if defined(__unix__) + +int tcpipOrLocalShdmemPlatformServer(XLinkProtocol_t *protocol, const char *devPathRead, const char *devPathWrite, void **fd) +{ + std::mutex connectionMutex; + std::condition_variable cv; + + bool isShdmemThreadFinished = false; + bool isTcpIpThreadFinished = false; + + int retTcpIp = -1, retShdmem = -1; + void *fdTcpIp = nullptr, *fdShdmem = nullptr; + long tcpIpSockFd = -1, shdmemSockFd = -1; + + auto threadShdmem = std::thread([&connectionMutex, + &cv, + &isShdmemThreadFinished, + &retShdmem, + &fdShdmem, + &shdmemSockFd](){ + auto ret = shdmemPlatformServer(SHDMEM_DEFAULT_SOCKET, SHDMEM_DEFAULT_SOCKET, &fdShdmem, &shdmemSockFd); + { + std::unique_lock l(connectionMutex); + retShdmem = ret; + isShdmemThreadFinished = true; + } + cv.notify_one(); + }); + + auto threadTcpip = std::thread([&connectionMutex, + &cv, + &isTcpIpThreadFinished, + &retTcpIp, + &devPathRead, + &devPathWrite, + &fdTcpIp, + &tcpIpSockFd]() { + auto ret = tcpipPlatformServer(devPathRead, devPathWrite, &fdTcpIp, &tcpIpSockFd); + { + std::unique_lock l(connectionMutex); + retTcpIp = ret; + isTcpIpThreadFinished = true; + } + cv.notify_one(); + }); + + { + std::unique_lock lock(connectionMutex); + cv.wait(lock, [&isShdmemThreadFinished, &isTcpIpThreadFinished]{ return isShdmemThreadFinished || isTcpIpThreadFinished; }); + + } + + // As soon as either one finishes, the other can be cleaned + // Use signals, as "accept" cannot be unblocked by "close"ing the underlying socket + if(!isTcpIpThreadFinished) { + if(tcpIpSockFd >= 0) { + shutdown(tcpIpSockFd, SHUT_RDWR); + #if defined(SO_LINGER) + const int set = 0; + setsockopt(tcpIpSockFd, SOL_SOCKET, SO_LINGER, (const char*)&set, sizeof(set)); + #endif + close(tcpIpSockFd); + } + + mvLog(MVLOG_ERROR, "Failed to start server with TCP/IP"); + } + + if(!isShdmemThreadFinished) { + if(shdmemSockFd >= 0) { + shutdown(shdmemSockFd, SHUT_RDWR); + #if defined(SO_LINGER) + const int set = 0; + setsockopt(shdmemSockFd, SOL_SOCKET, SO_LINGER, (const char*)&set, sizeof(set)); + #endif + close(shdmemSockFd); + } + + mvLog(MVLOG_ERROR, "Failed to start server with SHDMEM"); + } + + // Wait for both threads to wrap up + if(threadTcpip.joinable()) threadTcpip.join(); + if(threadShdmem.joinable()) threadShdmem.join(); + + // Asign the final protocol (once both threads finalize) + if(retTcpIp == 0) { + *fd = fdTcpIp; + *protocol = X_LINK_TCP_IP; + } + + if(retShdmem == X_LINK_SUCCESS) { + *fd = fdShdmem; + shdmemSetProtocol(protocol, devPathRead, devPathWrite); + } + + // if both connected, close TCP_IP + if(retTcpIp == 0 && retShdmem == X_LINK_SUCCESS) { + tcpipPlatformClose(fdTcpIp); + } + + return X_LINK_SUCCESS; +} + +int tcpipOrLocalShdmemPlatformConnect(XLinkProtocol_t *protocol, const char *devPathRead, const char *devPathWrite, void **fd) { + if(shdmemPlatformConnect(SHDMEM_DEFAULT_SOCKET, SHDMEM_DEFAULT_SOCKET, fd) == X_LINK_SUCCESS) { + return shdmemSetProtocol(protocol, devPathRead, devPathWrite); + } + + if (tcpipPlatformConnect(devPathRead, devPathWrite, fd) == X_LINK_SUCCESS) { + *protocol = X_LINK_TCP_IP; + return X_LINK_SUCCESS; + } + + return X_LINK_ERROR; +} + +#else + +int tcpipOrLocalShdmemPlatformServer(XLinkProtocol_t *protocol, const char *devPathRead, const char *devPathWrite, void **fd) +{ + *protocol = X_LINK_TCP_IP; + return tcpipPlatformServer(devPathRead, devPathWrite, fd, nullptr); +} + +int tcpipOrLocalShdmemPlatformConnect(XLinkProtocol_t *protocol, const char *devPathRead, const char *devPathWrite, void **fd) +{ + *protocol = X_LINK_TCP_IP; + return tcpipPlatformConnect(devPathRead, devPathWrite, fd); +} + +#endif diff --git a/src/pc/protocols/tcpip_memshd.h b/src/pc/protocols/tcpip_memshd.h new file mode 100644 index 00000000..f382f847 --- /dev/null +++ b/src/pc/protocols/tcpip_memshd.h @@ -0,0 +1,15 @@ +#pragma once + +#include "XLinkPlatform.h" +#include "XLinkPublicDefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int tcpipOrLocalShdmemPlatformServer(XLinkProtocol_t *protocol, const char *devPathRead, const char *devPathWrite, void **fd); +int tcpipOrLocalShdmemPlatformConnect(XLinkProtocol_t *protocol, const char *devPathRead, const char *devPathWrite, void **fd); + +#ifdef __cplusplus +} +#endif diff --git a/src/pc/protocols/usb_host.cpp b/src/pc/protocols/usb_host.cpp index a57b749a..a364dd2d 100644 --- a/src/pc/protocols/usb_host.cpp +++ b/src/pc/protocols/usb_host.cpp @@ -24,24 +24,53 @@ #include "usb_host.h" #include "../PlatformDeviceFd.h" +// std +#include +#include +#include +#include +#include +#include +#include + +// Used server side only +#if defined(__unix__) +#include +#include +#include +#endif + constexpr static int MAXIMUM_PORT_NUMBERS = 7; using VidPid = std::pair; static const int MX_ID_TIMEOUT_MS = 100; +static const int MX_ID_NOPS_TIMEOUT_MS = 500; static constexpr auto DEFAULT_OPEN_TIMEOUT = std::chrono::seconds(5); static constexpr auto DEFAULT_WRITE_TIMEOUT = 2000; +static constexpr auto DEFAULT_READ_TIMEOUT = 2000; static constexpr std::chrono::milliseconds DEFAULT_CONNECT_TIMEOUT{20000}; static constexpr std::chrono::milliseconds DEFAULT_SEND_FILE_TIMEOUT{10000}; +static constexpr size_t SERVER_CHUNKSZ = 15 * 1024 * 1024; static constexpr auto USB1_CHUNKSZ = 64; -static constexpr int USB_ENDPOINT_IN = 0x81; -static constexpr int USB_ENDPOINT_OUT = 0x01; +static constexpr int USB_VSC_INTERFACE = 0; +static constexpr int USB_VSC_ENDPOINT_IN = 0x81; +static constexpr int USB_VSC_ENDPOINT_OUT = 0x01; + +// TBD could be taken from the USB descriptor, based on strings +static constexpr int USB_EP_INTERFACE_GATE = 2; +static constexpr int USB_EP_INTERFACE_DEVICE = 3; +static constexpr int USB_EP_ENDPOINT_GATE_IN = 0x83; +static constexpr int USB_EP_ENDPOINT_GATE_OUT = 0x03; +static constexpr int USB_EP_ENDPOINT_DEVICE_IN = 0x84; +static constexpr int USB_EP_ENDPOINT_DEVICE_OUT = 0x04; static constexpr int XLINK_USB_DATA_TIMEOUT = 0; static unsigned int bulk_chunklen = DEFAULT_CHUNKSZ; static int write_timeout = DEFAULT_WRITE_TIMEOUT; static int initialized; +static std::atomic isServer { false }; struct UsbSetupPacket { uint8_t requestType; @@ -93,11 +122,26 @@ static std::unordered_map vidPidToDeviceS {{0x03E7, 0xf63b}, X_LINK_BOOTED}, {{0x03E7, 0xf63c}, X_LINK_BOOTLOADER}, {{0x03E7, 0xf63d}, X_LINK_FLASH_BOOTED}, + {{0x05C6, 0x901d}, X_LINK_GATE}, +}; + +struct USBGateRequest { + uint32_t RequestNum; + uint32_t RequestSize; +}; + +struct GateResponse { + uint32_t state; + uint32_t protocol; + uint32_t platform; }; static std::string getLibusbDevicePath(libusb_device *dev); static libusb_error getLibusbDeviceMxId(XLinkDeviceState_t state, std::string devicePath, const libusb_device_descriptor* pDesc, libusb_device *dev, std::string& outMxId); +static libusb_error getLibusbDeviceGateResponse(const libusb_device_descriptor* pDesc, libusb_device *dev, GateResponse& outGateResponse, std::string& outSerial); static const char* xlink_libusb_strerror(int x); +static bool libusbDeviceHasInterface(libusb_device* dev, int interfaceNumber); +static xLinkPlatformErrorCode_t refLibusbDeviceByNameWithInterface(const char* name, int interfaceNumber, libusb_device** pdev); #ifdef _WIN32 std::string getWinUsbMxId(VidPid vidpid, libusb_device* dev); #endif @@ -113,7 +157,7 @@ xLinkPlatformErrorCode_t getUSBDevices(const deviceDesc_t in_deviceRequirements, static libusb_device **devs = NULL; auto numDevices = libusb_get_device_list(context, &devs); if(numDevices < 0) { - mvLog(MVLOG_DEBUG, "Unable to get USB device list: %s", xlink_libusb_strerror(numDevices)); + mvLog(MVLOG_DEBUG, "Unable to get USB device list: %s", xlink_libusb_strerror(static_cast(numDevices))); return X_LINK_PLATFORM_ERROR; } @@ -165,39 +209,71 @@ xLinkPlatformErrorCode_t getUSBDevices(const deviceDesc_t in_deviceRequirements, } } - // Get device mxid + XLinkPlatform_t platform = X_LINK_MYRIAD_X; + XLinkProtocol_t protocol = X_LINK_USB_VSC; std::string mxId; - libusb_error rc = getLibusbDeviceMxId(state, devicePath, &desc, devs[i], mxId); - mvLog(MVLOG_DEBUG, "getLibusbDeviceMxId returned: %s", xlink_libusb_strerror(rc)); - switch (rc) - { - case LIBUSB_SUCCESS: - status = X_LINK_SUCCESS; - break; - case LIBUSB_ERROR_ACCESS: - status = X_LINK_INSUFFICIENT_PERMISSIONS; - break; - case LIBUSB_ERROR_BUSY: - status = X_LINK_DEVICE_ALREADY_IN_USE; - break; - default: - status = X_LINK_ERROR; - break; + + // Check for RVC3 and RVC4 first + if(state == X_LINK_GATE || in_deviceRequirements.platform == X_LINK_RVC3 || in_deviceRequirements.platform == X_LINK_RVC4){ + if(!libusbDeviceHasInterface(devs[i], USB_EP_INTERFACE_GATE)) { + // This may be the case on Windows with WinUSB, separate devices created for each interface + continue; + } + + GateResponse gateResponse; + auto gateRc = getLibusbDeviceGateResponse(&desc, devs[i], gateResponse, mxId); + if(gateRc != LIBUSB_SUCCESS) { + continue; + } + + if (gateResponse.platform == 4){ + platform = X_LINK_RVC4; + } else { + platform = X_LINK_RVC3; + } + + protocol = (XLinkProtocol_t)gateResponse.protocol; + state = (XLinkDeviceState_t)gateResponse.state; + + } else { + // Get device mxid + libusb_error rc = getLibusbDeviceMxId(state, devicePath, &desc, devs[i], mxId); + mvLog(MVLOG_DEBUG, "getLibusbDeviceMxId returned: %s", xlink_libusb_strerror(rc)); + switch (rc) + { + case LIBUSB_SUCCESS: + status = X_LINK_SUCCESS; + break; + case LIBUSB_ERROR_ACCESS: + status = X_LINK_INSUFFICIENT_PERMISSIONS; + break; + case LIBUSB_ERROR_BUSY: + status = X_LINK_DEVICE_ALREADY_IN_USE; + break; + default: + status = X_LINK_ERROR; + break; + } + } - // compare with MxId + // Comparisons / filters + // compare deviceId std::string requiredMxId(in_deviceRequirements.mxid); if(requiredMxId.length() > 0 && requiredMxId != mxId){ // Current device doesn't match the "filter" continue; } - - // TODO(themarpe) - check platform + // compare platform + if(in_deviceRequirements.platform != X_LINK_ANY_PLATFORM && in_deviceRequirements.platform != platform){ + // Current device doesn't match the "filter" + continue; + } // Everything passed, fillout details of found device out_foundDevices[numDevicesFound].status = status; - out_foundDevices[numDevicesFound].platform = X_LINK_MYRIAD_X; - out_foundDevices[numDevicesFound].protocol = X_LINK_USB_VSC; + out_foundDevices[numDevicesFound].platform = platform; + out_foundDevices[numDevicesFound].protocol = protocol; out_foundDevices[numDevicesFound].state = state; memset(out_foundDevices[numDevicesFound].name, 0, sizeof(out_foundDevices[numDevicesFound].name)); strncpy(out_foundDevices[numDevicesFound].name, devicePath.c_str(), sizeof(out_foundDevices[numDevicesFound].name)); @@ -219,14 +295,15 @@ xLinkPlatformErrorCode_t getUSBDevices(const deviceDesc_t in_deviceRequirements, } extern "C" xLinkPlatformErrorCode_t refLibusbDeviceByName(const char* name, libusb_device** pdev) { + return refLibusbDeviceByNameWithInterface(name, -1, pdev); +} - std::lock_guard l(mutex); - +static xLinkPlatformErrorCode_t refLibusbDeviceByNameWithInterface(const char* name, int interfaceNumber, libusb_device** pdev) { // Get list of usb devices static libusb_device **devs = NULL; auto numDevices = libusb_get_device_list(context, &devs); if(numDevices < 0) { - mvLog(MVLOG_DEBUG, "Unable to get USB device list: %s", xlink_libusb_strerror(numDevices)); + mvLog(MVLOG_DEBUG, "Unable to get USB device list: %s", xlink_libusb_strerror(static_cast(numDevices))); return X_LINK_PLATFORM_ERROR; } @@ -240,6 +317,10 @@ extern "C" xLinkPlatformErrorCode_t refLibusbDeviceByName(const char* name, libu // Check if compare with name std::string requiredName(name); if(requiredName.length() > 0 && requiredName == devicePath){ + if(interfaceNumber >= 0 && !libusbDeviceHasInterface(devs[i], interfaceNumber)) { + continue; + } + // Found, increase ref and exit the loop libusb_ref_device(devs[i]); *pdev = devs[i]; @@ -289,6 +370,33 @@ std::string getLibusbDevicePath(libusb_device *dev) { return devicePath; } +static bool libusbDeviceHasInterface(libusb_device* dev, int interfaceNumber) { + if(dev == nullptr) { + return false; + } + + libusb_config_descriptor* cdesc = nullptr; + auto rc = libusb_get_config_descriptor(dev, 0, &cdesc); + if(rc != LIBUSB_SUCCESS || cdesc == nullptr) { + return false; + } + + bool found = false; + for(int i = 0; i < cdesc->bNumInterfaces && !found; i++) { + const auto& iface = cdesc->interface[i]; + for(int alt = 0; alt < iface.num_altsetting && !found; alt++) { + const auto& ifdesc = iface.altsetting[alt]; + if(interfaceNumber >= 0 && ifdesc.bInterfaceNumber != interfaceNumber) { + continue; + } + found = true; + } + } + + libusb_free_config_descriptor(cdesc); + return found; +} + libusb_error getLibusbDeviceMxId(XLinkDeviceState_t state, std::string devicePath, const libusb_device_descriptor* pDesc, libusb_device *dev, std::string& outMxId) { char mxId[XLINK_MAX_MX_ID_SIZE] = {0}; @@ -311,12 +419,14 @@ libusb_error getLibusbDeviceMxId(XLinkDeviceState_t state, std::string devicePat libusb_device_handle *handle = nullptr; int libusb_rc = LIBUSB_SUCCESS; - // Retry getting MX ID for 15ms - const std::chrono::milliseconds RETRY_TIMEOUT{15}; // 15ms + // Retry getting MX ID for 1 second + const std::chrono::milliseconds RETRY_TIMEOUT{1000}; // 1000ms const std::chrono::microseconds SLEEP_BETWEEN_RETRIES{100}; // 100us + int tryCount = 0; auto t1 = std::chrono::steady_clock::now(); do { + tryCount++; // Open device - if not already if(handle == nullptr){ @@ -389,6 +499,27 @@ libusb_error getLibusbDeviceMxId(XLinkDeviceState_t state, std::string devicePat // /////////////////////// // Start + // If first attempt was unsuccessful, try sending NOPs to clear out potential bad state + if (tryCount > 1) { + int size = 256 * 1024 + 16; + uint8_t *nops = (uint8_t *)calloc(size, 1); + if (nops) { + transferred = 0; + libusb_rc = libusb_bulk_transfer(handle, send_ep, nops, size, &transferred, MX_ID_NOPS_TIMEOUT_MS); + mvLog(MVLOG_DEBUG, "Sending NOPs to %s\n", devicePath.c_str()); + free(nops); + if (libusb_rc < 0 || size != transferred) { + mvLog(MVLOG_ERROR, "libusb_bulk_transfer (%s), transfer: %d, expected: %d", xlink_libusb_strerror(libusb_rc), transferred, size); + // Mark as error and retry + libusb_rc = -1; + // retry + std::this_thread::sleep_for(SLEEP_BETWEEN_RETRIES); + continue; + } + } + } + + mvLog(MVLOG_DEBUG, "Sending MXID read to %s, try %d\n", devicePath.c_str(), tryCount); // WD Protection & MXID Retrieval Command transferred = 0; libusb_rc = libusb_bulk_transfer(handle, send_ep, ((uint8_t*) usb_mx_id_get_payload()), usb_mx_id_get_payload_size(), &transferred, MX_ID_TIMEOUT_MS); @@ -440,7 +571,7 @@ libusb_error getLibusbDeviceMxId(XLinkDeviceState_t state, std::string devicePat // Convert to HEX presentation and store into mx_id for (int i = 0; i < expectedMxIdReadSize; i++) { - sprintf(mxId + 2*i, "%02X", rbuf[i]); + snprintf(mxId + 2*i, 3, "%02X", rbuf[i]); } // Indicate no error @@ -468,6 +599,14 @@ libusb_error getLibusbDeviceMxId(XLinkDeviceState_t state, std::string devicePat libusb_close(handle); } + // On windows, if libusb_rc is LIBUSB_ERROR_ACCESS and state is X_LINK_BOOTED, some other process is using the device + // In this case, we want the libusb error to be LIBUSB_ERROR_BUSY + #ifdef _WIN32 + if(libusb_rc == LIBUSB_ERROR_ACCESS && state == X_LINK_BOOTED) { + libusb_rc = LIBUSB_ERROR_BUSY; + } + #endif + // if mx_id couldn't be retrieved, exit by returning error if(libusb_rc != 0){ return (libusb_error) libusb_rc; @@ -492,12 +631,75 @@ libusb_error getLibusbDeviceMxId(XLinkDeviceState_t state, std::string devicePat } +static libusb_error getLibusbDeviceGateResponse(const libusb_device_descriptor* pDesc, libusb_device *dev, GateResponse& outGateResponse, std::string& outSerial) { + GateResponse gateResponse = {0}; + std::string serial = ""; + + // get serial from usb descriptor + libusb_device_handle *handle = nullptr; + int libusb_rc = LIBUSB_SUCCESS; + libusb_rc = libusb_open(dev, &handle); + if (libusb_rc != 0){ + return (libusb_error) libusb_rc; + } + + libusb_rc = libusb_claim_interface(handle, USB_EP_INTERFACE_GATE); + if (libusb_rc != 0){ + libusb_close(handle); + return (libusb_error) libusb_rc; + } + + USBGateRequest usbGateRequest = {12, 0}; + + int transferred = 0; + + libusb_rc = libusb_bulk_transfer(handle, USB_EP_ENDPOINT_GATE_OUT, (unsigned char*)&usbGateRequest, sizeof(usbGateRequest), &transferred, DEFAULT_WRITE_TIMEOUT); + if (libusb_rc != 0) { + libusb_close(handle); + return (libusb_error) libusb_rc; + } + + USBGateRequest usbGateResponse = { 0 }; + libusb_rc = libusb_bulk_transfer(handle, USB_EP_ENDPOINT_GATE_IN, (unsigned char*)&usbGateResponse, sizeof(usbGateResponse), &transferred, DEFAULT_WRITE_TIMEOUT); + if (libusb_rc != 0) { + libusb_close(handle); + return (libusb_error) libusb_rc; + } + + std::vector respBuffer; + respBuffer.resize(usbGateResponse.RequestSize); + libusb_rc = libusb_bulk_transfer(handle, USB_EP_ENDPOINT_GATE_IN, (unsigned char*)&respBuffer[0], usbGateResponse.RequestSize, &transferred, DEFAULT_WRITE_TIMEOUT); + if (libusb_rc != 0) { + libusb_close(handle); + return (libusb_error) libusb_rc; + } + + + size_t serialStrLen = usbGateResponse.RequestSize - sizeof(GateResponse); + serial.resize(serialStrLen + 1); + for (int i = 0; i < serialStrLen; ++i) { + serial[i] = respBuffer[i]; + } + serial[serialStrLen] = '\0'; + outSerial = serial; + + memcpy(&gateResponse, &respBuffer[serialStrLen], sizeof(gateResponse)); + outGateResponse = gateResponse; + + // Close opened device + if(handle != nullptr){ + libusb_close(handle); + } + + return libusb_error::LIBUSB_SUCCESS; +} + const char* xlink_libusb_strerror(int x) { return libusb_strerror((libusb_error) x); } -static libusb_error usb_open_device(libusb_device *dev, uint8_t* endpoint, libusb_device_handle*& handle) +static libusb_error usb_open_device(XLinkProtocol_t protocol, libusb_device *dev, uint8_t* endpoint, libusb_device_handle*& handle) { struct libusb_config_descriptor *cdesc; const struct libusb_interface_descriptor *ifdesc; @@ -506,6 +708,28 @@ static libusb_error usb_open_device(libusb_device *dev, uint8_t* endpoint, libus if((res = libusb_open(dev, &h)) < 0) { + + // On windows, if libusb_rc is LIBUSB_ERROR_ACCESS and state is X_LINK_BOOTED, some other process is using the device + // In this case, we want the libusb error to be LIBUSB_ERROR_BUSY + #ifdef _WIN32 + if(res == LIBUSB_ERROR_ACCESS) { + + struct libusb_device_descriptor desc; + auto res2 = libusb_get_device_descriptor(dev, &desc); + if (res2 < 0) { + mvLog(MVLOG_DEBUG, "Unable to get USB device descriptor: %s", xlink_libusb_strerror(res2)); + return (libusb_error) res2; + } + + VidPid vidpid{desc.idVendor, desc.idProduct}; + auto state = vidPidToDeviceState.at(vidpid); + + if(state == X_LINK_BOOTED) { + res = LIBUSB_ERROR_BUSY; + } + } + #endif + mvLog(MVLOG_DEBUG, "cannot open device: %s\n", xlink_libusb_strerror(res)); return (libusb_error) res; } @@ -530,12 +754,21 @@ static libusb_error usb_open_device(libusb_device *dev, uint8_t* endpoint, libus // Set to auto detach & reattach kernel driver, and ignore result (success or not supported) libusb_set_auto_detach_kernel_driver(h, 1); - if((res = libusb_claim_interface(h, 0)) < 0) - { - mvLog(MVLOG_DEBUG, "claiming interface 0 failed: %s\n", xlink_libusb_strerror(res)); - libusb_close(h); - return (libusb_error) res; + + if(protocol == X_LINK_USB_EP){ + if((res = libusb_claim_interface(h, USB_EP_INTERFACE_DEVICE)) < 0){ + mvLog(MVLOG_DEBUG, "claiming interface %d failed: %s\n", USB_EP_INTERFACE_DEVICE, xlink_libusb_strerror(res)); + libusb_close(h); + return (libusb_error) res; + } + } else { + if((res = libusb_claim_interface(h, USB_VSC_INTERFACE)) < 0){ + mvLog(MVLOG_DEBUG, "claiming interface %d failed: %s\n", USB_VSC_INTERFACE, xlink_libusb_strerror(res)); + libusb_close(h); + return (libusb_error) res; + } } + if((res = libusb_get_config_descriptor(dev, 0, &cdesc)) < 0) { mvLog(MVLOG_DEBUG, "Unable to get USB config descriptor: %s\n", xlink_libusb_strerror(res)); @@ -626,6 +859,16 @@ int usb_boot(const char *addr, const void *mvcmd, unsigned size) uint16_t bcdusb=-1; libusb_error res = LIBUSB_ERROR_ACCESS; + // Commands to enable watchdog on unbooted MX device + static const uint8_t wdog_en_cmd[] = { + // Header + 0x4D, 0x41, 0x32, 0x78, + // WD Protection - start + 0x9A, 0xA8, 0x00, 0x32, 0x20, 0xAD, 0xDE, 0xD0, 0xF1, + 0x9A, 0x9C, 0x00, 0x32, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, + 0x9A, 0xA8, 0x00, 0x32, 0x20, 0xAD, 0xDE, 0xD0, 0xF1, + 0x9A, 0xA4, 0x00, 0x32, 0x20, 0x01, 0x00, 0x00, 0x00, + }; auto t1 = steady_clock::now(); do { @@ -641,14 +884,17 @@ int usb_boot(const char *addr, const void *mvcmd, unsigned size) auto t2 = steady_clock::now(); do { - if((res = usb_open_device(dev, &endpoint, h)) == LIBUSB_SUCCESS){ + if((res = usb_open_device(X_LINK_USB_VSC, dev, &endpoint, h)) == LIBUSB_SUCCESS){ break; } std::this_thread::sleep_for(milliseconds(100)); } while(steady_clock::now() - t2 < DEFAULT_CONNECT_TIMEOUT); if(res == LIBUSB_SUCCESS) { - rc = send_file(h, endpoint, mvcmd, size, bcdusb); + rc = send_file(h, endpoint, wdog_en_cmd, sizeof(wdog_en_cmd), bcdusb); + if(rc == 0) { + rc = send_file(h, endpoint, mvcmd, size, bcdusb); + } libusb_release_interface(h, 0); libusb_close(h); } else { @@ -670,7 +916,7 @@ int usb_boot(const char *addr, const void *mvcmd, unsigned size) -xLinkPlatformErrorCode_t usbLinkOpen(const char *path, libusb_device_handle*& h) +xLinkPlatformErrorCode_t usbLinkOpen(XLinkProtocol_t protocol, const char *path, libusb_device_handle*& h) { using namespace std::chrono; if (path == NULL) { @@ -684,7 +930,14 @@ xLinkPlatformErrorCode_t usbLinkOpen(const char *path, libusb_device_handle*& h) auto t1 = steady_clock::now(); do { - if(refLibusbDeviceByName(path, &dev) == X_LINK_PLATFORM_SUCCESS){ + auto refRc = X_LINK_PLATFORM_ERROR; + if(protocol == X_LINK_USB_EP){ + refRc = refLibusbDeviceByNameWithInterface(path, USB_EP_INTERFACE_DEVICE, &dev); + } else { + refRc = refLibusbDeviceByName(path, &dev); + } + + if(refRc == X_LINK_PLATFORM_SUCCESS){ found = true; break; } @@ -695,7 +948,8 @@ xLinkPlatformErrorCode_t usbLinkOpen(const char *path, libusb_device_handle*& h) } uint8_t ep = 0; - libusb_error libusb_rc = usb_open_device(dev, &ep, h); + libusb_error libusb_rc = usb_open_device(protocol, dev, &ep, h); + libusb_unref_device(dev); if(libusb_rc == LIBUSB_SUCCESS) { return X_LINK_PLATFORM_SUCCESS; } else if(libusb_rc == LIBUSB_ERROR_ACCESS) { @@ -752,16 +1006,46 @@ xLinkPlatformErrorCode_t usbLinkBootBootloader(const char *path) { return X_LINK_PLATFORM_SUCCESS; } -void usbLinkClose(libusb_device_handle *f) +void usbLinkClose(XLinkProtocol_t protocol, libusb_device_handle *f) { - libusb_release_interface(f, 0); + + if (protocol == X_LINK_USB_EP){ + libusb_release_interface(f, USB_EP_INTERFACE_DEVICE); + } else { + libusb_release_interface(f, USB_VSC_INTERFACE); + } + libusb_close(f); } +extern int usbFdWrite; +extern int usbFdRead; +int usbPlatformServer(const char *devPathRead, const char *devPathWrite, void **fd) +{ +#if defined(__unix__) + // FIXME: get this info from the caller, don't hardcode here + int outfd = open("/dev/usb-ffs/device/ep1", O_WRONLY); + int infd = open("/dev/usb-ffs/device/ep2", O_RDONLY); + + if(outfd < 0 || infd < 0) { + return -1; + } + + usbFdRead = infd; + usbFdWrite = outfd; + + isServer = true; + + *fd = createPlatformDeviceFdKey((void*) (uintptr_t) usbFdRead); +#endif + + return 0; +} -int usbPlatformConnect(const char *devPathRead, const char *devPathWrite, void **fd) +int usbPlatformConnect(XLinkProtocol_t protocol, const char *devPathRead, const char *devPathWrite, void **fd) { + std::lock_guard l(mutex); #if (!defined(USE_USB_VSC)) #ifdef USE_LINK_JTAG struct sockaddr_in serv_addr; @@ -852,7 +1136,7 @@ int usbPlatformConnect(const char *devPathRead, const char *devPathWrite, void * #else libusb_device_handle* usbHandle = nullptr; - xLinkPlatformErrorCode_t ret = usbLinkOpen(devPathWrite, usbHandle); + xLinkPlatformErrorCode_t ret = usbLinkOpen(protocol, devPathWrite, usbHandle); if (ret != X_LINK_PLATFORM_SUCCESS) { @@ -864,14 +1148,16 @@ int usbPlatformConnect(const char *devPathRead, const char *devPathWrite, void * // (as file descriptors are reused and can cause a clash with lookups between scheduler and link) *fd = createPlatformDeviceFdKey(usbHandle); + isServer = false; #endif /*USE_USB_VSC*/ return 0; } -int usbPlatformClose(void *fdKey) +int usbPlatformClose(XLinkProtocol_t protocol, void *fdKey) { + std::lock_guard l(mutex); #ifndef USE_USB_VSC #ifdef USE_LINK_JTAG @@ -893,7 +1179,7 @@ int usbPlatformClose(void *fdKey) mvLog(MVLOG_FATAL, "Cannot find USB Handle by key: %" PRIxPTR, (uintptr_t) fdKey); return -1; } - usbLinkClose((libusb_device_handle *) tmpUsbHandle); + usbLinkClose(protocol, (libusb_device_handle *) tmpUsbHandle); if(destroyPlatformDeviceFdKey(fdKey)){ mvLog(MVLOG_FATAL, "Cannot destroy USB Handle key: %" PRIxPTR, (uintptr_t) fdKey); @@ -919,7 +1205,7 @@ int usbPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware -int usb_read(libusb_device_handle *f, void *data, size_t size) +int usb_read(libusb_device_handle *f, void *data, size_t size, uint8_t ep) { const int chunk_size = DEFAULT_CHUNKSZ; while(size > 0) @@ -927,7 +1213,7 @@ int usb_read(libusb_device_handle *f, void *data, size_t size) int bt, ss = (int)size; if(ss > chunk_size) ss = chunk_size; - int rc = libusb_bulk_transfer(f, USB_ENDPOINT_IN,(unsigned char *)data, ss, &bt, XLINK_USB_DATA_TIMEOUT); + int rc = libusb_bulk_transfer(f, ep, (unsigned char *)data, ss, &bt, XLINK_USB_DATA_TIMEOUT); if(rc) return rc; data = ((char *)data) + bt; @@ -936,7 +1222,7 @@ int usb_read(libusb_device_handle *f, void *data, size_t size) return 0; } -int usb_write(libusb_device_handle *f, const void *data, size_t size) +int usb_write(libusb_device_handle *f, const void *data, size_t size, uint8_t ep) { const int chunk_size = DEFAULT_CHUNKSZ; while(size > 0) @@ -944,7 +1230,7 @@ int usb_write(libusb_device_handle *f, const void *data, size_t size) int bt, ss = (int)size; if(ss > chunk_size) ss = chunk_size; - int rc = libusb_bulk_transfer(f, USB_ENDPOINT_OUT, (unsigned char *)data, ss, &bt, XLINK_USB_DATA_TIMEOUT); + int rc = libusb_bulk_transfer(f, ep, (unsigned char *)data, ss, &bt, XLINK_USB_DATA_TIMEOUT); if(rc) return rc; data = (char *)data + bt; @@ -953,7 +1239,43 @@ int usb_write(libusb_device_handle *f, const void *data, size_t size) return 0; } -int usbPlatformRead(void* fdKey, void* data, int size) +#if defined(__unix__) +static int usb_server_read(int fd, void* data, int size) { + size_t totalRead = 0; + auto* readPtr = static_cast(data); + const size_t readSize = static_cast(size); + + while(totalRead < readSize) { + const size_t chunk = std::min(SERVER_CHUNKSZ, readSize - totalRead); + const auto rc = read(fd, readPtr + totalRead, chunk); + if(rc <= 0) { + return -1; + } + totalRead += static_cast(rc); + } + + return static_cast(totalRead); +} + +static int usb_server_write(int fd, const void* data, int size) { + size_t totalWritten = 0; + const auto* writePtr = static_cast(data); + const size_t writeSize = static_cast(size); + + while(totalWritten < writeSize) { + const size_t chunk = std::min(SERVER_CHUNKSZ, writeSize - totalWritten); + const auto rc = write(fd, writePtr + totalWritten, chunk); + if(rc <= 0) { + return -1; + } + totalWritten += static_cast(rc); + } + + return static_cast(totalWritten); +} +#endif + +int usbPlatformRead(XLinkProtocol_t protocol, void* fdKey, void* data, int size) { int rc = 0; #ifndef USE_USB_VSC @@ -994,19 +1316,31 @@ int usbPlatformRead(void* fdKey, void* data, int size) #endif /*USE_LINK_JTAG*/ #else - void* tmpUsbHandle = NULL; - if(getPlatformDeviceFdFromKey(fdKey, &tmpUsbHandle)){ - mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); - return -1; - } - libusb_device_handle* usbHandle = (libusb_device_handle*) tmpUsbHandle; + if(isServer){ +#if defined(__unix__) + rc = usb_server_read(usbFdRead, data, size); +#else + rc = -1; +#endif + } else { + void* tmpUsbHandle = NULL; + if(getPlatformDeviceFdFromKey(fdKey, &tmpUsbHandle)){ + mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); + return -1; + } + libusb_device_handle* usbHandle = (libusb_device_handle*) tmpUsbHandle; - rc = usb_read(usbHandle, data, size); + if(protocol == X_LINK_USB_EP) { + rc = usb_read(usbHandle, data, size, USB_EP_ENDPOINT_DEVICE_IN); + } else { + rc = usb_read(usbHandle, data, size, USB_VSC_ENDPOINT_IN); + } + } #endif /*USE_USB_VSC*/ return rc; } -int usbPlatformWrite(void *fdKey, void *data, int size) +int usbPlatformWrite(XLinkProtocol_t protocol, void *fdKey, void *data, int size) { int rc = 0; #ifndef USE_USB_VSC @@ -1050,18 +1384,130 @@ int usbPlatformWrite(void *fdKey, void *data, int size) #endif /*USE_LINK_JTAG*/ #else - void* tmpUsbHandle = NULL; - if(getPlatformDeviceFdFromKey(fdKey, &tmpUsbHandle)){ - mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); - return -1; - } - libusb_device_handle* usbHandle = (libusb_device_handle*) tmpUsbHandle; + if(isServer){ +#if defined(__unix__) + rc = usb_server_write(usbFdWrite, data, size); +#else + rc = -1; +#endif + } else { + void* tmpUsbHandle = NULL; + if(getPlatformDeviceFdFromKey(fdKey, &tmpUsbHandle)){ + mvLog(MVLOG_FATAL, "Cannot find file descriptor by key: %" PRIxPTR, (uintptr_t) fdKey); + return -1; + } + libusb_device_handle* usbHandle = (libusb_device_handle*) tmpUsbHandle; - rc = usb_write(usbHandle, data, size); + if(protocol == X_LINK_USB_EP){ + rc = usb_write(usbHandle, data, size, USB_EP_ENDPOINT_DEVICE_OUT); + } else { + rc = usb_write(usbHandle, data, size, USB_VSC_ENDPOINT_OUT); + } + } #endif /*USE_USB_VSC*/ return rc; } +int usbPlatformGateRead(const char *name, void *data, int size, int timeout) +{ + std::lock_guard l(mutex); + + if (context == nullptr) return -1; + + int rc = 0; + + /* Get our device */ + libusb_device *gate_dev = NULL; + auto refRc = refLibusbDeviceByNameWithInterface(name, USB_EP_INTERFACE_GATE, &gate_dev); + if (refRc != X_LINK_PLATFORM_SUCCESS || gate_dev == NULL) { + rc = LIBUSB_ERROR_NO_DEVICE; + return rc; + } + + libusb_device_handle *gate_dev_handle; + libusb_open(gate_dev, &gate_dev_handle); + if (gate_dev_handle == NULL) { + rc = LIBUSB_ERROR_NO_DEVICE; + return rc; + } + + /* Not strictly necessary, but it is better to use it, + * as we're using kernel modules together with our interfaces + */ + rc = libusb_set_auto_detach_kernel_driver(gate_dev_handle, 1); + if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_NOT_SUPPORTED) { + libusb_close(gate_dev_handle); + + return rc; + } + + libusb_device* dev = libusb_get_device(gate_dev_handle); + + /* Now we claim our ffs interfaces */ + rc = libusb_claim_interface(gate_dev_handle, USB_EP_INTERFACE_GATE); + if (rc != LIBUSB_SUCCESS) { + libusb_close(gate_dev_handle); + + return rc; + } + + rc = libusb_bulk_transfer(gate_dev_handle, USB_EP_ENDPOINT_GATE_IN, (unsigned char*)data, size, &rc, timeout); + + libusb_close(gate_dev_handle); + + return rc; +} + +int usbPlatformGateWrite(const char *name, void *data, int size, int timeout) +{ + std::lock_guard l(mutex); + + if (context == nullptr) return -1; + + int rc = 0; + + /* Get our device */ + libusb_device *gate_dev = NULL; + auto refRc = refLibusbDeviceByNameWithInterface(name, USB_EP_INTERFACE_GATE, &gate_dev); + if (refRc != X_LINK_PLATFORM_SUCCESS || gate_dev == NULL) { + rc = LIBUSB_ERROR_NO_DEVICE; + return rc; + } + + libusb_device_handle *gate_dev_handle; + libusb_open(gate_dev, &gate_dev_handle); + if (gate_dev_handle == NULL) { + rc = LIBUSB_ERROR_NO_DEVICE; + return rc; + } + + /* Not strictly necessary, but it is better to use it, + * as we're using kernel modules together with our interfaces + */ + rc = libusb_set_auto_detach_kernel_driver(gate_dev_handle, 1); + if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_NOT_SUPPORTED) { + libusb_close(gate_dev_handle); + + return rc; + } + + libusb_device* dev = libusb_get_device(gate_dev_handle); + + /* Now we claim our ffs interfaces */ + rc = libusb_claim_interface(gate_dev_handle, USB_EP_INTERFACE_GATE); + if (rc != LIBUSB_SUCCESS) { + libusb_close(gate_dev_handle); + + return rc; + } + + rc = libusb_bulk_transfer(gate_dev_handle, USB_EP_ENDPOINT_GATE_OUT, (unsigned char*)data, size, &rc, timeout); + + libusb_close(gate_dev_handle); + + return rc; +} + #ifdef _WIN32 #include #include diff --git a/src/pc/protocols/usb_host.h b/src/pc/protocols/usb_host.h index 1103fa49..574ddfa2 100644 --- a/src/pc/protocols/usb_host.h +++ b/src/pc/protocols/usb_host.h @@ -47,12 +47,16 @@ int usb_boot(const char *addr, const void *mvcmd, unsigned size); int get_pid_by_name(const char* name); xLinkPlatformErrorCode_t usbLinkBootBootloader(const char* path); -int usbPlatformConnect(const char *devPathRead, const char *devPathWrite, void **fd); -int usbPlatformClose(void *fd); +int usbPlatformConnect(XLinkProtocol_t protocol, const char *devPathRead, const char *devPathWrite, void **fd); +int usbPlatformServer(const char *devPathRead, const char *devPathWrite, void **fd); +int usbPlatformClose(XLinkProtocol_t protocol, void *fd); int usbPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length); -int usbPlatformRead(void *fd, void *data, int size); -int usbPlatformWrite(void *fd, void *data, int size); +int usbPlatformRead(XLinkProtocol_t protocol, void *fd, void *data, int size); +int usbPlatformWrite(XLinkProtocol_t protocol, void *fd, void *data, int size); + +int usbPlatformGateRead(const char *name, void *data, int size, int timeout); +int usbPlatformGateWrite(const char *name, void *data, int size, int timeout); #else @@ -60,12 +64,16 @@ int usbPlatformWrite(void *fd, void *data, int size); static inline int usbInitialize(void* options) { return -1; } static inline xLinkPlatformErrorCode_t usbLinkBootBootloader(const char* path) { return X_LINK_PLATFORM_USB_DRIVER_NOT_LOADED; } -static inline int usbPlatformConnect(const char *devPathRead, const char *devPathWrite, void **fd) { return -1; } -static inline int usbPlatformClose(void *fd) { return -1; } +static inline int usbPlatformConnect(XLinkProtocol_t protocol, const char *devPathRead, const char *devPathWrite, void **fd) { return -1; } +static inline int usbPlatformServer(const char *devPathRead, const char *devPathWrite, void **fd) { return -1; } +static inline int usbPlatformClose(XLinkProtocol_t protocol, void *fd) { return -1; } static inline int usbPlatformBootFirmware(const deviceDesc_t* deviceDesc, const char* firmware, size_t length) { return -1; } -static inline int usbPlatformRead(void *fd, void *data, int size) { return -1; } -static inline int usbPlatformWrite(void *fd, void *data, int size) { return -1; } +static inline int usbPlatformRead(XLinkProtocol_t protocol, void *fd, void *data, int size) { return -1; } +static inline int usbPlatformWrite(XLinkProtocol_t protocol, void *fd, void *data, int size) { return -1; } + +static inline int usbPlatformGateRead(const char *name, void *data, int size, int timeout) { return -1; } +static inline int usbPlatformGateWrite(const char *name, void *data, int size, int timeout) { return -1; } static inline xLinkPlatformErrorCode_t getUSBDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t* out_foundDevices, int sizeFoundDevices, diff --git a/src/shared/XLinkCallback.cpp b/src/shared/XLinkCallback.cpp new file mode 100644 index 00000000..d4c39949 --- /dev/null +++ b/src/shared/XLinkCallback.cpp @@ -0,0 +1,44 @@ +#include "XLink.h" + +#include +#include +#include + + +static std::mutex mtx; +static uint16_t uniqueId{0}; +static std::unordered_map> callbacks; + +extern "C" { + +int XLinkAddLinkDownCb(void (*cb)(linkId_t)) { + std::unique_lock l(mtx); + + uint16_t cbId = uniqueId++; + if(callbacks.count(cbId)) { + return -1; + } + callbacks[cbId] = cb; + + return cbId; +} + +int XLinkRemoveLinkDownCb(int cbId) { + std::unique_lock l(mtx); + if(callbacks.count(cbId)) { + callbacks.erase(cbId); + } else { + return -1; + } + + return 0; +} + +void XLinkPlatformLinkDownNotify(linkId_t linkId) { + std::unique_lock l(mtx); + for(const auto& kv : callbacks) { + kv.second(linkId); + } +} + +} \ No newline at end of file diff --git a/src/shared/XLinkData.c b/src/shared/XLinkData.c index 5f52a274..2cc93658 100644 --- a/src/shared/XLinkData.c +++ b/src/shared/XLinkData.c @@ -25,17 +25,20 @@ #include "XLinkLog.h" #include "XLinkStringUtils.h" +#ifdef __unix__ +#include +#endif + // ------------------------------------ // Helpers declaration. Begin. // ------------------------------------ -#ifndef __DEVICE__ static XLinkError_t checkEventHeader(xLinkEventHeader_t header); -#endif - static float timespec_diff(struct timespec *start, struct timespec *stop); static XLinkError_t addEvent(xLinkEvent_t *event, unsigned int timeoutMs); +static XLinkError_t addEvent_(xLinkEvent_t *event, unsigned int timeoutMs, XLinkTimespec* outTime); static XLinkError_t addEventWithPerf(xLinkEvent_t *event, float* opTime, unsigned int timeoutMs); +static XLinkError_t addEventWithPerf_(xLinkEvent_t *event, float* opTime, unsigned int timeoutMs, XLinkTimespec* outTime); static XLinkError_t addEventWithPerfTimeout(xLinkEvent_t *event, float* opTime, unsigned int msTimeout); static XLinkError_t getLinkByStreamId(streamId_t streamId, xLinkDesc_t** out_link); @@ -69,7 +72,6 @@ streamId_t XLinkOpenStream(linkId_t id, const char* name, int stream_write_size) DispatcherWaitEventComplete(&link->deviceHandle, XLINK_NO_RW_TIMEOUT), INVALID_STREAM_ID); -#ifndef __DEVICE__ XLinkError_t eventStatus = checkEventHeader(event.header); if (eventStatus != X_LINK_SUCCESS) { mvLog(MVLOG_ERROR, "Got wrong package from device, error code = %s", XLinkErrorToStr(eventStatus)); @@ -80,22 +82,19 @@ streamId_t XLinkOpenStream(linkId_t id, const char* name, int stream_write_size) return INVALID_STREAM_ID; } } -#endif } streamId_t streamId = getStreamIdByName(link, name); -#ifndef __DEVICE__ if (streamId > 0x0FFFFFFF) { mvLog(MVLOG_ERROR, "Cannot find stream id by the \"%s\" name", name); mvLog(MVLOG_ERROR,"Max streamId reached!"); return INVALID_STREAM_ID; } -#else - if (streamId == INVALID_STREAM_ID) { - mvLog(MVLOG_ERROR,"Max streamId reached %x!", streamId); - return INVALID_STREAM_ID; - } -#endif + // TODO(themarpe) - server side + // if (streamId == INVALID_STREAM_ID) { + // mvLog(MVLOG_ERROR,"Max streamId reached %x!", streamId); + // return INVALID_STREAM_ID; + // } COMBINE_IDS(streamId, id); return streamId; @@ -118,8 +117,28 @@ XLinkError_t XLinkCloseStream(streamId_t const streamId) return X_LINK_SUCCESS; } -XLinkError_t XLinkWriteData(streamId_t const streamId, const uint8_t* buffer, - int size) +XLinkError_t XLinkGateWrite(const char *name, void *data, int size, int timeout) +{ + int rc = XLinkPlatformGateWrite(name, data, size, timeout); + if(rc < 0) { + return X_LINK_ERROR; + } else { + return X_LINK_SUCCESS; + } +} + +XLinkError_t XLinkGateRead(const char *name, void *data, int size, int timeout) +{ + int rc = XLinkPlatformGateRead(name, data, size, timeout); + if(rc < 0) { + return X_LINK_ERROR; + } else { + return X_LINK_SUCCESS; + } +} + +XLinkError_t XLinkWriteData_(streamId_t streamId, const uint8_t* buffer, + int size, XLinkTimespec* outTSend) { XLINK_RET_IF(buffer == NULL); @@ -132,9 +151,58 @@ XLinkError_t XLinkWriteData(streamId_t const streamId, const uint8_t* buffer, XLINK_INIT_EVENT(event, streamIdOnly, XLINK_WRITE_REQ, size,(void*)buffer, link->deviceHandle); - XLINK_RET_IF(addEventWithPerf(&event, &opTime, XLINK_NO_RW_TIMEOUT)); + XLINK_RET_IF(addEventWithPerf_(&event, &opTime, XLINK_NO_RW_TIMEOUT, outTSend)); + + if( glHandler->profEnable) { + glHandler->profilingData.totalWriteBytes += size; + glHandler->profilingData.totalWriteTime += opTime; + } + link->profilingData.totalWriteBytes += size; + link->profilingData.totalWriteTime += size; + + return X_LINK_SUCCESS; +} + +XLinkError_t XLinkWriteFd(streamId_t const streamId, const long fd) +{ + return XLinkWriteFd_(streamId, fd, NULL); +} + +XLinkError_t XLinkWriteFd_(streamId_t streamId, const long fd, XLinkTimespec* outTSend) +{ + float opTime = 0.0f; + xLinkDesc_t* link = NULL; + XLINK_RET_IF(getLinkByStreamId(streamId, &link)); + streamId_t streamIdOnly = EXTRACT_STREAM_ID(streamId); - if (glHandler->profEnable) { + xLinkEvent_t event = {0}; + XLINK_INIT_EVENT(event, streamIdOnly, XLINK_WRITE_FD_REQ, + sizeof(long),(void*)fd, link->deviceHandle); + + event.data2 = (void*)NULL; + event.data2Size = -1; + + int size = sizeof(long); +#if defined(__unix__) + if (event.deviceHandle.protocol != X_LINK_LOCAL_SHDMEM && + event.header.type == XLINK_WRITE_FD_REQ) { + + if (fd >= 0) { + // Determine file size through fstat + struct stat fileStats; + fstat(fd, &fileStats); + size = fileStats.st_size; + + if (size > 0) { + event.header.size = size; + } + } + } +#endif + + XLINK_RET_IF(addEventWithPerf_(&event, &opTime, XLINK_NO_RW_TIMEOUT, outTSend)); + + if( glHandler->profEnable) { glHandler->profilingData.totalWriteBytes += size; glHandler->profilingData.totalWriteTime += opTime; } @@ -144,6 +212,82 @@ XLinkError_t XLinkWriteData(streamId_t const streamId, const uint8_t* buffer, return X_LINK_SUCCESS; } +XLinkError_t XLinkWriteFdData(streamId_t streamId, const long fd, const uint8_t* dataBuffer, int dataSize) +{ + ASSERT_XLINK(dataBuffer); + + float opTime = 0; + xLinkDesc_t* link = NULL; + XLINK_RET_IF(getLinkByStreamId(streamId, &link)); + streamId = EXTRACT_STREAM_ID(streamId); + + int totalSize = dataSize; + xLinkEvent_t event = {0}; + XLINK_INIT_EVENT(event, streamId, XLINK_WRITE_FD_REQ, totalSize, (void*)fd, link->deviceHandle); + + event.data2 = (void*)dataBuffer; + event.data2Size = dataSize; + +#if defined(__unix__) + if (event.deviceHandle.protocol != X_LINK_LOCAL_SHDMEM && + event.header.type == XLINK_WRITE_FD_REQ) { + + if (fd >= 0) { + // Determine file size through fstat + struct stat fileStats; + fstat(fd, &fileStats); + int size = fileStats.st_size; + + if (size > 0) { + event.header.size += size; + totalSize += size; + } + } + } +#endif + + XLINK_RET_IF(addEventWithPerf(&event, &opTime, XLINK_NO_RW_TIMEOUT)); + + if( glHandler->profEnable) { + glHandler->profilingData.totalWriteBytes += totalSize; + glHandler->profilingData.totalWriteTime += opTime; + } + + return X_LINK_SUCCESS; +} + +XLinkError_t XLinkWriteData(streamId_t const streamId, const uint8_t* buffer, + int size) +{ + return XLinkWriteData_(streamId, buffer, size, NULL); +} + +XLinkError_t XLinkWriteData2(streamId_t streamId, const uint8_t* buffer1, int buffer1Size, const uint8_t* buffer2, int buffer2Size) +{ + ASSERT_XLINK(buffer1); + ASSERT_XLINK(buffer2); + + float opTime = 0; + xLinkDesc_t* link = NULL; + XLINK_RET_IF(getLinkByStreamId(streamId, &link)); + streamId = EXTRACT_STREAM_ID(streamId); + + int totalSize = buffer1Size + buffer2Size; + xLinkEvent_t event = {0}; + XLINK_INIT_EVENT(event, streamId, XLINK_WRITE_REQ, totalSize,(void*)buffer1, link->deviceHandle); + event.data2 = (void*)buffer2; + event.data2Size = buffer2Size; + + XLINK_RET_IF(addEventWithPerf(&event, &opTime, XLINK_NO_RW_TIMEOUT)); + + if( glHandler->profEnable) { + glHandler->profilingData.totalWriteBytes += totalSize; + glHandler->profilingData.totalWriteTime += opTime; + } + + return X_LINK_SUCCESS; +} + XLinkError_t XLinkReadData(streamId_t const streamId, streamPacketDesc_t** packet) { XLINK_RET_IF(packet == NULL); @@ -417,11 +561,11 @@ float timespec_diff(struct timespec *start, struct timespec *stop) return start->tv_nsec/ 1000000000.0f + start->tv_sec; } -XLinkError_t addEvent(xLinkEvent_t *event, unsigned int timeoutMs) +XLinkError_t addEvent_(xLinkEvent_t *event, unsigned int timeoutMs, XLinkTimespec* outTime) { ASSERT_XLINK(event); - xLinkEvent_t* ev = DispatcherAddEvent(EVENT_LOCAL, event); + xLinkEvent_t* ev = DispatcherAddEvent_(EVENT_LOCAL, event, outTime); if(ev == NULL) { mvLog(MVLOG_ERROR, "Dispatcher failed on adding event. type: %s, id: %d, stream name: %s\n", TypeToStr(event->header.type), event->header.id, event->header.streamName); @@ -459,28 +603,35 @@ XLinkError_t addEvent(xLinkEvent_t *event, unsigned int timeoutMs) return X_LINK_TIMEOUT; } } - XLINK_RET_ERR_IF( event->header.flags.bitField.ack != 1, X_LINK_COMMUNICATION_FAIL); return X_LINK_SUCCESS; } +XLinkError_t addEvent(xLinkEvent_t *event, unsigned int timeoutMs) +{ + return addEvent_(event, timeoutMs, NULL); +} -XLinkError_t addEventWithPerf(xLinkEvent_t *event, float* opTime, unsigned int timeoutMs) +XLinkError_t addEventWithPerf_(xLinkEvent_t *event, float* opTime, unsigned int timeoutMs, XLinkTimespec* outTime) { ASSERT_XLINK(opTime); struct timespec start, end; clock_gettime(CLOCK_REALTIME, &start); - XLINK_RET_IF_FAIL(addEvent(event, timeoutMs)); + XLINK_RET_IF_FAIL(addEvent_(event, timeoutMs, outTime)); clock_gettime(CLOCK_REALTIME, &end); *opTime = timespec_diff(&start, &end); return X_LINK_SUCCESS; } +XLinkError_t addEventWithPerf(xLinkEvent_t *event, float* opTime, unsigned int timeoutMs) +{ + return addEventWithPerf_(event, opTime, timeoutMs, NULL); +} XLinkError_t addEventTimeout(xLinkEvent_t *event, struct timespec abstime) { diff --git a/src/shared/XLinkDevice.c b/src/shared/XLinkDevice.c index f28683cf..dcf5c640 100644 --- a/src/shared/XLinkDevice.c +++ b/src/shared/XLinkDevice.c @@ -3,6 +3,7 @@ // #include +#include "XLinkPublicDefines.h" #include "stdio.h" #include "stdint.h" #include "string.h" @@ -28,6 +29,8 @@ #include "win_time.h" #endif +#include "tcpip_host.h" + // ------------------------------------ // Global fields. Begin. // ------------------------------------ @@ -56,13 +59,8 @@ static uint32_t init_once = 0; static linkId_t getNextAvailableLinkUniqueId(); static xLinkDesc_t* getNextAvailableLink(); static void freeGivenLink(xLinkDesc_t* link); - -#ifndef __DEVICE__ - static XLinkError_t parsePlatformError(xLinkPlatformErrorCode_t rc); -#endif // __DEVICE__ - // ------------------------------------ // Helpers declaration. End. // ------------------------------------ @@ -82,11 +80,6 @@ XLinkError_t XLinkInitialize(XLinkGlobalHandler_t* globalHandler) return X_LINK_SUCCESS; } -#ifdef __DEVICE__ - mvLogLevelSet(MVLOG_FATAL); - mvLogDefaultLevelSet(MVLOG_FATAL); -#endif - ASSERT_XLINK(XLINK_MAX_STREAMS <= MAX_POOLS_ALLOC); glHandler = globalHandler; if (sem_init(&pingSem,0,0)) { @@ -94,7 +87,7 @@ XLinkError_t XLinkInitialize(XLinkGlobalHandler_t* globalHandler) } int i; - xLinkPlatformErrorCode_t init_status = XLinkPlatformInit(globalHandler->options); + xLinkPlatformErrorCode_t init_status = XLinkPlatformInit(globalHandler); if (init_status != X_LINK_PLATFORM_SUCCESS) { pthread_mutex_unlock(&init_mutex); return parsePlatformError(init_status); @@ -140,44 +133,89 @@ XLinkError_t XLinkInitialize(XLinkGlobalHandler_t* globalHandler) link->availableStreams[stream].id = INVALID_STREAM_ID; } -#ifdef __DEVICE__ - link = getNextAvailableLink(); - if (link == NULL) { - pthread_mutex_unlock(&init_mutex); - return X_LINK_COMMUNICATION_NOT_OPEN; + init_once = 1; + int status = pthread_mutex_unlock(&init_mutex); + if(status){ + // rare and unstable scenario; xlink is technically initialized yet mutex unlock failed + return X_LINK_ERROR; } - link->peerState = XLINK_UP; - link->deviceHandle.xLinkFD = NULL; - link->deviceHandle.protocol = globalHandler->protocol; - xLinkDeviceHandle_t temp = {0}; - temp.protocol = globalHandler->protocol; - { - int rc; - if (rc = DispatcherStart(&temp)) { //myriad has one - mvLog(MVLOG_ERROR, " DispatcherStart(&temp) method call failed with an error: %d", rc); - pthread_mutex_unlock(&init_mutex); - return rc; - } + return X_LINK_SUCCESS; +} + +void XLinkDiscoveryServiceSetCallbackReset(void (*cb)()) { + tcpip_set_discovery_service_reset_callback(cb); +} +XLinkError_t XLinkDiscoveryServiceStart(const char* deviceId, XLinkDeviceState_t state, XLinkPlatform_t platform) { + return parsePlatformError(tcpip_start_discovery_service(deviceId, state, platform)); +} +bool XLinkDiscoveryServiceIsRunning() { + return tcpip_is_running_discovery_service(); +} +void XLinkDiscoveryServiceStop() { + tcpip_stop_discovery_service(); +} +void XLinkDiscoveryServiceDetach() { + tcpip_detach_discovery_service(); +} + +XLinkError_t XLinkServer(XLinkHandler_t* handler, const char* deviceId, XLinkDeviceState_t state, XLinkPlatform_t platform) { + // Start discovery + XLinkError_t ret = XLinkDiscoveryServiceStart(deviceId, state, platform); + if(ret != X_LINK_SUCCESS) { + return ret; } - while(((sem_wait(&pingSem) == -1) && errno == EINTR) - continue; + // Detach discovery + XLinkDiscoveryServiceDetach(); -#endif + // Start server and return + return XLinkServerOnly(handler); +} - init_once = 1; - int status = pthread_mutex_unlock(&init_mutex); - if(status){ - // rare and unstable scenario; xlink is technically initialized yet mutex unlock failed + +XLinkError_t XLinkServerOnly(XLinkHandler_t* handler) +{ + XLINK_RET_IF(handler == NULL); + if (strnlen(handler->devicePath, MAX_PATH_LENGTH) < 2) { + mvLog(MVLOG_ERROR, "Device path is incorrect"); return X_LINK_ERROR; } + xLinkDesc_t* link = getNextAvailableLink(); + XLINK_RET_IF(link == NULL); + mvLog(MVLOG_DEBUG,"%s() device name %s glHandler %p protocol %d\n", __func__, handler->devicePath, glHandler, handler->protocol); + + link->deviceHandle.protocol = handler->protocol; + int connectStatus = XLinkPlatformServer(handler->devicePath2, handler->devicePath, + &link->deviceHandle.protocol, &link->deviceHandle.xLinkFD); + + if (connectStatus < 0) { + /** + * Connection may be unsuccessful at some amount of first tries. + * In this case, asserting the status provides enormous amount of logs in tests. + */ + + // Free used link + freeGivenLink(link); + + // Return an informative error + return parsePlatformError(connectStatus); + } + + XLINK_RET_ERR_IF( + DispatcherStartServer(link) != X_LINK_SUCCESS, X_LINK_TIMEOUT); + + // Wait till client pings + while(((sem_wait(&pingSem) == -1) && errno == EINTR)) + continue; + + link->peerState = XLINK_UP; + link->hostClosedFD = 0; + handler->linkId = link->id; return X_LINK_SUCCESS; } -#ifndef __DEVICE__ - int XLinkIsDescriptionValid(const deviceDesc_t *in_deviceDesc, const XLinkDeviceState_t state) { return XLinkPlatformIsDescriptionValid(in_deviceDesc, state); } @@ -188,7 +226,7 @@ XLinkError_t XLinkFindFirstSuitableDevice(const deviceDesc_t in_deviceRequiremen xLinkPlatformErrorCode_t rc; unsigned numFoundDevices = 0; - rc = XLinkPlatformFindDevices(in_deviceRequirements, out_foundDevice, 1, &numFoundDevices); + rc = XLinkPlatformFindDevices(in_deviceRequirements, out_foundDevice, 1, &numFoundDevices, XLINK_DEVICE_DEFAULT_SEARCH_TIMEOUT_MS); if(numFoundDevices <= 0){ return X_LINK_DEVICE_NOT_FOUND; } @@ -198,13 +236,14 @@ XLinkError_t XLinkFindFirstSuitableDevice(const deviceDesc_t in_deviceRequiremen XLinkError_t XLinkFindAllSuitableDevices(const deviceDesc_t in_deviceRequirements, deviceDesc_t *out_foundDevicesPtr, const unsigned int devicesArraySize, - unsigned int* out_foundDevicesCount) { + unsigned int* out_foundDevicesCount, + int timeoutMs) { XLINK_RET_IF(out_foundDevicesPtr == NULL); XLINK_RET_IF(devicesArraySize <= 0); XLINK_RET_IF(out_foundDevicesCount == NULL); xLinkPlatformErrorCode_t rc; - rc = XLinkPlatformFindDevices(in_deviceRequirements, out_foundDevicesPtr, devicesArraySize, out_foundDevicesCount); + rc = XLinkPlatformFindDevices(in_deviceRequirements, out_foundDevicesPtr, devicesArraySize, out_foundDevicesCount, timeoutMs); return parsePlatformError(rc); } @@ -239,8 +278,8 @@ XLinkError_t XLinkConnect(XLinkHandler_t* handler) link->deviceHandle.protocol = handler->protocol; int connectStatus = XLinkPlatformConnect(handler->devicePath2, handler->devicePath, - link->deviceHandle.protocol, &link->deviceHandle.xLinkFD); - + &link->deviceHandle.protocol, &link->deviceHandle.xLinkFD); + if (connectStatus < 0) { /** * Connection may be unsuccessful at some amount of first tries. @@ -253,9 +292,9 @@ XLinkError_t XLinkConnect(XLinkHandler_t* handler) // Return an informative error return parsePlatformError(connectStatus); } - + XLINK_RET_ERR_IF( - DispatcherStart(&link->deviceHandle) != X_LINK_SUCCESS, X_LINK_TIMEOUT); + DispatcherStart(link) != X_LINK_SUCCESS, X_LINK_TIMEOUT); xLinkEvent_t event = {0}; @@ -269,14 +308,6 @@ XLinkError_t XLinkConnect(XLinkHandler_t* handler) } link->peerState = XLINK_UP; - #if (!defined(_WIN32) && !defined(_WIN64) ) - link->usbConnSpeed = get_usb_speed(); - mv_strcpy(link->mxSerialId, XLINK_MAX_MX_ID_SIZE, get_mx_serial()); - #else - link->usbConnSpeed = X_LINK_USB_SPEED_UNKNOWN; - mv_strcpy(link->mxSerialId, XLINK_MAX_MX_ID_SIZE, "UNKNOWN"); - #endif - link->hostClosedFD = 0; handler->linkId = link->id; return X_LINK_SUCCESS; @@ -401,9 +432,6 @@ XLinkError_t XLinkResetRemoteTimeout(linkId_t id, int timeoutMs) XLinkError_t XLinkResetAll() { -#if defined(NO_BOOT) - mvLog(MVLOG_INFO, "Devices will not be restarted for this configuration (NO_BOOT)"); -#else int i; for (i = 0; i < MAX_LINKS; i++) { if (availableXLinks[i].id != INVALID_LINK_ID) { @@ -425,12 +453,9 @@ XLinkError_t XLinkResetAll() } } } -#endif return X_LINK_SUCCESS; } -#endif // __DEVICE__ - XLinkError_t XLinkProfStart() { XLINK_RET_IF(glHandler == NULL); @@ -521,7 +546,7 @@ const char* XLinkGetMxSerial(linkId_t id){ // ------------------------------------ // Used only by getNextAvailableLink -static linkId_t getNextAvailableLinkUniqueId() +linkId_t getNextAvailableLinkUniqueId() { linkId_t start = nextUniqueLinkId; do @@ -553,7 +578,7 @@ static linkId_t getNextAvailableLinkUniqueId() return INVALID_LINK_ID; } -static xLinkDesc_t* getNextAvailableLink() { +xLinkDesc_t* getNextAvailableLink() { XLINK_RET_ERR_IF(pthread_mutex_lock(&availableXLinksMutex) != 0, NULL); @@ -590,7 +615,7 @@ static xLinkDesc_t* getNextAvailableLink() { return link; } -static void freeGivenLink(xLinkDesc_t* link) { +void freeGivenLink(xLinkDesc_t* link) { if(pthread_mutex_lock(&availableXLinksMutex) != 0){ mvLog(MVLOG_ERROR, "Cannot lock mutex\n"); @@ -606,9 +631,7 @@ static void freeGivenLink(xLinkDesc_t* link) { } -#ifndef __DEVICE__ - -static XLinkError_t parsePlatformError(xLinkPlatformErrorCode_t rc) { +XLinkError_t parsePlatformError(xLinkPlatformErrorCode_t rc) { switch (rc) { case X_LINK_PLATFORM_SUCCESS: return X_LINK_SUCCESS; @@ -621,11 +644,15 @@ static XLinkError_t parsePlatformError(xLinkPlatformErrorCode_t rc) { case X_LINK_PLATFORM_DEVICE_BUSY: return X_LINK_DEVICE_ALREADY_IN_USE; case X_LINK_PLATFORM_USB_DRIVER_NOT_LOADED: + case X_LINK_PLATFORM_USB_EP_DRIVER_NOT_LOADED: return X_LINK_INIT_USB_ERROR; case X_LINK_PLATFORM_TCP_IP_DRIVER_NOT_LOADED: return X_LINK_INIT_TCP_IP_ERROR; + case X_LINK_PLATFORM_LOCAL_SHDMEM_DRIVER_NOT_LOADED: + return X_LINK_INIT_LOCAL_SHDMEM_ERROR; case X_LINK_PLATFORM_PCIE_DRIVER_NOT_LOADED: return X_LINK_INIT_PCIE_ERROR; + case X_LINK_PLATFORM_TCP_IP_OR_LOCAL_SHDMEM_DRIVER_NOT_LOADED: case X_LINK_PLATFORM_ERROR: case X_LINK_PLATFORM_INVALID_PARAMETERS: default: @@ -633,8 +660,6 @@ static XLinkError_t parsePlatformError(xLinkPlatformErrorCode_t rc) { } } -#endif // __DEVICE__ - /** * @brief Returns enum string value * @return Pointer to null terminated string @@ -655,6 +680,8 @@ const char* XLinkErrorToStr(XLinkError_t val) { case X_LINK_NOT_IMPLEMENTED: return "X_LINK_NOT_IMPLEMENTED"; case X_LINK_INIT_USB_ERROR: return "X_LINK_INIT_USB_ERROR"; case X_LINK_INIT_TCP_IP_ERROR: return "X_LINK_INIT_TCP_IP_ERROR"; + case X_LINK_INIT_LOCAL_SHDMEM_ERROR: return "X_LINK_INIT_LOCAL_SHDMEM_ERROR"; + case X_LINK_INIT_TCP_IP_OR_LOCAL_SHDMEM_ERROR: return "X_LINK_INIT_TCP_IP_OR_LOCAL_SHDMEM_ERROR"; case X_LINK_INIT_PCIE_ERROR: return "X_LINK_INIT_PCIE_ERROR"; default: return "INVALID_ENUM_VALUE"; @@ -673,6 +700,9 @@ const char* XLinkProtocolToStr(XLinkProtocol_t val) { case X_LINK_PCIE: return "X_LINK_PCIE"; case X_LINK_IPC: return "X_LINK_IPC"; case X_LINK_TCP_IP: return "X_LINK_TCP_IP"; + case X_LINK_LOCAL_SHDMEM: return "X_LINK_LOCAL_SHDMEM"; + case X_LINK_TCP_IP_OR_LOCAL_SHDMEM: return "X_LINK_TCP_IP_OR_LOCAL_SHDMEM"; + case X_LINK_USB_EP: return "X_LINK_USB_EP"; case X_LINK_NMB_OF_PROTOCOLS: return "X_LINK_NMB_OF_PROTOCOLS"; case X_LINK_ANY_PROTOCOL: return "X_LINK_ANY_PROTOCOL"; default: @@ -690,6 +720,8 @@ const char* XLinkPlatformToStr(XLinkPlatform_t val) { case X_LINK_ANY_PLATFORM: return "X_LINK_ANY_PLATFORM"; case X_LINK_MYRIAD_2: return "X_LINK_MYRIAD_2"; case X_LINK_MYRIAD_X: return "X_LINK_MYRIAD_X"; + case X_LINK_RVC3: return "X_LINK_RVC3"; + case X_LINK_RVC4: return "X_LINK_RVC4"; default: return "INVALID_ENUM_VALUE"; break; @@ -707,6 +739,9 @@ const char* XLinkDeviceStateToStr(XLinkDeviceState_t val) { case X_LINK_UNBOOTED: return "X_LINK_UNBOOTED"; case X_LINK_BOOTLOADER: return "X_LINK_BOOTLOADER"; case X_LINK_BOOTED_NON_EXCLUSIVE: return "X_LINK_BOOTED_NON_EXCLUSIVE"; + case X_LINK_GATE: return "X_LINK_GATE"; + case X_LINK_GATE_BOOTED: return "X_LINK_GATE_BOOTED"; + case X_LINK_GATE_SETUP: return "X_LINK_GATE_SETUP"; default: return "INVALID_ENUM_VALUE"; break; @@ -723,12 +758,10 @@ const char* XLinkPCIEBootloaderToStr(XLinkPCIEBootloader val) { case X_LINK_PCIE_UNKNOWN_BOOTLOADER: return "X_LINK_PCIE_UNKNOWN_BOOTLOADER"; case X_LINK_PCIE_SIMPLIFIED_BOOTLOADER: return "X_LINK_PCIE_SIMPLIFIED_BOOTLOADER"; case X_LINK_PCIE_UNIFIED_BOOTLOADER: return "X_LINK_PCIE_UNIFIED_BOOTLOADER"; - default: - return "INVALID_ENUM_VALUE"; - break; } + return "INVALID_ENUM_VALUE"; } // ------------------------------------ // Helpers implementation. End. -// ------------------------------------ \ No newline at end of file +// ------------------------------------ diff --git a/src/shared/XLinkDispatcher.c b/src/shared/XLinkDispatcher.c index 2e3e402c..95e066f1 100644 --- a/src/shared/XLinkDispatcher.c +++ b/src/shared/XLinkDispatcher.c @@ -9,6 +9,7 @@ /// #ifndef _GNU_SOURCE #define _GNU_SOURCE // fix for warning: implicit declaration of function 'pthread_setname_np' +#include "XLinkTime.h" #endif #include @@ -58,6 +59,7 @@ typedef struct xLinkEventPriv_t { xLinkEvent_t *retEv; xLinkEventState_t isServed; xLinkEventOrigin_t origin; + XLinkTimespec* sendTime; XLink_sem_t* sem; void* data; } xLinkEventPriv_t; @@ -99,6 +101,7 @@ typedef struct { uint32_t dispatcherLinkDown; uint32_t dispatcherDeviceFdDown; + uint32_t server; } xLinkSchedulerState_t; @@ -164,6 +167,11 @@ static xLinkEventPriv_t* getNextQueueElemToProc(eventQueueHandler_t *q ); static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, eventQueueHandler_t *q, xLinkEvent_t* event, XLink_sem_t* sem, xLinkEventOrigin_t o); +static xLinkEvent_t* addNextQueueElemToProc_(xLinkSchedulerState_t* curr, + eventQueueHandler_t *q, xLinkEvent_t* event, + XLink_sem_t* sem, xLinkEventOrigin_t o, + XLinkTimespec* outTime); + static xLinkEventPriv_t* dispatcherGetNextEvent(xLinkSchedulerState_t* curr); @@ -210,12 +218,22 @@ XLinkError_t DispatcherInitialize(DispatcherControlFunctions *controlFunc) { return X_LINK_SUCCESS; } -XLinkError_t DispatcherStart(xLinkDeviceHandle_t *deviceHandle) +XLinkError_t DispatcherStart(xLinkDesc_t *link) { + return DispatcherStartImpl(link, false); +} +XLinkError_t DispatcherStartServer(xLinkDesc_t *link) { + return DispatcherStartImpl(link, true); +} + +typedef struct { + int schedulerId; + linkId_t linkId; +} eventSchedulerContext; + +XLinkError_t DispatcherStartImpl(xLinkDesc_t *link, bool server) { - ASSERT_XLINK(deviceHandle); -#ifndef __DEVICE__ - ASSERT_XLINK(deviceHandle->xLinkFD != NULL); -#endif + ASSERT_XLINK(link); + ASSERT_XLINK(link->deviceHandle.xLinkFD != NULL); pthread_attr_t attr; int eventIdx; @@ -238,8 +256,9 @@ XLinkError_t DispatcherStart(xLinkDeviceHandle_t *deviceHandle) schedulerState[idx].resetXLink = 0; schedulerState[idx].dispatcherLinkDown = 0; schedulerState[idx].dispatcherDeviceFdDown = 0; + schedulerState[idx].server = server; - schedulerState[idx].deviceHandle = *deviceHandle; + schedulerState[idx].deviceHandle = link->deviceHandle; schedulerState[idx].schedulerId = idx; schedulerState[idx].lQueue.cur = schedulerState[idx].lQueue.q; @@ -279,41 +298,24 @@ XLinkError_t DispatcherStart(xLinkDeviceHandle_t *deviceHandle) return X_LINK_ERROR; } -#ifdef __DEVICE__ - if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0) { - mvLog(MVLOG_ERROR,"pthread_attr_setinheritsched error"); - pthread_attr_destroy(&attr); - } - if (pthread_attr_setschedpolicy(&attr, SCHED_RR) != 0) { - mvLog(MVLOG_ERROR,"pthread_attr_setschedpolicy error"); - pthread_attr_destroy(&attr); - } -#ifdef XLINK_THREADS_PRIORITY - struct sched_param param; - if (pthread_attr_getschedparam(&attr, ¶m) != 0) { - mvLog(MVLOG_ERROR,"pthread_attr_getschedparam error"); - pthread_attr_destroy(&attr); - } - param.sched_priority = XLINK_THREADS_PRIORITY; - if (pthread_attr_setschedparam(&attr, ¶m) != 0) { - mvLog(MVLOG_ERROR,"pthread_attr_setschedparam error"); - pthread_attr_destroy(&attr); - } -#endif -#endif - while(((sem_wait(&addSchedulerSem) == -1) && errno == EINTR)) continue; mvLog(MVLOG_DEBUG,"%s() starting a new thread - schedulerId %d \n", __func__, idx); + + eventSchedulerContext* ctx = malloc(sizeof(eventSchedulerContext)); + ASSERT_XLINK(ctx); + ctx->schedulerId = idx; + ctx->linkId = link->id; int sc = pthread_create(&schedulerState[idx].xLinkThreadId, &attr, eventSchedulerRun, - (void*)&schedulerState[idx].schedulerId); + (void*)ctx); if (sc) { mvLog(MVLOG_ERROR,"Thread creation failed with error: %d", sc); if (pthread_attr_destroy(&attr) != 0) { perror("Thread attr destroy failed\n"); } + free(ctx); return X_LINK_ERROR; } @@ -356,7 +358,7 @@ int DispatcherDeviceFdDown(xLinkDeviceHandle_t *deviceHandle){ return dispatcherDeviceFdDown(curr); } -xLinkEvent_t* DispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event) +xLinkEvent_t* DispatcherAddEvent_(xLinkEventOrigin_t origin, xLinkEvent_t *event, XLinkTimespec* outTime) { xLinkSchedulerState_t* curr = findCorrespondingScheduler(event->deviceHandle.xLinkFD); XLINK_RET_ERR_IF(curr == NULL, NULL); @@ -364,6 +366,7 @@ xLinkEvent_t* DispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event) if(curr->resetXLink) { return NULL; } + mvLog(MVLOG_DEBUG, "Receiving event %s %d\n", TypeToStr(event->header.type), origin); int rc; while(((rc = XLink_sem_wait(&curr->addEventSem)) == -1) && errno == EINTR) @@ -392,9 +395,9 @@ xLinkEvent_t* DispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event) const uint32_t tmpMoveSem = event->header.flags.bitField.moveSemantic; event->header.flags.raw = 0; event->header.flags.bitField.moveSemantic = tmpMoveSem; - ev = addNextQueueElemToProc(curr, &curr->lQueue, event, sem, origin); + ev = addNextQueueElemToProc_(curr, &curr->lQueue, event, sem, origin, outTime); } else { - ev = addNextQueueElemToProc(curr, &curr->rQueue, event, NULL, origin); + ev = addNextQueueElemToProc_(curr, &curr->rQueue, event, NULL, origin, outTime); } if (XLink_sem_post(&curr->addEventSem)) { mvLog(MVLOG_ERROR,"can't post semaphore\n"); @@ -404,6 +407,10 @@ xLinkEvent_t* DispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event) } return ev; } +xLinkEvent_t* DispatcherAddEvent(xLinkEventOrigin_t origin, xLinkEvent_t *event) +{ + return DispatcherAddEvent_(origin, event, NULL); +} int DispatcherWaitEventComplete(xLinkDeviceHandle_t *deviceHandle, unsigned int timeoutMs) { @@ -435,24 +442,23 @@ int DispatcherWaitEventComplete(xLinkDeviceHandle_t *deviceHandle, unsigned int while(((rc = XLink_sem_wait(id)) == -1) && errno == EINTR) continue; } -#ifndef __DEVICE__ - if (rc) { - xLinkEvent_t event = {0}; - event.header.type = XLINK_RESET_REQ; - event.deviceHandle = *deviceHandle; - mvLog(MVLOG_ERROR,"waiting is timeout, sending reset remote event"); - DispatcherAddEvent(EVENT_LOCAL, &event); - id = getSem(pthread_self(), curr); - int rc; - while(((rc = XLink_sem_wait(id)) == -1) && errno == EINTR) - continue; - if (id == NULL || rc) { - // Calling non-thread safe dispatcherReset from external thread - // TODO - investigate further and resolve - dispatcherReset(curr); - } + + if (!curr->server && rc) { + xLinkEvent_t event = {0}; + event.header.type = XLINK_RESET_REQ; + event.deviceHandle = *deviceHandle; + mvLog(MVLOG_ERROR,"waiting is timeout, sending reset remote event"); + DispatcherAddEvent(EVENT_LOCAL, &event); + id = getSem(pthread_self(), curr); + int rc; + while(((rc = XLink_sem_wait(id)) == -1) && errno == EINTR) + continue; + if (id == NULL || rc) { + // Calling non-thread safe dispatcherReset from external thread + // TODO - investigate further and resolve + dispatcherReset(curr); } -#endif + } return rc; } @@ -470,8 +476,7 @@ int DispatcherWaitEventCompleteTimeout(xLinkDeviceHandle_t *deviceHandle, struct int rc = XLink_sem_timedwait(id, &abstime); int err = errno; -#ifndef __DEVICE__ - if (rc) { + if (curr->server && rc) { if(err == ETIMEDOUT){ return X_LINK_TIMEOUT; } else { @@ -488,7 +493,6 @@ int DispatcherWaitEventCompleteTimeout(xLinkDeviceHandle_t *deviceHandle, struct } } } -#endif return rc; } @@ -501,21 +505,26 @@ char* TypeToStr(int type) case XLINK_WRITE_REQ: return "XLINK_WRITE_REQ"; case XLINK_READ_REQ: return "XLINK_READ_REQ"; case XLINK_READ_REL_REQ: return "XLINK_READ_REL_REQ"; - case XLINK_READ_REL_SPEC_REQ: return "XLINK_READ_REL_SPEC_REQ"; case XLINK_CREATE_STREAM_REQ:return "XLINK_CREATE_STREAM_REQ"; case XLINK_CLOSE_STREAM_REQ: return "XLINK_CLOSE_STREAM_REQ"; case XLINK_PING_REQ: return "XLINK_PING_REQ"; case XLINK_RESET_REQ: return "XLINK_RESET_REQ"; - case XLINK_REQUEST_LAST: return "XLINK_REQUEST_LAST"; - case XLINK_WRITE_RESP: return "XLINK_WRITE_RESP"; + case XLINK_STATIC_REQUEST_LAST: return "XLINK_STATIC_REQUEST_LAST"; case XLINK_READ_RESP: return "XLINK_READ_RESP"; + case XLINK_WRITE_RESP: return "XLINK_WRITE_RESP"; case XLINK_READ_REL_RESP: return "XLINK_READ_REL_RESP"; - case XLINK_READ_REL_SPEC_RESP: return "XLINK_READ_REL_SPEC_RESP"; case XLINK_CREATE_STREAM_RESP: return "XLINK_CREATE_STREAM_RESP"; case XLINK_CLOSE_STREAM_RESP: return "XLINK_CLOSE_STREAM_RESP"; case XLINK_PING_RESP: return "XLINK_PING_RESP"; case XLINK_RESET_RESP: return "XLINK_RESET_RESP"; + case XLINK_STATIC_RESP_LAST: return "XLINK_STATIC_RESP_LAST"; + case XLINK_READ_REL_SPEC_REQ: return "XLINK_READ_REL_SPEC_REQ"; + case XLINK_WRITE_FD_REQ: return "XLINK_WRITE_FD_REQ"; + case XLINK_REQUEST_LAST: return "XLINK_REQUEST_LAST"; + case XLINK_READ_REL_SPEC_RESP: return "XLINK_READ_REL_SPEC_RESP"; + case XLINK_WRITE_FD_RESP: return "XLINK_WRITE_FD_REQ"; case XLINK_RESP_LAST: return "XLINK_RESP_LAST"; + default: break; } @@ -724,7 +733,10 @@ static void* __cdecl eventSchedulerRun(void* ctx) static void* eventSchedulerRun(void* ctx) #endif { - int schedulerId = *((int*) ctx); + eventSchedulerContext context = *((eventSchedulerContext*)ctx); + free(ctx); + + int schedulerId = context.schedulerId; mvLog(MVLOG_DEBUG,"%s() schedulerId %d\n", __func__, schedulerId); XLINK_RET_ERR_IF(schedulerId >= MAX_SCHEDULERS, NULL); @@ -738,30 +750,6 @@ static void* eventSchedulerRun(void* ctx) return NULL; } -#ifdef __DEVICE__ - if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) != 0) { - pthread_attr_destroy(&attr); - mvLog(MVLOG_ERROR,"pthread_attr_setinheritsched error"); - return NULL; - } - if (pthread_attr_setschedpolicy(&attr, SCHED_RR) != 0) { - pthread_attr_destroy(&attr); - mvLog(MVLOG_ERROR,"pthread_attr_setschedpolicy error"); - return NULL; - } -#ifdef XLINK_THREADS_PRIORITY - struct sched_param param; - if (pthread_attr_getschedparam(&attr, ¶m) != 0) { - mvLog(MVLOG_ERROR,"pthread_attr_getschedparam error"); - pthread_attr_destroy(&attr); - } - param.sched_priority = XLINK_THREADS_PRIORITY; - if (pthread_attr_setschedparam(&attr, ¶m) != 0) { - mvLog(MVLOG_ERROR,"pthread_attr_setschedparam error"); - pthread_attr_destroy(&attr); - } -#endif -#endif sc = pthread_create(&readerThreadId, &attr, eventReader, curr); if (sc) { mvLog(MVLOG_ERROR, "Thread creation failed"); @@ -790,6 +778,10 @@ static void* eventSchedulerRun(void* ctx) mvLog(MVLOG_ERROR, "Waiting for thread failed"); } + // Notify that the link went down + void XLinkPlatformLinkDownNotify(linkId_t linkId); + XLinkPlatformLinkDownNotify(context.linkId); + sc = pthread_attr_destroy(&attr); if (sc) { mvLog(MVLOG_WARN, "Thread attr destroy failed"); @@ -810,7 +802,9 @@ static void* eventSchedulerRun(void* ctx) static int isEventTypeRequest(xLinkEventPriv_t* event) { - return event->packet.header.type < XLINK_REQUEST_LAST; + return (event->packet.header.type < XLINK_STATIC_REQUEST_LAST || + (event->packet.header.type >= XLINK_READ_REL_SPEC_REQ && + event->packet.header.type < XLINK_REQUEST_LAST)); } static void postAndMarkEventServed(xLinkEventPriv_t *event) @@ -911,7 +905,8 @@ static int dispatcherResponseServe(xLinkEventPriv_t * event, xLinkSchedulerState if (curr->lQueue.q[i].isServed == EVENT_PENDING && header->id == evHeader->id && - header->type == evHeader->type - XLINK_REQUEST_LAST -1) + ((header->type == evHeader->type - XLINK_STATIC_REQUEST_LAST - 1) || + (header->type == evHeader->type - XLINK_REQUEST_LAST - 1 + XLINK_READ_REL_SPEC_REQ))) { mvLog(MVLOG_DEBUG,"----------------------ISserved %s\n", TypeToStr(header->type)); @@ -984,10 +979,10 @@ static xLinkEventPriv_t* getNextQueueElemToProc(eventQueueHandler_t *q ){ * @brief Add event to Queue * @note It called from dispatcherAddEvent */ -static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, +static xLinkEvent_t* addNextQueueElemToProc_(xLinkSchedulerState_t* curr, eventQueueHandler_t *q, xLinkEvent_t* event, - XLink_sem_t* sem, xLinkEventOrigin_t o) -{ + XLink_sem_t* sem, xLinkEventOrigin_t o, + XLinkTimespec* outTime) { xLinkEvent_t* ev; XLINK_RET_ERR_IF(pthread_mutex_lock(&(curr->queueMutex)) != 0, NULL); xLinkEventPriv_t* eventP = getNextElementWithState(q->base, q->end, q->cur, EVENT_SERVED); @@ -1005,8 +1000,10 @@ static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, if (o == EVENT_LOCAL) { // XLink API caller provided buffer for return the final result to eventP->retEv = event; + eventP->sendTime = outTime; }else{ eventP->retEv = NULL; + eventP->sendTime = NULL; } q->cur = eventP; eventP->isServed = EVENT_ALLOCATED; @@ -1014,6 +1011,12 @@ static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, XLINK_RET_ERR_IF(pthread_mutex_unlock(&(curr->queueMutex)) != 0, NULL); return ev; } +static xLinkEvent_t* addNextQueueElemToProc(xLinkSchedulerState_t* curr, + eventQueueHandler_t *q, xLinkEvent_t* event, + XLink_sem_t* sem, xLinkEventOrigin_t o) +{ + return addNextQueueElemToProc_(curr, q, event, sem, o, NULL); +} static xLinkEventPriv_t* dispatcherGetNextEvent(xLinkSchedulerState_t* curr) { @@ -1180,11 +1183,7 @@ static XLinkError_t sendEvents(xLinkSchedulerState_t* curr) { event = dispatcherGetNextEvent(curr); if(event == NULL) { mvLog(MVLOG_ERROR,"Dispatcher received NULL event!"); -#ifndef __DEVICE__ - break; //Mean that user reset XLink. -#else - continue; -#endif + break; // Means that user reset XLink. } if(event->packet.deviceHandle.xLinkFD @@ -1218,8 +1217,7 @@ static XLinkError_t sendEvents(xLinkSchedulerState_t* curr) { toSend = &response.packet; } - res = getResp(&event->packet, &response.packet); - + res = getResp(&event->packet, &response.packet, curr->server); if (isEventTypeRequest(event)) { XLINK_RET_ERR_IF(pthread_mutex_lock(&(curr->queueMutex)) != 0, X_LINK_ERROR); if (event->origin == EVENT_LOCAL) { //we need to do this for locals only @@ -1232,24 +1230,17 @@ static XLinkError_t sendEvents(xLinkSchedulerState_t* curr) { } if (res == 0 && event->packet.header.flags.bitField.localServe == 0) { -#ifndef __DEVICE__ - if (toSend->header.type == XLINK_RESET_REQ) { + if (!curr->server && toSend->header.type == XLINK_RESET_REQ) { curr->resetXLink = 1; mvLog(MVLOG_DEBUG,"Send XLINK_RESET_REQ, stopping sendEvents thread."); if(toSend->deviceHandle.protocol == X_LINK_PCIE) { toSend->header.type = XLINK_PING_REQ; mvLog(MVLOG_DEBUG, "Request for reboot not sent, only ping event"); - } else { -#if defined(NO_BOOT) - toSend->header.type = XLINK_PING_REQ; - mvLog(MVLOG_INFO, "Request for reboot not sent, only ping event"); -#endif // defined(NO_BOOT) - } } -#endif // __DEVICE__ + XLINK_RET_ERR_IF(pthread_mutex_unlock(&(curr->queueMutex)) != 0, X_LINK_ERROR); - if (glControlFunc->eventSend(toSend) != 0) { + if (glControlFunc->eventSend(toSend, event->sendTime) != 0) { // Error out curr->resetXLink = 1; XLINK_RET_ERR_IF(pthread_mutex_lock(&(curr->queueMutex)) != 0, X_LINK_ERROR); diff --git a/src/shared/XLinkDispatcherImpl.c b/src/shared/XLinkDispatcherImpl.c index 9be4c80a..e0d84288 100644 --- a/src/shared/XLinkDispatcherImpl.c +++ b/src/shared/XLinkDispatcherImpl.c @@ -20,6 +20,10 @@ #include "XLinkLog.h" #include "XLinkStringUtils.h" +#ifdef __unix__ +#include +#endif + // ------------------------------------ // Helpers declaration. Begin. // ------------------------------------ @@ -31,7 +35,7 @@ static streamPacketDesc_t* movePacketFromStream(streamDesc_t *stream); static streamPacketDesc_t* getPacketFromStream(streamDesc_t* stream); static int releasePacketFromStream(streamDesc_t* stream, uint32_t* releasedSize); static int releaseSpecificPacketFromStream(streamDesc_t* stream, uint32_t* releasedSize, uint8_t* data); -static int addNewPacketToStream(streamDesc_t* stream, void* buffer, uint32_t size, XLinkTimespec trsend, XLinkTimespec treceive); +static int addNewPacketToStream(streamDesc_t* stream, void* buffer, uint32_t size, long fd, XLinkTimespec trsend, XLinkTimespec treceive); static int handleIncomingEvent(xLinkEvent_t* event, XLinkTimespec treceive); @@ -45,14 +49,269 @@ static int handleIncomingEvent(xLinkEvent_t* event, XLinkTimespec treceive); // XLinkDispatcherImpl.h implementation. Begin. // ------------------------------------ +int writeEventMultipart(xLinkDeviceHandle_t* deviceHandle, void* data, int totalSize, void* data2, int data2Size) +{ + // Regular, single-part case + if(data2 == NULL || data2Size <= 0) { + return XLinkPlatformWrite(deviceHandle, data, totalSize); + } + + // Multipart case + int errorCode = 0; + void *dataToWrite[] = {data, data2, NULL}; + int sizeToWrite[] = {totalSize - data2Size, data2Size, 0}; + + int writtenByteCount = 0, toWrite = 0, rc = 0; + + int totalSizeToWrite = 0; + + int pktlen = 0; + + // restriction on the output data size + // mitigates kernel crash on RPI when USB is used + const int xlinkPacketSizeMultiply = deviceHandle->protocol == X_LINK_USB_VSC ? 1024 : 1; //for usb3, usb2 is 512 + uint8_t swapSpaceScratchBufferVsc[1024 + 64]; + uint8_t swapSpaceScratchBuffer[1 + 64]; + uint8_t* swapSpace = swapSpaceScratchBuffer + ALIGN_UP((((uintptr_t)swapSpaceScratchBuffer) % 64), 64); + if(deviceHandle->protocol == X_LINK_USB_VSC) { + swapSpace = swapSpaceScratchBufferVsc + ALIGN_UP((((uintptr_t)swapSpaceScratchBufferVsc) % 64), 64); + } + + // the amount of bytes written from split transfer for "next" packet + int previousSplitWriteSize = 0; + for (int i = 0;; i++) { + void *currentPacket = dataToWrite[i]; + int currentPacketSize = sizeToWrite[i]; + if (currentPacket == NULL) break; + if (currentPacketSize == 0) break; + // printf("currentPacket %p size %d \n", currentPacket, currentPacketSize); + void *nextPacket = dataToWrite[i + 1]; + int nextPacketSize = sizeToWrite[i + 1]; + bool shouldSplitData = false; + + if (nextPacket != NULL && nextPacketSize > 0) { + totalSizeToWrite += currentPacketSize - (currentPacketSize % xlinkPacketSizeMultiply); + if(currentPacketSize % xlinkPacketSizeMultiply) { + shouldSplitData = true; + } + } else { + totalSizeToWrite += currentPacketSize; + } + + // printf("writtenByteCount %d %d\n",writtenByteCount , totalSizeToWrite); + int byteCountRelativeOffset = writtenByteCount; + while (writtenByteCount < totalSizeToWrite) { + toWrite = (pktlen && (totalSizeToWrite - writtenByteCount) > pktlen) + ? pktlen + : (totalSizeToWrite - writtenByteCount); + + rc = XLinkPlatformWrite(deviceHandle, &((char *)currentPacket)[writtenByteCount - byteCountRelativeOffset + previousSplitWriteSize], toWrite); + if (rc < 0) + { + errorCode = rc; + goto function_epilogue; + } + writtenByteCount += toWrite; + } + if (shouldSplitData) { + int remainingToWriteCurrent = currentPacketSize - (totalSizeToWrite - byteCountRelativeOffset); + // printf("remainingToWriteCurrent %d \n", remainingToWriteCurrent); + if(remainingToWriteCurrent < 0 || remainingToWriteCurrent > xlinkPacketSizeMultiply) ASSERT_XLINK(0); + int remainingToWriteNext = nextPacketSize > xlinkPacketSizeMultiply - remainingToWriteCurrent ? xlinkPacketSizeMultiply - remainingToWriteCurrent : nextPacketSize; + // printf("remainingToWriteNext %d \n", remainingToWriteNext); + if(remainingToWriteNext < 0 || remainingToWriteNext > xlinkPacketSizeMultiply) ASSERT_XLINK(0); + + if (remainingToWriteCurrent) { + memcpy(swapSpace, &((char *)currentPacket)[writtenByteCount - byteCountRelativeOffset + previousSplitWriteSize], remainingToWriteCurrent); + if(remainingToWriteNext) { + memcpy(swapSpace + remainingToWriteCurrent, nextPacket, remainingToWriteNext); + } + toWrite = remainingToWriteCurrent + remainingToWriteNext; + if(toWrite > xlinkPacketSizeMultiply) ASSERT_XLINK(0); + rc = XLinkPlatformWrite(deviceHandle, swapSpace, toWrite); + if (rc < 0) + { + errorCode = rc; + goto function_epilogue; + } + writtenByteCount += toWrite; + totalSizeToWrite += remainingToWriteCurrent; + // printf("%s wrote %d \n", __FUNCTION__, rc); + + previousSplitWriteSize = remainingToWriteNext; + } + } else { + previousSplitWriteSize = 0; + } + } + +function_epilogue: + if (errorCode) return errorCode; + return writtenByteCount; +} + +int writeFdEventMultipart(xLinkDeviceHandle_t* deviceHandle, long fd, int totalSize, void* data2, int data2Size) +{ + void *mmapAddr = NULL; + int mmapSize = totalSize - data2Size; + +#ifdef __unix__ + // mmap the fine in memory + if(deviceHandle->protocol != X_LINK_LOCAL_SHDMEM) { + mmapAddr = mmap(NULL, mmapSize, PROT_READ, MAP_SHARED, fd, 0); + if (mmapAddr == MAP_FAILED) { + mvLog(MVLOG_ERROR, "Failed to mmap file to stream it over\n"); + return X_LINK_ERROR; + } + + if(data2 == NULL || data2Size <= 0) { + return XLinkPlatformWrite(deviceHandle, mmapAddr, mmapSize); + } + + fd = -1; + } else { + if(data2 == NULL || data2Size <= 0) { + return XLinkPlatformWriteFd(deviceHandle, fd, NULL, -1); + } + } +#endif + + // Multipart case + int errorCode = 0; + void *dataToWrite[3]; + int sizeToWrite[3]; + + if(deviceHandle->protocol != X_LINK_LOCAL_SHDMEM) { + dataToWrite[0] = mmapAddr; + sizeToWrite[0] = mmapSize; + dataToWrite[1] = data2; + sizeToWrite[1] = data2Size; + dataToWrite[2] = NULL; + sizeToWrite[2] = 0; + } else { + dataToWrite[0] = data2; + sizeToWrite[0] = data2Size; + dataToWrite[1] = NULL; + sizeToWrite[1] = 0; + dataToWrite[2] = NULL; + sizeToWrite[2] = 0; + } + + int writtenByteCount = 0, toWrite = 0, rc = 0; + + int totalSizeToWrite = 0; + + int pktlen = 0; + + // restriction on the output data size + // mitigates kernel crash on RPI when USB is used + const int xlinkPacketSizeMultiply = deviceHandle->protocol == X_LINK_USB_VSC ? 1024 : 1; //for usb3, usb2 is 512 + uint8_t swapSpaceScratchBufferVsc[1024 + 64]; + uint8_t swapSpaceScratchBuffer[1 + 64]; + uint8_t* swapSpace = swapSpaceScratchBuffer + ALIGN_UP((((uintptr_t)swapSpaceScratchBuffer) % 64), 64); + if(deviceHandle->protocol == X_LINK_USB_VSC) { + swapSpace = swapSpaceScratchBufferVsc + ALIGN_UP((((uintptr_t)swapSpaceScratchBufferVsc) % 64), 64); + } + + // the amount of bytes written from split transfer for "next" packet + int previousSplitWriteSize = 0; + for (int i = 0;; i++) { + void *currentPacket = dataToWrite[i]; + int currentPacketSize = sizeToWrite[i]; + if (currentPacket == NULL) break; + if (currentPacketSize == 0) break; + // printf("currentPacket %p size %d \n", currentPacket, currentPacketSize); + void *nextPacket = dataToWrite[i + 1]; + int nextPacketSize = sizeToWrite[i + 1]; + bool shouldSplitData = false; + + if (nextPacket != NULL && nextPacketSize > 0) { + totalSizeToWrite += currentPacketSize - (currentPacketSize % xlinkPacketSizeMultiply); + if(currentPacketSize % xlinkPacketSizeMultiply) { + shouldSplitData = true; + } + } else { + totalSizeToWrite += currentPacketSize; + } + + // printf("writtenByteCount %d %d\n",writtenByteCount , totalSizeToWrite); + int byteCountRelativeOffset = writtenByteCount; + while (writtenByteCount < totalSizeToWrite) { + toWrite = (pktlen && (totalSizeToWrite - writtenByteCount) > pktlen) + ? pktlen + : (totalSizeToWrite - writtenByteCount); + + if(deviceHandle->protocol != X_LINK_LOCAL_SHDMEM || fd == -1) { + rc = XLinkPlatformWrite(deviceHandle, &((char *)currentPacket)[writtenByteCount - byteCountRelativeOffset + previousSplitWriteSize], toWrite); + } else { + rc = XLinkPlatformWriteFd(deviceHandle, fd, &((char *)currentPacket)[writtenByteCount - byteCountRelativeOffset + previousSplitWriteSize], toWrite); + fd = -1; + } + + if (rc < 0) + { + errorCode = rc; + goto function_epilogue; + } + writtenByteCount += toWrite; + } + if (shouldSplitData) { + int remainingToWriteCurrent = currentPacketSize - (totalSizeToWrite - byteCountRelativeOffset); + // printf("remainingToWriteCurrent %d \n", remainingToWriteCurrent); + if(remainingToWriteCurrent < 0 || remainingToWriteCurrent > xlinkPacketSizeMultiply) ASSERT_XLINK(0); + int remainingToWriteNext = nextPacketSize > xlinkPacketSizeMultiply - remainingToWriteCurrent ? xlinkPacketSizeMultiply - remainingToWriteCurrent : nextPacketSize; + // printf("remainingToWriteNext %d \n", remainingToWriteNext); + if(remainingToWriteNext < 0 || remainingToWriteNext > xlinkPacketSizeMultiply) ASSERT_XLINK(0); + + if (remainingToWriteCurrent) { + memcpy(swapSpace, &((char *)currentPacket)[writtenByteCount - byteCountRelativeOffset + previousSplitWriteSize], remainingToWriteCurrent); + if(remainingToWriteNext) { + memcpy(swapSpace + remainingToWriteCurrent, nextPacket, remainingToWriteNext); + } + toWrite = remainingToWriteCurrent + remainingToWriteNext; + if(toWrite > xlinkPacketSizeMultiply) ASSERT_XLINK(0); + + if(deviceHandle->protocol != X_LINK_LOCAL_SHDMEM || fd == -1) { + rc = XLinkPlatformWrite(deviceHandle, swapSpace, toWrite); + } else { + rc = XLinkPlatformWriteFd(deviceHandle, fd, swapSpace, toWrite); + fd = -1; + } + + if (rc < 0) + { + errorCode = rc; + goto function_epilogue; + } + writtenByteCount += toWrite; + totalSizeToWrite += remainingToWriteCurrent; + // printf("%s wrote %d \n", __FUNCTION__, rc); + + previousSplitWriteSize = remainingToWriteNext; + } + } else { + previousSplitWriteSize = 0; + } + } + +function_epilogue: +#ifdef __unix__ + if (mmapAddr != NULL) munmap(mmapAddr, mmapSize); +#endif + if (errorCode) return errorCode; + return writtenByteCount; +} + //adds a new event with parameters and returns event id -int dispatcherEventSend(xLinkEvent_t *event) +int dispatcherEventSend(xLinkEvent_t *event, XLinkTimespec* sendTime) { mvLog(MVLOG_DEBUG, "Send event: %s, size %d, streamId %ld.\n", TypeToStr(event->header.type), event->header.size, event->header.streamId); XLinkTimespec stime; getMonotonicTimestamp(&stime); + if (sendTime != NULL) *sendTime = stime; + event->header.tsecLsb = (uint32_t)stime.tv_sec; event->header.tsecMsb = (uint32_t)(stime.tv_sec >> 32); event->header.tnsec = (uint32_t)stime.tv_nsec; @@ -65,12 +324,17 @@ int dispatcherEventSend(xLinkEvent_t *event) } if (event->header.type == XLINK_WRITE_REQ) { - rc = XLinkPlatformWrite(&event->deviceHandle, - event->data, event->header.size); + rc = writeEventMultipart(&event->deviceHandle, event->data, event->header.size, event->data2, event->data2Size); if(rc < 0) { mvLog(MVLOG_ERROR,"Write failed %d\n", rc); return rc; } + } else if (event->header.type == XLINK_WRITE_FD_REQ) { + rc = writeFdEventMultipart(&event->deviceHandle, (long)event->data, event->header.size, event->data2, event->data2Size); + if(rc < 0) { + mvLog(MVLOG_ERROR,"Write failed %d\n", rc); + return rc; + } } return 0; @@ -78,8 +342,10 @@ int dispatcherEventSend(xLinkEvent_t *event) int dispatcherEventReceive(xLinkEvent_t* event){ // static xLinkEvent_t prevEvent = {0}; + long fd = -1; int rc = XLinkPlatformRead(&event->deviceHandle, - &event->header, sizeof(event->header)); + &event->header, sizeof(event->header), &fd); + (void)fd; XLinkTimespec treceive; getMonotonicTimestamp(&treceive); @@ -109,7 +375,7 @@ int dispatcherEventReceive(xLinkEvent_t* event){ } //this function should be called only for local requests -int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) +int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response, bool server) { streamDesc_t* stream; response->header.id = event->header.id; @@ -119,6 +385,7 @@ int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) mvLog(MVLOG_DEBUG, "%s\n",TypeToStr(event->header.type)); switch (event->header.type){ case XLINK_WRITE_REQ: + case XLINK_WRITE_FD_REQ: { //in case local tries to write after it issues close (writeSize is zero) stream = getStreamById(event->deviceHandle.xLinkFD, event->header.streamId); @@ -212,17 +479,18 @@ int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) case XLINK_CREATE_STREAM_REQ: { XLINK_EVENT_ACKNOWLEDGE(event); -#ifndef __DEVICE__ - event->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, - event->header.streamName, - event->header.size, 0, - INVALID_STREAM_ID); - mvLog(MVLOG_DEBUG, "XLINK_CREATE_STREAM_REQ - stream has been just opened with id %ld\n", - event->header.streamId); -#else - mvLog(MVLOG_DEBUG, "XLINK_CREATE_STREAM_REQ - do nothing. Stream will be " - "opened with forced id accordingly to response from the host\n"); -#endif + + if(!server) { + event->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, + event->header.streamName, + event->header.size, 0, + INVALID_STREAM_ID); + mvLog(MVLOG_DEBUG, "XLINK_CREATE_STREAM_REQ - stream has been just opened with id %ld\n", + event->header.streamId); + } else { + mvLog(MVLOG_DEBUG, "XLINK_CREATE_STREAM_REQ - do nothing. Stream will be " + "opened with forced id accordingly to response from the host\n"); + } break; } case XLINK_CLOSE_STREAM_REQ: @@ -254,7 +522,8 @@ int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) mvLog(MVLOG_DEBUG,"XLINK_PING_REQ - do nothing\n"); break; } - case XLINK_WRITE_RESP: + case XLINK_WRITE_RESP: + case XLINK_WRITE_FD_RESP: case XLINK_READ_RESP: case XLINK_READ_REL_RESP: case XLINK_READ_REL_SPEC_RESP: @@ -278,7 +547,7 @@ int dispatcherLocalEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) } //this function should be called only for remote requests -int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response) +int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response, bool server) { response->header.id = event->header.id; response->header.flags.raw = 0; @@ -289,6 +558,25 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response switch (event->header.type) { + case XLINK_WRITE_FD_REQ: + { + //let remote write immediately as we have a local buffer for the data + response->header.type = XLINK_WRITE_FD_RESP; + response->header.size = event->header.size; + response->header.streamId = event->header.streamId; + response->deviceHandle = event->deviceHandle; + XLINK_EVENT_ACKNOWLEDGE(response); + + // we got some data. We should unblock a blocked read + int xxx = DispatcherUnblockEvent(-1, + XLINK_READ_REQ, + response->header.streamId, + event->deviceHandle.xLinkFD); + (void) xxx; + mvLog(MVLOG_DEBUG,"unblocked from stream %d %d\n", + (int)response->header.streamId, (int)xxx); + } + break; case XLINK_WRITE_REQ: { //let remote write immediately as we have a local buffer for the data @@ -369,17 +657,17 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response XLINK_EVENT_ACKNOWLEDGE(response); response->header.type = XLINK_CREATE_STREAM_RESP; //write size from remote means read size for this peer -#ifdef __DEVICE__ - response->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, - event->header.streamName, - 0, event->header.size, - event->header.streamId); -#else - response->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, - event->header.streamName, - 0, event->header.size, - INVALID_STREAM_ID); -#endif + if(server) { + response->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, + event->header.streamName, + 0, event->header.size, + event->header.streamId); + } else { + response->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, + event->header.streamName, + 0, event->header.size, + INVALID_STREAM_ID); + } if (response->header.streamId == INVALID_STREAM_ID) { response->header.flags.bitField.ack = 0; response->header.flags.bitField.sizeTooBig = 1; @@ -420,10 +708,12 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response stream->id = INVALID_STREAM_ID; stream->name[0] = '\0'; } -#ifdef __DEVICE__ - if(XLink_sem_destroy(&stream->sem)) - perror("Can't destroy semaphore"); -#endif + + // TODO(themarpe) - TBD + if(server) { + if(XLink_sem_destroy(&stream->sem)) + perror("Can't destroy semaphore"); + } } else { @@ -450,6 +740,8 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response // need to send the response, serve the event and then reset break; case XLINK_WRITE_RESP: + break; + case XLINK_WRITE_FD_RESP: break; case XLINK_READ_RESP: break; @@ -460,17 +752,17 @@ int dispatcherRemoteEventGetResponse(xLinkEvent_t* event, xLinkEvent_t* response case XLINK_CREATE_STREAM_RESP: { // write_size from the response the size of the buffer from the remote -#ifdef __DEVICE__ - response->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, - event->header.streamName, - event->header.size, 0, - event->header.streamId); - XLINK_RET_IF(response->header.streamId - == INVALID_STREAM_ID); - mvLog(MVLOG_DEBUG, "XLINK_CREATE_STREAM_REQ - stream has been just opened " - "with forced id=%ld accordingly to response from the host\n", - response->header.streamId); -#endif + if(server) { + response->header.streamId = XLinkAddOrUpdateStream(event->deviceHandle.xLinkFD, + event->header.streamName, + event->header.size, 0, + event->header.streamId); + XLINK_RET_IF(response->header.streamId + == INVALID_STREAM_ID); + mvLog(MVLOG_DEBUG, "XLINK_CREATE_STREAM_REQ - stream has been just opened " + "with forced id=%ld accordingly to response from the host\n", + response->header.streamId); + } response->deviceHandle = event->deviceHandle; break; } @@ -612,6 +904,7 @@ streamPacketDesc_t* movePacketFromStream(streamDesc_t* stream) } ret->data = NULL; ret->length = 0; + ret->fd = -1; // copy fields of first unused packet *ret = stream->packets[stream->firstPacketUnused]; @@ -703,11 +996,12 @@ int releaseSpecificPacketFromStream(streamDesc_t* stream, uint32_t* releasedSize return 0; } -int addNewPacketToStream(streamDesc_t* stream, void* buffer, uint32_t size, XLinkTimespec trsend, XLinkTimespec treceive) { +int addNewPacketToStream(streamDesc_t* stream, void* buffer, uint32_t size, long fd, XLinkTimespec trsend, XLinkTimespec treceive) { if (stream->availablePackets + stream->blockedPackets < XLINK_MAX_PACKETS_PER_STREAM) { stream->packets[stream->firstPacketFree].data = buffer; stream->packets[stream->firstPacketFree].length = size; + stream->packets[stream->firstPacketFree].fd = fd; stream->packets[stream->firstPacketFree].tRemoteSent = trsend; stream->packets[stream->firstPacketFree].tReceived = treceive; CIRCULAR_INCREMENT(stream->firstPacketFree, XLINK_MAX_PACKETS_PER_STREAM); @@ -722,12 +1016,15 @@ int handleIncomingEvent(xLinkEvent_t* event, XLinkTimespec treceive) { //specific actions to this peer mvLog(MVLOG_DEBUG, "%s, size %u, streamId %u.\n", TypeToStr(event->header.type), event->header.size, event->header.streamId); - ASSERT_XLINK(event->header.type >= XLINK_WRITE_REQ + ASSERT_XLINK((event->header.type >= XLINK_WRITE_REQ + && event->header.type != XLINK_STATIC_REQUEST_LAST + && event->header.type < XLINK_STATIC_RESP_LAST) || + (event->header.type >= XLINK_READ_REL_SPEC_REQ && event->header.type != XLINK_REQUEST_LAST - && event->header.type < XLINK_RESP_LAST); + && event->header.type < XLINK_RESP_LAST)); // Then read the data buffer, which is contained only in the XLINK_WRITE_REQ event - if(event->header.type != XLINK_WRITE_REQ) { + if(event->header.type != XLINK_WRITE_REQ && event->header.type != XLINK_WRITE_FD_REQ) { return 0; } @@ -743,12 +1040,13 @@ int handleIncomingEvent(xLinkEvent_t* event, XLinkTimespec treceive) { XLINK_OUT_WITH_LOG_IF(buffer == NULL, mvLog(MVLOG_FATAL,"out of memory to receive data of size = %zu\n", event->header.size)); - const int sc = XLinkPlatformRead(&event->deviceHandle, buffer, event->header.size); + long fd = -1; + const int sc = XLinkPlatformRead(&event->deviceHandle, buffer, event->header.size, &fd); XLINK_OUT_WITH_LOG_IF(sc < 0, mvLog(MVLOG_ERROR,"%s() Read failed %d\n", __func__, sc)); event->data = buffer; uint64_t tsec = event->header.tsecLsb | ((uint64_t)event->header.tsecMsb << 32); - XLINK_OUT_WITH_LOG_IF(addNewPacketToStream(stream, buffer, event->header.size, (XLinkTimespec){tsec, event->header.tnsec}, treceive), + XLINK_OUT_WITH_LOG_IF(addNewPacketToStream(stream, buffer, event->header.size, fd, (XLinkTimespec){tsec, event->header.tnsec}, treceive), mvLog(MVLOG_WARN,"No more place in stream. release packet\n")); rc = 0; diff --git a/src/shared/XLinkLog.c b/src/shared/XLinkLog.c index ba5c0024..3c4b246d 100644 --- a/src/shared/XLinkLog.c +++ b/src/shared/XLinkLog.c @@ -158,7 +158,7 @@ logprintf(mvLog_t curLogLvl, mvLog_t lvl, const char * func, const int line, if(!rtems_interrupt_is_in_progress()) { #endif -#if defined __sparc__ || !defined __DEVICE__ +#if 1 #ifdef __ANDROID__ // Convert to Android logging enumeration enum android_LogPriority logPrio = ANDROID_LOG_DEBUG + (lvl - MVLOG_DEBUG); diff --git a/src/shared/XLinkPrivateDefines.c b/src/shared/XLinkPrivateDefines.c index 910992aa..a394dabb 100644 --- a/src/shared/XLinkPrivateDefines.c +++ b/src/shared/XLinkPrivateDefines.c @@ -87,17 +87,16 @@ streamId_t XLinkAddOrUpdateStream(void *fd, const char *name, if (readSize && !stream->readSize) { stream->readSize = readSize; -#ifdef __DEVICE__ - // FIXME: not the best solution but the simplest for now: - // it is just for a check; real allocation will be done during receiving an usb package - void *buffer = XLinkPlatformAllocateData(ALIGN_UP(readSize, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); - if (buffer == NULL) { - mvLog(MVLOG_ERROR,"Cannot create stream. Requested memory = %u", stream->readSize); - return INVALID_STREAM_ID; - } else { - XLinkPlatformDeallocateData(buffer, ALIGN_UP(readSize, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); - } -#endif + // // Just allocate data + // // FIXME: not the best solution but the simplest for now: + // // it is just for a check; real allocation will be done during receiving an usb package + // void *buffer = XLinkPlatformAllocateData(ALIGN_UP(readSize, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); + // if (buffer == NULL) { + // mvLog(MVLOG_ERROR,"Cannot create stream. Requested memory = %u", stream->readSize); + // return INVALID_STREAM_ID; + // } else { + // XLinkPlatformDeallocateData(buffer, ALIGN_UP(readSize, __CACHE_LINE_SIZE), __CACHE_LINE_SIZE); + // } } if (writeSize && !stream->writeSize) { stream->writeSize = writeSize; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3527b616..3cef88ef 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,14 @@ -# Helper 'add_test' -macro(add_test test_name test_src) +# Add CMake testing infrastructure +include(CTest) +enable_testing() + +# Search for Bash executable +find_program(BASH_EXECUTABLE name "bash" + HINTS "C:/Program Files/Git/bin" # Windows hint +) + +# Helper 'xlink_add_test' +macro(xlink_add_test test_name test_src) add_executable(${test_name} ${test_src}) target_link_libraries(${test_name} ${TARGET_NAME}) set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 11) @@ -21,12 +30,53 @@ macro(add_test test_name test_src) ) endif() endif() + + # Add to test suite + add_test(NAME ${test_name} COMMAND + "$" + COMMAND_EXPAND_LISTS + ) + endmacro() -# Tests +# Helper 'xlink_add_client_server_test' +macro(xlink_add_client_server_test test_name num_iterations timeout) + # Add client + add_executable(${test_name}_client ${test_name}.cpp) + target_compile_definitions(${test_name}_client PRIVATE XLINK_TEST_CLIENT) + target_link_libraries(${test_name}_client ${TARGET_NAME}) + + # Add server + add_executable(${test_name}_server ${test_name}.cpp) + target_compile_definitions(${test_name}_server PRIVATE XLINK_TEST_SERVER) + target_link_libraries(${test_name}_server ${TARGET_NAME}) -# Multiple stream open -add_test(multiple_open_stream multiple_open_stream.cpp) + # Specify test script + if("${ARGV3}" STREQUAL "") + set(test_script "${CMAKE_CURRENT_LIST_DIR}/bash/test_client_server.sh") + else() + set(test_script "${CMAKE_CURRENT_LIST_DIR}/bash/${ARGV3}") + endif() + + # Add to test suite + add_test(NAME ${test_name} COMMAND + ${BASH_EXECUTABLE} "${test_script}" + "$" + "$" + "${num_iterations}" + "${timeout}" + COMMAND_EXPAND_LISTS + ) +endmacro() + +# Tests # Multithreading search -add_test(multithreading_search_test multithreading_search_test.cpp) \ No newline at end of file +xlink_add_test(multithreading_search_test multithreading_search_test.cpp) + +# RTT test +xlink_add_test(rtt_test rtt_test.cpp) + +# WIP +# Throughput test +# xlink_add_test(throughput_test throughput_test.cpp) diff --git a/tests/multiple_open_stream.cpp b/tests/multiple_open_stream.cpp index 97729854..637c7a3c 100644 --- a/tests/multiple_open_stream.cpp +++ b/tests/multiple_open_stream.cpp @@ -36,6 +36,8 @@ int main() { XLinkGlobalHandler_t gHandler; XLinkInitialize(&gHandler); + deviceDesc_t deviceDesc; + /* // Search for booted device deviceDesc_t deviceDesc, inDeviceDesc; inDeviceDesc.protocol = X_LINK_ANY_PROTOCOL; @@ -44,6 +46,10 @@ int main() { printf("Didn't find a device\n"); return -1; } + */ + + strcpy(deviceDesc.name, "127.0.0.1"); + deviceDesc.protocol = X_LINK_TCP_IP; printf("Device name: %s\n", deviceDesc.name); @@ -93,7 +99,7 @@ int main() { // OK } else { streamId_t id; - memcpy(&id, p->data, sizeof(id)); + // memcpy(&id, p->data, sizeof(id)); printf("DESYNC error - name %s, id: 0x%08X, response id: 0x%08X\n", name.c_str(), s, id); success = false; } diff --git a/tests/multiple_open_stream_server.cpp b/tests/multiple_open_stream_server.cpp new file mode 100644 index 00000000..e3abf656 --- /dev/null +++ b/tests/multiple_open_stream_server.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +XLinkGlobalHandler_t xlinkGlobalHandler = {}; + +int main(int argc, const char** argv){ + + xlinkGlobalHandler.protocol = X_LINK_TCP_IP; + + // Initialize and suppress XLink logs + mvLogDefaultLevelSet(MVLOG_ERROR); + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + throw std::runtime_error("Couldn't initialize XLink"); + } + + // Start server + XLinkHandler_t handler; + std::string serverIp{"127.0.0.1"}; + handler.devicePath = &serverIp[0]; + handler.protocol = X_LINK_TCP_IP; + XLinkServer(&handler, "xlinkserver", X_LINK_BOOTED, X_LINK_MYRIAD_X); + + // loop through streams + constexpr static auto NUM_STREAMS = 16; + std::array threads; + for(int i = 0; i < NUM_STREAMS; i++){ + threads[i] = std::thread([i](){ + std::string name = "test_"; + auto s = XLinkOpenStream(0, (name + std::to_string(i)).c_str(), 1024); + assert(s != INVALID_STREAM_ID); + auto w = XLinkWriteData2(s, (uint8_t*) &s, sizeof(s/2), ((uint8_t*) &s) + sizeof(s/2), sizeof(s) - sizeof(s/2)); + assert(w == X_LINK_SUCCESS); + }); + } + for(auto& thread : threads){ + thread.join(); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "All threads joined\n"; + + return 0; +} \ No newline at end of file diff --git a/tests/multithreading_search_test.cpp b/tests/multithreading_search_test.cpp index f36b6d78..a84fad5f 100644 --- a/tests/multithreading_search_test.cpp +++ b/tests/multithreading_search_test.cpp @@ -41,7 +41,7 @@ int main() { suitableDevice.protocol = X_LINK_ANY_PROTOCOL; suitableDevice.platform = X_LINK_ANY_PLATFORM; - auto status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev); + auto status = XLinkFindAllSuitableDevices(suitableDevice, deviceDescAll.data(), deviceDescAll.size(), &numdev, XLINK_DEVICE_DEFAULT_SEARCH_TIMEOUT_MS); if(status != X_LINK_SUCCESS) throw std::runtime_error("Couldn't retrieve all connected devices"); // Print device details diff --git a/tests/rtt_test.cpp b/tests/rtt_test.cpp new file mode 100644 index 00000000..82882660 --- /dev/null +++ b/tests/rtt_test.cpp @@ -0,0 +1,166 @@ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +using namespace std::chrono; + +constexpr static int NUM_ITERATIONS = 10000; +constexpr static bool PRINT_DEBUG = false; +constexpr static microseconds RTT_THRESHOLD{5000}; + +struct Timestamp { + int64_t sec; + int64_t nsec; +}; + +int client(); +int server(); + +bool successServer{true}; +bool successClient{true}; + +int main(int argc, char** argv) { + // mvLogDefaultLevelSet(MVLOG_DEBUG); + + XLinkGlobalHandler_t gHandler; + XLinkInitialize(&gHandler); + std::thread ts([](){successServer = server() == 0;}); + std::this_thread::sleep_for(milliseconds(100)); + std::thread tc([](){successClient = client() == 0;}); + + ts.join(); + tc.join(); + + if(successServer && successClient) { + return 0; + } else { + return -1; + } +} + +// Client +int client() { + deviceDesc_t deviceDesc; + strcpy(deviceDesc.name, "127.0.0.1"); + deviceDesc.protocol = X_LINK_TCP_IP; + + printf("Device name: %s\n", deviceDesc.name); + + XLinkHandler_t handler; + handler.devicePath = deviceDesc.name; + handler.protocol = deviceDesc.protocol; + XLinkConnect(&handler); + + std::this_thread::sleep_for(milliseconds(100)); + auto s = XLinkOpenStream(handler.linkId, "rtt", 1024); + + if(s != INVALID_STREAM_ID) { + Timestamp ts = {}; + + streamPacketDesc_t* packet; + bool success = true; + + for(int i = 1; i <= NUM_ITERATIONS; i++){ + ts.sec = i; + ts.nsec = 0; + auto t1 = steady_clock::now(); + assert(XLinkWriteData(s, reinterpret_cast(&ts), sizeof(ts)) == X_LINK_SUCCESS); + auto t1point5 = steady_clock::now(); + assert(XLinkReadData(s, &packet) == X_LINK_SUCCESS); + auto t2 = steady_clock::now(); + assert(packet->length == sizeof(ts)); + memcpy(&ts, packet->data, packet->length); + XLinkReleaseData(s); + + if(PRINT_DEBUG) printf("client received - sec: %ld, nsec: %ld\n", ts.sec, ts.nsec); + assert((ts.sec + 100)*2 == ts.nsec); + + if(t2-t1 <= RTT_THRESHOLD) { + if(PRINT_DEBUG) printf("OK, rtt = %ldus. (write: %ldus)\n", duration_cast(t2-t1).count(), duration_cast(t1point5-t1).count()); + } else { + printf("NOK, rtt = %ldus. RTT too high (write: %ldus)\n", duration_cast(t2-t1).count(), duration_cast(t1point5-t1).count()); + success = false; + } + } + + if(success) { + printf("Success!\n"); + } else { + printf("Failed\n"); + return -1; + } + + } else { + printf("Failed\n"); + return -1; + } + + return 0; + +} + +// Server +XLinkGlobalHandler_t xlinkGlobalHandler = {}; +int server(){ + + xlinkGlobalHandler.protocol = X_LINK_TCP_IP; + auto status = XLinkInitialize(&xlinkGlobalHandler); + if(X_LINK_SUCCESS != status) { + throw std::runtime_error("Couldn't initialize XLink"); + } + + XLinkHandler_t handler; + std::string serverIp{"127.0.0.1"}; + + handler.devicePath = &serverIp[0]; + handler.protocol = X_LINK_TCP_IP; + XLinkServer(&handler, "test", X_LINK_BOOTED, X_LINK_MYRIAD_X); + auto s = XLinkOpenStream(handler.linkId, "rtt", 1024); + std::this_thread::sleep_for(milliseconds(100)); + + if(s != INVALID_STREAM_ID) { + for(int i = 1; i <= NUM_ITERATIONS; i++) { + Timestamp timestamp = {}; + streamPacketDesc_t* packet; + auto t1 = steady_clock::now(); + if(XLinkReadData(s, &packet) != X_LINK_SUCCESS) { + printf("failed.\n"); + return -1; + } + assert(packet->length == sizeof(timestamp)); + memcpy(×tamp, packet->data, packet->length); + XLinkReleaseData(s); + timestamp.nsec = (timestamp.sec + 100LL) * 2LL; + auto t1point5 = steady_clock::now(); + if(PRINT_DEBUG) printf("server sent - sec: %ld, nsec: %ld\n", timestamp.sec, timestamp.nsec); + if(XLinkWriteData(s, reinterpret_cast(×tamp), sizeof(timestamp)) != X_LINK_SUCCESS) { + printf("failed.\n"); + return -1; + } + auto t2 = steady_clock::now(); + if(PRINT_DEBUG) printf("Respond time: %ldus, (write: %ldus)\n", duration_cast(t2-t1).count(), duration_cast(t1point5-t1).count()); + } + + } else { + printf("failed.\n"); + return -1; + } + + return 0; +} diff --git a/tests/throughput_test.cpp b/tests/throughput_test.cpp new file mode 100644 index 00000000..25b8bb5f --- /dev/null +++ b/tests/throughput_test.cpp @@ -0,0 +1,161 @@ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "XLink/XLink.h" +#include "XLink/XLinkPublicDefines.h" +#include "XLink/XLinkLog.h" + +using namespace std::chrono; + +constexpr static int NUM_ITERATIONS = 10000; +constexpr static bool PRINT_DEBUG = false; +constexpr static size_t THROUGHPUT_THRESHOLD{1000*1024*1024}; + +constexpr static size_t BUFFER_SIZE = 1024*1024*2; + +struct Timestamp { + int64_t sec; + int64_t nsec; +}; + +int client(bool split); +int server(bool split); + + +int main(int argc, char** argv) { + // mvLogDefaultLevelSet(MVLOG_DEBUG); + + XLinkGlobalHandler_t gHandler; + XLinkInitialize(&gHandler); + bool successServer{true}; + bool successClient{true}; + + { + std::thread ts([&successServer](){successServer = server(false) == 0;}); + std::this_thread::sleep_for(milliseconds(100)); + std::thread tc([&successClient](){successClient = client(false) == 0;}); + + ts.join(); + tc.join(); + + if(!successServer || !successClient) { + return -1; + } + } + + { + std::thread ts([&successServer](){successServer = server(true) == 0;}); + std::this_thread::sleep_for(milliseconds(100)); + std::thread tc([&successClient](){successClient = client(true) == 0;}); + + ts.join(); + tc.join(); + + if(!successServer || !successClient) { + return -1; + } + } + + return 0; +} + +// Client +int client(bool split) { + deviceDesc_t deviceDesc; + strcpy(deviceDesc.name, "127.0.0.1"); + deviceDesc.protocol = X_LINK_TCP_IP; + + printf("Device name: %s\n", deviceDesc.name); + + XLinkHandler_t handler; + handler.devicePath = deviceDesc.name; + handler.protocol = deviceDesc.protocol; + assert(XLinkConnect(&handler) == X_LINK_SUCCESS); + + std::this_thread::sleep_for(milliseconds(100)); + auto s = XLinkOpenStream(handler.linkId, "rtt", 2*BUFFER_SIZE); + assert(s != INVALID_STREAM_ID); + + Timestamp ts = {}; + uint8_t buffer[BUFFER_SIZE]; + + auto t1 = steady_clock::now(); + for(int i = 1; i <= NUM_ITERATIONS; i++){ + if(split) { + assert(XLinkWriteData2(s, buffer, BUFFER_SIZE, reinterpret_cast(&ts), sizeof(ts)) == X_LINK_SUCCESS); + } else { + assert(XLinkWriteData(s, buffer, BUFFER_SIZE) == X_LINK_SUCCESS); + } + } + + streamPacketDesc_t* packet; + assert(XLinkReadData(s, &packet) == X_LINK_SUCCESS); + auto t2 = steady_clock::now(); + assert(XLinkReleaseData(s) == X_LINK_SUCCESS); + + size_t throughput = (BUFFER_SIZE*NUM_ITERATIONS) / duration_cast>(t2-t1).count(); + + if(throughput > THROUGHPUT_THRESHOLD) { + printf("Success - throughput: %ldMiB/s!\n", throughput/(1024*1024)); + } else { + printf("Fail - throughput: %ldMiB/s!\n", throughput/(1024*1024)); + return -1; + } + + assert(XLinkCloseStream(s) == X_LINK_SUCCESS); + assert(XLinkResetRemote(handler.linkId) == X_LINK_SUCCESS); + + return 0; + +} + +// Server +XLinkGlobalHandler_t xlinkGlobalHandler = {}; +int server(bool split){ + XLinkHandler_t handler; + std::string serverIp{"127.0.0.1"}; + + handler.devicePath = &serverIp[0]; + handler.protocol = X_LINK_TCP_IP; + XLinkServerOnly(&handler); + auto s = XLinkOpenStream(handler.linkId, "rtt", 2*BUFFER_SIZE); + std::this_thread::sleep_for(milliseconds(100)); + + if(s != INVALID_STREAM_ID) { + for(int i = 1; i <= NUM_ITERATIONS; i++) { + Timestamp timestamp = {}; + streamPacketDesc_t* packet; + auto t1 = steady_clock::now(); + if(XLinkReadData(s, &packet) != X_LINK_SUCCESS) { + printf("failed.\n"); + return -1; + } + XLinkReleaseData(s); + } + uint8_t tmp[4]; + assert(XLinkWriteData(s, reinterpret_cast(&tmp), sizeof(tmp)) == X_LINK_SUCCESS); + + } else { + printf("failed.\n"); + return -1; + } + + assert(XLinkCloseStream(s) == X_LINK_SUCCESS); + assert(XLinkResetRemote(handler.linkId) == X_LINK_SUCCESS); + + + return 0; +}