Started support for Linux SocketCAN

This commit is contained in:
fpagliughi
2022-01-24 00:09:02 -05:00
parent 3fe6a9be96
commit 4762cf1c33
13 changed files with 1038 additions and 11 deletions

View File

@@ -59,6 +59,7 @@ endif()
option(SOCKPP_BUILD_EXAMPLES "Build example applications" OFF)
option(SOCKPP_BUILD_TESTS "Build unit tests" OFF)
option(SOCKPP_BUILD_DOCUMENTATION "Create Doxygen reference documentation" OFF)
option(SOCKPP_BUILD_CAN "Build the Linux SocketCAN components" OFF)
# --- C++14 build flags ---
@@ -107,8 +108,8 @@ if(SOCKPP_BUILD_SHARED)
add_library(${SOCKPP} SHARED $<TARGET_OBJECTS:sockpp-objs>)
## add dependencies to the shared library
target_link_libraries(${SOCKPP} ${LIBS_SYSTEM})
## add dependencies to the shared library
target_link_libraries(${SOCKPP} ${LIBS_SYSTEM})
target_include_directories(${SOCKPP}
PUBLIC
@@ -117,6 +118,11 @@ if(SOCKPP_BUILD_SHARED)
${CMAKE_CURRENT_BINARY_DIR}/generated
)
target_compile_options(${SOCKPP} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W3>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>
)
set_target_properties(${SOCKPP} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
@@ -135,8 +141,8 @@ if(SOCKPP_BUILD_STATIC)
add_library(${SOCKPP_STATIC} STATIC $<TARGET_OBJECTS:sockpp-objs>)
## add dependencies to the static library
target_link_libraries(${SOCKPP_STATIC} ${LIBS_SYSTEM})
## add dependencies to the static library
target_link_libraries(${SOCKPP_STATIC} ${LIBS_SYSTEM})
target_include_directories(${SOCKPP_STATIC}
PUBLIC
@@ -145,6 +151,11 @@ if(SOCKPP_BUILD_STATIC)
${CMAKE_CURRENT_BINARY_DIR}/generated
)
target_compile_options(${SOCKPP_STATIC} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W3>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>
)
install(TARGETS ${SOCKPP}-static
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
@@ -187,6 +198,9 @@ if(SOCKPP_BUILD_EXAMPLES)
add_subdirectory(examples/udp)
if(UNIX)
add_subdirectory(examples/unix)
if(SOCKPP_BUILD_CAN)
add_subdirectory(examples/linux)
endif()
endif()
endif()

View File

@@ -0,0 +1,69 @@
# CMakeLists.txt
#
# CMake file for the Linux SocketCAN sample applications in the
# 'sockpp' library.
#
# ---------------------------------------------------------------------------
# This file is part of the "sockpp" C++ socket library.
#
# Copyright (c) 2021 Frank Pagliughi
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. 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.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# 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.
# ---------------------------------------------------------------------------
# --- For apps that use threads ---
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# --- Executables ---
add_executable(cantime cantime.cpp)
add_executable(canrecv canrecv.cpp)
# --- Link for executables ---
message(STATUS "Using library for SocketCAN samples: ${SOCKPP_LIB}")
target_link_libraries(cantime ${SOCKPP_LIB})
target_link_libraries(canrecv ${SOCKPP_LIB})
#target_link_libraries(unechosvr ${SOCKPP_LIB} Threads::Threads)
# --- Install ---
set(INSTALL_TARGETS
cantime
canrecv
)
install(TARGETS ${INSTALL_TARGETS}
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
)

View File

@@ -0,0 +1,96 @@
// canrecv.cpp
//
// Linux SoxketCAN reader example.
//
//
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2021 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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.
// --------------------------------------------------------------------------
#include <iostream>
#include <iomanip>
#include <string>
#include <chrono>
#include <thread>
#include "sockpp/can_socket.h"
#include "sockpp/can_frame.h"
#include "sockpp/version.h"
#include <net/if.h>
#include <sys/ioctl.h>
using namespace std;
// The clock to use to get time and pace the app.
using sysclock = chrono::system_clock;
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
cout << "Sample SocketCAN writer for 'sockpp' "
<< sockpp::SOCKPP_VERSION << endl;
string canIface = (argc > 1) ? argv[1] : "can0";
canid_t canID = (argc > 2) ? atoi(argv[2]) : 0x20;
sockpp::socket_initializer sockInit;
sockpp::can_address addr(canIface);
sockpp::can_socket sock(addr);
if (!sock) {
cerr << "Error binding to the CAN interface " << canIface << "\n\t"
<< sock.last_error_str() << endl;
return 1;
}
cout << "Created CAN socket on " << sock.address() << endl;
time_t t = sysclock::to_time_t(sysclock::now());
cout.setf(ios::fixed, ios::floatfield);
cout << setfill('0');
while (true) {
sockpp::can_frame frame;
sock.recv(&frame);
auto t = sock.last_frame_timestamp();
cout << t << " ";
for (uint8_t i=0; i<frame.can_dlc; ++i)
cout << hex << uppercase << setw(2) << unsigned(frame.data[i]) << " ";
cout << endl;
}
return (!sock) ? 1 : 0;
}

View File

@@ -0,0 +1,98 @@
// cantime.cpp
//
// Linux SoxketCAN writer example.
//
// This writes the 1-sec, 32-bit, Linux time_t value to the CAN bus each
// time it ticks. This is a simple (though not overly precise) way to
// synchronize the time for nodes on the bus
//
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2021 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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.
// --------------------------------------------------------------------------
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include "sockpp/can_socket.h"
#include "sockpp/can_frame.h"
#include "sockpp/version.h"
#include <net/if.h>
#include <sys/ioctl.h>
using namespace std;
// The clock to use to get time and pace the app.
using sysclock = chrono::system_clock;
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
cout << "Sample SocketCAN writer for 'sockpp' "
<< sockpp::SOCKPP_VERSION << endl;
string canIface = (argc > 1) ? argv[1] : "can0";
canid_t canID = (argc > 2) ? atoi(argv[2]) : 0x20;
sockpp::socket_initializer sockInit;
sockpp::can_address addr(canIface);
sockpp::can_socket sock(addr);
if (!sock) {
cerr << "Error binding to the CAN interface " << canIface << "\n\t"
<< sock.last_error_str() << endl;
return 1;
}
cout << "Created CAN socket on " << sock.address() << endl;
time_t t = sysclock::to_time_t(sysclock::now());
while (true) {
// Sleep until the clock ticks to the next second
this_thread::sleep_until(sysclock::from_time_t(t+1));
// Re-read the time in case we fell behind
t = sysclock::to_time_t(sysclock::now());
// Write the time to the CAN bus as a 32-bit int
auto nt = uint32_t(t);
sockpp::can_frame frame { canID, &nt, sizeof(nt) };
sock.send(frame);
}
return (!sock) ? 1 : 0;
}

