//---------------------------------------------------------------------------
// Copyright (C) 2001 Dallas Semiconductor Corporation, All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//---------------------------------------------------------------------------
//
//     Module Name: JibComm
//
//     Description: Low-level Java-Powered iButton Communication
//
//        Filename: jib96o.c
//
// Dependant Files: jib96.h
//
//         Version: 1.00 (based on cibcomm.c Ver 2.206)
//
//

#include "ownet.h"
#include "jib96.h"

// defines
#define STREAM_OVERHEAD 4
#define ME_RUNTIME      12
#define SLEEP_PAD       6


// exportable functions
//ushort SendCiBMessage(int, uchar *, ushort, uchar);
//ushort RecvCiBMessage(int, uchar *, ushort *);
//void SetMinRunTime(ushort);

// local functions
static uchar SendSegment(int, uchar *, uchar *, ushort, ushort *);
static uchar SendData(int, uchar *, uchar, ushort *);
static uchar SendHeader(int, uchar, uchar *, ushort, uchar, ushort *);
static uchar GetStatus(int, uchar *, uchar *, uchar *, uchar *);
static uchar SendStatus(int, uchar);
static uchar SendRun(int);
static uchar SendReset(int);
static uchar SendInterrupt(int);
static uchar RecvHeader(int, uchar *, ushort *, ushort *, uchar *, ushort *);
static uchar RecvData(int, uchar *, uchar, ushort, ushort *, uchar);
static uchar ReleaseSequence(int, ushort);
static uchar CheckCRC(int, ushort);
static uchar CheckStreamCRC(uchar *, int, ushort);
static void TimeDelay(int, uchar);
static ushort CRC16TGen(uchar, ushort);
static void GenCRC16Table();
static ushort CRC16(uchar, ushort);
static ushort CRC16Buf(uchar *, ushort, ushort);
static void GenCRC16CorrectionTable();
static ushort GetCRC16Correction(uchar);

// globals
static ushort MinRun = 96;
static int StreamLen = 0;
static uchar StreamBuff[256+STREAM_OVERHEAD];
static uchar LastSegment = FALSE;
static ushort CRCTable[256];
static ushort CRCCorrectionTable[140];
static uchar PowerOn = FALSE;
static uchar didsetup = FALSE;

