blob: e1a5a0aa11fcc0935fdef80f9d035564d2f99bc7 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "frame.h"
#include <endian.h>
#include <algorithm>
#include "fbl/span.h"
#include "fcs.h"
namespace ppp {
static constexpr uint8_t kAllStationsAddress = 0xff;
static constexpr uint8_t kControlField = 0x03;
static constexpr size_t kMaxFrameSize = 65536;
static constexpr uint8_t kControlEscapeSequence = 0x7d;
static constexpr uint8_t kControlEscapeComplement = 0x20;
static constexpr uint8_t kAsciiControl = 0x20;
static constexpr uint16_t kFrameCheckSequenceComplement = 0xffff;
static void PushEscaped(std::vector<uint8_t>* buffer, uint8_t b) {
if (b < kAsciiControl || b == kControlEscapeSequence || b == kFlagSequence) {
buffer->push_back(kControlEscapeSequence);
buffer->push_back(b ^ kControlEscapeComplement);
} else {
buffer->push_back(b);
}
}
static uint16_t FrameCheck(FrameView frame) {
uint16_t fcs = kFrameCheckSequenceInit;
fcs = Fcs(fcs, fbl::Span<const uint8_t>(&kAllStationsAddress, 1));
fcs = Fcs(fcs, fbl::Span<const uint8_t>(&kControlField, 1));
const uint8_t protocol_upper = static_cast<uint16_t>(frame.protocol) >> 8;
const uint8_t protocol_lower = static_cast<uint16_t>(frame.protocol) & 0xff;
fcs = Fcs(fcs, fbl::Span<const uint8_t>(&protocol_upper, 1));
fcs = Fcs(fcs, fbl::Span<const uint8_t>(&protocol_lower, 1));
fcs = Fcs(fcs, frame.information);
return fcs;
}
std::vector<uint8_t> SerializeFrame(FrameView frame) {
std::vector<uint8_t> buffer;
buffer.reserve(kMaxFrameSize);
buffer.push_back(kFlagSequence);
PushEscaped(&buffer, kAllStationsAddress);
PushEscaped(&buffer, kControlField);
const uint8_t protocol_upper = static_cast<uint16_t>(frame.protocol) >> 8;
const uint8_t protocol_lower = static_cast<uint16_t>(frame.protocol) & 0xff;
PushEscaped(&buffer, protocol_upper);
PushEscaped(&buffer, protocol_lower);
for (uint8_t b : frame.information) {
PushEscaped(&buffer, b);
}
const uint16_t fcs = FrameCheck(frame);
const uint16_t complement = fcs ^ kFrameCheckSequenceComplement;
const uint8_t complement_upper = complement >> 8;
const uint8_t complement_lower = complement & 0xff;
// FCS bytes are written in reverse of network order
PushEscaped(&buffer, complement_lower);
PushEscaped(&buffer, complement_upper);
buffer.push_back(kFlagSequence);
return buffer;
}
fit::result<Frame, DeserializationError> DeserializeFrame(fbl::Span<const uint8_t> raw_frame) {
std::vector<uint8_t> buffer;
buffer.reserve(raw_frame.size());
auto it = raw_frame.begin();
while (it != raw_frame.end()) {
const auto b = *it;
switch (b) {
case kFlagSequence:
if (it != raw_frame.begin() && it != raw_frame.end() - 1) {
return fit::error(DeserializationError::FormatInvalid);
}
buffer.push_back(b);
break;
case kControlEscapeSequence:
++it;
if (it == raw_frame.end()) {
return fit::error(DeserializationError::FormatInvalid);
}
buffer.push_back(*it ^ kControlEscapeComplement);
break;
default:
if (b >= kAsciiControl) {
buffer.push_back(b);
}
break;
}
++it;
}
if (buffer.size() < 8) {
return fit::error(DeserializationError::FormatInvalid);
}
if (buffer.front() != kFlagSequence || buffer.back() != kFlagSequence) {
return fit::error(DeserializationError::FormatInvalid);
}
const uint8_t* begin_frame = buffer.data() + 1;
const size_t frame_size = buffer.size() - 2;
const uint8_t address = begin_frame[0];
if (address != kAllStationsAddress) {
return fit::error(DeserializationError::UnrecognizedAddress);
}
const uint8_t control = begin_frame[1];
if (control != kControlField) {
return fit::error(DeserializationError::UnrecognizedControl);
}
const uint8_t protocol_upper = begin_frame[2];
const uint8_t protocol_lower = begin_frame[3];
const auto protocol = static_cast<Protocol>((protocol_upper << 8) | protocol_lower);
const uint16_t fcs =
Fcs(kFrameCheckSequenceInit, fbl::Span<const uint8_t>(begin_frame, frame_size));
if (fcs != kFrameCheckSequence) {
return fit::error(DeserializationError::FailedFrameCheckSequence);
}
// Erase everything but the information from the buffer.
const auto information_end = std::rotate(buffer.begin(), buffer.begin() + 5, buffer.end() - 3);
buffer.erase(information_end, buffer.end());
return fit::ok(Frame(protocol, std::move(buffer)));
}
} // namespace ppp