Skip to content

Commit

Permalink
Support for serial UART and local domain socket protocolInterfaces.
Browse files Browse the repository at this point in the history
  • Loading branch information
lhoward committed Apr 5, 2024
1 parent 2927907 commit dcfb508
Show file tree
Hide file tree
Showing 11 changed files with 1,932 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Expand Up @@ -30,6 +30,10 @@ option(BUILD_AVDECC_INTERFACE_PCAP_DYNAMIC_LINKING "Pcap protocol interface uses
option(BUILD_AVDECC_INTERFACE_MAC "Build the macOS native protocol interface (macOS only)." TRUE)
option(BUILD_AVDECC_INTERFACE_PROXY "Build the proxy protocol interface." FALSE)
option(BUILD_AVDECC_INTERFACE_VIRTUAL "Build the virtual protocol interface (for unit tests)." TRUE)
if(NOT WIN32)
option(BUILD_AVDECC_INTERFACE_SERIAL "Build the serial protocol interface." TRUE)
option(BUILD_AVDECC_INTERFACE_LOCAL "Build the local domain socket protocol interface." TRUE)
endif()
# Install options
option(INSTALL_AVDECC_EXAMPLES "Install examples." FALSE)
option(INSTALL_AVDECC_TESTS "Install unit tests." FALSE)
Expand Down
9 changes: 9 additions & 0 deletions include/la/avdecc/internals/protocolInterface.hpp
Expand Up @@ -66,6 +66,8 @@ class ProtocolInterface : public la::avdecc::utils::Subject<ProtocolInterface, s
MacOSNative = 1u << 1, /**< macOS native API protocol interface - Only usable on macOS. */
Proxy = 1u << 2, /**< IEEE Std 1722.1 Proxy protocol interface. */
Virtual = 1u << 3, /**< Virtual protocol interface. */
Serial = 1u << 4, /**< Serial port protocol interface. */
Local = 1u << 8, /**< Local domain socket protocol interface. */
};

/** Possible Error status returned (or thrown) by a ProtocolInterface */
Expand Down Expand Up @@ -314,6 +316,13 @@ class ProtocolInterface : public la::avdecc::utils::Subject<ProtocolInterface, s
ProtocolInterface& operator=(ProtocolInterface&&) = delete;

protected:
/**
* @brief Create a ProtocolInterface not associated with a network interface.
* @details Create a ProtocolInterface not associated with a network interface. The MAC address is initialized to zero.
* @param[in] executorName The name of the executor to use to dispatch incoming messages.
*/
ProtocolInterface(std::string const& executorName);

/**
* @brief Create a ProtocolInterface associated with specified network interface name.
* @details Create a ProtocolInterface associated with specified network interface name, checking the interface actually exists.
Expand Down
2 changes: 2 additions & 0 deletions include/la/avdecc/internals/typedefs.h
Expand Up @@ -134,6 +134,8 @@ enum avdecc_protocol_interface_type_e
avdecc_protocol_interface_type_macos_native = 1u << 1, /**< macOS native API protocol interface - Only usable on macOS. */
avdecc_protocol_interface_type_proxy = 1u << 2, /**< IEEE Std 1722.1 Proxy protocol interface. */
avdecc_protocol_interface_type_virtual = 1u << 3, /**< Virtual protocol interface. */
avdecc_protocol_interface_type_serial = 1u << 4, /**< Serial port protocol interface. */
avdecc_protocol_interface_type_local = 1u << 8, /**< Local domain socket protocol interface. */
};

/** Valid values for avdecc_protocol_interface_error_t */
Expand Down
24 changes: 24 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -219,6 +219,30 @@ if(BUILD_AVDECC_INTERFACE_VIRTUAL)
list(APPEND ADD_PRIVATE_COMPILE_OPTIONS "-DHAVE_PROTOCOL_INTERFACE_VIRTUAL")
endif()

# Serial Protocol interface
if(BUILD_AVDECC_INTERFACE_SERIAL)
list(APPEND SOURCE_FILES_PROTOCOL_INTERFACE
protocolInterface/protocolInterface_serial.cpp
protocolInterface/cobsSerialization.cpp
)
list(APPEND HEADER_FILES_PROTOCOL_INTERFACE
protocolInterface/protocolInterface_serial.hpp
protocolInterface/cobsSerialization.hpp
)
list(APPEND ADD_PRIVATE_COMPILE_OPTIONS "-DHAVE_PROTOCOL_INTERFACE_SERIAL")
endif()

