
/*---------------------------------------------------------------------------
 * Copyright (C) 1999,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.
 *---------------------------------------------------------------------------
 */

package com.dalsemi.onewire.container;

import com.dalsemi.onewire.utils.CRC16;
import com.dalsemi.onewire.*;
import com.dalsemi.onewire.adapter.*;
import com.dalsemi.onewire.container.*;


/**
 * The DS2760 1-Cell Li-Ion Battery Monitor is a data acquisition, information
 * storage, and safety protection device tailored for cost-sensitive battery pack
 * applications. This low-power device integrates precise temperature, voltage,
 * and current measurement, nonvolatile data storage, and Li-Ion protection
 * into the small footprint of either a TSSOP package or flip-chip. The
 * DS2760 is a key component in applications including adaptive charging,
 * remaining capacity estimation, safety monitoring, and battery-specific
 * data storage.
 *
 * @version    0.00, 28 Aug 2000
 * @author     COlmstea
 *
 * Converted to use interfaces, general rework KLA
 */
public class OneWireContainer30
   extends OneWireContainer
   implements ADContainer, TemperatureContainer
{

   /**
    * Memory functions.
    */
   private static final byte WRITE_DATA_COMMAND  = ( byte ) 0x6C;
   private static final byte READ_DATA_COMMAND   = ( byte ) 0x69;
   private static final byte COPY_DATA_COMMAND   = ( byte ) 0x48;
   private static final byte RECALL_DATA_COMMAND = ( byte ) 0xB8;
   private static final byte LOCK_COMMAND        = ( byte ) 0x6A;

   /**
    * Address of the Protection Register. Used to set/check flags with
    * setFlag/getFlag.
    */
   public static final byte PROTECTION_REGISTER = 0;

   /**
    * Address of the Status Register. Used to set/check flags with
    * setFlag/getFlag.
    */
   public static final byte STATUS_REGISTER = 1;

   /**
    * Address of the EEPROM Register. Used to set/check flags with
    * setFlag/getFlag.
    */
   public static final byte EEPROM_REGISTER = 7;

   /**
    * Address of the Special Feature Register. Used to check flags with
    * getFlag.
    */
   public static final byte SPECIAL_FEATURE_REGISTER = 8;

   /**
    * PROTECTION REGISTER FLAG: When this flag is true, it indicates that
    * the battery pack has experienced an overvoltage condition.
    * This flag must be reset!
    * Accessed with getFlag().
    */
   public static final byte OVERVOLTAGE_FLAG = ( byte ) 128;

   /**
    * PROTECTION REGISTER FLAG: When this flag is true, the battery pack has
    * experienced an undervoltage.
    * This flag must be reset!
    * Accessed with getFlag()
    */
   public static final byte UNDERVOLTAGE_FLAG = 64;

   /**
    * PROTECTION REGISTER FLAG: When this flag is true the battery has
    * experienced a charge-direction overcurrent condition.
    * This flag must be reset!
    * Accessed with getFlag()
    */
   public static final byte CHARGE_OVERCURRENT_FLAG = 32;

   /**
    * PROTECTION REGISTER FLAG: When this flag is true the battery has
    * experienced a discharge-direction overcurrent condition.
    * This flag must be reset()!
    * Accessed with getFlag()
    */
   public static final byte DISCHARGE_OVERCURRENT_FLAG = 16;

   /**
    * PROTECTION REGISTER FLAG: Mirrors the !CC output pin.
    * Accessed with getFlag()
    */
   public static final byte CC_PIN_STATE_FLAG = 8;

   /**
    * PROTECTION REGISTER FLAG: Mirrors the !DC output pin.
    * Accessed with getFlag()
    */
   public static final byte DC_PIN_STATE_FLAG = 4;

   /**
    * PROTECTION REGISTER FLAG: Reseting this flag will disable charging
    * regardless of cell or pack conditions.
    * Accessed with getFlag/setFlag.
    */
   public static final byte CHARGE_ENABLE_FLAG = 2;

   /**
    * PROTECTION REGISTER FLAG: Reseting this flag will disable discharging.
    * Accessed with getFlag/setFlag.
    */
   public static final byte DISCHARGE_ENABLE_FLAG = 1;

   /**
    * STATUS REGISTER FLAG: Enables/disables the DS2760 to enter sleep mode
    * when the DQ line goes low for greater than 2 seconds.
    * Accessed with getFlag/setFlag.
    */
   public static final byte SLEEP_MODE_ENABLE_FLAG = 32;

   /**
    * STATUS REGISTER FLAG: If set, the opcode for the Read Net Address command
    * will be set to 33h. If it is not set the opcode is set to 39h.
    * Accessed with getFlag/setFlag.
    */
   public static final byte READ_NET_ADDRESS_OPCODE_FLAG = 16;

   /**
    * EEPROM REGISTER FLAG: This flag will be true if the Copy Data Command
    * is in progress. Data may be written to EEPROM when this reads false.
    * Accessed with getFlag/setFlag.
    */
   public static final byte EEPROM_COPY_FLAG = ( byte ) 128;

   /**
    * EEPROM REGISTER FLAG: When this flag is true, the Lock Command is enabled.
    * The lock command is used to make memory permanently read only.
    * Accessed with getFlag/setFlag.
    */
   public static final byte EEPROM_LOCK_ENABLE_FLAG = 64;

   /**
    * EEPROM REGISTER FLAG: When this flag is true, Block 1 of the EEPROM
    * (addresses 48-63) is read-only.
    * Accessed with getFlag().
    */
   public static final byte EEPROM_BLOCK_1_LOCK_FLAG = 2;

   /**
    * EEPROM REGISTER FLAG: When this flag is true, Block 0 of the EEPROM
    * (addresses 32-47) is read-only.
    * Accessed with getFlag().
    */
   public static final byte EEPROM_BLOCK_0_LOCK_FLAG = 1;

   /**
    * SPECIAL FEATURE REGISTER FLAG: Mirrors the state of the !PS pin.
    * Accessed with getFlag().
    */
   public static final byte PS_PIN_STATE_FLAG = ( byte ) 128;

   /**
    * SPECIAL FEATURE REGISTER FLAG: Mirrors/sets the state of the PIO pin. The
    * PIO pin can be used as an output; resetting this flag disables the PIO
    * output driver.
    * Accessed with getFlag/setFlag.
    */
   public static final byte PIO_PIN_SENSE_AND_CONTROL_FLAG = 64;

   /**
    * Holds the value of the sensor external resistance.
    */
   private double Rsens = .05;

   /**
    * When this is true, all calculations are assumed to be done in the part.
    */
   private boolean internalResistor;

   /**
    * Default constructor
    */
   public OneWireContainer30 ()
   {
      super();

      internalResistor = true;
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer30 (DSPortAdapter sourceAdapter, byte[] newAddress)
   {
      super(sourceAdapter, newAddress);

      internalResistor = true;
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer30 (DSPortAdapter sourceAdapter, long newAddress)
   {
      super(sourceAdapter, newAddress);

      internalResistor = true;
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer30 (DSPortAdapter sourceAdapter, String newAddress)
   {
      super(sourceAdapter, newAddress);

      internalResistor = true;
   }

   /**
    * Returns the Dallas Semiconductor part number of the iButton
    * as a string.
    *
    * @return <code>String</code> representation of the iButton name
    *
    */
   public String getName ()
   {
      return "DS2760";
   }

   /**
    * Return the alternate Dallas Semiconductor part number or name.
    * ie. Smart Battery Monitor
    *
    *  @return <code>String</code> representation of the alternate names.
    */
   public String getAlternateNames ()
   {
      return "1-Cell Li-Ion Battery Monitor";
   }

   /**
    *  Return a short description of the function of the iButton type.
    *
    *  @return <code>String</code> representation of the function description.
    */
   public String getDescription ()
   {
      return "The DS2760 is a data acquisition, information storage, and safety"
             + " protection device tailored for cost-sensitive battery pack applications."
             + " This low-power device integrates precise temperature, voltage, and"
             + " current measurement , nonvolatile data storage, and Li-Ion protection"
             + " into the small footprint of either a TSSOP packet or flip-chip.";
   }

   /**
    * Set the DS2760 to use its internal .025 ohm resistor for measurements.
    * This should only be enabled if there is NO external resistor physically
    * attached to the device.
    */
   public synchronized void setResistorInternal ()
   {
      internalResistor = true;
   }

   /**
    * Set the DS2760 to use an external, user-selectable resistance. This
    * Resistance should be wired directly to the VSS (negative terminal of
    * the cell).
    *
    * @param <code>double</code> represents the resistance in ohms.
    */
   public synchronized void setResistorExternal (double Rsens)
   {
      internalResistor = false;
      this.Rsens       = Rsens;
   }

   /**
    * Read a register byte from the memory of the DS2760.  Note that there
    * is no error checking as the DS2760 performs no CRC on the data.
    * <p>
    * Note: This function should only be used when reading the register
    * memory of the 2760. The EEPROM blocks (addresses 32-64) should be
    * accessed with writeBlock/readBlock.
    *
    * @param memAddr the address to read (0-255)
    *
    * @return <code>byte</code> holding the data read from memory.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public byte readByte (int memAddr)
      throws OneWireIOException, OneWireException
   {
      byte[] buffer = new byte [3];

      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         /* setup the read */
         buffer [0] = READ_DATA_COMMAND;
         buffer [1] = ( byte ) memAddr;
         buffer [2] = ( byte ) 0xFF;

         adapter.dataBlock(buffer, 0, 3);

         return buffer [2];
      }
      else
         throw new OneWireException("OneWireContainer30-Device not found.");
   }

   /**
    * Reads bytes from the DS2760.  Note that there is no error-checking as
    * the DS2760 does not perform a CRC on the data.
    * <p>
    * Note: This function should only be used when reading the register
    * memory of the 2760. The EEPROM blocks (addresses 32-64) should be
    * accessed with writeBlock/readBlock.
    *
    * @param memAddr the address to read (0-255)
    * @param buffer
    * @param start
    * @param len
    *
    * @return <code>byte</code> holding the data read from memory.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public void readBytes (int memAddr, byte[] buffer, int start, int len)
      throws OneWireIOException, OneWireException
   {
      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {
         for (int i = start; i < start + len; i++)
            buffer [i] = ( byte ) 0x0ff;

         adapter.putByte(READ_DATA_COMMAND);
         adapter.putByte(memAddr & 0x0ff);
         adapter.dataBlock(buffer, start, len);
      }
      else
         throw new OneWireException("OneWireContainer30-Device not found.");
   }

   /**
    * Write a register byte to the memory of the DS2760.  Note that the
    * DS2760 does not make any use of cyclic redundancy checks (error-checking).
    * To ensure error free transmission, double check your write's.
    * <p>
    * Note: This method should only be used when writing to the register memory
    * of the 2760. The EEPROM blocks (addresses 32-64) require special treatment
    * and thus the writeBlock/readBlock functions should be used for those.
    *
    * @param memAddr the address to write (0-255).
    * @param data the data byte to write to memory.
    *
    * @throws OneWireIOException Error writing data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public void writeByte (int memAddr, byte data)
      throws OneWireIOException, OneWireException
   {
      byte[] buffer = new byte [3];

      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         /* first perform the write */
         buffer [0] = WRITE_DATA_COMMAND;
         buffer [1] = ( byte ) memAddr;
         buffer [2] = data;

         adapter.dataBlock(buffer, 0, 3);

         //don't read it back for verification...some addresses
         //have some R-only bits and some RW bits.  let the user
         //verify if they want to
      }
      else
         throw new OneWireException("OneWireContainer30-Device not found.");
   }

   /**
    * Read a 16 byte data block from one of the user EEPROM blocks.
    * Note that there is no error-checking as the DS2760 performs
    * no CRCs.
    *
    * @param blockNumber the EEPROM block number to read.
    * <p>Acceptable parameters are 0 and 1.
    *
    * @return <code>byte[]</code> holding the 16 data bytes.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public byte[] readEEPROMBlock (int blockNumber)
      throws OneWireIOException, OneWireException
   {
      byte[] buffer = new byte [18];
      byte[] result = new byte [16];

      // calculate the address (32 and 48 are valid addresses)
      byte memAddr = ( byte ) (32 + (blockNumber * 16));

      /* check for valid parameters */
      if ((blockNumber != 0) & (blockNumber != 1))
         throw new IllegalArgumentException(
            "OneWireContainer30-Block number " + blockNumber
            + " is not a valid EEPROM block.");

      /* perform the recall/read and verification */
      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         /* first recall the memory to shadow ram */
         buffer [0] = RECALL_DATA_COMMAND;
         buffer [1] = memAddr;

         adapter.dataBlock(buffer, 0, 2);

         /* now read the shadow ram */
         adapter.reset();
         adapter.select(address);

         buffer [0] = READ_DATA_COMMAND;

         // buffer[1] should still hold memAddr
         for (int i = 0; i < 16; i++)
            buffer [i + 2] = ( byte ) 0xff;

         adapter.dataBlock(buffer, 0, 18);

         // keep this result
         System.arraycopy(buffer, 2, result, 0, 16);

         //user can re-read for verification
         return result;
      }
      else
         throw new OneWireException("OneWireContainer30-Device not found.");
   }

   /**
    * Write a 16 byte data block to one of the user blocks. The block may be
    * rewritten at any time, except after it is locked with lockBlock().
    * This method performs error checking by verifying data written.
    *
    * @param blockNumber the block to write to.
    * <p>Acceptable parameters are 0 and 1.
    * @param data the 16 bytes of data to write.
    *
    * @throws OneWireIOException Error writing data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public void writeEEPROMBlock (int blockNumber, byte[] data)
      throws OneWireIOException, OneWireException
   {
      byte[] buffer = new byte [18];

      // the first block is at address 32 and the second is at address 48
      byte memAddr = ( byte ) (32 + (blockNumber * 16));

      /* check for valid parameters */
      if (data.length < 16)
         throw new IllegalArgumentException(
            "OneWireContainer30-Data block must consist of 16 bytes.");

      if ((blockNumber != 0) && (blockNumber != 1))
         throw new IllegalArgumentException(
            "OneWireContainer30-Block number " + blockNumber
            + " is not a valid EEPROM block.");

      // if the EEPROM block is locked throw a OneWireIOException
      if (((blockNumber == 0) && (getFlag(EEPROM_REGISTER, EEPROM_BLOCK_0_LOCK_FLAG)))
              || ((blockNumber == 1)
                  && (getFlag(EEPROM_REGISTER, EEPROM_BLOCK_1_LOCK_FLAG))))
         throw new OneWireIOException(
            "OneWireContainer30-Cant write data to locked EEPROM block.");

      /* perform the write/verification and copy */
      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         /* first write to shadow rom */
         buffer [0] = WRITE_DATA_COMMAND;
         buffer [1] = memAddr;

         for (int i = 0; i < 16; i++)
            buffer [i + 2] = data [i];

         adapter.dataBlock(buffer, 0, 18);

         /* read the shadow ram back for verification */
         adapter.reset();
         adapter.select(address);

         buffer [0] = READ_DATA_COMMAND;

         // buffer[1] should still hold memAddr
         for (int i = 0; i < 16; i++)
            buffer [i + 2] = ( byte ) 0xff;

         adapter.dataBlock(buffer, 0, 18);

         // verify data
         for (int i = 0; i < 16; i++)
            if (buffer [i + 2] != data [i])
               throw new OneWireIOException(
                  "OneWireContainer30-Error writing EEPROM block"
                  + blockNumber + ".");

         /* now perform the copy to EEPROM */
         adapter.reset();
         adapter.select(address);

         buffer [0] = COPY_DATA_COMMAND;

         // buffer[1] should still hold memAddr
         adapter.dataBlock(buffer, 0, 2);
      }
      else
         throw new OneWireException("OneWireContainer30-Device not found.");
   }

   /**
    * Permanently write-protects one of the user blocks of EEPROM.
    *
    * @param blockNumber block number to permanently write protect.
    * <p>Acceptable parameters are 0 and 1.
    *
    * @throws OneWireIOException Error locking block [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public void lockBlock (int blockNumber)
      throws OneWireIOException, OneWireException
   {

      // compute the byte location
      byte memAddr = ( byte ) (32 + (blockNumber * 16));

      /* check if the block is valid */
      if ((blockNumber != 0) & (blockNumber != 1))
         throw new IllegalArgumentException(
            "OneWireContainer30-Block " + blockNumber
            + " is not a valid EEPROM block.");

      /* perform the lock */
      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {
         adapter.putByte(LOCK_COMMAND);
         adapter.putByte(memAddr);
      }
      else
         throw new OneWireException("OneWireContainer30-Device not found.");
   }

   /**
    * Checks the specified flag in the specified registor.
    * <p>
    * Valid registors are: PROTECTION_REGISTER, STATUS_REGISTER, EEPROM_REGISTER
    * and SPECIAL_FEATURE_REGISTER.
    *
    * @param memAddr the registers address. Pre-defined
    * fields for each register are defined above.
    * @param flagToGet bitmask of desired flag. The acceptable parameters
    * pertaining to each register are defined as constant fields above.
    *
    * @return Value of the flag: True if flag is set (==1)
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    *
    */
   public boolean getFlag (int memAddr, byte flagToGet)
      throws OneWireIOException, OneWireException
   {

      // read the byte and perform a simple mask to determine if that byte is on
      byte data = readByte(memAddr);

      if ((data & flagToGet) != 0)
         return true;

      return false;
   }

   /**
    * Set one of the flags in one of the registers.
    * <p>
    * Valid registers are:
    * PROTECTION_REGISTER, STATUS_REGISTER, EEPROM_REGISTER and the infamous
    * SPECIAL_FEATURE_REGISTER.
    *
    * @param memAddr the register address. These addresses are
    * predefined above.
    * @param flagToSet bitmask of flag to set. Valid
    * parameters pertaining to each register are defined as constant fields above.
    * @param flagValue status to set the flag at.
    *
    * @throws OneWireIOException Error setting flag [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public void setFlag (int memAddr, byte flagToSet, boolean flagValue)
      throws OneWireIOException, OneWireException
   {

      // the desired default value for the status register flags has to be
      // set in a seperate register for some reason, so I treat it specially.
      if (memAddr == STATUS_REGISTER)
         memAddr = 49;

      byte data = readByte(memAddr);

      if (flagValue)
         data = ( byte ) (data | flagToSet);
      else
         data = ( byte ) (data & ~(flagToSet));

      writeByte(memAddr, data);
   }

   /**
    * Get the instantaneous current.
    *
    *
    * @param state
    * @return <code>double</code> containing the current in Amperes.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public double getCurrent (byte[] state)
      throws OneWireIOException, OneWireException
   {

      // grab the data
      int data = ((state [14] << 8) | (state [15] & 0x00ff));

      data = data >> 3;

      double result;

      // when the internal resistor is used, the device calculates it for you
      // the resolution is .625 mA
      if (internalResistor)
         result = (data * .625) / 1000;

         // otherwise convert to Amperes
      else
         result = data * .000015625 / Rsens;

      return result;
   }

   /**
    * Allows user to set the remaining capacity. Good for accurate capacity
    * measurements using temperature and battery life.
    * <p> By measuring your battery's current and voltage when it is fully
    * charged and when it is empty, you can figure out the voltage corresponding
    * to an empty battery and the current corresponding to a full one. These
    * values can be detected in your program and the remaining capacity can
    * be set to the empty/full levels accordingly for nice accuracy.
    *
    * @param remainingCapacity the remaining capacity IN mAHrs.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public void setRemainingCapacity (double remainingCapacity)
      throws OneWireIOException, OneWireException
   {
      int data;

      // if the internal resistor is used, it can be stored as is (in mAH)
      if (internalResistor)
         data = ( int ) (remainingCapacity * 4);
      else
         data = ( int ) (remainingCapacity * Rsens / .00626);

      // break into bytes and store
      writeByte(16, ( byte ) (data >> 8));
      writeByte(17, ( byte ) (data & 0xff));
   }

   /**
    * Calculate the remaining capacity in mAHours from the current Accumulator.
    * Accurate to +-.25 mAmp-hours.
    *
    *
    * @param state
    * @return <code>double</code> containing the mAHours of battery capacity remaining.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    */
   public double getRemainingCapacity (byte[] state)
      throws OneWireIOException, OneWireException
   {
      double result = 0;

      // grab the data
      int data = ((state [16] & 0xff) << 8) | (state [17] & 0xff);

      // if the internal resistor is being used the part calculates it for us
      if (internalResistor)
         result = data / 4.0;

         // this equation can be found on the data sheet
      else
         result = data * .00626 / Rsens;

      return result;
   }

   /**
    * Sets the state for the Programmable Input/Output pin.  In order to
    * operate as a switch, PIO must be tied to a pull-up resistor to VDD.
    *
    * @param on State of the PIO pin to set
    * @exception com.dalsemi.onewire.adapter.OneWireIOException
    * @exception com.dalsemi.onewire.OneWireException
    */

   //must have a pull-up resistor from PIO to VDD 4.7KOhm
   public void setLatchState (boolean on)
      throws OneWireIOException, OneWireException
   {

      //since bit 0 is read-only and bits 2-7 are don't cares,
      //we don't need to read location 8 first, we can just write
      writeByte(8, ( byte ) (on ? 0x40
                                : 0x00));
   }

   /**
    * Returns the latch state of the Programmable Input/Ouput
    * pin on the DS2760.
    *
    * @return Latch state of the Programmable Input/Ouput pin
    * @exception com.dalsemi.onewire.adapter.OneWireIOException
    * @exception com.dalsemi.onewire.OneWireException
    */
   public boolean getLatchState ()
      throws OneWireIOException, OneWireException
   {
      return ((readByte(8) & 0x40) == 0x40);
   }

   /**
    * Clears the overvoltage, undervoltage, charge overcurrent,
    * and discharge overcurrent flags.  Each time a violation
    * occurs, these flags stay set until reset.  This method
    * resets all 4 flags.
    *
    * @exception com.dalsemi.onewire.adapter.OneWireIOException
    * @exception com.dalsemi.onewire.OneWireException
    */
   public void clearConditions ()
      throws OneWireIOException, OneWireException
   {
      byte protect_reg = readByte(0);

      writeByte(0, ( byte ) (protect_reg & 0x0f));
   }

   /////////////////////////////////////////////////////////////////////
   //
   //  BEGIN CONTAINER INTERFACE METHODS
   //
   ////////////////////////////////////////////////////////////////////
   //--------
   //-------- A/D Feature methods
   //--------

   /**
    * Query to get the number of channels supported by this A/D.
    * Channel specific methods will use a channel number specified
    * by an integer from [0 to (getNumberChannels() - 1)].
    *
    * @return int containing the number of channels
    */
   public int getNumberADChannels ()
   {
      return 1;
   }

   /**
    * Query to see if this A/D measuring device has high/low
    * alarms.
    *
    * @return boolean, true if has high/low trips
    */
   public boolean hasADAlarms ()
   {
      return false;
   }

   /**
    * Query to get an array of available ranges for the specified
    * A/D channel.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    *
    * @return array of double indicated the available ranges.
    */
   public double[] getADRanges (int channel)
   {
      double[] result = new double [1];

      result [0] = 5.0;

      return result;
   }

   /**
    * Query to get an array of available resolutions based
    * on the specified range on the specified A/D channel.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param range
    *
    * @return array of double, indicated the available resolutions
    */
   public double[] getADResolutions (int channel, double range)
   {
      double[] result = new double [1];

      result [0] = 0.00488;

      return result;
   }

   /**
    * Query to see if this A/D supports doing multiple voltage
    * conversions at the same time.
    *
    * @return boolean, true if can do multi-channel voltage reads.
    */
   public boolean canADMultiChannelRead ()
   {
      return false;
   }

   //--------
   //-------- A/D IO Methods
   //--------

   /**
    * This method is used to perform voltage conversion on all specified
    * channels.  The method 'getVoltage()' can be used to read the result
    * of the conversion.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void doADConvert (int channel, byte[] state)
      throws OneWireIOException, OneWireException
   {

      //this actually should be an airball as well...
      //BECAUSE...
      //the voltage is constantly being read when the part is
      //in active mode, so we can just read it out from the state.
      //the part only leaves active mode if the battery runs out 
      //(i.e. voltage goes below threshold) in which case we should 
      //return the lower bound anyway...
   }

   /**
    * This method is used to perform voltage conversion on all specified
    * channels.  The method 'getVoltage()' can be used to read the result
    * of the conversion. This A/D must support multi-channel read
    * 'canMultiChannelRead()' if there are more then 1 channel is specified.
    *
    * @param doConvert - boolean array representing which channels
    *                    to perform conversion on.
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void doADConvert (boolean[] doConvert, byte[] state)
      throws OneWireIOException, OneWireException
   {
      throw new OneWireException(
         "This device does not support multi-channel reading");
   }

   /**
    * This method is used to read the voltage values.  Must
    * be used after a 'doADConvert()' method call.  Also must
    * include the last valid state from the 'readDevice()' method
    * and this A/D must support multi-channel read 'canMultiChannelRead()'
    * if there are more then 1 channel.
    *
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @return - double[] representing the voltage values for all channels
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public double[] getADVoltage (byte[] state)
      throws OneWireIOException, OneWireException
   {
      throw new OneWireException(
         "This device does not support multi-channel reading");
   }

   /**
    * This method is used to read a channels voltage value.  Must
    * be used after a 'doADConvert()' method call.  Also must
    * include the last valid state from the 'readDevice()' method.
    * Note, if more then one channel is to be read then it is more
    * efficient to use the 'getVoltage()' method that returns all
    * channel values.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @return - double representing the voltage value for the specified
    *                  channel
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public double getADVoltage (int channel, byte[] state)
      throws OneWireIOException, OneWireException
   {
      int    data;
      double result = 0.0;

      // the measurement is put in two bytes, 12 (MSB) and 13. Once the
      // two bytes are ORed, a right shift of 5 must occur (signed shift)
      data = (state [12] << 8) | (state [13] & 0x00ff);
      data = data >> 5;

      // that raw measurement is in 4.88 mV units -> convert to volts
      result = (data * 4.88) / 1000;

      return result;
   }

   //--------
   //-------- A/D 'get' Methods
   //--------

   /**
    * This method is used to extract the alarm voltage value of the
    * specified channel from the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @return - double representing the alarm_value in volts
    */
   public double getADAlarm (int channel, int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to extract the alarm enable value of the
    * specified channel from the provided state buffer.  The state
    * buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of the current state of the state
    *               returned from 'readDevice()'.
    *
    * @return - boolean, true if specified alarm is enabled
    */
   public boolean getADAlarmEnable (int channel, int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to check the alarm event value of the
    * specified channel from the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of the current state of the state
                    returned from 'readDevice()'.
    *
    * @return - boolean, true if specified alarm occurred
    */
   public boolean hasADAlarmed (int channel, int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to extract the conversion resolution of the
    * specified channel from the provided state buffer expressed in
    * volts.  The state is retrieved from the
    * 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the state
    *               returned from 'readDevice()'.
    *
    * @return - double, resolution of channel in volts
    */
   public double getADResolution (int channel, byte[] state)
   {
      return 0.00488;   //its always the same!
   }

   /**
    * This method is used to extract the input voltage range of the
    * specified channel from the provided state buffer.  The state
    * buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the state
    *                  returned from 'readDevice()'.
    *
    * @return - double representing the input voltage range
    */
   public double getADRange (int channel, byte[] state)
   {
      return 5.0;   //so is this one!
   }

   //--------
   //-------- A/D 'set' Methods
   //--------

   /**
    * This method is used to set the alarm voltage value of the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param alarm - double, alarm value (will be reduced to 8 bit resolution).
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADAlarm (int channel, int alarmType, double alarm,
                           byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to set the alarm enable value of the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param alarmEnable - boolean, alarm enable value
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADAlarmEnable (int channel, int alarmType,
                                 boolean alarmEnable, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to set the conversion resolution value for the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param resolution - double, resolution to use in volts
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADResolution (int channel, double resolution, byte[] state)
   {

      //airball! no resolutions to set!
   }

   /**
    * This method is used to set the input range for the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param range - double, representing the max volt range, use
    *                'getRanges()' method to get available ranges
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADRange (int channel, double range, byte[] state)
   {

      //yet another airball--YAAB...only one range on this part
   }

   //--------
   //-------- Temperature Feature methods
   //--------

   /**
    * Query to see if this temperature measuring device has high/low
    * trip alarms.
    *
    * @return boolean, true if has high/low trip alarms
    */
   public boolean hasTemperatureAlarms ()
   {
      return false;
   }

   /**
    * Query to see if this device has selectable resolution.
    *
    * @return boolean, true if has selectable resolution
    */
   public boolean hasSelectableTemperatureResolution ()
   {
      return false;
   }

   /**
    * Query to get an array of available resolutions in degrees C.
    *
    * @return double[], available of resolutions in degrees C
    */
   public double[] getTemperatureResolutions ()
   {
      double[] result = new double [1];

      result [0] = 0.125;

      return result;
   }

   /**
    * Query to get the high/low resolution in degrees C.
    *
    * @return double, high/low resolution resolution in C
    */
   public double getTemperatureAlarmResolution ()
      throws OneWireException
   {
      throw new OneWireException(
         "This device does not have temperature alarms");
   }

   /**
    * Query to get the maximum temperature in degrees C.
    *
    * @return double, maximum temperature in C
    */
   public double getMaxTemperature ()
   {
      return 85.0;
   }

   /**
    * Query to get the minimum temperature in degrees C.
    *
    * @return double, minimum temperature in C
    */
   public double getMinTemperature ()
   {
      return -40.0;
   }

   //--------
   //-------- Temperature I/O Methods
   //--------

   /**
    * Perform an temperature conversion.  Use this state information
    * to calculate the conversion time.
    *
    * @param state - byte array of device state
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void doTemperatureConvert (byte[] state)
      throws OneWireIOException, OneWireException
   {

      //for the same reason we don't have to do an AD conversion,
      //we don't have to do a temperature conversion--its done
      //continuously
   }

   //--------
   //-------- Temperature 'get' Methods
   //--------

   /**
    * This method extracts the Temperature Value in degrees C from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return double, temperature in degrees C from the last 'doTemperature()'
    */
   public double getTemperature (byte[] state)
   {
      double temperature;
      int    data;

      // the MSB is at 24, the LSB at 25 and the format is so that when
      // attached, the whole thing must be shifted right 5 (Signed)
      data = (state [24] << 8) | (state [25] & 0x00ff);
      data = data >> 5;

      // that raw measurement is in .125 degree units
      temperature = data / 8.0;

      return temperature;
   }

   /**
    * This method extracts the specified Alarm value in degrees C from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param alarmType - integer, indicating trip type ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of device state
    *
    * @return double, alarm trip temperature in degrees C
    */
   public double getTemperatureAlarm (int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException(
         "This device does not have temperature alarms");
   }

   /**
    * This method extracts the current resolution in degrees C from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return double, temperature resolution in degrees C
    */
   public double getTemperatureResolution (byte[] state)
   {
      return 0.125;
   }

   //--------
   //-------- Temperature 'set' Methods
   //--------

   /**
    * This method sets the alarm value in degrees C in the
    * provided state data.  Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param alarmType - integer, indicating trip type ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param alarmValue - double, high trip value in degrees C
    * @param state - byte array of device state
    */
   public void setTemperatureAlarm (int alarmType, double alarmValue,
                                    byte[] state)
      throws OneWireException, OneWireIOException
   {
      throw new OneWireException(
         "This device does not have temperature alarms");
   }

   /**
    * This method sets the current resolution in degrees C in the
    * provided state data.   Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param resolution - double, temperature resolution in degrees C
    * @param state - byte array of device state
    */
   public void setTemperatureResolution (double resolution, byte[] state)
      throws OneWireException, OneWireIOException
   {

      //airball, there can be only ONE resolution!
   }

   //--------
   //-------- Sensor I/O methods
   //--------

   /**
    * This method retrieves the 1-Wire device sensor state.  This state is
    * returned as a byte array.  Pass this byte array to the static query
    * and set methods.  If the device state needs to be changed then call
    * the 'writeDevice' to finalize the one or more change.  With the
    * DS2760, there are no CRC checks on data.  To ensure the integrity
    * of this data, call twice and make sure only fields expected to change
    * have changed.  This method returns bytes 0 to 31 of the memory.
    * Many applications will not need to worry about the lack of CRC checks.
    *
    * @return <code>byte[]<\code> 1-Wire device sensor state    *
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public byte[] readDevice ()
      throws OneWireIOException, OneWireException
   {
      byte[] buffer = new byte [32];
      byte[] result = new byte [32];

      /* perform the read twice to ensure a good transmission */
      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         /* do the first read */
         adapter.putByte(READ_DATA_COMMAND);
         adapter.putByte(0);
         adapter.getBlock(result, 0, 32);
      }
      else
         throw new OneWireException("OneWireContainer30-Device not found.");

      return result;
   }

   /**
    * This method write the 1-Wire device sensor state that
    * have been changed by the 'set' methods.  It knows which registers have
    * changed by looking at the bitmap fields appended to the state
    * data.
    *
    * @param  state - byte array of clock register page contents
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void writeDevice (byte[] state)
      throws OneWireIOException, OneWireException
   {

      /* need to write the following bytes:
       *   0 Protection register
       *   1 Status register
       *   7 EEPROM register
       *   8 Special feature register
       *   16 Accumulated current register MSB
       *   17 Accumulated current register LSB
       */

      //drain this....let's just make everything happen in real time
   }
}
