//---------------------------------------------------------------------------
// Copyright (C) 2000 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. 
//---------------------------------------------------------------------------
// 
// mbEPROM.c - Include memory bank EPROM functions.
//
// Version: 2.10
//

// Include Files
#include "ownet.h"
#include "mbeprom.h"

// general command defines 
#define READ_MEMORY_COMMAND_EPROM      0xF0
#define MAIN_READ_PAGE_COMMAND_EPROM   0xA5
#define STATUS_READ_PAGE_COMMAND_EPROM 0xAA
#define MAIN_WRITE_COMMAND_EPROM       0x0F
#define STATUS_WRITE_COMMAND_EPROM     0x55

// Local defines
#define SIZE_EPROM        2048
#define PAGE_LENGTH_EPROM 32

// Global variables
char *bankDescription_eprom     = "Main Memory";
SMALLINT  writeVerification_eprom    = TRUE;
SMALLINT  generalPurposeMemory_eprom = TRUE;
SMALLINT  readWrite_eprom            = FALSE;
SMALLINT  writeOnce_eprom            = TRUE;
SMALLINT  readOnly_eprom             = FALSE;
SMALLINT  nonVolatile_eprom          = TRUE;
SMALLINT  needsProgramPulse_eprom    = TRUE;
SMALLINT  needsPowerDelivery_eprom   = FALSE;
SMALLINT  hasExtraInfo_eprom         = TRUE;
SMALLINT  extraInfoLength_eprom      = 1;
char *extraInfoDesc_eprom       = "Inverted redirection page";
SMALLINT  pageAutoCRC_eprom          = TRUE;
SMALLINT  lockOffset                 = 0;
SMALLINT  lockRedirectOffset         = 0;
SMALLINT  canredirectPage            = FALSE;
SMALLINT  canlockPage                = FALSE;
SMALLINT  canlockRedirectPage        = FALSE;
SMALLINT  numPages                   = 64;
SMALLINT  CRCbytes                   = 2;


/**
 * Read  memory in the current bank with no CRC checking (device or
 * data). The resulting data from this API may or may not be what is on
 * the 1-Wire device.  It is recommends that the data contain some kind
 * of checking (CRC) like in the readPagePacketEPROM() method or have
 * the 1-Wire device provide the CRC as in readPageCRCEPROM().  readPageCRCEPROM()
 * however is not supported on all memory types, see 'hasPageAutoCRCEPROM()'.
 * If neither is an option then this method could be called more
 * then once to at least verify that the same thing is read consistantly.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part that the read is
 *          to be done on.
 * str_add  starting physical address
 * rd_cont  if 'true' then device read is continued without
 *          re-selecting.  This can only be used if the new
 *          read() continious where the last one led off
 *          and it is inside a 'beginExclusive/endExclusive'
 *          block.
 * buff     byte array to place read data into
 * len      length in bytes to read
 *
 * @return 'true' if the read was complete
 */
SMALLINT readEPROM(SMALLINT bank, int portnum, uchar *SNum, int str_add, 
                   SMALLINT rd_cont, uchar *buff, int len)
{
   int i;
   int start_pg, end_pg, num_bytes, pg;
   uchar raw_buf[256];

   // check if read exceeds memory
   if ((str_add + len) > getSizeEPROM(bank,SNum))
   {
      OWERROR(OWERROR_READ_OUT_OF_RANGE);
      return FALSE;
   }

   // check if status memory
   if(readPageWithCRC(bank,SNum) == STATUS_READ_PAGE_COMMAND_EPROM)
   {
      // no regular read memory so must use readPageCRC
      start_pg = str_add / getPageLengthEPROM(bank,SNum);
      end_pg   = ((str_add + len) / getPageLengthEPROM(bank,SNum)) - 1;

      if (((str_add + len) % getPageLengthEPROM(bank,SNum)) > 0)
         end_pg++;

      // loop to read the pages
      for (pg = start_pg; pg <= end_pg; pg++)
         if(!readPageCRCEPROM(bank,portnum,SNum,pg,
                     &raw_buf[(pg-start_pg)*getPageLengthEPROM(bank,SNum)]))
         {
            return FALSE;
         }

      // extract out the data
      for(i=0;i<len;i++)
         buff[i] = raw_buf[i];
   }
   // regular memory so use standard read memory command
   else
   {

      // see if need to access the device
      if (!rd_cont)
      {

         // select the device
         if (!owAccess(portnum))
         {
            OWERROR(OWERROR_DEVICE_SELECT_FAIL);
            return FALSE;
         }

         // build start reading memory block
         raw_buf[0] = READ_MEMORY_COMMAND_EPROM;
         raw_buf[1] = (str_add + getStartingAddressEPROM(bank,SNum)) & 0xFF;
         raw_buf[2] = (((str_add + getStartingAddressEPROM(bank,SNum)) & 0xFFFF) >> 8)
                      & 0xFF;
         raw_buf[3] = 0xFF;

         // check if get a 1 byte crc in a normal read.
         if(SNum[0] == 0x09)
            num_bytes = 4;
         else
            num_bytes = 3;

         // do the first block for command, address
         if(!owBlock(portnum,FALSE,raw_buf,num_bytes))
         {
            OWERROR(OWERROR_BLOCK_FAILED);
            return FALSE;
         }
      }

      // pre-fill readBuf with 0xFF 
      for (i=0;i<len;i++)
         buff[i] = 0xFF;

      // send second block to read data, return result
      if(!owBlock(portnum,FALSE,buff,len))
      {
         OWERROR(OWERROR_DEVICE_SELECT_FAIL);
         return FALSE;
      }
   }

   return TRUE;
}

/**
 * Write  memory in the current bank.  It is recommended that
 * when writing  data that some structure in the data is created
 * to provide error free reading back with readEPROM().  Or the
 * method 'writePagePacketEPROM()' could be used which automatically
 * wraps the data in a length and CRC.
 *
 * When using on Write-Once devices care must be taken to write into
 * into empty space.  If write() is used to write over an unlocked
 * page on a Write-Once device it will fail.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part that the write is
 *          to be done on.
 * str_add  starting address
 * buff     byte array containing data to write
 * len      length in bytes to write
 *
 * @return 'true' if the write was complete.
 */