# Local Domain Socket Protocol interface
if(BUILD_AVDECC_INTERFACE_LOCAL)
list(APPEND SOURCE_FILES_PROTOCOL_INTERFACE
protocolInterface/protocolInterface_local.cpp
)
list(APPEND HEADER_FILES_PROTOCOL_INTERFACE
protocolInterface/protocolInterface_local.hpp
)
list(APPEND ADD_PRIVATE_COMPILE_OPTIONS "-DHAVE_PROTOCOL_INTERFACE_LOCAL")
endif()

# Features
if(ENABLE_AVDECC_FEATURE_REDUNDANCY)
list(APPEND ADD_PUBLIC_COMPILE_OPTIONS "-DENABLE_AVDECC_FEATURE_REDUNDANCY")
Expand Down
125 changes: 125 additions & 0 deletions src/protocolInterface/cobsSerialization.cpp
@@ -0,0 +1,125 @@
/*
* Copyright 2011, Jacques Fortier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the “Software”), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include <stdexcept>

#include "cobsSerialization.hpp"

namespace la
{
namespace avdecc
{
namespace protocol
{
namespace cobs
{

/**
* COBS encodes a message
* @param input [in] pointer to the raw message
* @param input_length [in] the length of the raw message
* @param output [out] pointer to the output encode buffer
* @return the number of bytes written to "output".
*/
std::size_t encode(const std::uint8_t* input, std::size_t input_length, std::uint8_t* output) noexcept
{
std::size_t read_index = 0, write_index = 1;
std::size_t code_index = 0;
std::uint8_t code = 1;

while (read_index < input_length)
{
if (input[read_index] == 0)
{
output[code_index] = code;
code = 1;
code_index = write_index++;
read_index++;
}
else
{
output[write_index++] = input[read_index++];
code++;
if (code == 0xFF)
{
output[code_index] = code;
code = 1;
code_index = write_index++;
}
}
}

output[code_index] = code;

return write_index;
}

/**
* Decodes a COBS encoded message
* @param input [in] pointer to the COBS encoded message
* @param input_length [in] the length of the COBS encoded message
* @param output [in] pointer to the decode buffer
* @param output_length [in] length of the decode buffer
* @return the number of bytes written to "output" if "input" was successfully
* unstuffed, and 0 if there was an error unstuffing "input".
*/
std::size_t decode(const std::uint8_t* input, std::size_t input_length, std::uint8_t* output, std::size_t output_length)
{
std::size_t read_index = 0, write_index = 0;
std::uint8_t code, i;

while (read_index < input_length)
{
code = input[read_index];

if (read_index + code > input_length && code != 1)
{
return 0;
}

read_index++;

for (i = 1; i < code; i++)
{
if (write_index == output_length)
{
throw std::invalid_argument("Not enough room to decode");
}
output[write_index++] = input[read_index++];
}
if (code != 0xFF && read_index != input_length)
{
if (write_index == output_length)
{
throw std::invalid_argument("Not enough room to decode");
}
output[write_index++] = '\0';
}
}

return write_index;
}

} // namespace cobs
} // namespace protocol
} // namespace avdecc
} // namespace la
70 changes: 70 additions & 0 deletions src/protocolInterface/cobsSerialization.hpp
@@ -0,0 +1,70 @@
/*
* Copyright 2011, Jacques Fortier
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the “Software”), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#pragma once

#include <cstdint>
#include <cstddef>

namespace la
{
namespace avdecc
{
namespace protocol
{
namespace cobs
{
/**
* Delimiter byte to frame COBS encoded data
*/
static constexpr std::uint8_t DelimiterByte = 0;

/**
* Macro to calculate the maximum number of COBS pad bytes with a given payload size 'n'
* @note Do not use this macro to determine the overhead resulting from a COBS encoding. Use the return value from \ref cobs_encode instead
*/
#define COBS_BUFFER_PAD(n) ((((n) + 254 - 1) & ~(254 - 1)) / 254)

/**
* COBS encodes a message
* @param input [in] pointer to the raw message
* @param input_length [in] the length of the raw message
* @param output [out] pointer to the output encode buffer
* @return the number of bytes written to "output".
*/
std::size_t encode(const std::uint8_t* input, std::size_t input_length, std::uint8_t* output) noexcept;

