/*                                       -*- mode:C++; fill-column: 100 -*-

  SFBByteBuffer.cpp - Circular byte buffering between the UART interrupt and the 
                      background packet dispatching levels
  Copyright (C) 2009 The Regents of the University of New Mexico.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  USA

  $Id$
*/
#include "SFBByteBuffer.h"
#include "SFBAssert.h"
#include "SFBHWMisc.h"            // For suspendInterrupts, restoreInterrupts

void SFBRxByteBuffer::storeByteIL(u8 data) {

  u32 roomNeeded = 1;         // Will need at least one byte

  if (data == BB_ESC)         // Whups, it's an escape.  That
    ++roomNeeded;             // will require an extra byte

  if (flags != 0)             // Whups, we have flags pending.  That
    roomNeeded += 2;          // will take two more bytes to dump them

  if (roomNeeded > spaceAvailable()) { // Whups, not enough room at the inn
    flags |= BBFLAG_OVERFLOW;          // So, we're dropping a(nother) data byte
    return;                            // Sorry about that chief.
  }

  // Here we can safely store everything we need to
  if (flags) {
    uncheckedStoreByte(BB_ESC); // Send out the escape trigger
    uncheckedStoreByte(flags);  // Dump the flags behind it
    flags = 0;                  // It's good to be shut of that nastiness
  }

  if (data == BB_ESC) {
    uncheckedStoreByte(BB_ESC); // Send out the escape trigger
    uncheckedStoreByte(BB_EESC); // Send the escaped escape code behind it
  } else
    uncheckedStoreByte(data); // Not weird, just put the data
}

int SFBRxByteBuffer::fetchByteBG() {
  if (!bytesAvailable())         // No critical section, because in rx buf,
    return -1;
  u8 val = uncheckedFetchByte(); // interrupt can only increase bytesAvailable()
  if (val != BB_ESC)             
    return val;                  // Normal byte, there you go.

  API_ASSERT_NONZERO(bytesAvailable());  // Can't have rxed ESC w/o code after it
  val = uncheckedFetchByte();
  if (val == BB_EESC)            // It was an escaped escape.
    return BB_ESC;               // So they get an escape.

  return 0xffffff00|val;         // It was error or overflow.  Return negative != -1
}

void SFBRxByteBuffer::resetBG() {

  // Here we block, :(.  We can't avoid it because we must atomically
  // both clear the flags and set the indices equal.  But actually, we
  // don't quite clear the flags: We set BBFLAG_OVERFLOW instead, to
  // blow any packet that might have been partially deframed from this
  // buffer at the time we reset it.

  // If we didn't block, and cleared flags first, and then a HW error
  // interrupt occurred before we matched the indices, that would
  // unzero the flags and cause the next #storeByteIL call to emit a
  // kind of spurious error status.

  // If we didn't block, and matched the indices first, and then a HW
  // error interrupt occurred (without any associated good data)
  // before we cleared the flags, we would then zero the flags and so
  // fail to report a genuine error state.
  
  // Neither of which is really the end of the world, probably, but
  // we're trying to be squeaky clean in this particular file of code,
  // and, what the heck, it's a short sequence in a rarely-called
  // method.

  // Given that interrupts are disabled anyway, we set both indices to
  // zero because why not.  Slightly less entropy in the result.

  u32 status;                   
  suspendInterrupts(status);    // START CRITICAL SECTION
  flags = BBFLAG_OVERFLOW;      //   'Clear' flags and zero the
  oldIndex = newIndex = 0;      //   pointers to empty the buffer
  restoreInterrupts(status);    // END CRITICAL SECTION
}

int SFBTxByteBuffer::fetchByteIL() {
  if (bytesAvailable() > 0) 
    return uncheckedFetchByte();
  return -1;
}

void SFBTxByteBuffer::storeByteBG(u8 data) {
  if (pendingCount < 0)         // We previously overflowed and are dumping now
    return;                     // Oh yah yah, I stored your data no worries.

  if (spaceAvailable() > pendingCount)         // If space available is still enough,
    buf[wrap(newIndex+pendingCount++)] = data; // stash data and bump pendingCount
  else
    pendingCount = -1;          // Opps, we just overflowed
}

bool SFBTxByteBuffer::canStoreBytesBG(u32 count) {
  if (pendingCount < 0)  // If we've already overflowed and are dumping now..
    return true;         // Oh yah yah, I can store, what do you need, count?  No problem..

  return spaceAvailable() >= pendingCount+count;  // Here's if I can actually do it..
}


bool SFBTxByteBuffer::shipBytesBG() {
  bool ret = pendingCount >= 0; // True unless we overflowed somewhere along the way
  if (ret)                      // If no overflow, release any pending bytes
    newIndex = wrap(newIndex+pendingCount); // for shipment (atomic update)

  pendingCount = 0;             // Reset to nothing pending / no overflow
  return ret;
}

void SFBTxByteBuffer::resetBG() {

  // These two assignments suffice because tx buffer uses only
  // newIndex and oldIndex to decide what to do at IL.  (Unlike rx buf
  // which also has to deal with its flags).  Only BG looks at
  // pendingCount so we can manipulate that with impunity
  //
  // The assignment must go this way (vs 'oldIndex = newIndex;')
  // because BG owns oldIndex and so knows oldIndex won't change
  // between the time of loading it and the time of (atomically)
  // storing it in newIndex.

  newIndex = oldIndex;
  pendingCount = 0;
}
