forked from evilsocket/openbts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RLCMessages.h
1546 lines (1404 loc) · 53.1 KB
/
RLCMessages.h
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, 2014 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* 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.
*/
/**@file GPRS L2 RLC Messages, from GSM 04.60 Section 11 */
#ifndef GPRSL2RLCMESSAGES_H
#define GPRSL2RLCMESSAGES_H
#include <iostream>
#include <stdint.h>
#include "Defines.h"
#include "BitVector.h"
#include "Logger.h"
#include "GSMCommon.h"
//#include "GSMTransfer.h"
//#include "GSMTDMA.h"
//#include <Globals.h>
#include "MsgBase.h"
#include "RLCHdr.h"
#include "TBF.h"
#include "MemoryLeak.h"
namespace GPRS {
class TBF;
/** GSM 04.60 11.2 */
class RLCMessage : public Text2Str {
public:
virtual void text(std::ostream&) const = 0;
RLCMessage() { RN_MEMCHKNEW(RLCMessage) }
// The virtual keyword on a destructor indicates that both the base destructor (this one)
// and the derived class destructor are both called. Otherwise they aren't. It is foo bar.
virtual ~RLCMessage() { RN_MEMCHKDEL(RLCMessage) }
};
std::ostream& operator<<(std::ostream& os, const RLCMessage *msg);
#if RLCMESSAGES_IMPLEMENTATION
std::ostream& operator<<(std::ostream& os, const RLCMessage *msg)
{
msg->text(os);
return os;
}
#endif
class RLCMsgUplinkIE
{
public:
virtual void parseElement(const BitVector &src, size_t &rp) = 0;
virtual void writeBody(MsgCommon&dst) const = 0;
// The indivual IE can overwrite textElement is MsgCommon does not work very well for it.
virtual void textElement(std::ostream& os) const {
MsgCommonText dst(os);
writeBody(dst);
}
};
class RLCMsgDownlinkIE
{
public:
virtual void writeBody(MsgCommon&dst) const = 0;
void writeOptional01(MsgCommon &dst, bool control) const {
if (control) {
dst.write1();
writeBody(dst);
} else {
dst.write0();
}
}
};
// The RLCDownlink and RLCUplink Messages are RLC Control messages -
// they are not L3 Messages as defined in GSM 04.18.
// These messages are used at the RLC/MAC layer to control the PDCH channels
// assigned for packet (GPRS) use. Their primary use is to transfer
// L3 Messages and user data between the SGSN and the MS.
// Mobility Management routines reside entirely inside the SGSN.
class RLCDownlinkMessage :
public MACDownlinkHeader,
public RLCMessage
{
public:
/** Message Type GSM 04.06 11.2.0.2 */
// The ones we will implement marked with <<<
// Message marked PCCCH only cannot be implemented now.
// From sec 11.1.1.1: if the high bit of a downlink control message type is 1,
// then it is a distribution message to all MS, otherwise it is a non-distribution message.
// Note that the downlink message content is byte aligned (6 bit MessageType + 2 bit PageMode)
// Also note that the uplink message content is not byte aligned.
// From sec 11.1.1.2: Non-distribution messages may have distribution contents,
// followed by an Address Information (to identify a single MS) followed by
// non-distribution contents.
// NOTE: The unused bits at the end of the control message must be packed out
// as per GSM04.60sec11: one 0 bit, followed by 0 bits to get to a byte boundary,
// followed by byte 0x2b forever.
enum MessageType {
/*100001 */ PacketAccessReject = 0x21, // PACCH or PCCCH <<<<
/*000001 */ PacketCellChangeOrder = 0x1, // PCCCH or PACCH
/*000010 */ PacketDownlinkAssignment = 0x2, // PCCCH or PACCH <<<< acknowledged
/*000011 */ PacketMeasurementOrder = 0x3, // PCCCH or PACCH
// PagingRequest is only sent on PACCH to initiate an RR connection, so skip for now:
// Note: You can send still GSM paging messages on CCCH when MS is in packet-idle mode.
/*100010 */ PacketPagingRequest = 0x22, // PCCCH or PACCH
/*100011 */ PacketPDCHRelease = 0x23, // PACCH
/*000100 */ PacketPollingRequest = 0x4, // PACCH or PCCH <<<< not used implicitly acknowldged
/*000101 */ PacketPowerControlTimingAdvance = 0x5, // PACCH
/*100100 */ PacketPRACHParameters = 0x24, // PCCCH only
/*000110 */ PacketQueueingNotification = 0x6, // PCCCH only
/*000111 */ PacketTimeslotReconfigure = 0x7, // PACCH
/*001000 */ PacketTBFRelease = 0x8, // PACCH <<< acknowledged
/*001001 */ PacketUplinkAckNack = 0x9, // PACCH <<<
// The acknowledgement to Packet Uplink Assignment is not strictly needed
// because we will know as soon as the MS starts sending blocks.
/*001010 */ PacketUplinkAssignment = 0xa, // PCCCH or PACCH <<< acknowledged
/*100101 */ PacketDownlinkDummyControlBlock = 0x25, // PCCCH or PACCH
/*110001 */ PSI1 = 0x31,
/*110010 */ PSI2 = 0x32,
/*110011 */ PSI3 = 0x33,
/*110100 */ PSI3bis = 0x34,
/*110101 */ PSI4 = 0x35,
/*110110 */ PSI5 = 0x36,
/*110000 */ PSI6 = 0x30,
/*111000 */ PSI7 = 0x38,
/*111001 */ PSI8 = 0x39,
/*110111 */ PSI13 = 0x37,
/*111010 */ PSI14 = 0x3a,
/*111100 */ PSI3ter = 0x3c,
/*111101 */ PSI3quater = 0x3d,
/*111110 */ PSI15 = 0x3e
};
static const char *name(MessageType mtype);
// There are optional additional RLC Control Block header fields here, but
// we will not use them, so they are not even mentioned here.
MessageType mMessageType; // 6 bits
unsigned mPageMode; // 2 bits
TBF *mTBF; // If this message is associated with a tbf, here it is.
RLCDownlinkMessage(MessageType mtype, bool wRequiresAck, TBF *wtbf) :
mMessageType(mtype),
mPageMode(0),
mTBF(wtbf)
{
MACDownlinkHeader::init(MACPayloadType::RLCControl);
}
// Not all the messages have setTLLI().
virtual void setTLLI(int) {};
// Not all the messages have setTimingAdvance().
virtual void setTimingAdvance(int) {};
// writeBody defined in each message class to write the message body starting after PageMode.
virtual void writeBody(MsgCommon&dst) const = 0;
void writeHeader(MsgCommon &dst) const;
/** Serialize this message into a BitVector. The caller is responsible for deleting the memory.
*/
void write(BitVector&vec) const;
void text(std::ostream &os) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCDownlinkMessage::writeHeader(MsgCommon& dst) const {
MACDownlinkHeader::writeMACHeader(dst);
dst.WRITE_FIELD(mMessageType,6);
dst.WRITE_FIELD(mPageMode,2);
}
void RLCDownlinkMessage::text(std::ostream &os) const {
MsgCommonText dst(os);
os << name(mMessageType) << ":";
writeHeader(dst);
writeBody(dst);
}
void RLCDownlinkMessage::write(BitVector&vec) const {
MsgCommonWrite dst(vec);
writeHeader(dst);
writeBody(dst);
// GSM04.60 sec 11:
// The padding bits may be the 'null' string. Otherwise, the padding
// bits starts with bit '0', followed by 'spare padding'.
if (dst.wp & 0x7) { dst.write0(); } // one optional 0 bit
while (dst.wp & 0x7) { dst.writeL(); } // pad to a byte boundary.
while (dst.wp < RLCBlockSizeInBits[0]) { dst.writeField(0x2b,8); }
}
#endif
/** GSM 04.60 11.2 */
class RLCUplinkMessage : public RLCMessage
{
public:
// GSM04.60 sec 11.2
enum MessageType {
/* 000000 */ PacketCellChangeFailure = 0x0,
/* 000001 */ PacketControlAcknowledgement = 0x1,
/* 000010 */ PacketDownlinkAckNack = 0x2,
/* 000011 */ PacketUplinkDummyControlBlock = 0x3,
/* 000100 */ PacketMeasurementReport = 0x4,
/* 001010 */ PacketEnhancedMeasurementReport = 0xa,
/* 000101 */ PacketResourceRequest = 0x5,
/* 000110 */ PacketMobileTBFStatus = 0x6,
/* 000111 */ PacketPSIStatus = 0x7,
/* 001000 */ EGPRSPacketDownlinkAckNack = 0x8,
/* 001001 */ PacketPause = 0x9,
/* 001011 */ AdditionalMSRadioAccessCapabilities = 0xb
};
static const char *name(MessageType type);
MACUplinkHeader mmac; // 8 bits
MessageType mMessageType; // 6 bits
// Parse/text just the body. These must be defined in each descendent class
//virtual void textBody(std::ostream&os) const = 0;
virtual int getTFI() { return -1; }
virtual MSInfo *getMS(PDCHL1FEC *chan, bool create) { assert(0); }
virtual MSInfo *getTBF(PDCHL1FEC *chan) { assert(0); }
virtual void parseBody(const BitVector &src, size_t &rp) = 0;
virtual void writeBody(MsgCommon&dst) const = 0;
void parse(const RLCRawBlock *src);
void writeHeader(MsgCommon &dst) const;
/** Serialize this message into a BitVector. The caller is responsible for deleting the memory.
*/
void write(BitVector&vec) const;
void text(std::ostream &os) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCUplinkMessage::parse(const RLCRawBlock*src) {
// The MACUplinkHeader is explict (mmac) instead of a base class
// to make this line of code more obvious:
mmac = src->mmac; // Parsed previously.
size_t rp = mmac.lengthBits();
mMessageType = (MessageType) src->mData.readField(rp,6);
parseBody(src->mData,rp);
}
void RLCUplinkMessage::writeHeader(MsgCommon&dst) const {
mmac.writeMACHeader(dst);
dst.WRITE_FIELD(mMessageType,6);
}
void RLCUplinkMessage::text(std::ostream &os) const {
MsgCommonText dst(os);
os << name(mMessageType) << ":";
mmac.text(os); // or: mmac.writeMACHeader(dst);
dst.WRITE_FIELD(mMessageType,6);
writeBody(dst);
}
void RLCUplinkMessage::write(BitVector&vec) const {
MsgCommonWrite dst(vec);
writeHeader(dst);
writeBody(dst);
// GSM04.60 sec 11: For RLC messages: one 0 followed by byte-aligned 0x2b
if (dst.wp & 0x7) { dst.write0(); } // one optional 0 bit
while (dst.wp & 0x7) { dst.writeL(); } // pad to a byte boundary.
while (dst.wp < RLCBlockSizeInBits[0]) { dst.writeField(0x2b,8); }
}
#endif
// (pat) This is for downlink power parameters, as per GSM04.60 sec 11.2.29.
// We will probably never use this.
//struct L3IADownlinkPowerOptionIE {
// int mPowerOption; // 1 if P0, PWR_CTRL_MODE, PR_MODE should be included.
// unsigned mP0:4;
// unsigned mBTSPwrCtrlMode:1;
// unsigned mPRMode:1;
// void writePower(L3Frame &dst, int whichDesignationMethod) {
// if (mPowerOption) {
// whichDesignationMethod ? dst.writeH() : dst.writeField(1,1);
// dst.writeField(P0,4);
// dst.writeField(mBTSPwrCtrlMode,1);
// dst.writeField(mPRMode,1);
// } else {
// whichDesignationMethod ? dst.writeL() : dst.writeField(0,1);
// }
// }
// L3AssignmentPowerOptionIE::L3AssignmentPowerOptionIE() { memset(this,0,sizeof(*this)); }
//};
// GSM04.60 12.3
// Note this IE is both uplink and downlink.
struct RLCMsgPacketAckNackDescriptionIE : public RLCMsgUplinkIE, public RLCMsgDownlinkIE
{
Field<1> mFinalAckIndication;
Field<7> mSSN; // "Starting Sequence Number" except it is not.
// It is actually the ending sequence number.
static const int mbitmapsize = 64;
bool mBitMap[mbitmapsize];
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketAckNackDescriptionIE::parseElement(const BitVector &src, size_t &rp)
{
mFinalAckIndication = src.readField(rp,1);
mSSN = src.readField(rp,7);
for (int i = 0; i < mbitmapsize; i++) {
mBitMap[i] = src.readField(rp,1);
}
}
void RLCMsgPacketAckNackDescriptionIE::writeBody(MsgCommon&dst) const
{
dst.WRITE_ITEM(mFinalAckIndication);
dst.WRITE_ITEM(mSSN);
dst.writeBitMap((bool*)mBitMap,mbitmapsize,"Bitmap");
}
#endif
// GSM04.60 12.7
struct RLCMsgChannelRequestDescriptionIE : public RLCMsgUplinkIE
{
Field<4> mPeakThroughputClass; // See 3GPP 24.008
Field<2> mRadioPriority; // See 11.2.5 0 =highest, 3=lowest
Field<1> mRLCMode; // 0 == acknowledged, 1 == unacknowleged mode.
Field<1> mLLCPDUType; // 0 == LLC PDU is SACK or ACK[acknowledged mode], 1 == its not
Field<16> mRLCOctetCount; // Number of octets MS wants to transfer, or 0 if unspecified.
void parseElement(const BitVector &src, size_t &rp);
//void textElement(std::ostream&) const;
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgChannelRequestDescriptionIE::parseElement(const BitVector &src, size_t &rp)
{
mPeakThroughputClass = src.readField(rp,4);
mRadioPriority = src.readField(rp,2);
mRLCMode = src.readField(rp,1);
mLLCPDUType = src.readField(rp,1);
mRLCOctetCount = src.readField(rp,16);
}
void RLCMsgChannelRequestDescriptionIE::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mPeakThroughputClass);
dst.WRITE_ITEM(mRadioPriority);
dst.WRITE_ITEM(mRLCMode);
dst.WRITE_ITEM(mLLCPDUType);
dst.WRITE_ITEM(mRLCOctetCount);
}
/*
void RLCMsgChannelRequestDescriptionIE::textElement(std::ostream&os) const
{
os << " ChannelRequest=(";
RN_WRITE_TEXT(mPeakThroughputClass);
RN_WRITE_TEXT(mRadioPriority);
RN_WRITE_TEXT(mRLCMode);
RN_WRITE_TEXT(mLLCPDUType);
RN_WRITE_TEXT(mRLCOctetCount);
os << ")";
}
*/
#endif
struct MSRACap_s : public RLCMsgUplinkIE
{
Field<4> mAccessTechnologyType; // The caller parses and sets this.
bool mA5BitsPresent;
Field<3> mRFPowerCapability;
Field<7> mA5Bits;
Field<1> mESInd;
Field<1> mPS;
Field<1> mVGCS;
Field<1> mVBS;
bool mMultiSlotCapPresent;
bool mHSCDMultiSlotClassPresent;
Field<5> mHSCDMultiSlotClass;
bool mGPRSMultiSlotClassPresent;
Field<5> mGPRSMultiSlotClass;
Field<1> mGPRSExtendedDynamicAllocationCapability;
bool mSMSPresent;
Field<4> mSMS_VALUE; Field<4> mSM_VALUE;
// There is much more, but we dont keep it.
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textElement(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void MSRACap_s::parseElement(const BitVector &src, size_t &rp)
{
mRFPowerCapability = src.readField(rp,3);
if ((mA5BitsPresent = src.readField(rp,1))) {
mA5Bits = src.readField(rp,7);
}
mESInd = src.readField(rp,1);
mPS = src.readField(rp,1);
mVGCS = src.readField(rp,1);
mVBS = src.readField(rp,1);
if ((mMultiSlotCapPresent = src.readField(rp,1))) {
// Multislot Capability Struct:
if ((mHSCDMultiSlotClassPresent = src.readField(rp,1))) {
mHSCDMultiSlotClass = src.readField(rp,5);
}
if ((mGPRSMultiSlotClassPresent = src.readField(rp,1))) {
mGPRSMultiSlotClass = src.readField(rp,5);
mGPRSExtendedDynamicAllocationCapability = src.readField(rp,1);
}
if ((mSMSPresent = src.readField(rp,1))) {
mSMS_VALUE = src.readField(rp,4);
mSM_VALUE = src.readField(rp,4);
}
} else {
mGPRSMultiSlotClassPresent = 0;
}
}
void MSRACap_s::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mAccessTechnologyType);
dst.WRITE_ITEM(mRFPowerCapability);
if (dst.write01(mA5BitsPresent)) { dst.WRITE_ITEM(mA5Bits); }
dst.WRITE_ITEM(mESInd);
dst.WRITE_ITEM(mVGCS);
dst.WRITE_ITEM(mVBS);
if (mMultiSlotCapPresent) {
if (dst.write01(mHSCDMultiSlotClassPresent)) dst.WRITE_ITEM(mHSCDMultiSlotClass);
if (dst.write01(mGPRSMultiSlotClassPresent)) {
dst.WRITE_ITEM(mGPRSMultiSlotClass);
dst.WRITE_ITEM(mGPRSExtendedDynamicAllocationCapability);
}
if (dst.write01(mSMSPresent)) {
dst.WRITE_ITEM(mSMS_VALUE);
dst.WRITE_ITEM(mSM_VALUE);
}
}
// More stuff ignored.
}
/*
void MSRACap_s::textElement(std::ostream&os) const {
RN_WRITE_TEXT(mAccessTechnologyType);
RN_WRITE_TEXT(mRFPowerCapability);
RN_WRITE_OPT_TEXT(mA5Bits,mA5BitsPresent);
RN_WRITE_TEXT(mESInd);
RN_WRITE_TEXT(mPS);
RN_WRITE_TEXT(mVGCS);
RN_WRITE_TEXT(mVBS);
RN_WRITE_OPT_TEXT(mGPRSMultiSlotClass,mGPRSMultiSlotClassPresent);
}
*/
#endif
// GSM24.008sec10.5.5.12a
struct RLCMsgMSRACapabilityValuePartIE : public RLCMsgUplinkIE
{
// There can be many of these.
// The first one is all we are interested in.
// We will map all the other caps into the second struct, which we wont even use.
MSRACap_s MSRACap[2];
int mNumMSRACap;
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textElement(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgMSRACapabilityValuePartIE::parseElement(const BitVector &src, size_t &rp)
{
mNumMSRACap = 0;
do {
int AccessTechnologyType = src.readField(rp,4);
unsigned len = src.readField(rp,7); // Length in bits of the rest of this access cap.
// Kyle at L3 reported that this was crashing in the src.readField below,
// which means this struct is invalid or not being parsed properly.
// Lets check, although it actually needs to be much bigger than this
// because this is embedded inside a large message.
if (len + 1 >= src.size()) {
LOG(INFO) << "Invalid RA Capability Value Part struct"<<LOGVAR(len)<<LOGVAR(mNumMSRACap);
}
size_t startrp = rp;
if (AccessTechnologyType != 0xf) {
if (mNumMSRACap < 2) { // Save the first two.
MSRACap[mNumMSRACap].mAccessTechnologyType = AccessTechnologyType;
MSRACap[mNumMSRACap].parseElement(src,rp);
mNumMSRACap++;
}
} else {
// Additional access technologies. Just throw it away.
}
rp = startrp + len;
} while (src.readField(rp,1));
}
void RLCMsgMSRACapabilityValuePartIE::writeBody(MsgCommon&dst) const {
std::ostream *os;
if ((os = dst.getStream())) {
for (int i=0; i < mNumMSRACap; i++) {
*os << " RACapability[" << i <<"]=(";
MSRACap[i].writeBody(dst);
*os << ")";
}
} else {
// Not implemented. It is an uplink IE so we dont need it.
}
}
/*
void RLCMsgMSRACapabilityValuePartIE::textElement(std::ostream&os) const
{
for (int i=0; i < mNumMSRACap; i++) {
os << " RACapability[" << i <<"]=("; MSRACap[i].textElement(os); os << ")";
}
}
*/
#endif
// GSM 04.60 12.10
struct RLCMsgGlobalTFIIE : public RLCMsgDownlinkIE, public RLCMsgUplinkIE
{
Field_z<1> mIsDownlinkTFI; // 1 for downlink TFI, 0 for uplink TFI
Field_z<5> mGTFI; // This is NOT a TFI assignment; it is used
// to identify the MS which receives this message.
void setTFI(bool wIsDownlinkTFI,int wTFI) {
// TBF uninitialized TFI is -1; it is an error if that value finds its way here.
assert(wTFI >= 0 && wTFI < 32);
mIsDownlinkTFI = wIsDownlinkTFI; mGTFI = wTFI;
}
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgGlobalTFIIE::parseElement(const BitVector &src, size_t &rp) {
mIsDownlinkTFI = src.readField(rp,1);
mGTFI = src.readField(rp,5);
}
void RLCMsgGlobalTFIIE::writeBody(MsgCommon &dst) const {
dst.WRITE_ITEM(mIsDownlinkTFI);
dst.WRITE_ITEM(mGTFI);
}
#endif
// 3GPP 44-060 11.2.6
// See 45.008 for definitions of these things.
struct ChannelQualityReportIE : public RLCMsgUplinkIE
{
// 45.008 10.2.3: "C Value is the enormalized received signal level at the MS
// as defined in 10.2.3.1".
// During Packet Transfer mode (which we are for a DownlinkAckNack)
// C Value and SIGN_VAR (variance of C Value) are measured from BCCH and
// subsequently used to determine MS output power as per 10.2.1.
Field<6> mCValue;
// RXQUAL is derived from decoder BEP [Bit Error Probability]
Field<3> mRXQual; // From decoder, low is better, meaningless for CS-4 encoding.
// SIGN_VAR from 0 to 64 encoded as: 0 to 15.75dB in 0.25dB steps.
Field<6> mSignVar;
// 45.008 10.3: I_LEVEL is measured interference level on each channel.
Bool_z mHaveILevel[8];
Field<4> mILevel[8]; // For each timeslot.
void parseElement(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void ChannelQualityReportIE::parseElement(const BitVector &src, size_t &rp) {
mCValue = src.readField(rp,6);
mRXQual = src.readField(rp,3);
mSignVar = src.readField(rp,6);
for (int i = 0; i <= 7; i++) {
if ((mHaveILevel[i] = src.readField(rp,1))) {
mILevel[i] = src.readField(rp,4);
}
}
}
void ChannelQualityReportIE::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mCValue);
dst.WRITE_ITEM(mRXQual);
dst.WRITE_ITEM(mSignVar);
for (int i = 0; i <= 7; i++) {
if (mHaveILevel[i]) { dst.WRITE_ITEM(mILevel[i]); }
}
}
#endif
/** GSM04.60 11.2.6 From MS to network. */
struct RLCMsgPacketDownlinkAckNack : public RLCUplinkMessage
{
Field<5> mTFI;
RLCMsgPacketAckNackDescriptionIE mAND;
bool mHaveChannelRequest;
RLCMsgChannelRequestDescriptionIE mCRD;
ChannelQualityReportIE mCQR;
// We can ignore the rest of the message.
// struct ChannelQualityReport CQR;
// unsigned PFI:7 // (we wont use it)
RLCMsgPacketDownlinkAckNack(const RLCRawBlock *src) { parse(src); }
int getTFI() { return mTFI; }
void parseBody(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketDownlinkAckNack::parseBody(const BitVector &src, size_t &rp)
{
mTFI = src.readField(rp,5);
mAND.parseElement(src,rp);
mHaveChannelRequest = src.readField(rp,1);
if (mHaveChannelRequest) { mCRD.parseElement(src,rp); }
mCQR.parseElement(src,rp);
}
void RLCMsgPacketDownlinkAckNack::writeBody(MsgCommon&dst) const {
dst.WRITE_ITEM(mTFI);
mAND.writeBody(dst);
if (dst.write01(mHaveChannelRequest)) { mCRD.writeBody(dst); }
mCQR.writeBody(dst);
}
/*
void RLCMsgPacketDownlinkAckNack::textBody(std::ostream&os) const
{
RN_WRITE_TEXT(mTFI);
mAND.textElement(os);
if (mHaveChannelRequest) { mCRD.textElement(os); }
}
*/
#endif
//static uint64_t readOptField(BitVector&src,size_t&rp,int bits,bool&option) {
// if ((option = src.readField(rp,1))) {
// return src.readField(rp,bits);
// }
//}
/** GSM04.60 11.2.6 From MS to network. */
struct RLCMsgPacketControlAcknowledgement : public RLCUplinkMessage
{
Field<32> mTLLI; // Yes, this really is not byte aligned in the message.
Field<2> mCtrlAck; // Which segments of the RLC/MAC control message were received?
RLCMsgPacketControlAcknowledgement(const RLCRawBlock *src) { parse(src); }
void parseBody(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketControlAcknowledgement::parseBody(const BitVector&src, size_t&rp)
{
mTLLI = src.readField(rp,32);
mCtrlAck = src.readField(rp,2);
}
void RLCMsgPacketControlAcknowledgement::writeBody(MsgCommon&dst) const {
dst.writeField(mTLLI,32,"TLLI",tohex);
dst.WRITE_ITEM(mCtrlAck);
}
/*
void RLCMsgPacketControlAcknowledgement::textBody(std::ostream&os) const
{
RN_WRITE_TEXT(mTLLI);
RN_WRITE_TEXT(mCtrlAck);
}
*/
#endif
/** GSM04.60 11.2.16 From MS to network. */
struct RLCMsgPacketResourceRequest : public RLCUplinkMessage
{
bool mAccessTypePresent; // 0=two phase, 1=page response, 2=cell update, 3=mm procedure
Field<2> mAccessType;
RLCMsgGlobalTFIIE mGTFI;
bool mTLLIPresent; // 1 for TLLI present, 0 for TFI present.
Field<32> mTLLI;
bool mMSRadioAccessCapability2Present;
RLCMsgMSRACapabilityValuePartIE mMSRACap;
RLCMsgChannelRequestDescriptionIE mCRD;
bool mChangeMarkPresent;
Field<2> mChangeMark;
Field<5> mCValue;
bool mSignVarPresent;
Field<6> mSignVar; // Not present for two phase access.
bool mILevelPresent[8];
Field<4> mILevelTN[8];
bool mExtensionsPresent;
RLCMsgPacketResourceRequest(const RLCRawBlock *src) { parse(src); }
// Return the MSInfo identified by the contents of this message.
MSInfo *getMS(PDCHL1FEC *chan, bool create);
void parseBody(const BitVector &src, size_t &rp);
void writeBody(MsgCommon&dst) const;
//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketResourceRequest::parseBody(const BitVector &src, size_t &rp)
{
if ((mAccessTypePresent = src.readField(rp,1))) { mAccessType = src.readField(rp,2); }
if ((mTLLIPresent = src.readField(rp,1))) {
mTLLI = src.readField(rp,32);
} else {
mGTFI.parseElement(src,rp);
}
if ((mMSRadioAccessCapability2Present = src.readField(rp,1))) {
mMSRACap.parseElement(src,rp);
}
mCRD.parseElement(src,rp); // We will use this.
if ((mChangeMarkPresent = src.readField(rp,1))) { mChangeMark = src.readField(rp,2); }
mCValue = src.readField(rp,6);
if ((mSignVarPresent = src.readField(rp,1))) { mSignVar = src.readField(rp,2); }
for (int i = 0; i < 8; i++) {
mILevelTN[i] = (mILevelPresent[i] = src.readField(rp,1)) ? src.readField(rp,4) : -1;
}
mExtensionsPresent = rp < src.size() && src.readField(rp,1);
// But ignore the exensions.
}
void RLCMsgPacketResourceRequest::writeBody(MsgCommon&dst) const {
if (dst.write01(mAccessTypePresent)) { dst.WRITE_ITEM(mAccessType); }
if (dst.write01(mTLLIPresent)) {
dst.writeField(mTLLI,32,"TLLI",tohex);
} else {
mGTFI.writeBody(dst);
}
if (dst.write01(mMSRadioAccessCapability2Present)) {
mMSRACap.writeBody(dst);
}
mCRD.writeBody(dst);
if (dst.write01(mChangeMarkPresent)) { dst.WRITE_ITEM(mChangeMark); }
dst.WRITE_ITEM(mCValue);
if (dst.write01(mSignVarPresent)) { dst.WRITE_ITEM(mSignVar); }
for (int i = 0; i < 8; i++) {
if (dst.write01(mILevelPresent[i])) dst.WRITE_ITEM(mILevelTN[i]);
}
dst.writeField(mExtensionsPresent,1,"mExtensionsPresent");
}
/*
void RLCMsgPacketResourceRequest::textBody(std::ostream&os) const {
RN_WRITE_OPT_TEXT(mAccessType,mAccessTypePresent);
RN_WRITE_OPT_TEXT(mTLLI,mTLLIPresent);
RN_WRITE_OPT_TEXT(mIsDownlinkTFI,!mTLLIPresent);
RN_WRITE_OPT_TEXT(mTFI,!mTLLIPresent);
if (mMSRadioAccessCapability2Present) { mMSRACap.textElement(os); }
mCRD.textElement(os);
RN_WRITE_OPT_TEXT(mChangeMark,mChangeMarkPresent);
RN_WRITE_TEXT(mCValue);
RN_WRITE_OPT_TEXT(mSignVar,mSignVarPresent);
for (int i = 0; i < 8; i++) {
RN_WRITE_OPT_TEXT(mILevelTN[i],mILevelTN[i] >= 0);
}
}
*/
#endif
/** GSM04.60 11.2.8b From MS to network. */
struct RLCMsgPacketUplinkDummyControlBlock : public RLCUplinkMessage
{
Field<32> mTLLI;
RLCMsgPacketUplinkDummyControlBlock(const RLCRawBlock*src) { parse(src); }
void parseBody(const BitVector &src, size_t &rp);
void textBody(std::ostream&) const;
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketUplinkDummyControlBlock::parseBody(const BitVector &src, size_t &rp)
{
mTLLI = src.readField(rp,32);
}
void RLCMsgPacketUplinkDummyControlBlock::writeBody(MsgCommon&dst) const {
dst.writeField(mTLLI,32,"TLLI",tohex);
}
/*
void RLCMsgPacketUplinkDummyControlBlock::textBody(std::ostream&os) const
{
RN_WRITE_TEXT(mTLLI);
};
*/
#endif
// GSM04.60 12.12
struct RLCMsgPacketTimingAdvanceIE : public RLCMsgDownlinkIE
{
// Timing Advance defined in GSM05.10
Field_z<1> mHaveTimingAdvanceValue;
Field_z<6> mTimingAdvanceValue; // New timing advance if present.
// We dont use the following:
// Timeslot number defined in GSM05.10
Field_z<1> mHaveContinuousTiming; // Controls next two fields:
Field_z<4> mTimingAdvanceIndex; // Used for continuous timing advance.
Field_z<3> mTimingAdvanceTimeslotNumber; // Timeslot that gets the continuous timing advance.
void setTimingAdvance(int ta) { mHaveTimingAdvanceValue=1; mTimingAdvanceValue=ta; }
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketTimingAdvanceIE::writeBody(MsgCommon &dst) const {
if (dst.write01(mHaveTimingAdvanceValue)) {
dst.WRITE_ITEM(mTimingAdvanceValue);
}
if (dst.write01(mHaveContinuousTiming)) {
dst.WRITE_ITEM(mTimingAdvanceIndex);
dst.WRITE_ITEM(mTimingAdvanceTimeslotNumber);
}
}
#endif
// GSM04.60 12.12a - same as TimingAdvanceIE but allows separate
// values for uplink and downlink.
struct RLCMsgPacketGlobalTimingAdvanceIE : public RLCMsgDownlinkIE
{
// Timing Advance defined in GSM05.10
Bool_z mHaveTimingAdvanceValue;
Field_z<6> mTimingAdvanceValue; // New timing advance if present.
// Timeslot number defined in GSM05.10
// We dont use the following:
Bool_z mHaveUplinkContinuousTiming; // Controls next two fields:
Field_z<4> mUplinkTimingAdvanceIndex; // Used for continuous timing advance.
Field_z<3> mUplinkTimingAdvanceTimeslotNumber; // Timeslot that gets the continuous timing advance.
Bool_z mHaveDownlinkContinuousTiming; // Controls next two fields:
Field_z<4> mDownlinkTimingAdvanceIndex; // Used for continuous timing advance.
Field_z<3> mDownlinkTimingAdvanceTimeslotNumber; // Timeslot that gets the continuous timing advance.
void setTimingAdvance(int ta) { mHaveTimingAdvanceValue=1; mTimingAdvanceValue=ta; }
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketGlobalTimingAdvanceIE::writeBody(MsgCommon &dst) const {
if (dst.write01(mHaveTimingAdvanceValue)) {
dst.WRITE_ITEM(mTimingAdvanceValue);
}
if (dst.write01(mHaveUplinkContinuousTiming)) {
dst.WRITE_ITEM(mUplinkTimingAdvanceIndex);
dst.WRITE_ITEM(mUplinkTimingAdvanceTimeslotNumber);
}
if (dst.write01(mHaveDownlinkContinuousTiming)) {
dst.WRITE_ITEM(mDownlinkTimingAdvanceIndex);
dst.WRITE_ITEM(mDownlinkTimingAdvanceTimeslotNumber);
}
}
#endif
// GSM04.60 12.13
struct RLCMsgPowerControlParametersIE : public RLCMsgDownlinkIE
{
Field_z<4> mAlpha; // 4 bits.
Bool_z mHaveTN[8];
Field_z<5> mGammaTN[8]; // 5 bits each
void setAlpha(int alpha) { mAlpha = alpha; }
void setGamma(int timeslot, int gamma) { mGammaTN[timeslot] = gamma; mHaveTN[timeslot] = true; }
void setFrom(TBF *tbf);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPowerControlParametersIE::writeBody(MsgCommon &dst) const {
dst.WRITE_ITEM(mAlpha);
for (int i=0; i<8; i++) {
if (dst.write01(mHaveTN[i])) dst.WRITE_ITEM(mGammaTN[i]);
}
}
#endif
// This is not an official Informaion Element, but is commonly used in several messages.
// Used to identify to the MS targetted for a downlink message.
// The MS may be identified by TLLI, uplink TFI, or downlink TFI.
// In some messages, can also use TQI or Packet Request Reference,
// but we dont use those.
// This is not an official "Information Element", but it is used the
// same way in many downlink messages, since they all have to target an MS.
struct RLCMsgGlobalTFIorTLLIElt : public RLCMsgDownlinkIE
{
Bool_z mHaveGlobalTFI;
// Global TFI includes the following two fields:
RLCMsgGlobalTFIIE mGlobalTFI; // This is NOT a TFI assignment; it is used
// to identify the MS which receives this message.
Bool_z mHaveTLLI;
Field_z<32> mTLLI;
void setTLLI(int tlli) {
mHaveGlobalTFI=0; mHaveTLLI=1; mTLLI=tlli;
}
void setGlobalTFI(bool wIsDownlinkTFI,int wTFI) {
mHaveGlobalTFI = 1; mGlobalTFI.setTFI(wIsDownlinkTFI,wTFI);
}
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgGlobalTFIorTLLIElt::writeBody(MsgCommon &dst) const {
if (mHaveGlobalTFI) {
dst.write0();
mGlobalTFI.writeBody(dst);
} else if (mHaveTLLI) {
dst.write1();
dst.write0();
dst.writeField(mTLLI,32,"TLLI",tohex);
} else {
// Could be TQI or Packet Request Reference
assert(0);
}
}
#endif
// GSM04.60 12.21
class RLCMsgStartingFrameNumberIE : public RLCMsgDownlinkIE
{
public:
Field_z<1> mAbsoluteOrRelative; // 0 => absolute, 1 => relative
Field_z<16> mAbsoluteFrameNumber;
Field_z<13> mRelativeFrameNumber;
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgStartingFrameNumberIE::writeBody(MsgCommon &dst) const {
//dst.writeField(mAbsoluteOrRelative,1);
dst.WRITE_ITEM(mAbsoluteOrRelative);
if (mAbsoluteOrRelative) {
dst.WRITE_ITEM(mRelativeFrameNumber);
} else {
dst.WRITE_ITEM(mAbsoluteFrameNumber);
}
}
#endif
// GSM04.60 11.2.29
// Also used in Timeslot Reconfigure message 44.060 11.2.31
class RLCMsgPacketUplinkAssignmentDynamicAllocationElt : public RLCMsgDownlinkIE
{
public:
Field_z<1> mExtendedDynamicAllocation; // Needed if number up > number down
// P0, PR_MODE unused.
Field_z<1> mUSFGranularity; // Use 0 for USF specified per-block.
// In the TimeslotReconfigure message, this is never present, always one-bit 0:
Bool_z mUplinkTFIAssignmentPresent;
Field_z<5> mUplinkTFIAssignment;
//Bool_z mRLCDataBlocksGrantedPresent;
//Field_z<8> mRLCDataBlocksGranted;
Field_z<1> mTBFStartingTimePresent;
RLCMsgStartingFrameNumberIE mTBFStartingTime;
// There are two types of Timeslot allocation, with and without power control params.
Bool_z mHavePower; // Set if gamma supplied for any timeslot.
Field_z<4> mAlpha; // 4 bits.
Bool_z mHaveTN[8];
Field_z<3> mUSFTN[8]; // 3 bits each.
Field_z<5> mGammaTN[8]; // 5 bits each
void setUplinkTFI(int tfi) { mUplinkTFIAssignmentPresent=1; mUplinkTFIAssignment=tfi; }
void setUSF(int timeslot, int usf) { mUSFTN[timeslot] = usf; mHaveTN[timeslot] = true; }
void setGamma(int timeslot, int gamma) { mGammaTN[timeslot] = gamma; mHavePower = true; }
void setAlpha(int alpha) { mAlpha = alpha; mHavePower = true; }
void setFrom(TBF *tbf,MultislotSymmetry);
void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
void RLCMsgPacketUplinkAssignmentDynamicAllocationElt::writeBody(MsgCommon &dst) const {
dst.WRITE_ITEM(mExtendedDynamicAllocation);
dst.write0(); // No P0, PR_MODE.
dst.WRITE_ITEM(mUSFGranularity);
//dst.WRITE_OPT_FIELD01(mUplinkTFIAssignment,5,mUplinkTFIAssignmentPresent);
dst.WRITE_OPT_ITEM01(mUplinkTFIAssignment,mUplinkTFIAssignmentPresent);
// 44.060 removed the RLC Data Blocks Granted field, just inserts a 0 bit here.
// dst.WRITE_OPT_ITEM01(mRLCDataBlocksGranted,mRLCDataBlocksGrantedPresent);
dst.write0(); // RLC Data Blocks Granted removed from 44.060 11.2.29
//dst.writeField(mTBFStartingTimePresent,1);
dst.WRITE_ITEM(mTBFStartingTimePresent);
if (mTBFStartingTimePresent) {
mTBFStartingTime.writeBody(dst);
}
char name[14]; // Make the output pretty.
strcpy(name,"USF_TN?");
char *end = name+strlen(name)-1;
if (dst.write01(mHavePower)) {
dst.WRITE_ITEM(mAlpha);