SMALLINT writeEPROM(SMALLINT bank, int portnum, uchar *SNum, int str_add,
                    uchar *buff, int len)
{
   int      i;
   int      result;
   SMALLINT write_continue;
   int      crc_type;
   int      verify[256];

   // return if nothing to do
   if (len == 0)
      return TRUE;

   // check if power delivery is available
   if(!owHasProgramPulse(portnum))
   {
      OWERROR(OWERROR_NO_PROGRAM_PULSE);
      return FALSE;
   }

   // check if trying to write read only bank
   if(isReadOnlyEPROM(bank,portnum,SNum))
   {
      OWERROR(OWERROR_READ_ONLY);
      return FALSE;
   }

   for(i=0;i<len;i++)
   {
      verify[0] = writeVerify(bank,portnum,SNum,
          ((str_add+i+getStartingAddressEPROM(bank,SNum))/getPageLengthEPROM(bank,SNum)));

      if(isPageLocked(bank,portnum,SNum,(str_add+i+getStartingAddressEPROM(bank,SNum))/
                                         getPageLengthEPROM(bank,SNum)))
      {
         OWERROR(OWERROR_PAGE_LOCKED);
         return FALSE;
      }
   }

   // check if write exceeds memory
   if((str_add + len) > (getPageLengthEPROM(bank,SNum) * getNumberPagesEPROM(bank,SNum)))
   {
      OWERROR(OWERROR_WRITE_OUT_OF_RANGE);
      return FALSE;
   }

   // loop while still have bytes to write
   write_continue = TRUE;

   crc_type = numCRCbytes(bank,SNum) - 1;

   for (i=0;i<len;i++)
   {
      result = owProgramByte(portnum,buff[i],str_add+i+getStartingAddressEPROM(bank,SNum),
                             writeMemCmd(bank,SNum),crc_type,write_continue);
      if(verify[i])
      {
         if((result == -1) || ((uchar) result != buff[i]))
         {
            OWERROR(OWERROR_READBACK_EPROM_FAILED);
            return FALSE;
         }
         else
            write_continue = FALSE;
      }
   }

   return TRUE;
}

/**
 * Read  page in the current bank with no
 * CRC checking (device or data). The resulting data from this API
 * may or may not be what is on the 1-Wire device.  It is recommends
 * that the data contain some kind of checking (CRC) like in the
 * readPagePacketEPROM() method or have the 1-Wire device provide the
 * CRC as in readPageCRCEPROM().  readPageCRCEPROM() however is not
 * supported on all memory types, see 'hasPageAutoCRCEPROM()'.
 * If neither is an option then this method could be called more
 * then once to at least verify that the same thing is read consistantly.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to read
 * rd_cont  if 'true' then device read is continued without
 *          re-selecting.  This can only be used if the new
 *          read() continious where the last one led off
 *          and it is inside a 'beginExclusive/endExclusive'
 *          block.
 * buff     byte array containing data that was read.
 *
 * @return - returns '0' if the read page wasn't completed.
 *                   '1' if the operation is complete.
 */
SMALLINT readPageEPROM(SMALLINT bank, int portnum, uchar *SNum, int page,
                       SMALLINT rd_cont, uchar *buff)
{
   uchar extra[1];

   if(hasPageAutoCRCEPROM(bank,SNum))
   {
      if(!readPageExtraCRCEPROM(bank,portnum,SNum,page,buff,extra))
         return FALSE;
   }
   else
   {
      if(!readEPROM(bank,portnum,SNum,(page*getPageLengthEPROM(bank,SNum)),FALSE,
                    buff,getPageLengthEPROM(bank,SNum)))
         return FALSE;
   }

   return TRUE;
}

/**
 * Read  page with extra information in the current bank with no
 * CRC checking (device or data). The resulting data from this API
 * may or may not be what is on the 1-Wire device.  It is recommends
 * that the data contain some kind of checking (CRC) like in the
 * readPagePacketEPROM() method or have the 1-Wire device provide the
 * CRC as in readPageCRCEPROM().  readPageCRCEPROM() however is not
 * supported on all memory types, see 'hasPageAutoCRCEPROM()'.
 * If neither is an option then this method could be called more
 * then once to at least verify that the same thing is read consistantly.
 * See the method 'hasExtraInfoEPROM()' for a description of the optional
 * extra information some devices have.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to read
 * rd_cont  if 'true' then device read is continued without
 *          re-selecting.  This can only be used if the new
 *          read() continious where the last one led off
 *          and it is inside a 'beginExclusive/endExclusive'
 *          block.
 * buff     byte array containing data that was read
 * extra    the extra information
 *
 * @return - returns '0' if the read page wasn't completed with extra info.
 *                   '1' if the operation is complete.
 */
SMALLINT readPageExtraEPROM(SMALLINT bank, int portnum, uchar *SNum, int page,
                            SMALLINT rd_cont, uchar *buff, uchar *extra)
{
   // check if current bank is not scratchpad bank, or not page 0
   if(!hasExtraInfoEPROM(bank,SNum))
   {
      OWERROR(OWERROR_EXTRA_INFO_NOT_SUPPORTED);
      return FALSE;
   }

   return readPageExtraCRCEPROM(bank,portnum,SNum,page,buff,extra);
}

/**
 * Read a complete memory page with CRC verification provided by the
 * device with extra information.  Not supported by all devices.
 * See the method 'hasPageAutoCRCEPROM()'.
 * See the method 'haveExtraInfoEPROM()' for a description of the optional
 * extra information.
 *
 * bank      to tell what memory bank of the ibutton to use.
 * portnum   the port number of the port being used for the
 *           1-Wire Network.
 * SNum      the serial number for the part.
 * page      the page to read
 * read_buff byte array containing data that was read.
 * extra     the extra information
 *
 * @return - returns '0' if the read page wasn't completed with extra info.
 *                   '1' if the operation is complete.
 */
