You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/06/15 13:23:45 UTC
[incubator-nuttx-apps] 02/02: Ported candump & cansend apps from
can-utils (https://github.com/linux-can/can-utils)
This is an automated email from the ASF dual-hosted git repository.
gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git
commit 1ae2f4a304ff324493612ad590aba155ec99d4ea
Author: Peter van der Perk <pe...@nxp.com>
AuthorDate: Mon Jun 15 12:29:01 2020 +0200
Ported candump & cansend apps from can-utils (https://github.com/linux-can/can-utils)
---
canutils/candump/Kconfig | 16 +
canutils/candump/Make.defs | 24 ++
canutils/candump/Makefile | 34 ++
canutils/candump/candump.c | 809 ++++++++++++++++++++++++++++++++++++++++
canutils/cansend/Kconfig | 16 +
canutils/cansend/Make.defs | 24 ++
canutils/cansend/Makefile | 34 ++
canutils/cansend/cansend.c | 169 +++++++++
canutils/libcanutils/Kconfig | 16 +
canutils/libcanutils/Make.defs | 23 ++
canutils/libcanutils/Makefile | 28 ++
canutils/libcanutils/lib.c | 609 ++++++++++++++++++++++++++++++
canutils/libcanutils/lib.h | 222 +++++++++++
canutils/libcanutils/terminal.h | 95 +++++
14 files changed, 2119 insertions(+)
diff --git a/canutils/candump/Kconfig b/canutils/candump/Kconfig
new file mode 100644
index 0000000..0ba3eb0
--- /dev/null
+++ b/canutils/candump/Kconfig
@@ -0,0 +1,16 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config CANUTILS_CANDUMP
+ tristate "SocketCAN candump tool"
+ default n
+ depends on NET_CAN
+ select CANUTILS_LIBCANUTILS
+ ---help---
+ Enable the SocketCAN candump tool ported from
+ https://github.com/linux-can/can-utils
+
+if CANUTILS_CANDUMP
+endif
diff --git a/canutils/candump/Make.defs b/canutils/candump/Make.defs
new file mode 100644
index 0000000..bf15862
--- /dev/null
+++ b/canutils/candump/Make.defs
@@ -0,0 +1,24 @@
+############################################################################
+# apps/canutils/candump/Make.defs
+# Adds selected applications to apps/ build
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################/
+
+ifneq ($(CONFIG_CANUTILS_CANDUMP),)
+CONFIGURED_APPS += $(APPDIR)/canutils/candump
+endif
diff --git a/canutils/candump/Makefile b/canutils/candump/Makefile
new file mode 100644
index 0000000..97b0d09
--- /dev/null
+++ b/canutils/candump/Makefile
@@ -0,0 +1,34 @@
+############################################################################
+# apps/canutils/candump/Makefile
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################/
+
+-include $(TOPDIR)/Make.defs
+
+# SocketCAN userspace utilities and tools candump tool
+# https://github.com/linux-can/can-utils/blob/master/candump.c
+
+PROGNAME = candump
+PRIORITY = SCHED_PRIORITY_DEFAULT
+STACKSIZE = 3072
+MODULE = $(CONFIG_CANUTILS_CANDUMP)
+
+CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/canutils/libcanutils}
+MAINSRC = candump.c
+
+include $(APPDIR)/Application.mk
diff --git a/canutils/candump/candump.c b/canutils/candump/candump.c
new file mode 100644
index 0000000..c0f6bd3
--- /dev/null
+++ b/canutils/candump/candump.c
@@ -0,0 +1,809 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * candump.c
+ *
+ * Copyright (c) 2002-2009 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <li...@vger.kernel.org>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <time.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <net/if.h>
+
+#include <nuttx/can.h>
+#include <netpacket/can.h>
+
+#include "terminal.h"
+#include "lib.h"
+
+/* for hardware timestamps - since Linux 2.6.30 */
+#ifndef SO_TIMESTAMPING
+#define SO_TIMESTAMPING 37
+#endif
+
+/* from #include <linux/net_tstamp.h> - since Linux 2.6.30 */
+#define SOF_TIMESTAMPING_SOFTWARE (1<<4)
+#define SOF_TIMESTAMPING_RX_SOFTWARE (1<<3)
+#define SOF_TIMESTAMPING_RAW_HARDWARE (1<<6)
+
+#define MAXSOCK 16 /* max. number of CAN interfaces given on the cmdline */
+#define MAXIFNAMES 30 /* size of receive name index to omit ioctls */
+#define MAXCOL 6 /* number of different colors for colorized output */
+#define ANYDEV "any" /* name of interface to receive from any CAN interface */
+#define ANL "\r\n" /* newline in ASC mode */
+
+#define SILENT_INI 42 /* detect user setting on commandline */
+#define SILENT_OFF 0 /* no silent mode */
+#define SILENT_ANI 1 /* silent mode with animation */
+#define SILENT_ON 2 /* silent mode (completely silent) */
+
+#define BOLD ATTBOLD
+#define RED ATTBOLD FGRED
+#define GREEN ATTBOLD FGGREEN
+#define YELLOW ATTBOLD FGYELLOW
+#define BLUE ATTBOLD FGBLUE
+#define MAGENTA ATTBOLD FGMAGENTA
+#define CYAN ATTBOLD FGCYAN
+
+const char col_on [MAXCOL][19] = {BLUE, RED, GREEN, BOLD, MAGENTA, CYAN};
+const char col_off [] = ATTRESET;
+
+static char *cmdlinename[MAXSOCK];
+static __u32 dropcnt[MAXSOCK];
+static __u32 last_dropcnt[MAXSOCK];
+static char devname[MAXIFNAMES][IFNAMSIZ+1];
+static int dindex[MAXIFNAMES];
+static int max_devname_len; /* to prevent frazzled device name output */
+const int canfd_on = 1;
+
+#define MAXANI 4
+const char anichar[MAXANI] = {'|', '/', '-', '\\'};
+const char extra_m_info[4][4] = {"- -", "B -", "- E", "B E"};
+
+extern int optind, opterr, optopt;
+
+static volatile int running = 1;
+
+void print_usage(char *prg)
+{
+ fprintf(stderr, "%s - dump CAN bus traffic.\n", prg);
+ fprintf(stderr, "\nUsage: %s [options] <CAN interface>+\n", prg);
+ fprintf(stderr, " (use CTRL-C to terminate %s)\n\n", prg);
+ 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, " -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");
+ fprintf(stderr, " -S (swap byte order in printed CAN data[] - marked with '%c' )\n", SWAP_DELIMITER);
+ fprintf(stderr, " -s <level> (silent mode - %d: off (default) %d: animation %d: silent)\n", SILENT_OFF, SILENT_ANI, SILENT_ON);
+ fprintf(stderr, " -l (log CAN-frames into file. Sets '-s %d' by default)\n", SILENT_ON);
+ fprintf(stderr, " -L (use log file format on stdout)\n");
+ fprintf(stderr, " -n <count> (terminate after reception of <count> CAN frames)\n");
+ fprintf(stderr, " -r <size> (set socket receive buffer to <size>)\n");
+ fprintf(stderr, " -D (Don't exit if a \"detected\" can device goes down.\n");
+ fprintf(stderr, " -d (monitor dropped CAN frames)\n");
+ fprintf(stderr, " -e (dump CAN error frames in human-readable format)\n");
+ fprintf(stderr, " -x (print extra message infos, rx/tx brs esi)\n");
+ fprintf(stderr, " -T <msecs> (terminate after <msecs> without any reception)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Up to %d CAN interfaces with optional filter sets can be specified\n", MAXSOCK);
+ fprintf(stderr, "on the commandline in the form: <ifname>[,filter]*\n");
+ fprintf(stderr, "\nFilters:\n");
+ fprintf(stderr, " Comma separated filters can be specified for each given CAN interface:\n");
+ fprintf(stderr, " <can_id>:<can_mask>\n (matches when <received_can_id> & mask == can_id & mask)\n");
+ fprintf(stderr, " <can_id>~<can_mask>\n (matches when <received_can_id> & mask != can_id & mask)\n");
+ fprintf(stderr, " #<error_mask>\n (set error frame filter, see include/linux/can/error.h)\n");
+ fprintf(stderr, " [j|J]\n (join the given CAN filters - logical AND semantic)\n");
+ fprintf(stderr, "\nCAN IDs, masks and data content are given and expected in hexadecimal values.\n");
+ fprintf(stderr, "When the can_id is 8 digits long the CAN_EFF_FLAG is set for 29 bit EFF format.\n");
+ fprintf(stderr, "Without any given filter all data frames are received ('0:0' default filter).\n");
+ fprintf(stderr, "\nUse interface name '%s' to receive from all CAN interfaces.\n", ANYDEV);
+ fprintf(stderr, "\nExamples:\n");
+ fprintf(stderr, "%s -c -c -ta can0,123:7FF,400:700,#000000FF can2,400~7F0 can3 can8\n\n", prg);
+ fprintf(stderr, "%s -l any,0~0,#FFFFFFFF\n (log only error frames but no(!) data frames)\n", prg);
+ fprintf(stderr, "%s -l any,0:0,#FFFFFFFF\n (log error frames and also all data frames)\n", prg);
+ fprintf(stderr, "%s vcan2,12345678:DFFFFFFF\n (match only for extended CAN ID 12345678)\n", prg);
+ fprintf(stderr, "%s vcan2,123:7FF\n (matches CAN ID 123 - including EFF and RTR frames)\n", prg);
+ fprintf(stderr, "%s vcan2,123:C00007FF\n (matches CAN ID 123 - only SFF and non-RTR frames)\n", prg);
+ fprintf(stderr, "\n");
+}
+
+void sigterm(int signo)
+{
+ running = 0;
+}
+
+int idx2dindex(int ifidx, int socket) {
+
+ int i;
+ struct ifreq ifr;
+
+ for (i=0; i < MAXIFNAMES; i++) {
+ if (dindex[i] == ifidx)
+ return i;
+ }
+
+ /* create new interface index cache entry */
+
+ /* remove index cache zombies first */
+ for (i=0; i < MAXIFNAMES; i++) {
+ if (dindex[i]) {
+ ifr.ifr_ifindex = dindex[i];
+ if (ioctl(socket, SIOCGIFNAME, &ifr) < 0)
+ dindex[i] = 0;
+ }
+ }
+
+ for (i=0; i < MAXIFNAMES; i++)
+ if (!dindex[i]) /* free entry */
+ break;
+
+ if (i == MAXIFNAMES) {
+ fprintf(stderr, "Interface index cache only supports %d interfaces.\n",
+ MAXIFNAMES);
+ exit(1);
+ }
+
+ dindex[i] = ifidx;
+
+ ifr.ifr_ifindex = ifidx;
+ if (ioctl(socket, SIOCGIFNAME, &ifr) < 0)
+ perror("SIOCGIFNAME");
+
+ if (max_devname_len < (int)strlen(ifr.ifr_name))
+ max_devname_len = strlen(ifr.ifr_name);
+
+ strcpy(devname[i], ifr.ifr_name);
+
+#ifdef DEBUG
+ printf("new index %d (%s)\n", i, devname[i]);
+#endif
+
+ return i;
+}
+
+int main(int argc, char **argv)
+{
+ fd_set rdfs;
+ int s[MAXSOCK];
+ unsigned char timestamp = 0;
+ unsigned char hwtimestamp = 0;
+ unsigned char down_causes_exit = 1;
+ unsigned char dropmonitor = 0;
+ unsigned char extra_msg_info = 0;
+ unsigned char silent = SILENT_INI;
+ unsigned char silentani = 0;
+ unsigned char color = 0;
+ unsigned char view = 0;
+ unsigned char log = 0;
+ unsigned char logfrmt = 0;
+ int count = 0;
+ int rcvbuf_size = 0;
+ int opt, ret;
+ int currmax, numfilter;
+ int join_filter;
+ char *ptr, *nptr;
+ struct sockaddr_can addr;
+ char ctrlmsg[CMSG_SPACE(sizeof(struct timeval) + 3*sizeof(struct timespec) + sizeof(__u32))];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct can_filter *rfilter;
+ can_err_mask_t err_mask;
+ struct canfd_frame frame;
+ int nbytes, i, maxdlen;
+ struct ifreq ifr;
+ struct timeval tv, last_tv;
+ struct timeval timeout, timeout_config = { 0, 0 }, *timeout_current = NULL;
+ FILE *logfile = NULL;
+
+#if 0 /* NuttX doesn't support these signals */
+ signal(SIGTERM, sigterm);
+ signal(SIGHUP, sigterm);
+#endif
+ signal(SIGINT, sigterm);
+
+ last_tv.tv_sec = 0;
+ last_tv.tv_usec = 0;
+
+ while ((opt = getopt(argc, argv, "t:HciaSs:lDdxLn:r:heT:?")) != -1) {
+ switch (opt) {
+ case 't':
+ timestamp = optarg[0];
+ if ((timestamp != 'a') && (timestamp != 'A') &&
+ (timestamp != 'd') && (timestamp != 'z')) {
+ fprintf(stderr, "%s: unknown timestamp mode '%c' - ignored\n",
+ basename(argv[0]), optarg[0]);
+ timestamp = 0;
+ }
+ break;
+
+ case 'H':
+ hwtimestamp = 1;
+ break;
+
+ case 'c':
+ color++;
+ break;
+
+ case 'i':
+ view |= CANLIB_VIEW_BINARY;
+ break;
+
+ case 'a':
+ view |= CANLIB_VIEW_ASCII;
+ break;
+
+ case 'S':
+ view |= CANLIB_VIEW_SWAP;
+ break;
+
+ case 'e':
+ view |= CANLIB_VIEW_ERROR;
+ break;
+
+ case 's':
+ silent = atoi(optarg);
+ if (silent > SILENT_ON) {
+ print_usage(basename(argv[0]));
+ exit(1);
+ }
+ break;
+
+ case 'l':
+ log = 1;
+ break;
+
+ case 'D':
+ down_causes_exit = 0;
+ break;
+
+ case 'd':
+ dropmonitor = 1;
+ break;
+
+ case 'x':
+ extra_msg_info = 1;
+ break;
+
+ case 'L':
+ logfrmt = 1;
+ break;
+
+ case 'n':
+ count = atoi(optarg);
+ if (count < 1) {
+ print_usage(basename(argv[0]));
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ rcvbuf_size = atoi(optarg);
+ if (rcvbuf_size < 1) {
+ print_usage(basename(argv[0]));
+ exit(1);
+ }
+ break;
+
+ case 'T':
+ errno = 0;
+ timeout_config.tv_usec = strtol(optarg, NULL, 0);
+ if (errno != 0) {
+ print_usage(basename(argv[0]));
+ exit(1);
+ }
+ timeout_config.tv_sec = timeout_config.tv_usec / 1000;
+ timeout_config.tv_usec = (timeout_config.tv_usec % 1000) * 1000;
+ timeout_current = &timeout;
+ break;
+ default:
+ print_usage(basename(argv[0]));
+ exit(1);
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ print_usage(basename(argv[0]));
+ exit(0);
+ }
+
+ if (logfrmt && view) {
+ fprintf(stderr, "Log file format selected: Please disable ASCII/BINARY/SWAP options!\n");
+ exit(0);
+ }
+
+ if (silent == SILENT_INI) {
+ if (log) {
+ fprintf(stderr, "Disabled standard output while logging.\n");
+ silent = SILENT_ON; /* disable output on stdout */
+ } else
+ silent = SILENT_OFF; /* default output */
+ }
+
+ currmax = argc - optind; /* find real number of CAN devices */
+
+ if (currmax > MAXSOCK) {
+ fprintf(stderr, "More than %d CAN devices given on commandline!\n", MAXSOCK);
+ return 1;
+ }
+
+ for (i=0; i < currmax; i++) {
+
+ ptr = argv[optind+i];
+ nptr = strchr(ptr, ',');
+
+#ifdef DEBUG
+ printf("open %d '%s'.\n", i, ptr);
+#endif
+
+ s[i] = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+ if (s[i] < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ cmdlinename[i] = ptr; /* save pointer to cmdline name of this socket */
+
+ if (nptr)
+ nbytes = nptr - ptr; /* interface name is up the first ',' */
+ else
+ nbytes = strlen(ptr); /* no ',' found => no filter definitions */
+
+ if (nbytes >= IFNAMSIZ) {
+ fprintf(stderr, "name of CAN device '%s' is too long!\n", ptr);
+ return 1;
+ }
+
+ if (nbytes > max_devname_len)
+ max_devname_len = nbytes; /* for nice printing */
+
+ addr.can_family = AF_CAN;
+
+ memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name));
+ strncpy(ifr.ifr_name, ptr, nbytes);
+
+#ifdef DEBUG
+ printf("using interface name '%s'.\n", ifr.ifr_name);
+#endif
+
+ if (strcmp(ANYDEV, ifr.ifr_name)) {
+ if (ioctl(s[i], SIOCGIFINDEX, &ifr) < 0) {
+ perror("SIOCGIFINDEX");
+ exit(1);
+ }
+ addr.can_ifindex = ifr.ifr_ifindex;
+ } else
+ addr.can_ifindex = 0; /* any can interface */
+
+ if (nptr) {
+
+ /* found a ',' after the interface name => check for filters */
+
+ /* determine number of filters to alloc the filter space */
+ numfilter = 0;
+ ptr = nptr;
+ while (ptr) {
+ numfilter++;
+ ptr++; /* hop behind the ',' */
+ ptr = strchr(ptr, ','); /* exit condition */
+ }
+
+ rfilter = malloc(sizeof(struct can_filter) * numfilter);
+ if (!rfilter) {
+ fprintf(stderr, "Failed to create filter space!\n");
+ return 1;
+ }
+
+ numfilter = 0;
+ err_mask = 0;
+ join_filter = 0;
+
+ while (nptr) {
+
+ ptr = nptr+1; /* hop behind the ',' */
+ nptr = strchr(ptr, ','); /* update exit condition */
+
+ if (sscanf(ptr, "%x:%x",
+ &rfilter[numfilter].can_id,
+ &rfilter[numfilter].can_mask) == 2) {
+ rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG;
+ if (*(ptr+8) == ':')
+ rfilter[numfilter].can_id |= CAN_EFF_FLAG;
+ numfilter++;
+ } else if (sscanf(ptr, "%x~%x",
+ &rfilter[numfilter].can_id,
+ &rfilter[numfilter].can_mask) == 2) {
+ rfilter[numfilter].can_id |= CAN_INV_FILTER;
+ rfilter[numfilter].can_mask &= ~CAN_ERR_FLAG;
+ if (*(ptr+8) == '~')
+ rfilter[numfilter].can_id |= CAN_EFF_FLAG;
+ numfilter++;
+ } else if (*ptr == 'j' || *ptr == 'J') {
+ join_filter = 1;
+ } else if (sscanf(ptr, "#%x", &err_mask) != 1) {
+ fprintf(stderr, "Error in filter option parsing: '%s'\n", ptr);
+ return 1;
+ }
+ }
+
+ if (err_mask)
+ setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
+ &err_mask, sizeof(err_mask));
+
+ if (join_filter && setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_JOIN_FILTERS,
+ &join_filter, sizeof(join_filter)) < 0) {
+ perror("setsockopt CAN_RAW_JOIN_FILTERS not supported by your Linux Kernel");
+ return 1;
+ }
+
+ if (numfilter)
+ setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FILTER,
+ rfilter, numfilter * sizeof(struct can_filter));
+
+ free(rfilter);
+
+ } /* if (nptr) */
+
+ /* try to switch the socket into CAN FD mode */
+ setsockopt(s[i], SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on));
+
+ if (rcvbuf_size) {
+
+ int curr_rcvbuf_size;
+ socklen_t curr_rcvbuf_size_len = sizeof(curr_rcvbuf_size);
+
+ /* try SO_RCVBUFFORCE first, if we run with CAP_NET_ADMIN */
+ if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUFFORCE,
+ &rcvbuf_size, sizeof(rcvbuf_size)) < 0) {
+#ifdef DEBUG
+ printf("SO_RCVBUFFORCE failed so try SO_RCVBUF ...\n");
+#endif
+ if (setsockopt(s[i], SOL_SOCKET, SO_RCVBUF,
+ &rcvbuf_size, sizeof(rcvbuf_size)) < 0) {
+ perror("setsockopt SO_RCVBUF");
+ return 1;
+ }
+
+ if (getsockopt(s[i], SOL_SOCKET, SO_RCVBUF,
+ &curr_rcvbuf_size, &curr_rcvbuf_size_len) < 0) {
+ perror("getsockopt SO_RCVBUF");
+ return 1;
+ }
+
+ /* Only print a warning the first time we detect the adjustment */
+ /* n.b.: The wanted size is doubled in Linux in net/sore/sock.c */
+ if (!i && curr_rcvbuf_size < rcvbuf_size*2)
+ fprintf(stderr, "The socket receive buffer size was "
+ "adjusted due to /proc/sys/net/core/rmem_max.\n");
+ }
+ }
+
+ if (timestamp || log || logfrmt) {
+
+ if (hwtimestamp) {
+ const int timestamping_flags = (SOF_TIMESTAMPING_SOFTWARE | \
+ SOF_TIMESTAMPING_RX_SOFTWARE | \
+ SOF_TIMESTAMPING_RAW_HARDWARE);
+
+ if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMPING,
+ ×tamping_flags, sizeof(timestamping_flags)) < 0) {
+ perror("setsockopt SO_TIMESTAMPING is not supported by your Linux kernel");
+ return 1;
+ }
+ } else {
+ const int timestamp_on = 1;
+
+ if (setsockopt(s[i], SOL_SOCKET, SO_TIMESTAMP,
+ ×tamp_on, sizeof(timestamp_on)) < 0) {
+ perror("setsockopt SO_TIMESTAMP");
+ return 1;
+ }
+ }
+ }
+
+ if (dropmonitor) {
+
+ const int dropmonitor_on = 1;
+
+ if (setsockopt(s[i], SOL_SOCKET, SO_RXQ_OVFL,
+ &dropmonitor_on, sizeof(dropmonitor_on)) < 0) {
+ perror("setsockopt SO_RXQ_OVFL not supported by your Linux Kernel");
+ return 1;
+ }
+ }
+
+ if (bind(s[i], (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("bind");
+ return 1;
+ }
+ }
+
+ if (log) {
+ time_t currtime;
+ struct tm now;
+ char fname[83]; /* suggested by -Wformat-overflow= */
+
+ if (time(&currtime) == (time_t)-1) {
+ perror("time");
+ return 1;
+ }
+
+ localtime_r(&currtime, &now);
+
+ sprintf(fname, "candump-%04d-%02d-%02d_%02d%02d%02d.log",
+ now.tm_year + 1900,
+ now.tm_mon + 1,
+ now.tm_mday,
+ now.tm_hour,
+ now.tm_min,
+ now.tm_sec);
+
+ if (silent != SILENT_ON)
+ fprintf(stderr, "Warning: Console output active while logging!\n");
+
+ fprintf(stderr, "Enabling Logfile '%s'\n", fname);
+
+ logfile = fopen(fname, "w");
+ if (!logfile) {
+ perror("logfile");
+ return 1;
+ }
+ }
+
+ /* these settings are static and can be held out of the hot path */
+ iov.iov_base = &frame;
+ msg.msg_name = &addr;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &ctrlmsg;
+
+ while (running) {
+
+ FD_ZERO(&rdfs);
+ for (i=0; i<currmax; i++)
+ FD_SET(s[i], &rdfs);
+
+ if (timeout_current)
+ *timeout_current = timeout_config;
+
+ if ((ret = select(s[currmax-1]+1, &rdfs, NULL, NULL, timeout_current)) <= 0) {
+ //perror("select");
+ running = 0;
+ continue;
+ }
+
+ for (i=0; i<currmax; i++) { /* check all CAN RAW sockets */
+
+ if (FD_ISSET(s[i], &rdfs)) {
+
+ int idx;
+
+ /* these settings may be modified by recvmsg() */
+ iov.iov_len = sizeof(frame);
+ msg.msg_namelen = sizeof(addr);
+ msg.msg_controllen = sizeof(ctrlmsg);
+ msg.msg_flags = 0;
+
+ nbytes = recvmsg(s[i], &msg, 0);
+ idx = idx2dindex(addr.can_ifindex, s[i]);
+
+ if (nbytes < 0) {
+ if ((errno == ENETDOWN) && !down_causes_exit) {
+ fprintf(stderr, "%s: interface down\n", devname[idx]);
+ continue;
+ }
+ perror("read");
+ return 1;
+ }
+
+ if ((size_t)nbytes == CAN_MTU)
+ maxdlen = CAN_MAX_DLEN;
+ else if ((size_t)nbytes == CANFD_MTU)
+ maxdlen = CANFD_MAX_DLEN;
+ else {
+ fprintf(stderr, "read: incomplete CAN frame\n");
+ return 1;
+ }
+
+ if (count && (--count == 0))
+ running = 0;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg && (cmsg->cmsg_level == SOL_SOCKET);
+ cmsg = CMSG_NXTHDR(&msg,cmsg)) {
+ if (cmsg->cmsg_type == SO_TIMESTAMP) {
+ memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
+ } else if (cmsg->cmsg_type == SO_TIMESTAMPING) {
+
+ struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg);
+
+ /*
+ * stamp[0] is the software timestamp
+ * stamp[1] is deprecated
+ * stamp[2] is the raw hardware timestamp
+ * 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;
+ } else if (cmsg->cmsg_type == SO_RXQ_OVFL)
+ memcpy(&dropcnt[i], CMSG_DATA(cmsg), sizeof(__u32));
+ }
+
+ /* check for (unlikely) dropped frames on this specific socket */
+ if (dropcnt[i] != last_dropcnt[i]) {
+
+ __u32 frames = dropcnt[i] - last_dropcnt[i];
+
+ if (silent != SILENT_ON)
+ printf("DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n",
+ frames, (frames > 1)?"s":"", devname[idx], dropcnt[i]);
+
+ if (log)
+ fprintf(logfile, "DROPCOUNT: dropped %d CAN frame%s on '%s' socket (total drops %d)\n",
+ frames, (frames > 1)?"s":"", devname[idx], dropcnt[i]);
+
+ last_dropcnt[i] = dropcnt[i];
+ }
+
+ /* once we detected a EFF frame indent SFF frames accordingly */
+ if (frame.can_id & CAN_EFF_FLAG)
+ view |= CANLIB_VIEW_INDENT_SFF;
+
+ if (log) {
+ char buf[CL_CFSZ]; /* max length */
+
+ /* log CAN frame with absolute timestamp & device */
+ sprint_canframe(buf, &frame, 0, maxdlen);
+ fprintf(logfile, "(%010ld.%06ld) %*s %s\n",
+ tv.tv_sec, tv.tv_usec,
+ max_devname_len, devname[idx], buf);
+ }
+
+ if ((logfrmt) && (silent == SILENT_OFF)){
+ char buf[CL_CFSZ]; /* max length */
+
+ /* print CAN frame in log file style to stdout */
+ sprint_canframe(buf, &frame, 0, maxdlen);
+ printf("(%010ld.%06ld) %*s %s\n",
+ tv.tv_sec, tv.tv_usec,
+ max_devname_len, devname[idx], buf);
+ goto out_fflush; /* no other output to stdout */
+ }
+
+ if (silent != SILENT_OFF){
+ if (silent == SILENT_ANI) {
+ printf("%c\b", anichar[silentani%=MAXANI]);
+ silentani++;
+ }
+ goto out_fflush; /* no other output to stdout */
+ }
+
+ printf(" %s", (color>2)?col_on[idx%MAXCOL]:"");
+
+ switch (timestamp) {
+
+ case 'a': /* absolute with timestamp */
+ printf("(%010ld.%06ld) ", tv.tv_sec, tv.tv_usec);
+ break;
+
+ case 'A': /* absolute with date */
+ {
+ struct tm tm;
+ char timestring[25];
+
+ tm = *localtime(&tv.tv_sec);
+ strftime(timestring, 24, "%Y-%m-%d %H:%M:%S", &tm);
+ printf("(%s.%06ld) ", timestring, tv.tv_usec);
+ }
+ break;
+
+ case 'd': /* delta */
+ case 'z': /* starting with zero */
+ {
+ struct timeval 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 (diff.tv_sec < 0)
+ diff.tv_sec = diff.tv_usec = 0;
+ printf("(%03ld.%06ld) ", diff.tv_sec, diff.tv_usec);
+
+ if (timestamp == 'd')
+ last_tv = tv; /* update for delta calculation */
+ }
+ break;
+
+ default: /* no timestamp output */
+ break;
+ }
+
+ printf(" %s", (color && (color<3))?col_on[idx%MAXCOL]:"");
+ printf("%*s", max_devname_len, devname[idx]);
+
+ if (extra_msg_info) {
+
+ if (msg.msg_flags & MSG_DONTROUTE)
+ printf (" TX %s", extra_m_info[frame.flags & 3]);
+ else
+ printf (" RX %s", extra_m_info[frame.flags & 3]);
+ }
+
+ printf("%s ", (color==1)?col_off:"");
+
+ fprint_long_canframe(stdout, &frame, NULL, view, maxdlen);
+
+ printf("%s", (color>1)?col_off:"");
+ printf("\n");
+ }
+
+ out_fflush:
+ fflush(stdout);
+ }
+ }
+
+ for (i=0; i<currmax; i++)
+ close(s[i]);
+
+ if (log)
+ fclose(logfile);
+
+ return 0;
+}
diff --git a/canutils/cansend/Kconfig b/canutils/cansend/Kconfig
new file mode 100644
index 0000000..9d4adea
--- /dev/null
+++ b/canutils/cansend/Kconfig
@@ -0,0 +1,16 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config CANUTILS_CANSEND
+ tristate "SocketCAN cansend tool"
+ default n
+ depends on NET_CAN
+ select CANUTILS_LIBCANUTILS
+ ---help---
+ Enable the SocketCAN cansend tool ported from
+ https://github.com/linux-can/can-utils
+
+if CANUTILS_CANSEND
+endif
diff --git a/canutils/cansend/Make.defs b/canutils/cansend/Make.defs
new file mode 100644
index 0000000..2361a59
--- /dev/null
+++ b/canutils/cansend/Make.defs
@@ -0,0 +1,24 @@
+############################################################################
+# apps/canutils/cansend/Make.defs
+# Adds selected applications to apps/ build
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################/
+
+ifneq ($(CONFIG_CANUTILS_CANSEND),)
+CONFIGURED_APPS += $(APPDIR)/canutils/cansend
+endif
diff --git a/canutils/cansend/Makefile b/canutils/cansend/Makefile
new file mode 100644
index 0000000..ccfca2a
--- /dev/null
+++ b/canutils/cansend/Makefile
@@ -0,0 +1,34 @@
+############################################################################
+# apps/canutils/cansend/Makefile
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################/
+
+-include $(TOPDIR)/Make.defs
+
+# SocketCAN userspace utilities and tools cansend tool
+# https://github.com/linux-can/can-utils/blob/master/cansend.c
+
+PROGNAME = cansend
+PRIORITY = SCHED_PRIORITY_DEFAULT
+STACKSIZE = 2048
+MODULE = $(CONFIG_CANUTILS_CANSEND)
+
+CFLAGS += ${shell $(INCDIR) "$(CC)" $(APPDIR)/canutils/libcanutils}
+MAINSRC = cansend.c
+
+include $(APPDIR)/Application.mk
diff --git a/canutils/cansend/cansend.c b/canutils/cansend/cansend.c
new file mode 100644
index 0000000..0a8e677
--- /dev/null
+++ b/canutils/cansend/cansend.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * cansend.c - send CAN-frames via CAN_RAW sockets
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <li...@vger.kernel.org>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <nuttx/can.h>
+#include <netpacket/can.h>
+
+#include "lib.h"
+
+void print_usage_send(char *prg)
+{
+ fprintf(stderr, "%s - send CAN-frames via CAN_RAW sockets.\n", prg);
+ fprintf(stderr, "\nUsage: %s <device> <can_frame>.\n", prg);
+ fprintf(stderr, "\n<can_frame>:\n");
+ fprintf(stderr, " <can_id>#{data} for 'classic' CAN 2.0 data frames\n");
+ fprintf(stderr, " <can_id>#R{len} for 'classic' CAN 2.0 data frames\n");
+ fprintf(stderr, " <can_id>##<flags>{data} for CAN FD frames\n\n");
+ fprintf(stderr, "<can_id>:\n"
+ " 3 (SFF) or 8 (EFF) hex chars\n");
+ fprintf(stderr, "{data}:\n"
+ " 0..8 (0..64 CAN FD) ASCII hex-values (optionally separated by '.')\n");
+ fprintf(stderr, "{len}:\n"
+ " an optional 0..8 value as RTR frames can contain a valid dlc field\n");
+ fprintf(stderr, "<flags>:\n"
+ " a single ASCII Hex value (0 .. F) which defines canfd_frame.flags\n\n");
+ fprintf(stderr, "Examples:\n");
+ fprintf(stderr, " 5A1#11.2233.44556677.88 / 123#DEADBEEF / 5AA# / 123##1 / 213##311223344 /\n"
+ " 1F334455#1122334455667788 / 123#R / 00000123#R3\n\n");
+}
+
+
+
+int main(int argc, char **argv)
+{
+ int s; /* can raw socket */
+ int required_mtu;
+ int mtu;
+ int enable_canfd = 1;
+ struct sockaddr_can addr;
+ struct canfd_frame frame;
+ struct ifreq ifr;
+
+ /* check command line options */
+ if (argc != 3) {
+ print_usage_send(argv[0]);
+ return 1;
+ }
+
+ /* parse CAN frame */
+ required_mtu = parse_canframe(argv[2], &frame);
+ if (!required_mtu){
+ fprintf(stderr, "\nWrong CAN-frame format!\n\n");
+ print_usage_send(argv[0]);
+ return 1;
+ }
+
+ /* open socket */
+ if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ strncpy(ifr.ifr_name, argv[1], IFNAMSIZ - 1);
+ ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+ ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name);
+ if (!ifr.ifr_ifindex) {
+ perror("if_nametoindex");
+ return 1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = ifr.ifr_ifindex;
+
+ if (required_mtu > (int)CAN_MTU) {
+
+ /* check if the frame fits into the CAN netdevice */
+ if (ioctl(s, SIOCGIFMTU, &ifr) < 0) {
+ perror("SIOCGIFMTU");
+ return 1;
+ }
+ mtu = ifr.ifr_mtu;
+
+ if (mtu != CANFD_MTU) {
+ printf("CAN interface is not CAN FD capable - sorry.\n");
+ return 1;
+ }
+
+ /* interface is ok - try to switch the socket into CAN FD mode */
+ if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
+ &enable_canfd, sizeof(enable_canfd))){
+ printf("error when enabling CAN FD support\n");
+ return 1;
+ }
+
+ /* ensure discrete CAN FD length values 0..8, 12, 16, 20, 24, 32, 64 */
+ frame.len = can_dlc2len(can_len2dlc(frame.len));
+ }
+
+ /* disable default receive filter on this RAW socket */
+ /* This is obsolete as we do not read from the socket at all, but for */
+ /* this reason we can remove the receive list in the Kernel to save a */
+ /* little (really a very little!) CPU usage. */
+ setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("bind");
+ return 1;
+ }
+
+ /* send frame */
+ if (write(s, &frame, required_mtu) != required_mtu) {
+ perror("write");
+ return 1;
+ }
+
+ close(s);
+
+ return 0;
+}
diff --git a/canutils/libcanutils/Kconfig b/canutils/libcanutils/Kconfig
new file mode 100644
index 0000000..922fb1f
--- /dev/null
+++ b/canutils/libcanutils/Kconfig
@@ -0,0 +1,16 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config CANUTILS_LIBCANUTILS
+ bool "CAN-utils support library"
+ default n
+ depends on NET_CAN
+ ---help---
+ Enable the CAN-utils support library ported from
+ https://github.com/linux-can/can-utils
+
+if CANUTILS_LIBCANUTILS
+
+endif
diff --git a/canutils/libcanutils/Make.defs b/canutils/libcanutils/Make.defs
new file mode 100644
index 0000000..7f6c7bc
--- /dev/null
+++ b/canutils/libcanutils/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/canutils/libcanutils/Make.defs
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################/
+
+ifeq ($(CONFIG_CANUTILS_LIBCANUTILS),y)
+CONFIGURED_APPS += $(APPDIR)/canutils/libcanutils
+endif
diff --git a/canutils/libcanutils/Makefile b/canutils/libcanutils/Makefile
new file mode 100644
index 0000000..01e7022
--- /dev/null
+++ b/canutils/libcanutils/Makefile
@@ -0,0 +1,28 @@
+############################################################################
+# apps/canutils/libcanutils/Makefile
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################/
+
+include $(TOPDIR)/Make.defs
+
+# SocketCAN userspace utilities and tools library
+# https://github.com/linux-can/can-utils
+
+CSRCS = lib.c
+
+include $(APPDIR)/Application.mk
diff --git a/canutils/libcanutils/lib.c b/canutils/libcanutils/lib.c
new file mode 100644
index 0000000..e8a645f
--- /dev/null
+++ b/canutils/libcanutils/lib.c
@@ -0,0 +1,609 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * lib.c - library for command line tools
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <li...@vger.kernel.org>
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <sys/socket.h> /* for sa_family_t */
+#include <nuttx/can.h>
+#include <nuttx/can/error.h>
+#include <netpacket/can.h>
+
+#include "lib.h"
+
+#define CANID_DELIM '#'
+#define DATA_SEPERATOR '.'
+
+const char hex_asc_upper[] = "0123456789ABCDEF";
+
+#define hex_asc_upper_lo(x) hex_asc_upper[((x) & 0x0F)]
+#define hex_asc_upper_hi(x) hex_asc_upper[((x) & 0xF0) >> 4]
+
+static inline void put_hex_byte(char *buf, __u8 byte)
+{
+ buf[0] = hex_asc_upper_hi(byte);
+ buf[1] = hex_asc_upper_lo(byte);
+}
+
+static inline void _put_id(char *buf, int end_offset, canid_t id)
+{
+ /* build 3 (SFF) or 8 (EFF) digit CAN identifier */
+ while (end_offset >= 0) {
+ buf[end_offset--] = hex_asc_upper_lo(id);
+ id >>= 4;
+ }
+}
+
+#define put_sff_id(buf, id) _put_id(buf, 2, id)
+#define put_eff_id(buf, id) _put_id(buf, 7, 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};
+
+/* get data length from can_dlc with sanitized can_dlc */
+unsigned char can_dlc2len(unsigned char can_dlc)
+{
+ return dlc2len[can_dlc & 0x0F];
+}
+
+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 */
+ 12, 12, 12, 12, /* 21 - 24 */
+ 13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */
+ 14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */
+ 14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */
+ 15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */
+ 15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */
+
+/* map the sanitized data length to an appropriate data length code */
+unsigned char can_len2dlc(unsigned char len)
+{
+ if (len > 64)
+ return 0xF;
+
+ return len2dlc[len];
+}
+
+unsigned char asc2nibble(char c) {
+
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+
+ return 16; /* error */
+}
+
+int hexstring2data(char *arg, unsigned char *data, int maxdlen) {
+
+ int len = strlen(arg);
+ int i;
+ unsigned char tmp;
+
+ if (!len || len%2 || len > maxdlen*2)
+ return 1;
+
+ memset(data, 0, maxdlen);
+
+ for (i=0; i < len/2; i++) {
+
+ tmp = asc2nibble(*(arg+(2*i)));
+ if (tmp > 0x0F)
+ return 1;
+
+ data[i] = (tmp << 4);
+
+ tmp = asc2nibble(*(arg+(2*i)+1));
+ if (tmp > 0x0F)
+ return 1;
+
+ data[i] |= tmp;
+ }
+
+ return 0;
+}
+
+int parse_canframe(char *cs, struct canfd_frame *cf) {
+ /* documentation see lib.h */
+
+ int i, idx, dlen, len;
+ int maxdlen = CAN_MAX_DLEN;
+ int ret = CAN_MTU;
+ unsigned char tmp;
+
+ len = strlen(cs);
+ //printf("'%s' len %d\n", cs, len);
+
+ memset(cf, 0, sizeof(*cf)); /* init CAN FD frame, e.g. LEN = 0 */
+
+ if (len < 4)
+ return 0;
+
+ if (cs[3] == CANID_DELIM) { /* 3 digits */
+
+ idx = 4;
+ for (i=0; i<3; i++){
+ if ((tmp = asc2nibble(cs[i])) > 0x0F)
+ return 0;
+ cf->can_id |= (tmp << (2-i)*4);
+ }
+
+ } else if (cs[8] == CANID_DELIM) { /* 8 digits */
+
+ idx = 9;
+ for (i=0; i<8; i++){
+ if ((tmp = asc2nibble(cs[i])) > 0x0F)
+ return 0;
+ cf->can_id |= (tmp << (7-i)*4);
+ }
+ if (!(cf->can_id & CAN_ERR_FLAG)) /* 8 digits but no errorframe? */
+ cf->can_id |= CAN_EFF_FLAG; /* then it is an extended frame */
+
+ } else
+ return 0;
+
+ if((cs[idx] == 'R') || (cs[idx] == 'r')){ /* RTR frame */
+ cf->can_id |= CAN_RTR_FLAG;
+
+ /* check for optional DLC value for CAN 2.0B frames */
+ if(cs[++idx] && (tmp = asc2nibble(cs[idx])) <= CAN_MAX_DLC)
+ cf->len = tmp;
+
+ return ret;
+ }
+
+ if (cs[idx] == CANID_DELIM) { /* CAN FD frame escape char '##' */
+
+ maxdlen = CANFD_MAX_DLEN;
+ ret = CANFD_MTU;
+
+ /* CAN FD frame <canid>##<flags><data>* */
+ if ((tmp = asc2nibble(cs[idx+1])) > 0x0F)
+ return 0;
+
+ cf->flags = tmp;
+ idx += 2;
+ }
+
+ for (i=0, dlen=0; i < maxdlen; i++){
+
+ if(cs[idx] == DATA_SEPERATOR) /* skip (optional) separator */
+ idx++;
+
+ if(idx >= len) /* end of string => end of data */
+ break;
+
+ if ((tmp = asc2nibble(cs[idx++])) > 0x0F)
+ return 0;
+ cf->data[i] = (tmp << 4);
+ if ((tmp = asc2nibble(cs[idx++])) > 0x0F)
+ return 0;
+ cf->data[i] |= tmp;
+ dlen++;
+ }
+ cf->len = dlen;
+
+ return ret;
+}
+
+void fprint_canframe(FILE *stream , struct canfd_frame *cf, char *eol, int sep, int maxdlen) {
+ /* documentation see lib.h */
+
+ char buf[CL_CFSZ]; /* max length */
+
+ sprint_canframe(buf, cf, sep, maxdlen);
+ fprintf(stream, "%s", buf);
+ if (eol)
+ fprintf(stream, "%s", eol);
+}
+
+void sprint_canframe(char *buf , struct canfd_frame *cf, int sep, int maxdlen) {
+ /* documentation see lib.h */
+
+ int i,offset;
+ int len = (cf->len > maxdlen) ? maxdlen : cf->len;
+
+ if (cf->can_id & CAN_ERR_FLAG) {
+ put_eff_id(buf, cf->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG));
+ buf[8] = '#';
+ offset = 9;
+ } else if (cf->can_id & CAN_EFF_FLAG) {
+ put_eff_id(buf, cf->can_id & CAN_EFF_MASK);
+ buf[8] = '#';
+ offset = 9;
+ } else {
+ put_sff_id(buf, cf->can_id & CAN_SFF_MASK);
+ buf[3] = '#';
+ offset = 4;
+ }
+
+ /* standard CAN frames may have RTR enabled. There are no ERR frames with RTR */
+ if (maxdlen == CAN_MAX_DLEN && cf->can_id & CAN_RTR_FLAG) {
+ buf[offset++] = 'R';
+ /* print a given CAN 2.0B DLC if it's not zero */
+ if (cf->len && cf->len <= CAN_MAX_DLC)
+ buf[offset++] = hex_asc_upper_lo(cf->len);
+
+ buf[offset] = 0;
+ return;
+ }
+
+ if (maxdlen == CANFD_MAX_DLEN) {
+ /* add CAN FD specific escape char and flags */
+ buf[offset++] = '#';
+ buf[offset++] = hex_asc_upper_lo(cf->flags);
+ if (sep && len)
+ buf[offset++] = '.';
+ }
+
+ for (i = 0; i < len; i++) {
+ put_hex_byte(buf + offset, cf->data[i]);
+ offset += 2;
+ if (sep && (i+1 < len))
+ buf[offset++] = '.';
+ }
+
+ buf[offset] = 0;
+}
+
+void fprint_long_canframe(FILE *stream , struct canfd_frame *cf, char *eol, int view, int maxdlen) {
+ /* documentation see lib.h */
+
+ char buf[CL_LONGCFSZ];
+
+ sprint_long_canframe(buf, cf, view, maxdlen);
+ fprintf(stream, "%s", buf);
+ if ((view & CANLIB_VIEW_ERROR) && (cf->can_id & CAN_ERR_FLAG)) {
+ snprintf_can_error_frame(buf, sizeof(buf), cf, "\n\t");
+ fprintf(stream, "\n\t%s", buf);
+ }
+ if (eol)
+ fprintf(stream, "%s", eol);
+}
+
+void sprint_long_canframe(char *buf , struct canfd_frame *cf, int view, int maxdlen) {
+ /* documentation see lib.h */
+
+ int i, j, dlen, offset;
+ int len = (cf->len > maxdlen)? maxdlen : cf->len;
+
+ /* initialize space for CAN-ID and length information */
+ memset(buf, ' ', 15);
+
+ if (cf->can_id & CAN_ERR_FLAG) {
+ put_eff_id(buf, cf->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG));
+ offset = 10;
+ } else if (cf->can_id & CAN_EFF_FLAG) {
+ put_eff_id(buf, cf->can_id & CAN_EFF_MASK);
+ offset = 10;
+ } else {
+ if (view & CANLIB_VIEW_INDENT_SFF) {
+ put_sff_id(buf + 5, cf->can_id & CAN_SFF_MASK);
+ offset = 10;
+ } else {
+ put_sff_id(buf, cf->can_id & CAN_SFF_MASK);
+ offset = 5;
+ }
+ }
+
+ /* The len value is sanitized by maxdlen (see above) */
+ if (maxdlen == CAN_MAX_DLEN) {
+ buf[offset + 1] = '[';
+ buf[offset + 2] = len + '0';
+ buf[offset + 3] = ']';
+
+ /* standard CAN frames may have RTR enabled */
+ if (cf->can_id & CAN_RTR_FLAG) {
+ sprintf(buf+offset+5, " remote request");
+ return;
+ }
+ } else {
+ buf[offset] = '[';
+ buf[offset + 1] = (len/10) + '0';
+ buf[offset + 2] = (len%10) + '0';
+ buf[offset + 3] = ']';
+ }
+ offset += 5;
+
+ if (view & CANLIB_VIEW_BINARY) {
+ dlen = 9; /* _10101010 */
+ if (view & CANLIB_VIEW_SWAP) {
+ for (i = len - 1; i >= 0; i--) {
+ buf[offset++] = (i == len-1)?' ':SWAP_DELIMITER;
+ for (j = 7; j >= 0; j--)
+ buf[offset++] = (1<<j & cf->data[i])?'1':'0';
+ }
+ } else {
+ for (i = 0; i < len; i++) {
+ buf[offset++] = ' ';
+ for (j = 7; j >= 0; j--)
+ buf[offset++] = (1<<j & cf->data[i])?'1':'0';
+ }
+ }
+ } else {
+ dlen = 3; /* _AA */
+ if (view & CANLIB_VIEW_SWAP) {
+ for (i = len - 1; i >= 0; i--) {
+ if (i == len-1)
+ buf[offset++] = ' ';
+ else
+ buf[offset++] = SWAP_DELIMITER;
+
+ put_hex_byte(buf + offset, cf->data[i]);
+ offset += 2;
+ }
+ } else {
+ for (i = 0; i < len; i++) {
+ buf[offset++] = ' ';
+ put_hex_byte(buf + offset, cf->data[i]);
+ offset += 2;
+ }
+ }
+ }
+
+ buf[offset] = 0; /* terminate string */
+
+ /*
+ * The ASCII & ERRORFRAME output is put at a fixed len behind the data.
+ * For now we support ASCII output only for payload length up to 8 bytes.
+ * Does it make sense to write 64 ASCII byte behind 64 ASCII HEX data on the console?
+ */
+ if (len > CAN_MAX_DLEN)
+ return;
+
+ if (cf->can_id & CAN_ERR_FLAG)
+ sprintf(buf+offset, "%*s", dlen*(8-len)+13, "ERRORFRAME");
+ else if (view & CANLIB_VIEW_ASCII) {
+ j = dlen*(8-len)+4;
+ if (view & CANLIB_VIEW_SWAP) {
+ sprintf(buf+offset, "%*s", j, "`");
+ offset += j;
+ for (i = len - 1; i >= 0; i--)
+ if ((cf->data[i] > 0x1F) && (cf->data[i] < 0x7F))
+ buf[offset++] = cf->data[i];
+ else
+ buf[offset++] = '.';
+
+ sprintf(buf+offset, "`");
+ } else {
+ sprintf(buf+offset, "%*s", j, "'");
+ offset += j;
+ for (i = 0; i < len; i++)
+ if ((cf->data[i] > 0x1F) && (cf->data[i] < 0x7F))
+ buf[offset++] = cf->data[i];
+ else
+ buf[offset++] = '.';
+
+ sprintf(buf+offset, "'");
+ }
+ }
+}
+
+static const char *error_classes[] = {
+ "tx-timeout",
+ "lost-arbitration",
+ "controller-problem",
+ "protocol-violation",
+ "transceiver-status",
+ "no-acknowledgement-on-tx",
+ "bus-off",
+ "bus-error",
+ "restarted-after-bus-off",
+};
+
+static const char *controller_problems[] = {
+ "rx-overflow",
+ "tx-overflow",
+ "rx-error-warning",
+ "tx-error-warning",
+ "rx-error-passive",
+ "tx-error-passive",
+ "back-to-error-active",
+};
+
+static const char *protocol_violation_types[] = {
+ "single-bit-error",
+ "frame-format-error",
+ "bit-stuffing-error",
+ "tx-dominant-bit-error",
+ "tx-recessive-bit-error",
+ "bus-overload",
+ "active-error",
+ "error-on-tx",
+};
+
+static const char *protocol_violation_locations[] = {
+ "unspecified",
+ "unspecified",
+ "id.28-to-id.21",
+ "start-of-frame",
+ "bit-srtr",
+ "bit-ide",
+ "id.20-to-id.18",
+ "id.17-to-id.13",
+ "crc-sequence",
+ "reserved-bit-0",
+ "data-field",
+ "data-length-code",
+ "bit-rtr",
+ "reserved-bit-1",
+ "id.4-to-id.0",
+ "id.12-to-id.5",
+ "unspecified",
+ "active-error-flag",
+ "intermission",
+ "tolerate-dominant-bits",
+ "unspecified",
+ "unspecified",
+ "passive-error-flag",
+ "error-delimiter",
+ "crc-delimiter",
+ "acknowledge-slot",
+ "end-of-frame",
+ "acknowledge-delimiter",
+ "overload-flag",
+ "unspecified",
+ "unspecified",
+ "unspecified",
+};
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+static int snprintf_error_data(char *buf, size_t len, uint8_t err,
+ const char **arr, int arr_len)
+{
+ int i, n = 0, count = 0;
+
+ if (!err || len <= 0)
+ return 0;
+
+ for (i = 0; i < arr_len; i++) {
+ if (err & (1 << i)) {
+ if (count)
+ n += snprintf(buf + n, len - n, ",");
+ n += snprintf(buf + n, len - n, "%s", arr[i]);
+ count++;
+ }
+ }
+
+ return n;
+}
+
+static int snprintf_error_lostarb(char *buf, size_t len, const struct canfd_frame *cf)
+{
+ if (len <= 0)
+ return 0;
+ return snprintf(buf, len, "{at bit %d}", cf->data[0]);
+}
+
+static int snprintf_error_ctrl(char *buf, size_t len, const struct canfd_frame *cf)
+{
+ int n = 0;
+
+ if (len <= 0)
+ return 0;
+
+ n += snprintf(buf + n, len - n, "{");
+ n += snprintf_error_data(buf + n, len - n, cf->data[1],
+ controller_problems,
+ ARRAY_SIZE(controller_problems));
+ n += snprintf(buf + n, len - n, "}");
+
+ return n;
+}
+
+static int snprintf_error_prot(char *buf, size_t len, const struct canfd_frame *cf)
+{
+ int n = 0;
+
+ if (len <= 0)
+ return 0;
+
+ n += snprintf(buf + n, len - n, "{{");
+ n += snprintf_error_data(buf + n, len - n, cf->data[2],
+ protocol_violation_types,
+ ARRAY_SIZE(protocol_violation_types));
+ n += snprintf(buf + n, len - n, "}{");
+ if (cf->data[3] > 0 &&
+ cf->data[3] < ARRAY_SIZE(protocol_violation_locations))
+ n += snprintf(buf + n, len - n, "%s",
+ protocol_violation_locations[cf->data[3]]);
+ n += snprintf(buf + n, len - n, "}}");
+
+ return n;
+}
+
+void snprintf_can_error_frame(char *buf, size_t len, const struct canfd_frame *cf,
+ const char* sep)
+{
+ canid_t class, mask;
+ int i, n = 0, classes = 0;
+ char *defsep = ",";
+
+ if (!(cf->can_id & CAN_ERR_FLAG))
+ return;
+
+ class = cf->can_id & CAN_EFF_MASK;
+ if (class > (1 << ARRAY_SIZE(error_classes))) {
+ fprintf(stderr, "Error class %#x is invalid\n", class);
+ return;
+ }
+
+ if (!sep)
+ sep = defsep;
+
+ for (i = 0; i < (int)ARRAY_SIZE(error_classes); i++) {
+ mask = 1 << i;
+ if (class & mask) {
+ if (classes)
+ n += snprintf(buf + n, len - n, "%s", sep);
+ n += snprintf(buf + n, len - n, "%s", error_classes[i]);
+ if (mask == CAN_ERR_LOSTARB)
+ n += snprintf_error_lostarb(buf + n, len - n,
+ cf);
+ if (mask == CAN_ERR_CRTL)
+ n += snprintf_error_ctrl(buf + n, len - n, cf);
+ if (mask == CAN_ERR_PROT)
+ n += snprintf_error_prot(buf + n, len - n, cf);
+ classes++;
+ }
+ }
+
+ if (cf->data[6] || cf->data[7]) {
+ n += snprintf(buf + n, len - n, "%s", sep);
+ n += snprintf(buf + n, len - n, "error-counter-tx-rx{{%d}{%d}}",
+ cf->data[6], cf->data[7]);
+ }
+}
diff --git a/canutils/libcanutils/lib.h b/canutils/libcanutils/lib.h
new file mode 100644
index 0000000..24fd41e
--- /dev/null
+++ b/canutils/libcanutils/lib.h
@@ -0,0 +1,222 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * lib.h - library include for command line tools
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <li...@vger.kernel.org>
+ *
+ */
+
+#ifndef CAN_UTILS_LIB_H
+#define CAN_UTILS_LIB_H
+
+#include <stdio.h>
+
+/* Compatibility for NuttX */
+typedef uint8_t __u8;
+typedef uint32_t __u32;
+
+/* buffer sizes for CAN frame string representations */
+
+#define CL_ID (sizeof("12345678##1"))
+#define CL_DATA sizeof(".AA")
+#define CL_BINDATA sizeof(".10101010")
+
+ /* CAN FD ASCII hex short representation with DATA_SEPERATORs */
+#define CL_CFSZ (2*CL_ID + 64*CL_DATA)
+
+/* CAN FD ASCII hex long representation with binary output */
+#define CL_LONGCFSZ (2*CL_ID + sizeof(" [255] ") + (64*CL_BINDATA))
+
+/* CAN DLC to real data length conversion helpers especially for CAN FD */
+
+/* get data length from can_dlc with sanitized can_dlc */
+unsigned char can_dlc2len(unsigned char can_dlc);
+
+/* map the sanitized data length to an appropriate data length code */
+unsigned char can_len2dlc(unsigned char len);
+
+unsigned char asc2nibble(char c);
+/*
+ * Returns the decimal value of a given ASCII hex character.
+ *
+ * While 0..9, a..f, A..F are valid ASCII hex characters.
+ * On invalid characters the value 16 is returned for error handling.
+ */
+
+int hexstring2data(char *arg, unsigned char *data, int maxdlen);
+/*
+ * Converts a given ASCII hex string to a (binary) byte string.
+ *
+ * A valid ASCII hex string consists of an even number of up to 16 chars.
+ * Leading zeros '00' in the ASCII hex string are interpreted.
+ *
+ * Examples:
+ *
+ * "1234" => data[0] = 0x12, data[1] = 0x34
+ * "001234" => data[0] = 0x00, data[1] = 0x12, data[2] = 0x34
+ *
+ * Return values:
+ * 0 = success
+ * 1 = error (in length or the given characters are no ASCII hex characters)
+ *
+ * Remark: The not written data[] elements are initialized with zero.
+ *
+ */
+
+int parse_canframe(char *cs, struct canfd_frame *cf);
+/*
+ * Transfers a valid ASCII string describing a CAN frame into struct canfd_frame.
+ *
+ * CAN 2.0 frames
+ * - string layout <can_id>#{R{len}|data}
+ * - {data} has 0 to 8 hex-values that can (optionally) be separated by '.'
+ * - {len} can take values from 0 to 8 and can be omitted if zero
+ * - return value on successful parsing: CAN_MTU
+ *
+ * CAN FD frames
+ * - string layout <can_id>##<flags>{data}
+ * - <flags> a single ASCII Hex value (0 .. F) which defines canfd_frame.flags
+ * - {data} has 0 to 64 hex-values that can (optionally) be separated by '.'
+ * - return value on successful parsing: CANFD_MTU
+ *
+ * Return value on detected problems: 0
+ *
+ * <can_id> can have 3 (standard frame format) or 8 (extended frame format)
+ * hexadecimal chars
+ *
+ *
+ * Examples:
+ *
+ * 123# -> standard CAN-Id = 0x123, len = 0
+ * 12345678# -> extended CAN-Id = 0x12345678, len = 0
+ * 123#R -> standard CAN-Id = 0x123, len = 0, RTR-frame
+ * 123#R0 -> standard CAN-Id = 0x123, len = 0, RTR-frame
+ * 123#R7 -> standard CAN-Id = 0x123, len = 7, RTR-frame
+ * 7A1#r -> standard CAN-Id = 0x7A1, len = 0, RTR-frame
+ *
+ * 123#00 -> standard CAN-Id = 0x123, len = 1, data[0] = 0x00
+ * 123#1122334455667788 -> standard CAN-Id = 0x123, len = 8
+ * 123#11.22.33.44.55.66.77.88 -> standard CAN-Id = 0x123, len = 8
+ * 123#11.2233.44556677.88 -> standard CAN-Id = 0x123, len = 8
+ * 32345678#112233 -> error frame with CAN_ERR_FLAG (0x2000000) set
+ *
+ * 123##0112233 -> CAN FD frame standard CAN-Id = 0x123, flags = 0, len = 3
+ * 123##1112233 -> CAN FD frame, flags = CANFD_BRS, len = 3
+ * 123##2112233 -> CAN FD frame, flags = CANFD_ESI, len = 3
+ * 123##3 -> CAN FD frame, flags = (CANFD_ESI | CANFD_BRS), len = 0
+ * ^^
+ * CAN FD extension to handle the canfd_frame.flags content
+ *
+ * Simple facts on this compact ASCII CAN frame representation:
+ *
+ * - 3 digits: standard frame format
+ * - 8 digits: extendend frame format OR error frame
+ * - 8 digits with CAN_ERR_FLAG (0x2000000) set: error frame
+ * - an error frame is never a RTR frame
+ * - CAN FD frames do not have a RTR bit
+ */
+
+void fprint_canframe(FILE *stream , struct canfd_frame *cf, char *eol, int sep, int maxdlen);
+void sprint_canframe(char *buf , struct canfd_frame *cf, int sep, int maxdlen);
+/*
+ * Creates a CAN frame hexadecimal output in compact format.
+ * The CAN data[] is separated by '.' when sep != 0.
+ *
+ * The type of the CAN frame (CAN 2.0 / CAN FD) is specified by maxdlen:
+ * maxdlen = 8 -> CAN2.0 frame
+ * maxdlen = 64 -> CAN FD frame
+ *
+ * 12345678#112233 -> extended CAN-Id = 0x12345678, len = 3, data, sep = 0
+ * 12345678#R -> extended CAN-Id = 0x12345678, RTR, len = 0
+ * 12345678#R5 -> extended CAN-Id = 0x12345678, RTR, len = 5
+ * 123#11.22.33.44.55.66.77.88 -> standard CAN-Id = 0x123, dlc = 8, sep = 1
+ * 32345678#112233 -> error frame with CAN_ERR_FLAG (0x2000000) set
+ * 123##0112233 -> CAN FD frame standard CAN-Id = 0x123, flags = 0, len = 3
+ * 123##2112233 -> CAN FD frame, flags = CANFD_ESI, len = 3
+ *
+ * Examples:
+ *
+ * fprint_canframe(stdout, &frame, "\n", 0); // with eol to STDOUT
+ * fprint_canframe(stderr, &frame, NULL, 0); // no eol to STDERR
+ *
+ */
+
+#define CANLIB_VIEW_ASCII 0x1
+#define CANLIB_VIEW_BINARY 0x2
+#define CANLIB_VIEW_SWAP 0x4
+#define CANLIB_VIEW_ERROR 0x8
+#define CANLIB_VIEW_INDENT_SFF 0x10
+
+#define SWAP_DELIMITER '`'
+
+void fprint_long_canframe(FILE *stream , struct canfd_frame *cf, char *eol, int view, int maxdlen);
+void sprint_long_canframe(char *buf , struct canfd_frame *cf, int view, int maxdlen);
+/*
+ * Creates a CAN frame hexadecimal output in user readable format.
+ *
+ * The type of the CAN frame (CAN 2.0 / CAN FD) is specified by maxdlen:
+ * maxdlen = 8 -> CAN2.0 frame
+ * maxdlen = 64 -> CAN FD frame
+ *
+ * 12345678 [3] 11 22 33 -> extended CAN-Id = 0x12345678, dlc = 3, data
+ * 12345678 [0] remote request -> extended CAN-Id = 0x12345678, RTR
+ * 14B0DC51 [8] 4A 94 E8 2A EC 58 55 62 'J..*.XUb' -> (with ASCII output)
+ * 20001111 [7] C6 23 7B 32 69 98 3C ERRORFRAME -> (CAN_ERR_FLAG set)
+ * 12345678 [03] 11 22 33 -> CAN FD with extended CAN-Id = 0x12345678, dlc = 3
+ *
+ * 123 [3] 11 22 33 -> CANLIB_VIEW_INDENT_SFF == 0
+ * 123 [3] 11 22 33 -> CANLIB_VIEW_INDENT_SFF == set
+ *
+ * Examples:
+ *
+ * // CAN FD frame with eol to STDOUT
+ * fprint_long_canframe(stdout, &frame, "\n", 0, CANFD_MAX_DLEN);
+ *
+ * // CAN 2.0 frame without eol to STDERR
+ * fprint_long_canframe(stderr, &frame, NULL, 0, CAN_MAX_DLEN);
+ *
+ */
+
+void 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.
+ */
+
+#endif
diff --git a/canutils/libcanutils/terminal.h b/canutils/libcanutils/terminal.h
new file mode 100644
index 0000000..198b48e
--- /dev/null
+++ b/canutils/libcanutils/terminal.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <li...@vger.kernel.org>
+ *
+ */
+
+#ifndef TERMINAL_H
+#define TERMINAL_H
+
+/* reset to default */
+
+#define ATTRESET "\33[0m"
+
+/* attributes */
+
+#define ATTBOLD "\33[1m"
+#define ATTUNDERLINE "\33[4m"
+#define ATTBLINK "\33[5m"
+#define ATTINVERSE "\33[7m"
+#define ATTINVISIBLE "\33[8m"
+
+/* foreground colors */
+
+#define FGBLACK "\33[30m"
+#define FGRED "\33[31m"
+#define FGGREEN "\33[32m"
+#define FGYELLOW "\33[33m"
+#define FGBLUE "\33[34m"
+#define FGMAGENTA "\33[35m"
+#define FGCYAN "\33[36m"
+#define FGWHITE "\33[37m"
+
+/* background colors */
+
+#define BGBLACK "\33[40m"
+#define BGRED "\33[41m"
+#define BGGREEN "\33[42m"
+#define BGYELLOW "\33[43m"
+#define BGBLUE "\33[44m"
+#define BGMAGENTA "\33[45m"
+#define BGCYAN "\33[46m"
+#define BGWHITE "\33[47m"
+
+/* cursor */
+
+#define CSR_HOME "\33[H"
+#define CSR_UP "\33[A"
+#define CSR_DOWN "\33[B"
+#define CSR_RIGHT "\33[C"
+#define CSR_LEFT "\33[D"
+
+#define CSR_HIDE "\33[?25l"
+#define CSR_SHOW "\33[?25h"
+
+/* clear screen */
+
+#define CLR_SCREEN "\33[2J"
+
+#endif /* TERMINAL_H */