Compare commits

...

26 Commits

Author SHA1 Message Date
Oleksij Rempel 3fabeb6a52 j1939-vehicle-position-srv: Introduce J1939 and NMEA 2000 Vehicle Position Server
This patch adds `j1939-vehicle-position-srv`, a server for sending
vehicle position data over CAN using J1939 or NMEA 2000 protocols. It
retrieves GPS data from gpsd or simulates data if gpsd is unavailable.
By default, it operates in J1939 profile but can switch to NMEA 2000
with the `-p nmea2000` option.

Usage Examples:
1. With gpsd:
   j1939acd -r 64-95 -c /tmp/1122334455667789.jacd 1122334455667789 vcan0 &
   j1939-vehicle-position-srv -i vcan0 -n 0x1122334455667789

2. In simulation mode without gpsd:
   j1939-vehicle-position-srv -i vcan0 -s -p nmea2000

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
[Yegor: add CMakeLists.txt integration]
Co-developed-by: Yegor Yefremov <yegorslists@googlemail.com>
2025-03-01 14:01:20 +01:00
Marc Kleine-Budde fafeedec1e github-actions: install libgps-dev 2025-03-01 13:28:52 +01:00
Oleksij Rempel e8559479fb libj1939: Add function to connect a socket
Introduce `libj1939_connect_socket` function to handle socket
connections.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
2025-03-01 13:28:52 +01:00
Marc Kleine-Budde 6050aa155d
Merge pull request #581 from yegorich/cmake-linting
CMakeLists.txt: remove unneeded spaces
2025-03-01 12:33:50 +01:00
Yegor Yefremov 302184f383 CMakeLists.txt: remove unneeded spaces
Signed-off-by: Yegor Yefremov <yegorslists@googlemail.com>
2025-03-01 10:51:12 +01:00
Oliver Hartkopp c542c9ada7 gitignore: remove accidentally added Makefile item
Fixes: 4577316bd6 ("Update .gitignore")
Link: https://github.com/linux-can/can-utils/pull/577#issuecomment-2690362731
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-02-28 21:34:03 +01:00
Khem Raj 2b8c7c5f4b Include time.h for timespec struct definition
Fixes
git/isobusfs/../libj1939.h:33:18: error: field has incomplete type 'struct timespec'
   33 |         struct timespec next_send_time;
      |                         ^
Signed-off-by: Khem Raj <raj.khem@gmail.com>
2025-02-14 12:41:31 +01:00
Jan Engelhardt 4577316bd6 Update .gitignore 2025-02-14 12:40:42 +01:00
Marc Kleine-Budde 4364d655b8
Merge pull request #576 from jengelh/master
build: give libisobusfs a version
2025-02-12 16:55:15 +01:00
Jan Engelhardt 2e71e396c5 build: give libisobusfs a version 2025-02-10 15:32:27 +01:00
Marc Kleine-Budde 01083a64eb
Merge pull request #573 from hartkopp/canxl-asc-conversion
CANXL support for ASC conversion tools
2025-01-24 15:39:01 +01:00
Oliver Hartkopp 008f9f8e22 asc2log: add option to disable direction info
The ASC file provides an information whether the CAN frame is sent (Tx)
or received (Rx) on the local CAN node.

With the new '-d' option the generation of this information is disabled
for the log file generation to simplify the comparision of log files.

E.g. log1 -> asc -> log2 always created the T/R information in the log2
file which is then hard to compare to the log1 file which might not
have the T/R information at all.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 15:08:46 +01:00
Oliver Hartkopp 66e7c08beb log2asc: add CAN XL support
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 15:04:43 +01:00
Oliver Hartkopp 4b66e8c56f asc2log: shrink string buffer for header analysis
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:59:57 +01:00
Oliver Hartkopp e6e3253972 asc2log: shrink string buffer for CAN identifier
Since using the %n feature the tmp1 buffer is only used for the handling
of the CAN identifier. So this buffer can be shrinked for that use case.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:56:16 +01:00
Oliver Hartkopp 6d69bef837 asc2log: add CAN CC and CAN FD support for CAN XL message events
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:51:52 +01:00
Oliver Hartkopp 4d8b247258 asc2log: make use of sscanf consumed characters feature
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:48:32 +01:00
Oliver Hartkopp 0e9c53f6d3 asc2log: add CAN XL support for CAN XL message events
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:33:59 +01:00
Oliver Hartkopp 99c5c14790 can.h: canxl: support Remote Request Substitution bit access
The Remote Request Substitution bit is a dominant bit ("0") in the CAN XL
frame. As some CAN XL controllers support to access this bit a new
CANXL_RRS value has been defined for the canxl_frame.flags element.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:22:21 +01:00
Oliver Hartkopp df8e08fa70 asc2log: prepare helper functions for CAN XL frame handling
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:22:21 +01:00
Oliver Hartkopp cb5ab07f34 asc2log: handle and skip TxRq messages
TxRq messages are internal measurement messages that document the time of
a transmission request (to the CAN driver) to be checked against the real
transmission time, when the CAN message has been sent.

There is no such feature in the SocketCAN log format, so we detect this
tag but skip TxRq messages in the ASC log files.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:22:21 +01:00
Oliver Hartkopp 0e692bf42e asc2log: unify multi-line comments
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
2025-01-24 14:21:59 +01:00
Marc Kleine-Budde 0984817438
Merge pull request #571 from marckleinebudde/cangen-canbusload
cangen, canbusload: new features
2025-01-22 15:54:06 +01:00
Zhu Yi b3da2f62b9 canbusload: support busload visualization
Add '-v' option for visualize busload, the output shows a moving
histogram of the past 90 seconds.

canbusload 2024-09-23 17:19:33 (exact bitstuffing)
 can0@500k   487   55558   31048       0  99% |XXXXXXXXXXXXXXXXXXX.|
100%|..........................................................................................
 95%|..............................................................................XXXXXXXXXXXX
 90%|.............................................................................XXXXXXXXXXXXX
 85%|.............................................................................XXXXXXXXXXXXX
 80%|.............................................................................XXXXXXXXXXXXX
 75%|.............................................................................XXXXXXXXXXXXX
 70%|.............................................................................XXXXXXXXXXXXX
 65%|.............................................................................XXXXXXXXXXXXX
 60%|............................................................................XXXXXXXXXXXXXX
 55%|............................................................................XXXXXXXXXXXXXX
 50%|............................................................................XXXXXXXXXXXXXX
 45%|............................................................................XXXXXXXXXXXXXX
 40%|............................................................................XXXXXXXXXXXXXX
 35%|.........................................XXX................................XXXXXXXXXXXXXX
 30%|.........................................XXXX...............................XXXXXXXXXXXXXX
 25%|........................................XXXXXX.............................XXXXXXXXXXXXXXX
 20%|XXXXXXXX...............................XXXXXXXXXXXXXXXXX....XXXXXXXXXXX...XXXXXXXXXXXXXXXX
 15%|XXXXXXXXX.............................XXXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 10%|XXXXXXXXX.XXXXXXXXXXXXXXXXXXX..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  5%|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Hubert Streidl <hubert.streidl@de.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
Link: https://lore.kernel.org/r/20250120162332.19157-3-mark.jonas@de.bosch.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
2025-01-20 18:07:16 +01:00
Zhu Yi b85418d75c canbusload: support busload statistic
Add '-s' option for display busload statistic, the output contains
minimal, maximum and exponentially-damped moving sums of one second
average (borrowed from Linux load average algorithm) since start or
reset (press 'r' while running).

