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 */