SMALLINT readPageExtraCRCEPROM(SMALLINT bank, int portnum, uchar *SNum, int page,
                               uchar *read_buff, uchar *extra)
{
   int    i, len = 0, lastcrc = 0, addr, extractPnt;
   uchar raw_buf[PAGE_LENGTH_EPROM + 6];

   owSerialNum(portnum,SNum,FALSE);

   // only needs to be implemented if supported by hardware
   if(!hasPageAutoCRCEPROM(bank,SNum))
   {
      OWERROR(OWERROR_CRC_NOT_SUPPORTED);
      return FALSE;
   }

   // check if read exceeds memory
   if(page > (getNumberPagesEPROM(bank,SNum)))
   {
      OWERROR(OWERROR_READ_OUT_OF_RANGE);
      return FALSE;
   }


   // select the device
   if (!owAccess(portnum))
   {
      OWERROR(OWERROR_DEVICE_SELECT_FAIL);
      return FALSE;
   }

   // build start reading memory block with: command, address, (extraInfo?), (crc?)
   len = 3 + getExtraInfoLengthEPROM(bank,SNum);

   if(crcAfterAdd(bank,SNum))
   {
      len += numCRCbytes(bank,SNum);
   }

   for(i=0;i<len;i++)
      raw_buf[i] = 0xFF;

   raw_buf[0] = readPageWithCRC(bank,SNum);

   addr = page * getPageLengthEPROM(bank,SNum) + getStartingAddressEPROM(bank,SNum);

   raw_buf[1] = addr & 0xFF;
   raw_buf[2] = ((addr & 0xFFFF) >> 8) & 0xFF;

   // do the first block 
   if(!owBlock(portnum,FALSE,&raw_buf[0],len))
   {
      OWERROR(OWERROR_BLOCK_FAILED);
      return FALSE;
   }

   // check CRC
   if (numCRCbytes(bank,SNum) == 2)
   {
      setcrc16(portnum,0);
      for(i=0;i<len;i++)
         lastcrc = docrc16(portnum,raw_buf[i]);
   }
   else
   {
      setcrc8(portnum,0);
      for(i=0;i<len;i++)
         lastcrc = docrc8(portnum,raw_buf[i]);
   }

   if((getExtraInfoLengthEPROM(bank,SNum) > 0) || (crcAfterAdd(bank,SNum)))
   {
      // check CRC
      if(numCRCbytes(bank,SNum) == 2)
      {
         if (lastcrc != 0xB001)
         {
            OWERROR(OWERROR_CRC_FAILED);
            return FALSE;
         }
      }
      else
      {
         if (lastcrc != 0)
         {
            return FALSE;
         }
      }

      lastcrc = 0;

      // extract the extra information
      extractPnt = len - getExtraInfoLengthEPROM(bank,SNum) - numCRCbytes(bank,SNum);
      if(getExtraInfoLengthEPROM(bank,SNum))
         for(i=extractPnt;i<(extractPnt+getExtraInfoLengthEPROM(bank,SNum));i++)
            extra[i-extractPnt] = raw_buf[i];
   }

   // pre-fill with 0xFF 
   for(i=0;i<(getPageLengthEPROM(bank,SNum) + numCRCbytes(bank,SNum));i++)
      raw_buf[i] = 0xFF;

   // send block to read data + crc
   if(!owBlock(portnum,FALSE,&raw_buf[0],(getPageLengthEPROM(bank,SNum)+numCRCbytes(bank,SNum))))
   {
      OWERROR(OWERROR_BLOCK_FAILED);
      return FALSE;
   }

   // check the CRC
   if (numCRCbytes(bank,SNum) == 2)
   {
      setcrc16(portnum, (ushort) lastcrc);
      for(i=0;i<(getPageLengthEPROM(bank,SNum)+numCRCbytes(bank,SNum));i++)
         lastcrc = docrc16(portnum,raw_buf[i]);

      if (lastcrc != 0xB001)
      {
         OWERROR(OWERROR_CRC_FAILED);
         return FALSE;
      }
   }
   else
   {
      setcrc8(portnum, (uchar) lastcrc);
      for(i=0;i<(getPageLengthEPROM(bank,SNum)+numCRCbytes(bank,SNum));i++)
         lastcrc = docrc8(portnum,raw_buf[i]);

      if (lastcrc != 0)
      {
         OWERROR(OWERROR_CRC_FAILED);
         return FALSE;
      }
   }

   // extract the page data
   for(i=0;i<getPageLengthEPROM(bank,SNum);i++)
   {
      read_buff[i] = raw_buf[i];
   }

   return TRUE;
}

/**
 * Read a complete memory page with CRC verification provided by the
 * device.  Not supported by all devices.  See the method
 * 'hasPageAutoCRCEPROM()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to read
 * buff     byte array containing data that was read
 *
 * @return - returns '0' if the read page wasn't completed.
 *                   '1' if the operation is complete.
 */
SMALLINT readPageCRCEPROM(SMALLINT bank, int portnum, uchar *SNum, int page, uchar *buff)
{
   uchar extra[1];

   return readPageExtraCRCEPROM(bank,portnum,SNum,page,buff,extra);
}

/**
 * Read a Universal Data Packet.
 *
 * The Universal Data Packet always starts on page boundaries but
 * can end anywhere in the page.  The structure specifies the length of
 * data bytes not including the length byte and the CRC16 bytes.
 * There is one length byte. The CRC16 is first initialized to
 * the page number.  This provides a check to verify the page that
 * was intended is being read.  The CRC16 is then calculated over
 * the length and data bytes.  The CRC16 is then inverted and stored
 * low byte first followed by the high byte.  This is structure is
 * used by this method to verify the data but is not returned, only
 * the data payload is returned.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to read
 * rd_cont  if 'true' then device read is continued without
 *          re-selecting.  This can only be used if the new
 *          read() continious where the last one led off
 *          and it is inside a 'beginExclusive/endExclusive'
 *          block.
 * buff     byte array containing data that was read.
 * len      length of the packet
 *
 * @return - returns '0' if the read page packet wasn't completed
 *                   '1' if the operation is complete.
 */
