#include "IrQOS.h"
#include "CBufferSegment.h"
#include "IrDALog.h"
#include "AppleIrDA.h"
#if (hasTracing > 0 && hasIrQOSTracing > 0)
enum IrLogCodes
{
kLogNew = 1,
kLogFree,
kLogReset,
kLogReset_Baud,
kLogReset_Data,
kLogReset_BOF,
kLogReset_Disconnect,
kLogSetBaud1,
kLogSetBaud2,
kLogSetData1,
kLogSetData2,
kLogSetWindow,
kLogSetDisconnect,
kLogGetBaud,
kLogGetBaudFailed,
kLogGetMaxTurn,
kLogGetMaxTurnFailed,
kLogGetDataSize,
kLogGetDataSizeFailed,
kLogGetWindowSize,
kLogGetWindowSizeFailed,
kLogGetBofs,
kLogGetBofsFailed,
kLogGetMin,
kLogGetMinFailed,
kLogGetDisconnect,
kLogGetDisconnectFailed,
kLogAddInfo,
kLogAddInfoFailed,
kLogExtract,
kLogExtractData,
kLogNegotiateBaud1,
kLogNegotiateBaud2,
kLogNormMinTurn,
kLogNormMaxLine,
kLogNormWindow,
kLogNormData,
kLogNormRequested,
kLogNormSmallerWindow,
kLogNormSmallerData
};
static
EventTraceCauseDesc IrLogEvents[] = {
{kLogNew, "IrQOS: new, obj="},
{kLogFree, "IrQOS: free, obj="},
{kLogReset, "IrQOS: reset"},
{kLogReset_Baud, "IrQOS: reset max turn=, baud bitmap="},
{kLogReset_Data, "IrQOS: reset datasize=, window bits="},
{kLogReset_BOF, "IrQOS: reset bof=, min turn="},
{kLogReset_Disconnect, "IrQOS: reset disconnect link="},
{kLogSetBaud1, "IrQOS: set baud, bps="},
{kLogSetBaud2, "IrQOS: set baud, result=, fBaudRate="},
{kLogSetData1, "IrQOS: set data, buffersize="},
{kLogSetData2, "IrQOS: set data, result=, fDataSize="},
{kLogSetWindow, "IrQOS: set windowsize, ct=, bitmap="},
{kLogSetDisconnect, "IrQOS: set disconnect threshold, seconds=, mask="},
{kLogGetBaud, "IrQOS: get baud, bitpos=, bps="},
{kLogGetBaudFailed, "IrQOS: get baud failed, logic error!"},
{kLogGetMaxTurn, "IrQOS: get max turnaround, bitpos=, ms="},
{kLogGetMaxTurnFailed, "IrQOS: get max failed, logic error!"},
{kLogGetDataSize, "IrQOS: get data size, size="},
{kLogGetDataSizeFailed, "IrQOS: get data size failed, logic error"},
{kLogGetWindowSize, "IrQOS: get window size, count="},
{kLogGetWindowSizeFailed, "IrQOS: get window size logic error!"},
{kLogGetBofs, "IrQOS: get bofs adjusted for speed, ct="},
{kLogGetBofsFailed, "IrQOS: get bofs failed, logic error"},
{kLogGetMin, "IrQOS: get min turnaround, delay="},
{kLogGetMinFailed, "IrQOS: get min turnaround logic error!"},
{kLogGetDisconnect, "IrQOS: get disconnect time, ms="},
{kLogGetDisconnectFailed, "IrQOS: get disconnect time failed, logic error"},
{kLogAddInfo, "IrQOS: add info, buffersize="},
{kLogAddInfoFailed, "IrQOS: add info failed logic error!"},
{kLogExtract, "IrQOS: extract info from buffer, addr="},
{kLogExtractData, "IrQOS: extract info, id=, len | value="},
{kLogNegotiateBaud1, "IrQOS: negotiate speed, input=, input="},
{kLogNegotiateBaud2, "IrQOS: negotiate speed, result="},
{kLogNormMinTurn, "IrQOS: min turn, speedBit | minturnBit, turn in bytes="},
{kLogNormMaxLine, "IrQOS: max line capacity="},
{kLogNormWindow, "IrQOS: had to normalize window mask, old=, new="},
{kLogNormData, "IrQOS: had to normalize data size mask, old=, new="},
{kLogNormRequested, "IrQOS: requested data line capacity="},
{kLogNormSmallerWindow, "IrQOS: shrinking window size to fit, new="},
{kLogNormSmallerData, "IrQOS: shrinking data size to fit, new="}
};
#define XTRACE(x, y, z) IrDALogAdd( x, y, (uintptr_t)z & 0xffff, IrLogEvents, true )
#else
#define XTRACE(x,y,z) ((void)0)
#endif
#define kMicroseconds -1
static const ULong IrBaudRateTable[] = { k9600bps, k19200bps, k38400bps, k57600bps,
k115200bps, k576000bps, k1Mbps, k4Mbs };
static const UByte IrExtraBOFsTable[8] = {48, 24, 12, 6, 3, 2, 1, 0};
static const TTimeout IrMaxTurnTimeTable[4] = {500 * kMilliseconds,
250 * kMilliseconds,
100 * kMilliseconds,
50 * kMilliseconds};
static const TTimeout IrMinTurnTimeTable[8] = { 10 * kMilliseconds,
5 * kMilliseconds,
1 * kMilliseconds,
500 * kMicroseconds,
100 * kMicroseconds,
50 * kMicroseconds,
10 * kMicroseconds,
0};
static const TTimeout IrLinkDiscThreshold[8] = { 3 * kSeconds,
8 * kSeconds,
12 * kSeconds,
16 * kSeconds,
20 * kSeconds,
25 * kSeconds,
30 * kSeconds,
40 * kSeconds};
const UInt16 IrMinTurnInBytesTable[8][8] = {{ 10, 5, 1, 0, 0, 0, 0, 0}, { 20, 10, 2, 1, 0, 0, 0, 0}, { 40, 20, 4, 2, 0, 0, 0, 0}, { 58, 29, 6, 3, 1, 0, 0, 0}, { 115, 58, 12, 6, 1, 1, 0, 0}, { 720, 360, 72, 36, 7, 4, 2, 0}, { 1440, 720, 144, 72, 14, 7, 4, 0}, { 5000, 2500, 500, 250, 50, 25, 5, 0}};
const ULong IrMaxLineCapacityTable1[4] = {400, 800, 1600, 2360};
const ULong IrMaxLineCapacityTable2[4] = {4800, 2400, 960, 480};
const ULong IrMaxLineCapacityTable576[4] = { 28800, 11520, 5760, 2880 };
const ULong IrMaxLineCapacityTable1Mbps[4] = { 57600, 28800, 11520, 5760 };
const ULong IrMaxLineCapacityTable4Mbps[4] = { 200000, 100000, 40000, 20000 };
#define super OSObject
OSDefineMetaClassAndStructors(TIrQOS, OSObject);
TIrQOS *
TIrQOS::tIrQOS(USBIrDAQoS *qos)
{
TIrQOS *obj = new TIrQOS;
XTRACE(kLogNew, 0, obj);
if (obj && !obj->init(qos)) {
obj->release();
obj = nil;
}
return obj;
}
bool TIrQOS::init(USBIrDAQoS *qos)
{
fDeviceQOS = qos;
Reset();
return super::init();
}
void TIrQOS::free(void)
{
XTRACE(kLogFree, 0, this); super::free(); }
void TIrQOS::Reset()
{
#ifdef THROTTLE_SPEED
int TESTING_ONLY_BAUD_THROTTLED;
UInt16 THROTTLE = THROTTLE_SPEED; #endif
XTRACE(kLogReset, 0, this);
fBaudRate = kQOSDefaultBaudRates;
fMaxTurnAroundTime = kQOSDefaultMaxTurnTime;
fDataSize = kQOSDefaultDataSizes;
fWindowSize = kQOSDefaultWindowSize;
fNumExtraBOFs = kQOSDefaultExtraBOFs;
fMinTurnAroundTime = kQOSDefaultMinTurnTime;
fLinkDiscThreshold = kQOSDefaultDiscThresholds;
if (fDeviceQOS) {
fDataSize = fDeviceQOS->datasize; fWindowSize = fDeviceQOS->windowsize; fMinTurnAroundTime = fDeviceQOS->minturn; fBaudRate = fDeviceQOS->baud1 << 8 | fDeviceQOS->baud2; #ifdef THROTTLE_SPEED
{ if (fBaudRate & THROTTLE) { fBaudRate &= THROTTLE;
IOLog("Baud throttled, bitmap now set to 0x%x\n", fBaudRate);
}
else
IOLog("baud not throttled, would have been zero, 0x%x, 0x%x\n", fBaudRate, THROTTLE);
}
#endif
fNumExtraBOFs = fDeviceQOS->bofs; }
XTRACE(kLogReset_Baud, fMaxTurnAroundTime, fBaudRate);
XTRACE(kLogReset_Data, fDataSize, fWindowSize);
XTRACE(kLogReset_BOF, fNumExtraBOFs, fMinTurnAroundTime);
XTRACE(kLogReset_Disconnect, 0, fLinkDiscThreshold);
}
IrDAErr TIrQOS::SetBaudRate(BitRate bitsPerSec)
{
ULong newBaudRate = 0;
IrDAErr result;
switch(bitsPerSec) {
case k4Mbs:
newBaudRate |= kQOS4Mbps;
case k1Mbps:
newBaudRate |= kQOS1Mbps;
case k576000bps:
newBaudRate |= kQOS576000bps;
case k115200bps:
newBaudRate |= kQOS115200bps;
case k57600bps:
newBaudRate |= kQOS57600bps;
case k38400bps:
newBaudRate |= kQOS38400bps;
case k19200bps:
newBaudRate |= kQOS19200bps;
case k9600bps:
newBaudRate |= kQOS9600bps;
fBaudRate = (UByte)newBaudRate;
result = noErr;
break;
default:
result = errBadArg;
break;
}
XTRACE(kLogSetBaud1, bitsPerSec >> 16, bitsPerSec);
XTRACE(kLogSetBaud2, result, fBaudRate);
return result;
}
IrDAErr TIrQOS::SetDataSize(ULong bufferSize)
{
ULong newDataSize = 0;
IrDAErr result;
switch(bufferSize) {
case 2048:
newDataSize |= kQOS2048Bytes;
case 1024:
newDataSize |= kQOS1024Bytes;
case 512:
newDataSize |= kQOS512Bytes;
case 256:
newDataSize |= kQOS256Bytes;
case 128:
newDataSize |= kQOS128Bytes;
case 64:
newDataSize |= kQOS64Bytes;
fDataSize = (UByte)newDataSize;
result = noErr;
break;
default:
result = errBadArg;
break;
}
XTRACE(kLogSetData1, bufferSize >> 16, bufferSize);
XTRACE(kLogSetData2, result, fDataSize);
return result;
}
IrDAErr TIrQOS::SetWindowSize(ULong numFrames)
{
if (--numFrames >= 7) return errBadArg;
fWindowSize = (UByte)((kQOSValidWindowSizes >> (6 - numFrames)) & kQOSValidWindowSizes);
XTRACE(kLogSetWindow, numFrames, fWindowSize);
return noErr;
}
IrDAErr TIrQOS::SetLinkDiscThresholdTime(TTimeout linkDiscThresholdTime)
{
ULong newLinkDiscThreshold = 0;
IrDAErr result;
switch(linkDiscThresholdTime / kSeconds) {
case 40:
newLinkDiscThreshold |= kQOSDiscAfter40secs;
case 30:
newLinkDiscThreshold |= kQOSDiscAfter30secs;
case 25:
newLinkDiscThreshold |= kQOSDiscAfter25secs;
case 20:
newLinkDiscThreshold |= kQOSDiscAfter20secs;
case 16:
newLinkDiscThreshold |= kQOSDiscAfter16secs;
case 12:
newLinkDiscThreshold |= kQOSDiscAfter12secs;
case 8:
newLinkDiscThreshold |= kQOSDiscAfter8secs;
case 3:
newLinkDiscThreshold |= kQOSDiscAfter3secs;
fLinkDiscThreshold = (UByte)newLinkDiscThreshold;
result = noErr;
break;
default:
result = errBadArg;
break;
}
XTRACE(kLogSetDisconnect, linkDiscThresholdTime / kSeconds, fLinkDiscThreshold);
return result;
}
BitRate TIrQOS::GetBaudRate()
{
ULong bitPos;
if( fBaudRate>>8 & kQOSValidBaudRatesHigh ) {
bitPos = HighestBitOn(fBaudRate>>8 ) + 7; }
else {
bitPos = HighestBitOn(fBaudRate) - 1; }
require(bitPos < sizeof(IrBaudRateTable) / sizeof(IrBaudRateTable[0]), Fail);
XTRACE(kLogGetBaud, bitPos, IrBaudRateTable[bitPos]);
return IrBaudRateTable[bitPos];
Fail:
XTRACE(kLogGetBaudFailed, 0xffff, 9600);
return k9600bps;
}
TTimeout TIrQOS::GetMaxTurnAroundTime()
{
ULong bitPos = HighestBitOn(fMaxTurnAroundTime);
require(bitPos < sizeof(IrMaxTurnTimeTable) / sizeof(IrMaxTurnTimeTable[0]), Fail);
XTRACE(kLogGetMaxTurn, bitPos, IrMaxTurnTimeTable[bitPos]);
return IrMaxTurnTimeTable[bitPos];
Fail:
XTRACE(kLogGetMaxTurnFailed, 0xffff, 500);
return (500 * kMilliseconds);
}
ULong TIrQOS::GetDataSize()
{
ULong size;
require(fDataSize, Fail);
size = 64 * (1 << HighestBitOn(fDataSize));
XTRACE(kLogGetDataSize, 0, size);
return size;
Fail:
XTRACE(kLogGetDataSizeFailed, 0xffff, 64);
return kQOS64Bytes;
}
ULong TIrQOS::GetWindowSize()
{
ULong size;
require(fWindowSize, Fail);
size = HighestBitOn(fWindowSize) + 1;
XTRACE(kLogGetWindowSize, 0, size);
return size;
Fail:
XTRACE(kLogGetWindowSizeFailed, 0xffff, 1);
return 1;
}
ULong TIrQOS::GetExtraBOFs()
{
ULong result;
ULong bofs;
require(fNumExtraBOFs, Fail);
bofs = IrExtraBOFsTable[HighestBitOn(fNumExtraBOFs)];
result = (bofs * GetBaudRate()) / 115200;
XTRACE(kLogGetBofs, 0, result);
return result;
Fail:
XTRACE(kLogGetBofsFailed, 0xffff, 48);
return 48;
}
TTimeout TIrQOS::GetMinTurnAroundTime()
{
ULong bitpos;
TTimeout result;
require(fMinTurnAroundTime, Fail);
bitpos = HighestBitOn(fMinTurnAroundTime);
result = IrMinTurnTimeTable[bitpos];
XTRACE(kLogGetMin, result >> 16, result);
return result;
Fail:
XTRACE(kLogGetMinFailed, 0, IrMinTurnTimeTable[0]);
return IrMinTurnTimeTable[0];
}
TTimeout TIrQOS::GetLinkDiscThresholdTime()
{
TTimeout result;
ULong bitpos;
require(fLinkDiscThreshold, Fail);
bitpos = HighestBitOn(fLinkDiscThreshold);
result = IrLinkDiscThreshold[bitpos];
XTRACE(kLogGetDisconnect, result >> 16, result);
return result;
Fail:
XTRACE(kLogGetDisconnectFailed, 0xffff, IrLinkDiscThreshold[0]);
return IrLinkDiscThreshold[0];
}
ULong TIrQOS::AddInfoToBuffer(UByte* buffer, ULong maxBytes)
{
IrDAErr result;
XTRACE(kLogAddInfo, 0, maxBytes);
result = NormalizeInfo();
require(result == noErr, Bogus);
require(maxBytes >= (kQOSNumberOfIdentifiers * 3 + 1), Bogus);
*buffer++ = kQOSBaudRateId;
*buffer++ = 2;
*buffer++ = ( UInt8 )fBaudRate; *buffer++ = ( UInt8 )( fBaudRate >> 8 );
*buffer++ = kQOSMaxTurnAroundTimeId;
*buffer++ = 1;
*buffer++ = fMaxTurnAroundTime;
*buffer++ = kQOSDataSizeId;
*buffer++ = 1;
*buffer++ = fDataSize;
*buffer++ = kQOSWindowSizeId;
*buffer++ = 1;
*buffer++ = fWindowSize;
*buffer++ = kQOSNumberOfExtraBOFsId;
*buffer++ = 1;
*buffer++ = fNumExtraBOFs;
*buffer++ = kQOSMinTurnAroundTimeId;
*buffer++ = 1;
*buffer++ = fMinTurnAroundTime;
*buffer++ = kQOSLinkDiscThresholdId;
*buffer++ = 1;
*buffer++ = fLinkDiscThreshold;
return kQOSNumberOfIdentifiers * 3 + 1;
Bogus:
XTRACE(kLogAddInfoFailed, 0xffff, 0);
return 0;
}
IrDAErr TIrQOS::ExtractInfoFromBuffer(CBufferSegment* buffer)
{
ULong value;
UByte idLenVal[3];
XTRACE(kLogExtract, 0, buffer);
fBaudRate = kQOS9600bps;
fMaxTurnAroundTime = kQOSMaxTurnTime500ms;
fDataSize = kQOS64Bytes;
fWindowSize = kQOS1Frame;
fNumExtraBOFs = kQOS48ExtraBOFs;
fMinTurnAroundTime = kQOSMinTurnTime10ms;
fLinkDiscThreshold = kQOSDefaultDiscThresholds;
while (true) {
if (buffer->Getn(&idLenVal[0], sizeof(idLenVal)) != sizeof(idLenVal)) break;
XTRACE(kLogExtractData, idLenVal[0], (idLenVal[1] << 8) | idLenVal[2]);
value = idLenVal[2];
switch(idLenVal[0]) {
case kQOSBaudRateId:
if (value & kQOSValidBaudRatesLow) {
fBaudRate = (UByte)(value & kQOSValidBaudRatesLow);
}
if( idLenVal[1] == 2 ) { value = buffer->Peek(); value &= kQOSValidBaudRatesHigh; fBaudRate += value << 8; }
break;
case kQOSMaxTurnAroundTimeId:
if (value & kQOSValidMaxTurnTimes) {
fMaxTurnAroundTime = (UByte)(value & kQOSValidMaxTurnTimes);
}
break;
case kQOSDataSizeId:
if (value & kQOSValidDataSizes) {
fDataSize = (UByte)(value & kQOSValidDataSizes);
}
break;
case kQOSWindowSizeId:
if (value & kQOSValidWindowSizes) {
fWindowSize = (UByte)(value & kQOSValidWindowSizes);
}
break;
case kQOSNumberOfExtraBOFsId:
if (value & kQOSValidExtraBOFs) {
fNumExtraBOFs = (UByte)(value & kQOSValidExtraBOFs);
}
break;
case kQOSMinTurnAroundTimeId:
if (value & kQOSValidMinTurnTimes) {
fMinTurnAroundTime = (UByte)(value & kQOSValidMinTurnTimes);
}
break;
case kQOSLinkDiscThresholdId:
if (value & kQOSValidDiscThresholds) {
fLinkDiscThreshold = (UByte)(value & kQOSValidDiscThresholds);
}
break;
default:
break;
}
if (idLenVal[1] > 1) {
buffer->Seek(idLenVal[1] - 1, kPosCur);
}
}
return noErr;
}
IrDAErr TIrQOS::NegotiateWith(TIrQOS* peerDeviceQOS)
{
require(peerDeviceQOS, Fail);
XTRACE(kLogNegotiateBaud1, peerDeviceQOS->fBaudRate, fBaudRate);
XTRACE(kLogNegotiateBaud2, 0, fBaudRate & peerDeviceQOS->fBaudRate);
fBaudRate &= peerDeviceQOS->fBaudRate;
fLinkDiscThreshold &= peerDeviceQOS->fLinkDiscThreshold;
if ((fBaudRate == 0) || (fLinkDiscThreshold == 0)) {
return errIncompatibleRemote;
}
return NormalizeInfo();
Fail:
return errIncompatibleRemote;
}
IrDAErr TIrQOS::NormalizeInfo()
{
IrDAErr result = noErr;
ULong minTurnTimeInBytes;
ULong maxLineCapacity;
ULong extraBOFs;
ULong maxWindowsBit;
Boolean firSpeed = GetBaudRate() > k115200bps;
require(fBaudRate, Bogus);
require(fMinTurnAroundTime, Bogus);
require(fMaxTurnAroundTime, Bogus);
require(fWindowSize, Bogus);
require(fDataSize, Bogus);
{
ULong speed; ULong minturn; speed = HighestBitOn(fBaudRate >> 1); minturn = HighestBitOn(fMinTurnAroundTime);
minTurnTimeInBytes = IrMinTurnInBytesTable[speed][minturn];
XTRACE(kLogNormMinTurn, (speed << 8) | minturn, minTurnTimeInBytes);
}
{
ULong maxTurn = HighestBitOn(fMaxTurnAroundTime);
require(maxTurn < 4, Bogus);
switch( GetBaudRate() )
{
case k4Mbs:
maxLineCapacity = IrMaxLineCapacityTable4Mbps[maxTurn];
break;
case k1Mbps:
maxLineCapacity = IrMaxLineCapacityTable1Mbps[maxTurn];
break;
case k576000bps:
maxLineCapacity = IrMaxLineCapacityTable576[maxTurn];
break;
case k115200bps:
maxLineCapacity = IrMaxLineCapacityTable2[maxTurn];
break;
default:
maxLineCapacity = IrMaxLineCapacityTable1[HighestBitOn(fBaudRate>>1)];
}
XTRACE(kLogNormMaxLine, maxLineCapacity >> 16, maxLineCapacity);
}
extraBOFs = GetExtraBOFs();
{
UInt8 old_windowsize, old_datasize; UInt8 bitpos;
old_windowsize = fWindowSize;
old_datasize = fDataSize;
bitpos = HighestBitOn(fWindowSize); if (bitpos > 0) fWindowSize |= (1 << bitpos) -1;
bitpos = HighestBitOn(fDataSize); if (bitpos > 0) fDataSize |= (1 << bitpos) -1;
if (old_windowsize != fWindowSize)
XTRACE(kLogNormWindow, old_windowsize, fWindowSize);
if (old_datasize != fDataSize)
XTRACE(kLogNormData, old_datasize, fDataSize);
}
while (true) {
ULong requestedLineCapacity;
if( firSpeed )
requestedLineCapacity = ( ( GetDataSize() + 4 ) * GetWindowSize() ) + minTurnTimeInBytes;
else
requestedLineCapacity = ((GetDataSize() + 6 + extraBOFs) * GetWindowSize()) + minTurnTimeInBytes;
XTRACE(kLogNormRequested, requestedLineCapacity >> 16, requestedLineCapacity);
if (requestedLineCapacity < maxLineCapacity) break;
maxWindowsBit = HighestBitOn(fWindowSize);
if (maxWindowsBit != 0) { fWindowSize &= (UByte)(~(1 << maxWindowsBit));
XTRACE(kLogNormSmallerWindow, 0, fWindowSize);
require(fWindowSize, Bogus); }
else {
fDataSize &= (UByte)(~(1 << HighestBitOn(fDataSize)));
XTRACE(kLogNormSmallerData, 0, fDataSize);
if (fDataSize == 0) {
result = errIncompatibleRemote;
break;
}
}
}
return result;
Bogus:
return errIncompatibleRemote;
}
ULong TIrQOS::HighestBitOn(UByte aByte)
{
ULong bitPosition;
UByte bitMask = 0x80;
require(aByte != 0, Fail);
for (bitPosition = 7, bitMask = 0x80; bitMask != 0; bitPosition--, bitMask >>= 1) {
if ((aByte & bitMask) != 0) {
break;
}
}
return bitPosition;
Fail:
return (ULong)-1;
}