1/* 2 File: IrQOS.c 3 4 Contains: Implementation of IrDA Quality Of Service class 5 6*/ 7 8 9#include "IrQOS.h" 10#include "CBufferSegment.h" 11#include "IrDALog.h" 12#include "AppleIrDA.h" 13 14#if (hasTracing > 0 && hasIrQOSTracing > 0) 15 16enum IrLogCodes 17{ 18 kLogNew = 1, 19 kLogFree, 20 21 kLogReset, 22 kLogReset_Baud, 23 kLogReset_Data, 24 kLogReset_BOF, 25 kLogReset_Disconnect, 26 27 kLogSetBaud1, 28 kLogSetBaud2, 29 kLogSetData1, 30 kLogSetData2, 31 32 kLogSetWindow, 33 kLogSetDisconnect, 34 35 kLogGetBaud, 36 kLogGetBaudFailed, 37 kLogGetMaxTurn, 38 kLogGetMaxTurnFailed, 39 40 kLogGetDataSize, 41 kLogGetDataSizeFailed, 42 kLogGetWindowSize, 43 kLogGetWindowSizeFailed, 44 45 kLogGetBofs, 46 kLogGetBofsFailed, 47 kLogGetMin, 48 kLogGetMinFailed, 49 kLogGetDisconnect, 50 kLogGetDisconnectFailed, 51 52 kLogAddInfo, 53 kLogAddInfoFailed, 54 kLogExtract, 55 kLogExtractData, 56 kLogNegotiateBaud1, 57 kLogNegotiateBaud2, 58 59 kLogNormMinTurn, 60 kLogNormMaxLine, 61 kLogNormWindow, 62 kLogNormData, 63 kLogNormRequested, 64 kLogNormSmallerWindow, 65 kLogNormSmallerData 66 67}; 68 69static 70EventTraceCauseDesc IrLogEvents[] = { 71 {kLogNew, "IrQOS: new, obj="}, 72 {kLogFree, "IrQOS: free, obj="}, 73 74 {kLogReset, "IrQOS: reset"}, 75 {kLogReset_Baud, "IrQOS: reset max turn=, baud bitmap="}, 76 {kLogReset_Data, "IrQOS: reset datasize=, window bits="}, 77 {kLogReset_BOF, "IrQOS: reset bof=, min turn="}, 78 {kLogReset_Disconnect, "IrQOS: reset disconnect link="}, 79 80 {kLogSetBaud1, "IrQOS: set baud, bps="}, 81 {kLogSetBaud2, "IrQOS: set baud, result=, fBaudRate="}, 82 {kLogSetData1, "IrQOS: set data, buffersize="}, 83 {kLogSetData2, "IrQOS: set data, result=, fDataSize="}, 84 85 {kLogSetWindow, "IrQOS: set windowsize, ct=, bitmap="}, 86 {kLogSetDisconnect, "IrQOS: set disconnect threshold, seconds=, mask="}, 87 88 {kLogGetBaud, "IrQOS: get baud, bitpos=, bps="}, 89 {kLogGetBaudFailed, "IrQOS: get baud failed, logic error!"}, 90 {kLogGetMaxTurn, "IrQOS: get max turnaround, bitpos=, ms="}, 91 {kLogGetMaxTurnFailed, "IrQOS: get max failed, logic error!"}, 92 93 {kLogGetDataSize, "IrQOS: get data size, size="}, 94 {kLogGetDataSizeFailed, "IrQOS: get data size failed, logic error"}, 95 {kLogGetWindowSize, "IrQOS: get window size, count="}, 96 {kLogGetWindowSizeFailed, "IrQOS: get window size logic error!"}, 97 98 {kLogGetBofs, "IrQOS: get bofs adjusted for speed, ct="}, 99 {kLogGetBofsFailed, "IrQOS: get bofs failed, logic error"}, 100 {kLogGetMin, "IrQOS: get min turnaround, delay="}, 101 {kLogGetMinFailed, "IrQOS: get min turnaround logic error!"}, 102 {kLogGetDisconnect, "IrQOS: get disconnect time, ms="}, 103 {kLogGetDisconnectFailed, "IrQOS: get disconnect time failed, logic error"}, 104 105 {kLogAddInfo, "IrQOS: add info, buffersize="}, 106 {kLogAddInfoFailed, "IrQOS: add info failed logic error!"}, 107 {kLogExtract, "IrQOS: extract info from buffer, addr="}, 108 {kLogExtractData, "IrQOS: extract info, id=, len | value="}, 109 {kLogNegotiateBaud1, "IrQOS: negotiate speed, input=, input="}, 110 {kLogNegotiateBaud2, "IrQOS: negotiate speed, result="}, 111 112 {kLogNormMinTurn, "IrQOS: min turn, speedBit | minturnBit, turn in bytes="}, 113 {kLogNormMaxLine, "IrQOS: max line capacity="}, 114 {kLogNormWindow, "IrQOS: had to normalize window mask, old=, new="}, 115 {kLogNormData, "IrQOS: had to normalize data size mask, old=, new="}, 116 {kLogNormRequested, "IrQOS: requested data line capacity="}, 117 {kLogNormSmallerWindow, "IrQOS: shrinking window size to fit, new="}, 118 {kLogNormSmallerData, "IrQOS: shrinking data size to fit, new="} 119}; 120 121#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, IrLogEvents, true ) 122 123#else 124#define XTRACE(x,y,z) ((void)0) 125#endif 126 127 128#define kMicroseconds -1 129 130static const ULong IrBaudRateTable[] = { k9600bps, k19200bps, k38400bps, k57600bps, 131 k115200bps, k576000bps, k1Mbps, k4Mbs }; 132 133static const UByte IrExtraBOFsTable[8] = {48, 24, 12, 6, 3, 2, 1, 0}; 134 135static const TTimeout IrMaxTurnTimeTable[4] = {500 * kMilliseconds, 136 250 * kMilliseconds, 137 100 * kMilliseconds, 138 50 * kMilliseconds}; 139 140static const TTimeout IrMinTurnTimeTable[8] = { 10 * kMilliseconds, 141 5 * kMilliseconds, 142 1 * kMilliseconds, 143 500 * kMicroseconds, 144 100 * kMicroseconds, 145 50 * kMicroseconds, 146 10 * kMicroseconds, 147 0}; 148 149static const TTimeout IrLinkDiscThreshold[8] = { 3 * kSeconds, 150 8 * kSeconds, 151 12 * kSeconds, 152 16 * kSeconds, 153 20 * kSeconds, 154 25 * kSeconds, 155 30 * kSeconds, 156 40 * kSeconds}; 157 158// The following tables are from 6.6.11 of IrLAP 1.1 159 160// 10ms 5ms 1ms .5ms .1ms .05ms .01ms 0ms 161const UInt16 IrMinTurnInBytesTable[8][8] = {{ 10, 5, 1, 0, 0, 0, 0, 0}, // 9600 bps 162 { 20, 10, 2, 1, 0, 0, 0, 0}, // 19200 bps 163 { 40, 20, 4, 2, 0, 0, 0, 0}, // 38400 bps 164 { 58, 29, 6, 3, 1, 0, 0, 0}, // 57600 bps 165 { 115, 58, 12, 6, 1, 1, 0, 0}, // 115200 bps 166 { 720, 360, 72, 36, 7, 4, 2, 0}, // 576000 bps 167 { 1440, 720, 144, 72, 14, 7, 4, 0}, // 1.152 Mbps 168 { 5000, 2500, 500, 250, 50, 25, 5, 0}}; // 4 Mbps 169 170// This is the max line capacity table for baud rates below 115.2k (500ms maxTurn assumed) 171const ULong IrMaxLineCapacityTable1[4] = {400, 800, 1600, 2360}; 172 173// This is the max line capacity table for 115.2k (based on maxTurn of 500/250/100/50 ms) 174// Includes 20% overhead for transpearency, 175const ULong IrMaxLineCapacityTable2[4] = {4800, 2400, 960, 480}; 176 177// FIR capacity with no overhead for bit stuffing 178 179const ULong IrMaxLineCapacityTable576[4] = { 28800, 11520, 5760, 2880 }; 180const ULong IrMaxLineCapacityTable1Mbps[4] = { 57600, 28800, 11520, 5760 }; 181const ULong IrMaxLineCapacityTable4Mbps[4] = { 200000, 100000, 40000, 20000 }; 182 183//-------------------------------------------------------------------------------- 184#define super OSObject 185 OSDefineMetaClassAndStructors(TIrQOS, OSObject); 186//-------------------------------------------------------------------------------- 187 188 189//-------------------------------------------------------------------------------- 190// TIrQOS 191//-------------------------------------------------------------------------------- 192/*static*/ 193TIrQOS * 194TIrQOS::tIrQOS(USBIrDAQoS *qos) 195{ 196 TIrQOS *obj = new TIrQOS; 197 198 XTRACE(kLogNew, 0, obj); 199 200 if (obj && !obj->init(qos)) { 201 obj->release(); 202 obj = nil; 203 } 204 return obj; 205} 206 207//-------------------------------------------------------------------------------- 208// init 209//-------------------------------------------------------------------------------- 210bool TIrQOS::init(USBIrDAQoS *qos) 211{ 212 fDeviceQOS = qos; // save the qos from usb 213 214 Reset(); // reset to initial values 215 216 return super::init(); // done 217 218} // TIrQOS::init 219 220 221//-------------------------------------------------------------------------------- 222// Free 223//-------------------------------------------------------------------------------- 224void TIrQOS::free(void) 225{ 226 XTRACE(kLogFree, 0, this); // only reason we have a free 227 super::free(); // is for the log 228} 229 230 231//-------------------------------------------------------------------------------- 232// Reset 233//-------------------------------------------------------------------------------- 234void TIrQOS::Reset() 235{ 236#ifdef THROTTLE_SPEED 237 int TESTING_ONLY_BAUD_THROTTLED; 238 UInt16 THROTTLE = THROTTLE_SPEED; // 0x3 = 9600 or slower, 0x7 = 19.2, 0f=38.4 239#endif 240 241 XTRACE(kLogReset, 0, this); 242 243 // todo - could init from kext prefs file instead of compiled-in 244 245 fBaudRate = kQOSDefaultBaudRates; 246 fMaxTurnAroundTime = kQOSDefaultMaxTurnTime; 247 fDataSize = kQOSDefaultDataSizes; 248 fWindowSize = kQOSDefaultWindowSize; 249 fNumExtraBOFs = kQOSDefaultExtraBOFs; 250 fMinTurnAroundTime = kQOSDefaultMinTurnTime; 251 fLinkDiscThreshold = kQOSDefaultDiscThresholds; 252 253 if (fDeviceQOS) { 254 fDataSize = fDeviceQOS->datasize; // bytes per frame supported 255 fWindowSize = fDeviceQOS->windowsize; // 1 thru 7 frames per ack 256 fMinTurnAroundTime = fDeviceQOS->minturn; // min turnaround time 257 fBaudRate = fDeviceQOS->baud1 << 8 | fDeviceQOS->baud2; // 16 bits of baud 258#ifdef THROTTLE_SPEED 259 { // if want to slow down the connection 260 if (fBaudRate & THROTTLE) { // if a valid result, then throttle back for testing 261 fBaudRate &= THROTTLE; 262 IOLog("Baud throttled, bitmap now set to 0x%x\n", fBaudRate); 263 } 264 else 265 IOLog("baud not throttled, would have been zero, 0x%x, 0x%x\n", fBaudRate, THROTTLE); 266 } 267#endif 268 fNumExtraBOFs = fDeviceQOS->bofs; // number of bofs the pod wants 269 } 270 271 // Log all the new values 272 XTRACE(kLogReset_Baud, fMaxTurnAroundTime, fBaudRate); 273 XTRACE(kLogReset_Data, fDataSize, fWindowSize); 274 XTRACE(kLogReset_BOF, fNumExtraBOFs, fMinTurnAroundTime); 275 XTRACE(kLogReset_Disconnect, 0, fLinkDiscThreshold); 276 277} // TIrQOS::Reset 278 279 280//-------------------------------------------------------------------------------- 281// SetBaudRate 282//-------------------------------------------------------------------------------- 283IrDAErr TIrQOS::SetBaudRate(BitRate bitsPerSec) 284{ 285 ULong newBaudRate = 0; 286 IrDAErr result; 287 288 switch(bitsPerSec) { 289 // NOTE: Each case falls thru on purpose to build the bit mask 290 case k4Mbs: 291 newBaudRate |= kQOS4Mbps; 292 293 case k1Mbps: 294 newBaudRate |= kQOS1Mbps; 295 296 case k576000bps: 297 newBaudRate |= kQOS576000bps; 298 299 case k115200bps: 300 newBaudRate |= kQOS115200bps; 301 302 case k57600bps: 303 newBaudRate |= kQOS57600bps; 304 305 case k38400bps: 306 newBaudRate |= kQOS38400bps; 307 308 case k19200bps: 309 newBaudRate |= kQOS19200bps; 310 311 case k9600bps: 312 newBaudRate |= kQOS9600bps; 313 314 fBaudRate = (UByte)newBaudRate; 315 result = noErr; 316 break; 317 318 default: 319 result = errBadArg; 320 break; 321 } 322 323 XTRACE(kLogSetBaud1, bitsPerSec >> 16, bitsPerSec); 324 XTRACE(kLogSetBaud2, result, fBaudRate); 325 326 return result; 327 328} // TIrQOS::SetBaudRate 329 330 331 332//-------------------------------------------------------------------------------- 333// SetDataSize 334//-------------------------------------------------------------------------------- 335IrDAErr TIrQOS::SetDataSize(ULong bufferSize) 336{ 337 ULong newDataSize = 0; 338 IrDAErr result; 339 340 switch(bufferSize) { 341 // NOTE: Each case falls thru on purpose to build the bit mask 342 case 2048: 343 newDataSize |= kQOS2048Bytes; 344 345 case 1024: 346 newDataSize |= kQOS1024Bytes; 347 348 case 512: 349 newDataSize |= kQOS512Bytes; 350 351 case 256: 352 newDataSize |= kQOS256Bytes; 353 354 case 128: 355 newDataSize |= kQOS128Bytes; 356 357 case 64: 358 newDataSize |= kQOS64Bytes; 359 360 fDataSize = (UByte)newDataSize; 361 result = noErr; 362 break; 363 364 default: 365 result = errBadArg; 366 break; 367 } 368 369 XTRACE(kLogSetData1, bufferSize >> 16, bufferSize); 370 XTRACE(kLogSetData2, result, fDataSize); 371 return result; 372 373} // TIrQOS::SetDataSize 374 375 376//-------------------------------------------------------------------------------- 377// SetWindowSize 378//-------------------------------------------------------------------------------- 379IrDAErr TIrQOS::SetWindowSize(ULong numFrames) 380{ 381 // Only acceptible values are 1 thru 7 382 if (--numFrames >= 7) return errBadArg; 383 384 fWindowSize = (UByte)((kQOSValidWindowSizes >> (6 - numFrames)) & kQOSValidWindowSizes); 385 386 XTRACE(kLogSetWindow, numFrames, fWindowSize); 387 return noErr; 388 389} // TIrQOS::SetWindowSize 390 391 392 393//-------------------------------------------------------------------------------- 394// SetLinkDiscThresholdTime 395//-------------------------------------------------------------------------------- 396IrDAErr TIrQOS::SetLinkDiscThresholdTime(TTimeout linkDiscThresholdTime) 397{ 398 ULong newLinkDiscThreshold = 0; 399 IrDAErr result; 400 401 switch(linkDiscThresholdTime / kSeconds) { 402 // NOTE: Each case falls thru on purpose to build the bit mask 403 case 40: 404 newLinkDiscThreshold |= kQOSDiscAfter40secs; 405 406 case 30: 407 newLinkDiscThreshold |= kQOSDiscAfter30secs; 408 409 case 25: 410 newLinkDiscThreshold |= kQOSDiscAfter25secs; 411 412 case 20: 413 newLinkDiscThreshold |= kQOSDiscAfter20secs; 414 415 case 16: 416 newLinkDiscThreshold |= kQOSDiscAfter16secs; 417 418 case 12: 419 newLinkDiscThreshold |= kQOSDiscAfter12secs; 420 421 case 8: 422 newLinkDiscThreshold |= kQOSDiscAfter8secs; 423 424 case 3: 425 newLinkDiscThreshold |= kQOSDiscAfter3secs; 426 427 fLinkDiscThreshold = (UByte)newLinkDiscThreshold; 428 result = noErr; 429 break; 430 431 default: 432 result = errBadArg; 433 break; 434 } 435 436 XTRACE(kLogSetDisconnect, linkDiscThresholdTime / kSeconds, fLinkDiscThreshold); 437 return result; 438 439} // TIrQOS::SetLinkDiscThresholdTime 440 441 442//-------------------------------------------------------------------------------- 443// GetBaudRate 444//-------------------------------------------------------------------------------- 445BitRate TIrQOS::GetBaudRate() 446{ 447 ULong bitPos; 448 449 // note: the -1 and +7 are due to 2400 baud not being in our table 450 451 if( fBaudRate>>8 & kQOSValidBaudRatesHigh ) { 452 bitPos = HighestBitOn(fBaudRate>>8 ) + 7; // Look at next byte 453 } 454 else { 455 bitPos = HighestBitOn(fBaudRate) - 1; // Look at the low byte 456 } 457 require(bitPos < sizeof(IrBaudRateTable) / sizeof(IrBaudRateTable[0]), Fail); 458 459 XTRACE(kLogGetBaud, bitPos, IrBaudRateTable[bitPos]); 460 461 return IrBaudRateTable[bitPos]; 462 463Fail: 464 XTRACE(kLogGetBaudFailed, 0xffff, 9600); 465 return k9600bps; // better than crashing, but not much 466 467} // TIrQOS::GetBaudRate 468 469 470//-------------------------------------------------------------------------------- 471// GetMaxTurnAroundTime 472//-------------------------------------------------------------------------------- 473TTimeout TIrQOS::GetMaxTurnAroundTime() 474{ 475 ULong bitPos = HighestBitOn(fMaxTurnAroundTime); 476 require(bitPos < sizeof(IrMaxTurnTimeTable) / sizeof(IrMaxTurnTimeTable[0]), Fail); 477 478 XTRACE(kLogGetMaxTurn, bitPos, IrMaxTurnTimeTable[bitPos]); 479 480 return IrMaxTurnTimeTable[bitPos]; 481 482Fail: 483 XTRACE(kLogGetMaxTurnFailed, 0xffff, 500); 484 485 return (500 * kMilliseconds); 486 487} // TIrQOS::GetMaxTurnAroundTime 488 489 490//-------------------------------------------------------------------------------- 491// GetDataSize 492//-------------------------------------------------------------------------------- 493ULong TIrQOS::GetDataSize() 494{ 495 ULong size; 496 497 require(fDataSize, Fail); 498 499 size = 64 * (1 << HighestBitOn(fDataSize)); 500 501 XTRACE(kLogGetDataSize, 0, size); 502 503 return size; 504 505Fail: 506 XTRACE(kLogGetDataSizeFailed, 0xffff, 64); 507 return kQOS64Bytes; 508 509} // TIrQOS::GetDataSize 510 511 512//-------------------------------------------------------------------------------- 513// GetWindowSize 514//-------------------------------------------------------------------------------- 515ULong TIrQOS::GetWindowSize() 516{ 517 ULong size; 518 519 require(fWindowSize, Fail); 520 521 size = HighestBitOn(fWindowSize) + 1; 522 523 XTRACE(kLogGetWindowSize, 0, size); 524 525 return size; 526 527Fail: 528 XTRACE(kLogGetWindowSizeFailed, 0xffff, 1); 529 return 1; 530 531} // TIrQOS::GetWindowSize 532 533 534//-------------------------------------------------------------------------------- 535// GetExtraBOFs 536//-------------------------------------------------------------------------------- 537ULong TIrQOS::GetExtraBOFs() 538{ 539 ULong result; 540 ULong bofs; 541 542 require(fNumExtraBOFs, Fail); 543 544 bofs = IrExtraBOFsTable[HighestBitOn(fNumExtraBOFs)]; // map to actual bof count 545 546 result = (bofs * GetBaudRate()) / 115200; // normalize per 115k bps base 547 548 XTRACE(kLogGetBofs, 0, result); 549 550 // Return extra BOFs adjusted for the actual baud rate 551 return result; 552 553Fail: 554 XTRACE(kLogGetBofsFailed, 0xffff, 48); 555 return 48; 556 557} // TIrQOS::GetExtraBOFs 558 559 560//-------------------------------------------------------------------------------- 561// GetMinTurnAroundTime 562//-------------------------------------------------------------------------------- 563TTimeout TIrQOS::GetMinTurnAroundTime() 564{ 565 ULong bitpos; 566 TTimeout result; 567 568 require(fMinTurnAroundTime, Fail); 569 570 bitpos = HighestBitOn(fMinTurnAroundTime); 571 result = IrMinTurnTimeTable[bitpos]; 572 XTRACE(kLogGetMin, result >> 16, result); 573 return result; 574 575Fail: 576 XTRACE(kLogGetMinFailed, 0, IrMinTurnTimeTable[0]); 577 return IrMinTurnTimeTable[0]; 578 579} // TIrQOS::GetMinTurnAroundTime 580 581 582//-------------------------------------------------------------------------------- 583// GetLinkDiscThresholdTime 584//-------------------------------------------------------------------------------- 585TTimeout TIrQOS::GetLinkDiscThresholdTime() 586{ 587 TTimeout result; 588 ULong bitpos; 589 590 require(fLinkDiscThreshold, Fail); 591 592 bitpos = HighestBitOn(fLinkDiscThreshold); 593 result = IrLinkDiscThreshold[bitpos]; 594 XTRACE(kLogGetDisconnect, result >> 16, result); 595 596 return result; 597 598Fail: 599 XTRACE(kLogGetDisconnectFailed, 0xffff, IrLinkDiscThreshold[0]); 600 return IrLinkDiscThreshold[0]; 601 602} // TIrQOS::GetLinkDiscThresholdTime 603 604 605//-------------------------------------------------------------------------------- 606// AddInfoToBuffer 607//-------------------------------------------------------------------------------- 608ULong TIrQOS::AddInfoToBuffer(UByte* buffer, ULong maxBytes) 609{ 610 // Make sure my parms are consistent before sending them out 611 IrDAErr result; 612 613 XTRACE(kLogAddInfo, 0, maxBytes); 614 615 result = NormalizeInfo(); 616 require(result == noErr, Bogus); 617 require(maxBytes >= (kQOSNumberOfIdentifiers * 3 + 1), Bogus); // Add the extra byte for the baud rate 618 619 // Add baud rate 620 *buffer++ = kQOSBaudRateId; 621 *buffer++ = 2; 622 *buffer++ = ( UInt8 )fBaudRate; // Low byte goes out first (2400 - 1Mbps) 623 *buffer++ = ( UInt8 )( fBaudRate >> 8 ); // High byte second (4Mbps+) 624 625 // Add max turn around time 626 *buffer++ = kQOSMaxTurnAroundTimeId; 627 *buffer++ = 1; 628 *buffer++ = fMaxTurnAroundTime; 629 630 // Add data size 631 *buffer++ = kQOSDataSizeId; 632 *buffer++ = 1; 633 *buffer++ = fDataSize; 634 635 // Add window size 636 *buffer++ = kQOSWindowSizeId; 637 *buffer++ = 1; 638 *buffer++ = fWindowSize; 639 640 // Add num extra BOFs 641 *buffer++ = kQOSNumberOfExtraBOFsId; 642 *buffer++ = 1; 643 *buffer++ = fNumExtraBOFs; 644 645 // Add min turn around time 646 *buffer++ = kQOSMinTurnAroundTimeId; 647 *buffer++ = 1; 648 *buffer++ = fMinTurnAroundTime; 649 650 // Add link disconnect/threshold 651 *buffer++ = kQOSLinkDiscThresholdId; 652 *buffer++ = 1; 653 *buffer++ = fLinkDiscThreshold; 654 655 return kQOSNumberOfIdentifiers * 3 + 1; 656 657Bogus: 658 XTRACE(kLogAddInfoFailed, 0xffff, 0); 659 660 return 0; 661 662} // TIrQOS::AddInfoToBuffer 663 664 665//-------------------------------------------------------------------------------- 666// ExtractInfoFromBuffer 667//-------------------------------------------------------------------------------- 668IrDAErr TIrQOS::ExtractInfoFromBuffer(CBufferSegment* buffer) 669{ 670 ULong value; 671 UByte idLenVal[3]; // id byte, length byte, value byte 672 673 XTRACE(kLogExtract, 0, buffer); 674 675 // Preset fields to default values (in case some values are not provided) 676 fBaudRate = kQOS9600bps; 677 fMaxTurnAroundTime = kQOSMaxTurnTime500ms; 678 fDataSize = kQOS64Bytes; 679 fWindowSize = kQOS1Frame; 680 fNumExtraBOFs = kQOS48ExtraBOFs; 681 fMinTurnAroundTime = kQOSMinTurnTime10ms; 682 fLinkDiscThreshold = kQOSDefaultDiscThresholds; 683 684 // Basically, repeat: read id byte, read len byte, read len value bytes, update value. 685 686 while (true) { 687 // Need another 3 bytes minimum to continue 688 if (buffer->Getn(&idLenVal[0], sizeof(idLenVal)) != sizeof(idLenVal)) break; 689 690 XTRACE(kLogExtractData, idLenVal[0], (idLenVal[1] << 8) | idLenVal[2]); 691 692 // Put value into a local (compiler wasn't smart enough, so I'm helping it) 693 value = idLenVal[2]; 694 695 // Assign the value to the appropriate field. Mask off unknown bits first. 696 // If value after masking is 0, then don't change the field - leave as default. 697 switch(idLenVal[0]) { 698 case kQOSBaudRateId: 699 // Baud rate is the only field that can have more than one byte in the value 700 // The low order byte is sent first (< 4Mbps), followed by the high order byte (4Mbps or greater) 701 if (value & kQOSValidBaudRatesLow) { 702 fBaudRate = (UByte)(value & kQOSValidBaudRatesLow); 703 } 704 if( idLenVal[1] == 2 ) { // Check if 4 Mbps or greater field was sent 705 value = buffer->Peek(); // Get it out of the buffer, but don't advance 706 value &= kQOSValidBaudRatesHigh; // that's done later. Mask off unused bits 707 fBaudRate += value << 8; // Put it into the high byte of the baud rate member 708 } 709 break; 710 711 case kQOSMaxTurnAroundTimeId: 712 if (value & kQOSValidMaxTurnTimes) { 713 fMaxTurnAroundTime = (UByte)(value & kQOSValidMaxTurnTimes); 714 } 715 break; 716 717 case kQOSDataSizeId: 718 if (value & kQOSValidDataSizes) { 719 fDataSize = (UByte)(value & kQOSValidDataSizes); 720 } 721 break; 722 723 case kQOSWindowSizeId: 724 if (value & kQOSValidWindowSizes) { 725 fWindowSize = (UByte)(value & kQOSValidWindowSizes); 726 } 727 break; 728 729 case kQOSNumberOfExtraBOFsId: 730 if (value & kQOSValidExtraBOFs) { 731 fNumExtraBOFs = (UByte)(value & kQOSValidExtraBOFs); 732 } 733 break; 734 735 case kQOSMinTurnAroundTimeId: 736 if (value & kQOSValidMinTurnTimes) { 737 fMinTurnAroundTime = (UByte)(value & kQOSValidMinTurnTimes); 738 } 739 break; 740 741 case kQOSLinkDiscThresholdId: 742 if (value & kQOSValidDiscThresholds) { 743 fLinkDiscThreshold = (UByte)(value & kQOSValidDiscThresholds); 744 } 745 break; 746 747 default: 748 // Ignore other negotiation parameters I don't understand 749 break; 750 } 751 752 // If length is something other than 1 then skip additional value fields 753 if (idLenVal[1] > 1) { 754 buffer->Seek(idLenVal[1] - 1, kPosCur); 755 } 756 } 757 758 return noErr; 759 760} // TIrQOS::ExtractInfoFromBuffer 761 762 763//-------------------------------------------------------------------------------- 764// NegotiateWith 765//-------------------------------------------------------------------------------- 766IrDAErr TIrQOS::NegotiateWith(TIrQOS* peerDeviceQOS) 767{ 768 require(peerDeviceQOS, Fail); 769 770 XTRACE(kLogNegotiateBaud1, peerDeviceQOS->fBaudRate, fBaudRate); 771 XTRACE(kLogNegotiateBaud2, 0, fBaudRate & peerDeviceQOS->fBaudRate); 772 773 // Baud rate is intersection of my values and peer devices values 774 fBaudRate &= peerDeviceQOS->fBaudRate; 775 776 // Link disconnect/threshold is intersection of my and peer devices values 777 fLinkDiscThreshold &= peerDeviceQOS->fLinkDiscThreshold; 778 779 // Can't connect if no agreement on baud rate and/or link disconnect threshold 780 if ((fBaudRate == 0) || (fLinkDiscThreshold == 0)) { 781 return errIncompatibleRemote; 782 } 783 784 // Make sure that my parms are still consistent in case baud rate changed 785 return NormalizeInfo(); 786 787Fail: 788 return errIncompatibleRemote; 789 790} // TIrQOS::NegotiateWith 791 792 793//-------------------------------------------------------------------------------- 794// NormalizeInfo 795//-------------------------------------------------------------------------------- 796IrDAErr TIrQOS::NormalizeInfo() 797{ 798 IrDAErr result = noErr; 799 ULong minTurnTimeInBytes; 800 ULong maxLineCapacity; 801 ULong extraBOFs; 802 ULong maxWindowsBit; 803 Boolean firSpeed = GetBaudRate() > k115200bps; 804 805 require(fBaudRate, Bogus); 806 require(fMinTurnAroundTime, Bogus); 807 require(fMaxTurnAroundTime, Bogus); 808 require(fWindowSize, Bogus); 809 require(fDataSize, Bogus); 810 811 // Lookup minimum turnaround time in bytes from table (based on baud rate) 812 { 813 ULong speed; // bit position speed 814 ULong minturn; // bit position min turnaround 815 speed = HighestBitOn(fBaudRate >> 1); // shifting out 2400 bps and shifting in 4mbit 816 minturn = HighestBitOn(fMinTurnAroundTime); 817 minTurnTimeInBytes = IrMinTurnInBytesTable[speed][minturn]; // convert to byte count 818 819 XTRACE(kLogNormMinTurn, (speed << 8) | minturn, minTurnTimeInBytes); 820 } 821 822 // Lookup maximum line capacity 823 { 824 ULong maxTurn = HighestBitOn(fMaxTurnAroundTime); 825 require(maxTurn < 4, Bogus); 826 827 switch( GetBaudRate() ) 828 { 829 case k4Mbs: 830 maxLineCapacity = IrMaxLineCapacityTable4Mbps[maxTurn]; 831 break; 832 833 case k1Mbps: 834 maxLineCapacity = IrMaxLineCapacityTable1Mbps[maxTurn]; 835 break; 836 837 case k576000bps: 838 maxLineCapacity = IrMaxLineCapacityTable576[maxTurn]; 839 break; 840 841 case k115200bps: 842 maxLineCapacity = IrMaxLineCapacityTable2[maxTurn]; 843 break; 844 845 default: 846 maxLineCapacity = IrMaxLineCapacityTable1[HighestBitOn(fBaudRate>>1)]; 847 } 848 XTRACE(kLogNormMaxLine, maxLineCapacity >> 16, maxLineCapacity); 849 } 850 851 extraBOFs = GetExtraBOFs(); // Don't need these for FIR 852 853 // make sure windowsize and datasize are proper bitmaps 854 { 855 UInt8 old_windowsize, old_datasize; // debugging 856 UInt8 bitpos; 857 858 old_windowsize = fWindowSize; 859 old_datasize = fDataSize; 860 861 bitpos = HighestBitOn(fWindowSize); // All window sizes below maxWindow are valid 862 if (bitpos > 0) // if max windowsize > 1 863 fWindowSize |= (1 << bitpos) -1; // then turn on all the lower bits too 864 865 bitpos = HighestBitOn(fDataSize); // get max packet size 866 if (bitpos > 0) // if more than min size (64 bytes) 867 fDataSize |= (1 << bitpos) -1; // then turn on all the lower bits too 868 869 if (old_windowsize != fWindowSize) 870 XTRACE(kLogNormWindow, old_windowsize, fWindowSize); 871 872 if (old_datasize != fDataSize) 873 XTRACE(kLogNormData, old_datasize, fDataSize); 874 } 875 876 // Pare things down until they fit 877 while (true) { 878 ULong requestedLineCapacity; 879 if( firSpeed ) 880 requestedLineCapacity = ( ( GetDataSize() + 4 ) * GetWindowSize() ) + minTurnTimeInBytes; 881 else 882 requestedLineCapacity = ((GetDataSize() + 6 + extraBOFs) * GetWindowSize()) + minTurnTimeInBytes; 883 884 XTRACE(kLogNormRequested, requestedLineCapacity >> 16, requestedLineCapacity); 885 886 if (requestedLineCapacity < maxLineCapacity) break; 887 888 // First decrement window (if more than one choice specified) 889 maxWindowsBit = HighestBitOn(fWindowSize); 890 if (maxWindowsBit != 0) { // if more than a single window left 891 // Turn off high bit (reduce window count by 1) 892 fWindowSize &= (UByte)(~(1 << maxWindowsBit)); 893 XTRACE(kLogNormSmallerWindow, 0, fWindowSize); 894 require(fWindowSize, Bogus); // sanity check, should never hit this 895 } 896 897 // If at only one window left, try decrementing buffer size instead 898 else { 899 // Turn off high bit 900 fDataSize &= (UByte)(~(1 << HighestBitOn(fDataSize))); 901 XTRACE(kLogNormSmallerData, 0, fDataSize); 902 if (fDataSize == 0) { 903 result = errIncompatibleRemote; 904 break; 905 } 906 } 907 } 908 909 return result; 910 911Bogus: 912 return errIncompatibleRemote; 913 914} // TIrQOS::NormalizeInfo 915 916 917//-------------------------------------------------------------------------------- 918// HighestBitOn 919//-------------------------------------------------------------------------------- 920ULong TIrQOS::HighestBitOn(UByte aByte) 921{ 922 ULong bitPosition; 923 UByte bitMask = 0x80; 924 925 require(aByte != 0, Fail); 926 927 for (bitPosition = 7, bitMask = 0x80; bitMask != 0; bitPosition--, bitMask >>= 1) { 928 if ((aByte & bitMask) != 0) { 929 break; 930 } 931 } 932 933 return bitPosition; 934 935Fail: 936 return (ULong)-1; 937 938} // TIrQOS::HighestBitOn 939 940