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