/
TBF.cpp
1445 lines (1327 loc) · 52.5 KB
/
TBF.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#define TBF_IMPLEMENTATION 1
#include "MSInfo.h"
#include "TBF.h"
#include "FEC.h"
#include "RLCMessages.h"
#include "BSSG.h"
#include "RLCEngine.h"
namespace GPRS {
typedef SGSN::GprsSgsnDownlinkPdu DownlinkQPdu;
static bool SendExtraTA = 0;// DEBUG: Send an extra TA message
// after an Immediate Assignment just to prove it is working.
// 4-24-2012, turned it back off, does not help CS-4 bug.
static bool T3168Behavior = 1; // Dont send downlink assignments while t3168 running.
static int configTbfRetry() {
return gConfig.getNum("GPRS.TBF.Retry");
}
bool MsgTransaction::mtMsgPending(MsgTransactionType mttype)
{
// The multiple tests here are overkill.
return mtMsgExpectedBits.isSet(mttype) &&
mtExpectedAckBSN[mttype].valid() &&
mtExpectedAckBSN[mttype] + BSNLagTime >= gBSNNext;
}
// Is any message pending?
bool MsgTransaction::mtMsgPending()
{
// We are not 'waiting' until the expectedAckBSN is valid, because:
// 1. the first time through the TBF state machine's logic,
// it tests this function before the msgAckTime has been set.
// 2. In RLCEngine.cpp, we wait on an optional RRBP reservation,
// so we dont want to wait forever if the first one does not get
// its reservation and expectedAckBSN is still invalid.
//GPRSLOG(1) << "mtMsgPending:" <<LOGVAR(mtExpectedAckBSN)
//<<LOGVAR(gBSNNext) <<" cmp=" << (mtExpectedAckBSN >= gBSNNext);
// Wait lag-time extra blocks beyond when we expect
// it to make sure we dont launch another sendassignment
// at the same time it is being received.
// Note that the operators here are in class RLCBSN_t and do modulo logic.
if (mtMsgExpectedBits) {
for (int i = 0; i < MsgTransMax; i++) {
if (mtExpectedAckBSN[i].valid() && mtExpectedAckBSN[i] + BSNLagTime >= gBSNNext) return true;
}
}
// We could probably clear mtMsgExpectedBits at this point...
return false;
}
TBF *TBF::newUpTBF(MSInfo *ms,RLCMsgChannelRequestDescriptionIE &mCRD,
uint32_t tlli,
bool onRach) // If true, request on RACH, else request on uplink ack-nack.
{
// Changed 5-20-2012. There is only ONE uplink tbf,
// and assigning a new one just changes the priority of the existing.
// Note that there is also a GPRS optional feature to allow multiple simultaneous
// separate TBFs with separate RLCs, but we dont support it.
TBF *activeTbf = 0;
ms->msN3101 = 0; // I'm going to reset this no matter what happens below.
if (ms->msCountActiveTBF(RLCDir::Up, &activeTbf) >= 1) {
//GLOG(INFO) << "MS denied additional uplink TBF because GPRS.TBF.Up.Max limit exceeded";
if (activeTbf->mtGetState() == TBFState::DataFinal) {
// In this state this uplink TBF has already completed and we have sent the
// uplinkAckNack message. We now set the TBF_EST field in that message
// so the MS may respond to the RRBP reservation with a PacketResourceRequest.
// Doesn't really matter whether that is the case or not - in this state,
// go ahead and start a new uplink TBF.
} else if (activeTbf->isTransmitting()) {
// When the MS requests a second TBF when one is already running,
// it means it has a new PDU with a different priority or QoS (throughput.)
// See 44.060 8.1.1.1.2 and 9.5.
// We are required to reissue the TBF assignment within timer T3168.
// Originally I just sent a new PacketUplinkAssignment, but that does not always,
// we have to end the TBF and restart it.
// Maybe if the new priority/QoS is lower, then just reissuing a new
// PacketUplinkAssignment on the current TBF would work, but I'm just
// going to reissue the TBF.
// This code waits until the current PDU finishes and then terminates the transfer,
// which implies that we expect the MS to comply by setting the uplink MAC header Countdown Value to 0
// within the T3168 grace period.
// FIXME: Temporarily provide a way to disable this in case it has a bug:
if (gConfig.getBool("GPRS.Reassign.Enable")) {
activeTbf->mtPerformReassign = true;
}
return NULL;
} else {
// The activeTbf is in the midst of being issued a new assignment.
if (tlli != activeTbf->mtTlli) {
// The new uplink tbf has a different tlli.
// This case happens only for the AttachComplete message when it is
// sent by the MS in an uplink tbf requested in the downlinkacknack
// for the AttachAccept message. So check for this special case:
if (tlli != ms->msTlli && tlli != ms->msOldTlli) {
// Should not happen because the internal SGSN provides
// the newTlli as an alias with the AttachAccept message.
GLOG(ERR) << "uplink TBF request"<<LOGVAR(tlli)<<"does not match MS:"<<ms;
// But go ahead and do it anyway.
}
GPRSLOG(1) << "Changing uplink TBF tlli to"<<LOGVAR(tlli)<<ms;
activeTbf->mtTlli = tlli;
} else {
// Just ignore this duplicate uplink request.
// TODO: We can infer the MS mode if this arrives on RACH.
GPRSLOG(1) << "MS denied second uplink TBF" << activeTbf;
}
return NULL;
}
}
TBF *tbf = (TBF*) new RLCUpEngine(ms,mCRD.mRLCOctetCount);
tbf->mtTlli = tlli;
tbf->mtUnAckMode = mCRD.mRLCMode;
if (mCRD.mLLCPDUType == 0) { // 0 == LLC PDU is SACK or ACK, 1 == its not
GLOG(ERR) << "Uplink PDU LLC SACK request"<<ms;
}
tbf->mtSetState(TBFState::DataReadyToConnect);
return tbf;
}
ChannelCodingType TBF::mtChannelCoding() const
{ // Return 0 - 3 for CS-1 or CS-4 for data transfer.
assert(mtChannelCodingMax >= ChannelCodingCS1 && mtChannelCodingMax <= ChannelCodingCS4);
ChannelCodingType result;
if (mtChannelCodingMax == ChannelCodingCS1) {
result = ChannelCodingCS1; // Locked to lowest speed. No need to query the MS RSSI.
} else {
ChannelCodingType dynamicCS = mtMS->msGetChannelCoding(mtDir);
result = min(mtChannelCodingMax,dynamicCS);
}
mtMS->msChannelCoding.addPoint((int)result);
return result;
}
TBF::TBF(MSInfo *wms, RLCDirType wdir)
: mtState(TBFState::Unused), mtDebugId(++Stats.countTBF), mtMS(wms), mtDir(wdir), mtTFI(-1)
{
gReports.incr("GPRS.TBF");
RN_MEMCHKNEW(TBF)
mtChannelCodingMax = ChannelCodingMax; // This may be changed by caller.
mtCCMin = mtCCMax = (ChannelCodingType)-1;
gL2MAC.macAddTBF(this);
mtMS->msAddTBF(this);
// Reset these counters to avoid killing the TBF before it has a chance to do anything:
mtMS->msTalkUpTime.setNow();
mtMS->msTalkDownTime.setNow();
mtMS->msCountTbfs++;
// Reset these counts just to validate them.
//mtPersistLastUse.setNow();
//mtPersistKeepAlive.setNow();
}
TBF::~TBF() { RN_MEMCHKDEL(TBF) } // housekeeping handled by mtDelete()
const char *TBF::tbfid(bool verbose)
{
static char buf[30];
int n1 = sprintf(buf, "%c%d", (mtDir==RLCDir::Up ? 'U' : 'D'), mtDebugId);
if (verbose) {
// Just add the seconds part of the time string, which looks like "HH:MM:SS.T".
const char *ts = strrchr(timestr().c_str(),':');
if (ts) { n1 += sprintf(buf+n1," %s",ts+1); }
// Add the TLLI.
//sprintf(buf+n1," MS#%d,%x",mtMS->msDebugId,mtMS->msTlli);
sprintf(buf+n1," MS#%d",mtMS->msDebugId);
}
return (const char *)buf;
}
//void TBF::setRadData(RadData &wRD)
//{
// mtMS->msSetRadData(wRD);
// if (wRD.mRSSI < mtLowRSSI) { mtLowRSSI = wRD.mRSSI; }
//}
void TBF::mtDelete(bool forever)
{
devassert(! mtAttached);
//mtDetach(); // Did this already, but be safe and call again.
mtMS->msForgetTBF(this);
gL2MAC.macForgetTBF(this,forever); // TBF destruction happens in here.
}
void MsgTransaction::text(std::ostream &os) const
{
os << LOGHEX(mtMsgExpectedBits);
os << LOGHEX(mtMsgAckBits);
for (int i = 0; i < MsgTransMax; i++) {
if (mtExpectedAckBSN[i].valid() && mtExpectedAckBSN[i] + BSNLagTime >= gBSNNext) {
os << " mtExpectedAckBSN["<<i<<"]="<<mtExpectedAckBSN[i];
}
}
}
std::string TBF::tbfDump(bool verbose) const
{
std::ostringstream os;
//int tn = -1, usf = 0;
//PDCHL1FEC *pacch = mtMS->msPacch;
//if (mtDir == RLCDir::Up && pacch) {
// tn = pacch->TN();
// usf = (tn >= 0) ? (int)mtMS->msUSFs[tn] : -1;
//}
// os << LOGVAR(pacch) << LOGVAR(usf);
os << this // Dumps the operator<< value, which is sprintf(TBF#%d,mtDebugId)
<< LOGVAR(mtMS)
//<< " mtDir=" << RLCDir::name(mtDir)
<< LOGVAR(mtDir)
<< "\n";
os << "\t"; mtMS->msDumpChannels(os); os << "\n";
if (0) {
PDCHL1Uplink *up; PDCHL1Downlink *down;
os << "\t";
RN_FOR_ALL(PDCHL1DownlinkList_t,mtMS->msPCHDowns,down) {
os << format(" down%d:%d",down->ARFCN(),down->TN());
}
RN_FOR_ALL(PDCHL1UplinkList_t,mtMS->msPCHUps,up) {
int tn = up->TN();
os << format(" up%d:%d usf=%d",up->ARFCN(),tn,(int)mtMS->msUSFs[tn]);
}
}
os << "\t"
<< LOGVAR2("mtState=",TBFState::name(mtState))
<< LOGVAR(mtAttached)
<< LOGVAR(mtTFI)
<< LOGHEX(mtTlli);
if (mtDir == RLCDir::Down) { os <<" size="<<engineDownPDUSize(); }
if (!verbose) return os.str();
os << "\n\t";
MsgTransaction::text(os);
os << "\n";
os << "\t";
//os << LOGVARRANGE("ChannelCoding",mtChannelCoding(),mtCCMin,mtCCMax); //moved to SignalQuality.
os << LOGVAR(mtUnAckMode);
os <<LOGVAR2("OnCCCH",mtAssignmentOnCCCH);
if (mtAssignCounter) os << LOGVAR(mtAssignCounter);
if (mtReassignCounter) os << LOGVAR(mtReassignCounter);
if (mtPerformReassign) os << LOGVAR(mtPerformReassign);
if (mtMS->msN3101) os << LOGVAR2("N3101",mtMS->msN3101);
if (mtN3103) os << LOGVAR2("N3103",mtN3103);
if (mtN3105) os << LOGVAR2("N3105",mtN3105);
if (mtDeadTime.valid()) os << LOGVAR2("deadTime",- mtDeadTime.elapsed());
if (mtMS->msT3193.active()) { os<<LOGVAR2("msT3193",mtMS->msT3193.remaining()); }
if (mtMS->msT3168.active()) { os<<LOGVAR2("msT3168",mtMS->msT3168.remaining()); }
if (mtDescription.size()) os <<" descr="<<mtDescription;
os <<"\n";
mtMS->msDumpCommon(os);
mtMS->dumpSignalQuality(os);
os << "\t"; engineDump(os);
unsigned total, unique, grants;
engineGetStats(&total,&unique,&grants);
// Note that this statistic is off by the small number of blocks
// that have been sent but not yet acknowledged.
os << "\n\t blocks:" << LOGVAR(total) << LOGVAR(unique) << LOGVAR(grants);
//float efficiency = 1.0 * slotsused / slotstotal;
// os << LOGVAR(efficiency);
//os << "\n";
return os.str();
}
static void tbfDumpAll()
{
if (GPRSDebug & 2) {
GPRSLOG(2) <<"TBF DUMP:\n";
TBF *tbf;
RN_MAC_FOR_ALL_TBF(tbf) { GPRSLOG(2) << tbf->tbfDump(true) << "\n"; }
}
}
// Can never have too many const in a language, thats what I always say.
RLCDownEngine *TBF::getDownEngine()
{
return (mtDir == RLCDir::Down) ? dynamic_cast<RLCDownEngine *>(this) : NULL;
}
//RLCDownEngine const *TBF::getDownEngine() const
//{
// return (mtDir == RLCDir::Down) ? dynamic_cast<RLCDownEngine const*>(this) : NULL;
//}
// Release resources for this TBF: TFI, and if we were the last user of this USF,
// release that as well.
// Note this code is overkill at the moment, because there is only
// one tfi list shared among all channels.
// This does not release the channel assignment.
void TBF::mtDetach()
{
if (mtAttached) {
mtAttached = false; // Must do this before calling msCleanUSFs()
// Set the state to the transitory Deleting state, so that the callers
// who wade through the TBF lists will ignore this one now.
mtSetState(TBFState::Deleting);
mtFreeTFI();
if (mtDir == RLCDir::Up) { mtMS->msCleanUSFs(); }
}
}
// TODO: The TBF needs to detach and then re-attach.
void TBF::mtDeReattach()
{
if (mtAttached) {
mtAttached = false; // Must do this before calling msCleanUSFs()
mtSetState(TBFState::DataReadyToConnect);
mtFreeTFI();
if (mtDir == RLCDir::Up) { mtMS->msCleanUSFs(); }
}
}
uint32_t TBF::mtGetTlli()
{
uint32_t tlli = mtTlli;
if (gFixConvertForeignTLLI) {
if ((tlli & 0xc0000000) == 0x80000000) { // Is it a foreign tlli?
tlli |= TLLI_LOCAL_BIT;
GPRSLOG(1) << "*** Converting foreign tlli to local:"<<LOGHEX(tlli);
}
}
return tlli;
}
// 7-25: Neither the Blackberry nor Multitech like this message.
// Maybe because I include the downlink assignment when there is no downlink TBF?
static bool sendTimeslotReconfigure(
PDCHL1FEC *pacch, // The PACCH channel.
TBF *tbf, // This is an uplink tbf.
std::ostream *os) // for testing - if set, print out the message instead of sending it.
{
RLCMsgPacketTimeslotReconfigure *msg = new RLCMsgPacketTimeslotReconfigure(tbf);
//msg->setTimingAdvance(ms->msGetTA());
// This will set mtExpectedAckBSN if the message is sent.
// If the MS gets this message, it will send Packet Control Acknowledgment
// in the block specified by RRBP.
if (os) {
msg->text(*os);
delete msg;
return true;
} else {
GPRSLOG(1) << "GPRS sendReassignment "<<tbf<<" sending:" << msg;
tbfDumpAll();
// This will increment the counter if the message is really sent.
return pacch->downlink()->
send1MsgFrame(tbf,msg,2,MsgTransReassign,&tbf->mtReassignCounter);
}
}
static bool sendAssignmentPacch(
PDCHL1FEC *pacch, // The PACCH channel.
TBF *tbf,
bool isNewAssignment, // If false, it is a reassignment.
MsgTransactionType msgstate,
std::ostream *os) // for testing - if set, print out the message instead of sending it.
{
MSInfo *ms = tbf->mtMS;
// Send assignment message on PACCH.
// A reassignment message is identical to an assignment message except
// for the ControlAck bit, however, now we use timeslot reconfigure
// for reassignments.
RLCDownlinkMessage *msg;
if (tbf->mtDir == RLCDir::Up) {
RLCMsgPacketUplinkAssignment *ulmsg = new RLCMsgPacketUplinkAssignment(tbf);
RLCMsgPacketUplinkAssignmentDynamicAllocationElt *dynelt;
dynelt = ulmsg->setDynamicAllocation();
dynelt->setUplinkTFI(tbf->mtTFI);
dynelt->setFrom(tbf,MultislotSymmetric);
msg = ulmsg;
} else {
RLCMsgPacketDownlinkAssignment *dlmsg =
new RLCMsgPacketDownlinkAssignment(tbf,isNewAssignment);
msg = dlmsg;
}
// todo: stop timers for ms?
// todo: start timers?
msg->setTimingAdvance(ms->msGetTA());
msg->setTLLI(tbf->mtGetTlli());
// This will set mtExpectedAckBSN if the message is sent.
// If the MS gets this message, it will send Packet Control Acknowledgment
// in the block specified by RRBP.
if (os) {
msg->text(*os);
delete msg;
return true;
} else {
unsigned *pcounter = NULL;
switch (msgstate) {
case MsgTransAssign1:
case MsgTransAssign2:
GPRSLOG(1) << "GPRS sendAssignment "<<tbf<<" sending:" << msg;
pcounter = &tbf->mtAssignCounter;
break;
case MsgTransReassign:
GPRSLOG(1) << "GPRS sendReassignment "<<tbf<<" sending:" << msg;
pcounter = &tbf->mtReassignCounter;
break;
default: devassert(0);
}
tbfDumpAll();
// This will increment the counter if the message is really sent.
devassert(pcounter);
return pacch->downlink()->send1MsgFrame(tbf,msg,2,msgstate,pcounter);
}
}
void sendAssignmentCcch(
PDCHL1FEC *pacch, // The PACCH channel.
TBF *tbf,
std::ostream *os) // for testing - if set, print out the message instead of sending it.
{
static GSM::CCCHLogicalChannel *ourAGCH = 0;
MSInfo *ms = tbf->mtMS;
// Send a message on CCCH.
// TODO FIXME:
// GSM 44.60 5.5.1.5 Describes the conditions under which DRX should be applied.
// The MS sends the DRX info to the SGSN in the Attach message.
// None of that implemented yet.
bool drxmode = false;
// BUG: The returned AGCH time is not monotonically increasing. The bug traces all
// the way back down that the getAGCH call is not, in fact, returning the next one.
// I am guessing that the choice of CCCH has a bug that does not really
// return the next CCCH slot because of the way the idle frames are handled.
// Anyway, this screws up sendAssignment; see todo.txt.
// To try to fix this, we will use just one AGCH, ever, to try to get our
// messages delivered in order.
// TODO: We need to use ourAGCH for unattached MS,
// but we could select a different AGCH for every attached MS.
if (ourAGCH == 0) { ourAGCH = gBTS.getAGCH(); }
// Make a reservation for the poll response.
RLCBSN_t resBSN = 0;
// TODO: Fix this. The DRX mode is sent to us by the MS I think in the
// initial attach message which went to the SGSN.
if (gFixDRX && tbf->mtCcchAssignCounter >= (unsigned)gFixDRX) {
// We may have lost contact with the MS because it is in DRX mode.
// Send the message on all 3 AGCH channels twice - we need
// to saturate both 51 multi-frames because there
// are 6 paging channels. And we just hope no other
// AGCH messages are in the way.
// We need to increase the reservation to make sure it is beyond
// the CCCH in the second 51 multiframe.
drxmode = true;
}
GSM::CCCHLogicalChannel *currentAGCH = drxmode ? gBTS.getPCH(0) : ourAGCH;
resBSN = pacch->makeCCCHReservation(currentAGCH,RLCBlockReservation::ForPoll,tbf,NULL,drxmode,MsgTransAssign1);
if (! resBSN.valid()) {
// We will try again later.
return /*false*/; // We did not use the packet channel downlink.
}
// TODO: mtSetAckExpected function should be moved into the makeReservation code.
// We have to wait for the time whether we sent the poll or not.
tbf->mtSetAckExpected(resBSN,MsgTransAssign1);
// The RequestReference is not used for this type of downlink assignment,
// which contains TLLI instead; we have to set RequestReference to a number that
// cannot possibly be confused with any valid value, ie, somewhere in the future,
// at the time this is sent.
RLCBSN_t impossibleBSN = resBSN + 1000;
GSM::Time impossible(BSN2FrameNumber(impossibleBSN),0); // TN not used.
// The downlink bit documentation is goofed up in GSM 4.18: They clarified it
// in GSM 44.018 sec 10.5.2.25b: This bit is 1 only for downlink TBFs.
L3ImmediateAssignment amsg(
L3RequestReference(0,impossible),
pacch->packetChannelDescription(),
GSM::L3TimingAdvance(ms->msGetTA()),
true,tbf->mtDir == RLCDir::Down); // tbf, downlink
L3IAPacketAssignment *pa = amsg.packetAssign();
// DEBUG: I tried taking out power:
// 12-16: Put power back in:
pa->setPacketPowerOptions(GetPowerAlpha(),GetPowerGamma());
if (gFixIAUsePoll) {
pa->setPacketPollTime(resBSN.FN());
}
if (tbf->mtDir == RLCDir::Up) {
// This assignment type is only for 1-phase access, which we do not support.
// An uplink immediate assignment can only be in response to a RACH
// and is identified by the request reference. Instead we send a
// one-block uplink assignment to get a packet resource request,
// then do the uplink TBF on PACCH based on that.
devassert(0);
// There is only one USF (ie, no multi-slot).
//int usf = ms->msUSFs[pacch->uplink()->TN()];
int usf = ms->msUSFs[pacch->TN()];
devassert(USF_VALID(usf));
pa->setPacketUplinkAssignDynamic(tbf->mtTFI, tbf->mtChannelCoding(), usf);
} else {
pa->setPacketDownlinkAssign(tbf->mtGetTlli(), tbf->mtTFI,
tbf->mtChannelCoding(), tbf->mtUnAckMode, 1);
}
if (os) {
amsg.text(*os);
} else {
GPRSLOG(1) << "GPRS sendAssignment "<<tbf<<" sending L3ImmediateAssignment:"
<<LOGVAR2("agchload",currentAGCH->load()) << amsg;
LOGWATCHF("%s CCCH load=%d\n", tbf->tbfid(1), currentAGCH->load());
tbfDumpAll();
tbf->mtAssignCounter++;
// FIXME TODO:
if (drxmode) {
// Send the message on a paging channel.
// This code assumes the paging channel setup described
// in the class L3ControlChannelDescription.
// This code should be replaced with real paging channels.
// Our BS_PA_MFRMS is 2, so send the paging message twice to make
// sure it is sent in all (2) available paging 51-multiframes.
currentAGCH->send(amsg);
currentAGCH->send(amsg);
// DEBUG: try just plastering these messages out there.
//for (int ii = 0; ii < 6; ii++) {
// GSM::CCCHLogicalChannel *anyagch = gBTS.getAGCH();
// anyagch->send(amsg);
//}
} else {
currentAGCH->send(amsg); // send() takes care of converting it to a BitVector.
}
tbf->mtCcchAssignCounter++;
}
}
// Return true if we send a block on the downlink.
// NOTE: The L3ImmediateAssignment message can not assign multislot assignments.
// NOTE: If MS T3204 (GSM04.08) 1 second, started when MS sends RACH, expires before
// receiving ImmediateAssignment, MS aborts packet access procedure.
bool sendAssignment(
PDCHL1FEC *pacch, // The PACCH channel.
TBF *tbf,
std::ostream *os) // for testing - if set, print out the message instead of sending it.
{
MSInfo *ms = tbf->mtMS;
// This did not help the cause=3105 errors. My idea was that the packet downlink assignment
// is sent in the block immediately following the previous one, and I thought maybe there
// Should we send the message on CCCH or PACCH?
// It depends on which channel the MS is listening to now.
// In short: if there are any active TBFs, or T3168 is running, or T3193 is running,
// the MS is on PACCH.
// The spec is all muddled about this; there is no clear state machine
// showing what the MS is doing, rather, there are separate uplink and downlink
// timers that get started in various modes, and it is not really even obvious
// whether those timers should be started depending on other modes.
bool onccch = (ms->getRROperatingMode() == RROperatingMode::PacketIdle);
// We have maintained the T3193 and T3168 timers exclusively for this moment!
// T3168 is for uplink requests initiated by the MS.
// T3192/3193 are for a new network initiated downlink after completion of previous downlink.
if (ms->msT3193.expired()) { ms->msT3193.reset(); }
if (ms->msT3168.expired()) {
ms->msT3168.reset();
if (tbf->mtDir == RLCDir::Up) {
tbf->mtCancel(MSStopCause::T3168,TbfNoRetry);
return false;
}
}
if (ms->msT3193.active()) { onccch = false; }
if (ms->msT3168.active()) {
if (T3168Behavior) {
// If T3168 is running, the MS supposedly ignores downlink assignments.
// GSM04.60 7.1.3.1, and I quote:
// "At sending of the PACKET RESOURCE REQUEST message,
// the mobile station shall start timer T3168. Further more,
// the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT messages −
// but may acknowledge such messages if they contain a valid RRBP field −
// while timer T3168 is running." GSM44.60 says something different.
if (tbf->mtDir == RLCDir::Down) { return false; } // wait until later.
}
onccch = false;
}
// This is a total hack. After several attempts, just ignore
// whether we think it should be on ccch or not, and alternate back and forth.
// This only works for downlink assignments, which include TLLI.
// The uplink immediate assignment on CCCH is only for one-phase access
// after a RACH, and we dont use that.
tbf->mtAssignmentOnCCCH = false; // For uplink TBF, always false.
if (tbf->mtDir == RLCDir::Down) {
if (tbf->mtAssignCounter < 4) {
tbf->mtAssignmentOnCCCH = onccch;
} else {
tbf->mtAssignmentOnCCCH = ! tbf->mtAssignmentOnCCCH;
}
}
if (GPRSDebug & 1) {
GPRSLOG(1) <<ms <<" OnCCCH="<<tbf->mtAssignmentOnCCCH
<<" RRMode="<<ms->getRROperatingMode()
<<" T3193="<<(ms->msT3193.active()?ms->msT3193.remaining():0)
<<" T3168="<<(ms->msT3168.active()?ms->msT3168.remaining():0);
}
if (tbf->mtAssignmentOnCCCH) {
sendAssignmentCcch(pacch,tbf,os);
return false; // We did not use the packet channel downlink.
} else {
// Send assignment message on PACCH.
return sendAssignmentPacch(pacch,tbf,true,MsgTransAssign1,os);
}
}
static bool sendTA(PDCHL1Downlink *down,TBF *tbf)
{
RLCMsgPacketPowerControlTimingAdvance *tamsg = new RLCMsgPacketPowerControlTimingAdvance(tbf);
// DEBUG: If you change the TFI or downlink flag, this
// is unanswered, as expected:
//tamsg->setGlobalTFI(1,tbf->mtTFI);
GPRSLOG(1) << "GPRS sendTA "<<tbf<<" sending:" << tamsg;
RLCDownlinkMessage *msg = tamsg;
// Lets ask for a response and see what happens:
return down->send1MsgFrame(tbf,msg,2,MsgTransTA,NULL);
}
bool MSInfo::msCanUseDownlinkTn(unsigned tn)
{
PDCHL1Downlink *down;
RN_FOR_ALL(PDCHL1DownlinkList_t,msPCHDowns,down) {
if (down->TN() == tn) {return true;}
}
return false;
}
bool MSInfo::msCanUseUplinkTn(unsigned tn)
{
PDCHL1Uplink *up;
RN_FOR_ALL(PDCHL1UplinkList_t,msPCHUps,up) {
if (up->TN() == tn) {return true;}
}
return false;
}
bool TBF::mtAllocateUSF()
{
// TODO WAY LATER: Is there a deadlock condition possible if multiple multislot MS
// are in contention over USFs in multiple channels?
// I dont think so because we run in one thread and so they cannot become codependent.
PDCHL1Uplink *up;
RN_FOR_ALL(PDCHL1UplinkList_t,mtMS->msPCHUps,up) {
// Is this channel bidirectional? Otherwise the MS cannot use the USF.
if (!mtMS->msCanUseDownlinkTn(up->TN())) {continue;}
int usf = up->mchParent->allocateUSF(mtMS);
// This only allocates a new USF if one is not already allocated for this MS.
if (usf < 0) {
// Failure. But we will keep the TFIs and USFs we have so far and try again later.
GLOG(INFO) << "USF congestion on uplink";
return false;
}
mtMS->msUSFs[up->TN()] = usf; // It might already be assigned, or maybe new.
}
return true;
}
// Set the TFI in the channels in use by the MS that will be used for this TBF transaction.
// The newvalue must be == tbf to set it, or == NULL to reset it.
static
void propagateTFI(TBF*tbf,int tfi,TBF*newvalue)
{
if (tbf->mtDir == RLCDir::Up) {
PDCHL1Uplink *up;
RN_FOR_ALL(PDCHL1UplinkList_t,tbf->mtMS->msPCHUps,up) {
up->setTFITBF(tfi,RLCDir::Up,newvalue);
}
} else {
PDCHL1Downlink *down;
RN_FOR_ALL(PDCHL1DownlinkList_t,tbf->mtMS->msPCHDowns,down) {
down->setTFITBF(tfi,RLCDir::Down,newvalue);
}
}
}
void TBF::mtFreeTFI()
{
if (mtTFI >= 0) {
propagateTFI(this,mtTFI,NULL); // Reset tfi in all channels
mtTFI = -1;
}
}
bool TBF::mtAllocateTFI()
{
if (mtTFI < 0) {
devassert(mtMS->msPCHDowns.size() && mtMS->msPCHUps.size());
PDCHL1FEC* pdch = mtMS->msPCHDowns.front()->parent();
int tfi = pdch->mchTFIs->findFreeTFI(mtDir);
if (tfi < 0) {
GLOG(INFO) << "TFI congestion on "<<mtDir;
return false;
}
mtTFI = tfi;
}
propagateTFI(this,mtTFI,this); // Set TFI in all channels in use by MS.
return true;
}
bool TBF::mtAttach()
{
MSInfo *ms = mtMS;
// msAssignChannels only does something if this is the first transaction.
if (! ms->msAssignChannels()) return false;
if (! mtAllocateTFI()) return false;
if (mtDir == RLCDir::Up && ! mtAllocateUSF()) return false;
mtAttached = true;
mtSetState(TBFState::DataWaiting1);
return true;
}
void TBF::mtSetState(TBFState::type wstate)
{
if (mtState != wstate) {
//mtMsgReset(); // May be starting a new transaction message state.
mtState = wstate;
tbfDumpAll();
if (mtState == TBFState::Dead) {
// TODO: We may be able to reduce the dead time by the length of time
// the MS has not responded and we have not sent it any messages.
// This is how long the TBF will be dead, ie, reserving its resources.
int timerval = mtDir == RLCDir::Up ? gL2MAC.macT3169Value : gL2MAC.macT3195Value;
mtDeadTime.setFuture(timerval);
}
if (mtState == TBFState::DataTransmit) {
LOGWATCHF("%s Start%s bytes=%d down/up=%d/%d\n", tbfid(1),
mtAssignmentOnCCCH ? " CCCH" : "",
engineGetBytesPending(), mtMS->msPCHDowns.size(),mtMS->msPCHUps.size());
// FIXME:
// There is some housekeeping to do.
// If a previous uplink TBF died for this MS, and then the MS was issued
// a new uplink channel assignment, it may still have the the old USFs reserved
// for the old channel assignment.
// We should free those now, but it is just an efficiency issue.
} else if (mtState == TBFState::Finished) {
LOGWATCHF("%s Fin\n", tbfid(1));
} else if (mtState == TBFState::Dead) {
LOGWATCHF("%s Dead\n", tbfid(1));
}
}
}
// Stop all the active TBFs associated with this MS in the specified dir.
// They enter the dead state, which means their resources cannot be reused
// until the timeout expires.
void MSInfo::msStop(RLCDir::type dir, MSStopCause::type cause, TbfCancelMode cmode,
unsigned howlong) // howlong in msecs to disable MS - unused now.
{
TBF *tbf;
RN_MS_FOR_ALL_TBF(this,tbf) {
if (dir != RLCDir::Either && tbf->mtDir != dir) continue;
// 6-26-2012: Changing this test from isTransmitting to isActive.
//if (tbf->isTransmitting())
if (tbf->isActive()) {
tbf->mtCancel(cause,cmode);
}
}
#if 0
//if (!msTxxxx.active()) {
// GPRSLOG(1) << this <<" STOPPED" <<LOGHEX(msTLLI) <<" cause="<<(int)cause;
// msTxxxx.set(howlong);
// // DEBUG: Dont do it;
// return;
// msStopCause = cause;
// // Suspend all the tbfs.
// TBF *tbf;
// RN_FOR_ALL(TBFList_t,msTBFs,tbf) {
// if (tbf->isActive()) { tbf->mtSetState(TBFState::Dead); }
// }
// GLOG(INFO) << "MS with " <<LOGHEX(msTLLI) << " temporarily stopped, cause: " << (int)cause;
//}
#endif
}
#if 0
void MSInfo::msRestart()
{
TBF *tbf;
RN_MS_FOR_ALL_TBF(this,tbf) {
// We only delete dead tbfs, ie, the ones that we stopped previously.
// That is so if someone changes this code in the future to allow
// new TBFs to start during the 5 second delay in resource release,
// we wont delete those.
if (tbf->mtGetState() == TBFState::Dead) { tbf->mtCancel(); }
}
msTxxxx.reset();
msT3191.reset();
msT3193.reset(); // Should have expired already, but be sure.
//msMode = RROperatingMode::PacketIdle;
}
#endif
static RLCDownEngine *createDownlinkTbf(MSInfo *ms, DownlinkQPdu *dlmsg, bool isRetry, ChannelCodingType codingMax)
{
ms->msStalled = 0;
GPRSLOG(2) << "<---- downlink PDU";
RLCDownEngine *engine = new RLCDownEngine(ms);
// Wrong! Removed 4-24 : ms->msT3193.reset();
// At this point the RLCEngine takes charge of the dlmsg memory.
TBF *tbf = engine->getTBF();
tbf->mtTlli = dlmsg->mTlli; // Dont think mtTlli is used in a downlink TBF.
tbf->mtChannelCodingMax = codingMax;
//tbf->mtIsRetry = isRetry;
engine->engineWriteHighSide(dlmsg);
engine->mtSetState(TBFState::DataReadyToConnect);
return engine;
}
// Service this MS, called from the service loop every RLCBSN time.
// Counters and Timers defined in GSM04.60 sec 13.
void MSInfo::msService()
{
// After the last downlink TBF, the MS waits until this timer expires
// before going to PacketIdle mode.
if (msT3193.expired()) {
msT3193.reset();
// If there are no uplink TBFs going, the MS has dropped to PacketIdle mode.
//if (! msCountActiveTBF(RLCDir::Up)) {
// msMode = RROperatingMode::PacketIdle;
//}
}
if (msIsSuspended()) {return;}
if (msTBFs.size()) {
msIdleCounter = 0;
} else {
if (++msIdleCounter > gL2MAC.macMSIdleMax) {
msDelete();
return;
}
}
// If MS running, check for counter expiration and stop if necessary.
// =========== From GSM04.60 sec 13: ==========
// N3101:
// When the network after setting USF, receives a valid data block from the mobile station, it will
// reset counter N3101. The network will increment counter N3101 for each USF for which no data
// is received. N3101max shall be greater than 8.
// N3103:
// N3103 is reset when transmitting the final PACKET UPLINK ACK/NACK message within a TBF
// (final ack indicator set to 1). If the network does not receive the PACKET CONTROL
// ACKNOWLEDGEMENT message in the scheduled block, it shall increment counter N3103 and
// retransmit the PACKET UPLINK ACK/NACK message. If counter N3103 exceeds its limit, the
// network shall start timer T3169.
// N3105:
// When the network after sending a RRBP field in the downlink RLC data block , receives a valid
// RLC/MAC control message from the mobile station, it will reset counter N3105. The network will
// increment counter N3105 for each allocated data block for which no RLC/MAC control message
// is received. The value of N3105max is network dependent.
// ===========================================
// (pat) Having multiple timers here is overkill in the spec;
// they all do the same thing and will probably have the same value:
// they wait to make sure the MS is in PacketIdle mode before releasing resources.
// When N3101 or N3103 counter expires, timeout using T3169.
// 7-5: I want to count RLC block periods, not USFs, so multiply by the number
// of uplink channels in use.
if (msN3101 * min(1,(int)msPCHUps.size()) > gL2MAC.macN3101Max) {
msStop(RLCDir::Up,MSStopCause::N3101,TbfRetryAfterWait,gL2MAC.macT3169Value);
}
// TODO:
//if (msN3103 > gL2MAC.macN3103Max) {
// msStop(MSStopCause::N3103,gL2MAC.macT3169Value);
//}
// 12-22: I am taking this timer out for now, because it needs to be in the TBF,
// not the MS. We will detect timeout here using RRBPs. If you want to put it back in,
// move it to class TBF.
//if (msT3191.expired()) {
// Spec says when T3191 expires (5 secs) can release resources,
// but we will wait another second.
// msStop(MSStopCause::T3191,1000);
//}
// If MS is stopped, check for timer expiry and restart if necessary.
//if (msTxxxx.expired()) { msRestart(); }
// If there is a downlink message and this MS does not have any downlink TBFs running,
// create a new TBF.
while (msDownlinkQueue.size()) {
// We will not start a new downlink TBF as long as there is any kind
// of downlink TBF.
// Formerly, (if 0==StallOnlyForActiveTBF) we also stalled for dead TBFS
// (indicating the MS is probably unreachable) but that tended to kill off
// active TBFs when any one died for mysterious reasons, so I turned it off.
// 6-24-2012 UPDATE: I am going to reset StallOnlyForActive because we dont
// have bugs and we now use dead tbfs to legitimately block downlinks until expiry.
bool stallOnlyForActiveTBF = configGetNumQ("GPRS.TBF.StallOnlyForActive",0);
TBF *blockingtbf;
if (! msCountTBF2(RLCDir::Down,stallOnlyForActiveTBF?TbfMActive:TbfMAny,&blockingtbf)) {
DownlinkQPdu *dlmsg = msDownlinkQueue.read();
// Because the message is queued for this MS, it means the tlli
// is equal to either msTlli or msOldTlli. The SGSN tells us which
// one to use. Make sure it is the current one.
// The tlli is changed on the next message after an attach procedure.
msChangeTlli(dlmsg->mTlli);
createDownlinkTbf(this,dlmsg,false,ChannelCodingMax);
} else {
// This code just prints a nice message:
devassert(blockingtbf);
// stalltype is 1 for stalled by active, 2 for stalled by dead tbf.
unsigned stalltype = blockingtbf->isActive() ? 1 : 2;
if (stalltype != msStalled) {
GPRSLOG(2) << this <<" msDownlinkQueue stalled by "
<<(stalltype==1 ? "active:" : "dead:") << blockingtbf;
msStalled = stalltype;
}
}
break;
}
// If the MS has a delayed request an uplink TBF, start it up.
// TODO: If the Q is small, try flow control?
// FIXME: no longer necessary?
//if (msUplinkRequest) {
// TBF *tbf = TBF::newUpTBF(ms,msrmsg->mCRD);
//if (ms->msCountActiveTBF(RLCDir::Up, &activeTbf) >= 1)
//}
// Over-riding TBF killer.
// In the spec there is no such timer; instead there are timers for individual
// states from the spec, but that does not include all the individual substates
// we may go through, like reassignment. If any of those has a bug the TBF
// state machine may hang forever. This would usually prevent that.
// I am defaulting this timer to 6 secs which is longer than any other.
if (msTBFs.size()) {
// TODO: Should be TBF.NonResponsivve.
int timerVal = gConfig.getNum("GPRS.Timers.MS.NonResponsive"); // value of 0 disables.
if (timerVal > 0 && msTalkUpTime.elapsed() > timerVal) {
msStop(RLCDir::Either,MSStopCause::NonResponsive,TbfNoRetry,gL2MAC.macT3169Value);
}
}
if (((int)gBSNNext % 24) == 0) msTrafficMetric = msTrafficMetric / 2;
}
// The TBF ends either in mtFinishSuccess or mtCancel.
void TBF::mtFinishSuccess()
{
mtMS->msT3191.reset();
//GPRSLOG(1) << "@@@ok" << this <<" dir="<<mtDir <<" descr="<<mtDescription <<" OnCCCH="<<mtAssignmentOnCCCH;
GPRSLOG(1) << "@@@ok" << this->tbfDump(false)<<timestr();
mtSetState(TBFState::Finished);
Timeval now;
mtMS->msAddConnectTime(now.delta(mtStartTime)); // This includes the time it took the TBF to get its assignment through.
// When a downlink TBF stops, set T3193, which measures how long the MS camps on the line.
// Note that this timer runs even if there are intervening uplink TBFs.
if (mtDir == RLCDir::Down) {
mtMS->msT3193.set();
} else {
// If the last uplink TBF ends and the T3193 is not running,
// MS immediately enters PacketIdle mode.
//int anyactive = msCountActiveTBF(RLCDir::Either);
//if (!anyactive) {
// mtMS->msMode = RROperatingMode::PacketIdle;
//}
}
}
// If the TBF never even started, the previous dlmsg will not have been pulled