Skip to content

Commit

Permalink
Merge pull request #422 from bakerstu/bracz-hub-socketcan
Browse files Browse the repository at this point in the history
Adds SocketCan support to the openmrn hub.
  • Loading branch information
balazsracz authored Sep 2, 2020
2 parents d14cf8b + b1849d1 commit 8c547bc
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 51 deletions.
64 changes: 49 additions & 15 deletions applications/hub/main.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@

#include <memory>

#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/GcTcpHub.hxx"
#include "utils/ClientConnection.hxx"
#include "executor/Executor.hxx"
#include "executor/Service.hxx"
#include "os/os.h"
#include "utils/ClientConnection.hxx"
#include "utils/GcTcpHub.hxx"
#include "utils/Hub.hxx"
#include "utils/HubDeviceSelect.hxx"
#include "utils/SocketCan.hxx"
#include "utils/constants.hxx"

Executor<1> g_executor("g_executor", 0, 1024);
Service g_service(&g_executor);
Expand All @@ -58,6 +60,7 @@ OVERRIDE_CONST(gridconnect_buffer_delay_usec, 2000);

int port = 12021;
const char *device_path = nullptr;
const char *socket_can_path = nullptr;
int upstream_port = 12021;
const char *upstream_host = nullptr;
bool timestamped = false;
Expand All @@ -67,18 +70,28 @@ bool printpackets = false;

void usage(const char *e)
{
fprintf(stderr, "Usage: %s [-p port] [-d device_path] [-u upstream_host] "
"[-q upstream_port] [-m] [-n mdns_name] [-t] [-l]\n\n",
e);
fprintf(stderr, "GridConnect CAN HUB.\nListens to a specific TCP port, "
"reads CAN packets from the incoming connections using "
"the GridConnect protocol, and forwards all incoming "
"packets to all other participants.\n\nArguments:\n");
fprintf(stderr,
"Usage: %s [-p port] [-d device_path] [-u upstream_host] "
"[-q upstream_port] [-m] [-n mdns_name] "
#if defined(__linux__)
"[-s socketcan_interface] "
#endif
"[-t] [-l]\n\n",
e);
fprintf(stderr,
"GridConnect CAN HUB.\nListens to a specific TCP port, "
"reads CAN packets from the incoming connections using "
"the GridConnect protocol, and forwards all incoming "
"packets to all other participants.\n\nArguments:\n");
fprintf(stderr, "\t-p port specifies the port number to listen on, "
"default is 12021.\n");
fprintf(stderr, "\t-d device is a path to a physical device doing "
"serial-CAN or USB-CAN. If specified, opens device and "
"adds it to the hub.\n");
#if defined(__linux__)
fprintf(stderr, "\t-s socketcan_interface is a socketcan device (e.g. 'can0'). "
"If specified, opens device and adds it to the hub.\n");
#endif
fprintf(stderr, "\t-u upstream_host is the host name for an upstream "
"hub. If specified, this hub will connect to an upstream "
"hub.\n");
Expand All @@ -100,7 +113,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:s:u:q:tlmn:")) >= 0)
{
switch (opt)
{
Expand All @@ -110,6 +123,11 @@ void parse_args(int argc, char *argv[])
case 'd':
device_path = optarg;
break;
#if defined(__linux__)
case 's':
socket_can_path = optarg;
break;
#endif
case 'p':
port = atoi(optarg);
break;
Expand Down Expand Up @@ -160,12 +178,28 @@ 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 (socket_can_path)
{
int s = socketcan_open(socket_can_path, 1);
if (s >= 0)
{
new HubDeviceSelect<CanHubFlow>(&can_hub0, s);
fprintf(stderr, "Opened SocketCan %s: fd %d\n", socket_can_path, s);
}
else
{
fprintf(stderr, "Failed to open SocketCan %s.\n", socket_can_path);
}
}
#endif

if (upstream_host)
{
connections.emplace_back(new UpstreamConnectionClient(
Expand Down
1 change: 1 addition & 0 deletions applications/hub/targets/linux.rpi1/AvaHiMDNS.cxx
36 changes: 36 additions & 0 deletions applications/hub/targets/linux.rpi1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Adding SocketCAN support on the Raspberry Pi 4

These are my notes from adding SocketCAN support and building the **hub** application in a Raspberry Pi.

- Start with the pre-built JMRI - RPI disk image from: [M Steve Todd's - JMRI RaspberryPi as Access Point](https://mstevetodd.com/rpi)

- Install the MCP2517 CAN interface hardware from: [2-Channel CAN-BUS(FD) Shield for Raspberry Pi](https://www.seeedstudio.com/2-Channel-CAN-BUS-FD-Shield-for-Raspberry-Pi-p-4072.html) and followed their instructions for building the MCP2517 kernel device driver on their [Support Wiki](http://wiki.seeedstudio.com/2-Channel-CAN-BUS-FD-Shield-for-Raspberry-Pi/#software) page.

- Install some needed packages on the RPi:

`sudo apt install git doxygen libavahi-client-dev`

- Download the OpenMRN source code to the RPi:

`cd ~`

`git clone https://github.com/bakerstu/openmrn.git`

- Build the **hub** application:

`cd openmrn/applications/hub/targets/linux.rpi1/`

`make`

- Configure the **can0** interface for 125,000 bps and run the **hub** application at system at start-up by creating the file: `/etc/network/interfaces.d/can0` with the following lines:
```
allow-hotplug can0
iface can0 can static
bitrate 125000
up /home/pi/openmrn/applications/hub/targets/linux.rpi1/hub -s $IFACE &
```

- Configure the LCC Layout Connection in JMRI to use
- System Connection: `CAN via GridConnect Network Interface`
- IP Address/Host Name: `localhost`
- TCP/UDP Port: `12021`
44 changes: 9 additions & 35 deletions src/openlcb/SimpleStack.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,19 @@
#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>
#include <unistd.h>

#include "openlcb/SimpleStack.hxx"

#include "openmrn_features.h"
#include "openlcb/EventHandler.hxx"
#include "openlcb/NodeInitializeFlow.hxx"
#include "openlcb/SimpleNodeInfo.hxx"
#include "openmrn_features.h"
#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
4 changes: 3 additions & 1 deletion src/utils/ClientConnection.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@
#ifndef _UTILS_CLIENTCONNECTION_HXX_
#define _UTILS_CLIENTCONNECTION_HXX_

#include "utils/GridConnectHub.hxx"
#include <stdio.h>
#include <termios.h> /* tc* functions */
#include <unistd.h>

#include "utils/GridConnectHub.hxx"
#include "utils/socket_listener.hxx"

/// Abstract base class for the Hub's connections.
class ConnectionClient
{
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 <errno.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <stdio.h>
#include <sys/ioctl.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 8c547bc

Please sign in to comment.