SMALLINT readPagePacketEPROM(SMALLINT bank, int portnum, uchar *SNum, int page,
                             SMALLINT rd_cont, uchar *buff, int *len)
{
   uchar  raw_buf[PAGE_LENGTH_EPROM];
   uchar  extra[1];
   ushort lastcrc16 = 0;
   int i;

   // read entire page with read page CRC
   if(!readPageExtraCRCEPROM(bank,portnum,SNum,page,&raw_buf[0],&extra[0]))
      return FALSE;

   // check if length is realistic
   if ((raw_buf[0] & 0xFF) > getMaxPacketDataLengthEPROM(bank,SNum))
   {
      OWERROR(OWERROR_INVALID_PACKET_LENGTH);
      return FALSE;
   }

   // verify the CRC is correct
   setcrc16(portnum,(ushort) ((getStartingAddressEPROM(bank,SNum)/PAGE_LENGTH_EPROM) + page));
   for(i=0;i<(raw_buf[0]+3);i++)
      lastcrc16 = docrc16(portnum,raw_buf[i]);

   if(lastcrc16 == 0xB001)
   {
      // extract the data out of the packet
      for(i=0;i<raw_buf[0];i++)
         buff[i] = raw_buf[i+1];

      // return the length
      *len = raw_buf[0];
   }
   else
   {
      OWERROR(OWERROR_CRC_FAILED);
      return FALSE;
   }

   return TRUE;
}

/**
 * Read a Universal Data Packet and extra information.  See the
 * method 'readPagePacketEPROM()' for a description of the packet structure.
 * See the method 'hasExtraInfoEPROM()' for a description of the optional
 * extra information some devices have.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to read
 * rd_cont  if 'true' then device read is continued without
 *          re-selecting.  This can only be used if the new
 *          read() continious where the last one led off
 *          and it is inside a 'beginExclusive/endExclusive'
 *          block.
 * buff     byte array containing data that was read.
 * len      length of the packet
 * extra    extra information
 *
 * @return - returns '0' if the read page packet wasn't completed
 *                   '1' if the operation is complete.
 */
SMALLINT readPagePacketExtraEPROM(SMALLINT bank, int portnum, uchar *SNum,
                                  int page, SMALLINT rd_cont, uchar *buff,
                                  int *len, uchar *extra)
{
   uchar raw_buf[PAGE_LENGTH_EPROM];
   ushort lastcrc16 = 0;
   int i;

   // check if current bank is not scratchpad bank, or not page 0
   if (!hasExtraInfoEPROM(bank,SNum))
   {
      OWERROR(OWERROR_EXTRA_INFO_NOT_SUPPORTED);
      return FALSE;
   }

   // read entire page with read page CRC
   if(!readPageExtraCRCEPROM(bank,portnum,SNum,page,raw_buf,extra))
      return FALSE;

   // check if length is realistic
   if ((raw_buf[0] & 0xFF) > getMaxPacketDataLengthEPROM(bank,SNum))
   {
      OWERROR(OWERROR_INVALID_PACKET_LENGTH);
      return FALSE;
   }

   // verify the CRC is correct
   setcrc16(portnum,(ushort) ((getStartingAddressEPROM(bank,SNum)/PAGE_LENGTH_EPROM) + page));
   for(i=0;i<(raw_buf[0]+3);i++)
      lastcrc16 = docrc16(portnum,raw_buf[i]);

   if(lastcrc16 == 0xB001)
   {
      // extract the data out of the packet
      for(i=0;i<raw_buf[0];i++)
         buff[i] = raw_buf[i+1];

      // return the length
      *len = raw_buf [0];
   }
   else
   {
      OWERROR(OWERROR_CRC_FAILED);
      return FALSE;
   }

   return TRUE;
}

/**
 * Write a Universal Data Packet.  See the method 'readPagePacketEPROM()'
 * for a description of the packet structure.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page the packet is being written to.
 * buff     byte array containing data that to write.
 * len      length of the packet
 *
 * @return - returns '0' if the write page packet wasn't completed
 *                   '1' if the operation is complete.
 */
SMALLINT writePagePacketEPROM(SMALLINT bank, int portnum, uchar *SNum, int page,
                              uchar *buff, int len)
{
   uchar raw_buf[PAGE_LENGTH_EPROM];
   int   crc = 0, i;

   // make sure length does not exceed max
   if (len > getMaxPacketDataLengthEPROM(bank,SNum))
   {
      OWERROR(OWERROR_INVALID_PACKET_LENGTH);
      return FALSE;
   }

   // see if this bank is general read/write
   if (!isGeneralPurposeMemoryEPROM(bank,SNum))
   {
      OWERROR(OWERROR_NOT_GENERAL_PURPOSE);
      return FALSE;
   }

   // construct the packet to write
   raw_buf[0] = (uchar) len;

   for(i=0;i<len;i++)
      raw_buf[i+1] = buff[i];

   setcrc16(portnum,(ushort) ((getStartingAddressEPROM(bank,SNum)/PAGE_LENGTH_EPROM) + page));
   for(i=0;i<len+1;i++)
      crc = docrc16(portnum,raw_buf[i]);

   raw_buf[len + 1] = ~crc & 0xFF;
   raw_buf[len + 2] = ((~crc & 0xFFFF) >> 8) & 0xFF;

   // write the packet, return result
   if(!writeEPROM(bank,portnum,SNum,page*getPageLengthEPROM(bank,SNum)+
                  getStartingAddressEPROM(bank,SNum),&raw_buf[0],len+3))
      return FALSE;

   return TRUE;
}

/**
 * Query to get the number of pages in current memory bank.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  number of pages in current memory bank
 */
SMALLINT getNumberPagesEPROM(SMALLINT bank, uchar *SNum)
{
   int pages = numPages;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 0)
            pages = 4;
         else if(bank == 1)
            pages = 1;
         break;

      case 0x0B:
         if((bank == 1) || (bank == 2) || (bank == 3))
            pages = 1;
         else if(bank == 4)
            pages = 8;
         break;

      case 0x0F:
         if(bank == 0)
            pages = 256;
         else if((bank == 1) || (bank == 2))
            pages = 4;
         else if(bank == 3)
            pages = 3;
         else if(bank == 4)
            pages = 32;
         break;

      case 0x12:
         if(bank == 0)
            pages = 4;
         else if(bank == 1)
            pages = 1;
         break;

      case 0x13:
         if(bank == 0)
            pages = 16;
         else if(bank == 4)
            pages = 2;
         else if((bank == 1) || (bank == 2) || (bank == 3))
            pages = 1;
         break;

      default:
         pages = numPages;
         break;
   }

   return pages;
}