/**
* Decodes a COBS encoded message
* @param input [in] pointer to the COBS encoded message
* @param input_length [in] the length of the COBS encoded message
* @param output [in] pointer to the decode buffer
* @param output_length [in] length of the decode buffer
* @return the number of bytes written to "output" if "input" was successfully
* unstuffed, and 0 if there was an error unstuffing "input".
*/
std::size_t decode(const std::uint8_t* input, std::size_t input_length, std::uint8_t* output, std::size_t output_length);

} // namespace cobs
} // namespace protocol
} // namespace avdecc
} // namespace la
46 changes: 46 additions & 0 deletions src/protocolInterface/protocolInterface.cpp
Expand Up @@ -39,13 +39,31 @@
#ifdef HAVE_PROTOCOL_INTERFACE_VIRTUAL
# include "protocolInterface/protocolInterface_virtual.hpp"
#endif // HAVE_PROTOCOL_INTERFACE_VIRTUAL
#ifdef HAVE_PROTOCOL_INTERFACE_SERIAL
# include "protocolInterface/protocolInterface_serial.hpp"
#endif // HAVE_PROTOCOL_INTERFACE_SERIAL
#ifdef HAVE_PROTOCOL_INTERFACE_LOCAL
# include "protocolInterface/protocolInterface_local.hpp"
#endif // HAVE_PROTOCOL_INTERFACE_LOCAL

namespace la
{
namespace avdecc
{
namespace protocol
{
// constructor for non-network protocol interfaces
ProtocolInterface::ProtocolInterface(std::string const& executorName)
: _executorName{ executorName }
{
// Check if the executor exists
if (!ExecutorManager::getInstance().isExecutorRegistered(_executorName))
{
throw Exception(Error::ExecutorNotInitialized, "The receive executor '" + std::string{ _executorName } + "' is not registered");
}
_networkInterfaceMacAddress = { 0, 0, 0, 0, 0, 0 };
}

// Throws an Exception if networkInterfaceName is not usable
ProtocolInterface::ProtocolInterface(std::string const& networkInterfaceName, std::string const& executorName)
: _networkInterfaceName(networkInterfaceName)
Expand Down Expand Up @@ -196,6 +214,14 @@ ProtocolInterface* LA_AVDECC_CALL_CONVENTION ProtocolInterface::createRawProtoco
case Type::Virtual:
return ProtocolInterfaceVirtual::createRawProtocolInterfaceVirtual(networkInterfaceName, { { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 } }, executorName);
#endif // HAVE_PROTOCOL_INTERFACE_VIRTUAL
#if defined(HAVE_PROTOCOL_INTERFACE_SERIAL)
case Type::Serial:
return ProtocolInterfaceSerial::createRawProtocolInterfaceSerial(networkInterfaceName, executorName);
#endif // HAVE_PROTOCOL_INTERFACE_SERIAL
#if defined(HAVE_PROTOCOL_INTERFACE_LOCAL)
case Type::Local:
return ProtocolInterfaceLocal::createRawProtocolInterfaceLocal(networkInterfaceName, executorName);
#endif // HAVE_PROTOCOL_INTERFACE_LOCAL
default:
break;
}
Expand All @@ -222,6 +248,10 @@ std::string LA_AVDECC_CALL_CONVENTION ProtocolInterface::typeToString(Type const
return "IEEE Std 1722.1 proxy";
case Type::Virtual:
return "Virtual interface";
case Type::Serial:
return "Serial port interface";
case Type::Local:
return "Local domain socket interface";
default:
return "Unknown protocol interface type";
}
Expand Down Expand Up @@ -264,6 +294,22 @@ ProtocolInterface::SupportedProtocolInterfaceTypes LA_AVDECC_CALL_CONVENTION Pro
s_supportedProtocolInterfaceTypes.set(Type::Virtual);
}
#endif // HAVE_PROTOCOL_INTERFACE_VIRTUAL

// Serial
#if defined(HAVE_PROTOCOL_INTERFACE_SERIAL)
if (protocol::ProtocolInterfaceSerial::isSupported())
{
s_supportedProtocolInterfaceTypes.set(Type::Serial);
}
#endif // HAVE_PROTOCOL_INTERFACE_SERIAL

// Local domain socket
#if defined(HAVE_PROTOCOL_INTERFACE_LOCAL)
if (protocol::ProtocolInterfaceLocal::isSupported())
{
s_supportedProtocolInterfaceTypes.set(Type::Local);
}
#endif // HAVE_PROTOCOL_INTERFACE_LOCAL
}

return s_supportedProtocolInterfaceTypes;
Expand Down

0 comments on commit dcfb508

Please sign in to comment.