1/*
2    File:       IrIASClient.c
3
4    Contains:   Implementation of the TIASClient class
5
6*/
7
8#include "IrGlue.h"                 // includes CommErrors.h
9#include "IrIASClient.h"
10#include "IrIASService.h"
11#include "IrLSAPConn.h"
12#include "CBufferSegment.h"
13#include "IrDALog.h"
14
15#if (hasTracing > 0 && hasIASClientTracing > 0)
16enum IrIASClientTraceCodes
17{
18    kLogNew = 1,
19    kLogInit,
20    kLogFree,
21    kUnexpectedEvent,
22
23    kConnectRequestEvent,
24    kConnectReplyEvent,
25    kDisconnectRequestEvent,
26    kDisconnectReplyEvent,
27    kLookupRequestEvent,
28    kLookupReplyEvent,
29    kGetDataRequestEvent,
30    kGetDataReplyEvent,
31    kPutDataRequestEvent,
32    kPutDataReplyEvent,
33
34    kSendRequestEvent,
35    kParseInputEvent,
36    kLogParseReplyEvent,
37
38    kEnqueueEvent,
39    kDequeueEventStart,
40    kDequeueEventEnd
41
42};
43
44static
45EventTraceCauseDesc TraceEvents[] = {
46    {kLogNew,                       "iasclient: create, obj="},
47    {kLogInit,                      "iasclient: init"},
48    {kLogFree,                      "iasclient: free, obj="},
49    {kUnexpectedEvent,              "iasclient: unexpected event"},
50
51    {kConnectRequestEvent,          "iasclient: connect request"},
52    {kConnectReplyEvent,            "iasclient: connect reply"},
53    {kDisconnectRequestEvent,       "iasclient: disconnect request"},
54    {kDisconnectReplyEvent,         "iasclient: disconnect reply"},
55    {kLookupRequestEvent,           "iasclient: lookup request"},
56    {kLookupReplyEvent,             "iasclient: lookup reply"},
57    {kGetDataRequestEvent,          "iasclient: get data request"},
58    {kGetDataReplyEvent,            "iasclient: get data reply"},
59    {kPutDataRequestEvent,          "iasclient: put data request"},
60    {kPutDataReplyEvent,            "iasclient: put data reply"},
61
62    {kSendRequestEvent,             "iasclient: send ias request"},
63    {kParseInputEvent,              "iasclient: parse ias input"},
64    {kLogParseReplyEvent,           "iasclient: parse ias reply, lkupstatus="},
65
66    {kEnqueueEvent,                 "iasclient: Event Queued"},
67    {kDequeueEventStart,            "iasclient: Event Start"},
68    {kDequeueEventEnd,              "iasclient: Event End"}
69};
70
71#define XTRACE(x, y, z) IrDALogAdd ( x, y, ((uintptr_t)z & 0xffff), TraceEvents, true)
72#else
73#define XTRACE(x, y, z) ((void)0)
74#endif
75
76//--------------------------------------------------------------------------------
77#define super TIrStream
78OSDefineMetaClassAndStructors(TIASClient, TIrStream)
79//--------------------------------------------------------------------------------
80
81//--------------------------------------------------------------------------------
82//      tIASClient
83//--------------------------------------------------------------------------------
84/*static*/
85TIASClient *
86TIASClient::tIASClient(TIrGlue* irda, TIrStream* client)
87{
88    TIASClient *obj = new TIASClient;
89
90    XTRACE(kLogNew, 0, obj);
91    if (obj && !obj->Init(irda, client)) {
92	obj->release();
93	obj = nil;
94    }
95    return obj;
96}
97
98
99//--------------------------------------------------------------------------------
100//      free
101//--------------------------------------------------------------------------------
102void TIASClient::free()
103{
104    XTRACE(kLogFree, 0, this);
105
106#define FREE(x) { if (x) { (x)->release(); x = nil; } }
107
108    FREE(fLSAPConn);
109    FREE(fAttribute);
110
111    if (fRequestReply) {
112	fIrDA->ReleaseEventBlock(fRequestReply);
113	fRequestReply = nil;
114    }
115
116    // Delete the buffer
117    if (fGetPutBuffer) {
118	fGetPutBuffer->Delete();            // jdg: new style free the buffer
119	fGetPutBuffer = nil;
120    }
121
122    super::free();
123
124} // TIASClient::~TIASClient
125
126
127//--------------------------------------------------------------------------------
128//      Init
129//--------------------------------------------------------------------------------
130Boolean TIASClient::Init(TIrGlue* irda, TIrStream* client)
131{
132    ULong myLSAPId;
133    IrDAErr result;
134
135    XTRACE(kLogInit, 0, this);
136
137    fState = kIrIASClientDisconnected;
138    fReceiveState = kIASClientReceiveReply;
139
140    fClient = client;
141    fLookupRequest = nil;
142
143    fLSAPConn = nil;
144    fRequestReply = nil;
145    fGetPutBuffer = nil;
146    fAttribute = nil;
147
148
149    // Init IrStream
150#if (hasTracing > 0 && hasIASClientTracing > 0)
151    if (!super::Init(irda, TraceEvents, kEnqueueEvent)) return false;
152#else
153    if (!super::Init(irda)) return false;
154#endif
155
156    // New, init LSAPConn
157    fLSAPConn = TLSAPConn::tLSAPConn(irda, this);
158    require(fLSAPConn, Fail);
159
160    // allocate an event block to use (defer until needed?)
161    fRequestReply = irda->GrabEventBlock();
162    require(fRequestReply, Fail);
163
164    // Allocate, init the buffer segment
165    fGetPutBuffer = CBufferSegment::New( kIASClientBufferSize );
166    XREQUIRE(fGetPutBuffer, Fail);
167
168    // Get and assign a dynamic lsapId to the connection
169    myLSAPId = kAssignDynamicLSAPId;    // awful, rewrite
170    result = irda->ObtainLSAPId(myLSAPId);
171    XREQUIRENOT(result, Fail);
172    fLSAPConn->AssignId(myLSAPId);
173
174    return true;
175
176Fail:
177    return false;
178
179} // TIASClient::Init
180
181
182//--------------------------------------------------------------------------------
183//      NextState
184//--------------------------------------------------------------------------------
185void TIASClient::NextState(ULong event)
186{
187    switch (fState) {
188	case kIrIASClientDisconnected:
189	    HandleDisconnectedStateEvent(event);
190	    break;
191
192	case kIrIASClientConnected:
193	    HandleConnectedStateEvent(event);
194	    break;
195
196	default:
197	    XTRACE(kUnexpectedEvent, 0, event);
198	    DebugLog("TIASClient::NextState: bad fState");
199	    break;
200    }
201
202} // TIASClient::NextState
203
204
205//--------------------------------------------------------------------------------
206//      HandleDisconnectedStateEvent
207//--------------------------------------------------------------------------------
208void TIASClient::HandleDisconnectedStateEvent(ULong event)
209{
210    switch (event) {
211	case kIrConnectRequestEvent:
212	    {
213		XTRACE(kConnectRequestEvent, 0, 0);
214		// Set the destination LSAPId and pass the request on to LSAPConn
215		TIrConnLstnRequest* connectRequest = (TIrConnLstnRequest*)GetCurrentEvent();
216		connectRequest->fLSAPId = kNameServerLSAPId;
217		connectRequest->fData = nil;
218		fLSAPConn->EnqueueEvent(connectRequest);
219	    }
220	    break;
221
222	case kIrConnectReplyEvent:
223	    {
224		// Pass reply back to client - change state if connected
225		TIrConnLstnReply* connectReply = (TIrConnLstnReply*)GetCurrentEvent();
226		XTRACE(kConnectReplyEvent, 0, connectReply->fResult);
227		if (connectReply->fResult == noErr) {
228		    fState = kIrIASClientConnected;
229		}
230		fClient->EnqueueEvent(connectReply);
231	    }
232	    break;
233
234	case kIrDisconnectRequestEvent:
235	    {
236		XTRACE(kDisconnectRequestEvent, 0, 0);
237		// Pass the disconnect request to the lsapConn
238		fLSAPConn->EnqueueEvent(GetCurrentEvent());
239	    }
240	    break;
241
242	case kIrDisconnectReplyEvent:
243	    XTRACE(kDisconnectReplyEvent, 0, 0);
244	    // Already in disconnected state - just pass reply back to client
245	    fClient->EnqueueEvent(GetCurrentEvent());
246	    break;
247
248	default:
249	    DebugLog("TIASClient::HandleDisconnectedStateEvent: bad event");
250	    break;
251    }
252
253} // TIASClient::HandleDisconnectedStateEvent
254
255
256//--------------------------------------------------------------------------------
257//      HandleConnectedStateEvent
258//--------------------------------------------------------------------------------
259void TIASClient::HandleConnectedStateEvent(ULong event)
260{
261    switch (event) {
262	case kIrLookupRequestEvent:
263	    {
264		XTRACE(kLookupRequestEvent, 0, 0);
265		IrDAErr result = SendRequest();
266		if (result != noErr) {
267		    LookupComplete(result);
268		}
269	    }
270	    break;
271
272	case kIrPutDataReplyEvent:
273	    {
274		TIrPutReply* putReply = (TIrPutReply*)GetCurrentEvent();
275		XTRACE(kPutDataReplyEvent, 0, putReply->fResult);
276		if (putReply->fResult != noErr) {
277		    // Complete lookup request if any errors
278		    LookupComplete(putReply->fResult);
279		}
280		else {
281		    GetStart();
282		}
283	    }
284	    break;
285
286	case kIrGetDataReplyEvent:
287	    {
288		TIrGetReply* getReply = (TIrGetReply*)GetCurrentEvent();
289		XTRACE(kGetDataReplyEvent, 0, getReply->fResult);
290		if (getReply->fResult != noErr) {
291		    // Complete lookup request if any errors
292		    LookupComplete(getReply->fResult);
293		}
294		else {
295		    ParseInput();
296		}
297	    }
298	    break;
299
300	case kIrReleaseRequestEvent:
301	case kIrDisconnectRequestEvent:
302	    XTRACE(kDisconnectRequestEvent, 1, event);
303	    // Pass the disconnect request to the lsapConn
304	    fLSAPConn->EnqueueEvent(GetCurrentEvent());
305	    break;
306
307	case kIrReleaseReplyEvent:
308	case kIrDisconnectReplyEvent:
309	    XTRACE(kDisconnectReplyEvent, 1, event);
310	    // Now we're disconnected again
311	    fState = kIrIASClientDisconnected;
312	    // Pass the disconnect reply to the client
313	    fClient->EnqueueEvent(GetCurrentEvent());
314	    // NOTE: Lookups in progress will be cleaned up.  The disconnect
315	    // will force either the get or the put in progress to complete
316	    // with an error.  When they complete with an error LookupComplete
317	    // is called (see  above) and LookupComplete frees fAttribute if
318	    // necessary and sends a reply back to the client.
319	    break;
320
321	default:
322	    DebugLog("TIASClient::HandleConnectedStateEvent: bad event");
323	    break;
324    }
325
326} // TIASClient::HandleConnectedStateEvent
327
328
329//================================ Helper methods ================================
330
331
332//--------------------------------------------------------------------------------
333//      SendRequest
334//--------------------------------------------------------------------------------
335IrDAErr TIASClient::SendRequest()
336{
337    Size classNameLen;
338    Size attrNameLen;
339    TIrLookupRequest* lookupRequest = (TIrLookupRequest*)GetCurrentEvent();
340
341    XTRACE(kSendRequestEvent, 0, 0);
342
343    // Save the request so it can be replied to
344    XASSERT(fLookupRequest == nil);
345    fLookupRequest = lookupRequest;
346
347    // Get lengths of class and attr strings
348    classNameLen = strlen((const char*)(lookupRequest->fClassName));
349    attrNameLen = strlen((const char*)(lookupRequest->fAttrName));
350
351    // Validate that className and attrName strings fit in buffer provided
352    XASSERT((classNameLen + attrNameLen + 3) <= kIASClientBufferSize);
353    if ((classNameLen + attrNameLen + 3) > kIASClientBufferSize) {
354	return kIrDAErrBadParameter;
355    }
356
357    // Fill out the request
358    fGetPutBuffer->Seek(0, kPosBeg);
359    fGetPutBuffer->Put(kIASOpGetValueByClass | kIASFrameLstBit);
360    fGetPutBuffer->Put((UByte)classNameLen);
361    fGetPutBuffer->Putn((const UByte *)lookupRequest->fClassName, classNameLen);
362    fGetPutBuffer->Put((UByte)attrNameLen);
363    fGetPutBuffer->Putn((const UByte *)lookupRequest->fAttrName, attrNameLen);
364
365    PutStart();
366
367    return noErr;
368
369} // TIASClient::SendRequest
370
371
372//--------------------------------------------------------------------------------
373//      ParseInput
374//--------------------------------------------------------------------------------
375void TIASClient::ParseInput()
376{
377    UByte ctrlByte;
378    Boolean lastFrame;
379    Boolean ackedFrame;
380    IrDAErr result;
381
382    // A reply frame has been received - parse it and decide what to do with it
383
384    fGetPutBuffer->Seek(0, kPosBeg);
385    ctrlByte = fGetPutBuffer->Get();
386    lastFrame = ctrlByte & kIASFrameLstBit;
387    ackedFrame = ctrlByte & kIASFrameAckBit;
388
389    XTRACE(kParseInputEvent, ctrlByte, fReceiveState);
390
391    switch(fReceiveState) {
392	case kIASClientReceiveReply:
393	    if (ackedFrame) {
394		// The peer device is acking (unnecessary/optionally) my single frame request
395		// It should have the lst bit on
396		XASSERT(lastFrame);
397		// Keep waiting for the actual reply
398		GetStart();
399	    }
400	    else {
401		if (ctrlByte == (kIASOpGetValueByClass | kIASFrameLstBit)) {
402		    result = ParseReply();
403		    LookupComplete(result);
404		}
405		else if (lastFrame) {
406		    LookupComplete(kIrDAErrGeneric);    // ***FIXME: Better error code
407		}
408		else {
409		    fReceiveState = kIASClientReceiveWaitFinal;
410		}
411	    }
412	    break;
413
414	case kIASClientReceiveWaitFinal:
415	    // I don't accept multi-frame replies, so all I want to do is get the
416	    // final frame of the reply so I can complete the lookup request with an error.
417	    XASSERT(!ackedFrame);
418	    if (lastFrame) {
419		// Reset the receive state
420		fReceiveState = kIASClientReceiveReply;
421		LookupComplete(kIrDAErrGeneric);    // ***FIXME: Better error code
422	    }
423	    break;
424
425	default:
426	    break;
427    }
428
429    // Ack the frame I don't want/care about
430    if (fReceiveState == kIASClientReceiveWaitFinal) {
431	fGetPutBuffer->Seek(0, kPosBeg);
432	fGetPutBuffer->Put(kIASOpGetValueByClass | kIASFrameAckBit);
433	PutStart();
434    }
435
436} // TIASClient::ParseInput
437
438
439//--------------------------------------------------------------------------------
440//      ParseReply
441//--------------------------------------------------------------------------------
442IrDAErr TIASClient::ParseReply()
443{
444    UByte lookupStatus;
445
446    // Get the reply status code
447    lookupStatus = fGetPutBuffer->Get();
448    require(lookupStatus == kIASRetOkay, Fail);
449
450    // Create an attribute and let the attribute extract the info
451    check(fAttribute == nil);
452    fAttribute = TIASAttribute::tIASAttribute(fGetPutBuffer);
453    require(fAttribute, FailNoMem);
454
455    return noErr;
456
457Fail:
458    XTRACE(kLogParseReplyEvent, 0, lookupStatus);
459
460FailNoMem:                          // fix: better err return
461    return kIrDAErrGeneric;
462
463} // TIASClient::ParseReply
464
465
466//--------------------------------------------------------------------------------
467//      GetStart
468//--------------------------------------------------------------------------------
469void TIASClient::GetStart()
470{
471    XTRACE(kGetDataRequestEvent, 0, 0);
472
473    TIrGetRequest* getRequest = (TIrGetRequest*)fRequestReply;
474    getRequest->fEvent = kIrGetDataRequestEvent;
475    getRequest->fData = fGetPutBuffer;
476    getRequest->fOffset = 0;
477    getRequest->fLength = fGetPutBuffer->GetSize();
478    fLSAPConn->EnqueueEvent(getRequest);
479
480} // TIASClient::GetStart
481
482
483//--------------------------------------------------------------------------------
484//      PutStart
485//--------------------------------------------------------------------------------
486void TIASClient::PutStart()
487{
488    XTRACE(kPutDataRequestEvent, 0, 0);
489
490    TIrPutRequest* putRequest = (TIrPutRequest*)fRequestReply;
491    putRequest->fEvent = kIrPutDataRequestEvent;
492    putRequest->fData = fGetPutBuffer;
493    putRequest->fOffset = 0;
494    putRequest->fLength = fGetPutBuffer->Position();
495    fLSAPConn->EnqueueEvent(putRequest);
496
497} // TIASClient::PutStart
498
499
500//--------------------------------------------------------------------------------
501//      LookupComplete
502//--------------------------------------------------------------------------------
503void TIASClient::LookupComplete(IrDAErr result)
504{
505    XTRACE(kLookupReplyEvent, 0, result);
506
507    // Cleanup fAttribute if any errors
508    if ((result != noErr) && (fAttribute != nil)) {
509	fAttribute->release();
510	fAttribute = nil;
511    }
512
513    // Reply to the client
514    TIrLookupReply* lookupReply = (TIrLookupReply*)fLookupRequest;
515    lookupReply->fEvent = kIrLookupReplyEvent;
516    lookupReply->fResult = result;
517    lookupReply->fAttribute = fAttribute;
518
519    // Reset my locals
520    fLookupRequest = nil;
521    fAttribute = nil;
522
523    fClient->EnqueueEvent(lookupReply);
524
525} // TIASClient::LookupComplete
526