2020-01-21 19:34:23 +01:00
|
|
|
/** Node-type: CAN bus
|
|
|
|
*
|
2020-07-08 16:07:24 +02:00
|
|
|
* @author Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
|
2020-01-21 19:34:23 +01:00
|
|
|
* @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC
|
|
|
|
* @license GNU General Public License (version 3)
|
|
|
|
*
|
|
|
|
* VILLASnode
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*********************************************************************************/
|
|
|
|
|
2020-07-08 13:33:13 +02:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
2020-07-08 16:06:57 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
|
|
#include <linux/can.h>
|
|
|
|
#include <linux/can/raw.h>
|
2020-07-08 16:07:24 +02:00
|
|
|
#include <linux/sockios.h>
|
2020-07-08 16:06:57 +02:00
|
|
|
|
2020-01-21 19:34:23 +01:00
|
|
|
#include <villas/nodes/can.hpp>
|
|
|
|
#include <villas/utils.hpp>
|
|
|
|
#include <villas/sample.h>
|
|
|
|
#include <villas/plugin.h>
|
2020-07-08 13:32:08 +02:00
|
|
|
#include <villas/signal.h>
|
|
|
|
#include <villas/node.h>
|
2020-01-21 19:34:23 +01:00
|
|
|
|
2020-07-08 13:32:08 +02:00
|
|
|
|
|
|
|
/* Forward declarations */
|
2020-01-21 19:34:23 +01:00
|
|
|
static struct plugin p;
|
|
|
|
|
|
|
|
using namespace villas::node;
|
|
|
|
using namespace villas::utils;
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_init(struct vnode *n)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
|
|
|
struct can *c = (struct can *) n->_vd;
|
|
|
|
|
2020-07-08 16:06:57 +02:00
|
|
|
c->interface_name = nullptr;
|
|
|
|
c->socket = 0;
|
2020-07-08 16:07:57 +02:00
|
|
|
c->sample_buf = nullptr;
|
2020-07-08 16:08:00 +02:00
|
|
|
c->sample_buf_num = 0;
|
2020-07-08 16:07:57 +02:00
|
|
|
c->in = nullptr;
|
|
|
|
c->out = nullptr;
|
2020-01-21 19:34:23 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_destroy(struct vnode *n)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
|
|
|
struct can *c = (struct can *) n->_vd;
|
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
free(c->interface_name);
|
2020-07-08 13:33:58 +02:00
|
|
|
|
|
|
|
if (c->socket != 0)
|
2020-07-08 16:07:48 +02:00
|
|
|
close(c->socket);
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:57 +02:00
|
|
|
free(c->sample_buf);
|
2020-07-08 16:07:48 +02:00
|
|
|
free(c->in);
|
|
|
|
free(c->out);
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-01-21 19:34:23 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-08 16:07:51 +02:00
|
|
|
int can_parse_signal(json_t *json, struct vlist *node_signals, struct can_signal *can_signals, size_t signal_index)
|
2020-07-08 16:07:32 +02:00
|
|
|
{
|
2020-07-08 16:07:48 +02:00
|
|
|
const char *name = nullptr;
|
|
|
|
uint64_t can_id = 0;
|
|
|
|
int can_size = 8;
|
|
|
|
int can_offset = 0;
|
|
|
|
struct signal* sig = nullptr;
|
|
|
|
int ret = 1;
|
|
|
|
json_error_t err;
|
|
|
|
|
2020-07-08 16:07:51 +02:00
|
|
|
ret = json_unpack_ex(json, &err, 0, "{ s?: s, s?: i, s?: i, s?: i }",
|
2020-07-08 13:33:58 +02:00
|
|
|
"name", &name,
|
|
|
|
"can_id", &can_id,
|
|
|
|
"can_size", &can_size,
|
|
|
|
"can_offset", &can_offset
|
|
|
|
);
|
2020-07-08 16:07:48 +02:00
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
jerror(&err, "Failed to parse signal configuration for can");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (can_size > 8 || can_size <= 0) {
|
|
|
|
error("can_size of %d for signal \"%s\" is invalid. You must satisfy 0 < can_size <= 8.", can_size, name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (can_offset > 8 || can_offset < 0) {
|
|
|
|
error("can_offset of %d for signal \"%s\" is invalid. You must satisfy 0 <= can_offset <= 8.", can_offset, name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2020-07-08 16:07:51 +02:00
|
|
|
sig = (struct signal*)vlist_at(node_signals, signal_index);
|
|
|
|
if ((!name && !sig->name) || (name && strcmp(name, sig->name) == 0)) {
|
|
|
|
can_signals[signal_index].id = can_id;
|
|
|
|
can_signals[signal_index].size = can_size;
|
|
|
|
can_signals[signal_index].offset = can_offset;
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
2020-07-08 16:07:48 +02:00
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
else
|
|
|
|
error("Signal configuration inconsistency detected: Signal with index %zu (\"%s\") does not match can_signal \"%s\"\n", signal_index, sig->name, name);
|
|
|
|
|
|
|
|
out: return ret;
|
2020-07-08 16:07:32 +02:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_parse(struct vnode *n, json_t *cfg)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
2020-07-08 16:07:24 +02:00
|
|
|
int ret = 1;
|
2020-01-21 19:34:23 +01:00
|
|
|
struct can *c = (struct can *) n->_vd;
|
2020-07-08 16:07:48 +02:00
|
|
|
size_t i;
|
|
|
|
json_t *json_in_signals;
|
|
|
|
json_t *json_out_signals;
|
|
|
|
json_t *json_signal;
|
2020-01-21 19:34:23 +01:00
|
|
|
json_error_t err;
|
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
c->in = nullptr;
|
|
|
|
c->out = nullptr;
|
2020-07-08 16:07:24 +02:00
|
|
|
|
2020-07-08 16:07:32 +02:00
|
|
|
ret = json_unpack_ex(cfg, &err, 0, "{ s: s, s: F, s?: { s?: o }, s?: { s?: o } }",
|
2020-07-08 13:33:58 +02:00
|
|
|
"interface_name", &c->interface_name,
|
|
|
|
"sample_rate", &c->sample_rate,
|
|
|
|
"in",
|
|
|
|
"signals", &json_in_signals,
|
|
|
|
"out",
|
|
|
|
"signals", &json_out_signals
|
|
|
|
);
|
2020-07-08 16:07:24 +02:00
|
|
|
if (ret) {
|
2020-01-21 19:34:23 +01:00
|
|
|
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
|
2020-07-08 16:07:48 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((c->in = (struct can_signal*)calloc(
|
2020-07-08 16:07:51 +02:00
|
|
|
json_array_size(json_in_signals),
|
|
|
|
sizeof(struct can_signal))) == nullptr) {
|
2020-07-08 16:07:48 +02:00
|
|
|
error("failed to allocate memory for input ids");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if ((c->out = (struct can_signal*)calloc(
|
2020-07-08 16:07:51 +02:00
|
|
|
json_array_size(json_out_signals),
|
|
|
|
sizeof(struct can_signal))) == nullptr) {
|
2020-07-08 16:07:48 +02:00
|
|
|
error("failed to allocate memory for output ids");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
json_array_foreach(json_in_signals, i, json_signal) {
|
2020-07-08 16:07:51 +02:00
|
|
|
if (can_parse_signal(json_signal, &n->in.signals, c->in, i) != 0) {
|
2020-07-08 16:07:48 +02:00
|
|
|
error("at signal %zu in node %s.",i , node_name(n));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
json_array_foreach(json_out_signals, i, json_signal) {
|
2020-07-08 16:07:51 +02:00
|
|
|
if (can_parse_signal(json_signal, &n->out.signals, c->out, i) != 0) {
|
2020-07-08 16:07:48 +02:00
|
|
|
error("at signal %zu in node %s.",i , node_name(n));
|
|
|
|
goto out;
|
|
|
|
}
|
2020-07-08 16:07:24 +02:00
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
ret = 0;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
|
|
|
out: if (ret != 0) {
|
2020-07-08 16:07:48 +02:00
|
|
|
free(c->in);
|
|
|
|
free(c->out);
|
|
|
|
c->in = nullptr;
|
|
|
|
c->out = nullptr;
|
2020-07-08 16:07:24 +02:00
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:24 +02:00
|
|
|
return ret;
|
2020-01-21 19:34:23 +01:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
char * can_print(struct vnode *n)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
|
|
|
struct can *c = (struct can *) n->_vd;
|
|
|
|
|
2020-07-08 16:06:57 +02:00
|
|
|
return strf("interface_name=%s", c->interface_name);
|
2020-01-21 19:34:23 +01:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_check(struct vnode *n)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
|
|
|
struct can *c = (struct can *) n->_vd;
|
|
|
|
|
2020-07-08 16:07:24 +02:00
|
|
|
if (c->interface_name == nullptr || strlen(c->interface_name) == 0) {
|
2020-07-08 16:07:48 +02:00
|
|
|
error("interface_name is empty. Please specify the name of the CAN interface!");
|
2020-07-08 16:07:24 +02:00
|
|
|
return 1;
|
2020-07-08 16:07:48 +02:00
|
|
|
}
|
2020-01-21 19:34:23 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_prepare(struct vnode *n)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
2020-07-08 16:07:57 +02:00
|
|
|
struct can *c = (struct can *) n->_vd;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
|
|
|
c->sample_buf = (union signal_data*) calloc(vlist_length(&n->in.signals), sizeof(union signal_data));
|
|
|
|
|
2020-07-08 16:07:57 +02:00
|
|
|
return (c->sample_buf != 0 ? 0 : 1);
|
2020-01-21 19:34:23 +01:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_start(struct vnode *n)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
2020-07-08 16:07:57 +02:00
|
|
|
int ret = 1;
|
2020-07-08 16:06:57 +02:00
|
|
|
struct sockaddr_can addr = {0};
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
2020-01-21 19:34:23 +01:00
|
|
|
struct can *c = (struct can *) n->_vd;
|
2020-07-08 16:06:57 +02:00
|
|
|
c->start_time = time_now();
|
2020-01-21 19:34:23 +01:00
|
|
|
|
2020-07-08 13:33:58 +02:00
|
|
|
if ((c->socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
|
2020-07-08 16:07:48 +02:00
|
|
|
error("Error while opening CAN socket");
|
2020-07-08 16:07:57 +02:00
|
|
|
goto out;
|
2020-07-08 16:06:57 +02:00
|
|
|
}
|
2020-01-21 19:34:23 +01:00
|
|
|
|
2020-07-08 16:06:57 +02:00
|
|
|
strcpy(ifr.ifr_name, c->interface_name);
|
2020-07-08 16:07:48 +02:00
|
|
|
if (ioctl(c->socket, SIOCGIFINDEX, &ifr) != 0) {
|
|
|
|
error("Could not find interface with name \"%s\".", c->interface_name);
|
2020-07-08 16:07:57 +02:00
|
|
|
goto out;
|
2020-07-08 16:07:48 +02:00
|
|
|
}
|
2020-07-08 16:06:57 +02:00
|
|
|
|
|
|
|
addr.can_family = AF_CAN;
|
|
|
|
addr.can_ifindex = ifr.ifr_ifindex;
|
|
|
|
|
2020-07-08 13:33:58 +02:00
|
|
|
if (bind(c->socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
2020-07-08 16:07:48 +02:00
|
|
|
error("Could not bind to interface with name \"%s\" (%d).", c->interface_name, ifr.ifr_ifindex);
|
2020-07-08 16:07:57 +02:00
|
|
|
goto out;
|
2020-07-08 16:06:57 +02:00
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:57 +02:00
|
|
|
ret = 0;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
|
|
|
out: return ret;
|
2020-01-21 19:34:23 +01:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_stop(struct vnode *n)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
2020-07-08 16:06:57 +02:00
|
|
|
struct can *c = (struct can *) n->_vd;
|
2020-01-21 19:34:23 +01:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
if (c->socket != 0) {
|
|
|
|
close(c->socket);
|
|
|
|
c->socket = 0;
|
|
|
|
}
|
2020-01-21 19:34:23 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
int can_conv_to_raw(union signal_data* sig, struct signal *from, void* to, int size)
|
|
|
|
{
|
|
|
|
if (size <= 0 || size > 8) {
|
|
|
|
error("signal size cannot be larger than 8!");
|
|
|
|
return 1;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
switch(from->type) {
|
2020-07-08 13:33:58 +02:00
|
|
|
case SignalType::BOOLEAN:
|
|
|
|
*(uint8_t*)to = sig->b;
|
2020-07-08 16:08:00 +02:00
|
|
|
return 0;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
|
|
|
case SignalType::INTEGER:
|
|
|
|
switch(size) {
|
|
|
|
case 1:
|
|
|
|
*(int8_t*)to = (int8_t)sig->i;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
*(int16_t*)to = (int16_t)sig->i;
|
|
|
|
sig->i = (int64_t)*(int16_t*)from;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
*(int16_t*)to = (int16_t)sig->i;
|
|
|
|
*((int8_t*)to+2) = (int8_t)(sig->i >> 16);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
*(int32_t*)to = (int32_t)sig->i;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
*(int64_t*)to = sig->i;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
case SignalType::FLOAT:
|
|
|
|
switch(size) {
|
|
|
|
case 4:
|
|
|
|
assert(sizeof(float) == 4);
|
|
|
|
*(float*)to = (float)sig->f;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
*(double*)to = sig->f;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
case SignalType::COMPLEX:
|
|
|
|
if (size != 8)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
*(float*)to = sig->z.real();
|
|
|
|
*((float*)to+1) = sig->z.imag();
|
2020-07-08 16:08:00 +02:00
|
|
|
return 0;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
|
|
|
fail: error("unsupported conversion to %s from raw (%p, %d)", signal_type_to_str(from->type), to, size);
|
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-07-08 16:07:57 +02:00
|
|
|
int can_conv_from_raw(union signal_data* sig, void* from, int size, struct signal *to)
|
|
|
|
{
|
|
|
|
if (size <= 0 || size > 8) {
|
2020-07-08 16:08:00 +02:00
|
|
|
error("signal size cannot be larger than 8!");
|
2020-07-08 16:07:57 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:57 +02:00
|
|
|
switch(to->type) {
|
2020-07-08 13:33:58 +02:00
|
|
|
case SignalType::BOOLEAN:
|
|
|
|
sig->b = (bool)*(uint8_t*)from;
|
2020-07-08 16:07:57 +02:00
|
|
|
return 0;
|
2020-07-08 13:33:58 +02:00
|
|
|
case SignalType::INTEGER:
|
|
|
|
switch(size) {
|
|
|
|
case 1:
|
|
|
|
sig->i = (int64_t)*(int8_t*)from;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
sig->i = (int64_t)*(int16_t*)from;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
sig->i = (int64_t)*(int16_t*)from;
|
|
|
|
sig->i += ((int64_t)*((int8_t*)(from)+2)) << 16;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
sig->i = (int64_t)*(int32_t*)from;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
sig->i = *(uint64_t*)from;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
case SignalType::FLOAT:
|
|
|
|
switch(size) {
|
|
|
|
case 4:
|
|
|
|
assert(sizeof(float) == 4);
|
|
|
|
sig->f = (double)*(float*)from;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
sig->f = *(double*)from;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
case SignalType::COMPLEX:
|
|
|
|
if (size != 8)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
sig->z = std::complex<float>(*(float*)from, *((float*)from+1));
|
2020-07-08 16:07:57 +02:00
|
|
|
return 0;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:57 +02:00
|
|
|
default:
|
2020-07-08 16:08:00 +02:00
|
|
|
goto fail;
|
2020-07-08 16:07:57 +02:00
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
fail:
|
|
|
|
error("unsupported conversion from %s to raw (%p, %d)", signal_type_to_str(to->type), from, size);
|
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
return 1;
|
2020-07-08 16:07:57 +02:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_read(struct vnode *n, struct sample *smps[], unsigned cnt, unsigned *release)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
2020-07-08 16:08:00 +02:00
|
|
|
int ret = 0;
|
2020-07-08 16:06:57 +02:00
|
|
|
int nbytes;
|
2020-07-08 16:07:48 +02:00
|
|
|
unsigned nread = 0;
|
2020-07-08 16:06:57 +02:00
|
|
|
struct can_frame frame;
|
2020-07-08 16:07:48 +02:00
|
|
|
struct timeval tv;
|
|
|
|
bool found_id = false;
|
2020-01-21 19:34:23 +01:00
|
|
|
|
2020-07-08 16:06:57 +02:00
|
|
|
struct can *c = (struct can *) n->_vd;
|
|
|
|
|
2020-01-21 19:34:23 +01:00
|
|
|
assert(cnt >= 1 && smps[0]->capacity >= 1);
|
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
nbytes = read(c->socket, &frame, sizeof(struct can_frame));
|
|
|
|
if (nbytes == -1) {
|
|
|
|
error("CAN read() returned -1. Is the CAN interface up?");
|
2020-07-08 16:08:00 +02:00
|
|
|
goto out;
|
2020-07-08 16:07:48 +02:00
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
if ((unsigned)nbytes != sizeof(struct can_frame)) {
|
2020-07-08 13:33:58 +02:00
|
|
|
error("CAN read() error. read() returned %d bytes but expected %zu", nbytes, sizeof(struct can_frame));
|
2020-07-08 16:08:00 +02:00
|
|
|
goto out;
|
2020-07-08 16:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
debug(0,"received can message: (id:%d, len:%u, data: 0x%x:0x%x)",
|
2020-07-08 13:33:58 +02:00
|
|
|
frame.can_id,
|
|
|
|
frame.can_dlc,
|
|
|
|
((uint32_t*)&frame.data)[0],
|
|
|
|
((uint32_t*)&frame.data)[1]);
|
2020-07-08 16:07:48 +02:00
|
|
|
|
|
|
|
if (ioctl(c->socket, SIOCGSTAMP, &tv) == 0) {
|
|
|
|
TIMEVAL_TO_TIMESPEC(&tv, &smps[nread]->ts.received);
|
|
|
|
smps[nread]->flags |= (int) SampleFlags::HAS_TS_RECEIVED;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i=0; i < vlist_length(&(n->in.signals)); i++) {
|
|
|
|
if (c->in[i].id == frame.can_id) {
|
2020-07-08 16:07:57 +02:00
|
|
|
if (can_conv_from_raw(&c->sample_buf[i],
|
|
|
|
((uint8_t*)&frame.data) + c->in[i].offset,
|
|
|
|
c->in[i].size,
|
2020-07-08 16:08:00 +02:00
|
|
|
(struct signal*) vlist_at(&n->in.signals, i)) != 0) {
|
|
|
|
goto out;
|
2020-07-08 16:07:57 +02:00
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
c->sample_buf_num++;
|
2020-07-08 16:07:48 +02:00
|
|
|
found_id = true;
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
if (!found_id) {
|
|
|
|
error("did not find signal for can id %d\n", frame.can_id);
|
|
|
|
return 0;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
debug(0, "received %zu signals\n", c->sample_buf_num);
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
/* Copy signal data to sample only when all signals have been received */
|
|
|
|
if (c->sample_buf_num == vlist_length(&n->in.signals)) {
|
|
|
|
smps[nread]->length = c->sample_buf_num;
|
|
|
|
memcpy(smps[nread]->data, c->sample_buf, c->sample_buf_num*sizeof(union signal_data));
|
|
|
|
c->sample_buf_num = 0;
|
|
|
|
smps[nread]->flags |= (int) SampleFlags::HAS_DATA;
|
|
|
|
ret = 1;
|
2020-07-08 13:33:58 +02:00
|
|
|
}
|
|
|
|
else {
|
2020-07-08 16:08:00 +02:00
|
|
|
smps[nread]->length = 0;
|
|
|
|
ret = 0;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
|
|
|
out: /* Set signals, because other VILLASnode parts expect us to */
|
2020-07-08 16:07:48 +02:00
|
|
|
smps[nread]->signals = &n->in.signals;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
return ret;
|
2020-01-21 19:34:23 +01:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_write(struct vnode *n, struct sample *smps[], unsigned cnt, unsigned *release)
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
2020-07-08 16:06:57 +02:00
|
|
|
int nbytes;
|
2020-07-08 16:07:48 +02:00
|
|
|
unsigned nwrite;
|
2020-07-08 16:07:28 +02:00
|
|
|
struct can_frame *frame;
|
2020-07-08 16:07:48 +02:00
|
|
|
size_t fsize = 0; /* number of frames in use */
|
2020-07-08 16:07:28 +02:00
|
|
|
|
|
|
|
struct can *c = (struct can *) n->_vd;
|
|
|
|
|
|
|
|
assert(cnt >= 1 && smps[0]->capacity >= 1);
|
|
|
|
|
2020-07-08 13:33:58 +02:00
|
|
|
frame = (struct can_frame*) calloc(sizeof(struct can_frame), vlist_length(&(n->out.signals)));
|
2020-07-08 16:07:48 +02:00
|
|
|
|
|
|
|
for (nwrite=0; nwrite < cnt; nwrite++) {
|
|
|
|
for (size_t i=0; i < vlist_length(&(n->out.signals)); i++) {
|
2020-07-08 13:33:58 +02:00
|
|
|
if (c->out[i].offset != 0) /* frame is shared */
|
2020-07-08 16:07:48 +02:00
|
|
|
continue;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
frame[fsize].can_dlc = c->out[i].size;
|
|
|
|
frame[fsize].can_id = c->out[i].id;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
can_conv_to_raw(
|
|
|
|
&smps[nwrite]->data[i],
|
|
|
|
(struct signal*)vlist_at(&(n->out.signals), i),
|
|
|
|
&frame[fsize].data,
|
|
|
|
c->out[i].size);
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
fsize++;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
for (size_t i=0; i < vlist_length(&(n->out.signals)); i++) {
|
|
|
|
if (c->out[i].offset == 0) { /* frame already stored */
|
|
|
|
continue;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
for (size_t j=0; j < fsize; j++) {
|
2020-07-08 13:33:58 +02:00
|
|
|
if (c->out[i].id != frame[j].can_id)
|
2020-07-08 16:08:00 +02:00
|
|
|
continue;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:08:00 +02:00
|
|
|
frame[j].can_dlc += c->out[i].size;
|
|
|
|
can_conv_to_raw(
|
|
|
|
&smps[nwrite]->data[i],
|
|
|
|
(struct signal*)vlist_at(&(n->out.signals), i),
|
|
|
|
(uint8_t*)&frame[j].data + c->out[i].offset,
|
|
|
|
c->out[i].size);
|
|
|
|
break;
|
2020-07-08 16:07:48 +02:00
|
|
|
}
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
for (size_t j=0; j < fsize; j++) {
|
2020-07-08 13:33:58 +02:00
|
|
|
debug(0,"writing can message: (id:%d, dlc:%u, data:0x%x:0x%x)",
|
|
|
|
frame[j].can_id,
|
|
|
|
frame[j].can_dlc,
|
|
|
|
((uint32_t*)&frame[j].data)[0],
|
|
|
|
((uint32_t*)&frame[j].data)[1]
|
|
|
|
);
|
2020-07-08 16:07:48 +02:00
|
|
|
|
|
|
|
if ((nbytes = write(c->socket, &frame[j], sizeof(struct can_frame))) == -1) {
|
|
|
|
error("CAN write() returned -1. Is the CAN interface up?");
|
|
|
|
return nwrite;
|
|
|
|
}
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
if ((unsigned)nbytes != sizeof(struct can_frame)) {
|
|
|
|
error("CAN write() error. write() returned %d bytes but expected %zu",
|
2020-07-08 13:33:58 +02:00
|
|
|
nbytes, sizeof(struct can_frame));
|
2020-07-08 16:07:48 +02:00
|
|
|
return nwrite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 19:34:23 +01:00
|
|
|
|
2020-07-08 13:32:08 +02:00
|
|
|
return nwrite;
|
2020-01-21 19:34:23 +01:00
|
|
|
}
|
|
|
|
|
2020-08-25 21:00:52 +02:00
|
|
|
int can_poll_fds(struct vnode *n, int fds[])
|
2020-01-21 19:34:23 +01:00
|
|
|
{
|
2020-07-08 16:07:39 +02:00
|
|
|
struct can *c = (struct can *) n->_vd;
|
2020-07-08 13:33:58 +02:00
|
|
|
|
2020-07-08 16:07:48 +02:00
|
|
|
fds[0] = c->socket;
|
2020-01-21 19:34:23 +01:00
|
|
|
|
2020-07-08 16:07:39 +02:00
|
|
|
return 1; /* The number of file descriptors which have been set in fds */
|
2020-01-21 19:34:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
__attribute__((constructor(110)))
|
2020-07-08 13:32:08 +02:00
|
|
|
static void register_plugin() {
|
|
|
|
p.name = "can";
|
|
|
|
p.description = "Receive CAN messages using the socketCAN driver";
|
|
|
|
p.node.instances.state = State::DESTROYED;
|
|
|
|
p.node.vectorize = 0;
|
|
|
|
p.node.size = sizeof(struct can);
|
|
|
|
p.node.init = can_init;
|
|
|
|
p.node.destroy = can_destroy;
|
|
|
|
p.node.prepare = can_prepare;
|
|
|
|
p.node.parse = can_parse;
|
|
|
|
p.node.print = can_print;
|
|
|
|
p.node.check = can_check;
|
|
|
|
p.node.start = can_start;
|
|
|
|
p.node.stop = can_stop;
|
|
|
|
p.node.read = can_read;
|
|
|
|
p.node.write = can_write;
|
|
|
|
p.node.poll_fds = can_poll_fds;
|
|
|
|
|
2020-09-10 11:11:42 +02:00
|
|
|
int ret = vlist_init(&p.node.instances);
|
|
|
|
if (!ret)
|
|
|
|
vlist_init_and_push(&plugins, &p);
|
2020-07-08 13:32:08 +02:00
|
|
|
}
|
2020-01-21 19:34:23 +01:00
|
|
|
|
|
|
|
__attribute__((destructor(110)))
|
2020-07-08 13:32:08 +02:00
|
|
|
static void deregister_plugin() {
|
|
|
|
if (plugins.state != State::DESTROYED)
|
|
|
|
vlist_remove_all(&plugins, &p);
|
|
|
|
}
|