17 Commits
Author SHA1 Message Date
Bryant JonesandKyle Schwarz ee6b846acc Driver: Fix lost wakeup in pushRx 2026-11-03 19:16:15 -05:00
Max BrombachandKyle Schwarz 645549193d Device: Allow skipping of device state refreshing on call to open 2026-11-03 11:20:04 -05:00
Kyle Schwarz dffb0bcefb CI: Deploy: Also push git history 2026-11-01 18:04:33 -05:00
Kyle Schwarz f4fe69f992 CI: Adjust twine upload args 2026-11-01 11:28:02 -05:00
Kyle Schwarz 7ff14ae86d CI: Add tag upload & deploy 2026-10-31 11:24:38 -04:00
Kyle Schwarz 5c61a3b3c1 All: Remove deprecated Drivers 2026-10-29 12:33:38 -04:00
Max BrombachandKyle Schwarz 320fec74bd Device: Add SFP-AD1101 2026-10-23 12:42:08 -04:00
Bryant JonesandKyle Schwarz fd8f7b328f Message: LIN: Fix checksum 2026-10-21 19:46:48 -04:00
Thomas StoddardandKyle Schwarz 6ac041cadc Add bootloader extension 2026-10-21 19:22:50 -04:00
Thomas StoddardandKyle Schwarz 4db27aa187 Bindings: Python: Add ISO-TP 2026-10-20 21:15:12 -04:00
Thomas StoddardandKyle Schwarz 1d3039775d Device: Refactor T1S settings 2026-10-06 15:16:33 -04:00
Max BrombachandKyle Schwarz fb241dc5cd EventManager: Fix potential ABBA deadlock if callback contains locks 2026-10-02 15:38:59 -04:00
Max BrombachandKyle Schwarz 3932b54947 Device: RAD-Gigastar and RAD-Gigastar 2: Fix bootloader pipeline 2026-10-02 14:28:40 -04:00
Nicholas ZamoraandKyle Schwarz f4836f279f Device: RAD-Gemini to RAD-Argos 2026-10-02 13:53:55 -04:00
Thomas StoddardandKyle Schwarz 7ef57e6737 Device: Improve TC10 message filtering 2026-09-30 13:07:13 -04:00
David RebbeandKyle Schwarz 4c0781190e Message: EthPhyMessage: Add Status 2026-09-29 15:01:36 -04:00
Kyle Schwarz 0a67c9cf5d Platform: Servd: Add address configuration 2026-09-28 12:02:40 -04:00
154 changed files with 14949 additions and 8198 deletions
+17 -27
View File
@@ -47,7 +47,7 @@ unit_test windows/x64:
script: script:
- apt update -y - apt update -y
- apt upgrade -y - apt upgrade -y
- apt install -y g++ ninja-build cmake libpcap-dev git ca-certificates - apt install -y g++ ninja-build cmake git ca-certificates
- echo "$ICS_IPA_CA_CRT" >/usr/local/share/ca-certificates/ica-ipa-ca.crt - echo "$ICS_IPA_CA_CRT" >/usr/local/share/ca-certificates/ica-ipa-ca.crt
- update-ca-certificates --fresh - update-ca-certificates --fresh
- sh ci/build-posix.sh - sh ci/build-posix.sh
@@ -64,7 +64,6 @@ unit_test windows/x64:
script: script:
- apt update -y - apt update -y
- apt upgrade -y - apt upgrade -y
- apt install -y libpcap-dev
- build/libicsneo-unit-tests - build/libicsneo-unit-tests
tags: tags:
- linux-build - linux-build
@@ -75,7 +74,7 @@ unit_test windows/x64:
script: script:
- apt update -y - apt update -y
- apt upgrade -y - apt upgrade -y
- apt install -y clang lld ninja-build cmake libpcap-dev git ca-certificates - apt install -y clang lld ninja-build cmake git ca-certificates
- echo "$ICS_IPA_CA_CRT" >/usr/local/share/ca-certificates/ica-ipa-ca.crt - echo "$ICS_IPA_CA_CRT" >/usr/local/share/ca-certificates/ica-ipa-ca.crt
- update-ca-certificates --fresh - update-ca-certificates --fresh
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh - CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
@@ -92,7 +91,6 @@ unit_test windows/x64:
script: script:
- apt update -y - apt update -y
- apt upgrade -y - apt upgrade -y
- apt install -y libpcap-dev
- build/libicsneo-unit-tests - build/libicsneo-unit-tests
tags: tags:
- linux-build - linux-build
@@ -159,7 +157,7 @@ unit_test linux/ubuntu/2604/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf - echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf - echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y - dnf upgrade -y
- dnf install -y g++ libpcap-devel cmake ninja-build git ca-certificates - dnf install -y g++ cmake ninja-build git ca-certificates
- echo "$ICS_IPA_CA_CRT" >/etc/pki/ca-trust/source/anchors/ica-ipa-ca.crt - echo "$ICS_IPA_CA_CRT" >/etc/pki/ca-trust/source/anchors/ica-ipa-ca.crt
- update-ca-trust - update-ca-trust
- sh ci/build-posix.sh - sh ci/build-posix.sh
@@ -180,7 +178,6 @@ unit_test linux/ubuntu/2604/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf - echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf - echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y - dnf upgrade -y
- dnf install -y libpcap-devel
- build/libicsneo-unit-tests - build/libicsneo-unit-tests
tags: tags:
- linux-build - linux-build
@@ -195,7 +192,7 @@ unit_test linux/ubuntu/2604/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf - echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf - echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y - dnf upgrade -y
- dnf install -y clang lld libpcap-devel cmake ninja-build git ca-certificates - dnf install -y clang lld cmake ninja-build git ca-certificates
- echo "$ICS_IPA_CA_CRT" >/etc/pki/ca-trust/source/anchors/ica-ipa-ca.crt - echo "$ICS_IPA_CA_CRT" >/etc/pki/ca-trust/source/anchors/ica-ipa-ca.crt
- update-ca-trust - update-ca-trust
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh - CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
@@ -216,7 +213,6 @@ unit_test linux/ubuntu/2604/amd64/clang:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf - echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf - echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y - dnf upgrade -y
- dnf install -y libpcap-devel
- build/libicsneo-unit-tests - build/libicsneo-unit-tests
tags: tags:
- linux-build - linux-build
@@ -279,13 +275,9 @@ build python/linux/amd64:
tags: tags:
- linux-native-amd64 - linux-native-amd64
variables: variables:
CIBW_BEFORE_ALL: sh ci/bootstrap-cibuildwheel.sh && sh ci/bootstrap-libpcap.sh
CIBW_BUILD: "*manylinux*" # no musl
CIBW_ARCHS: x86_64 CIBW_ARCHS: x86_64
CIBW_ENVIRONMENT: PCAP_ROOT=/project/libpcap/install
SKBUILD_CMAKE_DEFINE: ICSPB_BOOTSTRAP_DIR=/project/icspb
script: script:
- sh ci/build-wheel-posix.sh - sh ci/build-wheel-linux.sh
artifacts: artifacts:
paths: paths:
- wheelhouse - wheelhouse
@@ -295,13 +287,9 @@ build python/linux/arm64:
tags: tags:
- arm64-linux-build - arm64-linux-build
variables: variables:
CIBW_BEFORE_ALL: sh ci/bootstrap-cibuildwheel.sh && sh ci/bootstrap-libpcap.sh
CIBW_BUILD: "*manylinux*" # no musl
CIBW_ARCHS: aarch64 CIBW_ARCHS: aarch64
CIBW_ENVIRONMENT: PCAP_ROOT=/project/libpcap/install
SKBUILD_CMAKE_DEFINE: ICSPB_BOOTSTRAP_DIR=/project/icspb
script: script:
- sh ci/build-wheel-posix.sh - sh ci/build-wheel-linux.sh
artifacts: artifacts:
paths: paths:
- wheelhouse - wheelhouse
@@ -310,14 +298,8 @@ build python/macos:
stage: build stage: build
tags: tags:
- macos-arm64 - macos-arm64
variables:
CIBW_BEFORE_ALL: sh ci/bootstrap-libpcap.sh
CIBW_ARCHS: arm64
CIBW_ENVIRONMENT: PCAP_ROOT=$CI_PROJECT_DIR/libpcap/install
SKBUILD_CMAKE_DEFINE: ICSPB_BOOTSTRAP_DIR=$CI_PROJECT_DIR/icspb
MACOSX_DEPLOYMENT_TARGET: 10.14
script: script:
- sh ci/build-wheel-posix.sh - sh ci/build-wheel-macos.sh
artifacts: artifacts:
paths: paths:
- wheelhouse - wheelhouse
@@ -347,9 +329,10 @@ deploy python/pypi:
image: python:3.13 image: python:3.13
rules: rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_TAG
script: script:
- python3 -m pip install -U twine - python3 -m pip install -U twine
- twine upload wheelhouse/* - twine upload --verbose wheelhouse/*
dependencies: dependencies:
- build python/linux/amd64 - build python/linux/amd64
- build python/linux/arm64 - build python/linux/arm64
@@ -371,10 +354,17 @@ push github:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: delayed when: delayed
start_in: 10 minutes start_in: 10 minutes
variables:
REFSPEC: HEAD:master
- if: $CI_COMMIT_TAG
when: delayed
start_in: 10 minutes
variables:
REFSPEC: $CI_COMMIT_TAG
dependencies: dependencies:
- deploy python/pypi - deploy python/pypi
needs: needs:
- deploy python/pypi - deploy python/pypi
script: script:
- apk add git - apk add git
- git push https://$LIBICSNEO_GITHUB_USERNAME:$LIBICSNEO_GITHUB_TOKEN@github.com/intrepidcs/libicsneo.git HEAD:master - git push https://$LIBICSNEO_GITHUB_USERNAME:$LIBICSNEO_GITHUB_TOKEN@github.com/intrepidcs/libicsneo.git $REFSPEC
-10
View File
@@ -1,10 +0,0 @@
# Intrepid Control System, Inc. neoVI Devices
SUBSYSTEM=="usb", ATTRS{idVendor}=="093c", GROUP="users", MODE="0666"
KERNEL=="ttyUSB?", ATTRS{idVendor}=="093c", GROUP="users", MODE="0666"
KERNEL=="ttyACM?", ATTRS{idVendor}=="093c", GROUP="users", MODE="0666"
ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="093c", KERNEL=="ttyUSB*", \
RUN+="/bin/sh -c 'echo $id:1.0>/sys/bus/usb/drivers/ftdi_sio/unbind'"
ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="093c", DRIVER=="usbhid", \
RUN+="/bin/sh -c 'echo $id:1.0>/sys/bus/usb/drivers/usbhid/unbind'"
+81 -145
View File
@@ -53,22 +53,20 @@ option(LIBICSNEO_BUILD_ICSNEOC2 "Build dynamic C2 library" ON)
option(LIBICSNEO_BUILD_ICSNEOC2_STATIC "Build static C2 library" ON) option(LIBICSNEO_BUILD_ICSNEOC2_STATIC "Build static C2 library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON) option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON) option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON)
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
# Device Drivers # Device Drivers
# You almost certainly don't want firmio for your build, # You almost certainly don't want firmio for your build,
# it is only relevant for communication between Linux and # it is only relevant for communication between Linux and
# CoreMini from the onboard processor of the device. # CoreMini from the onboard processor of the device.
option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini within the same device" OFF) option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini within the same device" OFF)
option(LIBICSNEO_ENABLE_RAW_ETHERNET "Enable devices which communicate over raw ethernet" ON)
option(LIBICSNEO_ENABLE_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
option(LIBICSNEO_ENABLE_TCP "Enable devices which communicate over TCP" OFF)
option(LIBICSNEO_ENABLE_DXX "Enable devices which communicate over D2XX/D3XX via libredxx" ON)
option(LIBICSNEO_ENABLE_BINDINGS_PYTHON "Enable Python library" OFF) option(LIBICSNEO_ENABLE_BINDINGS_PYTHON "Enable Python library" OFF)
option(LIBICSNEO_ENABLE_BOOTLOADER "Enable bootloader firmware update support" OFF)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
include(GNUInstallDirs) include(GNUInstallDirs)
@@ -142,82 +140,10 @@ if(LIBICSNEO_BUILD_DOCS)
endif() endif()
endif() endif()
if(WIN32) if(LIBICSNEO_ENABLE_FIRMIO AND NOT WIN32)
set(PLATFORM_SRC set(PLATFORM_SRC platform/posix/firmio.cpp)
platform/windows/strings.cpp
platform/windows/registry.cpp
)
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
list(APPEND PLATFORM_SRC
platform/windows/pcap.cpp
platform/windows/internal/pcapdll.cpp
)
endif()
if(LIBICSNEO_ENABLE_CDCACM)
list(APPEND PLATFORM_SRC
platform/windows/cdcacm.cpp
)
endif()
else() # Darwin or Linux
set(PLATFORM_SRC)
if(LIBICSNEO_ENABLE_FIRMIO)
list(APPEND PLATFORM_SRC
platform/posix/firmio.cpp
)
endif()
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
list(APPEND PLATFORM_SRC
platform/posix/pcap.cpp
)
endif()
if(LIBICSNEO_ENABLE_CDCACM)
list(APPEND PLATFORM_SRC
platform/posix/cdcacm.cpp
)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
list(APPEND PLATFORM_SRC
platform/posix/darwin/cdcacmdarwin.cpp
)
else() # Linux or other
list(APPEND PLATFORM_SRC
platform/posix/linux/cdcacmlinux.cpp
)
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
message(WARNING
"There is no CDCACM platform port defined for ${CMAKE_SYSTEM_NAME}!\n"
"The Linux platform code will be used, as it will generally allow building, but some devices may not enumerate properly."
)
endif()
endif()
endif()
endif() endif()
if(LIBICSNEO_ENABLE_DXX)
list(APPEND PLATFORM_SRC
platform/dxx.cpp
)
endif()
if(LIBICSNEO_ENABLE_TCP)
list(APPEND PLATFORM_SRC
platform/tcp.cpp
)
endif()
# Extensions
set(LIBICSNEO_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
foreach(EXT_PATH ${LIBICSNEO_EXTENSION_DIRS})
get_filename_component(EXT_DIR ${EXT_PATH} NAME)
message("Adding extension " ${EXT_DIR})
add_subdirectory(${EXT_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${EXT_DIR})
endforeach()
set(SRC_FILES set(SRC_FILES
communication/message/flexray/control/flexraycontrolmessage.cpp communication/message/flexray/control/flexraycontrolmessage.cpp
communication/message/callback/streamoutput/a2bwavoutput.cpp communication/message/callback/streamoutput/a2bwavoutput.cpp
@@ -306,6 +232,28 @@ set(SRC_FILES
${PLATFORM_SRC} ${PLATFORM_SRC}
) )
if(LIBICSNEO_ENABLE_BOOTLOADER)
list(APPEND SRC_FILES
device/extensions/bootloader/api.cpp
device/extensions/bootloader/version.cpp
device/extensions/bootloader/platform/logging.cpp
device/extensions/bootloader/parser/ief.cpp
device/extensions/bootloader/parser/fwzip.cpp
device/extensions/bootloader/parser/fwbin.cpp
device/extensions/bootloader/parser/fwbundle.cpp
device/extensions/bootloader/parser/fwloader.cpp
device/extensions/bootloader/communication/red.cpp
device/extensions/bootloader/communication/radmultichipcommunication.cpp
device/extensions/bootloader/communication/communication.cpp
device/extensions/bootloader/communication/radperiphcommunication.cpp
device/extensions/bootloader/communication/radcommunication.cpp
device/extensions/bootloader/communication/redpacketizer.cpp
device/extensions/bootloader/communication/redcore.cpp
device/extensions/bootloader/communication/application.cpp
device/extensions/bootloader/communication/redbase.cpp
)
endif()
# Generate build info header # Generate build info header
execute_process( execute_process(
COMMAND git rev-parse --abbrev-ref HEAD COMMAND git rev-parse --abbrev-ref HEAD
@@ -335,17 +283,6 @@ configure_file(api/icsneocpp/buildinfo.h.template ${CMAKE_CURRENT_BINARY_DIR}/ge
configure_file(api/icsneoc/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc) configure_file(api/icsneoc/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
configure_file(api/icsneoc2/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc2/version.rc) configure_file(api/icsneoc2/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc2/version.rc)
foreach(EXTINC ${LIBICSNEO_EXTENSION_INCLUDES})
message("Including " ${EXTINC})
list(APPEND LIBICSNEO_EXT_CODE_INCS_LIST "#include \"${EXTINC}\"")
endforeach()
list(JOIN LIBICSNEO_EXT_CODE_INCS_LIST "\n" LIBICSNEO_EXT_CODE_INCS)
foreach(EXTCLASS ${LIBICSNEO_EXTENSION_CLASSES})
list(APPEND LIBICSNEO_EXT_CODE_LIST "device->addExtension(std::make_shared<${EXTCLASS}>(*device))\;")
endforeach()
list(JOIN LIBICSNEO_EXT_CODE_LIST "\n\t" LIBICSNEO_EXT_CODE)
configure_file(include/icsneo/device/extensions/builtin.h.template ${CMAKE_CURRENT_BINARY_DIR}/generated/extensions/builtin.h)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
add_library(icsneocpp add_library(icsneocpp
@@ -356,78 +293,24 @@ add_library(icsneocpp
api/icsneocpp/version.cpp api/icsneocpp/version.cpp
${SRC_FILES} ${SRC_FILES}
) )
message("Include paths " ${LIBICSNEO_EXTENSION_INCLUDE_PATHS})
target_include_directories(icsneocpp target_include_directories(icsneocpp
PUBLIC PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:> $<INSTALL_INTERFACE:>
PRIVATE PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
) )
target_link_libraries(icsneocpp PUBLIC Threads::Threads $<$<BOOL:${WIN32}>:ws2_32 iphlpapi>) target_link_libraries(icsneocpp PUBLIC Threads::Threads $<$<BOOL:${WIN32}>:ws2_32 iphlpapi>)
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums) target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
target_compile_options(icsneocpp PRIVATE ${LIBICSNEO_COMPILER_WARNINGS}) target_compile_options(icsneocpp PRIVATE ${LIBICSNEO_COMPILER_WARNINGS})
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
target_link_libraries(icsneocpp PUBLIC ${LIBICSNEO_EXTENSION_TARGETS})
if(LIBICSNEO_ENABLE_FIRMIO) if(LIBICSNEO_ENABLE_FIRMIO)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FIRMIO) target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FIRMIO)
endif() endif()
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_RAW_ETHERNET)
endif()
if(LIBICSNEO_ENABLE_CDCACM)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_CDCACM)
if(APPLE)
find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation REQUIRED)
find_library(IO_KIT_FRAMEWORK IOKit REQUIRED)
target_link_libraries(icsneocpp PRIVATE ${CORE_FOUNDATION_FRAMEWORK} ${IO_KIT_FRAMEWORK})
endif()
endif()
if(LIBICSNEO_ENABLE_DXX)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_DXX)
target_link_libraries(icsneocpp PRIVATE libredxx::libredxx)
endif()
if(LIBICSNEO_ENABLE_TCP)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
if(WIN32)
target_link_libraries(icsneocpp PRIVATE ws2_32 iphlpapi)
endif()
endif()
# fatfs # fatfs
add_subdirectory(third-party/fatfs) add_subdirectory(third-party/fatfs)
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PRIVATE fatfs) target_link_libraries(icsneocpp PRIVATE fatfs)
# dxx
if(LIBICSNEO_ENABLE_DXX)
include(FetchContent)
FetchContent_Declare(libredxx
GIT_REPOSITORY https://github.com/Zeranoe/libredxx.git
GIT_TAG e823a96c39a64ab41b7d1632dbe8f86bb854df83
)
set(LIBREDXX_DISABLE_INSTALL ON)
FetchContent_MakeAvailable(libredxx)
endif()
# pcap
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
if(WIN32)
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
add_definitions(-DWPCAP -DHAVE_REMOTE)
else()
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
endif()
else()
find_package(PCAP REQUIRED)
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
endif(WIN32)
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
# protobuf # protobuf
if(DEFINED ENV{LIBICSNEO_ICSPB_REPO}) if(DEFINED ENV{LIBICSNEO_ICSPB_REPO})
set(LIBICSNEO_ICSPB_REPO "$ENV{LIBICSNEO_ICSPB_REPO}") set(LIBICSNEO_ICSPB_REPO "$ENV{LIBICSNEO_ICSPB_REPO}")
@@ -443,6 +326,59 @@ FetchContent_Declare(icspb
FetchContent_MakeAvailable(icspb) FetchContent_MakeAvailable(icspb)
target_link_libraries(icsneocpp PRIVATE icspb::icspb) target_link_libraries(icsneocpp PRIVATE icspb::icspb)
# libicsbl
if(LIBICSNEO_ENABLE_BOOTLOADER)
if(NOT LIBICSNEO_LIBICSBL_ZIP_URL)
if(DEFINED ENV{LIBICSNEO_LIBICSBL_ZIP_URL})
set(LIBICSNEO_LIBICSBL_ZIP_URL "$ENV{LIBICSNEO_LIBICSBL_ZIP_URL}")
else()
if(NOT LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX)
if(DEFINED ENV{LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX})
set(LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX "$ENV{LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX}")
else()
set(LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX "https://cdn.intrepidcs.net/libicsbl/5ce699ada0ccaadb92cabda6e3537c09b333020e")
endif()
endif()
if(NOT LIBICSNEO_LIBICSBL_VERSION)
if(DEFINED ENV{LIBICSNEO_LIBICSBL_VERSION})
set(LIBICSNEO_LIBICSBL_VERSION "$ENV{LIBICSNEO_LIBICSBL_VERSION}")
else()
set(LIBICSNEO_LIBICSBL_VERSION "26.4.0")
endif()
endif()
if(WIN32)
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8 OR CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "ARM")
message(FATAL_ERROR "Unsupported bootloader architecture: ${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}")
endif()
set(LIBICSNEO_LIBICSBL_ZIP_URL "${LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX}/libicsbl-${LIBICSNEO_LIBICSBL_VERSION}-windows-amd64.zip")
elseif(APPLE)
set(LIBICSNEO_LIBICSBL_ZIP_URL "${LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX}/libicsbl-${LIBICSNEO_LIBICSBL_VERSION}-macos-universal.zip")
else()
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(LIBICSNEO_LIBICSBL_ZIP_URL "${LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX}/libicsbl-${LIBICSNEO_LIBICSBL_VERSION}-linux-x86_64-glibc.zip")
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
set(LIBICSNEO_LIBICSBL_ZIP_URL "${LIBICSNEO_LIBICSBL_ZIP_URL_PREFIX}/libicsbl-${LIBICSNEO_LIBICSBL_VERSION}-linux-aarch64-glibc.zip")
else()
message(FATAL_ERROR "Unsupported CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
endif()
endif()
endif()
FetchContent_Declare(
libicsbl
URL "${LIBICSNEO_LIBICSBL_ZIP_URL}"
)
FetchContent_MakeAvailable(libicsbl)
set(libicsbl_ROOT "${libicsbl_SOURCE_DIR}")
find_package(libicsbl CONFIG REQUIRED)
target_link_libraries(icsneocpp PUBLIC libicsbl::icsbl)
# miniz
add_subdirectory(third-party/miniz)
target_link_libraries(icsneocpp PRIVATE miniz)
endif()
if(LIBICSNEO_BUILD_ICSNEOC) if(LIBICSNEO_BUILD_ICSNEOC)
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc) add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
target_include_directories(icsneoc target_include_directories(icsneoc
@@ -575,7 +511,7 @@ if(LIBICSNEO_BUILD_UNIT_TESTS)
test/unit/apperrordecodertest.cpp test/unit/apperrordecodertest.cpp
test/unit/icsneoc2.cpp test/unit/icsneoc2.cpp
test/unit/ethphyregistertest.cpp test/unit/ethphyregistertest.cpp
test/unit/windowsstrings.cpp test/unit/tc10test.cpp
test/unit/periodictest.cpp test/unit/periodictest.cpp
) )
+1 -1
View File
@@ -30,13 +30,13 @@ Instructions for installing each API can be found in its respective documentatio
- neoVI PLASMA - neoVI PLASMA
- neoVI RED 2 - neoVI RED 2
- RAD-A2B - RAD-A2B
- RAD-Argos
- RAD-Comet 2 - RAD-Comet 2
- RAD-Comet 3 - RAD-Comet 3
- RAD-Epsilon - RAD-Epsilon
- RAD-EpsilonXL - RAD-EpsilonXL
- RAD-Galaxy - RAD-Galaxy
- RAD-Galaxy 2 - RAD-Galaxy 2
- RAD-Gemini
- RAD-Gigastar - RAD-Gigastar
- RAD-Gigastar 2 - RAD-Gigastar 2
- RAD-Moon 2 - RAD-Moon 2
+2 -2
View File
@@ -706,7 +706,7 @@ icsneoc2_error_t icsneoc2_settings_t1s_burst_timer_set(icsneoc2_device_t* device
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_settings_t1s_max_burst_timer_for_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t* value) { icsneoc2_error_t icsneoc2_settings_t1s_max_burst_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t* value) {
// Make sure the device is valid // Make sure the device is valid
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
@@ -725,7 +725,7 @@ icsneoc2_error_t icsneoc2_settings_t1s_max_burst_timer_for_get(icsneoc2_device_t
} }
} }
icsneoc2_error_t icsneoc2_settings_t1s_max_burst_timer_for_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t value) { icsneoc2_error_t icsneoc2_settings_t1s_max_burst_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t value) {
// Make sure the device is valid // Make sure the device is valid
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
+12
View File
@@ -117,6 +117,9 @@ static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is
static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device"; static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device";
static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device."; static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device.";
static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count."; static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count.";
static constexpr const char* TC10_REQUEST_FAILED = "The device rejected the TC10 request.";
static constexpr const char* FIRMWARE_UPDATE_NOT_SUPPORTED = "Firmware updates are not supported for this device.";
static constexpr const char* FIRMWARE_UPDATE_FAILED = "The firmware update failed.";
// Transport Errors // Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed."; static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@@ -168,6 +171,7 @@ static constexpr const char* SERVD_POLL_ERROR = "Error polling on Servd socket";
static constexpr const char* SERVD_NODATA_ERROR = "No data received from Servd"; static constexpr const char* SERVD_NODATA_ERROR = "No data received from Servd";
static constexpr const char* SERVD_JOIN_MULTICAST_ERROR = "Error joining Servd multicast group"; static constexpr const char* SERVD_JOIN_MULTICAST_ERROR = "Error joining Servd multicast group";
static constexpr const char* SERVD_NOT_REACHABLE = "Could not reach Servd; ensure it is installed and running"; static constexpr const char* SERVD_NOT_REACHABLE = "Could not reach Servd; ensure it is installed and running";
static constexpr const char* SERVD_DISABLED = "Servd is disabled via LIBICSNEO_USE_SERVD; no devices will be found";
// DXX // DXX
static constexpr const char* DXX_ERROR_SYS = "System error, check errno/GetLastError()"; static constexpr const char* DXX_ERROR_SYS = "System error, check errno/GetLastError()";
@@ -381,6 +385,12 @@ const char* APIEvent::DescriptionForType(Type type) {
return DISK_FORMAT_NOT_SUPPORTED; return DISK_FORMAT_NOT_SUPPORTED;
case Type::DiskFormatInvalidCount: case Type::DiskFormatInvalidCount:
return DISK_FORMAT_INVALID_COUNT; return DISK_FORMAT_INVALID_COUNT;
case Type::TC10RequestFailed:
return TC10_REQUEST_FAILED;
case Type::FirmwareUpdateNotSupported:
return FIRMWARE_UPDATE_NOT_SUPPORTED;
case Type::FirmwareUpdateFailed:
return FIRMWARE_UPDATE_FAILED;
// VSA // VSA
case Type::VSABufferCorrupted: case Type::VSABufferCorrupted:
@@ -442,6 +452,8 @@ const char* APIEvent::DescriptionForType(Type type) {
return SERVD_JOIN_MULTICAST_ERROR; return SERVD_JOIN_MULTICAST_ERROR;
case Type::ServdNotReachable: case Type::ServdNotReachable:
return SERVD_NOT_REACHABLE; return SERVD_NOT_REACHABLE;
case Type::ServdDisabled:
return SERVD_DISABLED;
// DXX // DXX
case Type::DXXErrorSys: case Type::DXXErrorSys:
+7 -3
View File
@@ -99,9 +99,13 @@ void EventManager::addEventInternal(APIEvent event) {
} }
void EventManager::runCallbacks(APIEvent event) { void EventManager::runCallbacks(APIEvent event) {
std::lock_guard<std::mutex> lk(callbacksMutex); std::map<int, icsneo::EventCallback> tempCallbacks;
for(auto& i : callbacks) {
i.second.callIfMatch(std::make_shared<APIEvent>(event)); std::lock_guard<std::mutex> lk(callbacksMutex);
tempCallbacks = callbacks;
}
for(auto& i : tempCallbacks)
i.second.callIfMatch(std::make_shared<APIEvent>(event));
} }
void EventManager::setEventLimit(size_t newLimit) { void EventManager::setEventLimit(size_t newLimit) {
+2 -2
View File
@@ -1073,8 +1073,8 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
case NEODEVICE_RADMOON3: case NEODEVICE_RADMOON3:
*pDeviceSettingsType = DeviceRADMoon3SettingsType; *pDeviceSettingsType = DeviceRADMoon3SettingsType;
break; break;
case NEODEVICE_RADGEMINI: case NEODEVICE_RADARGOS:
*pDeviceSettingsType = DeviceRADGeminiSettingsType; *pDeviceSettingsType = DeviceRADArgosSettingsType;
break; break;
case NEODEVICE_RED2: case NEODEVICE_RED2:
*pDeviceSettingsType = DeviceRed2SettingsType; *pDeviceSettingsType = DeviceRed2SettingsType;
+13
View File
@@ -26,6 +26,7 @@ pybind11_add_module(icsneopy
icsneopy/communication/message/message.cpp icsneopy/communication/message/message.cpp
icsneopy/communication/message/canmessage.cpp icsneopy/communication/message/canmessage.cpp
icsneopy/communication/message/canerrormessage.cpp icsneopy/communication/message/canerrormessage.cpp
icsneopy/communication/message/iso15765message.cpp
icsneopy/communication/message/ethernetmessage.cpp icsneopy/communication/message/ethernetmessage.cpp
icsneopy/communication/message/linmessage.cpp icsneopy/communication/message/linmessage.cpp
icsneopy/communication/message/tc10statusmessage.cpp icsneopy/communication/message/tc10statusmessage.cpp
@@ -56,6 +57,18 @@ pybind11_add_module(icsneopy
) )
target_link_libraries(icsneopy PRIVATE icsneocpp) target_link_libraries(icsneopy PRIVATE icsneocpp)
if(LIBICSNEO_ENABLE_BOOTLOADER)
target_sources(icsneopy PRIVATE icsneopy/bootloader/bootloader.cpp)
target_compile_definitions(icsneopy PRIVATE ICSNEO_ENABLE_BOOTLOADER)
if(WIN32)
# Windows has no build RPATH; the .pyd only finds DLLs in its own directory
add_custom_command(TARGET icsneopy POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:libicsbl::icsbl>" "$<TARGET_FILE_DIR:icsneopy>"
VERBATIM
)
endif()
endif()
install(TARGETS icsneopy LIBRARY DESTINATION icsneopy) install(TARGETS icsneopy LIBRARY DESTINATION icsneopy)
find_program(STUBGEN_EXE stubgen) find_program(STUBGEN_EXE stubgen)
+4
View File
@@ -92,6 +92,9 @@ void init_event(pybind11::module_& m) {
.value("SettingNotAvaiableDevice", APIEvent::Type::SettingNotAvaiableDevice) .value("SettingNotAvaiableDevice", APIEvent::Type::SettingNotAvaiableDevice)
.value("DiskFormatNotSupported", APIEvent::Type::DiskFormatNotSupported) .value("DiskFormatNotSupported", APIEvent::Type::DiskFormatNotSupported)
.value("DiskFormatInvalidCount", APIEvent::Type::DiskFormatInvalidCount) .value("DiskFormatInvalidCount", APIEvent::Type::DiskFormatInvalidCount)
.value("TC10RequestFailed", APIEvent::Type::TC10RequestFailed)
.value("FirmwareUpdateNotSupported", APIEvent::Type::FirmwareUpdateNotSupported)
.value("FirmwareUpdateFailed", APIEvent::Type::FirmwareUpdateFailed)
.value("FailedToRead", APIEvent::Type::FailedToRead) .value("FailedToRead", APIEvent::Type::FailedToRead)
.value("FailedToWrite", APIEvent::Type::FailedToWrite) .value("FailedToWrite", APIEvent::Type::FailedToWrite)
.value("DriverFailedToOpen", APIEvent::Type::DriverFailedToOpen) .value("DriverFailedToOpen", APIEvent::Type::DriverFailedToOpen)
@@ -127,6 +130,7 @@ void init_event(pybind11::module_& m) {
.value("ServdNoDataError", APIEvent::Type::ServdNoDataError) .value("ServdNoDataError", APIEvent::Type::ServdNoDataError)
.value("ServdJoinMulticastError", APIEvent::Type::ServdJoinMulticastError) .value("ServdJoinMulticastError", APIEvent::Type::ServdJoinMulticastError)
.value("ServdNotReachable", APIEvent::Type::ServdNotReachable) .value("ServdNotReachable", APIEvent::Type::ServdNotReachable)
.value("ServdDisabled", APIEvent::Type::ServdDisabled)
.value("DXXErrorSys", APIEvent::Type::DXXErrorSys) .value("DXXErrorSys", APIEvent::Type::DXXErrorSys)
.value("DXXErrorInt", APIEvent::Type::DXXErrorInt) .value("DXXErrorInt", APIEvent::Type::DXXErrorInt)
.value("DXXErrorOverflow", APIEvent::Type::DXXErrorOverflow) .value("DXXErrorOverflow", APIEvent::Type::DXXErrorOverflow)
@@ -0,0 +1,43 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <pybind11/native_enum.h>
#include "icsneo/device/extensions/bootloader/api.h"
#include "icsneo/device/extensions/bootloader/parser/fwloader.h"
namespace icsneo {
struct BootloaderNamespace {};
void init_bootloader(pybind11::module_& m) {
// Dummy class to act as Bootloader namespace
pybind11::classh<BootloaderNamespace> bootloader(m, "Bootloader");
// Opaque handle returned by FWLoader.load and consumed by update
pybind11::classh<Bootloader::FWRepo>(bootloader, "FWRepo");
pybind11::classh<Bootloader::FWLoader>(bootloader, "FWLoader")
.def_static("load", &Bootloader::FWLoader::Load, pybind11::arg("path"), pybind11::call_guard<pybind11::gil_scoped_release>());
pybind11::native_enum<Bootloader::StatusType>(bootloader, "StatusType", "enum.IntEnum")
.value("Progress", Bootloader::StatusType::Progress)
.value("Failed", Bootloader::StatusType::Failed)
.finalize();
bootloader.def_static("update",
[](Device& device, const Bootloader::FWRepo& fw, Bootloader::StatusHandler statusHandler, bool alreadyInBootloader, bool forceFlash) {
if(!statusHandler) {
statusHandler = [](Bootloader::StatusType, const std::string&, std::optional<double>) {};
}
return Bootloader::update(device, fw, statusHandler, alreadyInBootloader, forceFlash);
},
pybind11::arg("device"),
pybind11::arg("fw"),
pybind11::arg("statusHandler") = pybind11::none(),
pybind11::arg("already_in_bootloader") = false,
pybind11::arg("force_flash") = false,
pybind11::call_guard<pybind11::gil_scoped_release>());
}
} // namespace icsneo
@@ -0,0 +1,48 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "icsneo/communication/message/iso15765message.h"
namespace icsneo {
void init_iso15765message(pybind11::module_& m) {
auto args = pybind11::classh<Iso15765MessageArgs>(m, "Iso15765MessageArgs");
pybind11::classh<Iso15765MessageArgs::ArbId>(args, "ArbId")
.def(pybind11::init<uint32_t, bool>(), pybind11::arg("id") = 0, pybind11::arg("is_29bit") = false)
.def_readwrite("id", &Iso15765MessageArgs::ArbId::Id)
.def_readwrite("is_29bit", &Iso15765MessageArgs::ArbId::Is29Bit);
args.def(pybind11::init<>())
.def_property("tx_index", &Iso15765MessageArgs::getTxIndex, &Iso15765MessageArgs::setTxIndex)
.def_property("arb_id",
[](Iso15765MessageArgs& self) -> Iso15765MessageArgs::ArbId& {
return const_cast<Iso15765MessageArgs::ArbId&>(self.getArbId());
},
&Iso15765MessageArgs::setArbId,
pybind11::return_value_policy::reference_internal)
.def_property("flow_control_arb_id",
[](Iso15765MessageArgs& self) -> Iso15765MessageArgs::ArbId& {
return const_cast<Iso15765MessageArgs::ArbId&>(self.getFlowControlArbId());
},
&Iso15765MessageArgs::setFlowControlArbId,
pybind11::return_value_policy::reference_internal)
.def_property("flow_control_arb_id_mask", &Iso15765MessageArgs::getFlowControlArbIdMask, &Iso15765MessageArgs::setFlowControlArbIdMask)
.def_property("is_can_fd", &Iso15765MessageArgs::getIsCanFd, &Iso15765MessageArgs::setIsCanFd)
.def_property("is_brs_enabled", &Iso15765MessageArgs::getIsBrsEnabled, &Iso15765MessageArgs::setIsBrsEnabled)
.def_property("tx_dl", &Iso15765MessageArgs::getTxDl, &Iso15765MessageArgs::setTxDl)
.def_property("st_min", &Iso15765MessageArgs::getStMin, &Iso15765MessageArgs::setStMin)
.def_property("block_size", &Iso15765MessageArgs::getBlockSize, &Iso15765MessageArgs::setBlockSize)
.def_property("extended_address", &Iso15765MessageArgs::getExtendedAddress, &Iso15765MessageArgs::setExtendedAddress)
.def_property("flow_control_extended_address", &Iso15765MessageArgs::getFlowControlExtendedAddress, &Iso15765MessageArgs::setFlowControlExtendedAddress)
.def_property("padding_value", &Iso15765MessageArgs::getPaddingValue, &Iso15765MessageArgs::setPaddingValue)
.def_property("fs_timeout", &Iso15765MessageArgs::getFsTimeout, &Iso15765MessageArgs::setFsTimeout)
.def_property("fs_wait_timeout", &Iso15765MessageArgs::getFsWaitTimeout, &Iso15765MessageArgs::setFsWaitTimeout)
.def_property("cf_timeout", &Iso15765MessageArgs::getCfTimeout, &Iso15765MessageArgs::setCfTimeout)
.def_property("data",
&Iso15765MessageArgs::getData,
[](Iso15765MessageArgs& self, std::vector<uint8_t> data) { self.setData(std::move(data)); })
.def_property("is_flow_control_enabled", &Iso15765MessageArgs::getIsFlowControlEnabled, &Iso15765MessageArgs::setIsFlowControlEnabled);
}
} // namespace icsneo
+2 -1
View File
@@ -134,7 +134,8 @@ void init_chipid(pybind11::module_& m) {
.value("SFPModule_lan8670_MCHIP", ChipID::SFPModule_lan8670_MCHIP) .value("SFPModule_lan8670_MCHIP", ChipID::SFPModule_lan8670_MCHIP)
.value("RADGigastar2_ZYNQ", ChipID::RADGigastar2_ZYNQ) .value("RADGigastar2_ZYNQ", ChipID::RADGigastar2_ZYNQ)
.value("SFPModule_ent11100_MCHIP", ChipID::SFPModule_ent11100_MCHIP) .value("SFPModule_ent11100_MCHIP", ChipID::SFPModule_ent11100_MCHIP)
.value("RADGemini_MCHIP", ChipID::RADGemini_MCHIP) .value("RADArgos_MCHIP", ChipID::RADArgos_MCHIP)
.value("SFPModule_ad1101_MCHIP", ChipID::SFPModule_ad1101_MCHIP)
.value("Invalid", ChipID::Invalid) .value("Invalid", ChipID::Invalid)
.finalize(); .finalize();
} }
+8 -1
View File
@@ -2,6 +2,7 @@
#include <pybind11/stl.h> #include <pybind11/stl.h>
#include <pybind11/functional.h> #include <pybind11/functional.h>
#include <pybind11/chrono.h> #include <pybind11/chrono.h>
#include <pybind11/native_enum.h>
#include "icsneo/device/device.h" #include "icsneo/device/device.h"
#include "icsneo/device/extensions/deviceextension.h" #include "icsneo/device/extensions/deviceextension.h"
@@ -12,7 +13,9 @@
namespace icsneo { namespace icsneo {
void init_device(pybind11::module_& m) { void init_device(pybind11::module_& m) {
pybind11::classh<Device>(m, "Device") pybind11::classh<Device> device(m, "Device");
device
.def("__repr__", &Device::describe) .def("__repr__", &Device::describe)
.def("add_message_callback", &Device::addMessageCallback, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("add_message_callback", &Device::addMessageCallback, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("clear_script", &Device::clearScript, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("clear_script", &Device::clearScript, pybind11::call_guard<pybind11::gil_scoped_release>())
@@ -45,6 +48,10 @@ void init_device(pybind11::module_& m) {
.def("is_online_supported", &Device::isOnlineSupported) .def("is_online_supported", &Device::isOnlineSupported)
.def("is_online", &Device::isOnline) .def("is_online", &Device::isOnline)
.def("is_open", &Device::isOpen) .def("is_open", &Device::isOpen)
.def("iso15765_enable", &Device::iso15765Enable, pybind11::arg("network"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("iso15765_disable_all", &Device::iso15765DisableAll, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("iso15765_transmit_message", &Device::iso15765TransmitMessage, pybind11::arg("network"), pybind11::arg("message"), pybind11::arg("timeout"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("iso15765_setup_rx_flow_control", &Device::iso15765SetupRxFlowControl, pybind11::arg("network"), pybind11::arg("message"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("open", [](Device& device) { return device.open(); }, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("open", [](Device& device) { return device.open(); }, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("prepare_script_load", &Device::prepareScriptLoad, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("prepare_script_load", &Device::prepareScriptLoad, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("remove_message_callback", &Device::removeMessageCallback, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("remove_message_callback", &Device::removeMessageCallback, pybind11::call_guard<pybind11::gil_scoped_release>())
@@ -38,7 +38,7 @@ void init_devicetype(pybind11::module_& m) {
.value("RADGalaxy2", DeviceType::Enum::RADGalaxy2) .value("RADGalaxy2", DeviceType::Enum::RADGalaxy2)
.value("RADwBMS", DeviceType::Enum::RADwBMS) .value("RADwBMS", DeviceType::Enum::RADwBMS)
.value("RADMoon3", DeviceType::Enum::RADMoon3) .value("RADMoon3", DeviceType::Enum::RADMoon3)
.value("RADGemini", DeviceType::Enum::RADGemini) .value("RADArgos", DeviceType::Enum::RADArgos)
.value("RADComet2", DeviceType::Enum::RADComet2) .value("RADComet2", DeviceType::Enum::RADComet2)
.value("FIRE3_FlexRay", DeviceType::Enum::FIRE3_FlexRay) .value("FIRE3_FlexRay", DeviceType::Enum::FIRE3_FlexRay)
.value("FIRE3_T1S_LIN", DeviceType::Enum::FIRE3_T1S_LIN) .value("FIRE3_T1S_LIN", DeviceType::Enum::FIRE3_T1S_LIN)
+8
View File
@@ -15,6 +15,7 @@ void init_devicetype(pybind11::module_&);
void init_message(pybind11::module_&); void init_message(pybind11::module_&);
void init_canmessage(pybind11::module_&); void init_canmessage(pybind11::module_&);
void init_canerrormessage(pybind11::module_&); void init_canerrormessage(pybind11::module_&);
void init_iso15765message(pybind11::module_&);
void init_ethernetmessage(pybind11::module_&); void init_ethernetmessage(pybind11::module_&);
void init_linmessage(pybind11::module_&); void init_linmessage(pybind11::module_&);
void init_tc10statusmessage(pybind11::module_&); void init_tc10statusmessage(pybind11::module_&);
@@ -43,6 +44,9 @@ void init_idevicesettings(pybind11::module_&);
void init_ethphymessage(pybind11::module_&); void init_ethphymessage(pybind11::module_&);
void init_livedata(pybind11::module_&); void init_livedata(pybind11::module_&);
void init_livedatamessage(pybind11::module_&); void init_livedatamessage(pybind11::module_&);
#ifdef ICSNEO_ENABLE_BOOTLOADER
void init_bootloader(pybind11::module_&);
#endif
PYBIND11_MODULE(icsneopy, m) { PYBIND11_MODULE(icsneopy, m) {
pybind11::options options; pybind11::options options;
@@ -60,6 +64,7 @@ PYBIND11_MODULE(icsneopy, m) {
init_message(m); init_message(m);
init_canmessage(m); init_canmessage(m);
init_canerrormessage(m); init_canerrormessage(m);
init_iso15765message(m);
init_ethernetmessage(m); init_ethernetmessage(m);
init_linmessage(m); init_linmessage(m);
init_tc10statusmessage(m); init_tc10statusmessage(m);
@@ -86,6 +91,9 @@ PYBIND11_MODULE(icsneopy, m) {
init_device(m); init_device(m);
init_deviceextension(m); init_deviceextension(m);
init_idevicesettings(m); init_idevicesettings(m);
#ifdef ICSNEO_ENABLE_BOOTLOADER
init_bootloader(m);
#endif
m.def("find_all_devices", &FindAllDevices); m.def("find_all_devices", &FindAllDevices);
m.def("get_supported_devices", &GetSupportedDevices); m.def("get_supported_devices", &GetSupportedDevices);
-18
View File
@@ -1,18 +0,0 @@
#!/bin/sh
VERSION="1.10.5"
ROOT="$PWD/libpcap"
SOURCE="$ROOT/source"
BUILD="$ROOT/build"
INSTALL="$ROOT/install"
mkdir -p "$ROOT"
cd "$ROOT" || exit 1
curl -LO "https://www.tcpdump.org/release/libpcap-$VERSION.tar.xz" || exit 1
tar -xf "libpcap-$VERSION.tar.xz" || exit 1
mv "libpcap-$VERSION" "$SOURCE" || exit 1
cmake -D CMAKE_POSITION_INDEPENDENT_CODE=ON -D CMAKE_INSTALL_PREFIX="$INSTALL" -D BUILD_SHARED_LIBS=OFF -D BUILD_WITH_LIBNL=OFF -D DISABLE_DBUS=ON -D DISABLE_LINUX_USBMON=ON -D DISABLE_BLUETOOTH=ON -D DISABLE_NETMAP=ON -D DISABLE_DPDK=ON -D DISABLE_RDMA=ON -D DISABLE_DAG=ON -D DISABLE_SEPTEL=ON -D DISABLE_SNF=ON -D DISABLE_TC=ON -B "$BUILD" -S "$SOURCE" || exit 1
cmake --build "$BUILD" || exit 1
cmake --install "$BUILD" || exit 1
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON -DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_ENABLE_TCP=OFF || exit 1 cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON -DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_ENABLE_BOOTLOADER=ON || exit 1
cmake --build build || exit 1 cmake --build build || exit 1
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
export CIBW_BEFORE_ALL="sh ci/bootstrap-cibuildwheel.sh"
export CIBW_BUILD="*manylinux*" # no musl
export CIBW_ENVIRONMENT="LD_LIBRARY_PATH=/project/fc/libicsbl-src/lib64"
export SKBUILD_CMAKE_DEFINE="LIBICSNEO_ENABLE_BOOTLOADER=ON;ICSPB_BOOTSTRAP_DIR=/project/icspb;FETCHCONTENT_BASE_DIR=/project/fc"
sh ci/build-wheel-posix.sh
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
export CIBW_ARCHS="arm64"
export SKBUILD_CMAKE_DEFINE="LIBICSNEO_ENABLE_BOOTLOADER=ON;ICSPB_BOOTSTRAP_DIR=$PWD/icspb;FETCHCONTENT_BASE_DIR=$PWD/fc"
export REPAIR_LIBRARY_PATH="$PWD/fc/libicsbl-src/lib"
export MACOSX_DEPLOYMENT_TARGET="10.14"
sh ci/build-wheel-posix.sh
+3
View File
@@ -3,6 +3,9 @@
call "%VCVARS64_2022%" call "%VCVARS64_2022%"
set "SKBUILD_CMAKE_DEFINE=LIBICSNEO_ENABLE_BOOTLOADER=ON;ICSPB_BOOTSTRAP_DIR=%CD%\icspb;FETCHCONTENT_BASE_DIR=%CD%\fc"
set "PATH=%CD%\fc\libicsbl-src\bin;%PATH%"
python.exe -m venv env || exit /b 1 python.exe -m venv env || exit /b 1
call env\Scripts\Activate.bat || exit /b 1 call env\Scripts\Activate.bat || exit /b 1
python.exe -m pip install cibuildwheel || exit /b 1 python.exe -m pip install cibuildwheel || exit /b 1
+1 -1
View File
@@ -3,6 +3,6 @@
mkdir build >nul 2>&1 mkdir build >nul 2>&1
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=ON -DLIBICSNEO_BUILD_UNIT_TESTS=ON-DLIBICSNEO_ENABLE_TCP=ON || exit /b 1 cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=ON -DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_ENABLE_BOOTLOADER=ON || exit /b 1
cmake --build build || exit /b 1 cmake --build build || exit /b 1
-74
View File
@@ -1,74 +0,0 @@
# - Try to find libpcap include dirs and libraries
#
# Usage of this module as follows:
#
# find_package(PCAP)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# PCAP_ROOT_DIR Set this variable to the root installation of
# libpcap if the module has problems finding the
# proper installation path.
#
# Variables defined by this module:
#
# PCAP_FOUND System has libpcap, include and library dirs found
# PCAP_INCLUDE_DIR The libpcap include directories.
# PCAP_LIBRARY The libpcap library (possibly includes a thread
# library e.g. required by pf_ring's libpcap)
# HAVE_PF_RING If a found version of libpcap supports PF_RING
find_path(PCAP_ROOT_DIR
NAMES include/pcap.h
)
find_path(PCAP_INCLUDE_DIR
NAMES pcap.h
HINTS ${PCAP_ROOT_DIR}/include
)
find_library(PCAP_LIBRARY
NAMES pcap
HINTS ${PCAP_ROOT_DIR}/lib
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PCAP DEFAULT_MSG
PCAP_LIBRARY
PCAP_INCLUDE_DIR
)
include(CheckCSourceCompiles)
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
check_c_source_compiles("int main() { return 0; }" PCAP_LINKS_SOLO)
set(CMAKE_REQUIRED_LIBRARIES)
# check if linking against libpcap also needs to link against a thread library
if (NOT PCAP_LINKS_SOLO)
find_package(Threads)
if (THREADS_FOUND)
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
check_c_source_compiles("int main() { return 0; }" PCAP_NEEDS_THREADS)
set(CMAKE_REQUIRED_LIBRARIES)
endif ()
if (THREADS_FOUND AND PCAP_NEEDS_THREADS)
set(_tmp ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
list(REMOVE_DUPLICATES _tmp)
set(PCAP_LIBRARY ${_tmp}
CACHE STRING "Libraries needed to link against libpcap" FORCE)
else ()
message(FATAL_ERROR "Couldn't determine how to link against libpcap")
endif ()
endif ()
include(CheckFunctionExists)
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
check_function_exists(pcap_get_pfring_id HAVE_PF_RING)
set(CMAKE_REQUIRED_LIBRARIES)
mark_as_advanced(
PCAP_ROOT_DIR
PCAP_INCLUDE_DIR
PCAP_LIBRARY
)
+7 -1
View File
@@ -9,7 +9,13 @@
using namespace icsneo; using namespace icsneo;
bool Driver::pushRx(const uint8_t* buf, size_t numReceived) { bool Driver::pushRx(const uint8_t* buf, size_t numReceived) {
bool ret = readBuffer.write(buf, numReceived); bool ret;
{
// Write under the lock waitForRx checks its predicate with, or a notify between that check and
// the wait is lost and the reader sleeps the full timeout (1s for the read task) with data ready
std::lock_guard<std::mutex> lk(rxWaitMutex);
ret = readBuffer.write(buf, numReceived);
}
rxWaitCv.notify_all(); rxWaitCv.notify_all();
@@ -152,6 +152,7 @@ static std::vector<uint8_t> EncodeFromMessageLIN(std::shared_ptr<Frame> frame, c
linmsg->linMsgType == LINMessage::Type::LIN_UPDATE_RESPONDER) && dataLen > 0; linmsg->linMsgType == LINMessage::Type::LIN_UPDATE_RESPONDER) && dataLen > 0;
if(hasData) { if(hasData) {
LINMessage::calcChecksum(*linmsg);
// len includes data bytes + 1 checksum byte // len includes data bytes + 1 checksum byte
linpacket->CoreMiniBitsLIN.len = static_cast<uint16_t>(dataLen + 1); linpacket->CoreMiniBitsLIN.len = static_cast<uint16_t>(dataLen + 1);
std::copy(linmsg->data.begin(), linmsg->data.begin() + dataLen, linpacket->data); std::copy(linmsg->data.begin(), linmsg->data.begin() + dataLen, linpacket->data);
+1
View File
@@ -39,6 +39,7 @@ std::shared_ptr<EthPhyMessage> HardwareEthernetPhyRegisterPacket::DecodeToMessag
phyMessage->WriteEnable = (pEntry->WriteEnable != 0u); phyMessage->WriteEnable = (pEntry->WriteEnable != 0u);
phyMessage->Clause45Enable = (pEntry->Clause45Enable != 0u); phyMessage->Clause45Enable = (pEntry->Clause45Enable != 0u);
phyMessage->BusIndex = static_cast<uint8_t>(pEntry->BusIndex); phyMessage->BusIndex = static_cast<uint8_t>(pEntry->BusIndex);
phyMessage->Status = static_cast<uint8_t>(pEntry->status);
phyMessage->Version = static_cast<uint8_t>(pEntry->version); phyMessage->Version = static_cast<uint8_t>(pEntry->version);
if(phyMessage->Clause45Enable) if(phyMessage->Clause45Enable)
phyMessage->Clause45 = pEntry->clause45; phyMessage->Clause45 = pEntry->clause45;
+34 -34
View File
@@ -275,7 +275,7 @@ std::vector<VersionReport> Device::getChipVersions(bool refreshComponents) {
return chipVersions; return chipVersions;
} }
bool Device::open(OpenFlags flags, OpenStatusHandler handler) { bool Device::open(OpenFlags flags) {
if(!com) { if(!com) {
report(APIEvent::Type::Unknown, APIEvent::Severity::Error); report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
return false; return false;
@@ -287,38 +287,31 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
startHeartbeat(); startHeartbeat();
APIEvent::Type attemptErr = attemptToBeginCommunication(); if((flags & OpenFlags::SkipRefreshDeviceState) == 0) {
if(attemptErr != APIEvent::Type::NoErrorFound) { auto refreshErr = refreshDeviceState();
// We could not communicate with the device, let's see if an extension can if(refreshErr != APIEvent::Type::NoErrorFound) {
bool tryAgain = false;
forEachExtension([&tryAgain, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) -> bool {
if(ext->onDeviceCommunicationDead(flags, handler))
tryAgain = true;
return true;
});
if(!tryAgain) {
com->close(); com->close();
report(attemptErr, APIEvent::Severity::Error); stopHeartbeat();
return false; // Extensions couldn't save us report(refreshErr, APIEvent::Severity::Error);
}
attemptErr = attemptToBeginCommunication();
if(attemptErr != APIEvent::Type::NoErrorFound) {
com->close();
report(attemptErr, APIEvent::Severity::Error);
return false; return false;
} }
} }
bool block = false; return true;
forEachExtension([&block, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) { }
if(ext->onDeviceOpen(flags, handler))
return true; APIEvent::Type Device::refreshDeviceState() {
block = true; if(isOnline()) {
return false; return APIEvent::Type::DeviceCurrentlyOnline;
}); }
if(block) // Extensions say no
return false; if(!com || !com->isOpen()) {
return APIEvent::Type::Unknown;
}
APIEvent::Type attemptErr = attemptToBeginCommunication();
if(attemptErr != APIEvent::Type::NoErrorFound) {
return attemptErr;
}
refreshComponentVersions(); refreshComponentVersions();
if(!settings->disabled) { if(!settings->disabled) {
@@ -336,7 +329,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
if(supportsLiveData()) if(supportsLiveData())
clearAllLiveData(); clearAllLiveData();
return true; return APIEvent::Type::NoErrorFound;
} }
bool Device::reconnect(std::chrono::milliseconds timeout, std::chrono::milliseconds interval) { bool Device::reconnect(std::chrono::milliseconds timeout, std::chrono::milliseconds interval) {
@@ -412,7 +405,6 @@ APIEvent::Type Device::attemptToBeginCommunication() {
else else
versions = std::move(*maybeVersions); versions = std::move(*maybeVersions);
// Get component versions before the extension "onDeviceOpen" hooks so that we can properly check verisons
if(supportsComponentVersions()) { if(supportsComponentVersions()) {
if(auto compVersions = com->getComponentVersionsSync()) if(auto compVersions = com->getComponentVersionsSync())
componentVersions = std::move(*compVersions); componentVersions = std::move(*compVersions);
@@ -3659,7 +3651,7 @@ bool Device::requestTC10Wake(Network::NetID network) {
*(Network::NetID*)args.data() = network; *(Network::NetID*)args.data() = network;
auto msg = com->waitForMessageSync([&] { auto msg = com->waitForMessageSync([&] {
return com->sendCommand(ExtendedCommand::RequestTC10Wake, args); return com->sendCommand(ExtendedCommand::RequestTC10Wake, args);
}, std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), std::chrono::milliseconds(1000)); }, std::make_shared<ExtendedResponseFilter>(ExtendedCommand::RequestTC10Wake), std::chrono::milliseconds(1000));
if(!msg) { if(!msg) {
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
@@ -3672,7 +3664,11 @@ bool Device::requestTC10Wake(Network::NetID network) {
return false; return false;
} }
return resp->response == ExtendedResponse::OK; if(resp->response != ExtendedResponse::OK) {
report(APIEvent::Type::TC10RequestFailed, APIEvent::Severity::Error);
return false;
}
return true;
} }
bool Device::requestTC10Sleep(Network::NetID network) { bool Device::requestTC10Sleep(Network::NetID network) {
@@ -3684,7 +3680,7 @@ bool Device::requestTC10Sleep(Network::NetID network) {
*(Network::NetID*)args.data() = network; *(Network::NetID*)args.data() = network;
auto msg = com->waitForMessageSync([&] { auto msg = com->waitForMessageSync([&] {
return com->sendCommand(ExtendedCommand::RequestTC10Sleep, args); return com->sendCommand(ExtendedCommand::RequestTC10Sleep, args);
}, std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), std::chrono::milliseconds(1000)); }, std::make_shared<ExtendedResponseFilter>(ExtendedCommand::RequestTC10Sleep), std::chrono::milliseconds(1000));
if(!msg) { if(!msg) {
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
@@ -3697,7 +3693,11 @@ bool Device::requestTC10Sleep(Network::NetID network) {
return false; return false;
} }
return typed->response == ExtendedResponse::OK; if(typed->response != ExtendedResponse::OK) {
report(APIEvent::Type::TC10RequestFailed, APIEvent::Severity::Error);
return false;
}
return true;
} }
bool Device::reboot(bool safe) { bool Device::reboot(bool safe) {
+6 -41
View File
@@ -1,29 +1,12 @@
#include "icsneo/device/devicefinder.h" #include "icsneo/device/devicefinder.h"
#include "icsneo/platform/devices.h" #include "icsneo/platform/devices.h"
#include "icsneo/device/founddevice.h" #include "icsneo/device/founddevice.h"
#include "generated/extensions/builtin.h"
#include "icsneo/platform/servd.h" #include "icsneo/platform/servd.h"
#ifdef ICSNEO_ENABLE_FIRMIO #ifdef ICSNEO_ENABLE_FIRMIO
#include "icsneo/platform/firmio.h" #include "icsneo/platform/firmio.h"
#endif #endif
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
#include "icsneo/platform/pcap.h"
#endif
#ifdef ICSNEO_ENABLE_CDCACM
#include "icsneo/platform/cdcacm.h"
#endif
#ifdef ICSNEO_ENABLE_DXX
#include "icsneo/platform/dxx.h"
#endif
#ifdef ICSNEO_ENABLE_TCP
#include "icsneo/platform/tcp.h"
#endif
using namespace icsneo; using namespace icsneo;
template<typename T> template<typename T>
@@ -56,22 +39,8 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
} else { } else {
#ifdef ICSNEO_ENABLE_FIRMIO #ifdef ICSNEO_ENABLE_FIRMIO
FirmIO::Find(newDriverFoundDevices); FirmIO::Find(newDriverFoundDevices);
#endif #else
EventManager::GetInstance().add(APIEvent::Type::ServdDisabled, APIEvent::Severity::EventWarning);
#ifdef ICSNEO_ENABLE_TCP
TCP::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
PCAP::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_CDCACM
CDCACM::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_DXX
DXX::Find(newDriverFoundDevices);
#endif #endif
} }
@@ -221,8 +190,8 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<RADMoon3>(dev, newFoundDevices); makeIfSerialMatches<RADMoon3>(dev, newFoundDevices);
#endif #endif
#ifdef __RADGEMINI_H_ #ifdef __RADARGOS_H_
makeIfSerialMatches<RADGemini>(dev, newFoundDevices); makeIfSerialMatches<RADArgos>(dev, newFoundDevices);
#endif #endif
#ifdef __RADMOONDUO_H_ #ifdef __RADMOONDUO_H_
@@ -274,10 +243,6 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
#endif #endif
} }
for(auto& device : newFoundDevices) {
AddBuiltInExtensionsTo(device);
}
// Grab a weak pointer from the new devices // Grab a weak pointer from the new devices
foundDevices.insert(foundDevices.end(), newFoundDevices.begin(), newFoundDevices.end()); foundDevices.insert(foundDevices.end(), newFoundDevices.begin(), newFoundDevices.end());
@@ -388,8 +353,8 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADMoon3::DEVICE_TYPE, RADMoon3::DEVICE_TYPE,
#endif #endif
#ifdef __RADGEMINI_H_ #ifdef __RADARGOS_H_
RADGemini::DEVICE_TYPE, RADArgos::DEVICE_TYPE,
#endif #endif
#ifdef __RADMOONDUO_H_ #ifdef __RADMOONDUO_H_
+366
View File
@@ -0,0 +1,366 @@
#include "icsneo/device/extensions/bootloader/api.h"
#include "icsneo/device/extensions/bootloader/communication/radcommunication.h"
#include "icsneo/device/extensions/bootloader/communication/radmultichipcommunication.h"
#include "icsneo/device/extensions/bootloader/communication/radperiphcommunication.h"
#include "icsneo/device/extensions/bootloader/communication/redcore.h"
#include "icsneo/device/extensions/bootloader/communication/red.h"
#include "icsneo/device/extensions/bootloader/communication/application.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include "icsneo/communication/message/resetstatusmessage.h"
#include "icsneo/api/lifetime.h"
#include <algorithm>
#include <chrono>
#include <set>
#include <thread>
#include <unordered_set>
using namespace icsneo;
using namespace icsneo::Bootloader;
static std::unique_ptr<Bootloader::Communication> getCommunication(
icsneo::Device& device, const FlashPhase& phase, const ChipInfo& chip
) {
switch(phase.comm) {
case BootloaderCommunication::Application:
return std::make_unique<Application>(device, chip, phase.authenticate, phase.encrypt);
case BootloaderCommunication::RED:
return std::make_unique<REDCommunication>(device, chip, phase.authenticate, phase.encrypt);
case BootloaderCommunication::REDCore:
return std::make_unique<REDCoreCommunication>(device, chip);
case BootloaderCommunication::RAD:
return std::make_unique<RADCommunication>(device, chip);
case BootloaderCommunication::RADMultiChip:
return std::make_unique<RADMultiChipCommunication>(device, chip, phase.authenticate, phase.slot);
case BootloaderCommunication::RADGalaxy2Peripheral:
return std::make_unique<RADPeriphCommunication>(device, chip);
case BootloaderCommunication::Invalid:
break;
}
return nullptr;
}
static bool findInfo(icsneo::Device& device, ChipID chip, ChipInfo& info) {
const auto& allInfo = device.getChipInfo();
auto iter = std::find_if(allInfo.begin(), allInfo.end(), [chip](const auto& candidate) { return candidate.id == chip; });
if(iter == allInfo.end()) {
return false;
}
info = *iter;
return true;
}
static const FlashPhase* findCorrespondingFlashPhase(const BootloaderPipeline& pipeline, ChipID chip) {
for(const auto& phase : pipeline.phases) {
if(phase->getType() == BootloaderPhase::Type::Flash) {
const auto* flashPhase = static_cast<const FlashPhase*>(phase.get());
if(flashPhase->chip == chip) {
return flashPhase;
}
}
}
return nullptr;
}
static std::string chipString(ChipID chip) {
return "chip id: " + std::to_string(static_cast<int>(chip));
}
bool Bootloader::requiresUpdate(icsneo::Device& device, const FWRepo& fw) {
const auto versions = device.getChipVersions();
for(const auto& info : device.getChipInfo()) {
if(!info.defaultEnabled || !info.iefName) {
continue;
}
auto iter = std::find_if(versions.begin(), versions.end(), [&info](const auto& ver) { return ver.id == info.id; });
if(iter == versions.end()) {
continue;
}
auto ver = fw.getChipVersion(info.iefName);
if(ver && *ver != iter->makeAppVersion()) {
return true;
}
}
return false;
}
/**
* Open the communication, run the action, then close it
*/
template<typename Action>
static bool withCommunication(Bootloader::Communication* com, const ChipInfo& info, const char* phaseName, Action action) {
if(!com) {
Log::Error(phaseName, "no bootloader communication for " + chipString(info.id));
return false;
}
if(!com->open()) {
Log::Error(phaseName, "failed to open communication for " + chipString(info.id));
return false;
}
if(!action(*com)) {
return false;
}
if(!com->close()) {
Log::Error(phaseName, "failed to close communication for " + chipString(info.id));
return false;
}
return true;
}
static bool runPipeline(
icsneo::Device& device,
const BootloaderPipeline& pipeline,
const FWRepo& fw,
const StatusHandler& statusHandler,
bool alreadyInBootloader,
bool forceFlash
) {
icsneo::Lifetime suppressHeartbeats = device.suppressDisconnects();
const auto versions = device.getChipVersions();
std::unordered_set<icsneo::ChipID> chipsFlashed;
bool updateAll = false;
if(auto iter = pipeline.settings.find(BootloaderSetting::UpdateAll); iter != pipeline.settings.end()) {
updateAll = std::get<bool>(iter->second);
}
for(const auto& phase : pipeline.phases) {
switch(phase->getType()) {
case BootloaderPhase::Type::EnterBootloader: {
if(alreadyInBootloader) {
Log::Trace("EnterBootloaderPhase", "device already in bootloader, skipping");
break;
}
Log::Info("EnterBootloaderPhase", "entering bootloader");
if(!enterBootloader(device)) {
Log::Error("EnterBootloaderPhase", "failed to enter bootloader");
return false;
}
break;
}
case BootloaderPhase::Type::Flash: {
const auto& flashPhase = static_cast<const FlashPhase&>(*phase);
Log::Info("FlashPhase", "started for " + chipString(flashPhase.chip));
ChipInfo info;
if(!findInfo(device, flashPhase.chip, info)) {
Log::Error("FlashPhase", "failed to find chip info for " + chipString(flashPhase.chip));
return false;
}
const auto sourceVer = info.iefName ? fw.getChipVersion(info.iefName) : std::nullopt;
if(!sourceVer) {
Log::Trace("FlashPhase", "skipped, no source version for " + chipString(flashPhase.chip));
break;
}
if(!updateAll && !forceFlash) {
auto iter = std::find_if(versions.begin(), versions.end(), [&info](const auto& ver) { return ver.id == info.id; });
if(iter == versions.end()) {
Log::Trace("FlashPhase", "skipped, chip not reported by the device, " + chipString(flashPhase.chip));
break;
}
if(iter->makeAppVersion() == *sourceVer) {
Log::Trace("FlashPhase", "skipped, chip already at target version, " + chipString(flashPhase.chip));
break;
}
}
auto com = getCommunication(device, flashPhase, info);
if(!withCommunication(com.get(), info, "FlashPhase", [&](Bootloader::Communication& opened) {
if(!opened.flash(fw, statusHandler)) {
Log::Error("FlashPhase", "failed to flash " + chipString(flashPhase.chip));
return false;
}
return true;
})) {
return false;
}
chipsFlashed.insert(info.id);
Log::Info("FlashPhase", "completed successfully for " + chipString(flashPhase.chip));
break;
}
case BootloaderPhase::Type::Finalize: {
const auto& finalizePhase = static_cast<const FinalizePhase&>(*phase);
if(!chipsFlashed.count(finalizePhase.chip)) {
Log::Trace("FinalizePhase", "skipped, chip not flashed, " + chipString(finalizePhase.chip));
break;
}
Log::Info("FinalizePhase", "started for " + chipString(finalizePhase.chip));
const FlashPhase* flashPhase = findCorrespondingFlashPhase(pipeline, finalizePhase.chip);
ChipInfo info;
if(!flashPhase || !findInfo(device, finalizePhase.chip, info)) {
// Should never happen (developer error)
Log::Error("FinalizePhase", "no flash phase or chip info for " + chipString(finalizePhase.chip));
return false;
}
auto com = getCommunication(device, *flashPhase, info);
if(com && !com->hasFinalizationStep()) {
Log::Trace("FinalizePhase", "skipped, no finalization step for " + chipString(finalizePhase.chip));
break;
}
if(!withCommunication(com.get(), info, "FinalizePhase", [&](Bootloader::Communication& opened) {
if(!opened.finalize()) {
Log::Error("FinalizePhase", "failed to finalize " + chipString(finalizePhase.chip));
return false;
}
return true;
})) {
return false;
}
Log::Info("FinalizePhase", "completed successfully for " + chipString(finalizePhase.chip));
break;
}
case BootloaderPhase::Type::Reconnect: {
Log::Info("ReconnectPhase", "reconnecting to device");
if(!device.reconnect(std::chrono::milliseconds(60000))) {
Log::Error("ReconnectPhase", "failed to reconnect to device");
return false;
}
break;
}
case BootloaderPhase::Type::Wait: {
const auto& waitPhase = static_cast<const WaitPhase&>(*phase);
Log::Trace("WaitPhase", "waiting " + std::to_string(waitPhase.timeout.count()) + "ms");
std::this_thread::sleep_for(waitPhase.timeout);
break;
}
case BootloaderPhase::Type::EnterApplication: {
const auto& enterAppPhase = static_cast<const EnterApplicationPhase&>(*phase);
if(!chipsFlashed.count(enterAppPhase.chip)) {
Log::Trace("EnterApplicationPhase", "skipped, chip not flashed, " + chipString(enterAppPhase.chip));
break;
}
Log::Info("EnterApplicationPhase", "started for " + chipString(enterAppPhase.chip));
const FlashPhase* flashPhase = findCorrespondingFlashPhase(pipeline, enterAppPhase.chip);
ChipInfo info;
if(!flashPhase || !findInfo(device, enterAppPhase.chip, info)) {
// Should never happen (developer error)
Log::Error("EnterApplicationPhase", "no flash phase or chip info for " + chipString(enterAppPhase.chip));
return false;
}
auto com = getCommunication(device, *flashPhase, info);
if(!withCommunication(com.get(), info, "EnterApplicationPhase", [&](Bootloader::Communication& opened) {
if(!opened.enterApplication()) {
Log::Error("EnterApplicationPhase", "failed to enter application for " + chipString(enterAppPhase.chip));
return false;
}
return true;
})) {
return false;
}
Log::Info("EnterApplicationPhase", "completed successfully for " + chipString(enterAppPhase.chip));
break;
}
}
}
return exitBootloader(device, statusHandler, !chipsFlashed.empty());
}
bool Bootloader::update(
icsneo::Device& device,
const FWRepo& fw,
StatusHandler statusHandler,
bool alreadyInBootloader,
bool forceFlash
) {
const auto pipeline = device.getBootloader();
if(!pipeline) {
device.getEventHandler()(APIEvent::Type::FirmwareUpdateNotSupported, APIEvent::Severity::Error);
return false;
}
if(!runPipeline(device, pipeline, fw, statusHandler, alreadyInBootloader, forceFlash)) {
device.getEventHandler()(APIEvent::Type::FirmwareUpdateFailed, APIEvent::Severity::Error);
return false;
}
return true;
}
bool Bootloader::enterBootloader(icsneo::Device& device) {
if(!device.com->sendCommand(icsneo::Command::EnterBootloader)) {
return false;
}
return device.reconnect(std::chrono::milliseconds(30000));
}
bool Bootloader::exitBootloader(icsneo::Device& device, StatusHandler statusHandler, bool updatedChips) {
if(!updatedChips) {
return true;
}
statusHandler(StatusType::Progress, "Connecting to the device...", std::nullopt);
return verifyConnection(device, statusHandler);
}
/**
* Toggle network communication and wait up to 5 seconds for a reset status confirming the change.
* Like icsneo::Device::goOnline(), a timeout is not an error.
*/
static bool setCommunicationEnabled(icsneo::Device& device, const StatusHandler& statusHandler, bool enable, double progressStart) {
static constexpr std::chrono::seconds Timeout(5);
const auto filter = std::make_shared<MessageFilter>(Message::Type::ResetStatus);
if(!device.com->sendCommand(Command::EnableNetworkCommunication, enable)) {
return false;
}
device.com->sendCommand(Command::UpdateLEDState, std::vector<uint8_t>({uint8_t(enable ? 0x10 : 0x04)}));
const auto startTime = std::chrono::steady_clock::now();
auto elapsedTime = std::chrono::duration<double>(0);
while(elapsedTime < Timeout) {
bool failOut = false;
auto msg = device.com->waitForMessageSync([&device, &failOut]() {
if(!device.com->sendCommand(Command::RequestStatusUpdate)) {
failOut = true;
return false;
}
return true;
}, filter, std::chrono::milliseconds(100));
if(failOut) {
return false;
}
if(msg && std::static_pointer_cast<icsneo::ResetStatusMessage>(msg)->comEnabled == enable) {
break;
}
// Each call covers half of the overall progress
statusHandler(StatusType::Progress, "Connecting to the device...", progressStart + (elapsedTime / Timeout) / 2.0);
elapsedTime = std::chrono::steady_clock::now() - startTime;
}
statusHandler(StatusType::Progress, "Connecting to the device...", progressStart + 0.5);
return true;
}
bool Bootloader::verifyConnection(icsneo::Device& device, StatusHandler statusHandler) {
// Firmware needs to confirm the update by verifying that it can enable/disable the connection on the device
return setCommunicationEnabled(device, statusHandler, true, 0.0) && setCommunicationEnabled(device, statusHandler, false, 0.5);
}
bool Bootloader::isSFPChip(const icsneo::Bootloader::Chip& chip) {
static const std::set<icsneo::ChipID> sfpChipIDs = {
icsneo::ChipID::SFPModule_88q2112_MCHIP,
icsneo::ChipID::SFPModule_88q2221_MCHIP,
icsneo::ChipID::SFPModule_88q3244_MCHIP,
icsneo::ChipID::SFPModule_ent11100_MCHIP,
icsneo::ChipID::SFPModule_lan8670_MCHIP,
icsneo::ChipID::SFPModule_ad1101_MCHIP,
};
return sfpChipIDs.find(chip.getID()) != sfpChipIDs.end();
}
bool Bootloader::isSFPIEF(const icsneo::Bootloader::IEF& ief) {
static const std::set<icsneo::Device::ProductID> sfpProductIDs = {
icsneo::Device::ProductID::SFPModule_88q2112,
icsneo::Device::ProductID::SFPModule_88q2221m,
icsneo::Device::ProductID::SFPModule_88q4364,
icsneo::Device::ProductID::SFPModule_ent11100,
icsneo::Device::ProductID::SFPModule_lan8670,
icsneo::Device::ProductID::SFPModule_ad1101,
};
return sfpProductIDs.find(static_cast<icsneo::Device::ProductID>(ief.getVersion().prodId)) != sfpProductIDs.end();
}
@@ -0,0 +1,407 @@
#include "icsneo/device/extensions/bootloader/communication/application.h"
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include "icsneo/device/extensions/bootloader/api.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/message/main51message.h"
#include <algorithm>
#include <condition_variable>
#include <cstring>
#include <limits>
#include <mutex>
#include <optional>
#include <thread>
#include <icsbl/icsbl.h>
namespace icsneo {
namespace Bootloader {
bool Application::enterApplication() {
// Always reboot
Log::Info(__func__, "sending reboot command");
bool ok = device.com->sendCommand(icsneo::ExtendedCommand::Reboot);
if(!ok) {
Log::Error(__func__, "reboot command failed");
}
return ok;
}
bool Application::finalize() {
Log::Debug(__func__, "sending finalize command");
bool ok = sendCommand(Update::Finalize, [](double) {});
if(!ok) {
Log::Error(__func__, "finalize command failed");
}
return ok;
}
bool Application::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
Log::Info(__func__, "starting flash for " + targetChip.getName());
const auto& srcVer = fw.getVersion();
std::vector<uint8_t> bin;
if(!fw.getFW(targetChip.getIEFName(), bin)) {
Log::Error(__func__, "failed to get firmware for " + targetChip.getIEFName());
return false;
}
IEF ief;
if(!ief.load(std::move(bin))) {
Log::Error(__func__, "failed to load IEF for " + targetChip.getName());
return false;
}
auto currentString = "Preparing to update " + targetChip.getName() + "...";
handler(StatusType::Progress, currentString, std::nullopt);
// The supplied IEF is not for this product
if(ief.getVersion().prodId != static_cast<uint8_t>(device.getProductID())) {
Log::Error(__func__, "IEF product ID mismatch for " + targetChip.getName());
return false;
}
// The supplied IEF is for this product, but not for this chip
if(ief.getVersion().chipId != static_cast<uint8_t>(targetChip.getID())) {
Log::Error(__func__, "IEF chip ID mismatch for " + targetChip.getName());
return false;
}
if(doAuthChecks) {
if(!authenticate()) {
Log::Error(__func__, "authentication failed for " + targetChip.getName());
return false;
}
// Tell the application software what version of Vehicle Spy 3 this firmware is from.
// We check to make sure it approves, but also, the device will not accept new firmware
// if we have not sent this.
// Sometimes new hardware revisions can not accept firmware which is too old.
if(!sendCommand(Update::CheckVersion, [](double) {}, 0, {srcVer.major, srcVer.minor, srcVer.release, srcVer.build})) {
Log::Error(__func__, "version check failed for " + targetChip.getName());
return false;
}
}
bool ok = sendSoftware(ief.getSectors(), ief.getBlockSize(), handler);
if(!ok) {
Log::Error(__func__, "sendSoftware failed for " + targetChip.getName());
}
return ok;
}
bool Application::authenticate() {
icsbl_seed_v1 seed = {};
if(!icsbl_generate_seed_v1(device.getSerialNumber(), &seed)) {
Log::Error(__func__, "libicsbl could not generate an authentication challenge");
return false;
}
// Send the challenge (seed.a), the device replies with seed.b and one trailing byte
std::vector<uint8_t> cmdArgs(sizeof(seed.a));
writeLE32(cmdArgs.data(), seed.a[0]);
writeLE32(cmdArgs.data() + 4, seed.a[1]);
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::Main51);
auto msg = device.com->waitForMessageSync([&] {
return device.com->sendCommand(icsneo::Command::ValidateDevice, cmdArgs);
}, filter, std::chrono::milliseconds(200));
if(!msg) {
Log::Error(__func__, "No response from the device");
return false;
}
auto main51Msg = std::static_pointer_cast<icsneo::Main51Message>(msg);
if(main51Msg->data.size() != sizeof(seed.b) + 1) {
Log::Error(__func__, "Unexpected authentication reply size from the device");
return false;
}
const bool authenticated = readLE32(main51Msg->data.data()) == seed.b[0] && readLE32(main51Msg->data.data() + 4) == seed.b[1];
if(!authenticated) {
Log::Error(__func__, "The authentication reply does not match, the device did not authenticate");
}
return authenticated;
}
bool Application::validateFirmware(const DeviceAppVersion& chipSrcVer) {
return sendCommand(Update::CheckComponentVersion, [](double) {}, 0, {chipSrcVer.major, chipSrcVer.minor});
}
bool Application::sendSoftware(const std::vector<uint8_t>& bin, const icsneo::Bootloader::StatusHandler& handler) {
Log::Debug(__func__, "starting for " + targetChip.getName() + " (" + std::to_string(bin.size()) + " bytes)");
auto currentString = "Preparing to update " + targetChip.getName() + "...";
handler(StatusType::Progress, currentString, std::nullopt);
if(bin.empty() || bin.size() > std::numeric_limits<uint32_t>::max()) {
Log::Error(__func__, "invalid firmware size for " + targetChip.getName());
return false;
}
const uint32_t totalSize = static_cast<uint32_t>(bin.size());
Log::Debug(__func__, "erasing " + targetChip.getName());
if(!sendCommand(Update::Erase, 0, totalSize, [&](double progress) {
handler(StatusType::Progress, currentString, progress);
}, std::chrono::milliseconds(45000))) {
Log::Error(__func__, "erase failed for " + targetChip.getName());
return false;
}
currentString = "Updating " + targetChip.getName() + "...";
handler(StatusType::Progress, currentString, std::nullopt);
const uint32_t stepSize = std::min<uint32_t>(totalSize, 2048);
uint32_t bytesTransferred = 0;
while(bytesTransferred < totalSize) {
const uint32_t bytesToSend = std::min(stepSize, totalSize - bytesTransferred);
if(!sendCommand(Update::Write, [](double) {}, bytesTransferred, { bin.begin() + bytesTransferred, bin.begin() + bytesTransferred + bytesToSend})) {
Log::Error(__func__, "write failed at offset " + std::to_string(bytesTransferred) + " for " + targetChip.getName());
return false;
}
bytesTransferred += bytesToSend;
handler(StatusType::Progress, currentString, double(bytesTransferred) / totalSize);
}
Log::Debug(__func__, "write complete, validating " + targetChip.getName());
if(!validateComponent()) {
Log::Error(__func__, "validation failed for " + targetChip.getName());
return false;
}
Log::Debug(__func__, "completed successfully for " + targetChip.getName());
return true;
}
bool Application::sendSoftware(const std::vector<IEF::Sector>& sectorPairs, uint32_t blockSize, const icsneo::Bootloader::StatusHandler& handler) {
if(sectorPairs.empty()) {
Log::Error(__func__, "no sectors to flash for " + targetChip.getName());
return false;
}
Log::Debug(__func__, "starting for " + targetChip.getName() + " (" + std::to_string(sectorPairs.size()) + " sectors)");
auto currentString = "Preparing to update " + targetChip.getName() + "...";
handler(StatusType::Progress, currentString, std::nullopt);
uint64_t totalSize = 0;
for(const auto& sector : sectorPairs) {
totalSize += sector.second.size();
}
if(totalSize > std::numeric_limits<uint32_t>::max()) {
Log::Error(__func__, "total size too large for " + targetChip.getName());
return false;
}
if(totalSize == 0) {
Log::Error(__func__, "total size is 0 for " + targetChip.getName());
return false;
}
Log::Debug(__func__, "erasing " + targetChip.getName() + " (" + std::to_string(totalSize) + " bytes)");
if(!sendCommand(Update::Erase, 0, static_cast<uint32_t>(totalSize), [&](double progress) {
handler(StatusType::Progress, currentString, progress);
}, std::chrono::milliseconds(45000))) {
Log::Error(__func__, "erase failed for " + targetChip.getName());
return false;
}
if(isSFPChip(targetChip)) {
// SFPs are not ready to write until 4 seconds after finishing erasing (for some reason)
Log::Trace(__func__, "SFP chip detected, waiting 4000ms after erase");
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
}
currentString = "Updating " + targetChip.getName() + "...";
handler(StatusType::Progress, currentString, std::nullopt);
uint32_t bytesTransferred = 0;
for(const auto& pair : sectorPairs) {
const auto& rawData = pair.second;
const uint32_t offset = pair.first * blockSize;
std::vector<uint8_t> processedData(rawData.begin(), rawData.end());
if(encrypted) {
if(rawData.size() % 8 != 0) {
Log::Error(__func__, "raw data size not aligned");
return false;
}
icsbl_encrypt_v1(device.getSerialNumber(), rawData.data(), rawData.size(), processedData.data());
}
if(!sendCommand(Update::Write, [](double) {}, offset, processedData)) {
Log::Error(__func__, "write failed at offset " + std::to_string(offset) + " for " + targetChip.getName());
return false;
}
bytesTransferred += static_cast<uint32_t>(rawData.size());
handler(StatusType::Progress, currentString, double(bytesTransferred) / double(totalSize));
}
Log::Debug(__func__, "write complete, validating " + targetChip.getName());
bool ok = validateComponent();
if(!ok) {
Log::Error(__func__, "validation failed for " + targetChip.getName());
}
else {
Log::Debug(__func__, "completed successfully for " + targetChip.getName());
}
return ok;
}
bool Application::validateComponent() {
Log::Debug(__func__, "validating " + targetChip.getName());
bool ok = sendCommand(Update::ValidateComponent, [](double) {}, 0, {}, std::chrono::milliseconds(45000));
if(!ok) {
Log::Error(__func__, "validation failed for " + targetChip.getName());
}
return ok;
}
/**
* Send one SoftwareUpdate sub-command and wait for it to complete.
*
* The device answers the request with OK when it is already done, or OperationPending when it will
* finish later. For a pending command we poll Update::GetProgress (except for Write and
* ValidateComponent, which do not report progress) until the device sends a final OK or the timeout
* passes. Progress replies are OperationPending with a SoftwareUpdateCommandHeader whose
* commandSizeOrProgress holds the progress so far, or a GenericReturn with no progress.
*/
bool Application::sendCommand(Update command, uint32_t offset, uint32_t length, const CommandStatusHandler& handler, std::chrono::milliseconds timeout, std::vector<uint8_t> args) {
bool finishedResponse = false;
std::condition_variable finishedResponseCV;
std::mutex finishedResponseMutex;
std::optional<uint32_t> progress;
std::condition_variable progressCV;
std::mutex progressMutex;
bool firstResponse = true;
// Register the callback before sending the command, so that if it goes from pending to finished quickly we will still catch it
const auto callback = std::make_shared<icsneo::MessageCallback>(std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::ExtendedResponse), [&](std::shared_ptr<icsneo::Message> msg) {
if(firstResponse) {
firstResponse = false;
return true; // let waitForMessageSync handle the first message
}
auto extMsg = std::static_pointer_cast<ExtendedResponseMessage>(msg);
if(extMsg->command != ExtendedCommand::SoftwareUpdate) {
Log::Error(__func__, "Got an extended response for a command other than SoftwareUpdate");
return false;
}
if(extMsg->response == ExtendedResponse::OK) {
{
std::scoped_lock lk(finishedResponseMutex);
finishedResponse = true;
}
finishedResponseCV.notify_one();
} else if(extMsg->response == ExtendedResponse::OperationPending) {
if(extMsg->data.size() < sizeof(ExtendedResponseMessage::ResponseHeader)) {
Log::Error(__func__, "The pending response is too short to contain a header");
return false;
}
ExtendedResponseMessage::ResponseHeader respHeader;
std::memcpy(&respHeader, extMsg->data.data(), sizeof(respHeader));
if(respHeader.command == ExtendedCommand::SoftwareUpdate) {
if(respHeader.length != sizeof(SoftwareUpdateCommandHeader) ||
extMsg->data.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + sizeof(SoftwareUpdateCommandHeader)) {
Log::Error(__func__, "The progress response does not contain a SoftwareUpdate header");
return false;
}
SoftwareUpdateCommandHeader resp;
std::memcpy(&resp, extMsg->data.data() + sizeof(ExtendedResponseMessage::ResponseHeader), sizeof(resp));
{
std::scoped_lock lk(progressMutex);
progress.emplace(resp.commandSizeOrProgress);
}
progressCV.notify_one();
} else if(respHeader.command == ExtendedCommand::GenericReturn) {
// No progress information, the final OK comes later
{
std::scoped_lock lk(progressMutex);
progress.emplace(0);
}
progressCV.notify_one();
} else {
Log::Error(__func__, "The pending response is for an unexpected command");
return false;
}
} else {
Log::Error(__func__, "The device reported an error for the SoftwareUpdate command");
return false;
}
return true;
});
const auto finishedCallbackHandle = device.com->addMessageCallback(callback);
Lifetime statusCallbackCleanup([&] {
device.com->removeMessageCallback(finishedCallbackHandle);
});
SoftwareUpdateCommandHeader header = {};
header.componentIdentifier = static_cast<uint32_t>(targetChip.getID());
header.commandType = command;
header.offset = offset;
header.commandSizeOrProgress = length;
header.expansionSlot = targetChip.expansionSlot;
const auto headerBytes = reinterpret_cast<const uint8_t*>(&header);
args.insert(args.begin(), headerBytes, headerBytes + sizeof(header));
{
auto msg = device.com->waitForMessageSync([&] {
return device.com->sendCommand(ExtendedCommand::SoftwareUpdate, args);
}, std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::ExtendedResponse), timeout);
if(!msg) {
Log::Error(__func__, "No response to the SoftwareUpdate command");
return false;
}
auto extMsg = std::static_pointer_cast<ExtendedResponseMessage>(msg);
if(extMsg->command != ExtendedCommand::SoftwareUpdate) {
Log::Error(__func__, "Got an extended response for a command other than SoftwareUpdate");
return false;
}
if(extMsg->response == ExtendedResponse::OK) {
return true;
}
if(extMsg->response != ExtendedResponse::OperationPending) {
Log::Error(__func__, "The device rejected the SoftwareUpdate command");
return false;
}
}
SoftwareUpdateCommandHeader progressHeader = {};
progressHeader.componentIdentifier = static_cast<uint32_t>(targetChip.getID());
progressHeader.commandType = Update::GetProgress;
progressHeader.expansionSlot = targetChip.expansionSlot;
const auto progressHeaderBytes = reinterpret_cast<const uint8_t*>(&progressHeader);
const std::vector<uint8_t> progressArgs(progressHeaderBytes, progressHeaderBytes + sizeof(progressHeader));
const auto deadline = std::chrono::steady_clock::now() + timeout;
// Write and ValidateComponent do not report progress, just wait for the final OK
if(command != Update::Write && command != Update::ValidateComponent) {
while(std::chrono::steady_clock::now() < deadline) {
{
std::unique_lock lk(progressMutex);
progress.reset();
if(!device.com->sendCommand(ExtendedCommand::SoftwareUpdate, progressArgs)) {
Log::Error(__func__, "Failed to send progress request to the device");
return false;
}
if(!progressCV.wait_for(lk, std::chrono::milliseconds(5000), [&]{ return progress.has_value(); })) {
Log::Error(__func__, "No progress response from device");
return false;
}
handler(*progress / double(length));
}
{
std::scoped_lock lk(finishedResponseMutex);
if(finishedResponse) {
break;
}
}
}
}
std::unique_lock lk(finishedResponseMutex);
return finishedResponseCV.wait_until(lk, deadline, [&]{ return finishedResponse; });
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,14 @@
#include "icsneo/device/extensions/bootloader/communication/communication.h"
using namespace icsneo::Bootloader;
uint32_t Communication::readLE32(const uint8_t* buf) {
return (uint32_t)buf[0] | ((uint32_t)buf[1] << 8) | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24);
}
void Communication::writeLE32(uint8_t* buf, uint32_t num) {
buf[0] = num & 0xFFu;
buf[1] = (num >> 8) & 0xFFu;
buf[2] = (num >> 16) & 0xFFu;
buf[3] = (num >> 24) & 0xFFu;
}
@@ -0,0 +1,547 @@
#include "icsneo/device/extensions/bootloader/communication/radcommunication.h"
#include "icsneo/device/extensions/bootloader/communication/bootloaderversion.h"
#include "icsneo/device/extensions/bootloader/parser/fwzip.h"
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <algorithm>
#include <cstring>
#include <thread>
#include <icsbl/icsbl.h>
using namespace icsneo::Bootloader;
RADCommunication::RADCommunication(icsneo::Device& dev, const ChipInfo& info)
: Communication(dev), targetChip(info) {}
bool RADCommunication::supportsFlashBuffer() const {
switch(targetChip.getID()) {
case icsneo::ChipID::RADGigastar_USBZ_ZYNQ:
case icsneo::ChipID::RADGigastar_USBZ_Z7007S_ZYNQ:
case icsneo::ChipID::RADGigastar_USBZ_Z7010_ZYNQ: {
return true;
}
default:
return false;
}
}
RADCommunication::Request RADCommunication::makeRequest(Request::Type type, size_t payloadSize, size_t expectedResponseLen) {
const size_t headerSize = supportsZynqBinaryFlasher ? ZynqFlasherHeaderSize : 0;
const size_t packetSize = headerSize + 1 + payloadSize; // Header + command + payload
// This shouldn't cause any re-allocations since we already reserved the MaxMessageLength in initialize()
txBuffer.resize(packetSize, 0);
if(supportsZynqBinaryFlasher) {
std::copy(std::begin(ZynqFlasherSync), std::end(ZynqFlasherSync), txBuffer.begin());
// The length covers the command byte and the payload
const uint16_t payloadPlusCmd = static_cast<uint16_t>(payloadSize) + 1;
txBuffer[4] = payloadPlusCmd & 0xFFu;
txBuffer[5] = (payloadPlusCmd >> 8) & 0xFFu;
}
txBuffer[headerSize] = static_cast<uint8_t>(type);
Request newRequest;
newRequest.type = type;
newRequest.expectedResponseLen = expectedResponseLen;
if(payloadSize > 0) {
newRequest.payload = &txBuffer[headerSize + 1]; // First byte right after the command
}
return newRequest;
}
bool RADCommunication::sendRequestNoResponse() {
/**
* Send our current packet, the payload was written to it if the user
* of this function wrote to request.payload. The remaining details were
* written when makeRequest() was called
*/
return device.com->sendPacket(txBuffer);
}
RADCommunication::Response& RADCommunication::sendRequest(const Request& request, std::chrono::milliseconds timeout) {
static constexpr size_t ResponseHeaderSize = 2;
const size_t rxSize = ResponseHeaderSize + request.expectedResponseLen;
/**
* Clear previous response
*/
lastResponse = Response();
if(!sendRequestNoResponse()) {
Log::Error(__func__, "Failed to send request " + std::to_string(static_cast<uint8_t>(request.type)));
return lastResponse;
}
auto& rb = device.com->driver->getReadBuffer();
rb.clear();
auto result = device.com->driver->waitForRx(rxSize, timeout);
if(!result) {
Log::Error(__func__, "No response to request " + std::to_string(static_cast<uint8_t>(request.type)));
return lastResponse;
}
rb.read(rxBuffer.data(), 0, rxSize);
rb.clear();
lastResponse.done = true;
/**
* Second byte is success/failure byte
*/
lastResponse.type = static_cast<Response::Type>(rxBuffer[1]);
/**
* The rest is the payload, if we requested any
*/
if(request.expectedResponseLen > 0) {
lastResponse.payload = &rxBuffer[2];
lastResponse.size = request.expectedResponseLen;
}
return lastResponse;
}
bool RADCommunication::enterApplication() {
Log::Info(__func__, "sending EnterApplication request");
makeRequest(Request::Type::EnterApplication);
bool ok = sendRequestNoResponse();
if(!ok) {
Log::Error(__func__, "EnterApplication request failed");
}
return ok;
}
bool RADCommunication::close() {
device.com->resumeReads();
return true;
}
bool RADCommunication::open() {
device.com->pauseReads();
if(!initialize()) {
close();
return false;
}
if(!sanityCheck()) {
// Note: RAD-Gigastar on low power USB flash fails here for some reason, try another sanity check
if(!sanityCheck()) {
close();
return false;
}
}
if(!getBootloaderVersion()) {
// Try again, just like the sanity checks
if(!getBootloaderVersion()) {
close();
return false;
}
}
if(!sanityCheck()) {
// Note: RAD-Gigastar on low power USB flash fails here for some reason, try another sanity check
if(!sanityCheck()) {
close();
return false;
}
}
if(!authenticate()) {
close();
return false;
}
return true;
}
bool RADCommunication::initialize() {
auto& rb = device.com->driver->getReadBuffer();
rxBuffer = std::vector<uint8_t>(MaxMessageLength, 0);
txBuffer.reserve(MaxMessageLength);
// An EnterBootloader command on Main51, as a framed short packet (0xAA, then length << 4 | netid)
// with a trailing 0xAF, followed by the same command without the 0xAA framing
const uint8_t lengthAndNetID = (1 << 4) | static_cast<uint8_t>(icsneo::Network::NetID::Main51);
std::vector<uint8_t> pkt = {
0xAA,
lengthAndNetID,
static_cast<uint8_t>(icsneo::Command::EnterBootloader),
0xAF
};
rb.clear();
if(!device.com->sendPacket(pkt)) {
Log::Error(__func__, "Failed to send the framed enter bootloader command");
return false;
}
pkt = {
lengthAndNetID,
static_cast<uint8_t>(icsneo::Command::EnterBootloader)
};
if(!device.com->sendPacket(pkt)) {
Log::Error(__func__, "Failed to send the unframed enter bootloader command");
return false;
}
// The Zynq binary flasher answers with the command, Success, then ZynqFlasherSync
static constexpr size_t ZynqFlasherReplySize = 2 + sizeof(ZynqFlasherSync);
if(!device.com->driver->waitForRx(ZynqFlasherReplySize, std::chrono::milliseconds(1000))) {
Log::Error(__func__, "No response to the enter bootloader command");
return false;
}
const size_t rxLen = rb.size();
if(rxLen == ZynqFlasherReplySize) {
rb.read(rxBuffer.data(), 0, rxLen);
}
rb.clear();
supportsZynqBinaryFlasher =
rxLen == ZynqFlasherReplySize &&
rxBuffer[0] == static_cast<uint8_t>(icsneo::Command::EnterBootloader) &&
rxBuffer[1] == static_cast<uint8_t>(Response::Type::Success) &&
std::equal(std::begin(ZynqFlasherSync), std::end(ZynqFlasherSync), rxBuffer.begin() + 2);
return true;
}
bool RADCommunication::finalize() {
return true;
}
bool RADCommunication::hasFinalizationStep() const {
return false;
}
bool RADCommunication::sanityCheck() {
if(!supportsZynqBinaryFlasher) {
return true;
}
// Two numbered test patterns the bootloader must accept, the 8 byte replies are not checked
constexpr uint8_t PatternA[8] = {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0xAA, 0x55, 0x55};
constexpr uint8_t PatternB[8] = {0x12, 0x21, 0x34, 0x43, 0x56, 0x65, 0x78, 0x87};
auto request = makeRequest(Request::Type::SanityCheck, 1 + sizeof(PatternA), sizeof(PatternA));
request.payload[0] = 0x01u;
std::copy(std::begin(PatternA), std::end(PatternA), request.payload + 1);
if(!sendRequest(request, std::chrono::milliseconds(1000))) {
return false;
}
request.payload[0] = 0x02u;
std::copy(std::begin(PatternB), std::end(PatternB), request.payload + 1);
return sendRequest(request, std::chrono::milliseconds(1000));
}
bool RADCommunication::getBootloaderVersion() {
auto request = makeRequest(Request::Type::BootloaderVersion, 0, sizeof(BootloaderVersionResponse));
auto response = sendRequest(request, std::chrono::milliseconds(1000));
if(!response) {
return false;
}
if(response.size < sizeof(BootloaderVersionResponse)) {
return false;
}
BootloaderVersionResponse version;
std::memcpy(&version, response.payload, sizeof(version));
targetChip.info.id = static_cast<ChipID>(version.zynqId);
return true;
}
bool RADCommunication::validateChipVersion(const icsneo::DeviceAppVersion& chipSrcVer) {
if(!supportsZynqBinaryFlasher) {
return true;
}
auto request = makeRequest(Request::Type::ValidateVersion, 2, 2);
request.payload[0] = chipSrcVer.major;
request.payload[1] = chipSrcVer.minor;
return sendRequest(request, std::chrono::milliseconds(1000));
}
bool RADCommunication::authenticate() {
icsbl_seed_v1 seed = {};
if(!icsbl_generate_seed_v1(device.getSerialNumber(), &seed)) {
Log::Error(__func__, "libicsbl could not generate an authentication challenge");
return false;
}
auto request = makeRequest(Request::Type::Authenticate, sizeof(seed.a), sizeof(seed.b));
writeLE32(&request.payload[0], seed.a[0]);
writeLE32(&request.payload[4], seed.a[1]);
auto response = sendRequest(request, std::chrono::milliseconds(2000));
if(!response) {
return false;
}
return seed.b[0] == readLE32(&response.payload[0]) && seed.b[1] == readLE32(&response.payload[4]);
}
bool RADCommunication::sendSoftware(const std::vector<uint8_t>& chipSoftware) {
Log::Debug(__func__, "starting for " + targetChip.getName() + " (" + std::to_string(chipSoftware.size()) + " bytes)");
static constexpr size_t MaxEthernetPayload = 1487;
if(supportsZynqBinaryFlasher && chipSoftware.size() < sizeof(uint32_t)) {
Log::Error(__func__, "firmware too short to contain a CRC for " + targetChip.getName());
return false;
}
Log::Trace(__func__, "sending BeginTransfer for " + targetChip.getName());
auto request = makeRequest(Request::Type::BeginTransfer, 0, 0);
auto response = sendRequest(request, std::chrono::milliseconds(10000));
if(!response) {
Log::Error(__func__, "BeginTransfer failed for " + targetChip.getName());
return false;
}
const size_t headerSize = 1 + (supportsZynqBinaryFlasher ? ZynqFlasherHeaderSize : 0); // Command byte plus the optional Zynq flasher header
const size_t maxPayloadSize = MaxEthernetPayload - headerSize;
size_t remainingBytes = chipSoftware.size();
size_t currentlyReading = 0;
statusHandler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
std::nullopt
);
while(remainingBytes > 0) {
const double progress = static_cast<double>(currentlyReading) / static_cast<double>(chipSoftware.size());
statusHandler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
progress
);
size_t numToSend = std::min<size_t>(maxPayloadSize, remainingBytes);
request = makeRequest(Request::Type::FWPayload, numToSend, supportsZynqBinaryFlasher ? 2 : 0);
std::copy(
chipSoftware.begin() + currentlyReading,
chipSoftware.begin() + currentlyReading + numToSend,
request.payload
);
if(supportsFlashBuffer()) {
if(!sendRequest(request, std::chrono::milliseconds(2000))) {
return false;
}
} else if(!sendRequestNoResponse()) {
return false;
}
currentlyReading += numToSend;
remainingBytes -= numToSend;
if(device.com->driver->isEthernet()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
Log::Trace(__func__, "sending EndTransfer for " + targetChip.getName());
request = makeRequest(Request::Type::EndTransfer, 0, 0);
response = sendRequest(request, std::chrono::milliseconds(10000));
if(!response) {
Log::Error(__func__, "EndTransfer failed for " + targetChip.getName());
return false;
}
request = makeRequest(Request::Type::DownloadedBinInfo, 0, 4);
response = sendRequest(request, std::chrono::milliseconds(6000));
if(!response) {
Log::Error(__func__, "DownloadedBinInfo failed for " + targetChip.getName());
return false;
}
const uint32_t numDownloaded = readLE32(response.payload);
if(numDownloaded != static_cast<uint32_t>(chipSoftware.size())) {
Log::Error(__func__, "downloaded size mismatch for " + targetChip.getName() + ": expected " + std::to_string(chipSoftware.size()) + ", got " + std::to_string(numDownloaded));
return false;
}
if(supportsZynqBinaryFlasher) {
request = makeRequest(Request::Type::VerifyCRC, sizeof(uint32_t), 2);
/**
* Copy CRC from end of the binary
*/
std::copy(chipSoftware.end() - sizeof(uint32_t), chipSoftware.end(), request.payload);
response = sendRequest(request, std::chrono::milliseconds(2000));
if(!response) {
return false;
}
// The first response byte is 1 when the CRC matched
if(response.payload[0] != 0x01) {
Log::Error(__func__, "CRC mismatch for " + targetChip.getName());
return false;
}
}
statusHandler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
1.0
);
Log::Debug(__func__, "completed successfully for " + targetChip.getName());
return true;
}
bool RADCommunication::getProgrammingStatus(size_t sizeToValidate) {
Log::Debug(__func__, "starting for " + targetChip.getName());
auto request = makeRequest(Request::Type::ProgramFlash, 0, supportsZynqBinaryFlasher ? 2 : 0);
if(!sendRequest(request, std::chrono::milliseconds(2000))) {
Log::Error(__func__, "ProgramFlash failed for " + targetChip.getName());
return false;
}
request = makeRequest(Request::Type::ProgrammingStatus, 0, 4);
uint32_t status = 0;
const auto beginTime = std::chrono::steady_clock::now();
statusHandler(
StatusType::Progress,
"Programming " + targetChip.getName() + "...",
std::nullopt
);
while(status < static_cast<uint32_t>(sizeToValidate)) {
const double progress = static_cast<double>(status) / static_cast<double>(sizeToValidate);
statusHandler(
StatusType::Progress,
"Programming " + targetChip.getName() + "...",
progress
);
const auto& response = sendRequest(request, std::chrono::milliseconds(60000));
if(!response) {
return false;
}
status = readLE32(response.payload);
if(std::chrono::steady_clock::now() - beginTime >= std::chrono::milliseconds(180000)) {
return false;
}
}
statusHandler(
StatusType::Progress,
"Programming " + targetChip.getName() + "...",
1.0
);
Log::Debug(__func__, "completed successfully for " + targetChip.getName());
return true;
}
bool RADCommunication::loadZips(const FWRepo& fw, std::vector<uint8_t>& chipSoftware) {
if(const auto& segmentNames = targetChip.getMultiIEFName(); !segmentNames.empty()) {
std::vector<std::unique_ptr<FWZip>> zips;
zips.reserve(segmentNames.size());
size_t totalSize = 0;
for(const auto& name : segmentNames) {
std::vector<uint8_t> segment;
if(!fw.getFW(name, segment)) {
return false;
}
zips.push_back(std::make_unique<FWZip>(std::move(segment)));
auto& zip = zips.back();
if(!zip->size()) {
return false;
}
totalSize += *zip->size();
}
chipSoftware.resize(totalSize, 0);
size_t offset = 0;
for(auto& zip : zips) {
if(!zip->readAll(chipSoftware.data() + offset)) {
return false;
}
offset += *(zip->size());
}
return true;
} else if(!targetChip.getIEFName().empty()) {
std::vector<uint8_t> bin;
if(!fw.getFW(targetChip.getIEFName(), bin)) {
return false;
}
FWZip fwZip(std::move(bin));
if(!fwZip.readAll(chipSoftware)) {
return false;
}
return true;
}
// Should never happen
return false;
}
bool RADCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
Log::Info(__func__, "starting flash for " + targetChip.getName());
auto chipSrcVer = fw.getChipVersion(targetChip.getIEFName());
if(!chipSrcVer) {
Log::Error(__func__, "no chip version found for " + targetChip.getIEFName());
return false;
}
std::vector<uint8_t> chipSoftware;
if(!loadZips(fw, chipSoftware)) {
Log::Error(__func__, "loadZips failed for " + targetChip.getName());
return false;
}
statusHandler = handler;
if(!validateChipVersion(*chipSrcVer)) {
Log::Error(__func__, "validateChipVersion failed for " + targetChip.getName());
return false;
}
if(!sendSoftware(chipSoftware)) {
Log::Error(__func__, "sendSoftware failed for " + targetChip.getName());
return false;
}
if(!supportsFlashBuffer()) {
if(!getProgrammingStatus(chipSoftware.size())) {
Log::Error(__func__, "getProgrammingStatus failed for " + targetChip.getName());
return false;
}
}
Log::Info(__func__, "completed successfully for " + targetChip.getName());
return true;
}
@@ -0,0 +1,144 @@
#include "icsneo/device/extensions/bootloader/communication/radmultichipcommunication.h"
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include "icsneo/device/extensions/bootloader/parser/ief.h"
#include "icsneo/device/extensions/bootloader/api.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <algorithm>
#include <thread>
using namespace icsneo::Bootloader;
RADMultiChipCommunication::RADMultiChipCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash, bool auth, uint8_t slot)
: RADCommunication(dev, chipToFlash), doAuthChecks(auth), slot(slot) {}
bool RADMultiChipCommunication::authenticate() {
if(!doAuthChecks) {
Log::Trace(__func__, "auth checks disabled, skipping");
return true;
}
Log::Debug(__func__, "starting authentication");
if(!standardComm->authenticate()) {
Log::Warning(__func__, "standardComm auth failed, retrying with sanity check");
device.com->pauseReads();
if(!sanityCheck()) {
if(!sanityCheck()) {
device.com->resumeReads();
Log::Error(__func__, "sanity check failed after retries");
return false;
}
}
if(!RADCommunication::authenticate()) {
device.com->resumeReads();
Log::Error(__func__, "RADCommunication auth failed");
return false;
}
device.com->resumeReads();
}
Log::Debug(__func__, "authentication successful");
return true;
}
bool RADMultiChipCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
Log::Info(__func__, "starting flash for " + targetChip.getName());
auto chipSrcVer = fw.getChipVersion(targetChip.getIEFName());
if(!chipSrcVer) {
Log::Error(__func__, "no chip version found for " + targetChip.getIEFName());
return false;
}
if(!authenticate()) {
Log::Error(__func__, "authentication failed for " + targetChip.getName());
return false;
}
if(!standardComm->validateFirmware(*chipSrcVer)) {
Log::Error(__func__, "firmware validation failed for " + targetChip.getName());
handler(StatusType::Failed, "Failed to validate firmware", std::nullopt);
return false;
}
if(targetChip.getFirmwareType() == FirmwareType::Zip) {
std::vector<uint8_t> chipSoftware;
if(!loadZips(fw, chipSoftware)) {
Log::Error(__func__, "loadZips failed for " + targetChip.getName());
return false;
}
if(!standardComm->sendSoftware(chipSoftware, handler)) {
return false;
}
} else {
std::vector<uint8_t> bin;
if(!fw.getFW(targetChip.getIEFName(), bin)) {
return false;
}
IEF ief;
if(!ief.load(std::move(bin))) {
return false;
}
// The supplied IEF is not for this product
if(ief.getVersion().prodId != static_cast<uint8_t>(device.getProductID()) && !isSFPIEF(ief)) {
Log::Error(__func__, "IEF product ID mismatch for " + targetChip.getName());
return false;
}
// The supplied IEF is for this product, but not for this chip
if(ief.getVersion().chipId != static_cast<uint8_t>(targetChip.getID())) {
Log::Error(__func__, "IEF chip ID mismatch for " + targetChip.getName());
return false;
}
if(!standardComm->sendSoftware(ief.getSectors(), ief.getBlockSize(), handler)) {
return false;
}
}
Log::Debug(__func__, "calling finalize");
if(!standardComm->finalize()) {
Log::Error(__func__, "finalize failed");
return false;
}
Log::Debug(__func__, "finalize successful, proceeding to reboot");
handler(StatusType::Progress, "Rebooting device...", std::nullopt);
return true;
}
bool RADMultiChipCommunication::enterApplication() {
Log::Info(__func__, "starting");
if(!standardComm->enterApplication()) {
Log::Error(__func__, "reboot failed");
return false;
}
Log::Debug(__func__, "reboot sent, waiting 5000ms");
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
return true;
}
bool RADMultiChipCommunication::open() {
const auto& components = device.getComponentVersions();
auto component = std::find_if(components.begin(), components.end(), [this](const auto& candidate) {
if(slot != UINT8_MAX && slot != candidate.expansionSlot) {
return false;
}
return targetChip.getID() == (ChipID)candidate.identifier;
});
if(component == components.end()) {
Log::Error(__func__, "no component found for " + targetChip.getName());
return false;
}
standardComm = std::make_unique<Application>(device, targetChip.info);
standardComm->setExpansionSlot(component->expansionSlot);
// Pause the read thread so it doesn't consume any responses
device.com->pauseReads();
const bool res = initialize();
device.com->resumeReads();
return res;
}
bool RADMultiChipCommunication::close() {
return true;
}
@@ -0,0 +1,334 @@
#include "icsneo/device/extensions/bootloader/communication/radperiphcommunication.h"
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <cstddef>
#include <thread>
#include <icsbl/icsbl.h>
using namespace icsneo::Bootloader;
RADPeriphCommunication::RADPeriphCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash)
: Communication(dev), targetChip(chipToFlash) {}
RADPeriphCommunication::~RADPeriphCommunication() {
if(device.com->readsArePaused()) {
device.com->resumeReads();
}
}
bool RADPeriphCommunication::sendRequestNoResponse(Request req, std::vector<uint8_t>&& args) {
return sendRequestNoResponse(req, targetChip.getID(), std::move(args));
}
bool RADPeriphCommunication::sendRequestNoResponse(Request req, ChipID id, std::vector<uint8_t>&& args) {
args.insert(args.begin(), {
(uint8_t)id,
(uint8_t)req,
});
return device.com->sendCommand(icsneo::Command::PeripheralBootloader, args);
}
RADPeriphCommunication::Response RADPeriphCommunication::sendRequest(Request req, std::vector<uint8_t>&& args, std::chrono::milliseconds timeout) {
return sendRequest(req, targetChip.getID(), std::move(args), timeout);
}
RADPeriphCommunication::Response RADPeriphCommunication::sendRequest(Request req, ChipID id, std::vector<uint8_t>&& args, std::chrono::milliseconds timeout) {
// Total reply sizes, including the header bytes
size_t expectedResponseLength = 0;
bool checkBlAccepted = false;
switch(req) {
case Request::Mode:
expectedResponseLength = 6;
break;
case Request::Version:
expectedResponseLength = 24;
checkBlAccepted = true;
break;
case Request::Authenticate:
expectedResponseLength = 13;
checkBlAccepted = true;
break;
case Request::Echo:
expectedResponseLength = 3;
break;
case Request::Erase:
case Request::FirmwareValidate:
case Request::FlashSector:
expectedResponseLength = 3;
checkBlAccepted = true;
break;
default:
expectedResponseLength = 3;
break;
}
auto& rb = device.com->driver->getReadBuffer();
rb.clear();
if(!sendRequestNoResponse(req, id, std::move(args))) {
Log::Error(__func__, "Failed to send request " + std::to_string(static_cast<uint8_t>(req)));
return {};
}
auto result = device.com->driver->waitForRx(expectedResponseLength, timeout);
if(!result) {
Log::Error(__func__, "No response to request " + std::to_string(static_cast<uint8_t>(req)));
return {};
}
Response resp;
resp.data.resize(expectedResponseLength, 0);
rb.read(resp.data.data(), 0, expectedResponseLength);
resp.success = resp.data[ReplySuccessOffset] == ReplySuccess;
if(!resp.success) {
Log::Error(__func__, "The peripheral reported failure for request " + std::to_string(static_cast<uint8_t>(req)));
return resp;
}
if(checkBlAccepted && resp.data[ReplyAcceptedOffset] != ReplyAccepted) {
Log::Error(__func__, "The bootloader rejected request " + std::to_string(static_cast<uint8_t>(req)));
return {};
}
return resp;
}
RADPeriphCommunication::Mode RADPeriphCommunication::getMode() {
auto resp = sendRequest(Request::Mode, {}, std::chrono::milliseconds(2000));
if(!resp) {
return Mode::NoAnswer;
}
static constexpr size_t ModeOffset = 4;
return (Mode)resp.data[ModeOffset];
}
bool RADPeriphCommunication::sendPeriphTest() {
// Echo is sent to chip id 0, the reply carries the bitwise inverse of the test byte
const uint8_t testByte = 0x00;
auto resp = sendRequest(Request::Echo, (ChipID)0x00, {testByte}, std::chrono::milliseconds(2000));
if(!resp) {
return false;
}
if(resp.data[2] != static_cast<uint8_t>(~testByte)) {
Log::Error(__func__, "The peripheral echo reply did not match");
return false;
}
return true;
}
bool RADPeriphCommunication::readSerialNumber() {
auto resp = sendRequest(Request::Version, {0x00}, std::chrono::milliseconds(2000));
if(!resp) {
return false;
}
// The reply payload is the start of a BootloaderVersionResponse
static constexpr size_t SerialOffset = ReplyHeaderSize + offsetof(BootloaderVersionResponse, serialNum);
serialNumber = readLE32(resp.data.data() + SerialOffset);
return true;
}
bool RADPeriphCommunication::authenticate() {
icsbl_seed_v1 seed = {};
if(!icsbl_generate_seed_v1(serialNumber, &seed)) {
Log::Error(__func__, "libicsbl could not generate an authentication challenge");
return false;
}
std::vector<uint8_t> args(BootloaderArgsOffset + sizeof(seed.a), 0);
writeLE32(args.data() + BootloaderArgsOffset, seed.a[0]);
writeLE32(args.data() + BootloaderArgsOffset + 4, seed.a[1]);
auto resp = sendRequest(Request::Authenticate, std::move(args), std::chrono::milliseconds(3000));
if(!resp) {
return false;
}
// seed.b follows the header and one more byte
static constexpr size_t ReplyOffset = ReplyHeaderSize + 1;
return readLE32(resp.data.data() + ReplyOffset) == seed.b[0] &&
readLE32(resp.data.data() + ReplyOffset + 4) == seed.b[1];
}
bool RADPeriphCommunication::validateFirmware() {
return sendRequest(Request::FirmwareValidate, {0x00u}, std::chrono::milliseconds(2000));
}
bool RADPeriphCommunication::flashSector(const IEF::Sector& sector) {
if(sector.second.size() < FlashSectorFirstChunkSize) {
Log::Error(__func__, "Invalid sector length");
return false;
}
// The first request is the 32-bit little-endian address followed by the first chunk, encrypted
std::vector<uint8_t> args(BootloaderArgsOffset + sizeof(uint32_t) + FlashSectorFirstChunkSize, 0);
writeLE32(args.data() + BootloaderArgsOffset, sector.first);
icsbl_encrypt_v1(serialNumber, sector.second.begin(), FlashSectorFirstChunkSize, args.data() + BootloaderArgsOffset + sizeof(uint32_t));
if(!sendRequestNoResponse(Request::FlashSector, std::move(args))) {
return false;
}
std::this_thread::sleep_for(std::chrono::milliseconds(400));
// The second request is the rest of the sector, encrypted
const size_t bytesLeft = sector.second.size() - FlashSectorFirstChunkSize;
args = std::vector<uint8_t>(bytesLeft, 0);
icsbl_encrypt_v1(serialNumber, sector.second.begin() + FlashSectorFirstChunkSize, bytesLeft, args.data());
return sendRequest(Request::FlashSector, std::move(args), std::chrono::milliseconds(3000));
}
bool RADPeriphCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
// Validate the firmware before touching the device so a bad file can not leave it erased
std::vector<uint8_t> bin;
if(!fw.getFW(targetChip.getIEFName(), bin)) {
return false;
}
IEF ief;
if(!ief.load(std::move(bin))) {
return false;
}
// The supplied IEF is not for this product
if(ief.getVersion().prodId != static_cast<uint8_t>(device.getProductID())) {
Log::Error(__func__, "IEF product ID mismatch for " + targetChip.getName());
return false;
}
// The supplied IEF is for this product, but not for this chip
if(ief.getVersion().chipId != static_cast<uint8_t>(targetChip.getID())) {
Log::Error(__func__, "IEF chip ID mismatch for " + targetChip.getName());
return false;
}
const auto& sectors = ief.getSectors();
uint32_t totalSize = 0;
for(const auto& sector : sectors) {
if(sector.second.size() < FlashSectorFirstChunkSize) {
Log::Error(__func__, "Invalid sector length in IEF for " + targetChip.getName());
return false;
}
totalSize += static_cast<uint32_t>(sector.second.size());
}
if(!readSerialNumber()) {
return false;
}
if(!authenticate()) {
return false;
}
if(!sendRequest(Request::Erase)) {
Log::Error(__func__, "Failed to erase firmware");
return false;
}
handler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
std::nullopt
);
uint32_t sizeFlashed = 0;
for(const auto& sector : sectors) {
if(!flashSector(sector)) {
return false;
}
sizeFlashed += static_cast<uint32_t>(sector.second.size());
handler(
StatusType::Progress,
"Flashing " + targetChip.getName() + "...",
(double)sizeFlashed / (double)totalSize
);
}
return validateFirmware();
}
bool RADPeriphCommunication::waitForConnect(std::chrono::milliseconds timeout) {
const auto deadline = std::chrono::steady_clock::now() + timeout;
while(std::chrono::steady_clock::now() < deadline) {
if(sendPeriphTest()) {
return true;
}
}
return false;
}
bool RADPeriphCommunication::open() {
device.com->pauseReads();
int attempts = 3;
do {
mode = getMode();
if(mode == Mode::NoAnswer) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
mode = getMode();
if(mode == Mode::NoAnswer) {
return false;
}
}
if(mode == Mode::MainApplication) {
if(!sendRequestNoResponse(Request::SwitchToBootloader)) {
return false;
}
if(!device.reconnect(std::chrono::seconds(30))) {
return false;
}
if(!waitForConnect(std::chrono::seconds(30))) {
return false;
}
} else if(mode == Mode::Bootloader) {
return true;
}
} while(attempts--);
return false;
}
bool RADPeriphCommunication::close() {
if(mode != Mode::Bootloader) {
device.com->resumeReads();
return true;
}
if(!sendRequestNoResponse(Request::SwitchToApp)) {
return false;
}
std::this_thread::sleep_for(std::chrono::seconds(5)); // measured at 2s, 5s for buffer, yes this is needed
if(!device.reconnect(std::chrono::seconds(30))) {
return false;
}
if(!waitForConnect()) {
return false;
}
mode = getMode();
if(mode == Mode::MainApplication) {
device.com->resumeReads();
return true;
}
return false;
}
bool RADPeriphCommunication::enterApplication() {
return true;
}
bool RADPeriphCommunication::finalize() {
return true;
}
bool RADPeriphCommunication::hasFinalizationStep() const {
return false;
}
@@ -0,0 +1,244 @@
#include "icsneo/device/extensions/bootloader/communication/red.h"
#include "icsneo/device/extensions/bootloader/parser/ief.h"
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <cstring>
#include <optional>
#include <icsbl/icsbl.h>
namespace icsneo {
namespace Bootloader {
REDCommunication::REDCommunication(icsneo::Device& dev, const ChipInfo& chip, bool auth, bool encryptWithDeviceKey)
: REDCommunicationBase(dev, chip), doAuthChecks(auth), encryptFirmwareWithDeviceKey(encryptWithDeviceKey) {}
bool REDCommunication::open() {
device.com->pauseReads();
if(doAuthChecks) {
if(!authenticate()) {
return false;
}
}
return getVersion();
}
bool REDCommunication::authenticate(bool firstTime) {
icsbl_seed_v1 seed = {};
if(!icsbl_generate_seed_v1(device.getSerialNumber(), &seed)) {
Log::Error(__func__, "libicsbl could not generate an authentication challenge");
return false;
}
// The challenge is sent as: uint8_t 0x09, uint16_t 0 (a length on top of the protocol length, always zero here),
// one padding byte, then seed.a
std::vector<uint8_t> challenge(3 + sizeof(seed.a));
challenge[0] = 0x09;
std::memcpy(&challenge[3], &seed.a, sizeof(seed.a));
// Ask the bootloader to decode the seed
Log::Debug(__func__, "Authenticating");
if(!sendCommand(uint8_t(Command::Authenticate), challenge, std::chrono::seconds(1), false)) {
if(firstTime) {
// Attempt to flush out any buffers
Log::Debug(__func__, "Authentication failed, flushing buffers");
std::vector<uint8_t> flush(4096, PaddingByte);
device.com->sendPacket(flush);
// Allow any failure messages to come back
const bool any = waitForRx(std::chrono::milliseconds(100));
Log::Trace(__func__, any ? "A message came back" : "No communication came back");
lastData.clear();
// Now try again
return authenticate(false); // Either way, we don't need to continue authenticating
} else {
Log::Error(__func__, "The bootloader rejected the authentication challenge twice");
enterApplication();
return false;
}
}
// The reply is one byte followed by seed.b
if(lastData.back().data.size() < 1 + sizeof(seed.b)) {
Log::Error(__func__, "The authentication reply is too short");
enterApplication();
return false;
}
if(std::memcmp(&lastData.back().data[1], &seed.b, sizeof(seed.b)) != 0) {
Log::Error(__func__, "The authentication reply does not match, the device did not authenticate");
enterApplication();
return false;
}
lastData.clear();
return true;
}
bool REDCommunication::getVersion() {
if(!sendCommand(uint8_t(Command::GetVersion), std::chrono::seconds(1), false)) {
Log::Error(__func__, "The bootloader did not answer the version request");
enterApplication();
return false;
}
if(lastData.front().data.size() < VersionResponse::EncodedSize) {
Log::Error(__func__, "The bootloader version reply is too short");
enterApplication();
return false;
}
currentVersion = VersionResponse(lastData.front());
lastData.clear();
if(doAuthChecks) {
// Check that the serial number the bootloader reports is correct
uint8_t correctSerial[sizeof(VersionResponse::serial_num)] = {};
uint32_t devSerial = device.getSerialNumber();
std::memcpy(correctSerial, &devSerial, sizeof(devSerial));
if(std::memcmp(correctSerial, currentVersion.serial_num, sizeof(correctSerial))) {
Log::Error(__func__, "The serial number reported by the bootloader does not match the device");
enterApplication();
return false;
}
}
blockSize = currentVersion.block_size;
device.setBootloaderVersion({currentVersion.bl_major, currentVersion.bl_minor});
return true;
}
bool REDCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
const auto& srcVer = fw.getVersion();
std::vector<uint8_t> bin;
if(!fw.getFW(targetChip.getIEFName(), bin)) {
return false;
}
IEF ief;
if(!ief.load(std::move(bin))) {
return false;
}
// The supplied IEF is not for this product
if(ief.getVersion().prodId != static_cast<uint8_t>(device.getProductID())) {
Log::Error(__func__, "IEF product ID mismatch for " + targetChip.getName());
return false;
}
// The supplied IEF is for this product, but not for this chip
if(ief.getVersion().chipId != static_cast<uint8_t>(targetChip.getID())) {
Log::Error(__func__, "IEF chip ID mismatch for " + targetChip.getName());
return false;
}
Log::Debug(__func__, "IEF version " + std::to_string(ief.getVersion().appMajor) + "." + std::to_string(ief.getVersion().appMinor)
+ ", " + std::to_string(ief.getSectors().size()) + " sectors");
if(doAuthChecks && device.supportsSwVersionValidate()) {
// Tell the bootloader what version of Vehicle Spy 3 this firmware is from.
// We check to make sure it approves, but also, some devices will not accept
// new firmware if we have not sent this.
// Sometimes new hardware revisions can not accept firmware which is too old.
if(!sendCommand(uint8_t(Command::ValidateSWVersion), {srcVer.major, srcVer.minor, srcVer.release, srcVer.build})) {
Log::Error(__func__, "SW version validation failed for " + targetChip.getName());
return false;
}
}
handler(StatusType::Progress,
"Preparing to update " + targetChip.getName() + "...", std::nullopt);
Log::Debug(__func__, "Erasing " + targetChip.getName());
if(!sendCommand(uint8_t(Command::Erase), std::chrono::seconds(45))) {
Log::Error(__func__, "Erase failed for " + targetChip.getName());
return false;
}
const auto& sectorPairs = ief.getSectors();
size_t sectorsFlashed = 0;
for(const auto& [id, sector] : sectorPairs) {
Log::Trace(__func__, "Flashing sector " + std::to_string(id));
handler(StatusType::Progress,
"Updating " + targetChip.getName() + "...",
double(sectorsFlashed++) / sectorPairs.size());
if(sector.size() > blockSize) {
Log::Error(__func__, "Sector " + std::to_string(id) + " too large");
return false;
}
// Sectors smaller than the block size are padded out with zeros
std::vector<uint8_t> padded(sector.begin(), sector.end());
padded.resize(blockSize, 0);
std::vector<uint8_t> encrypted(padded.size());
if(encryptFirmwareWithDeviceKey)
icsbl_encrypt_v1(device.getSerialNumber(), padded.data(), padded.size(), encrypted.data());
else
encrypted = std::move(padded);
encrypted.insert(encrypted.begin(), {
0x00, 0x00, 0x00, // Padding bytes
// Address
uint8_t((id >> 0x00) & 0xFF),
uint8_t((id >> 0x08) & 0xFF),
uint8_t((id >> 0x10) & 0xFF),
uint8_t((id >> 0x18) & 0xFF),
});
// Unencrypted firmware is used for FIRE2 Zynq (and VNET) which also uses the top bit of the ID
// to denote whether the sector is flashed to chip A or chip B. That is indicated
// with a 1 (for chip B) or a 0 as the first byte of the flash packet payload.
if(!encryptFirmwareWithDeviceKey)
encrypted.insert(encrypted.begin(), uint8_t(id & (1u << 31) ? 1 : 0));
if(!sendCommand(uint8_t(Command::Flash), encrypted)) {
Log::Error(__func__, "Flash failed at sector " + std::to_string(id) + " for " + targetChip.getName());
return false;
}
}
// Tell the application that we flashed 100%
handler(StatusType::Progress,
"Updating " + targetChip.getName() + "...",
1.0);
handler(StatusType::Progress, "Finalizing update...", std::nullopt);
if(!sendCommand(uint8_t(Command::ValidateFirmware))) {
Log::Error(__func__, "Firmware validation failed for " + targetChip.getName());
return false;
}
return true;
}
REDCommunication::VersionResponse::VersionResponse(const REDPacketizer::RxFrame& r) {
auto i = r.data.begin();
bl_major = *i++;
bl_minor = *i++;
app_major = *i++;
app_minor = *i++;
hardware_rev_major = *i++;
hardware_rev_minor = *i++;
flags = *i++;
block_size = *i++;
block_size |= uint16_t(*i++) << 8;
man_day = *i++;
man_month = *i++;
man_year = *i++;
man_year |= uint16_t(*i++) << 8;
for(size_t x = 0; x != sizeof(serial_num); ++x)
serial_num[x] = *i++;
prod_id = *i++;
last_error_code = *i++;
pad_byte = *i++;
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,86 @@
#include "icsneo/device/extensions/bootloader/communication/redbase.h"
#include <limits>
namespace icsneo {
namespace Bootloader {
REDCommunicationBase::REDCommunicationBase(icsneo::Device& dev, const ChipInfo& chip)
: Communication(dev), targetChip(chip) {}
bool REDCommunicationBase::open() {
device.com->pauseReads();
return true;
}
bool REDCommunicationBase::close() {
device.com->resumeReads();
return true;
}
bool REDCommunicationBase::handleData() {
auto& rb = device.com->driver->getReadBuffer();
if(rp.input(rb)) {
lastData = rp.output();
return true;
}
return false;
}
bool REDCommunicationBase::waitForRx(std::chrono::milliseconds timeout) {
return device.com->driver->waitForRx([this]() { return handleData(); }, timeout);
}
bool REDCommunicationBase::sendCommand(uint8_t cmd, std::vector<uint8_t> args, std::chrono::seconds timeout, bool clearLastResponse) {
lastData.clear();
device.com->driver->getReadBuffer().clear();
if(!sendCommandRaw(cmd, std::move(args)))
return false;
if(!waitForRx(std::chrono::duration_cast<std::chrono::milliseconds>(timeout)))
return false;
const bool ret = lastData.front().resp == REDPacketizer::Response::Accepted;
if(clearLastResponse) {
lastData.clear();
}
return ret;
}
bool REDCommunicationBase::sendCommandRaw(uint8_t cmd, std::vector<uint8_t> args) {
// For some reason, some devices need it padded to even total bytes, and some to odd total bytes
const bool padEven = device.getType() == DeviceType::FIRE2;
args.insert(args.begin(), uint8_t(cmd)); // Prepend the command byte to the arguments
if(args.size() > std::numeric_limits<uint16_t>::max())
return false;
uint16_t size = uint16_t(args.size());
// The header counts, and the header is 5 bytes, so we're checking for even inner size here
const bool padding = size % 2 != (padEven ? 0 : 1);
if(padding) // We will add padding, so make space for it
size++;
// Prepend the magic, 16-bit little-endian chip ID, and 16-bit little-endian size
const uint16_t chipID = uint16_t(targetChip.getID());
args.insert(args.begin(), {
REDPacketizer::Magic,
uint8_t(chipID & 0xFF), uint8_t(chipID >> 8),
uint8_t(size & 0xFF), uint8_t(size >> 8)
});
if(padding)
args.push_back(PaddingByte); // Ignored by the firmware
// Write it to the device
return device.com->sendPacket(args);
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,111 @@
#include "icsneo/device/extensions/bootloader/communication/redcore.h"
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <algorithm>
#include <limits>
namespace icsneo {
namespace Bootloader {
bool REDCoreCommunication::flash(const FWRepo& fw, const icsneo::Bootloader::StatusHandler& handler) {
constexpr const size_t MaxSectorSize = 0x600;
std::vector<uint8_t> bin;
if(!fw.getFW(targetChip.getIEFName(), bin)) {
return false;
}
if(bin.size() > std::numeric_limits<uint32_t>::max()) {
Log::Error(__func__, "Firmware too large for " + targetChip.getName());
return false;
}
const std::string message = "Updating " + targetChip.getName() + "...";
// First send the firmware
const uint32_t fileSize = uint32_t(bin.size());
size_t sectorsFlashed = 0;
const size_t sectorCount = (fileSize / MaxSectorSize) + (fileSize % MaxSectorSize ? 1 : 0);
std::vector<uint8_t> sector;
for(size_t i = 0; i < fileSize; i += MaxSectorSize) {
Log::Trace(__func__, "Sending sector " + std::to_string(sectorsFlashed));
handler(StatusType::Progress, message, double(sectorsFlashed++) / sectorCount);
sector.assign(bin.begin() + i, bin.begin() + i + std::min<size_t>(MaxSectorSize, fileSize - i));
if(!sendCommand(uint8_t(Command::SendFirmware), sector)) {
Log::Error(__func__, "Failed to send sector " + std::to_string(sectorsFlashed - 1) + " for " + targetChip.getName());
return false;
}
}
// Then flash it
Log::Debug(__func__, "Starting core flash for " + targetChip.getName());
handler(StatusType::Progress, message, std::nullopt);
// There is a long timeout on the StartFirmwareFlash because the bootloader may block here,
// potentially for the entire duration of the flash
if(!sendCommand(uint8_t(Command::StartFirmwareFlash), {
uint8_t(fileSize & 0xFF), uint8_t((fileSize >> 8) & 0xFF),
uint8_t((fileSize >> 16) & 0xFF), uint8_t((fileSize >> 24) & 0xFF),
}, std::chrono::seconds(60))) {
Log::Error(__func__, "Failed to start core flash for " + targetChip.getName());
return false;
}
// Watch the responses to know when it's done
const auto endTime = std::chrono::steady_clock::now() + std::chrono::seconds(60);
uint32_t flashed = 0;
while(flashed < fileSize && std::chrono::steady_clock::now() < endTime) {
if(!sendCommandRaw(uint8_t(Command::GetFlashProgress))) {
Log::Error(__func__, "Failed to request flash progress for " + targetChip.getName());
return false;
}
if(!waitForRx(std::chrono::milliseconds(3000))) {
Log::Trace(__func__, "No flash progress packet within 3 seconds");
continue;
}
const uint32_t oldFlashed = flashed;
for(const auto& packet : lastData) {
if(packet.resp != REDPacketizer::Response::Accepted)
return false;
if(packet.data.size() != 4) {
Log::Trace(__func__, "Got a flash progress packet of length " + std::to_string(packet.data.size()));
continue;
}
const uint32_t newFlashed = readLE32(packet.data.data());
if(newFlashed >= flashed)
flashed = newFlashed;
else
Log::Trace(__func__, "Got a flash progress packet which went backwards " + std::to_string(newFlashed) + " " + std::to_string(flashed));
// After sending us the packet that confirms the flash, the flash position is cleared and will send us zeros.
// We don't need any of these other packets, in that case.
if(flashed >= fileSize)
break;
}
lastData.clear();
Log::Trace(__func__, "Flashed " + std::to_string(flashed) + " of " + std::to_string(fileSize));
if(flashed != oldFlashed)
handler(StatusType::Progress, message, std::min(double(flashed) / fileSize, 1.0));
}
return flashed == fileSize;
}
bool REDCoreCommunication::sendCommandRaw(uint8_t cmd, std::vector<uint8_t> args) {
if(args.size() > std::numeric_limits<uint16_t>::max())
return false;
const uint16_t size = static_cast<uint16_t>(args.size());
args.insert(args.begin(), { CoreFlasherMagic, uint8_t(size & 0xFF), uint8_t((size >> 8) & 0xFF), cmd });
return REDCommunicationBase::sendCommandRaw(CoreFlasherCommand, std::move(args));
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,85 @@
#include "icsneo/device/extensions/bootloader/communication/redpacketizer.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <algorithm>
namespace icsneo {
namespace Bootloader {
bool REDPacketizer::input(icsneo::RingBuffer& bytes) {
while(bytes.size() > 0) {
switch(state) {
case HEADER:
if(bytes[0] == Magic) {
working.emplace_back();
state = CMD;
} else {
Log::Trace(__func__, "Got bootloader unknown byte: " + std::to_string(bytes[0]));
}
bytes.pop_front();
break;
case CMD:
working.back().resp = Response(bytes[0]);
if(working.back().resp != Response::Accepted && working.back().resp != Response::Rejected) {
Log::Trace(__func__, "Got bootloader unknown response: " + std::to_string(bytes[0]));
working.pop_back();
state = HEADER;
} else {
state = LENGTH_LOWER;
}
bytes.pop_front();
break;
case LENGTH_LOWER:
working.back().len = bytes[0];
state = LENGTH_UPPER;
bytes.pop_front();
break;
case LENGTH_UPPER:
working.back().len |= uint16_t(bytes[0]) << 8;
if(working.back().len == 0) {
working.back().done = true;
state = HEADER;
} else {
state = DATA;
}
bytes.pop_front();
break;
case DATA: {
// The RingBuffer is not contiguous, so copy out with read() rather than taking a pointer
auto& frame = working.back();
const size_t dataToGo = frame.len - frame.data.size();
const size_t toCopy = std::min(bytes.size(), dataToGo);
const size_t oldSize = frame.data.size();
frame.data.resize(oldSize + toCopy);
bytes.read(frame.data.data() + oldSize, 0, toCopy);
bytes.pop(toCopy);
if(toCopy == dataToGo) {
frame.done = true;
state = HEADER;
}
break;
}
}
}
return std::any_of(working.begin(), working.end(), [](const RxFrame& rx) { return rx.done; });
}
std::vector<REDPacketizer::RxFrame> REDPacketizer::output() {
// Hand back the completed frames, anything still in progress stays in working
std::vector<RxFrame> done;
std::vector<RxFrame> pending;
for(auto& rx : working) {
if(rx.done)
done.push_back(std::move(rx));
else
pending.push_back(std::move(rx));
}
working = std::move(pending);
return done;
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,249 @@
#include "icsneo/device/extensions/bootloader/parser/fwbin.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include "icsneo/device/extensions/bootloader/parser/ief.h"
#include <unordered_map>
namespace icsneo {
namespace Bootloader {
/**
* fw.bin file format (all integers little-endian):
*
* uint8_t versionLength
* char version[versionLength] Vehicle Spy 3 version, "major.minor.release.build"
* uint8_t hashLength
* char hash[hashLength] Build hash, unused
* Then tags until the end of the file:
* uint32_t nameHash HashDjb2() of the tag name, e.g. "fire2_core"
* uint8_t type TagData::Type
* uint32_t length
* uint8_t data[length]
*
* Exactly one Signature tag is required, it uses the reserved name hash 0.
*/
uint32_t FWBin::HashDjb2(const std::string& tag) {
uint32_t retv = 5381;
for(char c : tag)
retv = ((retv << 5) + retv) + c;
return retv;
}
bool FWBin::load(const std::string& path) {
file.open(path, std::ios::binary);
if(!file.is_open() || file.fail() || file.eof()) {
Log::Error(__func__, "Could not open file at " + path);
return false;
}
// Read out the version string, it is a single byte length followed by a string
uint8_t size;
file.read((char*)&size, 1);
if(file.fail() || file.eof()) {
Log::Error(__func__, "The fw.bin is truncated, could not read the version length");
return false;
}
std::string unparsedVersion;
unparsedVersion.resize(size);
file.read(unparsedVersion.data(), size);
if(file.fail() || file.eof()) {
Log::Error(__func__, "The fw.bin is truncated, could not read the version string");
return false;
}
if(!version.load(unparsedVersion)) {
Log::Error(__func__, "The fw.bin version string is invalid: " + unparsedVersion);
return false;
}
// Read out the hash string, it is a single byte length followed by a string
file.read((char*)&size, 1);
if(file.fail() || file.eof()) {
Log::Error(__func__, "The fw.bin is truncated, could not read the hash length");
return false;
}
// The hash string is not used, skip over it
file.seekg(size, file.cur);
if(file.fail() || file.eof()) {
Log::Error(__func__, "The fw.bin is truncated, could not read the hash string");
return false;
}
while(true) {
uint32_t tagHash;
file.read((char*)&tagHash, sizeof(uint32_t));
if(file.eof()) {
break;
}
if(file.fail()) {
Log::Error(__func__, "The fw.bin is truncated, could not read a tag name hash");
return false;
}
if(tags.count(tagHash)) {
// Duplicate tag
Log::Error(__func__, "The fw.bin contains a duplicate tag");
return false;
}
TagData& current = tags[tagHash];
// Read the type field
file.read((char*)&current.type, sizeof(TagData::Type));
if(file.fail() || file.eof()) {
Log::Error(__func__, "The fw.bin is truncated, could not read a tag type");
return false;
}
switch(current.type) {
case TagData::Type::DataBytes:
case TagData::Type::DataLength:
case TagData::Type::IEFVersion:
if(tagHash == SignatureHash) {
Log::Error(__func__, "The fw.bin contains a data tag using the reserved signature name hash");
return false;
}
break;
case TagData::Type::Signature:
if(tagHash != SignatureHash) {
Log::Error(__func__, "The fw.bin contains a signature tag with a non-signature name hash");
return false;
}
break;
default: // Unknown type
Log::Error(__func__, "The fw.bin contains a tag of unknown type " + std::to_string(static_cast<int>(current.type)));
return false;
}
// Read the length field
file.read((char*)&current.arrayLength, sizeof(uint32_t));
if(file.fail() || file.eof()) {
Log::Error(__func__, "The fw.bin is truncated, could not read a tag length");
return false;
}
// Note the current position so we can jump back to it and read it later
current.arrayPosition = file.tellg();
file.seekg(current.arrayLength, file.cur);
if(file.fail()) {
Log::Error(__func__, "The fw.bin is truncated, a tag extends past the end of the file");
return false;
}
}
file.clear();
// The signature tag is required to be present, but it is not verified
if(tags.count(SignatureHash) == 0) {
Log::Error(__func__, "The fw.bin is missing its signature tag");
return false;
}
return true;
}
bool FWBin::readTag(const std::string& tag, std::vector<uint8_t>& out) const {
const auto hash = HashDjb2(tag);
if(hash == SignatureHash) {
Log::Error(__func__, "The signature tag can not be read as data");
return false;
}
const auto it = tags.find(hash);
if(it == tags.end()) {
Log::Error(__func__, "The fw.bin does not contain the tag " + tag);
return false;
}
return it->second.read(file, out);
}
bool FWBin::TagData::read(std::ifstream& stream, std::vector<uint8_t>& out) const {
stream.clear();
stream.seekg(arrayPosition, stream.beg);
out.resize(arrayLength);
stream.read((char*)out.data(), arrayLength);
if(stream.fail()) {
Log::Error(__func__, "The fw.bin is truncated, could not read tag data");
return false;
}
return true;
}
bool FWBin::getFW(const std::string& iefName, std::vector<uint8_t>& fw) const {
return readTag(iefName, fw);
}
FWBin::VersionTags FWBin::GetVersionTags(const std::string& iefName) {
static const std::unordered_map<std::string, VersionTags> VersionTable = {
{"fire2_core", {"fire2_core_version_major", "fire2_core_version_minor"}},
{"fire2_core_sg4", {"fire2_core_sg4_version_major", "fire2_core_sg4_version_minor"}},
{"RADA2B_SW_bin", {"RADA2B_SW_Version_Major", "RADA2B_SW_Version_Minor"}},
{"RADA2B_REVB_SW_bin", {"RADA2B_SW_Version_Major", "RADA2B_SW_Version_Minor"}},
{"RADComet_SW_bin", {"RADComet_SW_Version_Major", "RADComet_SW_Version_Minor"}},
{"RADComet3_SW_bin", {"RADComet3_SW_Version_Major", "RADComet3_SW_Version_Minor"}},
{"RADGalaxy_SG2_SW_bin", {"RADGalaxy_SG2_SW_Version_Major", "RADGalaxy_SG2_SW_Version_Minor"}},
{"RADGalaxy_SG2_FFG_SW_bin", {"RADGalaxy_SG2_FFG_SW_Version_Major", "RADGalaxy_SG2_SW_Version_Minor"}},
{"RADGalaxy2_SW_bin_p1", {"RADGalaxy2_SW_Version_Major", "RADGalaxy2_SW_Version_Minor"}},
{"RADGalaxy2_SW_bin_p2", {"RADGalaxy2_SW_Version_Major", "RADGalaxy2_SW_Version_Minor"}},
{"RADGigastar_SW_bin", {"RADGigastar_SW_Version_Major", "RADGigastar_SW_Version_Minor"}},
{"RADGigastar_FFG_SW_bin", {"RADGigastar_SW_Version_Major", "RADGigastar_SW_Version_Minor"}},
{"RADGigastar_USBz_SW_bin", {"RADGigastar_USBz_SW_Version_Major", "RADGigastar_USBz_SW_Version_Minor"}},
{"RADGigastar_USBz_Z7010_SW_bin", {"RADGigastar_USBz_SW_Version_Major", "RADGigastar_USBz_SW_Version_Minor"}},
{"RADGigastar_USBz_Z7007s_SW_bin", {"RADGigastar_USBz_SW_Version_Major", "RADGigastar_USBz_SW_Version_Minor"}},
{"RADGigastar2_T1S_LIN_SW_bin", {"RADGigastar2_T1S_LIN_SW_Version_Major", "RADGigastar2_T1S_LIN_SW_Version_Minor"}},
{"RADGigastar2_T1S_CAN_SW_bin", {"RADGigastar2_T1S_CAN_SW_Version_Major", "RADGigastar2_T1S_CAN_SW_Version_Minor"}},
{"RADMoon2_SW_bin", {"RADMoon2_SW_Version_Major", "RADMoon2_SW_Version_Minor"}},
{"RADMoon2_Z7010_SW_bin", {"RADMoon2_SW_Version_Major", "RADMoon2_SW_Version_Minor"}},
{"RADMoonT1S_SW_bin", {"RADMoonT1S_SW_Version_Major", "RADMoonT1S_SW_Version_Minor"}},
{"vcan44_core_ief", {"vcan44_core_version_major", "vcan44_core_version_minor"}}
};
auto iter = VersionTable.find(iefName);
return iter != VersionTable.end() ? iter->second : VersionTags();
}
std::optional<icsneo::DeviceAppVersion> FWBin::getChipVersion(const std::string& iefName) const {
auto versionTags = GetVersionTags(iefName);
if(versionTags) {
std::vector<uint8_t> majData;
std::vector<uint8_t> minData;
if(!readTag(versionTags.majorTag, majData)) {
return std::nullopt;
}
if(!readTag(versionTags.minorTag, minData)) {
return std::nullopt;
}
if(majData.size() != 1)
return std::nullopt;
if(minData.size() != 1)
return std::nullopt;
icsneo::DeviceAppVersion chipVersion;
chipVersion.major = majData[0];
chipVersion.minor = minData[0];
return chipVersion;
}
std::vector<uint8_t> buf;
if(!readTag(iefName, buf)) {
return std::nullopt;
}
IEF ief;
if(!ief.load(std::move(buf))) {
return std::nullopt;
}
icsneo::DeviceAppVersion chipVersion;
chipVersion.major = ief.getVersion().appMajor;
chipVersion.minor = ief.getVersion().appMinor;
return chipVersion;
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,142 @@
#include "icsneo/device/extensions/bootloader/parser/fwbundle.h"
#include "icsneo/device/extensions/bootloader/parser/fwbin.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include "icsneo/core/crc32.h"
#include <cstddef>
#include <cstring>
#include <fstream>
#include <iterator>
#include <limits>
namespace icsneo {
namespace Bootloader {
/**
* Firmware bundle file format (all integers little-endian):
*
* FWBundleHeader
* Then chips until the end of the file:
* FWBundleChip
* uint8_t data[FWBundleChip::length]
*
* The CRC32 covers the whole file with the crc field set to zero.
*/
#pragma pack(push, 1)
struct FWBundleHeader {
char magic[2]; // "FW"
uint16_t fmtVersion;
uint32_t crc;
uint8_t build;
uint8_t maintenance;
uint8_t minor;
uint8_t major;
uint8_t type; // Build type, unused
};
struct FWBundleChip {
uint32_t hash; // FWBin::HashDjb2() of the chip's IEF name
uint8_t build;
uint8_t maintenance;
uint8_t minor;
uint8_t major;
uint32_t length;
};
#pragma pack(pop)
bool FWBundle::IsFWBundle(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if(!file.is_open())
return false;
// Only check the magic, so a truncated bundle is still reported as a bundle error by load()
char magic[sizeof(FWBundleHeader::magic)];
file.read(magic, sizeof(magic));
if(file.fail())
return false;
return magic[0] == 'F' && magic[1] == 'W';
}
bool FWBundle::load(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if(!file.is_open()) {
Log::Error(__func__, "Could not open file at " + path);
return false;
}
std::vector<uint8_t> bytes((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
if(file.bad()) {
Log::Error(__func__, "Could not read file at " + path);
return false;
}
if(bytes.size() < sizeof(FWBundleHeader) || bytes.size() > std::numeric_limits<uint32_t>::max()) {
Log::Error(__func__, "The firmware bundle is smaller than its header or larger than 4 GiB");
return false;
}
FWBundleHeader header;
std::memcpy(&header, bytes.data(), sizeof(header));
if(header.magic[0] != 'F' || header.magic[1] != 'W') {
Log::Error(__func__, "The file is not a firmware bundle, bad magic");
return false;
}
// The CRC is computed with the CRC field zeroed
std::memset(bytes.data() + offsetof(FWBundleHeader, crc), 0, sizeof(header.crc));
if(icsneo::crc32(0, bytes.data(), static_cast<uint32_t>(bytes.size())) != header.crc) {
Log::Error(__func__, "The firmware bundle failed CRC validation");
return false;
}
std::unordered_map<uint32_t, ChipBinary> parsed;
size_t offset = sizeof(FWBundleHeader);
while(offset < bytes.size()) {
if(bytes.size() - offset < sizeof(FWBundleChip)) {
Log::Error(__func__, "The firmware bundle is truncated, could not read a chip header");
return false;
}
FWBundleChip chip;
std::memcpy(&chip, bytes.data() + offset, sizeof(chip));
offset += sizeof(FWBundleChip);
if(bytes.size() - offset < chip.length) {
Log::Error(__func__, "The firmware bundle is truncated, a chip binary extends past the end of the file");
return false;
}
auto& bin = parsed[chip.hash];
bin.data.assign(bytes.begin() + offset, bytes.begin() + offset + chip.length);
bin.version.major = chip.major;
bin.version.minor = chip.minor;
bin.version.release = chip.maintenance;
bin.version.build = chip.build;
offset += chip.length;
}
version.major = header.major;
version.minor = header.minor;
version.release = header.maintenance;
version.build = header.build;
binaries = std::move(parsed);
return true;
}
bool FWBundle::getFW(const std::string& iefName, std::vector<uint8_t>& fw) const {
const auto it = binaries.find(FWBin::HashDjb2(iefName));
if(it == binaries.end()) {
Log::Error(__func__, "The chip binary could not be found");
return false;
}
fw = it->second.data;
return true;
}
std::optional<icsneo::DeviceAppVersion> FWBundle::getChipVersion(const std::string& iefName) const {
const auto it = binaries.find(FWBin::HashDjb2(iefName));
if(it == binaries.end())
return std::nullopt;
return icsneo::DeviceAppVersion(it->second.version.major, it->second.version.minor);
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,25 @@
#include "icsneo/device/extensions/bootloader/parser/fwloader.h"
#include "icsneo/device/extensions/bootloader/parser/fwbin.h"
#include "icsneo/device/extensions/bootloader/parser/fwbundle.h"
namespace icsneo {
namespace Bootloader {
std::unique_ptr<FWRepo> FWLoader::Load(const std::string& path) {
if(FWBundle::IsFWBundle(path)) {
auto bundle = std::make_unique<FWBundle>();
if(!bundle->load(path))
return nullptr;
return bundle;
}
auto bin = std::make_unique<FWBin>();
if(!bin->load(path))
return nullptr;
return bin;
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,73 @@
#include "icsneo/device/extensions/bootloader/parser/fwzip.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <miniz.h>
namespace icsneo {
namespace Bootloader {
class ZipHandle {
public:
ZipHandle() {
mz_zip_zero_struct(&handle);
}
~ZipHandle() {
mz_zip_reader_end(&handle);
}
operator mz_zip_archive*() {
return &handle;
}
private:
mz_zip_archive handle;
};
// A zip firmware image holds a single entry, the raw binary to send to the device
FWZip::FWZip(std::vector<uint8_t>&& tagData)
: handle(std::make_unique<ZipHandle>()), zipData(std::move(tagData)) {
if(!mz_zip_reader_init_mem(*handle, zipData.data(), zipData.size(), 0)) {
Log::Error(__func__, "The firmware zip could not be opened");
return;
}
mz_zip_archive_file_stat file;
if(!mz_zip_reader_file_stat(*handle, 0, &file)) {
Log::Error(__func__, "The firmware zip is empty or its first entry could not be read");
} else {
uncompressedSize = static_cast<size_t>(file.m_uncomp_size);
}
}
FWZip::~FWZip() = default;
bool FWZip::readAll(uint8_t* out) {
if(!uncompressedSize) {
return false;
}
if(!out) {
Log::Error(__func__, "No output buffer for the firmware zip");
return false;
}
if(!mz_zip_reader_extract_to_mem(*handle, 0, out, *uncompressedSize, 0)) {
Log::Error(__func__, "The firmware zip could not be decompressed");
return false;
}
return true;
}
bool FWZip::readAll(std::vector<uint8_t>& out) {
if(!uncompressedSize) {
return false;
}
out = std::vector<uint8_t>(*uncompressedSize, 0);
return readAll(out.data());
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,74 @@
#include "icsneo/device/extensions/bootloader/parser/ief.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <cstring>
namespace icsneo {
namespace Bootloader {
/**
* IEF file format (all integers little-endian):
*
* uint8_t magic 0xAA
* Version version
* uint8_t configLength
* uint8_t config[configLength] Unused
* Then sectors until the end of the file:
* uint8_t magic 0xAA
* uint32_t index Sector index, see the communication for how this maps to an address
* uint32_t length
* uint8_t data[length]
*/
bool IEF::load(std::vector<uint8_t>&& bytes) {
static constexpr uint8_t Magic = 0xAA;
// Magic, version, config byte count
static constexpr size_t FixedHeaderSize = 1 + sizeof(Version) + 1;
// Magic, index, size
static constexpr size_t SectorHeaderSize = 1 + sizeof(uint32_t) + sizeof(uint32_t);
data = std::move(bytes);
sectors.clear();
if(data.size() < FixedHeaderSize) {
Log::Error(__func__, "IEF too short");
return false;
}
if(data.front() != Magic) {
Log::Error(__func__, "Invalid IEF magic");
return false;
}
std::memcpy(&version, data.data() + 1, sizeof(Version));
const size_t headerSize = FixedHeaderSize + data[FixedHeaderSize - 1];
if(data.size() <= headerSize) {
Log::Error(__func__, "Invalid IEF config bytes");
return false;
}
size_t offset = headerSize;
while(data.size() - offset >= SectorHeaderSize) {
if(data[offset] != Magic) {
Log::Error(__func__, "Invalid IEF sector magic");
return false;
}
uint32_t index;
uint32_t size;
std::memcpy(&index, data.data() + offset + 1, sizeof(index));
std::memcpy(&size, data.data() + offset + 1 + sizeof(index), sizeof(size));
offset += SectorHeaderSize;
if(data.size() - offset < size) {
Log::Error(__func__, "Invalid IEF sector");
return false;
}
sectors.emplace_back(index, BytesView(data.data() + offset, size));
offset += size;
}
return true;
}
} // namespace Bootloader
} // namespace icsneo
@@ -0,0 +1,57 @@
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <cstdlib>
#include <iostream>
namespace icsneo {
namespace Bootloader {
namespace Log {
// Set ICS_BOOTLOADER_LOG_LEVEL to 1 (error) through 5 (trace) to enable logging to stderr
static int GetLogLevel() {
static const int level = [] {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4996) // getenv
#endif
const char* env = std::getenv("ICS_BOOTLOADER_LOG_LEVEL");
#ifdef _MSC_VER
#pragma warning(pop)
#endif
return env ? std::atoi(env) : 0;
}();
return level;
}
static void Write(int level, const char* prefix, const std::string& subject, const std::string& msg) {
if(GetLogLevel() >= level)
std::cerr << prefix << subject << " - " << msg << std::endl;
}
void Trace(const std::string& subject, const std::string& msg) {
Write(5, "[TRACE] ", subject, msg);
}
void Debug(const std::string& subject, const std::string& msg) {
Write(4, "[DEBUG] ", subject, msg);
}
void Info(const std::string& subject, const std::string& msg) {
Write(3, "[INFO] ", subject, msg);
}
void Warning(const std::string& subject, const std::string& msg) {
Write(2, "[WARN] ", subject, msg);
}
void Error(const std::string& subject, const std::string& msg) {
Write(1, "[ERROR] ", subject, msg);
}
} // namespace Log
} // namespace Bootloader
} // namespace icsneo
+50
View File
@@ -0,0 +1,50 @@
#include "icsneo/device/extensions/bootloader/version.h"
#include "icsneo/device/extensions/bootloader/platform/logging.h"
#include <limits>
#include <optional>
#include <regex>
namespace icsneo {
namespace Bootloader {
bool FirmwareSourceVersion::load(const std::string& toParse) {
static const std::regex VersionRegex{"(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)"};
std::smatch match;
if(!std::regex_match(toParse, match, VersionRegex)) {
Log::Error(__func__, "Failed to parse version");
return false;
}
const auto parse = [](const std::string& input) -> std::optional<ValueType> {
// The regex guarantees only digits, limit the length so stoul can not overflow
if(input.size() > 3) {
return std::nullopt;
}
const auto value = std::stoul(input);
if(value > std::numeric_limits<ValueType>::max()) {
return std::nullopt;
}
return static_cast<ValueType>(value);
};
const auto parsedMajor = parse(match[1]);
const auto parsedMinor = parse(match[2]);
const auto parsedRelease = parse(match[3]);
const auto parsedBuild = parse(match[4]);
if(!parsedMajor || !parsedMinor || !parsedRelease || !parsedBuild) {
Log::Error(__func__, "Invalid sized version number");
return false;
}
major = *parsedMajor;
minor = *parsedMinor;
release = *parsedRelease;
build = *parsedBuild;
return true;
}
} // namespace Bootloader
} // namespace icsneo
+174
View File
@@ -1113,3 +1113,177 @@ bool IDeviceSettings::setLinuxConfigurationPort(LinuxConfigurationPort port) {
(*os)->ethConfigurationPort = static_cast<uint8_t>(port); (*os)->ethConfigurationPort = static_cast<uint8_t>(port);
return true; return true;
} }
namespace {
void reportT1SUnavailable(const icsneo::device_eventhandler_t& report) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
}
std::optional<bool> t1sFlag(const ETHERNET10T1S_SETTINGS* t1s, uint8_t flag, const icsneo::device_eventhandler_t& report) {
if(t1s == nullptr) {
reportT1SUnavailable(report);
return std::nullopt;
}
return (t1s->flags & flag) != 0;
}
bool setT1SFlag(ETHERNET10T1S_SETTINGS* t1s, uint8_t flag, bool enable, const icsneo::device_eventhandler_t& report) {
if(t1s == nullptr) {
reportT1SUnavailable(report);
return false;
}
if(enable)
t1s->flags |= flag;
else
t1s->flags = static_cast<uint8_t>(t1s->flags & ~flag);
return true;
}
std::optional<uint8_t> t1sByte(const uint8_t* field, const icsneo::device_eventhandler_t& report) {
if(field == nullptr) {
reportT1SUnavailable(report);
return std::nullopt;
}
return *field;
}
bool setT1SByte(uint8_t* field, uint8_t value, const icsneo::device_eventhandler_t& report) {
if(field == nullptr) {
reportT1SUnavailable(report);
return false;
}
*field = value;
return true;
}
} // namespace
std::optional<bool> IDeviceSettings::isT1SPLCAEnabledFor(Network net) const {
return t1sFlag(getT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA, report);
}
bool IDeviceSettings::setT1SPLCAFor(Network net, bool enable) {
return setT1SFlag(getMutableT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA, enable, report);
}
std::optional<uint8_t> IDeviceSettings::getT1SLocalIDFor(Network net) const {
const auto* t1s = getT1SSettingsFor(net);
return t1sByte(t1s == nullptr ? nullptr : &t1s->local_id, report);
}
bool IDeviceSettings::setT1SLocalIDFor(Network net, uint8_t id) {
auto* t1s = getMutableT1SSettingsFor(net);
return setT1SByte(t1s == nullptr ? nullptr : &t1s->local_id, id, report);
}
std::optional<uint8_t> IDeviceSettings::getT1SMaxNodesFor(Network net) const {
const auto* t1s = getT1SSettingsFor(net);
return t1sByte(t1s == nullptr ? nullptr : &t1s->max_num_nodes, report);
}
bool IDeviceSettings::setT1SMaxNodesFor(Network net, uint8_t nodes) {
auto* t1s = getMutableT1SSettingsFor(net);
return setT1SByte(t1s == nullptr ? nullptr : &t1s->max_num_nodes, nodes, report);
}
std::optional<uint8_t> IDeviceSettings::getT1STxOppTimerFor(Network net) const {
const auto* t1s = getT1SSettingsFor(net);
return t1sByte(t1s == nullptr ? nullptr : &t1s->to_timer, report);
}
bool IDeviceSettings::setT1STxOppTimerFor(Network net, uint8_t timer) {
auto* t1s = getMutableT1SSettingsFor(net);
return setT1SByte(t1s == nullptr ? nullptr : &t1s->to_timer, timer, report);
}
std::optional<uint8_t> IDeviceSettings::getT1SMaxBurstFor(Network net) const {
const auto* t1s = getT1SSettingsFor(net);
return t1sByte(t1s == nullptr ? nullptr : &t1s->max_burst_count, report);
}
bool IDeviceSettings::setT1SMaxBurstFor(Network net, uint8_t burst) {
auto* t1s = getMutableT1SSettingsFor(net);
return setT1SByte(t1s == nullptr ? nullptr : &t1s->max_burst_count, burst, report);
}
std::optional<uint8_t> IDeviceSettings::getT1SBurstTimerFor(Network net) const {
const auto* t1s = getT1SSettingsFor(net);
return t1sByte(t1s == nullptr ? nullptr : &t1s->burst_timer, report);
}
bool IDeviceSettings::setT1SBurstTimerFor(Network net, uint8_t timer) {
auto* t1s = getMutableT1SSettingsFor(net);
return setT1SByte(t1s == nullptr ? nullptr : &t1s->burst_timer, timer, report);
}
std::optional<uint8_t> IDeviceSettings::getT1SLocalIDAlternateFor(Network net) const {
const auto* t1s = getT1SSettingsFor(net);
return t1sByte(t1s == nullptr ? nullptr : &t1s->local_id_alternate, report);
}
bool IDeviceSettings::setT1SLocalIDAlternateFor(Network net, uint8_t id) {
auto* t1s = getMutableT1SSettingsFor(net);
return setT1SByte(t1s == nullptr ? nullptr : &t1s->local_id_alternate, id, report);
}
std::optional<bool> IDeviceSettings::isT1STerminationEnabledFor(Network net) const {
return t1sFlag(getT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_TERMINATION, report);
}
bool IDeviceSettings::setT1STerminationFor(Network net, bool enable) {
return setT1SFlag(getMutableT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_TERMINATION, enable, report);
}
std::optional<bool> IDeviceSettings::isT1SBusDecodingBeaconsEnabledFor(Network net) const {
return t1sFlag(getT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS, report);
}
bool IDeviceSettings::setT1SBusDecodingBeaconsFor(Network net, bool enable) {
return setT1SFlag(getMutableT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS, enable, report);
}
std::optional<bool> IDeviceSettings::isT1SBusDecodingAllEnabledFor(Network net) const {
return t1sFlag(getT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL, report);
}
bool IDeviceSettings::setT1SBusDecodingAllFor(Network net, bool enable) {
return setT1SFlag(getMutableT1SSettingsFor(net), ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL, enable, report);
}
std::optional<uint8_t> IDeviceSettings::getT1SMultiIDEnableMaskFor(Network net) const {
const auto* ext = getT1SSettingsExtFor(net);
return t1sByte(ext == nullptr ? nullptr : &ext->enable_multi_id, report);
}
bool IDeviceSettings::setT1SMultiIDEnableMaskFor(Network net, uint8_t mask) {
auto* ext = getMutableT1SSettingsExtFor(net);
return setT1SByte(ext == nullptr ? nullptr : &ext->enable_multi_id, mask, report);
}
std::optional<uint8_t> IDeviceSettings::getT1SMultiIDFor(Network net, uint8_t index) const {
const auto* ext = getT1SSettingsExtFor(net);
if(ext == nullptr) {
reportT1SUnavailable(report);
return std::nullopt;
}
if(index >= sizeof(ext->multi_id)) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return ext->multi_id[index];
}
bool IDeviceSettings::setT1SMultiIDFor(Network net, uint8_t index, uint8_t id) {
auto* ext = getMutableT1SSettingsExtFor(net);
if(ext == nullptr) {
reportT1SUnavailable(report);
return false;
}
if(index >= sizeof(ext->multi_id)) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
ext->multi_id[index] = id;
return true;
}
-8
View File
@@ -8,7 +8,6 @@ Dependencies
The minimum requirements to build libicsneo are: The minimum requirements to build libicsneo are:
- CMake version 3.12 or newer - CMake version 3.12 or newer
- A C++17 compiler - A C++17 compiler
- libpcap on Linux and macOS
Building library & examples Building library & examples
@@ -30,10 +29,3 @@ libicsneo supports being added as a sub-projects with CMake's
#. Clone libicsneo into the desired location within the project #. Clone libicsneo into the desired location within the project
#. Add ``add_subdirectory(path/to/libicsneo)`` to ``CMakeLists.txt`` #. Add ``add_subdirectory(path/to/libicsneo)`` to ``CMakeLists.txt``
#. Link the project to libicsneo with ``target_link_libraries(app icsneocpp)`` #. Link the project to libicsneo with ``target_link_libraries(app icsneocpp)``
Linux udev Rules
================
Linux users may want to install the included udev rules to run libicsneo based
applications without root, this can be done with:
``cp 99-intrepidcs.rules /etc/udev/rules.d/``
+22
View File
@@ -107,3 +107,25 @@ Analog Output Control
.. literalinclude:: ../../examples/python/analog_out/analog_out_basic.py .. literalinclude:: ../../examples/python/analog_out/analog_out_basic.py
:language: python :language: python
ISO-TP CAN1 Loopback
====================
Two devices, CAN1 wired together. ``--raw`` checks the cable, ``--sf`` sends one
firmware single frame, ``--both`` sends a segmented payload with firmware flow control.
:download:`Download example <../../examples/python/iso15765/iso15765_loopback.py>`
.. literalinclude:: ../../examples/python/iso15765/iso15765_loopback.py
:language: python
Firmware Update
===============
Updates the first device found from a ``fw.bin`` or firmware bundle, see
:doc:`firmware_update`.
:download:`Download example <../../examples/python/bootloader/update.py>`
.. literalinclude:: ../../examples/python/bootloader/update.py
:language: python
-11
View File
@@ -25,14 +25,3 @@ To upgrade an existing installation:
.. code-block:: bash .. code-block:: bash
pip install --pre --upgrade icsneopy pip install --pre --upgrade icsneopy
Linux udev Rules
================
Linux users may want to install the included udev rules to run icsneopy-based
applications without root. The rules file can be found in the libicsneo source
repository at https://github.com/intrepidcs/libicsneo/.
.. code-block:: bash
sudo cp 99-intrepidcs.rules /etc/udev/rules.d/
-14
View File
@@ -15,17 +15,3 @@ communication library. The source code for libicsneo can be found on GitHub:
icsneoc/index icsneoc/index
icsneoc2/index icsneoc2/index
Linux Installation
==================
Applications that use raw Ethernet device discovery, such as PCAP-backed
discovery, need permission to open raw network sockets. Instead of running your
application with ``sudo``, grant the installed executable the required Linux
capabilities:
.. code-block:: bash
sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/your-app
Replace ``/usr/bin/your-app`` with the full path to the application executable.
+2 -3
View File
@@ -52,7 +52,7 @@ Although the example program will build without successfully completing the step
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions. First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev` 1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build` 2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build` 3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile. 4. Run `cmake ..` to generate your Makefile.
@@ -72,8 +72,7 @@ Although the example program will build without successfully completing the step
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library. 5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. * Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo ./libicsneoc-example` to run the example. 6. Run `./libicsneoc-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
## macOS ## macOS
+2 -3
View File
@@ -52,7 +52,7 @@ Although the example program will build without successfully completing the step
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions. First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev` 1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build` 2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build` 3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile. 4. Run `cmake ..` to generate your Makefile.
@@ -72,8 +72,7 @@ Although the example program will build without successfully completing the step
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library. 5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. * Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo ./libicsneoc-example` to run the example. 6. Run `./libicsneoc-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
## macOS ## macOS
+2 -3
View File
@@ -52,7 +52,7 @@ Although the example program will build without successfully completing the step
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions. First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev` 1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build` 2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build` 3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile. 4. Run `cmake ..` to generate your Makefile.
@@ -72,8 +72,7 @@ Although the example program will build without successfully completing the step
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library. 5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. * Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo ./libicsneoc-example` to run the example. 6. Run `./libicsneoc-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
## macOS ## macOS
+1 -1
View File
@@ -378,7 +378,7 @@ static int configure_t1s_port(icsneoc2_device_t* device, icsneoc2_netid_t netid,
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S TX opportunity timer", res); if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S TX opportunity timer", res);
res = icsneoc2_settings_t1s_burst_timer_set(device, netid, T1S_BURST_TIMER); res = icsneoc2_settings_t1s_burst_timer_set(device, netid, T1S_BURST_TIMER);
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S burst timer", res); if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S burst timer", res);
res = icsneoc2_settings_t1s_max_burst_timer_for_set(device, netid, T1S_MAX_BURST); res = icsneoc2_settings_t1s_max_burst_set(device, netid, T1S_MAX_BURST);
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S max burst", res); if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S max burst", res);
res = icsneoc2_settings_t1s_is_termination_enabled_for(device, netid, &termination); res = icsneoc2_settings_t1s_is_termination_enabled_for(device, netid, &termination);
+2 -3
View File
@@ -32,15 +32,14 @@ If you haven't done this, `third-party/libicsneo` will be empty and you won't be
### Ubuntu 18.04 LTS ### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev` 1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build` 2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build` 3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile. 4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make libicsneocpp-interactive-example` to build. 5. Run `make libicsneocpp-interactive-example` to build.
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. * Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example. 6. Now run `./libicsneocpp-interactive-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6. 7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
### macOS ### macOS
+2 -3
View File
@@ -32,15 +32,14 @@ If you haven't done this, `third-party/libicsneo` will be empty and you won't be
### Ubuntu 18.04 LTS ### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libpcap0.8-dev` 1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build` 2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build` 3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile. 4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make libicsneocpp-interactive-example` to build. 5. Run `make libicsneocpp-interactive-example` to build.
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. * Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example. 6. Now run `./libicsneocpp-interactive-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6. 7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
### macOS ### macOS
+48
View File
@@ -0,0 +1,48 @@
import sys
import icsneopy
def update(firmware_path):
devices = icsneopy.find_all_devices()
if len(devices) == 0:
print("no devices found")
return False
device = devices[0]
print(f"selected {device}")
if not device.open():
print("unable to open device")
return False
fw = icsneopy.Bootloader.FWLoader.load(firmware_path)
if fw is None:
print(f"unable to load firmware from {firmware_path}")
return False
last_status = None
# repeated status messages overwrite the current line so progress updates in place
def logger(status_type, status, progress):
nonlocal last_status
if last_status not in (None, status):
print()
last_status = status
print(f"\r{status}" + (f" ({progress * 100:.1f}%)" if progress is not None else ""), end="", flush=True)
# force_flash updates every chip, even those already at the version in the firmware file
ok = icsneopy.Bootloader.update(device, fw, logger, force_flash=True)
print()
if not ok:
print(f"update failed: {icsneopy.get_last_error()}")
return False
print("update complete")
return True
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"usage: {sys.argv[0]} <fw.bin|bundle>")
sys.exit(1)
sys.exit(0 if update(sys.argv[1]) else 1)
@@ -0,0 +1,264 @@
"""
Two-device ISO-TP loopback on CAN1.
Firmware segments the payload. This script sends it once and prints CAN1
frames with the ISO-TP PCI nibble decoded (SF/FF/CF/FC).
Wire CAN1 of two devices together (CANH/CANL, terminated).
python iso15765_loopback.py --raw
python iso15765_loopback.py --sf
python iso15765_loopback.py --both
"""
import argparse
import threading
import time
from datetime import timedelta
import icsneopy
CAN1 = icsneopy.Network.NetID.DWCAN_01
def pci_describe(data):
if not data:
return ""
pci_type = data[0] >> 4
pci_low = data[0] & 0x0F
if pci_type == 0x0:
if pci_low > 7 or len(data) < 1 + pci_low:
return ""
return f"SF len={pci_low}"
if pci_type == 0x1:
if len(data) < 8:
return ""
length = (pci_low << 8) | data[1]
if length < 8:
return ""
return f"FF len={length}"
if pci_type == 0x2:
if len(data) < 2:
return ""
return f"CF SN={pci_low}"
if pci_type == 0x3:
if len(data) < 3 or pci_low > 2:
return ""
fs_name = ("CTS", "WAIT", "OVFLW")[pci_low]
return f"FC FS={fs_name} BS={data[1]} STmin=0x{data[2]:02X}"
return ""
def print_api_result(what, ok):
line = f" {what}: {'OK' if ok else 'FAIL'}"
if not ok:
err = icsneopy.get_last_error()
if err.get_type() != icsneopy.APIEvent.Type.NoErrorFound:
line += f" [{err.describe()}]"
print(line)
def is_extended(arb_id):
return arb_id > 0x7FF
def make_args(tx_id, fc_id, payload, rx):
msg = icsneopy.Iso15765MessageArgs()
msg.tx_index = 0
msg.arb_id = icsneopy.Iso15765MessageArgs.ArbId(tx_id, is_extended(tx_id))
msg.flow_control_arb_id = icsneopy.Iso15765MessageArgs.ArbId(fc_id, is_extended(fc_id))
msg.flow_control_arb_id_mask = 0x1FFFFFFF if is_extended(tx_id) else 0x7FF
msg.is_can_fd = False
msg.is_brs_enabled = False
msg.tx_dl = 8
msg.padding_value = 0x00
msg.fs_timeout = 75
msg.fs_wait_timeout = 150
msg.cf_timeout = 150
msg.is_flow_control_enabled = rx
if payload:
msg.data = payload
return msg
def transmit_can(device, arb_id, data):
frame = icsneopy.CANMessage()
frame.network = icsneopy.Network(CAN1)
frame.arbid = arb_id
# RawMessage.data is vector<uint8_t>: a list of ints, not bytes.
frame.data = list(data)
frame.isExtended = is_extended(arb_id)
frame.isCANFD = False
ok = device.transmit(frame)
if not ok:
print_api_result("transmit CAN", ok)
return ok
class Sniffer:
def __init__(self, tag):
self.tag = tag
self.t0 = None
self.lock = threading.Lock()
def on_message(self, message):
if not isinstance(message, icsneopy.CANMessage):
return
if message.network.get_type() != icsneopy.Network.Type.CAN:
return
with self.lock:
if self.t0 is None:
self.t0 = message.timestamp
rel_ms = 0.0
if message.timestamp >= self.t0:
rel_ms = (message.timestamp - self.t0) / 1e6
direction = "TX" if message.transmitted else "RX"
width = 8 if is_extended(message.arbid) else 3
data = " ".join(f"{b:02X}" for b in message.data)
line = (
f" [{self.tag} {direction}] {rel_ms:8.3f} ms "
f"{message.network} 0x{message.arbid:0{width}X} "
f"[{len(message.data)}] {data}"
)
pci = pci_describe(message.data)
if pci:
line += f" {pci}"
print(line)
def open_and_online(device, baud, keep_baud):
print(f"Opening {device.describe()} ...")
if not device.open():
print_api_result("open", False)
return False
if not keep_baud and device.settings is not None:
net = icsneopy.Network(CAN1)
if not device.settings.set_baudrate(net, baud):
print_api_result("set_baudrate", False)
elif not device.settings.apply(True):
print_api_result("settings.apply(temporary)", False)
if not device.go_online():
print_api_result("go_online", False)
device.close()
return False
return True
def close_device(device):
if device is None:
return
device.iso15765_disable_all()
if device.is_online():
device.go_offline()
if device.is_open():
device.close()
def find_by_serial(devices, serial):
for device in devices:
if device.get_serial().lower() == serial.lower():
return device
return None
def parse_args():
parser = argparse.ArgumentParser(
description="Two Intrepid devices with CAN1 wired together (CANH/CANL, terminated)."
)
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument("--raw", action="store_true", help="Raw CAN ping-pong (cable check)")
mode.add_argument("--sf", action="store_true", help="Firmware ISO-TP single-frame TX from tester")
mode.add_argument("--both", action="store_true", help="Firmware ISO-TP TX on tester, firmware flow-control on ECU")
parser.add_argument("--tester", default="", help="Tester (sender) serial. Optional if exactly two devices are found.")
parser.add_argument("--ecu", default="", help="ECU / peer serial. Optional if exactly two devices are found.")
parser.add_argument("--baud", type=int, default=500000, help="CAN1 baud, applied temporarily (default 500000)")
parser.add_argument("--keep-baud", action="store_true", help="Do not change device baud")
parser.add_argument("--tx-id", type=lambda s: int(s, 0), default=0x7E0, help="Tester ISO-TP CAN ID (default 0x7E0)")
parser.add_argument("--fc-id", type=lambda s: int(s, 0), default=0x7E8, help="Flow-control CAN ID (default 0x7E8)")
parser.add_argument("--bytes", type=int, default=None, help="ISO-TP payload size (sf default 5, both default 20)")
parser.add_argument("--listen-ms", type=int, default=2000, help="Time to listen after the action (default 2000)")
return parser.parse_args()
def main():
args = parse_args()
print(f"libicsneo {icsneopy.get_version()}")
devices = icsneopy.find_all_devices()
print(f"Found {len(devices)} device(s)")
for device in devices:
print(f" {device.describe()} serial={device.get_serial()}")
if len(devices) < 2:
raise SystemExit("Need two devices connected.")
if not args.tester and not args.ecu:
if len(devices) != 2:
raise SystemExit("More than two devices found; pass --tester and --ecu serials.")
tester, ecu = devices[0], devices[1]
print("No serials given; using first as tester, second as ECU.")
else:
if not args.tester or not args.ecu:
raise SystemExit("Pass both --tester and --ecu, or neither.")
tester = find_by_serial(devices, args.tester)
ecu = find_by_serial(devices, args.ecu)
if tester is None or ecu is None:
raise SystemExit("Could not match tester/ECU serials.")
payload_size = args.bytes if args.bytes is not None else (5 if args.sf else 20)
payload = [i & 0xFF for i in range(payload_size)]
print(f"Tester: {tester.describe()}")
print(f"ECU: {ecu.describe()}")
if not args.raw:
print(f"CAN1 TX 0x{args.tx_id:X} FC 0x{args.fc_id:X} payload {payload_size} bytes")
print()
network = icsneopy.Network(CAN1)
try:
if not open_and_online(tester, args.baud, args.keep_baud):
return 1
if not open_and_online(ecu, args.baud, args.keep_baud):
return 1
for device, sniffer in ((tester, Sniffer("TESTER")), (ecu, Sniffer("ECU "))):
callback = icsneopy.MessageCallback(sniffer.on_message, icsneopy.MessageFilter(CAN1))
device.add_message_callback(callback)
if args.raw:
print("Mode --raw: tester 0x123, then ECU 0x456")
transmit_can(tester, 0x123, [0x11, 0x22, 0x33, 0x44])
time.sleep(0.2)
transmit_can(ecu, 0x456, [0x55, 0x66, 0x77, 0x88])
elif args.sf:
print("Mode --sf: firmware ISO-TP single frame from tester")
if len(payload) > 7:
print(" warning: payload > 7 bytes will be a First Frame (no flow control in this mode)")
ok = tester.iso15765_enable(network)
print_api_result("tester iso15765_enable", ok)
msg = make_args(args.tx_id, args.fc_id, payload, rx=False)
ok = tester.iso15765_transmit_message(network, msg, timedelta(0))
print_api_result("tester iso15765_transmit_message", ok)
else:
print("Mode --both: firmware TX on tester, firmware flow-control on ECU")
ok = tester.iso15765_enable(network)
print_api_result("tester iso15765_enable", ok)
ok = ecu.iso15765_enable(network)
print_api_result("ECU iso15765_enable", ok)
msg = make_args(args.tx_id, args.fc_id, [], rx=True)
ok = ecu.iso15765_setup_rx_flow_control(network, msg)
print_api_result("ECU iso15765_setup_rx_flow_control", ok)
time.sleep(0.1)
msg = make_args(args.tx_id, args.fc_id, payload, rx=False)
ok = tester.iso15765_transmit_message(network, msg, timedelta(0))
print_api_result("tester iso15765_transmit_message", ok)
time.sleep(args.listen_ms / 1000.0)
finally:
close_device(tester)
close_device(ecu)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+4
View File
@@ -126,6 +126,9 @@ public:
SettingNotAvaiableDevice = 0x2057, SettingNotAvaiableDevice = 0x2057,
DiskFormatNotSupported = 0x2058, DiskFormatNotSupported = 0x2058,
DiskFormatInvalidCount = 0x2059, DiskFormatInvalidCount = 0x2059,
TC10RequestFailed = 0x205A,
FirmwareUpdateNotSupported = 0x205B,
FirmwareUpdateFailed = 0x205C,
// Transport Events // Transport Events
FailedToRead = 0x3000, FailedToRead = 0x3000,
@@ -167,6 +170,7 @@ public:
ServdNoDataError = ServdBindError + 9, ServdNoDataError = ServdBindError + 9,
ServdJoinMulticastError = ServdBindError + 10, ServdJoinMulticastError = ServdBindError + 10,
ServdNotReachable = ServdBindError + 11, ServdNotReachable = ServdBindError + 11,
ServdDisabled = ServdBindError + 12,
// DXX // DXX
DXXErrorSys = 0x6100, DXXErrorSys = 0x6100,
+3
View File
@@ -31,7 +31,10 @@ enum class Command : uint8_t {
LoadCoreMini = 0x43, LoadCoreMini = 0x43,
GetRTC = 0x49, GetRTC = 0x49,
SetRTC = 0x50, SetRTC = 0x50,
PeripheralBootloader = 0x83,
EnterBootloader = 0xA0,
RequestSerialNumber = 0xA1, RequestSerialNumber = 0xA1,
ValidateDevice = 0xA2,
GetMainVersion = 0xA3, // Previously known as RED_CMD_APP_VERSION_REQ GetMainVersion = 0xA3, // Previously known as RED_CMD_APP_VERSION_REQ
SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM
//GetSettings = 0xA5, // Previously known as RED_CMD_READ_BAUD_REQ, now unused //GetSettings = 0xA5, // Previously known as RED_CMD_READ_BAUD_REQ, now unused
@@ -8,6 +8,7 @@
#include "icsneo/communication/packet.h" #include "icsneo/communication/packet.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <optional>
namespace icsneo { namespace icsneo {
@@ -22,6 +23,9 @@ struct PhyMessage {
bool Clause45Enable = false; bool Clause45Enable = false;
uint8_t BusIndex = 0; uint8_t BusIndex = 0;
uint8_t Version = PhyPacketVersion; uint8_t Version = PhyPacketVersion;
// Unset on locally constructed messages; every decoded response sets the raw wire status.
// Ignored when sending. A value does not establish firmware error coverage.
std::optional<uint8_t> Status;
union { union {
Clause22Message Clause22{}; Clause22Message Clause22{};
Clause45Message Clause45; Clause45Message Clause45;
+3 -2
View File
@@ -91,9 +91,10 @@ struct FlashPhase : public BootloaderPhase {
bool authenticate = true; bool authenticate = true;
bool encrypt = true; bool encrypt = true;
bool checkOutOfDate = true; bool checkOutOfDate = true;
uint8_t slot = UINT8_MAX;
FlashPhase(ChipID chip, BootloaderCommunication comm, bool authenticate = true, bool encrypt = true, bool checkOutOfDate = true) FlashPhase(ChipID chip, BootloaderCommunication comm, bool authenticate = true, bool encrypt = true, bool checkOutOfDate = true, uint8_t slot = UINT8_MAX)
: chip(chip), comm(comm), authenticate(authenticate), encrypt(encrypt), checkOutOfDate(checkOutOfDate) {} : chip(chip), comm(comm), authenticate(authenticate), encrypt(encrypt), checkOutOfDate(checkOutOfDate), slot(slot) {}
}; };
enum class BootloaderSetting { enum class BootloaderSetting {
+2 -1
View File
@@ -138,7 +138,8 @@ enum class ChipID : icsneoc2_chip_id_t {
RADMOONT1S_ZCHIP = icsneoc2_chip_id_radmoont1s_zchip, RADMOONT1S_ZCHIP = icsneoc2_chip_id_radmoont1s_zchip,
RADGigastar2_ZYNQ = icsneoc2_chip_id_radgigastar2_zynq, RADGigastar2_ZYNQ = icsneoc2_chip_id_radgigastar2_zynq,
SFPModule_ent11100_MCHIP = icsneoc2_chip_id_sfpmodule_ent11100_mchip, SFPModule_ent11100_MCHIP = icsneoc2_chip_id_sfpmodule_ent11100_mchip,
RADGemini_MCHIP = icsneoc2_chip_id_radgemini_mchip, RADArgos_MCHIP = icsneoc2_chip_id_radargos_mchip,
SFPModule_ad1101_MCHIP = icsneoc2_chip_id_sfpmodule_ad1101_mchip,
Invalid = icsneoc2_chip_id_invalid Invalid = icsneoc2_chip_id_invalid
}; };
+7 -40
View File
@@ -151,7 +151,8 @@ public:
RADMoonT1S = 56, RADMoonT1S = 56,
RADGigastar2 = 57, RADGigastar2 = 57,
SFPModule_ent11100 = 58, SFPModule_ent11100 = 58,
RADGemini = 60, RADArgos = 60,
SFPModule_ad1101 = 61,
}; };
virtual ProductID getProductID() const = 0; virtual ProductID getProductID() const = 0;
@@ -205,21 +206,10 @@ public:
public: public:
enum Enum { enum Enum {
/** /**
* Even if the firmware does not match the current firmware version, * Skip refreshing the device state. Instead, only open com
* the device will not be updated. * and start the heartbeat thread.
*
* Note: The device may still be flashed if the device has no firmware
*
* This has no effect if the DFU extension is not present
*/ */
SuppressAutoUpdate = 1 << 0, SkipRefreshDeviceState = 1 << 0,
/**
* Force reflash the device.
*
* This has no effect if the DFU extension is not present
*/
ForceReflash = 1 << 1
}; };
using EnumType = std::underlying_type<Enum>::type; using EnumType = std::underlying_type<Enum>::type;
@@ -230,31 +220,8 @@ public:
const Enum val; const Enum val;
}; };
enum class OpenDirective { bool open(OpenFlags flags = {});
Continue, APIEvent::Type refreshDeviceState();
Cancel,
Skip
};
enum class OpenStatusType {
QuestionContinueSkipCancel,
QuestionContinueCancel,
Progress,
Failed
};
/**
* @brief Alias of callback function for progress of a bootloader pipeline task
*
* The progress value passed into the callback function indicates the following:
* 1) If the value is std::nullopt, this implies the status message is for an asynchronous task or a task in which the current progress is unknown.
* 2) If the value is between 0 and 1 (i.e., 0 <= progress <= 1), this implies the actual progress of the current task as a percentage.
* 3) Values not between 0 and 1 (i.e., progress < 0 or progress > 1) are undefined.
*/
using OpenStatusHandler = std::function<Device::OpenDirective(OpenStatusType type, const std::string& status, std::optional<double> progress)>;
bool open(OpenFlags flags = {}, OpenStatusHandler handler =
[](OpenStatusType, const std::string&, std::optional<double>) { return Device::OpenDirective::Continue; });
virtual bool close(); virtual bool close();
virtual bool isOnline() const { return online; } virtual bool isOnline() const { return online; }
virtual bool isOpen() const { return com->isOpen(); } virtual bool isOpen() const { return com->isOpen(); }
+4 -4
View File
@@ -62,7 +62,7 @@ public:
RADGigastar2 = icsneoc2_devicetype_rad_gigastar2, RADGigastar2 = icsneoc2_devicetype_rad_gigastar2,
FIRE3_T1S_LIN = icsneoc2_devicetype_fire3_t1s_lin, FIRE3_T1S_LIN = icsneoc2_devicetype_fire3_t1s_lin,
FIRE3_T1S_SENT = icsneoc2_devicetype_fire3_t1s_sent, FIRE3_T1S_SENT = icsneoc2_devicetype_fire3_t1s_sent,
RADGemini = icsneoc2_devicetype_rad_gemini, RADArgos = icsneoc2_devicetype_rad_argos,
RED = icsneoc2_devicetype_red, RED = icsneoc2_devicetype_red,
ECU = icsneoc2_devicetype_ecu, ECU = icsneoc2_devicetype_ecu,
IEVB = icsneoc2_devicetype_ievb, IEVB = icsneoc2_devicetype_ievb,
@@ -153,8 +153,8 @@ public:
return "RAD-EpsilonXL"; return "RAD-EpsilonXL";
case RADMoon3: case RADMoon3:
return "RAD-Moon 3"; return "RAD-Moon 3";
case RADGemini: case RADArgos:
return "RAD-Gemini"; return "RAD-Argos";
case RADComet2: case RADComet2:
return "RAD-Comet 2"; return "RAD-Comet 2";
case RED: case RED:
@@ -281,7 +281,7 @@ private:
#define ICSNEO_DEVICETYPE_RADGIGASTAR2 ((devicetype_t)icsneoc2_devicetype_rad_gigastar2) #define ICSNEO_DEVICETYPE_RADGIGASTAR2 ((devicetype_t)icsneoc2_devicetype_rad_gigastar2)
#define ICSNEO_DEVICETYPE_FIRE3_T1S_LIN ((devicetype_t)icsneoc2_devicetype_fire3_t1s_lin) #define ICSNEO_DEVICETYPE_FIRE3_T1S_LIN ((devicetype_t)icsneoc2_devicetype_fire3_t1s_lin)
#define ICSNEO_DEVICETYPE_FIRE3_T1S_SENT ((devicetype_t)icsneoc2_devicetype_fire3_t1s_sent) #define ICSNEO_DEVICETYPE_FIRE3_T1S_SENT ((devicetype_t)icsneoc2_devicetype_fire3_t1s_sent)
#define ICSNEO_DEVICETYPE_RADGEMINI ((devicetype_t)icsneoc2_devicetype_rad_gemini) #define ICSNEO_DEVICETYPE_RADARGOS ((devicetype_t)icsneoc2_devicetype_rad_argos)
#define ICSNEO_DEVICETYPE_RED ((devicetype_t)icsneoc2_devicetype_red) #define ICSNEO_DEVICETYPE_RED ((devicetype_t)icsneoc2_devicetype_red)
#define ICSNEO_DEVICETYPE_ECU ((devicetype_t)icsneoc2_devicetype_ecu) #define ICSNEO_DEVICETYPE_ECU ((devicetype_t)icsneoc2_devicetype_ecu)
#define ICSNEO_DEVICETYPE_IEVB ((devicetype_t)icsneoc2_devicetype_ievb) #define ICSNEO_DEVICETYPE_IEVB ((devicetype_t)icsneoc2_devicetype_ievb)
@@ -0,0 +1,55 @@
#ifndef __BOOTLOADER_API_H_
#define __BOOTLOADER_API_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/chip.h"
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include "icsneo/device/extensions/bootloader/parser/ief.h"
#include "icsneo/device/device.h"
#include <string>
#include <optional>
namespace icsneo {
namespace Bootloader {
enum class StatusType {
Progress,
Failed
};
/**
* @brief Alias of callback function for progress of a bootloader pipeline task
*
* The progress value passed into the callback function indicates the following:
* 1) If the value is std::nullopt, this implies the status message is for an asynchronous task or a task in which the current progress is unknown.
* 2) If the value is between 0 and 1 (i.e., 0 <= progress <= 1), this implies the actual progress of the current task as a percentage.
* 3) Values not between 0 and 1 (i.e., progress < 0 or progress > 1) are undefined.
*/
using StatusHandler = std::function<void(StatusType type, const std::string& status, std::optional<double> progress)>;
bool requiresUpdate(icsneo::Device& device, const FWRepo& fw);
bool update(
icsneo::Device& device,
const FWRepo& fw,
StatusHandler statusHandler = [](StatusType, const std::string&, std::optional<double>) {},
bool alreadyInBootloader = false,
bool forceFlash = false
);
bool enterBootloader(icsneo::Device& device);
bool exitBootloader(icsneo::Device& device, StatusHandler statusHandler, bool updatedChips);
bool verifyConnection(icsneo::Device& device, StatusHandler statusHandler);
bool isSFPChip(const icsneo::Bootloader::Chip& chip);
bool isSFPIEF(const icsneo::Bootloader::IEF& ief);
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_API_H_
@@ -0,0 +1,48 @@
#ifndef __BOOTLOADER_BYTESVIEW_H_
#define __BOOTLOADER_BYTESVIEW_H_
#ifdef __cplusplus
#include <cstdint>
#include <cstddef>
namespace icsneo {
namespace Bootloader {
class BytesView {
private:
const uint8_t* ptr = nullptr;
size_t length = 0;
public:
BytesView() = default;
BytesView(const uint8_t* buf, size_t size) : ptr(buf), length(size) {}
const uint8_t* begin() const {
return ptr;
}
const uint8_t* end() const {
return ptr + length;
}
const uint8_t* data() const {
return ptr;
}
size_t size() const {
return length;
}
const uint8_t& operator[](size_t index) const {
return ptr[index];
}
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_BYTESVIEW_H_
@@ -0,0 +1,35 @@
#ifndef __BOOTLOADER_CHIP_H_
#define __BOOTLOADER_CHIP_H_
#ifdef __cplusplus
#include "icsneo/device/chipinfo.h"
#include <cstdint>
#include <string>
#include <vector>
namespace icsneo {
namespace Bootloader {
struct Chip {
uint8_t expansionSlot = 0;
ChipInfo info;
Chip(const ChipInfo& info) : info(info) {}
ChipID getID() const { return info.id; }
std::string getName() const { return info.name ? info.name : ""; }
std::string getIEFName() const { return info.iefName ? info.iefName : ""; }
const std::vector<const char*>& getMultiIEFName() const { return info.iefSegments; }
FirmwareType getFirmwareType() const { return info.fwType; }
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_CHIP_H_
@@ -0,0 +1,92 @@
#ifndef __BOOTLOADER_APPLICATION_H_
#define __BOOTLOADER_APPLICATION_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/communication/communication.h"
#include "icsneo/device/extensions/bootloader/parser/ief.h"
#include <chrono>
#include <cstdint>
#include <functional>
#include <vector>
namespace icsneo {
namespace Bootloader {
/**
* The software update protocol used with the application
* on RED devices, does not require entering the bootloader.
*/
class Application : public Communication {
public:
Application(icsneo::Device& dev, const ChipInfo& chip, bool doAuthChecks = false, bool encrypt = false)
: Communication(dev), targetChip(chip), doAuthChecks(doAuthChecks), encrypted(encrypt) {}
void setExpansionSlot(uint8_t slot) { targetChip.expansionSlot = slot; }
bool enterApplication() override;
bool hasFinalizationStep() const override {
return true;
}
bool finalize() override;
bool flash(const FWRepo& repo, const StatusHandler& handler) override;
bool open() override {
return true;
}
bool close() override {
return true;
}
bool authenticate();
bool sendSoftware(const std::vector<uint8_t>& bin, const StatusHandler& handler);
bool sendSoftware(const std::vector<IEF::Sector>& sectors, uint32_t blockSize, const StatusHandler& handler);
bool validateFirmware(const DeviceAppVersion& chipSrcVer);
bool validateComponent();
private:
// Sub-commands of ExtendedCommand::SoftwareUpdate
enum class Update : uint8_t {
Write = 0,
Erase = 1,
GetProgress = 2,
CheckVersion = 5,
ValidateComponent = 6,
Finalize = 7,
CheckComponentVersion = 9
};
// Prefixes the arguments of every ExtendedCommand::SoftwareUpdate request, and is echoed back with progress
#pragma pack(push, 2)
struct SoftwareUpdateCommandHeader {
uint32_t componentIdentifier; // unique id for the software component - analogous to what used to be "chip id"
Update commandType;
uint8_t expansionSlot;
uint32_t offset;
uint32_t commandSizeOrProgress; // size
};
#pragma pack(pop)
Chip targetChip;
const bool doAuthChecks;
const bool encrypted;
using CommandStatusHandler = std::function<void(double progress)>;
bool sendCommand(Update command, const CommandStatusHandler& handler, uint32_t offset = 0,
const std::vector<uint8_t>& args = {}, std::chrono::milliseconds timeout = std::chrono::milliseconds(3000)) {
return sendCommand(command, offset, static_cast<uint32_t>(args.size()), handler, timeout, args);
}
bool sendCommand(Update command, uint32_t offset, uint32_t length, const CommandStatusHandler& handler,
std::chrono::milliseconds timeout = std::chrono::milliseconds(3000), std::vector<uint8_t> args = {});
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_APPLICATION_H_
@@ -0,0 +1,37 @@
#ifndef __BOOTLOADER_BOOTLOADERVERSION_H_
#define __BOOTLOADER_BOOTLOADERVERSION_H_
#ifdef __cplusplus
#include <cstdint>
namespace icsneo {
namespace Bootloader {
#pragma pack(push, 1)
struct BootloaderVersionResponse {
uint8_t blMajor;
uint8_t blMinor;
uint16_t reserved1;
uint8_t hwMajor;
uint8_t hwMinor;
uint8_t hwPlatform;
uint16_t reserved2;
uint8_t manDay;
uint8_t manMonth;
uint16_t manYear;
uint8_t serialNum[8];
uint8_t hwId;
uint8_t reserved3;
uint8_t zynqId;
};
#pragma pack(pop)
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_BOOTLOADERVERSION_H_
@@ -0,0 +1,50 @@
#ifndef __BOOTLOADER_COMMUNICATION_H_
#define __BOOTLOADER_COMMUNICATION_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/chip.h"
#include "icsneo/device/device.h"
#include "icsneo/device/extensions/bootloader/api.h"
#include <cstdint>
namespace icsneo {
namespace Bootloader {
class FWRepo;
/**
* A bootloader protocol for one chip.
*
* Authentication is a challenge/response handled by libicsbl: icsbl_generate_seed_v1() derives
* a challenge (seed.a) and the reply the device must send back (seed.b) from the serial number.
* Firmware is encrypted for a specific device with icsbl_encrypt_v1(), also keyed by the serial number.
*/
class Communication {
public:
Communication(icsneo::Device& dev) : device(dev) {}
virtual ~Communication() = default;
virtual bool enterApplication() = 0;
virtual bool hasFinalizationStep() const = 0;
virtual bool finalize() = 0;
virtual bool flash(const FWRepo& repo, const StatusHandler& handler) = 0;
virtual bool open() = 0;
virtual bool close() = 0;
protected:
static uint32_t readLE32(const uint8_t* data);
static void writeLE32(uint8_t* data, uint32_t val);
icsneo::Device& device;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_COMMUNICATION_H_
@@ -0,0 +1,108 @@
#ifndef __BOOTLOADER_RADCOMMUNICATION_H_
#define __BOOTLOADER_RADCOMMUNICATION_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/communication/communication.h"
#include <chrono>
#include <cstdint>
#include <vector>
namespace icsneo {
namespace Bootloader {
/**
* The bootloader protocol used by RAD devices.
*
* Requests are a command byte followed by the payload, responses are the command byte,
* a Response::Type byte and then the payload. Newer RAD bootloaders (the "Zynq binary
* flasher") also prefix each request with ZynqFlasherSync and a 16-bit little-endian
* length of the command byte plus payload.
*/
class RADCommunication : public Communication {
protected:
static constexpr size_t MaxMessageLength = 1518;
static constexpr uint8_t ZynqFlasherSync[4] = {0xAA, 0xAA, 0x55, 0x55};
static constexpr size_t ZynqFlasherHeaderSize = sizeof(ZynqFlasherSync) + sizeof(uint16_t);
struct Request {
enum class Type : uint8_t {
BootloaderVersion = 0x01,
EnterApplication = 0x02,
BeginTransfer = 0x03,
FWPayload = 0x04,
EndTransfer = 0x05,
ProgramFlash = 0x06,
DownloadedBinInfo = 0x07,
ProgrammingStatus = 0x08,
Authenticate = 0x09,
SanityCheck = 0x0A,
VerifyCRC = 0x0B,
ValidateVersion = 0x0F,
Invalid = 0xFF
} type = Type::Invalid;
uint8_t* payload = nullptr;
size_t expectedResponseLen = 0;
};
Request makeRequest(Request::Type type, size_t payloadSize = 0, size_t expectedResponseLen = 0);
struct Response {
enum class Type {
Failure = 0x00,
Success = 0x01
} type = Type::Failure;
uint8_t* payload = nullptr;
size_t size = 0;
bool done = false;
operator bool() const {
return done && type == Type::Success;
}
};
Response& sendRequest(const Request& request, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000));
bool sendRequestNoResponse();
StatusHandler statusHandler; // Progress updates, set in flash()
Response lastResponse; // Last response received from sendRequest
bool supportsZynqBinaryFlasher = false; // Dictates how messages are formatted, this gets set in initialize()
std::vector<uint8_t> rxBuffer;
std::vector<uint8_t> txBuffer;
Chip targetChip; // Chip to flash
bool loadZips(const FWRepo& fw, std::vector<uint8_t>& chipSoftware);
bool initialize();
bool sanityCheck();
bool getBootloaderVersion();
bool validateChipVersion(const icsneo::DeviceAppVersion& srcVer);
virtual bool authenticate();
bool sendSoftware(const std::vector<uint8_t>& chipSoftware);
bool getProgrammingStatus(size_t sizeToValidate);
bool supportsFlashBuffer() const;
public:
RADCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash);
bool enterApplication() override;
bool finalize() override;
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
bool close() override;
bool hasFinalizationStep() const override;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_RADCOMMUNICATION_H_
@@ -0,0 +1,37 @@
#ifndef __BOOTLOADER_RADMULTICHIPCOMMUNICATION_H_
#define __BOOTLOADER_RADMULTICHIPCOMMUNICATION_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/communication/radcommunication.h"
#include "icsneo/device/extensions/bootloader/communication/application.h"
#include <cstdint>
#include <memory>
namespace icsneo {
namespace Bootloader {
class RADMultiChipCommunication : public RADCommunication {
private:
std::unique_ptr<Application> standardComm;
const bool doAuthChecks;
const uint8_t slot;
bool authenticate() override;
public:
RADMultiChipCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash, bool auth, uint8_t slot = UINT8_MAX);
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
bool close() override;
bool enterApplication() override;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_RADMULTICHIPCOMMUNICATION_H_
@@ -0,0 +1,96 @@
#ifndef __BOOTLOADER_RADPERIPHCOMMUNICATION_H_
#define __BOOTLOADER_RADPERIPHCOMMUNICATION_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/communication/communication.h"
#include "icsneo/device/extensions/bootloader/communication/bootloaderversion.h"
#include "icsneo/device/extensions/bootloader/parser/ief.h"
#include <chrono>
#include <cstdint>
#include <vector>
namespace icsneo {
namespace Bootloader {
/**
* The bootloader protocol used by peripheral chips on the RAD-Galaxy 2, tunneled through
* Command::PeripheralBootloader. Requests are the chip id, the Request and the arguments.
* Replies are a byte, ReplySuccess, then for bootloader requests ReplyAccepted, then the payload.
*/
class RADPeriphCommunication : public Communication {
protected:
static constexpr size_t ReplySuccessOffset = 1;
static constexpr size_t ReplyAcceptedOffset = 2;
static constexpr uint8_t ReplySuccess = 0x01;
static constexpr uint8_t ReplyAccepted = 0x77;
static constexpr size_t ReplyHeaderSize = 3;
// Requests that write to the bootloader have three leading zero bytes before their arguments
static constexpr size_t BootloaderArgsOffset = 3;
// Flash sectors are sent as two requests, the first with this many bytes and the second with the rest
static constexpr size_t FlashSectorFirstChunkSize = 1024;
enum class Mode : uint8_t {
NoAnswer = 0x00u,
MainApplication = 0xAFu,
Bootloader = 0xFEu
} mode = Mode::NoAnswer;
Chip targetChip;
uint32_t serialNumber = 0;
public:
enum class Request : uint8_t {
SwitchToApp = 0x01u,
SwitchToBootloader = 0x02u,
Version = 0x12u,
Authenticate = 0x13u,
FlashSector = 0x14u,
FirmwareValidate = 0x16u,
Erase = 0x20u,
Echo = 0xAFu,
Mode = 0xADu,
};
struct Response {
bool success = false;
std::vector<uint8_t> data;
operator bool() const {
return success;
}
};
bool waitForConnect(std::chrono::milliseconds timeout = std::chrono::milliseconds(30000));
bool sendRequestNoResponse(Request req, std::vector<uint8_t>&& args = {});
bool sendRequestNoResponse(Request req, ChipID id, std::vector<uint8_t>&& args = {});
Response sendRequest(Request req, std::vector<uint8_t>&& args = {}, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000));
Response sendRequest(Request req, ChipID id, std::vector<uint8_t>&& args = {}, std::chrono::milliseconds timeout = std::chrono::milliseconds(2000));
Mode getMode();
bool sendPeriphTest();
bool readSerialNumber();
bool authenticate();
bool validateFirmware();
bool flashSector(const IEF::Sector& sector);
RADPeriphCommunication(icsneo::Device& dev, const ChipInfo& chipToFlash);
~RADPeriphCommunication();
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
bool close() override;
bool enterApplication() override;
bool finalize() override;
bool hasFinalizationStep() const override;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_RADPERIPHCOMMUNICATION_H_
@@ -0,0 +1,72 @@
#ifndef __BOOTLOADER_RED_H_
#define __BOOTLOADER_RED_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/communication/redbase.h"
#include <cstddef>
#include <cstdint>
namespace icsneo {
namespace Bootloader {
/**
* The bootloader communication protocol used for non-FPGA
* chips on neoVI and ValueCAN devices.
*
* Zynq doesn't count as an FPGA chip for the sake of
* this, so it's handled here.
*/
class REDCommunication : public REDCommunicationBase {
public:
REDCommunication(icsneo::Device& dev, const ChipInfo& chip, bool auth, bool encryptWithDeviceKey = true);
bool enterApplication() override { return sendCommand(uint8_t(Command::EnterApplication)); }
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
bool open() override;
private:
const bool doAuthChecks;
const bool encryptFirmwareWithDeviceKey;
size_t blockSize = 2048;
enum class Command : uint8_t {
GetVersion = 0x12,
Authenticate = 0x13,
Flash = 0x14,
ValidateFirmware = 0x16,
Erase = 0x20,
EnterApplication = 0x21,
ValidateSWVersion = 0x27,
};
// The reply to Command::GetVersion
struct VersionResponse {
uint8_t bl_major, bl_minor, app_major, app_minor, flags, prod_id;
uint8_t hardware_rev_major, hardware_rev_minor;
uint8_t man_day, man_month;
uint16_t man_year;
uint8_t serial_num[8];
uint16_t block_size;
uint8_t last_error_code;
uint8_t pad_byte;
static constexpr size_t EncodedSize = 24;
VersionResponse() = default;
VersionResponse(const REDPacketizer::RxFrame& r);
} currentVersion;
// Called from open()
bool authenticate(bool firstTime = true);
bool getVersion();
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_RED_H_
@@ -0,0 +1,66 @@
#ifndef __BOOTLOADER_REDBASE_H_
#define __BOOTLOADER_REDBASE_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/communication/communication.h"
#include "icsneo/device/extensions/bootloader/communication/redpacketizer.h"
#include <chrono>
#include <cstdint>
#include <vector>
namespace icsneo {
namespace Bootloader {
/**
* Implementation shared by REDCommunication and REDCoreCommunication
*
* Requests are framed as:
* uint8_t REDPacketizer::Magic
* uint16_t chip id (little-endian)
* uint16_t length of the command byte, arguments and padding (little-endian)
* uint8_t command
* uint8_t arguments[]
* uint8_t optional PaddingByte, see sendCommandRaw()
*/
class REDCommunicationBase : public Communication {
public:
REDCommunicationBase(icsneo::Device& dev, const ChipInfo& chip);
~REDCommunicationBase() {
// This must happen before the Packetizer is destroyed
device.com->resumeReads();
}
bool open() override;
bool close() override;
bool hasFinalizationStep() const override {
return false;
}
bool finalize() override {
return true;
}
protected:
static constexpr uint8_t PaddingByte = 0x55;
Chip targetChip;
std::vector<REDPacketizer::RxFrame> lastData;
REDPacketizer rp;
bool handleData();
bool sendCommand(uint8_t cmd, std::chrono::seconds timeout, bool clearLastResponse = true) { return sendCommand(cmd, {}, timeout, clearLastResponse); }
bool sendCommand(uint8_t cmd, std::vector<uint8_t> args = {}, std::chrono::seconds timeout = std::chrono::seconds(1), bool clearLastResponse = true);
virtual bool sendCommandRaw(uint8_t cmd, std::vector<uint8_t> args = {});
bool waitForRx(std::chrono::milliseconds timeout);
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_REDBASE_H_
@@ -0,0 +1,54 @@
#ifndef __BOOTLOADER_REDCORE_H_
#define __BOOTLOADER_REDCORE_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/communication/redbase.h"
#include <cstdint>
#include <vector>
namespace icsneo {
namespace Bootloader {
/**
* The bootloader communication protocol used for FPGA
* chips on neoVI and ValueCAN devices.
*
* Zynq doesn't count as an FPGA chip for the sake of
* this, it's just targetting the non-hybrid FPGAs.
*
* This was previously called FIRE 2 Core Flasher, but
* it does more than just FIRE 2 nowadays.
*/
class REDCoreCommunication : public REDCommunicationBase {
public:
using REDCommunicationBase::REDCommunicationBase;
bool enterApplication() override { return false; }
bool flash(const FWRepo& fw, const StatusHandler& handler) override;
private:
/**
* Core flasher commands are tunneled through a RED request using CoreFlasherCommand,
* the arguments are CoreFlasherMagic, a 16-bit little-endian length, then the core command and its arguments
*/
static constexpr uint8_t CoreFlasherCommand = 0xCD;
static constexpr uint8_t CoreFlasherMagic = 0xEF;
enum class Command : uint8_t {
SendFirmware = 0,
StartFirmwareFlash = 1,
GetFlashProgress = 2,
};
bool sendCommandRaw(uint8_t cmd, std::vector<uint8_t> args = {}) override;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_REDCORE_H_
@@ -0,0 +1,56 @@
#ifndef __BOOTLOADER_REDPACKETIZER_H_
#define __BOOTLOADER_REDPACKETIZER_H_
#ifdef __cplusplus
#include "icsneo/core/ringbuffer.h"
#include <cstdint>
#include <vector>
namespace icsneo {
namespace Bootloader {
/**
* Splits bytes from a RED bootloader into frames:
* uint8_t Magic
* uint8_t Response
* uint16_t length (little-endian)
* uint8_t data[length]
*/
class REDPacketizer {
public:
static constexpr uint8_t Magic = 0xAA;
enum class Response : uint8_t {
Accepted = 0x77,
Rejected = 0x7F,
};
struct RxFrame {
uint16_t len = 0;
Response resp = Response::Rejected;
std::vector<uint8_t> data;
bool done = false;
};
/**
* Call with bytes received from the device.
* The returned boolean indicates if there is output waiting in output()
*/
bool input(icsneo::RingBuffer& bytes);
std::vector<RxFrame> output();
private:
enum ReadState { HEADER, CMD, LENGTH_LOWER, LENGTH_UPPER, DATA };
ReadState state = HEADER;
std::vector<RxFrame> working;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_REDPACKETIZER_H_
@@ -0,0 +1,74 @@
#ifndef __BOOTLOADER_FWBIN_H_
#define __BOOTLOADER_FWBIN_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include <cstdint>
#include <fstream>
#include <map>
#include <optional>
#include <string>
#include <vector>
namespace icsneo {
namespace Bootloader {
class FWBin : public FWRepo {
public:
FWBin() = default;
bool load(const std::string& path);
const FirmwareSourceVersion& getVersion() const override { return version; }
bool getFW(const std::string& iefName, std::vector<uint8_t>& fw) const override;
std::optional<icsneo::DeviceAppVersion> getChipVersion(const std::string& iefName) const override;
static uint32_t HashDjb2(const std::string& str);
private:
static constexpr const uint32_t SignatureHash = 0;
struct VersionTags {
const char* majorTag = nullptr;
const char* minorTag = nullptr;
operator bool() const {
return majorTag && minorTag;
}
};
static VersionTags GetVersionTags(const std::string& iefName);
struct TagData {
enum class Type : uint8_t {
DataBytes = 0,
DataLength = 1,
IEFVersion = 2,
Signature = 3,
Unknown = 0xFF
};
Type type = Type::Unknown;
std::streampos arrayPosition = 0;
uint32_t arrayLength = 0;
bool read(std::ifstream& stream, std::vector<uint8_t>& out) const;
};
bool readTag(const std::string& tag, std::vector<uint8_t>& out) const;
mutable std::ifstream file;
FirmwareSourceVersion version;
std::map<uint32_t, TagData> tags;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_FWBIN_H_
@@ -0,0 +1,45 @@
#ifndef __BOOTLOADER_FWBUNDLE_H_
#define __BOOTLOADER_FWBUNDLE_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace icsneo {
namespace Bootloader {
class FWBundle : public FWRepo {
public:
FWBundle() = default;
bool load(const std::string& path);
static bool IsFWBundle(const std::string& path);
const FirmwareSourceVersion& getVersion() const override { return version; }
bool getFW(const std::string& iefName, std::vector<uint8_t>& fw) const override;
std::optional<icsneo::DeviceAppVersion> getChipVersion(const std::string& iefName) const override;
private:
struct ChipBinary {
std::vector<uint8_t> data;
FirmwareSourceVersion version;
};
FirmwareSourceVersion version;
std::unordered_map<uint32_t, ChipBinary> binaries;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_FWBUNDLE_H_
@@ -0,0 +1,27 @@
#ifndef __BOOTLOADER_FWLOADER_H_
#define __BOOTLOADER_FWLOADER_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/parser/fwrepo.h"
#include <memory>
#include <string>
namespace icsneo {
namespace Bootloader {
class FWLoader {
public:
// Detects whether the file is a firmware bundle or a fw.bin and loads it, returns nullptr on failure
static std::unique_ptr<FWRepo> Load(const std::string& path);
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_FWLOADER_H_
@@ -0,0 +1,34 @@
#ifndef __BOOTLOADER_FWREPO_H_
#define __BOOTLOADER_FWREPO_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/version.h"
#include "icsneo/device/deviceversion.h"
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
namespace icsneo {
namespace Bootloader {
class FWRepo {
public:
FWRepo() = default;
virtual ~FWRepo() = default;
virtual bool getFW(const std::string& iefName, std::vector<uint8_t>& fw) const = 0;
virtual std::optional<icsneo::DeviceAppVersion> getChipVersion(const std::string& iefName) const = 0;
virtual const FirmwareSourceVersion& getVersion() const = 0;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_FWREPO_H_
@@ -0,0 +1,37 @@
#ifndef __BOOTLOADER_FWZIP_H_
#define __BOOTLOADER_FWZIP_H_
#ifdef __cplusplus
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>
namespace icsneo {
namespace Bootloader {
class ZipHandle;
class FWZip {
private:
std::unique_ptr<ZipHandle> handle;
std::vector<uint8_t> zipData;
std::optional<size_t> uncompressedSize = std::nullopt;
public:
FWZip(std::vector<uint8_t>&& tagData);
~FWZip();
std::optional<size_t> size() const { return uncompressedSize; }
bool readAll(uint8_t* out);
bool readAll(std::vector<uint8_t>& out);
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_FWZIP_H_
@@ -0,0 +1,47 @@
#ifndef __BOOTLOADER_IEF_H_
#define __BOOTLOADER_IEF_H_
#ifdef __cplusplus
#include "icsneo/device/extensions/bootloader/bytesview.h"
#include <cstdint>
#include <utility>
#include <vector>
namespace icsneo {
namespace Bootloader {
class IEF {
public:
struct Version {
uint8_t appMajor;
uint8_t appMinor;
uint8_t prodId;
uint8_t chipId;
};
using Sector = std::pair<uint32_t, BytesView>;
IEF() = default;
bool load(std::vector<uint8_t>&& bytes);
const Version& getVersion() const { return version; }
const std::vector<Sector>& getSectors() const { return sectors; }
// IEFs are comprised of equally sized blocks of data - take the size of the first
uint32_t getBlockSize() const { return sectors.empty() ? 0 : static_cast<uint32_t>(sectors.front().second.size()); }
private:
std::vector<uint8_t> data;
Version version = {};
std::vector<Sector> sectors;
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_IEF_H_
@@ -0,0 +1,28 @@
#ifndef __BOOTLOADER_LOGGING_H_
#define __BOOTLOADER_LOGGING_H_
#ifdef __cplusplus
#include <string>
namespace icsneo {
namespace Bootloader {
namespace Log {
void Trace(const std::string& subject, const std::string& msg);
void Debug(const std::string& subject, const std::string& msg);
void Info(const std::string& subject, const std::string& msg);
void Warning(const std::string& subject, const std::string& msg);
void Error(const std::string& subject, const std::string& msg);
} // namespace Log
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_LOGGING_H_
@@ -0,0 +1,32 @@
#ifndef __BOOTLOADER_VERSION_H_
#define __BOOTLOADER_VERSION_H_
#ifdef __cplusplus
#include <cstdint>
#include <string>
namespace icsneo {
namespace Bootloader {
/**
* Tracks the Vehicle Spy 3 version that a firmware
* package is intended for.
*/
struct FirmwareSourceVersion {
using ValueType = uint8_t;
ValueType major = 0;
ValueType minor = 0;
ValueType release = 0;
ValueType build = 0;
bool load(const std::string& toParse);
};
} // namespace Bootloader
} // namespace icsneo
#endif // __cplusplus
#endif // __BOOTLOADER_VERSION_H_
@@ -1,17 +0,0 @@
#ifndef __ICSNEO_DEVICE_EXTENSIONS_BUILTIN_H_
#define __ICSNEO_DEVICE_EXTENSIONS_BUILTIN_H_
@LIBICSNEO_EXT_CODE_INCS@
#include "icsneo/device/device.h"
#include <memory>
namespace icsneo {
static void AddBuiltInExtensionsTo(const std::shared_ptr<icsneo::Device>& device) {
(void)device; // [[maybe_unused]]
@LIBICSNEO_EXT_CODE@
}
}
#endif
@@ -16,12 +16,6 @@ public:
virtual ~DeviceExtension() = default; virtual ~DeviceExtension() = default;
virtual const char* getName() const = 0; virtual const char* getName() const = 0;
// Return false to block opening
virtual bool onDeviceOpen(Device::OpenFlags, const Device::OpenStatusHandler&) { return true; }
// Return true to indicate that communication should now be back
virtual bool onDeviceCommunicationDead(Device::OpenFlags, const Device::OpenStatusHandler&) { return false; }
virtual void onGoOnline() {} virtual void onGoOnline() {}
virtual void onGoOffline() {} virtual void onGoOffline() {}
virtual void onDeviceClose() {} virtual void onDeviceClose() {}
+48 -137
View File
@@ -1205,143 +1205,32 @@ public:
return std::nullopt; return std::nullopt;
} }
virtual std::optional<bool> isT1SPLCAEnabledFor(Network net) const { // 10BASE-T1S. Devices expose a network by overriding getT1SSettingsFor / getT1SSettingsExtFor.
(void)net; // Every field in the mapped struct is readable; a null accessor means the network has no T1S block.
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); std::optional<bool> isT1SPLCAEnabledFor(Network net) const;
return std::nullopt; bool setT1SPLCAFor(Network net, bool enable);
} std::optional<uint8_t> getT1SLocalIDFor(Network net) const;
bool setT1SLocalIDFor(Network net, uint8_t id);
virtual bool setT1SPLCAFor(Network net, bool enable) { std::optional<uint8_t> getT1SMaxNodesFor(Network net) const;
(void)net; (void)enable; bool setT1SMaxNodesFor(Network net, uint8_t nodes);
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); std::optional<uint8_t> getT1STxOppTimerFor(Network net) const;
return false; bool setT1STxOppTimerFor(Network net, uint8_t timer);
} std::optional<uint8_t> getT1SMaxBurstFor(Network net) const;
bool setT1SMaxBurstFor(Network net, uint8_t burst);
virtual std::optional<uint8_t> getT1SLocalIDFor(Network net) const { std::optional<uint8_t> getT1SBurstTimerFor(Network net) const;
(void)net; bool setT1SBurstTimerFor(Network net, uint8_t timer);
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); std::optional<uint8_t> getT1SLocalIDAlternateFor(Network net) const;
return std::nullopt; bool setT1SLocalIDAlternateFor(Network net, uint8_t id);
} std::optional<bool> isT1STerminationEnabledFor(Network net) const;
bool setT1STerminationFor(Network net, bool enable);
virtual bool setT1SLocalIDFor(Network net, uint8_t id) { std::optional<bool> isT1SBusDecodingBeaconsEnabledFor(Network net) const;
(void)net; (void)id; bool setT1SBusDecodingBeaconsFor(Network net, bool enable);
return false; std::optional<bool> isT1SBusDecodingAllEnabledFor(Network net) const;
} bool setT1SBusDecodingAllFor(Network net, bool enable);
std::optional<uint8_t> getT1SMultiIDEnableMaskFor(Network net) const;
virtual std::optional<uint8_t> getT1SMaxNodesFor(Network net) const { bool setT1SMultiIDEnableMaskFor(Network net, uint8_t mask);
(void)net; std::optional<uint8_t> getT1SMultiIDFor(Network net, uint8_t index) const;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); bool setT1SMultiIDFor(Network net, uint8_t index, uint8_t id);
return std::nullopt;
}
virtual bool setT1SMaxNodesFor(Network net, uint8_t nodes) {
(void)net; (void)nodes;
return false;
}
virtual std::optional<uint8_t> getT1STxOppTimerFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1STxOppTimerFor(Network net, uint8_t timer) {
(void)net; (void)timer;
return false;
}
virtual std::optional<uint8_t> getT1SMaxBurstFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SMaxBurstFor(Network net, uint8_t burst) {
(void)net; (void)burst;
return false;
}
virtual std::optional<uint8_t> getT1SBurstTimerFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SBurstTimerFor(Network net, uint8_t timer) {
(void)net; (void)timer;
return false;
}
virtual std::optional<uint8_t> getT1SLocalIDAlternateFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SLocalIDAlternateFor(Network net, uint8_t id) {
(void)net; (void)id;
return false;
}
virtual std::optional<bool> isT1STerminationEnabledFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1STerminationFor(Network net, bool enable) {
(void)net; (void)enable;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
virtual std::optional<bool> isT1SBusDecodingBeaconsEnabledFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SBusDecodingBeaconsFor(Network net, bool enable) {
(void)net; (void)enable;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
virtual std::optional<bool> isT1SBusDecodingAllEnabledFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SBusDecodingAllFor(Network net, bool enable) {
(void)net; (void)enable;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
virtual std::optional<uint8_t> getT1SMultiIDEnableMaskFor(Network net) const {
(void)net;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SMultiIDEnableMaskFor(Network net, uint8_t mask) {
(void)net; (void)mask;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
virtual std::optional<uint8_t> getT1SMultiIDFor(Network net, uint8_t index) const {
(void)net; (void)index;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
virtual bool setT1SMultiIDFor(Network net, uint8_t index, uint8_t id) {
(void)net; (void)index; (void)id;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
virtual std::optional<bool> isPerfTestEnabled() const { virtual std::optional<bool> isPerfTestEnabled() const {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning); report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
@@ -1427,6 +1316,28 @@ protected:
return reinterpret_cast<ICSNEO_UNALIGNED(uint64_t*)>((void*)(settings.data() + (offset - settingsInDeviceRAM.data()))); return reinterpret_cast<ICSNEO_UNALIGNED(uint64_t*)>((void*)(settings.data() + (offset - settingsInDeviceRAM.data())));
} }
// Pointer into the device RAM image. Override per device. Unknown networks return nullptr
// and do not report; the public T1S methods report SettingNotAvaiableDevice.
virtual const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const { (void)net; return nullptr; }
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) {
if(disabled || readonly)
return nullptr;
const uint8_t* offset = reinterpret_cast<const uint8_t*>(getT1SSettingsFor(net));
if(offset == nullptr)
return nullptr;
return reinterpret_cast<ETHERNET10T1S_SETTINGS*>(settings.data() + (offset - settingsInDeviceRAM.data()));
}
virtual const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const { (void)net; return nullptr; }
ETHERNET10T1S_SETTINGS_EXT* getMutableT1SSettingsExtFor(Network net) {
if(disabled || readonly)
return nullptr;
const uint8_t* offset = reinterpret_cast<const uint8_t*>(getT1SSettingsExtFor(net));
if(offset == nullptr)
return nullptr;
return reinterpret_cast<ETHERNET10T1S_SETTINGS_EXT*>(settings.data() + (offset - settingsInDeviceRAM.data()));
}
static bool SetNetworkEnabled(uint64_t* bitfields, size_t count, uint64_t networkID) { static bool SetNetworkEnabled(uint64_t* bitfields, size_t count, uint64_t networkID) {
const size_t index = networkID / 64; const size_t index = networkID / 64;
const size_t offset = networkID & 0x3F; const size_t offset = networkID & 0x3F;
@@ -269,215 +269,6 @@ public:
} }
} }
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
std::optional<uint8_t> getT1SLocalIDAlternateFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id_alternate);
}
bool setT1SLocalIDAlternateFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id_alternate = id;
return true;
}
std::optional<bool> isT1SBusDecodingBeaconsEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS) != 0);
}
bool setT1SBusDecodingBeaconsFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
return true;
}
std::optional<bool> isT1SBusDecodingAllEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL) != 0);
}
bool setT1SBusDecodingAllFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
return true;
}
std::optional<uint8_t> getT1SMultiIDEnableMaskFor(Network net) const override {
const ETHERNET10T1S_SETTINGS_EXT* t1sExt = getT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return std::nullopt;
return std::make_optional(t1sExt->enable_multi_id);
}
bool setT1SMultiIDEnableMaskFor(Network net, uint8_t mask) override {
ETHERNET10T1S_SETTINGS_EXT* t1sExt = getMutableT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return false;
t1sExt->enable_multi_id = mask;
return true;
}
std::optional<uint8_t> getT1SMultiIDFor(Network net, uint8_t index) const override {
const ETHERNET10T1S_SETTINGS_EXT* t1sExt = getT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return std::nullopt;
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional(t1sExt->multi_id[index]);
}
bool setT1SMultiIDFor(Network net, uint8_t index, uint8_t id) override {
ETHERNET10T1S_SETTINGS_EXT* t1sExt = getMutableT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return false;
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
t1sExt->multi_id[index] = id;
return true;
}
std::optional<bool> isPerfTestEnabled() const override { std::optional<bool> isPerfTestEnabled() const override {
auto cfg = getStructurePointer<neovifire3t1slin_settings_t>(); auto cfg = getStructurePointer<neovifire3t1slin_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
@@ -496,11 +287,10 @@ public:
} }
private: private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const { const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovifire3t1slin_settings_t>(); auto cfg = getStructurePointer<neovifire3t1slin_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_01: return &(cfg->t1s1); case Network::NetID::AE_01: return &(cfg->t1s1);
case Network::NetID::AE_02: return &(cfg->t1s2); case Network::NetID::AE_02: return &(cfg->t1s2);
@@ -510,69 +300,24 @@ private:
case Network::NetID::AE_06: return &(cfg->t1s6); case Network::NetID::AE_06: return &(cfg->t1s6);
case Network::NetID::AE_07: return &(cfg->t1s7); case Network::NetID::AE_07: return &(cfg->t1s7);
case Network::NetID::AE_08: return &(cfg->t1s8); case Network::NetID::AE_08: return &(cfg->t1s8);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) { const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const override {
auto cfg = getMutableStructurePointer<neovifire3t1slin_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01: return &(cfg->t1s1);
case Network::NetID::AE_02: return &(cfg->t1s2);
case Network::NetID::AE_03: return &(cfg->t1s3);
case Network::NetID::AE_04: return &(cfg->t1s4);
case Network::NetID::AE_05: return &(cfg->t1s5);
case Network::NetID::AE_06: return &(cfg->t1s6);
case Network::NetID::AE_07: return &(cfg->t1s7);
case Network::NetID::AE_08: return &(cfg->t1s8);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const {
auto cfg = getStructurePointer<neovifire3t1slin_settings_t>(); auto cfg = getStructurePointer<neovifire3t1slin_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1Ext); case Network::NetID::AE_01: return &(cfg->t1s1Ext);
case Network::NetID::AE_04: return &(cfg->t1s2Ext); case Network::NetID::AE_02: return &(cfg->t1s2Ext);
case Network::NetID::AE_05: return &(cfg->t1s3Ext); case Network::NetID::AE_03: return &(cfg->t1s3Ext);
case Network::NetID::AE_06: return &(cfg->t1s4Ext); case Network::NetID::AE_04: return &(cfg->t1s4Ext);
case Network::NetID::AE_07: return &(cfg->t1s5Ext); case Network::NetID::AE_05: return &(cfg->t1s5Ext);
case Network::NetID::AE_08: return &(cfg->t1s6Ext); case Network::NetID::AE_06: return &(cfg->t1s6Ext);
case Network::NetID::AE_09: return &(cfg->t1s7Ext); case Network::NetID::AE_07: return &(cfg->t1s7Ext);
case Network::NetID::AE_10: return &(cfg->t1s8Ext); case Network::NetID::AE_08: return &(cfg->t1s8Ext);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS_EXT* getMutableT1SSettingsExtFor(Network net) {
auto cfg = getMutableStructurePointer<neovifire3t1slin_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1Ext);
case Network::NetID::AE_04: return &(cfg->t1s2Ext);
case Network::NetID::AE_05: return &(cfg->t1s3Ext);
case Network::NetID::AE_06: return &(cfg->t1s4Ext);
case Network::NetID::AE_07: return &(cfg->t1s5Ext);
case Network::NetID::AE_08: return &(cfg->t1s6Ext);
case Network::NetID::AE_09: return &(cfg->t1s7Ext);
case Network::NetID::AE_10: return &(cfg->t1s8Ext);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
@@ -1,19 +1,19 @@
#ifndef __RADGEMINI_H_ #ifndef __RADARGOS_H_
#define __RADGEMINI_H_ #define __RADARGOS_H_
#ifdef __cplusplus #ifdef __cplusplus
#include "icsneo/device/device.h" #include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h" #include "icsneo/device/devicetype.h"
#include "icsneo/device/tree/radgemini/radgeminisettings.h" #include "icsneo/device/tree/radargos/radargossettings.h"
namespace icsneo { namespace icsneo {
class RADGemini : public Device { class RADArgos : public Device {
public: public:
// Serial numbers start with GE // Serial numbers start with GE
// USB PID is 0x110E, standard driver is CDCACM // USB PID is 0x110E, standard driver is CDCACM
ICSNEO_FINDABLE_DEVICE(RADGemini, DeviceType::RADGemini, "GE"); ICSNEO_FINDABLE_DEVICE(RADArgos, DeviceType::RADArgos, "GE");
static const std::vector<Network>& GetSupportedNetworks() { static const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = { static std::vector<Network> supportedNetworks = {
@@ -28,11 +28,11 @@ public:
bool supportsTC10() const override { return true; } bool supportsTC10() const override { return true; }
ProductID getProductID() const override { return ProductID::RADGemini; } ProductID getProductID() const override { return ProductID::RADArgos; }
const std::vector<ChipInfo>& getChipInfo() const override { const std::vector<ChipInfo>& getChipInfo() const override {
static std::vector<ChipInfo> chips = { static std::vector<ChipInfo> chips = {
{ChipID::RADGemini_MCHIP, true, "MCHIP", "radgemini_mchip_ief", 0, FirmwareType::IEF}, {ChipID::RADArgos_MCHIP, true, "MCHIP", "radargos_mchip_ief", 0, FirmwareType::IEF},
}; };
return chips; return chips;
} }
@@ -40,15 +40,15 @@ public:
BootloaderPipeline getBootloader() override { BootloaderPipeline getBootloader() override {
return BootloaderPipeline() return BootloaderPipeline()
.add<EnterBootloaderPhase>() .add<EnterBootloaderPhase>()
.add<FlashPhase>(ChipID::RADGemini_MCHIP, BootloaderCommunication::RED) .add<FlashPhase>(ChipID::RADArgos_MCHIP, BootloaderCommunication::RED)
.add<EnterApplicationPhase>(ChipID::RADGemini_MCHIP) .add<EnterApplicationPhase>(ChipID::RADArgos_MCHIP)
.add<ReconnectPhase>() .add<ReconnectPhase>()
.add<WaitPhase>(std::chrono::milliseconds(3000)); .add<WaitPhase>(std::chrono::milliseconds(3000));
} }
protected: protected:
RADGemini(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { RADArgos(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADGeminiSettings>(makeDriver); initialize<RADArgosSettings>(makeDriver);
} }
void setupPacketizer(Packetizer& packetizer) override { void setupPacketizer(Packetizer& packetizer) override {
@@ -74,4 +74,4 @@ protected:
#endif // __cplusplus #endif // __cplusplus
#endif // __RADGEMINI_H_ #endif // __RADARGOS_H_
@@ -1,5 +1,5 @@
#ifndef LIBICSNEO_RADGEMINISETTINGS_H #ifndef LIBICSNEO_RADARGOSSETTINGS_H
#define LIBICSNEO_RADGEMINISETTINGS_H #define LIBICSNEO_RADARGOSSETTINGS_H
#include <stdint.h> #include <stdint.h>
#include "icsneo/device/idevicesettings.h" #include "icsneo/device/idevicesettings.h"
@@ -31,18 +31,18 @@ typedef struct {
uint16_t enableLatencyTest : 1; uint16_t enableLatencyTest : 1;
uint16_t reserved : 15; uint16_t reserved : 15;
} flags; // 2 } flags; // 2
} radgemini_settings_t; } radargos_settings_t;
#pragma pack(pop) #pragma pack(pop)
#ifdef __cplusplus #ifdef __cplusplus
static_assert(sizeof(radgemini_settings_t) == 86, "RADGemini settings size mismatch"); static_assert(sizeof(radargos_settings_t) == 86, "RADArgos settings size mismatch");
#include <iostream> #include <iostream>
class RADGeminiSettings : public IDeviceSettings { class RADArgosSettings : public IDeviceSettings {
public: public:
RADGeminiSettings(Device* device) : IDeviceSettings(device, sizeof(radgemini_settings_t)) {} RADArgosSettings(Device* device) : IDeviceSettings(device, sizeof(radargos_settings_t)) {}
}; };
} }
@@ -50,4 +50,4 @@ public:
#endif // __cplusplus #endif // __cplusplus
#endif // LIBICSNEO_RADGEMINISETTINGS_H #endif // LIBICSNEO_RADARGOSSETTINGS_H
@@ -130,133 +130,6 @@ public:
} }
} }
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
std::optional<bool> isT1STerminationEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_TERMINATION) != 0);
}
bool setT1STerminationFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_TERMINATION;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_TERMINATION;
return true;
}
std::optional<bool> isPerfTestEnabled() const override { std::optional<bool> isPerfTestEnabled() const override {
auto cfg = getStructurePointer<radcomet2_settings_t>(); auto cfg = getStructurePointer<radcomet2_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
@@ -274,32 +147,41 @@ public:
return true; return true;
} }
std::vector<TerminationGroup> getTerminationGroups() const override {
return {
{ Network(Network::NetID::DWCAN_01) },
{ Network(Network::NetID::DWCAN_02) }
};
}
protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<radcomet2_settings_t>();
if(cfg == nullptr)
return nullptr;
return &cfg->termination_enables;
}
private: private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const { const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radcomet2_settings_t>(); auto cfg = getStructurePointer<radcomet2_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1); case Network::NetID::AE_02: return &(cfg->t1s1);
case Network::NetID::AE_03: return &(cfg->t1s2); case Network::NetID::AE_03: return &(cfg->t1s2);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) { const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const override {
auto cfg = getMutableStructurePointer<radcomet2_settings_t>(); auto cfg = getStructurePointer<radcomet2_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1); case Network::NetID::AE_02: return &(cfg->t1s1Ext);
case Network::NetID::AE_03: return &(cfg->t1s2); case Network::NetID::AE_03: return &(cfg->t1s2Ext);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
@@ -165,215 +165,6 @@ public:
} }
} }
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
std::optional<uint8_t> getT1SLocalIDAlternateFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id_alternate);
}
bool setT1SLocalIDAlternateFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id_alternate = id;
return true;
}
std::optional<bool> isT1SBusDecodingBeaconsEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS) != 0);
}
bool setT1SBusDecodingBeaconsFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
return true;
}
std::optional<bool> isT1SBusDecodingAllEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL) != 0);
}
bool setT1SBusDecodingAllFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
return true;
}
std::optional<uint8_t> getT1SMultiIDEnableMaskFor(Network net) const override {
const ETHERNET10T1S_SETTINGS_EXT* t1sExt = getT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return std::nullopt;
return std::make_optional(t1sExt->enable_multi_id);
}
bool setT1SMultiIDEnableMaskFor(Network net, uint8_t mask) override {
ETHERNET10T1S_SETTINGS_EXT* t1sExt = getMutableT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return false;
t1sExt->enable_multi_id = mask;
return true;
}
std::optional<uint8_t> getT1SMultiIDFor(Network net, uint8_t index) const override {
const ETHERNET10T1S_SETTINGS_EXT* t1sExt = getT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return std::nullopt;
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional(t1sExt->multi_id[index]);
}
bool setT1SMultiIDFor(Network net, uint8_t index, uint8_t id) override {
ETHERNET10T1S_SETTINGS_EXT* t1sExt = getMutableT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return false;
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
t1sExt->multi_id[index] = id;
return true;
}
bool setPhyRoleFor(Network net, AELinkMode mode) override { bool setPhyRoleFor(Network net, AELinkMode mode) override {
if (mode != AE_LINK_AUTO && mode != AE_LINK_MASTER && mode != AE_LINK_SLAVE) { if (mode != AE_LINK_AUTO && mode != AE_LINK_MASTER && mode != AE_LINK_SLAVE) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
@@ -668,12 +459,26 @@ public:
return true; return true;
} }
std::vector<TerminationGroup> getTerminationGroups() const override {
return {
{ Network(Network::NetID::DWCAN_01) },
{ Network(Network::NetID::DWCAN_02) }
};
}
protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr)
return nullptr;
return &cfg->termination_enables;
}
private: private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const { const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radcomet3_settings_t>(); auto cfg = getStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1); case Network::NetID::AE_02: return &(cfg->t1s1);
case Network::NetID::AE_03: return &(cfg->t1s2); case Network::NetID::AE_03: return &(cfg->t1s2);
@@ -681,35 +486,14 @@ private:
case Network::NetID::AE_05: return &(cfg->t1s4); case Network::NetID::AE_05: return &(cfg->t1s4);
case Network::NetID::AE_06: return &(cfg->t1s5); case Network::NetID::AE_06: return &(cfg->t1s5);
case Network::NetID::AE_07: return &(cfg->t1s6); case Network::NetID::AE_07: return &(cfg->t1s6);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) { const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const override {
auto cfg = getMutableStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1);
case Network::NetID::AE_03: return &(cfg->t1s2);
case Network::NetID::AE_04: return &(cfg->t1s3);
case Network::NetID::AE_05: return &(cfg->t1s4);
case Network::NetID::AE_06: return &(cfg->t1s5);
case Network::NetID::AE_07: return &(cfg->t1s6);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const {
auto cfg = getStructurePointer<radcomet3_settings_t>(); auto cfg = getStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1Ext); case Network::NetID::AE_02: return &(cfg->t1s1Ext);
case Network::NetID::AE_03: return &(cfg->t1s2Ext); case Network::NetID::AE_03: return &(cfg->t1s2Ext);
@@ -717,27 +501,7 @@ private:
case Network::NetID::AE_05: return &(cfg->t1s4Ext); case Network::NetID::AE_05: return &(cfg->t1s4Ext);
case Network::NetID::AE_06: return &(cfg->t1s5Ext); case Network::NetID::AE_06: return &(cfg->t1s5Ext);
case Network::NetID::AE_07: return &(cfg->t1s6Ext); case Network::NetID::AE_07: return &(cfg->t1s6Ext);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS_EXT* getMutableT1SSettingsExtFor(Network net) {
auto cfg = getMutableStructurePointer<radcomet3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_02: return &(cfg->t1s1Ext);
case Network::NetID::AE_03: return &(cfg->t1s2Ext);
case Network::NetID::AE_04: return &(cfg->t1s3Ext);
case Network::NetID::AE_05: return &(cfg->t1s4Ext);
case Network::NetID::AE_06: return &(cfg->t1s5Ext);
case Network::NetID::AE_07: return &(cfg->t1s6Ext);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
@@ -41,7 +41,8 @@ public:
{ChipID::SFPModule_88q2221_MCHIP, true, "SFP-MV2221M", "sfp_mv2221m_b2_mchip_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_88q2221_MCHIP, true, "SFP-MV2221M", "sfp_mv2221m_b2_mchip_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_lan8670_MCHIP, true, "SFP-MC8670", "sfp_module_lan8670_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_lan8670_MCHIP, true, "SFP-MC8670", "sfp_module_lan8670_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_88q3244_MCHIP, true, "SFP-MV3244", "sfp_module_88q3244_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_88q3244_MCHIP, true, "SFP-MV3244", "sfp_module_88q3244_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_ent11100_MCHIP, true, "SFP-EN11100", "sfp_module_ent11100_ief", 3, FirmwareType::IEF} {ChipID::SFPModule_ent11100_MCHIP, true, "SFP-EN11100", "sfp_module_ent11100_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_ad1101_MCHIP, true, "SFP-AD1101", "sfp_module_adi1101_ief", 3, FirmwareType::IEF},
}; };
return chips; return chips;
} }
@@ -87,9 +88,32 @@ public:
} }
BootloaderPipeline pipeline; BootloaderPipeline pipeline;
auto compVersions = getComponentVersions();
for(size_t i = 0; i < chipVersions.size(); i++) { for(size_t i = 0; i < chipVersions.size(); i++) {
const auto& version = chipVersions[i]; const auto& version = chipVersions[i];
pipeline.add<FlashPhase>(version.id, BootloaderCommunication::RADMultiChip, i == 0); auto it = compVersions.begin();
std::vector<ComponentVersion> sameIdComponents;
while(it != compVersions.end()) {
it = std::find_if(it, compVersions.end(), [&version](const auto& component) {
return (ChipID)component.identifier == version.id;
});
if (it != compVersions.end()) {
sameIdComponents.push_back(*it);
it++;
}
}
if (sameIdComponents.size() > 1) {
std::vector<ComponentVersion> tempCompVersions;
for (const auto& component : compVersions) {
if ((ChipID)component.identifier != version.id || component.expansionSlot != sameIdComponents[0].expansionSlot) {
tempCompVersions.push_back(component);
}
}
compVersions = std::move(tempCompVersions);
}
if (!sameIdComponents.empty()) {
pipeline.add<FlashPhase>(version.id, BootloaderCommunication::RADMultiChip, i == 0, true, true, sameIdComponents[0].expansionSlot);
}
} }
pipeline.add<EnterApplicationPhase>(mainChipID); pipeline.add<EnterApplicationPhase>(mainChipID);
pipeline.add<WaitPhase>(std::chrono::milliseconds(3000)); pipeline.add<WaitPhase>(std::chrono::milliseconds(3000));
@@ -135,7 +135,8 @@ public:
{ChipID::SFPModule_88q2221_MCHIP, true, "SFP-MV2221M", "sfp_mv2221m_b2_mchip_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_88q2221_MCHIP, true, "SFP-MV2221M", "sfp_mv2221m_b2_mchip_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_lan8670_MCHIP, true, "SFP-MC8670", "sfp_module_lan8670_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_lan8670_MCHIP, true, "SFP-MC8670", "sfp_module_lan8670_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_88q3244_MCHIP, true, "SFP-MV3244", "sfp_module_88q3244_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_88q3244_MCHIP, true, "SFP-MV3244", "sfp_module_88q3244_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_ent11100_MCHIP, true, "SFP-EN11100", "sfp_module_ent11100_ief", 3, FirmwareType::IEF} {ChipID::SFPModule_ent11100_MCHIP, true, "SFP-EN11100", "sfp_module_ent11100_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_ad1101_MCHIP, true, "SFP-AD1101", "sfp_module_adi1101_ief", 3, FirmwareType::IEF},
}; };
static std::vector<ChipInfo> t1sChips = { static std::vector<ChipInfo> t1sChips = {
@@ -147,7 +148,8 @@ public:
{ChipID::SFPModule_88q2221_MCHIP, true, "SFP-MV2221M", "sfp_mv2221m_b2_mchip_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_88q2221_MCHIP, true, "SFP-MV2221M", "sfp_mv2221m_b2_mchip_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_lan8670_MCHIP, true, "SFP-MC8670", "sfp_module_lan8670_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_lan8670_MCHIP, true, "SFP-MC8670", "sfp_module_lan8670_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_88q3244_MCHIP, true, "SFP-MV3244", "sfp_module_88q3244_ief", 3, FirmwareType::IEF}, {ChipID::SFPModule_88q3244_MCHIP, true, "SFP-MV3244", "sfp_module_88q3244_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_ent11100_MCHIP, true, "SFP-EN11100", "sfp_module_ent11100_ief", 3, FirmwareType::IEF} {ChipID::SFPModule_ent11100_MCHIP, true, "SFP-EN11100", "sfp_module_ent11100_ief", 3, FirmwareType::IEF},
{ChipID::SFPModule_ad1101_MCHIP, true, "SFP-AD1101", "sfp_module_adi1101_ief", 3, FirmwareType::IEF},
}; };
if(variantToFlash == FirmwareVariant::T1Sx8_CANx4_LINx6) { if(variantToFlash == FirmwareVariant::T1Sx8_CANx4_LINx6) {
@@ -171,9 +173,32 @@ public:
} }
BootloaderPipeline pipeline; BootloaderPipeline pipeline;
auto compVersions = getComponentVersions();
for(size_t i = 0; i < chipVersions.size(); i++) { for(size_t i = 0; i < chipVersions.size(); i++) {
const auto& version = chipVersions[i]; const auto& version = chipVersions[i];
pipeline.add<FlashPhase>(version.id, BootloaderCommunication::RADMultiChip, i == 0); auto it = compVersions.begin();
std::vector<ComponentVersion> sameIdComponents;
while(it != compVersions.end()) {
it = std::find_if(it, compVersions.end(), [&version](const auto& component) {
return (ChipID)component.identifier == version.id;
});
if (it != compVersions.end()) {
sameIdComponents.push_back(*it);
it++;
}
}
if (sameIdComponents.size() > 1) {
std::vector<ComponentVersion> tempCompVersions;
for (const auto& component : compVersions) {
if ((ChipID)component.identifier != version.id || component.expansionSlot != sameIdComponents[0].expansionSlot) {
tempCompVersions.push_back(component);
}
}
compVersions = std::move(tempCompVersions);
}
if (!sameIdComponents.empty()) {
pipeline.add<FlashPhase>(version.id, BootloaderCommunication::RADMultiChip, i == 0, true, true, sameIdComponents[0].expansionSlot);
}
} }
pipeline.add<EnterApplicationPhase>(mainChipID); pipeline.add<EnterApplicationPhase>(mainChipID);
pipeline.add<WaitPhase>(std::chrono::milliseconds(3000)); pipeline.add<WaitPhase>(std::chrono::milliseconds(3000));
@@ -250,222 +250,6 @@ namespace icsneo
} }
} }
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
std::optional<uint8_t> getT1SLocalIDAlternateFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id_alternate);
}
bool setT1SLocalIDAlternateFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id_alternate = id;
return true;
}
std::optional<bool> isT1STerminationEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_TERMINATION) != 0);
}
bool setT1STerminationFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_TERMINATION;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_TERMINATION;
return true;
}
std::optional<bool> isT1SBusDecodingBeaconsEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS) != 0);
}
bool setT1SBusDecodingBeaconsFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
return true;
}
std::optional<bool> isT1SBusDecodingAllEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional<bool>((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL) != 0);
}
bool setT1SBusDecodingAllFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
return true;
}
std::optional<uint8_t> getT1SMultiIDEnableMaskFor(Network net) const override {
const ETHERNET10T1S_SETTINGS_EXT* t1sExt = getT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return std::nullopt;
return std::make_optional(t1sExt->enable_multi_id);
}
bool setT1SMultiIDEnableMaskFor(Network net, uint8_t mask) override {
ETHERNET10T1S_SETTINGS_EXT* t1sExt = getMutableT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return false;
t1sExt->enable_multi_id = mask;
return true;
}
std::optional<uint8_t> getT1SMultiIDFor(Network net, uint8_t index) const override {
const ETHERNET10T1S_SETTINGS_EXT* t1sExt = getT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return std::nullopt;
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional(t1sExt->multi_id[index]);
}
bool setPhyEnableFor(Network net, bool enable) override { bool setPhyEnableFor(Network net, bool enable) override {
auto cfg = getMutableStructurePointer<radgigastar2_settings_t>(); auto cfg = getMutableStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
@@ -524,20 +308,6 @@ namespace icsneo
return GetNetworkEnabled(bitfields, 2, networkID); return GetNetworkEnabled(bitfields, 2, networkID);
} }
bool setT1SMultiIDFor(Network net, uint8_t index, uint8_t id) override {
ETHERNET10T1S_SETTINGS_EXT* t1sExt = getMutableT1SSettingsExtFor(net);
if(t1sExt == nullptr)
return false;
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
t1sExt->multi_id[index] = id;
return true;
}
std::optional<bool> isPerfTestEnabled() const override { std::optional<bool> isPerfTestEnabled() const override {
auto cfg = getStructurePointer<radgigastar2_settings_t>(); auto cfg = getStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
@@ -556,11 +326,10 @@ namespace icsneo
} }
private: private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const { const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radgigastar2_settings_t>(); auto cfg = getStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1); case Network::NetID::AE_03: return &(cfg->t1s1);
case Network::NetID::AE_04: return &(cfg->t1s2); case Network::NetID::AE_04: return &(cfg->t1s2);
@@ -570,37 +339,14 @@ namespace icsneo
case Network::NetID::AE_08: return &(cfg->t1s6); case Network::NetID::AE_08: return &(cfg->t1s6);
case Network::NetID::AE_09: return &(cfg->t1s7); case Network::NetID::AE_09: return &(cfg->t1s7);
case Network::NetID::AE_10: return &(cfg->t1s8); case Network::NetID::AE_10: return &(cfg->t1s8);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) { const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const override {
auto cfg = getMutableStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1);
case Network::NetID::AE_04: return &(cfg->t1s2);
case Network::NetID::AE_05: return &(cfg->t1s3);
case Network::NetID::AE_06: return &(cfg->t1s4);
case Network::NetID::AE_07: return &(cfg->t1s5);
case Network::NetID::AE_08: return &(cfg->t1s6);
case Network::NetID::AE_09: return &(cfg->t1s7);
case Network::NetID::AE_10: return &(cfg->t1s8);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const {
auto cfg = getStructurePointer<radgigastar2_settings_t>(); auto cfg = getStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1Ext); case Network::NetID::AE_03: return &(cfg->t1s1Ext);
case Network::NetID::AE_04: return &(cfg->t1s2Ext); case Network::NetID::AE_04: return &(cfg->t1s2Ext);
@@ -610,29 +356,7 @@ namespace icsneo
case Network::NetID::AE_08: return &(cfg->t1s6Ext); case Network::NetID::AE_08: return &(cfg->t1s6Ext);
case Network::NetID::AE_09: return &(cfg->t1s7Ext); case Network::NetID::AE_09: return &(cfg->t1s7Ext);
case Network::NetID::AE_10: return &(cfg->t1s8Ext); case Network::NetID::AE_10: return &(cfg->t1s8Ext);
default: default: return nullptr;
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
ETHERNET10T1S_SETTINGS_EXT* getMutableT1SSettingsExtFor(Network net) {
auto cfg = getMutableStructurePointer<radgigastar2_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_03: return &(cfg->t1s1Ext);
case Network::NetID::AE_04: return &(cfg->t1s2Ext);
case Network::NetID::AE_05: return &(cfg->t1s3Ext);
case Network::NetID::AE_06: return &(cfg->t1s4Ext);
case Network::NetID::AE_07: return &(cfg->t1s5Ext);
case Network::NetID::AE_08: return &(cfg->t1s6Ext);
case Network::NetID::AE_09: return &(cfg->t1s7Ext);
case Network::NetID::AE_10: return &(cfg->t1s8Ext);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
@@ -48,112 +48,6 @@ class RADMoonT1SSettings : public IDeviceSettings {
public: public:
RADMoonT1SSettings(Device* device) : IDeviceSettings(device, sizeof(radmoont1s_settings_t)) {} RADMoonT1SSettings(Device* device) : IDeviceSettings(device, sizeof(radmoont1s_settings_t)) {}
std::optional<bool> isT1SPLCAEnabledFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional((t1s->flags & ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA) != 0);
}
bool setT1SPLCAFor(Network net, bool enable) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
if(enable)
t1s->flags |= ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
else
t1s->flags &= ~ETHERNET10T1S_SETTINGS_FLAG_ENABLE_PLCA;
return true;
}
std::optional<uint8_t> getT1SLocalIDFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->local_id);
}
bool setT1SLocalIDFor(Network net, uint8_t id) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->local_id = id;
return true;
}
std::optional<uint8_t> getT1SMaxNodesFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_num_nodes);
}
bool setT1SMaxNodesFor(Network net, uint8_t nodes) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_num_nodes = nodes;
return true;
}
std::optional<uint8_t> getT1STxOppTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->to_timer);
}
bool setT1STxOppTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->to_timer = timer;
return true;
}
std::optional<uint8_t> getT1SMaxBurstFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->max_burst_count);
}
bool setT1SMaxBurstFor(Network net, uint8_t burst) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->max_burst_count = burst;
return true;
}
std::optional<uint8_t> getT1SBurstTimerFor(Network net) const override {
const ETHERNET10T1S_SETTINGS* t1s = getT1SSettingsFor(net);
if(t1s == nullptr)
return std::nullopt;
return std::make_optional(t1s->burst_timer);
}
bool setT1SBurstTimerFor(Network net, uint8_t timer) override {
ETHERNET10T1S_SETTINGS* t1s = getMutableT1SSettingsFor(net);
if(t1s == nullptr)
return false;
t1s->burst_timer = timer;
return true;
}
std::optional<bool> isPerfTestEnabled() const override { std::optional<bool> isPerfTestEnabled() const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>(); auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
@@ -172,215 +66,26 @@ public:
} }
private: private:
const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const { const ETHERNET10T1S_SETTINGS* getT1SSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>(); auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_01: case Network::NetID::AE_01: return &(cfg->t1s);
return &(cfg->t1s); default: return nullptr;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
ETHERNET10T1S_SETTINGS* getMutableT1SSettingsFor(Network net) { const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const override {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01:
return &(cfg->t1s);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
const ETHERNET10T1S_SETTINGS_EXT* getT1SSettingsExtFor(Network net) const {
auto cfg = getStructurePointer<radmoont1s_settings_t>(); auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr) if(cfg == nullptr)
return nullptr; return nullptr;
switch(net.getNetID()) { switch(net.getNetID()) {
case Network::NetID::AE_01: case Network::NetID::AE_01: return &(cfg->t1sExt);
return &(cfg->t1sExt); default: return nullptr;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
} }
} }
ETHERNET10T1S_SETTINGS_EXT* getMutableT1SSettingsExtFor(Network net) {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::AE_01:
return &(cfg->t1sExt);
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return nullptr;
}
}
std::optional<uint8_t> getT1SLocalIDAlternateFor(Network net) const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return std::nullopt;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional(cfg->t1s.local_id_alternate);
}
bool setT1SLocalIDAlternateFor(Network net, uint8_t id) override {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return false;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
cfg->t1s.local_id_alternate = id;
return true;
}
std::optional<bool> isT1SBusDecodingBeaconsEnabledFor(Network net) const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return std::nullopt;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional<bool>((cfg->t1s.flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS) != 0);
}
bool setT1SBusDecodingBeaconsFor(Network net, bool enable) override {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return false;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
if(enable)
cfg->t1s.flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
else
cfg->t1s.flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_BEACONS;
return true;
}
std::optional<bool> isT1SBusDecodingAllEnabledFor(Network net) const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return std::nullopt;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional<bool>((cfg->t1s.flags & ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL) != 0);
}
bool setT1SBusDecodingAllFor(Network net, bool enable) override {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return false;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
if(enable)
cfg->t1s.flags |= ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
else
cfg->t1s.flags &= ~ETHERNET10T1S_SETTINGS_FLAG_BUS_DECODING_ALL;
return true;
}
std::optional<uint8_t> getT1SMultiIDEnableMaskFor(Network net) const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return std::nullopt;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional(cfg->t1sExt.enable_multi_id);
}
bool setT1SMultiIDEnableMaskFor(Network net, uint8_t mask) override {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return false;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
cfg->t1sExt.enable_multi_id = mask;
return true;
}
std::optional<uint8_t> getT1SMultiIDFor(Network net, uint8_t index) const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return std::nullopt;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return std::nullopt;
}
return std::make_optional(cfg->t1sExt.multi_id[index]);
}
bool setT1SMultiIDFor(Network net, uint8_t index, uint8_t id) override {
auto cfg = getMutableStructurePointer<radmoont1s_settings_t>();
if(cfg == nullptr)
return false;
if(net.getNetID() != Network::NetID::AE_01) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
if(index >= 7) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
cfg->t1sExt.multi_id[index] = id;
return true;
}
const RAD_GPTP_SETTINGS* getGPTPSettings() const override { const RAD_GPTP_SETTINGS* getGPTPSettings() const override {
auto cfg = getStructurePointer<radmoont1s_settings_t>(); auto cfg = getStructurePointer<radmoont1s_settings_t>();
return cfg ? &cfg->gPTP : nullptr; return cfg ? &cfg->gPTP : nullptr;
@@ -5,7 +5,6 @@
#include "icsneo/device/device.h" #include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h" #include "icsneo/device/devicetype.h"
#include "icsneo/platform/cdcacm.h"
#include "icsneo/device/tree/vividcan/vividcansettings.h" #include "icsneo/device/tree/vividcan/vividcansettings.h"
namespace icsneo { namespace icsneo {
+9 -9
View File
@@ -280,7 +280,7 @@ typedef unsigned __int64 uint64_t;
#define NEODEVICE_GIGASTAR2 (0x00000029) #define NEODEVICE_GIGASTAR2 (0x00000029)
#define NEODEVICE_FIRE3_T1S_LIN (0x0000002A) #define NEODEVICE_FIRE3_T1S_LIN (0x0000002A)
#define NEODEVICE_FIRE3_T1S_SENT (0x0000002B) #define NEODEVICE_FIRE3_T1S_SENT (0x0000002B)
#define NEODEVICE_RADGEMINI (0x0000002C) #define NEODEVICE_RADARGOS (0x0000002C)
#define NEODEVICE_RED_DEPRECATED (0x00000040) #define NEODEVICE_RED_DEPRECATED (0x00000040)
#define NEODEVICE_ECU_DEPRECATED (0x00000080) #define NEODEVICE_ECU_DEPRECATED (0x00000080)
@@ -975,7 +975,7 @@ typedef union _stChipVersions
{ {
uint8_t mchip_major; uint8_t mchip_major;
uint8_t mchip_minor; uint8_t mchip_minor;
} radgemini_versions; } radargos_versions;
struct struct
{ {
@@ -2794,7 +2794,7 @@ typedef struct _SRADMoon3Settings
#define SRADMoon3Settings_SIZE 68 #define SRADMoon3Settings_SIZE 68
typedef struct _SRADGeminiSettings typedef struct _SRADArgosSettings
{ {
uint16_t perf_en; // 2 uint16_t perf_en; // 2
@@ -2815,9 +2815,9 @@ typedef struct _SRADGeminiSettings
uint16_t enableLatencyTest : 1; uint16_t enableLatencyTest : 1;
uint16_t reserved : 15; uint16_t reserved : 15;
} flags; // 2 } flags; // 2
} SRADGeminiSettings; } SRADArgosSettings;
#define SRADGeminiSettings_SIZE 86 #define SRADArgosSettings_SIZE 86
typedef struct _SRADGigastarSettings typedef struct _SRADGigastarSettings
{ {
@@ -4450,7 +4450,7 @@ typedef struct _GLOBAL_SETTINGS
SRADEpsilonSettings epsilon; SRADEpsilonSettings epsilon;
SRADBMSSettings rad_bms; SRADBMSSettings rad_bms;
SRADMoon3Settings radmoon3; SRADMoon3Settings radmoon3;
SRADGeminiSettings radgemini; SRADArgosSettings radargos;
SRADCometSettings radcomet; SRADCometSettings radcomet;
SRADComet3Settings radcomet3; SRADComet3Settings radcomet3;
SRADGigastar2Settings radgigastar2; SRADGigastar2Settings radgigastar2;
@@ -4509,7 +4509,7 @@ typedef enum _EDeviceSettingsType
DeviceRADGalaxy2SettingsType, DeviceRADGalaxy2SettingsType,
DeviceRADGigastar2SettingsType, DeviceRADGigastar2SettingsType,
DeviceRADMoonT1SSettingsType, DeviceRADMoonT1SSettingsType,
DeviceRADGeminiSettingsType, DeviceRADArgosSettingsType,
DeviceFire3T1sLinSettingsType, DeviceFire3T1sLinSettingsType,
// add new settings type here // add new settings type here
// Also add to map inside cicsneoVI::Init() // Also add to map inside cicsneoVI::Init()
@@ -4545,7 +4545,7 @@ typedef struct _SDeviceSettings
SRADEpsilonSettings epsilon; SRADEpsilonSettings epsilon;
SRADBMSSettings rad_bms; SRADBMSSettings rad_bms;
SRADMoon3Settings radmoon3; SRADMoon3Settings radmoon3;
SRADGeminiSettings radgemini; SRADArgosSettings radargos;
SFire3FlexraySettings fire3Flexray; SFire3FlexraySettings fire3Flexray;
SFire3T1sLinSettings fire3t1slin; SFire3T1sLinSettings fire3t1slin;
SRADCometSettings radcomet; SRADCometSettings radcomet;
@@ -5430,7 +5430,7 @@ CHECK_STRUCT_SIZE(SRADEpsilonSettings);
CHECK_STRUCT_SIZE(RAD_GPTP_SETTINGS); CHECK_STRUCT_SIZE(RAD_GPTP_SETTINGS);
CHECK_STRUCT_SIZE(SRADBMSSettings); CHECK_STRUCT_SIZE(SRADBMSSettings);
CHECK_STRUCT_SIZE(SRADMoon3Settings); CHECK_STRUCT_SIZE(SRADMoon3Settings);
CHECK_STRUCT_SIZE(SRADGeminiSettings); CHECK_STRUCT_SIZE(SRADArgosSettings);
CHECK_STRUCT_SIZE(SFire3FlexraySettings); CHECK_STRUCT_SIZE(SFire3FlexraySettings);
CHECK_STRUCT_SIZE(SFire3T1sLinSettings); CHECK_STRUCT_SIZE(SFire3T1sLinSettings);
CHECK_STRUCT_SIZE(CANHubSettings); CHECK_STRUCT_SIZE(CANHubSettings);

Some files were not shown because too many files have changed in this diff Show More