Skip to content

Commit

Permalink
Adds SocketCan support to the openmrn hub.
Browse files Browse the repository at this point in the history
This enables running the hub on a BeagleBone or a Raspberry Pi with a CAN HAT.
All other options are still usable, including repeating traffic between a SocketCAN port and a TCP client,
or a SocketCAN port and a USB port via GridConnect.
  • Loading branch information
balazsracz committed Sep 1, 2020
1 parent d14cf8b commit 10ce4bd
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 36 deletions.
28 changes: 26 additions & 2 deletions applications/hub/main.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/HubDeviceSelect.hxx"
#include "utils/SocketCan.hxx"
#include "utils/GcTcpHub.hxx"
#include "utils/ClientConnection.hxx"
#include "executor/Executor.hxx"
Expand All @@ -64,6 +66,7 @@ bool timestamped = false;
bool export_mdns = false;
const char* mdns_name = "openmrn_hub";
bool printpackets = false;
const char* socketcan_port = nullptr;

void usage(const char *e)
{
Expand Down Expand Up @@ -100,7 +103,7 @@ void usage(const char *e)
void parse_args(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "hp:d:u:q:tlmn:")) >= 0)
while ((opt = getopt(argc, argv, "hp:d:u:q:tlmn:s:")) >= 0)
{
switch (opt)
{
Expand Down Expand Up @@ -132,6 +135,11 @@ void parse_args(int argc, char *argv[])
case 'l':
printpackets = true;
break;
#if defined(__linux__)
case 's':
socketcan_port = optarg;
break;
#endif
default:
fprintf(stderr, "Unknown option %c\n", opt);
usage(argv[0]);
Expand Down Expand Up @@ -160,11 +168,27 @@ int appl_main(int argc, char *argv[])
void mdns_client_start();
void mdns_publish(const char *name, uint16_t port);

if (export_mdns) {
if (export_mdns)
{
mdns_client_start();
mdns_publish(mdns_name, port);
}
#endif
#if defined(__linux__)
if (socketcan_port)
{
int s = socketcan_open(socketcan_port, 1);
if (s >= 0)
{
new HubDeviceSelect<CanHubFlow>(&can_hub0, s);
fprintf(stderr, "Opened SocketCan %s: fd %d\n", socketcan_port, s);
}
else
{
fprintf(stderr, "Failed to open SocketCan %s.\n", socketcan_port);
}
}
#endif

if (upstream_host)
{
Expand Down
42 changes: 8 additions & 34 deletions src/openlcb/SimpleStack.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@
#include <net/if.h>
#include <termios.h> /* tc* functions */
#endif
#if defined(__linux__)
#include "utils/HubDeviceSelect.hxx"
#include <linux/sockios.h>
#include <sys/ioctl.h>
#endif

#include <sys/stat.h>
#include <sys/types.h>
Expand All @@ -57,6 +52,8 @@
#include "openlcb/EventHandler.hxx"
#include "openlcb/NodeInitializeFlow.hxx"
#include "openlcb/SimpleNodeInfo.hxx"
#include "utils/HubDeviceSelect.hxx"
#include "utils/SocketCan.hxx"

namespace openlcb
{
Expand Down Expand Up @@ -419,35 +416,12 @@ void SimpleCanStackBase::add_gridconnect_tty(
void SimpleCanStackBase::add_socketcan_port_select(
const char *device, int loopback)
{
int s;
struct sockaddr_can addr;
struct ifreq ifr;

s = socket(PF_CAN, SOCK_RAW, CAN_RAW);

// Set the blocking limit to the minimum allowed, typically 1024 in Linux
int sndbuf = 0;
setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));

// turn on/off loopback
setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));

// setup error notifications
can_err_mask_t err_mask = CAN_ERR_TX_TIMEOUT | CAN_ERR_LOSTARB |
CAN_ERR_CRTL | CAN_ERR_PROT | CAN_ERR_TRX | CAN_ERR_ACK |
CAN_ERR_BUSOFF | CAN_ERR_BUSERROR | CAN_ERR_RESTARTED;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));
strcpy(ifr.ifr_name, device);

::ioctl(s, SIOCGIFINDEX, &ifr);

addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;

bind(s, (struct sockaddr *)&addr, sizeof(addr));

auto *port = new HubDeviceSelect<CanHubFlow>(can_hub(), s);
additionalComponents_.emplace_back(port);
int s = socketcan_open(device, loopback);
if (s >= 0)
{
auto *port = new HubDeviceSelect<CanHubFlow>(can_hub(), s);
additionalComponents_.emplace_back(port);
}
}
#endif
extern Pool *const __attribute__((__weak__)) g_incoming_datagram_allocator =
Expand Down
101 changes: 101 additions & 0 deletions src/utils/SocketCan.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/** \copyright
* Copyright (c) 2020, Balazs Racz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \file SocketCan.cxx
*
* Helper functions to connect to CAN devices via SocketCan.
*
* @author Balazs Racz
* @date 1 Sep 2020
*/