//--------------------------------------------------------------------------
//
// Top-Level function to send a message to the CiB
//
//      portnum   = port number the device is on
//      mp        = Pointer to message string
//      mlen      = Length of message (bytes)
//      ExecTime  = Estimated execution time (0-15, where 0 = minimum)
//
//      return    = Error code, or zero if all went well
//
ushort SendCiBMessage(int portnum,
                        uchar *mp,
                        ushort mlen,
                        uchar ExecTime)
{
  uchar ETime;            // Temp value for ExecTime
  uchar Retry;            // Retry counter
  uchar seg;              // Current segment number
  uchar InFree;           // Inbound FIFO bytes available
  uchar OutUsed;          // Outbound FIFO bytes used
  uchar owms;             // micro status
  uchar cpst;             // Co-Processor status
  ushort checksum = 0;  // checksum accumulator
  ushort sum = 0;       // Temporary checksum store
  uchar error;

  // check for setup
  if (!didsetup)
  {
     didsetup = TRUE;
     GenCRC16Table();
     GenCRC16CorrectionTable();
  }

  seg = 127;          // Start with no segment number done

  ExecTime &= 0xFF;
  ETime = (uchar) ExecTime;

  // Set-up a Retry loop
  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      // Get the status from the device
      if((error = GetStatus(portnum, &InFree, &OutUsed, &owms, &cpst)) != 0)
        break;

      // See if a POR has occurred and treat it as an error if so
      if(owms & CE_BPOR)
      {
        error = ER_POR;
        break;
      }

      // See if the co-processor was running when we stopped
      if(cpst & 1)
      {
        // Send the time value to the CIB
        if((error = SendStatus(portnum, ETime)) != 0)
          break;

        // Execute the co-processor and allow it to finish up
        if((error = SendRun(portnum)) != 0)
          break;

        TimeDelay(portnum, (uchar) (ETime+SLEEP_PAD));
      }
      else
      {
        // If the co-processor is not running, then

        // Work around for 0 owms
        if(!(owms & 0x7f) && !OutUsed)
          owms = 0x3F;

        // See if the previous command has not yet been completed
        if((owms & 0x20) ||
           ((owms & 0x1F) == CE_FIRSTBIRTH) ||
           (owms == 0x1F))
        {

           // If Operation does not finish double previous time until 8
           // then increment by one until max time (15 = 3.75 s +64 ms) 9/25 CIM

            if (ETime == 0)
               ETime++;
            else if (ETime < 8)
               ETime <<=1;
            else if ((ETime >= 8) && (ETime < 15))
               ETime++;
            else
              ETime = 15;

          // if we don't allow a First Birthday (FB) to complete
          // we can end up in an infinite POR loop - FBs
          // currently take on the order of 1.5 Seconds

          if((owms & 0x1F) == CE_FIRSTBIRTH)
          {
            // Send the time value to the CIB
            if((error = SendStatus(portnum, ME_RUNTIME)) != 0)
              break;
          }
          else
          {
            // Send the time value to the CIB
            if((error = SendStatus(portnum, ETime)) != 0)
              break;
          }

          // Execute the micro and allow it to finish up
          if((error = SendRun(portnum)) != 0)
            break;

          if((owms & 0x1F) == CE_FIRSTBIRTH)
          {
            TimeDelay(portnum, ME_RUNTIME);
            TimeDelay(portnum, 1);
          }
          else
          {
            // Wait for whatever we did above to finish up
            // don't forget the extra pad for the coprocessor alarm
            TimeDelay(portnum, (uchar)(ETime+SLEEP_PAD));
          }

        }
        else
        {
          // See if the device has room for new command. Error if not
          if(InFree < HEADERSIZE)
          {
            error = ER_NOROOM;
            break;
          }

          // If the segment we're about to send is not the FIRST one,
          // check the status to see if we had any comm errors on the last
          // (Errors left over from prior uses don't concern us!)

          if(seg != 127)
          {
            if(((owms & 31) > 1) & ((owms & 31) < 8))
            {
              error = (uchar)((owms & 31) | ER_COMMERROR);
              break;
            }
          }

          // If the segment number MS bit = 1, then we're done
          if(seg & 128)
          {
            // Successful exit
            return 0;
          }

          // When we have finished a segment, go to the next segment
          seg++;
          seg = (uchar)(seg & 127);

          //If about to send First segment, clear checksum accumulator
          checksum = seg ? checksum + sum : 0;

          // For each new segment that we attempt to send out, we start a
          // temporary checksum in 'sum' with the current checksum value.
          // If the segment transfer fails, we will not have added
          // anything to the actual checksum so we can try again

          sum = checksum;

          // Send out the next segment
          if((error = SendSegment(portnum, &seg, mp, mlen, &sum)) != 0)
            break;

          // Now we have loaded the data segment into IPR register and
          // the header for it into the FIFO. Next set the execution time
          // if it's the last message segment, and the execute the message

          if(seg & 128)
          {
            ETime = (uchar) ExecTime;

            if((error = SendStatus(portnum, ETime)) != 0)
                break;
          }
          else
            ETime = 0;

          // Send the run-interrupt command to the device
          if((error = SendInterrupt(portnum)) != 0)
             break;
        }

        TimeDelay(portnum, ETime);
      }

    } // End of forever loop

    // Save the error cause
    ETime = error;

    // If we had an error and broke out of the loop, reset the micro
    if((error = SendReset(portnum)) != 0)
      break;

    // Send the time value to the CIB
    if((error = SendStatus(portnum, (uchar)(Retry? (Retry<<1): 1))) != 0)
      break;

    // Then allow it to run and report a completion status
    if((error = SendRun(portnum)) != 0)
      break;

    // See if a little run time will cure the problem that caused the reset
    TimeDelay(portnum, (uchar)((Retry? (Retry<<1): 1)+SLEEP_PAD));

    // We always begin again at the start (segment 0) after an error
    seg = 127;

    // Restore the original error cause code
    error = ETime;

  } //End of Retry loop

  // Error-return if we failed all the retries
  return error;
}

//--------------------------------------------------------------------------
//
// Send out one (specific) segment of the message to the device
//
//      portnum = port number the device is on
//      pseg    = Pointer to current segment number (0 = First)
//      mp      = Pointer to message string
//      msglen  = Length of message string (bytes)
//      psum    = Pointer to checksum accumulator
//
//      return  =   Error code, or zero if all went well
//
uchar SendSegment(int portnum, uchar* pseg, uchar* mp, ushort msglen, ushort* psum)
{
  uchar     seglen;      // Length of segment we're sending
  ushort    indexx;       // Offset of segment we're sending
  uchar     remain;      // Bytes remaining before this seg
  uchar*    segstart;    // Start of segment in message
  uchar     error;

  // Offset of next segment to send
  indexx = (ushort)(*pseg * SEGSIZE);

  // Computer how much data remains unsent to the device
  remain = (uchar)(msglen - indexx);      // Length of segment remaining send

  // Limit the segment size to that which is allowed in this device
  if(remain > SEGSIZE)
  {
    // If remain is bigger than max, send max
    seglen = SEGSIZE;
  }
  else
  {
    // If remain will fit, wrap it up
    seglen = remain;
    // Flag this as the last segment
    *pseg |= 128;
  }

  // Define the point in the message where this segment begins
  segstart = mp + indexx;

  // Send out the segment data to the device
  if((error = SendData(portnum, segstart, seglen, psum)) != 0)
    return error;

  // Send out the header to the device
  if((error = SendHeader(portnum, *pseg, segstart, remain, seglen, psum)) != 0)
    return error;

  return 0;
}

