kopia lustrzana https://github.com/mobilinkd/tnc3-firmware
329 wiersze
6.8 KiB
C++
329 wiersze
6.8 KiB
C++
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
|
|
// All rights reserved.
|
|
|
|
#ifndef MOBILINKD___HDLC_DECODER_HPP_
|
|
#define MOBILINKD___HDLC_DECODER_HPP_
|
|
|
|
#include "HdlcFrame.hpp"
|
|
|
|
#include <cstdint>
|
|
|
|
namespace mobilinkd { namespace tnc { namespace hdlc {
|
|
|
|
struct Decoder
|
|
{
|
|
static const uint16_t FLAG = 0x7E00;
|
|
static const uint16_t ABORT = 0x7F;
|
|
static const uint16_t IDLE = 0xFF;
|
|
|
|
enum state {SEARCH, HUNT, FRAMING};
|
|
|
|
state state_;
|
|
int ones_;
|
|
uint16_t buffer_;
|
|
IoFrame* frame_;
|
|
int bits_;
|
|
bool passall_;
|
|
bool ready_;
|
|
|
|
Decoder(bool pass_all = false);
|
|
|
|
void reset() {
|
|
state_ = SEARCH;
|
|
ones_ = 0;
|
|
buffer_ = 0;
|
|
if (frame_) frame_->clear();
|
|
ready_ = false;
|
|
}
|
|
|
|
bool process(bool bit)
|
|
{
|
|
switch (state_)
|
|
{
|
|
case SEARCH:
|
|
search(bit);
|
|
break;
|
|
case HUNT:
|
|
hunt(bit);
|
|
break;
|
|
case FRAMING:
|
|
frame(bit);
|
|
break;
|
|
default:
|
|
reset();
|
|
break;
|
|
}
|
|
|
|
return ready_;
|
|
}
|
|
|
|
/**
|
|
* Process a bit. If the bit results in a frame, a result set containing
|
|
* the frame, the FCS, and a flag indicating whether it is valid is
|
|
* returned. If HDLC was contructed with passall=false, the flag returned
|
|
* is always true as only valid frames are returned.
|
|
*
|
|
* When PLL is passed, when true it indicates that the bit should be
|
|
* processed, and when false indicates that any frame in progress should
|
|
* be dropped and the state reset to SEARCH.
|
|
*/
|
|
IoFrame* operator()(bool bit, bool pll);
|
|
|
|
void add_bit(bool bit)
|
|
{
|
|
const uint16_t BIT = 0x8000;
|
|
|
|
buffer_ >>= 1;
|
|
buffer_ |= (BIT * int(bit));
|
|
bits_ += 1;
|
|
}
|
|
|
|
char get_char()
|
|
{
|
|
char result = (buffer_ & 0xFF);
|
|
|
|
return result;
|
|
}
|
|
|
|
void consume_byte()
|
|
{
|
|
const uint16_t MASK = 0xFF00;
|
|
|
|
buffer_ &= MASK;
|
|
bits_ -= 8;
|
|
}
|
|
|
|
void consume_bit()
|
|
{
|
|
const uint16_t MASK = 0xFF00;
|
|
|
|
uint16_t tmp = (buffer_ & 0x7F);
|
|
tmp <<= 1;
|
|
buffer_ &= MASK;
|
|
buffer_ |= tmp;
|
|
bits_ -= 1;
|
|
}
|
|
|
|
void start_search()
|
|
{
|
|
state_ = SEARCH;
|
|
}
|
|
|
|
bool have_flag()
|
|
{
|
|
const uint16_t MASK = 0xFF00;
|
|
|
|
return (buffer_ & MASK) == FLAG;
|
|
}
|
|
|
|
void start_hunt()
|
|
{
|
|
state_ = HUNT;
|
|
bits_ = 0;
|
|
buffer_ = 0;
|
|
}
|
|
|
|
void search(bool bit)
|
|
{
|
|
add_bit(bit);
|
|
|
|
if (have_flag())
|
|
{
|
|
start_hunt();
|
|
}
|
|
}
|
|
|
|
bool have_frame_error()
|
|
{
|
|
switch (buffer_ & 0xFF00)
|
|
{
|
|
case 0xFF00:
|
|
case 0xFE00:
|
|
case 0xFC00:
|
|
case 0x7F00:
|
|
case 0x7E00:
|
|
case 0x3F00:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool have_bogon()
|
|
{
|
|
const uint16_t MASK = 0xFF00;
|
|
|
|
if (bits_ != 8) return false;
|
|
|
|
const uint16_t test = (buffer_ & MASK);
|
|
|
|
switch (test)
|
|
{
|
|
case 0xFF00:
|
|
case 0xFE00:
|
|
case 0x7F00:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void start_frame()
|
|
{
|
|
state_ = FRAMING;
|
|
frame_->clear();
|
|
ones_ = 0;
|
|
buffer_ &= 0xFF00;
|
|
}
|
|
|
|
void hunt(bool bit)
|
|
{
|
|
const uint16_t MASK = 0xFF00;
|
|
|
|
add_bit(bit);
|
|
buffer_ &= MASK;
|
|
|
|
if (bits_ != 8) return;
|
|
|
|
if (have_flag()) {
|
|
start_hunt();
|
|
return;
|
|
}
|
|
|
|
if (have_bogon()) {
|
|
start_search();
|
|
return;
|
|
}
|
|
|
|
if (not have_frame_error()) {
|
|
start_frame();
|
|
return;
|
|
}
|
|
|
|
start_search();
|
|
}
|
|
|
|
void frame(bool bit)
|
|
{
|
|
add_bit(bit);
|
|
|
|
if (ones_ < 5) {
|
|
if (buffer_ & 0x80) ones_ += 1;
|
|
else ones_ = 0;
|
|
|
|
if (bits_ == 16) {
|
|
if (frame_->push_back(get_char())) {
|
|
consume_byte();
|
|
} else {
|
|
// Allocation error.
|
|
frame_->clear();
|
|
start_search();
|
|
}
|
|
}
|
|
|
|
if (have_flag()) {
|
|
if (frame_->size() > 17) {
|
|
ready_ = true;
|
|
}
|
|
}
|
|
} else {
|
|
// 5 ones in a row means the next one should be 0 and be skipped.
|
|
|
|
if ((buffer_ & 0x80) == 0) {
|
|
ones_ = 0;
|
|
consume_bit();
|
|
return;
|
|
} else {
|
|
|
|
// Framing error. Drop the frame. If there is a FLAG
|
|
// in the buffer, go into HUNT otherwise SEARCH.
|
|
if (frame_->size() > 17) {
|
|
ready_ = true;
|
|
return;
|
|
}
|
|
|
|
if ((buffer_ >> (16 - bits_) & 0xFF) == 0x7E) {
|
|
// Cannot call start_hunt() here because we need
|
|
// to preserve buffer state.
|
|
bits_ -= 8;
|
|
state_ = HUNT;
|
|
frame_->clear();
|
|
} else {
|
|
start_search();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool frame_end()
|
|
{
|
|
uint16_t tmp = (buffer_ >> (16 - bits_));
|
|
return (tmp & 0xFF) == FLAG;
|
|
}
|
|
|
|
bool frame_abort()
|
|
{
|
|
uint16_t tmp = (buffer_ >> (16 - bits_));
|
|
return (tmp & 0x7FFF) == 0x7FFF;
|
|
}
|
|
|
|
void abort_frame()
|
|
{
|
|
bits_ = 8;
|
|
buffer_ &= 0xFF00;
|
|
frame_->clear();
|
|
}
|
|
|
|
bool ready() const
|
|
{
|
|
return ready_;
|
|
}
|
|
};
|
|
|
|
#if 0
|
|
|
|
Decoder::Decoder(bool pass_all)
|
|
: state_(SEARCH), ones_(0), buffer_(0), frame_(ioFramePool().acquire())
|
|
, bits_(0), passall_(pass_all), ready_(false)
|
|
{}
|
|
|
|
IoFrame* Decoder::operator()(bool bit, bool pll)
|
|
{
|
|
IoFrame* result = nullptr;
|
|
|
|
// It appears that the ioFramePool may not get initialized in the proper
|
|
// order during some builds.
|
|
if (nullptr == frame_) frame_ = ioFramePool().acquire();
|
|
|
|
if (not pll) {
|
|
if ((state_ == FRAMING) and (frame_->size() > 17) and passall_) {
|
|
frame_->parse_fcs();
|
|
if (passall_ or frame_->ok()) {
|
|
result = frame_;
|
|
ready_ = false;
|
|
frame_ = ioFramePool().acquire();
|
|
return result;
|
|
}
|
|
}
|
|
reset();
|
|
} else {
|
|
if (process(bit)) {
|
|
ready_ = false;
|
|
frame_->parse_fcs();
|
|
if (passall_ or frame_->ok()) {
|
|
result = frame_;
|
|
frame_ = ioFramePool().acquire();
|
|
return result;
|
|
}
|
|
frame_->clear();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
}}} // mobilinkd::tnc::hdlc
|
|
|
|
#endif // MOBILINKD___HDLC_DECODER_HPP_
|