Package lejos.nxt.comm

Source Code of lejos.nxt.comm.Bluetooth$Connector

package lejos.nxt.comm;

import java.util.*;

import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.RemoteDevice;

import lejos.nxt.SystemSettings;

/**
* Provides Bluetooth communications.
* Allows inbound and outbound connections.
* Provides access to to device registration.
*/
public class Bluetooth extends NXTCommDevice
{
  public static  final byte MSG_BEGIN_INQUIRY = 0;
  public static  final byte MSG_CANCEL_INQUIRY = 1;
  public static  final byte MSG_CONNECT = 2;
  public static  final byte MSG_OPEN_PORT = 3;
  public static  final byte MSG_LOOKUP_NAME = 4;
  public static  final byte MSG_ADD_DEVICE = 5;
  public static  final byte MSG_REMOVE_DEVICE = 6;
  public static  final byte MSG_DUMP_LIST = 7;
  public static  final byte MSG_CLOSE_CONNECTION = 8;
  public static  final byte MSG_ACCEPT_CONNECTION = 9;
  public static  final byte MSG_PIN_CODE = 10;
  public static  final byte MSG_OPEN_STREAM = 11;
  public static  final byte MSG_START_HEART = 12;
  public static  final byte MSG_HEARTBEAT = 13;
  public static  final byte MSG_INQUIRY_RUNNING = 14;
  public static  final byte MSG_INQUIRY_RESULT = 15;
  public static  final byte MSG_INQUIRY_STOPPED = 16;
  public static  final byte MSG_LOOKUP_NAME_RESULT = 17;
  public static  final byte MSG_LOOKUP_NAME_FAILURE = 18;
  public static  final byte MSG_CONNECT_RESULT = 19;
  public static  final byte MSG_RESET_INDICATION = 20;
  public static  final byte MSG_REQUEST_PIN_CODE = 21;
  public static  final byte MSG_REQUEST_CONNECTION = 22;
  public static  final byte MSG_LIST_RESULT = 23;
  public static  final byte MSG_LIST_ITEM = 24;
  public static  final byte MSG_LIST_DUMP_STOPPED = 25;
  public static  final byte MSG_CLOSE_CONNECTION_RESULT = 26;
  public static  final byte MSG_PORT_OPEN_RESULT = 27;
  public static  final byte MSG_SET_DISCOVERABLE = 28;
  public static  final byte MSG_CLOSE_PORT = 29;
  public static  final byte MSG_CLOSE_PORT_RESULT = 30;
  public static  final byte MSG_PIN_CODE_ACK = 31;
  public static  final byte MSG_SET_DISCOVERABLE_ACK = 32;
  public static  final byte MSG_SET_FRIENDLY_NAME = 33;
  public static  final byte MSG_SET_FRIENDLY_NAME_ACK = 34;
  public static  final byte MSG_GET_LINK_QUALITY = 35;
  public static  final byte MSG_LINK_QUALITY_RESULT = 36;
  public static  final byte MSG_SET_FACTORY_SETTINGS = 37;
  public static  final byte MSG_SET_FACTORY_SETTINGS_ACK = 38;
  public static  final byte MSG_GET_LOCAL_ADDR = 39;
  public static  final byte MSG_GET_LOCAL_ADDR_RESULT = 40;
  public static  final byte MSG_GET_FRIENDLY_NAME = 41;
  public static  final byte MSG_GET_DISCOVERABLE = 42;
  public static  final byte MSG_GET_PORT_OPEN = 43;
  public static  final byte MSG_GET_FRIENDLY_NAME_RESULT = 44;
  public static  final byte MSG_GET_DISCOVERABLE_RESULT = 45;
  public static  final byte MSG_GET_PORT_OPEN_RESULT = 46;
  public static  final byte MSG_GET_VERSION = 47;
  public static  final byte MSG_GET_VERSION_RESULT = 48;
  public static  final byte MSG_GET_BRICK_STATUSBYTE_RESULT = 49;
  public static  final byte MSG_SET_BRICK_STATUSBYTE_RESULT = 50;
  public static  final byte MSG_GET_BRICK_STATUSBYTE = 51;
  public static  final byte MSG_SET_BRICK_STATUSBYTE = 52;
  public static  final byte MSG_GET_OPERATING_MODE = 53;
  public static  final byte MSG_SET_OPERATING_MODE = 54;
  public static  final byte MSG_OPERATING_MODE_RESULT = 55;
  public static  final byte MSG_GET_CONNECTION_STATUS = 56;
  public static  final byte MSG_CONNECTION_STATUS_RESULT = 57;
  public static  final byte MSG_GOTO_DFU_MODE = 58;
  public static  final byte MSG_ANY = -1;
 
  public static final byte BT_PENDING_INPUT = 1;
  public static final byte BT_PENDING_OUTPUT = 2;

    public static final String PIN = "lejos.bluetooth_pin";
    static final int BUFSZ = 256;

  private static final byte CHANCNT = 4;
  private static final byte RS_INIT = -1;
  private static final byte RS_IDLE = 0;
  private static final byte RS_CMD = 1;
  private static final byte RS_WAIT = 2;
  private static final byte RS_REPLY = 3;
  private static final byte RS_REQUESTCONNECT = 4;
  private static final byte RS_ERROR = 5;
 
  private static final byte IO_TIME = 100;
  private static final byte CMD_TIME = 50;
 
  private static final short TO_SWITCH = 500;
  private static final short TO_REPLY = 250;
    private static final short TO_CHECK = 500;
  private static final short TO_SHORT = 2000;
  private static final short TO_LONG = 30000;
  private static final short TO_RESET = 5000;
  private static final byte TO_CLOSE = 100;
  private static final byte TO_FORCERESET = -1;
  private static final byte TO_NONE = 0;
  private static final short TO_FLUSH = 500;
  private static final byte TO_SWITCH_WAIT = 75;
 
  private static final byte CN_NONE = -1;
  private static final int CN_IDLE = 0x7ffffff;
   
    private static final byte IS_IDLE = 0;
    private static final byte IS_RUNNING = 1;
    private static final byte IS_COMPLETE = 2;
    private static final byte IS_CANCELED = 3;

   
  static BTConnection [] Chans = new BTConnection[CHANCNT];
  static byte [] cmdBuf = new byte[128];
  static byte [] replyBuf = new byte[256];
  static int cmdTimeout;
  static int reqState = RS_INIT;
  static int savedState;
  static boolean listening = false;
  static int connected;
  static int resetCnt;
  static boolean powerOn;
  static boolean publicPowerOn = false;
  private static byte [] pin;
  static final Object sync = new Object();
  static String cachedName;
  static String cachedAddress;
    static byte inquiryState = IS_IDLE;
  static boolean notifyToken = false;
   