//--------------------------------------------------------------------------
//
// Send out the data portion of a message segment to the device
//
//      portnum   =   port number the device is on
//      mp        =   Pointer to segment in the message string
//      seglen    =   length of this segment
//      psum      =   Pointer to checksum accumulator
//
//      return    =   Error code, or zero if all went well
//
uchar SendData(int portnum, uchar* mp, uchar seglen, ushort* psum)
{
  uchar  Retry,
         error;
  uchar  i;
  ushort crc;

  // Now try (and Retry) to output the data without error
  for (Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      // Send out the data segment to the device and compute the crc
      // and checksum as it goes out
      StreamLen = 0;

      StreamBuff[StreamLen++] = OW_IPRWRITE;
      StreamBuff[StreamLen++] = seglen;

      for(i = 0; i < seglen; i++)
        StreamBuff[StreamLen++] = mp[i];

      // set up the crc bytes

      // CHECK FOR BLIVET

      StreamBuff[StreamLen++] = 0xff;
      StreamBuff[StreamLen++] = 0xff;

      if(!owAccess(portnum))
      {
         error = ER_ACCESS;
         break;
      }

      if(!owBlock(portnum, FALSE, StreamBuff, StreamLen))
      {
        error = ER_ACCESS;
        break;
      }

      crc = CRC16Buf(StreamBuff, 0, (ushort)(StreamLen-2));

      // Check the returning CRC from the device
      if((error = CheckStreamCRC(StreamBuff,StreamLen-2,crc)) != 0)
      {
        if((error = CheckStreamCRC(StreamBuff,StreamLen-2, (ushort)(crc ^ GetCRC16Correction(sizeof(crc))))) != 0)
          break;

        StreamBuff[2] &= 0x0FE;
      }

      for(i = 0; i < seglen; i++)
        mp[i] = StreamBuff[i+2];

      for(i = 0; i < seglen; i++)
        *psum += mp[i];

      // If all has gone well, return with AOK status
      return 0;
    }
  }

  // If we've retried and failed, return with failure status
  return (uchar)(error | ER_SENDDATA);
}

//--------------------------------------------------------------------------
//
// Send out the header portion of a message segment to the device
//
//      portnum  =   port number the device is on
//      seg      =   Segment number to send out (0-127)
//      mp       =   Pointer to segment in message string
//      remain   =   Number of bytes remaining, including this segment
//      psum     =   Pointer to checksum accumulator
//
//      return   =   Error code, or zero if all went well
//
uchar SendHeader(int portnum, uchar seg, uchar* mp, ushort remain, uchar seglen, ushort* psum)
{
  ushort    crc,
            segcrc;
  uchar     Retry,
            error;
  FIFORDWR  FB;
  ushort    i;

  // We must compute the data segment's CRC for the header
  // This includes the segment length value

  segcrc = CRC16(seglen, 0);
  segcrc = CRC16Buf(mp, segcrc, seglen);

  // Now try (and Retry) sending the header to the device
  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      if(!owAccess(portnum))
      {
        error = ER_ACCESS;
        break;
      }

      FB.Cmd = OW_FIFOWRITE;
      FB.Len = FIFO_LEN;

      FB.FIFOBuf[0] = seg;
      FB.FIFOBuf[1] = seglen;
      FB.FIFOBuf[2] = LSB(remain);
      FB.FIFOBuf[3] = MSB(remain);
      FB.FIFOBuf[4] = LSB(segcrc);
      FB.FIFOBuf[5] = MSB(segcrc);

      for(i = 0; i < 6; i++)
        *psum += FB.FIFOBuf[i];

      FB.FIFOBuf[6] = LSB(*psum);
      FB.FIFOBuf[7] = MSB(*psum);

      if(!owBlock(portnum, FALSE, &FB.Cmd, 10))
      {
         error = ER_ACCESS;
         break;
      }

      crc = CRC16Buf(&FB.Cmd, 0, sizeof(FB));

      //
      // Add the checksum bytes to the checksum for subsequent segments
      // (Uses segcrc because it's handy!)

      segcrc = *psum;
      *psum += LSB(segcrc);
      *psum += MSB(segcrc);

      // Check the returning CRC from the device
      if((error = CheckCRC(portnum, crc)) != 0)
        break;

      // Send the release sequence and check the ACK bit
      if((error = ReleaseSequence(portnum, RS_FIFOWRITE)) != 0)
        break;

      // If all went well, return with OK status
      return 0;
    }
  }

  // If we retried and failed, return with failure status
  return (uchar)(error | ER_SENDHDR);
}