/**
 * Query to get the memory bank size in bytes.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  memory bank size in bytes.
 */
int getSizeEPROM(SMALLINT bank, uchar *SNum)
{
   int size = SIZE_EPROM;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 0)
            size = 128;
         else if(bank == 1)
            size = 8;
         break;

      case 0x0B:
         if((bank == 1) || (bank == 2) || (bank == 3))
            size = 8;
         else if(bank == 4)
            size = 64;
         break;

      case 0x0F:
         if(bank == 0)
            size = 8192;
         else if((bank == 1) || (bank == 2))
            size = 32;
         else if(bank == 3)
            size = 24;
         else if(bank == 4)
            size = 256;
         break;

      case 0x12:
         if(bank == 0)
            size = 128;
         else if(bank == 1)
            size = 8;
         break;

      case 0x13:
         if(bank == 0)
            size = 512;
         else if(bank == 4)
            size = 16;
         else if((bank == 1) || (bank == 2) || (bank == 3))
            size = 8;
         break;

      default:
         size = SIZE_EPROM;
         break;
   }

   return size;
}

/**
 * Query to get the starting physical address of this bank.  Physical
 * banks are sometimes sub-divided into logical banks due to changes
 * in attributes.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  physical starting address of this logical bank.
 */
int getStartingAddressEPROM(SMALLINT bank, uchar *SNum)
{
   int addr = 0;

   switch(SNum[0])
   {
      case 0x0B:
         if(bank == 2)
            addr = 32;
         else if(bank == 3)
            addr = 64;
         else if(bank == 4)
            addr = 256;
         break;

      case 0x0F:
         if(bank == 2)
            addr = 32;
         else if(bank == 3)
            addr = 64;
         else if(bank == 4)
            addr = 256;
         break;

      case 0x13:
         if(bank == 2)
            addr = 32;
         else if(bank == 3)
            addr = 64;
         else if(bank == 4)
            addr = 256;
         break;

      default:
         addr = 0;
         break;
   }

   return addr;
}

/**
 * Query to get page length in bytes in current memory bank.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return   page length in bytes in current memory bank
 */
SMALLINT getPageLengthEPROM(SMALLINT bank, uchar *SNum)
{
   int len = PAGE_LENGTH_EPROM;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 1)
            len = 8;
         break;

      case 0x0B:
         if((bank > 0) && (bank < 5))
            len = 8;

      case 0x0F:
         if((bank > 0) && (bank <5))
            len = 8;
         break;

      case 0x12:
         if(bank == 1)
            len = 8;
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            len = 8;
         break;

      default:
         len = PAGE_LENGTH_EPROM;
         break;
   }

   return len;
}

/**
 * Query to see get a string description of the current memory bank.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  String containing the memory bank description
 */
char *getBankDescriptionEPROM(SMALLINT bank, uchar *SNum)
{
   switch(SNum[0])
   {
      case 0x09:
         if(bank == 1)
            return "Write protect pages and page redirection";
         break;

      case 0x0B:
         if(bank == 1)
            return "Write protect pages";
         else if(bank == 2)
            return "Write protect redirection";
         else if(bank == 3)
            return "Bitmap of used pages for file structure";
         else if(bank == 4)
            return "Page redirection bytes";
         break;

      case 0x0F:
         if(bank == 1)
            return "Write protect pages";
         else if(bank == 2)
            return "Write protect redirection";
         else if(bank == 3)
            return "Bitmap of used pages for file structure";
         else if(bank == 4)
            return "Pages redirection bytes";
         break;

      case 0x12:
         if(bank == 1)
            return "Write protect pages, page redirection, switch control";
         break;

      case 0x13:
         if(bank == 1)
            return "Write protect pages";
         else if(bank == 2)
            return "Write protect redirection";
         else if(bank == 3)
            return "Bitmap of used pages for file structure";
         else if(bank == 4)
            return "Page redirection bytes";
         break;

      default:
         return bankDescription_eprom;
   }

   return bankDescription_eprom;
}

/**
 * Query to see if the current memory bank is general purpose
 * user memory.  If it is NOT then it is Memory-Mapped and writing
 * values to this memory will affect the behavior of the 1-Wire
 * device.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank is general purpose
 */
SMALLINT isGeneralPurposeMemoryEPROM(SMALLINT bank, uchar *SNum)
{
   int gp_mem = generalPurposeMemory_eprom;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 1)
            gp_mem = FALSE;
         break;

      case 0x0B:
         if((bank > 0) && (bank < 5))
            gp_mem = FALSE;

      case 0x0F:
         if((bank > 0) && (bank <5))
            gp_mem = FALSE;
         break;

      case 0x12:
         if(bank == 1)
            gp_mem = FALSE;
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            gp_mem = FALSE;
         break;

      default:
         gp_mem = generalPurposeMemory_eprom;
         break;
   }

   return gp_mem;
}

/**
 * Query to see if current memory bank is read/write.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank is read/write
 */
SMALLINT isReadWriteEPROM(SMALLINT bank, int portnum, uchar *SNum)
{
   return readWrite_eprom;
}

/**
 * Query to see if current memory bank is write write once such
 * as with EPROM technology.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank can only be written once
 */
SMALLINT isWriteOnceEPROM(SMALLINT bank, int portnum, uchar *SNum)
{
   return writeOnce_eprom;
}

/**
 * Query to see if current memory bank is read only.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank can only be read
 */
SMALLINT isReadOnlyEPROM(SMALLINT bank, int portnum, uchar *SNum)
{
   return readOnly_eprom;
}

/**
 * Query to see if current memory bank non-volatile.  Memory is
 * non-volatile if it retains its contents even when removed from
 * the 1-Wire network.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank non volatile.
 */
SMALLINT isNonVolatileEPROM(SMALLINT bank, uchar *SNum)
{
   return nonVolatile_eprom;
}

