Source code: com/synchrona/jred/irlap/IrLAPFramer.java
1 /*
2 **************************************************************************
3 ** $Header: /cvsroot/jred/jred/src/com/synchrona/jred/irlap/IrLAPFramer.java,v 1.1.1.1 2000/07/05 04:41:52 mpatters Exp $
4 **
5 ** Copyright (C) 2000 Synchrona, Inc. All rights reserved.
6 **
7 ** This file is part of JRed, a 100% Java implementation of the IrDA
8 ** infrared communications protocols.
9 **
10 ** This file may be distributed under the terms of the Synchrona Public
11 ** License as defined by Synchrona, Inc. and appearing in the file
12 ** LICENSE included in the packaging of this file. The Synchrona Public
13 ** License is based on the Q Public License as defined by Troll Tech AS
14 ** of Norway; it differs only in its use of the courts of Florida, USA
15 ** rather than those of Oslo, Norway.
16 **************************************************************************
17 */
18 package com.synchrona.jred.irlap;
19
20 import com.synchrona.util.Log;
21 import java.io.DataInputStream;
22 import java.io.DataOutputStream;
23 import javax.comm.SerialPort;
24
25 public class IrLAPFramer implements Runnable {
26 private static final byte COMMAND_DISCONNECT = (byte) 0x43;
27 private static final byte BROADCAST_ADDRESS = (byte) 0xFE;
28 private static final int COMM_PARAMETER_ARRAY_LENGTH = 21;
29 private static final byte FINAL_SLOT = (byte) 0xFF;
30 private static final byte INFORMATION_FORMAT = (byte) 0x00;
31 private static final byte IRLAP_VERSION = (byte) 0x00;
32 private static final byte MASK_CR_BIT = (byte) 0x01;
33 private static final byte MASK_NR_BITS = (byte) 0xE0;
34 private static final byte MASK_NS_BITS = (byte) 0x0E;
35 private static final byte MASK_PF_BIT = (byte) 0x10;
36 private static final byte MASK_SUPERVISORY_CONTROLS = (byte) 0x0F;
37 private static final byte RR_CONTROL = (byte) 0x01;
38 private static final byte SNRM_COMMAND = (byte) 0x83;
39 private static final byte SUPERVISORY_FORMAT = (byte) 0x01;
40 private static final byte UA_RESPONSE = (byte) 0x63;
41 private static final byte UNNUMBERED_FORMAT = (byte) 0x03;
42 private static final byte XID_COMMAND = (byte) 0x2F;
43 private static final byte XID_FORMAT = (byte) 0x01;
44 private static final byte XID_RESPONSE = (byte) 0xAF;
45
46 private byte [] m_ayCommParameters;
47 private byte [] m_ayOutputFrame;
48 private boolean m_bDoLogging;
49 private iIrLAPFramerListener m_listener;
50 private Log m_log;
51 private SerialPort m_port;
52 private iFrameWrapper m_wrapper;
53
54 public IrLAPFramer(Log log, SerialPort port) throws Exception {
55 m_ayCommParameters = new byte[COMM_PARAMETER_ARRAY_LENGTH];
56 m_ayOutputFrame = new byte[AsyncFrameWrapper.MAX_FRAME_LENGTH];
57 m_bDoLogging = false;
58 m_listener = null;
59 m_log = log;
60 m_port = port;
61
62 // default IrLAP serial parameters
63 m_port.setSerialPortParams(Integer.getInteger("portSpeed", 9600).intValue(),
64 SerialPort.DATABITS_8,
65 SerialPort.STOPBITS_1,
66 SerialPort.PARITY_NONE);
67 // readFrame() needs to block while data is unavailable
68 m_port.disableReceiveThreshold();
69 m_port.disableReceiveTimeout();
70
71 m_port.setRTS(true);
72 m_port.setDTR(false);
73
74 m_wrapper = new AsyncFrameWrapper(log,
75 new DataInputStream(port.getInputStream()),
76 new DataOutputStream(port.getOutputStream()));
77
78 }
79
80 public void addIrLAPFramerListener(iIrLAPFramerListener listener) throws Exception {
81 if ( null != m_listener ) {
82 throw new Exception("This IrLAPFramer already has a listener.");
83 } else {
84 m_listener = listener;
85 }
86 }
87
88 public void initialize() {
89 m_port.setRTS(true);
90 m_port.setDTR(false);
91 }
92
93 public void run() {
94 boolean bContinue = true;
95 byte [] ayFrame = new byte[AsyncFrameWrapper.MAX_FRAME_LENGTH];
96
97 while ( bContinue ) {
98 try {
99 int nBytesRead = readFrame(ayFrame);
100 if ( nBytesRead > 0 ) {
101 dispatchFrame(ayFrame, nBytesRead);
102 }
103 } catch ( Exception e ) {
104 System.err.println(e);
105 e.printStackTrace(System.err);
106 }
107 }
108 }
109
110 public void sendI(byte yConnection, int nNr, int nNs, boolean bFinal, byte [] ayData, int nOffset, int nLength) throws Exception {
111 m_log.debug("IrLAPFramer", "sendI Nr=" + nNr + " Ns=" + nNs);
112 int nPosition = 0;
113
114 m_ayOutputFrame[nPosition++] = (byte) (yConnection << 1);
115 m_ayOutputFrame[nPosition++] = (byte) ((nNr << 5 ) | (bFinal ? 0x10 : 0x00) | (nNs << 1));
116 for ( int i = 0; i < nLength; i++ ) {
117 m_ayOutputFrame[nPosition++] = ayData[nOffset + i];
118 }
119
120 m_wrapper.send(m_ayOutputFrame, 0, nPosition);
121 }
122
123 public void sendRR(byte yConnection, int nNr, boolean bCommand, boolean bFinal) throws Exception {
124 m_log.debug("IrLAPFramer", "sendRR");
125 int nPosition = 0;
126
127 m_ayOutputFrame[nPosition++] = (byte) ((yConnection << 1) | ( bCommand ? 0x01 : 0x00 ));
128 m_ayOutputFrame[nPosition++] = (byte) ((nNr << 5 ) | (bFinal ? 0x10 : 0x00) | RR_CONTROL);
129
130 m_wrapper.send(m_ayOutputFrame, 0, nPosition);
131 }
132
133 public void sendSNRM(int nSource, int nDestination, byte yConnection) throws Exception {
134 m_log.debug("IrLAPFramer", "sendSNRM");
135 int nPosition = 0;
136
137 m_ayOutputFrame[nPosition++] = (byte) (BROADCAST_ADDRESS | MASK_CR_BIT);
138 m_ayOutputFrame[nPosition++] = (byte) (SNRM_COMMAND | MASK_PF_BIT);
139
140 nPosition = intToBytes(m_ayOutputFrame, nPosition, nSource);
141 nPosition = intToBytes(m_ayOutputFrame, nPosition, nDestination);
142
143 m_ayOutputFrame[nPosition++] = (byte) ((yConnection << 1) & 0xFE);
144
145 m_ayOutputFrame[nPosition++] = (byte) 0x01; // (1) baud rate
146 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
147 m_ayOutputFrame[nPosition++] = (byte) 0x02; // 9600 bps
148
149 m_ayOutputFrame[nPosition++] = (byte) 0x82; // (2) max turnaround time
150 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
151 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
152
153 m_ayOutputFrame[nPosition++] = (byte) 0x83; // (3) data size
154 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
155 m_ayOutputFrame[nPosition++] = (byte) 0x01; // 500ms
156
157 m_ayOutputFrame[nPosition++] = (byte) 0x84; // (4) window size
158 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
159 m_ayOutputFrame[nPosition++] = (byte) 0x01; // stop-and-wait; 1 frame/window
160
161 m_ayOutputFrame[nPosition++] = (byte) 0x85; // (5) additional BOF's
162 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
163 m_ayOutputFrame[nPosition++] = (byte) 0x01; // 48 BOF's @ 115200
164
165 m_ayOutputFrame[nPosition++] = (byte) 0x86; // (6) min turnaround time
166 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
167 m_ayOutputFrame[nPosition++] = (byte) 0x01; // 3 seconds
168
169 m_ayOutputFrame[nPosition++] = (byte) 0x08; // (7) link disconnect time
170 m_ayOutputFrame[nPosition++] = (byte) 0x01; // data is 1 byte in length
171 m_ayOutputFrame[nPosition++] = (byte) 0x80; // 40 seconds
172
173 m_wrapper.send(m_ayOutputFrame, 0, nPosition);
174 }
175
176 public void sendUA(int nSource, int nDestination, byte yConnection, boolean bSendParameters) throws Exception {
177 m_log.debug("IrLAPFramer", "sendUA");
178
179 int nPosition = 0;
180
181 m_ayOutputFrame[nPosition++] = (byte) ((yConnection << 1) & 0xFE);
182 m_ayOutputFrame[nPosition++] = (byte) (UA_RESPONSE | MASK_PF_BIT);
183
184 nPosition = intToBytes(m_ayOutputFrame, nPosition, nSource);
185 nPosition = intToBytes(m_ayOutputFrame, nPosition, nDestination);
186
187 if ( bSendParameters ) {
188 for ( int i =0; i < m_ayCommParameters.length; i++ ) {
189 m_ayOutputFrame[nPosition++] = m_ayCommParameters[i];
190 }
191 }
192
193 m_wrapper.send(m_ayOutputFrame, 0, nPosition);
194 }
195
196 public void sendXID(int nSource, int nDestination, boolean bCommand, byte ySlot, byte [] ayDiscoveryInfo) throws Exception {
197 m_log.debug("IrLAPFramer", "sendXID");
198 int nPosition = 0;
199
200 if ( bCommand ) {
201 m_ayOutputFrame[nPosition++] = (byte) (BROADCAST_ADDRESS | MASK_CR_BIT);
202 m_ayOutputFrame[nPosition++] = (byte) (XID_COMMAND | MASK_PF_BIT);
203 } else {
204 m_ayOutputFrame[nPosition++] = BROADCAST_ADDRESS;
205 m_ayOutputFrame[nPosition++] = (byte) (XID_RESPONSE | MASK_PF_BIT);
206 }
207 m_ayOutputFrame[nPosition++] = XID_FORMAT;
208
209 m_ayOutputFrame[nPosition++] = (byte) ((nSource & 0xFF000000) >>> 24);
210 m_ayOutputFrame[nPosition++] = (byte) ((nSource & 0x00FF0000) >>> 16);
211 m_ayOutputFrame[nPosition++] = (byte) ((nSource & 0x0000FF00) >>> 8);
212 m_ayOutputFrame[nPosition++] = (byte) ((nSource & 0x000000FF));
213
214 m_ayOutputFrame[nPosition++] = (byte) ((nDestination & 0xFF000000) >>> 24);
215 m_ayOutputFrame[nPosition++] = (byte) ((nDestination & 0x00FF0000) >>> 16);
216 m_ayOutputFrame[nPosition++] = (byte) ((nDestination & 0x0000FF00) >>> 8);
217 m_ayOutputFrame[nPosition++] = (byte) ((nDestination & 0x000000FF));
218
219 // Flag field tells how many slots per discovery; 0x01 means 6 and
220 // is what PalmOS devices do.
221 m_ayOutputFrame[nPosition++] = (byte) 0x01;
222 m_ayOutputFrame[nPosition++] = ySlot;
223 m_ayOutputFrame[nPosition++] = IRLAP_VERSION;
224
225 m_log.debug("IrLAPFramer", "FINAL_SLOT " + FINAL_SLOT + " ySlot " + ySlot + " disco length " + ayDiscoveryInfo.length);
226
227 // Plug in discovery hints
228 if ( FINAL_SLOT == ySlot ) {
229 for ( int i = 0; i < ayDiscoveryInfo.length; i++ ) {
230 m_ayOutputFrame[nPosition++] = ayDiscoveryInfo[i];
231 }
232 }
233
234 m_wrapper.send(m_ayOutputFrame, 0, nPosition);
235 }
236
237 public void setConnectionParameters(byte [] ayParameters) throws Exception {
238 m_log.debug("IrLAPFramer", "[setConnectionParameters] do nothing for now");
239
240 int i = 0;
241
242 m_ayCommParameters[i++] = (byte) 0x01; // (1) baud rate
243 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
244 m_ayCommParameters[i++] = (byte) 0x02; // 9600 bps
245
246 m_ayCommParameters[i++] = (byte) 0x82; // (2) max turnaround time
247 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
248 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
249
250 m_ayCommParameters[i++] = (byte) 0x83; // (3) data size
251 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
252 m_ayCommParameters[i++] = (byte) 0x01; // 500ms
253
254 m_ayCommParameters[i++] = (byte) 0x84; // (4) window size
255 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
256 m_ayCommParameters[i++] = (byte) 0x01; // stop-and-wait; 1 frame/window
257
258 m_ayCommParameters[i++] = (byte) 0x85; // (5) additional BOF's
259 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
260 m_ayCommParameters[i++] = (byte) 0x01; // 48 BOF's @ 115200
261
262 m_ayCommParameters[i++] = (byte) 0x86; // (6) min turnaround time
263 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
264 m_ayCommParameters[i++] = (byte) 0x01; // 3 seconds
265
266 m_ayCommParameters[i++] = (byte) 0x08; // (7) link disconnect time
267 m_ayCommParameters[i++] = (byte) 0x01; // data is 1 byte in length
268 m_ayCommParameters[i++] = (byte) 0x80; // 40 seconds
269 }
270
271 //---------------------------------------------------------------
272 // End public methods
273 //---------------------------------------------------------------
274
275 private int bytesToInt(byte [] ayBytes, int nOffset) {
276 return ((ayBytes[nOffset ] << 24) & 0xFF000000)
277 | ((ayBytes[nOffset + 1] << 16) & 0x00FF0000)
278 | ((ayBytes[nOffset + 2] << 8) & 0x0000FF00)
279 | ( ayBytes[nOffset + 3] & 0x000000FF);
280 }
281
282 private void dispatchFrame(byte [] ayFrame, int nLength) throws Exception {
283 // 0 bit of address field indicates whether this frame is a
284 // command (bit 0 = 1) or a response (bit 0 = 0)
285 byte yConnection = (byte) (ayFrame[0] >> 1);
286 boolean bCommand = ((ayFrame[0] & MASK_CR_BIT) != 0) ? true : false;
287 boolean bPoll = ((ayFrame[1] & MASK_PF_BIT) != 0) ? true : false;
288 byte yControl = ayFrame[1];
289
290 if ( UNNUMBERED_FORMAT == (yControl & UNNUMBERED_FORMAT) ) {
291 switch ( yControl & ~MASK_PF_BIT ) {
292 case SNRM_COMMAND:
293 dispatchSNRM(ayFrame, bCommand, nLength);
294 break;
295 case XID_COMMAND: // fall through
296 case XID_RESPONSE:
297 dispatchXID(ayFrame, bCommand, nLength);
298 break;
299 case COMMAND_DISCONNECT:
300 dispatchDISC(yConnection, bCommand, bPoll, ayFrame, nLength);
301 break;
302 default:
303 m_log.debug("IrLAPFramer", "Unimplemented control: " + (yControl & ~MASK_PF_BIT));
304 break;
305 }
306 } else if ( SUPERVISORY_FORMAT == (yControl & SUPERVISORY_FORMAT) ) {
307 int nNr = (yControl & MASK_NR_BITS) >> 5;
308
309 switch ( yControl & MASK_SUPERVISORY_CONTROLS ) {
310 case RR_CONTROL:
311 dispatchRR(yConnection, nNr, bCommand, bPoll);
312 break;
313 default:
314 m_log.debug("IrLAPFramer", "Unimplemented supervisory control: " + (yControl & MASK_SUPERVISORY_CONTROLS));
315 break;
316 }
317 } else if ( INFORMATION_FORMAT == (yControl & INFORMATION_FORMAT) ) {
318 int nNr = (yControl & MASK_NR_BITS) >> 5;
319 int nNs = (yControl & MASK_NS_BITS) >> 1;
320
321 dispatchInformation(yConnection, bCommand, bPoll, nNs, nNr, ayFrame, nLength);
322 }
323 }
324
325 private void dispatchDISC(byte yConnection, boolean bCommand, boolean bPoll, byte [] ayFrame, int nLength) throws Exception {
326 m_log.debug("IrLAPFramer", "dispatchDISC");
327
328 if ( m_listener != null ) {
329 m_listener.handleDisconnect(yConnection, bCommand, bPoll);
330 }
331 }
332
333 private void dispatchInformation(byte yConnection, boolean bCommand, boolean bPoll, int nNs, int nNr, byte [] ayFrame, int nLength) throws Exception {
334 m_log.debug("IrLAPFramer", "dispatchInformation");
335
336 if ( m_listener != null ) {
337 byte [] ayData = new byte[nLength - 2];
338 for ( int nSource = 2, nDestination = 0; nSource < nLength; nSource++, nDestination++ ) {
339 ayData[nDestination] = ayFrame[nSource];
340 }
341
342 m_listener.handleData(yConnection, bCommand, nNs, nNr, bPoll, ayData);
343 }
344 }
345
346 private void dispatchRR(byte yConnection, int nNr, boolean bCommand, boolean bPoll) throws Exception {
347 m_log.debug("IrLAPFramer", "dispatchRR");
348
349 if ( null != m_listener ) {
350 m_listener.handleRR(yConnection, nNr, bCommand, bPoll);
351 }
352 }
353
354 private void dispatchSNRM(byte [] ayFrame, boolean bCommand, int nLength) throws Exception {
355 m_log.debug("IrLAPFramer", "dispatchSNRM");
356
357 if ( null != m_listener ) {
358 int nSource = bytesToInt(ayFrame, 2);
359 int nDestination = bytesToInt(ayFrame, 6);
360 byte yConnection = (byte) (ayFrame[10] >>> 1);
361 byte [] ayParameters = new byte[nLength - 11];
362
363 for ( int i = 0, j = 11; j < nLength; i++, j++ ) {
364 ayParameters[i] = ayFrame[j];
365 }
366
367 m_listener.handleSNRM(nSource, nDestination, yConnection, ayParameters);
368 }
369 }
370
371 private void dispatchXID(byte [] ayFrame, boolean bCommand, int nLength) throws Exception {
372 if ( (nLength < 14) || (ayFrame.length < 14) ) {
373 throw new Exception("XID frames must be at least 14 bytes long. (length=" + nLength + ",array length=" + ayFrame.length);
374 }
375
376 int i = 3; // skip past address, XID command, and format
377
378 /*
379 if ( ayFrame[i++] != XID_FORMAT ) {
380 //throw new Exception("XID format identifier is supposed to be " + XID_FORMAT + ".");
381 m_log.debug("dispatchXID", "XID format identifier is supposed to be " + XID_FORMAT + ".");
382 }
383 */
384
385 int nSource = bytesToInt(ayFrame, i);
386 i += 4;
387 m_log.debug("dispatchXID", "source: " + nSource);
388
389 int nDestination = bytesToInt(ayFrame, i);
390 i += 4;
391 m_log.debug("dispatchXID", "destination: " + nDestination);
392
393 // TBD: Decode flags. These are okay for PalmOS.
394 byte yFlags = ayFrame[i++];
395 byte yNumSlots = 6;
396 byte ySlot = ayFrame[i++];
397 byte yVersion = ayFrame[i++];
398 byte [] ayHints = null;
399
400 if ( FINAL_SLOT == ySlot ) {
401 m_log.debug("IrLAPFramer", "nLength " + nLength + " i " + i);
402 int nHintLength = nLength - i;
403 if ( nHintLength > 0 ) {
404 ayHints = new byte[nLength - i];
405 for ( int j = 0; j < ayHints.length; j++, i++ ) {
406 ayHints[j] = ayFrame[i];
407 }
408 }
409 }
410
411 if ( null != m_listener ) {
412 m_listener.handleXID(nSource, nDestination, yNumSlots, ySlot, bCommand, ayHints);
413 }
414 }
415
416 /**
417 * Write an integer in network order to the given array of bytes at
418 * the given offset.
419 * @return Returns the offset plus the size of an integer (4 bytes)
420 */
421 private int intToBytes(byte [] ayBytes, int nOffset, int nValue) throws Exception {
422 if ( nOffset < 0 ) {
423 throw new Exception("Negative offset.");
424 }
425 if ( (ayBytes.length - nOffset) < 4 ) {
426 throw new Exception("Writing values at this offset would overflow the array bounds.");
427 }
428
429 ayBytes[nOffset++] = (byte) ((nValue & 0xFF000000) >>> 24);
430 ayBytes[nOffset++] = (byte) ((nValue & 0x00FF0000) >>> 16);
431 ayBytes[nOffset++] = (byte) ((nValue & 0x0000FF00) >>> 8);
432 ayBytes[nOffset++] = (byte) ((nValue & 0x000000FF));
433
434 return nOffset;
435 }
436
437 private int readFrame(byte [] ayFrame) throws Exception {
438 int nBytesRead = m_wrapper.receive(ayFrame, 0, ayFrame.length);
439 return nBytesRead;
440 }
441 }