//--------------------------------------------------------------------------
//
// Return the device status values -
//
//    portnum   =   port number the device is on
//    InFree    =   Pointer to status = Number of free bytes in FIFO
//    OutUsed   =   Pointer to status = Number of bytes waiting in FIFO
//    owms      =   Pointer to status = One-Wire Micro Status byte
//    cpst      =   Pointer to status = Co-Processor Status Byte
//
//    return    =   Error code, or zero if all went well
//
uchar GetStatus(int portnum, uchar* InFree, uchar* OutUsed, uchar* owms, uchar* cpst)
{
  uchar    IOBuf[5];
  uchar    Retry,
           error;
  ushort crc;
  int      i;

  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      StreamLen = 0;

      StreamBuff[StreamLen++] = OW_STATUSREAD;

      // CHECK FOR BLIVET

      for(i = 0; i < 6; i++) // 2 for CRC read
        StreamBuff[StreamLen++] = 0xff;

      if(!owAccess(portnum))
      {
         error = ER_ACCESS;
         break;
      }

      if(!owBlock(portnum, FALSE, StreamBuff, StreamLen))
      {
        error = ER_ACCESS;
        break;
      }

      crc = CRC16Buf(StreamBuff, 0, (ushort)(StreamLen-2));

      if((error = CheckStreamCRC(StreamBuff,StreamLen-2,crc)) != 0)
      {
        if(!(StreamBuff[1] & 1))
          break;

        StreamBuff[1] &= 0xFE;

        crc = CRC16Buf(StreamBuff, 0, (ushort)(StreamLen-2));

        if((error = CheckStreamCRC(StreamBuff,StreamLen-2,crc)) != 0)
          break;
      }

      for(i = 0; i < 5; i++)
        IOBuf[i] = StreamBuff[i];

      *InFree  = IOBuf[1];
      *OutUsed = IOBuf[2];
      *owms    = IOBuf[3];
      *cpst    = IOBuf[4];

      // If success, return an AOK status
      return 0;
    }
  }

  // If we've re-tried and failed, return a failure status
  return (uchar)(error | ER_GETSTATUS);
}

//--------------------------------------------------------------------------
//
//   Send the owus byte (timing value) to the device -
//
//      portnum     =   port number the device is on
//      owus        =   One-Wire Uart Status byte
//
//      return      =   Error code, or zero if all went well
//
uchar SendStatus(int portnum, uchar owus)
{
  uchar  Retry,
         error;
  ushort crc;

  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      if(!owAccess(portnum))
      {
        error = ER_ACCESS;
        break;
      }

      crc = 0;
      crc = CRC16((uchar)owTouchByte(portnum, OW_STATUSWRITE), crc);
      crc = CRC16((uchar)owTouchByte(portnum, owus), crc);


      if((error = CheckCRC(portnum, crc)) != 0)
        break;

      if((error = ReleaseSequence(portnum, RS_STATUSWRITE)) != 0)
        break;

      // If all is well, exit with OK status
      return 0;
    }
  }

  // If retries failed, return with failure status
  return (uchar)(error | ER_SENDSTATUS);
}

//--------------------------------------------------------------------------
//
// The SendRun function sends the micro-run command to the device
//
//      portnum   =   port number the device is on
//      return    =   Error code, or zero if all went well
//
uchar SendRun(int portnum)
{
  uchar  Retry,
         error;

  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      if(!owAccess(portnum))
      {
        error = ER_ACCESS;
        break;
      }

      if(!owWriteByte(portnum, OW_MICRORUN))
      {
         error = ER_ACCESS;
         break;
      }

      // Issue the release sequence code
      if((error = ReleaseSequence(portnum, RS_MICRORUN)) != 0)
        break;

      // If all went well, return with AOK status
      return 0;
    }
  }

  // If errors occurred return error status
  return (uchar)(error | ER_SENDRUN);
}

//--------------------------------------------------------------------------
//
// The SendReset function sends the micro-reset command to the device
//
//      portnum   =   port number the device is on
//      return    = Error code, or zero if all went well
//
uchar SendReset(int portnum)
{
  uchar Retry,
       error;

  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      if(!owAccess(portnum))
      {
        error = ER_ACCESS;
        break;
      }

      if(!owWriteByte(portnum, OW_MICRORESET))
      {
         error = ER_ACCESS;
         break;
      }

      if((error = ReleaseSequence(portnum, RS_MICRORESET)) != 0)
        break;

      // If all went well, return w/o error
      return 0;
    }
  }

  return (uchar)(error | ER_SENDRESET);
}

//--------------------------------------------------------------------------
//
// The SendInterrupt function sends the micro-interrupt command to the device
//
//      portnum   =   port number the device is on
//      return    =   Error code, or zero if all went well
//
uchar SendInterrupt(int portnum)
{
  uchar Retry,
        error;

  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      if(!owAccess(portnum))
      {
        error = ER_ACCESS;
        break;
      }

      if(!owWriteByte(portnum, OW_MICROINTERRUPT))
      {
         error = ER_ACCESS;
         break;
      }

      if((error = ReleaseSequence(portnum, RS_MICROINTERRUPT)) != 0)
        break;

      return 0;
    }
  }

  return (uchar)(error | ER_SENDINT);
}