  /**
   * Low-level method to write BT data
   *
   * @param buf the buffer to send
   * @param off the offset to start the write from.
   * @param len the number of bytes to send
   * @return number of bytes actually written
   */
  public static native int btWrite(byte[] buf, int off, int len);
  /**
   * Low-level method to read BT data
   *
   * @param buf the buffer to read data into
   * @param off the offset at which to start the transfer
   * @param len the number of bytes to read
   * @return number of bytes actually read
   */
  public static native int btRead(byte[] buf, int off, int len)
  /**
   *Low-Level method to access the Bluetooth interface. Bitwise values returned.
   * @return    0 No data pending
   *        0x1 input pending
   *        0x2 output pending
   */
  public static native int btPending();
  /**
   * Low-level method to switch BC4 chip between command
   * and data (stream) mode.
   *
   * @param mode 0=data mode, 1=command mode
   */
  public static native void btSetArmCmdMode(int mode);
 
  /**
   * Low-level method to get the BC4 chip mode
     *
     * @return the current mode
     */
  public static native int btGetBC4CmdMode();
 
  /**
   * Low-level method to start ADC converter
   *
   */
  public static native void btStartADConverter();

  /**
   * Low-level method to take the BC4 reset line low
   */
  public static native void btSetResetLow();
 
  /**
   * Low-level method to take the BC4 reset line high
   */
  public static native void btSetResetHigh();
 
 
  /**
   * Low-level method to send a BT command or data
   *
   * @param buf the buffer to send
   * @param len the number of bytes to send
   */
  public static native void btSend(byte[] buf, int len);

  /**
   * Low-level method to receive BT replies or data
   *
   * @param buf the buffer to receive data in
   */
  public static native void btReceive(byte[] buf);

    public static native void btEnable();

    public static native void btDisable();

  /**
   * Prevent users from instantiating this (all static members).
   */
  private Bluetooth()
  {
  }
 
  private static void cmdInit(int cmd, int len, int param1, int param2)
  {
    // Helper function. Setup a simple command in the buffer ready to go.
    cmdBuf[0] = (byte)len;
    cmdBuf[1] = (byte)cmd;
    cmdBuf[2] = (byte)param1;
    cmdBuf[3] = (byte)param2;
  }
 
  private static void startTimeout(int period)
  {
    // Start a command timeout
    cmdTimeout = (int)System.currentTimeMillis() + period;
  }
 
  private static boolean checkTimeout()
  {
    return (cmdTimeout > 0 && (int)System.currentTimeMillis() > cmdTimeout);
  }
 
  private static void cancelTimeout()
  {
    cmdTimeout = -1;
  }
 
  /**
   * The main Bluetooth control thread. This controls access to the Bluetooth
   * interface. It controls and performs all low level access to the device.
   * Switches it between data channels and command streams as required.
   */
  static class BTThread extends Thread
  {
    static final int MO_STREAM = 0;
    static final int MO_CMD = 1;
    static final int MO_UNKNOWN = -1;
    private int mode;
    private int curChan;

    public BTThread()
    {
      mode = MO_CMD;
      reqState = RS_INIT;
      curChan = CN_NONE;
      resetCnt = 0;
            btEnable();
      // Make sure power is on(may cause a reset!)
      btSetResetHigh();
      for(int i = 0; i < CHANCNT; i++)
        Chans[i] = new BTConnection(i);
      connected = 0;
      listening = false;
      cancelTimeout();
            // Load the pin etc.
            loadSettings();
      setDaemon(true);
            start();
            //powerOn = true;
            //Bluetooth.reset();
      // Setup initial state
      powerOn = false;
      setPower(true);
      setOperatingMode((byte)1);
      closePort();
      cachedName = getFriendlyName();
      cachedAddress = getLocalAddress();
    }

   
    private void sendCommand()
    {
      // Command should be all setup and ready to go in cmdBuf
      int checkSum = 0;
      int len = (int)cmdBuf[0] & 0xff;
      //1 RConsole.print("send cmd " + (int)cmdBuf[1] + "\n");
      for(int i=0;i<len;i++)
      {
        checkSum += cmdBuf[i+1];
      }
      checkSum = -checkSum;
      cmdBuf[len+1] = (byte) ((checkSum >> 8) & 0xff);
      cmdBuf[len+2] = (byte) checkSum;
      cmdBuf[0] = (byte)(len + 2);
      btWrite(cmdBuf,0, len+3);
    }

    private int recvReply()
    {
      // Read a reply and place it in replyBuf
      if (checkTimeout()) return -1;
      int cnt = Bluetooth.btRead(replyBuf, 0, 1);
      if (cnt <= 0) return 0;
      int len = (int)replyBuf[0] & 0xff;
      if (len < 3 ||len >= replyBuf.length)
      {
        //1 RConsole.print("Bad packet len " + len + " cnt " + cnt + "\n");
        return -1;
      }
      int timeout = (int)System.currentTimeMillis() + TO_REPLY;
      while (cnt < len+1)
      {
        cnt += Bluetooth.btRead(replyBuf, cnt, len + 1 - cnt);
        if ((int)System.currentTimeMillis() > timeout)
        {
          //1 RConsole.print("recvReply timeout\n");
          return -1;
        }
      }
     
      int csum = len; 
      len -= 2;
      for(int i = 0; i < len; i++)
        csum += (int)replyBuf[i+1] & 0xff;
      csum = -csum;
      //1 RConsole.print("Got reply " + replyBuf[1] + "\n");
      if (((byte) csum == replyBuf[len+2]) && ((byte)(csum >> 8) == replyBuf[len+1]))
        return len;
      else
      {
        //1 RConsole.print("Bad csum\n");
        return -1;
      }
    }
 
    /**
     * Perform a hardware reset of the BlueCore chip.
     *
     */ 
    private void reset()
    {

      synchronized(Bluetooth.sync)
      {
        int len;
        //RConsole.print("hardware reset\n");
        for(int resetCnt = 0; resetCnt < 2; resetCnt++)
        {
          // Ditch any pending data in the input buffer
          startTimeout(TO_FLUSH);
          while (!checkTimeout())
          {
            recvReply();
          }
          // RConsole.print("End of flush\n");
          // BC4 reset seq. First take the reset line low...
          btSetArmCmdMode(MO_CMD);
          btSetResetLow();
          // Keep it that way for 100ms and discard any input
          startTimeout(100);
          while (!checkTimeout())
          {
            recvReply();
          }
          // Now bring it high
          btSetResetHigh();
          // Now wait for either 5 seconds or for a RESET_INDICATION
          startTimeout(TO_RESET);
          while ((len = recvReply()) == 0 || ( len > 0 && replyBuf[1] != MSG_RESET_INDICATION))
              Thread.yield();
                    //1 if (len < 0) RConsole.print("Reset timed out");
          // Check things are working
          //1 RConsole.print("Send mode cmd\n");
          cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0);
          sendCommand();
          startTimeout(TO_SHORT);
          while ((len = recvReply()) == 0 || (len > 0 && replyBuf[1] != MSG_OPERATING_MODE_RESULT))
              Thread.yield();
          //1 if (len < 0) RConsole.print("mode had timed out\n");
          // if we got the response without a timeout we are done!
          if (len > 0) break;
        }
        // We are now in command mode
        mode = MO_CMD;
        // Now reset everything else that is going on gulp!
        for(int i = 0; i < CHANCNT; i++)
          Chans[i].reset();
        //1 RConsole.print("reset complete state is " + reqState + "\n");
        listening = false;
        connected = 0;
        curChan = CN_NONE;
        cancelTimeout();
        // Tell anyone that is waiting
        if (reqState > RS_IDLEreqState = RS_ERROR;
        Bluetooth.sync.notifyAll();
        resetCnt++;

      }
    }
   
