diff --git a/CMakeLists.txt b/CMakeLists.txt index e49436d..90613cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,8 @@ add_executable(skl_tunnel radio/src/radio_spi.c radio/src/radio_gpio.c radio/src/radio_hw_instance.cpp + radio/src/radio_protocol_frame.cpp + radio/src/radio_protocol.cpp app/src/app_board.cpp app/src/app_transparent_client.cpp diff --git a/radio/include/radio_protocol.hpp b/radio/include/radio_protocol.hpp new file mode 100644 index 0000000..d1e35d2 --- /dev/null +++ b/radio/include/radio_protocol.hpp @@ -0,0 +1,18 @@ +// +// Created by erki on 28.07.22. +// + +#ifndef SKL_TUNNEL_RADIO_PROTOCOL_HPP +#define SKL_TUNNEL_RADIO_PROTOCOL_HPP + +#include "radio_protocol_frame.hpp" + +namespace radio::protocol +{ + +std::size_t composeFrameBuffer(const FrameStructure& frame); +FrameStructure decomposeFrameBuffer(const std::uint8_t* data); + +} + +#endif //SKL_TUNNEL_RADIO_PROTOCOL_HPP diff --git a/radio/include/radio_protocol_frame.hpp b/radio/include/radio_protocol_frame.hpp new file mode 100644 index 0000000..0900db4 --- /dev/null +++ b/radio/include/radio_protocol_frame.hpp @@ -0,0 +1,135 @@ +// +// Created by erki on 18.07.22. +// + +#ifndef SKL_TUNNEL_RADIO_FRAME_HPP +#define SKL_TUNNEL_RADIO_FRAME_HPP + +#include +#include +#include +#include +#include + +#include + +/** + * Reference section 37.1 of the SAMR21 datasheet for these structures. + */ +namespace radio::protocol +{ + +struct PhyHeader +{ + std::uint8_t frame_length : 7; + std::uint8_t reserved : 1; +} __attribute__((packed)); + +struct FrameControlField +{ + enum FrameType + { + FRAME_TYPE_BEACON = 0b000, + FRAME_TYPE_DATA = 0b001, + FRAME_TYPE_ACKNOWLEDGE = 0b010, + FRAME_TYPE_MAC_COMMAND = 0b011 + }; + + enum AddressingMode + { + ADDRESSING_MODE_NOT_SPECIFIED = 0b00, + ADDRESSING_MODE_SHORT = 0b10, + ADDRESSING_MODE_LONG = 0b11 + }; + + FrameType type : 3; + std::uint8_t security_enabled : 1; + std::uint8_t frame_pending : 1; + std::uint8_t ack_requested : 1; + std::uint8_t pan_id_compression : 1; + std::uint8_t : 3; + AddressingMode destination_addressing_mode : 2; + std::uint8_t frame_version : 2; + AddressingMode source_addressing_mode : 2; +} __attribute__((packed)); + +struct Address +{ + std::variant mac = 0ull; + std::uint16_t pan_id = 0; + + FrameControlField::AddressingMode getAddressingMode() const + { + if (std::holds_alternative(mac)) + return FrameControlField::ADDRESSING_MODE_SHORT; + else if (std::holds_alternative(mac)) + return FrameControlField::ADDRESSING_MODE_LONG; + else + return FrameControlField::ADDRESSING_MODE_NOT_SPECIFIED; + } + + void setShortAddress(const std::uint16_t address) + { + mac = address; + } + + void setLongAddress(const std::uint64_t address) + { + mac = address; + } + + std::uint16_t getShortAddress() const + { + const std::uint16_t* addr = std::get_if(&mac); + SKULLC_ASSERT_DEBUG(addr != nullptr); + + return *addr; + } + + std::uint64_t getLongAddress() const + { + const std::uint64_t* addr = std::get_if(&mac); + SKULLC_ASSERT_DEBUG(addr != nullptr); + + return *addr; + } +}; + +struct FrameStructure +{ + FrameControlField frame_control_field; + std::uint8_t sequence_number; + Address destination_address; + Address source_address; + std::uint16_t frame_checksum; + + static constexpr std::size_t max_frame_size = 127; + static constexpr std::size_t max_payload_size + = max_frame_size - sizeof(FrameControlField) - sizeof(frame_checksum) + - sizeof(sequence_number) - (2 * (sizeof(std::uint16_t) + sizeof(std::uint64_t))); + + std::uint8_t payload_length; + std::array payload; + + FrameStructure(); + + static FrameStructure createDataFrame(); + + FrameStructure& setPayload(const std::uint8_t* data, const std::uint8_t length); + template + FrameStructure& setPayload(const std::array& data) + { + static_assert(N <= max_payload_size, "Data length N exceeds max_payload_size."); + return setPayload(data.data(), std::uint8_t(N)); + } + + FrameStructure& setSourceAddress(const Address& addr); + FrameStructure& setDestinationAddress(const Address& addr); + + std::uint8_t getTotalFrameLength() const; + std::uint8_t calculatePayloadLength(const std::uint8_t total_frame_length) const; +}; + +} + +#endif //SKL_TUNNEL_RADIO_FRAME_HPP diff --git a/radio/src/radio_protocol.cpp b/radio/src/radio_protocol.cpp new file mode 100644 index 0000000..1603d10 --- /dev/null +++ b/radio/src/radio_protocol.cpp @@ -0,0 +1,146 @@ +// +// Created by erki on 28.07.22. +// + +#include "radio_protocol.hpp" + +#include + +namespace +{ + +using namespace radio::protocol; + +[[nodiscard]] std::uint8_t* serializeAddress(std::uint8_t* data, const Address& address) +{ + std::memmove(data, &address.pan_id, sizeof(address.pan_id)); + data += sizeof(address.pan_id); + + if (address.getAddressingMode() == FrameControlField::ADDRESSING_MODE_SHORT) + { + const std::uint16_t mac = address.getShortAddress(); + std::memmove(data, &mac, sizeof(mac)); + data += sizeof(mac); + } + else + { + const std::uint64_t mac = address.getLongAddress(); + std::memmove(data, &mac, sizeof(mac)); + data += sizeof(mac); + } + + return data; +} + +[[nodiscard]] std::uint8_t* serializePhyHeader(std::uint8_t* data, const PhyHeader& header) +{ + static_assert(sizeof(PhyHeader) == 1, "PHY Header must be 1 byte/octet."); + std::memmove(data, &header, sizeof(PhyHeader)); + data += sizeof(PhyHeader); + + return data; +} + +[[nodiscard]] std::uint8_t* serializeMacProtocolDataUnit(std::uint8_t* data, const FrameStructure& frame) +{ + static_assert(sizeof(frame.frame_control_field) == 2, "FCF must be 2 bytes/octets."); + std::memmove(data, &frame.frame_control_field, sizeof(frame.frame_control_field)); + data += sizeof(FrameControlField); + + std::memmove(data, &frame.sequence_number, sizeof(frame.sequence_number)); + data += sizeof(frame.sequence_number); + + data = serializeAddress(data, frame.destination_address); + data = serializeAddress(data, frame.source_address); + + std::memmove(data, frame.payload.data(), frame.payload_length); + data += frame.payload_length; + + return data; +} + +[[nodiscard]] const std::uint8_t* deserializeAddress(const std::uint8_t* data, const FrameControlField::AddressingMode& addressing_mode, Address& address) +{ + std::memmove(&address.pan_id, data, sizeof(address.pan_id)); + data += sizeof(address.pan_id); + + if (addressing_mode == FrameControlField::ADDRESSING_MODE_SHORT) + { + std::uint16_t mac = 0; + std::memmove(&mac, data, sizeof(mac)); + data += sizeof(mac); + + address.setShortAddress(mac); + } + else + { + std::uint64_t mac = 0; + std::memmove(&mac, data, sizeof(mac)); + data += sizeof(mac); + + address.setLongAddress(mac); + } + + return data; +} + +[[nodiscard]] const std::uint8_t* deserializePhyHeader(const std::uint8_t* data, PhyHeader& header) +{ + std::memmove(&header, data, sizeof(PhyHeader)); + data += sizeof(PhyHeader); + + return data; +} + +[[nodiscard]] FrameStructure deserializeMacProtocolDataUnit(const std::uint8_t* data, const std::uint8_t total_frame_length) +{ + FrameStructure frame; + std::memmove(&frame.frame_control_field, data, sizeof(frame.frame_control_field)); + data += sizeof(frame.frame_control_field); + + std::memmove(&frame.sequence_number, data, sizeof(frame.sequence_number)); + data += sizeof(frame.sequence_number); + + data = deserializeAddress(data, frame.frame_control_field.destination_addressing_mode, frame.destination_address); + data = deserializeAddress(data, frame.frame_control_field.source_addressing_mode, frame.source_address); + + frame.payload_length = frame.calculatePayloadLength(total_frame_length); + std::memmove(frame.payload.data(), data, frame.payload_length); + data += frame.payload_length; + + std::memmove(&frame.frame_checksum, data, sizeof(frame.frame_checksum)); + + return frame; +} + +} + +namespace radio::protocol +{ + +std::size_t composeFrameBuffer(std::uint8_t* data, const FrameStructure& frame) +{ + std::uint8_t* const data_start = data; + + PhyHeader header; + std::memset(&header, 0, sizeof(PhyHeader)); + header.frame_length = frame.getTotalFrameLength(); + data = serializePhyHeader(data, header); + + data = serializeMacProtocolDataUnit(data, frame); + + return data - data_start; +} + +FrameStructure decomposeFrameBuffer(const std::uint8_t* data) +{ + PhyHeader header; + data = deserializePhyHeader(data, header); + + const std::uint8_t total_frame_length = header.frame_length; + const FrameStructure frame = deserializeMacProtocolDataUnit(data, total_frame_length); + + return frame; +} + +} diff --git a/radio/src/radio_protocol_frame.cpp b/radio/src/radio_protocol_frame.cpp new file mode 100644 index 0000000..9234e06 --- /dev/null +++ b/radio/src/radio_protocol_frame.cpp @@ -0,0 +1,99 @@ +// +// Created by erki on 28.07.22. +// + +#include "radio_protocol_frame.hpp" + +#include + +namespace +{ + +using namespace radio::protocol; + +template +constexpr T zeroInitialized() +{ + static_assert(std::is_trivially_default_constructible::value, "Struct is not trivially default constructible."); + + T t; + std::memset(&t, 0, sizeof(T)); + + return t; +} + +std::uint8_t get_address_size(const Address& address) +{ + std::uint8_t size = sizeof(address.pan_id); + if (address.getAddressingMode() == FrameControlField::ADDRESSING_MODE_SHORT) + size += sizeof(std::uint16_t); + else + size += sizeof(std::uint64_t); + + return size; +}; + +} + +namespace radio::protocol +{ + +FrameStructure::FrameStructure() + : frame_control_field(zeroInitialized()) + , sequence_number(0) + , frame_checksum(0) + , payload_length(0) +{ } + +FrameStructure FrameStructure::createDataFrame() +{ + FrameStructure structure; + structure.frame_control_field.type = FrameControlField::FRAME_TYPE_DATA; + structure.frame_control_field.frame_version = 1; + + return structure; +} + +FrameStructure& FrameStructure::setPayload(const std::uint8_t* data, const std::uint8_t length) +{ + std::memmove(payload.data(), data, length); + return *this; +} + +FrameStructure& FrameStructure::setSourceAddress(const Address& addr) +{ + source_address = addr; + frame_control_field.source_addressing_mode = addr.getAddressingMode(); + return *this; +} + +FrameStructure& FrameStructure::setDestinationAddress(const Address& addr) +{ + destination_address = addr; + frame_control_field.destination_addressing_mode = addr.getAddressingMode(); + return *this; +} + +std::uint8_t FrameStructure::getTotalFrameLength() const +{ + std::uint8_t size = sizeof(frame_control_field) + sizeof(sequence_number); + + size += get_address_size(destination_address); + size += get_address_size(source_address); + size += payload_length; + size += sizeof(frame_checksum); + + return size; +} + +std::uint8_t FrameStructure::calculatePayloadLength(const std::uint8_t total_frame_length) const +{ + std::uint8_t payload_length = total_frame_length - sizeof(frame_control_field) - sizeof(sequence_number); + payload_length -= get_address_size(destination_address); + payload_length -= get_address_size(source_address); + payload_length -= sizeof(frame_checksum); + + return payload_length; +} + +}