//--------------------------------------------------------------------------
//
// Return Message Function -
//
//    portnum   =   port number the device is on
//    mp        =   Current Pointer to message buffer string
//    bufsize   =   Number of bytes in provided buffer space
//
//    return    =   Error code, or zero if all went well
//
ushort RecvCiBMessage(int portnum, uchar *mp, ushort *bufsize)
{
  uchar  ETime = 0;      // Temporary time value
  uchar  *mbuf;          // Start of message buffer
  uchar  InFree;         // Inbound FIFO bytes available
  uchar  OutUsed;        // Outbound FIFO bytes used
  uchar  owms;           // micro status
  uchar  cpst;           // Co-Processor status
  uchar  seg = 0;        // Current segment number
  ushort hdrcrc = 0;     // Crc from header
  ushort hdrsum = 0;     // Checksum from header
  ushort checksum;       // Running checksum of the message
  ushort sum = 0;        // Temporary checksum store
  uchar  size = 0;       // Size of current segment
  ushort msglen;         // Length of message received so far
  uchar  oldseg;         // Prior segment number for checking order
  uchar  Retry,
         error;
  uchar  JavaiButton = 0;

  checksum = msglen = 0;
  // Remember where the buffer starts
  mbuf = mp;

  // Set up a loop to try (and Retry) getting a message from the device
  for(Retry = 0; Retry < 6; Retry++)
  {
    // When we Retry, Retry from the top
    oldseg = 0xFF;

    while(1)
    {
      //Get the device status and see if there is a segment there to read
      if((error = GetStatus(portnum, &InFree, &OutUsed, &owms, &cpst)) != 0)
        break;

      // See if a POR has occurred and treat it as an error if so
      if(owms & CE_BPOR)
      {
        error = ER_POR;
        break;
      }

      // See if the co-processor was running when we stopped
      if((cpst & 1) != 0)
      {
        // Send the time value to the CIB
        if((error = SendStatus(portnum, ETime)) != 0)
          break;

        // Execute the co-processor and allow it to finish up
        if((error = SendRun(portnum)) != 0)
          break;

        // Compute a delay time to complete
        ETime = (uchar)(cpst >> 4);

        // Wait for whatever we did above to finish up
        TimeDelay(portnum, (uchar)(ETime+SLEEP_PAD));
      }
      else
      {
        // If the co-processor is not running, then

        // See if the previous command has not yet been completed
        if(owms & 0x20)
        {
          //
          // Decide on a time value to complete the prior operation -
          // For now, we'll send back 1/2 of the status value as a time -

          ETime = (uchar)((owms & 0x1F) >> 1);

          if(ETime == 0)
            ETime++;

          //
          // if we don't allow a First Birthday (FB) to complete
          // we can end up in an infinite POR loop - FBs
          // currently take on the order of 1.5 Seconds

          if((owms & 0x1F) == CE_FIRSTBIRTH)
          {
            // Send the time value to the CIB
            if((error = SendStatus(portnum, ME_RUNTIME)) != 0)
              break;
          }
          else
          {
            // Send the time value to the CIB
            if((error = SendStatus(portnum, ETime)) != 0)
              break;
          }

          // Execute the micro and allow it to finish up
          if((error = SendRun(portnum)) != 0)
            break;

          //
          // wait an extra 1.5 seconds to compensate for first birthday.

          if((owms & 0x1F) == CE_FIRSTBIRTH)
          {
            TimeDelay(portnum, ME_RUNTIME);
            TimeDelay(portnum, 1);
          }
          else
          {
            // Wait for whatever we did above to finish up
            TimeDelay(portnum, ETime);
          }
        }
      }

      // See if there's a segment header waiting for us
      if(OutUsed == 0)
      {
        error = ER_NOHEADER;
        break;
      }

      // See if the header is valid (ie: the correct length)
      if(OutUsed != HEADERSIZE)
      {
        error = ER_BADHEADER;
        break;
      }

      // Read the header in from the device
      if((error = RecvHeader(portnum, &seg, &hdrcrc, &hdrsum, &size, &sum)) != 0)
        break;

      // See if this is the first segment of a new message
      if(((seg & 127) == 0) && (!JavaiButton))
      {
        // When we get the first segment of a new message we must
        // initialize everything

        msglen = checksum = 0;
        oldseg = 0;
        mp     = mbuf;
      }
      else
      {
        if(JavaiButton)
        {
          checksum = 0;
          seg = oldseg = 0;
        }
        // See if the segments are in proper order
        else if (++oldseg != (seg & 127))
        {
          error = CE_BADSEQ;
          break;
        }
      }

      // See if the new message data would overrun the buffer
      if((msglen + size) > *bufsize)
      {
        error = ER_OVERRUN;
        break;
      }

      // Make sum equal the whole progressive checksum including header
      checksum += sum;

      // Read in the segment data
      if((error = RecvData(portnum, mp, size, hdrcrc, &sum, JavaiButton)) != 0)
        break;

      // If the message data came in OK then update values
      checksum += sum;        // Keep the end-to-end checksum current

      if(!JavaiButton)
      {
        msglen   += size;       // Keep track of the size of the message
        mp       += size;       // Advance the message pointer
      }
      else
      {
        msglen   += (ushort)(size - 3);       // Keep track of the size of the message
        mp       += size - 3;       // Advance the message pointer
      }

      // Verify the progressive checksum value
      if(hdrsum != checksum)
      {
        error = ER_CHECKSUM;
        break;
      }

      // Keep the running sum correct
      checksum += LSB(hdrsum);
      checksum += MSB(hdrsum);

      //
      // See if this was the last message segment. If it was, then we
      // return with AOK status, and rcvd message size replaces bufsize.

      if(LastSegment && (owms != 14))
      {
        *bufsize = msglen;
        return 0;
      }
      else if(owms == 14)
      {
        JavaiButton = 1;

        if ((error = SendStatus(portnum, 1)) != 0)
	        break;
        if (SendRun(portnum) != 0)
          break;

        TimeDelay(portnum, 2);
      }
      else
      {
        // Send the time value to the CIB
        if((error = SendStatus(portnum, 2)) != 0)
          break;

        // Allow the micro to run and bring up our next segment
        if(SendRun(portnum) != 0)
          break;

        TimeDelay(portnum, 2+SLEEP_PAD);
      }
    }

    // Send the time value to the CIB
    if((error = SendStatus(portnum, (uchar)(Retry? (Retry<<1): 1))) != 0)
      break;

    // If we have any failure, we must alow the micro to run or we
    // may not recover from it
    if(SendRun(portnum) == 0)
    {
      TimeDelay(portnum, (uchar)((Retry? (Retry<<1): 1)+SLEEP_PAD));
    }
  }

  // Return with error status if retries exhausted
  return error;
}

