skl-tunnel/radio/include/radio_protocol_frame.hpp

136 lines
3.3 KiB
C++

//
// Created by erki on 18.07.22.
//
#ifndef SKL_TUNNEL_RADIO_FRAME_HPP
#define SKL_TUNNEL_RADIO_FRAME_HPP
#include <array>
#include <cstdint>
#include <type_traits>
#include <optional>
#include <variant>
#include <utility_assert.hpp>
/**
* 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<std::uint16_t, std::uint64_t> mac = 0ull;
std::uint16_t pan_id = 0;
FrameControlField::AddressingMode getAddressingMode() const
{
if (std::holds_alternative<std::uint16_t>(mac))
return FrameControlField::ADDRESSING_MODE_SHORT;
else if (std::holds_alternative<std::uint64_t>(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<std::uint16_t>(&mac);
SKULLC_ASSERT_DEBUG(addr != nullptr);
return *addr;
}
std::uint64_t getLongAddress() const
{
const std::uint64_t* addr = std::get_if<std::uint64_t>(&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<std::uint8_t, max_payload_size> payload;
FrameStructure();
static FrameStructure createDataFrame();
FrameStructure& setPayload(const std::uint8_t* data, const std::uint8_t length);
template<std::size_t N>
FrameStructure& setPayload(const std::array<std::uint8_t, N>& 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