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,
+						&timestamping_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,
+					       &timestamp_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 */