//--------------------------------------------------------------------------
//
// Read a header (FIFO) from the device -
//
//    portnum = port number the device is on
//    seg     = Pointer to segment number from the device
//    hdrcrc  = CRC-16 of the segment data area from the header
//    hdrsum  = Running checksum result from the header
//    size    = Segment data area size from the header
//    sum     = Checksum of the header bytes
//
//    return = Error code, or zero if all went well
//
uchar RecvHeader(int portnum,
                 uchar* seg,
                 ushort* hdrcrc,
                 ushort* hdrsum,
                 uchar* size,
                 ushort* sum)
{
  ushort   owcrc;
  ushort   i;
  FIFORDWR FB;
  uchar    Retry,
           error;

  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      FB.Cmd = OW_FIFOREAD;
      FB.Len = FIFO_LEN;

      // CHECK FOR BLIVET
      // write 0xff's to stream buff directly
      memset(FB.FIFOBuf, 0xFF, 8);

      StreamLen = 0;

      memcpy(StreamBuff,&FB,sizeof(FB));
      StreamLen += 10;

      // set up crc
      StreamBuff[StreamLen++] = 0xff;
      StreamBuff[StreamLen++] = 0xff;

      if(!owAccess(portnum))
      {
         error = ER_ACCESS;
         break;
      }

      if(!owBlock(portnum, FALSE, StreamBuff, StreamLen))
      {
        error = ER_ACCESS;
        break;
      }

      memcpy(&FB,StreamBuff,sizeof(FB));
      owcrc = CRC16Buf(&FB.Cmd, 0, sizeof(FB));

      if((error = CheckStreamCRC(StreamBuff,StreamLen-2,owcrc)) != 0)
      {
        if((error = CheckStreamCRC(StreamBuff,StreamLen-2, (ushort)(owcrc ^ GetCRC16Correction(10)))) != 0)
          break;

        FB.FIFOBuf[0] &= 0x0FE;
      }

      *sum = 0;

      for(i = 0; i < 6; i++)
        *sum += FB.FIFOBuf[i];

      *seg    = FB.FIFOBuf[0];
      *size   = FB.FIFOBuf[1];
      *hdrcrc = (ushort)((FB.FIFOBuf[5] << 8) | FB.FIFOBuf[4]);
      *hdrsum = (ushort)(FB.FIFOBuf[7] << 8) | FB.FIFOBuf[6];

      if(((*seg) & 128) == 128)
        LastSegment = TRUE;
      else
        LastSegment = FALSE;

      if((error = ReleaseSequence(portnum, RS_FIFOREAD)) != 0)
        break;

      // If all went well, return OK status
      return 0;
    }
  }

  // If retries exhautsed, return error status
  return (uchar)(error | ER_RECVHDR);
}

