nuttx-apps/canutils/slcan/slcan.c

505 lines
15 KiB
C

/****************************************************************************
* 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 <syslog.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) \
syslog(LOG_DEBUG, 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)
{
syslog(LOG_ERR, "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)
{
syslog(LOG_ERR, "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)
{
syslog(LOG_ERR, "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)
{
syslog(LOG_ERR, "Failed to open serial channel %s\n", chrdev);
return -1;
}
else
{
/* Create CAN socket */
if (caninit(candev, &s, &addr, &ctrlmsg[0], &frame, &msg, &iov) < 0)
{
syslog(LOG_ERR, "Failed to open CAN socket %s\n", candev);
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%lX\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%08lX%d", frame.can_id, frame.len);
sbp = &sbuf[10];
}
else
{
/* 11 bit address */
sprintf(sbuf, "t%03lX%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;
debug_print("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)
{
syslog(LOG_ERR, "set speed %d failed\n",
canspeed);
fail_return(fd);
}
else
{
debug_print("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)
{
syslog(LOG_ERR, "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)
{
syslog(LOG_ERR, "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;
}