// Copyright 2015 Mobilinkd LLC // All rights reserved. #ifndef MOBILINKD___HDLC_DECODER_HPP_ #define MOBILINKD___HDLC_DECODER_HPP_ #include "HdlcFrame.hpp" #include 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_