//--------------------------------------------------------------------------
//
// Return the segment data from the device
//
//    portnum = port number the device is on
//    mp      = Pointer to message buffer next available position
//    size    = Size of message segment (from header)
//    hdrcrc  = CRC-16 of the data segment from header
//    sum     = Checksum accumulator for segment data
//
//    return  = Error code, or zero if all went well
//
uchar RecvData(int portnum, uchar* mp, uchar size, ushort hdrcrc, ushort* sum, uchar JavaiButton)
{
  uchar* mstart;
  ushort crc,
         owcrc,
         cksum,
         n;
  int    i;
  uchar  Retry,
         error;
  uchar* TempArray;

  mstart = mp;

  for(Retry = 0; Retry < 3; Retry++)
  {
    while(1)
    {
      // Get the current buffer pointer
      mp = mstart;

      crc = CRC16(size,0);

      // Initialize the local checksum accumulator
      cksum = 0;

      if(!JavaiButton)
      {
        StreamLen = 0;

        StreamBuff[StreamLen++] = OW_IPRREAD;
        StreamBuff[StreamLen++] = size;

        // CHECK FOR BLIVET

        for(i = 0; i < size+2; i++)// 2 for crc read
          StreamBuff[StreamLen++] = 0xff;

        if(!owAccess(portnum))
        {
           error = ER_ACCESS;
           break;
        }

        if(!owBlock(portnum, FALSE, StreamBuff, StreamLen))
        {
          error = ER_ACCESS;
          break;
        }

        owcrc = CRC16Buf(StreamBuff,0,(ushort)(size+2));

        // Check the returning CRC values against what we received
        if((error = CheckStreamCRC(StreamBuff,StreamLen-2,owcrc)) != 0)
        {
          // fix for bit blivet
          if((error = CheckStreamCRC(StreamBuff,StreamLen-2,(ushort)(owcrc ^ GetCRC16Correction((uchar)(size+2))))) != 0)
            break;

          StreamBuff[2] &= 0xFE;
        }

        for(i = 0; i < size; i++)
          mp[i] = StreamBuff[i+2];

        crc   = CRC16Buf(StreamBuff+1,0,(ushort)(size+1));

        for(n = 0; n < size; n++)
          cksum += mp[n];

        // See if the CRC of the data matches what we expected as-per header
        if(hdrcrc != crc)
        {
          error = ER_HDRCRC;
          break;
        }

        // Since we've been successful, update the checksum and return AOK
        *sum = cksum;

        return 0;
      } // if(!JavaiButton)
      else
      {
        if(!(TempArray = malloc((size)*2)))
        {
          error = ER_OVERRUN;
          break;
        }

        StreamLen = 0;

        StreamBuff[StreamLen++] = OW_IPRREAD;
        StreamBuff[StreamLen++] = size;

         // CHECK FOR BLIVET

        for(i = 0; i < size+2; i++)// 2 for crc read
          StreamBuff[StreamLen++] = 0xff;

        if(!owAccess(portnum))
        {
           error = ER_ACCESS;
           break;
        }

        if(!owBlock(portnum, FALSE, StreamBuff, StreamLen))
        {
          error = ER_ACCESS;
          free(TempArray);
          break;
        }

        owcrc = CRC16Buf(StreamBuff,0,(ushort)(size+2));

        // Check the returning CRC values against what we received
        if((error = CheckStreamCRC(StreamBuff,StreamLen-2,owcrc)) != 0)
        {
          if((error = CheckStreamCRC(StreamBuff,StreamLen-2,(ushort)(owcrc ^ GetCRC16Correction((uchar)(size+2))))) != 0)
            break;

          StreamBuff[2] &= 0x0FE;
        }

        for(i = 0; i < size; i++)
          TempArray[i] = StreamBuff[i+2];

        crc   = CRC16Buf(StreamBuff+1,0,(ushort)(size+1));

        for(n = 0; n < size; n++)
          cksum += TempArray[n];

        // See if the CRC of the data matches what we expected as-per header
        if(hdrcrc != crc)
        {
          error = ER_HDRCRC;
          free(TempArray);
          TempArray = NULL;
          break;
        }

        // Since we've been successful, update the checksum and return AOK
        *sum = cksum;
        if(size>2)
           memcpy(mp, TempArray+3,size-3);
        free(TempArray);
        return 0;
      }// else
    } // for
  }// for Retry

  // If retries failed, return the error status
  return (uchar)(error | ER_RECVDATA);
}

//--------------------------------------------------------------------------
//
// Issue the release sequence to the device and check the ACK response
//
//    portnum = port number the device is on
//    rscode  = Release sequence code (command specific)
//
//    return = Error code, or zero if all went well
//
uchar ReleaseSequence(int portnum, ushort rscode)
{
  uchar IOBuf[20];
  ushort rslt;

  IOBuf[0] = LSB(rscode);
  IOBuf[1] = MSB(rscode);

  if(!owBlock(portnum, FALSE, IOBuf, 2))
  {
     return ER_ACCESS;
  }

  if((rscode == RS_MICRORUN) || (rscode == RS_MICROINTERRUPT))
  {
    PowerOn = TRUE;
    rslt = !owReadBitPower(portnum, 0);
  }
  else
    rslt = owTouchBit(portnum, 1);

  // ACK bit from device (should be zero)
  if (!rslt)
    return 0;
  else
    return ER_ACK;
}