/**
 * Query to see if current memory bank pages need the adapter to
 * have a 'ProgramPulse' in order to write to the memory.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if writing to the current memory bank pages
 *                 requires a 'ProgramPulse'.
 */
SMALLINT needsProgramPulseEPROM(SMALLINT bank, uchar *SNum)
{
   return needsProgramPulse_eprom;
}

/**
 * Query to see if current memory bank pages need the adapter to
 * have a 'PowerDelivery' feature in order to write to the memory.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if writing to the current memory bank pages
 *                 requires 'PowerDelivery'.
 */
SMALLINT needsPowerDeliveryEPROM(SMALLINT bank, uchar *SNum)
{
   return needsPowerDelivery_eprom;
}

/**
 * Checks to see if this memory bank's pages deliver extra
 * information outside of the normal data space,  when read.  Examples
 * of this may be a redirection byte, counter, tamper protection
 * bytes, or SHA-1 result.  If this method returns true then the
 * methods with an 'extraInfo' parameter can be used.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  true if reading the this memory bank's
 *               pages provides extra information
 */
SMALLINT hasExtraInfoEPROM(SMALLINT bank, uchar *SNum)
{
   SMALLINT hasExtra = hasExtraInfo_eprom;

   switch(SNum[0])
   {
      case 0x09:
         hasExtra = FALSE;
         break;

      case 0x0B:
         if((bank > 0) && (bank < 5))
            hasExtra = FALSE;
         break;

      case 0x0F:
         if((bank > 0) && (bank < 5))
            hasExtra = FALSE;
         break;

      case 0x12:
         if(bank == 1)
            hasExtra = FALSE;
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            hasExtra = FALSE;
         break;

      default:
         hasExtra = hasExtraInfo_eprom;
         break;
   }

   return hasExtra;
}

/**
 * Query to get the length in bytes of extra information that
 * is read when read a page in the current memory bank.  See
 * 'hasExtraInfoEPROM()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  number of bytes in Extra Information read when reading
 *          pages in the current memory bank.
 */
SMALLINT getExtraInfoLengthEPROM(SMALLINT bank, uchar *SNum)
{
   SMALLINT len = extraInfoLength_eprom;

   switch(SNum[0])
   {
      case 0x09:
         len = 0;
         break;

      case 0x0B:
         if((bank > 0) && (bank < 5))
            len = 0;
         break;

      case 0x0F:
         if((bank > 0) && (bank < 5))
            len = 0;
         break;

      case 0x12:
         if(bank == 1)
            len = 0;
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            len = 0;
         break;

      default:
         len = extraInfoLength_eprom;
         break;
   }

   return len;
}

/**
 * Query to get a string description of what is contained in
 * the Extra Informationed return when reading pages in the current
 * memory bank.  See 'hasExtraInfoEPROM()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return string describing extra information.
 */
char *getExtraInfoDescEPROM(SMALLINT bank, uchar *SNum)
{
   switch(SNum[0])
   {
      case 0x09:
         return " ";
         break;

      case 0x0B:
         if((bank > 0) && (bank < 5))
            return " ";
         break;

      case 0x0F:
         if((bank > 0) && (bank < 5))
            return " ";
         break;

      case 0x12:
         if(bank == 1)
            return " ";
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            return " ";
         break;

      default:
         return extraInfoDesc_eprom;
   }


   return extraInfoDesc_eprom;
}

/**
 * Query to see if current memory bank pages can be read with
 * the contents being verified by a device generated CRC.
 * This is used to see if the 'ReadPageCRCEPROM()' can be used.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank can be read with self
 *          generated CRC.
 */
SMALLINT hasPageAutoCRCEPROM(SMALLINT bank, uchar *SNum)
{
   return pageAutoCRC_eprom;
}

/**
 * Query to get Maximum data page length in bytes for a packet
 * read or written in the current memory bank.  See the 'ReadPagePacket()'
 * and 'WritePagePacket()' methods.  This method is only usefull
 * if the current memory bank is general purpose memory.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  max packet page length in bytes in current memory bank
 */
SMALLINT getMaxPacketDataLengthEPROM(SMALLINT bank, uchar *SNum)
{
   return getPageLengthEPROM(bank,SNum) - 3;
}

//--------
//-------- OTPMemoryBank I/O methods
//--------

/**
 * Lock the specifed page in the current memory bank.  Not supported
 * by all devices.  See the method 'canLockPageEPROM()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to be locked
 *
 * @return  true if the page was locked 
 *          false otherwise
 */
SMALLINT lockPage(SMALLINT bank, int portnum, uchar *SNum, int page)
{
   int nbyt,nbit;
   SMALLINT write_bank;
   uchar wr_byte[1];

   // setting the bank to write
   // and since all the page lock info is in
   // bank 1 for all the EPROM parts.
   write_bank = 1;

   if(isPageLocked(write_bank,portnum,SNum,page))
   {
      OWERROR(OWERROR_PAGE_ALREADY_LOCKED);
      return FALSE;
   }

   // create byte to write to mlLock to lock page
   nbyt    = (page >> 3);
   nbit    = page - (nbyt << 3);

   wr_byte[0] = (uchar) ~(0x01 << nbit);

   // write the lock bit
   if(!writeEPROM(write_bank,portnum,SNum,nbyt + lockOffset,wr_byte,1))
      return FALSE;

   // read back to verify
   if(!isPageLocked(write_bank,portnum,SNum,page))
   {
      OWERROR(OWERROR_READ_BACK_NOT_VALID);
      return FALSE;
   }

   return TRUE;
}

/**
 * Query to see if the specified page is locked.
 * See the method 'canLockPage()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to check if locked
 *
 * @return  true if page locked.
 */