canbusload 2024-09-23 17:15:18 (exact bitstuffing)
 can0@500k   942  107535   60168       0  18% min:  0%, max: 21%, load: 16%   6%   2% |XXX.................|

Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Hubert Streidl <hubert.streidl@de.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
Link: https://lore.kernel.org/r/20250120162332.19157-2-mark.jonas@de.bosch.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
2025-01-20 18:01:10 +01:00
Zhu Yi 6eb97b57c5 cangen: support socket priority
Add '-P' option for allow user to set the socket priority. This can be
useful in conjuction with queuing discipline.

Signed-off-by: Zhu Yi <yi.zhu5@cn.bosch.com>
Signed-off-by: Hubert Streidl <hubert.streidl@de.bosch.com>
Signed-off-by: Mark Jonas <mark.jonas@de.bosch.com>
Link: https://lore.kernel.org/r/20250120162332.19157-1-mark.jonas@de.bosch.com
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
2025-01-20 18:01:10 +01:00
13 changed files with 3784 additions and 127 deletions

View File

@ -30,7 +30,10 @@ 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
podman exec -i -u root stable dpkg --add-architecture arm64
podman exec -i -u root stable dpkg --add-architecture armhf
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 \
@ -38,27 +41,30 @@ jobs:
gcc-aarch64-linux-gnu \
gcc-arm-linux-gnueabihf \
gcc-mips-linux-gnu \
libgps-dev \
libgps-dev:arm64 \
libgps-dev:armhf \
make
- 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
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=ON -B build-${toolchain}
podman exec -i stable cmake --build build-${toolchain}
- name: Configure & Build with arm-linux-gnueabihf-clang
@ -67,14 +73,14 @@ jobs:
env:
toolchain: arm-linux-gnueabihf-clang
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=ON -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
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=ON -B build-${toolchain}
podman exec -i stable cmake --build build-${toolchain}
- name: Configure & Build with aarch64-linux-gnu-clang
@ -83,7 +89,7 @@ jobs:
env:
toolchain: aarch64-linux-gnu-clang
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=ON -B build-${toolchain}
podman exec -i stable cmake --build build-${toolchain}
- name: Configure & Build with mips-linux-gnu-gcc

6
.gitignore vendored
View File

@ -1,6 +1,12 @@
*~
*.a
*.so
*.so.*
*.o
.ccls-cache
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
compile_commands.json
tags
/build

View File