#include "utils/SocketCan.hxx"
#include "can_frame.h"
#include <string.h>

#if defined(__linux__)

#include <linux/sockios.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdio.h>

/// This macro executes an OS call, and if it returns negative result, then
/// prints the errno to stderr, and terminates the current function with -1
/// return value.
/// @param where textual description of what function was called
/// (e.g. "socket")
/// @param x... the function call.
#define ERRNOLOG(where, x...) \
do \
{ \
if ((x) < 0) \
{ \
perror(where); \
return -1; \
} \
} while (0)

int socketcan_open(const char *device, int loopback)
{
int s;
struct sockaddr_can addr;
struct ifreq ifr;

s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
ERRNOLOG("socket", s);

// Set the blocking limit to the minimum allowed, typically 1024 in Linux
int sndbuf = 0;
ERRNOLOG("setsockopt(sndbuf)",
setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)));

// turn on/off loopback
ERRNOLOG("setsockopt(loopback)",
setsockopt(
s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)));

// setup error notifications
can_err_mask_t err_mask = CAN_ERR_TX_TIMEOUT | CAN_ERR_LOSTARB |
CAN_ERR_CRTL | CAN_ERR_PROT | CAN_ERR_TRX | CAN_ERR_ACK |
CAN_ERR_BUSOFF | CAN_ERR_BUSERROR | CAN_ERR_RESTARTED;
ERRNOLOG("setsockopt(filter)",
setsockopt(
s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask)));
strcpy(ifr.ifr_name, device);

ERRNOLOG("interface set", ::ioctl(s, SIOCGIFINDEX, &ifr));

addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;

ERRNOLOG("bind", bind(s, (struct sockaddr *)&addr, sizeof(addr)));

return s;
}

#endif
49 changes: 49 additions & 0 deletions src/utils/SocketCan.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/** \copyright
* Copyright (c) 2020, Balazs Racz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \file SocketCan.hxx
*
* Helper functions to connect to CAN devices via SocketCan.
*
* @author Balazs Racz
* @date 1 Sep 2020
*/

#ifndef _UTILS_SOCKETCAN_HXX_
#define _UTILS_SOCKETCAN_HXX_

#if defined(__linux__)

/// Opens a SocketCan socket.
/// @param device the name of the CAN device, e.g. can0
/// @param loopback 1 to enable loopback locally to other open references,
/// 0 to disable loopback locally to other open references.
/// @return an open socket file descriptor, or -1 if there was an error.
int socketcan_open(const char* device, int loopback);

#endif

#endif // _UTILS_SOCKETCAN_HXX_
1 change: 1 addition & 0 deletions src/utils/sources
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ CXXSRCS += \
Queue.cxx \
JSHubPort.cxx \
ReflashBootloader.cxx \
SocketCan.cxx \
constants.cxx \
gc_format.cxx \
logging.cxx \
Expand Down

0 comments on commit 10ce4bd

Please sign in to comment.