IEEE 802.15.4-2006 protocol implementation

This commit is contained in:
Erki 2022-07-28 14:50:10 +03:00
parent c994e63478
commit 6a9697124e
5 changed files with 400 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,135 @@
//
// 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

View File

@ -0,0 +1,146 @@
//
// Created by erki on 28.07.22.
//
#include "radio_protocol.hpp"
#include <cstring>
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;
}
}

View File

@ -0,0 +1,99 @@
//
// Created by erki on 28.07.22.
//
#include "radio_protocol_frame.hpp"
#include <cstring>
namespace
{
using namespace radio::protocol;
template<typename T>
constexpr T zeroInitialized()
{
static_assert(std::is_trivially_default_constructible<T>::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<FrameControlField>())
, 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;
}
}