    private void processReply()
    {
      // Read and process and command replies from the bc4
      // If the reply buffer is free, look to see if we have a new reply
      synchronized(Bluetooth.sync)
      {
        //RConsole.print("processn reply " + reqState + "\n");
        int len;
        while (reqState < RS_REPLY && (len = recvReply()) != 0)
        {
          //RConsole.print("process request\n");
          // Got a message. We only deal with the messages we have to deal
          // with here. In general we allow the calling thread to decide
          // what to do (this includes ignoring the message!).
          //RConsole.print("got request " + (int)replyBuf[1] + " state " + reqState + "\n");
          if (len < 0 || replyBuf[1] == MSG_RESET_INDICATION)
          {
            //1 RConsole.print("Got reply error\n");
            reset();
            break;
          }
          else if (replyBuf[1] == MSG_CLOSE_CONNECTION_RESULT)
          {
            if (replyBuf[3] >= 0 && replyBuf[3] < Chans.length)
            {
              if (Chans[replyBuf[3]].disconnected())
                connected--;
              if (replyBuf[3] == (byte)curChan) curChan = CN_NONE;
            }
          }
          else if (replyBuf[1] == MSG_REQUEST_CONNECTION)
          {
            if (listening)
            {
              // Push the current state
              savedState = reqState;
              reqState = RS_REQUESTCONNECT;
            }
            else
            {
              // No one wants to know so reject it.
              // Note: Doing this seems to cause a device reset!
              cmdInit(MSG_ACCEPT_CONNECTION, 2, 0, 0);
              sendCommand();
              continue;
            }
          }
          else if (replyBuf[1] == MSG_REQUEST_PIN_CODE)
          {
            // If we have no pin then nothing to do
            if (pin == null) continue;
            // Otherwise send the pin as requested
            cmdInit(MSG_PIN_CODE, 24, 0, 0);
            System.arraycopy(replyBuf, 2, cmdBuf, 2, 7);
            for(int i = 0; i < 16; i++)
              cmdBuf[i + 9] = (i < pin.length ? pin[i] : 0);
            sendCommand();
            continue;
          } else if (replyBuf[1] == MSG_PIN_CODE_ACK)
            continue;
          // All other messages give the caller it to deal with
          if (reqState == RS_WAIT)
            reqState = RS_REPLY;
          if (reqState >= RS_REPLY)
            Bluetooth.sync.notifyAll();
        }
      }
      //RConsole.print("process reply end\n");
    }
   
    private void processCommands()
    {
      // Process commands. Return when we should consider switching to
      // stream mode.
      //RConsole.print("Process cmd1\n");
      switchToCmd();
      int cmdEnd = (int)System.currentTimeMillis() + CMD_TIME;
      while (cmdEnd > (int)System.currentTimeMillis() || reqState > RS_IDLE)
      {
        //RConsole.print("ProcessCommands state " + reqState + "\n");
        synchronized(Bluetooth.sync)
        {
          if (reqState == RS_CMD)
          {
            // Have a command ready to go so send it
            sendCommand();
            reqState = RS_WAIT;
          }
          processReply();
        }
        Thread.yield();
      }
      //RConsole.print("Process cmd end\n");
    }
   
    private int selectChan()
    {
      // Select the next channel to be processed
      if (connected == 0) return -1;
      int i;
      int cur = curChan;
      for(i = 0; i < Chans.length; i++)
      {
        cur = (cur + 1) % Chans.length;
        if (Chans[cur].needsAttention())
        {
          // if (cur != curChan) RConsole.print("Selected " + cur + "\n");
          return cur;
        }
      }
      // No active channel found say we are idle
      return CN_IDLE;
    }
   
    private void processStreams()
    {
      // Process the streams. Return when we should switch to command mode
      // RConsole.print("PS cur " + curChan + " state " + reqState + "\n");
      while (true)
      {
        synchronized(Bluetooth.sync)
        {
          //RConsole.print("Process streams " + reqState + "\n");
          if (reqState != RS_IDLE) return;
          int next = selectChan();
          if (next < 0) return;
          if (next != CN_IDLE)
          {
            if (!switchToStream(next)) return;
            // Perform I/O on the current stream. Switching from one stream
            // to another is a slow process, so we spend at least IO_TIME ms
            // on each stream before switching away.
            //RConsole.print("Process streams 2" + reqState + "\n");
            int ioEnd = (int)System.currentTimeMillis() + IO_TIME;     
            while (ioEnd > (int)System.currentTimeMillis() && Chans[curChan].state >= BTConnection.CS_CONNECTED)
            {
              if (bc4Mode() != MO_STREAM) return;
              Chans[curChan].send();
              Chans[curChan].recv();
              Thread.yield();
            }
          }
          else
          {
            //1 RConsole.print("Stream idle\n");
            if (bc4Mode() != mode) return;
          }
          //RConsole.print("Process streams 3" + reqState + "\n");
          // Do we need to switch back to command mode?
          if (listening) return;
        }
        Thread.yield();
      }
    }
   
    private int waitSwitch(int target, boolean flush)
    {
      // Wait for the BC4 to switch to mode, or timeout...
      int timeout = (int) System.currentTimeMillis() + TO_SWITCH;
      while (timeout > (int)System.currentTimeMillis())
      {
        if (bc4Mode() == target) return target;
        // Need to flush input when switching to command mode
        if (flush && curChan >= 0) Chans[curChan].flushInput();
      }
      //RConsole.print("Failed to switch\n");
      mode = MO_UNKNOWN;
      curChan = CN_NONE;
      return bc4Mode();
    }
   
    private boolean switchToStream(int chan)
    {
      // Decide which (if any) stream to switch to
      if (mode == MO_STREAM && chan == curChan) return true;
      switchToCmd();
      //RConsole.print("Switch to chan " + chan + " handle " + Chans[chan].handle + "\n");
      cmdInit(MSG_OPEN_STREAM, 2, Chans[chan].handle, 0);
      sendCommand();
      // Now wait for the BC4 to switch
      if (waitSwitch(MO_STREAM, false) != MO_STREAM) return false;
      //RConsole.print("In stream mode\n");
      // Make sure we process any remaining command input that may have arrived
      processReply();
      // Finally switch the ARM over
      btSetArmCmdMode(MO_STREAM);
      mode = MO_STREAM;
      curChan = chan;
      return true;
    }
   
    private void switchToCmd()
    {
      // Need to switch back into command mode
      // First step send any pending data
      if (mode == MO_CMD) return;
            //RConsole.println("switch to cmd " + btPending() + " " + bc4Mode());
      if (mode == MO_STREAM && bc4Mode() == MO_CMD && curChan >= 0)
      {
        //1 RConsole.print("Trying early flush\n");
        Chans[curChan].flushInput();
      }
      // wait for any output to drain. If this times out we are probably
            // in big trouble and heading for a reset.
      int timeout = (int) System.currentTimeMillis() + TO_FLUSH;
      while(((btPending() & BT_PENDING_OUTPUT) != 0) &&
                    (timeout > (int)System.currentTimeMillis()))
        Thread.yield();
            //RConsole.println("Flush complete " + btPending());
            if ((btPending() & BT_PENDING_OUTPUT) != 0)
            {
                // Failed to flush, reset and give up.
                reset();
                return;
            }
      // Need to have a minimum period of no output to the BC4
      try{Thread.sleep(TO_SWITCH_WAIT);}catch(Exception e){}
      btSetArmCmdMode(MO_CMD);
      // If there is any input data left we could be in trouble. Try and
      // flush everything.
      if (waitSwitch(MO_CMD, true) != MO_CMD)
      {
        //RConsole.print("Failed to switch to cmd\n");
        reset();
        return;
      }
      mode = MO_CMD;
    }
   