//--------------------------------------------------------------------------
//
// Check the returning crc bytes against the crc provided (expected)
//
//      portnum =   port number the device is on
//      crc     =   CRC16 result of received bytes from device
//
//      return  =   Error code, or zero if all went well
//
uchar CheckCRC(int portnum, ushort crc)
{
  uchar IOBuf[2];

  // CHECK FOR BLIVET

  IOBuf[0] = IOBuf[1] = 0xFF;

  if(!owBlock(portnum, FALSE, IOBuf, 2))
  {
     return ER_ACCESS;
  }

  // Verify the CRC LS byte response
  if(IOBuf[0] != (uchar) ~LSB(crc))
    if((IOBuf[0] & 0x0FE) != (uchar) ~LSB(crc))
      return ER_CRC;

  // Verify the CRC MS byte response
  if(IOBuf[1] != (uchar) ~MSB(crc))
    return ER_CRC;

  return 0;
}

//--------------------------------------------------------------------------
//
// Check the returning crc bytes in stream buffer bp against the crc provided
// (expected) -
//
//      data   =   pointer to stream buffer
//      offset =   offset to first SRS byte
//      crc    =   CRC16 result of received bytes from device
//
//      return =   Error code, or zero if all went well
//
uchar CheckStreamCRC(uchar* data, int offset, ushort crc)
{

  if(data[offset] != (LSB(crc)^0xFF))
  {
    return ER_CRC;
  }

  if(data[offset+1] != (MSB(crc)^0xFF))
  {
    return ER_CRC;
  }

  return 0;
}

//--------------------------------------------------------------------------
//
// Produce a time delay as-per et -
//
//      return      =   None
//
void TimeDelay(int portnum, uchar et)
{
   msDelay(et*250 + MinRun);

   if(PowerOn)
   {
      PowerOn = FALSE;
      if(MODE_NORMAL != owLevel(portnum,MODE_NORMAL))
      {
         OWERROR(OWERROR_LEVEL_FAILED);
      }
   }
}

//--------------------------------------------------------------------------
//
//   CRC16 generation. Used here only for table generation.
//
ushort CRC16TGen(uchar x, ushort crc)
{
  uchar bit;
  uchar n;

  for(n = 0; n < 8; n++)
  {
    bit = (uchar)((crc ^ x) & 1);
    crc >>= 1;

    if(bit)
      crc ^= 0xA001;

    x >>= 1;
  }

  return crc;
}


//--------------------------------------------------------------------------
//
//    Generate 256 word table for CRC16 lookup.
//
void GenCRC16Table()
{
  ushort i;

  for(i = 0; i < 256; ++i)
    CRCTable[i] = CRC16TGen((uchar) i, 0);

  GenCRC16CorrectionTable();
}

//--------------------------------------------------------------------------
//
//    Fast CRC16 generation.
//
ushort CRC16(uchar x, ushort crc)
{
  return CRCTable[(crc ^ x) & 0xFF] ^ crc >> 8;
}

//--------------------------------------------------------------------------
//
//    Compute CRC16 of buffer pointed to by bp.
//
ushort CRC16Buf(uchar* bp, ushort crc, ushort Count)
{
  ushort i;

  for(i = 0; i < Count; i++)
    crc = CRCTable[(crc ^ bp[i]) & 0xFF] ^ crc >> 8;

  return crc;
}

//--------------------------------------------------------------------------
//
//    Compute CRC16 correctionTable
//
void GenCRC16CorrectionTable()
{
  int   l_i,
        l_Size  = sizeof(CRCCorrectionTable)/sizeof(ushort);
  ushort  l_CurrentCRC  = 0;

  memset(CRCCorrectionTable,  0,  sizeof(CRCCorrectionTable));
  CRCCorrectionTable[2] = CRC16(1, l_CurrentCRC);

  for(l_i = 3; l_i < l_Size; l_i++)
  {
    CRCCorrectionTable[l_i] = CRC16(0, CRCCorrectionTable[l_i-1]);
  }
}

//--------------------------------------------------------------------------
//
//    Get CRC16 correction value
//
ushort GetCRC16Correction(uchar p_Length)
{
  if(p_Length > sizeof(CRCCorrectionTable)/sizeof(ushort))
    return 0;

  if(p_Length < 1)
    return 0;

  return CRCCorrectionTable[p_Length-1];
}

//--------------------------------------------------------------------------
//
void SetMinRunTime(ushort MR)
{
   MinRun = MR;
}
