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