OSDN Git Service

lejos_NXJ_win32_0_5_0beta.zip
[nxt-jsp/lejos_nxj.git] / nxtOSEK / lejos_nxj / src / java / classes / lejos / nxt / comm / Bluetooth.java
1 package lejos.nxt.comm;
2 import java.util.*;
3
4
5 /**
6  * Provides Bluetooth comminications.
7  * Allows inbound and outbound connections.
8  * Provides access to to device registration.
9  */
10 public class Bluetooth
11 {
12         public static  final int MSG_BEGIN_INQUIRY = 0;
13         public static  final int MSG_CANCEL_INQUIRY = 1;
14         public static  final int MSG_CONNECT = 2;
15         public static  final int MSG_OPEN_PORT = 3;
16         public static  final int MSG_LOOKUP_NAME = 4;
17         public static  final int MSG_ADD_DEVICE = 5;
18         public static  final int MSG_REMOVE_DEVICE = 6;
19         public static  final int MSG_DUMP_LIST = 7;
20         public static  final int MSG_CLOSE_CONNECTION = 8;
21         public static  final int MSG_ACCEPT_CONNECTION = 9;
22         public static  final int MSG_PIN_CODE = 10;
23         public static  final int MSG_OPEN_STREAM = 11;
24         public static  final int MSG_START_HEART = 12;
25         public static  final int MSG_HEARTBEAT = 13;
26         public static  final int MSG_INQUIRY_RUNNING = 14;
27         public static  final int MSG_INQUIRY_RESULT = 15;
28         public static  final int MSG_INQUIRY_STOPPED = 16;
29         public static  final int MSG_LOOKUP_NAME_RESULT = 17;
30         public static  final int MSG_LOOKUP_NAME_FAILURE = 18;
31         public static  final int MSG_CONNECT_RESULT = 19;
32         public static  final int MSG_RESET_INDICATION = 20;
33         public static  final int MSG_REQUEST_PIN_CODE = 21;
34         public static  final int MSG_REQUEST_CONNECTION = 22;
35         public static  final int MSG_LIST_RESULT = 23;
36         public static  final int MSG_LIST_ITEM = 24;
37         public static  final int MSG_LIST_DUMP_STOPPED = 25;
38         public static  final int MSG_CLOSE_CONNECTION_RESULT = 26;
39         public static  final int MSG_PORT_OPEN_RESULT = 27;
40         public static  final int MSG_SET_DISCOVERABLE = 28;
41         public static  final int MSG_CLOSE_PORT = 29;
42         public static  final int MSG_CLOSE_PORT_RESULT = 30;
43         public static  final int MSG_PIN_CODE_ACK = 31;
44         public static  final int MSG_SET_DISCOVERABLE_ACK = 32;
45         public static  final int MSG_SET_FRIENDLY_NAME = 33;
46         public static  final int MSG_SET_FRIENDLY_NAME_ACK = 34;
47         public static  final int MSG_GET_LINK_QUALITY = 35;
48         public static  final int MSG_LINK_QUALITY_RESULT = 36;
49         public static  final int MSG_SET_FACTORY_SETTINGS = 37;
50         public static  final int MSG_SET_FACTORY_SETTINGS_ACK = 38;
51         public static  final int MSG_GET_LOCAL_ADDR = 39;
52         public static  final int MSG_GET_LOCAL_ADDR_RESULT = 40;
53         public static  final int MSG_GET_FRIENDLY_NAME = 41;
54         public static  final int MSG_GET_DISCOVERABLE = 42;
55         public static  final int MSG_GET_PORT_OPEN = 43;
56         public static  final int MSG_GET_FRIENDLY_NAME_RESULT = 44;
57         public static  final int MSG_GET_DISCOVERABLE_RESULT = 45;
58         public static  final int MSG_GET_PORT_OPEN_RESULT = 46;
59         public static  final int MSG_GET_VERSION = 47;
60         public static  final int MSG_GET_VERSION_RESULT = 48;
61         public static  final int MSG_GET_BRICK_STATUSBYTE_RESULT = 49;
62         public static  final int MSG_SET_BRICK_STATUSBYTE_RESULT = 50;
63         public static  final int MSG_GET_BRICK_STATUSBYTE = 51;
64         public static  final int MSG_SET_BRICK_STATUSBYTE = 52;
65         public static  final int MSG_GET_OPERATING_MODE = 53;
66         public static  final int MSG_SET_OPERATING_MODE = 54;
67         public static  final int MSG_OPERATING_MODE_RESULT = 55;
68         public static  final int MSG_GET_CONNECTION_STATUS = 56;
69         public static  final int MSG_CONNECTION_STATUS_RESULT = 57;
70         public static  final int MSG_GOTO_DFU_MODE = 58;
71         public static  final int MSG_ANY = -1;
72         
73         public static final int BT_PENDING_INPUT = 1;
74         public static final int BT_PENDING_OUTPUT = 2;
75
76         private static final int CHANCNT = 4;
77         private static final int RS_INIT = -1;
78         private static final int RS_IDLE = 0;
79         private static final int RS_CMD = 1;
80         private static final int RS_WAIT = 2;
81         private static final int RS_REPLY = 3;
82         private static final int RS_REQUESTCONNECT = 4;
83         private static final int RS_ERROR = 5;
84         
85         private static final int IO_TIME = 100;
86         private static final int CMD_TIME = 50;
87         private static final int TO_SWITCH = 500;
88         private static final int TO_REPLY = 250;
89         private static final int TO_SHORT = 2000;
90         private static final int TO_LONG = 30000;
91         private static final int TO_RESET = 5000;
92         private static final int TO_INIT = 500;
93         private static final int TO_CLOSE = 100;
94         private static final int TO_FORCERESET = -1;
95         private static final int TO_NONE = 0;
96         private static final int TO_FLUSH = 500;
97         
98         static BTConnection [] Chans = new BTConnection[CHANCNT];
99         static byte [] cmdBuf = new byte[128];
100         static byte [] replyBuf = new byte[256];
101         static int cmdTimeout;
102         static int reqState = RS_INIT;
103         static int savedState;
104         static boolean listening = false;
105         static int connected;
106         static int resetCnt;
107         static boolean powerOn;
108         static boolean publicPowerOn = false;
109         public final static byte[] defaultPin = 
110             {(byte) '1', (byte) '2', (byte) '3', (byte) '4'};
111         private static byte [] pin = defaultPin;
112         static Object sync = new Object();
113         static byte[] cachedName;
114         static byte[] cachedAddress;
115
116         /**
117          * Low-level method to write BT data
118          * 
119          * @param buf the buffer to send
120          * @param off the offset to start the write from.
121          * @param len the number of bytes to send
122          * @return number of bytes actually written
123          */
124         public static native int btWrite(byte[] buf, int off, int len);
125         /**
126          * Low-level method to read BT data
127          * 
128          * @param buf the buffer to read data into
129          * @param off the offset at which to start the transfer
130          * @param len the number of bytes to read
131          * @return number of bytes actually read
132          */
133         public static native int btRead(byte[] buf, int off, int len);  
134         /**
135          *Low-Level method to access the Bluetooth interface. Bitwise values returned.
136          * @return              0 No data pending
137          *                              0x1 input pending
138          *                              0x2 output pending
139          */
140         public static native int btPending();
141         /**
142          * Low-level method to switch BC4 chip between command
143          * and data (stream) mode.
144          * 
145          * @param mode 0=data mode, 1=command mode
146          */
147         public static native void btSetArmCmdMode(int mode);
148         
149         /**
150          * Low-level method to get the BC4 chip mode
151          */
152         public static native int btGetBC4CmdMode();
153         
154         /**
155          * Low-level method to start ADC converter
156          *
157          */
158         public static native void btStartADConverter();
159
160         /**
161          * Low-level method to take the BC4 reset line low
162          */
163         public static native void btSetResetLow();
164         
165         /**
166          * Low-level method to take the BC4 reset line high
167          */
168         public static native void btSetResetHigh();
169         
170         
171         /**
172          * Low-level method to send a BT command or data
173          *
174          * @param buf the buffer to send
175          * @param len the number of bytes to send
176          */
177         public static native void btSend(byte[] buf, int len);
178
179         /**
180          * Low-level method to receive BT replies or data
181          *
182          * @param buf the buffer to receive data in
183          */
184         public static native void btReceive(byte[] buf);
185
186         
187         public Bluetooth()
188         {
189         }
190         
191         private static void cmdInit(int cmd, int len, int param1, int param2)
192         {
193                 // Helper function. Setup a simple command in the buffer ready to go.
194                 cmdBuf[0] = (byte)len;
195                 cmdBuf[1] = (byte)cmd;
196                 cmdBuf[2] = (byte)param1;
197                 cmdBuf[3] = (byte)param2;
198         }
199         
200         private static void startTimeout(int period)
201         {
202                 // Start a command timeout
203                 cmdTimeout = (int)System.currentTimeMillis() + period;
204         }
205         
206         private static boolean checkTimeout()
207         {
208                 return (cmdTimeout > 0 && (int)System.currentTimeMillis() > cmdTimeout);
209         }
210         
211         private static void cancelTimeout()
212         {
213                 cmdTimeout = -1;
214         }
215         
216         /**
217          * The main Bluetooth control thread. This controls access to the Bluetooth
218          * interface. It controls and peforms all low level access to the device.
219          * Switches it between data channels and command streams as required.
220          */
221         static class BTThread extends Thread
222         {
223                 static final int MO_STREAM = 0;
224                 static final int MO_CMD = 1;
225                 private int mode;
226                 private int curChan;
227
228                 public BTThread()
229                 {
230                         mode = MO_CMD;
231                         reqState = RS_INIT;
232                         curChan = -1;
233                         resetCnt = 0;
234                         // Make sure power is on(may cause a reset!)
235                         btSetResetHigh();
236                         for(int i = 0; i < CHANCNT; i++)
237                                 Chans[i] = new BTConnection(i);
238                         connected = 0;
239                         listening = false;
240                         cancelTimeout();
241                         setDaemon(true);
242                         start();
243                         // Setup initial state
244                         powerOn = false;
245                         setPower(true);
246                         setOperatingMode((byte)1);
247                         closePort();
248                         cachedName = getFriendlyName();
249                         cachedAddress = getLocalAddress();
250                 }
251
252                 
253                 private void sendCommand()
254                 {
255                         // Command should be all setup and ready to go in cmdBuf
256                         int checkSum = 0;
257                         int len = (int)cmdBuf[0] & 0xff;
258                         //1 Debug.out("send cmd " + (int)cmdBuf[1] + "\n");
259                         for(int i=0;i<len;i++)
260                         {
261                                 checkSum += cmdBuf[i+1];
262                         }
263                         checkSum = -checkSum;
264                         cmdBuf[len+1] = (byte) ((checkSum >> 8) & 0xff);
265                         cmdBuf[len+2] = (byte) checkSum;
266                         cmdBuf[0] = (byte)(len + 2);
267                         btWrite(cmdBuf,0, len+3);
268                 }
269
270                 private int recvReply()
271                 {
272                         // Read a reply and place it in replyBuf
273                         if (checkTimeout()) return -1;
274                         int cnt = Bluetooth.btRead(replyBuf, 0, 1);
275                         if (cnt <= 0) return 0;
276                         int len = (int)replyBuf[0] & 0xff;
277                         if (len < 3 ||len >= replyBuf.length)
278                         {
279                                 //1 Debug.out("Bad packet len " + len + " cnt " + cnt + "\n");
280                                 return -1;
281                         }
282                         int timeout = (int)System.currentTimeMillis() + TO_REPLY;
283                         while (cnt < len+1)
284                         {
285                                 cnt += Bluetooth.btRead(replyBuf, cnt, len + 1 - cnt);
286                                 if ((int)System.currentTimeMillis() > timeout)
287                                 {
288                                         //1 Debug.out("recvReply timeout\n");
289                                         return -1;
290                                 }
291                         }
292                         
293                         int csum = len; 
294                         len -= 2;
295                         for(int i = 0; i < len; i++)
296                                 csum += (int)replyBuf[i+1] & 0xff;
297                         csum = -csum;
298                         //1 Debug.out("Got reply " + replyBuf[1] + "\n");
299                         if (((byte) csum == replyBuf[len+2]) && ((byte)(csum >> 8) == replyBuf[len+1]))
300                                 return len;
301                         else
302                         {
303                                 //1 Debug.out("Bad csum\n");
304                                 return -1;
305                         }
306                 }
307         
308                 /**
309                  * Perform a hardware reset of the BlueCore chip.
310                  * 
311                  */     
312                 private void reset() 
313                 {
314
315                         synchronized(Bluetooth.sync)
316                         {
317                                 int len;
318                                 //1 Debug.out("hardware reset\n");
319                                 for(int resetCnt = 0; resetCnt < 2; resetCnt++)
320                                 {
321                                         // Ditch any pending data in the input buffer
322                                         startTimeout(TO_FLUSH);
323                                         while (!checkTimeout())
324                                         {
325                                                 recvReply();
326                                         }
327                                         // Debug.out("End of flush\n");
328                                         // BC4 reset seq. First take the reset line low...
329                                         btSetArmCmdMode(MO_CMD);
330                                         btSetResetLow();
331                                         // Keep it that way for 100ms and discard any input
332                                         startTimeout(100);
333                                         while (!checkTimeout())
334                                         {
335                                                 recvReply();
336                                         }
337                                         // Now bring it high
338                                         btSetResetHigh();
339                                         // Now wait for either 5 seconds or for a RESET_INDICATION
340                                         startTimeout(TO_RESET);
341                                         while ((len = recvReply()) == 0 || ( len > 0 && replyBuf[1] != MSG_RESET_INDICATION))
342                                                         Thread.yield();
343                                         //1 if (len < 0) Debug.out("Reset timed out");
344                                         // Check things are working
345                                         //1 Debug.out("Send mode cmd\n");
346                                         cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0);
347                                         sendCommand();
348                                         startTimeout(TO_SHORT);
349                                         while ((len = recvReply()) == 0 || (len > 0 && replyBuf[1] != MSG_OPERATING_MODE_RESULT))
350                                                         Thread.yield();
351                                         //1 if (len < 0) Debug.out("mode had timed out\n");
352                                         // if we got the response without a timeout we are done!
353                                         if (len > 0) break;
354                                 }
355                                 // We are now in command mode
356                                 mode = MO_CMD;
357                                 // Now reset everything else that is going on gulp!
358                                 for(int i = 0; i < CHANCNT; i++)
359                                         Chans[i].reset();
360                                 //1 Debug.out("reset complete state is " + reqState + "\n");
361                                 listening = false;
362                                 connected = 0;
363                                 curChan = -1;
364                                 cancelTimeout();
365                                 // Tell anyone that is waiting
366                                 if (reqState > RS_IDLE) reqState = RS_ERROR;
367                                 Bluetooth.sync.notifyAll();
368                                 resetCnt++;
369                         }
370                 }
371                 
372                 private void processReply()
373                 {
374                         // Read and process and command replies from the bc4
375                         // If the reply buffer is free, look to see if we have a new reply
376                         synchronized(Bluetooth.sync)
377                         {
378                                 //Debug.out("processn reply " + reqState + "\n");
379                                 int len;
380                                 while (reqState < RS_REPLY && (len = recvReply()) != 0)
381                                 {
382                                         //Debug.out("process request\n");
383                                         // Got a message. We only deal with the messages we have to deal
384                                         // with here. In general we allow the calling thread to decide
385                                         // what to do (this includes ignoring the message!).
386                                         //1 Debug.out("got request " + (int)replyBuf[1] + " state " + reqState + "\n");
387                                         if (len < 0 || replyBuf[1] == MSG_RESET_INDICATION)
388                                         {
389                                                 //1 Debug.out("Got reply error\n");
390                                                 reset();
391                                                 break;
392                                         }
393                                         else if (replyBuf[1] == MSG_CLOSE_CONNECTION_RESULT)
394                                         {
395                                                 if (replyBuf[3] >= 0 && replyBuf[3] < Chans.length)
396                                                 {
397                                                         if (Chans[replyBuf[3]].disconnected())
398                                                                 connected--;
399                                                         if (replyBuf[3] == (byte)curChan) curChan = -1;
400                                                 }
401                                         }
402                                         else if (replyBuf[1] == MSG_REQUEST_CONNECTION)
403                                         {
404                                                 if (listening)
405                                                 {
406                                                         // Push the current state
407                                                         savedState = reqState;
408                                                         reqState = RS_REQUESTCONNECT;
409                                                 }
410                                                 else
411                                                 {
412                                                         // No one wants to know so reject it.
413                                                         // Note: Doing this seems to cause a device reset!
414                                                         cmdInit(MSG_ACCEPT_CONNECTION, 2, 0, 0);
415                                                         sendCommand();
416                                                         continue;
417                                                 }
418                                         }
419                                         else if (replyBuf[1] == MSG_REQUEST_PIN_CODE)
420                                         {
421                                                 // If we have no pin then nothing to do
422                                                 if (pin == null) continue;
423                                                 // Otherwise send the pin as requested
424                                                 cmdInit(MSG_PIN_CODE, 24, 0, 0);
425                                                 System.arraycopy(replyBuf, 2, cmdBuf, 2, 7);
426                                                 for(int i = 0; i < 16; i++)
427                                                         cmdBuf[i + 9] = (i < pin.length ? pin[i] : 0);
428                                                 sendCommand();
429                                                 continue;
430                                         } else if (replyBuf[1] == MSG_PIN_CODE_ACK)
431                                                 continue;
432                                         // All other messages give the caller it to deal with
433                                         if (reqState == RS_WAIT)
434                                                 reqState = RS_REPLY;
435                                         if (reqState >= RS_REPLY)
436                                                 Bluetooth.sync.notifyAll();
437                                 }
438                         }
439                         //Debug.out("process reply end\n");
440                 }
441                 
442                 private void processCommands()
443                 {
444                         // Process commands. Return when we should consider switching to
445                         // stream mode.
446                         //Debug.out("Process cmd1\n");
447                         switchToCmd();
448                         int cmdEnd = (int)System.currentTimeMillis() + CMD_TIME;
449                         while (cmdEnd > (int)System.currentTimeMillis() || reqState > RS_IDLE)
450                         {
451                                 //Debug.out("ProcessCommands state " + reqState + "\n");
452                                 synchronized(Bluetooth.sync)
453                                 {
454                                         if (reqState == RS_CMD)
455                                         {
456                                                 // Have a command ready to go so send it
457                                                 sendCommand();
458                                                 reqState = RS_WAIT;
459                                         }
460                                         processReply();
461                                 }
462                                 Thread.yield();
463                         }
464                         //Debug.out("Process cmd end\n");
465                 }
466                 
467                 private int selectChan()
468                 {
469                         // Select the next channel to be processed
470                         if (connected == 0) return -1;
471                         int i;
472                         int cur = curChan;
473                         for(i = 0; i < Chans.length; i++)
474                         {
475                                 cur = (cur + 1) % Chans.length;
476                                 if (Chans[cur].needsAttention()) 
477                                 {
478                                         // if (cur != curChan) Debug.out("Selected " + cur + "\n");
479                                         return cur;
480                                 }
481                         }
482                         // Nothing better found so stick with the current channel
483                         return curChan;
484                 }
485                 
486                 private void processStreams()
487                 {
488                         // Process the streams. Return when we should switch to command mode
489                         // Debug.out("PS cur " + curChan + " state " + reqState + "\n");
490                         while (true)
491                         {
492                                 synchronized(Bluetooth.sync)
493                                 {
494                                         //Debug.out("Process streams " + reqState + "\n");
495                                         if (reqState != RS_IDLE) return;
496                                         int next = selectChan();
497                                         if (next < 0 || !switchToStream(next)) return;
498                                         // Perform I/O on the current stream. Switching from one stream
499                                         // to another is a slow process, so we spend at least IO_TIME ms
500                                         // on each stream before switching away.
501                                         //Debug.out("Process streams 2" + reqState + "\n");
502                                         int ioEnd = (int)System.currentTimeMillis() + IO_TIME;                  
503                                         while (ioEnd > (int)System.currentTimeMillis() && Chans[curChan].state >= BTConnection.CS_CONNECTED)
504                                         {
505                                                 if (bc4Mode() != MO_STREAM) return;
506                                                 Chans[curChan].send();
507                                                 Chans[curChan].recv();
508                                                 Thread.yield();
509                                         }
510                                         //Debug.out("Process streams 3" + reqState + "\n");
511                                         // Do we need to switch back to command mode?
512                                         if (listening) return;
513                                 }
514                                 Thread.yield();
515                         }
516                 }
517                 
518                 private int waitSwitch(int target, boolean flush)
519                 {
520                         // Wait for the BC4 to switch to mode, or timeout...
521                         int timeout = (int) System.currentTimeMillis() + TO_SWITCH;
522                         while (timeout > (int)System.currentTimeMillis())
523                         {
524                                 if (bc4Mode() == target) return target;
525                                 // Need to flush input when switching to command mode
526                                 if (flush && curChan >= 0) Chans[curChan].flushInput();
527                         }
528                         //1 Debug.out("Failed to switch\n");
529                         mode = -1;
530                         curChan = -1;
531                         return bc4Mode();
532                 }
533                 
534                 private boolean switchToStream(int chan)
535                 {
536                         // Decide which (if any) stream to switch to
537                         if (mode == MO_STREAM && chan == curChan) return true;
538                         switchToCmd();
539                         //1 Debug.out("Switch to chan " + chan + " handle " + Chans[chan].handle + "\n");
540                         cmdInit(MSG_OPEN_STREAM, 2, Chans[chan].handle, 0);
541                         sendCommand();
542                         // Now wait for the BC4 to switch
543                         if (waitSwitch(MO_STREAM, false) != MO_STREAM) return false;
544                         //1 Debug.out("In stream mode\n");
545                         // Make sure we process any remaining command input that may have arrived
546                         processReply();
547                         // Finally switch the ARM over
548                         btSetArmCmdMode(MO_STREAM);
549                         mode = MO_STREAM;
550                         curChan = chan;
551                         return true;
552                 }
553                 
554                 private void switchToCmd()
555                 {
556                         // Need to switch back into command mode
557                         // First step send any pending data
558                         if (mode == MO_CMD) return;
559                         //1 Debug.out("switch to cmd\n");
560                         if (mode == MO_STREAM && bc4Mode() == MO_CMD && curChan >= 0)
561                         {
562                                 //1 Debug.out("Trying early flush\n");
563                                 Chans[curChan].flushInput();
564                         }
565                         // wait for any output to drain
566                         while((btPending() & BT_PENDING_OUTPUT) != 0)
567                                 Thread.yield();
568                         try{Thread.sleep(100);}catch(Exception e){}
569                         btSetArmCmdMode(MO_CMD);
570                         // If there is any input data left we could be in trouble. Try and
571                         // flush everything.
572                         if (waitSwitch(MO_CMD, true) != MO_CMD)
573                         {
574                                 //1 Debug.out("Failed to switch to cmd\n");
575                                 reset();
576                                 return;
577                         }
578                         mode = MO_CMD;
579                 }
580                 
581                 private int bc4Mode()
582                 {
583                         // return the current mode of the BC4 chip
584                         int ret = btGetBC4CmdMode();
585                         // > 512 indicates a high logic level which is mode 0!
586                         if (ret > 512)
587                                 return MO_STREAM;
588                         else
589                                 return MO_CMD;
590                 }
591                 
592                 private void waitInit()
593                 {
594                         synchronized (Bluetooth.sync)
595                         {
596                                 reqState = RS_INIT;
597                                 processCommands();
598                                 reqState = RS_IDLE;
599                                 Bluetooth.sync.notifyAll();
600                         }
601                 }
602                 
603                 public void run()
604                 {
605                         //1 Debug.out("Thread running\n");
606                         waitInit();
607                         //1 Debug.out("Init complete\n");
608                         while(true)
609                         {
610                                 processCommands();
611                                 processStreams();
612                                 Thread.yield();
613                         }
614                 }
615         }
616
617         // Create the Bluetooth device thread.
618         private static BTThread btThread = new BTThread();
619         
620         static private int waitState(int target)
621         {
622                 // Debug.out("Wait state " + target + "\n");
623                 synchronized (Bluetooth.sync) {
624                         // Wait for the system to enter the specified state (or timeout)
625                         while (reqState != target && reqState != RS_ERROR)
626                                 try{Bluetooth.sync.wait();}catch(Exception e){}
627                         if (reqState == RS_ERROR)
628                                 return -1;
629                         else
630                                 return 0;
631                 }
632         }
633         
634         private static void cmdStart()
635         {
636                 // Wait for the system to be idle. Ignore timeout errors.
637                 while (waitState(RS_IDLE) < 0)
638                         try{Bluetooth.sync.wait();}catch(Exception e){}
639         }
640         
641         private static void cmdComplete()
642         {
643                 // command now complete. Reset to idle (clears any timeout state)
644                 synchronized(Bluetooth.sync)
645                 {
646                         reqState = RS_IDLE;
647                         cancelTimeout();
648                         Bluetooth.sync.notifyAll();
649                 }
650         }
651         
652         private static int cmdWait(int state, int waitState, int msg, int timeout)
653         {
654                 //1 Debug.out("Cmd wait\n");
655                 synchronized (Bluetooth.sync)
656                 {
657                         // Check we have power if not fail the request
658                         if (!powerOn) return -1;
659                         if (waitState > 0) reqState = waitState;
660                         if (timeout != TO_NONE) startTimeout(timeout);
661                         while (true)
662                         {
663                                 if (waitState(state) < 0) return -1;
664                                 if (msg == MSG_ANY || replyBuf[1] == msg) return 0;
665                                 // Ignore any unwanted message
666                                 if (reqState == RS_REPLY) reqState = RS_WAIT; 
667                         }
668                 }
669         }
670
671         /**
672          * Set the pin to be used for pairing/connecting to the system
673          * 
674          * @param newPin the new pin code
675          * 
676          */
677         public static void setPin(byte[] newPin)
678         {
679                 pin = newPin;
680         }
681
682         /**
683          * Return the pin to be used for pairing/connecting to the system
684          * 
685          * @return The current pin code
686          * 
687          */     
688         public static byte[] getPin()
689         {
690                 return pin;
691         }
692
693         
694         /**
695          * Close an open connection
696          * 
697          * @param handle the handle for the connection
698          * @return the status 0 = success
699          */
700         public static int closeConnection(byte handle)
701         {
702                 int ret = -1;
703                 //1 Debug.out("Close connection state " + reqState + "\n");
704                 synchronized (Bluetooth.sync)
705                 {
706                         cmdStart();
707                         // We can have a race condition when both ends of the connection
708                         // close at the same time. This can mean we try and close an already
709                         // closed connection. If we do this the BC4 goes into reset mode.
710                         // To try and avoid this happening we insert a different length
711                         // delay between outbound and inbound streams. We then wait to see
712                         // if the other end has already closed things...
713                         int timeout = (handle == 3 ? 5*TO_CLOSE : TO_CLOSE);
714                         reqState = RS_WAIT;
715                         try{Bluetooth.sync.wait(timeout);}catch(Exception e){}
716                         reqState = RS_IDLE;
717                         // There is a small chance that we may have had a reset so make sure
718                         // that we still have an open channel to close!
719                         byte [] status = getConnectionStatus();
720                         //1 Debug.out("Conn status " + status[handle]);
721                         if (status == null || status[handle] != 2) return -1;
722                         if (Chans[handle].state != BTConnection.CS_DISCONNECTING) return -1;
723                         cmdInit(MSG_CLOSE_CONNECTION, 2, handle, 0);
724                         if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_CONNECTION_RESULT, TO_SHORT) >= 0)
725                                 ret = (int)replyBuf[2];
726                         int retryCnt = 5;
727                         do {
728                                 // We may have a race condition here, or have triggered a reset
729                                 // wait for things to settle
730                                 reqState = RS_WAIT;
731                                 try{Bluetooth.sync.wait(TO_REPLY);}catch(Exception e){}
732                                 reqState = RS_IDLE;
733                         } while (getConnectionStatus() == null);
734                         cmdComplete();
735                         return ret;
736                 }
737         }
738
739         /**
740          * Opens the  port to allow incoming connections.
741          * 
742          * @return an array of three bytes: success, handle, ps_success
743          */
744         public static byte[] openPort() {
745                 byte[] result = new byte[3];
746                 synchronized (Bluetooth.sync)
747                 {
748                         cmdStart();
749                         cmdInit(MSG_OPEN_PORT, 1, 0, 0);
750                         if (cmdWait(RS_REPLY, RS_CMD, MSG_PORT_OPEN_RESULT, TO_SHORT) < 0)
751                                 result = null;
752                         else
753                                 System.arraycopy(replyBuf, 2, result, 0, 3);
754                         //1 Debug.out("Port open handle " + (int)replyBuf[3] + " status " + (int)replyBuf[2] + "\n");
755                         cmdComplete();
756                         return result;
757                 }
758         }
759
760         
761         /**
762          * Closes the  port to disallow incoming connections.
763          * 
764          * @return an array of two bytes: success, ps_success
765          */
766         public static byte [] closePort() {
767                 byte [] result = new byte[2];
768                 //1 Debug.out("Close port\n");
769                 synchronized (Bluetooth.sync)
770                 {
771                         cmdStart();
772                         // The Lego doc says the handle should always be 3!
773                         cmdInit(MSG_CLOSE_PORT, 2, 3, 0);
774                         if (cmdWait(RS_REPLY, RS_CMD, MSG_CLOSE_PORT_RESULT, TO_SHORT) < 0)
775                                 result = null;
776                         else
777                                 System.arraycopy(replyBuf, 2, result, 0, 2);
778                         cmdComplete();
779                         return result;
780                 }
781         }
782
783         /**
784          * Wait for a remote device to connect.
785          * 
786          * @param pin the pin to use
787          * @return a BTConnection
788          */
789         public static BTConnection waitForConnection(byte[] pin)
790         {
791                 //1 Debug.out("waitForConnection\n");
792                 synchronized (Bluetooth.sync)
793                 {
794                         BTConnection ret = null;
795                         // only one listener at once
796                         if (listening) return null;
797                         // First open up the listening port
798                         byte [] port = openPort();
799                         if (port == null || port[0] != 1 || port[1] >=  Chans.length || port[1] < 0)
800                         {
801                                 //1 Debug.out("Failed to open port\n");
802                                 return null;
803                         }
804                         // Now in listening mode
805                         listening = true;
806                         byte []savedPin = getPin();
807                         setPin(pin);
808                         // Wait for special connect indication
809                         while (listening && reqState != RS_REQUESTCONNECT)
810                                 try{Bluetooth.sync.wait();}catch(Exception e){}
811                         if (listening)
812                         {
813                                 //1 Debug.out("Got connect request\n");
814                                 // Restore state
815                                 reqState = savedState;
816                                 // and wait until we have control
817                                 cmdStart();
818                                 // Acknowledge the request
819                                 cmdInit(MSG_ACCEPT_CONNECTION, 2, 1, 0);
820                                 if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0)
821                                 {
822                                         //1 Debug.out("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n");
823                                         if (replyBuf[2] == 1)
824                                         {
825                                                 byte handle = replyBuf[3];
826                                                 // Got a connection
827                                                 if (handle >= 0 && handle < Chans.length)
828                                                 {
829                                                         // Assert(Chans[handle].state == CS_IDLE);
830                                                         Chans[handle].bind(handle);
831                                                         // now have one more connected
832                                                         connected++;
833                                                         ret = Chans[handle];
834                                                 }
835                                         }
836                                 }
837                                 listening = false;
838                                 cmdComplete();
839
840                         }
841                         setPin(savedPin);
842                         closePort();
843                         return ret;
844                 }
845         }
846                 
847         public static BTConnection waitForConnection()
848         {
849                 return waitForConnection(defaultPin);
850         }
851         
852         /**
853          * Connects to a remote device
854          * 
855          * @param remoteDevice remote device
856          * @return BTConnection Object or null
857          */
858         public static BTConnection connect(BTRemoteDevice remoteDevice) {
859                 if (remoteDevice == null) return null;
860                 return connect(remoteDevice.getDeviceAddr());
861         }
862         /**
863          * Connects to a Device by it's Byte-Device-Address Array
864          * Uses default pin "1234"
865          * 
866          * @param device_addr byte-Array with device-Address
867          * @return BTConnection Object or null
868          */
869         public static BTConnection connect(byte[] device_addr) {
870                 return connect(device_addr, defaultPin);
871         }
872         
873         /**
874          * Connects to a Device by it's Byte-Device-Address Array
875          * 
876          * @param device_addr byte-Array with device-Address
877          * @param pin the pin to use
878          * @return BTConnection Object or null
879          */
880         public static BTConnection connect(byte[] device_addr, byte[] pin) {
881                 //1 Debug.out("Connect\n");
882                 synchronized(Bluetooth.sync)
883                 {
884                         BTConnection ret = null;
885                         byte[] savedPin = getPin();
886                         setPin(pin);
887                         cmdStart();
888                         cmdInit(MSG_CONNECT, 8, 0, 0);
889                         System.arraycopy(device_addr, 0, cmdBuf, 2, 7);
890                         if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECT_RESULT, TO_LONG) >= 0)
891                         {
892                                 //1 Debug.out("Connect result " + (int)replyBuf[2] + " handle " + (int)replyBuf[3] + "\n");
893                                 if (replyBuf[2] != 0)
894                                 {
895                                         byte handle = replyBuf[3];
896                                         // Now need to check that the connection is not closed imm
897                                         reqState = RS_WAIT;
898                                         try{Bluetooth.sync.wait(300);}catch(Exception e){}
899                                         if (reqState == RS_WAIT && handle >= 0 && handle < Chans.length)
900                                         {
901                                                 // Got a connection
902                                                 Chans[handle].bind(handle);
903                                                 // now have one more connected
904                                                 connected++;
905                                                 ret = Chans[handle];
906                                         }
907                                 }
908                         }
909                         cmdComplete();
910                         setPin(savedPin);
911                         return ret;
912                 }
913         }
914
915         
916         /**
917          * Get the Bluetooth signal strength (link quality)
918          * Higher values mean stronger signal.
919          * 
920          * 
921          * @return link quality value 0 to 255. 
922          * 
923          */
924         public static int getSignalStrength(byte handle) {
925                 //1 Debug.out("getSignalStrength\n");
926                 synchronized (Bluetooth.sync)
927                 {
928                         int ret = -1;
929                         cmdStart();
930                         if (Chans[handle].state != BTConnection.CS_CONNECTED) return -1;
931                         cmdInit(MSG_GET_LINK_QUALITY, 2, handle, 0);
932                         if (cmdWait(RS_REPLY, RS_CMD, MSG_LINK_QUALITY_RESULT, TO_SHORT) >= 0)  
933                                 ret = replyBuf[2] & 0xff;
934                         cmdComplete();
935                         return ret;
936                 }
937         }
938         
939         
940         /**
941          * Get the friendly name of the local device
942          * @return the friendly name
943          */
944         public static byte [] getFriendlyName() {
945                 byte[] result = new byte[16];
946                 //1 Debug.out("getFriendlyName\n");
947                 synchronized (Bluetooth.sync)
948                 {
949                         // If power is off return the cached name.
950                         if (!powerOn) return cachedName;
951                         cmdStart();
952                         cmdInit(MSG_GET_FRIENDLY_NAME, 1, 0, 0);
953                         if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_FRIENDLY_NAME_RESULT, TO_SHORT) < 0)      
954                                 result = null;
955                         else
956                                 System.arraycopy(replyBuf, 2, result, 0, 16);
957                         cmdComplete();
958                         return result;
959                 }
960         }
961                 
962         /**
963          * Set the name of the local device
964          * @param name the friendly name for the device
965          */
966         public static boolean setFriendlyName(byte[] name) {
967                 //1 Debug.out("setFriendlyName\n");
968                 synchronized (Bluetooth.sync)
969                 {
970                         boolean ret=false;
971                         cmdStart();
972                         cmdInit(MSG_SET_FRIENDLY_NAME, 17, 0, 0);
973                         System.arraycopy(name, 0, cmdBuf, 2, 16);
974                         if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FRIENDLY_NAME_ACK, TO_LONG) >= 0) 
975                                 ret = true;
976                         cmdComplete();
977                         return ret;
978                 }       
979         }
980                 
981         /**
982          * get the Bluetooth address of the local device
983          * @return the local address
984          */
985         public static byte[] getLocalAddress() {
986                 byte[] result = new byte[7];
987                 //1 Debug.out("getLocalAddress\n");
988                 synchronized (Bluetooth.sync)
989                 {
990                         // If power is off return cached name.
991                         if (!powerOn) return cachedAddress;
992                         cmdStart();
993                         cmdInit(MSG_GET_LOCAL_ADDR, 1, 0, 0);
994                         if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_LOCAL_ADDR_RESULT, TO_SHORT) < 0) 
995                                 result = null;
996                         else
997                                 System.arraycopy(replyBuf, 2, result, 0, 7);
998                         cmdComplete();
999                         return result;
1000                 }
1001         }       
1002         
1003         
1004         /**
1005          * The internal Chip has a list of already paired Devices. This Method returns a 
1006          * Vector-List which contains all the known Devices on the List. These need not be reachable. 
1007          * To connect to a "not-known"-Device, you should use the Inquiry-Process. 
1008          * The pairing-Process can also be done with the original Lego-Firmware. The List of known 
1009          * devices will not get lost, when installing the LeJOS Firmware. 
1010          * 
1011          * @return Vector with List of known Devices
1012          */
1013         public static Vector getKnownDevicesList() {
1014                 //1 Debug.out("getKnownDevicesList\n");
1015                 synchronized(Bluetooth.sync)
1016                 {
1017                         int state = RS_CMD;
1018                         byte[] device = new byte[7];
1019                         byte[] devclass = new byte[4];
1020                         char[] name = new char[16];
1021                         Vector retVec = new Vector(1);
1022                         BTRemoteDevice curDevice;
1023                         cmdStart();
1024                         cmdInit(MSG_DUMP_LIST, 1, 0, 0);
1025                         while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
1026                         {
1027                                 state = RS_WAIT;
1028                                 if (replyBuf[1] == MSG_LIST_ITEM)
1029                                 {
1030                                         System.arraycopy(replyBuf, 2, device, 0, 7);
1031                                         int nl = 0;
1032                                         for(; nl < 16 && replyBuf[nl+9] != 0; nl++)
1033                                                 name[nl] = (char)replyBuf[nl+9];
1034                                         System.arraycopy(replyBuf, 25, devclass, 0, 4);
1035                                         curDevice = new BTRemoteDevice(name, nl, device, devclass);
1036                                         //1 Debug.out("got name " + curDevice.getFriendlyName() + "\n");
1037                                         retVec.addElement(curDevice);
1038                                 }
1039                                 else if (replyBuf[1] == MSG_LIST_DUMP_STOPPED)
1040                                         break;
1041                         }
1042                         cmdComplete();
1043                         return retVec;
1044                 }
1045         }
1046         
1047         /**
1048          * Gets a Device of the BC4-Chips internal list of known Devices 
1049          * (those who have been paired before) into the BTDevice Object. 
1050          * @param fName Friendly-Name of the device
1051          * @return BTDevice Object or null, if not found.
1052          */
1053         public static BTRemoteDevice getKnownDevice(String fName) {
1054                 BTRemoteDevice btd = null;
1055                 //look the name up in List of Known Devices
1056                 Vector devList = getKnownDevicesList();
1057                 if (devList.size() > 0) {
1058                         for (int i = 0; i < devList.size(); i++) {
1059                                 btd = (BTRemoteDevice) devList.elementAt(i);
1060                                 if (btd.getFriendlyName().equals(fName)) {
1061                                         return btd; 
1062                                 }
1063                         }
1064                 }
1065                 return null;
1066         }
1067
1068         /**
1069          * Add device to known devices
1070          * @param d Remote Device
1071          * @return true iff add was successful
1072          */
1073         public static boolean addDevice(BTRemoteDevice d) {
1074                 byte [] addr = d.getDeviceAddr();
1075                 String name = d.getFriendlyName();
1076                 byte[] cod = d.getDeviceClass();
1077                 //1 Debug.out("addDevice " + name + "\n");
1078                 synchronized (Bluetooth.sync)
1079                 {
1080                         boolean ret=false;
1081                         cmdStart();
1082                         cmdInit(MSG_ADD_DEVICE, 28, 0, 0);
1083                         System.arraycopy(addr, 0, cmdBuf, 2, 7);
1084                         System.arraycopy(cod, 0, cmdBuf, 25, 4);
1085                         for(int i=0;i<name.length();i++)  cmdBuf[i+9] = (byte) name.charAt(i);
1086                         for(int i=name.length(); i < 16; i++) cmdBuf[i+9] = 0;
1087                         if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0)   
1088                                 ret = replyBuf[2] == 0x50;
1089                         cmdComplete();
1090                         return ret;
1091                 }               
1092         }
1093         
1094         /**
1095          * Add device to known devices
1096          * @param d Remote Device
1097          * @return true iff remove was successful
1098          */
1099         public static boolean removeDevice(BTRemoteDevice d) {
1100                 byte [] addr = d.getDeviceAddr();
1101                 synchronized (Bluetooth.sync)
1102                 {
1103                         boolean ret = false;
1104                         cmdStart();
1105                         cmdInit(MSG_REMOVE_DEVICE, 8, 0, 0);
1106                         System.arraycopy(addr, 0, cmdBuf, 2, 7);        
1107                         if (cmdWait(RS_REPLY, RS_CMD, MSG_LIST_RESULT, TO_LONG) >= 0)   
1108                                 ret = replyBuf[2] == 0x53;
1109                         cmdComplete();
1110                         return ret;
1111                 }       
1112         }
1113         
1114         /**
1115          * Start a Bluetooth inquiry process
1116          * 
1117          * @param maxDevices the maximum number of devices to discover
1118          * @param timeout the timeout value in units of 1.28 econds
1119          * @param cod the class of device to look for
1120          * @return a vector of all the devices found
1121          */
1122         public static Vector inquire(int maxDevices,  int timeout, byte[] cod) {
1123                 Vector retVec = new Vector();
1124                 byte[] device = new byte[7];
1125                 char[] name = new char[16];
1126                 synchronized (Bluetooth.sync)
1127                 {
1128                         int state = RS_CMD;
1129                         cmdStart();
1130                         cmdInit(MSG_BEGIN_INQUIRY, 8, maxDevices, 0);
1131                         cmdBuf[4] = (byte)timeout;
1132                         System.arraycopy(cod, 0, cmdBuf, 5, 4); 
1133                         while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
1134                         {
1135                                 state = RS_WAIT;
1136                                 if (replyBuf[1] == MSG_INQUIRY_RESULT)
1137                                 {
1138                                         System.arraycopy(replyBuf, 2, device, 0, 7);
1139                                         int nameLen = 0;
1140                                         for(;nameLen<16 && replyBuf[9+nameLen] != 0;nameLen++)
1141                                                 name[nameLen] = (char) replyBuf[9+nameLen];
1142                                         System.arraycopy(replyBuf, 25, cod, 0, 4);
1143                                         // add the Element to the Vector List
1144                                         retVec.addElement(new BTRemoteDevice(name, nameLen, device, cod));                                      
1145                                 }
1146                                 else if (replyBuf[1] == MSG_INQUIRY_STOPPED)
1147                                 {
1148                                         cmdComplete();
1149                                         // Fill in the names    
1150                                         for (int i = 0; i < retVec.size(); i++) {
1151                                                 BTRemoteDevice btrd = ((BTRemoteDevice) retVec.elementAt(i));
1152                                                 String s = btrd.getFriendlyName();
1153                                                 if (s.length() == 0) {
1154                                                         String nm = lookupName(btrd.getDeviceAddr());
1155                                                         btrd.setFriendlyName(nm.toCharArray(),nm.length());
1156                                                 }
1157                                         }
1158                                         return retVec;
1159                                 }
1160                         }
1161                         cmdComplete();
1162                         return retVec;
1163                 }                       
1164         }
1165         
1166         /**
1167          * Look up the name of a device using its address
1168          * 
1169          * @param addr device address
1170          * @return friendly name of device
1171          */
1172         public static String lookupName(byte [] addr) {
1173                 char[] name = new char[16];
1174                 synchronized (Bluetooth.sync)
1175                 {
1176                         String ret = "";
1177                         int state = RS_CMD;
1178                         cmdStart();
1179                         cmdInit(MSG_LOOKUP_NAME, 8, 0, 0);
1180                         System.arraycopy(addr, 0, cmdBuf, 2, 7);        
1181                         while (cmdWait(RS_REPLY, state, MSG_ANY, TO_LONG) >= 0)
1182                         {
1183                                 state = RS_WAIT;
1184                                 if (replyBuf[1] == MSG_LOOKUP_NAME_RESULT)
1185                                 {
1186                                         int len = 0;
1187                                         for(; len < 16 && replyBuf[len+9] != 0; len++)
1188                                                 name[len] = (char)replyBuf[len+9];
1189                                         ret = new String(name, 0, len);
1190                                         break;
1191                                 }
1192                                 else if (replyBuf[1] == MSG_LOOKUP_NAME_FAILURE)
1193                                         break;
1194                                 
1195                         }
1196                         cmdComplete();
1197                         return ret;
1198                 }                       
1199         }
1200                 
1201         
1202         /**
1203          * Get the status of all connections
1204          * 
1205          * @return byte array of status for each handle
1206          */
1207         public static byte[] getConnectionStatus() {
1208                 byte[] result = new byte[4];
1209                 //1 Debug.out("getConnectionStatus\n");
1210                 synchronized (Bluetooth.sync)
1211                 {
1212                         cmdStart();
1213                         cmdInit(MSG_GET_CONNECTION_STATUS, 1, 0, 0);
1214                         if (cmdWait(RS_REPLY, RS_CMD, MSG_CONNECTION_STATUS_RESULT, TO_SHORT) < 0)      
1215                                 result = null;
1216                         else
1217                                 System.arraycopy(replyBuf, 5, result, 0, 4);
1218                         cmdComplete();
1219                         return result;
1220                 }
1221         }
1222         
1223         /**
1224          * Get the major and minor version of the BlueCore code
1225          * 
1226          * @return an array of two bytes: major version, minor version
1227          */
1228         public static byte[] getVersion() {
1229                 byte [] version = new byte[2];
1230                 //1 Debug.out("getVersion\n");
1231                 synchronized (Bluetooth.sync)
1232                 {
1233                         cmdStart();
1234                         cmdInit(MSG_GET_VERSION, 1, 0, 0);
1235                         if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_VERSION_RESULT, TO_SHORT) < 0)    
1236                                 version = null;
1237                         else
1238                                 System.arraycopy(replyBuf, 2, version, 0, 2);
1239                         cmdComplete();
1240                         return version;
1241                 }
1242         }
1243         
1244         /**
1245          * Get the persistent status value from the BC4 chip
1246          * 
1247          * @return the byte value
1248          */
1249         public static int getStatus() {
1250                 //1 Debug.out("getStatus\n");
1251                 synchronized (Bluetooth.sync)
1252                 {
1253                         int ret = -1;
1254                         cmdStart();
1255                         cmdInit(MSG_GET_BRICK_STATUSBYTE, 1, 0, 0);
1256                         if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0)  
1257                                 ret = ((int)replyBuf[2] & 0xff) | (((int)replyBuf[3] & 0xff) << 8);
1258                         cmdComplete();
1259                         return ret;
1260                 }
1261         }
1262
1263         /**
1264          * Set the persistent status byte for the BC4 chip
1265          * 
1266          * @param status the byte status value
1267          * @return < 0 Error
1268          */
1269         public static int setStatus(int status) {
1270                 //1 Debug.out("setStatus\n");
1271                 synchronized (Bluetooth.sync)
1272                 {
1273                         int ret = -1;
1274                         cmdStart();
1275                         cmdInit(MSG_SET_BRICK_STATUSBYTE, 3, status & 0xff, (status >> 8) & 0xff);
1276                         if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_BRICK_STATUSBYTE_RESULT, TO_SHORT) >= 0)  
1277                                 ret = 0;
1278                         cmdComplete();
1279                         return ret;
1280                 }
1281         }
1282         
1283         /**
1284          * Get the visibility (discoverable) status of the device
1285          * 
1286          * @return 1 = visible, 0 = invisible
1287          */
1288         public static int getVisibility() {
1289                 //1 Debug.out("getVisibility\n");
1290                 synchronized (Bluetooth.sync)
1291                 {
1292                         int ret = -1;
1293                         cmdStart();
1294                         cmdInit(MSG_GET_DISCOVERABLE, 1, 0, 0);
1295                         if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_DISCOVERABLE_RESULT, TO_SHORT) >= 0)      
1296                                 ret = replyBuf[2];
1297                         cmdComplete();
1298                         return ret;
1299                 }
1300         }
1301         
1302         /**
1303          * Get the port open status, 
1304          * i.e whether connections are being accepted
1305          * 
1306          * @return 1 if the port is open, 0 otherwise
1307          */
1308         public static int getPortOpen() {
1309                 //1 Debug.out("getPortOpen\n");
1310                 synchronized (Bluetooth.sync)
1311                 {
1312                         int ret = -1;
1313                         cmdStart();
1314                         cmdInit(MSG_GET_PORT_OPEN, 1, 0, 0);
1315                         if (cmdWait(RS_REPLY, RS_CMD, MSG_GET_PORT_OPEN_RESULT, TO_SHORT) >= 0) 
1316                                 ret = replyBuf[2];
1317                         cmdComplete();
1318                         return ret;
1319                 }       
1320         }
1321         
1322         /**
1323          * Get the operating mode (stream breaking or not) 
1324          * 
1325          * @return 0 = stream breaking mode, 1 = don't break stream mode
1326          *                 < 0 Error
1327          */
1328         public static int getOperatingMode() {
1329                 //1 Debug.out("getOperatingMode\n");
1330                 synchronized (Bluetooth.sync)
1331                 {
1332                         int ret = -1;
1333                         cmdStart();
1334                         cmdInit(MSG_GET_OPERATING_MODE, 1, 0, 0);
1335                         if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0)        
1336                                 ret = replyBuf[2];
1337                         cmdComplete();
1338                         return ret;
1339                 }       
1340         }
1341         
1342         /**
1343          * Set Bluetooth visibility (discoverable) on or off for the local device
1344          * 
1345          * @param visible true to set visibility on, false to set it off
1346          * @return < 0 error 
1347          */
1348         public static int setVisibility(byte visible) {
1349                 //1 Debug.out("setVisibility\n");
1350                 synchronized (Bluetooth.sync)
1351                 {
1352                         int ret = -1;
1353                         cmdStart();
1354                         cmdInit(MSG_SET_DISCOVERABLE, 2, visible, 0);
1355                         if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_DISCOVERABLE_ACK, TO_SHORT) >= 0) 
1356                                 ret = 0;
1357                         cmdComplete();
1358                         return ret;
1359                 }       
1360         }
1361         
1362         /**
1363          * Reset the settings of the BC4 chip to the factory defaults.
1364          * The NXT should be restarted after this.
1365          *
1366          */
1367         public static int setFactorySettings() {
1368                 //1 Debug.out("setFactorySettings\n");
1369                 synchronized (Bluetooth.sync)
1370                 {
1371                         int ret = -1;
1372                         cmdStart();
1373                         cmdInit(MSG_SET_FACTORY_SETTINGS, 1, 0, 0);
1374                         if (cmdWait(RS_REPLY, RS_CMD, MSG_SET_FACTORY_SETTINGS_ACK, TO_SHORT) >= 0)     
1375                                 ret = 0;
1376                         cmdComplete();
1377                         return ret;
1378                 }
1379         }
1380         
1381         /**
1382          * Set the operating mode
1383          * 
1384          * @param mode 0 = Stream breaking, 1 don't break stream 
1385          * @return      < 0 error
1386          */
1387         public static int setOperatingMode(byte mode) {
1388                 //1 Debug.out("setOperatingMode\n");
1389                 synchronized (Bluetooth.sync)
1390                 {
1391                         int ret = -1;
1392                         cmdStart();
1393                         cmdInit(MSG_SET_OPERATING_MODE, 2, mode, 0);
1394                         if (cmdWait(RS_REPLY, RS_CMD, MSG_OPERATING_MODE_RESULT, TO_SHORT) >= 0)        
1395                                 ret = 0;
1396                         cmdComplete();
1397                         return ret;
1398                 }
1399         }
1400         
1401         /**
1402          * Force a reset of the Bluetooth module.
1403          * Notes:
1404          * After this call power will be on.
1405          * Any existing connections will be closed
1406          * Any listening threads will be aborted
1407          * 
1408          */     
1409         public static void reset()
1410         {
1411                 synchronized (Bluetooth.sync)
1412                 {
1413                         cmdStart();
1414                         // Force a timeout and hence a reset
1415                         cmdWait(RS_REPLY, RS_WAIT, MSG_RESET_INDICATION, TO_FORCERESET);
1416                         cmdComplete();
1417                 }               
1418         }
1419         
1420         /**
1421          * Set the power to the module
1422          * 
1423          * @param on power on or off 
1424          */
1425         public static void setPower(boolean on)
1426         {
1427                 synchronized (Bluetooth.sync)
1428                 {
1429                         if (powerOn == on) return;
1430                         if (on)
1431                         {
1432                                 btSetResetHigh();
1433                                 powerOn = true;
1434                                 // Now make sure things have settled
1435                                 for(int i =0; i < 5; i++)
1436                                         if (getOperatingMode() >= 0) break;
1437                         }
1438                         else
1439                         {
1440                                 // Powering off. Do we need to reset things?
1441                                 boolean wasListening = listening;
1442                                 // Wait for any other commands to complete
1443                                 cmdStart();
1444                                 if (connected > 0 || listening)
1445                                         reset();
1446                                 // Wait for the listening thread to exit
1447                                 if (wasListening)
1448                                         try{Bluetooth.sync.wait(2000);}catch(Exception e){}
1449                                 //1 Debug.out("Power going off\n");
1450                                 btSetResetLow();
1451                                 powerOn = false;
1452                         }
1453                         publicPowerOn = powerOn;
1454                 }
1455         }
1456
1457         /**
1458          * Return the current state of the module power
1459          * 
1460          * @return power on or off 
1461          */
1462         public static boolean getPower()
1463         {
1464                 synchronized(Bluetooth.sync)
1465                 {
1466                         return publicPowerOn;
1467                 }
1468         }
1469         
1470
1471         public static int getResetCount()
1472         {
1473                 return resetCnt;
1474         }
1475         /**
1476          * The following are provided for compatibility with the old Bluetooth
1477          * class. They should not be used, in new programs and should probably
1478          * be removed.
1479          */
1480         
1481         /**
1482          * Read a packet from the stream. Do not block and for small packets
1483          * (< 254 bytes), do not return a partial packet.
1484          * @param       buf             Buffer to read data into.
1485          * @param       len             Number of bytes to read.
1486          * @return                      > 0 number of bytes read.
1487          *                                      other values see read.
1488          */
1489         public static int readPacket(byte buf[], int len)
1490         {
1491                 return Chans[3].readPacket(buf, len);
1492         }
1493         
1494         /**
1495          * Send a data packet.
1496          * Must be in data mode.
1497          * @param buf the data to send
1498          * @param bufLen the number of bytes to send
1499          */
1500         public static void sendPacket(byte [] buf, int bufLen)
1501         {
1502                 Chans[3].sendPacket(buf, bufLen);
1503         }
1504         
1505         /**
1506          * Set the BC4 mode, and wait for that mode to be confirmed by the chip.
1507          *
1508          * @param mode the requested mode 1 == Command mode 0 == Stream mode
1509          */
1510         public static void btSetCmdMode(int mode)
1511         {
1512                 
1513         }
1514
1515 }