@ -4,9 +4,9 @@ project(can-utils LANGUAGES C)
message(STATUS "CMake version: ${CMAKE_VERSION}")
include (CheckFunctionExists)
include (CheckSymbolExists)
include (GNUInstallDirs)
include(CheckFunctionExists)
include(CheckSymbolExists)
include(GNUInstallDirs)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
@ -14,6 +14,12 @@ 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)
find_package(PkgConfig REQUIRED)
if(ENABLE_GPS)
pkg_check_modules(GPS REQUIRED libgps)
endif()
if(ENABLE_WERROR)
add_compile_options(-Werror)
@ -32,8 +38,8 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCLOCK_TAI=11")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSO_TXTIME=61")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSCM_TXTIME=SO_TXTIME")
include_directories (.)
include_directories (./include)
include_directories(.)
include_directories(./include)
check_function_exists(fork HAVE_FORK)
@ -67,6 +73,10 @@ set(PROGRAMS_J1939_TIMEDATE
j1939-timedate-cli
)
set(PROGRAMS_J1939_VEHICLE_POSITION
j1939-vehicle-position-srv
)
set(PROGRAMS_ISOBUSFS
isobusfs-srv
isobusfs-cli
@ -126,6 +136,7 @@ if(NOT ANDROID)
set_target_properties(isobusfs PROPERTIES
PUBLIC_HEADER "${PUBLIC_HEADER_ISOBUSFS}"
SOVERSION 0
)
install(TARGETS isobusfs
@ -190,6 +201,24 @@ if(NOT ANDROID)
j1939-timedate-srv
DESTINATION ${CMAKE_INSTALL_BINDIR})
if(ENABLE_GPS)
set(PUBLIC_HEADER_J1939_VEHICLE_POSITION
j1939_vehicle_position/j1939_vehicle_position_cmn.h
)
add_executable(j1939-vehicle-position-srv
j1939_vehicle_position/j1939_vehicle_position_srv.c
)
target_link_libraries(j1939-vehicle-position-srv
PRIVATE can j1939 ${GPS_LIBRARIES}
)
install(TARGETS
j1939-vehicle-position-srv
DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
endif()
add_library(can STATIC

View File

@ -67,6 +67,9 @@ PROGRAMS_J1939_TIMEDATE := \
j1939-timedate-srv \
j1939-timedate-cli
PROGRAMS_J1939_VEHICLE_POSITION := \
j1939-vehicle-position-srv
PROGRAMS_ISOBUSFS := \
isobusfs-srv \
isobusfs-cli
@ -98,6 +101,7 @@ PROGRAMS_SLCAN := \
PROGRAMS := \
$(PROGRAMS_CANGW) \
$(PROGRAMS_J1939_TIMEDATE) \
$(PROGRAMS_J1939_VEHICLE_POSITION) \
$(PROGRAMS_ISOBUSFS) \
$(PROGRAMS_ISOTP) \
$(PROGRAMS_J1939) \
@ -126,7 +130,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 +158,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 +189,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) -lgps -o $@
isobusfs-srv: lib.o \
libj1939.o \
isobusfs/isobusfs_cmn.o \

571
asc2log.c
View File

@ -58,7 +58,17 @@
#include "lib.h"
#define BUFLEN 400 /* CAN FD mode lines can be pretty long */
#define BUFLEN 6500 /* CAN XL mode lines can be pretty long */
#define NO_DIR '.'
/* relevant flags in Flags field */
#define ASC_F_RTR 0x00000010
#define ASC_F_FDF 0x00001000
#define ASC_F_BRS 0x00002000
#define ASC_F_ESI 0x00004000
#define ASC_F_XLF 0x00400000
#define ASC_F_RES 0x00800000
#define ASC_F_SEC 0x01000000
extern int optind, opterr, optopt;
@ -69,10 +79,12 @@ static void print_usage(char *prg)
fprintf(stderr, "Options:\n");
fprintf(stderr, "\t-I <infile>\t(default stdin)\n");
fprintf(stderr, "\t-O <outfile>\t(default stdout)\n");
fprintf(stderr, "\t-d (disable direction information R/T)\n");
fprintf(stderr, "\t-v (verbose)\n");
}
static void prframe(FILE *file, struct timeval *tv, int dev,
struct canfd_frame *cf, char *extra_info)
cu_t *cf, char dir)
{
static char abuf[BUFLEN];
@ -83,19 +95,23 @@ static void prframe(FILE *file, struct timeval *tv, int dev,
else
fprintf(file, "canX ");
snprintf_canframe(abuf, sizeof(abuf), (cu_t *)cf, 0);
fprintf(file, "%s%s", abuf, extra_info);
snprintf_canframe(abuf, sizeof(abuf), cf, 0);
if (dir == NO_DIR)
fprintf(file, "%s\n", abuf);
else
fprintf(file, "%s %c\n", abuf, dir);
}
static void get_can_id(struct canfd_frame *cf, char *idstring, int base)
static void get_can_id(canid_t *can_id, char *idstring, int base)
{
if (idstring[strlen(idstring)-1] == 'x') {
cf->can_id = CAN_EFF_FLAG;
*can_id = CAN_EFF_FLAG;
idstring[strlen(idstring)-1] = 0;
} else
cf->can_id = 0;
*can_id = 0;
cf->can_id |= strtoul(idstring, NULL, base);
*can_id |= strtoul(idstring, NULL, base);
}
static void calc_tv(struct timeval *tv, struct timeval *read_tv,
@ -131,7 +147,7 @@ static void calc_tv(struct timeval *tv, struct timeval *read_tv,
}
static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
char base, int dplace, FILE *outfile)
char base, int dplace, int disable_dir, FILE *outfile)
{
int interface;
static struct timeval tv; /* current frame timestamp */
@ -142,27 +158,26 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
int dlc = 0;
int len = 0;
int data[8];
char tmp1[BUFLEN];
char dir[3]; /* 'Rx' or 'Tx' plus terminating zero */
char *extra_info;
char idstr[21];
char dir[5]; /* 'Rx'/'Tx'/'TxRq' plus terminating zero */
int i, items;
unsigned long long sec, usec;
/* check for ErrorFrames */
if (sscanf(buf, "%llu.%llu %d %s",
if (sscanf(buf, "%llu.%llu %d %20s",
&sec, &usec,
&interface, tmp1) == 4) {
&interface, idstr) == 4) {
read_tv.tv_sec = sec;
read_tv.tv_usec = usec;
if (!strncmp(tmp1, "ErrorFrame", strlen("ErrorFrame"))) {
if (!strncmp(idstr, "ErrorFrame", strlen("ErrorFrame"))) {
/* do not know more than 'Error' */
cf.can_id = (CAN_ERR_FLAG | CAN_ERR_BUSERROR);
cf.len = CAN_ERR_DLC;
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
prframe(outfile, &tv, interface, &cf, "\n");
prframe(outfile, &tv, interface, (cu_t *)&cf, NO_DIR);
fflush(outfile);
return;
}
@ -172,15 +187,15 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
/* check for CAN frames with (hexa)decimal values */
if (base == 'h')
items = sscanf(buf, "%llu.%llu %d %s %2s %c %x %x %x %x %x %x %x %x %x",
items = sscanf(buf, "%llu.%llu %d %20s %4s %c %x %x %x %x %x %x %x %x %x",
&sec, &usec, &interface,
tmp1, dir, &rtr, &dlc,
idstr, dir, &rtr, &dlc,
&data[0], &data[1], &data[2], &data[3],
&data[4], &data[5], &data[6], &data[7]);
else
items = sscanf(buf, "%llu.%llu %d %s %2s %c %x %d %d %d %d %d %d %d %d",
items = sscanf(buf, "%llu.%llu %d %20s %4s %c %x %d %d %d %d %d %d %d %d",
&sec, &usec, &interface,
tmp1, dir, &rtr, &dlc,
idstr, dir, &rtr, &dlc,
&data[0], &data[1], &data[2], &data[3],
&data[4], &data[5], &data[6], &data[7]);
@ -205,9 +220,9 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
/* check for CAN ID with (hexa)decimal value */
if (base == 'h')
get_can_id(&cf, tmp1, 16);
get_can_id(&cf.can_id, idstr, 16);
else
get_can_id(&cf, tmp1, 10);
get_can_id(&cf.can_id, idstr, 10);
/* dlc > 8 => len == CAN_MAX_DLEN => fill len8_dlc value */
if (dlc > CAN_MAX_DLC)
@ -216,6 +231,9 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
if (strlen(dir) != 2) /* "Rx" or "Tx" */
return;
if (disable_dir)
dir[0] = NO_DIR;
/* check for signed integer overflow */
if (dplace == 4 && read_tv.tv_usec >= INT_MAX / 100)
return;
@ -223,11 +241,6 @@ static void eval_can(char* buf, struct timeval *date_tvp, char timestamps,
if (dplace == 5 && read_tv.tv_usec >= INT_MAX / 10)
return;
if (dir[0] == 'R')
extra_info = " R\n";
else
extra_info = " T\n";
cf.len = len;
if (rtr == 'r')
cf.can_id |= CAN_RTR_FLAG;
@ -236,13 +249,13 @@ 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, &cf, extra_info);
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
fflush(outfile);
}
}
static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
int dplace, FILE *outfile)
int dplace, int disable_dir, FILE *outfile)
{
int interface;
static struct timeval tv; /* current frame timestamp */
@ -251,29 +264,33 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
unsigned char brs, esi, ctmp;
unsigned int flags;
int dlc, dlen = 0;
char tmp1[BUFLEN];
char dir[3]; /* 'Rx' or 'Tx' plus terminating zero */
char *extra_info;
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;
/* The CANFD format is mainly in hex representation but <DataLength>
and probably some content we skip anyway. Don't trust the docs! */
/*
* The CANFD format is mainly in hex representation but <DataLength>
* and probably some content we skip anyway. Don't trust the docs!
*/
/* 21.671796 CANFD 1 Tx 11 msgCanFdFr1 1 0 a 16 \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 59 c0 \
100000 214 223040 80000000 46500250 460a0250 20011736 20010205 */
/*
* 21.671796 CANFD 1 Tx 11 msgCanFdFr1 1 0 a 16 \
* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 59 c0 \
* 100000 214 223040 80000000 46500250 460a0250 20011736 20010205
*/
/* check for valid line without symbolic name */
if (sscanf(buf, "%llu.%llu %*s %d %2s %s %hhx %hhx %x %d ",
if (sscanf(buf, "%llu.%llu %*s %d %4s %20s %hhx %hhx %x %d %n",
&sec, &usec, &interface,
dir, tmp1, &brs, &esi, &dlc, &dlen) != 9) {
dir, idstr, &brs, &esi, &dlc, &dlen, &n) != 9) {
/* check for valid line with a symbolic name */
if (sscanf(buf, "%llu.%llu %*s %d %2s %s %*s %hhx %hhx %x %d ",
if (sscanf(buf, "%llu.%llu %*s %d %4s %20s %*s %hhx %hhx %x %d %n",
&sec, &usec, &interface,
dir, tmp1, &brs, &esi, &dlc, &dlen) != 9) {
dir, idstr, &brs, &esi, &dlc, &dlen, &n) != 9) {
/* no valid CANFD format pattern */
return;
@ -290,6 +307,9 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
if (strlen(dir) != 2) /* "Rx" or "Tx" */
return;
if (disable_dir)
dir[0] = NO_DIR;
/* check for signed integer overflow */
if (dplace == 4 && read_tv.tv_usec >= INT_MAX / 100)
return;
@ -298,26 +318,13 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
if (dplace == 5 && read_tv.tv_usec >= INT_MAX / 10)
return;
if (dir[0] == 'R')
extra_info = " R\n";
else
extra_info = " T\n";
/* don't trust ASCII content - sanitize data length */
if (dlen != can_fd_dlc2len(can_fd_len2dlc(dlen)))
return;
get_can_id(&cf, tmp1, 16);
get_can_id(&cf.can_id, idstr, 16);
/* now search for the beginning of the data[] content */
sprintf(tmp1, " %x %x %x %2d ", brs, esi, dlc, dlen);
/* search for the pattern generated by real data */
ptr = strcasestr(buf, tmp1);
if (ptr == NULL)
return;
ptr += strlen(tmp1); /* start of ASCII hex frame data */
ptr = buf + n; /* start of ASCII hex frame data */
cf.len = dlen;
@ -341,12 +348,6 @@ static void eval_canfd(char* buf, struct timeval *date_tvp, char timestamps,
if (sscanf(ptr, " %*x %*x %x ", &flags) != 1)
return;
/* relevant flags in Flags field */
#define ASC_F_RTR 0x00000010
#define ASC_F_FDF 0x00001000
#define ASC_F_BRS 0x00002000
#define ASC_F_ESI 0x00004000
if (flags & ASC_F_FDF) {
cf.flags = CANFD_FDF;
if (flags & ASC_F_BRS)
@ -373,12 +374,408 @@ 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, &cf, extra_info);
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
fflush(outfile);
/* No support for really strange CANFD ErrorFrames format m( */
}
static void eval_canxl_cc(char* buf, struct timeval *date_tvp, char timestamps,
int dplace, int disable_dir, FILE *outfile)
{
int interface;
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;
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;
/*
* 59.171614 CANXL 2 Rx CBFF 243215 176 432 msgCanCCTest1 \
* f 8 e1 89 e8 c2 b9 6d 5a f1 174 00000000 00000000 \
* 000000050005000e 0000000000a00010 0000000a000a001d \
* 0000000000a00002 000000100010000f 000000000a00001
*/
/* check for valid line without symbolic name */
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 */
&sec, &usec, &interface, dir,
idstr,
&dlc, &dlen, &n) != 7) {
/* check for valid line with a symbolic name */
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 */
&sec, &usec, &interface, dir,
idstr,
&dlc, &dlen, &n) != 7) {
/* no valid CAN CC format pattern */
return;
}
}
read_tv.tv_sec = sec;
read_tv.tv_usec = usec;
/* check for allowed (unsigned) value ranges */
if ((dlen > CAN_MAX_DLEN) || (dlc > CAN_MAX_RAW_DLC))
return;
if (strlen(dir) != 2) /* "Rx" or "Tx" */
return;
if (disable_dir)
dir[0] = NO_DIR;
/* check for signed integer overflow */
if (dplace == 4 && read_tv.tv_usec >= INT_MAX / 100)
return;
/* check for signed integer overflow */
if (dplace == 5 && read_tv.tv_usec >= INT_MAX / 10)
return;
get_can_id(&cf.can_id, idstr, 16);
ptr = buf + n; /* start of ASCII hex frame data */
cf.len = dlen;
for (i = 0; i < dlen; i++) {
ctmp = asc2nibble(ptr[0]);
if (ctmp > 0x0F)
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;
if (flags & ASC_F_RTR) {
cf.can_id |= CAN_RTR_FLAG;
/* dlen is always 0 for classic CAN RTR frames
but the DLC value is valid in RTR cases */
cf.len = dlc;
/* sanitize payload length value */
if (dlc > CAN_MAX_DLEN)
cf.len = CAN_MAX_DLEN;
}
/* check for extra DLC when having a Classic CAN with 8 bytes payload */
if ((cf.len == CAN_MAX_DLEN) && (dlc > CAN_MAX_DLEN) && (dlc <= CAN_MAX_RAW_DLC))
cf.len8_dlc = dlc;
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
fflush(outfile);
}
static void eval_canxl_fd(char* buf, struct timeval *date_tvp, char timestamps,
int dplace, int disable_dir, FILE *outfile)
{
int interface;
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;
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;
/*
* 59.171614 CANXL 2 Rx FBFF 243215 176 432 msgCanFDTest2 \
* 9 12 e1 89 e8 c2 b9 6d 5a f1 11 22 33 44 a 12345 00001240 00000000 \
* 000000050005000e 0000000000a00010 0000000a000a001d \
* 0000000000a00002 000000100010000f 000000000a00001
*/
/* check for valid line without symbolic name */
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 */
&sec, &usec, &interface, dir,
idstr,
&dlc, &dlen, &n) != 7) {
/* check for valid line with a symbolic name */
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 */
&sec, &usec, &interface, dir,
idstr,
&dlc, &dlen, &n) != 7) {
/* no valid CAN CC format pattern */
return;
}
}
read_tv.tv_sec = sec;
read_tv.tv_usec = usec;
/* check for allowed (unsigned) value ranges */
if ((dlen > CANFD_MAX_DLEN) || (dlc > CANFD_MAX_DLC))
return;
if (strlen(dir) != 2) /* "Rx" or "Tx" */
return;
if (disable_dir)
dir[0] = NO_DIR;
/* check for signed integer overflow */
if (dplace == 4 && read_tv.tv_usec >= INT_MAX / 100)
return;
/* check for signed integer overflow */
if (dplace == 5 && read_tv.tv_usec >= INT_MAX / 10)
return;
get_can_id(&cf.can_id, idstr, 16);
ptr = buf + n; /* start of ASCII hex frame data */
cf.len = dlen;
for (i = 0; i < dlen; i++) {
ctmp = asc2nibble(ptr[0]);
if (ctmp > 0x0F)
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;
if (!(flags & ASC_F_FDF))
return;
cf.flags = CANFD_FDF;
if (flags & ASC_F_BRS)
cf.flags |= CANFD_BRS;
if (flags & ASC_F_ESI)
cf.flags |= CANFD_ESI;
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
fflush(outfile);
}
static void eval_canxl_xl(char* buf, struct timeval *date_tvp, char timestamps,
int dplace, int disable_dir, FILE *outfile)
{
int interface;
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 int af, flags;
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;
/*
* 59.171614 CANXL 2 Rx XLFF 984438 4656 432 msgCanXlTest1 \
* e0 0 1fe 511 1 1f96 00 00000000 e1 89 e8 c2 b9 6d 5a f1 c5 97 ( .. ) \
* ( .. ) c7 e3 4e f6 bf 12cfbd62 02503000 00000000 000000050005000e \
* 0000000000a00010 0000000a000a001d 0000000000a00002 000000100010000f \
* 000000000a00001
*/
/* check for valid line without symbolic name */
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 */
"%*s %*s %hhx %x %n", /* stuff bit count, crc, VCID, AF */
&sec, &usec, &interface, dir,
idstr,
&sdt, &secbit, &dlc, &dlen,
&vcid, &af, &n) != 11) {
/* check for valid line with a symbolic name */
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 %*s %hhx %x %n", /* stuff bit count, crc, VCID, AF */
&sec, &usec, &interface, dir,
idstr,
&sdt, &secbit, &dlc, &dlen,
&vcid, &af, &n) != 11) {
/* no valid CANXL format pattern */
return;
}
}
read_tv.tv_sec = sec;
read_tv.tv_usec = usec;
/* check for allowed (unsigned) value ranges */
if ((dlen > CANXL_MAX_DLEN) || (dlc > CANXL_MAX_DLC) || (secbit > 1))
return;
cf.sdt = sdt;
cf.af = af;
if (strlen(dir) != 2) /* "Rx" or "Tx" */
return;
if (disable_dir)
dir[0] = NO_DIR;
/* check for signed integer overflow */
if (dplace == 4 && read_tv.tv_usec >= INT_MAX / 100)
return;
/* check for signed integer overflow */
if (dplace == 5 && read_tv.tv_usec >= INT_MAX / 10)
return;
/* don't trust ASCII content - sanitize data length */
if (dlen != dlc + 1)
return;
get_can_id(&cf.prio, idstr, 16);
if ((cf.prio & CANXL_PRIO_MASK) != cf.prio)
return;
if (vcid)
cf.prio |= (vcid << CANXL_VCID_OFFSET);
ptr = buf + n; /* start of ASCII hex frame data */
cf.len = dlen;
for (i = 0; i < dlen; i++) {
ctmp = asc2nibble(ptr[0]);
if (ctmp > 0x0F)
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;
/* mandatory for CAN XL frames */
if (!(flags & ASC_F_XLF))
return;
/* mark as CAN XL */
cf.flags = CANXL_XLF;
if (flags & ASC_F_SEC)
cf.flags |= CANXL_SEC;
if (flags & ASC_F_RES)
cf.flags |= CANXL_RRS;
calc_tv(&tv, &read_tv, date_tvp, timestamps, dplace);
prframe(outfile, &tv, interface, (cu_t *)&cf, dir[0]);
fflush(outfile);
/* No support for CAN XL ErrorFrames */
}
static void eval_canxl(char* buf, struct timeval *date_tvp, char timestamps,
int dplace, int disable_dir, FILE *outfile)
{
int interface;
char dir[5]; /* 'Rx'/'Tx'/'TxRq' plus terminating zero */
char frfo[5]; /* frame format 'CBFF'/'CEFF'/'FBFF'/'FEFF'/'XLFF' plus terminating zero */
unsigned long long sec, usec;
/*
* The CANXL format is mainly in hex representation but <DataLength>
* and probably some content we skip anyway. Check out the new spec:
* CAN, Log & Trigger ASC Logging Format Spec V 1.4.17 of 2024-05-21
*/
/*
* This is a CAN XL ("XLFF") example for the CANXL Message Event:
*
* 59.171614 CANXL 2 Rx XLFF 984438 4656 432 msgCanXlTest1 \
* e0 0 1fe 511 1 1f96 00 00000000 e1 89 e8 c2 b9 6d 5a f1 c5 97 ( .. ) \
* ( .. ) c7 e3 4e f6 bf 12cfbd62 02503000 00000000 000000050005000e \
* 0000000000a00010 0000000a000a001d 0000000000a00002 000000100010000f \
* 000000000a00001
*/
/* check for valid line until frame format tag */
if (sscanf(buf, "%llu.%llu %*s %d %4s %4s ",
&sec, &usec, &interface, dir, frfo) != 5)
return; /* no valid CANXL format pattern */
if (strlen(dir) != 2) /* "Rx" or "Tx" */
return;
if (strlen(frfo) != 4) /* 'CBFF'/'CEFF'/'FBFF'/'FEFF'/'XLFF' */
return;
if (!strncmp(frfo, "XLFF", 4))
eval_canxl_xl(buf, date_tvp, timestamps, dplace, disable_dir, outfile);
else if (!strncmp(frfo, "FBFF", 4))
eval_canxl_fd(buf, date_tvp, timestamps, dplace, disable_dir, outfile);
else if (!strncmp(frfo, "FEFF", 4))
eval_canxl_fd(buf, date_tvp, timestamps, dplace, disable_dir, outfile);
else if (!strncmp(frfo, "CBFF", 4))
eval_canxl_cc(buf, date_tvp, timestamps, dplace, disable_dir, outfile);
else if (!strncmp(frfo, "CEFF", 4))
eval_canxl_cc(buf, date_tvp, timestamps, dplace, disable_dir, outfile);
}
static int get_date(struct timeval *tv, char *date)
{
struct tm tms;
@ -393,9 +790,11 @@ static int get_date(struct timeval *tv, char *date)
}
if (!strptime(date, "%B %d %I:%M:%S %p %Y", &tms)) {
/* The string might contain a milliseconds value which strptime()
does not support. So we read the ms value into the year variable
before parsing the real year value (hack) */
/*
* The string might contain a milliseconds value which strptime()
* does not support. So we read the ms value into the year variable
* before parsing the real year value (hack)
*/
if (!strptime(date, "%B %d %I:%M:%S.%Y %p %Y", &tms))
return 1;
sscanf(date, "%*s %*d %*d:%*d:%*d.%3u ", &msecs);
@ -410,9 +809,11 @@ static int get_date(struct timeval *tv, char *date)
}
if (!strptime(date, "%B %d %H:%M:%S %Y", &tms)) {
/* The string might contain a milliseconds value which strptime()
does not support. So we read the ms value into the year variable
before parsing the real year value (hack) */
/*
* The string might contain a milliseconds value which strptime()
* does not support. So we read the ms value into the year variable
* before parsing the real year value (hack)
*/
if (!strptime(date, "%B %d %H:%M:%S.%Y %Y", &tms))
return 1;
sscanf(date, "%*s %*d %*d:%*d:%*d.%3u ", &msecs);
@ -434,11 +835,11 @@ static int get_date(struct timeval *tv, char *date)
int main(int argc, char **argv)
{
char buf[BUFLEN], tmp1[BUFLEN], tmp2[BUFLEN];
char buf[BUFLEN], tmp1[10], tmp2[10];
FILE *infile = stdin;
FILE *outfile = stdout;
static int verbose;
static int verbose, disable_dir;
static struct timeval date_tv; /* date of the ASC file */
static int dplace; /* decimal place 4, 5 or 6 or uninitialized */
static char base; /* 'd'ec or 'h'ex */
@ -446,7 +847,7 @@ int main(int argc, char **argv)
int opt;
unsigned long long sec, usec;
while ((opt = getopt(argc, argv, "I:O:v?")) != -1) {
while ((opt = getopt(argc, argv, "I:O:dv?")) != -1) {
switch (opt) {
case 'I':
infile = fopen(optarg, "r");
@ -464,6 +865,10 @@ int main(int argc, char **argv)
}
break;
case 'd':
disable_dir = 1;
break;
case 'v':
verbose = 1;
break;
@ -488,7 +893,7 @@ int main(int argc, char **argv)
/* check for base and timestamp entries in the header */
if ((!base) &&
(sscanf(buf, "base %s timestamps %s", tmp1, tmp2) == 2)) {
(sscanf(buf, "base %9s timestamps %9s", tmp1, tmp2) == 2)) {
base = tmp1[0];
timestamps = tmp2[0];
if (verbose)
@ -522,7 +927,7 @@ int main(int argc, char **argv)
}
/* check for decimal places length in valid CAN frames */
if (sscanf(buf, "%llu.%s %s ", &sec, tmp2,
if (sscanf(buf, "%llu.%9s %9s ", &sec, tmp2,
tmp1) != 3)
continue; /* dplace remains zero until first found CAN frame */
@ -537,15 +942,19 @@ int main(int argc, char **argv)
}
}
/* the representation of a valid CAN frame is known here */
/* so try to get CAN frames and ErrorFrames and convert them */
/*
* The representation of a valid CAN frame is known here.
* So try to get CAN frames and ErrorFrames and convert them.
*/
/* check classic CAN format or the CANFD tag which can take both types */
if (sscanf(buf, "%llu.%llu %s ", &sec, &usec, tmp1) == 3){
if (!strncmp(tmp1, "CANFD", 5))
eval_canfd(buf, &date_tv, timestamps, dplace, outfile);
/* check classic CAN format or the CANFD/CANXL tag which can take different types */
if (sscanf(buf, "%llu.%llu %9s ", &sec, &usec, tmp1) == 3) {
if (!strncmp(tmp1, "CANXL", 5))
eval_canxl(buf, &date_tv, timestamps, dplace, disable_dir, outfile);
else if (!strncmp(tmp1, "CANFD", 5))
eval_canfd(buf, &date_tv, timestamps, dplace, disable_dir, outfile);
else
eval_can(buf, &date_tv, timestamps, base, dplace, outfile);
eval_can(buf, &date_tv, timestamps, base, dplace, disable_dir, outfile);
}
}
fclose(outfile);

View File

@ -45,14 +45,17 @@
#include <ctype.h>
#include <libgen.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
@ -71,6 +74,25 @@
#define PERCENTRES 5 /* resolution in percent for bargraph */
#define NUMBAR (100 / PERCENTRES) /* number of bargraph elements */
#define BRSTRLEN 20
#define VISUAL_WINDOW 90 /* window width for visualization */
/*
* Inspired from
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/
* include/linux/sched/loadavg.h
*
* Following are the fixed-point math constants and the exponential-damping
* factors for:
* - 1 samples/s in 1 minute
* - 1 samples/s in 5 minutes
* - 1 samples/s in 15 minutes
* in fixed-point representation.
*/
#define FP_SHIFT 12 /* bits of precision */
#define FP_ONE (1 << FP_SHIFT) /* 1.0 fixed-point representation */
#define EXP_1 4028 /* (1 / e ^ (1 / 60)) * FP_ONE */
#define EXP_5 4082 /* (1 / e ^ (1 / 300)) * FP_ONE */
#define EXP_15 4091 /* (1 / e ^ (1 / 900)) * FP_ONE */
extern int optind, opterr, optopt;
@ -85,19 +107,30 @@ static struct {
unsigned int recv_bits_total;
unsigned int recv_bits_payload;
unsigned int recv_bits_dbitrate;
unsigned int load_min;
unsigned int load_max;
unsigned int load_1m;
unsigned int load_5m;
unsigned int load_15m;
unsigned int loads[VISUAL_WINDOW];
unsigned int index;
} stat[MAXDEVS + 1];
static volatile int running = 1;
static volatile sig_atomic_t signal_num;
static int max_devname_len; /* to prevent frazzled device name output */
static int max_bitratestr_len;
static int currmax;
static unsigned int currmax;
static unsigned char redraw;
static unsigned char timestamp;
static unsigned char color;
static unsigned char bargraph;
static bool statistic;
static bool reset;
static bool visualize;
static enum cfl_mode mode = CFL_WORSTCASE;
static char *prg;
static struct termios old;
static void print_usage(char *prg)
{
@ -111,6 +144,8 @@ static void print_usage(char *prg)
fprintf(stderr, " -r (redraw the terminal - similar to top)\n");
fprintf(stderr, " -i (ignore bitstuffing in bandwidth calculation)\n");
fprintf(stderr, " -e (exact calculation of stuffed bits)\n");
fprintf(stderr, " -s (show statistics, press 'r' to reset)\n");
fprintf(stderr, " -v (show busload visualization)\n");
fprintf(stderr, "\n");
fprintf(stderr, "Up to %d CAN interfaces with mandatory bitrate can be specified on the \n", MAXDEVS);
fprintf(stderr, "commandline in the form: <ifname>@<bitrate>[,<dbitrate>]\n");
@ -161,9 +196,19 @@ static void create_bitrate_string(int stat_idx, int *max_bitratestr_len)
*max_bitratestr_len = ptr;
}
static unsigned int calc_load(unsigned int load_fp,
unsigned int exp_fp,
unsigned int sample)
{
unsigned int sample_fp = sample << FP_SHIFT;
unsigned int damped_sum = (load_fp * exp_fp) +
(sample_fp * (FP_ONE - exp_fp));
return damped_sum >> FP_SHIFT;
}
static void printstats(int signo)
{
int i, j, percent;
unsigned int i, j, k, percent, index;
if (redraw)
printf("%s", CSR_HOME);
@ -234,6 +279,30 @@ static void printstats(int signo)
stat[i].recv_bits_dbitrate,
percent);
if (statistic) {
if (reset) {
stat[i].load_min = UINT_MAX;
stat[i].load_max = 0;
stat[i].load_1m = 0;
stat[i].load_5m = 0;
stat[i].load_15m = 0;
}
stat[i].load_min = MIN(stat[i].load_min, percent);
stat[i].load_max = MAX(stat[i].load_max, percent);
stat[i].load_1m = calc_load(stat[i].load_1m, EXP_1, percent);
stat[i].load_5m = calc_load(stat[i].load_5m, EXP_5, percent);
stat[i].load_15m = calc_load(stat[i].load_15m, EXP_15, percent);
printf(" min:%3d%%, max:%3d%%, load:%3d%% %3d%% %3d%%",
stat[i].load_min,
stat[i].load_max,
(stat[i].load_1m + (FP_ONE >> 1)) >> FP_SHIFT,
(stat[i].load_5m + (FP_ONE >> 1)) >> FP_SHIFT,
(stat[i].load_15m + (FP_ONE >> 1)) >> FP_SHIFT);
}
if (bargraph) {
printf(" |");
@ -251,6 +320,28 @@ static void printstats(int signo)
printf("|");
}
if (visualize) {
stat[i].loads[stat[i].index] = percent;
stat[i].index = (stat[i].index + 1) % VISUAL_WINDOW;
printf("\n");
for (j = 0; j < NUMBAR; j++) {
printf("%3d%%|", (NUMBAR - j) * PERCENTRES);
index = stat[i].index;
for (k = 0; k < VISUAL_WINDOW; k++) {
percent = stat[i].loads[index];
if ((percent / PERCENTRES) >= (NUMBAR - j))
printf("X");
else
printf(".");
index = (index + 1) % VISUAL_WINDOW;
}
printf("\n");
}
}
if (color)
printf("%s", ATTRESET);
@ -264,6 +355,8 @@ static void printstats(int signo)
stat[i].recv_direction = '.';
}
reset = false;
if (!redraw)
printf("\n");
@ -272,6 +365,11 @@ static void printstats(int signo)
alarm(1);
}
void cleanup()
{
tcsetattr(STDIN_FILENO, TCSANOW, &old);
}
int main(int argc, char **argv)
{
fd_set rdfs;
@ -282,12 +380,20 @@ int main(int argc, char **argv)
struct canfd_frame frame;
struct iovec iov;
struct msghdr msg;
int nbytes, i;
unsigned int i;
int nbytes;
int have_anydev = 0;
unsigned int anydev_bitrate = 0;
unsigned int anydev_dbitrate = 0;
char anydev_bitratestr[BRSTRLEN]; /* 100000/2000000 => 100k/2M */
struct termios temp;
tcgetattr(STDIN_FILENO, &old);
atexit(cleanup);
temp = old;
temp.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &temp);
signal(SIGTERM, sigterm);
signal(SIGHUP, sigterm);
@ -297,7 +403,7 @@ int main(int argc, char **argv)
prg = basename(argv[0]);
while ((opt = getopt(argc, argv, "rtbcieh?")) != -1) {
while ((opt = getopt(argc, argv, "rtbciesvh?")) != -1) {
switch (opt) {
case 'r':
redraw = 1;
@ -323,6 +429,15 @@ int main(int argc, char **argv)
mode = CFL_EXACT;
break;
case 's':
statistic = true;
reset = true;
break;
case 'v':
visualize = true;
break;
default:
print_usage(prg);
exit(1);
@ -449,12 +564,19 @@ int main(int argc, char **argv)
while (running) {
FD_ZERO(&rdfs);
FD_SET(s, &rdfs);
FD_SET(STDIN_FILENO, &rdfs);
if (select(s + 1, &rdfs, NULL, NULL, NULL) < 0) {
//perror("pselect");
continue;
}
if (FD_ISSET(STDIN_FILENO, &rdfs)) {
if (getchar() == 'r') {
reset = true;
}
}
/* these settings may be modified by recvmsg() */
iov.iov_len = sizeof(frame);
msg.msg_namelen = sizeof(addr);

View File

@ -189,6 +189,7 @@ static void print_usage(char *prg)
fprintf(stderr, " -A <mode> (CAN XL AF generation mode - see below, no e/o mode)\n");
fprintf(stderr, " -V <mode> (CAN XL VCID generation mode - see below, no e/o mode)\n");
fprintf(stderr, " -p <timeout> (poll on -ENOBUFS to write frames with <timeout> ms)\n");
fprintf(stderr, " -P <priority> (set socket priority using SO_PRIORITY)\n");
fprintf(stderr, " -n <count> (terminate after <count> CAN frames - default infinite)\n");
fprintf(stderr, " -i (ignore -ENOBUFS return values on write() syscalls)\n");
fprintf(stderr, " -x (disable local loopback of generated CAN frames)\n");
@ -479,6 +480,7 @@ int main(int argc, char **argv)
uint64_t incdata = 0;
__u8 *data; /* base pointer for CC/FD or XL data */
int incdlc = 0;
int priority = -1;
unsigned long rnd;
unsigned char fixdata[CANFD_MAX_DLEN];
unsigned char rand_position[CANFD_MAX_DLEN] = { 0 };
@ -512,7 +514,7 @@ int main(int argc, char **argv)
{ 0, 0, 0, 0 },
};
while ((opt = getopt_long(argc, argv, "g:atefbEXR8mI:L:D:F:S:A:V:p:n:ixc:vh?", long_options, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "g:atefbEXR8mI:L:D:F:S:A:V:p:P:n:ixc:vh?", long_options, NULL)) != -1) {
switch (opt) {
case 'g':
gap = strtod(optarg, NULL);
@ -682,6 +684,14 @@ int main(int argc, char **argv)
}
break;
case 'P':
priority = atoi(optarg);
if (priority < 0) {
printf("socket priority has to be >= 0\n");
exit(1);
}
break;
case 'i':
ignore_enobufs = true;
break;
@ -750,6 +760,16 @@ int main(int argc, char **argv)
*/
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
/*
* user can use tc to configure the queuing discipline (e.g. mqprio),
* together with SO_PRIORITY option to specify the message send from
* this socket should go to which queue.
*/
if (priority >= 0 &&
setsockopt(s, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority))) {
printf("error setting SO_PRIORITY\n");
}
if (loopback_disable) {
const int loopback = 0;

View File

@ -182,7 +182,7 @@ struct canfd_frame {
/*
* defined bits for canxl_frame.flags
*
* The canxl_frame.flags element contains two bits CANXL_XLF and CANXL_SEC
* The canxl_frame.flags element contains three bits CANXL_[XLF|SEC|RRS]
* and shares the relative position of the struct can[fd]_frame.len element.
* The CANXL_XLF bit ALWAYS needs to be set to indicate a valid CAN XL frame.
* As a side effect setting this bit intentionally breaks the length checks
@ -192,6 +192,7 @@ struct canfd_frame {
*/
#define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
#define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
#define CANXL_RRS 0x02 /* Remote Request Substitution */
/* the 8-bit VCID is optionally placed in the canxl_frame.prio element */
#define CANXL_VCID_OFFSET 16 /* bit offset of VCID in prio element */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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);

172
log2asc.c
View File

@ -54,6 +54,15 @@
#include "lib.h"
/* relevant flags in Flags field */
#define ASC_F_RTR 0x00000010
#define ASC_F_FDF 0x00001000
#define ASC_F_BRS 0x00002000
#define ASC_F_ESI 0x00004000
#define ASC_F_XLF 0x00400000
#define ASC_F_RES 0x00800000
#define ASC_F_SEC 0x01000000
extern int optind, opterr, optopt;
static void print_usage(char *prg)
@ -65,18 +74,17 @@ static void print_usage(char *prg)
fprintf(stderr, " -O <outfile> (default stdout)\n");
fprintf(stderr, " -4 (reduce decimal place to 4 digits)\n");
fprintf(stderr, " -n (set newline to cr/lf - default lf)\n");
fprintf(stderr, " -f (use CANFD format also for Classic CAN)\n");
fprintf(stderr, " -f (use CANFD format also for CAN CC)\n");
fprintf(stderr, " -x (use CANXL format also for CAN CC/FD)\n");
fprintf(stderr, " -r (suppress dlc for RTR frames - pre v8.5 tools)\n");
}
static void can_asc(struct canfd_frame *cfd, int devno, int nortrdlc,
static void can_asc(struct can_frame *cf, int devno, int nortrdlc,
char *extra_info, FILE *outfile)
{
int i;
unsigned int i, dlc;
char id[10];
char *dir = "Rx";
int dlc;
struct can_frame *cf = (struct can_frame *)cfd; /* for len8_dlc */
fprintf(outfile, "%-2d ", devno); /* channel number left aligned */
@ -93,7 +101,7 @@ static void can_asc(struct canfd_frame *cfd, int devno, int nortrdlc,
dir = "Tx";
}
fprintf(outfile, "%-15s %s ", id, dir);
fprintf(outfile, "%-15s %-4s ", id, dir);
if (cf->len == CAN_MAX_DLC &&
cf->len8_dlc > CAN_MAX_DLC &&
@ -120,19 +128,13 @@ static void can_asc(struct canfd_frame *cfd, int devno, int nortrdlc,
static void canfd_asc(struct canfd_frame *cf, int devno, int mtu,
char *extra_info, FILE *outfile)
{
int i;
unsigned int i;
char id[10];
char *dir = "Rx";
unsigned int flags = 0;
unsigned int dlen = cf->len;
unsigned int dlc = can_fd_len2dlc(dlen);
/* relevant flags in Flags field */
#define ASC_F_RTR 0x00000010
#define ASC_F_FDF 0x00001000
#define ASC_F_BRS 0x00002000
#define ASC_F_ESI 0x00004000
/* check for extra info */
if (strlen(extra_info) > 0) {
/* only the first char is defined so far */
@ -140,7 +142,7 @@ static void canfd_asc(struct canfd_frame *cf, int devno, int mtu,
dir = "Tx";
}
fprintf(outfile, "CANFD %3d %s ", devno, dir); /* 3 column channel number right aligned */
fprintf(outfile, "CANFD %3d %-4s ", devno, dir); /* 3 column channel number right aligned */
sprintf(id, "%X%c", cf->can_id & CAN_EFF_MASK,
(cf->can_id & CAN_EFF_FLAG)?'x':' ');
@ -174,13 +176,124 @@ static void canfd_asc(struct canfd_frame *cf, int devno, int mtu,
fprintf(outfile, "%2d", dlen);
for (i = 0; i < (int)dlen; i++) {
for (i = 0; i < dlen; i++) {
fprintf(outfile, " %02X", cf->data[i]);
}
fprintf(outfile, " %8d %4d %8X 0 0 0 0 0", 130000, 130, flags);
}
static void canxl_asc(cu_t *cu, int devno, int mtu,
char *extra_info, FILE *outfile)
{
char id[10];
char *dir = "Rx";
char *frametype;
unsigned char *dataptr;
unsigned int i, dlen, dlc, flags = 0;
/* check for extra info */
if (strlen(extra_info) > 0) {
/* only the first char is defined so far */
if (extra_info[0] == 'T')
dir = "Tx";
}
switch (mtu) {
case CANXL_MTU:
sprintf(id, "%X", cu->xl.prio & CANXL_PRIO_MASK);
frametype = "XLFF";
dataptr = &cu->xl.data[0];
dlen = cu->xl.len;
dlc = dlen - 1;
flags = (ASC_F_XLF | ASC_F_FDF | ASC_F_BRS);
if (cu->xl.flags & CANXL_SEC)
flags |= ASC_F_SEC;
if (cu->xl.flags & CANXL_RRS)
flags |= ASC_F_RES;
break;
case CANFD_MTU:
if (cu->fd.can_id & CAN_EFF_FLAG) {
sprintf(id, "%Xx", cu->fd.can_id & CAN_EFF_MASK);
frametype = "FEFF";
} else {
sprintf(id, "%X", cu->fd.can_id & CAN_SFF_MASK);
frametype = "FBFF";
}
dataptr = &cu->fd.data[0];
dlen = cu->fd.len;
dlc = can_fd_len2dlc(dlen);
flags = ASC_F_FDF;
if (cu->fd.flags & CANFD_BRS)
flags |= ASC_F_BRS;
if (cu->fd.flags & CANFD_ESI)
flags |= ASC_F_ESI;
break;
case CAN_MTU:
if (cu->cc.can_id & CAN_EFF_FLAG) {
sprintf(id, "%Xx", cu->cc.can_id & CAN_EFF_MASK);
frametype = "CEFF";
} else {
sprintf(id, "%X", cu->cc.can_id & CAN_SFF_MASK);
frametype = "CBFF";
}
dataptr = &cu->cc.data[0];
dlen = cu->cc.len;
dlc = dlen ;
/* check for extra DLC when having a Classic CAN with 8 bytes payload */
if ((dlen == CAN_MAX_DLEN) && (cu->cc.len8_dlc > CAN_MAX_DLEN) &&
(cu->cc.len8_dlc <= CAN_MAX_RAW_DLC))
dlc = cu->cc.len8_dlc;
if (cu->cc.can_id & CAN_RTR_FLAG) {
/* no data length but dlc for RTR frames */
dlen = 0;
flags = ASC_F_RTR;
}
break;
default:
return;
}
fprintf(outfile, "CANXL %3d %-4s ", devno, dir); /* 3 column channel number and direction */
fprintf(outfile, "%s 984438 4656 ", frametype); /* frame type / msg duration / bit count */
fprintf(outfile, "%9s ", id); /* ID / symbolic name (empty) */
if (mtu == CANXL_MTU) /* SDT, SEC bit for CAN XL only */
fprintf(outfile, "%02x %d ", cu->xl.sdt, (cu->xl.flags & CANXL_SEC)?1:0);
fprintf(outfile, "%x %d", dlc, dlen); /* DLC and data length */
if (mtu == CANXL_MTU) /* SBC / PCRC / VCID / AF */
fprintf(outfile, " 1 1f96 %02x %08x",
(unsigned char)((cu->xl.prio >> CANXL_VCID_OFFSET) & CANXL_VCID_VAL_MASK),
cu->xl.af);
for (i = 0; i < dlen; i++) {
fprintf(outfile, " %02x", dataptr[i]);
}
if (mtu == CANFD_MTU) /* stuff field */
fprintf(outfile, " 8");
fprintf(outfile, " 123123 %08x %08x", flags, 0); /* fcsc, msg flags, msg flags ext */
fprintf(outfile, /* bitrate settings for CC/FD/XL */
" 000000050005000e 0000000000a00010"
" 0000000a000a001d 0000000000a00002"
" 000000100010000f 0000000000a00001");
}
#define DEVSZ 22
#define EXTRASZ 20
#define TIMESZ sizeof("(1345212884.318850) ")
@ -205,11 +318,11 @@ int main(int argc, char **argv)
static struct timeval tv, start_tv;
FILE *infile = stdin;
FILE *outfile = stdout;
static int maxdev, devno, i, crlf, fdfmt, nortrdlc, d4, opt, mtu;
static int maxdev, devno, i, crlf, fdfmt, xlfmt, nortrdlc, d4, opt, mtu;
int print_banner = 1;
unsigned long long sec, usec;
while ((opt = getopt(argc, argv, "I:O:4nfr?")) != -1) {
while ((opt = getopt(argc, argv, "I:O:4nfxr?")) != -1) {
switch (opt) {
case 'I':
infile = fopen(optarg, "r");
@ -235,6 +348,10 @@ int main(int argc, char **argv)
fdfmt = 1;
break;
case 'x':
xlfmt = 1;
break;
case 'r':
nortrdlc = 1;
break;
@ -300,6 +417,9 @@ int main(int argc, char **argv)
(crlf)?"\r\n":"\n");
fprintf(outfile, "no internal events logged%s",
(crlf)?"\r\n":"\n");
fprintf(outfile, "// version 18.2.0%s", (crlf)?"\r\n":"\n");
fprintf(outfile, "// Measurement UUID: cc9c7b54-68ae-"
"46d2-a43a-6aa87df7dd74%s", (crlf)?"\r\n":"\n");
}
for (i = 0, devno = 0; i < maxdev; i++) {
@ -313,14 +433,8 @@ int main(int argc, char **argv)
mtu = parse_canframe(afrbuf, &cu);
/* convert only CAN CC and CAN FD frames */
if ((mtu != CAN_MTU) && (mtu != CANFD_MTU)) {
printf("no valid CAN CC/FD frame\n");
return 1;
}
/* we don't support error message frames in CAN FD */
if ((mtu == CANFD_MTU) && (cu.cc.can_id & CAN_ERR_FLAG))
/* no error message frames in non CAN CC frames */
if ((mtu != CAN_MTU) && (cu.cc.can_id & CAN_ERR_FLAG))
continue;
tv.tv_sec = tv.tv_sec - start_tv.tv_sec;
@ -335,10 +449,12 @@ int main(int argc, char **argv)
else
fprintf(outfile, "%4llu.%06llu ", (unsigned long long)tv.tv_sec, (unsigned long long)tv.tv_usec);
if ((mtu == CAN_MTU) && (fdfmt == 0))
can_asc(&cu.fd, devno, nortrdlc, extra_info, outfile);
else
if ((mtu == CAN_MTU) && (fdfmt == 0) && (xlfmt == 0))
can_asc(&cu.cc, devno, nortrdlc, extra_info, outfile);
else if ((mtu != CANXL_MTU) && (xlfmt == 0))
canfd_asc(&cu.fd, devno, mtu, extra_info, outfile);
else
canxl_asc(&cu, devno, mtu, extra_info, outfile);
if (crlf)
fprintf(outfile, "\r");