View File

@@ -0,0 +1,201 @@
/**
* @file can_address.h
*
* Class for the Linux SocketCAN socket address.
*
* @author Frank Pagliughi
* @author SoRo Systems, Inc.
* @author www.sorosys.com
*
* @date March 2021
*/
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2014-2021 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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.
// --------------------------------------------------------------------------
#ifndef __sockpp_can_addr_h
#define __sockpp_can_addr_h
#include "sockpp/platform.h"
#include "sockpp/sock_address.h"
#include <iostream>
#include <string>
#include <cstring>
#include <sys/un.h>
#include <linux/can.h>
namespace sockpp {
/////////////////////////////////////////////////////////////////////////////
/**
* Class that represents a Linux SocketCAN address.
* This inherits from the CAN form of a socket address, @em sockaddr_can.
*/
class can_address : public sock_address
{
/** The underlying C struct for SocketCAN addresses */
sockaddr_can addr_;
/** The size of the underlying address struct, in bytes */
static constexpr size_t SZ = sizeof(sockaddr_can);
public:
/** The address family for this type of address */
static constexpr sa_family_t ADDRESS_FAMILY = AF_CAN;
/** Iface to use to indicate binding to all interfaces */
static const unsigned ALL_IFACE = 0;
/**
* Constructs an empty address.
* The address is initialized to all zeroes.
*/
can_address() : addr_() {}
/**
* Creates an address for binding to a specific CAN interface
* @param ifindex The interface index to use. This must, obviously, be
* an index to a CAN interface.
*/
explicit can_address(unsigned ifindex);
/**
* Constructs an address for the specified CAN interface.
* The interface might be "can0", "can1", "vcan0", etc.
* @param iface The name of the CAN interface
*/
can_address(const std::string& iface);
/**
* Constructs the address by copying the specified structure.
* @param addr The generic address
* @throws std::invalid_argument if the address is not a SocketCAN
* address (i.e. family is not AF_CAN)
*/
explicit can_address(const sockaddr& addr);
/**
* Constructs the address by copying the specified structure.
* @param addr The other address
*/
can_address(const sock_address& addr) {
std::memcpy(&addr_, addr.sockaddr_ptr(), SZ);
}
/**
* Constructs the address by copying the specified structure.
* @param addr The other address
* @throws std::invalid_argument if the address is not properly
* initialized as a SocketCAN address (i.e. family is not
* AF_CAN)
*/
can_address(const sockaddr_can& addr) : addr_(addr) {}
/**
* Constructs the address by copying the specified address.
* @param addr The other address
*/
can_address(const can_address& addr) : addr_(addr.addr_) {}
/**
* Checks if the address is set to some value.
* This doesn't attempt to determine if the address is valid, simply
* that it's not all zero.
* @return @em true if the address has been set, @em false otherwise.
*/
bool is_set() const { return family() != AF_UNSPEC; }
/**
* Gets the name of the CAN interface to which this address refers.
* @return The name of the CAN interface to which this address refers.
*/
std::string iface() const;
/**
* Gets the size of the address structure.
* Note: In this implementation, this should return sizeof(this) but
* more convenient in some places, and the implementation might change
* in the future, so it might be more compatible with future revisions
* to use this call.
* @return The size of the address structure.
*/
socklen_t size() const override { return socklen_t(SZ); }
// TODO: Do we need a:
// create(iface)
// to mimic the inet_address behavior?
/**
* Gets a pointer to this object cast to a const @em sockaddr.
* @return A pointer to this object cast to a const @em sockaddr.
*/
const sockaddr* sockaddr_ptr() const override {
return reinterpret_cast<const sockaddr*>(&addr_);
}
/**
* Gets a pointer to this object cast to a @em sockaddr.
* @return A pointer to this object cast to a @em sockaddr.
*/
sockaddr* sockaddr_ptr() override {
return reinterpret_cast<sockaddr*>(&addr_);
}
/**
* Gets a const pointer to this object cast to a @em sockaddr_can.
* @return const sockaddr_can pointer to this object.
*/
const sockaddr_can* sockaddr_can_ptr() const { return &addr_; }
/**
* Gets a pointer to this object cast to a @em sockaddr_can.
* @return sockaddr_can pointer to this object.
*/
sockaddr_can* sockaddr_can_ptr() { return &addr_; }
/**
* Gets a printable string for the address.
* @return A string representation of the address in the form
* "unix:<path>"
*/
std::string to_string() const {
return std::string("can:") + iface();
}
};
// --------------------------------------------------------------------------
/**
* Stream inserter for the address.
* @param os The output stream
* @param addr The address
* @return A reference to the output stream.
*/
std::ostream& operator<<(std::ostream& os, const can_address& addr);
/////////////////////////////////////////////////////////////////////////////
// end namespace sockpp
}
#endif // __sockpp_can_addr_h

