Source code: com/synchrona/jred/irlap/IrLAPContext.java
1 /*
2 **************************************************************************
3 ** $Header: /cvsroot/jred/jred/src/com/synchrona/jred/irlap/IrLAPContext.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 com.synchrona.util.Utilities;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.io.PrintWriter;
25 import java.util.Random;
26 import javax.swing.Timer;
27
28 /**
29 * IrLAPContext offers services for the IrLAP states.
30 * Log messages mimic directives in the IrLAP specification, which
31 * helps in debuggering.
32 */
33 public class IrLAPContext implements iIrLAPFramerListener {
34 public static final int DEFAULT_F_TIMEOUT_MSEC = 500;
35 public static final int DEFAULT_MIN_TURNAROUND_MSEC = 5; // was 10
36 public static final int DEFAULT_QUERY_TIMEOUT_MSEC = 500;
37 public static final int DEFAULT_WATCHDOG_TIMEOUT_MSEC = 5000;
38 public static final int DEFAULT_WINDOW_SIZE = 1;
39
40 // TBD: This should really be provided by the service user (IrOBEX) via
41 // IrLMP.
42 private static final byte [] DISCOVERY_INFO = {
43 (byte) 0x82, // means "I am a PDA, and this byte is followed by another"
44 (byte) 0x20, // means "I am a file server and this is the last service hint"
45 (byte) 0x00, // means "ASCII data follows"
46 (byte) 'J', // what follows is a device hint that identifies this station
47 (byte) 'R',
48 (byte) 'e',
49 (byte) 'd',
50 (byte) '0',
51 (byte) '.',
52 (byte) '1'
53 };
54
55 private byte [] m_ayCommParameters = null;
56 private boolean m_bAckRequired = false;
57 private boolean m_bConnectionConfirmed = false;
58 private boolean m_bFrameSent = false;
59 private boolean m_bMediaBusy = false;
60 private boolean m_bRemoteBusy = false;
61 private ConnectionLog m_connectionLog = null;
62 private DiscoveryLog m_discoveryLog = null;
63 private IrLAPFramer m_framer = null;
64 private Timer m_fTimer = null;
65 private iIrLAPListener m_listener = null;
66 private Log m_log = null;
67 private int m_nFTimeout = DEFAULT_F_TIMEOUT_MSEC;
68 private int m_nMyAddress = 12345678;
69 private int m_nQueryTimeout = DEFAULT_QUERY_TIMEOUT_MSEC;
70 private int m_nRemoteAddress = 0;
71 private int m_nRetryCount = 0;
72 private int m_nWatchdogTimeout = DEFAULT_WATCHDOG_TIMEOUT_MSEC;
73 private int m_nVr = 0;
74 private int m_nVs = 0;
75 private int m_nWindowSize = 1;
76 private Timer m_queryTimer = null;
77 private Random m_random = new Random();
78 private IrLAPSendQueue m_sendQueue = new IrLAPSendQueue();
79 private IrLAPState m_state = IrLAPStateNDM.getInstance();
80 private IrLAPContext m_this = null;
81 private Timer m_watchdogTimer = null;
82 private byte m_yConnection = 0;
83 private byte m_yResponseSlot = 2;
84
85 private IrLAPContext() {
86 // No-arg constructor is private so we don't get a null
87 // log device; that would cause NullPointerExceptions
88 // all over the place.
89 }
90
91 public IrLAPContext(Log log) {
92 m_log = log;
93 m_this = this;
94 m_yResponseSlot = (byte) 2;
95 m_connectionLog = new ConnectionLog(m_log);
96 m_discoveryLog = new DiscoveryLog(m_log);
97
98 generateAddress();
99
100 m_fTimer = new Timer(m_nFTimeout, new ActionListener() {
101 public void actionPerformed(ActionEvent action) {
102 try {
103 m_log.debug("IrLAPContext", "F-timer-expired");
104 m_state.handleFTimerExpired(m_this);
105 } catch ( Exception e ) {
106 System.err.println(e);
107 }
108 }
109 });
110
111 m_queryTimer = new Timer(m_nQueryTimeout, new ActionListener() {
112 public void actionPerformed(ActionEvent action) {
113 try {
114 m_log.debug("IrLAPContext", "query-timer-expired");
115 m_state.handleQueryTimerExpired(m_this);
116 } catch ( Exception e ) {
117 System.err.println(e);
118 }
119 }
120 });
121
122 m_watchdogTimer = new Timer(m_nWatchdogTimeout, new ActionListener() {
123 public void actionPerformed(ActionEvent action) {
124 try {
125 m_log.debug("IrLAPContext", "WD-timer-expired");
126 m_state.handleWatchdogTimerExpired(m_this);
127 } catch ( Exception e ) {
128 System.err.println(e);
129 }
130 }
131 });
132 }
133
134 //------------------------------------------------------------------
135 // Users of IrLAPContext instances use these
136 //------------------------------------------------------------------
137
138 public void addIrLAPListener(iIrLAPListener listener) throws Exception {
139 if ( null != m_listener ) {
140 throw new Exception("This IrLAPContext already has a listener.");
141 } else {
142 m_listener = listener;
143 }
144 }
145
146 /**
147 * @deprecated
148 */
149 public void setLog(PrintWriter log) {
150 }
151
152 /**
153 * @deprecated
154 */
155 public void setLogging(boolean bDoLogging) {
156 }
157
158 //------------------------------------------------------------------
159 // Implement iIrLAPFramerListener
160 //------------------------------------------------------------------
161
162 public void handleData(byte yConnection, boolean bCommand, int nNs, int nNr, boolean bPoll, byte [] ayData) throws Exception {
163 m_log.debug("IrLAPContext", "Recv i:" + ( bCommand ? "cmd" : "rsp" ) + ":" + (0x00000007 & nNs) + ":" + (0x00000007 & nNr) + ":" + ( bPoll ? "P" : "~P"));
164 m_state.handleData(this, yConnection, nNr, bCommand, bPoll, ayData);
165 }
166
167 public void handleDisconnect(byte yConnection, boolean bCommand, boolean bPoll) throws Exception {
168 m_log.debug("IrLAPContext", "u:disc:" + (bCommand ? "cmd" : "rsp") + ":" + (bPoll ? "P" : "~P"));
169 m_state.handleDisconnect(this, m_nRemoteAddress, yConnection);
170 }
171
172 public void handleRR(byte yConnection, int nNr, boolean bCommand, boolean bPoll) throws Exception {
173 m_log.debug("IrLAPContext", "Recv s:rr:" + ( bCommand ? "cmd" : "rsp" ) + ":" + (0x00000007 & nNr) + ":" + ( bPoll ? "P" : "~P"));
174 m_state.handleRR(this, yConnection, nNr, bCommand, bPoll);
175 }
176
177 public void handleSNRM(int nSource, int nDestination, byte yConnection, byte [] ayParameters) throws Exception {
178 m_log.debug("IrLAPContext", "Recv u:snrm:cmd:P:" + Utilities.byteToString(yConnection) + ":" + nSource);
179 m_yConnection = yConnection;
180 m_state.handleSNRM(this, nSource, nDestination, yConnection, ayParameters);
181 }
182
183 public void handleXID(int nSource, int nDestination, byte yNumSlots, byte ySlot, boolean bCommand, byte [] ayHints) throws Exception {
184 if ( (byte) 0xFF == ySlot ) {
185 m_log.debug("IrLAPContext", "Recv End-Discovery-XID-Cmd");
186 } else {
187 m_log.debug("IrLAPContext", "Recv Discovery-XID-Cmd:" + yNumSlots + "," + ySlot);
188 }
189 m_state.handleXID(this, nSource, nDestination, ySlot, bCommand, ayHints);
190 }
191
192 //------------------------------------------------------------------
193 // End implementation of iIrLAPFramerListener, start methods to
194 // support IrLAPState
195 //------------------------------------------------------------------
196
197 public void applyConnectionParameters() throws Exception {
198 m_log.debug("IrLAPContext", "\tApply-Connection-Parameters");
199 }
200 /*
201 public void connectionConfirm() {
202 m_log.debug("connection-confirm");
203 }
204
205 public void connectRequest(int nDestination) throws Exception {
206 m_log.debug("Connect-Request(" + nDestination + ")");
207 m_state.connectionRequest(this, nDestination);
208 }
209 */
210 public void connectIndication(int nRemote, byte yConnection, byte [] ayParameters) throws Exception {
211 m_log.debug("IrLAPContext", "\tConnect-Indication");
212
213 ConnectionInformation conn = new ConnectionInformation(yConnection, ayParameters, m_discoveryLog.get(nRemote));
214 m_connectionLog.add(conn);
215
216 if ( null != m_listener ) {
217 m_nRemoteAddress = nRemote;
218 //m_listener.connectIndication(this, nRemote, yConnection, ayParameters);
219 m_listener.connectIndication(this, conn);
220 }
221 }
222
223 public void connectResponse(int nDestination, byte yConnection, byte [] ayParams) throws Exception {
224 m_state.connectResponse(this, nDestination, yConnection, ayParams);
225 }
226
227
228 public void dataIndication(byte yConnection, byte [] ayData) throws Exception {
229 m_log.debug("IrLAPContext", "\tData-Indication");
230 if ( null != m_listener ) {
231 ConnectionInformation connInfo = m_connectionLog.get(yConnection);
232 m_listener.dataIndication(this, connInfo, ayData);
233 }
234 }
235
236 public void discoveryIndication(DiscoveryInformation discoveryInfo) throws Exception {
237 m_log.debug("IrLAPContext", "\tDiscovery-Indication(" + discoveryInfo + ")");
238 m_discoveryLog.add(discoveryInfo);
239 if ( null != m_listener ) {
240 m_listener.discoveryIndication(this, discoveryInfo);
241 }
242 }
243
244 public void flushQueue(byte yConnection) throws Exception {
245 if ( m_sendQueue.pendingDataRequests() ) {
246 for ( int i = 0; i < m_sendQueue.size(); i++ ) {
247 IrLAPFrame nextFrame = m_sendQueue.get(i);
248 byte [] ayNextData = nextFrame.getData();
249 m_log.debug("IrLAPContext", "Data-Request(" + Utilities.bytesToString(ayNextData, 0, ayNextData.length) + ")");
250 m_framer.sendI(yConnection, m_nVr, nextFrame.getSequenceNumber(), true, ayNextData, 0, ayNextData.length);
251 }
252 }
253 }
254
255 public void generateAddress() {
256 m_nMyAddress = m_random.nextInt();
257 m_log.debug("IrLAPContext", "\tGenerate-Random-Address => " + m_nMyAddress);
258 }
259
260 public byte generateConnectionAddress() throws Exception {
261 byte yConnection = (byte) 0x35;
262 m_log.debug("IrLAPContext", "\tGenerate-Random-ConnectionAdr(" + yConnection + ")");
263 return yConnection;
264 }
265
266 public byte generateResponseSlot(byte yNumSlots, byte ySlot) throws Exception {
267 m_yResponseSlot = (byte) (m_random.nextInt() % (yNumSlots - ySlot) + ySlot);
268 if ( m_yResponseSlot < 0 ) {
269 m_yResponseSlot *= -1;
270 }
271 m_log.debug("IrLAPContext", "\tslot := Generate-Random-Time-Slot(" + yNumSlots + "," + ySlot + ") => " + m_yResponseSlot);
272 return m_yResponseSlot;
273 }
274
275 public byte getConnection() {
276 return m_yConnection;
277 }
278
279 public byte getResponseSlot() {
280 return m_yResponseSlot;
281 }
282
283 public int getVr() {
284 return m_nVr;
285 }
286
287 public int getVs() {
288 return m_nVs;
289 }
290
291 public void incrementVr() throws Exception {
292 m_log.debug("IrLAPContext", "\tVr := (Vr + 1) % 8");
293 m_nVr = (m_nVr + 1) % 8;
294 }
295
296 public void initializeConnectionState() throws Exception {
297 m_log.debug("IrLAPContext", "\tInitialize-Connection-State");
298
299 m_bFrameSent = false;
300 m_bRemoteBusy = false;
301 m_nRetryCount = 0;
302 m_nVr = 0;
303 m_nVs = 0;
304 m_nWindowSize = DEFAULT_WINDOW_SIZE;
305
306 //m_framer.initialize();
307 }
308
309 public boolean isConnectionConfirmed() {
310 return m_bConnectionConfirmed;
311 }
312
313 public boolean isFrameSent() {
314 return m_bFrameSent;
315 }
316
317 public boolean isMediaBusy() throws Exception {
318 m_log.debug("IrLAPContext", "\tmediaBusy = " + m_bMediaBusy);
319 return m_bMediaBusy;
320 }
321
322 public boolean isRemoteBusy() throws Exception {
323 m_log.debug("IrLAPContext", "\tremoteBusy = " + m_bRemoteBusy);
324 return m_bRemoteBusy;
325 }
326
327
328 /**
329 * Does nothing right now. We stick to 9600bps, 8 data bits, 1 stop bit,
330 * no parity. We gotta fix that -- 9600 is painfully slow.
331 */
332 public void negotiateConnectionParameters(byte [] ayParameters) throws Exception {
333 m_log.debug("IrLAPContext", "\tNegotiate-Connection-Parameters: " + Utilities.bytesToString(ayParameters, 0, ayParameters.length));
334 m_ayCommParameters = ayParameters;
335 m_framer.setConnectionParameters(m_ayCommParameters);
336 }
337
338 public void nextState(IrLAPState nextState) throws Exception {
339 m_log.debug("IrLAPContext", "[transition from " + m_state.getClass().getName() + " to " + nextState.getClass().getName() + "]");
340 m_state = nextState;
341 }
342
343 public boolean pendingDataRequests() throws Exception {
344 boolean bPending = m_sendQueue.pendingDataRequests();
345 m_log.debug("IrLAPContext", bPending ? "\tPending-Data-Requests" : "\tNo-Pending-Data-Requests");
346 return bPending;
347 }
348
349 public void sendData(byte yConnection, byte [] ayData, int nOffset, int nLength) throws Exception {
350 m_log.debug("IrLAPContext", "sendData");
351 m_sendQueue.addFrame(new IrLAPFrame(m_nVs, ayData, nOffset, nLength));
352 m_nVs = (m_nVs + 1) % 8;
353 }
354
355 public void sendDiscoveryResponse(int nDestination) throws Exception {
356 m_log.debug("IrLAPContext", "\tSend-Discovery-XID-Rsp:" + m_nMyAddress + ",discovery-info");
357 m_framer.sendXID(m_nMyAddress, nDestination, false, (byte) 0xFF, DISCOVERY_INFO);
358 }
359
360 public void sendNext(byte yConnection) throws Exception {
361 if ( !m_sendQueue.pendingDataRequests() ) {
362 throw new Exception("No pending requests");
363 }
364 IrLAPFrame nextFrame = m_sendQueue.getNext();
365 byte [] ayNextData = nextFrame.getData();
366 m_log.debug("IrLAPContext", "Data-Request(" + Utilities.bytesToString(ayNextData, 0, ayNextData.length) + ")");
367 m_framer.sendI(yConnection, m_nVr, nextFrame.getSequenceNumber(), true, ayNextData, 0, ayNextData.length);
368 }
369
370 public void sendRR(byte yConnection, boolean bCommand, boolean bFinal) throws Exception {
371 m_log.debug("IrLAPContext", "\tSend s:rr:" + (bCommand ? "cmd" : "rsp") + ":" + m_nVr + ":" + (bFinal ? "F" : "~F"));
372 m_framer.sendRR(yConnection, m_nVr, bCommand, bFinal);
373 }
374
375 public void sendSNRM(int nDestination, byte yConnection) throws Exception {
376 m_log.debug("IrLAPContext", "\tsend u:snrm:cmd:P:" + yConnection + ":" + nDestination);
377 m_framer.sendSNRM(m_nMyAddress, nDestination, yConnection);
378 }
379
380 public void sendUA(int nDestination, byte yConnection, boolean bSendParameters) throws Exception {
381 if ( bSendParameters ) {
382 m_log.debug("IrLAPContext", "\tsend u:ua:cmd:F (+ parameters) (" + Utilities.byteToString(yConnection) + ")");
383 } else {
384 m_log.debug("IrLAPContext", "\tsend u:ua:cmd:F (" + Utilities.byteToString(yConnection) + ")");
385 }
386 m_framer.sendUA(m_nMyAddress, nDestination, yConnection, bSendParameters);
387 }
388
389 /*
390 public void setAckRequired(boolean bAckRequired) {
391 m_log.debug("ackRequired := " + bAckRequired);
392 m_bAckRequired = bAckRequired;
393 }
394
395 public void setConnectionConfirmed(boolean bConnectionConfirmed) {
396 m_log.debug("connection-confirm(" + bConnectionConfirmed + ")");
397 m_bConnectionConfirmed = bConnectionConfirmed;
398 }
399 */
400
401 public void setFramer(IrLAPFramer framer) throws Exception {
402 m_framer = framer;
403 m_framer.addIrLAPFramerListener(this);
404 }
405
406 public void setFrameSent(boolean bFrameSent) throws Exception {
407 m_log.debug("IrLAPContext", "\tframeSent := " + bFrameSent);
408 m_bFrameSent = bFrameSent;
409 }
410
411 public void setRemoteBusy(boolean bRemoteBusy) throws Exception {
412 m_log.debug("IrLAPContext", "\tremoteBusy := " + bRemoteBusy);
413 m_bRemoteBusy = bRemoteBusy;
414 }
415
416 public void startFTimer() throws Exception {
417 m_log.debug("IrLAPContext", "\tstart-F-timer");
418 m_fTimer.setRepeats(false);
419 m_fTimer.start();
420 }
421
422 public void startQueryTimer() throws Exception {
423 m_log.debug("IrLAPContext", "\tstart-query-timer");
424 m_queryTimer.setRepeats(false);
425 m_queryTimer.start();
426 }
427
428 public void startWatchdogTimer() throws Exception {
429 m_log.debug("IrLAPContext", "\tstart-WD-timer");
430 m_watchdogTimer.stop();
431 m_watchdogTimer.setRepeats(false);
432 m_watchdogTimer.start();
433 }
434
435 public void stopQueryTimer() throws Exception {
436 m_log.debug("IrLAPContext", "\tstop-query-timer");
437 m_queryTimer.stop();
438 }
439
440 public void stopWatchdogTimer() throws Exception {
441 m_log.debug("IrLAPContext", "\tstop-WD-timer");
442 m_watchdogTimer.stop();
443 }
444
445 /**
446 * Acknowledge frames through Nr - 1 by removing them from
447 * the send queue.
448 */
449 public void updateNr(int nNr) throws Exception {
450 // Damned sign extension.
451 nNr &= 0x00000007;
452 m_log.debug("IrLAPContext", "\tUpdate Nr Received (Nr: " + nNr + ")");
453 m_log.debug("IrLAPContext", "\t\tsend queue size = " + m_sendQueue.size());
454
455 int i = 0;
456 while ( i < m_sendQueue.size() ) {
457 IrLAPFrame frame = m_sendQueue.get(i);
458 int sequenceNumber = frame.getSequenceNumber();
459 m_log.debug("IrLAPContext", "\t\tframe sequence number = " + frame.getSequenceNumber());
460 if ( (sequenceNumber < nNr) || ((sequenceNumber == 7) && (0 == nNr)) ) {
461 m_log.debug("IrLAPContext", "\t\tpeer acknowledges receipt of frame " + frame.getSequenceNumber() + ", removing from send buffer");
462 m_sendQueue.remove(i);
463 i --;
464 }
465 i++;
466 }
467 }
468
469 public void waitMinimumTurnaroundTime() throws Exception {
470 //m_log.debug("IrLAPContext", "\tWait-Minimum-Turnaround-Time");
471 //Thread.sleep(DEFAULT_MIN_TURNAROUND_MSEC);
472 }
473
474 public void zeroRetryCount() throws Exception {
475 m_log.debug("IrLAPContext", "\tretryCount := 0");
476 m_nRetryCount = 0;
477 }
478 }