You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2021/01/21 14:12:23 UTC
[incubator-nuttx-apps] 01/02: canutils/slcan: Add SLCAN utility
This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx-apps.git
commit e12e8016f24fb31853f1b7263c3f32a17b3abf01
Author: Peter van der Perk <pe...@nxp.com>
AuthorDate: Thu Jan 14 17:01:50 2021 +0100
canutils/slcan: Add SLCAN utility
---
canutils/slcan/Kconfig | 21 ++
canutils/slcan/Make.defs | 24 +++
canutils/slcan/Makefile | 34 ++++
canutils/slcan/slcan.c | 504 +++++++++++++++++++++++++++++++++++++++++++++++
canutils/slcan/slcan.h | 47 +++++
5 files changed, 630 insertions(+)
diff --git a/canutils/slcan/Kconfig b/canutils/slcan/Kconfig
new file mode 100644
index 0000000..39b4eff
--- /dev/null
+++ b/canutils/slcan/Kconfig
@@ -0,0 +1,21 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config CANUTILS_SLCAN
+ tristate "SocketCAN slcan tool"
+ default n
+ depends on NET_CAN
+ ---help---
+ Enable the SocketCAN slcan tool
+
+if CANUTILS_SLCAN
+
+config SLCAN_TRACE
+ bool "Print trace output"
+ default y
+ ---help---
+ Debug trace output
+
+endif
diff --git a/canutils/slcan/Make.defs b/canutils/slcan/Make.defs
new file mode 100644
index 0000000..dd6f6db
--- /dev/null
+++ b/canutils/slcan/Make.defs
@@ -0,0 +1,24 @@
+############################################################################
+# apps/canutils/slcan/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_SLCAN),)
+CONFIGURED_APPS += $(APPDIR)/canutils/slcan
+endif
diff --git a/canutils/slcan/Makefile b/canutils/slcan/Makefile
new file mode 100644
index 0000000..974d68e
--- /dev/null
+++ b/canutils/slcan/Makefile
@@ -0,0 +1,34 @@
+############################################################################
+# apps/canutils/slcan/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 $(APPDIR)/Make.defs
+
+# Network packet socket example
+
+PROGNAME = slcan
+PRIORITY = SCHED_PRIORITY_DEFAULT
+STACKSIZE = 3048
+MODULE = $(CONFIG_CANUTILS_SLCAN)
+
+CSRCS =
+
+MAINSRC = slcan.c
+
+include $(APPDIR)/Application.mk
diff --git a/canutils/slcan/slcan.c b/canutils/slcan/slcan.c
new file mode 100644
index 0000000..b02542c
--- /dev/null
+++ b/canutils/slcan/slcan.c
@@ -0,0 +1,504 @@
+/****************************************************************************
+ * apps/canutils/slcan/slcan.c
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <nuttx/clock.h>
+#include <sched.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <net/if.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <net/if.h>
+#include <termios.h>
+#include <nuttx/can.h>
+#include <netpacket/can.h>
+
+#include "slcan.h"
+
+/****************************************************************************
+ * Defines
+ ****************************************************************************/
+
+#define DEFAULT_PRIORITY 100
+#define DEFAULT_STACK_SIZE 2048
+
+#ifdef CONFIG_SLCAN_TRACE
+# define DEBUG 1
+#else
+# define DEBUG 0
+#endif
+
+#define debug_print(fmt, ...) \
+ do \
+ { \
+ if (DEBUG) \
+ fprintf(stdout, fmt, ##__VA_ARGS__); \
+ } \
+ while (0)
+
+/****************************************************************************
+ * private data
+ ****************************************************************************/
+
+#ifdef CONFIG_SLCAN_TRACE
+static char opening[] = "starting slcan\n";
+#else
+static char opening[] = "";
+#endif
+
+static void ok_return(int fd)
+{
+ write(fd, "\r", 1);
+}
+
+static void fail_return(int fd)
+{
+ write(fd, "\a", 1); /* BELL return for error */
+}
+
+static int readlinebuffer(int fd, char *buf, int maxlen)
+{
+ size_t n;
+ int meslen = 0;
+ int noeol = 1;
+ uint8_t ch;
+
+ while ((meslen < maxlen) && noeol)
+ {
+ n = read(fd, &ch, 1);
+ if (n > 0)
+ {
+ /* valid input */
+
+ if (ch == '\r')
+ {
+ noeol = 0;
+ *buf = '\0';
+ }
+ else
+ {
+ *buf++ = ch;
+ meslen++;
+ }
+ }
+ else
+ {
+ return (meslen);
+ }
+ }
+ return (meslen);
+}
+
+static int caninit(char *candev, int *s, struct sockaddr_can *addr,
+ char *ctrlmsg, struct canfd_frame *frame,
+ struct msghdr *msg, struct iovec *iov)
+{
+ struct ifreq ifr;
+
+ debug_print("slcanBus\n");
+ if ((*s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
+ {
+ debug_print("Error opening CAN socket\n");
+ return -1;
+ }
+ strncpy(ifr.ifr_name, candev, 4);
+ ifr.ifr_name[4] = '\0';
+ ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name);
+ if (!ifr.ifr_ifindex)
+ {
+ debug_print("error finding index %s\n", candev);
+ return -1;
+ }
+ memset(addr, 0, sizeof(struct sockaddr));
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = ifr.ifr_ifindex;
+ setsockopt(*s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
+
+ if (bind(*s, (struct sockaddr *)addr, sizeof(struct sockaddr)) < 0)
+ {
+ debug_print("bind error\n");
+ return -1;
+ }
+
+ iov->iov_base = frame;
+ msg->msg_name = addr;
+ msg->msg_iov = iov;
+ msg->msg_iovlen = 1;
+ msg->msg_control = ctrlmsg;
+
+ /* CAN interface ready to be used */
+
+ debug_print("CAN socket open\n");
+
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: slcan_main
+ ****************************************************************************/
+
+int main(int argc, char *argv[])
+{
+ /* UART */
+
+ char buf[31];
+ int mode = 0;
+ size_t n;
+ int canspeed = 1000000; /* default to 1MBps */
+ int fd; /* UART slcan channel */
+
+ /* CAN */
+
+ int s, nbytes, i, ret, reccount;
+ struct sockaddr_can addr;
+ struct canfd_frame frame;
+ struct msghdr msg;
+ struct iovec iov;
+ fd_set rdfs;
+ char ctrlmsg[CMSG_SPACE(sizeof(struct timeval) +
+ 3 * sizeof(struct timespec) + sizeof(int))];
+ char sbuf[40], *sbp;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "Usage: slcan <can device> <uart device>\n");
+ fflush(stderr);
+ return -1;
+ }
+
+ char *chrdev = argv[2];
+ char *candev = argv[1];
+
+ debug_print("Starting slcan on NuttX\n");
+ fd = open(chrdev, O_RDWR | O_BINARY);
+ if (fd < 0)
+ {
+ fprintf(stderr, "Failed to open serial channel %s\n", chrdev);
+ fflush(stderr);
+ return -1;
+ }
+ else
+ {
+ /* Create CAN socket */
+
+ if (caninit(candev, &s, &addr, &ctrlmsg[0], &frame, &msg, &iov) < 0)
+ {
+ fprintf(stderr, "Failed to open CAN socket %s\n", candev);
+ fflush(stderr);
+ close(fd);
+ return -1;
+ }
+
+ /* serial interface active */
+
+ debug_print("Serial interface open %s\n", chrdev);
+ write(fd, opening, (sizeof(opening) - 1));
+
+ while (mode < 100)
+ {
+ /* Setup ooll */
+
+ FD_ZERO(&rdfs);
+ FD_SET(s, &rdfs); /* CAN Socket */
+ FD_SET(fd, &rdfs); /* UART */
+
+ if ((ret = select(s + 1, &rdfs, NULL, NULL, NULL)) <= 0)
+ {
+ continue;
+ }
+
+ if (FD_ISSET(s, &rdfs))
+ {
+ /* CAN received new message in socketCAN input */
+
+ iov.iov_len = sizeof(frame);
+ msg.msg_namelen = sizeof(addr);
+ msg.msg_controllen = sizeof(ctrlmsg);
+ msg.msg_flags = 0;
+ nbytes = recvmsg(s, &msg, 0);
+
+ if (nbytes == CAN_MTU)
+ {
+ reccount++;
+ debug_print("R%d, Id:0x%X\n", reccount, frame.can_id);
+ if (frame.can_id & CAN_EFF_FLAG)
+ {
+ /* 29 bit address */
+
+ frame.can_id = frame.can_id & ~CAN_EFF_FLAG;
+ sprintf(sbuf, "T%08X%d", frame.can_id, frame.len);
+ sbp = &sbuf[10];
+ }
+ else
+ {
+ /* 11 bit address */
+
+ sprintf(sbuf, "t%03X%d", frame.can_id, frame.len);
+ sbp = &sbuf[5];
+ }
+
+ for (i = 0; i < frame.len; i++)
+ {
+ sprintf(sbp, "%02X", frame.data[i]);
+ sbp += 2;
+ }
+
+ *sbp++ = '\r';
+ *sbp = '\0';
+ write(fd, sbuf, strlen(sbuf));
+ }
+ }
+
+ if (FD_ISSET(fd, &rdfs))
+ {
+ /* UART receive */
+
+ n = readlinebuffer(fd, buf, 30);
+
+ switch (mode)
+ {
+ case 0: /* CAN channel not open */
+ if (n > 0)
+ {
+ if (buf[0] == 'F')
+ {
+ /* return clear flags */
+
+ write(fd, "F00\r", 4);
+ }
+ else if (buf[0] == 'O')
+ {
+ /* open CAN interface */
+
+ mode = 1;
+ printf("Open interface\n");
+ ok_return(fd);
+ }
+ else if (buf[0] == 'S')
+ {
+ /* set CAN interface speed */
+
+ switch (buf[1])
+ {
+ case '0':
+ canspeed = 10000;
+ break;
+ case '1':
+ canspeed = 20000;
+ break;
+ case '2':
+ canspeed = 50000;
+ break;
+ case '3':
+ canspeed = 100000;
+ break;
+ case '4':
+ canspeed = 125000;
+ break;
+ case '5':
+ canspeed = 250000;
+ break;
+ case '6':
+ canspeed = 500000;
+ break;
+ case '7':
+ canspeed = 800000;
+ break;
+ case '8': /* set speed to 1Mbps */
+ canspeed = 1000000;
+ break;
+ default:
+ break;
+ }
+
+ struct ifreq ifr;
+
+ /* set the device name */
+
+ strncpy(ifr.ifr_name, argv[1], IFNAMSIZ - 1);
+ ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+
+ ifr.ifr_ifru.ifru_can_data.arbi_bitrate =
+ canspeed / 1000; /* Convert bit/s to kbit/s */
+ ifr.ifr_ifru.ifru_can_data.arbi_samplep = 80;
+
+ if (ioctl(s, SIOCSCANBITRATE, &ifr) < 0)
+ {
+ printf("set speed %d failed\n", canspeed);
+ fail_return(fd);
+ }
+ else
+ {
+ printf("set speed %d\n", canspeed);
+ ok_return(fd);
+ }
+ }
+ else
+ {
+ /* whatever */
+
+ ok_return(fd);
+ }
+ }
+ break;
+ case 1: /* CAN task running open interface */
+ if (n > 0)
+ {
+ if (buf[0] == 'C')
+ {
+ /* close interface */
+
+ mode = 0;
+ debug_print("Close interface\n");
+ ok_return(fd);
+ }
+ else if (buf[0] == 'T')
+ {
+ /* Transmit an extended 29 bit CAN frame */
+
+ char tbuf[9];
+ int idval, val;
+
+ /* get 29bit CAN ID */
+
+ strncpy(tbuf, &buf[1], 8);
+ tbuf[8] = '\0';
+ sscanf(tbuf, "%x", &idval);
+
+ frame.len = buf[9] - '0'; /* get byte count */
+
+ /* get canmessage */
+
+ for (i = 0; i < frame.len; i++)
+ {
+ tbuf[0] = buf[10 + (2 * i)];
+ tbuf[1] = buf[11 + (2 * i)];
+ tbuf[2] = '\0';
+ sscanf(tbuf, "%x", &val);
+ frame.data[i] = val & 0xff;
+ }
+
+ debug_print("Transmitt: 0x%X ", idval);
+ for (i = 0; i < frame.len; i++)
+ {
+ debug_print("0x%02X ", frame.data[i]);
+ }
+
+ debug_print("\n");
+
+ frame.can_id = idval | CAN_EFF_FLAG; /* 29 bit */
+
+ if (write(s, &frame, CAN_MTU) != CAN_MTU)
+ {
+ debug_print("transmitt error\n");
+
+ /* TODO update error flags */
+ }
+
+ ok_return(fd);
+ }
+ else if (buf[0] == 't')
+ {
+ /* Transmit an 11 bit CAN frame */
+
+ char tbuf[9];
+ int idval, val;
+
+ /* get 11bit CAN ID */
+
+ strncpy(tbuf, &buf[1], 3);
+ tbuf[3] = '\0';
+ sscanf(tbuf, "%x", &idval);
+
+ frame.len = buf[4] - '0'; /* get byte count */
+
+ /* get canmessage */
+
+ for (i = 0; i < frame.len; i++)
+ {
+ tbuf[0] = buf[5 + (2 * i)];
+ tbuf[1] = buf[6 + (2 * i)];
+ tbuf[2] = '\0';
+ sscanf(tbuf, "%x", &val);
+ frame.data[i] = val & 0xff;
+ }
+
+ debug_print("Transmitt: 0x%X ", idval);
+ for (i = 0; i < frame.len; i++)
+ {
+ debug_print("0x%02X ", frame.data[i]);
+ }
+
+ debug_print("\n");
+
+ frame.can_id = idval; /* 11 bit address command */
+
+ if (write(s, &frame, CAN_MTU) != CAN_MTU)
+ {
+ debug_print("transmitt error\n");
+
+ /* TODO update error flags */
+ }
+
+ ok_return(fd);
+ }
+ else
+ {
+ /* whatever */
+
+ ok_return(fd);
+ }
+ }
+ break;
+ default: /* should not happen */
+ mode = 100;
+ break;
+ }
+ }
+ }
+
+ close(fd);
+ close(s);
+ }
+
+ return 0;
+}
diff --git a/canutils/slcan/slcan.h b/canutils/slcan/slcan.h
new file mode 100644
index 0000000..a40c9bc
--- /dev/null
+++ b/canutils/slcan/slcan.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+ * apps/canutils/slcan/slcan.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef SLCAN_H
+#define SLCAN_H
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* S6 - CAN speed 500 kBit/s
+ * S8 - CAN speed 1 Mbit/s
+ * O - open channel
+ * C - close channel
+ * C
+ * S8
+ * O
+ * F -> return status flags
+ */
+#define SLCAN_REC_FIFO_FULL (1 << 0)
+#define SLCAN_SND_FIFO_FULL (1 << 1)
+#define SLCAN_ERROR_WARN (1 << 2)
+#define SLCAN_DATA_OVERRUN (1 << 3)
+#define SLCAN_ERROR_PASSIVE (1 << 5)
+#define SLCAN_ARBITRATION_LOST (1 << 6)
+#define SLCAN_BUS_ERROR (1 << 7)
+
+/* T1401557F8f601000000▒0 */
+
+#endif /* SLCAN_H */