mirror of
https://github.com/linux-can/can-utils.git
synced 2026-11-04 15:31:10 +01:00
Compare commits
100
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f55228325 | ||
|
|
aa54819b65 | ||
|
|
8222f517b7 | ||
|
|
775611dba2 | ||
|
|
cd0ff30788 | ||
|
|
3a980d83e0 | ||
|
|
1a90b9a75d | ||
|
|
13dc8e9cc5 | ||
|
|
d5571a5bc3 | ||
|
|
33f7ddb5ed | ||
|
|
c7a02c0258 | ||
|
|
a63fd488b2 | ||
|
|
64280dc9c3 | ||
|
|
1663fc32b3 | ||
|
|
38cf8dd1a5 | ||
|
|
411a68675f | ||
|
|
6bdb574707 | ||
|
|
fa647cc3e2 | ||
|
|
cbbad5eede | ||
|
|
95aae6bf83 | ||
|
|
bf5fe736c3 | ||
|
|
14245b7c79 | ||
|
|
90383a0cca | ||
|
|
d5cb91c8ea | ||
|
|
9743a2ffcf | ||
|
|
d5ed23583c | ||
|
|
60301896d1 | ||
|
|
99ab67143a | ||
|
|
a0aeaed234 | ||
|
|
3cae8a449b | ||
|
|
44e6eb45e3 | ||
|
|
9e444073b1 | ||
|
|
9d4f3c82a2 | ||
|
|
7e8e247b2f | ||
|
|
3fe1c42bbf | ||
|
|
1520ab5b98 | ||
|
|
aa902ae2af | ||
|
|
a0b592178e | ||
|
|
4f2fdecfba | ||
|
|
cbbe4b41ea | ||
|
|
2da0c7e95e | ||
|
|
1f038b3cd8 | ||
|
|
392ccc970b | ||
|
|
7939678070 | ||
|
|
88a0417a6b | ||
|
|
65e715d56d | ||
|
|
374fecde09 | ||
|
|
bfbf0c851f | ||
|
|
ed7822c9a2 | ||
|
|
e9ea9d1168 | ||
|
|
2d793d38d2 | ||
|
|
340a3b8c92 | ||
|
|
8838ce80fc | ||
|
|
d6c57240ed | ||
|
|
3e0e6ae3a4 | ||
|
|
8bf5f8873f | ||
|
|
0094905376 | ||
|
|
987bc8aac2 | ||
|
|
6b46063eee | ||
|
|
93a7b2dfd3 | ||
|
|
af09afb734 | ||
|
|
69efecc428 | ||
|
|
3698814740 | ||
|
|
6ed0f0ea7f | ||
|
|
287469245c | ||
|
|
7a318636e7 | ||
|
|
7fb81e6779 | ||
|
|
a904183b4e | ||
|
|
e2172546aa | ||
|
|
705b3202a9 | ||
|
|
31a59be02f | ||
|
|
4d908bd7cf | ||
|
|
837e2bb343 | ||
|
|
ec16ef97ff | ||
|
|
651c8818dd | ||
|
|
130e0dced2 | ||
|
|
ff90f4ec21 | ||
|
|
e3ee283443 | ||
|
|
e8130a3575 | ||
|
|
1250c12a30 | ||
|
|
9b5b030877 | ||
|
|
da3914c491 | ||
|
|
18b1e7970a | ||
|
|
491cabea07 | ||
|
|
f1cea504a4 | ||
|
|
900c7e9f6e | ||
|
|
b208876e65 | ||
|
|
72a9fd8ccd | ||
|
|
e448d542e8 | ||
|
|
71b2aec834 | ||
|
|
a24bff8b08 | ||
|
|
fe9ea67814 | ||
|
|
e8559479fb | ||
|
|
6050aa155d | ||
|
|
302184f383 | ||
|
|
c542c9ada7 | ||
|
|
2b8c7c5f4b | ||
|
|
4577316bd6 | ||
|
|
4364d655b8 | ||
|
|
2e71e396c5 |
@@ -30,68 +30,127 @@ jobs:
|
||||
podman run --name stable -di --userns=keep-id:uid=1000,gid=1000 -v "$PWD":/home -w /home ${{ matrix.release }} bash
|
||||
podman exec -i stable uname -a
|
||||
podman exec -i stable id
|
||||
|
||||
- name: Update APT Sources List (Ubuntu Only)
|
||||
if:
|
||||
startsWith(matrix.release, 'ubuntu:') && matrix.release != 'ubuntu:20.04'
|
||||
run: |
|
||||
podman exec -i -u root stable apt update
|
||||
podman exec -e DEBIAN_FRONTEND='noninteractive' -i -u root stable apt install -o APT::Install-Suggests=false -qy \
|
||||
lsb-release
|
||||
|
||||
podman exec -i -u root stable \
|
||||
test -e /etc/apt/sources.list &&
|
||||
podman exec -i -u root stable \
|
||||
sed -i -e 's|\(http.*:\)|[arch=amd64] \1|g' /etc/apt/sources.list
|
||||
|
||||
podman exec -i -u root stable \
|
||||
test -e /etc/apt/sources.list.d/ubuntu.sources &&
|
||||
podman exec -i -u root stable \
|
||||
sed -i -e '/^Components:/a Architectures: amd64' /etc/apt/sources.list.d/ubuntu.sources
|
||||
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports/ $(podman exec -i stable lsb_release -cs) main restricted universe multiverse" | \
|
||||
podman exec -i -u root stable tee -a /etc/apt/sources.list.d/cross.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports/ $(podman exec -i stable lsb_release -cs)-updates main restricted universe multiverse" | \
|
||||
podman exec -i -u root stable tee -a /etc/apt/sources.list.d/cross.list
|
||||
echo "deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports/ $(podman exec -i stable lsb_release -cs)-backports main restricted universe multiverse" | \
|
||||
podman exec -i -u root stable tee -a /etc/apt/sources.list.d/cross.list
|
||||
|
||||
- name: Add Architecture
|
||||
if:
|
||||
matrix.release != 'ubuntu:20.04'
|
||||
run: |
|
||||
podman exec -i -u root stable dpkg --add-architecture arm64
|
||||
podman exec -i -u root stable dpkg --add-architecture armhf
|
||||
|
||||
- name: Install Development Packages
|
||||
env:
|
||||
release: ${{ matrix.release == 'debian:experimental' && '-t experimental' || '' }}
|
||||
run: |
|
||||
podman exec -i -u root stable apt update
|
||||
podman exec -e DEBIAN_FRONTEND='noninteractive' -i -u root stable apt upgrade -o APT::Install-Suggests=false -qy
|
||||
podman exec -e DEBIAN_FRONTEND='noninteractive' -i -u root stable apt install -o APT::Install-Suggests=false -qy ${release} \
|
||||
clang \
|
||||
cmake \
|
||||
gcc \
|
||||
gcc-aarch64-linux-gnu \
|
||||
gcc-arm-linux-gnueabihf \
|
||||
gcc-mips-linux-gnu \
|
||||
libgps-dev \
|
||||
make
|
||||
|
||||
- name: Install Cross Libs
|
||||
env:
|
||||
release: ${{ matrix.release == 'debian:experimental' && '-t experimental' || '' }}
|
||||
if:
|
||||
matrix.release != 'ubuntu:20.04'
|
||||
run: |
|
||||
podman exec -e DEBIAN_FRONTEND='noninteractive' -i -u root stable apt install -o APT::Install-Suggests=false -qy ${release} \
|
||||
libgps-dev:arm64 \
|
||||
libgps-dev:armhf
|
||||
|
||||
- name: Configure & Build with gcc
|
||||
env:
|
||||
cc: gcc
|
||||
run: |
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${cc} -DENABLE_WERROR=ON -B build-${cc}
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${cc} -DENABLE_WERROR=ON -DENABLE_GPS=ON -B build-${cc}
|
||||
podman exec -i stable cmake --build build-${cc}
|
||||
|
||||
- name: Configure & Build with clang
|
||||
env:
|
||||
cc: clang
|
||||
run: |
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${cc} -DENABLE_WERROR=ON -B build-${cc}
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${cc} -DENABLE_WERROR=ON -DENABLE_GPS=ON -B build-${cc}
|
||||
podman exec -i stable cmake --build build-${cc}
|
||||
|
||||
- name: Configure & Build with arm-linux-gnueabihf-gcc
|
||||
env:
|
||||
toolchain: arm-linux-gnueabihf-gcc
|
||||
gps: ${{ matrix.release == 'ubuntu:20.04' && 'OFF' || 'ON' }}
|
||||
run: |
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -B build-${toolchain}
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -DENABLE_GPS=${gps} -B build-${toolchain}
|
||||
podman exec -i stable cmake --build build-${toolchain}
|
||||
|
||||
- name: Configure & Build with arm-linux-gnueabihf-clang
|
||||
if:
|
||||
${{ matrix.release != 'ubuntu:20.04' && matrix.release != 'debian:oldstable-slim' }}
|
||||
matrix.release != 'ubuntu:20.04' && matrix.release != 'debian:oldstable-slim'
|
||||
env:
|
||||
toolchain: arm-linux-gnueabihf-clang
|
||||
gps: ${{ matrix.release == 'ubuntu:20.04' && 'OFF' || 'ON' }}
|
||||
run: |
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -B build-${toolchain}
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -DENABLE_GPS=${gps} -B build-${toolchain}
|
||||
podman exec -i stable cmake --build build-${toolchain}
|
||||
|
||||
- name: Configure & Build with aarch64-linux-gnu-gcc
|
||||
env:
|
||||
toolchain: aarch64-linux-gnu-gcc
|
||||
gps: ${{ matrix.release == 'ubuntu:20.04' && 'OFF' || 'ON' }}
|
||||
run: |
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -B build-${toolchain}
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -DENABLE_GPS=${gps} -B build-${toolchain}
|
||||
podman exec -i stable cmake --build build-${toolchain}
|
||||
|
||||
- name: Configure & Build with aarch64-linux-gnu-clang
|
||||
if:
|
||||
${{ matrix.release != 'ubuntu:20.04' && matrix.release != 'debian:oldstable-slim' }}
|
||||
matrix.release != 'ubuntu:20.04' && matrix.release != 'debian:oldstable-slim'
|
||||
env:
|
||||
toolchain: aarch64-linux-gnu-clang
|
||||
gps: ${{ matrix.release == 'ubuntu:20.04' && 'OFF' || 'ON' }}
|
||||
run: |
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -B build-${toolchain}
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -DENABLE_GPS=${gps} -B build-${toolchain}
|
||||
podman exec -i stable cmake --build build-${toolchain}
|
||||
|
||||
- name: Configure & Build with mips-linux-gnu-gcc
|
||||
- name: Configure & Build with gcc (Makefile)
|
||||
env:
|
||||
toolchain: mips-linux-gnu-gcc
|
||||
cc: gcc
|
||||
run: |
|
||||
podman exec -i stable cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=cmake/${toolchain}.cmake -DENABLE_WERROR=ON -B build-${toolchain}
|
||||
podman exec -i stable cmake --build build-${toolchain}
|
||||
podman exec -i stable make CC=${cc}
|
||||
podman exec -i stable make clean
|
||||
|
||||
- name: Configure & Build with clang (Makefile)
|
||||
env:
|
||||
cc: clang
|
||||
run: |
|
||||
podman exec -i stable make CC=${cc}
|
||||
podman exec -i stable make clean
|
||||
|
||||
- name: Show logs
|
||||
if: ${{ failure() }}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
*~
|
||||
*.a
|
||||
*.so
|
||||
*.so.*
|
||||
*.o
|
||||
.ccls-cache
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
compile_commands.json
|
||||
tags
|
||||
/build
|
||||
@@ -10,6 +16,7 @@ tags
|
||||
/can-calc-bit-timing
|
||||
/canbusload
|
||||
/candump
|
||||
/canerrsim
|
||||
/canfdtest
|
||||
/cangen
|
||||
/cangw
|
||||
@@ -33,6 +40,7 @@ tags
|
||||
/j1939sr
|
||||
/j1939-timedate-cli
|
||||
/j1939-timedate-srv
|
||||
/j1939-vehicle-position-srv
|
||||
/log2asc
|
||||
/log2long
|
||||
/mcp251xfd-dump
|
||||
|
||||
+142
-97
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.5...3.10)
|
||||
|
||||
project(can-utils LANGUAGES C)
|
||||
|
||||
@@ -12,8 +12,38 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
if(CMAKE_EXPORT_COMPILE_COMMANDS STREQUAL "")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "project default" FORCE)
|
||||
endif()
|
||||
|
||||
# Add an option to enable treating warnings as errors
|
||||
option(ENABLE_WERROR "Treat all compiler warnings as errors" OFF)
|
||||
option(ENABLE_GPS "Enable GPS support" OFF)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
|
||||
# Options for controlling targets. These groups correspond to the README.
|
||||
option(ENABLE_BASIC_TOOLS "Build basic tools" ON)
|
||||
option(ENABLE_IP_SERVER "Build tools providing CAN access via IP sockets" ON)
|
||||
option(ENABLE_GATEWAY "Build in-kernel gateway configuration tools" ON)
|
||||
option(ENABLE_MEASUREMENT "Build measurement tools" ON)
|
||||
option(ENABLE_ISOTP "Build isotp tools" ON)
|
||||
option(ENABLE_LOG_FILE "Build log file converter tools" ON)
|
||||
option(ENABLE_SLCAN "Build slcan tools" ON)
|
||||
option(ENABLE_MCP251XFD "Build MCP251XFD tools" ON)
|
||||
|
||||
# cmake_dependent_option is only available in CMake 3.22 and later.
|
||||
if(ANDROID)
|
||||
set(ENABLE_J1939 OFF)
|
||||
set(ENABLE_ISOBUSFS OFF)
|
||||
else()
|
||||
option(ENABLE_J1939 "Build J1939 tools" ON)
|
||||
option(ENABLE_ISOBUSFS "Build ISOBUS tools" ON)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
if(ENABLE_GPS)
|
||||
pkg_check_modules(GPS REQUIRED libgps)
|
||||
endif()
|
||||
|
||||
if(ENABLE_WERROR)
|
||||
add_compile_options(-Werror)
|
||||
@@ -37,24 +67,64 @@ include_directories (./include)
|
||||
|
||||
check_function_exists(fork HAVE_FORK)
|
||||
|
||||
set(PROGRAMS_CANLIB
|
||||
asc2log
|
||||
canbusload
|
||||
# List of all programs to be built.
|
||||
# Each program is expected to have a corresponding source file with the same name.
|
||||
set(PROGRAMS)
|
||||
# List of programs to link against can.
|
||||
set(PROGRAMS_CANLIB)
|
||||
# List of programs to link against j1939 and can.
|
||||
set(PROGRAMS_J1939)
|
||||
|
||||
if(ENABLE_BASIC_TOOLS)
|
||||
list(APPEND PROGRAMS cansniffer)
|
||||
list(APPEND PROGRAMS_CANLIB
|
||||
candump
|
||||
cangen
|
||||
canplayer
|
||||
cansend
|
||||
cangen
|
||||
cansequence
|
||||
log2asc
|
||||
log2long
|
||||
slcanpty
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HAVE_FORK)
|
||||
if(ENABLE_IP_SERVER AND HAVE_FORK)
|
||||
list(APPEND PROGRAMS bcmserver)
|
||||
list(APPEND PROGRAMS_CANLIB canlogserver)
|
||||
endif()
|
||||
|
||||
set(PROGRAMS_J1939
|
||||
if(ENABLE_GATEWAY)
|
||||
list(APPEND PROGRAMS cangw)
|
||||
endif()
|
||||
|
||||
if(ENABLE_MEASUREMENT)
|
||||
list(APPEND PROGRAMS
|
||||
canfdtest
|
||||
canerrsim
|
||||
)
|
||||
list(APPEND PROGRAMS_CANLIB canbusload)
|
||||
|
||||
add_executable(can-calc-bit-timing
|
||||
calc-bit-timing/can-calc-bit-timing.c
|
||||
)
|
||||
install(TARGETS can-calc-bit-timing DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
if(ENABLE_ISOTP)
|
||||
list(APPEND PROGRAMS
|
||||
isotpdump
|
||||
isotpperf
|
||||
isotprecv
|
||||
isotpsend
|
||||
isotpsniffer
|
||||
isotptun
|
||||
)
|
||||
|
||||
if(HAVE_FORK)
|
||||
list(APPEND PROGRAMS isotpserver)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_J1939)
|
||||
list(APPEND PROGRAMS_J1939
|
||||
j1939acd
|
||||
j1939cat
|
||||
j1939spy
|
||||
@@ -62,72 +132,46 @@ set(PROGRAMS_J1939
|
||||
testj1939
|
||||
)
|
||||
|
||||
set(PROGRAMS_J1939_TIMEDATE
|
||||
j1939-timedate-srv
|
||||
j1939-timedate-cli
|
||||
add_executable(j1939-timedate-cli
|
||||
j1939_timedate/j1939_timedate_cli.c
|
||||
)
|
||||
|
||||
set(PROGRAMS_ISOBUSFS
|
||||
isobusfs-srv
|
||||
isobusfs-cli
|
||||
target_link_libraries(j1939-timedate-cli
|
||||
PRIVATE can j1939
|
||||
)
|
||||
install(TARGETS j1939-timedate-cli DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
set(PROGRAMS
|
||||
${PROGRAMS_CANLIB}
|
||||
canfdtest
|
||||
cangw
|
||||
cansniffer
|
||||
isotpdump
|
||||
isotpperf
|
||||
isotprecv
|
||||
isotpsend
|
||||
isotpsniffer
|
||||
isotptun
|
||||
slcan_attach
|
||||
slcand
|
||||
add_executable(j1939-timedate-srv
|
||||
j1939_timedate/j1939_timedate_srv.c
|
||||
)
|
||||
|
||||
if(HAVE_FORK)
|
||||
list(APPEND PROGRAMS bcmserver)
|
||||
list(APPEND PROGRAMS isotpserver)
|
||||
target_link_libraries(j1939-timedate-srv
|
||||
PRIVATE can j1939
|
||||
)
|
||||
install(TARGETS j1939-timedate-srv DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
add_executable(can-calc-bit-timing
|
||||
calc-bit-timing/can-calc-bit-timing.c
|
||||
if(ENABLE_J1939 AND ENABLE_GPS)
|
||||
add_executable(j1939-vehicle-position-srv
|
||||
j1939_vehicle_position/j1939_vehicle_position_srv.c
|
||||
)
|
||||
|
||||
add_executable(mcp251xfd-dump
|
||||
mcp251xfd/mcp251xfd-dev-coredump.c
|
||||
mcp251xfd/mcp251xfd-dump.c
|
||||
mcp251xfd/mcp251xfd-main.c
|
||||
mcp251xfd/mcp251xfd-regmap.c
|
||||
target_link_libraries(j1939-vehicle-position-srv
|
||||
PRIVATE can j1939 ${GPS_LIBRARIES}
|
||||
)
|
||||
install(TARGETS j1939-vehicle-position-srv DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID)
|
||||
list(APPEND PROGRAMS ${PROGRAMS_J1939})
|
||||
|
||||
add_library(j1939 STATIC
|
||||
libj1939.c
|
||||
)
|
||||
|
||||
target_link_libraries(j1939
|
||||
PRIVATE can
|
||||
)
|
||||
|
||||
add_library(isobusfs SHARED
|
||||
if(ENABLE_ISOBUSFS)
|
||||
add_library(isobusfs EXCLUDE_FROM_ALL
|
||||
isobusfs/isobusfs_cmn.c
|
||||
isobusfs/isobusfs_cmn_dh.c
|
||||
)
|
||||
|
||||
set(PUBLIC_HEADER_ISOBUSFS
|
||||
isobusfs/isobusfs_cmn.h
|
||||
isobusfs/isobusfs_cmn_cm.h
|
||||
)
|
||||
|
||||
set_target_properties(isobusfs PROPERTIES
|
||||
PUBLIC_HEADER "${PUBLIC_HEADER_ISOBUSFS}"
|
||||
SOVERSION 0
|
||||
)
|
||||
|
||||
install(TARGETS isobusfs
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
@@ -141,10 +185,10 @@ if(NOT ANDROID)
|
||||
isobusfs/isobusfs_cli_selftests.c
|
||||
isobusfs/isobusfs_cli_int.c
|
||||
)
|
||||
|
||||
target_link_libraries(isobusfs-cli
|
||||
PRIVATE isobusfs can j1939
|
||||
)
|
||||
install(TARGETS isobusfs-cli DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
add_executable(isobusfs-srv
|
||||
isobusfs/isobusfs_srv.c
|
||||
@@ -155,48 +199,55 @@ if(NOT ANDROID)
|
||||
isobusfs/isobusfs_srv_fh.c
|
||||
isobusfs/isobusfs_srv_vh.c
|
||||
)
|
||||
|
||||
target_link_libraries(isobusfs-srv
|
||||
PRIVATE isobusfs can j1939
|
||||
)
|
||||
|
||||
install(TARGETS
|
||||
isobusfs-cli
|
||||
isobusfs-srv
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
set(PUBLIC_HEADER_j1939_TIMEDATE
|
||||
j1939_timedate/j1939_timedate_cmn.h
|
||||
)
|
||||
|
||||
add_executable(j1939-timedate-cli
|
||||
j1939_timedate/j1939_timedate_cli.c
|
||||
)
|
||||
|
||||
target_link_libraries(j1939-timedate-cli
|
||||
PRIVATE can j1939
|
||||
)
|
||||
|
||||
add_executable(j1939-timedate-srv
|
||||
j1939_timedate/j1939_timedate_srv.c
|
||||
)
|
||||
|
||||
target_link_libraries(j1939-timedate-srv
|
||||
PRIVATE can j1939
|
||||
)
|
||||
|
||||
install(TARGETS
|
||||
j1939-timedate-cli
|
||||
j1939-timedate-srv
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
install(TARGETS isobusfs-srv DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
add_library(can STATIC
|
||||
if(ENABLE_LOG_FILE)
|
||||
list(APPEND PROGRAMS_CANLIB
|
||||
asc2log
|
||||
log2asc
|
||||
log2long
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SLCAN)
|
||||
list(APPEND PROGRAMS
|
||||
slcan_attach
|
||||
slcand
|
||||
)
|
||||
list(APPEND PROGRAMS_CANLIB slcanpty)
|
||||
endif()
|
||||
|
||||
if(ENABLE_MCP251XFD)
|
||||
add_executable(mcp251xfd-dump
|
||||
mcp251xfd/mcp251xfd-dev-coredump.c
|
||||
mcp251xfd/mcp251xfd-dump.c
|
||||
mcp251xfd/mcp251xfd-main.c
|
||||
mcp251xfd/mcp251xfd-regmap.c
|
||||
)
|
||||
install(TARGETS mcp251xfd-dump DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif()
|
||||
|
||||
list(APPEND PROGRAMS
|
||||
${PROGRAMS_CANLIB}
|
||||
${PROGRAMS_J1939}
|
||||
)
|
||||
|
||||
add_library(can STATIC EXCLUDE_FROM_ALL
|
||||
lib.c
|
||||
canframelen.c
|
||||
)
|
||||
|
||||
add_library(j1939 STATIC EXCLUDE_FROM_ALL
|
||||
libj1939.c
|
||||
)
|
||||
target_link_libraries(j1939
|
||||
PRIVATE can
|
||||
)
|
||||
|
||||
foreach(name ${PROGRAMS})
|
||||
add_executable(${name} ${name}.c)
|
||||
|
||||
@@ -213,12 +264,6 @@ foreach(name ${PROGRAMS})
|
||||
install(TARGETS ${name} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endforeach()
|
||||
|
||||
install(TARGETS
|
||||
can-calc-bit-timing
|
||||
mcp251xfd-dump
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
add_custom_target(uninstall
|
||||
"${CMAKE_COMMAND}" -P "${CMAKE_SOURCE_DIR}/cmake/make_uninstall.cmake"
|
||||
COMMENT "Add uninstall target"
|
||||
|
||||
@@ -46,6 +46,7 @@ MAKEFLAGS := -k
|
||||
CFLAGS := -O2 -Wall -Wno-parentheses -Wsign-compare
|
||||
|
||||
HAVE_FORK := $(shell ./check_cc.sh "$(CC)" fork_test.c)
|
||||
HAVE_LIBGPS := $(shell test -x "`which pkg-config`" && pkg-config --exists libgps && echo 1 || echo 0)
|
||||
|
||||
CPPFLAGS += \
|
||||
-I. \
|
||||
@@ -67,6 +68,11 @@ PROGRAMS_J1939_TIMEDATE := \
|
||||
j1939-timedate-srv \
|
||||
j1939-timedate-cli
|
||||
|
||||
ifeq ($(HAVE_LIBGPS),1)
|
||||
PROGRAMS_J1939_VEHICLE_POSITION := \
|
||||
j1939-vehicle-position-srv
|
||||
endif
|
||||
|
||||
PROGRAMS_ISOBUSFS := \
|
||||
isobusfs-srv \
|
||||
isobusfs-cli
|
||||
@@ -98,6 +104,7 @@ PROGRAMS_SLCAN := \
|
||||
PROGRAMS := \
|
||||
$(PROGRAMS_CANGW) \
|
||||
$(PROGRAMS_J1939_TIMEDATE) \
|
||||
$(PROGRAMS_J1939_VEHICLE_POSITION) \
|
||||
$(PROGRAMS_ISOBUSFS) \
|
||||
$(PROGRAMS_ISOTP) \
|
||||
$(PROGRAMS_J1939) \
|
||||
@@ -106,6 +113,7 @@ PROGRAMS := \
|
||||
can-calc-bit-timing \
|
||||
canbusload \
|
||||
candump \
|
||||
canerrsim \
|
||||
canfdtest \
|
||||
cangen \
|
||||
cansequence \
|
||||
@@ -126,7 +134,8 @@ endif
|
||||
all: $(PROGRAMS)
|
||||
|
||||
clean:
|
||||
rm -f $(PROGRAMS) *.o mcp251xfd/*.o isobusfs/*.o j1939_timedate/*.o
|
||||
rm -f $(PROGRAMS) *.o mcp251xfd/*.o isobusfs/*.o j1939_timedate/*.o \
|
||||
j1939_vehicle_position/*.o
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||
@@ -153,6 +162,8 @@ isobusfs_srv.o: lib.h libj1939.h
|
||||
isobusfs_c.o: lib.h libj1939.h
|
||||
j1939_timedate_srv.o: lib.h libj1939.h
|
||||
j1939_timedate_cli.o: lib.h libj1939.h
|
||||
j1939_vehicle_position_srv.o: lib.h libj1939.h
|
||||
|
||||
canframelen.o: canframelen.h
|
||||
|
||||
asc2log: asc2log.o lib.o
|
||||
@@ -182,6 +193,12 @@ j1939-timedate-cli: lib.o \
|
||||
j1939_timedate/j1939_timedate_cli.o
|
||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
j1939-vehicle-position-srv: \
|
||||
lib.o \
|
||||
libj1939.o \
|
||||
j1939_vehicle_position/j1939_vehicle_position_srv.o
|
||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) $(shell pkg-config --libs libgps) -o $@
|
||||
|
||||
isobusfs-srv: lib.o \
|
||||
libj1939.o \
|
||||
isobusfs/isobusfs_cmn.o \
|
||||
|
||||
@@ -29,6 +29,7 @@ subsystem (aka SocketCAN):
|
||||
* canbusload : calculate and display the CAN busload
|
||||
* can-calc-bit-timing : userspace version of in-kernel bitrate calculation
|
||||
* canfdtest : Full-duplex test program (DUT and host part)
|
||||
* canerrsim : CAN error message simulator
|
||||
|
||||
#### ISO-TP tools [ISO15765-2:2016 for Linux](https://github.com/hartkopp/can-isotp)
|
||||
* isotpsend : send a single ISO-TP PDU
|
||||
@@ -49,6 +50,10 @@ subsystem (aka SocketCAN):
|
||||
Follow the link to see examples on how this tools can be used:
|
||||
[Kickstart guide to can-j1939 on linux](https://github.com/linux-can/can-utils/blob/master/can-j1939-kickstart.md)
|
||||
|
||||
#### ISOBus File server tools
|
||||
* isobusfs-cli : ISOBus file client
|
||||
* isobusfs-srv : ISOBus file server
|
||||
|
||||
#### Log file converters
|
||||
* asc2log : convert ASC logfile to compact CAN frame logfile
|
||||
* log2asc : convert compact CAN frame logfile to ASC logfile
|
||||
@@ -75,4 +80,4 @@ Follow the link to see examples on how this tools can be used:
|
||||
* [SocketCAN Documentation (Linux Kernel)](https://www.kernel.org/doc/html/latest/networking/can.html)
|
||||
* [Elinux.org CAN Bus Page](http://elinux.org/CAN_Bus)
|
||||
* [Debian Package Description](https://packages.debian.org/sid/can-utils)
|
||||
|
||||
* [J1939 kernel module installation on Debian](can-j1939-install-kernel-module.md)
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
@@ -84,7 +85,7 @@ static void print_usage(char *prg)
|
||||
}
|
||||
|
||||
static void prframe(FILE *file, struct timeval *tv, int dev,
|
||||
cu_t *cf, char dir)
|
||||
union cfu *cf, char dir)
|
||||
{
|
||||
static char abuf[BUFLEN];
|
||||
|
||||
@@ -114,6 +115,42 @@ static void get_can_id(canid_t *can_id, char *idstring, int base)
|
||||
*can_id |= strtoul(idstring, NULL, base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read 'len' ASCII hex bytes separated by single whitespace characters.
|
||||
* Returns a pointer to the content behind the data or NULL on error.
|
||||
*/
|
||||
static char *get_hex_data(char *ptr, unsigned char *data, int len)
|
||||
{
|
||||
unsigned char ctmp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ctmp = asc2nibble(ptr[0]);
|
||||
if (ctmp > 0x0F)
|
||||
return NULL;
|
||||
|
||||
data[i] = (ctmp << 4);
|
||||
|
||||
ctmp = asc2nibble(ptr[1]);
|
||||
if (ctmp > 0x0F)
|
||||
return NULL;
|
||||
|
||||
data[i] |= ctmp;
|
||||
|
||||
/*
|
||||
* The hex byte has to be followed by a separator (space/tab)
|
||||
* or an EOL char ('\n', '\r'). This also ensures that we do
|
||||
* not skip the string termination with ptr += 3 below.
|
||||
*/
|
||||
if (!isspace((unsigned char)ptr[2]))
|
||||
return NULL;
|
||||
|
||||
ptr += 3; /* start of next ASCII hex byte */
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void calc_tv(struct timeval *tv, struct timeval *read_tv,
|
||||
struct timeval *date_tv, char timestamps, int dplace)
|
||||
{
|
||||
@@ -155,7 +192,7 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
struct canfd_frame cf = { 0 };
|
||||
struct can_frame *ccf = (struct can_frame *)&cf; /* for len8_dlc */
|
||||
char rtr;
|
||||
int dlc = 0;
|
||||
unsigned int dlc = 0;
|
||||
int len = 0;
|
||||
int data[8];
|
||||
char idstr[21];
|
||||
@@ -177,7 +214,7 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
cf.len = CAN_ERR_DLC;
|
||||
|
||||
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
|
||||
prframe(outfile, &tv, interface, (cu_t *)&cf, NO_DIR);
|
||||
prframe(outfile, &tv, interface, (union cfu *)&cf, NO_DIR);
|
||||
fflush(outfile);
|
||||
return;
|
||||
}
|
||||
@@ -249,7 +286,7 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
cf.data[i] = data[i] & 0xFFU;
|
||||
|
||||
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
|
||||
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
|
||||
prframe(outfile, &tv, interface, (union cfu *)&cf, dir[0]);
|
||||
fflush(outfile);
|
||||
}
|
||||
}
|
||||
@@ -261,13 +298,12 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
static struct timeval tv; /* current frame timestamp */
|
||||
static struct timeval read_tv; /* frame timestamp from ASC file */
|
||||
struct canfd_frame cf = { 0 };
|
||||
unsigned char brs, esi, ctmp;
|
||||
unsigned char brs, esi;
|
||||
unsigned int flags;
|
||||
int dlc, dlen = 0;
|
||||
unsigned int dlc, dlen = 0;
|
||||
char idstr[21];
|
||||
char dir[5]; /* 'Rx'/'Tx'/'TxRq' plus terminating zero */
|
||||
char *ptr;
|
||||
int i;
|
||||
int n = 0; /* sscanf consumed characters */
|
||||
unsigned long long sec, usec;
|
||||
|
||||
@@ -283,12 +319,12 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
*/
|
||||
|
||||
/* check for valid line without symbolic name */
|
||||
if (sscanf(buf, "%llu.%llu %*s %d %4s %20s %hhx %hhx %x %d %n",
|
||||
if (sscanf(buf, "%llu.%llu %*s %d %4s %20s %hhx %hhx %x %u %n",
|
||||
&sec, &usec, &interface,
|
||||
dir, idstr, &brs, &esi, &dlc, &dlen, &n) != 9) {
|
||||
|
||||
/* check for valid line with a symbolic name */
|
||||
if (sscanf(buf, "%llu.%llu %*s %d %4s %20s %*s %hhx %hhx %x %d %n",
|
||||
if (sscanf(buf, "%llu.%llu %*s %d %4s %20s %*s %hhx %hhx %x %u %n",
|
||||
&sec, &usec, &interface,
|
||||
dir, idstr, &brs, &esi, &dlc, &dlen, &n) != 9) {
|
||||
|
||||
@@ -328,22 +364,10 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
|
||||
cf.len = dlen;
|
||||
|
||||
for (i = 0; i < dlen; i++) {
|
||||
ctmp = asc2nibble(ptr[0]);
|
||||
if (ctmp > 0x0F)
|
||||
ptr = get_hex_data(ptr, cf.data, dlen);
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
cf.data[i] = (ctmp << 4);
|
||||
|
||||
ctmp = asc2nibble(ptr[1]);
|
||||
if (ctmp > 0x0F)
|
||||
return;
|
||||
|
||||
cf.data[i] |= ctmp;
|
||||
|
||||
ptr += 3; /* start of next ASCII hex byte */
|
||||
}
|
||||
|
||||
/* skip MessageDuration and MessageLength to get Flags value */
|
||||
if (sscanf(ptr, " %*x %*x %x ", &flags) != 1)
|
||||
return;
|
||||
@@ -374,7 +398,7 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
}
|
||||
|
||||
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
|
||||
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
|
||||
prframe(outfile, &tv, interface, (union cfu *)&cf, dir[0]);
|
||||
fflush(outfile);
|
||||
|
||||
/* No support for really strange CANFD ErrorFrames format m( */
|
||||
@@ -387,13 +411,11 @@ static void eval_canxl_cc(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
static struct timeval tv; /* current frame timestamp */
|
||||
static struct timeval read_tv; /* frame timestamp from ASC file */
|
||||
struct can_frame cf = { 0 };
|
||||
unsigned char ctmp;
|
||||
unsigned int flags;
|
||||
int dlc, dlen = 0;
|
||||
unsigned int dlc, dlen = 0;
|
||||
char idstr[21];
|
||||
char dir[5]; /* 'Rx'/'Tx'/'TxRq' plus terminating zero */
|
||||
char *ptr;
|
||||
int i;
|
||||
int n = 0; /* sscanf consumed characters */
|
||||
unsigned long long sec, usec;
|
||||
|
||||
@@ -408,7 +430,7 @@ static void eval_canxl_cc(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
if (sscanf(buf,
|
||||
"%llu.%llu %*s %d %4s " /* time, CANXL, channel, direction */
|
||||
"%*s %*s %*s %20s " /* frame format, msg dur, bit count, ID */
|
||||
"%x %d %n", /* DLC, Datalen */
|
||||
"%x %u %n", /* DLC, Datalen */
|
||||
&sec, &usec, &interface, dir,
|
||||
idstr,
|
||||
&dlc, &dlen, &n) != 7) {
|
||||
@@ -416,7 +438,7 @@ static void eval_canxl_cc(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
if (sscanf(buf,
|
||||
"%llu.%llu %*s %d %4s " /* time, CANXL, channel, direction */
|
||||
"%*s %*s %*s %20s " /* frame format, msg dur, bit count, ID */
|
||||
"%*s %x %d %n", /* sym name, DLC, Datalen */
|
||||
"%*s %x %u %n", /* sym name, DLC, Datalen */
|
||||
&sec, &usec, &interface, dir,
|
||||
idstr,
|
||||
&dlc, &dlen, &n) != 7) {
|
||||
@@ -452,22 +474,10 @@ static void eval_canxl_cc(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
|
||||
cf.len = dlen;
|
||||
|
||||
for (i = 0; i < dlen; i++) {
|
||||
ctmp = asc2nibble(ptr[0]);
|
||||
if (ctmp > 0x0F)
|
||||
ptr = get_hex_data(ptr, cf.data, dlen);
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
cf.data[i] = (ctmp << 4);
|
||||
|
||||
ctmp = asc2nibble(ptr[1]);
|
||||
if (ctmp > 0x0F)
|
||||
return;
|
||||
|
||||
cf.data[i] |= ctmp;
|
||||
|
||||
ptr += 3; /* start of next ASCII hex byte */
|
||||
}
|
||||
|
||||
/* skip FCRC to get Flags value */
|
||||
if (sscanf(ptr, "%*x %x ", &flags) != 1)
|
||||
return;
|
||||
@@ -487,7 +497,7 @@ static void eval_canxl_cc(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
cf.len8_dlc = dlc;
|
||||
|
||||
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
|
||||
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
|
||||
prframe(outfile, &tv, interface, (union cfu *)&cf, dir[0]);
|
||||
fflush(outfile);
|
||||
}
|
||||
|
||||
@@ -498,13 +508,11 @@ static void eval_canxl_fd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
static struct timeval tv; /* current frame timestamp */
|
||||
static struct timeval read_tv; /* frame timestamp from ASC file */
|
||||
struct canfd_frame cf = { 0 };
|
||||
unsigned char ctmp;
|
||||
unsigned int flags;
|
||||
int dlc, dlen = 0;
|
||||
unsigned int dlc, dlen = 0;
|
||||
char idstr[21];
|
||||
char dir[5]; /* 'Rx'/'Tx'/'TxRq' plus terminating zero */
|
||||
char *ptr;
|
||||
int i;
|
||||
int n = 0; /* sscanf consumed characters */
|
||||
unsigned long long sec, usec;
|
||||
|
||||
@@ -519,7 +527,7 @@ static void eval_canxl_fd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
if (sscanf(buf,
|
||||
"%llu.%llu %*s %d %4s " /* time, CANXL, channel, direction */
|
||||
"%*s %*s %*s %20s " /* frame format, msg dur, bit count, ID */
|
||||
"%x %d %n", /* DLC, Datalen */
|
||||
"%x %u %n", /* DLC, Datalen */
|
||||
&sec, &usec, &interface, dir,
|
||||
idstr,
|
||||
&dlc, &dlen, &n) != 7) {
|
||||
@@ -527,7 +535,7 @@ static void eval_canxl_fd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
if (sscanf(buf,
|
||||
"%llu.%llu %*s %d %4s " /* time, CANXL, channel, direction */
|
||||
"%*s %*s %*s %20s " /* frame format, msg dur, bit count, ID */
|
||||
"%*s %x %d %n", /* sym name, DLC, Datalen */
|
||||
"%*s %x %u %n", /* sym name, DLC, Datalen */
|
||||
&sec, &usec, &interface, dir,
|
||||
idstr,
|
||||
&dlc, &dlen, &n) != 7) {
|
||||
@@ -563,22 +571,10 @@ static void eval_canxl_fd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
|
||||
cf.len = dlen;
|
||||
|
||||
for (i = 0; i < dlen; i++) {
|
||||
ctmp = asc2nibble(ptr[0]);
|
||||
if (ctmp > 0x0F)
|
||||
ptr = get_hex_data(ptr, cf.data, dlen);
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
cf.data[i] = (ctmp << 4);
|
||||
|
||||
ctmp = asc2nibble(ptr[1]);
|
||||
if (ctmp > 0x0F)
|
||||
return;
|
||||
|
||||
cf.data[i] |= ctmp;
|
||||
|
||||
ptr += 3; /* start of next ASCII hex byte */
|
||||
}
|
||||
|
||||
/* skip stuff field and FCRC to get Flags value */
|
||||
if (sscanf(ptr, "%*s %*s %x ", &flags) != 1)
|
||||
return;
|
||||
@@ -594,7 +590,7 @@ static void eval_canxl_fd(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
cf.flags |= CANFD_ESI;
|
||||
|
||||
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
|
||||
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
|
||||
prframe(outfile, &tv, interface, (union cfu *)&cf, dir[0]);
|
||||
fflush(outfile);
|
||||
}
|
||||
|
||||
@@ -605,13 +601,12 @@ static void eval_canxl_xl(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
static struct timeval tv; /* current frame timestamp */
|
||||
static struct timeval read_tv; /* frame timestamp from ASC file */
|
||||
struct canxl_frame cf = { 0 };
|
||||
unsigned char sdt, vcid, secbit, ctmp;
|
||||
unsigned char sdt, vcid, secbit;
|
||||
unsigned int af, flags;
|
||||
int dlc, dlen = 0;
|
||||
unsigned int dlc, dlen = 0;
|
||||
char idstr[21];
|
||||
char dir[5]; /* 'Rx'/'Tx'/'TxRq' plus terminating zero */
|
||||
char *ptr;
|
||||
int i;
|
||||
int n = 0; /* sscanf consumed characters */
|
||||
unsigned long long sec, usec;
|
||||
|
||||
@@ -627,7 +622,7 @@ static void eval_canxl_xl(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
if (sscanf(buf,
|
||||
"%llu.%llu %*s %d %4s " /* time, CANXL, channel, direction */
|
||||
"%*s %*s %*s %20s " /* frame format, msg dur, bit count, ID */
|
||||
"%hhx %hhx %x %d " /* SDT, SEC, DLC, Datalen */
|
||||
"%hhx %hhx %x %u " /* SDT, SEC, DLC, Datalen */
|
||||
"%*s %*s %hhx %x %n", /* stuff bit count, crc, VCID, AF */
|
||||
&sec, &usec, &interface, dir,
|
||||
idstr,
|
||||
@@ -637,7 +632,7 @@ static void eval_canxl_xl(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
if (sscanf(buf,
|
||||
"%llu.%llu %*s %d %4s " /* time, CANXL, channel, direction */
|
||||
"%*s %*s %*s %20s " /* frame format, msg dur, bit count, ID */
|
||||
"%*s %hhx %hhx %x %d " /* sym name, SDT, SEC, DLC, Datalen */
|
||||
"%*s %hhx %hhx %x %u " /* sym name, SDT, SEC, DLC, Datalen */
|
||||
"%*s %*s %hhx %x %n", /* stuff bit count, crc, VCID, AF */
|
||||
&sec, &usec, &interface, dir,
|
||||
idstr,
|
||||
@@ -689,22 +684,10 @@ static void eval_canxl_xl(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
|
||||
cf.len = dlen;
|
||||
|
||||
for (i = 0; i < dlen; i++) {
|
||||
ctmp = asc2nibble(ptr[0]);
|
||||
if (ctmp > 0x0F)
|
||||
ptr = get_hex_data(ptr, cf.data, dlen);
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
cf.data[i] = (ctmp << 4);
|
||||
|
||||
ctmp = asc2nibble(ptr[1]);
|
||||
if (ctmp > 0x0F)
|
||||
return;
|
||||
|
||||
cf.data[i] |= ctmp;
|
||||
|
||||
ptr += 3; /* start of next ASCII hex byte */
|
||||
}
|
||||
|
||||
/* skip FCRC to get Flags value */
|
||||
if (sscanf(ptr, "%*x %x ", &flags) != 1)
|
||||
return;
|
||||
@@ -723,7 +706,7 @@ static void eval_canxl_xl(char* buf, struct timeval *date_tvp, char timestamps,
|
||||
cf.flags |= CANXL_RRS;
|
||||
|
||||
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
|
||||
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
|
||||
prframe(outfile, &tv, interface, (union cfu *)&cf, dir[0]);
|
||||
fflush(outfile);
|
||||
|
||||
/* No support for CAN XL ErrorFrames */
|
||||
|
||||
+28
-8
@@ -153,7 +153,7 @@ int main(void)
|
||||
|
||||
char buf[MAXLEN];
|
||||
char format[FORMATSZ];
|
||||
char rxmsg[50];
|
||||
char rxmsg[64];
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpragmas"
|
||||
@@ -234,7 +234,7 @@ int main(void)
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
again:
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sc, &readfds);
|
||||
FD_SET(sa, &readfds);
|
||||
@@ -242,6 +242,8 @@ int main(void)
|
||||
select((sc > sa)?sc+1:sa+1, &readfds, NULL, NULL, NULL);
|
||||
|
||||
if (FD_ISSET(sc, &readfds)) {
|
||||
size_t size = sizeof(rxmsg);
|
||||
int len = 0, res;
|
||||
|
||||
recvfrom(sc, &msg, sizeof(msg), 0,
|
||||
(struct sockaddr*)&caddr, &caddrlen);
|
||||
@@ -249,17 +251,35 @@ int main(void)
|
||||
ifr.ifr_ifindex = caddr.can_ifindex;
|
||||
ioctl(sc, SIOCGIFNAME, &ifr);
|
||||
|
||||
sprintf(rxmsg, "< %s %03X %d ", ifr.ifr_name,
|
||||
res = snprintf(rxmsg, size, "< %s %03X %d ", ifr.ifr_name,
|
||||
msg.msg_head.can_id, msg.frame.can_dlc);
|
||||
if (res < 0 || (size_t)res >= size) {
|
||||
printf("Error: rxmsg buffer (size %zu) too small for data.\n", size);
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( i = 0; i < msg.frame.can_dlc; i++)
|
||||
sprintf(rxmsg + strlen(rxmsg), "%02X ",
|
||||
msg.frame.data[i]);
|
||||
len += res;
|
||||
|
||||
for (i = 0; i < msg.frame.can_dlc; i++) {
|
||||
res = snprintf(rxmsg + len, size - len, "%02X ", msg.frame.data[i]);
|
||||
if (res < 0 || (size_t)res >= (size - len)) {
|
||||
printf("Error: rxmsg buffer (size %zu) too small for data.\n", size);
|
||||
goto again;
|
||||
}
|
||||
|
||||
len += res;
|
||||
}
|
||||
|
||||
/* delimiter '\0' for Adobe(TM) Flash(TM) XML sockets */
|
||||
strcat(rxmsg, ">\0");
|
||||
res = snprintf(rxmsg + len, size - len, ">");
|
||||
if (res < 0 || (size_t)res >= (size - len)) {
|
||||
printf("Error: rxmsg buffer (size %zu) too small for data.\n", size);
|
||||
continue;
|
||||
}
|
||||
|
||||
send(sa, rxmsg, strlen(rxmsg) + 1, 0);
|
||||
len += res;
|
||||
|
||||
send(sa, rxmsg, len + 1, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1121,6 +1121,11 @@ static const unsigned int common_data_bitrates[] = {
|
||||
4000000,
|
||||
2000000,
|
||||
1000000,
|
||||
800000,
|
||||
500000,
|
||||
250000,
|
||||
125000,
|
||||
100000,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@
|
||||
#ifndef SO_TIMESTAMPING
|
||||
#define SO_TIMESTAMPING 37
|
||||
#endif
|
||||
#ifndef SCM_TIMESTAMPING
|
||||
#define SCM_TIMESTAMPING SO_TIMESTAMPING
|
||||
#endif
|
||||
|
||||
#define TIMESTAMPSZ 50 /* string 'absolute with date' requires max 49 bytes */
|
||||
|
||||
@@ -113,7 +116,8 @@ static const int canfx_on = 1;
|
||||
|
||||
#define MAXANI 4
|
||||
static const char anichar[MAXANI] = { '|', '/', '-', '\\' };
|
||||
static const char extra_m_info[4][4] = { "- -", "B -", "- E", "B E" };
|
||||
static const char extra_fd_info[4][4] = { "- -", "B -", "- E", "B E" };
|
||||
static const char extra_xl_info[4][4] = { "- -", "S -", "- R", "S R" };
|
||||
|
||||
extern int optind, opterr, optopt;
|
||||
|
||||
@@ -128,6 +132,7 @@ static void print_usage(void)
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr, " -t <type> (timestamp: (a)bsolute/(d)elta/(z)ero/(A)bsolute w date)\n");
|
||||
fprintf(stderr, " -H (read hardware timestamps instead of system timestamps)\n");
|
||||
fprintf(stderr, " -N (log nanosecond timestamps instead of microseconds)\n");
|
||||
fprintf(stderr, " -c (increment color mode level)\n");
|
||||
fprintf(stderr, " -i (binary output - may exceed 80 chars/line)\n");
|
||||
fprintf(stderr, " -a (enable additional ASCII output)\n");
|
||||
@@ -142,7 +147,7 @@ static void print_usage(void)
|
||||
fprintf(stderr, " -d (monitor dropped CAN frames)\n");
|
||||
fprintf(stderr, " -e (dump CAN error frames in human-readable format)\n");
|
||||
fprintf(stderr, " -8 (display raw DLC values in {} for Classical CAN)\n");
|
||||
fprintf(stderr, " -x (print extra message infos, rx/tx brs esi)\n");
|
||||
fprintf(stderr, " -x (print extra message infos, rx/tx [brs esi|sec rrs])\n");
|
||||
fprintf(stderr, " -T <msecs> (terminate after <msecs> if no frames were received)\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Up to %d CAN interfaces with optional filter sets can be specified\n", MAXSOCK);
|
||||
@@ -220,16 +225,22 @@ static int idx2dindex(int ifidx, int socket)
|
||||
return i;
|
||||
}
|
||||
|
||||
static int sprint_timestamp(char *ts_buffer, const char timestamp,
|
||||
const struct timeval *tv, struct timeval *const last_tv)
|
||||
static int sprint_timestamp(char *ts_buffer, const char timestamp, const char use_ns,
|
||||
const struct timespec *ts, struct timespec *const last_ts)
|
||||
{
|
||||
int numchars = 0;
|
||||
|
||||
switch (timestamp) {
|
||||
case 'a': /* absolute with timestamp */
|
||||
if (use_ns) {
|
||||
numchars = sprintf(ts_buffer, "(%010llu.%09llu) ",
|
||||
(unsigned long long)ts->tv_sec,
|
||||
(unsigned long long)ts->tv_nsec);
|
||||
} else {
|
||||
numchars = sprintf(ts_buffer, "(%010llu.%06llu) ",
|
||||
(unsigned long long)tv->tv_sec,
|
||||
(unsigned long long)tv->tv_usec);
|
||||
(unsigned long long)ts->tv_sec,
|
||||
(unsigned long long)ts->tv_nsec / 1000);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'A': /* absolute with date */
|
||||
@@ -237,32 +248,43 @@ static int sprint_timestamp(char *ts_buffer, const char timestamp,
|
||||
struct tm tm;
|
||||
char timestring[25];
|
||||
|
||||
tm = *localtime(&tv->tv_sec);
|
||||
tm = *localtime(&ts->tv_sec);
|
||||
strftime(timestring, 24, "%Y-%m-%d %H:%M:%S", &tm);
|
||||
if (use_ns) {
|
||||
numchars = sprintf(ts_buffer, "(%s.%09llu) ", timestring,
|
||||
(unsigned long long)ts->tv_nsec);
|
||||
} else {
|
||||
numchars = sprintf(ts_buffer, "(%s.%06llu) ", timestring,
|
||||
(unsigned long long)tv->tv_usec);
|
||||
(unsigned long long)ts->tv_nsec / 1000);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'd': /* delta */
|
||||
case 'z': /* starting with zero */
|
||||
{
|
||||
struct timeval diff;
|
||||
struct timespec diff;
|
||||
|
||||
if (last_tv->tv_sec == 0) /* first init */
|
||||
*last_tv = *tv;
|
||||
diff.tv_sec = tv->tv_sec - last_tv->tv_sec;
|
||||
diff.tv_usec = tv->tv_usec - last_tv->tv_usec;
|
||||
if (diff.tv_usec < 0)
|
||||
diff.tv_sec--, diff.tv_usec += 1000000;
|
||||
if (last_ts->tv_sec == 0) /* first init */
|
||||
*last_ts = *ts;
|
||||
diff.tv_sec = ts->tv_sec - last_ts->tv_sec;
|
||||
diff.tv_nsec = ts->tv_nsec - last_ts->tv_nsec;
|
||||
if (diff.tv_nsec < 0)
|
||||
diff.tv_sec--, diff.tv_nsec += 1000000000;
|
||||
if (diff.tv_sec < 0)
|
||||
diff.tv_sec = diff.tv_usec = 0;
|
||||
diff.tv_sec = diff.tv_nsec = 0;
|
||||
if (use_ns) {
|
||||
numchars = sprintf(ts_buffer, "(%03llu.%09llu) ",
|
||||
(unsigned long long)diff.tv_sec,
|
||||
(unsigned long long)diff.tv_nsec);
|
||||
} else {
|
||||
numchars = sprintf(ts_buffer, "(%03llu.%06llu) ",
|
||||
(unsigned long long)diff.tv_sec,
|
||||
(unsigned long long)diff.tv_usec);
|
||||
(unsigned long long)diff.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
if (timestamp == 'd')
|
||||
*last_tv = *tv; /* update for delta calculation */
|
||||
*last_ts = *ts; /* update for delta calculation */
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -288,6 +310,7 @@ int main(int argc, char **argv)
|
||||
unsigned char timestamp = 0;
|
||||
unsigned char logtimestamp = 'a';
|
||||
unsigned char hwtimestamp = 0;
|
||||
unsigned char use_ns = 0;
|
||||
unsigned char down_causes_exit = 1;
|
||||
unsigned char dropmonitor = 0;
|
||||
unsigned char extra_msg_info = 0;
|
||||
@@ -319,10 +342,10 @@ int main(int argc, char **argv)
|
||||
struct cmsghdr *cmsg;
|
||||
struct can_filter *rfilter;
|
||||
can_err_mask_t err_mask;
|
||||
static cu_t cu; /* union for CAN CC/FD/XL frames */
|
||||
static union cfu cu; /* union for CAN CC/FD/XL frames */
|
||||
int nbytes, i;
|
||||
struct ifreq ifr;
|
||||
struct timeval tv, last_tv;
|
||||
struct timespec ts, last_ts;
|
||||
int timeout_ms = -1; /* default to no timeout */
|
||||
FILE *logfile = NULL;
|
||||
char fname[83]; /* suggested by -Wformat-overflow= */
|
||||
@@ -334,12 +357,12 @@ int main(int argc, char **argv)
|
||||
signal(SIGHUP, sigterm);
|
||||
signal(SIGINT, sigterm);
|
||||
|
||||
last_tv.tv_sec = 0;
|
||||
last_tv.tv_usec = 0;
|
||||
last_ts.tv_sec = 0;
|
||||
last_ts.tv_nsec = 0;
|
||||
|
||||
progname = basename(argv[0]);
|
||||
|
||||
while ((opt = getopt(argc, argv, "t:HciaSs:lf:Ln:r:Dde8xT:h?")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "t:HNciaSs:lf:Ln:r:Dde8xT:h?")) != -1) {
|
||||
switch (opt) {
|
||||
case 't':
|
||||
timestamp = optarg[0];
|
||||
@@ -359,6 +382,10 @@ int main(int argc, char **argv)
|
||||
hwtimestamp = 1;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
use_ns = 1;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
color++;
|
||||
break;
|
||||
@@ -783,9 +810,13 @@ int main(int argc, char **argv)
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg && (cmsg->cmsg_level == SOL_SOCKET);
|
||||
cmsg = CMSG_NXTHDR(&msg,cmsg)) {
|
||||
if (cmsg->cmsg_type == SO_TIMESTAMP) {
|
||||
if (cmsg->cmsg_type == SCM_TIMESTAMP) {
|
||||
struct timeval tv;
|
||||
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
|
||||
} else if (cmsg->cmsg_type == SO_TIMESTAMPING) {
|
||||
ts.tv_sec = tv.tv_sec;
|
||||
ts.tv_nsec = tv.tv_usec;
|
||||
ts.tv_nsec *= 1000;
|
||||
} else if (cmsg->cmsg_type == SCM_TIMESTAMPING) {
|
||||
struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg);
|
||||
|
||||
/*
|
||||
@@ -795,8 +826,7 @@ int main(int argc, char **argv)
|
||||
* See chapter 2.1.2 Receive timestamps in
|
||||
* linux/Documentation/networking/timestamping.txt
|
||||
*/
|
||||
tv.tv_sec = stamp[2].tv_sec;
|
||||
tv.tv_usec = stamp[2].tv_nsec / 1000;
|
||||
ts = stamp[2];
|
||||
} else if (cmsg->cmsg_type == SO_RXQ_OVFL) {
|
||||
memcpy(&obj->dropcnt, CMSG_DATA(cmsg), sizeof(__u32));
|
||||
}
|
||||
@@ -831,11 +861,9 @@ int main(int argc, char **argv)
|
||||
/* build common log format output */
|
||||
if ((log) || ((logfrmt) && (silent == SILENT_OFF))) {
|
||||
|
||||
alen = sprint_timestamp(afrbuf, logtimestamp,
|
||||
&tv, &last_tv);
|
||||
alen = sprint_timestamp(afrbuf, logtimestamp, use_ns, &ts, &last_ts);
|
||||
|
||||
alen += sprintf(afrbuf + alen, "%*s ",
|
||||
max_devname_len, devname[idx]);
|
||||
alen += sprintf(afrbuf + alen, "%*s ", max_devname_len, devname[idx]);
|
||||
|
||||
alen += snprintf_canframe(afrbuf + alen, sizeof(afrbuf) - alen, &cu, 0);
|
||||
}
|
||||
@@ -861,18 +889,23 @@ int main(int argc, char **argv)
|
||||
|
||||
/* print (colored) long CAN frame style to stdout */
|
||||
alen = sprintf(afrbuf, " %s", (color > 2) ? col_on[idx % MAXCOL] : "");
|
||||
alen += sprint_timestamp(afrbuf + alen, timestamp, &tv, &last_tv);
|
||||
alen += sprint_timestamp(afrbuf + alen, timestamp, use_ns, &ts, &last_ts);
|
||||
alen += sprintf(afrbuf + alen, " %s%*s",
|
||||
(color && (color < 3)) ? col_on[idx % MAXCOL] : "",
|
||||
max_devname_len, devname[idx]);
|
||||
|
||||
if (extra_msg_info) {
|
||||
if (msg.msg_flags & MSG_DONTROUTE)
|
||||
alen += sprintf(afrbuf + alen, " TX %s",
|
||||
extra_m_info[cu.fd.flags & 3]);
|
||||
alen += sprintf(afrbuf + alen, " TX");
|
||||
else
|
||||
alen += sprintf(afrbuf + alen, " RX %s",
|
||||
extra_m_info[cu.fd.flags & 3]);
|
||||
alen += sprintf(afrbuf + alen, " RX");
|
||||
|
||||
if (cu.xl.flags & CANXL_XLF)
|
||||
alen += sprintf(afrbuf + alen, " %s",
|
||||
extra_xl_info[cu.xl.flags & 3]);
|
||||
else
|
||||
alen += sprintf(afrbuf + alen, " %s",
|
||||
extra_fd_info[cu.fd.flags & 3]);
|
||||
}
|
||||
|
||||
alen += sprintf(afrbuf + alen, "%s ", (color == 1) ? col_off : "");
|
||||
|
||||
+563
@@ -0,0 +1,563 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// canerrsim - utility to simulate SocketCAN error messages, by Zeljko Avramovic (c) 2024 //
|
||||
// //
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause //
|
||||
// //
|
||||
// Virtual CAN adapter vcan0 is hard coded and you can bring it up like this: //
|
||||
// sudo modprobe vcan //
|
||||
// sudo ip link add dev vcan0 type vcan //
|
||||
// sudo ip link set vcan0 mtu 72 # needed for CAN FD //
|
||||
// sudo ip link set vcan0 up //
|
||||
// //
|
||||
// To simulate error messages use canerrsim utility like this: //
|
||||
// ./canerrsim vcan0 LostArBit=09 Data4=AA TX BusOff NoAck ShowBits //
|
||||
// //
|
||||
// That should show in canerrdump utility as: //
|
||||
// 0x06A [8] 09 00 80 00 AA 00 00 00 ERR=LostArBit09,NoAck,BusOff,Prot(Type(TX),Loc(Unspec)) //
|
||||
// //
|
||||
// Alternatively, you could use candump from can-utils to check only error messages like this: //
|
||||
// candump -tA -e -c -a any,0~0,#FFFFFFFF //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/error.h>
|
||||
#include <linux/can/raw.h>
|
||||
#include <net/if.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define STR_EQUAL 0
|
||||
|
||||
void show_help_and_exit()
|
||||
{
|
||||
printf("\n");
|
||||
printf("Usage: canerrsim <CAN interface> <options>\n");
|
||||
printf("\n");
|
||||
printf("CAN interface: ( CAN interface is case sensitive )\n");
|
||||
printf(" can0 ( or can1, can2 or virtual ones like vcan0, vcan1...\n");
|
||||
printf("\n");
|
||||
printf("Options: ( options are not case sensitive )\n");
|
||||
printf(" ( ERROR CLASS (MASK) IN CAN ID: )\n");
|
||||
printf(" TxTimeout ( TX timeout by netdevice driver )\n");
|
||||
printf(" NoAck ( received no ACK on transmission )\n");
|
||||
printf(" BusOff ( bus off )\n");
|
||||
printf(" BusError ( bus error, may flood! )\n");
|
||||
printf(" Restarted ( controller restarted )\n");
|
||||
printf(" TxCount=<00..FF> ( TX error counter )\n");
|
||||
printf(" RxCount=<00..FF> ( RX error counter )\n");
|
||||
printf(" ( ARBITRATIONLOST IN CAN ID + BIT NUMBER IN DATA[0]: )\n");
|
||||
printf(" LostArBit=<00..29> ( decimal lost arbitration bit number in bitstream )\n");
|
||||
printf(" ( CONTROLLER IN CAN ID + ERROR STATUS IN DATA[1]: )\n");
|
||||
printf(" OverflowRX ( RX buffer overflow )\n");
|
||||
printf(" OverflowTX ( TX buffer overflow )\n");
|
||||
printf(" WarningRX ( reached warning level for RX errors )\n");
|
||||
printf(" WarningTX ( reached warning level for TX errors )\n");
|
||||
printf(" PassiveRX ( reached error passive status RX, errors > 127 )\n");
|
||||
printf(" PassiveTX ( reached error passive status TX, errors > 127 )\n");
|
||||
printf(" Active ( recovered to error active state )\n");
|
||||
printf(" ( PROTOCOL ERROR IN CAN ID + TYPE IN DATA[2]: )\n");
|
||||
printf(" SingleBit ( single bit error )\n");
|
||||
printf(" FrameFormat ( frame format error )\n");
|
||||
printf(" BitStuffing ( bit stuffing error )\n");
|
||||
printf(" Bit0 ( unable to send dominant bit )\n");
|
||||
printf(" Bit1 ( unable to send recessive bit )\n");
|
||||
printf(" BusOverload ( bus overload )\n");
|
||||
printf(" ActiveAnnouncement ( active error announcement )\n");
|
||||
printf(" TX ( error occurred on transmission )\n");
|
||||
printf(" ( PROTOCOL ERROR IN CAN ID + LOCATION IN DATA[3]: )\n");
|
||||
printf(" SOF ( start of frame )\n");
|
||||
printf(" ID28_21 ( ID bits 21..28, SFF: 3..10 )\n");
|
||||
printf(" ID20_18 ( ID bits 18..20, SFF: 0..2 )\n");
|
||||
printf(" SRTR ( substitute RTR, SFF: RTR )\n");
|
||||
printf(" IDE ( identifier extension )\n");
|
||||
printf(" ID17_13 ( ID bits 13..17 )\n");
|
||||
printf(" ID12_05 ( ID bits 5..12 )\n");
|
||||
printf(" ID04_00 ( ID bits 0..4 )\n");
|
||||
printf(" RTR ( RTR )\n");
|
||||
printf(" RES1 ( reserved bit 1 )\n");
|
||||
printf(" RES0 ( reserved bit 0 )\n");
|
||||
printf(" DLC ( data length code )\n");
|
||||
printf(" DATA ( data section )\n");
|
||||
printf(" CRC_SEQ ( CRC sequence )\n");
|
||||
printf(" CRC_DEL ( CRC delimiter )\n");
|
||||
printf(" ACK ( ACK slot )\n");
|
||||
printf(" ACK_DEL ( ACK delimiter )\n");
|
||||
printf(" EOF ( end of frame )\n");
|
||||
printf(" INTERM ( intermission )\n");
|
||||
printf(" ( TRANSCEIVER ERROR IN CAN ID + STATUS IN DATA[4]: )\n");
|
||||
printf(" ( CANH CANL )\n");
|
||||
printf(" TransUnspec ( 0000 0000 )\n");
|
||||
printf(" CanHiNoWire ( 0000 0100 )\n");
|
||||
printf(" CanHiShortToBAT ( 0000 0101 )\n");
|
||||
printf(" CanHiShortToVCC ( 0000 0110 )\n");
|
||||
printf(" CanHiShortToGND ( 0000 0111 )\n");
|
||||
printf(" CanLoNoWire ( 0100 0000 )\n");
|
||||
printf(" CanLoShortToBAT ( 0101 0000 )\n");
|
||||
printf(" CanLoShortToVCC ( 0110 0000 )\n");
|
||||
printf(" CanLoShortToGND ( 0111 0000 )\n");
|
||||
printf(" CanLoShortToCanHi ( 1000 0000 )\n");
|
||||
printf(" ( CUSTOM BYTE TO DATA[0..7]: )\n");
|
||||
printf(" Data<0..7>=<00..FF> ( write hex number to one of 8 payload bytes )\n");
|
||||
printf(" ( DEBUG HELPERS: )\n");
|
||||
printf(" ShowBits ( display all frame bits )\n");
|
||||
printf("\n");
|
||||
printf("Examples:\n");
|
||||
printf("\n");
|
||||
printf(" ./canerrsim can1 LostArBit=09 Data3=AA Data4=BB ShowBits\n");
|
||||
printf(" ( can1: 9th arb. bit lost, custom bytes in Data[3] and Data[4], show debug frame bits )\n");
|
||||
printf("\n");
|
||||
printf(" ./canerrsim vcan0 NoAck TxTimeout Active\n");
|
||||
printf(" ( vcan0: received no ACK on transmission, driver timeout, protocol type active error announcement )\n");
|
||||
printf("\n");
|
||||
printf(" ./canerrsim vcan0 BusError CanHiNoWire Restarted INTERM\n");
|
||||
printf(" ( vcan0: bus error, lost CANH wiring, controller restarted, protocol location intermission )\n");
|
||||
printf("\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void __attribute__((format (printf, 1, 2))) err_exit(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vfprintf(stdout, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void show_invalid_option(const char *option)
|
||||
{
|
||||
err_exit("Error: Invalid option %s\n", option);
|
||||
}
|
||||
|
||||
void show_err_and_exit(const char *err_type)
|
||||
{
|
||||
err_exit("Error: You can only have one %s parameter!\n", err_type);
|
||||
}
|
||||
|
||||
void show_loc_err_and_exit()
|
||||
{
|
||||
show_err_and_exit("protocol location");
|
||||
}
|
||||
|
||||
void show_arb_err_and_exit()
|
||||
{
|
||||
show_err_and_exit("arbitration bit");
|
||||
}
|
||||
|
||||
void show_transc_err_and_exit()
|
||||
{
|
||||
show_err_and_exit("transceiver");
|
||||
}
|
||||
|
||||
void print_binary(uint32_t number)
|
||||
{
|
||||
uint32_t mask = 0x80000000; // start with the most significant bit
|
||||
for (int i = 0; i < 32; i++) {
|
||||
putchar((number & mask) ? '1' : '0');
|
||||
mask >>= 1; // shift the mask to the right
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int sock;
|
||||
struct sockaddr_can addr;
|
||||
struct ifreq ifr;
|
||||
struct can_frame frame;
|
||||
bool show_bits = false, location_processed = false, transceiver_processed = false, arbitration_processed = false;
|
||||
|
||||
printf("CAN Sockets Error Messages Simulator\n");
|
||||
if (argc < 3)
|
||||
show_help_and_exit();
|
||||
|
||||
// initialize CAN frame
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
frame.can_id = CAN_ERR_FLAG;
|
||||
frame.can_dlc = CAN_ERR_DLC;
|
||||
|
||||
// Parse command line parameters
|
||||
for (int i = 2; i < argc; i++) {
|
||||
//printf("strlen(argv[%d]) = %d\n", i, strlen(argv[i]));
|
||||
|
||||
// error class (mask) in can_id
|
||||
if (strcasecmp(argv[i], "TxTimeout") == STR_EQUAL)
|
||||
frame.can_id |= CAN_ERR_TX_TIMEOUT; // generate TxTimeout error
|
||||
else if (strcasecmp(argv[i], "NoAck") == STR_EQUAL)
|
||||
frame.can_id |= CAN_ERR_ACK; // generate NoAck error
|
||||
else if (strcasecmp(argv[i], "BusOff") == STR_EQUAL)
|
||||
frame.can_id |= CAN_ERR_BUSOFF; // generate BusOff error
|
||||
else if (strcasecmp(argv[i], "BusError") == STR_EQUAL)
|
||||
frame.can_id |= CAN_ERR_BUSERROR; // generate BusError error
|
||||
else if (strcasecmp(argv[i], "Restarted") == STR_EQUAL)
|
||||
frame.can_id |= CAN_ERR_RESTARTED; // generate Restarted error
|
||||
// error status of CAN controller / data[1]
|
||||
else if (strcasecmp(argv[i], "OverflowRX") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; // generate RX Overflow suberror
|
||||
} else if (strcasecmp(argv[i], "OverflowTX") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] |= CAN_ERR_CRTL_TX_OVERFLOW; // generate TX Overflow suberror
|
||||
} else if (strcasecmp(argv[i], "WarningRX") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] |= CAN_ERR_CRTL_RX_WARNING; // generate RX Warning suberror
|
||||
} else if (strcasecmp(argv[i], "WarningTX") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] |= CAN_ERR_CRTL_TX_WARNING; // generate TX Warning suberror
|
||||
} else if (strcasecmp(argv[i], "PassiveRX") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] |= CAN_ERR_CRTL_RX_PASSIVE; // generate RX Passive suberror
|
||||
} else if (strcasecmp(argv[i], "PassiveTX") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] |= CAN_ERR_CRTL_TX_PASSIVE; // generate TX Passive suberror
|
||||
} else if (strcasecmp(argv[i], "Active") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] |= CAN_ERR_CRTL_ACTIVE; // generate Active suberror
|
||||
} else if (strcasecmp(argv[i], "CtrlUnspec") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CRTL; // generate Controller error
|
||||
frame.data[1] = CAN_ERR_CRTL_UNSPEC; // generate Unspec suberror
|
||||
}
|
||||
// error in CAN protocol (type) / data[2]
|
||||
else if (strcasecmp(argv[i], "SingleBit") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_BIT; // generate SingleBit suberror
|
||||
} else if (strcasecmp(argv[i], "FrameFormat") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_FORM; // generate FrameFormat suberror
|
||||
} else if (strcasecmp(argv[i], "BitStuffing") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_STUFF; // generate BitStuffing suberror
|
||||
} else if (strcasecmp(argv[i], "Bit0") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_BIT0; // generate Bit0 suberror
|
||||
} else if (strcasecmp(argv[i], "Bit1") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_BIT1; // generate Bit1 suberror
|
||||
} else if (strcasecmp(argv[i], "BusOverload") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_OVERLOAD; // generate BusOverload suberror
|
||||
} else if (strcasecmp(argv[i], "ActiveAnnouncement") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_ACTIVE; // generate ActiveAnnouncement suberror
|
||||
} else if (strcasecmp(argv[i], "TX") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_TX; // generate TX suberror
|
||||
} else if (strcasecmp(argv[i], "ProtUnspec") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Type error
|
||||
frame.data[2] = CAN_ERR_PROT_UNSPEC; // generate Unspec suberror
|
||||
}
|
||||
// error in CAN protocol (location) / data[3]
|
||||
else if (strcasecmp(argv[i], "LocUnspec") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_UNSPEC; // generate Unspec suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "SOF") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_SOF; // generate SOF suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "SOF") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_SOF; // generate SOF suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "ID28_21") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_ID28_21; // generate ID28_21 suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "ID20_18") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_ID20_18; // generate ID20_18 suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "SRTR") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_SRTR; // generate SRTR suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "IDE") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_IDE; // generate IDE suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "ID17_13") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_ID17_13; // generate ID17_13 suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "ID12_05") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_ID12_05; // generate ID12_05 suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "ID04_00") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_ID04_00; // generate ID04_00 suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "RTR") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_RTR; // generate RTR suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "RES1") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_RES1; // generate RES1 suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "RES0") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_RES0; // generate RES0 suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "DLC") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_DLC; // generate DLC suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "DATA") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_DATA; // generate DATA suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CRC_SEQ") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; // generate CRC_SEQ suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CRC_DEL") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_CRC_DEL; // generate CRC_DEL suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "ACK") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_ACK; // generate ACK suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "ACK_DEL") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_ACK_DEL; // generate ACK_DEL suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "EOF") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_EOF; // generate EOF suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "INTERM") == STR_EQUAL) {
|
||||
if (location_processed)
|
||||
show_loc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_PROT; // generate Protocol Location error
|
||||
frame.data[3] = CAN_ERR_PROT_LOC_INTERM; // generate INTERM suberror
|
||||
location_processed = true;
|
||||
}
|
||||
// error status of CAN transceiver / data[4]
|
||||
else if (strcasecmp(argv[i], "TransUnspec") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_UNSPEC; // generate EOF suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanHiNoWire") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANH_NO_WIRE; // generate CanHiNoWire suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanHiShortToBAT") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANH_SHORT_TO_BAT; // generate CanHiShortToBAT suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanHiShortToVCC") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANH_SHORT_TO_VCC; // generate CanHiShortToVCC suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanHiShortToGND") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANH_SHORT_TO_GND; // generate CanHiShortToGND suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanLoNoWire") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANL_NO_WIRE; // generate CanLoNoWire suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanLoShortToBAT") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANL_SHORT_TO_BAT; // generate CanLoShortToBAT suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanLoShortToVCC") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANL_SHORT_TO_VCC; // generate CanLoShortToVCC suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanLoShortToGND") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANL_SHORT_TO_GND; // generate CanLoShortToGND suberror
|
||||
location_processed = true;
|
||||
} else if (strcasecmp(argv[i], "CanLoShortToCanHi") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_TRX; // generate Transceiver error
|
||||
frame.data[4] = CAN_ERR_TRX_CANL_SHORT_TO_CANH; // generate CanLoShortToCanHi suberror
|
||||
location_processed = true;
|
||||
}
|
||||
// LostArBit=29 (Totallength=12)
|
||||
else if ((strlen(argv[i]) == 12) && // 'LostArBit=29'
|
||||
(argv[i][9] == '=') && // '='
|
||||
(argv[i][10] >= '0' && argv[i][10] <= '2') && // valid bits are from 00 to 29 (in decimal)
|
||||
(argv[i][11] >= '0' && argv[i][11] <= '9')) { // valid bits are from 00 to 29 (in decimal)
|
||||
unsigned char arb_bit_num = (argv[i][10] - '0') * 10 + argv[i][11] - '0'; // convert decimal bitnumber to byte
|
||||
argv[i][9] = 0; // terminate string for comparison
|
||||
if (strcasecmp(argv[i], "LostArBit") == STR_EQUAL) {
|
||||
if (arbitration_processed)
|
||||
show_arb_err_and_exit();
|
||||
frame.can_id |= CAN_ERR_LOSTARB; // generate LostArbitartionBit error
|
||||
frame.data[0] = arb_bit_num; // bitnumber
|
||||
arbitration_processed = true;
|
||||
} else {
|
||||
argv[i][9] = '='; // undo string termination
|
||||
show_invalid_option(argv[i]);
|
||||
}
|
||||
}
|
||||
// Data1=F4 (Totallength=8) // since this does not set any error bit, has to be combined with other errors
|
||||
else if ((strlen(argv[i]) == 8) && // 'Data1=F4'
|
||||
(argv[i][4] >= '0' && argv[i][4] <= '7') && // valid data bytes are from 0 to 7 (in decimal)
|
||||
(argv[i][5] == '=') && // '='
|
||||
((argv[i][6] >= '0' && argv[i][6] <= '9') || (argv[i][6] >= 'A' && argv[i][6] <= 'F')) && // first hexadecimal digit
|
||||
((argv[i][7] >= '0' && argv[i][7] <= '9') || (argv[i][6] >= 'A' && argv[i][6] <= 'F'))) { // second hexadecimal digit
|
||||
unsigned char data_byte_value, data_byte_no = 0;
|
||||
data_byte_no = argv[i][4] - '0'; // convert order number of data byte (Data1 to 1, Data2 to 2...)
|
||||
data_byte_value = 0;
|
||||
if (argv[i][6] >= 'A') // convert higher digit hexadecimal char to byte
|
||||
data_byte_value += (argv[i][6] - 'A' + 10) * 16;
|
||||
else
|
||||
data_byte_value += (argv[i][6] - '0') * 16;
|
||||
if (argv[i][7] >= 'A') // convert lower digit hexadecimal char to byte
|
||||
data_byte_value += (argv[i][7] - 'A' + 10);
|
||||
else
|
||||
data_byte_value += (argv[i][7] - '0');
|
||||
argv[i][4] = 0; // terminate string for comparison
|
||||
if (strcasecmp(argv[i], "Data") == STR_EQUAL) {
|
||||
if (transceiver_processed)
|
||||
show_transc_err_and_exit();
|
||||
frame.data[data_byte_no] = data_byte_value; // populate proper data byte
|
||||
arbitration_processed = true;
|
||||
} else {
|
||||
argv[i][4] = data_byte_no + '0'; // undo string termination
|
||||
show_invalid_option(argv[i]);
|
||||
}
|
||||
}
|
||||
// RxCount=F4 or TxCount=3A (Totallength=10)
|
||||
else if ((strlen(argv[i]) == 10) && // 'RxCounter=F4' or 'TxCounter=3A'
|
||||
(argv[i][7] == '=') && // '='
|
||||
((argv[i][8] >= '0' && argv[i][8] <= '9') || (argv[i][8] >= 'A' && argv[i][8] <= 'F')) && // first hexadecimal digit
|
||||
((argv[i][9] >= '0' && argv[i][9] <= '9') || (argv[i][9] >= 'A' && argv[i][9] <= 'F'))) { // second hexadecimal digit
|
||||
unsigned char counter_value = 0;
|
||||
if (argv[i][8] >= 'A') // convert higher digit hexadecimal char to byte
|
||||
counter_value += (argv[i][8] - 'A' + 10) * 16;
|
||||
else
|
||||
counter_value += (argv[i][8] - '0') * 16;
|
||||
if (argv[i][9] >= 'A') // convert lower digit hexadecimal char to byte
|
||||
counter_value += (argv[i][9] - 'A' + 10);
|
||||
else
|
||||
counter_value += (argv[i][9] - '0');
|
||||
argv[i][7] = 0; // terminate string for comparison
|
||||
if (strcasecmp(argv[i], "TxCount") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CNT; // generate TxCounter error
|
||||
frame.data[6] = counter_value; // populate proper data byte
|
||||
} else if (strcasecmp(argv[i], "RxCount") == STR_EQUAL) {
|
||||
frame.can_id |= CAN_ERR_CNT; // generate RxCounter error
|
||||
frame.data[7] = counter_value; // populate proper data byte
|
||||
} else {
|
||||
argv[i][7] = '='; // undo string termination
|
||||
show_invalid_option(argv[i]);
|
||||
}
|
||||
} else if (strcasecmp(argv[i], "ShowBits") == STR_EQUAL) // DEBUG helper
|
||||
show_bits = true; // Display frame as bits
|
||||
else
|
||||
show_invalid_option(argv[i]);
|
||||
}
|
||||
|
||||
if (show_bits == true) {
|
||||
printf("CAN ID = ");
|
||||
print_binary(frame.can_id);
|
||||
printf("\n");
|
||||
// printf("frame.can_dlc = %d\n", frame.can_dlc);
|
||||
printf("CAN Data = ");
|
||||
for (size_t i = 0; i < frame.can_dlc; i++)
|
||||
printf("%02X ", frame.data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// create socket
|
||||
if ((sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
|
||||
err_exit("Error while opening socket\n");
|
||||
|
||||
// set interface name
|
||||
if (strlen(argv[1]) >= IFNAMSIZ)
|
||||
err_exit("Name of CAN device '%s' is too long!\n\n", argv[1]);
|
||||
|
||||
strcpy(ifr.ifr_name, argv[1]); // can0, vcan0...
|
||||
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
|
||||
err_exit("Error setting CAN interface name %s\n", argv[1]);
|
||||
|
||||
// bind socket to the CAN interface
|
||||
addr.can_family = AF_CAN;
|
||||
addr.can_ifindex = ifr.ifr_ifindex;
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
err_exit("Error in socket bind\n");
|
||||
|
||||
// Send CAN error frame
|
||||
if (write(sock, &frame, sizeof(frame)) < 0)
|
||||
err_exit("Error writing to socket\n");
|
||||
else
|
||||
printf("CAN error frame sent\n");
|
||||
|
||||
close(sock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ static void print_usage(char *prg)
|
||||
fprintf(stderr, " -X (generate CAN XL CAN frames)\n");
|
||||
fprintf(stderr, " -R (generate RTR frames)\n");
|
||||
fprintf(stderr, " -8 (allow DLC values greater then 8 for Classic CAN frames)\n");
|
||||
fprintf(stderr, " -m (mix -e -f -b -E -R -X frames)\n");
|
||||
fprintf(stderr, " -m (mix CC [-e -R] FD [-f -b -E] XL [-X] on capable devices)\n");
|
||||
fprintf(stderr, " -I <mode> (CAN ID generation mode - see below)\n");
|
||||
fprintf(stderr, " -L <mode> (CAN data length code (dlc) generation mode - see below)\n");
|
||||
fprintf(stderr, " -D <mode> (CAN data (payload) generation mode - see below)\n");
|
||||
@@ -299,7 +299,7 @@ static int setsockopt_txtime(int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_send_one(int fd, cu_t *cu, size_t len, int timeout)
|
||||
static int do_send_one(int fd, union cfu *cu, size_t len, int timeout)
|
||||
{
|
||||
uint8_t control[CMSG_SPACE(sizeof(uint64_t))] = { 0 };
|
||||
struct iovec iov = {
|
||||
@@ -339,10 +339,9 @@ resend:
|
||||
nbytes = sendmsg(fd, &msg, 0);
|
||||
if (nbytes < 0) {
|
||||
ret = -errno;
|
||||
if (ret != -ENOBUFS) {
|
||||
perror("write");
|
||||
if (ret != -ENOBUFS)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ignore_enobufs && !timeout) {
|
||||
perror("write");
|
||||
return ret;
|
||||
@@ -455,6 +454,9 @@ int main(int argc, char **argv)
|
||||
unsigned char extended = 0;
|
||||
unsigned char canfd = 0;
|
||||
unsigned char canxl = 0;
|
||||
unsigned char mixcc = 1; /* mix default */
|
||||
unsigned char mixfd = 1; /* mix default */
|
||||
unsigned char mixxl = 1; /* mix default */
|
||||
unsigned char brs = 0;
|
||||
unsigned char esi = 0;
|
||||
unsigned char mix = 0;
|
||||
@@ -492,7 +494,7 @@ int main(int argc, char **argv)
|
||||
struct can_raw_vcid_options vcid_opts = {
|
||||
.flags = CAN_RAW_XL_VCID_TX_PASS,
|
||||
};
|
||||
static cu_t cu;
|
||||
static union cfu cu;
|
||||
int i;
|
||||
struct ifreq ifr = { 0 };
|
||||
const int enable_canfx = 1;
|
||||
@@ -574,7 +576,6 @@ int main(int argc, char **argv)
|
||||
|
||||
case 'm':
|
||||
mix = 1;
|
||||
canfd = 1; /* to switch the socket into CAN FD mode */
|
||||
view |= CANLIB_VIEW_INDENT_SFF;
|
||||
break;
|
||||
|
||||
@@ -777,34 +778,32 @@ int main(int argc, char **argv)
|
||||
&loopback, sizeof(loopback));
|
||||
}
|
||||
|
||||
if (canfd || canxl) {
|
||||
|
||||
/* check if the frame fits into the CAN netdevice */
|
||||
/* get CAN netdevice MTU for frame type capabilities */
|
||||
if (ioctl(s, SIOCGIFMTU, &ifr) < 0) {
|
||||
perror("SIOCGIFMTU");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (canfd) {
|
||||
/* ensure discrete CAN FD length values 0..8, 12, 16, 20, 24, 32, 64 */
|
||||
cu.fd.len = can_fd_dlc2len(can_fd_len2dlc(cu.fd.len));
|
||||
} else {
|
||||
/* limit fixed CAN XL data length to 64 */
|
||||
if (cu.fd.len > CANFD_MAX_DLEN)
|
||||
cu.fd.len = CANFD_MAX_DLEN;
|
||||
}
|
||||
|
||||
if (canxl && (ifr.ifr_mtu < (int)CANXL_MIN_MTU)) {
|
||||
/* check CAN XL support */
|
||||
if (ifr.ifr_mtu < (int)CANXL_MIN_MTU) {
|
||||
mixxl = 0;
|
||||
if (canxl) {
|
||||
printf("CAN interface not CAN XL capable - sorry.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (canfd && (ifr.ifr_mtu < (int)CANFD_MTU)) {
|
||||
/* check CAN FD support */
|
||||
if (ifr.ifr_mtu < (int)CANFD_MTU) {
|
||||
mixfd = 0;
|
||||
if (canfd) {
|
||||
printf("CAN interface not CAN FD capable - sorry.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ifr.ifr_mtu == (int)CANFD_MTU) {
|
||||
/* enable CAN FD on the socket */
|
||||
if (mixfd) {
|
||||
/* interface is ok - try to switch the socket into CAN FD mode */
|
||||
if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
|
||||
&enable_canfx, sizeof(enable_canfx))){
|
||||
@@ -813,7 +812,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (ifr.ifr_mtu >= (int)CANXL_MIN_MTU) {
|
||||
/* enable CAN XL on the socket */
|
||||
if (mixxl) {
|
||||
/* interface is ok - try to switch the socket into CAN XL mode */
|
||||
if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_XL_FRAMES,
|
||||
&enable_canfx, sizeof(enable_canfx))){
|
||||
@@ -828,6 +828,16 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* sanitize given values */
|
||||
if (canfd || canxl) {
|
||||
if (canfd) {
|
||||
/* ensure discrete CAN FD length values 0..8, 12, 16, 20, 24, 32, 64 */
|
||||
cu.fd.len = can_fd_dlc2len(can_fd_len2dlc(cu.fd.len));
|
||||
} else {
|
||||
/* limit fixed CAN XL data length to 64 */
|
||||
if (cu.fd.len > CANFD_MAX_DLEN)
|
||||
cu.fd.len = CANFD_MAX_DLEN;
|
||||
}
|
||||
} else {
|
||||
/* sanitize Classical CAN 2.0 frame length */
|
||||
if (len8_dlc) {
|
||||
@@ -987,12 +997,12 @@ int main(int argc, char **argv)
|
||||
rnd = random();
|
||||
|
||||
if (xl_flags_mode == MODE_RANDOM) {
|
||||
cu.xl.flags = rnd & CANXL_SEC;
|
||||
cu.xl.flags = rnd & (CANXL_SEC | CANXL_RRS);
|
||||
} else if (xl_flags_mode == MODE_FIX) {
|
||||
cu.xl.flags = xl_flags;
|
||||
} else if (xl_flags_mode == MODE_INCREMENT) {
|
||||
xl_flags ^= CANXL_SEC;
|
||||
cu.xl.flags = (xl_flags & CANXL_SEC);
|
||||
xl_flags++;
|
||||
cu.xl.flags = (xl_flags & (CANXL_SEC | CANXL_RRS));
|
||||
}
|
||||
|
||||
/* mark CAN XL frame */
|
||||
@@ -1040,8 +1050,31 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
ret = do_send_one(s, &cu, mtu, polltimeout);
|
||||
if (ret)
|
||||
if ((ret == -EINVAL) && mix) {
|
||||
/* mix mode: disable unsupported CAN frame type */
|
||||
switch (mtu) {
|
||||
case CAN_MTU:
|
||||
mixcc = 0;
|
||||
break;
|
||||
case CANFD_MTU:
|
||||
mixfd = 0;
|
||||
break;
|
||||
case CANXL_MTU:
|
||||
mixxl = 0;
|
||||
break;
|
||||
default:
|
||||
printf ("mix mode: unknown MTU");
|
||||
return 1;
|
||||
}
|
||||
if (!mixcc && !mixfd && !mixxl) {
|
||||
printf ("mix mode: no valid CAN frame types\n");
|
||||
return 1;
|
||||
}
|
||||
} else if (ret) {
|
||||
/* other error than -ENOBUFS and -EINVAL */
|
||||
perror("write");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (burst_sent_count >= burst_count)
|
||||
burst_sent_count = 0;
|
||||
@@ -1082,18 +1115,41 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (mix) {
|
||||
canfd = 0;
|
||||
canxl = 0;
|
||||
i = random();
|
||||
extended = i & 1;
|
||||
if (mixfd) {
|
||||
canfd = i & 2;
|
||||
if (canfd) {
|
||||
brs = i & 4;
|
||||
esi = i & 8;
|
||||
}
|
||||
/* generate CAN XL traffic if the interface is capable */
|
||||
if (ifr.ifr_mtu >= (int)CANXL_MIN_MTU)
|
||||
canxl = ((i & 96) == 96);
|
||||
|
||||
rtr_frame = ((i & 24) == 24); /* reduce RTR frames to 1/4 */
|
||||
}
|
||||
if (mixxl) {
|
||||
if (mixfd)
|
||||
canxl = ((i & 96) == 96); /* 1/4 */
|
||||
else
|
||||
canxl = ((i & 32) == 32); /* 1/2 */
|
||||
}
|
||||
if (mixcc) {
|
||||
rtr_frame = ((i & 24) == 24); /* reduce RTR to 1/4 */
|
||||
} else {
|
||||
/* no CC frames allowed - CAN XL-only mode? */
|
||||
if (!canxl && !canfd) {
|
||||
/* force XL or FD frames */
|
||||
if (mixxl)
|
||||
canxl = 1;
|
||||
else if (mixfd) {
|
||||
canfd = 1;
|
||||
brs = i & 4;
|
||||
esi = i & 8;
|
||||
} else {
|
||||
printf ("mix mode: no valid CAN frame types\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ static int parse_mod(char *optarg, struct modattr *modmsg)
|
||||
ptr = optarg;
|
||||
nptr = strchr(ptr, ':');
|
||||
|
||||
if ((nptr - ptr > 3) || (nptr - ptr == 0))
|
||||
if (!nptr || (nptr - ptr > 3) || (nptr - ptr == 0))
|
||||
return 1;
|
||||
|
||||
if (!strncmp(ptr, "AND", 3))
|
||||
@@ -355,7 +355,7 @@ static int parse_mod(char *optarg, struct modattr *modmsg)
|
||||
ptr = nptr+1;
|
||||
nptr = strchr(ptr, ':');
|
||||
|
||||
if ((nptr - ptr > 3) || (nptr - ptr == 0))
|
||||
if (!nptr || (nptr - ptr > 3) || (nptr - ptr == 0))
|
||||
return 3;
|
||||
|
||||
modmsg->modtype = 0;
|
||||
@@ -406,7 +406,7 @@ static int parse_fdmod(char *optarg, struct fdmodattr *modmsg)
|
||||
ptr = optarg;
|
||||
nptr = strchr(ptr, ':');
|
||||
|
||||
if ((nptr - ptr > 3) || (nptr - ptr == 0))
|
||||
if (!nptr || (nptr - ptr > 3) || (nptr - ptr == 0))
|
||||
return 1;
|
||||
|
||||
if (!strncmp(ptr, "AND", 3))
|
||||
@@ -423,7 +423,7 @@ static int parse_fdmod(char *optarg, struct fdmodattr *modmsg)
|
||||
ptr = nptr+1;
|
||||
nptr = strchr(ptr, ':');
|
||||
|
||||
if ((nptr - ptr > 4) || (nptr - ptr == 0))
|
||||
if (!nptr || (nptr - ptr > 4) || (nptr - ptr == 0))
|
||||
return 3;
|
||||
|
||||
modmsg->modtype = 0;
|
||||
|
||||
+26
-4
@@ -177,7 +177,8 @@ int main(int argc, char **argv)
|
||||
sigset_t sigset;
|
||||
fd_set rdfs;
|
||||
int s[MAXDEV];
|
||||
int socki, accsocket;
|
||||
int socki;
|
||||
int accsocket = -1;
|
||||
canid_t mask[MAXDEV] = {0};
|
||||
canid_t value[MAXDEV] = {0};
|
||||
int inv_filter[MAXDEV] = {0};
|
||||
@@ -191,7 +192,7 @@ int main(int argc, char **argv)
|
||||
.rx_vcid_mask = 0,
|
||||
};
|
||||
struct can_filter rfilter;
|
||||
static cu_t cu; /* union for CAN CC/FD/XL frames */
|
||||
static union cfu cu; /* union for CAN CC/FD/XL frames */
|
||||
const int canfx_on = 1;
|
||||
int nbytes, i, j;
|
||||
struct ifreq ifr;
|
||||
@@ -286,7 +287,7 @@ int main(int argc, char **argv)
|
||||
inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
inaddr.sin_port = htons(port);
|
||||
|
||||
while(bind(socki, (struct sockaddr*)&inaddr, sizeof(inaddr)) < 0) {
|
||||
while(running && bind(socki, (struct sockaddr*)&inaddr, sizeof(inaddr)) < 0) {
|
||||
struct timespec f = {
|
||||
.tv_nsec = 100 * 1000 * 1000,
|
||||
};
|
||||
@@ -295,18 +296,28 @@ int main(int argc, char **argv)
|
||||
nanosleep(&f, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if loop exited due to signal (during nanosleep) rather than
|
||||
* successful bind.
|
||||
*/
|
||||
if (!running) {
|
||||
close(socki);
|
||||
return 128 + signal_num;
|
||||
}
|
||||
|
||||
if (listen(socki, 3) != 0) {
|
||||
perror("listen");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while(1) {
|
||||
while(running) {
|
||||
accsocket = accept(socki, (struct sockaddr*)&clientaddr, &sin_size);
|
||||
if (accsocket > 0) {
|
||||
//printf("accepted\n");
|
||||
if (!fork())
|
||||
break;
|
||||
close(accsocket);
|
||||
accsocket = -1;
|
||||
}
|
||||
else if (errno != EINTR) {
|
||||
perror("accept");
|
||||
@@ -314,6 +325,17 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if loop exited due to signal (accept returned EINTR) rather
|
||||
* than successful fork
|
||||
*/
|
||||
if (!running) {
|
||||
if (accsocket > 0)
|
||||
close(accsocket);
|
||||
close(socki);
|
||||
return 128 + signal_num;
|
||||
}
|
||||
|
||||
for (i=0; i<currmax; i++) {
|
||||
|
||||
pr_debug("open %d '%s' m%08X v%08X i%d e%d.\n",
|
||||
|
||||
+23
-11
@@ -68,7 +68,7 @@
|
||||
#endif
|
||||
|
||||
#define DEVSZ 22 /* IFNAMSZ + 6 */
|
||||
#define TIMESZ sizeof("(1345212884.318850) ")
|
||||
#define TIMESZ sizeof("(1345212884.318850123) ")
|
||||
#define BUFSZ (TIMESZ + DEVSZ + AFRSZ)
|
||||
|
||||
/* adapt sscanf() functions below on error */
|
||||
@@ -261,7 +261,7 @@ int main(int argc, char **argv)
|
||||
struct can_raw_vcid_options vcid_opts = {
|
||||
.flags = CAN_RAW_XL_VCID_TX_PASS,
|
||||
};
|
||||
static cu_t cu;
|
||||
static union cfu cu;
|
||||
static struct timeval today_tv, log_tv, last_log_tv, diff_tv;
|
||||
struct timespec sleep_ts;
|
||||
int s; /* CAN_RAW socket */
|
||||
@@ -452,14 +452,20 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
log_tv.tv_sec = sec;
|
||||
log_tv.tv_usec = usec;
|
||||
|
||||
/*
|
||||
* ensure the fractions of seconds are 6 decimal places long to catch
|
||||
* ensure the fractions of seconds are 6 or 9 decimal places long to catch
|
||||
* 3rd party or handcrafted logfiles that treat the timestamp as float
|
||||
*/
|
||||
if (strchr(buf, ')') - strchr(buf, '.') != 7) {
|
||||
fprintf(stderr, "timestamp format in logfile requires 6 decimal places\n");
|
||||
switch (strchr(buf, ')') - strchr(buf, '.')) {
|
||||
case 7: //6
|
||||
log_tv.tv_usec = usec;
|
||||
break;
|
||||
case 10: //9
|
||||
log_tv.tv_usec = usec / 1000;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "timestamp format in logfile requires 6 or 9 decimal places\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -541,19 +547,25 @@ int main(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "(%llu.%llu) %s %s", &sec, &usec, device, afrbuf) != 4) {
|
||||
if (sscanf(buf, "(%llu.%llu) %21s %6299s", &sec, &usec, device, afrbuf) != 4) {
|
||||
fprintf(stderr, "incorrect line format in logfile\n");
|
||||
return 1;
|
||||
}
|
||||
log_tv.tv_sec = sec;
|
||||
log_tv.tv_usec = usec;
|
||||
|
||||
/*
|
||||
* ensure the fractions of seconds are 6 decimal places long to catch
|
||||
* ensure the fractions of seconds are 6 or 9 decimal places long to catch
|
||||
* 3rd party or handcrafted logfiles that treat the timestamp as float
|
||||
*/
|
||||
if (strchr(buf, ')') - strchr(buf, '.') != 7) {
|
||||
fprintf(stderr, "timestamp format in logfile requires 6 decimal places\n");
|
||||
switch (strchr(buf, ')') - strchr(buf, '.')) {
|
||||
case 7: //6
|
||||
log_tv.tv_usec = usec;
|
||||
break;
|
||||
case 10: //9
|
||||
log_tv.tv_usec = usec / 1000;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "timestamp format in logfile requires 6 or 9 decimal places\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,10 @@ static void print_usage(char *prg)
|
||||
"_{dlc}:\n"
|
||||
" an optional 9..F data length code value when payload length is 8\n"
|
||||
"<flags>:\n"
|
||||
" a single ASCII Hex value (0 .. F) which defines canfd_frame.flags\n"
|
||||
" a single ASCII Hex value (0 .. F) which defines canfd_frame.flags:\n"
|
||||
" %x CANFD_BRS\n"
|
||||
" %x CANFD_ESI\n"
|
||||
" %x CANFD_FDF\n"
|
||||
"\n"
|
||||
"<vcid>:\n"
|
||||
" 2 hex chars - virtual CAN network identifier (00 .. FF)\n"
|
||||
@@ -100,7 +103,8 @@ static void print_usage(char *prg)
|
||||
" 1F334455#1122334455667788_B / 123#R / 00000123#R3 / 333#R8_E /\n"
|
||||
" 45123#81:00:12345678#11223344.556677 / 00242#81:07:40000123#112233\n"
|
||||
"\n",
|
||||
prg, prg);
|
||||
prg, prg,
|
||||
CANFD_BRS, CANFD_ESI, CANFD_FDF);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -113,7 +117,7 @@ int main(int argc, char **argv)
|
||||
struct can_raw_vcid_options vcid_opts = {
|
||||
.flags = CAN_RAW_XL_VCID_TX_PASS,
|
||||
};
|
||||
static cu_t cu;
|
||||
static union cfu cu;
|
||||
struct ifreq ifr;
|
||||
|
||||
/* check command line options */
|
||||
|
||||
@@ -671,6 +671,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
while (1) {
|
||||
ret = isobusfs_cli_process_events_and_tasks(priv);
|
||||
if (ret == ISOBUSFS_CLI_RET_EXIT) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#define ISOBUSFS_CLI_MAX_EPOLL_EVENTS 10
|
||||
#define ISOBUSFS_CLI_DEFAULT_WAIT_TIMEOUT_MS 1000 /* ms */
|
||||
|
||||
/* internal return codes, not errno values */
|
||||
#define ISOBUSFS_CLI_RET_EXIT 1
|
||||
|
||||
enum isobusfs_cli_state {
|
||||
ISOBUSFS_CLI_STATE_CONNECTING,
|
||||
ISOBUSFS_CLI_STATE_IDLE,
|
||||
|
||||
@@ -43,7 +43,7 @@ int isobusfs_cli_ccd_req(struct isobusfs_priv *priv, const char *name,
|
||||
req->tan = isobusfs_cli_get_next_tan(priv);
|
||||
|
||||
memcpy(&req->name[0], name, name_len);
|
||||
req->name_len = name_len;
|
||||
req->name_len = htole16(name_len);
|
||||
|
||||
if (padding_size) {
|
||||
/* Fill the rest of the res structure with 0xff */
|
||||
|
||||
@@ -308,7 +308,7 @@ int isobusfs_cli_fa_of_req(struct isobusfs_priv *priv, const char *name,
|
||||
req->tan = isobusfs_cli_get_next_tan(priv);
|
||||
req->flags = flags;
|
||||
memcpy(&req->name[0], name, name_len);
|
||||
req->name_len = name_len;
|
||||
req->name_len = htole16(name_len);
|
||||
|
||||
if (padding_size) {
|
||||
/* Fill the rest of the res structure with 0xff */
|
||||
|
||||
+70
-23
@@ -16,6 +16,12 @@
|
||||
#define MAX_COMMAND_LENGTH 256
|
||||
|
||||
#define MAX_DISPLAY_FILENAME_LENGTH 100
|
||||
/*
|
||||
* ISO 11783-13:2021 B.21 minimal directory entry payload size in bytes:
|
||||
* 1 (name length) + 1 (min name byte) + 1 (attributes) +
|
||||
* 2 (date) + 2 (time) + 4 (size).
|
||||
*/
|
||||
#define ISOBUSFS_MIN_DIR_ENTRY_SIZE (1 + 1 + 1 + 2 + 2 + 4)
|
||||
|
||||
struct command_mapping {
|
||||
const char *command;
|
||||
@@ -66,8 +72,8 @@ static int cmd_help(struct isobusfs_priv *priv, const char *options)
|
||||
static int cmd_exit(struct isobusfs_priv *priv, const char *options)
|
||||
{
|
||||
pr_int("exit interactive mode\n");
|
||||
/* Return -EINTR to indicate the program should exit */
|
||||
return -EINTR;
|
||||
|
||||
return ISOBUSFS_CLI_RET_EXIT;
|
||||
}
|
||||
|
||||
static int cmd_dmesg(struct isobusfs_priv *priv, const char *options)
|
||||
@@ -510,8 +516,12 @@ isobusfs_cli_ls_handle_open_dir_sent(struct isobusfs_priv *priv,
|
||||
|
||||
ctx->handle = res->handle;
|
||||
|
||||
ctx->offset = 0;
|
||||
ctx->entry_count = 0;
|
||||
|
||||
ret = isobusfs_cli_send_and_register_fa_sf_event(priv, ctx->handle,
|
||||
0, ctx->entry_count,
|
||||
ISOBUSFS_FA_SEEK_SET,
|
||||
ctx->offset,
|
||||
cb, ctx);
|
||||
if (ret)
|
||||
pr_int("Failed to send seek file request: %i\n", ret);
|
||||
@@ -530,7 +540,7 @@ isobusfs_cli_ls_handle_seek_dir_sent(struct isobusfs_priv *priv,
|
||||
{
|
||||
isobusfs_event_callback cb = isobusfs_cli_ls_event_callback;
|
||||
struct isobusfs_fa_seekf_res *res =
|
||||
(struct isobusfs_fa_seekf_res *)msg;
|
||||
(struct isobusfs_fa_seekf_res *)msg->buf;
|
||||
uint16_t count;
|
||||
int ret;
|
||||
|
||||
@@ -543,8 +553,10 @@ isobusfs_cli_ls_handle_seek_dir_sent(struct isobusfs_priv *priv,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* set max possible number fitting in to 16bits */
|
||||
count = UINT16_MAX;
|
||||
/* ISO 11783-13:2021 C.3.5.2: count is number of directory entries. */
|
||||
count = ISOBUSFS_MAX_DATA_LENGH / ISOBUSFS_MIN_DIR_ENTRY_SIZE;
|
||||
if (!count)
|
||||
count = 1;
|
||||
ctx->request_count = count;
|
||||
|
||||
ret = isobusfs_cli_send_and_register_fa_rf_event(priv, ctx->handle,
|
||||
@@ -616,8 +628,8 @@ static bool isobusfs_cli_extract_directory_entry(const uint8_t *buffer,
|
||||
uint16_t *file_time,
|
||||
uint32_t *file_size)
|
||||
{
|
||||
size_t entry_total_len, copy_len;
|
||||
uint8_t filename_length;
|
||||
size_t entry_total_len;
|
||||
|
||||
if (*pos + 2 > buffer_length) {
|
||||
pr_int("Error: Incomplete data in buffer\n");
|
||||
@@ -633,8 +645,14 @@ static bool isobusfs_cli_extract_directory_entry(const uint8_t *buffer,
|
||||
}
|
||||
|
||||
(*pos)++;
|
||||
strncpy(filename, (const char *)buffer + *pos, filename_length);
|
||||
filename[filename_length] = '\0';
|
||||
|
||||
if (filename_length > ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH)
|
||||
copy_len = ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH;
|
||||
else
|
||||
copy_len = filename_length;
|
||||
|
||||
strncpy(filename, (const char *)buffer + *pos, copy_len);
|
||||
filename[copy_len] = '\0';
|
||||
*pos += filename_length;
|
||||
if (filename_length > MAX_DISPLAY_FILENAME_LENGTH) {
|
||||
/* Truncate the filename and replace the last character
|
||||
@@ -688,15 +706,17 @@ isobusfs_cli_print_directory_entry(struct isobusfs_cli_ls_context *ctx,
|
||||
static void
|
||||
isobusfs_cli_print_directory_entries(struct isobusfs_cli_ls_context *ctx,
|
||||
const uint8_t *buffer,
|
||||
size_t buffer_length)
|
||||
size_t buffer_length,
|
||||
uint16_t max_entries)
|
||||
{
|
||||
char filename[ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH + 1];
|
||||
uint16_t file_date, file_time;
|
||||
uint32_t file_size;
|
||||
uint8_t attributes;
|
||||
size_t pos = 0;
|
||||
uint16_t entries = 0;
|
||||
|
||||
while (pos < buffer_length) {
|
||||
while (pos < buffer_length && entries < max_entries) {
|
||||
if (!isobusfs_cli_extract_directory_entry(buffer, buffer_length,
|
||||
&pos, filename,
|
||||
&attributes,
|
||||
@@ -709,6 +729,7 @@ isobusfs_cli_print_directory_entries(struct isobusfs_cli_ls_context *ctx,
|
||||
file_date, file_time,
|
||||
file_size);
|
||||
ctx->entry_count++;
|
||||
entries++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,26 +742,42 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||
(struct isobusfs_read_file_response *)msg->buf;
|
||||
size_t buffer_length = msg->len - sizeof(*res);
|
||||
isobusfs_event_callback cb;
|
||||
size_t entries_before;
|
||||
size_t entries_in_message;
|
||||
uint16_t count;
|
||||
int ret;
|
||||
|
||||
pr_debug("< rx: Read File Response. Error code: %i", res->error_code);
|
||||
|
||||
if (isobusfs_cli_int_is_error(priv, 0, res->error_code, res->tan))
|
||||
goto error;
|
||||
|
||||
count = le16toh(res->count);
|
||||
if (count && count != buffer_length) {
|
||||
pr_int("Buffer length mismatch: %u != %zu\n", count,
|
||||
buffer_length);
|
||||
goto error;
|
||||
if (count && buffer_length) {
|
||||
entries_before = ctx->entry_count;
|
||||
isobusfs_cli_print_directory_entries(ctx, res->data,
|
||||
buffer_length, count);
|
||||
entries_in_message = ctx->entry_count - entries_before;
|
||||
} else {
|
||||
entries_in_message = 0;
|
||||
}
|
||||
|
||||
if (count)
|
||||
isobusfs_cli_print_directory_entries(ctx, res->data,
|
||||
buffer_length);
|
||||
if (count != entries_in_message) {
|
||||
pr_warn("Directory entry count mismatch: server sent %u, parsed %zu\n",
|
||||
count, entries_in_message);
|
||||
/* Continue processing if we parsed at least some entries.
|
||||
* Strict validation would reject valid responses if parsing
|
||||
* fails partway through due to corruption.
|
||||
*/
|
||||
if (entries_in_message == 0 && count > 0) {
|
||||
pr_int("Error: Failed to parse any directory entries\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
cb = isobusfs_cli_ls_event_callback;
|
||||
if (count) {
|
||||
|
||||
if (res->error_code == ISOBUSFS_ERR_END_OF_FILE) {
|
||||
ret = isobusfs_cli_send_and_register_fa_cf_event(priv,
|
||||
ctx->handle,
|
||||
cb, ctx);
|
||||
@@ -750,8 +787,20 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||
}
|
||||
|
||||
ctx->state = ISOBUSFS_CLI_LS_STATE_CLOSE_DIR_SENT;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
pr_int("Error: zero-length read without EOF\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Directory seek offset is entry index, not byte offset.
|
||||
* Server side seek rewinds and skips "offset" entries.
|
||||
*/
|
||||
ctx->offset = ctx->entry_count;
|
||||
|
||||
ret = isobusfs_cli_send_and_register_fa_sf_event(priv,
|
||||
ctx->handle, 0,
|
||||
ctx->offset,
|
||||
@@ -760,11 +809,9 @@ isobusfs_cli_ls_handle_read_dir_sent(struct isobusfs_priv *priv,
|
||||
pr_int("Failed to send seek file request: %i\n", ret);
|
||||
|
||||
ctx->state = ISOBUSFS_CLI_LS_STATE_SEEK_DIR_SENT;
|
||||
}
|
||||
|
||||
return;
|
||||
error:
|
||||
|
||||
ctx->state = ISOBUSFS_CLI_LS_STATE_ERROR;
|
||||
}
|
||||
|
||||
@@ -1077,7 +1124,7 @@ int isobusfs_cli_interactive(struct isobusfs_priv *priv)
|
||||
}
|
||||
}
|
||||
|
||||
pr_int("unknown comand\n");
|
||||
pr_int("unknown command\n");
|
||||
isobusfs_cli_promt(priv);
|
||||
} else {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
|
||||
@@ -211,7 +211,7 @@ static struct isobusfs_cli_test_dir_path test_dir_patterns[] = {
|
||||
{ "~tilde_dir", true },
|
||||
/* expected result \\vol1\dir1\~\ */
|
||||
{ "\\\\vol1\\dir1\\~", true },
|
||||
/* expected result \\vol1\~\ not clear if it is manufacture speficic dir */
|
||||
/* expected result \\vol1\~\ not clear if it is manufacture specific dir */
|
||||
{ "\\~\\", true },
|
||||
/* expected result \\~\ */
|
||||
{ "\\\\~\\", false },
|
||||
@@ -603,23 +603,24 @@ struct isobusfs_cli_test_rf_path {
|
||||
uint8_t flags;
|
||||
uint32_t offset;
|
||||
uint32_t read_size;
|
||||
uint32_t expected_size;
|
||||
bool expect_pass;
|
||||
};
|
||||
|
||||
static struct isobusfs_cli_test_rf_path test_rf_patterns[] = {
|
||||
/* expected result \\vol1\dir1\dir2\ */
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 0, 0, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 0, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 1, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 2, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 3, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, 8, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, 8 * 100, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 100, 8 * 100, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, ISOBUSFS_MAX_DATA_LENGH, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, (ISOBUSFS_MAX_DATA_LENGH & ~3) + 16, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, ISOBUSFS_MAX_DATA_LENGH + 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, -1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 0, 0, 0, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 0, 1, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 1, 1, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 2, 1, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1k", 0, 3, 1, 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, 8, 8, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, 8 * 100, 8 * 100, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 100, 8 * 100, 8 * 100, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, ISOBUSFS_MAX_DATA_LENGH, ISOBUSFS_MAX_DATA_LENGH, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, (ISOBUSFS_MAX_DATA_LENGH & ~3) + 16, (ISOBUSFS_MAX_DATA_LENGH & ~3) + 16, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, ISOBUSFS_MAX_DATA_LENGH + 1, ISOBUSFS_MAX_DATA_LENGH + 1, true },
|
||||
{ "\\\\vol1\\dir1\\dir2\\file1m", 0, 0, UINT32_MAX, 1024 * 1024, true },
|
||||
};
|
||||
|
||||
size_t current_rf_pattern_test;
|
||||
@@ -678,7 +679,8 @@ static int isobusfs_cli_test_rf_req(struct isobusfs_priv *priv, bool *complete)
|
||||
&test_rf_patterns[current_rf_pattern_test];
|
||||
uint32_t actual_sum, expected_sum;
|
||||
struct timespec current_time;
|
||||
ssize_t remaining_size, read_size;
|
||||
int64_t remaining_size;
|
||||
ssize_t read_size;
|
||||
int ret;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
|
||||
@@ -801,16 +803,23 @@ static int isobusfs_cli_test_rf_req(struct isobusfs_priv *priv, bool *complete)
|
||||
free(priv->read_data);
|
||||
priv->read_data = NULL;
|
||||
|
||||
remaining_size = (tp->offset + tp->read_size) -
|
||||
(priv->read_offset + priv->read_data_len);
|
||||
pr_debug("remaining_size: %zd, read_offset: %zu, read_data_len: %zu, test read size: %zu, test offset %zu",
|
||||
remaining_size, priv->read_offset, priv->read_data_len,
|
||||
remaining_size = ((int64_t)tp->offset + tp->read_size) -
|
||||
((int64_t)priv->read_offset + priv->read_data_len);
|
||||
pr_debug("remaining_size: %lld, read_offset: %u, read_data_len: %zu, test read size: %u, test offset %u",
|
||||
(long long)remaining_size, priv->read_offset, priv->read_data_len,
|
||||
tp->read_size, tp->offset);
|
||||
if (remaining_size < 0) {
|
||||
pr_err("pattern test failed: %s. Read size is too big",
|
||||
tp->path_name);
|
||||
ret = -EINVAL;
|
||||
goto test_fail;
|
||||
} else if (remaining_size == 0) {
|
||||
if (tp->read_size > tp->expected_size) {
|
||||
pr_err("read test failed: %s. Server returned more data than expected file size (%u > %u)",
|
||||
tp->path_name, tp->read_size, tp->expected_size);
|
||||
ret = -EINVAL;
|
||||
goto test_fail;
|
||||
}
|
||||
} else if (remaining_size > 0 && priv->read_data_len != 0) {
|
||||
priv->read_offset += priv->read_data_len;
|
||||
|
||||
@@ -825,12 +834,21 @@ static int isobusfs_cli_test_rf_req(struct isobusfs_priv *priv, bool *complete)
|
||||
goto test_fail;
|
||||
test_start_time = current_time;
|
||||
break;
|
||||
} else if (remaining_size > 0 && priv->read_data_len == 0 && tp->expect_pass) {
|
||||
pr_err("read test failed: %s. Read size is zero, but expected more data: %zd",
|
||||
tp->path_name, remaining_size);
|
||||
} else if (remaining_size > 0 && priv->read_data_len == 0) {
|
||||
if (tp->read_size > tp->expected_size &&
|
||||
tp->read_size - remaining_size == tp->expected_size) {
|
||||
/* this is acceptable case when read size
|
||||
* is larger than actual file size
|
||||
*/
|
||||
pr_info("read test passed: %s. Reached end of file as expected.",
|
||||
tp->path_name);
|
||||
} else {
|
||||
pr_err("read test failed: %s. Server returned zero bytes, but expected more data: %lld",
|
||||
tp->path_name, (long long)remaining_size);
|
||||
ret = -EINVAL;
|
||||
goto test_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* fall troth */
|
||||
case ISOBUSFS_CLI_STATE_TEST_CLEANUP:
|
||||
|
||||
@@ -611,7 +611,7 @@ int isobusfs_cmn_connect_socket(int sock, struct sockaddr_can *addr)
|
||||
}
|
||||
|
||||
/* FIXME: linger is currently not supported by the kernel J1939 stack
|
||||
* but it would be nice to have it. Especially if we wont to stop sending
|
||||
* but it would be nice to have it. Especially if we won't to stop sending
|
||||
* messages on a socket when the connection is lost.
|
||||
*/
|
||||
int isobusfs_cmn_set_linger(int sock)
|
||||
|
||||
@@ -39,7 +39,7 @@ struct isobusfs_cm_fss {
|
||||
uint8_t status;
|
||||
uint8_t num_open_files;
|
||||
uint8_t reserved[5];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.1.3 Client Connection Maintenance
|
||||
@@ -58,7 +58,7 @@ struct isobusfs_cm_ccm {
|
||||
uint8_t fs_function;
|
||||
uint8_t version;
|
||||
uint8_t reserved[6];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.1.4 Get File Server Properties
|
||||
@@ -75,7 +75,7 @@ struct isobusfs_cm_ccm {
|
||||
struct isobusfs_cm_get_fs_props_req {
|
||||
uint8_t fs_function;
|
||||
uint8_t reserved[7];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* File Server Capabilities */
|
||||
/* server support removable volumes */
|
||||
@@ -104,7 +104,7 @@ struct isobusfs_cm_get_fs_props_resp {
|
||||
uint8_t max_open_files;
|
||||
uint8_t fs_capabilities;
|
||||
uint8_t reserved[4];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define ISOBUSFS_VOL_MODE_PREP_TO_REMOVE BIT(1)
|
||||
#define ISOBUSFS_VOL_MODE_USED_BY_CLIENT BIT(0)
|
||||
@@ -130,7 +130,7 @@ struct isobusfs_cm_vol_stat_req {
|
||||
uint8_t volume_mode;
|
||||
__le16 name_len;
|
||||
char name[ISOBUSFS_MAX_VOLUME_NAME_LENGTH];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
enum isobusfs_vol_status {
|
||||
@@ -169,7 +169,7 @@ struct isobusfs_cm_vol_stat_res {
|
||||
uint8_t error_code;
|
||||
__le16 name_len;
|
||||
char name[ISOBUSFS_MAX_VOLUME_NAME_LENGTH];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
#endif /* ISOBUSFS_CMN_CM_H */
|
||||
|
||||
@@ -23,7 +23,7 @@ int isobusfs_cmn_dh_validate_dir_path(const char *path, bool writable)
|
||||
ret = access(path, mode);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
pr_err("failed to acces path %s, for read %s. %s", path,
|
||||
pr_err("failed to access path %s, for read %s. %s", path,
|
||||
writable ? "and write" : "", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ struct isobusfs_dh_get_cd_req {
|
||||
uint8_t fs_function;
|
||||
uint8_t tan;
|
||||
uint8_t reserved[6];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.2.2.3 Get Current Directory Response
|
||||
@@ -50,7 +50,7 @@ struct isobusfs_dh_get_cd_res {
|
||||
__le32 free_space;
|
||||
__le16 name_len;
|
||||
uint8_t name[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.2.3.2 Change Current Directory Request
|
||||
@@ -71,7 +71,7 @@ struct isobusfs_dh_ccd_req {
|
||||
uint8_t tan;
|
||||
__le16 name_len;
|
||||
uint8_t name[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.2.3.3 Change Current Directory Response
|
||||
@@ -101,7 +101,7 @@ struct isobusfs_dh_ccd_res {
|
||||
uint8_t tan;
|
||||
uint8_t error_code;
|
||||
uint8_t reserved[5];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
#endif /* _ISOBUSFS_CMN_DH_H */
|
||||
|
||||
+11
-11
@@ -105,7 +105,7 @@ struct isobusfs_fa_openf_req {
|
||||
uint8_t flags;
|
||||
__le16 name_len;
|
||||
uint8_t name[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.3.3 Open File Response
|
||||
@@ -141,7 +141,7 @@ struct isobusfs_fa_openf_res {
|
||||
uint8_t handle;
|
||||
uint8_t attributes;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/* B.17 Position mode */
|
||||
/* From the beginning of the file */
|
||||
@@ -168,7 +168,7 @@ struct isobusfs_fa_seekf_req {
|
||||
uint8_t handle;
|
||||
uint8_t position_mode;
|
||||
__le32 offset;
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.4.2 Seek File Response
|
||||
@@ -195,7 +195,7 @@ struct isobusfs_fa_seekf_res {
|
||||
uint8_t error_code;
|
||||
uint8_t reserved;
|
||||
__le32 position;
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.5.2 Read File Request
|
||||
@@ -220,7 +220,7 @@ struct isobusfs_fa_readf_req {
|
||||
uint8_t handle;
|
||||
__le16 count;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.5.3 Read File Response (Handle-referenced file)
|
||||
@@ -250,7 +250,7 @@ struct isobusfs_read_file_response {
|
||||
uint8_t error_code;
|
||||
__le16 count;
|
||||
uint8_t data[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.5.4 Read Directory Response (Handle-referenced directory)
|
||||
@@ -280,7 +280,7 @@ struct isobusfs_read_dir_response {
|
||||
uint8_t error_code;
|
||||
__le16 count;
|
||||
uint8_t data[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.6.2 Write File Request
|
||||
@@ -303,7 +303,7 @@ struct isobusfs_write_file_request {
|
||||
uint8_t handle;
|
||||
__le16 count;
|
||||
uint8_t data[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.6.3 Write File Response
|
||||
@@ -333,7 +333,7 @@ struct isobusfs_write_file_response {
|
||||
uint8_t error_code;
|
||||
__le16 count;
|
||||
uint8_t reserved[3];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.7.1 Close File Request
|
||||
@@ -354,7 +354,7 @@ struct isobusfs_close_file_request {
|
||||
uint8_t tan;
|
||||
uint8_t handle;
|
||||
uint8_t reserved[5];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.3.7.2 Close File Response
|
||||
@@ -378,7 +378,7 @@ struct isobusfs_close_file_res {
|
||||
uint8_t tan;
|
||||
uint8_t error_code;
|
||||
uint8_t reserved[6];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
#endif /* _LINUX_ISOBUS_FS_H */
|
||||
|
||||
+10
-10
@@ -32,7 +32,7 @@ struct isobusfs_move_file_request {
|
||||
/* Variable length data follows */
|
||||
/* uint8_t src_path[]; */
|
||||
/* uint8_t dst_path[]; */
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.2.3 Move File Response
|
||||
@@ -64,7 +64,7 @@ struct isobusfs_move_file_response {
|
||||
uint8_t tan;
|
||||
uint8_t error_code;
|
||||
uint8_t reserved[5];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.3.1 Delete File Request
|
||||
@@ -87,7 +87,7 @@ struct isobusfs_delete_file_request {
|
||||
uint8_t mode;
|
||||
__le16 path_len;
|
||||
uint8_t path[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
|
||||
/**
|
||||
@@ -118,7 +118,7 @@ struct isobusfs_delete_file_response {
|
||||
uint8_t tan;
|
||||
uint8_t error_code;
|
||||
uint8_t reserved[5];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.4.2 Get File Attributes Request
|
||||
@@ -139,7 +139,7 @@ struct isobusfs_get_file_attributes_request {
|
||||
uint8_t tan;
|
||||
__le16 pathname_len;
|
||||
uint8_t pathname[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.4.3 Get File Attributes Response
|
||||
@@ -171,7 +171,7 @@ struct isobusfs_get_file_attributes_response {
|
||||
uint8_t error_code;
|
||||
uint8_t attributes;
|
||||
__le32 file_size;
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.5.2 Set File Attributes Request
|
||||
@@ -194,7 +194,7 @@ struct isobusfs_set_file_attributes_request {
|
||||
uint8_t attributes;
|
||||
__le16 name_length;
|
||||
uint8_t name[]; /* Variable length */
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.5.3 Set File Attributes Response
|
||||
@@ -225,7 +225,7 @@ struct isobusfs_set_file_attributes_response {
|
||||
uint8_t tan;
|
||||
uint8_t error_code;
|
||||
uint8_t reserved[5];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.6.2 Get File Date & Time Request
|
||||
@@ -246,7 +246,7 @@ struct isobusfs_get_file_date_time_request {
|
||||
uint8_t tan;
|
||||
__le16 name_length;
|
||||
uint8_t name[]; /* Variable length */
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.4.6.2 Get File Date & Time Response
|
||||
@@ -278,6 +278,6 @@ struct isobusfs_get_file_date_time_response {
|
||||
uint8_t error_code;
|
||||
__le16 date;
|
||||
__le16 time;
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#endif /* _LINUX_ISOBUS_FS_H */
|
||||
|
||||
@@ -29,7 +29,7 @@ struct isobusfs_va_init_vol_req {
|
||||
uint8_t volume_flags;
|
||||
__le16 name_len;
|
||||
uint8_t name[];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* C.5.2.3 Initialize Volume Response
|
||||
@@ -62,6 +62,6 @@ struct isobusfs_va_init_vol_res {
|
||||
uint8_t tan;
|
||||
uint8_t error_code;
|
||||
uint8_t reserved[4];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#endif /* _ISOBUSFS_CMN_VA_H */
|
||||
|
||||
@@ -70,7 +70,7 @@ static int isobusfs_srv_rx_fs(struct isobusfs_srv_priv *priv,
|
||||
cg);
|
||||
|
||||
/* ISO 11783-13:2021 - Annex C.1.1 Overview:
|
||||
* If a client sends a command, which is not defined withing this
|
||||
* If a client sends a command, which is not defined within this
|
||||
* documentation, the file server shall respond with a
|
||||
* NACK (ISO 11783-3:2018 Chapter 5.4.5)
|
||||
*/
|
||||
@@ -279,7 +279,7 @@ static int isobusfs_srv_sock_fss_prepare(struct isobusfs_srv_priv *priv)
|
||||
return ret;
|
||||
|
||||
/* keep address and name and overwrite PGN */
|
||||
/* TOOD: actually, this is PGN input filter. Should we use different
|
||||
/* TODO: actually, this is PGN input filter. Should we use different
|
||||
* PGN?
|
||||
*/
|
||||
addr.can_addr.j1939.pgn = ISOBUSFS_PGN_CL_TO_FS;
|
||||
@@ -720,7 +720,7 @@ static int isobusfs_srv_parse_args(struct isobusfs_srv_priv *priv, int argc,
|
||||
}
|
||||
|
||||
if (!local_name_set)
|
||||
pr_warn("local name is not set. Wont be able to generate proper manufacturer-specific directory name. Falling mack to MCMC0000");
|
||||
pr_warn("local name is not set. Won't be able to generate proper manufacturer-specific directory name. Falling mack to MCMC0000");
|
||||
isobusfs_srv_generate_mfs_dir_name(priv);
|
||||
|
||||
pr_debug("Server configuration:");
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
@@ -473,8 +474,14 @@ static int isobusfs_srv_process_volume_status_request(struct isobusfs_srv_priv *
|
||||
|
||||
req_name_len = le16toh(req->name_len);
|
||||
|
||||
pr_debug("< rx volume status request. mode: %x, length: %d, name: %s",
|
||||
req->volume_mode, req_name_len, req->name);
|
||||
/* the volume name has to fit into the received message */
|
||||
if (req_name_len > msg->len - offsetof(struct isobusfs_cm_vol_stat_req, name)) {
|
||||
pr_warn("volume name length exceeds message length");
|
||||
return ISOBUSFS_ERR_MALFORMED_REQUEST;
|
||||
}
|
||||
|
||||
pr_debug("< rx volume status request. mode: %x, length: %zu, name: %.*s",
|
||||
req->volume_mode, req_name_len, (int)req_name_len, req->name);
|
||||
|
||||
client = isobusfs_srv_get_client_by_msg(priv, msg);
|
||||
if (!client) {
|
||||
|
||||
@@ -170,10 +170,13 @@ static int isobusfs_srv_dh_current_dir_res(struct isobusfs_srv_priv *priv,
|
||||
switch (ret) {
|
||||
case -ENOENT:
|
||||
error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND;
|
||||
break;
|
||||
case -ENOMEDIUM:
|
||||
error_code = ISOBUSFS_ERR_VOLUME_NOT_INITIALIZED;
|
||||
break;
|
||||
case -ENOMEM:
|
||||
error_code = ISOBUSFS_ERR_OUT_OF_MEM;
|
||||
break;
|
||||
default:
|
||||
error_code = ISOBUSFS_ERR_OTHER;
|
||||
}
|
||||
@@ -657,15 +660,22 @@ static int isobusfs_srv_dh_ccd_res(struct isobusfs_srv_priv *priv,
|
||||
uint8_t error_code = ISOBUSFS_ERR_SUCCESS;
|
||||
struct isobusfs_srv_client *client;
|
||||
struct isobusfs_dh_ccd_res res;
|
||||
uint16_t name_len = le16toh(req->name_len);
|
||||
size_t abs_path_len;
|
||||
char *abs_path;
|
||||
int ret;
|
||||
|
||||
/* the path name has to fit into the received message */
|
||||
if (name_len > msg->len - sizeof(*req)) {
|
||||
pr_warn("path name length exceeds message length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We assime, the relative path stored in res->name is not longer
|
||||
* than absolue path
|
||||
* We assume, the relative path stored in res->name is not longer
|
||||
* than absolute path
|
||||
*/
|
||||
if (req->name_len > ISOBUSFS_SRV_MAX_PATH_LEN) {
|
||||
if (name_len > ISOBUSFS_SRV_MAX_PATH_LEN) {
|
||||
pr_warn("path too long");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -684,10 +694,10 @@ static int isobusfs_srv_dh_ccd_res(struct isobusfs_srv_priv *priv,
|
||||
}
|
||||
|
||||
pr_debug("< rx change current directory request from client 0x%2x: %.*s. Current directory: %s",
|
||||
client->addr, req->name_len, req->name, client->current_dir);
|
||||
client->addr, name_len, req->name, client->current_dir);
|
||||
/* Normalize provided string and convert it to absolute ISOBUS FS path */
|
||||
ret = isobusfs_convert_relative_to_absolute(priv, client->current_dir,
|
||||
(char *)req->name, req->name_len,
|
||||
(char *)req->name, name_len,
|
||||
abs_path, abs_path_len);
|
||||
if (ret < 0)
|
||||
goto process_error;
|
||||
@@ -697,7 +707,7 @@ static int isobusfs_srv_dh_ccd_res(struct isobusfs_srv_priv *priv,
|
||||
process_error:
|
||||
if (ret < 0) {
|
||||
/* linux_error_to_isobusfs_error() can't distinguish between
|
||||
* -EINVAL vor SRC and DST, so we have to do it manually.
|
||||
* -EINVAL for SRC and DST, so we have to do it manually.
|
||||
*/
|
||||
if (ret == -EINVAL)
|
||||
error_code = ISOBUSFS_ERR_INVALID_DST_NAME;
|
||||
|
||||
+245
-46
@@ -45,6 +45,9 @@ static int isobusfs_srv_add_file(struct isobusfs_srv_priv *priv,
|
||||
}
|
||||
|
||||
priv->handles[j].path = strdup(path);
|
||||
if (!priv->handles[j].path)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->handles[j].fd = fd;
|
||||
priv->handles[j].dir = dir;
|
||||
|
||||
@@ -108,6 +111,28 @@ isobusfs_srv_get_handle(struct isobusfs_srv_priv *priv, int handle)
|
||||
return &priv->handles[handle];
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the handle only when it is in use and has been opened by the
|
||||
* requesting client. Otherwise return NULL.
|
||||
*/
|
||||
static struct isobusfs_srv_handles *
|
||||
isobusfs_srv_get_client_handle(struct isobusfs_srv_priv *priv,
|
||||
struct isobusfs_srv_client *client, int handle)
|
||||
{
|
||||
struct isobusfs_srv_handles *hdl = isobusfs_srv_get_handle(priv, handle);
|
||||
unsigned int i;
|
||||
|
||||
if (!hdl || !hdl->path)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hdl->clients); i++) {
|
||||
if (hdl->clients[i] == client)
|
||||
return hdl;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int isobusfs_srv_release_handle(struct isobusfs_srv_priv *priv,
|
||||
struct isobusfs_srv_client *client,
|
||||
int handle)
|
||||
@@ -137,6 +162,7 @@ static int isobusfs_srv_release_handle(struct isobusfs_srv_priv *priv,
|
||||
closedir(hdl->dir);
|
||||
else
|
||||
close(hdl->fd);
|
||||
free(hdl->path);
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
priv->handles_count--;
|
||||
}
|
||||
@@ -495,15 +521,119 @@ static int check_access_with_base(const char *base_dir,
|
||||
return access(full_path, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* ISO 11783-13:2021 B.21 and B.15:
|
||||
* Filters directory entries that can be returned in a Read File response
|
||||
* for directory handles while collecting the attributes and name length.
|
||||
*/
|
||||
/**
|
||||
* isobusfs_srv_dir_entry_visible() - Filter visible directory entries.
|
||||
* @handle: Directory handle for access checks.
|
||||
* @entry: Directory entry to inspect.
|
||||
* @file_stat: Stat buffer to fill for the entry.
|
||||
* @entry_name_len: Returns entry name length on success.
|
||||
* @attributes: Returns computed attributes on success.
|
||||
*
|
||||
* ISO 11783-13:2021 B.21 and B.15 define the directory entry layout and
|
||||
* attributes. This helper skips entries that are not readable or too long
|
||||
* and returns attributes for the entry that will be serialized.
|
||||
*
|
||||
* Return: true when the entry should be emitted, false otherwise.
|
||||
*/
|
||||
static bool isobusfs_srv_dir_entry_visible(struct isobusfs_srv_handles *handle,
|
||||
const struct dirent *entry,
|
||||
struct stat *file_stat,
|
||||
size_t *entry_name_len,
|
||||
uint8_t *attributes)
|
||||
{
|
||||
if (check_access_with_base(handle->path, entry->d_name, R_OK) != 0)
|
||||
return false;
|
||||
|
||||
if (fstatat(handle->fd, entry->d_name, file_stat, 0) < 0)
|
||||
return false;
|
||||
|
||||
*entry_name_len = strlen(entry->d_name);
|
||||
if (*entry_name_len > ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH)
|
||||
return false;
|
||||
|
||||
if (S_ISDIR(file_stat->st_mode))
|
||||
*attributes |= ISOBUSFS_ATTR_DIRECTORY;
|
||||
if (check_access_with_base(handle->path, entry->d_name, W_OK) != 0)
|
||||
*attributes |= ISOBUSFS_ATTR_READ_ONLY;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* ISO 11783-13:2021 C.3.4.2 and C.3.5.2:
|
||||
* Directory offsets/counts are in entries, so advance the directory stream
|
||||
* by visible entries only and report EOF if the entry offset is past the end.
|
||||
*/
|
||||
/**
|
||||
* isobusfs_srv_dir_skip_entries() - Advance directory stream by entries.
|
||||
* @handle: Directory handle to reposition.
|
||||
* @offset: Entry offset to seek to.
|
||||
*
|
||||
* ISO 11783-13:2021 C.3.4.2 and C.3.5.2 state directory offsets/counts are in
|
||||
* entries. This helper advances by visible entries only.
|
||||
*
|
||||
* Return: ISOBUSFS_ERR_SUCCESS or a protocol error code.
|
||||
*/
|
||||
static int isobusfs_srv_dir_skip_entries(struct isobusfs_srv_handles *handle,
|
||||
int32_t offset)
|
||||
{
|
||||
struct dirent *entry;
|
||||
int32_t skipped = 0;
|
||||
|
||||
rewinddir(handle->dir);
|
||||
|
||||
while (skipped < offset && (entry = readdir(handle->dir)) != NULL) {
|
||||
struct stat file_stat;
|
||||
size_t entry_name_len = 0;
|
||||
uint8_t attributes = 0;
|
||||
|
||||
if (!isobusfs_srv_dir_entry_visible(handle, entry, &file_stat,
|
||||
&entry_name_len,
|
||||
&attributes))
|
||||
continue;
|
||||
|
||||
skipped++;
|
||||
}
|
||||
|
||||
if (offset > 0 && skipped < offset)
|
||||
return ISOBUSFS_ERR_END_OF_FILE;
|
||||
|
||||
return ISOBUSFS_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* isobusfs_srv_read_directory() - Read directory entries into a response buffer.
|
||||
* @handle: Directory handle to read.
|
||||
* @buffer: Output buffer for directory entries.
|
||||
* @max_bytes: Maximum payload bytes allowed in the response.
|
||||
* @max_entries: Maximum number of entries to return.
|
||||
* @readed_size: Returns payload size in bytes.
|
||||
* @entries_read: Returns number of entries serialized.
|
||||
*
|
||||
* ISO 11783-13:2021 C.3.5.2: directory Count is entry based. This function
|
||||
* encodes up to @max_entries entries while respecting @max_bytes.
|
||||
*
|
||||
* Return: ISOBUSFS_ERR_SUCCESS or a protocol error code.
|
||||
*/
|
||||
static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||
uint8_t *buffer, size_t count,
|
||||
ssize_t *readed_size)
|
||||
uint8_t *buffer, size_t max_bytes,
|
||||
uint16_t max_entries,
|
||||
ssize_t *readed_size,
|
||||
uint16_t *entries_read)
|
||||
{
|
||||
DIR *dir = handle->dir;
|
||||
struct dirent *entry;
|
||||
size_t pos = 0;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* ISO 11783-13:2021 C.3.5.2:
|
||||
* Directory offsets are entry indices, not byte positions.
|
||||
* Position the directory stream to the previously stored offset (handle->dir_pos).
|
||||
*
|
||||
* Handling Changes in Directory Contents:
|
||||
@@ -519,13 +649,18 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||
* either returning an error or restarting from the beginning of the directory, depending
|
||||
* on the application's requirements.
|
||||
*/
|
||||
for (int i = 0; i < handle->dir_pos &&
|
||||
(entry = readdir(dir)) != NULL; i++) {
|
||||
/* Iterating to the desired position */
|
||||
if (handle->dir_pos < 0) {
|
||||
pr_err("Directory handle in invalid state (pos=%d)\n",
|
||||
handle->dir_pos);
|
||||
return ISOBUSFS_ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
ret = isobusfs_srv_dir_skip_entries(handle, handle->dir_pos);
|
||||
if (ret != ISOBUSFS_ERR_SUCCESS)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Directory Entry Layout:
|
||||
* Directory Entry Layout (ISO 11783-13:2021 B.21):
|
||||
* This loop reads directory entries and encodes them into a buffer.
|
||||
* Each entry in the buffer follows the format specified in ISO 11783-13:2021.
|
||||
*
|
||||
@@ -550,26 +685,23 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||
* The handle->dir_pos is incremented after processing each entry, marking
|
||||
* the current position in the directory stream for subsequent reads.
|
||||
*/
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
*entries_read = 0;
|
||||
while ((entry = readdir(dir)) != NULL &&
|
||||
(*entries_read) < max_entries) {
|
||||
size_t entry_name_len, entry_total_len;
|
||||
__le16 file_date, file_time;
|
||||
uint8_t attributes = 0;
|
||||
struct stat file_stat;
|
||||
uint8_t attributes = 0;
|
||||
__le32 size;
|
||||
|
||||
if (check_access_with_base(handle->path, entry->d_name, R_OK) != 0)
|
||||
continue; /* Skip this entry if it's not readable */
|
||||
|
||||
if (fstatat(handle->fd, entry->d_name, &file_stat, 0) < 0)
|
||||
continue; /* Skip this entry on error */
|
||||
|
||||
entry_name_len = strlen(entry->d_name);
|
||||
if (entry_name_len > ISOBUSFS_MAX_DIR_ENTRY_NAME_LENGTH)
|
||||
if (!isobusfs_srv_dir_entry_visible(handle, entry, &file_stat,
|
||||
&entry_name_len,
|
||||
&attributes))
|
||||
continue;
|
||||
|
||||
entry_total_len = 1 + entry_name_len + 1 + 2 + 2 + 4;
|
||||
|
||||
if (pos + entry_total_len > count)
|
||||
if (pos + entry_total_len > max_bytes)
|
||||
break;
|
||||
|
||||
buffer[pos++] = (uint8_t)entry_name_len;
|
||||
@@ -577,10 +709,6 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||
memcpy(buffer + pos, entry->d_name, entry_name_len);
|
||||
pos += entry_name_len;
|
||||
|
||||
if (S_ISDIR(file_stat.st_mode))
|
||||
attributes |= ISOBUSFS_ATTR_DIRECTORY;
|
||||
if (check_access_with_base(handle->path, entry->d_name, W_OK) != 0)
|
||||
attributes |= ISOBUSFS_ATTR_READ_ONLY;
|
||||
buffer[pos++] = attributes;
|
||||
|
||||
file_date = htole16(convert_to_file_date(file_stat.st_mtime));
|
||||
@@ -594,9 +722,11 @@ static int isobusfs_srv_read_directory(struct isobusfs_srv_handles *handle,
|
||||
size = htole32(file_stat.st_size);
|
||||
memcpy(buffer + pos, &size, sizeof(size));
|
||||
pos += sizeof(size);
|
||||
(*entries_read)++;
|
||||
}
|
||||
|
||||
*readed_size = pos;
|
||||
handle->dir_pos += *entries_read;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -609,13 +739,19 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||
struct isobusfs_srv_client *client;
|
||||
struct isobusfs_fa_readf_req *req;
|
||||
ssize_t readed_size = 0;
|
||||
uint16_t entries_read = 0;
|
||||
uint8_t error_code = 0;
|
||||
ssize_t send_size;
|
||||
bool res_allocated = false;
|
||||
size_t alloc_size;
|
||||
size_t max_bytes = 0;
|
||||
uint16_t count = 0;
|
||||
int ret = 0;
|
||||
int count;
|
||||
bool is_dir = false;
|
||||
|
||||
req = (struct isobusfs_fa_readf_req *)msg->buf;
|
||||
count = le16toh(req->count);
|
||||
res = (struct isobusfs_read_file_response *)&res_fail[0];
|
||||
|
||||
pr_debug("< rx: Read File Request. tan: %d, handle: %d, count: %d",
|
||||
req->tan, req->handle, count);
|
||||
@@ -627,17 +763,6 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||
* TODO: currently we are not able to detect support transport mode,
|
||||
* so ETP is assumed.
|
||||
*/
|
||||
if (count > ISOBUSFS_MAX_DATA_LENGH)
|
||||
count = ISOBUSFS_MAX_DATA_LENGH;
|
||||
|
||||
res = malloc(sizeof(*res) + count);
|
||||
if (!res) {
|
||||
pr_warn("failed to allocate memory");
|
||||
res = (struct isobusfs_read_file_response *)&res_fail[0];
|
||||
error_code = ISOBUSFS_ERR_OUT_OF_MEM;
|
||||
goto send_response;
|
||||
}
|
||||
|
||||
client = isobusfs_srv_get_client_by_msg(priv, msg);
|
||||
if (!client) {
|
||||
pr_warn("client not found");
|
||||
@@ -645,16 +770,42 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||
goto send_response;
|
||||
}
|
||||
|
||||
handle = isobusfs_srv_get_handle(priv, req->handle);
|
||||
handle = isobusfs_srv_get_client_handle(priv, client, req->handle);
|
||||
if (!handle) {
|
||||
pr_warn("failed to find file with handle: %x", req->handle);
|
||||
error_code = ISOBUSFS_ERR_FILE_ORPATH_NOT_FOUND;
|
||||
goto send_response;
|
||||
}
|
||||
|
||||
/* Determine whether to read a file or a directory */
|
||||
if (handle->dir) {
|
||||
ret = isobusfs_srv_read_directory(handle, res->data, count,
|
||||
&readed_size);
|
||||
/* ISO 11783-13:2021 C.3.5.2: count is entry count for directories. */
|
||||
is_dir = true;
|
||||
max_bytes = ISOBUSFS_MAX_DATA_LENGH;
|
||||
} else {
|
||||
if (count > ISOBUSFS_MAX_DATA_LENGH)
|
||||
count = ISOBUSFS_MAX_DATA_LENGH;
|
||||
max_bytes = count;
|
||||
}
|
||||
|
||||
/* the response is padded to ISOBUSFS_MIN_TRANSFER_LENGH (see below) */
|
||||
alloc_size = sizeof(*res) + max_bytes;
|
||||
if (alloc_size < ISOBUSFS_MIN_TRANSFER_LENGH)
|
||||
alloc_size = ISOBUSFS_MIN_TRANSFER_LENGH;
|
||||
|
||||
res = malloc(alloc_size);
|
||||
if (!res) {
|
||||
pr_warn("failed to allocate memory");
|
||||
res = (struct isobusfs_read_file_response *)&res_fail[0];
|
||||
error_code = ISOBUSFS_ERR_OUT_OF_MEM;
|
||||
goto send_response;
|
||||
}
|
||||
res_allocated = true;
|
||||
|
||||
if (is_dir) {
|
||||
ret = isobusfs_srv_read_directory(handle, res->data, max_bytes,
|
||||
count, &readed_size,
|
||||
&entries_read);
|
||||
} else {
|
||||
ret = isobusfs_srv_read_file(handle, res->data, count,
|
||||
&readed_size);
|
||||
@@ -663,7 +814,10 @@ static int isobusfs_srv_fa_rf_req(struct isobusfs_srv_priv *priv,
|
||||
if (ret < 0) {
|
||||
error_code = ret;
|
||||
readed_size = 0;
|
||||
} else if (count != 0 && readed_size == 0) {
|
||||
entries_read = 0;
|
||||
} else if (count != 0 &&
|
||||
((is_dir && entries_read == 0) ||
|
||||
(!is_dir && readed_size == 0))) {
|
||||
error_code = ISOBUSFS_ERR_END_OF_FILE;
|
||||
}
|
||||
|
||||
@@ -673,11 +827,18 @@ send_response:
|
||||
ISOBUSFS_FA_F_READ_FILE_RES);
|
||||
res->tan = req->tan;
|
||||
res->error_code = error_code;
|
||||
if (is_dir)
|
||||
res->count = htole16(entries_read);
|
||||
else
|
||||
res->count = htole16(readed_size);
|
||||
|
||||
send_size = sizeof(*res) + readed_size;
|
||||
if (send_size < ISOBUSFS_MIN_TRANSFER_LENGH)
|
||||
if (send_size < ISOBUSFS_MIN_TRANSFER_LENGH) {
|
||||
/* fill the unused bytes with 0xff */
|
||||
memset((uint8_t *)res + send_size, 0xff,
|
||||
ISOBUSFS_MIN_TRANSFER_LENGH - send_size);
|
||||
send_size = ISOBUSFS_MIN_TRANSFER_LENGH;
|
||||
}
|
||||
|
||||
/* send to socket */
|
||||
ret = isobusfs_srv_sendto(priv, msg, res, send_size);
|
||||
@@ -686,10 +847,11 @@ send_response:
|
||||
goto free_res;
|
||||
}
|
||||
|
||||
pr_debug("> tx: Read File Response. Error code: %d (%s), readed size: %d",
|
||||
pr_debug("> tx: Read File Response. Error code: %d (%s), read size: %d",
|
||||
error_code, isobusfs_error_to_str(error_code), readed_size);
|
||||
|
||||
free_res:
|
||||
if (res_allocated)
|
||||
free(res);
|
||||
return ret;
|
||||
}
|
||||
@@ -754,19 +916,56 @@ static int isobusfs_srv_seek(struct isobusfs_srv_priv *priv,
|
||||
return ISOBUSFS_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* isobusfs_srv_seek_directory() - Seek a directory by entry index.
|
||||
* @handle: Directory handle to seek.
|
||||
* @offset: Entry index to seek to.
|
||||
*
|
||||
* ISO 11783-13:2021 C.3.4.2 defines directory offsets as entry indices.
|
||||
*
|
||||
* Return: ISOBUSFS_ERR_SUCCESS or a protocol error code.
|
||||
*/
|
||||
static int isobusfs_srv_seek_directory(struct isobusfs_srv_handles *handle,
|
||||
int32_t offset)
|
||||
{
|
||||
DIR *dir = fdopendir(handle->fd);
|
||||
int32_t current_pos = handle->dir_pos;
|
||||
int ret;
|
||||
|
||||
if (!dir)
|
||||
if (!handle->dir)
|
||||
return ISOBUSFS_ERR_OTHER;
|
||||
|
||||
rewinddir(dir);
|
||||
/* Check if handle is in valid state */
|
||||
if (current_pos < 0) {
|
||||
pr_err("Cannot seek directory in invalid state (pos=%d)\n",
|
||||
current_pos);
|
||||
return ISOBUSFS_ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < offset; i++) {
|
||||
if (readdir(dir) == NULL)
|
||||
return ISOBUSFS_ERR_END_OF_FILE;
|
||||
/* Validate requested offset */
|
||||
if (offset < 0) {
|
||||
pr_err("Invalid seek offset: %d\n", offset);
|
||||
return ISOBUSFS_ERR_INVALID_REQUESTED_LENGHT;
|
||||
}
|
||||
|
||||
/*
|
||||
* ISO 11783-13:2021 C.3.4.2:
|
||||
* Directory offsets are entry indices. If we fail to seek, restore
|
||||
* the previous entry position since the position shall not change on error.
|
||||
*/
|
||||
ret = isobusfs_srv_dir_skip_entries(handle, offset);
|
||||
if (ret != ISOBUSFS_ERR_SUCCESS) {
|
||||
int restore_ret;
|
||||
|
||||
restore_ret = isobusfs_srv_dir_skip_entries(handle, current_pos);
|
||||
if (restore_ret != ISOBUSFS_ERR_SUCCESS) {
|
||||
pr_warn("Failed to restore directory position after seek failure: %d -> %d",
|
||||
current_pos, restore_ret);
|
||||
/* Directory stream is now in undefined state.
|
||||
* Mark position as invalid.
|
||||
*/
|
||||
handle->dir_pos = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
handle->dir_pos = offset;
|
||||
@@ -796,7 +995,7 @@ static int isobusfs_srv_fa_sf_req(struct isobusfs_srv_priv *priv,
|
||||
goto send_response;
|
||||
}
|
||||
|
||||
handle = isobusfs_srv_get_handle(priv, req->handle);
|
||||
handle = isobusfs_srv_get_client_handle(priv, client, req->handle);
|
||||
if (!handle) {
|
||||
pr_warn("failed to find handle: %x", req->handle);
|
||||
error_code = ISOBUSFS_ERR_INVALID_HANDLE;
|
||||
|
||||
@@ -41,6 +41,7 @@ static void gmtime_to_j1939_pgn_65254_td(struct j1939_time_date_packet *tdp)
|
||||
utc_tm = gmtime_r(&now, &utc_tm_buf);
|
||||
local_tm = localtime_r(&now, &local_tm_buf);
|
||||
|
||||
if (local_tm) {
|
||||
/* Calculate the offsets */
|
||||
hour_offset = local_tm->tm_hour - utc_tm->tm_hour;
|
||||
minute_offset = local_tm->tm_min - utc_tm->tm_min;
|
||||
@@ -52,6 +53,11 @@ static void gmtime_to_j1939_pgn_65254_td(struct j1939_time_date_packet *tdp)
|
||||
else
|
||||
hour_offset -= 24; /* before midnight */
|
||||
}
|
||||
} else {
|
||||
/* The local time offsets cannot be determined at the moment */
|
||||
hour_offset = 0xF9;
|
||||
minute_offset = 0xFF;
|
||||
}
|
||||
|
||||
/*
|
||||
* Seconds (spn959):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -618,6 +618,12 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
case J1939_PGN_ADDRESS_COMMANDED:
|
||||
/* 8 bytes NAME + 1 byte new source address */
|
||||
if (ret < 9)
|
||||
break;
|
||||
/* only accept valid unicast addresses */
|
||||
if (dat[8] > J1939_MAX_UNICAST_ADDR)
|
||||
break;
|
||||
if (!host_is_little_endian())
|
||||
bswap(dat, 8);
|
||||
memcpy(&cmd_name, dat, 8);
|
||||
|
||||
@@ -83,8 +83,8 @@ static inline void _put_id(char *buf, int end_offset, canid_t id)
|
||||
|
||||
/* CAN DLC to real data length conversion helpers */
|
||||
|
||||
static const unsigned char dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 12, 16, 20, 24, 32, 48, 64};
|
||||
static const unsigned char dlc2len[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64};
|
||||
|
||||
/* get data length from raw data length code (DLC) */
|
||||
unsigned char can_fd_dlc2len(unsigned char dlc)
|
||||
@@ -92,7 +92,8 @@ unsigned char can_fd_dlc2len(unsigned char dlc)
|
||||
return dlc2len[dlc & 0x0F];
|
||||
}
|
||||
|
||||
static const unsigned char len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
|
||||
static const unsigned char len2dlc[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
|
||||
9, 9, 9, 9, /* 9 - 12 */
|
||||
10, 10, 10, 10, /* 13 - 16 */
|
||||
11, 11, 11, 11, /* 17 - 20 */
|
||||
@@ -154,7 +155,7 @@ int hexstring2data(char *arg, unsigned char *data, int maxdlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_canframe(char *cs, cu_t *cu)
|
||||
int parse_canframe(char *cs, union cfu *cu)
|
||||
{
|
||||
/* documentation see lib.h */
|
||||
|
||||
@@ -169,21 +170,23 @@ int parse_canframe(char *cs, cu_t *cu)
|
||||
|
||||
memset(cu, 0, sizeof(*cu)); /* init CAN CC/FD/XL frame, e.g. LEN = 0 */
|
||||
|
||||
if (len < 4)
|
||||
return 0;
|
||||
if (len >= 4 && cs[3] == CANID_DELIM) { /* 3 digits SFF CAN ID */
|
||||
|
||||
if (cs[3] == CANID_DELIM) { /* 3 digits SFF */
|
||||
idx = 4; /* start of frame data */
|
||||
|
||||
idx = 4;
|
||||
/* get 3 digits SFF CAN ID value */
|
||||
for (i = 0; i < 3; i++) {
|
||||
if ((tmp = asc2nibble(cs[i])) > 0x0F)
|
||||
return 0;
|
||||
cu->cc.can_id |= tmp << (2 - i) * 4;
|
||||
}
|
||||
|
||||
} else if (cs[5] == CANID_DELIM) { /* 5 digits CAN XL VCID/PRIO*/
|
||||
} else if (len >= 21 && cs[5] == CANID_DELIM && cs[20] == CANID_DELIM) {
|
||||
/* 5 digits CAN XL VCID/PRIO - but also check for 2nd '#' here */
|
||||
|
||||
idx = 6;
|
||||
idx = 6; /* start of CAN XL frame extra content (AF, SDT, etc) */
|
||||
|
||||
/* get 5 digits CAN XL VCID/PRIO */
|
||||
for (i = 0; i < 5; i++) {
|
||||
if ((tmp = asc2nibble(cs[i])) > 0x0F)
|
||||
return 0;
|
||||
@@ -195,9 +198,11 @@ int parse_canframe(char *cs, cu_t *cu)
|
||||
cu->xl.prio &= CANXL_PRIO_MASK;
|
||||
cu->xl.prio |= tmp;
|
||||
|
||||
} else if (cs[8] == CANID_DELIM) { /* 8 digits EFF */
|
||||
} else if (len >= 9 && cs[8] == CANID_DELIM) { /* 8 digits EFF CAN ID */
|
||||
|
||||
idx = 9;
|
||||
idx = 9; /* start of frame data */
|
||||
|
||||
/* get 8 digits EFF CAN ID value */
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((tmp = asc2nibble(cs[i])) > 0x0F)
|
||||
return 0;
|
||||
@@ -238,11 +243,12 @@ int parse_canframe(char *cs, cu_t *cu)
|
||||
cu->fd.flags |= CANFD_FDF; /* dual-use */
|
||||
idx += 2;
|
||||
|
||||
} else if (cs[idx + 14] == CANID_DELIM) { /* CAN XL frame '#80:00:11223344#' */
|
||||
} else if (idx == 6) { /* CAN XL frame extra content '#80:00:11223344#' */
|
||||
maxdlen = CANXL_MAX_DLEN;
|
||||
mtu = CANXL_MTU;
|
||||
data = cu->xl.data; /* fill CAN XL data */
|
||||
data = cu->xl.data; /* overwrite pointer to CAN XL data */
|
||||
|
||||
/* get CAN XL frame extra content */
|
||||
if ((cs[idx + 2] != XL_HDR_DELIM) || (cs[idx + 5] != XL_HDR_DELIM))
|
||||
return 0;
|
||||
|
||||
@@ -276,6 +282,7 @@ int parse_canframe(char *cs, cu_t *cu)
|
||||
idx++; /* skip CANID_DELIM */
|
||||
}
|
||||
|
||||
/* copy CAN frame data content */
|
||||
for (i = 0, dlen = 0; i < maxdlen; i++) {
|
||||
if (cs[idx] == DATA_SEPERATOR) /* skip (optional) separator */
|
||||
idx++;
|
||||
@@ -308,7 +315,7 @@ int parse_canframe(char *cs, cu_t *cu)
|
||||
return mtu;
|
||||
}
|
||||
|
||||
int snprintf_canframe(char *buf, size_t size, cu_t *cu, int sep)
|
||||
int snprintf_canframe(char *buf, size_t size, union cfu *cu, int sep)
|
||||
{
|
||||
/* documentation see lib.h */
|
||||
|
||||
@@ -432,7 +439,7 @@ int snprintf_canframe(char *buf, size_t size, cu_t *cu, int sep)
|
||||
return offset;
|
||||
}
|
||||
|
||||
int snprintf_long_canframe(char *buf, size_t size, cu_t *cu, int view)
|
||||
int snprintf_long_canframe(char *buf, size_t size, union cfu *cu, int view)
|
||||
{
|
||||
/* documentation see lib.h */
|
||||
|
||||
@@ -657,6 +664,7 @@ int snprintf_long_canframe(char *buf, size_t size, cu_t *cu, int view)
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* check CANERR_BUFFSZ in lib.h when changing the error frame strings */
|
||||
static const char *error_classes[] = {
|
||||
"tx-timeout",
|
||||
"lost-arbitration",
|
||||
@@ -837,6 +845,13 @@ int snprintf_can_error_frame(char *buf, size_t len, const struct canfd_frame *cf
|
||||
if (!sep)
|
||||
sep = defsep;
|
||||
|
||||
/* the size of the provided buffer has to fit CANERR_BUFFSZ (see lib.h) */
|
||||
if (len < CANERR_BUFFSZ) {
|
||||
if (len)
|
||||
buf[0] = 0; /* empty terminated string */
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < (int)ARRAY_SIZE(error_classes); i++) {
|
||||
mask = 1 << i;
|
||||
if (class & mask) {
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define pr_debug(fmt, args...) printf(fmt, ##args)
|
||||
#else
|
||||
@@ -57,11 +59,11 @@ static inline int pr_debug(const char* fmt, ...) {return 0;}
|
||||
#endif
|
||||
|
||||
/* CAN CC/FD/XL frame union */
|
||||
typedef union {
|
||||
union cfu {
|
||||
struct can_frame cc;
|
||||
struct canfd_frame fd;
|
||||
struct canxl_frame xl;
|
||||
} cu_t;
|
||||
};
|
||||
|
||||
/*
|
||||
* The buffer size for ASCII CAN frame string representations
|
||||
@@ -108,7 +110,7 @@ int hexstring2data(char *arg, unsigned char *data, int maxdlen);
|
||||
*
|
||||
*/
|
||||
|
||||
int parse_canframe(char *cs, cu_t *cu);
|
||||
int parse_canframe(char *cs, union cfu *cu);
|
||||
/*
|
||||
* Transfers a valid ASCII string describing a CAN frame into the CAN union
|
||||
* containing CAN CC/FD/XL structs.
|
||||
@@ -178,7 +180,7 @@ int parse_canframe(char *cs, cu_t *cu);
|
||||
* - CAN FD frames do not have a RTR bit
|
||||
*/
|
||||
|
||||
int snprintf_canframe(char *buf, size_t size, cu_t *cu, int sep);
|
||||
int snprintf_canframe(char *buf, size_t size, union cfu *cu, int sep);
|
||||
/*
|
||||
* Creates a CAN frame hexadecimal output in compact format.
|
||||
* The CAN data[] is separated by '.' when sep != 0.
|
||||
@@ -211,7 +213,7 @@ int snprintf_canframe(char *buf, size_t size, cu_t *cu, int sep);
|
||||
|
||||
#define SWAP_DELIMITER '`'
|
||||
|
||||
int snprintf_long_canframe(char *buf, size_t size, cu_t *cu, int view);
|
||||
int snprintf_long_canframe(char *buf, size_t size, union cfu *cu, int view);
|
||||
/*
|
||||
* Creates a CAN frame hexadecimal output in user readable format.
|
||||
*
|
||||
@@ -236,11 +238,30 @@ int snprintf_long_canframe(char *buf, size_t size, cu_t *cu, int view);
|
||||
* data bytes is limited to 64 to fit terminal output use-cases.
|
||||
*/
|
||||
|
||||
int snprintf_can_error_frame(char *buf, size_t len, const struct canfd_frame *cf,
|
||||
const char *sep);
|
||||
/*
|
||||
* Creates a CAN error frame output in user readable format.
|
||||
*
|
||||
* buf: buffer for the output string
|
||||
* len: size of the provided buffer buf in bytes (incl. string termination)
|
||||
* sep: separator string between the error classes (NULL => default ",")
|
||||
*
|
||||
* The buffer size len needs to be at least CANERR_BUFFSZ bytes - otherwise
|
||||
* an empty string is returned.
|
||||
*
|
||||
* Calculation of the required buffer size:
|
||||
* The longest error frame content (all error classes and details set) has
|
||||
* 465 characters plus the string termination = 466 bytes. Additionally up
|
||||
* to 9 separators are inserted which results in 466 + 9 * strlen(sep) bytes,
|
||||
* e.g. 466 + 9 * 2 = 484 bytes for "\n\t" (as used in candump).
|
||||
* With CANERR_BUFFSZ = 800 the separator must not exceed 37 characters.
|
||||
*
|
||||
* When the error frame content (the strings in lib.c) is changed, the current
|
||||
* values have to be re-calculated and CANERR_BUFFSZ has to be adapted if needed.
|
||||
*/
|
||||
#define CANERR_BUFFSZ 800
|
||||
|
||||
int snprintf_can_error_frame(char *buf, size_t len, const struct canfd_frame *cf,
|
||||
const char *sep);
|
||||
|
||||
/**
|
||||
* timespec_diff_ms - calculate timespec difference in milliseconds
|
||||
|
||||
+25
@@ -253,6 +253,31 @@ int libj1939_bind_socket(int sock, struct sockaddr_can *addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* libj1939_connect_socket - Connects a socket to a CAN address.
|
||||
* @sock: The socket file descriptor.
|
||||
* @addr: The CAN address to connect to.
|
||||
*
|
||||
* This function attempts to establish a connection between the given socket
|
||||
* and the specified CAN address. If the connection fails, it logs an error
|
||||
* message with the error code and a description of the error.
|
||||
*
|
||||
* Return: 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
int libj1939_connect_socket(int sock, struct sockaddr_can *addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = connect(sock, (void *)addr, sizeof(*addr));
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
pr_err("failed to connect socket: %d (%s)", ret, strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* libj1939_socket_prio - Set the priority of a J1939 socket
|
||||
* @sock: The file descriptor of the socket
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/can/j1939.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifndef J1939_LIB_H
|
||||
@@ -42,6 +43,7 @@ void libj1939_init_sockaddr_can(struct sockaddr_can *sac, uint32_t pgn);
|
||||
|
||||
int libj1939_open_socket(void);
|
||||
int libj1939_bind_socket(int sock, struct sockaddr_can *addr);
|
||||
int libj1939_connect_socket(int sock, struct sockaddr_can *addr);
|
||||
int libj1939_socket_prio(int sock, int prio);
|
||||
int libj1939_set_broadcast(int sock);
|
||||
int libj1939_add_socket_to_epoll(int epoll_fd, int sock, uint32_t events);
|
||||
|
||||
@@ -183,7 +183,7 @@ static void canfd_asc(struct canfd_frame *cf, int devno, int mtu,
|
||||
fprintf(outfile, " %8d %4d %8X 0 0 0 0 0", 130000, 130, flags);
|
||||
}
|
||||
|
||||
static void canxl_asc(cu_t *cu, int devno, int mtu,
|
||||
static void canxl_asc(union cfu *cu, int devno, int mtu,
|
||||
char *extra_info, FILE *outfile)
|
||||
{
|
||||
char id[10];
|
||||
@@ -296,7 +296,7 @@ static void canxl_asc(cu_t *cu, int devno, int mtu,
|
||||
|
||||
#define DEVSZ 22
|
||||
#define EXTRASZ 20
|
||||
#define TIMESZ sizeof("(1345212884.318850) ")
|
||||
#define TIMESZ sizeof("(1345212884.318850123) ")
|
||||
#define BUFSZ (DEVSZ + AFRSZ + EXTRASZ + TIMESZ)
|
||||
|
||||
/* adapt sscanf() functions below on error */
|
||||
@@ -314,7 +314,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
static char buf[BUFSZ], device[DEVSZ], afrbuf[AFRSZ], extra_info[EXTRASZ];
|
||||
|
||||
static cu_t cu;
|
||||
static union cfu cu;
|
||||
static struct timeval tv, start_tv;
|
||||
FILE *infile = stdin;
|
||||
FILE *outfile = stdout;
|
||||
@@ -407,7 +407,22 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
tv.tv_sec = sec;
|
||||
|
||||
/*
|
||||
* ensure the fractions of seconds are 6 or 9 decimal places long to catch
|
||||
* 3rd party or handcrafted logfiles that treat the timestamp as float
|
||||
*/
|
||||
switch (strchr(buf, ')') - strchr(buf, '.')) {
|
||||
case 7: //6
|
||||
tv.tv_usec = usec;
|
||||
break;
|
||||
case 10: //9
|
||||
tv.tv_usec = usec / 1000;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "timestamp format in logfile requires 6 or 9 decimal places\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (print_banner) { /* print banner */
|
||||
print_banner = 0;
|
||||
|
||||
+4
-4
@@ -51,7 +51,7 @@
|
||||
#include "lib.h"
|
||||
|
||||
#define DEVSZ 22
|
||||
#define TIMESZ 22 /* sizeof("(1345212884.318850) ") */
|
||||
#define TIMESZ 25 /* sizeof("(1345212884.318850123) ") */
|
||||
#define BUFSZ (DEVSZ + AFRSZ + TIMESZ)
|
||||
|
||||
/* adapt sscanf() functions below on error */
|
||||
@@ -61,14 +61,14 @@
|
||||
#if (DEVSZ != 22)
|
||||
#error "DEVSZ value does not fit sscanf restrictions!"
|
||||
#endif
|
||||
#if (TIMESZ != 22)
|
||||
#if (TIMESZ != 25)
|
||||
#error "TIMESZ value does not fit sscanf restrictions!"
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static char buf[BUFSZ], timestamp[TIMESZ], device[DEVSZ], afrbuf[AFRSZ];
|
||||
static cu_t cu;
|
||||
static union cfu cu;
|
||||
int mtu;
|
||||
|
||||
while (fgets(buf, BUFSZ-1, stdin)) {
|
||||
@@ -78,7 +78,7 @@ int main(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sscanf(buf, "%21s %21s %6299s", timestamp, device, afrbuf) != 3)
|
||||
if (sscanf(buf, "%24s %21s %6299s", timestamp, device, afrbuf) != 3)
|
||||
return 1;
|
||||
|
||||
mtu = parse_canframe(afrbuf, &cu);
|
||||
|
||||
@@ -72,8 +72,7 @@ int mcp251xfd_regmap_read(struct mcp251xfd_priv *priv,
|
||||
return 0;
|
||||
|
||||
/* maybe it's something like "spi0.0" */
|
||||
tmp = strchr(file_path, '/');
|
||||
if (tmp)
|
||||
if (strchr(file_path, '/'))
|
||||
return -ENOENT;
|
||||
|
||||
/* first try literally */
|
||||
|
||||
@@ -160,7 +160,7 @@ static int look_up_uart_speed(long int s)
|
||||
return B3500000;
|
||||
#endif
|
||||
#ifdef B3710000
|
||||
case 3710000
|
||||
case 3710000:
|
||||
return B3710000;
|
||||
#endif
|
||||
#ifdef B4000000
|
||||
@@ -370,8 +370,13 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* retrieve the name of the created CAN netdevice */
|
||||
if (ioctl(fd, SIOCGIFNAME, ifr.ifr_name) < 0) {
|
||||
if (name) {
|
||||
perror("ioctl SIOCGIFNAME");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
/* Graceful degradation: we only needed the name for display. */
|
||||
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "<unknown>");
|
||||
}
|
||||
}
|
||||
|
||||
syslogger(LOG_NOTICE, "attached TTY %s to netdevice %s\n", ttypath, ifr.ifr_name);
|
||||
|
||||
+12
-23
@@ -58,6 +58,7 @@ static int pty2can(int pty, int socket, struct can_filter *fi,
|
||||
static char buf[200];
|
||||
char replybuf[10]; /* for answers to received commands */
|
||||
unsigned int ptr;
|
||||
unsigned int msglen; /* length of the current SLCAN message w/o '\r' */
|
||||
struct can_frame frame;
|
||||
int ret, i;
|
||||
static unsigned int rxoffset = 0; /* points to the end of an received incomplete SLCAN message */
|
||||
@@ -102,6 +103,7 @@ rx_restart:
|
||||
return 0;
|
||||
}
|
||||
|
||||
msglen = tmp;
|
||||
cmd = buf[0];
|
||||
buf[nbytes] = 0;
|
||||
|
||||
@@ -112,10 +114,11 @@ rx_restart:
|
||||
pr_debug("%c", buf[tmp]);
|
||||
pr_debug("\n");
|
||||
|
||||
/* terminate the current SLCAN message to not read the following content */
|
||||
buf[msglen] = 0;
|
||||
|
||||
/* check for filter configuration commands */
|
||||
if (cmd == 'm' || cmd == 'M') {
|
||||
buf[9] = 0; /* terminate filter string */
|
||||
ptr = 9;
|
||||
#if 0
|
||||
/* the filter is no SocketCAN filter :-( */
|
||||
|
||||
@@ -140,14 +143,12 @@ rx_restart:
|
||||
/* check for timestamp on/off command */
|
||||
if (cmd == 'Z') {
|
||||
*tstamp = buf[1] & 0x01;
|
||||
ptr = 2;
|
||||
goto rx_out_ack;
|
||||
}
|
||||
|
||||
/* check for 'O'pen command */
|
||||
if (cmd == 'O') {
|
||||
setsockopt(socket, SOL_CAN_RAW, CAN_RAW_FILTER, fi, sizeof(struct can_filter));
|
||||
ptr = 1;
|
||||
*is_open = 1;
|
||||
goto rx_out_ack;
|
||||
}
|
||||
@@ -155,7 +156,6 @@ rx_restart:
|
||||
/* check for 'C'lose command */
|
||||
if (cmd == 'C') {
|
||||
setsockopt(socket, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
|
||||
ptr = 1;
|
||||
*is_open = 0;
|
||||
goto rx_out_ack;
|
||||
}
|
||||
@@ -164,14 +164,12 @@ rx_restart:
|
||||
if (cmd == 'V') {
|
||||
sprintf(replybuf, "V1013\r");
|
||||
tmp = strlen(replybuf);
|
||||
ptr = 1;
|
||||
goto rx_out;
|
||||
}
|
||||
/* check for 'v'ersion command */
|
||||
if (cmd == 'v') {
|
||||
sprintf(replybuf, "v1014\r");
|
||||
tmp = strlen(replybuf);
|
||||
ptr = 1;
|
||||
goto rx_out;
|
||||
}
|
||||
|
||||
@@ -179,7 +177,6 @@ rx_restart:
|
||||
if (cmd == 'N') {
|
||||
sprintf(replybuf, "N4242\r");
|
||||
tmp = strlen(replybuf);
|
||||
ptr = 1;
|
||||
goto rx_out;
|
||||
}
|
||||
|
||||
@@ -187,29 +184,23 @@ rx_restart:
|
||||
if (cmd == 'F') {
|
||||
sprintf(replybuf, "F00\r");
|
||||
tmp = strlen(replybuf);
|
||||
ptr = 1;
|
||||
goto rx_out;
|
||||
}
|
||||
|
||||
/* correctly answer unsupported commands */
|
||||
if (cmd == 'U') {
|
||||
ptr = 2;
|
||||
goto rx_out_ack;
|
||||
}
|
||||
if (cmd == 'S') {
|
||||
ptr = 2;
|
||||
goto rx_out_ack;
|
||||
}
|
||||
if (cmd == 's') {
|
||||
ptr = 5;
|
||||
goto rx_out_ack;
|
||||
}
|
||||
if (cmd == 'P' || cmd == 'A') {
|
||||
ptr = 1;
|
||||
goto rx_out_nack;
|
||||
}
|
||||
if (cmd == 'X') {
|
||||
ptr = 2;
|
||||
if (buf[1] & 0x01)
|
||||
goto rx_out_ack;
|
||||
else
|
||||
@@ -217,16 +208,18 @@ rx_restart:
|
||||
}
|
||||
|
||||
/* catch unknown commands */
|
||||
if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R')) {
|
||||
ptr = nbytes - 1;
|
||||
if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R'))
|
||||
goto rx_out_nack;
|
||||
}
|
||||
|
||||
if (cmd & 0x20) /* tiny chars 'r' 't' => SFF */
|
||||
ptr = 4; /* dlc position tiiid */
|
||||
else
|
||||
ptr = 9; /* dlc position Tiiiiiiiid */
|
||||
|
||||
/* the CAN ID has to fit into the current SLCAN message */
|
||||
if (ptr > msglen)
|
||||
goto rx_out_nack;
|
||||
|
||||
memset(&frame.data, 0, 8); /* clear data[] */
|
||||
|
||||
if ((cmd | 0x20) == 'r' && buf[ptr] != '0') {
|
||||
@@ -249,7 +242,6 @@ rx_restart:
|
||||
|
||||
buf[ptr] = frame.can_dlc; /* restore following byte */
|
||||
frame.can_dlc = 0;
|
||||
ptr--; /* we have no dlc component in the violation case */
|
||||
|
||||
} else {
|
||||
if (!(buf[ptr] >= '0' && buf[ptr] < '9'))
|
||||
@@ -277,9 +269,6 @@ rx_restart:
|
||||
goto rx_out_nack;
|
||||
frame.data[i] |= tmp;
|
||||
}
|
||||
/* point to last real data */
|
||||
if (frame.can_dlc)
|
||||
ptr--;
|
||||
}
|
||||
|
||||
ret = write(socket, &frame, sizeof(frame));
|
||||
@@ -303,8 +292,8 @@ rx_out:
|
||||
}
|
||||
|
||||
/* check if there is another command in this buffer */
|
||||
if (nbytes > ptr + 1) {
|
||||
for (tmp = 0, ptr++; ptr + tmp < nbytes; tmp++)
|
||||
if (nbytes > msglen + 1) {
|
||||
for (tmp = 0, ptr = msglen + 1; ptr + tmp < nbytes; tmp++)
|
||||
buf[tmp] = buf[ptr + tmp];
|
||||
nbytes = tmp;
|
||||
goto rx_restart;
|
||||
|
||||
+1
-1
@@ -266,7 +266,7 @@ int main(int argc, char *argv[])
|
||||
fprintf(stderr, "- while (1)\n");
|
||||
while (todo_echo || todo_recv) {
|
||||
/*
|
||||
* re-use peername for storing the sender's peername of
|
||||
* reuse peername for storing the sender's peername of
|
||||
* received packets
|
||||
*/
|
||||
if (verbose)
|
||||
|
||||
Reference in New Issue
Block a user