103
include/sockpp/can_frame.h Normal file
View File

@@ -0,0 +1,103 @@
/**
* @file can_frame.h
*
* Class for the Linux SocketCAN frames.
*
* @author Frank Pagliughi
* @author SoRo Systems, Inc.
* @author www.sorosys.com
*
* @date March 2021
*/
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2021 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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.
// --------------------------------------------------------------------------
#ifndef __sockpp_can_frame_h
#define __sockpp_can_frame_h
#include "sockpp/platform.h"
#include <string>
#include <cstring>
//#include <sys/un.h>
#include <linux/can.h>
namespace sockpp {
/////////////////////////////////////////////////////////////////////////////
/**
* Class that represents a Linux SocketCAN frame.
* This inherits from the Linux CAN frame struct, just providing easier
construction.
*/
class can_frame : public ::can_frame
{
using base = ::can_frame;
/** The size of the underlying address struct, in bytes */
static constexpr size_t SZ = sizeof(::can_frame);
public:
/**
* Constructs an empty frame.
* The frame is initialized to all zeroes.
*/
can_frame() : base{} {}
/**
* Constructs a frame with the specified ID and data.
* @param canID The CAN identifier for the frame
* @param data The data field for the frame
*/
can_frame(canid_t canID, const std::string& data)
: can_frame{ canID, data.data(), data.length() } {}
/**
* Constructs a frame with the specified ID and data.
* @param canID The CAN identifier for the frame
* @param data The data field for the frame
* @param n The number of bytes in the data field
*/
can_frame(canid_t canID, const void* data, size_t n) : base{} {
this->can_id = canID;
this->can_dlc = n;
::memcpy(&this->data, data, n);
}
};
/////////////////////////////////////////////////////////////////////////////
// end namespace sockpp
}
#endif // __sockpp_can_frame_h