SMALLINT isPageLocked(SMALLINT bank, int portnum, uchar *SNum, int page)
{
   int pg_len, read_pg, index, nbyt, nbit;
   SMALLINT read_bank;
   uchar read_buf[PAGE_LENGTH_EPROM];

   // setting the bank to read for information
   // and since all the page lock info is in
   // bank 1 for all the EPROM parts.
   read_bank = 1;

   // read page that locked bit is on 
   pg_len  = getPageLengthEPROM(read_bank,SNum);
   read_pg = (page + lockOffset) / (pg_len * 8);

   if(!readPageCRCEPROM(read_bank,portnum,SNum,read_pg,read_buf))
      return FALSE;

   // return boolean on locked bit
   index = (page + lockOffset) - (read_pg * 8 * pg_len);
   nbyt  = (index >> 3);
   nbit  = index - (nbyt << 3);

   return !(((read_buf[nbyt] >> nbit) & 0x01) == 0x01);
}

/**
 * Redirect the specifed page in the current memory bank to a new page.
 * Not supported by all devices.  See the method 'canRedirectPage()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     the page to be locked
 * newPage  new page number to redirect to
 *
 * @return  true if the page was redirected
 */
SMALLINT redirectPage(SMALLINT bank, int portnum, uchar *SNum, int page, int newPage)
{
   uchar wr_byte[1];  // create byte to redirect page
   SMALLINT write_bank;

   if(isRedirectPageLocked(bank,portnum,SNum,page))
   {
      OWERROR(OWERROR_REDIRECTED_PAGE);
      return FALSE;
   }

   // setting the bank to write
   switch(SNum[0])
   {
      case 0x09: case 0x12:
         write_bank = 1;
         break;

      case 0x0B: case 0x0F: case 0x13:
         write_bank = 4;
         break;

      default:
         write_bank = 4;
         break;
   }

   wr_byte[0] = (uchar) ~newPage;

   // write the redirection byte
   if(!writeEPROM(write_bank,portnum,SNum,page + redirectOffset(bank,SNum),wr_byte,1))
      return FALSE;

   return TRUE;
}


/**
 * Gets the page redirection of the specified page.
 * Not supported by all devices. 
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     number of page check for redirection
 *
 * @return  the new page number or 0 if not redirected
 */
SMALLINT getRedirectedPage(SMALLINT bank, int portnum, uchar *SNum, int page)
{
   int pg_len, read_pg;
   SMALLINT read_bank;
   uchar read_buf[PAGE_LENGTH_EPROM];

   // setting the bank to read for information
   switch(SNum[0])
   {
      case 0x09: case 0x12:
         read_bank = 1;
         break;

      case 0x0B: case 0x0F: case 0x13:
         read_bank = 4;
         break;

      default:
         read_bank = 4;
         break;
   }

   // read page that redirect byte is on 
   pg_len  = getPageLengthEPROM(read_bank,SNum);
   read_pg = (page + redirectOffset(bank,SNum)) / pg_len;

   if(!readPageCRCEPROM(read_bank,portnum,SNum,read_pg,read_buf))
      return FALSE;

   // return page
   return (SMALLINT) (~read_buf[(page + redirectOffset(bank,SNum)) % pg_len] & 0x000000FF);
}

/**
 * Lock the redirection option for the specifed page in the current
 * memory bank. Not supported by all devices.  See the method
 * 'canLockRedirectPage()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     number of page to redirect
 *
 * @return  true if the redirected page is locked.
 */
SMALLINT lockRedirectPage(SMALLINT bank, int portnum, uchar *SNum, int page)
{
   int nbyt, nbit;
   SMALLINT write_bank;
   uchar wr_byte[1];

   if(isRedirectPageLocked(bank,portnum,SNum,page))
   {
      OWERROR(OWERROR_LOCKING_REDIRECTED_PAGE_AGAIN);
      return FALSE;
   }

   // setting the bank to write
   switch(SNum[0])
   {
      case 0x0B: case 0x0F: case 0x13:
         write_bank = 2;
         break;

      default:
         write_bank = 0;
         break;
   }

   // create byte to write to mlLock to lock page
   nbyt    = (page >> 3);
   nbit    = page - (nbyt << 3);

   wr_byte[0] = (uchar) ~(0x01 << nbit);

   // write the lock bit
   if(!writeEPROM(write_bank,portnum,SNum,nbyt + lockRedirectOffset,wr_byte,1))
      return FALSE;
   
   // read back to verify
   if (!isRedirectPageLocked(bank,portnum,SNum,page) || (write_bank == 0))
   {
      OWERROR(OWERROR_COULD_NOT_LOCK_REDIRECT);
      return FALSE;
   }

   return TRUE;
}

/**
 * Query to see if the specified page has redirection locked.
 * Not supported by all devices.  See the method 'canRedirectPage()'.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * portnum  the port number of the port being used for the
 *          1-Wire Network.
 * SNum     the serial number for the part.
 * page     number of page to check for redirection
 *
 * @return  true if redirection is locked for this page
 */
SMALLINT isRedirectPageLocked(SMALLINT bank, int portnum, uchar *SNum, int page)
{
   int pg_len, read_pg, index, nbyt, nbit;
   SMALLINT read_bank;
   uchar read_buf[PAGE_LENGTH_EPROM];

   // setting the bank to read for information
   switch(SNum[0])
   {
      case 0x0B: case 0x0F: case 0x13:
         read_bank = 2;
         break;

      default:
         return FALSE;
   }

   // read page that lock redirect bit is on 
   pg_len  = getPageLengthEPROM(read_bank,SNum);
   read_pg = (page + lockRedirectOffset) / (pg_len * 8);

   if(!readPageCRCEPROM(read_bank,portnum,SNum,read_pg,read_buf))
      return FALSE;

   // return boolean on lock redirect bit
   index = (page + lockRedirectOffset) - (read_pg * 8 * pg_len);
   nbyt  = (index >> 3);
   nbit  = index - (nbyt << 3);

   return !(((read_buf[nbyt] >> nbit) & 0x01) == 0x01);
}

/**
 * Query to see if current memory bank pages can be redirected
 * to another pages.  This is mostly used in Write-Once memory
 * to provide a means to update.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank pages can be redirected
 *          to a new page.
 */
SMALLINT canRedirectPageEPROM(SMALLINT bank, uchar *SNum)
{
   SMALLINT canRedirect = canredirectPage;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 0)
            canRedirect = TRUE;
         break;

      case 0x0B:
         if(bank == 0)
            canRedirect = TRUE;
         break;

      case 0x0F:
         if(bank == 0)
            canRedirect = TRUE;
         break;

      case 0x12:
         if(bank == 0)
            canRedirect = TRUE;
         break;

      case 0x13:
         if(bank == 0)
            canRedirect = TRUE;
         break;

      default:
         canRedirect = canredirectPage;
         break;
   }

   return canRedirect;
}

