canutils/slcan: Add SLCAN utility
This commit is contained in:
parent
412cf2b955
commit
e12e8016f2
21
canutils/slcan/Kconfig
Normal file
21
canutils/slcan/Kconfig
Normal file
@ -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
|
24
canutils/slcan/Make.defs
Normal file
24
canutils/slcan/Make.defs
Normal file
@ -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
|
34
canutils/slcan/Makefile
Normal file
34
canutils/slcan/Makefile
Normal file
@ -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
|
504
canutils/slcan/slcan.c
Normal file
504
canutils/slcan/slcan.c
Normal file
@ -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;
|
||||
}
|
47
canutils/slcan/slcan.h
Normal file
47
canutils/slcan/slcan.h
Normal file
@ -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 */
|
Loading…
x
Reference in New Issue
Block a user