206
include/sockpp/can_socket.h Normal file
View File

@@ -0,0 +1,206 @@
/**
* @file can_socket.h
*
* Class (typedef) for Linux SocketCAN socket.
*
* @author Frank Pagliughi
* @author SoRo Systems, Inc.
* @author www.sorosys.com
*
* @date March 2021
*/
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2021 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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.
// --------------------------------------------------------------------------
#ifndef __sockpp_can_socket_h
#define __sockpp_can_socket_h
#include "sockpp/datagram_socket.h"
#include "sockpp/can_address.h"
#include "sockpp/can_frame.h"
namespace sockpp {
/////////////////////////////////////////////////////////////////////////////
/**
* Socket type for Linux SocketCAN.
*
* Note that technically these are RAW sockets, not DGRAM. We can/should
* organize the underlying hierarch to properly indicate this, but for
* practical usge, it doesn't matter too MUCH.
* The BCM CAN sockets are DGRAM sockets, but those aren't implemented yet.
* It wouldn't take too much to add them, though.
*/
class can_socket : public datagram_socket
{
/** The base class */
using base = datagram_socket;
// Non-copyable
can_socket(const can_socket&) =delete;
can_socket& operator=(const can_socket&) =delete;
/**
* We can't connect to a raw CAN socket;
* we can only bind the address/iface.
*/
bool connect(const sock_address&) =delete;
protected:
static socket_t create_handle(int type, int protocol) {
return socket_t(::socket(PROTOCOL_FAMILY, type, protocol));
}
public:
/**
* The SocketCAN protocol family.
* Note that AF_CAN == PF_CAN, which is used in many of the CAN
* examples.
*/
static const int PROTOCOL_FAMILY = AF_CAN;
/** The socket 'type' for communications semantics. */
static constexpr int COMM_TYPE = SOCK_RAW;
/**
* Creates an uninitialized CAN socket.
*/
can_socket() {}
/**
* Creates a CAN socket from an existing OS socket handle and
* claims ownership of the handle.
* @param handle A socket handle from the operating system.
*/
explicit can_socket(socket_t handle) : base(handle) {}
/**
* Creates a CAN socket and binds it to the address.
* @param addr The address to bind.
*/
explicit can_socket(const can_address& addr);
/**
* Move constructor.
* @param other The other socket to move to this one
*/
can_socket(can_socket&& other) : base(std::move(other)) {}
/**
* Move assignment.
* @param rhs The other socket to move into this one.
* @return A reference to this object.
*/
can_socket& operator=(can_socket&& rhs) {
base::operator=(std::move(rhs));
return *this;
}
/**
* Gets the system time of the last frame read from the socket.
* @return The system time of the last frame read from the socket with
* microsecond precision.
*/
std::chrono::system_clock::time_point last_frame_time();
/**
* Gets a floating point timestamp of the last frame read from the
* socket.
* This is the number of seconds since the Unix epoch (time_t), with
* floating-point, microsecond precision.
* @return A floating-point timestamp with microsecond precision.
*/
double last_frame_timestamp();
// ----- I/O -----
/**
* Sends a frame to the CAN interfacce at the specified address.
* @param frame The CAN frame to send.
* @param flags The flags. See send(2).
* @param addr The remote destination of the data.
* @return the number of bytes sent on success or, @em -1 on failure.
*/
ssize_t send_to(const can_frame& frame, int flags, const can_address& addr) {
return check_ret(
::sendto(handle(), &frame, sizeof(can_frame), flags,
addr.sockaddr_ptr(), addr.size())
);
}
/**
* Sends a frame to the CAN interface at the specified address.
* @param frame The CAN frame to send.
* @param addr The remote destination of the data.
* @return the number of bytes sent on success or, @em -1 on failure.
*/
ssize_t send_to(const can_frame& frame, const can_address& addr) {
return check_ret(
::sendto(handle(), &frame, sizeof(can_frame), 0,
addr.sockaddr_ptr(), addr.size())
);
}
/**
* Sends a frame to the CAN bus.
* The socket should be bound before calling this.
* @param frame The CAN frame to send.
* @param flags The option bit flags. See send(2).
* @return @em zero on success, @em -1 on failure.
*/
ssize_t send(const can_frame& frame, int flags=0) {
return check_ret(::send(handle(), &frame, sizeof(can_frame), flags));
}
/**
* Receives a message from the CAN interface at the specified address.
* @param frame CAN frame to get the incoming data.
* @param flags The option bit flags. See send(2).
* @param srcAddr Receives the address of the peer that sent the
* message
* @return The number of bytes read or @em -1 on error.
*/
ssize_t recv_from(can_frame* frame, int flags, can_address* srcAddr=nullptr);
/**
* Receives a message on the socket.
* @param frame CAN frame to get the incoming data.
* @param flags The option bit flags. See send(2).
* @return The number of bytes read or @em -1 on error.
*/
ssize_t recv(can_frame* frame, int flags=0) {
return check_ret(::recv(handle(), frame, sizeof(can_frame), flags));
}
};
/////////////////////////////////////////////////////////////////////////////
// end namespace sockpp
}
#endif // __sockpp_can_socket_h