    private int bc4Mode()
    {
      // return the current mode of the BC4 chip
      int ret = btGetBC4CmdMode();
      // > 512 indicates a high logic level which is mode 0!
      if (ret > 512)
        return MO_STREAM;
      else
        return MO_CMD;
    }
   
    private void waitInit()
    {
      synchronized (Bluetooth.sync)
      {
        reqState = RS_INIT;
        processCommands();
        reqState = RS_IDLE;
        Bluetooth.sync.notifyAll();
      }
    }
   
    public void run()
    {
      //1 RConsole.print("Thread running\n");
      waitInit();
      //1 RConsole.print("Init complete\n");
      while(true)
      {
        processCommands();
        processStreams();
        Thread.yield();
      }
    }
  }

  // Create the Bluetooth device thread.
  private static BTThread btThread = new BTThread();
 
  static private int waitState(int target)
  {
    // RConsole.print("Wait state " + target + "\n");
    synchronized (Bluetooth.sync) {
      // Wait for the system to enter the specified state (or timeout)
      while (reqState != target && reqState != RS_ERROR)
        try{Bluetooth.sync.wait();}catch(Exception e){}
      if (reqState == RS_ERROR)
        return -1;
      else
        return 0;
    }
  }
 
  private static void cmdStart()
  {
    // Wait for the system to be idle. Ignore timeout errors.
    while (waitState(RS_IDLE) < 0)
      try{Bluetooth.sync.wait();}catch(Exception e){}
  }
 
  private static void cmdComplete()
  {
    // command now complete. Reset to idle (clears any timeout state)
    synchronized(Bluetooth.sync)
    {
      reqState = RS_IDLE;
      cancelTimeout();
      Bluetooth.sync.notifyAll();
    }
  }
 
  private static int cmdWait(int state, int waitState, int msg, int timeout)
  {
    //1 RConsole.print("Cmd wait\n");
    synchronized (Bluetooth.sync)
    {
      // Check we have power if not fail the request
      if (!powerOn) return -1;
      if (waitState > 0) reqState = waitState;
      if (timeout != TO_NONE) startTimeout(timeout);
      while (true)
      {
        if (waitState(state) < 0) return -1;
        if (msg == MSG_ANY || replyBuf[1] == msg) return 0;
        // Ignore any unwanted message
        if (reqState == RS_REPLY) reqState = RS_WAIT;
      }
    }
  }

  /**
   * Set the pin to be used for pairing/connecting to the system
   *
   * @param newPin the new pin code
   *
   */
  public static void setPin(byte[] newPin)
  {
    pin = newPin;
  }

