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