/**
 * Query to see if current memory bank pages can be locked.  A
 * locked page would prevent any changes to the memory.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank pages can be redirected
 *          to a new page.
 */
SMALLINT canLockPageEPROM(SMALLINT bank, uchar *SNum)
{
   SMALLINT canLock = canlockPage;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 0)
            canLock = TRUE;
         break;

      case 0x0B:
         if(bank == 0)
            canLock = TRUE;
         break;

      case 0x0F:
         if(bank == 0)
            canLock = TRUE;
         break;

      case 0x12:
         if(bank == 0)
            canLock = TRUE;
         break;

      case 0x13:
         if(bank == 0)
            canLock = TRUE;
         break;

      default:
         canLock = canlockPage;
         break;
   }

   return canLock;
}

/**
 * Query to see if current memory bank pages can be locked from
 * being redirected.  This would prevent a Write-Once memory from
 * being updated.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  'true' if current memory bank pages can be locked from
 *          being redirected to a new page.
 */
SMALLINT canLockRedirectPageEPROM(SMALLINT bank, uchar *SNum)
{
   SMALLINT lockRedirect = canlockRedirectPage;

   switch(SNum[0])
   {
      case 0x0B:
         if(bank == 0)
            lockRedirect = TRUE;
         break;

      case 0x0F:
         if(bank == 0)
            lockRedirect = TRUE;
         break;

      case 0x13:
         if(bank == 0)
            lockRedirect = TRUE;
         break;

      default:
         lockRedirect = canlockRedirectPage;
         break;
   }

   return lockRedirect;
}


//--------
//-------- Local functions
//--------

/**
 * Send the command for writing to memory for a device given
 * the memory bank and serial number.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  the command for writing to the memory bank.
 */
uchar writeMemCmd(SMALLINT bank, uchar *SNum)
{
   uchar command = MAIN_WRITE_COMMAND_EPROM;
   
   switch(SNum[0])
   {
      case 0x09:
         if(bank == 1)
            command = STATUS_WRITE_COMMAND_EPROM;
         break;

      case 0x0B:
         if((bank > 0) && (bank < 5))
            command = STATUS_WRITE_COMMAND_EPROM;
         break;

      case 0x0F:
         if((bank > 0) && (bank < 5))
            command = STATUS_WRITE_COMMAND_EPROM;
         break;

      case 0x12:
         if(bank == 1)
            command = STATUS_WRITE_COMMAND_EPROM;
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            command = STATUS_WRITE_COMMAND_EPROM;
         break;

      default:
         command = MAIN_WRITE_COMMAND_EPROM;
         break;
   }

   return command;
}

/**
 * Get the number of CRC bytes for the device.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  the number of CRC bytes.
 */
SMALLINT numCRCbytes(SMALLINT bank, uchar *SNum)
{
   SMALLINT numbytes = CRCbytes;

   switch(SNum[0])
   {
      case 0x09:
         if((bank == 0) || (bank == 1))
            numbytes = 1;
         break;

      default:
         numbytes = CRCbytes;
         break;
   }

   return numbytes;
}

/**
 * Get the status of the page the pages write verification
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  true if write verification is used and
 *          false if it is not used.
 */
SMALLINT writeVerify(SMALLINT bank, int portnum, uchar *SNum, int page)
{
   if(isPageLocked(bank,portnum,SNum,page) || 
      isRedirectPageLocked(bank,portnum,SNum,page))
      return FALSE;

   return writeVerification_eprom;
}

/**
 * Get crc after sending command,address
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  true if crc is can be gotten after sending command,address
 *          false otherwise
 */
SMALLINT crcAfterAdd(SMALLINT bank, uchar *SNum)
{
   SMALLINT crcAfter = TRUE;

   switch(SNum[0])
   {
      case 0x0B:
         if((bank > 0) && (bank < 5))
            crcAfter = FALSE;
         break;

      case 0x0F:
         if((bank > 0) && (bank < 5))
            crcAfter = FALSE;
         break;

      case 0x12:
         if(bank == 1)
            crcAfter = FALSE;
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            crcAfter = FALSE;
         break;

      default:
         crcAfter = TRUE;
         break;
   }

   return crcAfter;
}

/**
 * Get the byte command for reading the page with crc.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  the byte command for reading the page
 */
uchar readPageWithCRC(SMALLINT bank, uchar *SNum)
{
   uchar readPage = MAIN_READ_PAGE_COMMAND_EPROM;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 0)
            readPage = 0xC3;
         else if(bank == 1)
            readPage = STATUS_READ_PAGE_COMMAND_EPROM;
         break;

      case 0x0B:
         if((bank > 0) && (bank < 5))
            readPage = STATUS_READ_PAGE_COMMAND_EPROM;
         break;

      case 0x0F:
         if((bank > 0) && (bank < 5))
            readPage = STATUS_READ_PAGE_COMMAND_EPROM;
         break;

      case 0x12:
         if(bank == 1)
            readPage = STATUS_READ_PAGE_COMMAND_EPROM;
         break;

      case 0x13:
         if((bank > 0) && (bank < 5))
            readPage = STATUS_READ_PAGE_COMMAND_EPROM;
         break;

      default:
         readPage = MAIN_READ_PAGE_COMMAND_EPROM;
         break;
   }

   return readPage;
}

/**
 * Get the offset into the redirection status information.
 *
 * bank     to tell what memory bank of the ibutton to use.
 * SNum     the serial number for the part.
 *
 * @return  the offset into the redirection status
 */
SMALLINT redirectOffset(SMALLINT bank, uchar *SNum)
{
   int offset = 0;

   switch(SNum[0])
   {
      case 0x09:
         if(bank == 0)
            offset = 1;
         break;
         
      case 0x12:
         if(bank == 0)
            offset = 1;
         break;

      default:
         offset = 0;
         break;
   }

   return offset;
}