  /**
   * Return the pin to be used for pairing/connecting to the system
   *
   * @return The current pin code
   *
   */ 
  public static byte[] getPin()
  {
    return pin;
  }

 
  /**
   * Close an open connection
   *
   * @param handle the handle for the connection
   * @return the status 0 = success
   */
  public static int closeConnection(byte handle)
  {
    int ret = -1;
    //1 RConsole.print("Close connection state " + reqState + "\n");
    synchronized (Bluetooth.sync)
    {
      cmdStart();
      // We can have a race condition when both ends of the connection
      // close at the same time. This can mean we try and close an already
      // closed connection. If we do this the BC4 goes into reset mode.
      // To try and avoid this happening we insert a different length
      // delay between outbound and inbound streams. We then wait to see
      // if the other end has already closed things...
      int timeout = (handle == 3 ? 5*TO_CLOSE : TO_CLOSE);
      reqState = RS_WAIT;
      try{Bluetooth.sync.wait(timeout);}catch(Exception e){}
      reqState = RS_IDLE;
      // There is a small chance that we may have had a reset so make sure
      // that we still have an open channel to close!
      byte [] status = getConnectionStatus();
      //1 RConsole.print("Conn status " + status[handle]);
      if (status == null || status[handle] != 2) return -1;
      if (Chans[handle].state != BTConnection.CS_DISCONNECTING) return -1;
      cmdInit(MSG_CLOSE_CONNECTION, 2, handle, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_CONNECTION_RESULT, TO_SHORT) >= 0)
        ret = (int)replyBuf[2];
      do {
        // We may have a race condition here, or have triggered a reset
        // wait for things to settle
        reqState = RS_WAIT;
        try{Bluetooth.sync.wait(TO_REPLY);}catch(Exception e){}
        reqState = RS_IDLE;
      } while (getConnectionStatus() == null);
      cmdComplete();
      return ret;
    }
  }

  /**
   * Opens the  port to allow incoming connections.
   *
   * @return an array of three bytes: success, handle, ps_success
   */
  public static byte[] openPort() {
    byte[] result = new byte[3];
    synchronized (Bluetooth.sync)
    {
      cmdStart();
      cmdInit(MSG_OPEN_PORT, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_PORT_OPEN_RESULT, TO_SHORT) < 0)
        result = null;
      else
        System.arraycopy(replyBuf, 2, result, 0, 3);
      //1 RConsole.print("Port open handle " + (int)replyBuf[3] + " status " + (int)replyBuf[2] + "\n");
      cmdComplete();
      return result;
    }
  }

 
  /**
   * Closes the  port to disallow incoming connections.
   *
   * @return an array of two bytes: success, ps_success
   */
  public static byte [] closePort() {
    byte [] result = new byte[2];
    //1 RConsole.print("Close port\n");
    synchronized (Bluetooth.sync)
    {
      cmdStart();
      // The Lego doc says the handle should always be 3!
      cmdInit(MSG_CLOSE_PORT, 2, 3, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_PORT_RESULT, TO_SHORT) < 0)
        result = null;
      else
        System.arraycopy(replyBuf, 2, result, 0, 2);
      cmdComplete();
      return result;
    }
  }

  /**
   * Wait for a remote device to connect.
   * @param timeout time in ms to wait for connection, 0 == wait for ever
     * @param mode the I/O mode to be used for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code>
   * @param pin the pin to use, null use current default
   * @return a BTConnection
   */
  public static BTConnection waitForConnection(int timeout, int mode, byte[] pin)
  {
    //1 RConsole.print("waitForConnection\n");
    synchronized (Bluetooth.sync)
    {
      BTConnection ret = null;
      // only one listener at once
      if (listening) return null;
      // First open up the listening port
      byte [] port = openPort();
      if (port == null || port[0] != 1 || port[1] >=  Chans.length || port[1] < 0)
      {
        //1 RConsole.print("Failed to open port\n");
        return null;
      }
      // Now in listening mode
      listening = true;
      byte []savedPin = null;
            if (pin != null)
            {
                savedPin = getPin();
                setPin(pin);
            }
            if (timeout == 0) timeout = 0x7fffffff;
      // Wait for special connect indication
      while (listening && reqState != RS_REQUESTCONNECT)
            {
        try{
                    Bluetooth.sync.wait(timeout < 1000 ? timeout : 1000);
                } catch(InterruptedException e){listening=false;}
                timeout -= 1000;
                if (timeout <= 0) listening = false;
            }
      if (listening)
      {
        //1 RConsole.print("Got connect request\n");
       
        // !Kludge Alert! Extract address here:
        byte [] addr = new byte[ADDRESS_LEN];
                System.arraycopy(replyBuf, 2, addr, 0, ADDRESS_LEN);
        //RConsole.print("waitForConnect() Address: " + Bluetooth.addressToString(addr) + "\n");
       
        // Restore state
        reqState = savedState;
        // and wait until we have control
        cmdStart();
        // Acknowledge the request
        cmdInit(MSG_ACCEPT_CONNECTION, 2, 1, 0);
        if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0)
        {
          //1 RConsole.print("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n");
          if (replyBuf[2] == 1)
          {
            byte handle = replyBuf[3];
            // Got a connection
            if (handle >= 0 && handle < Chans.length)
            {
              // Assert(Chans[handle].state == CS_IDLE);
              Chans[handle].bind(handle, addressToString(addr), mode);
              // now have one more connected
              connected++;
              ret = Chans[handle];
            }
          }
        }
        listening = false;
        cmdComplete();

      }
      if (savedPin != null) setPin(savedPin);
      closePort();
      return ret;
    }
  }
 
  /**
   * Uses the current default PIN
   * @return the Bluetooth connection
   */
  public static BTConnection waitForConnection()
  {
    return waitForConnection(0, 0, null);
  }

  /**
   * Uses the current default PIN
     * @param timeout time in ms to wait for connection, 0 == wait for ever
     * @param mode the I/O mode to be used for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code>
     * @return the Bluetooth connection
   */
  public static BTConnection waitForConnection(int timeout, int mode)
  {
    return waitForConnection(timeout, mode, null);
  }

  /**
   * Connects to a remote device. Uses the current default pin.
   *
   * @param remoteDevice remote device
   * @return BTConnection Object or null
   */
  public static BTConnection connect(RemoteDevice remoteDevice) {
    if (remoteDevice == null) return null;
    return connect(stringToAddress(remoteDevice.getDeviceAddr()));
  }

    /**
     * Connect to the specified device, either by name or address
     * @param target String name or address
     * @param mode I/O mode for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code>
     * @param pin The pin to use for this connection
     * @return BTConnection object or null
     */
    public static BTConnection connect(String target, int mode, byte[] pin)
    {
        if (target == null) return null;
        if (isAddress(target))
            return connect(stringToAddress(target), mode, pin);
        else
        {
            // We have a device name. Try and locate it in the list of known
            // devices
            RemoteDevice dev = getKnownDevice(target);
            if (dev != null)
                // Found a match connect to it
                return connect(stringToAddress(dev.getBluetoothAddress()), mode, pin);
            return null;
        }
    }

    /**
     * Connect to the specified device, either by name or address
     * @param target String name or address
     * @param mode I/O mode for this connection. <code>NXTConnection.RAW, .LCP, or .PACKET</code>
     * @return BTConnection object or null
     */
    public static BTConnection connect(String target, int mode)
    {
        return connect(target, mode, null);
    }


  /**
   * Connects to a Device by it's Byte-Device-Address Array
   * Uses the current default pin
   *
   * @param device_addr byte-Array with device-Address
   * @return BTConnection Object or null
   */
  private static BTConnection connect(byte[] device_addr) {
    return connect(device_addr, 0, null);
  }
 
  /**
   * Connects to a Device by it's Byte-Device-Address Array
   *
   * @param device_addr byte-Array with device-Address
   * @param mode The data mode. Either PACKET, LCP, or RAW found in <code>NXTConnection</code>
   * @param pin the pin to use. Must be ASCII code, so '0' = 48
   * @return BTConnection Object or null
   */
  private static BTConnection connect(byte[] device_addr, int mode, byte[] pin) {
   
    //1 RConsole.print("Connect\n");
    synchronized(Bluetooth.sync)
    {
      BTConnection ret = null;
      byte[] savedPin = null;
            if (pin != null)
            {
                savedPin = getPin();
                setPin(pin);
            }
      cmdStart();
      cmdInit(MSG_CONNECT, 8, 0, 0);
      System.arraycopy(device_addr, 0, cmdBuf, 2, ADDRESS_LEN);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0)
      {
        //1 RConsole.print("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n");
        if (replyBuf[2] != 0)
        {
          byte handle = replyBuf[3];
          // Now need to check that the connection is not closed imm
          reqState = RS_WAIT;
          try{Bluetooth.sync.wait(300);}catch(Exception e){}
          if (reqState == RS_WAIT && handle >= 0 && handle < Chans.length)
          {
            // Got a connection
            Chans[handle].bind(handle, addressToString(device_addr), mode);
            // now have one more connected
            connected++;
            ret = Chans[handle];
          }
        }
      }
      cmdComplete();
      if (savedPin != null) setPin(savedPin);
      return ret;
    }
  }

 
  /**
   * Get the Bluetooth signal strength (link quality)
   * Higher values mean stronger signal.
   *
     * @param handle The handle/channel of the connection
     * @return link quality value 0 to 255.
   *
   */
  public static int getSignalStrength(byte handle) {
    //1 RConsole.print("getSignalStrength\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      if (Chans[handle].state != BTConnection.CS_CONNECTED) return -1;
      cmdInit(MSG_GET_LINK_QUALITY, 2, handle, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_LINK_QUALITY_RESULT, TO_SHORT) >= 0
        ret = replyBuf[2] & 0xff;
      cmdComplete();
      return ret;
    }
  }
 
 
  /**
   * Get the friendly name of the local device
   * @return the friendly name
   */
  public static String getFriendlyName() {
    byte[] result = new byte[NAME_LEN];
    //1 RConsole.print("getFriendlyName\n");
    synchronized (Bluetooth.sync)
    {
            // If we have one return the cached address, saves switching into
            // command mode, or powering up the device...
      if (cachedName != null) return cachedName;
      cmdStart();
      cmdInit(MSG_GET_FRIENDLY_NAME, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_FRIENDLY_NAME_RESULT, TO_SHORT) < 0
        result = null;
      else
        System.arraycopy(replyBuf, 2, result, 0, NAME_LEN);
      cmdComplete();
            if (result != null) cachedName = nameToString(result);
      return cachedName;
    }
  }
   
  /**
   * Set the name of the local device
     * @param strName the friendly name for the device
     * @return true if ok false if there is an error
   */
  public static boolean setFriendlyName(String strName) {
    //1 RConsole.print("setFriendlyName\n");
    synchronized (Bluetooth.sync)
    {
      boolean ret=false;
      cmdStart();
      cmdInit(MSG_SET_FRIENDLY_NAME, 17, 0, 0);
            byte[] name = stringToName(strName);
      System.arraycopy(name, 0, cmdBuf, 2, NAME_LEN);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FRIENDLY_NAME_ACK, TO_LONG) >= 0
        ret = true;
      cmdComplete();
            if (ret) cachedName = nameToString(name);
      return ret;
   
  }
   
  /**
   * get the Bluetooth address of the local device
   * @return the local address
   */
  public static String getLocalAddress() {
    byte[] result = new byte[ADDRESS_LEN];
    //1 RConsole.print("getLocalAddress\n");
    synchronized (Bluetooth.sync)
    {
            // If we have one return the cached address... Saves switching
            // into command mode.
            if (cachedAddress != null) return cachedAddress;
      cmdStart();
      cmdInit(MSG_GET_LOCAL_ADDR, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_LOCAL_ADDR_RESULT, TO_SHORT) < 0
        result = null;
      else
        System.arraycopy(replyBuf, 2, result, 0, ADDRESS_LEN);
      cmdComplete();
      if (result != null) cachedAddress = addressToString(result);
            return cachedAddress;
    }
  } 
 
 
  /**
   * The internal Chip has a list of already paired Devices. This Method returns a
   * Vector-List which contains all the known Devices on the List. These need not be reachable.
   * To connect to a "not-known"-Device, you should use the Inquiry-Process.
   * The pairing-Process can also be done with the original Lego-Firmware. The List of known
   * devices will not get lost, when installing the LeJOS Firmware.
   *
   * @return Vector with List of known Devices
   */
  public static Vector getKnownDevicesList() {
    //1 RConsole.print("getKnownDevicesList\n");
    synchronized(Bluetooth.sync)
    {
      int state = RS_CMD;
      byte[] device = new byte[ADDRESS_LEN];
      byte[] devclass = new byte[4];
      byte[] name = new byte[NAME_LEN];
      Vector retVec = new Vector(1);
      RemoteDevice curDevice;
      cmdStart();
      cmdInit(MSG_DUMP_LIST, 1, 0, 0);
      while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
      {
        state = RS_WAIT;
        if (replyBuf[1] == MSG_LIST_ITEM)
        {
          System.arraycopy(replyBuf, 2, device, 0, ADDRESS_LEN);
                    System.arraycopy(replyBuf, 9, name, 0, NAME_LEN);
          System.arraycopy(replyBuf, 25, devclass, 0, 4);
          curDevice = new RemoteDevice(nameToString(name), addressToString(device), devclass);
          //1 RConsole.print("got name " + curDevice.getFriendlyName() + "\n");
          retVec.addElement(curDevice);
        }
        else if (replyBuf[1] == MSG_LIST_DUMP_STOPPED)
          break;
      }
      cmdComplete();
      return retVec;
    }
  }
 
  /**
   * Gets a Device of the BC4-Chips internal list of known Devices
   * (those who have been paired before) into the BTDevice Object.
   * @param fName Friendly-Name of the device
   * @return BTDevice Object or null, if not found.
   */
  public static RemoteDevice getKnownDevice(String fName) {
    RemoteDevice btd = null;
    //look the name up in List of Known Devices
    Vector devList = getKnownDevicesList();
    if (devList.size() > 0) {
      for (int i = 0; i < devList.size(); i++) {
        btd = (RemoteDevice) devList.elementAt(i);
        if (btd.getFriendlyName(false).equals(fName)) {
          return btd;
        }
      }
    }
    return null;
  }

  /**
   * Add device to known devices
   * @param d Remote Device
   * @return true if add was successful
   */
  public static boolean addDevice(RemoteDevice d) {
    String addr = d.getDeviceAddr();
    String name = d.getFriendlyName(false);
    byte[] cod = d.getDeviceClass();
    //1 RConsole.print("addDevice " + name + "\n");
    synchronized (Bluetooth.sync)
    {
      boolean ret=false;
      cmdStart();
      cmdInit(MSG_ADD_DEVICE, 28, 0, 0);
      System.arraycopy(stringToAddress(addr), 0, cmdBuf, 2, ADDRESS_LEN);
      System.arraycopy(cod, 0, cmdBuf, 25, 4);
            System.arraycopy(stringToName(name), 0, cmdBuf, 9, NAME_LEN);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0
        ret = replyBuf[2] == 0x50;
      cmdComplete();
      return ret;
    }   
  }
 
  /**
   * Remove device from known devices
   * @param d Remote Device
   * @return true if remove was successful
   */
  public static boolean removeDevice(RemoteDevice d) {
    String addr = d.getDeviceAddr();
    synchronized (Bluetooth.sync)
    {
      boolean ret = false;
      cmdStart();
      cmdInit(MSG_REMOVE_DEVICE, 8, 0, 0);
      System.arraycopy(stringToAddress(addr), 0, cmdBuf, 2, ADDRESS_LEN);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0
        ret = replyBuf[2] == 0x53;
      cmdComplete();
      return ret;
   
  }

    /**
     * Cancel a Bluetooth inquiry process that has been started using startInquire
     * @return true if the request is cancelled false if there is an error.
     */
  public static boolean cancelInquiry() {
    synchronized (Bluetooth.sync)
    {
            // Are we running an inquiry?
            if (inquiryState != IS_RUNNING) return false;
            // Make sure we are in a "safe" state to issue the cancel
            while (reqState != RS_WAIT && inquiryState == IS_RUNNING)
                try {Bluetooth.sync.wait(1);}catch(Exception e){}
            // Make sure we are still doing the inquiry
            if (inquiryState != IS_RUNNING) return false;
            // Issue the cancel command and return, the state will be reset to
            // RS_WAIT once the command has been sent.
            cmdInit(MSG_CANCEL_INQUIRY, 1, 0, 0);
            reqState = RS_CMD;
            // Mark the inquiry as being aborted
            inquiryState = IS_CANCELED;
            return true;
        }
  }
 
  /**
   * Start a Bluetooth inquiry process and notify listener of each device found. This method
   * is primarily used by DiscoveryAgent.startInquiry() and is run in a separate thread.
   *
   * @param maxDevices the maximum number of devices to discover
     * @param timeout the timeout value in units of 1.28 seconds
     * @param listy The listener to notify
   */
  /*
   * DEV NOTES:
   * This method contains some redundant code in inquire(int, int, byte []).
   * It seemed better to use redundant code rather than try to amalgamate these methods because
   * the menu system does not use any javax.bluetooth classes (other than RemoteDevice) and
   * therefore the linker will cut this method out, resulting in more efficient memory use.
   */
  public static void inquireNotify(int maxDevices,  int timeout, final DiscoveryListener listy) {
    byte [] cod = {0,0,0,0}; // find all devices
    byte[] device = new byte[ADDRESS_LEN];
    byte[] name = new byte[NAME_LEN];
        byte[] retCod = new byte[4];
        synchronized (Bluetooth.sync)
    {
      int state = RS_CMD;
      cmdStart();
      cmdInit(MSG_BEGIN_INQUIRY, 8, maxDevices, 0);
      cmdBuf[4] = (byte)timeout;
      System.arraycopy(cod, 0, cmdBuf, 5, 4)
      inquiryState = IS_RUNNING;
      while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
      {
        state = RS_WAIT;
        if (replyBuf[1] == MSG_INQUIRY_RESULT)
        {
          System.arraycopy(replyBuf, 2, device, 0, ADDRESS_LEN);
                    System.arraycopy(replyBuf, 9, name, 0, NAME_LEN);
          System.arraycopy(replyBuf, 25, retCod, 0, 4);
         
          int codRecord = (retCod[0] << 8) + retCod[1];
          codRecord = (codRecord << 8) + retCod[2];
          codRecord = (codRecord << 8) + retCod[3];
         
          final DeviceClass deviceClass = new DeviceClass(codRecord);
          final RemoteDevice rd = new RemoteDevice(nameToString(name), addressToString(device), retCod);
         
          // Spawn a separate thread to notify in case doesn't return immediately:
          Thread t = new Thread() {
            public void run() {
              // Make sure only one notify thread is running at a time using notifyToken
              while(notifyToken) {Thread.yield();}
              notifyToken = true; // indicate this notify is executing
              if(inquiryState != IS_CANCELED)
                listy.deviceDiscovered(rd, deviceClass);
              notifyToken = false; // when notify returns set back to false
            }
          };
          // TODO: Set Daemon?
          t.setDaemon(true);
          t.start();
         
        }
        else if (replyBuf[1] == MSG_INQUIRY_STOPPED)
        {
                    if (inquiryState == IS_RUNNING) inquiryState = IS_COMPLETE;
                    break;
        }
      }
            // At this stage we have completed the inquiry. inquiryState will
            // tell us how we have completed things...
            // IS_RUNNING: Some sort of error (typically a reset).
            // IS_CANCELED: Inquiry was aborted.
            // IS_COMPLETE: Normal completion.
            // Spawn a separate thread to notify in case doesn't return immediately:
           
      Thread t;
            if (inquiryState == IS_COMPLETE)
                t = new Thread() {
                    public void run() {
                            listy.inquiryCompleted(DiscoveryListener.INQUIRY_COMPLETED);
                    }
                };
            else if (inquiryState == IS_CANCELED)
                t = new Thread() {
                    public void run() {
                            listy.inquiryCompleted(DiscoveryListener.INQUIRY_TERMINATED);
                    }
                };
            else
                t = new Thread() {
                    public void run() {
                            listy.inquiryCompleted(DiscoveryListener.INQUIRY_ERROR);
                    }
                };
            // TODO: Daemon thread?
            t.setDaemon(true);
            t.start()
            inquiryState = IS_IDLE;
      cmdComplete();
           
    }     
  }
 
 
  /**
   * Start a Bluetooth inquiry process
   *
   * @param maxDevices the maximum number of devices to discover
   * @param timeout the timeout value in units of 1.28 seconds
   * @param cod the class of device to look for
   * @return a vector of all the devices found
   */
  public static Vector inquire(int maxDevices,  int timeout, byte[] cod) {
    Vector retVec = new Vector();
    byte[] device = new byte[ADDRESS_LEN];
    byte[] name = new byte[NAME_LEN];
        byte[] retCod = new byte[4];
    synchronized (Bluetooth.sync)
    {
      int state = RS_CMD;
      cmdStart();
      cmdInit(MSG_BEGIN_INQUIRY, 8, maxDevices, 0);
      cmdBuf[4] = (byte)timeout;
      System.arraycopy(cod, 0, cmdBuf, 5, 4)
      while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
      {
        state = RS_WAIT;
        if (replyBuf[1] == MSG_INQUIRY_RESULT)
        {
          System.arraycopy(replyBuf, 2, device, 0, ADDRESS_LEN);
                    System.arraycopy(replyBuf, 9, name, 0, NAME_LEN);
          System.arraycopy(replyBuf, 25, retCod, 0, 4);
          // add the Element to the Vector List
          retVec.addElement(new RemoteDevice(nameToString(name), addressToString(device), retCod));
        }
        else if (replyBuf[1] == MSG_INQUIRY_STOPPED)
        {
          cmdComplete();
          // Fill in the names 
          for (int i = 0; i < retVec.size(); i++) {
            RemoteDevice btrd = ((RemoteDevice) retVec.elementAt(i));
            String s = btrd.getFriendlyName(false);
            if (s.length() == 0) {
              String nm = lookupName(btrd.getDeviceAddr());
              btrd.setFriendlyName(nm);
            }
          }
          return retVec;
        }
      }
      cmdComplete();
      return retVec;
    }     
  }
 
  /**
   * Look up the name of a device using its address
   *
   * @param addr device address
   * @return friendly name of device
   */
  public static String lookupName(String addr) {
    char[] name = new char[NAME_LEN];
    synchronized (Bluetooth.sync)
    {
      String ret = "";
      int state = RS_CMD;
      cmdStart();
      cmdInit(MSG_LOOKUP_NAME, 8, 0, 0);
      System.arraycopy(stringToAddress(addr), 0, cmdBuf, 2, ADDRESS_LEN);
      while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
      {
        state = RS_WAIT;
        if (replyBuf[1] == MSG_LOOKUP_NAME_RESULT)
        {
          int len = 0;
          for(; len < NAME_LEN && replyBuf[len+9] != 0; len++)
            name[len] = (char)replyBuf[len+9];
          ret = new String(name, 0, len);
          break;
        }
        else if (replyBuf[1] == MSG_LOOKUP_NAME_FAILURE)
          break;
       
      }
      cmdComplete();
      return ret;
    }     
  }
   
 
  /**
   * Get the status of all connections
   *
   * @return byte array of status for each handle
   */
  public static byte[] getConnectionStatus() {
    byte[] result = new byte[4];
    //1 RConsole.print("getConnectionStatus\n");
    synchronized (Bluetooth.sync)
    {
      cmdStart();
      cmdInit(MSG_GET_CONNECTION_STATUS, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECTION_STATUS_RESULT, TO_SHORT) < 0
        result = null;
      else
        System.arraycopy(replyBuf, 5, result, 0, 4);
      cmdComplete();
      return result;
    }
  }
 
  /**
   * Get the major and minor version of the BlueCore code
   *
   * @return an array of two bytes: major version, minor version
   */
  public static byte[] getVersion() {
    byte [] version = new byte[2];
    //1 RConsole.print("getVersion\n");
    synchronized (Bluetooth.sync)
    {
      cmdStart();
      cmdInit(MSG_GET_VERSION, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_VERSION_RESULT, TO_SHORT) < 0
        version = null;
      else
        System.arraycopy(replyBuf, 2, version, 0, 2);
      cmdComplete();
      return version;
    }
  }
 
  /**
   * Get the persistent status value from the BC4 chip
   *
   * @return the byte value
   */
  public static int getStatus() {
    //1 RConsole.print("getStatus\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_GET_BRICK_STATUSBYTE, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0
        ret = ((int)replyBuf[2] & 0xff) | (((int)replyBuf[3] & 0xff) << 8);
      cmdComplete();
      return ret;
    }
  }

  /**
   * Set the persistent status byte for the BC4 chip
   *
   * @param status the byte status value
   * @return < 0 Error
   */
  public static int setStatus(int status) {
    //1 RConsole.print("setStatus\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_SET_BRICK_STATUSBYTE, 3, status & 0xff, (status >> 8) & 0xff);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0
        ret = 0;
      cmdComplete();
      return ret;
    }
  }
 
  /**
   * Get the visibility (discoverable) status of the device
   *
   * @return 1 = visible, 0 = invisible
   */
  public static int getVisibility() {
    //1 RConsole.print("getVisibility\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_GET_DISCOVERABLE, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_DISCOVERABLE_RESULT, TO_SHORT) >= 0
        ret = replyBuf[2];
      cmdComplete();
      return ret;
    }
  }
 
  /**
   * Get the port open status,
   * i.e whether connections are being accepted
   *
   * @return 1 if the port is open, 0 otherwise
   */
  public static int getPortOpen() {
    //1 RConsole.print("getPortOpen\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_GET_PORT_OPEN, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_PORT_OPEN_RESULT, TO_SHORT) >= 0
        ret = replyBuf[2];
      cmdComplete();
      return ret;
   
  }
 
  /**
   * Get the operating mode (stream breaking or not)
   *
   * @return 0 = stream breaking mode, 1 = don't break stream mode
   *       < 0 Error
   */
  public static int getOperatingMode() {
    //1 RConsole.print("getOperatingMode\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0
        ret = replyBuf[2];
      cmdComplete();
      return ret;
   
  }
 
  /**
   * Set Bluetooth visibility (discoverable) on or off for the local device
   *
   * @param visible true to set visibility on, false to set it off
   * @return < 0 error
   */
  public static int setVisibility(byte visible) {
    //1 RConsole.print("setVisibility\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_SET_DISCOVERABLE, 2, visible, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_DISCOVERABLE_ACK, TO_SHORT) >= 0
        ret = 0;
      cmdComplete();
      return ret;
   
  }
 
  /**
   * Reset the settings of the BC4 chip to the factory defaults.
   * The NXT should be restarted after this.
   *
     * @return 0 if ok < 0 if error
     */
  public static int setFactorySettings() {
    //1 RConsole.print("setFactorySettings\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_SET_FACTORY_SETTINGS, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FACTORY_SETTINGS_ACK, TO_SHORT) >= 0
        ret = 0;
      cmdComplete();
      return ret;
    }
  }
 
  /**
   * Set the operating mode
   *
   * @param mode 0 = Stream breaking, 1 don't break stream
   * @return  < 0 error
   */
  public static int setOperatingMode(byte mode) {
    //1 RConsole.print("setOperatingMode\n");
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_SET_OPERATING_MODE, 2, mode, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0
        ret = 0;
      cmdComplete();
      return ret;
    }
  }
 
  /**
   * Force a reset of the Bluetooth module.
   * Notes:
   * After this call power will be on.
   * Any existing connections will be closed
   * Any listening threads will be aborted
   *
   */ 
  public static void reset()
  {
    synchronized (Bluetooth.sync)
    {
      cmdStart();
      // Force a timeout and hence a reset
      cmdWait(RS_REPLY, RS_WAIT, MSG_RESET_INDICATION, TO_FORCERESET);
      cmdComplete();
    }   
  }

    private static int checkDevice()
    {
    synchronized (Bluetooth.sync)
    {
      int ret = -1;
      cmdStart();
      cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0);
      if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_CHECK) >= 0)
        ret = replyBuf[2];
      cmdComplete();
      return ret;
    }
    }
 
  /**
   * Set the power to the module
   *
   * @param on power on or off
   */
  public static void setPower(boolean on)
  {
    synchronized (Bluetooth.sync)
    {
      if (powerOn == on) return;
      if (on)
      {
        btSetResetHigh();
        powerOn = true;
        // Now make sure things have settled
        for(int i =0; i < 5; i++)
          if (checkDevice() >= 0) break;
      }
      else
      {
        // Powering off. Do we need to reset things?
        boolean wasListening = listening;
        // Wait for any other commands to complete
        cmdStart();
        if (connected > 0 || listening)
          reset();
        // Wait for the listening thread to exit
        if (wasListening)
          try{Bluetooth.sync.wait(2000);}catch(Exception e){}
        //1 RConsole.print("Power going off\n");
        btSetResetLow();
        powerOn = false;
      }
      publicPowerOn = powerOn;
    }
  }

  /**
   * Return the current state of the module power
   *
   * @return power on or off
   */
  public static boolean getPower()
  {
    synchronized(Bluetooth.sync)
    {
      return publicPowerOn;
    }
  }
 

  public static int getResetCount()
  {
    return resetCnt;
  }

    public static void loadSettings()
    {
        // Retrieve default PIN from System properties
      String pinStr = SystemSettings.getStringSetting(PIN, "1234");
      byte [] defaultPin = new byte[pinStr.length()];
        pin = defaultPin;
      for(int i=0;i<pinStr.length();i++)
        pin[i] = (byte)pinStr.charAt(i);
    }


  /**
   * The following are provided for compatibility with the old Bluetooth
   * class. They should not be used, in new programs and should probably
   * be removed.
   */
 
  /**
   * Read a packet from the stream. Do not block and for small packets
   * (< 254 bytes), do not return a partial packet.
   * @param  buf    Buffer to read data into.
   * @param  len    Number of bytes to read.
   * @return      > 0 number of bytes read.
   *          other values see read.
   */
  public static int readPacket(byte buf[], int len)
  {
    return Chans[3].readPacket(buf, len);
  }
 
  /**
   * Send a data packet.
   * Must be in data mode.
   * @param buf the data to send
   * @param bufLen the number of bytes to send
   */
  public static void sendPacket(byte [] buf, int bufLen)
  {
    Chans[3].sendPacket(buf, bufLen);
  }
 
  /**
   * Set the BC4 mode, and wait for that mode to be confirmed by the chip.
   *
   * @param mode the requested mode 1 == Command mode 0 == Stream mode
   */
  public static void btSetCmdMode(int mode)
  {
   
  }
   
    /**
     * Class to provide polymorphic access to the connection methods.
     * Gets returned as a singleton by getConnector and can be used to create
     * connections.
     */
    static class Connector extends NXTCommConnector
    {
        /**
         * Open a connection to the specified name/address using the given I/O mode
         * @param target The name or address of the device/host to connect to.
         * @param mode The I/O mode to use for this connection
         * @return A NXTConnection object for the new connection or null if error.
         */
        public NXTConnection connect(String target, int mode)
        {
            return Bluetooth.connect(target, mode);
        }

        /**
         * Wait for an incoming connection, or for the request to timeout.
         * @param timeout Time in ms to wait for the connection to be made
         * @param mode I/O mode to be used for the accepted connection.
         * @return A NXTConnection object for the new connection or null if error.
         */
        public NXTConnection waitForConnection(int timeout, int mode)
        {
            return Bluetooth.waitForConnection(timeout, mode);
        }
    }
   
    static NXTCommConnector connector = null;
   
    /**
     * Provides access to the singleton connection object.
     * This object can be used to create new connections.
     * @return the connector object
     */
    public static NXTCommConnector getConnector()
    {
        if (connector == null)
            connector = new Connector();
        return connector;
    }

}
TOP

Related Classes of lejos.nxt.comm.Bluetooth$Connector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.