IEEE 802.15.4-2006 protocol implementation
This commit is contained in:
parent
c994e63478
commit
6a9697124e
@ -48,6 +48,8 @@ add_executable(skl_tunnel
|
|||||||
radio/src/radio_spi.c
|
radio/src/radio_spi.c
|
||||||
radio/src/radio_gpio.c
|
radio/src/radio_gpio.c
|
||||||
radio/src/radio_hw_instance.cpp
|
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_board.cpp
|
||||||
app/src/app_transparent_client.cpp
|
app/src/app_transparent_client.cpp
|
||||||
|
|||||||
18
radio/include/radio_protocol.hpp
Normal file
18
radio/include/radio_protocol.hpp
Normal 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
|
||||||
135
radio/include/radio_protocol_frame.hpp
Normal file
135
radio/include/radio_protocol_frame.hpp
Normal 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
|
||||||
146
radio/src/radio_protocol.cpp
Normal file
146
radio/src/radio_protocol.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
99
radio/src/radio_protocol_frame.cpp
Normal file
99
radio/src/radio_protocol_frame.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user