View File

@@ -69,8 +69,12 @@ class datagram_socket : public socket
datagram_socket& operator=(const datagram_socket&) =delete;
protected:
static socket_t create_handle(int domain) {
return socket_t(::socket(domain, COMM_TYPE, 0));
static socket_t create_handle(int domain, int protocol=0) {
return socket_t(::socket(domain, COMM_TYPE, protocol));
}
static socket_t create_handle(int domain, int type, int protocol) {
return socket_t(::socket(domain, type, protocol));
}
public:
@@ -265,7 +269,7 @@ public:
*/
datagram_socket_tmpl(socket_t handle) : base(handle) {}
/**
* Creates a UDP socket and binds it to the address.
* Creates a datagram socket and binds it to the address.
* @param addr The address to bind.
*/
datagram_socket_tmpl(const ADDR& addr) : base(addr) {}

View File

@@ -62,14 +62,47 @@ namespace sockpp {
#define SOCKPP_SOCKET_T_DEFINED
#endif
/**
* Converts a number of microseconds to a relative timeval.
* @param dur A chrono duration of microseconds.
* @return A timeval
*/
timeval to_timeval(const std::chrono::microseconds& dur);
/**
* Converts a chrono duration to a relative timeval.
* @param dur A chrono duration.
* @return A timeval.
*/
template<class Rep, class Period>
timeval to_timeval(const std::chrono::duration<Rep,Period>& dur) {
return to_timeval(std::chrono::duration_cast<std::chrono::microseconds>(dur));
}
/**
* Converts a relative timeval to a chrono duration.
* @param tv A timeval.
* @return A chrono duration.
*/
inline std::chrono::microseconds to_duration(const timeval& tv)
{
auto dur = std::chrono::seconds{tv.tv_sec}
+ std::chrono::microseconds{tv.tv_usec};
return std::chrono::duration_cast<std::chrono::microseconds>(dur);
}
/**
* Converts an absolute timeval to a chrono time_point.
* @param tv A timeval.
* @return A chrono time_point.
*/
inline std::chrono::system_clock::time_point to_timepoint(const timeval& tv)
{
return std::chrono::system_clock::time_point {
std::chrono::duration_cast<std::chrono::system_clock::duration>(to_duration(tv))
};
}
/////////////////////////////////////////////////////////////////////////////
/**

View File

@@ -37,7 +37,7 @@
# ---------------------------------------------------------------------------
add_library(sockpp-objs OBJECT
acceptor.cpp
acceptor.cpp
connector.cpp
datagram_socket.cpp
exception.cpp
@@ -49,8 +49,14 @@ add_library(sockpp-objs OBJECT
if(UNIX)
target_sources(sockpp-objs PUBLIC
unix/unix_address.cpp
${CMAKE_CURRENT_SOURCE_DIR}/unix/unix_address.cpp
)
if(SOCKPP_BUILD_CAN)
target_sources(sockpp-objs PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/linux/can_address.cpp
${CMAKE_CURRENT_SOURCE_DIR}/linux/can_socket.cpp
)
endif()
endif()
# This is only necessary for older compilers, but doesn't hurt

View File

@@ -43,7 +43,7 @@ using namespace std::chrono;
namespace sockpp {
/////////////////////////////////////////////////////////////////////////////
// udp_socket
// datagram_socket
/////////////////////////////////////////////////////////////////////////////
datagram_socket::datagram_socket(const sock_address& addr)
@@ -53,6 +53,7 @@ datagram_socket::datagram_socket(const sock_address& addr)
if (check_socket_bool(h)) {
reset(h);
// TODO: If the bind fails, should we close the socket and fail completely?
bind(addr);
}
}

104
src/linux/can_address.cpp Normal file
View File

@@ -0,0 +1,104 @@
// can_address.cpp
//
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2014-2021 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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.
// --------------------------------------------------------------------------
#include "sockpp/can_address.h"
#include "sockpp/socket.h"
#include <cstring>
#include <stdexcept>
#include <sys/ioctl.h>
#include <net/if.h>
using namespace std;
namespace sockpp {
/////////////////////////////////////////////////////////////////////////////
constexpr sa_family_t can_address::ADDRESS_FAMILY;
// --------------------------------------------------------------------------
can_address::can_address(unsigned ifindex) : addr_{}
{
addr_.can_family = AF_CAN;
addr_.can_ifindex = ifindex;
}
can_address::can_address(const string& iface) : addr_{}
{
unsigned idx = if_nametoindex(iface.c_str());
if (idx != 0) {
addr_.can_family = AF_CAN;
addr_.can_ifindex = idx;
}
}
can_address::can_address(const sockaddr& addr)
{
auto domain = addr.sa_family;
if (domain != AF_CAN)
throw std::invalid_argument("Not a SocketCAN address");
std::memcpy(&addr_, &addr, sizeof(sockaddr));
}
string can_address::iface() const
{
if (addr_.can_family == AF_UNSPEC)
return string("none");
if (addr_.can_ifindex == 0)
return string("any");
char buf[IF_NAMESIZE];
const char* iface = if_indextoname(addr_.can_ifindex, buf);
return string(iface ? iface : "unknown");
}
// --------------------------------------------------------------------------
ostream& operator<<(ostream& os, const can_address& addr)
{
os << "can:" << addr.iface();
return os;
}
/////////////////////////////////////////////////////////////////////////////
// End namespace sockpp
}

92
src/linux/can_socket.cpp Normal file
View File

@@ -0,0 +1,92 @@
// can_socket.cpp
//
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2021 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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.
// --------------------------------------------------------------------------
#include "sockpp/can_socket.h"
#include "sockpp/socket.h"
#include <sys/ioctl.h>
using namespace std;
using namespace std::chrono;
namespace sockpp {
/////////////////////////////////////////////////////////////////////////////
can_socket::can_socket(const can_address& addr)
{
socket_t h = create_handle(SOCK_RAW, CAN_RAW);
if (check_socket_bool(h)) {
reset(h);
bind(addr);
}
}
system_clock::time_point can_socket::last_frame_time()
{
timeval tv {};
// TODO: Handle error
::ioctl(handle(), SIOCGSTAMP, &tv);
return to_timepoint(tv);
}
double can_socket::last_frame_timestamp()
{
timeval tv {};
// TODO: Handle error
::ioctl(handle(), SIOCGSTAMP, &tv);
return double(tv.tv_sec) + 1.0e-6 * tv.tv_usec;
}
// --------------------------------------------------------------------------
ssize_t can_socket::recv_from(can_frame *frame, int flags,
can_address* srcAddr /*=nullptr*/)
{
sockaddr* p = srcAddr ? srcAddr->sockaddr_ptr() : nullptr;
socklen_t len = srcAddr ? srcAddr->size() : 0;
// TODO: Check returned length
return check_ret(::recvfrom(handle(), frame, sizeof(can_frame),
flags, p, &len));
}
/////////////////////////////////////////////////////////////////////////////
// End namespace sockpp
}