mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
synced 2025-11-12 18:06:30 +00:00
Compare commits
1848 Commits
jolly/outd
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deed90dd00 | ||
|
|
4f67a9bf46 | ||
|
|
1989a19066 | ||
|
|
8c29236d35 | ||
|
|
ab178903d4 | ||
|
|
b657213773 | ||
|
|
94dc6a8b6c | ||
|
|
826576287e | ||
|
|
86f4c093d1 | ||
|
|
c6e911cf22 | ||
|
|
9c1db1738f | ||
|
|
d65bd9d7b2 | ||
|
|
c9880b97cf | ||
|
|
4c2387026a | ||
|
|
2761e574de | ||
|
|
dc2aaac29f | ||
|
|
38f80be73b | ||
|
|
1a1557a60a | ||
|
|
f62b0ec37d | ||
|
|
9d2fd018ff | ||
|
|
bd54205475 | ||
|
|
4c51eaf05b | ||
|
|
6e25119c18 | ||
|
|
c43570c351 | ||
|
|
c48d27b57b | ||
|
|
48517620b9 | ||
|
|
9b63cd04e4 | ||
|
|
58916318ef | ||
|
|
f53815f2fc | ||
|
|
632542348a | ||
|
|
c432e062ea | ||
|
|
6bab522e90 | ||
|
|
eb13c79cc0 | ||
|
|
faf0ccb241 | ||
|
|
4b6f0bfe69 | ||
|
|
20271c421c | ||
|
|
c6571b5581 | ||
|
|
50272a4776 | ||
|
|
393484a5d0 | ||
|
|
0e35aee194 | ||
|
|
c6dcfe32f3 | ||
|
|
4b7a71f93f | ||
|
|
292d04d19d | ||
|
|
d9367e34db | ||
|
|
4e453b41f3 | ||
|
|
fa48b4b720 | ||
|
|
f593fc5cbb | ||
|
|
039ee8200a | ||
|
|
34f61af3d0 | ||
|
|
2ab840a1fa | ||
|
|
54742f287c | ||
|
|
14339f6fac | ||
|
|
25ebf3c8f9 | ||
|
|
846fd248dc | ||
|
|
710e0e9ad8 | ||
|
|
434799720c | ||
|
|
ffc533b1af | ||
|
|
ab3aca65c5 | ||
|
|
16e1678bfc | ||
|
|
58046e45d1 | ||
|
|
2c0931cedc | ||
|
|
127e7392f6 | ||
|
|
feee2b9b83 | ||
|
|
42445ea944 | ||
|
|
ed066b5328 | ||
|
|
86580e1966 | ||
|
|
b5ae0811d1 | ||
|
|
50a1ede693 | ||
|
|
ce3bd2522a | ||
|
|
222f674116 | ||
|
|
54e6450293 | ||
|
|
0b998b15da | ||
|
|
c1f38c7f0b | ||
|
|
ade9c2f553 | ||
|
|
9a6f0b191a | ||
|
|
7bd92a3e1d | ||
|
|
4bab867d9f | ||
|
|
107e94c9f8 | ||
|
|
56f223d8d1 | ||
|
|
5d376845bb | ||
|
|
82519264ca | ||
|
|
d7f0558b5c | ||
|
|
755a8d61bb | ||
|
|
fecab50066 | ||
|
|
c7cc4162e1 | ||
|
|
95f8fa1f7c | ||
|
|
582a15e413 | ||
|
|
5bc9612c02 | ||
|
|
3a42d17b14 | ||
|
|
fd1fbdb8db | ||
|
|
99360a304f | ||
|
|
15c58ace75 | ||
|
|
68bfdff3f0 | ||
|
|
4f2c8cd96a | ||
|
|
df58ddf6d8 | ||
|
|
c1f31c46ac | ||
|
|
91cc780b40 | ||
|
|
1e97951582 | ||
|
|
702ebee751 | ||
|
|
30617115ba | ||
|
|
5447f3acf6 | ||
|
|
81c549d5be | ||
|
|
3cba94d70e | ||
|
|
58fdc54a7f | ||
|
|
0057bd76fd | ||
|
|
2ad15d51fa | ||
|
|
3973eb5fe8 | ||
|
|
11f01105a0 | ||
|
|
4fe090146f | ||
|
|
423bf8c408 | ||
|
|
cf6c71263f | ||
|
|
8afc6bad80 | ||
|
|
5b9d0bb8e5 | ||
|
|
a89008b724 | ||
|
|
a70bf72ce5 | ||
|
|
9688dc9aca | ||
|
|
c85e093969 | ||
|
|
36177c6b58 | ||
|
|
95e2266832 | ||
|
|
4e1c9adb67 | ||
|
|
7c9a4a41bc | ||
|
|
ed2afa3bed | ||
|
|
50aa492b85 | ||
|
|
fe8de457ac | ||
|
|
4df2658884 | ||
|
|
10475f5832 | ||
|
|
dfbf3d2c09 | ||
|
|
47a3b780db | ||
|
|
1f8e229221 | ||
|
|
00f52cc3d6 | ||
|
|
b18d2a5fd9 | ||
|
|
151bc5b0d3 | ||
|
|
9345eb34d3 | ||
|
|
cf6b3bc08f | ||
|
|
66e8a49734 | ||
|
|
94a367f224 | ||
|
|
13a12e2e3b | ||
|
|
9313da517d | ||
|
|
a78f0d5bfe | ||
|
|
8e69fc0c3a | ||
|
|
069a637be8 | ||
|
|
41ff273226 | ||
|
|
0c10b3cdc1 | ||
|
|
abba102d7b | ||
|
|
830ca26034 | ||
|
|
55aca83098 | ||
|
|
44768f2127 | ||
|
|
952cb3d5d7 | ||
|
|
a58ec61514 | ||
|
|
1aef113bb7 | ||
|
|
57dcde4242 | ||
|
|
b71aab5646 | ||
|
|
4668dc6f07 | ||
|
|
79784d0249 | ||
|
|
fdbcea3532 | ||
|
|
05be90367a | ||
|
|
3877848e22 | ||
|
|
e0fb465678 | ||
|
|
809dc8b046 | ||
|
|
f7e1df0da2 | ||
|
|
ced5c1f5c8 | ||
|
|
7b7fb225ce | ||
|
|
a06ac18d22 | ||
|
|
41a22a7ab8 | ||
|
|
ab7159f6ec | ||
|
|
c0805e6389 | ||
|
|
202a47886c | ||
|
|
c0a250d17d | ||
|
|
1e77ca88af | ||
|
|
54211b1e1b | ||
|
|
f7ec52560f | ||
|
|
91e3567a15 | ||
|
|
1a5439b739 | ||
|
|
fc464935a4 | ||
|
|
201da4e5b2 | ||
|
|
7bb8cd683c | ||
|
|
7963edba09 | ||
|
|
2238228e3c | ||
|
|
8f1701fe24 | ||
|
|
0298c0b6a0 | ||
|
|
a100a6bc56 | ||
|
|
3a27102e59 | ||
|
|
db5e339da4 | ||
|
|
2e6b60df45 | ||
|
|
d1049dc8cc | ||
|
|
e91c4c72b1 | ||
|
|
906aafc9e2 | ||
|
|
8a35e640a3 | ||
|
|
4a5209d8bc | ||
|
|
289f90048b | ||
|
|
0ece97d718 | ||
|
|
a45aafd39c | ||
|
|
2182e627cd | ||
|
|
793583ea21 | ||
|
|
f473ec9d7a | ||
|
|
519d071131 | ||
|
|
47f15fb6fd | ||
|
|
e891222920 | ||
|
|
113fb419ec | ||
|
|
54b159aab9 | ||
|
|
ad79b857cd | ||
|
|
e8dcf64881 | ||
|
|
97296b299c | ||
|
|
05f9f59a67 | ||
|
|
a281495008 | ||
|
|
03de898d19 | ||
|
|
924aaad4bc | ||
|
|
ac3fd12026 | ||
|
|
695ce77167 | ||
|
|
54faf023be | ||
|
|
f842e06c88 | ||
|
|
eb70a4098f | ||
|
|
55022ea1b1 | ||
|
|
f5a251bcee | ||
|
|
1e00947c29 | ||
|
|
bed48cc14f | ||
|
|
c2d3625bf6 | ||
|
|
1188167a92 | ||
|
|
480c8acc8b | ||
|
|
d3123ea0f5 | ||
|
|
a5b9fb5304 | ||
|
|
8b4b19a012 | ||
|
|
da971ee502 | ||
|
|
86fad1ec4e | ||
|
|
d6b913fc39 | ||
|
|
b77a2c992e | ||
|
|
3db4789be8 | ||
|
|
1e6c350a9f | ||
|
|
b644fd1f09 | ||
|
|
5012e07685 | ||
|
|
398f60e11c | ||
|
|
b645365546 | ||
|
|
95e4a2b3ae | ||
|
|
ea2147af90 | ||
|
|
8938431fdc | ||
|
|
7d0f9a0ec3 | ||
|
|
30d9a5989e | ||
|
|
022f9e56e5 | ||
|
|
7fd9a29eba | ||
|
|
58cd1d2f8a | ||
|
|
4e8f2c310f | ||
|
|
74aa3523f3 | ||
|
|
259a694ba7 | ||
|
|
9c4f9ffec1 | ||
|
|
7d3ee9ed8d | ||
|
|
228628860f | ||
|
|
daa6c1c645 | ||
|
|
4c5a7d315c | ||
|
|
49efd9b606 | ||
|
|
e309d80dd4 | ||
|
|
2bbdf2e3d7 | ||
|
|
cb98894eb1 | ||
|
|
305763dc6f | ||
|
|
9897b26e3e | ||
|
|
f1159c559b | ||
|
|
b47b137c66 | ||
|
|
87eec1fd74 | ||
|
|
133fe4a852 | ||
|
|
569f0d27c7 | ||
|
|
4808d1ceb8 | ||
|
|
270c9ea5d9 | ||
|
|
343ec9b9fd | ||
|
|
8072e354cc | ||
|
|
d87722d03c | ||
|
|
46fd7a0316 | ||
|
|
a2848546d2 | ||
|
|
2c1fed20ab | ||
|
|
e9bddabc01 | ||
|
|
b6450840dc | ||
|
|
b1bbcae71c | ||
|
|
758ace867b | ||
|
|
16705a4db1 | ||
|
|
4d1663594b | ||
|
|
983bb7eb31 | ||
|
|
528820dbe1 | ||
|
|
87c6dd3c26 | ||
|
|
38de84cdc4 | ||
|
|
9f6d1a85e1 | ||
|
|
b385969039 | ||
|
|
442198cd41 | ||
|
|
e9f77d377b | ||
|
|
7bde60f260 | ||
|
|
b3f239785c | ||
|
|
834cbab97d | ||
|
|
9767e44fd4 | ||
|
|
1136fdb563 | ||
|
|
35668a2e06 | ||
|
|
a611b4f6a0 | ||
|
|
8353d97904 | ||
|
|
8347359cb0 | ||
|
|
a6c5db954f | ||
|
|
1ab35d100b | ||
|
|
5bece2a0ed | ||
|
|
3839605ec9 | ||
|
|
974cc7b324 | ||
|
|
d038361e95 | ||
|
|
0d0ac1b949 | ||
|
|
194b6f06a0 | ||
|
|
08c5037d60 | ||
|
|
781f04a8f8 | ||
|
|
24c643e143 | ||
|
|
19622aee5b | ||
|
|
c1a726c573 | ||
|
|
5d5b50aaf3 | ||
|
|
9459ebde32 | ||
|
|
290d9030e9 | ||
|
|
0a369e560c | ||
|
|
84abd2f65e | ||
|
|
01aef5ed8b | ||
|
|
5d2d2ec466 | ||
|
|
a1ac2f017f | ||
|
|
5fc95771bb | ||
|
|
bd9973a64f | ||
|
|
e3ef51ec06 | ||
|
|
cd6c466922 | ||
|
|
6ce1299567 | ||
|
|
9b5c960f55 | ||
|
|
8e2bd1e79d | ||
|
|
40a9e603b9 | ||
|
|
38aaa10ed4 | ||
|
|
2597cf1494 | ||
|
|
542e388e4f | ||
|
|
db56a3563e | ||
|
|
962d717f41 | ||
|
|
1d68cdff92 | ||
|
|
1156776572 | ||
|
|
a76cdaceb2 | ||
|
|
59d64fb234 | ||
|
|
7a1fdfde8a | ||
|
|
c0ddaa94e6 | ||
|
|
e4bcd2103c | ||
|
|
c82c1f5efc | ||
|
|
48587d5475 | ||
|
|
1d52c1e02e | ||
|
|
0f41b710b7 | ||
|
|
11b0f00c00 | ||
|
|
ce0dae9fda | ||
|
|
fad2128d17 | ||
|
|
b69928cfaf | ||
|
|
59fc0bda6e | ||
|
|
0052051c07 | ||
|
|
b75c27febd | ||
|
|
183b01eb4c | ||
|
|
fad557ec0f | ||
|
|
f2dad593ae | ||
|
|
5f10fbb166 | ||
|
|
a2c574ede1 | ||
|
|
74b750df81 | ||
|
|
811c90c5fd | ||
|
|
9d5a115ee2 | ||
|
|
8f628ab77f | ||
|
|
eb1e0fa859 | ||
|
|
5935707489 | ||
|
|
9e7361a8ba | ||
|
|
bcb226d607 | ||
|
|
85faa762c3 | ||
|
|
f2c6c83816 | ||
|
|
732373d7b4 | ||
|
|
26743ac4f9 | ||
|
|
f861d312fe | ||
|
|
72e395656d | ||
|
|
259532fcf9 | ||
|
|
d21e961a8b | ||
|
|
cbf05f5146 | ||
|
|
43f0bce253 | ||
|
|
81b610ea2c | ||
|
|
07b6487c5a | ||
|
|
b90d34b3d1 | ||
|
|
a0a0b7fb0e | ||
|
|
088dcbc016 | ||
|
|
4c593c8c8a | ||
|
|
a2d972a38a | ||
|
|
3f2022e2cb | ||
|
|
93ad3fd9b9 | ||
|
|
0614e9333f | ||
|
|
fac8332649 | ||
|
|
c9915660ff | ||
|
|
6fd4733041 | ||
|
|
0d7bc876c0 | ||
|
|
0b0391ff66 | ||
|
|
2ae8337669 | ||
|
|
c10cb81593 | ||
|
|
d0156fd6a8 | ||
|
|
c33a024d4a | ||
|
|
5bb87b83d1 | ||
|
|
c68e97012c | ||
|
|
7b9e56a500 | ||
|
|
09afd6f230 | ||
|
|
97e88fd35f | ||
|
|
a107f8f496 | ||
|
|
2338e5318e | ||
|
|
5dc6e465cb | ||
|
|
ce60011e77 | ||
|
|
de0e558baf | ||
|
|
322456ed47 | ||
|
|
17402a5902 | ||
|
|
f094b46d1c | ||
|
|
db9ea55c6e | ||
|
|
e6bca376aa | ||
|
|
98eb03c391 | ||
|
|
9ee90d2323 | ||
|
|
acd67cbdf1 | ||
|
|
20331ae5f6 | ||
|
|
3301cc900e | ||
|
|
3f27fb56e4 | ||
|
|
72ec18ce5a | ||
|
|
a4e8c10a38 | ||
|
|
2e90a1f015 | ||
|
|
b30f28f38d | ||
|
|
c374ab00ac | ||
|
|
c8280a538a | ||
|
|
f5e275aec0 | ||
|
|
70a211747b | ||
|
|
99eb353337 | ||
|
|
f3ac06bbaf | ||
|
|
b2653fe619 | ||
|
|
799cf8221a | ||
|
|
d2e50e7f21 | ||
|
|
a0cbde700a | ||
|
|
ce160147f4 | ||
|
|
d71c566ee6 | ||
|
|
0b25f693b3 | ||
|
|
f497412f20 | ||
|
|
2b6acd8873 | ||
|
|
e5e2f747c3 | ||
|
|
24fa27453f | ||
|
|
980ef1e38b | ||
|
|
007056e4ad | ||
|
|
754b093d16 | ||
|
|
de63548f04 | ||
|
|
10ec506cc9 | ||
|
|
e50ce6e45c | ||
|
|
20848c3ae5 | ||
|
|
7faa5da209 | ||
|
|
efad80bfbf | ||
|
|
81b40cbaf3 | ||
|
|
866becefff | ||
|
|
2f16924959 | ||
|
|
570f9135cd | ||
|
|
de7bb75ccf | ||
|
|
1de6873810 | ||
|
|
29aeb901e4 | ||
|
|
773cb74ec8 | ||
|
|
40d4e35fb3 | ||
|
|
bbafcc1602 | ||
|
|
1145fd263c | ||
|
|
2679ec0a9f | ||
|
|
f22163b1df | ||
|
|
6fd8ffb6fe | ||
|
|
0daf913e0c | ||
|
|
b47e53b5fa | ||
|
|
5fc6e010a5 | ||
|
|
55f06c3d77 | ||
|
|
e60d9c7e9d | ||
|
|
1553049226 | ||
|
|
4b57b6da54 | ||
|
|
d8e5e8bb3b | ||
|
|
e87066d01e | ||
|
|
584daba8e9 | ||
|
|
39a65056da | ||
|
|
8a87f913bd | ||
|
|
74bc150ab2 | ||
|
|
fa5f91c05f | ||
|
|
fb65682d34 | ||
|
|
2d075be3b8 | ||
|
|
9b2f7c420e | ||
|
|
5574a58cd6 | ||
|
|
fee767f21c | ||
|
|
9a530a2430 | ||
|
|
9cbfe11ee2 | ||
|
|
c74e217799 | ||
|
|
ac2b866426 | ||
|
|
3ff1a3c6d5 | ||
|
|
8832c2e5d4 | ||
|
|
d83c8ffb6b | ||
|
|
1ec29c7324 | ||
|
|
28b4d27209 | ||
|
|
ea9de4ae25 | ||
|
|
5e300ce565 | ||
|
|
47de23266d | ||
|
|
d636f74923 | ||
|
|
f960d5bcf4 | ||
|
|
3568fcfa7d | ||
|
|
3898cd6843 | ||
|
|
9f62b92258 | ||
|
|
f6b83a24a3 | ||
|
|
4590b91728 | ||
|
|
e525bf94ba | ||
|
|
e4a243c02b | ||
|
|
463746917f | ||
|
|
8ea3cbe6b2 | ||
|
|
034e32f0eb | ||
|
|
29248d6941 | ||
|
|
cc76d412fd | ||
|
|
bc4f3930a9 | ||
|
|
bcc6408cd1 | ||
|
|
e36fb5b802 | ||
|
|
900c2e277a | ||
|
|
771da85a11 | ||
|
|
e26467c4ed | ||
|
|
60bf845f25 | ||
|
|
c515551625 | ||
|
|
5b71697618 | ||
|
|
7cce825fa4 | ||
|
|
98e4c53cad | ||
|
|
c0b4f4a633 | ||
|
|
c0190c8a5a | ||
|
|
72c102acf8 | ||
|
|
cdbc5dbd1c | ||
|
|
ea39fade07 | ||
|
|
87bfbe4fe2 | ||
|
|
5cb002f0ef | ||
|
|
99c437b7c7 | ||
|
|
54681c3ffe | ||
|
|
bd0b0b3242 | ||
|
|
771de1f439 | ||
|
|
65a0d1d19b | ||
|
|
5530f12f71 | ||
|
|
d7c3265223 | ||
|
|
b507e428e8 | ||
|
|
fbfab297ee | ||
|
|
db12f254ce | ||
|
|
506eb23dc5 | ||
|
|
585cfb28b8 | ||
|
|
3f064f516d | ||
|
|
32499b614b | ||
|
|
30f6617c79 | ||
|
|
d0fc9e80fe | ||
|
|
3a61b920f2 | ||
|
|
f6d282822e | ||
|
|
c925ccccfc | ||
|
|
657a4c0ccd | ||
|
|
fc75cc0ecf | ||
|
|
1e6eb30f51 | ||
|
|
1d8497ba6a | ||
|
|
ffebd24456 | ||
|
|
17954da56c | ||
|
|
78f58618f3 | ||
|
|
bd0dac3783 | ||
|
|
0bf622e057 | ||
|
|
fd734de4d1 | ||
|
|
87b6e7dbed | ||
|
|
ce27d1e126 | ||
|
|
ef444142c8 | ||
|
|
05bca3524a | ||
|
|
b3b0c49d1c | ||
|
|
cef2f843b4 | ||
|
|
afbf189ef2 | ||
|
|
d752d7cebe | ||
|
|
8dc09e73d0 | ||
|
|
f681f07cd0 | ||
|
|
2ccb6aef89 | ||
|
|
58543709e4 | ||
|
|
1ec211f57f | ||
|
|
1879442443 | ||
|
|
f4c77e686a | ||
|
|
ad586a9fe4 | ||
|
|
bddf1ad7f6 | ||
|
|
9d1cdb1f69 | ||
|
|
488aa29083 | ||
|
|
e13cdc503e | ||
|
|
812d466bbd | ||
|
|
04797b1e35 | ||
|
|
2beb1b85e0 | ||
|
|
2b5c629055 | ||
|
|
63700ead34 | ||
|
|
474dc77894 | ||
|
|
38cfa734f4 | ||
|
|
5211d9deca | ||
|
|
cfb6321b88 | ||
|
|
5360ef5447 | ||
|
|
e46d8dcaab | ||
|
|
28f160e76c | ||
|
|
45fdc44d68 | ||
|
|
37ae22ab13 | ||
|
|
a8892a69b3 | ||
|
|
ef1fe58e19 | ||
|
|
df0fd8ba27 | ||
|
|
9e5ef54594 | ||
|
|
c7849c2dfe | ||
|
|
aef6bf43e5 | ||
|
|
2585451408 | ||
|
|
19b15a5b93 | ||
|
|
3bcc7ab418 | ||
|
|
cd2ac56bd4 | ||
|
|
2c076bcb4d | ||
|
|
6170ac041f | ||
|
|
aad87b66e8 | ||
|
|
ab8b01effd | ||
|
|
7b7f2048b8 | ||
|
|
8bb7904458 | ||
|
|
f3038e7b2a | ||
|
|
f17dfc062a | ||
|
|
210ccf4a1d | ||
|
|
5c3a9880ca | ||
|
|
e4e70d052e | ||
|
|
fba931bab6 | ||
|
|
243a204021 | ||
|
|
2d24eba903 | ||
|
|
3a499f3cb2 | ||
|
|
0d482c5c97 | ||
|
|
1f0ca54bba | ||
|
|
7fe3895b46 | ||
|
|
79c6c4db3b | ||
|
|
76791d20f5 | ||
|
|
6d73205280 | ||
|
|
b7439f28a1 | ||
|
|
3f640773f3 | ||
|
|
5c3783b7e8 | ||
|
|
a558ad4269 | ||
|
|
eb64d43922 | ||
|
|
41d6b35670 | ||
|
|
0e6e45a65f | ||
|
|
34513feff9 | ||
|
|
902e3e58db | ||
|
|
12a0987b36 | ||
|
|
0e6ac799f7 | ||
|
|
8ab35b14e9 | ||
|
|
32518cce5a | ||
|
|
f57dccbbfb | ||
|
|
ba66ea4406 | ||
|
|
2ba608415b | ||
|
|
4a1796fe76 | ||
|
|
c1e44908fe | ||
|
|
3fa235fe01 | ||
|
|
d4a39291e0 | ||
|
|
fb59a93425 | ||
|
|
e742cc0997 | ||
|
|
3eb47363ad | ||
|
|
0367ddd62b | ||
|
|
6e96dd4665 | ||
|
|
898dddb1d1 | ||
|
|
48b1e7a86f | ||
|
|
3447c4a8a4 | ||
|
|
1473b377c2 | ||
|
|
8a8e0fb267 | ||
|
|
fb3fd09353 | ||
|
|
a4de02db5d | ||
|
|
02fbfc15c7 | ||
|
|
a0353547b1 | ||
|
|
136ebccc5e | ||
|
|
51754b6f35 | ||
|
|
d5ffeb5e63 | ||
|
|
f4d3973688 | ||
|
|
0fb91b736c | ||
|
|
a3ff316c5f | ||
|
|
ed6e4acdf6 | ||
|
|
0620656288 | ||
|
|
e43ef21d35 | ||
|
|
bea2edbc46 | ||
|
|
1beed38b54 | ||
|
|
fa3085b45e | ||
|
|
86e35e4887 | ||
|
|
8119ecd2db | ||
|
|
d3a0d91a38 | ||
|
|
01f9bc7bf1 | ||
|
|
9feaddc390 | ||
|
|
360e021d0a | ||
|
|
807dde070f | ||
|
|
57d3515abd | ||
|
|
2a47c73217 | ||
|
|
b26854c276 | ||
|
|
bd5647ee92 | ||
|
|
23c0e018e4 | ||
|
|
0160a29b6c | ||
|
|
fc8afc2f33 | ||
|
|
81b58ccd94 | ||
|
|
7426c5f957 | ||
|
|
0c55bf19a5 | ||
|
|
1f2e69fd35 | ||
|
|
f0af1b051a | ||
|
|
99278b1050 | ||
|
|
5b521891ba | ||
|
|
64bc9400d4 | ||
|
|
61469d1d86 | ||
|
|
47aab58ec3 | ||
|
|
dfb08d31fe | ||
|
|
96481c8d3f | ||
|
|
2eeddee132 | ||
|
|
57d91ea785 | ||
|
|
d2829b89c1 | ||
|
|
f605f3d891 | ||
|
|
452b533114 | ||
|
|
5c8f1ccff0 | ||
|
|
6bc5c2aac6 | ||
|
|
5828b60af5 | ||
|
|
58721eb350 | ||
|
|
6fb29639df | ||
|
|
032f94b099 | ||
|
|
32b58e6418 | ||
|
|
a27873f1e2 | ||
|
|
3a89f21540 | ||
|
|
d930b4d5f4 | ||
|
|
8f934f55d7 | ||
|
|
b1776b65b3 | ||
|
|
4e2a1e3e98 | ||
|
|
c182b98998 | ||
|
|
b682cd6a51 | ||
|
|
4095f31fea | ||
|
|
4ff37feb4c | ||
|
|
b9ba5ad239 | ||
|
|
4214c4ae53 | ||
|
|
099f4f2169 | ||
|
|
173d7fdbb9 | ||
|
|
076122f592 | ||
|
|
ac5e3e222a | ||
|
|
516697e29a | ||
|
|
3e9c071aa6 | ||
|
|
e63c72acfb | ||
|
|
54af2dba78 | ||
|
|
448750e4e2 | ||
|
|
218ee98de0 | ||
|
|
09269017d3 | ||
|
|
3df1532e97 | ||
|
|
e176a4d047 | ||
|
|
082443d2c7 | ||
|
|
78ab624c2a | ||
|
|
f0f7df1b87 | ||
|
|
143b2da4f8 | ||
|
|
7a9c1660cc | ||
|
|
0b0748a4be | ||
|
|
45143d270c | ||
|
|
f1a334be63 | ||
|
|
513c9bca17 | ||
|
|
33c52b6271 | ||
|
|
7e8e3978fe | ||
|
|
8ccf704608 | ||
|
|
42f2d61ac9 | ||
|
|
e6e4898027 | ||
|
|
9f06cbff6d | ||
|
|
a661bcd086 | ||
|
|
39f845848c | ||
|
|
782da2cf95 | ||
|
|
89b85e078e | ||
|
|
f75381498c | ||
|
|
8b4bd46b95 | ||
|
|
74906224ca | ||
|
|
5a5919435e | ||
|
|
bdc55fad62 | ||
|
|
8343b4adbb | ||
|
|
d2a219e644 | ||
|
|
731e2bb328 | ||
|
|
77988d469d | ||
|
|
847ed9f8cd | ||
|
|
4da385998a | ||
|
|
c5407c775a | ||
|
|
6dc90b8c86 | ||
|
|
2afec6dba5 | ||
|
|
0cc7212cfd | ||
|
|
adca67bcbb | ||
|
|
f633b8d8b2 | ||
|
|
1187a7719c | ||
|
|
910a387b0e | ||
|
|
4382e4e8fe | ||
|
|
735e435e8e | ||
|
|
5b22fb7953 | ||
|
|
c907b88ecd | ||
|
|
4c112dc5a6 | ||
|
|
137fd59bf4 | ||
|
|
c9ce6f916e | ||
|
|
5441e1f2f0 | ||
|
|
5d7f757e49 | ||
|
|
0fdaa9d383 | ||
|
|
7e4921d8e2 | ||
|
|
69d585e148 | ||
|
|
a76a7d0c6c | ||
|
|
d000d80968 | ||
|
|
92e9c17aec | ||
|
|
92b7a50605 | ||
|
|
e9fe0e3d06 | ||
|
|
a296118e6d | ||
|
|
164b59d757 | ||
|
|
d2d51ed109 | ||
|
|
01bd0cc42f | ||
|
|
9f46071409 | ||
|
|
c59ef12e51 | ||
|
|
2ecf0fdfc2 | ||
|
|
46fbfceac6 | ||
|
|
fdd79e9828 | ||
|
|
cac6b66638 | ||
|
|
088c7df571 | ||
|
|
0e5998087e | ||
|
|
0524e38d9e | ||
|
|
d81b3bf360 | ||
|
|
4cb6e04914 | ||
|
|
2399b1dbfc | ||
|
|
186206cff2 | ||
|
|
6298fbb7b2 | ||
|
|
93d947f5e8 | ||
|
|
896574e92b | ||
|
|
529ce88545 | ||
|
|
2141962baf | ||
|
|
71affcedba | ||
|
|
3e659ea9b7 | ||
|
|
00fd0df046 | ||
|
|
9d7357e4fe | ||
|
|
d0532b53eb | ||
|
|
ea98b7d784 | ||
|
|
7d32f55e4e | ||
|
|
2617bf2016 | ||
|
|
58818585bc | ||
|
|
8dce1de6d2 | ||
|
|
5081806f4d | ||
|
|
b3a17d6074 | ||
|
|
a4f570fe7a | ||
|
|
7df82d412e | ||
|
|
869c0c2e55 | ||
|
|
0bc982e714 | ||
|
|
467f633b16 | ||
|
|
b2de1f7888 | ||
|
|
effdec6e13 | ||
|
|
20c7c46bce | ||
|
|
406a1f0acf | ||
|
|
b2f0b62cd4 | ||
|
|
327e121a0f | ||
|
|
1714aeaa67 | ||
|
|
59e4a4fee1 | ||
|
|
ee5be3a009 | ||
|
|
c21f007277 | ||
|
|
ea9968f685 | ||
|
|
ef784e4e9e | ||
|
|
912131803b | ||
|
|
1a11d1db09 | ||
|
|
25a3ca4e59 | ||
|
|
ead08aae35 | ||
|
|
39eb95f130 | ||
|
|
cea806e5b9 | ||
|
|
b4d368b576 | ||
|
|
da0a194b57 | ||
|
|
59f50c2718 | ||
|
|
701afa4b3a | ||
|
|
a5eb67d91c | ||
|
|
842d781b5e | ||
|
|
b709144f1b | ||
|
|
8d55563523 | ||
|
|
1bcfa9aacf | ||
|
|
bfc54b551b | ||
|
|
49beba49eb | ||
|
|
d34ec1b969 | ||
|
|
47c682937c | ||
|
|
e64917a932 | ||
|
|
9dab1baef8 | ||
|
|
f826e8ab2b | ||
|
|
3e51d3e5bd | ||
|
|
ac0490ad2a | ||
|
|
4c9ec22546 | ||
|
|
a4f4822784 | ||
|
|
5b0df1f1c5 | ||
|
|
5759a19020 | ||
|
|
1962136a33 | ||
|
|
5a6bcfb797 | ||
|
|
8bfa659087 | ||
|
|
84bf0faed9 | ||
|
|
b216c6b165 | ||
|
|
10e37a5089 | ||
|
|
d78adfb577 | ||
|
|
1f18909335 | ||
|
|
b1be6112bb | ||
|
|
f60cf62f4f | ||
|
|
a10c39866b | ||
|
|
341dccd7e2 | ||
|
|
fd13f6c199 | ||
|
|
c4178e55ea | ||
|
|
0cd8e4eade | ||
|
|
bc219d5450 | ||
|
|
cd34dd3b2b | ||
|
|
f7740aa44b | ||
|
|
05d7b5dd59 | ||
|
|
18a17aa487 | ||
|
|
2813f931dd | ||
|
|
717cdf5405 | ||
|
|
9530a404ce | ||
|
|
8c8027c307 | ||
|
|
aae1bfbbe0 | ||
|
|
5579595464 | ||
|
|
241f5bcb00 | ||
|
|
3741f14689 | ||
|
|
865436dee0 | ||
|
|
a468cfaf2e | ||
|
|
062dfa1d0c | ||
|
|
49b78229ca | ||
|
|
c136be04f7 | ||
|
|
0fb294a8dd | ||
|
|
9dabfa2c2b | ||
|
|
356ac618f1 | ||
|
|
e8284a7f92 | ||
|
|
a49475b5a8 | ||
|
|
ccde5c9557 | ||
|
|
3de6d0602f | ||
|
|
333d7e6345 | ||
|
|
e6d26ec09c | ||
|
|
5382e0fc1f | ||
|
|
dd1700a397 | ||
|
|
7783964bb9 | ||
|
|
0a8fae8d14 | ||
|
|
db84235a0b | ||
|
|
20827374e9 | ||
|
|
b86a30dc22 | ||
|
|
5dd8d1bbd8 | ||
|
|
727295f206 | ||
|
|
1275a3f91a | ||
|
|
a01e2ee177 | ||
|
|
d0a887b28b | ||
|
|
4ae5406959 | ||
|
|
b609190369 | ||
|
|
49b83ec3a3 | ||
|
|
78ce59137f | ||
|
|
de9da39b33 | ||
|
|
d34646a865 | ||
|
|
53f0b4deb6 | ||
|
|
d935d88a8c | ||
|
|
e9a138e111 | ||
|
|
1d8744ce96 | ||
|
|
8adfcd06a1 | ||
|
|
127a1e0750 | ||
|
|
b3df58660f | ||
|
|
b78a4a6dfe | ||
|
|
06bdb3550c | ||
|
|
67b89cae08 | ||
|
|
e66de5b5ae | ||
|
|
ee78bf0882 | ||
|
|
da7250ad2c | ||
|
|
963cdaffd5 | ||
|
|
1f2bb6e93e | ||
|
|
5395073fff | ||
|
|
cc4214a429 | ||
|
|
0e63644d14 | ||
|
|
34bfbdaf9e | ||
|
|
168911b438 | ||
|
|
68fc12775f | ||
|
|
5d93f0f4ec | ||
|
|
bb47d957a8 | ||
|
|
f86307e1e4 | ||
|
|
ed3413e397 | ||
|
|
c0c3afd079 | ||
|
|
ae4838101a | ||
|
|
fb41afaaf6 | ||
|
|
9434e52af9 | ||
|
|
f276138202 | ||
|
|
d453eaa788 | ||
|
|
f868bdbe76 | ||
|
|
d71e8b32e3 | ||
|
|
4ea452689d | ||
|
|
e4727a3591 | ||
|
|
d38b92e972 | ||
|
|
0a4a6c1200 | ||
|
|
6348aea6a2 | ||
|
|
da66f71ffe | ||
|
|
da933e0ff8 | ||
|
|
fd9e16ce97 | ||
|
|
465f5bbb6f | ||
|
|
628dcfbc97 | ||
|
|
0ee31cfa38 | ||
|
|
8e70bb5bb4 | ||
|
|
22a901905c | ||
|
|
3463bd4adc | ||
|
|
e26ee01d56 | ||
|
|
0241526836 | ||
|
|
9f5f008aed | ||
|
|
8d2d9e8985 | ||
|
|
9bbe1600cc | ||
|
|
16ddc90eab | ||
|
|
7c7a86c080 | ||
|
|
a35c911a91 | ||
|
|
3c2eaebd21 | ||
|
|
fd71384104 | ||
|
|
b119198992 | ||
|
|
01826c13b1 | ||
|
|
959d1dee67 | ||
|
|
eebcb1e3e8 | ||
|
|
02352b487a | ||
|
|
50b097003b | ||
|
|
e6cadb4e3c | ||
|
|
1ec4d80176 | ||
|
|
9876f4bb21 | ||
|
|
7fd177b91c | ||
|
|
2d91260ea4 | ||
|
|
6bae2d11f1 | ||
|
|
0b4da058ad | ||
|
|
79cb245157 | ||
|
|
cbf9a721d6 | ||
|
|
505a86d396 | ||
|
|
36bdc5f7a4 | ||
|
|
d572054ca7 | ||
|
|
878bd1f296 | ||
|
|
1d7644b23a | ||
|
|
2ec6b8e758 | ||
|
|
df69809b82 | ||
|
|
5d94b5455f | ||
|
|
1a679127af | ||
|
|
cf2152b24c | ||
|
|
e8ccafc63d | ||
|
|
914955209e | ||
|
|
899d36d813 | ||
|
|
d32aa03520 | ||
|
|
2c9f980163 | ||
|
|
99ab0a8fa0 | ||
|
|
550a54184b | ||
|
|
23617c001d | ||
|
|
189742b66c | ||
|
|
e04fd0cf0f | ||
|
|
6922bcd929 | ||
|
|
7fdbf89ef3 | ||
|
|
6f0dc96929 | ||
|
|
1a5066112f | ||
|
|
d302e4fb28 | ||
|
|
68e2c6375e | ||
|
|
cb846ecbbc | ||
|
|
b82bd92e57 | ||
|
|
2fcfc29020 | ||
|
|
ce936f3cd4 | ||
|
|
c1c9d6a9d8 | ||
|
|
c8fd4b7c42 | ||
|
|
f929e62525 | ||
|
|
4acb6b7251 | ||
|
|
95e379241a | ||
|
|
543756adbe | ||
|
|
2cb1547993 | ||
|
|
9736d00b12 | ||
|
|
660709dc7c | ||
|
|
58b6646750 | ||
|
|
ed3ae4a392 | ||
|
|
f0bb25450c | ||
|
|
de810f2005 | ||
|
|
cad867ec8d | ||
|
|
280448ba7b | ||
|
|
1aa7527302 | ||
|
|
ca025c02ef | ||
|
|
97e48a3252 | ||
|
|
63d33ad2d7 | ||
|
|
7952282b78 | ||
|
|
a859a21800 | ||
|
|
7a05b039c8 | ||
|
|
e44383baa4 | ||
|
|
5a5d2b7a27 | ||
|
|
2b09c39c9c | ||
|
|
bacb65b48b | ||
|
|
656eed5975 | ||
|
|
173ef90a53 | ||
|
|
fd263b0dfd | ||
|
|
99db40ad2d | ||
|
|
22d7e75e1f | ||
|
|
f3f1bde4fc | ||
|
|
528ff3910f | ||
|
|
2efdf69734 | ||
|
|
9d5580b6dd | ||
|
|
4f8438a6cd | ||
|
|
7f4841b3ac | ||
|
|
7f28c97fcc | ||
|
|
be881c028f | ||
|
|
64e7b83139 | ||
|
|
d6752491e1 | ||
|
|
be314d9a54 | ||
|
|
14e26cbca3 | ||
|
|
2d2efb13e7 | ||
|
|
0f5c6956dd | ||
|
|
fbd82e4e9f | ||
|
|
b55f313735 | ||
|
|
215e18c9d4 | ||
|
|
2305afd86c | ||
|
|
4cc46d3e2a | ||
|
|
9e8593917f | ||
|
|
f1a7b8fc66 | ||
|
|
7c72acaa94 | ||
|
|
1ff70c26e3 | ||
|
|
7d5157ee17 | ||
|
|
be80c3670e | ||
|
|
2647a337a8 | ||
|
|
18831c3ca9 | ||
|
|
7505f1d636 | ||
|
|
4e7424d47b | ||
|
|
9876a3ba5d | ||
|
|
6b356a58d1 | ||
|
|
f2694b74c9 | ||
|
|
8eb17143f2 | ||
|
|
81a04f7d79 | ||
|
|
646da1ba8d | ||
|
|
5a3c84d0fd | ||
|
|
5f93f855a7 | ||
|
|
f04a5b33ec | ||
|
|
0316dc6e48 | ||
|
|
29e3a2f0f3 | ||
|
|
56d06f3e1e | ||
|
|
2ca86afdec | ||
|
|
741d25cb6f | ||
|
|
f4bb42459c | ||
|
|
4a07a3be11 | ||
|
|
0df80be95e | ||
|
|
acf66fb456 | ||
|
|
a35122c496 | ||
|
|
9f6867033f | ||
|
|
36df7740dd | ||
|
|
08c72fb4a9 | ||
|
|
8cba7e9b6d | ||
|
|
13965aed74 | ||
|
|
db88380b76 | ||
|
|
3b1c553773 | ||
|
|
9b3d7e0159 | ||
|
|
f9940ca8d7 | ||
|
|
53bc1861c4 | ||
|
|
b4beb545f7 | ||
|
|
d7630f2256 | ||
|
|
419b034975 | ||
|
|
b41262fa0b | ||
|
|
eb08f86c02 | ||
|
|
f2f24b0959 | ||
|
|
192bf33ffb | ||
|
|
2bef4dbd43 | ||
|
|
3fdcb2b84e | ||
|
|
ae9c575d2c | ||
|
|
c2141c6064 | ||
|
|
70955c765c | ||
|
|
a88d065606 | ||
|
|
5058bd6e9e | ||
|
|
fec94d1c5c | ||
|
|
14bb0947b4 | ||
|
|
3a3b6a7e86 | ||
|
|
53efa3eeb6 | ||
|
|
314ec4db40 | ||
|
|
f0e403911d | ||
|
|
6e9f9c20e9 | ||
|
|
cc34a5b43f | ||
|
|
22f8087b98 | ||
|
|
fc1b3e6c90 | ||
|
|
f2ba4cbf51 | ||
|
|
7e7a261de0 | ||
|
|
a47aaa4e7a | ||
|
|
08f631c197 | ||
|
|
369c2fb7b4 | ||
|
|
166c9fc827 | ||
|
|
7b57997874 | ||
|
|
cb7289094a | ||
|
|
96ccea8436 | ||
|
|
4c9e549aa3 | ||
|
|
0d05805b76 | ||
|
|
ed2dbf6954 | ||
|
|
bf49f042d4 | ||
|
|
aa9daa1b9d | ||
|
|
38f18698b3 | ||
|
|
5ffbb2744f | ||
|
|
a24e1cd508 | ||
|
|
37005a165d | ||
|
|
580edbc326 | ||
|
|
8e323b39f9 | ||
|
|
869449cdd0 | ||
|
|
8f8197f3fd | ||
|
|
a3a567e765 | ||
|
|
e1ca87f057 | ||
|
|
93c55d04e5 | ||
|
|
2b3121eebf | ||
|
|
2e3a81e4b3 | ||
|
|
5c75480e90 | ||
|
|
d88bb2e825 | ||
|
|
c362df25a2 | ||
|
|
27dc941475 | ||
|
|
ead5e4724c | ||
|
|
6e75bc7fe3 | ||
|
|
845c01ef3f | ||
|
|
554a835e90 | ||
|
|
39c6c7f738 | ||
|
|
b3100e187b | ||
|
|
e8f5fe5255 | ||
|
|
ce1beb423c | ||
|
|
784a0bd000 | ||
|
|
d87e1d6ab7 | ||
|
|
6167925147 | ||
|
|
4abc686d76 | ||
|
|
392a545336 | ||
|
|
4aa78a8bea | ||
|
|
6c3dc61db5 | ||
|
|
3b802e3c4a | ||
|
|
690a734ebf | ||
|
|
9e862e1e7f | ||
|
|
d0222cfe2d | ||
|
|
409f980a18 | ||
|
|
14e00f8f66 | ||
|
|
5265f59525 | ||
|
|
86b6f05d19 | ||
|
|
5643f35fb4 | ||
|
|
76d767cbe8 | ||
|
|
953c78987a | ||
|
|
c3c58046c7 | ||
|
|
111ebe84c2 | ||
|
|
eb93f592e5 | ||
|
|
f5898a0528 | ||
|
|
edfd7e3d94 | ||
|
|
acfb883011 | ||
|
|
42aba81c2f | ||
|
|
08e5d604d3 | ||
|
|
19d1e9270d | ||
|
|
218482769b | ||
|
|
d32cbbb130 | ||
|
|
8df447dc77 | ||
|
|
b8a5426cf0 | ||
|
|
2db0f08e08 | ||
|
|
7c8d39a67b | ||
|
|
c8cbfc2c98 | ||
|
|
ae0a799f44 | ||
|
|
91ff7d1864 | ||
|
|
9659d59307 | ||
|
|
cf6ae9d12f | ||
|
|
af75ce8e15 | ||
|
|
be4a08b58a | ||
|
|
60f77033ad | ||
|
|
e77d49f2a2 | ||
|
|
ac49d0943a | ||
|
|
16d29c7da4 | ||
|
|
b6b3c7eb27 | ||
|
|
1c95bd383e | ||
|
|
159d4de370 | ||
|
|
c62216b4fc | ||
|
|
6835cead8c | ||
|
|
6e013a136a | ||
|
|
af387e2199 | ||
|
|
444bc82081 | ||
|
|
23c4b3f158 | ||
|
|
af45473cd5 | ||
|
|
cef20ae67a | ||
|
|
ee31090b2e | ||
|
|
64921d217b | ||
|
|
56f99d19c3 | ||
|
|
e0b21f41c2 | ||
|
|
257b630216 | ||
|
|
e91bd3babd | ||
|
|
b139598b1c | ||
|
|
076f5c794d | ||
|
|
537b149828 | ||
|
|
4a6fe534ce | ||
|
|
2b349b5d33 | ||
|
|
ebebad1c92 | ||
|
|
56af6d55ed | ||
|
|
f76fedeed5 | ||
|
|
fea17f8b8c | ||
|
|
af9a39d954 | ||
|
|
28c40b1757 | ||
|
|
3449a61032 | ||
|
|
1c3b8998bc | ||
|
|
ac28905082 | ||
|
|
04e72d34f5 | ||
|
|
6eed1911fd | ||
|
|
b31f5ef699 | ||
|
|
d4ad731bae | ||
|
|
4f666bc113 | ||
|
|
7f79f0d332 | ||
|
|
77da35515c | ||
|
|
7b3675bf7a | ||
|
|
0f352a6f22 | ||
|
|
bf9042203d | ||
|
|
7af53e61f0 | ||
|
|
88fb6136fb | ||
|
|
e21b79cb21 | ||
|
|
f16a069fd7 | ||
|
|
5979fe9d8a | ||
|
|
400ec02e8a | ||
|
|
40da3e17e5 | ||
|
|
5a2b8be3f5 | ||
|
|
2b558857dd | ||
|
|
a8c2aaf6f0 | ||
|
|
69c9bfa089 | ||
|
|
b2439bbb8a | ||
|
|
3a10dbd564 | ||
|
|
e0853cdf42 | ||
|
|
5879c6493f | ||
|
|
47a57f6f86 | ||
|
|
61205a7e65 | ||
|
|
57cf69a18c | ||
|
|
dd08ac86e6 | ||
|
|
bae33a7001 | ||
|
|
5e46a20e03 | ||
|
|
c135b878cd | ||
|
|
1139ec1d0f | ||
|
|
3db617f14a | ||
|
|
efe62a7395 | ||
|
|
14376a73a5 | ||
|
|
506f156f7a | ||
|
|
1653f837e3 | ||
|
|
20b7ba7501 | ||
|
|
cc9358f95a | ||
|
|
16c6ecc365 | ||
|
|
5f494b8415 | ||
|
|
9ae282372c | ||
|
|
ed46afda6f | ||
|
|
ea65c72d06 | ||
|
|
c91c18e6ef | ||
|
|
e0c734dcfe | ||
|
|
07111668d4 | ||
|
|
4944c195d4 | ||
|
|
5cd496d208 | ||
|
|
83426b20a3 | ||
|
|
617c7127f4 | ||
|
|
23f93a15ca | ||
|
|
ec478756cc | ||
|
|
9380f5d218 | ||
|
|
ac89a555fa | ||
|
|
699b8dca49 | ||
|
|
ccc34e4d30 | ||
|
|
c6d4ceeda6 | ||
|
|
9ec49e2411 | ||
|
|
fa464bbce9 | ||
|
|
e565564bc9 | ||
|
|
cb1b494c89 | ||
|
|
6d86628e5b | ||
|
|
e2e004e7a9 | ||
|
|
ace7b570a0 | ||
|
|
f1379346f7 | ||
|
|
34cf156b80 | ||
|
|
9cc783a87d | ||
|
|
a99d95e3af | ||
|
|
e500e2e5d1 | ||
|
|
b671dbfe94 | ||
|
|
1e50a3dade | ||
|
|
70b96aa232 | ||
|
|
07eb655244 | ||
|
|
1eae96ca2f | ||
|
|
626369c2fb | ||
|
|
409efa1ec8 | ||
|
|
411686402b | ||
|
|
04a108617a | ||
|
|
e1d2b3568a | ||
|
|
da1a79ef5b | ||
|
|
51b1151044 | ||
|
|
94cde130ca | ||
|
|
e4bcb62dbf | ||
|
|
20f6fd1b63 | ||
|
|
b4584ff6c4 | ||
|
|
0808f68601 | ||
|
|
25db7c6116 | ||
|
|
7bf9f49728 | ||
|
|
0ae4313800 | ||
|
|
d0aee85b29 | ||
|
|
09fdf6622a | ||
|
|
37e896dff1 | ||
|
|
b33e675e5a | ||
|
|
8158ea7288 | ||
|
|
144a1d0516 | ||
|
|
8322d08071 | ||
|
|
a17fccbcf4 | ||
|
|
1751c62c98 | ||
|
|
f47f68a9d8 | ||
|
|
62e96a3535 | ||
|
|
a700dd9e11 | ||
|
|
17214bb06d | ||
|
|
befc760f86 | ||
|
|
489a2b35d8 | ||
|
|
10ed79553a | ||
|
|
1d0a52a349 | ||
|
|
6dbe822062 | ||
|
|
b3f713bd7b | ||
|
|
3c91cb881d | ||
|
|
a098c19b55 | ||
|
|
d9e102472a | ||
|
|
1db67e0a35 | ||
|
|
9200ce6019 | ||
|
|
ddfc0d5763 | ||
|
|
d3eac2867a | ||
|
|
1c68abaffa | ||
|
|
71e55118f5 | ||
|
|
7b9f825ae8 | ||
|
|
b0e5eaf59a | ||
|
|
9a2845d491 | ||
|
|
0e50ce6145 | ||
|
|
767193e20b | ||
|
|
d1cb41bfd0 | ||
|
|
5752285bc5 | ||
|
|
b75e23143b | ||
|
|
4f459799e3 | ||
|
|
be0cbc1b7e | ||
|
|
87597358cf | ||
|
|
9399046729 | ||
|
|
e43460b50f | ||
|
|
5367086175 | ||
|
|
dfef28de88 | ||
|
|
67c385046d | ||
|
|
fecece0e59 | ||
|
|
e04e0b0a20 | ||
|
|
6eeb7c7e74 | ||
|
|
6e4ccec6c4 | ||
|
|
0288cdb0a8 | ||
|
|
3d62fc55d8 | ||
|
|
87d7341fbe | ||
|
|
29d91e9271 | ||
|
|
801d6fe40c | ||
|
|
0494c75a53 | ||
|
|
0d39dc92b5 | ||
|
|
1f33294b1c | ||
|
|
2acfbebfd3 | ||
|
|
c0c580c414 | ||
|
|
56e8d71090 | ||
|
|
00fc6b13f2 | ||
|
|
90de3a7ffe | ||
|
|
d58b711eec | ||
|
|
cc12f02658 | ||
|
|
d0261b72de | ||
|
|
0c1c8778df | ||
|
|
0a0b5dcb32 | ||
|
|
502bd1feea | ||
|
|
e25b5b91f6 | ||
|
|
2cbe80b53e | ||
|
|
3bed5d11d2 | ||
|
|
cbb1e70554 | ||
|
|
c495209122 | ||
|
|
005ee7f862 | ||
|
|
2493c660e9 | ||
|
|
a3e4509ff9 | ||
|
|
eceb910fef | ||
|
|
95340242ed | ||
|
|
612e93e360 | ||
|
|
39645b824a | ||
|
|
1e96af6325 | ||
|
|
7c44415d78 | ||
|
|
0eabffdc35 | ||
|
|
1842c921b3 | ||
|
|
adcdf150a6 | ||
|
|
a41a71e2d4 | ||
|
|
297edf754f | ||
|
|
08fe76a893 | ||
|
|
5e9f40d3d9 | ||
|
|
18fef10641 | ||
|
|
f5c97476de | ||
|
|
49f26bf6e8 | ||
|
|
07e39302ec | ||
|
|
0f58af6627 | ||
|
|
635d47c78c | ||
|
|
1c15573012 | ||
|
|
efd5dbb168 | ||
|
|
510d7d36ed | ||
|
|
e481815583 | ||
|
|
08e57c8366 | ||
|
|
341689d6c5 | ||
|
|
ad1fcccf60 | ||
|
|
ca102af92b | ||
|
|
eb100244b0 | ||
|
|
418a4230e0 | ||
|
|
cf706b0775 | ||
|
|
e2732e2f59 | ||
|
|
f72fcfe219 | ||
|
|
7e994e392d | ||
|
|
6a8a1dcda2 | ||
|
|
d1d1633121 | ||
|
|
cd44ec41c5 | ||
|
|
f55e58f5cf | ||
|
|
350f64d9e2 | ||
|
|
4f3c420852 | ||
|
|
b2886f1a0d | ||
|
|
93d1711011 | ||
|
|
6a16e0c092 | ||
|
|
351a573396 | ||
|
|
3016888ee0 | ||
|
|
532a4b54f5 | ||
|
|
1dac2ebb71 | ||
|
|
b8f260176e | ||
|
|
6c813fc9bc | ||
|
|
538ac5b574 | ||
|
|
2354402b7a | ||
|
|
057c285cd7 | ||
|
|
1b3864fc47 | ||
|
|
4bbe3349c2 | ||
|
|
48aa0b0d99 | ||
|
|
0d12a2fa89 | ||
|
|
fe6e2e4a08 | ||
|
|
078bb713e1 | ||
|
|
2207c5e4ef | ||
|
|
febf1a0ac9 | ||
|
|
54044b0635 | ||
|
|
b59d61b4b4 | ||
|
|
e8915b9d9d | ||
|
|
2c98f1db78 | ||
|
|
c421e8aaf2 | ||
|
|
35cc1c0ff3 | ||
|
|
f3405e5b03 | ||
|
|
6d8884de49 | ||
|
|
77e58f602d | ||
|
|
5d77f14904 | ||
|
|
17a1d5e162 | ||
|
|
1e0c61032f | ||
|
|
cf1fae7f38 | ||
|
|
73191a443f | ||
|
|
fc03bbe078 | ||
|
|
9167055ed2 | ||
|
|
8f3520579a | ||
|
|
f81e2f7621 | ||
|
|
59fe8f79cc | ||
|
|
d8f339592d | ||
|
|
a09e33cdeb | ||
|
|
e5109ba1f0 | ||
|
|
3d0cc2f97d | ||
|
|
a004799699 | ||
|
|
b293043e2d | ||
|
|
80367aae17 | ||
|
|
772415fd8a | ||
|
|
afa72f5210 | ||
|
|
85c1ea5cb6 | ||
|
|
3ce011f44f | ||
|
|
d54d9f5c75 | ||
|
|
146514e180 | ||
|
|
55844795be | ||
|
|
7c3751b10b | ||
|
|
f4a1ec6ce7 | ||
|
|
c2fab7a6ff | ||
|
|
0a940087b2 | ||
|
|
2bf3446f7a | ||
|
|
746b390201 | ||
|
|
1cd9d886e6 | ||
|
|
fe2dcc8aec | ||
|
|
f3eec04655 | ||
|
|
765736dc77 | ||
|
|
7a16d46fdc | ||
|
|
ccde4c462d | ||
|
|
b03d427b08 | ||
|
|
73193110f2 | ||
|
|
1fe69323ad | ||
|
|
3fd2ddf1a2 | ||
|
|
dd4af8045f | ||
|
|
e45c19b3e8 | ||
|
|
882fc9b174 | ||
|
|
f34f34495b | ||
|
|
df022f6cb2 | ||
|
|
c7b998cc73 | ||
|
|
4af30533f0 | ||
|
|
f37e514a96 | ||
|
|
8f399de135 | ||
|
|
ba26368040 | ||
|
|
6f791d049a | ||
|
|
6f0796a33a | ||
|
|
fdcdde2756 | ||
|
|
402cdcd02f | ||
|
|
2f1974b5ac | ||
|
|
1b517342ae | ||
|
|
705653b2d7 | ||
|
|
aadfc2e121 | ||
|
|
9c623892f5 | ||
|
|
a42b2ad5ed | ||
|
|
550bb88a9e | ||
|
|
88553abf4d | ||
|
|
c92b964e2d | ||
|
|
b3a87ced5a | ||
|
|
81e8f0a8a2 | ||
|
|
752a3b2baa | ||
|
|
5e94cd4fde | ||
|
|
cc5a4cbe9b | ||
|
|
48df40d2a4 | ||
|
|
f1786a375f | ||
|
|
6f7cb2cb4f | ||
|
|
0f2b5fc749 | ||
|
|
5241c1a018 | ||
|
|
52ea8a0d87 | ||
|
|
e6e605ba86 | ||
|
|
c3f4330fa3 | ||
|
|
f86fb7a953 | ||
|
|
8a31f9e016 | ||
|
|
11f2d58dbd | ||
|
|
3cbf9e040c | ||
|
|
3c95776805 | ||
|
|
7a5f3c2153 | ||
|
|
7f3e662b34 | ||
|
|
cbb00ebd04 | ||
|
|
423dd2286b | ||
|
|
4c06d9155c | ||
|
|
c15d5cc230 | ||
|
|
9977e1545a | ||
|
|
6ab5b24be4 | ||
|
|
7decedcbf8 | ||
|
|
faf3ef45b3 | ||
|
|
e9b1ebba9d | ||
|
|
270f7fce1d | ||
|
|
f1593b7c49 | ||
|
|
ab6cd921e3 | ||
|
|
9a76968ec4 | ||
|
|
9c5539d46d | ||
|
|
86dc355a33 | ||
|
|
bc15570651 | ||
|
|
e358ff8fa4 | ||
|
|
df6b4f52e0 | ||
|
|
9eb8ace260 | ||
|
|
12c039cdb2 | ||
|
|
a6ba67cb3a | ||
|
|
8b16ae30fe | ||
|
|
c03e38291a | ||
|
|
1577779526 | ||
|
|
ef93bdb19b | ||
|
|
64b49bc3ce | ||
|
|
40fc8f9e46 | ||
|
|
b18aedcc50 | ||
|
|
9525567d77 | ||
|
|
6b5660c19f | ||
|
|
321f3c3104 | ||
|
|
092478f294 | ||
|
|
5a7f636ce4 | ||
|
|
c6382ddd6e | ||
|
|
6058220d2a | ||
|
|
58db60c68e | ||
|
|
c3d5325fc8 | ||
|
|
c70aae4697 | ||
|
|
937e2a6919 | ||
|
|
e5dc2a0ac5 | ||
|
|
796270bf83 | ||
|
|
9241fd0957 | ||
|
|
e9429b5d3e | ||
|
|
55cf994c29 | ||
|
|
b1302b083e | ||
|
|
32f9a59ab4 | ||
|
|
e23102602c | ||
|
|
b3d5ee2934 | ||
|
|
51e093bd1c | ||
|
|
985fd114f2 | ||
|
|
fce431cf3a | ||
|
|
aa35ba7584 | ||
|
|
857281f7ff | ||
|
|
d26318e4a7 | ||
|
|
4e8b50cd8d | ||
|
|
0e0f2f5faf | ||
|
|
a6e47c7f54 | ||
|
|
5697b4ccfa | ||
|
|
acb5427bda | ||
|
|
be57081721 | ||
|
|
b7840466ce | ||
|
|
a1da251c10 | ||
|
|
096f6f9f39 | ||
|
|
758dc12c9f | ||
|
|
28e5378b55 | ||
|
|
9948514086 | ||
|
|
77e05971b4 | ||
|
|
bc1626e5de | ||
|
|
b809866be5 | ||
|
|
5464c9baf2 | ||
|
|
34f6e5ebe6 | ||
|
|
474685e26e | ||
|
|
bd449f57a7 | ||
|
|
870c601f1d | ||
|
|
6b88cddc21 | ||
|
|
8021644e9d | ||
|
|
ec80f82824 | ||
|
|
fc498c9e7b | ||
|
|
875fc895a8 | ||
|
|
e1a075ab59 | ||
|
|
53a336f0e5 | ||
|
|
8d0e489484 | ||
|
|
15bb1a2a51 | ||
|
|
5da2014f13 | ||
|
|
7a344716a6 | ||
|
|
cb5c49b581 | ||
|
|
750ca67ce9 | ||
|
|
842808848c | ||
|
|
5a9658168a | ||
|
|
396f4161cb | ||
|
|
c1ae22694c | ||
|
|
90b87ea5e6 | ||
|
|
180def907b | ||
|
|
1997787c52 | ||
|
|
b98dd9e240 | ||
|
|
93e048fe27 | ||
|
|
158776411b | ||
|
|
f537298cca | ||
|
|
61a0a04d26 | ||
|
|
3dc56a3b34 | ||
|
|
ae03f22199 | ||
|
|
22b31190cb | ||
|
|
46bcb8d59d | ||
|
|
02beed5e98 | ||
|
|
2db7e7e7db | ||
|
|
24c1a5ba29 | ||
|
|
42de18f347 | ||
|
|
d9262b3b55 | ||
|
|
40cfaa6837 | ||
|
|
7292373f92 | ||
|
|
70ddde6929 | ||
|
|
f63cabd931 | ||
|
|
a54bbbbf02 | ||
|
|
dea63b96e0 | ||
|
|
05f8efc1a2 | ||
|
|
4f753c64d6 | ||
|
|
65be4808af | ||
|
|
6673005215 | ||
|
|
782f6ddf99 | ||
|
|
86300bbeea | ||
|
|
af8094d799 | ||
|
|
aa9c326d7e | ||
|
|
fcbc702112 | ||
|
|
d11290b90b | ||
|
|
6f9f434463 | ||
|
|
9ae367f639 | ||
|
|
b65e08a7be | ||
|
|
545876550f | ||
|
|
6796ed23ab | ||
|
|
09ef27ae04 | ||
|
|
4ed1dae533 | ||
|
|
1702f102af | ||
|
|
7ce21eb042 | ||
|
|
34bd8bdf30 | ||
|
|
9f0c1d216a | ||
|
|
cedf890928 | ||
|
|
621dc2fd01 | ||
|
|
111614a994 | ||
|
|
d6ef5345e5 | ||
|
|
63f29d6f73 | ||
|
|
6ca0a9076e | ||
|
|
9446485e3d | ||
|
|
f0984897a5 | ||
|
|
24e98d039d | ||
|
|
17b0d83a1f | ||
|
|
ba5543fbf3 | ||
|
|
b78adcdd11 | ||
|
|
9b30c7f46e | ||
|
|
b6acfdaa24 | ||
|
|
67ed34eedb | ||
|
|
1b33361bab | ||
|
|
b19d7268c3 | ||
|
|
1af73f6c81 | ||
|
|
ee7a535608 | ||
|
|
485860cc31 | ||
|
|
d507ce6b85 | ||
|
|
8d7a632eef | ||
|
|
4ffc260869 | ||
|
|
9f521cd4af | ||
|
|
b0250ebeac | ||
|
|
698b612188 | ||
|
|
ae947fcf67 | ||
|
|
344ff48756 | ||
|
|
173a240a7e | ||
|
|
45396a99d9 | ||
|
|
bfdd5f285b | ||
|
|
948a3d62b1 | ||
|
|
b0a0075845 | ||
|
|
11a748935e | ||
|
|
8481a0553d | ||
|
|
743bafa50c | ||
|
|
96efa70a9e | ||
|
|
02ab4a8803 | ||
|
|
96173aec93 | ||
|
|
1c344e2668 | ||
|
|
7380babdba | ||
|
|
0946f99b08 | ||
|
|
455613076a | ||
|
|
4f6a4e5d6d | ||
|
|
964ddb6aa0 | ||
|
|
9e21d84f1e | ||
|
|
099535a1a7 | ||
|
|
8692128365 | ||
|
|
bb20b2c64c | ||
|
|
bcafdf8d90 | ||
|
|
443c822f77 | ||
|
|
d868928888 | ||
|
|
d1d114f5b7 | ||
|
|
31d0df92ac | ||
|
|
17c31ce173 | ||
|
|
dcc9c39529 | ||
|
|
70862c9409 | ||
|
|
26da8361e9 | ||
|
|
483f77a275 | ||
|
|
985806030d | ||
|
|
52c911b3b4 | ||
|
|
2023bd18b6 | ||
|
|
d6bd91e4e5 | ||
|
|
9d938388f6 | ||
|
|
bc1e52cfbf | ||
|
|
741481d3e0 | ||
|
|
4ea940787e | ||
|
|
416ce69550 | ||
|
|
c0f1644c88 | ||
|
|
e8d9a5fa54 | ||
|
|
b67a8a348a | ||
|
|
90d5df4ae7 | ||
|
|
a9744debd9 | ||
|
|
dfe17d7f91 | ||
|
|
e13298d093 | ||
|
|
bb00704871 | ||
|
|
421fe79e39 | ||
|
|
939bfaefec | ||
|
|
f14ddb7830 | ||
|
|
997d2ac3fe | ||
|
|
cf0265a112 | ||
|
|
49be8bce50 | ||
|
|
148fb9a3bc | ||
|
|
98fe945a0d | ||
|
|
249c7e9431 | ||
|
|
40bd0c4b57 | ||
|
|
4b984b14a3 | ||
|
|
ed70cb733c | ||
|
|
d8157c07df | ||
|
|
90692f93cf | ||
|
|
90f08efe58 | ||
|
|
a30f47613a | ||
|
|
51c57045e5 | ||
|
|
a004e6a823 | ||
|
|
783aa4bcb8 | ||
|
|
b6bb55d88c | ||
|
|
7dac4862bc | ||
|
|
72075f0e00 | ||
|
|
81a12be317 | ||
|
|
050ace2fb4 | ||
|
|
570b44b29b | ||
|
|
0b874b64ef | ||
|
|
30a73d8544 | ||
|
|
4440845614 | ||
|
|
5dc29a51ef | ||
|
|
3afe56d7d8 | ||
|
|
827ed55c3b | ||
|
|
e5a093986b | ||
|
|
d1e340f0be | ||
|
|
71cce91a76 | ||
|
|
0f4541b691 | ||
|
|
273a222d7f | ||
|
|
7a5a67ab7b | ||
|
|
97644ed7f8 | ||
|
|
02d7cd2ac2 | ||
|
|
138f4e62d2 | ||
|
|
9aaf2ae24d | ||
|
|
e4050114f5 | ||
|
|
850d80b898 | ||
|
|
de5253a20f | ||
|
|
a23c7eee15 | ||
|
|
106ea79337 | ||
|
|
d87b4685b8 | ||
|
|
5b8a528251 | ||
|
|
f01929bcb6 | ||
|
|
8c3680dcc9 | ||
|
|
00950743d7 | ||
|
|
afdd9e1be5 | ||
|
|
3a7eb6b80f | ||
|
|
0c9b50c498 | ||
|
|
aafcd703f3 | ||
|
|
b83e2a7d5c | ||
|
|
5cae087ae9 | ||
|
|
f7adfdebe9 | ||
|
|
701d9f83f8 | ||
|
|
9eb552b239 | ||
|
|
499ff415a9 | ||
|
|
3b1332cdb4 | ||
|
|
5f14bd9410 | ||
|
|
a3c12fb6c5 | ||
|
|
a9be1547b1 | ||
|
|
99a107dbee | ||
|
|
80be275710 | ||
|
|
aafcbbb252 | ||
|
|
df4d20e95b | ||
|
|
9a91346fa1 | ||
|
|
514491d726 | ||
|
|
cd8a83a42c | ||
|
|
4b39dd1c00 | ||
|
|
adb2f18538 | ||
|
|
07e97cf8a5 | ||
|
|
cbcd124588 | ||
|
|
309ce74376 | ||
|
|
14db19ed11 | ||
|
|
802bb6eac8 | ||
|
|
a95348c03e | ||
|
|
1115f5972c | ||
|
|
df72c89e4b |
50
.gitignore
vendored
50
.gitignore
vendored
@@ -1,9 +1,11 @@
|
|||||||
*.o
|
*.o
|
||||||
*.lo
|
*.lo
|
||||||
*.a
|
*.a
|
||||||
|
*.sw?
|
||||||
Makefile.in
|
Makefile.in
|
||||||
Makefile
|
Makefile
|
||||||
.deps
|
.deps
|
||||||
|
src/cscope*
|
||||||
|
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
autom4te.cache
|
autom4te.cache
|
||||||
@@ -12,11 +14,57 @@ config.guess
|
|||||||
config.sub
|
config.sub
|
||||||
config.status
|
config.status
|
||||||
configure
|
configure
|
||||||
|
compile
|
||||||
depcomp
|
depcomp
|
||||||
install-sh
|
install-sh
|
||||||
missing
|
missing
|
||||||
libtool
|
*libtool
|
||||||
ltmain.sh
|
ltmain.sh
|
||||||
|
*~
|
||||||
|
|
||||||
core
|
core
|
||||||
core.*
|
core.*
|
||||||
|
|
||||||
|
osmoappdesc.pyc
|
||||||
|
|
||||||
|
# binaries
|
||||||
|
src/osmo-pcu
|
||||||
|
src/osmo-pcu-remote
|
||||||
|
|
||||||
|
# tests
|
||||||
|
.dirstamp
|
||||||
|
__pycache__/
|
||||||
|
tests/atconfig
|
||||||
|
tests/package.m4
|
||||||
|
tests/*/*Test
|
||||||
|
tests/*/*_test
|
||||||
|
tests/emu/pcu_emu
|
||||||
|
tests/testsuite
|
||||||
|
tests/testsuite.log
|
||||||
|
|
||||||
|
# ignore debian files
|
||||||
|
.tarball-version
|
||||||
|
debian/autoreconf.after
|
||||||
|
debian/autoreconf.before
|
||||||
|
debian/files
|
||||||
|
debian/*.debhelper*
|
||||||
|
debian/*.substvars
|
||||||
|
debian/osmo-pcu-dbg/
|
||||||
|
debian/osmo-pcu.substvars
|
||||||
|
debian/osmo-pcu/
|
||||||
|
debian/tmp/
|
||||||
|
|
||||||
|
osmo-pcu.pc
|
||||||
|
|
||||||
|
# manuals
|
||||||
|
doc/manuals/*.html
|
||||||
|
doc/manuals/*.svg
|
||||||
|
doc/manuals/*.pdf
|
||||||
|
doc/manuals/*__*.png
|
||||||
|
doc/manuals/*.check
|
||||||
|
doc/manuals/generated/
|
||||||
|
doc/manuals/osmomsc-usermanual.xml
|
||||||
|
doc/manuals/common
|
||||||
|
doc/manuals/build
|
||||||
|
|
||||||
|
contrib/osmo-pcu.spec
|
||||||
|
|||||||
3
.gitreview
Normal file
3
.gitreview
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[gerrit]
|
||||||
|
host=gerrit.osmocom.org
|
||||||
|
project=osmo-pcu
|
||||||
13
Makefile.am
13
Makefile.am
@@ -1,3 +1,14 @@
|
|||||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||||
|
|
||||||
SUBDIRS = src
|
SUBDIRS = include src doc tests contrib
|
||||||
|
EXTRA_DIST = \
|
||||||
|
README.md \
|
||||||
|
contrib/osmo-pcu.spec.in \
|
||||||
|
debian \
|
||||||
|
osmoappdesc.py \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||||
|
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||||
|
|
||||||
|
@RELMAKE@
|
||||||
|
|||||||
22
README
22
README
@@ -1,22 +0,0 @@
|
|||||||
This is an implementation of Packet Control Unit (PCU) according to TS 04.60
|
|
||||||
|
|
||||||
The PCU is part of BSS, so it connects directly to SGSN.
|
|
||||||
|
|
||||||
|
|
||||||
== Current limitations ==
|
|
||||||
|
|
||||||
* No PFC support
|
|
||||||
* No fixed allocation support
|
|
||||||
* No extended dynamic allocation support
|
|
||||||
* No unacknowledged mode operation
|
|
||||||
* No PCCCH/PBCCH support
|
|
||||||
* Only single slot assignment on uplink direction
|
|
||||||
* No half-duplex class support
|
|
||||||
* No two-phase access support
|
|
||||||
* No handover support
|
|
||||||
* No measurement support
|
|
||||||
* No polling for control ack on assignment
|
|
||||||
* No TA loop
|
|
||||||
* No power loop
|
|
||||||
* No CS loop
|
|
||||||
* No EGPRS
|
|
||||||
85
README.md
Normal file
85
README.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
osmo-pcu - Osmocom Packet Control Unit
|
||||||
|
======================================
|
||||||
|
|
||||||
|
This repository contains a C/C++-language implementation of a GPRS
|
||||||
|
Packet Control Unit, as specified by ETSI/3GPP. It is part of the
|
||||||
|
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
|
||||||
|
project.
|
||||||
|
|
||||||
|
The Packet Control Unit is terminating the Layer 2 (RLC/MAC) of the GPRS
|
||||||
|
radio interface and adapting it to the Gb Interface (BSSGP+NS Protocol)
|
||||||
|
towards the SGSN.
|
||||||
|
|
||||||
|
The PCU interfaces with the physical layer of the radio interface.
|
||||||
|
OsmoPCU is typically used co-located with the BTS, specifically
|
||||||
|
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki).
|
||||||
|
For legacy BTSs that run proprietary sotware without an interface to
|
||||||
|
OsmoPCU, you may also co-locate it with the BSC, specifically
|
||||||
|
[OsmoBSC](https://osmocom.org/projects/openbsc/wiki/Osmo-bsc)
|
||||||
|
|
||||||
|
Homepage
|
||||||
|
--------
|
||||||
|
|
||||||
|
The official homepage of the project is
|
||||||
|
https://osmocom.org/projects/osmopcu/wiki/OsmoPCU
|
||||||
|
|
||||||
|
GIT Repository
|
||||||
|
--------------
|
||||||
|
|
||||||
|
You can clone from the official osmo-pcu.git repository using
|
||||||
|
|
||||||
|
git clone git://git.osmocom.org/osmo-pcu.git
|
||||||
|
|
||||||
|
There is a cgit interface at http://git.osmocom.org/osmo-pcu/
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
We provide a
|
||||||
|
[user manual](http://ftp.osmocom.org/docs/latest/osmopcu-usermanual.pdf)
|
||||||
|
as well as a
|
||||||
|
[vty reference manual](http://ftp.osmocom.org/docs/latest/osmopcu-vty-reference.pdf)
|
||||||
|
|
||||||
|
Please note that a lot of the PCU configuration actually happens inside
|
||||||
|
the BSC, which passes this configuration via A-bis OML to the BTS, which
|
||||||
|
then in turn passes it via the PCU socket into OsmoPCU.
|
||||||
|
|
||||||
|
Mailing List
|
||||||
|
------------
|
||||||
|
|
||||||
|
Discussions related to osmo-pcu are happening on the
|
||||||
|
osmocom-net-gprs@lists.osmocom.org mailing list, please see
|
||||||
|
https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs for
|
||||||
|
subscription options and the list archive.
|
||||||
|
|
||||||
|
Please observe the [Osmocom Mailing List
|
||||||
|
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||||
|
when posting.
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
Our coding standards are described at
|
||||||
|
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
|
||||||
|
|
||||||
|
We us a gerrit based patch submission/review process for managing
|
||||||
|
contributions. Please see
|
||||||
|
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
|
||||||
|
more details
|
||||||
|
|
||||||
|
The current patch queue for osmo-pcu can be seen at
|
||||||
|
https://gerrit.osmocom.org/#/q/project:osmo-pcu+status:open
|
||||||
|
|
||||||
|
|
||||||
|
Current limitations
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* No PFC support
|
||||||
|
* No fixed allocation support (was removed from 3GPP Rel >= 5 anyway)
|
||||||
|
* No extended dynamic allocation support
|
||||||
|
* No unacknowledged mode operation
|
||||||
|
* Only single slot assignment on uplink direction
|
||||||
|
* No half-duplex class support (only semi-duplex)
|
||||||
|
* No TA loop
|
||||||
|
* No power loop
|
||||||
|
* Multi-BTS support not tested
|
||||||
34
TODO
Normal file
34
TODO
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
* Change functions with 100 parameters to get a struct as param
|
||||||
|
* Move move into the TBF class
|
||||||
|
* tbf/llc window code appears to be duplicated and nested in other
|
||||||
|
methods. This needs to be cleaned.
|
||||||
|
|
||||||
|
|
||||||
|
* Possible race condition:
|
||||||
|
When scheduling a Downlink Assignment on the UL-TBF we need to make
|
||||||
|
sure that the assignment is sent _before_ the final ack. With my fairness
|
||||||
|
changes it gets more likely that this event is trigerred.
|
||||||
|
|
||||||
|
* Optimize:
|
||||||
|
After receiving an ACK/NACK.. schedule another one if the window
|
||||||
|
is kind of stalled anyway. This way we avoid resending frames that
|
||||||
|
might have already arrived. It could increase the throughput..
|
||||||
|
|
||||||
|
Do not re-transmit after we got ack/nacked and where in the resending
|
||||||
|
mode... and increase the window.
|
||||||
|
|
||||||
|
<0004> tbf.cpp:907 - Sending new block at BSN 111
|
||||||
|
...
|
||||||
|
tbf.cpp:858 - Restarting at BSN 48, because all window is stalled.
|
||||||
|
...
|
||||||
|
tbf.cpp:1383 - V(B): (V(A)=59)"NNAAAAAAANAAAAANNAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXX"(V(S)-1=111) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid
|
||||||
|
.. retransmitting the nacked.. and then the ones that migh have
|
||||||
|
already arrived
|
||||||
|
<0004> tbf.cpp:834 TBF(TFI=0 TLLI=0xd7b78810 DIR=DL) downlink (V(A)==59 .. V(S)==112)
|
||||||
|
<0004> tbf.cpp:840 - Resending BSN 111
|
||||||
|
|
||||||
|
|
||||||
|
Figure out scheduling issue. Why do we reach the 20 re-transmits and
|
||||||
|
stil haven't received the ACK/NACK? was it scheduled? The whole
|
||||||
|
scheduler could be re-worked to be more determestic.. and answer
|
||||||
|
questions like if it has been sent or not
|
||||||
11
TODO-RELEASE
Normal file
11
TODO-RELEASE
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||||
|
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||||
|
# In short:
|
||||||
|
# LIBVERSION=c:r:a
|
||||||
|
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
|
||||||
|
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
|
||||||
|
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||||
|
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||||
|
#library what description / commit summary line
|
||||||
|
libosmocore struct bssgp_bvc_ctx->is_sgsn field used available only on libosmocore >1.5.1
|
||||||
|
libosmocore gprs_ns2_ip_bind_set_priority function used available only on libosmocore >1.5.1
|
||||||
248
configure.ac
248
configure.ac
@@ -1,13 +1,24 @@
|
|||||||
dnl Process this file with autoconf to produce a configure script
|
dnl Process this file with autoconf to produce a configure script
|
||||||
AC_INIT([osmo-pcu],
|
AC_INIT([osmo-pcu],
|
||||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||||
[osmocom-pcu@lists.osmocom.org])
|
[osmocom-net-gprs@lists.osmocom.org])
|
||||||
|
|
||||||
|
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||||
|
AC_CONFIG_AUX_DIR([.])
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||||
|
AC_CONFIG_TESTDIR(tests)
|
||||||
|
|
||||||
|
CXXFLAGS="$CXXFLAGS -std=gnu++03"
|
||||||
|
CFLAGS="$CFLAGS -std=gnu11"
|
||||||
|
|
||||||
dnl kernel style compile messages
|
dnl kernel style compile messages
|
||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||||
|
|
||||||
|
dnl include release helper
|
||||||
|
RELMAKE='-include osmo-release.mk'
|
||||||
|
AC_SUBST([RELMAKE])
|
||||||
|
|
||||||
dnl checks for programs
|
dnl checks for programs
|
||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
@@ -15,25 +26,236 @@ AC_PROG_CXX
|
|||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
LT_INIT
|
LT_INIT
|
||||||
|
|
||||||
|
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||||
|
AS_CASE(["$LD"],[*clang*],
|
||||||
|
[AS_CASE(["${host_os}"],
|
||||||
|
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||||
|
|
||||||
|
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||||
|
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||||
|
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||||
|
AC_MSG_WARN([You need to install pkg-config])
|
||||||
|
fi
|
||||||
|
PKG_PROG_PKG_CONFIG([0.20])
|
||||||
|
|
||||||
dnl checks for header files
|
dnl checks for header files
|
||||||
AC_HEADER_STDC
|
AC_HEADER_STDC
|
||||||
|
|
||||||
dnl Checks for typedefs, structures and compiler characteristics
|
dnl Checks for typedefs, structures and compiler characteristics
|
||||||
|
|
||||||
dnl checks for libraries
|
AC_ARG_ENABLE(sanitize,
|
||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
|
[AS_HELP_STRING([--enable-sanitize], [Compile with address sanitizer enabled], )],
|
||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
|
[sanitize=$enableval], [sanitize="no"])
|
||||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
|
if test x"$sanitize" = x"yes"
|
||||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
|
then
|
||||||
|
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||||
|
CXXFLAGS="$CXXFLAGS -fsanitize=address -fsanitize=undefined"
|
||||||
|
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||||
|
LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
|
||||||
|
fi
|
||||||
|
|
||||||
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
|
AC_ARG_ENABLE(werror,
|
||||||
AC_ARG_ENABLE(sysmocom-bts,
|
[AS_HELP_STRING(
|
||||||
AC_HELP_STRING([--enable-sysmocom-bts],
|
[--enable-werror],
|
||||||
[enable code for sysmocom femto-bts [default=no]]),
|
[Turn all compiler warnings into errors, with exceptions:
|
||||||
[enable_sysmocom_bts="yes"],[enable_sysmocom_bts="no"])
|
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||||
AC_MSG_RESULT([$enable_sysmocom_bts])
|
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
|
]
|
||||||
|
)],
|
||||||
|
[werror=$enableval], [werror="no"])
|
||||||
|
if test x"$werror" = x"yes"
|
||||||
|
then
|
||||||
|
WERROR_FLAGS="-Werror"
|
||||||
|
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||||
|
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||||
|
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||||
|
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(profile,
|
||||||
|
[AS_HELP_STRING([--enable-profile], [Compile with profiling support enabled], )],
|
||||||
|
[profile=$enableval], [profile="no"])
|
||||||
|
if test x"$profile" = x"yes"
|
||||||
|
then
|
||||||
|
CFLAGS="$CFLAGS -pg"
|
||||||
|
CPPFLAGS="$CPPFLAGS -pg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dnl checks for libraries
|
||||||
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0)
|
||||||
|
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.5.0)
|
||||||
|
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.5.0)
|
||||||
|
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.5.0)
|
||||||
|
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.5.0)
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether to enable direct DSP access for PDCH of sysmocom-bts])
|
||||||
|
AC_ARG_ENABLE(sysmocom-dsp,
|
||||||
|
AC_HELP_STRING([--enable-sysmocom-dsp],
|
||||||
|
[enable code for direct sysmocom DSP access[default=no]]),
|
||||||
|
[enable_sysmocom_dsp="$enableval"], [enable_sysmocom_dsp="unset"])
|
||||||
|
AC_ARG_WITH([sysmobts],
|
||||||
|
[AS_HELP_STRING([--with-sysmobts=INCLUDE_DIR],
|
||||||
|
[Location of the sysmobts API header files, implies --enable-sysmocom-dsp])],
|
||||||
|
[sysmobts_incdir="$withval"], [sysmobts_incdir=""])
|
||||||
|
if test "x$sysmobts_incdir" != "x"; then
|
||||||
|
# --with-sysmobts was passed, imply enable_sysmocom_dsp
|
||||||
|
if test "x$enable_sysmocom_dsp" = "xno"; then
|
||||||
|
AC_MSG_RESULT([error])
|
||||||
|
AC_MSG_ERROR([--with-sysmobts does not work with --disable-sysmocom-dsp])
|
||||||
|
fi
|
||||||
|
enable_sysmocom_dsp="yes"
|
||||||
|
# 'readlink' should make an absolute path, but must not return empty if the path does not exist,
|
||||||
|
# so we can still report on it below.
|
||||||
|
sysmobts_incdir="$(readlink -fm "$sysmobts_incdir")"
|
||||||
|
AC_SUBST([SYSMOBTS_INCDIR], $sysmobts_incdir)
|
||||||
|
AC_MSG_RESULT([yes, using -I$SYSMOBTS_INCDIR])
|
||||||
|
else
|
||||||
|
AC_SUBST([SYSMOBTS_INCDIR], "")
|
||||||
|
AC_MSG_RESULT([$enable_sysmocom_dsp])
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
|
||||||
|
if test "x$enable_sysmocom_dsp" = "xyes"; then
|
||||||
|
oldCPPFLAGS="$CPPFLAGS"
|
||||||
|
_sysmobts_include=""
|
||||||
|
_sysmobts_include_msg=""
|
||||||
|
if test -n "$SYSMOBTS_INCDIR"; then
|
||||||
|
_sysmobts_include="-I$SYSMOBTS_INCDIR"
|
||||||
|
_sysmobts_include_msg=" in -I$SYSMOBTS_INCDIR"
|
||||||
|
fi
|
||||||
|
CPPFLAGS="$CPPFLAGS $_sysmobts_include -I$srcdir/include $LIBOSMOCORE_CFLAGS"
|
||||||
|
AC_CHECK_HEADER([sysmocom/femtobts/superfemto.h],[],
|
||||||
|
[AC_MSG_ERROR([sysmocom/femtobts/superfemto.h can not be found$_sysmobts_include_msg, see --with-sysmobts])],
|
||||||
|
[#include <sysmocom/femtobts/superfemto.h>])
|
||||||
|
CPPFLAGS="$oldCPPFLAGS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless Litecell 1.5 BTS])
|
||||||
|
AC_ARG_ENABLE(lc15bts-phy,
|
||||||
|
AC_HELP_STRING([--enable-lc15bts-phy],
|
||||||
|
[enable code for Litecell 1.5 PHY [default=no]]),
|
||||||
|
[enable_lc15bts_phy="$enableval"],[enable_lc15bts_phy="no"])
|
||||||
|
AC_ARG_WITH([litecell15], [AS_HELP_STRING([--with-litecell15=INCLUDE_DIR], [Location of the litecell 1.5 API header files])],
|
||||||
|
[litecell15_cflags="-I$withval"],[litecell15_cflags=""])
|
||||||
|
AC_SUBST([LITECELL15_CFLAGS], $litecell15_cflags)
|
||||||
|
AC_MSG_RESULT([$enable_lc15bts_phy])
|
||||||
|
AM_CONDITIONAL(ENABLE_LC15BTS_PHY, test "x$enable_lc15bts_phy" = "xyes")
|
||||||
|
if test "$enable_litecell15" = "yes"; then
|
||||||
|
oldCPPFLAGS="$CPPFLAGS"
|
||||||
|
CPPFLAGS="$CPPFLAGS $LITECELL15_CFLAGS -I$srcdir/include $LIBOSMOCORE_CFLAGS"
|
||||||
|
AC_CHECK_HEADER([nrw/litecell15/litecell15.h],[],
|
||||||
|
[AC_MSG_ERROR([nrw/litecell15/litecell15.h can not be found using $litecell15_cflags])],
|
||||||
|
[#include <nrw/litecell15/litecell15.h>])
|
||||||
|
CPPFLAGS="$oldCPPFLAGS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless OC-2G BTS])
|
||||||
|
AC_ARG_ENABLE(oc2gbts-phy,
|
||||||
|
AC_HELP_STRING([--enable-oc2gbts-phy],
|
||||||
|
[enable code for OC-2G PHY [default=no]]),
|
||||||
|
[enable_oc2gbts_phy="$enableval"],[enable_oc2gbts_phy="no"])
|
||||||
|
AC_ARG_WITH([oc2g], [AS_HELP_STRING([--with-oc2g=INCLUDE_DIR], [Location of the OC-2G API header files])],
|
||||||
|
[oc2g_incdir="$withval"],[oc2g_incdir="$incdir"])
|
||||||
|
AC_SUBST([OC2G_INCDIR], $oc2g_incdir)
|
||||||
|
AC_MSG_RESULT([$enable_oc2gbts_phy])
|
||||||
|
AM_CONDITIONAL(ENABLE_OC2GBTS_PHY, test "x$enable_oc2gbts_phy" = "xyes")
|
||||||
|
if test "$enable_oc2g" = "yes"; then
|
||||||
|
oldCPPFLAGS=$CPPFLAGS
|
||||||
|
CPPFLAGS="$CPPFLAGS -I$OC2G_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
|
||||||
|
AC_CHECK_HEADER([nrw/oc2g/oc2g.h],[],
|
||||||
|
[AC_MSG_ERROR([nrw/oc2g/oc2g.h can not be found in $oc2g_incdir])],
|
||||||
|
[#include <nrw/oc2g/oc2g.h>])
|
||||||
|
CPPFLAGS=$oldCPPFLAGS
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([vty_tests],
|
||||||
|
AC_HELP_STRING([--enable-vty-tests],
|
||||||
|
[Include the VTY tests in make check [default=no]]),
|
||||||
|
[enable_vty_tests="$enableval"],[enable_vty_tests="no"])
|
||||||
|
if test "x$enable_vty_tests" = "xyes" ; then
|
||||||
|
AM_PATH_PYTHON
|
||||||
|
AC_CHECK_PROG(OSMOTESTVTY_CHECK,osmotestvty.py,yes)
|
||||||
|
if test "x$OSMOTESTVTY_CHECK" != "xyes" ; then
|
||||||
|
AC_MSG_ERROR([Please install osmocom-python to run the vty tests.])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
AC_MSG_CHECKING([whether to enable VTY tests])
|
||||||
|
AC_MSG_RESULT([$enable_vty_tests])
|
||||||
|
AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
|
||||||
|
|
||||||
|
STD_DEFINES_AND_INCLUDES="-Wall"
|
||||||
|
AC_SUBST(STD_DEFINES_AND_INCLUDES)
|
||||||
|
|
||||||
|
# Generate manuals
|
||||||
|
AC_ARG_ENABLE(manuals,
|
||||||
|
[AS_HELP_STRING(
|
||||||
|
[--enable-manuals],
|
||||||
|
[Generate manual PDFs [default=no]],
|
||||||
|
)],
|
||||||
|
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
|
||||||
|
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
|
||||||
|
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
|
||||||
|
fallback])
|
||||||
|
if test x"$osmo_ac_build_manuals" = x"yes"
|
||||||
|
then
|
||||||
|
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
|
||||||
|
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
|
||||||
|
else
|
||||||
|
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
|
||||||
|
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
|
||||||
|
else
|
||||||
|
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find and run check-depends
|
||||||
|
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
|
||||||
|
if ! test -x "$CHECK_DEPENDS"; then
|
||||||
|
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
|
||||||
|
fi
|
||||||
|
if ! $CHECK_DEPENDS; then
|
||||||
|
AC_MSG_ERROR("missing dependencies for --enable-manuals")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Put in Makefile with absolute path
|
||||||
|
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
|
||||||
|
AC_SUBST([OSMO_GSM_MANUALS_DIR])
|
||||||
|
fi
|
||||||
|
|
||||||
|
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||||
|
AC_ARG_WITH([systemdsystemunitdir],
|
||||||
|
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||||
|
[with_systemdsystemunitdir=auto])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
|
||||||
|
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||||
|
|
||||||
|
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
|
||||||
|
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
|
||||||
|
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
|
||||||
|
with_systemdsystemunitdir=no],
|
||||||
|
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
|
||||||
|
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
|
||||||
|
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
|
||||||
|
|
||||||
|
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||||
|
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||||
|
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
|
||||||
|
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
|
||||||
|
|
||||||
AC_OUTPUT(
|
AC_OUTPUT(
|
||||||
|
include/Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
|
doc/Makefile
|
||||||
|
doc/examples/Makefile
|
||||||
|
tests/Makefile
|
||||||
|
doc/manuals/Makefile
|
||||||
|
contrib/Makefile
|
||||||
|
contrib/systemd/Makefile
|
||||||
|
contrib/osmo-pcu.spec
|
||||||
Makefile)
|
Makefile)
|
||||||
|
|||||||
1
contrib/Makefile.am
Normal file
1
contrib/Makefile.am
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SUBDIRS = systemd
|
||||||
107
contrib/jenkins.sh
Executable file
107
contrib/jenkins.sh
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# jenkins build helper script for osmo-pcu. This is how we build on jenkins.osmocom.org
|
||||||
|
#
|
||||||
|
# environment variables:
|
||||||
|
# * with_dsp: the DSP to configure ("sysmo", "lc15" or "none")
|
||||||
|
# * with_vty: enable VTY tests if set to "True"
|
||||||
|
# * WITH_MANUALS: build manual PDFs if set to "1"
|
||||||
|
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
|
||||||
|
#
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||||
|
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
if [ -z "$MAKE" ]; then
|
||||||
|
echo 'The $MAKE variable is not defined, cannot build'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
base="$PWD"
|
||||||
|
deps="$base/deps"
|
||||||
|
inst="$deps/install"
|
||||||
|
export deps inst
|
||||||
|
|
||||||
|
osmo-clean-workspace.sh
|
||||||
|
|
||||||
|
mkdir "$deps" || true
|
||||||
|
|
||||||
|
# Collect configure options for osmo-pcu
|
||||||
|
PCU_CONFIG=""
|
||||||
|
if [ "$with_dsp" = sysmo ]; then
|
||||||
|
PCU_CONFIG="$PCU_CONFIG --enable-werror --enable-sysmocom-dsp --with-sysmobts=$inst/include/"
|
||||||
|
|
||||||
|
# For direct sysmo DSP access, provide the SysmoBTS Layer 1 API
|
||||||
|
cd "$deps"
|
||||||
|
osmo-layer1-headers.sh sysmo
|
||||||
|
mkdir -p "$inst/include/sysmocom/femtobts"
|
||||||
|
ln -s $deps/layer1-headers/include/* "$inst/include/sysmocom/femtobts/"
|
||||||
|
cd "$base"
|
||||||
|
|
||||||
|
elif [ "$with_dsp" = lc15 ]; then
|
||||||
|
PCU_CONFIG="$PCU_CONFIG --enable-lc15bts-phy --with-litecell15=$deps/layer1-headers/inc"
|
||||||
|
cd "$deps"
|
||||||
|
osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION"
|
||||||
|
cd "$base"
|
||||||
|
|
||||||
|
elif [ "$with_dsp" = oc2g ]; then
|
||||||
|
PCU_CONFIG="$PCU_CONFIG --enable-oc2gbts-phy --with-oc2g=$deps/layer1-headers/inc"
|
||||||
|
cd "$deps"
|
||||||
|
osmo-layer1-headers.sh oc2g "$FIRMWARE_VERSION"
|
||||||
|
cd "$base"
|
||||||
|
|
||||||
|
elif [ -z "$with_dsp" -o "$with_dsp" = none ]; then
|
||||||
|
echo "Direct DSP access disabled, sanitizer enabled"
|
||||||
|
PCU_CONFIG="$PCU_CONFIG --enable-werror --enable-sanitize"
|
||||||
|
else
|
||||||
|
echo 'Invalid $with_dsp value:' $with_dsp
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$with_vty" = "True" ]; then
|
||||||
|
PCU_CONFIG="$PCU_CONFIG --enable-vty-tests"
|
||||||
|
elif [ -z "$with_vty" -o "$with_vty" = "False" ]; then
|
||||||
|
echo "VTY tests disabled"
|
||||||
|
else
|
||||||
|
echo 'Invalid $with_vty value:' $with_vty
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||||
|
|
||||||
|
# Build deps
|
||||||
|
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||||
|
|
||||||
|
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
export PATH="$inst/bin:$PATH"
|
||||||
|
|
||||||
|
if [ "$WITH_MANUALS" = "1" ]; then
|
||||||
|
PCU_CONFIG="$PCU_CONFIG --enable-manuals"
|
||||||
|
fi
|
||||||
|
|
||||||
|
set +x
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo " =============================== osmo-pcu ==============================="
|
||||||
|
echo
|
||||||
|
set -x
|
||||||
|
|
||||||
|
autoreconf --install --force
|
||||||
|
./configure $PCU_CONFIG
|
||||||
|
$MAKE $PARALLEL_MAKE
|
||||||
|
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" \
|
||||||
|
$MAKE $PARALLEL_MAKE distcheck \
|
||||||
|
|| cat-testlogs.sh
|
||||||
|
|
||||||
|
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||||
|
make -C "$base/doc/manuals" publish
|
||||||
|
fi
|
||||||
|
|
||||||
|
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||||
|
osmo-clean-workspace.sh
|
||||||
83
contrib/osmo-pcu.spec.in
Normal file
83
contrib/osmo-pcu.spec.in
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#
|
||||||
|
# spec file for package osmo-pcu
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
|
||||||
|
#
|
||||||
|
# All modifications and additions to the file contributed by third parties
|
||||||
|
# remain the property of their copyright owners, unless otherwise agreed
|
||||||
|
# upon. The license for this file, and modifications and additions to the
|
||||||
|
# file, is the same license as for the pristine package itself (unless the
|
||||||
|
# license for the pristine package is not an Open Source License, in which
|
||||||
|
# case the license is the MIT License). An "Open Source License" is a
|
||||||
|
# license that conforms to the Open Source Definition (Version 1.9)
|
||||||
|
# published by the Open Source Initiative.
|
||||||
|
|
||||||
|
Name: osmo-pcu
|
||||||
|
Version: @VERSION@
|
||||||
|
Release: 0
|
||||||
|
Summary: Osmocom GPRS Packet Control Unit (PCU)
|
||||||
|
License: GPL-2.0-only
|
||||||
|
Group: Productivity/Telephony/Servers
|
||||||
|
URL: https://osmocom.org/projects/osmopcu
|
||||||
|
Source: %{name}-%{version}.tar.xz
|
||||||
|
BuildRequires: autoconf
|
||||||
|
BuildRequires: automake
|
||||||
|
BuildRequires: gcc-c++
|
||||||
|
BuildRequires: libtool
|
||||||
|
BuildRequires: pkgconfig >= 0.20
|
||||||
|
%if 0%{?suse_version}
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
%endif
|
||||||
|
BuildRequires: pkgconfig(libosmocore) >= 1.5.0
|
||||||
|
BuildRequires: pkgconfig(libosmogb) >= 1.5.0
|
||||||
|
BuildRequires: pkgconfig(libosmogsm) >= 1.5.0
|
||||||
|
BuildRequires: pkgconfig(libosmovty) >= 1.5.0
|
||||||
|
BuildRequires: pkgconfig(libosmoctrl) >= 1.5.0
|
||||||
|
%{?systemd_requires}
|
||||||
|
|
||||||
|
%description
|
||||||
|
Osmocom PCU code (RLC/MAC/PCU) for OpenBTS and OsmoBTS.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q
|
||||||
|
|
||||||
|
%build
|
||||||
|
echo "%{version}" >.tarball-version
|
||||||
|
autoreconf -fi
|
||||||
|
%configure \
|
||||||
|
--enable-shared \
|
||||||
|
--disable-static \
|
||||||
|
--docdir=%{_docdir}/%{name} \
|
||||||
|
--with-systemdsystemunitdir=%{_unitdir}
|
||||||
|
make %{?_smp_mflags}
|
||||||
|
|
||||||
|
%install
|
||||||
|
%make_install
|
||||||
|
|
||||||
|
%if 0%{?suse_version}
|
||||||
|
%preun
|
||||||
|
%service_del_preun %{name}.service
|
||||||
|
|
||||||
|
%postun
|
||||||
|
%service_del_postun %{name}.service
|
||||||
|
|
||||||
|
%pre
|
||||||
|
%service_add_pre %{name}.service
|
||||||
|
|
||||||
|
%post
|
||||||
|
%service_add_post %{name}.service
|
||||||
|
%endif
|
||||||
|
|
||||||
|
%check
|
||||||
|
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license COPYING
|
||||||
|
%doc README.md
|
||||||
|
%doc %{_docdir}/%{name}/examples
|
||||||
|
%{_bindir}/osmo-pcu
|
||||||
|
%dir %{_sysconfdir}/osmocom
|
||||||
|
%config(noreplace) %{_sysconfdir}/osmocom/osmo-pcu.cfg
|
||||||
|
%{_unitdir}/%{name}.service
|
||||||
|
|
||||||
|
%changelog
|
||||||
6
contrib/systemd/Makefile.am
Normal file
6
contrib/systemd/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
EXTRA_DIST = osmo-pcu.service
|
||||||
|
|
||||||
|
if HAVE_SYSTEMD
|
||||||
|
systemdsystemunit_DATA = \
|
||||||
|
osmo-pcu.service
|
||||||
|
endif
|
||||||
16
contrib/systemd/osmo-pcu.service
Normal file
16
contrib/systemd/osmo-pcu.service
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Osmocom osmo-pcu
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
RestartPreventExitStatus=1
|
||||||
|
|
||||||
|
# The msg queues must be read fast enough
|
||||||
|
CPUSchedulingPolicy=rr
|
||||||
|
CPUSchedulingPriority=1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
882
debian/changelog
vendored
Normal file
882
debian/changelog
vendored
Normal file
@@ -0,0 +1,882 @@
|
|||||||
|
osmo-pcu (0.9.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* Pass paging group instead of imsi where later is not needed
|
||||||
|
* Allow Gb PAGING-PS without P-TMSI
|
||||||
|
* Support Gb PAGING-CS
|
||||||
|
* Support PAGING-CS and PAGING-PS on on PTP-BVCI
|
||||||
|
* tests/rlcmac: print test name at the start
|
||||||
|
* tests/rlcmac: Memzero decoded struct
|
||||||
|
* tests/rlcmac: Fix missing commas with unexpected results
|
||||||
|
* tests/rlcmac: Use osmo_hexdump to print buffers
|
||||||
|
* tests/rlcmac: Don't check stderr output
|
||||||
|
* tests/rlcmac: Add test to showcase that decode_gsm_ra_cap() fails
|
||||||
|
* csn1: Extend CSN_SERIALIZE to allow 0 bit of length
|
||||||
|
* csn1: Allow CHOICE elements to re-process the bits used for the choice
|
||||||
|
* csn1: Fix pedantic compiler warnings in csn.1 dissectors
|
||||||
|
* csn1: Drop format_p union from CSN_DESCR
|
||||||
|
* gsm_rlcmac.cpp: Fix trailing whitespace
|
||||||
|
* cosmetic: csn1.cpp: Fix whitespace
|
||||||
|
* csn1.cpp: Rework ProcessError() function to print errors
|
||||||
|
* rlcmac: Return error code from csn1 encoder/decoder
|
||||||
|
* Check return code of rlcmac decode/encode functions
|
||||||
|
* rlcmac: Transform a few LOGPC messages to LOGP
|
||||||
|
* Fix trailing newline mess with LOGP(C) in rlcmac/csn1
|
||||||
|
* llc_queue::{dequeue,enqueue}() refactor
|
||||||
|
* gsm_rlcmac: fix Packet_Resource_Request_t: s/Slot/I_LEVEL_TN/
|
||||||
|
* tests/llc: Change unrealistic time jump to avoid runtime error under ARM
|
||||||
|
* Use clock_gettime(CLOCK_MONOTONIC) and timespec everywhere
|
||||||
|
* Use downlink BSSGP RA Cap IE
|
||||||
|
* tests/RLCMACTest: free allocated bitvectors
|
||||||
|
* tests/RLCMACTest: Several fixes and improvements to RAcap tests
|
||||||
|
* rlcmac: Don't pass array element to CSN1 descriptors
|
||||||
|
* csn1: Validate recursive array max size during decoding
|
||||||
|
* rlcmac: Fix bug receiving RA cap
|
||||||
|
* rlcmac: Log names of de/encoded rlcmac packet types
|
||||||
|
* rlcmac: Introduce MS Radio Access Capabilities 2 to fix related spare bits
|
||||||
|
* cosmetic: rlcmac: Fix comment typo and whitespace introduced recently
|
||||||
|
* rlcmac: Rename field to MS RA Cap2 in Additional_MS_Rad_Access_Cap_t
|
||||||
|
* pcu_l1_if.cpp: Add missing header ctype.h
|
||||||
|
* gsm_rlcmac: Use 'struct bitvec' instead of 'bitvec'
|
||||||
|
* cosmetic: Do not indent header includes inside extern C block
|
||||||
|
* gsm_rlcmac.cpp: Avoid declaring variable in for loop
|
||||||
|
* csn1.h: Fix trailing whitespace
|
||||||
|
* tbf.cpp: Include c++ <new> header required for new operator's replacement type
|
||||||
|
* gsm_rlcmac: Disable unused CSN1 descriptors
|
||||||
|
* Move gsm_rlcmac.cpp -> .c
|
||||||
|
* rlcmac: support decode FDD_CELL_INFORMATION of "UTRAN FDD Description
|
||||||
|
* rlcmac: add dissection of 2G->3G/4G PS handover
|
||||||
|
* csn1: Fix Several typos & whitespace
|
||||||
|
* csn1: verify enough bits present to decode whole CSN_UINT_ARRAY
|
||||||
|
* csn1: Properly verify CSN_BITMAP length
|
||||||
|
* csn1: Remove code block from CSN_NEXT_EXIST_LH
|
||||||
|
* pcu_l1_if: Don't use GSMTAP_CHANNEL_PACCH when sending unknown gsmtap blocks
|
||||||
|
* pdch: Avoid sending GSMTAP_CHANNEL_UNKOWN for rejected UL EGPRS data block
|
||||||
|
* tbf: Avoid crash: don't set TBF window size if setup failed
|
||||||
|
* bts: Rename mslot_class_from_ra
|
||||||
|
* bts: Fix Decoding EGPRS MultislotClass from 11-bit EGPRS PACKET CHANNEL REQUEST
|
||||||
|
* bts: Return uint8_t in egprs_mslot_class_from_ra()
|
||||||
|
* Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||||
|
* Expect ms object to exist before calling tbf_alloc_ul_tbf()
|
||||||
|
* Expect ms object to exist before calling tbf_alloc_dl_tbf()
|
||||||
|
* pdch: rcv_resource_request(): Clarify tbf_free only needed if MS used to exist beforehand
|
||||||
|
* Drop unneeded arg 'ta' in tbf_alloc_ul()
|
||||||
|
* bts: Drop specific functions to increase counters
|
||||||
|
* bts: Drop specific functions to add values to counters
|
||||||
|
* bts: Drop specific functions to add values to stats
|
||||||
|
* pcu: tbf_ul: Clean up maybe_schedule_uplink_acknack()
|
||||||
|
* sysmo: femtobts.h: Avoid redefining global variables
|
||||||
|
* rlc: Drop unused function gprs_rlc_data::put_data
|
||||||
|
* rlc: Move prepare() function out of gprs_rlc_data struct
|
||||||
|
* tbf_ul: Fix UL ACK not sent to MS if intermediate UL block is lost
|
||||||
|
* Get rid of class GprsCodingScheme
|
||||||
|
* gsmtap: Set signal level and SNR fields
|
||||||
|
* gprs_ms: Small clean ups in IMSI storage related code
|
||||||
|
* gprs_ms: Transfer known EGPRS MS class when mergling old MS
|
||||||
|
* tbf: Drop unneeded method set_tlli_from_ul
|
||||||
|
* pdch.cpp: Fix logging line format in rcv_block wrong length
|
||||||
|
* Set correct GSMTAP channel type for PDTCH messages returning error
|
||||||
|
* decoding.cpp: Improve logging in malformed UL data parsing
|
||||||
|
* tbf_dl: uint8_t is enough to store a TA value
|
||||||
|
* encoding: Encode TA as unsigned and check validty against GSM48_TA_INVALID
|
||||||
|
* encoding.cpp: Fix missing spacing in function param
|
||||||
|
* pdch.cpp: Avoid dropping existing DL TBF during rcv_resource_request
|
||||||
|
* pdch.cpp: Avoid resetting (egprs_)ms_class to unknown if not found in MS RadioAccCap
|
||||||
|
* pdch.cpp: Fix wrong annoying log line about non-scheduled ResourceReq received
|
||||||
|
* pdch.cpp: Store TLLI promptly on newly created TLLI in rcv_resource_request
|
||||||
|
* Fix typo in log message
|
||||||
|
* pdch: Drop unneeded notice log message in rcv pkt meas report
|
||||||
|
* Introduce log macro helper LOGPMS
|
||||||
|
* configure.ac: Fix trailing whitespace
|
||||||
|
* doc: Update VTY reference xml file
|
||||||
|
* Support setting rt-prio and cpu-affinity mask through VTY
|
||||||
|
* pdch: rcv pkt meas rep: Allocate MS object early in path and use it
|
||||||
|
* Fix recent typo preventing MS from registering
|
||||||
|
* gitignore: Add __pychache__ dir
|
||||||
|
* tbf: Don't log rlcmac_diag() output in separate lines
|
||||||
|
* gprs_ms_storage.h: Set pointer to NULL instead of 0
|
||||||
|
* Free all MS TBFs when receiving GPRS Suspension Request
|
||||||
|
* cosmetic: fix indentation alignment
|
||||||
|
* vty: Add 'show bts pdch' command
|
||||||
|
* cosmetic: Fix indentation of for loops
|
||||||
|
* cosmetic: Fix typo in comment
|
||||||
|
* Fix crash accessing NULL tbf->pdch[first_ts]
|
||||||
|
* contrib/jenkins: Enable parallel make in make distcheck
|
||||||
|
* Improve debug logging for alloc algos
|
||||||
|
* Fix several calls to LOGPAL
|
||||||
|
* Move gprs_rlcmac_ul_tbf::window to correct file
|
||||||
|
* Move constructor gprs_rlcmac_dl_tbf::BandWidth to correct file
|
||||||
|
* tbf: Make window() available to tbf base class
|
||||||
|
* tbf: Implement enable_egprs() once
|
||||||
|
* tbf: Set MS during constructor time
|
||||||
|
* Move ul_tbf allocation code to correct file
|
||||||
|
* Move dl_tbf allocation code to correct file
|
||||||
|
* tbf: Drop unused function disable_egprs()
|
||||||
|
* tests: ms: Pass correct pointer in constructor instead of NULL
|
||||||
|
* tbf: Clean up gprs_rlcmac_dl_tbf::handle()
|
||||||
|
* alloc_algo_b: Select TRX with least assigned TFIs during TBF alloc
|
||||||
|
* bts: define egprs_enabled as bool
|
||||||
|
* cosmetic: Fix ws between if keyword and parenthesis
|
||||||
|
* tbf_dl: Update (egprs_)ms_class for already known MS
|
||||||
|
* cosmetic: tests: pcu_emu: fix trailing whitespace
|
||||||
|
* gprs_ms: Use proper function to get CS
|
||||||
|
* Move BTS initial values inside bts.cpp
|
||||||
|
* pcuif: Improve BTS-supported CS/MCS handling
|
||||||
|
* Move EGPRS MS mode set to gprs_ms.cpp
|
||||||
|
* Take into account BTS supported (M)CS values when retrieving the maximum
|
||||||
|
* Enable egprs support through PCUIF from BTS/BSC
|
||||||
|
* pdch: Process received CS1-4 data blocks regardless of egprs_enabled
|
||||||
|
* tbf_dl: Don't fake EGPRS MS class when no related info is available
|
||||||
|
* tbf_ul: Allow non-egprs phones if EGPRS is enabled
|
||||||
|
* Get rid of bts->egprs_enabled
|
||||||
|
* Fix configuration of initial_(m)cs
|
||||||
|
* Fix mcs_is_valid(): UNKNOWN value is not a valid (M)CS
|
||||||
|
* gprs_ms: Avoid enabling EGPRS if no MCS are supported
|
||||||
|
* tbf_ul: Log mismatching TLLI on log message
|
||||||
|
* Fix ctr reports: Remove ctr description from already removed counter
|
||||||
|
* encoding: Fix duplicate word in log str
|
||||||
|
* sched: Fix sending GSMTAP DL data blocks with unset USF
|
||||||
|
* sched: Use correct GMSTAP category for EGPRS DL data blocks
|
||||||
|
* Support multiplexing of GPRS and EGPRS TBFs in one PDCH
|
||||||
|
* pdch: packet_paging_request: Put back non-fitting paging entry where where it was
|
||||||
|
* pdch: Log hexdump of decde failure for dl rlcmac block
|
||||||
|
* csn1: Fix readIndex pointer change in CSN_VARIABLE_ARRAY
|
||||||
|
* csn1: Log CSN_VARIABLE_ARRAY values as hex
|
||||||
|
* main: generate coredump and exit upon SIGABRT received
|
||||||
|
* tbf: Log previous TS when changing Control TS
|
||||||
|
* Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
|
||||||
|
* Dl TBF: Get rid of LLC UI dummy blocks following other data
|
||||||
|
* rlcmac: Fix typo in MT_PACKET_CELL_CHANGE_NOTIFICATION value_string
|
||||||
|
* gprs_rlcmac_sched: Use helper structure to store several tbf pointer params
|
||||||
|
* sched: Convert code handling next_list array to be size independant
|
||||||
|
* Convert GprsMS and helpers classes to C
|
||||||
|
* tbf: Fix wrong verb used in log message
|
||||||
|
* .gitignore: ignore files ending with ~
|
||||||
|
* doc: Improve CS/MCS GPRS/EGPRS considerations in User Manual
|
||||||
|
* tbf: remove 'software error' logs from tbf_free
|
||||||
|
* ms: Replace struct var with rate_ctr
|
||||||
|
* AllocTest: Avoid queuing tons of to-be-freed ms
|
||||||
|
* gprs_ms: Mark ms_ctrg_desc static
|
||||||
|
* Workaround ASan false positive runtime errors under some platforms
|
||||||
|
* Split PCU global PCU object from BTS object
|
||||||
|
* Move T_defs_pcu from BTS to PCU object
|
||||||
|
* Move force_two_phase field from BTS to PCU
|
||||||
|
* Move alpha,gamma fields from BTS to PCU
|
||||||
|
* Move dl_tbf_preemptive_retransmission field from BTS to PCU
|
||||||
|
* Move dl_arq_type field from BTS to PCU
|
||||||
|
* Move cs_adj* fields from BTS to PCU
|
||||||
|
* Move cs_downgrade_threshold field from BTS to PCU
|
||||||
|
* Move (m)cs_lqual_ranges fields from BTS to PCU
|
||||||
|
* Move ns_dialect field from BTS to PCU
|
||||||
|
* Move fc_* fields from BTS to PCU
|
||||||
|
* tests/tbf: Allocate PCU per test instead of globally
|
||||||
|
* Move ws_* fields from BTS to PCU
|
||||||
|
* Move llc_* fields from BTS to PCU
|
||||||
|
* Fix configuration mess of initial_cs/mcs between PCUIF and VTY
|
||||||
|
* Unify BTS into a C usable structure
|
||||||
|
* Get rid of bts singletons
|
||||||
|
* Rename 'bts_data' leftovers to 'bts'
|
||||||
|
* bts: combine bts_{init,cleanup} into consturctor/destructor methods
|
||||||
|
* Get rid of unused gsm_timer.{cpp,h}
|
||||||
|
* Convert gprs_bssgp_pcu.cpp to C
|
||||||
|
* Move tbf::free_all static methods to proper object files
|
||||||
|
* Convert osmo_bts_sock.cpp to C
|
||||||
|
* Allow multiple bts objects in PCU
|
||||||
|
* bts: Store RAC+CI from info_ind
|
||||||
|
* Get rid of singleton gprs_bssgp_pcu_current_bctx()
|
||||||
|
* Initial handling support for RIM messages
|
||||||
|
* gprs_pcu: Use libosmocore osmo_cgi_ps_cmp API
|
||||||
|
* ms: Drop always-false check
|
||||||
|
* sched: Check if egprs is enabled in TBF rather than MS being egprs capable
|
||||||
|
* tbf: Drop always-true condition checking for MS
|
||||||
|
* encoding: fix typos in comment
|
||||||
|
* ms: Set proper initial MCS values setting mode EGPRS_GMSK
|
||||||
|
* ms: Properly handle EGPRS_GMSK mode in ms_max_cs_dl/ul()
|
||||||
|
* Fix Dl EGPRS data blocks being generated occasionally on GPRS TBFs
|
||||||
|
* sched: Avoid picking TBF with nacked dl blocks when GMSK is required
|
||||||
|
* tbf: Make tbf_ms() param const
|
||||||
|
* Introduce NACC support
|
||||||
|
* NACC: Fix crash freeing struct if CTRL conn was refused during alloc
|
||||||
|
* NACC: delay CTRL conn socket init until it's needed
|
||||||
|
* NACC: allow setting keep time for entries in neigh and si cache
|
||||||
|
* NACC: Configure neighbor and SI resolution timeout values
|
||||||
|
* NACC: Send only Pkt Cell Chg Continue if SI retrieve fails
|
||||||
|
* doc: Mark PCU node red in network node diagram
|
||||||
|
* doc: Introduce section documenting NACC support
|
||||||
|
* nacc: Improve log line failing to establish CTRL neigh conn
|
||||||
|
* Update TS 04.60 references to new TS 44.060
|
||||||
|
* Drop comment about an already implemented TODO
|
||||||
|
* Move src/tbf.txt to doc/
|
||||||
|
* encoding: Fix comment description of S/P field
|
||||||
|
* tbf: Reuse stored result in variable in check_polling()
|
||||||
|
* tbf: Constify some methods
|
||||||
|
* nacc: Fix typo in function name
|
||||||
|
* nacc: Implement Pkt Cell Change Continue retransmission
|
||||||
|
* nacc: Avoid RIM procedures targeting cells under same PCU
|
||||||
|
* rlc.h: Fix struct bit fields on big endian systems
|
||||||
|
* cosmetic: fix typo in comment
|
||||||
|
* nacc_fsm: Move code filling struct to helper function
|
||||||
|
* nacc_fsm: Remove NACC_EV_RX_SI from in_event_mask of some states
|
||||||
|
* nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_RESOLVE_RAC_CI
|
||||||
|
* nacc_fsm: nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_REQUEST_SI
|
||||||
|
* nacc_fsm: Support receiving Pkt Cell Chg Notif while in some advanced states
|
||||||
|
* nacc_fsm: Improve log when sending RIM RAN-INFO to gather SI from remote cell
|
||||||
|
* vty: Write 'neighbor resolution' config to file
|
||||||
|
* cosmetic: fix line indentation
|
||||||
|
* sched: Avoid selecting TBF to tx NACC Dl msg if no TFI is assigned
|
||||||
|
* tests: Explicitly drop category from log
|
||||||
|
* tests: Replace deprecated API log_set_print_filename
|
||||||
|
* Use NULL as default value for pointer type
|
||||||
|
* find_multi_slots: Avoid calling mslot_class_get_tx() on each iteration
|
||||||
|
* find_multi_slots: Avoid multiple calls to mslot_class_get_rx()
|
||||||
|
* find_multi_slots: Mark mslot_class properties const
|
||||||
|
* find_multi_slots: Avoid multiple calls to mslot_class_get_type()
|
||||||
|
* Use ALPHA value received in SI13 from PCUIF
|
||||||
|
|
||||||
|
[ Vadim Yanitskiy ]
|
||||||
|
* pcu_l1_if.cpp: fix NULL-pointer dereference in imsi2paging_group()
|
||||||
|
* gsm_timer: fix comparison of constant LONG_MAX with an integer
|
||||||
|
* encoding: fix log_alert_exit(): do not treat error as format string
|
||||||
|
* tests/alloc: fix implicit conversion from 'double' to 'int8_t'
|
||||||
|
* gprs_bssgp_pcu: fix invalid use of non-static data member 'frame'
|
||||||
|
* gprs_bssgp_pcu: fixup: fix length check in gprs_bssgp_pcu_rx_dl_ud()
|
||||||
|
* csn1: fix csnStreamDecoder(): avoid conditional calls to bitvec_read_field()
|
||||||
|
* VTY: get rid of pcu_vty_go_parent() / pcu_vty_is_config_node()
|
||||||
|
* VTY: install talloc context introspection commands
|
||||||
|
* pcu_sock: fix memleak, allocate pcu_sock_state on stack
|
||||||
|
* pcu_sock: cosmetic: fix typo in a comment message
|
||||||
|
* tbf: cosmetic: fix spacing in gprs_rlcmac_tbf::create_ul_ass()
|
||||||
|
* tbf: fix NULL pointer dereference in create_[ul|dl]_ass()
|
||||||
|
* encoding: assert return value of bitvec_set_u64()
|
||||||
|
* csn1: fix some mistaken CSN.1 error names
|
||||||
|
* csn1: fix csnStreamDecoder(): catch unknown CSN_CHOICE values
|
||||||
|
* tests/rlcmac: mark Packet Polling Request as malformed
|
||||||
|
* csn1: fix existNextElement(): use bitvec_get_bit_pos()
|
||||||
|
* tests/rlcmac: additionally match debug output of the CSN.1 codec
|
||||||
|
* csn1: get rid of C++ specific code, compile with GCC
|
||||||
|
* csn1: fix csnStreamDecoder(): do not subtract no_of_bits twice
|
||||||
|
* csn1: fix csnStreamDecoder(): always keep remaining_bits_len updated
|
||||||
|
* csn1: fix csnStreamDecoder(): update bit_offset in CSN_EXIST{_LH}
|
||||||
|
* csn1: bitvec_get_uint() may return a negative, use %d
|
||||||
|
* csn1: use proper format specifier for unsigned integers
|
||||||
|
* gsm_rlcmac: fix misleading LOGP statement in decode_gsm_ra_cap()
|
||||||
|
* tests/rlcmac: fix malformed MS RA capability in testRAcap()
|
||||||
|
* tests/rlcmac: also verify encoding of MS RA Capability
|
||||||
|
* tests/rlcmac: add a new test vector for Packet Resource Request
|
||||||
|
* csn1: fix csnStreamDecoder(): skip bits unhandled by serialize()
|
||||||
|
* tests/rlcmac: also enable logging for DRLCMACDATA category
|
||||||
|
* rlcmac: fix encode_gsm_*(): do not suppress encoding errors
|
||||||
|
* csn1: fix: do not return 0 if no bits left in the buffer
|
||||||
|
* BSSGP: cosmetic use OSMO_IMSI_BUF_SIZE from libosmocore
|
||||||
|
* BSSGP: fix: properly encode P-TMSI in RR Paging Request
|
||||||
|
* pdch: fix packet_paging_request(): properly print paging MI
|
||||||
|
* pdch: cosmetic: use GSM_MI_TYPE_* constants from libosmocore
|
||||||
|
* fix: properly include pure C headers from C++ code
|
||||||
|
* l1if: fix pcu_rx_rach_ind(): use proper format string specifiers
|
||||||
|
* sba: fix possible memleak in SBAController::alloc()
|
||||||
|
* TBF/UL: fix rcv_data_block_acknowledged(): print the actual TLLI
|
||||||
|
* fix egprs_mslot_class_from_ra(): multislot class may not be present
|
||||||
|
* l1if: fix: s/pcu_rx_rach_ind_pdtch/pcu_rx_rach_ind_ptcch/g
|
||||||
|
* csn1: fix M_CHOICE: restirct maximum length of the choice list
|
||||||
|
* csn1: fix csnStreamEncoder(): also check length of the choice list
|
||||||
|
* csn1: fix csnStreamEncoder(): always check the choice index
|
||||||
|
* csn1: fix: never use enumerated types in codec structures
|
||||||
|
* encoding: cosmetic: use RLC_MODE_ACKNOWLEDGED where possible
|
||||||
|
* RLC/MAC: implement decoding of EGPRS Packet Channel Request
|
||||||
|
* encoding: fix write_ia_rest_egprs_uplink_sba(): add missing CHECK(rc)
|
||||||
|
* doc/manuals: fix typo in overview.adoc: s/Omsocom/Osmocom/g
|
||||||
|
* bts: refactor handling and parsing of RACH.ind
|
||||||
|
* BTS::parse_rach_ind(): properly handle EGPRS Packet Channel Request
|
||||||
|
* bts: add send_gsmtap_rach(), also send PTCCH/U over GSMTAP
|
||||||
|
* bts: fix send_gsmtap_rach(): properly pack 11 bit RA
|
||||||
|
* bts: cosmetic: use DUMMY_VEC for padding where possible
|
||||||
|
* encoding: drop log_alert_exit(), use OSMO_ASSERT() instead
|
||||||
|
* encoding: assert() presence of Downlink TBF
|
||||||
|
* direct-phy: fix handle_ph_ra_ind(): handle PH-RA.ind on PRACH SAPI
|
||||||
|
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||||
|
* pcu_l1_if: use proper format specifier for PCUIF version
|
||||||
|
* pcu_l1_if: constify the argument of pcu_rx_info_ind()
|
||||||
|
* pcu_l1_if: cosmetic: rename both 'trx'/'ts' to 'trx_nr'/'ts_nr'
|
||||||
|
* pcu_l1_if: cosmetic: move struct 'gprs_rlcmac_pdch' into the for loop
|
||||||
|
* pcu_l1_if: cosmetic: correct error message in pcu_rx_info_ind()
|
||||||
|
* gsm_rlcmac: use consistent naming for [Extended] Packet Timing Advance
|
||||||
|
* tbf: cosmetic: use GSM_MACBLOCK_LEN where possible
|
||||||
|
* tbf: allocate the bitvec on stack in create_{dl,ul}_ass()
|
||||||
|
* encoding: constify 'tbf' in UL/DL assignment functions
|
||||||
|
* encoding: do not encode out of range Timing Advance values
|
||||||
|
* encoding: fix RRBP field in write_packet_uplink_assignment()
|
||||||
|
* encoding: use bool for use_egprs in write_packet_uplink_assignment()
|
||||||
|
* encoding: pass pdch slot directly to encoding functions
|
||||||
|
* encoding: clarify docstring for write_packet_downlink_assignment()
|
||||||
|
* encoding: use CSN.1 codec to generate Packet Uplink Assignment
|
||||||
|
* encoding: implement handing of hopping parameters
|
||||||
|
* encoding: fix gen_freq_params(): do not check pdch twice
|
||||||
|
* pcuif_proto: version 10: add frequency hopping parameters
|
||||||
|
* pcu_l1_if: cosmetic: use ARRAY_SIZE() in pcu_rx_info_ind()
|
||||||
|
* pcu_l1_if: correct logging level in pcu_rx_info_ind()
|
||||||
|
* pcu_l1_if: cosmetic: make {local,remote}_sockaddr scoped variables
|
||||||
|
* pcu_l1_if: use proper format string specifiers: %d -> %u
|
||||||
|
* pcu_l1_if: print NSVC address in more common format
|
||||||
|
* gprs_bssgp_pcu: make osmo_sockaddr local/sgsn arguments const
|
||||||
|
* gprs_bssgp_pcu: fix possible memleak in gprs_nsvc_create_and_connect()
|
||||||
|
* struct gprs_rlcmac_bts: remove unused 'nsei' field
|
||||||
|
* gprs_bssgp_pcu: fix: do not crash on receipt of subsequent INFO.ind
|
||||||
|
* doc/manuals: (re-)generate XML VTY reference automatically
|
||||||
|
* fix tbf_select_slot_set(): use LOGP() instead of LOGPC()
|
||||||
|
* main: remove line breaks in print_help(), increase spacing
|
||||||
|
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||||
|
* BSSGP: use tlvp_val8() in gprs_bssgp_pcu_rx_paging_cs()
|
||||||
|
* BSSGP: constify argument 'tp' of gprs_bssgp_pcu_rx_paging_{cs,ps}
|
||||||
|
* TLLI 0x00000000 is a valid TLLI, use 0xffffffff instead
|
||||||
|
* gprs_rlcmac_sched: fix incorrect SBA frame number assignment
|
||||||
|
* bts: fix uninitialized memaccess in BTS::send_gsmtap_rach()
|
||||||
|
* bts: fix uninitialized memaccess in BTS::send_gsmtap()
|
||||||
|
* tests/rlcmac: add more test vectors for Packet Resource Request
|
||||||
|
* contrib/osmo-pcu.spec.in: require libosmo* version 1.4.0
|
||||||
|
* contrib/osmo-pcu.spec.in: add missing libosmoctrl dependency
|
||||||
|
* vty: register libosmocore's FSM introspection commands
|
||||||
|
|
||||||
|
[ Anders Broman ]
|
||||||
|
* csn1: Update M_NULL CSN_DESCR to match wireshark
|
||||||
|
* csn1: packet-csn1.c:179: warning: 'pui8' may be used uninitialized in this function
|
||||||
|
* csn1: Fix warning with -Wmissing-prototypes
|
||||||
|
* csn1: Try to fix cast discards '__attribute__((const))' qualifier from pointer target type
|
||||||
|
* gsm_rlcmac.cpp: hanged all M_BIT macros to M_UINT, as M_BIT does not use the referenced hf.
|
||||||
|
|
||||||
|
[ Jeff Morriss ]
|
||||||
|
* csn1: shuffle decrements of remaining_bits_len
|
||||||
|
|
||||||
|
[ Pascal Quantin ]
|
||||||
|
* csn1: Fix an infinite loop in CSN.1 dissector when having more than 255 padding bits
|
||||||
|
* gsm_rlcmac.h: Remove Uplink messages from the RlcMacDownlink_t structure
|
||||||
|
* gsm_rlcmac: Enhance dissection of PSI1
|
||||||
|
* gsm_rlcmac.cpp: Do not skip too many lines of the CSN_DESCR when the field is missing
|
||||||
|
* gsm_rlcmac.cpp: fix an out of bounds access
|
||||||
|
* gsm_rlcmac.cpp: fix another global-buffer-overflow error reported by ASAN
|
||||||
|
* gsm_rlcmac.cpp: fix global-buffer-overflow error reported by ASAN
|
||||||
|
|
||||||
|
[ Guy Harris ]
|
||||||
|
* csn1: Don't cast away constness
|
||||||
|
|
||||||
|
[ Alexis La Goutte ]
|
||||||
|
* csn1: fix this statement may fall through [-Werror=implicit-fallthrough=] found by gcc7
|
||||||
|
|
||||||
|
[ Bill Meier ]
|
||||||
|
* gsm_rlcmac.h: #if 0 unused stuff
|
||||||
|
|
||||||
|
[ Gerald Combs ]
|
||||||
|
* gsm_rlcmac.h: Make sure we have a corresponding 'u' member to RlcMacDownlink_t for every call
|
||||||
|
|
||||||
|
[ Vincent Helfre ]
|
||||||
|
* gsm_rlcmac: add dissection of NAS container
|
||||||
|
* gsm_rlcmac: improve dissection of MS RA Capability IE
|
||||||
|
|
||||||
|
[ AndersBroman ]
|
||||||
|
* gsm_rlcmac: Update : PACKET RESOURCE REQUEST to Release 14.0.0
|
||||||
|
|
||||||
|
[ Keith ]
|
||||||
|
* Send UL-CTRL Packet to GSMTAP even if we fail to decode.
|
||||||
|
* Don't check ul_control_block before decoding into it.
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* csn1.c: Almost all of the logging is DEBUG, not NOTICE
|
||||||
|
* TODO: remove those that have obviously been implemented 5+ years ago
|
||||||
|
* bts.cpp: Increase constructor priority
|
||||||
|
* Use osmo_fd_setup() whenever applicable
|
||||||
|
* Use osmo_fd_*_{disable,enable}
|
||||||
|
* gb manual: 08.16 -> 48.016 / 08.18 -> 48.018
|
||||||
|
* gb manual: NS is implemented in libosmogb, not libosmocore
|
||||||
|
* manuals/gb/ns.adoc: Update documentation regarding SNS capability
|
||||||
|
* migrate to DLBSSGP as log sub-system for BSSGP
|
||||||
|
|
||||||
|
[ Eric ]
|
||||||
|
* configure.ac: fix libtool issue with clang and sanitizer
|
||||||
|
* tbf: add virtual destructor
|
||||||
|
|
||||||
|
[ Philipp Maier ]
|
||||||
|
* gprs_debug: Use only LOGL_NOTICE as default loglevel
|
||||||
|
* vty: add attributes to VTY commands indicating when they apply
|
||||||
|
* pcu_main: add commandline option --vty-ref-xml
|
||||||
|
* gprs_bssgp_rim: add serving BSS NACC application
|
||||||
|
|
||||||
|
[ Oliver Smith ]
|
||||||
|
* contrib: import RPM spec
|
||||||
|
* contrib: integrate RPM spec
|
||||||
|
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||||
|
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||||
|
* configure.ac: set -std=gnu11
|
||||||
|
|
||||||
|
[ Neels Hofmeyr ]
|
||||||
|
* use new osmo_mobile_identity api (avoid deprecation)
|
||||||
|
* paging: pass struct osmo_mobile_identity, not encoded IE bytes
|
||||||
|
|
||||||
|
[ Alexander Couzens ]
|
||||||
|
* pcuif_proto: version 0xa: add support for IPv6 NSVCs
|
||||||
|
* Revert "pcuif_proto: version 0xa: add support for IPv6 NSVCs"
|
||||||
|
* pcuif_proto: version 10: add support for IPv6 NSVCs
|
||||||
|
* Use the new NS2 lib
|
||||||
|
* Rework NS configuration over the info indication
|
||||||
|
* pcu_l1_if: fix misaligned assignment of remote address
|
||||||
|
* NS2: follow the change of ownership
|
||||||
|
* gprs_bssgp_pcu: follow ns2 library changes
|
||||||
|
* NS2: rework handling of unknown primitive
|
||||||
|
* ns2: follow ns2 dialect changes
|
||||||
|
* ns2: follow changes to add a unique name to all binds
|
||||||
|
* ns2: follow ns2 sns api changes
|
||||||
|
* gprs_ns2: set default dialect to ipaccess
|
||||||
|
* gprs_rlcmac_sched: don't leak a sched_dummy()
|
||||||
|
* gprs_rlc_ts_alloc: ensure no rolling slots are allocated
|
||||||
|
* follow gprs_ns2 API enum changes
|
||||||
|
* gprs_ns2: migrate to the new vty syntax
|
||||||
|
* gprs_bssgp: rework and rename ns_create_nsvc -> ns_configure_nse
|
||||||
|
* gprs_bssgp: rename gprs_ns_config -> gprs_ns_update_config
|
||||||
|
* gprs_bssgp: use gprs_ns2_sns_add_bind() to allow the NSE to use the binds for IP-SNS configuration
|
||||||
|
|
||||||
|
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 14:41:00 +0100
|
||||||
|
|
||||||
|
osmo-pcu (0.8.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Alexander Couzens ]
|
||||||
|
* tests: test encoding of egprs ul ack/nacks
|
||||||
|
* tbf_dl: add comments to the scheduler
|
||||||
|
* encoding: fix space, tabs
|
||||||
|
|
||||||
|
[ Vadim Yanitskiy ]
|
||||||
|
* osmobts_sock.cpp: pcu_sock_cb(): use libosmocore's socket API
|
||||||
|
* osmobts_sock.cpp: pcu_sock_read(): use stack buffer, not heap
|
||||||
|
* osmobts_sock.cpp: pcu_sock_read(): further simplify the code
|
||||||
|
* osmobts_sock.cpp: do not print the same debug message twice
|
||||||
|
* VTY: refactor pcu_vty_show_ms_all(): use show_ms()
|
||||||
|
* VTY: fix command 'show tbf all': properly filter TBFs
|
||||||
|
* BSSGP: do not reject SUSPEND ACK / NACK messages
|
||||||
|
* BSSGP: properly print BVCI for signalling messages (BVCI=0)
|
||||||
|
* tests/tbf: suspend warnings about the link quality measurements
|
||||||
|
* GprsMs::update_cs_ul(): clarify the meaning of old_link_qual
|
||||||
|
* gprs_bssgp_destroy(): fix memleak and NULL-pointer dereference
|
||||||
|
* PTCCH: implement basic message codec and API
|
||||||
|
* PTCCH: properly handle RTS.req for PCU_IF_SAPI_PTCCH
|
||||||
|
* pcuif_proto.h: extend RACH.ind with TRX / TS numbers
|
||||||
|
* PTCCH: properly handle RACH.ind for PCU_IF_SAPI_PTCCH
|
||||||
|
* VTY: add warning about changing PCU socket path at run-time
|
||||||
|
* VTY: cosmetic: use osmo_talloc_replace_string()
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* cosmetic: tbf: Rename T and N arrays
|
||||||
|
* Use proper API osmo_timer_setup() to set up timer struct
|
||||||
|
* Introduce osmo_tdef infra and timer VTY commands
|
||||||
|
* bts.cpp: Fix osmo_tdef initialization on older g++ compilers
|
||||||
|
* Use osmo_tdef for BSSGP T1 and T2
|
||||||
|
* Use osmo_tdef to implement T3190
|
||||||
|
* tests: TbfTest: Unify stderr and stdout to ease debugging
|
||||||
|
* Use osmo_tdef to implement ms-idle-time
|
||||||
|
* Use osmo_tdef to implement dl-tbf-idle-time
|
||||||
|
* pdch.cpp: Refactor bitvec param passing in rcv_control_block
|
||||||
|
* pdch.cpp: Use pcu_l1_meas previously filled by lower layers
|
||||||
|
* cosmetic: fix whitespace
|
||||||
|
* Move out tbf subclasses from tbf.h to their own headers
|
||||||
|
* Move tbf_{dl,ul} child constructors to respective .cpp files
|
||||||
|
* tbf_dl: Setup m_llc_timer in constructor using osmocom API
|
||||||
|
* tbf_dl.cpp: Remove dup call to tbf_update_ms_class() in state GPRS_RLCMAC_WAIT_RELEASE
|
||||||
|
* vty: Fix osmo_tdef timers not listed in write config
|
||||||
|
* Log RACH Requests using GSMTAP
|
||||||
|
* Log AGCH and PCH blocks using GSMTAP
|
||||||
|
* pcu_l1_if.cpp: Fix GSMTAP Imm Assign PCH wrong encoding
|
||||||
|
* pcu_l1_if.cpp: Drop unneeded byte in Imm Ass PCH buffer
|
||||||
|
* pcu_l1_if.cpp: Imm Assign PCH: clarify size of different items
|
||||||
|
* pcu_l1_if.cpp: Replace value 23 with libosmocore's GSM_MACBLOCK_LEN
|
||||||
|
* Fix assertion hit upon CCCH Paging Request
|
||||||
|
* doc: vty: Update osmo-pcu_vty_reference.xml
|
||||||
|
* Clarify (M)CS related VTY attributes
|
||||||
|
* Remove dash from name used in VTY cmd prompt
|
||||||
|
* tbf_dl.cpp: Fix typo in log line
|
||||||
|
* pcu_l1_if: Check pag_req id_lv len fits buffer
|
||||||
|
* prs_bssgp_pcu.cpp: Mark priv funcs as static and remove trailing whitespace
|
||||||
|
* Fix trailing whitespace
|
||||||
|
* fix typo in log message
|
||||||
|
* Log BVCI PTP value upon msg recv
|
||||||
|
* Split identity_lv param into mi+mi_len
|
||||||
|
|
||||||
|
[ Oliver Smith ]
|
||||||
|
* doc: update generated VTY reference
|
||||||
|
* tbf_dl: make preemptive retransmission optional
|
||||||
|
* Forward ETWS Primary Notification to MS
|
||||||
|
* tests/app_info: fix compiling with older g++
|
||||||
|
* configure.ac: set C and C++ dialects
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* manual: Fix copy+paste error
|
||||||
|
* manual: Fix documentation missing "-D" command line option
|
||||||
|
* manual: Add missing documentation for '-i' command line option
|
||||||
|
|
||||||
|
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 19:40:02 +0100
|
||||||
|
|
||||||
|
osmo-pcu (0.7.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Rafael Diniz ]
|
||||||
|
* Added support for daemonize to osmo-pcu.
|
||||||
|
* Fix help message formatting of osmo-pcu.
|
||||||
|
|
||||||
|
[ Max ]
|
||||||
|
* Don't install pcuif_proto.h header
|
||||||
|
* Move C include to proper place
|
||||||
|
* Add define for dummy burst string
|
||||||
|
* Add encoding tests for Immediate Assignment
|
||||||
|
* Clarify write_immediate_assignment() signature
|
||||||
|
* Restructure IA Rest Octets encoders
|
||||||
|
* Rewrite Packet Downlink Assignment
|
||||||
|
* Rewrite EGPRS Packet Uplink IA Rest Octets for MBA
|
||||||
|
* Rewrite EGPRS Packet Uplink IA Rest Octets for SBA
|
||||||
|
* MCS: internalize 'family' parameter
|
||||||
|
* EDGE tests: reduce code duplication
|
||||||
|
* MCS: remove dead code
|
||||||
|
* EDGE tests: remove no-op check
|
||||||
|
* Use msgb_eq_data_print() in tests
|
||||||
|
* Tighten lqual table limits check
|
||||||
|
* Enable LGLOBAL logging for TBF tests
|
||||||
|
* Log (M)CS UL update errors
|
||||||
|
* MCS: move Coding Scheme enum outside of class definition
|
||||||
|
* Make get_retx_mcs() into regular function
|
||||||
|
* MCS: remove unused function
|
||||||
|
* Debian: bump copyright year
|
||||||
|
* Use unique NSEI/BVCI/NSVCI in TBF tests
|
||||||
|
* MS store: move test helper to unit test
|
||||||
|
* Explicitly clean up BTS singleton
|
||||||
|
* MCS: move HeaderType enum outside of class definition
|
||||||
|
* MCS: use value_string for conversion
|
||||||
|
* TBF-DL: log MCS as string
|
||||||
|
* Fix TA index encoder
|
||||||
|
* MCS: move Mode enum outside of class definition
|
||||||
|
* MCS: add mcs_is_*() helpers
|
||||||
|
* MCS: add Channel Coding Command encoder
|
||||||
|
* Fix Channel Coding Command for MCS
|
||||||
|
* Rewrite Packet Uplink IA Rest Octets for MBA
|
||||||
|
* Rewrite Packet Uplink IA Rest Octets for SBA
|
||||||
|
* Use Timing Advance Index in UL assignments
|
||||||
|
* TBF: update MCS counters
|
||||||
|
* TBF-DL: cosmetic update for helper routines
|
||||||
|
* Update IA Rest Octets encoding
|
||||||
|
* TS alloc: expand tests log
|
||||||
|
* vty: add commands to show TBF of a certain kind
|
||||||
|
* Update MCS selection for retransmission
|
||||||
|
* cosmetic: use const pointer for bts_data
|
||||||
|
* Add test for MS mode and (M)CS settings
|
||||||
|
* Use libosmocore for IMSI parsing
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* Mark gprs_ns_reconnect() as static (not used outside of C file)
|
||||||
|
* Optionally Use the NS Sub-Network-Service (SNS) on Gb
|
||||||
|
* pcu_l1_if: Fix erroneous endian-swapping of the CellID
|
||||||
|
* Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
|
||||||
|
* gprs_debug: Use named initializers and explicit array indicies
|
||||||
|
* bssgp: Fix dead code: PDUT_STATUS can never reach this part
|
||||||
|
|
||||||
|
[ JF Dionne ]
|
||||||
|
* encoding: Fixes TMSI vs MI bit selection in repeated page info
|
||||||
|
|
||||||
|
[ Oliver Smith ]
|
||||||
|
* tests: use -no-install libtool flag to avoid ./lt-* scripts
|
||||||
|
* debian: create -doc subpackage with pdf manuals
|
||||||
|
* contrib/jenkins.sh: run "make maintainer-clean"
|
||||||
|
|
||||||
|
[ Daniel Willmann ]
|
||||||
|
* Include pdch.h in bts.h even if we're not compiling C++
|
||||||
|
* oc2g: Remove custom alarms
|
||||||
|
* oc2g: Change log type (Litecell15->Oc2g)
|
||||||
|
* jenkins.sh: Add oc2g build support
|
||||||
|
* manuals: Add script to regenerate vty/counter documentation
|
||||||
|
* manuals: Update VTY documentation
|
||||||
|
|
||||||
|
[ Jean-Francois Dionne ]
|
||||||
|
* Initial commit for OC-2G support.
|
||||||
|
|
||||||
|
[ Minh-Quang Nguyen ]
|
||||||
|
* OC-2G: Fix missing header
|
||||||
|
* OC-2G: Fix TA adjustment
|
||||||
|
* OC-2G: Always use positive TA information provided in PH-RA-IND
|
||||||
|
|
||||||
|
[ Alexander Couzens ]
|
||||||
|
* gprs_bssgp_pcu: make gprs_bssgp_ns_cb public
|
||||||
|
* gprs_bssgp_pcu: explicit allocate & initialize bssgp_nsi instance
|
||||||
|
* encoding: correct encoding of CRBB in ACK/NACK when not byte aligned
|
||||||
|
* encoding: use `/* */` for comments instead of `#if 0 #endif`
|
||||||
|
* egprs_rlc_compression: fix white spaces
|
||||||
|
* tests/BitcompTest: fix wording in log message
|
||||||
|
* rlc: replace int with uint16_t
|
||||||
|
* Encoding: drop struct gprs_rlcmac_bts* from all functions
|
||||||
|
* decompress_crbb: add length argument for search_runlen
|
||||||
|
* Encoding: write_packet_ack_nack_desc_egprs: don't use a reference for rest_bits
|
||||||
|
* bts.cpp: ensure left-shift operation does not exceed uint32_t
|
||||||
|
* Encoding: use uint16_t when interacting with the window object
|
||||||
|
* Encoding: ACK/NACK: always encode with length field present
|
||||||
|
|
||||||
|
[ Keith ]
|
||||||
|
* Cosmetic: Osmcoom -> Osmocom
|
||||||
|
|
||||||
|
[ Vadim Yanitskiy ]
|
||||||
|
* src/pcu_l1_if.cpp: fix: properly pass measurements from PCUIF
|
||||||
|
* gprs_bssgp_pcu_rx_dl_ud(): fix: BSSGP_IE_IMSI is optional
|
||||||
|
* gprs_bssgp_pcu.cpp: check return code of gsm48_mi_to_string()
|
||||||
|
* gprs_bssgp_pcu_rx_dl_ud(): use OSMO_IMSI_BUF_SIZE
|
||||||
|
|
||||||
|
[ Thorsten Alteholz ]
|
||||||
|
* fix spelling errors detected by lintian
|
||||||
|
|
||||||
|
[ Eric Wild ]
|
||||||
|
* ubsan: fix shift
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
|
||||||
|
* Require newer libosmocore to avoid compile failures
|
||||||
|
|
||||||
|
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 21:09:53 +0200
|
||||||
|
|
||||||
|
osmo-pcu (0.6.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* debian/rules: Don't overwrite .tarball-version
|
||||||
|
* gprs_rlcmac_received_lost(): Fix regression / uninitialized now_tv
|
||||||
|
* initial checkin of manuals to public repo
|
||||||
|
* Add link to Asciidoc source code of manual
|
||||||
|
* Initial place-holder for the new Gb/IP interface documentation
|
||||||
|
* Gb message sequence chart: flip sides (SGSN should be right)
|
||||||
|
* Gb message sequence chart: Add notion of PCU unix domain socket
|
||||||
|
* Gb: Various spelling fixes
|
||||||
|
* gb: Some language improvements, formatting changes
|
||||||
|
* consistently use '3GPP TS' not sometimes 3GPP TS and sometimes TS.
|
||||||
|
* gb/NS: Clarify the language regarding the UDP port numbers / socket
|
||||||
|
* vty-ref: Update URI of docbook 5.0 schema
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* Cleanup of systemd service files
|
||||||
|
* configure.ac: Set CXXFLAGS during --enable-sanitize
|
||||||
|
* Install systemd services with autotools
|
||||||
|
* Move examples/ to doc/examples/
|
||||||
|
* Install osmo-pcu.cfg to docdir/examples
|
||||||
|
* Allow easily disabling GFDL references
|
||||||
|
|
||||||
|
[ Stefan Sperling ]
|
||||||
|
* check for overlong unix socket paths
|
||||||
|
|
||||||
|
[ Max ]
|
||||||
|
* deb: add missing copyright file
|
||||||
|
* OsmoPCU: fix Gb documentation front page
|
||||||
|
* OsmoPCU: expand NS documentation
|
||||||
|
* OsmoPCU: expand BSSGP documentation
|
||||||
|
* OsmoPCU: add MSC chart
|
||||||
|
|
||||||
|
[ Neels Hofmeyr ]
|
||||||
|
* Importing history from osmo-gsm-manuals.git
|
||||||
|
* make clean: also remove generated image files
|
||||||
|
* add 'make check' target
|
||||||
|
* fix 'make clean': shell glob, ignore failure
|
||||||
|
* refactor Makefile build rules, don't use the FORCE
|
||||||
|
|
||||||
|
[ Jonathan Brielmaier ]
|
||||||
|
* fix various typos across all manuals
|
||||||
|
|
||||||
|
[ Philipp ]
|
||||||
|
* configuration: fixing typos
|
||||||
|
|
||||||
|
[ Alexander Couzens ]
|
||||||
|
* OsmoPCU: add rate counter documentation
|
||||||
|
|
||||||
|
[ Daniel Willmann ]
|
||||||
|
* Change OpenBSC mentions to OsmoBSC where applicable
|
||||||
|
|
||||||
|
[ Oliver Smith ]
|
||||||
|
* build manuals moved here from osmo-gsm-manuals.git
|
||||||
|
* Fix DISTCHECK_CONFIGURE_FLAGS override
|
||||||
|
* contrib/jenkins.sh: build and publish manuals
|
||||||
|
* contrib: fix makedistcheck with disabled systemd
|
||||||
|
|
||||||
|
-- Harald Welte <laforge@gnumonks.org> Mon, 21 Jan 2019 19:03:52 +0100
|
||||||
|
|
||||||
|
osmo-pcu (0.5.1) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* Don't register SIGHUP handler without actually handling SIGHUP
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* tbf: Fix memset(0) on object with no trivial copy-assignment
|
||||||
|
* rlc: Fix memset(0) on object with no trivial copy-assignment
|
||||||
|
* jenkins.sh: use flag --enable-werror for sysmo and none
|
||||||
|
* tbf: Use incrementing id for rate_ctr_group_alloc
|
||||||
|
* tbf: Replace '.' in counter names with ':'
|
||||||
|
|
||||||
|
[ Stefan Sperling ]
|
||||||
|
* fix a one-byte stack buffer overrun in osmo-pcu
|
||||||
|
* read monotonic clock with clock_gettime() instead of gettimeofday()
|
||||||
|
* fix time-delta calculations for measurement reports
|
||||||
|
* change log level of "DL packet loss" log messages
|
||||||
|
* check bssgp_tlv_parse() return code in gprs_bssgp_pcu_rcvmsg()
|
||||||
|
|
||||||
|
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 27 Jul 2018 21:56:38 +0200
|
||||||
|
|
||||||
|
osmo-pcu (0.5.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Neels Hofmeyr ]
|
||||||
|
* jenkins: use osmo-clean-workspace.sh before and after build
|
||||||
|
* vty: skip installing cmds now always installed by default
|
||||||
|
* implement support for 3-digit MNC with leading zeros
|
||||||
|
* configure: add --enable-werror
|
||||||
|
* mslot_class: find_free_tfi(): use uint32_t to shift 1 << 31
|
||||||
|
* mslot_class: two more: use uint32_t to shift 1 << 31
|
||||||
|
* Revert "Use Timing Advance Index in UL assignments"
|
||||||
|
* Revert "Rewrite Packet Uplink Assignment"
|
||||||
|
* Revert "Rewrite Packet Downlink Assignment"
|
||||||
|
* configure: fix --enable-sysmocom-dsp and --with-sysmobts flags
|
||||||
|
* configure: properly quote CFLAGS in lc15 check
|
||||||
|
* Revert "Rewrite EGPRS Packet Uplink Assignment"
|
||||||
|
* use osmo_init_logging2() with proper talloc ctx
|
||||||
|
|
||||||
|
[ Minh-Quang Nguyen ]
|
||||||
|
* PCU: Fix TA adjustment
|
||||||
|
* PCU: display TA information in TBF stats
|
||||||
|
|
||||||
|
[ Max ]
|
||||||
|
* Remove unused parameter
|
||||||
|
* Move multislot table to separate file
|
||||||
|
* Replace '.' in counter names with ':'
|
||||||
|
* Fix compiler warning
|
||||||
|
* TBF: log timer override
|
||||||
|
* TBF: fix compiler warning in test
|
||||||
|
* TBF: expand timer logging
|
||||||
|
* vty: print class and TBFs for each MS
|
||||||
|
* DL window: constify resend_needed() function
|
||||||
|
* TBF: move EGPRS enablement into (U|D)L-TBF
|
||||||
|
* TBF-DL: fix misleading idle time check
|
||||||
|
* TBF: remove unused variable
|
||||||
|
* Remove unused includes and forward declarations
|
||||||
|
* Fix tests after rate_ctr change
|
||||||
|
* Introduce LOGTBF* for consistent logging
|
||||||
|
* TBF: implement independent T31xx timers
|
||||||
|
* TBF: add N3101 counter
|
||||||
|
* Fix warnings
|
||||||
|
* Add function to get max supported MS class
|
||||||
|
* Add --enable-sanitize configure option
|
||||||
|
* Enable sanitize for CI test
|
||||||
|
* Add tests for pcu_lsb()
|
||||||
|
* Add optional profiling support
|
||||||
|
* TBF: unify timer handling
|
||||||
|
* TBF: log timer invocation source
|
||||||
|
* TBF: bail out for unknown timers
|
||||||
|
* Fix llc_queue_size() type
|
||||||
|
* TBF-DL: mark rcvd_dl_ack() parameters as boolean
|
||||||
|
* window: move encoding into functions
|
||||||
|
* cosmetic: clarify coding scheme and puncturing
|
||||||
|
* Make TBF state private
|
||||||
|
* TBF: cleanup state flag handling
|
||||||
|
* Clarify RACH-related interfaces
|
||||||
|
* TBF-UL: add simpler test helper
|
||||||
|
* Avoid code duplication in TBF test
|
||||||
|
* TBF: move window parameters to UL/DL level
|
||||||
|
* TBF-DL: move priority computation into function
|
||||||
|
* TBF: unify EGPRS window calculation
|
||||||
|
* Don't access TBF internals in vty functions
|
||||||
|
* Fix jenkins.sh to match jenkins job axis filter
|
||||||
|
* Allocate global context for TypesTest
|
||||||
|
* Fix sanitizer build
|
||||||
|
* Rewrite EGPRS Packet Uplink Assignment
|
||||||
|
* Rewrite Packet Downlink Assignment
|
||||||
|
* Rewrite Packet Uplink Assignment
|
||||||
|
* Use Timing Advance Index in UL assignments
|
||||||
|
* Allow specifying sysmocom headers explicitly
|
||||||
|
* TBF: log source of state transitions
|
||||||
|
* jenkins.sh: Disable building doxygen for deps
|
||||||
|
* Set V_N and V_B to known initial state
|
||||||
|
* TBF: add dedicated log categories
|
||||||
|
* TBF: make UL/DL state internal
|
||||||
|
* TBF: make UL ack state internal
|
||||||
|
* TBF: make poll state internal
|
||||||
|
* TBF: adjust test log levels
|
||||||
|
* Add tests for find_multi_slots()
|
||||||
|
* AllocTest: adjust test_alloc_b()
|
||||||
|
* AllocTest: expand test output
|
||||||
|
* AllocTest: remove assumption on max MS class
|
||||||
|
* Add multislot classes from latest spec
|
||||||
|
* cosmetic: fix whitespace issue with include files
|
||||||
|
* TBF: decrease L1 logging verbosity in test
|
||||||
|
* TBF: override send function via linker option
|
||||||
|
* Simplify TS alloc: adjust allocator signatures
|
||||||
|
* Simplify TS alloc: fix allocation calls
|
||||||
|
* Simplify TS alloc: avoid TS reassignment
|
||||||
|
* Simplify TS alloc: use defines for constants
|
||||||
|
* Simplify TS alloc: adjust function signatures
|
||||||
|
* TS alloc: print suggested TRX on allocation errors
|
||||||
|
* Simplify TS alloc: internalize TRX check
|
||||||
|
* TBF: decrease logging verbosity for traffic
|
||||||
|
* TBF: add helpers for assignment type handling
|
||||||
|
* TBF: show assignment kind in vty
|
||||||
|
* vty: drop unused function
|
||||||
|
* RACH: improve single block detection
|
||||||
|
* TBF: move common test code into functions
|
||||||
|
* emu: use libosmocore definitions
|
||||||
|
* Use explicit type for pcu_lsb()
|
||||||
|
* Move paging generation into PDCH
|
||||||
|
* Move include guard to the top
|
||||||
|
* Update header includes
|
||||||
|
* Simplify TS alloc: split off RX mask computation
|
||||||
|
* Simplify TS alloc: separate capacity computation
|
||||||
|
* Simplify TS alloc: split allocation
|
||||||
|
* Simplify TS alloc: split USF/UL allocation
|
||||||
|
* Move PDCH-related functions into separate files
|
||||||
|
* Simplify TS alloc: don't use PDCH for free TFI
|
||||||
|
* Simplify TS alloc: constify max dl slot func
|
||||||
|
* TBF: make network counters internal
|
||||||
|
* Simplify TS alloc: move slot assignment
|
||||||
|
* Simplify TS alloc: move slot check into functions
|
||||||
|
|
||||||
|
[ Pau Espin Pedrol ]
|
||||||
|
* Print error cause of pcu socket connect failure
|
||||||
|
* gprs_bssgp_pcu.cpp: Comment unused function parse_ra_cap
|
||||||
|
|
||||||
|
[ Stefan Sperling ]
|
||||||
|
* Make osmo-pcu wait for BTS to become available at start-up time.
|
||||||
|
* improve documentation of Encoding::write_paging_request()
|
||||||
|
|
||||||
|
[ Alexander Couzens ]
|
||||||
|
* pcuif_proto.h: fix whitespaces and indention
|
||||||
|
* pcuif_proto: add version 8 features
|
||||||
|
|
||||||
|
[ Philipp Maier ]
|
||||||
|
* cosmetic: remove runaway semicolon
|
||||||
|
* pcu_l1_if: add frame number to log output
|
||||||
|
* tbf: add frame number to log output
|
||||||
|
|
||||||
|
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 16:20:00 +0200
|
||||||
|
|
||||||
|
osmo-pcu (0.4.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
[ Holger Hans Peter Freyther ]
|
||||||
|
* Initial release.
|
||||||
|
|
||||||
|
[ Max ]
|
||||||
|
* Use value string check from osmo-ci
|
||||||
|
* cosmetic: tighten direct-phy related code
|
||||||
|
* Support receiving SI13 from BTS
|
||||||
|
* Move gsmtap and accounting into separate function
|
||||||
|
* cosmetic: convert explicit warnings to fixme/todo
|
||||||
|
* Assert valid CS
|
||||||
|
* TBF-DL: extend index check for RLC block copy
|
||||||
|
* TS alloc: properly count UL slots
|
||||||
|
* cosmetic: reformat multislot classes table
|
||||||
|
|
||||||
|
[ Philipp Maier ]
|
||||||
|
* gb: allow only packets from a specific SGSN
|
||||||
|
|
||||||
|
[ Harald Welte ]
|
||||||
|
* tests: Don't use private version of log_info but global gprs_log_info
|
||||||
|
* Call osmo_init_logging() before static BTS constructor
|
||||||
|
* Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
|
||||||
|
* Debian: Cosmetic changes to control file; add better Description
|
||||||
|
* Debian: print test results in case of failure + clean-up autotest
|
||||||
|
* Debian: migrate from DEB_BUILD_HARDENING to DEB_BUILD_MAINT_OPTIONS
|
||||||
|
* Debian: upgrade to debhelper 9 / Standards 3.9.8
|
||||||
|
|
||||||
|
-- Harald Welte <laforge@gnumonks.org> Sun, 29 Oct 2017 12:03:05 +0100
|
||||||
|
|
||||||
|
osmo-pcu (0.3.0) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Initial release.
|
||||||
|
|
||||||
|
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Fri, 01 Apr 2016 18:59:00 +0200
|
||||||
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
9
|
||||||
47
debian/control
vendored
Normal file
47
debian/control
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
Source: osmo-pcu
|
||||||
|
Section: net
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||||
|
Build-Depends: debhelper (>= 9),
|
||||||
|
dh-autoreconf,
|
||||||
|
dh-systemd (>= 1.5),
|
||||||
|
autotools-dev,
|
||||||
|
pkg-config,
|
||||||
|
libosmocore-dev (>= 1.5.0),
|
||||||
|
osmo-gsm-manuals-dev (>= 1.1.0)
|
||||||
|
Standards-Version: 3.9.8
|
||||||
|
Homepage: http://osmocom.org/projects/osmopcu
|
||||||
|
Vcs-Git: git://git.osmocom.org/osmo-pcu
|
||||||
|
Vcs-Browser: http://git.osmocom.org/osmo-pcu/
|
||||||
|
|
||||||
|
Package: osmo-pcu
|
||||||
|
Architecture: any
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||||
|
Description: Osmocom GPRS/EDGE Packet Control Unit (PCU)
|
||||||
|
The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
|
||||||
|
in order to provide packet-switched services for 2G (2.5G, 2.75G)
|
||||||
|
networks. OsmoPCU is the Osmocom implementation of this network
|
||||||
|
element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
|
||||||
|
and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.
|
||||||
|
|
||||||
|
Package: osmo-pcu-dbg
|
||||||
|
Architecture: any
|
||||||
|
Section: debug
|
||||||
|
Priority: extra
|
||||||
|
Depends: osmo-pcu (= ${binary:Version}),
|
||||||
|
${misc:Depends}
|
||||||
|
Description: Debug symbols for the Osmocom GPRS/EDGE Packet Control Unit (PCU)
|
||||||
|
The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
|
||||||
|
in order to provide packet-switched services for 2G (2.5G, 2.75G)
|
||||||
|
networks. OsmoPCU is the Osmocom implementation of this network
|
||||||
|
element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
|
||||||
|
and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.
|
||||||
|
|
||||||
|
Package: osmo-pcu-doc
|
||||||
|
Architecture: all
|
||||||
|
Section: doc
|
||||||
|
Priority: optional
|
||||||
|
Depends: ${misc:Depends}
|
||||||
|
Description: ${misc:Package} PDF documentation
|
||||||
|
Various manuals: user manual, VTY reference manual and/or
|
||||||
|
protocol/interface manuals.
|
||||||
131
debian/copyright
vendored
Normal file
131
debian/copyright
vendored
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: osmo-pcu
|
||||||
|
Source: git://git.osmocom.org/osmo-pcu
|
||||||
|
Files-Excluded: debian
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2009-2015 Holger Hans Peter Freyther <zecke@selfish.org>
|
||||||
|
2013 Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||||
|
2016-2019 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||||
|
2015 by Yves Godin <support@nuranwireless.com>
|
||||||
|
License: AGPL-3.0+
|
||||||
|
|
||||||
|
Files: src/gprs_ms_storage.h
|
||||||
|
src/gprs_ms_storage.cpp
|
||||||
|
src/gprs_ms.h
|
||||||
|
src/gprs_coding_scheme.cpp
|
||||||
|
src/gprs_coding_scheme.h
|
||||||
|
src/coding_scheme.c
|
||||||
|
src/coding_scheme.h
|
||||||
|
src/cxx_linuxlist.h
|
||||||
|
src/pcu_vty_functions.cpp
|
||||||
|
src/pcu_vty_functions.h
|
||||||
|
src/pcu_utils.h
|
||||||
|
src/gprs_codel.c
|
||||||
|
src/gprs_codel.h
|
||||||
|
Copyright: 2016-2019 sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Files: osmoappdesc.py
|
||||||
|
Copyright: 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||||
|
License: GPL-3.0+
|
||||||
|
|
||||||
|
Files: src/gprs_debug.cpp
|
||||||
|
src/gprs_debug.h
|
||||||
|
src/pcu_main.cpp
|
||||||
|
src/pcu_l1_if.h
|
||||||
|
Copyright: 2012 Ivan Klyuchnikov
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Files: src/tbf.cpp
|
||||||
|
src/tbf_ul.cpp
|
||||||
|
src/tbf_dl.cpp
|
||||||
|
src/sba.cpp
|
||||||
|
src/sba.h
|
||||||
|
src/llc.cpp
|
||||||
|
src/encoding.cpp
|
||||||
|
src/encoding.h
|
||||||
|
src/gprs_rlcmac.cpp
|
||||||
|
src/gprs_rlcmac_ts_alloc.cpp
|
||||||
|
Copyright: 2012 Ivan Klyuchnikov
|
||||||
|
2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
2013 by Holger Hans Peter Freyther
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Files: src/gprs_rlcmac.h
|
||||||
|
src/gprs_bssgp_pcu.cpp
|
||||||
|
src/gprs_bssgp_pcu.h
|
||||||
|
src/bts.h
|
||||||
|
Copyright: 2012 Ivan Klyuchnikov
|
||||||
|
2013 by Holger Hans Peter Freyther
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Files: src/rlc.h
|
||||||
|
src/decoding.cpp
|
||||||
|
src/decoding.h
|
||||||
|
Copyright: 2012 Ivan Klyuchnikov
|
||||||
|
2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Files: src/rlc.cpp
|
||||||
|
src/llc.h
|
||||||
|
src/tbf.h
|
||||||
|
Copyright: 2013 by Holger Hans Peter Freyther
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Files: src/pcu_l1_if.cpp
|
||||||
|
src/gprs_rlcmac_meas.cpp
|
||||||
|
src/gprs_rlcmac_sched.cpp
|
||||||
|
src/osmobts_sock.cpp
|
||||||
|
Copyright: 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Files: src/csn1.h
|
||||||
|
src/csn1.cpp
|
||||||
|
src/gsm_rlcmac.cpp
|
||||||
|
src/gsm_rlcmac.h
|
||||||
|
Copyright: 2011 Vincent Helfre
|
||||||
|
2011 ST-Ericsson (Jari Sassi)
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
License: AGPL-3.0+
|
||||||
|
All rights not specifically granted under this license are reserved.
|
||||||
|
.
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Affero General Public License as published by the
|
||||||
|
Free Software Foundation; either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
License: GPL-3.0+
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
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. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General Public License
|
||||||
|
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||||
|
|
||||||
|
License: GPL-2.0+
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
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. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the GNU General Public License
|
||||||
|
Version 2 can be found in `/usr/share/common-licenses/GPL-2'.
|
||||||
1
debian/osmo-pcu-doc.install
vendored
Normal file
1
debian/osmo-pcu-doc.install
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
usr/share/doc/osmo-pcu-doc/*.pdf
|
||||||
4
debian/osmo-pcu.install
vendored
Normal file
4
debian/osmo-pcu.install
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
etc/osmocom/osmo-pcu.cfg
|
||||||
|
lib/systemd/system/osmo-pcu.service
|
||||||
|
usr/bin/osmo-pcu
|
||||||
|
usr/share/doc/osmo-pcu/examples/osmo-pcu/osmo-pcu.cfg
|
||||||
31
debian/rules
vendored
Executable file
31
debian/rules
vendored
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
|
||||||
|
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
|
||||||
|
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
|
||||||
|
|
||||||
|
#export DH_VERBOSE=1
|
||||||
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@ --with=systemd --with autoreconf --fail-missing
|
||||||
|
|
||||||
|
override_dh_strip:
|
||||||
|
dh_strip --dbg-package=osmo-pcu-dbg
|
||||||
|
|
||||||
|
override_dh_clean:
|
||||||
|
dh_clean
|
||||||
|
$(RM) tests/package.m4
|
||||||
|
$(RM) test/testsuite
|
||||||
|
|
||||||
|
# Print test results in case of a failure
|
||||||
|
override_dh_auto_test:
|
||||||
|
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||||
|
|
||||||
|
override_dh_auto_configure:
|
||||||
|
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||||
|
|
||||||
|
# Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers)
|
||||||
|
override_dh_compress:
|
||||||
|
dh_compress -X.pdf
|
||||||
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
||||||
4
doc/Makefile.am
Normal file
4
doc/Makefile.am
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
SUBDIRS = \
|
||||||
|
examples \
|
||||||
|
manuals \
|
||||||
|
$(NULL)
|
||||||
7
doc/examples/Makefile.am
Normal file
7
doc/examples/Makefile.am
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
examples_pcudir = $(docdir)/examples/osmo-pcu
|
||||||
|
examples_pcu_DATA = osmo-pcu.cfg
|
||||||
|
|
||||||
|
osmoconfdir = $(sysconfdir)/osmocom
|
||||||
|
osmoconf_DATA = osmo-pcu.cfg
|
||||||
|
|
||||||
|
EXTRA_DIST = osmo-pcu.cfg
|
||||||
5
doc/examples/osmo-pcu.cfg
Normal file
5
doc/examples/osmo-pcu.cfg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pcu
|
||||||
|
flow-control-interval 10
|
||||||
|
cs 2
|
||||||
|
alloc-algorithm dynamic
|
||||||
|
gamma 0
|
||||||
28
doc/manuals/Makefile.am
Normal file
28
doc/manuals/Makefile.am
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
EXTRA_DIST = osmopcu-gb.adoc \
|
||||||
|
osmopcu-gb-docinfo.xml \
|
||||||
|
osmopcu-usermanual.adoc \
|
||||||
|
osmopcu-usermanual-docinfo.xml \
|
||||||
|
osmopcu-vty-reference.xml \
|
||||||
|
regen_doc.sh \
|
||||||
|
chapters \
|
||||||
|
gb \
|
||||||
|
vty
|
||||||
|
|
||||||
|
if BUILD_MANUALS
|
||||||
|
ASCIIDOC = osmopcu-usermanual.adoc osmopcu-gb.adoc
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||||
|
osmopcu-gb.pdf: $(srcdir)/gb/*.adoc $(srcdir)/gb/*.msc
|
||||||
|
osmopcu-usermanual.pdf: $(srcdir)/chapters/*.adoc
|
||||||
|
|
||||||
|
VTY_REFERENCE = osmopcu-vty-reference.xml
|
||||||
|
|
||||||
|
BUILT_REFERENCE_XML = $(builddir)/vty/pcu_vty_reference.xml
|
||||||
|
$(builddir)/vty/pcu_vty_reference.xml: $(top_builddir)/src/osmo-pcu
|
||||||
|
mkdir -p $(builddir)/vty
|
||||||
|
$(top_builddir)/src/osmo-pcu --vty-ref-xml > $@
|
||||||
|
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||||
|
|
||||||
|
OSMO_REPOSITORY = osmo-pcu
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||||
|
endif
|
||||||
400
doc/manuals/chapters/configuration.adoc
Normal file
400
doc/manuals/chapters/configuration.adoc
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
== Configuring OsmoPCU
|
||||||
|
|
||||||
|
Contrary to other network elements (like OsmoBSC, OsmoNITB), the
|
||||||
|
OsmoPCU has a relatively simple minimum configuration.
|
||||||
|
|
||||||
|
This is primarily because most of the PCU configuration happens
|
||||||
|
indirectly from the BSC, who passes the configuation over A-bis OML via
|
||||||
|
OsmoBTS and its PCU socket into OsmoPCU.
|
||||||
|
|
||||||
|
A minimal OsmoPCU configuration file is provided below for your reference:
|
||||||
|
|
||||||
|
.Example: Minimal OsmoPCU configuration file (`osmo-pcu.cfg`)
|
||||||
|
----
|
||||||
|
pcu
|
||||||
|
flow-control-interval 10 <1>
|
||||||
|
cs 2 <2>
|
||||||
|
alloc-algorithm dynamic <3>
|
||||||
|
gamma 0
|
||||||
|
----
|
||||||
|
<1> send a BSSGP flow-control PDU every 10 seconds
|
||||||
|
<2> start a TBF with the initial coding scheme 2
|
||||||
|
<3> dynamically chose between single-slot or multi-slot TBF allocations
|
||||||
|
depending on system load
|
||||||
|
|
||||||
|
However, there are plenty of tuning parameters for people interested to
|
||||||
|
optimize PCU throughput or latency according to their requirements.
|
||||||
|
|
||||||
|
=== Configuring the Coding Schemes and Rate Adaption
|
||||||
|
|
||||||
|
As a reminder:
|
||||||
|
|
||||||
|
- GPRS supports Coding Schemes 1-4 (CS1-4), all of them use GMSK modulation
|
||||||
|
(same as GSM).
|
||||||
|
- EGPRS supports MCS1-9, where MCS1-4 is GMSK, and MCS5-9 use 8-PSK modulation.
|
||||||
|
|
||||||
|
The range of Coding Schemes above only apply to RLCMAC data blocks; RLCMAC
|
||||||
|
control blocks are always transmitted with CS1 (GMSK). Hence, CS1 is always
|
||||||
|
supported and must be always permitted.
|
||||||
|
|
||||||
|
The BSC includes a bit-mask of permitted [E]GPRS coding schemes as part of the
|
||||||
|
A-bis OML configuration, controlled by VTY `gprs mode (none|gprs|egprs)`. This
|
||||||
|
is passed from the BTS via the PCU socket into OsmoPCU, and the resulting set
|
||||||
|
can be further constrained by OsmoPCU VTY configuration.
|
||||||
|
|
||||||
|
Some additional OsmoPCU parameters can be set as described below.
|
||||||
|
|
||||||
|
==== Initial Coding Scheme
|
||||||
|
|
||||||
|
You can use the `cs <1-4> [<1-4>]` command at the `pcu` VTY config node
|
||||||
|
to set the initial GPRS coding scheme to be used. The optional second
|
||||||
|
value allows to specify a different initial coding scheme for uplink.
|
||||||
|
|
||||||
|
Similarly, `mcs <1-9> [<1-9>]` can be used to set up the initial EGPRS coding
|
||||||
|
scheme.
|
||||||
|
|
||||||
|
[[max_cs_mcs]]
|
||||||
|
==== Maximum Coding Scheme
|
||||||
|
|
||||||
|
You can use the `cs max <1-4> [<1-4>]` command at the `pcu` VTY config
|
||||||
|
node to set the maximum GPRS coding scheme that should be used as part of the
|
||||||
|
rate adaption. The optional second value allows to specify a different maximum
|
||||||
|
coding scheme for uplink.
|
||||||
|
|
||||||
|
Similarly, `mcs max <1-9> [<1-9>]` can be used to set up the maximum EGPRS
|
||||||
|
coding scheme.
|
||||||
|
|
||||||
|
The actual Maximum Coding Scheme for each direction used at runtime is actually
|
||||||
|
the result of taking the maximum value from the permitted GPRS coding schemes
|
||||||
|
received by the BSC (or BTS) over PCUIF which is equal or lower tho the
|
||||||
|
configured value.
|
||||||
|
|
||||||
|
Example: PCUIF announces permitted MCS bit-mask (`MCS1 MCS2 MCS3 MCS4`) and
|
||||||
|
OsmoPCU is configured `mcs max 6`, then the actual maximum MCS used at runtime
|
||||||
|
will be `MCS4`.
|
||||||
|
|
||||||
|
==== Rate Adaption Error Thresholds
|
||||||
|
|
||||||
|
You can use the `cs threshold <0-100> <0-100>` command at the `pcu` VTY
|
||||||
|
config node to determine the upper and lower limit for the error rate
|
||||||
|
percentage to use in the rate adaption. If the upper threshold is
|
||||||
|
reached, a lower coding sheme is chosen, and if the lower threshold is
|
||||||
|
reached, a higher coding scheme is chosen.
|
||||||
|
|
||||||
|
==== Rate Adation Link Quality Thresholds
|
||||||
|
|
||||||
|
You can use the `cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35> cs3
|
||||||
|
<0-35> <0-35> cs4 <0-35>` command at the `pcu` VTY config node to tune
|
||||||
|
the link quality ranges for the respective coding schemes.
|
||||||
|
|
||||||
|
==== Data Size based CS downgrade Threshold
|
||||||
|
|
||||||
|
You can use the `cs downgrade-threshold <1-10000>` command at the `pcu`
|
||||||
|
VTY config node to ask the PCU to down-grade the coding scheme if less
|
||||||
|
than the specified number of octets are left to be transmitted.
|
||||||
|
|
||||||
|
=== Miscellaneous Configuration / Tuning Parameters
|
||||||
|
|
||||||
|
==== Downlink TBF idle time
|
||||||
|
|
||||||
|
After a down-link TBF is idle (all data in the current LLC downlink
|
||||||
|
queue for the MS has been transmitted), we can keep the TBF established
|
||||||
|
for a configurable time. This avoids having to go through a new one or
|
||||||
|
two phase TBF establishment once the next data for downlink arrives.
|
||||||
|
|
||||||
|
You can use the `dl-tbf-idle-time <1-5000>` to specify that time in
|
||||||
|
units of milli-seconds. The default is 2 seconds.
|
||||||
|
|
||||||
|
==== MS idle time
|
||||||
|
|
||||||
|
Using the `ms-idle-time <1-7200>` command at the `pcu` VTY config node
|
||||||
|
you can configure the number of seconds for which the PCU should keep
|
||||||
|
the MS data structure alive before releasing it if there are no active
|
||||||
|
TBF for this MS.
|
||||||
|
|
||||||
|
The OsmoPCU default value is 60 seconds, which is slightly more than
|
||||||
|
what 3GPP TS 24.008 recommends for T3314 (44s).
|
||||||
|
|
||||||
|
The MS data structure only consumes memory in the PCU and does not
|
||||||
|
require any resources of the air interface.
|
||||||
|
|
||||||
|
==== Forcing two-phase access
|
||||||
|
|
||||||
|
If the MS is using a single-phase access, you can still force it to
|
||||||
|
use a two-phase access using the `two-phase-access` VTY configuration
|
||||||
|
command at the `pcu` VTY config node.
|
||||||
|
|
||||||
|
=== Configuring BSSGP flow control
|
||||||
|
|
||||||
|
BSSGP between SGSN and PCU contains a two-level nested flow control
|
||||||
|
mechanism:
|
||||||
|
|
||||||
|
. one global flow control instance for the overall (downlink) traffic
|
||||||
|
from the SGSN to this PCU
|
||||||
|
. a per-MS flow control instance for each individual MS served by this
|
||||||
|
PCU
|
||||||
|
|
||||||
|
Each of the flow control instance is implemented as a TBF (token bucket
|
||||||
|
filter).
|
||||||
|
|
||||||
|
==== Normal BSSGP Flow Control Tuning parameters
|
||||||
|
|
||||||
|
You can use the following commands at the `pcu` VTY config node to tune
|
||||||
|
the BSSGP flow control parameters:
|
||||||
|
|
||||||
|
`flow-control-interval <1-10>`::
|
||||||
|
configure the interval (in seconds) between subsequent flow
|
||||||
|
control PDUs from PCU to SGSN
|
||||||
|
`flow-control bucket-time <1-65534>`::
|
||||||
|
set the target downlink maximum queueing time in centi-seconds.
|
||||||
|
The PCU will attempt to adjust the advertised bucket size to match this
|
||||||
|
target.
|
||||||
|
|
||||||
|
==== Extended BSSGP Flow Control Tuning parameters
|
||||||
|
|
||||||
|
There are some extended flow control related parameters at the `pcu` VTY
|
||||||
|
config node that override the automatic flow control as specified in the
|
||||||
|
BSSGP specification. Use them with care!
|
||||||
|
|
||||||
|
`flow-control force-bvc-bucket-size <1-6553500>`::
|
||||||
|
force the BVC (global) bucket size to the given number of octets
|
||||||
|
`flow-control force-bvc-leak-rate <1-6553500>`::
|
||||||
|
force the BVC (global) bucket leak rate to the given number of bits/s
|
||||||
|
`flow-control force-ms-bucket-size <1-6553500>`::
|
||||||
|
force the per-MS bucket size to the given number of octets
|
||||||
|
`flow-control force-ms-leak-rate <1-6553500>`::
|
||||||
|
force the per-MS bucket leak rate to the given number of bits/s
|
||||||
|
|
||||||
|
|
||||||
|
=== Configuring LLC queue
|
||||||
|
|
||||||
|
The downlink LLC queue in the PCU towards the MS can be tuned with a
|
||||||
|
variety of parameters at the `pcu` VTY config node, depending on your
|
||||||
|
needs.
|
||||||
|
|
||||||
|
`queue lifetime <1-65534>`::
|
||||||
|
Each downlink LLC PDU is assigned a lifetime by the SGSN, which
|
||||||
|
is respected by the PDU *unless* you use this command to
|
||||||
|
override the PDU lifetime with a larger value (in centi-seconds)
|
||||||
|
`queue lifetime infinite`::
|
||||||
|
Never drop LLC PDUs, i.e. give them an unlimited lifetime.
|
||||||
|
`queue hysteresis <1-65535>`::
|
||||||
|
When the downlink LLC queue is full, the PCU starts dropping
|
||||||
|
packets. Using this parameter, we can set the lifetime
|
||||||
|
hysteresis in centi-seconds, i.e. it will continue discarding
|
||||||
|
until "lifetime - hysteresis" is reached.
|
||||||
|
`queue codel`::
|
||||||
|
Use the 'CoDel' (Controlled Delay) scheduling algorithm, which
|
||||||
|
is designed to overcome buffer bloat. It will use a default
|
||||||
|
interval of 4 seconds.
|
||||||
|
`queue codel interval <1-1000>`::
|
||||||
|
Use the 'CoDel' (Controlled Delay) scheduling algorithm, which
|
||||||
|
is designed to overcome buffer bloat. Use the specified
|
||||||
|
interval in centi-seconds.
|
||||||
|
`queue idle-ack-delay <1-65535>`::
|
||||||
|
Delay the request for an ACK after the last downlink LLC frame
|
||||||
|
by the specified amount of centi-seconds.
|
||||||
|
|
||||||
|
|
||||||
|
=== Configuring MS power control
|
||||||
|
|
||||||
|
GPRS MS power control works completely different than the close MS power
|
||||||
|
control loop in circuit-switched GSM.
|
||||||
|
|
||||||
|
Rather than instructing the MS constantly about which transmit power to
|
||||||
|
use, some parameters are provided to the MS by which the MS-based power
|
||||||
|
control algorithm is tuned.
|
||||||
|
|
||||||
|
See 3GPP TS 05.08 for further information on the algorithm and the
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
You can set those parameters at the `pcu` VTY config node as follows:
|
||||||
|
|
||||||
|
`gamma <0-62>`::
|
||||||
|
Set the gamma parameter for MS power control in units of dB.
|
||||||
|
|
||||||
|
Parameter `ALPHA` is set on the BSC VTY configuration file on a per-BTS basis,
|
||||||
|
and forwarded by OsmoPCU to the MS through the SI13 received from the former
|
||||||
|
over PCUIF. OsmoPCU VTY command `alpha <0-10>` overrides the value received over
|
||||||
|
PCUIF to keep backward compatibility with existing config files, but it is
|
||||||
|
currently deprecated and its use is discouraged; one should configure it per-BTS
|
||||||
|
in OsmoBSC VTY instead.
|
||||||
|
|
||||||
|
=== Configuring Network Assisted Cell Change (NACC)
|
||||||
|
|
||||||
|
Network Assisted Cell Change, defined in 3GPP TS 44.060 sub-clause 8.8, is a
|
||||||
|
feature providing the MS aid when changing to a new cell due to autonomous
|
||||||
|
reselection. In summary, the MS informs the current cell its intention to change
|
||||||
|
to a new target cell, and the network decides whether to grant the intended cell
|
||||||
|
change or order a change to another neighbor cell. It also provides several
|
||||||
|
System Informations of the target cell to the MS to allow for quicker
|
||||||
|
reselection towards it.
|
||||||
|
|
||||||
|
OsmoPCU will automatically provide the required neighbor System Information when
|
||||||
|
the MS requests NACC towards a target cell also under the management of the same
|
||||||
|
OsmoPCU instance, since it already has the System Information of all BTS under
|
||||||
|
their control, obtained through PCUIF when the BTS registers against OsmoPCU, so
|
||||||
|
no specific user configuration is required here.
|
||||||
|
|
||||||
|
In general, OsmoPCU requires to gather the information from somewhere else
|
||||||
|
before being able to provide it to the MS requesting the NACC.
|
||||||
|
|
||||||
|
If OsmoPCU fails to gather the System Information, it will simply answer the MS
|
||||||
|
allowing the proposed changed but without previously providing the System
|
||||||
|
Information of the target cell.
|
||||||
|
|
||||||
|
==== Neighbor Address Resolution
|
||||||
|
|
||||||
|
First of all, it needs to translate the <ARFCN + BSIC> identity of the target
|
||||||
|
cell to change to, provided by the MS, into an identity that the Core Network
|
||||||
|
can use and understand to identify the target cell, which happens to be a key
|
||||||
|
composed of <RAI + Cell Identity>. This key is also named conveniently as
|
||||||
|
CGI-PS, since it actually equals to the Circuit Switch CGI + RAC.
|
||||||
|
|
||||||
|
In order to apply this target cell identity translation, OsmoPCU uses the
|
||||||
|
OsmoBSC Neighbor Resolution CTRL interface (see OsmoBSC User Manual), since the
|
||||||
|
BSC is the node holding all the neighbor related information.
|
||||||
|
By default, the use of this interface is not configured and hence disabled in
|
||||||
|
OsmoPCU. As a result, until configured, the network won't be able to provide the
|
||||||
|
System Information to the MS prior to allowing the change during NACC against
|
||||||
|
remote cells, which means the cell change will take longer to complete. In order
|
||||||
|
to configure the interface, the OsmoBSC IP address and port to connect to must
|
||||||
|
be configured in OsmoPCU VTY.
|
||||||
|
|
||||||
|
These neighbor address resolutions (<ARFCN + BSIC> => <RAI + CI>) are by default
|
||||||
|
cached for a while in order to avoid querying the BSC frequently and, as a
|
||||||
|
result, optimizing the resolution time too.
|
||||||
|
|
||||||
|
.Example: Configure Neighbor Resolution CTRL interface against OsmoBSC
|
||||||
|
----
|
||||||
|
pcu
|
||||||
|
neighbor resolution 172.18.13.10 4248 <1>
|
||||||
|
timer X1 500 <2>
|
||||||
|
timer X0 60 <3>
|
||||||
|
----
|
||||||
|
<1> Port 4248 is the default and hence could be omitted in this case
|
||||||
|
<2> Time out if the BSC doesn't answer our CTRL resolution request after 500 ms
|
||||||
|
<3> Keep resolved neighbor addresses cached for 60 seconds
|
||||||
|
|
||||||
|
==== System Information Resolution
|
||||||
|
|
||||||
|
Once OsmoPCU gains knowledge of the target cell's address in the Core Network,
|
||||||
|
it can query its System Information.
|
||||||
|
|
||||||
|
OsmoPCU will gather the requested System Information of target cells under its
|
||||||
|
control without need for any external query, since the System Information of all
|
||||||
|
BTSs it manages are received over PCUIF and stored internally in OsmoPCU.
|
||||||
|
|
||||||
|
For those targets cells not managed by the OsmoPCU instance, the query is
|
||||||
|
accomplished by using RIM procedures (NACC RAN-INFO application) over the Gb
|
||||||
|
interface against the SGSN that OsmoPCU is connected to. In its turn, the SGSN
|
||||||
|
will potentially forward this query to the PCU serving the target cell, which
|
||||||
|
will provide back the System Information of that cell.
|
||||||
|
|
||||||
|
The System Information received from external PCUs over RIM are by default
|
||||||
|
cached for a while in order to avoid querying the SGSN frequently and, as a
|
||||||
|
result, optimizing the resolution time too.
|
||||||
|
|
||||||
|
.Example: Configure System Information resolution
|
||||||
|
----
|
||||||
|
pcu
|
||||||
|
timer X2 500 <1>
|
||||||
|
timer X11 60 <2>
|
||||||
|
----
|
||||||
|
<1> Time out if the SGSN doesn't answer our RIM RAN-INFO request request after 500 ms
|
||||||
|
<2> Keep resolved remote neighbor System Information cached for 60 seconds
|
||||||
|
|
||||||
|
|
||||||
|
=== GPRS vs EGPRS considerations
|
||||||
|
|
||||||
|
==== Configuration
|
||||||
|
|
||||||
|
OsmoPCU can be configured to either:
|
||||||
|
|
||||||
|
- Allocate only GPRS TBFs to all MS (no EGPRS)
|
||||||
|
- Allocate EGPRS TBFs to EGPRS capable phones while still falling back to
|
||||||
|
allocating GPRS TBFs on GPRS-only capable MS.
|
||||||
|
|
||||||
|
These two different modes of operation are selected by properly configuring the
|
||||||
|
Coding Schemes (see <<max_cs_mcs>>).
|
||||||
|
|
||||||
|
The first mode of operation (GPRS-only for all MS) can be accomplished
|
||||||
|
configuring OsmoPCU so that the resulting MCS set is empty. This can be done in
|
||||||
|
two ways:
|
||||||
|
|
||||||
|
- Announcing an empty MCS bit-mask over PCUIF to OsmoPCU:
|
||||||
|
That's actually done automatically by OsmoBSC on BTS with VTY config set to
|
||||||
|
`gprs mode gprs`.
|
||||||
|
- Configuring OsmoPCU to force an empty set by using VTY command `mcs max 0`.
|
||||||
|
|
||||||
|
Hence, if the resulting MCS bit-mask is not empty, (BSC configuring the BTS with
|
||||||
|
`gprs mode egprs` and OsmoPCU VTY containing something other than 'mcs max 0'),
|
||||||
|
EGPRS TBFs will be allocated for all MS announcing EGPRS capabilities.
|
||||||
|
|
||||||
|
It is important to remark that in order to use MCS5-9, the BTS must support
|
||||||
|
8-PSK modulation. Nevertheless, in case 8-PSK is not supported by the BTS, one
|
||||||
|
can still enable EGPRS and simply make sure 8-PSK MCS are never used by
|
||||||
|
configuring OsmoPCU with `mcs max 4 4`.
|
||||||
|
|
||||||
|
Similarly, a BTS may support 8-PSK modulation only on downlink, since it is
|
||||||
|
easier to implement than the uplink, together with the fact that higher downlink
|
||||||
|
throughput is usually more interesting from user point of view. In this
|
||||||
|
scenario, OsmoPCU can be configured to allow for full MCS range in downlink
|
||||||
|
while still preventing use of 8-PSK on the uplink: `mcs max 9 4`.
|
||||||
|
|
||||||
|
Some other interesting configurations to control use of EGPRS in the network
|
||||||
|
which lay outside OsmoPCU include:
|
||||||
|
|
||||||
|
- If `osmo-bts-trx` together with `osmo-trx` is used, remember to enable EGPRS
|
||||||
|
support (OsmoTRX VTY `egprs enable`).
|
||||||
|
|
||||||
|
- It is possible to improve EGPRS performance (in particular, the TBF
|
||||||
|
establishment timing) a bit by enabling 11-bit Access Burst support. This
|
||||||
|
allows EGPRS capable phones to indicate their EGPRS capability, establishment
|
||||||
|
cause, and multi-slot class directly in the Access Burst (OsmoTRX VTY
|
||||||
|
`ext-rach enable`, OsmoBSC VTY `gprs egprs-packet-channel-request`).
|
||||||
|
|
||||||
|
NOTE: If you enable MCS5-9 you will also need an 8-PSK capable OsmoBTS+PHY,
|
||||||
|
which means `osmo-bts-sysmo` or `osmo-bts-litecell15` with their associated PHY,
|
||||||
|
or `osmo-bts-trx` with `osmo-trx` properly configured.
|
||||||
|
|
||||||
|
==== GPRS+EGPRS multiplexing
|
||||||
|
|
||||||
|
Both EGPRS and GPRS-only capable MS can be driven concurrently in the same PDCH
|
||||||
|
timeslot by the PCU, hence no special configuration is required per timeslot
|
||||||
|
regarding this topic; OsmoPCU scheduler takes care of the specific requirements
|
||||||
|
when driving MS with different capabilities.
|
||||||
|
|
||||||
|
These specific requirements translate to some restrictions regarding which
|
||||||
|
Coding Schemes can be used at given frame numbers, and hence which kind of
|
||||||
|
RLCMAC blocks can be sent, which means serving a GPRS-only MS in a PDCH may end
|
||||||
|
up affecting slightly the downlink throughput of EGPRS capable MS.
|
||||||
|
|
||||||
|
Throughput loss based on MS capabilities with TBF attached to a certain PDCH
|
||||||
|
timeslot:
|
||||||
|
|
||||||
|
All UEs are EGPRS capable::
|
||||||
|
No throughput loss, since all data is sent using EGPRS, and EGPRS control
|
||||||
|
messages are used when appropriate.
|
||||||
|
|
||||||
|
All UEs are GPRS-only (doesn't support EGPRS)::
|
||||||
|
No throughput loss, since all data and control blocks use GPRS.
|
||||||
|
|
||||||
|
Some UEs are GPRS-only, some EGPRS::
|
||||||
|
In general EGPRS capable UEs will use EGPRS, and GPRS-only UEs will use GPRS,
|
||||||
|
with some restrictions affecting throughput of EGPRS capable UEs:
|
||||||
|
- Whenever a GPRS-only MS is to be polled to send uplink data to PCU, then a
|
||||||
|
downlink RLCMAC block modulated using GMSK must be sent, which means that if the
|
||||||
|
scheduler selects a EGPRS MS for downlink on that block it will force sending of
|
||||||
|
data with MCS1-4 (if it's new data, if it's a retransmission it cannot be
|
||||||
|
selected since MCS from original message cannot be changed). In the worst case
|
||||||
|
if no control block needs to be sent or no new data in MCS1-4 is available to
|
||||||
|
send, then an RLCMAC Dummy Block is sent.
|
||||||
|
- For synchronization purposes, each MS needs to receive an RLCMAC block which
|
||||||
|
it can fully decode at least every 360ms, which means the scheduler must enforce
|
||||||
|
a downlink block in CS1-4 every 360ms, that is, every 18th RLCMAC block. In
|
||||||
|
general this is not a big issue since anyway all control RLCMAC blocks are
|
||||||
|
encoded in CS1, so in case any control block is sent from time to time it's
|
||||||
|
accomplished and there's no penalty. However, if only EGPRS downlink data is sent
|
||||||
|
over that time frame, then the scheduler will force sending a RLCMAC Dummy
|
||||||
|
Block.
|
||||||
4
doc/manuals/chapters/counters.adoc
Normal file
4
doc/manuals/chapters/counters.adoc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[[counters]]
|
||||||
|
== Counters
|
||||||
|
|
||||||
|
include::./counters_generated.adoc[]
|
||||||
178
doc/manuals/chapters/counters_generated.adoc
Normal file
178
doc/manuals/chapters/counters_generated.adoc
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
// autogenerated by show asciidoc counters
|
||||||
|
These counters and their description based on Osmo-PCU 0.4.0.4-8d55 (Osmo-PCU).
|
||||||
|
|
||||||
|
// generating tables for rate_ctr_group
|
||||||
|
// rate_ctr_group table BSSGP Peer Statistics
|
||||||
|
.bssgp:bss_ctx - BSSGP Peer Statistics
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description
|
||||||
|
| packets:in | <<bssgp:bss_ctx_packets:in>> | Packets at BSSGP Level ( In)
|
||||||
|
| packets:out | <<bssgp:bss_ctx_packets:out>> | Packets at BSSGP Level (Out)
|
||||||
|
| bytes:in | <<bssgp:bss_ctx_bytes:in>> | Bytes at BSSGP Level ( In)
|
||||||
|
| bytes:out | <<bssgp:bss_ctx_bytes:out>> | Bytes at BSSGP Level (Out)
|
||||||
|
| blocked | <<bssgp:bss_ctx_blocked>> | BVC Blocking count
|
||||||
|
| discarded | <<bssgp:bss_ctx_discarded>> | BVC LLC Discarded count
|
||||||
|
| status | <<bssgp:bss_ctx_status>> | BVC Status count
|
||||||
|
|===
|
||||||
|
// rate_ctr_group table NSVC Peer Statistics
|
||||||
|
.ns:nsvc - NSVC Peer Statistics
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description
|
||||||
|
| packets:in | <<ns:nsvc_packets:in>> | Packets at NS Level ( In)
|
||||||
|
| packets:out | <<ns:nsvc_packets:out>> | Packets at NS Level (Out)
|
||||||
|
| bytes:in | <<ns:nsvc_bytes:in>> | Bytes at NS Level ( In)
|
||||||
|
| bytes:out | <<ns:nsvc_bytes:out>> | Bytes at NS Level (Out)
|
||||||
|
| blocked | <<ns:nsvc_blocked>> | NS-VC Block count
|
||||||
|
| dead | <<ns:nsvc_dead>> | NS-VC gone dead count
|
||||||
|
| replaced | <<ns:nsvc_replaced>> | NS-VC replaced other count
|
||||||
|
| nsei-chg | <<ns:nsvc_nsei-chg>> | NS-VC changed NSEI count
|
||||||
|
| inv-nsvci | <<ns:nsvc_inv-nsvci>> | NS-VCI was invalid count
|
||||||
|
| inv-nsei | <<ns:nsvc_inv-nsei>> | NSEI was invalid count
|
||||||
|
| lost:alive | <<ns:nsvc_lost:alive>> | ALIVE ACK missing count
|
||||||
|
| lost:reset | <<ns:nsvc_lost:reset>> | RESET ACK missing count
|
||||||
|
|===
|
||||||
|
// rate_ctr_group table NSVC Peer Statistics
|
||||||
|
.ns:nsvc - NSVC Peer Statistics
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description
|
||||||
|
| packets:in | <<ns:nsvc_packets:in>> | Packets at NS Level ( In)
|
||||||
|
| packets:out | <<ns:nsvc_packets:out>> | Packets at NS Level (Out)
|
||||||
|
| bytes:in | <<ns:nsvc_bytes:in>> | Bytes at NS Level ( In)
|
||||||
|
| bytes:out | <<ns:nsvc_bytes:out>> | Bytes at NS Level (Out)
|
||||||
|
| blocked | <<ns:nsvc_blocked>> | NS-VC Block count
|
||||||
|
| dead | <<ns:nsvc_dead>> | NS-VC gone dead count
|
||||||
|
| replaced | <<ns:nsvc_replaced>> | NS-VC replaced other count
|
||||||
|
| nsei-chg | <<ns:nsvc_nsei-chg>> | NS-VC changed NSEI count
|
||||||
|
| inv-nsvci | <<ns:nsvc_inv-nsvci>> | NS-VCI was invalid count
|
||||||
|
| inv-nsei | <<ns:nsvc_inv-nsei>> | NSEI was invalid count
|
||||||
|
| lost:alive | <<ns:nsvc_lost:alive>> | ALIVE ACK missing count
|
||||||
|
| lost:reset | <<ns:nsvc_lost:reset>> | RESET ACK missing count
|
||||||
|
|===
|
||||||
|
// rate_ctr_group table BTS Statistics
|
||||||
|
.bts - BTS Statistics
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description
|
||||||
|
| tbf:dl:alloc | <<bts_tbf:dl:alloc>> | TBF DL Allocated
|
||||||
|
| tbf:dl:freed | <<bts_tbf:dl:freed>> | TBF DL Freed
|
||||||
|
| tbf:dl:aborted | <<bts_tbf:dl:aborted>> | TBF DL Aborted
|
||||||
|
| tbf:ul:alloc | <<bts_tbf:ul:alloc>> | TBF UL Allocated
|
||||||
|
| tbf:ul:freed | <<bts_tbf:ul:freed>> | TBF UL Freed
|
||||||
|
| tbf:ul:aborted | <<bts_tbf:ul:aborted>> | TBF UL Aborted
|
||||||
|
| tbf:reused | <<bts_tbf:reused>> | TBF Reused
|
||||||
|
| tbf:alloc:algo-a | <<bts_tbf:alloc:algo-a>> | TBF Alloc Algo A
|
||||||
|
| tbf:alloc:algo-b | <<bts_tbf:alloc:algo-b>> | TBF Alloc Algo B
|
||||||
|
| tbf:failed:egprs-only | <<bts_tbf:failed:egprs-only>> | TBF Failed EGPRS-only
|
||||||
|
| rlc:sent | <<bts_rlc:sent>> | RLC Sent
|
||||||
|
| rlc:resent | <<bts_rlc:resent>> | RLC Resent
|
||||||
|
| rlc:restarted | <<bts_rlc:restarted>> | RLC Restarted
|
||||||
|
| rlc:stalled | <<bts_rlc:stalled>> | RLC Stalled
|
||||||
|
| rlc:nacked | <<bts_rlc:nacked>> | RLC Nacked
|
||||||
|
| rlc:final_block_resent | <<bts_rlc:final_block_resent>> | RLC Final Blk resent
|
||||||
|
| rlc:ass:timedout | <<bts_rlc:ass:timedout>> | RLC Assign Timeout
|
||||||
|
| rlc:ass:failed | <<bts_rlc:ass:failed>> | RLC Assign Failed
|
||||||
|
| rlc:ack:timedout | <<bts_rlc:ack:timedout>> | RLC Ack Timeout
|
||||||
|
| rlc:ack:failed | <<bts_rlc:ack:failed>> | RLC Ack Failed
|
||||||
|
| rlc:rel:timedout | <<bts_rlc:rel:timedout>> | RLC Release Timeout
|
||||||
|
| rlc:late-block | <<bts_rlc:late-block>> | RLC Late Block
|
||||||
|
| rlc:sent-dummy | <<bts_rlc:sent-dummy>> | RLC Sent Dummy
|
||||||
|
| rlc:sent-control | <<bts_rlc:sent-control>> | RLC Sent Control
|
||||||
|
| rlc:dl_bytes | <<bts_rlc:dl_bytes>> | RLC DL Bytes
|
||||||
|
| rlc:dl_payload_bytes | <<bts_rlc:dl_payload_bytes>> | RLC DL Payload Bytes
|
||||||
|
| rlc:ul_bytes | <<bts_rlc:ul_bytes>> | RLC UL Bytes
|
||||||
|
| rlc:ul_payload_bytes | <<bts_rlc:ul_payload_bytes>> | RLC UL Payload Bytes
|
||||||
|
| decode:errors | <<bts_decode:errors>> | Decode Errors
|
||||||
|
| sba:allocated | <<bts_sba:allocated>> | SBA Allocated
|
||||||
|
| sba:freed | <<bts_sba:freed>> | SBA Freed
|
||||||
|
| sba:timedout | <<bts_sba:timedout>> | SBA Timeout
|
||||||
|
| llc:timeout | <<bts_llc:timeout>> | Timedout Frames
|
||||||
|
| llc:dropped | <<bts_llc:dropped>> | Dropped Frames
|
||||||
|
| llc:scheduled | <<bts_llc:scheduled>> | Scheduled Frames
|
||||||
|
| llc:dl_bytes | <<bts_llc:dl_bytes>> | RLC encapsulated PDUs
|
||||||
|
| llc:ul_bytes | <<bts_llc:ul_bytes>> | full PDUs received
|
||||||
|
| rach:requests | <<bts_rach:requests>> | RACH requests
|
||||||
|
| 11bit_rach:requests | <<bts_11bit_rach:requests>> | 11BIT_RACH requests
|
||||||
|
| spb:uplink_first_segment | <<bts_spb:uplink_first_segment>> | First seg of UL SPB
|
||||||
|
| spb:uplink_second_segment | <<bts_spb:uplink_second_segment>> | Second seg of UL SPB
|
||||||
|
| spb:downlink_first_segment | <<bts_spb:downlink_first_segment>> | First seg of DL SPB
|
||||||
|
| spb:downlink_second_segment | <<bts_spb:downlink_second_segment>> | Second seg of DL SPB
|
||||||
|
| immediate:assignment_UL | <<bts_immediate:assignment_UL>> | Immediate Assign UL
|
||||||
|
| immediate:assignment_rej | <<bts_immediate:assignment_rej>> | Immediate Assign Rej
|
||||||
|
| immediate:assignment_DL | <<bts_immediate:assignment_DL>> | Immediate Assign DL
|
||||||
|
| channel:request_description | <<bts_channel:request_description>> | Channel Request Desc
|
||||||
|
| pkt:ul_assignment | <<bts_pkt:ul_assignment>> | Packet UL Assignment
|
||||||
|
| pkt:access_reject | <<bts_pkt:access_reject>> | Packet Access Reject
|
||||||
|
| pkt:dl_assignment | <<bts_pkt:dl_assignment>> | Packet DL Assignment
|
||||||
|
| ul:control | <<bts_ul:control>> | UL control Block
|
||||||
|
| ul:assignment_poll_timeout | <<bts_ul:assignment_poll_timeout>> | UL Assign Timeout
|
||||||
|
| ul:assignment_failed | <<bts_ul:assignment_failed>> | UL Assign Failed
|
||||||
|
| dl:assignment_timeout | <<bts_dl:assignment_timeout>> | DL Assign Timeout
|
||||||
|
| dl:assignment_failed | <<bts_dl:assignment_failed>> | DL Assign Failed
|
||||||
|
| pkt:ul_ack_nack_timeout | <<bts_pkt:ul_ack_nack_timeout>> | PUAN Poll Timeout
|
||||||
|
| pkt:ul_ack_nack_failed | <<bts_pkt:ul_ack_nack_failed>> | PUAN poll Failed
|
||||||
|
| pkt:dl_ack_nack_timeout | <<bts_pkt:dl_ack_nack_timeout>> | PDAN poll Timeout
|
||||||
|
| pkt:dl_ack_nack_failed | <<bts_pkt:dl_ack_nack_failed>> | PDAN poll Failed
|
||||||
|
| gprs:downlink_cs1 | <<bts_gprs:downlink_cs1>> | CS1 downlink
|
||||||
|
| gprs:downlink_cs2 | <<bts_gprs:downlink_cs2>> | CS2 downlink
|
||||||
|
| gprs:downlink_cs3 | <<bts_gprs:downlink_cs3>> | CS3 downlink
|
||||||
|
| gprs:downlink_cs4 | <<bts_gprs:downlink_cs4>> | CS4 downlink
|
||||||
|
| egprs:downlink_mcs1 | <<bts_egprs:downlink_mcs1>> | MCS1 downlink
|
||||||
|
| egprs:downlink_mcs2 | <<bts_egprs:downlink_mcs2>> | MCS2 downlink
|
||||||
|
| egprs:downlink_mcs3 | <<bts_egprs:downlink_mcs3>> | MCS3 downlink
|
||||||
|
| egprs:downlink_mcs4 | <<bts_egprs:downlink_mcs4>> | MCS4 downlink
|
||||||
|
| egprs:downlink_mcs5 | <<bts_egprs:downlink_mcs5>> | MCS5 downlink
|
||||||
|
| egprs:downlink_mcs6 | <<bts_egprs:downlink_mcs6>> | MCS6 downlink
|
||||||
|
| egprs:downlink_mcs7 | <<bts_egprs:downlink_mcs7>> | MCS7 downlink
|
||||||
|
| egprs:downlink_mcs8 | <<bts_egprs:downlink_mcs8>> | MCS8 downlink
|
||||||
|
| egprs:downlink_mcs9 | <<bts_egprs:downlink_mcs9>> | MCS9 downlink
|
||||||
|
| gprs:uplink_cs1 | <<bts_gprs:uplink_cs1>> | CS1 Uplink
|
||||||
|
| gprs:uplink_cs2 | <<bts_gprs:uplink_cs2>> | CS2 Uplink
|
||||||
|
| gprs:uplink_cs3 | <<bts_gprs:uplink_cs3>> | CS3 Uplink
|
||||||
|
| gprs:uplink_cs4 | <<bts_gprs:uplink_cs4>> | CS4 Uplink
|
||||||
|
| egprs:uplink_mcs1 | <<bts_egprs:uplink_mcs1>> | MCS1 Uplink
|
||||||
|
| egprs:uplink_mcs2 | <<bts_egprs:uplink_mcs2>> | MCS2 Uplink
|
||||||
|
| egprs:uplink_mcs3 | <<bts_egprs:uplink_mcs3>> | MCS3 Uplink
|
||||||
|
| egprs:uplink_mcs4 | <<bts_egprs:uplink_mcs4>> | MCS4 Uplink
|
||||||
|
| egprs:uplink_mcs5 | <<bts_egprs:uplink_mcs5>> | MCS5 Uplink
|
||||||
|
| egprs:uplink_mcs6 | <<bts_egprs:uplink_mcs6>> | MCS6 Uplink
|
||||||
|
| egprs:uplink_mcs7 | <<bts_egprs:uplink_mcs7>> | MCS7 Uplink
|
||||||
|
| egprs:uplink_mcs8 | <<bts_egprs:uplink_mcs8>> | MCS8 Uplink
|
||||||
|
| egprs:uplink_mcs9 | <<bts_egprs:uplink_mcs9>> | MCS9 Uplink
|
||||||
|
|===
|
||||||
|
// generating tables for osmo_stat_items
|
||||||
|
NSVC Peer Statistics
|
||||||
|
// osmo_stat_item_group table NSVC Peer Statistics
|
||||||
|
.ns.nsvc - NSVC Peer Statistics
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description | Unit
|
||||||
|
| alive.delay | <<ns.nsvc_alive.delay>> | ALIVE response time | ms
|
||||||
|
|===
|
||||||
|
NSVC Peer Statistics
|
||||||
|
// osmo_stat_item_group table NSVC Peer Statistics
|
||||||
|
.ns.nsvc - NSVC Peer Statistics
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description | Unit
|
||||||
|
| alive.delay | <<ns.nsvc_alive.delay>> | ALIVE response time | ms
|
||||||
|
|===
|
||||||
|
BTS Statistics
|
||||||
|
// osmo_stat_item_group table BTS Statistics
|
||||||
|
.bts - BTS Statistics
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description | Unit
|
||||||
|
| ms.present | <<bts_ms.present>> | MS Present |
|
||||||
|
|===
|
||||||
|
// generating tables for osmo_counters
|
||||||
|
// ungrouped osmo_counters
|
||||||
|
.ungrouped osmo counters
|
||||||
|
[options="header"]
|
||||||
|
|===
|
||||||
|
| Name | Reference | Description
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
68
doc/manuals/chapters/overview.adoc
Normal file
68
doc/manuals/chapters/overview.adoc
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
== Overview
|
||||||
|
|
||||||
|
=== About OsmoPCU
|
||||||
|
|
||||||
|
OsmoPCU is the Osmocom implementation of the GPRS PCU (Packet Control
|
||||||
|
Unit) element inside the GPRS network.
|
||||||
|
|
||||||
|
The OsmoPCU is co-located within the BTS and connects to OsmoBTS via its
|
||||||
|
PCU socket interface.
|
||||||
|
|
||||||
|
On the other side, OsmoPCU is connected via the Gb interface to the
|
||||||
|
SGSN.
|
||||||
|
|
||||||
|
[[fig-gprs-pcubts]]
|
||||||
|
.GPRS network architecture with PCU in BTS
|
||||||
|
[graphviz]
|
||||||
|
----
|
||||||
|
digraph G {
|
||||||
|
rankdir=LR;
|
||||||
|
MS0 [label="MS"]
|
||||||
|
MS1 [label="MS"]
|
||||||
|
MS0->BTS [label="Um"]
|
||||||
|
MS1->BTS [label="Um"]
|
||||||
|
BTS->BSC [label="Abis"]
|
||||||
|
BSC->MSC [label="A"]
|
||||||
|
BTS->PCU [label="pcu_sock"]
|
||||||
|
PCU->SGSN [label="Gb"]
|
||||||
|
SGSN->GGSN [label="GTP"]
|
||||||
|
PCU [color=red]
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Software Components
|
||||||
|
|
||||||
|
OsmoPCU consists of a variety of components, including
|
||||||
|
|
||||||
|
* Gb interface (NS/BSSGP protocol)
|
||||||
|
* `pcu_sock` interface towards OsmoBTS
|
||||||
|
* TBF management for uplink and downlink TBF
|
||||||
|
* RLC/MAC protocol implementation
|
||||||
|
* per-MS context for each MS currently served
|
||||||
|
* CSN.1 encoding/decoding routines
|
||||||
|
|
||||||
|
==== Gb Implementation
|
||||||
|
|
||||||
|
OsmoPCU implements the ETSI/3GPP specified Gb interface, including TS
|
||||||
|
08.16 (NS), TS 08.18 (BSSGP) protocols. As transport layer for NS, it
|
||||||
|
supports NS/IP (NS encapsulated in UDP/IP).
|
||||||
|
|
||||||
|
The actual Gb Implementation is part of the libosmogb library, which is
|
||||||
|
in turn part of the libosmocore software package. This allows the same
|
||||||
|
Gb implementation to be used from OsmoPCU, OsmoGbProxy as well as
|
||||||
|
OsmoSGSN.
|
||||||
|
|
||||||
|
==== `pcu_sock` Interface to OsmoBTS
|
||||||
|
|
||||||
|
The interface towards OsmoBTS is called 'pcu_sock' and implemented as a
|
||||||
|
set of non-standardized primitives over a unix domain socket. The
|
||||||
|
default file system path for this socket is `/tmp/pcu_bts`.
|
||||||
|
|
||||||
|
The PCU socket can be changed on both OsmoBTS and OsmoPCU to a different
|
||||||
|
file/path name, primarily to permit running multiple independent BTS+PCU
|
||||||
|
pairs on a single Linux machine without having to use filesystem
|
||||||
|
namespaces or other complex configurations.
|
||||||
|
|
||||||
|
NOTE: If you change the PCU socket path on OsmoBTS by means of the
|
||||||
|
`pcu-socket` VTY configuration command, you must ensure to make the
|
||||||
|
identical change on the OsmoPCU side.
|
||||||
44
doc/manuals/chapters/qos-example.adoc
Normal file
44
doc/manuals/chapters/qos-example.adoc
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
==== Full example of QoS for osmo-pcu uplink QoS
|
||||||
|
|
||||||
|
In the below example we will show the full set of configuration required
|
||||||
|
for both DSCP and PCP differentiation of uplink Gb traffic by osmo-pcu.
|
||||||
|
|
||||||
|
What we want to achieve in this example is the following configuration:
|
||||||
|
|
||||||
|
.DSCP and PCP assignments for osmo-bts uplink traffic in this example
|
||||||
|
[options="header",width="30%",cols="2,1,1"]
|
||||||
|
|===
|
||||||
|
|Traffic |DSCP|PCP
|
||||||
|
|Gb (NS) | 10| 1
|
||||||
|
|===
|
||||||
|
|
||||||
|
. configure the osmocom program to set the DSCP value
|
||||||
|
* osmo-pcu.cfg: `dscp 10` in `udp bind` vty node
|
||||||
|
. configure an egrees QoS map to map from priority to PCP
|
||||||
|
|
||||||
|
.Example Step 1: add related VTY configuration to `osmo-pcu.cfg`
|
||||||
|
----
|
||||||
|
...
|
||||||
|
pcu
|
||||||
|
gb ip-dscp 10
|
||||||
|
gb socket-priority 1
|
||||||
|
...
|
||||||
|
----
|
||||||
|
|
||||||
|
.Example Step 2: egress QoS map to map from DSCP values to priority values
|
||||||
|
----
|
||||||
|
$ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 1:1 5:5 6:6 7:7 <2>
|
||||||
|
----
|
||||||
|
<1> make sure to specify your specific VLAN interface name here instead of `eth0.9`.
|
||||||
|
<2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also
|
||||||
|
include the mappings for 5, 6, and 7 from the osmo-bts example here (see
|
||||||
|
<<userman-osmobts>>).
|
||||||
|
|
||||||
|
NOTE:: The settings of the `ip` command are volatile and only active until
|
||||||
|
the next reboot (or the network device or VLAN is removed). Please refer to
|
||||||
|
the documentation of your specific Linux distribution in order to find out how
|
||||||
|
to make such settings persistent by means of an `ifup` hook whenever the interface
|
||||||
|
comes up. For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local
|
||||||
|
script` (when using `network-scripts` and not NetworkManager). For Debian or Ubuntu,
|
||||||
|
this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d`
|
||||||
|
script.
|
||||||
35
doc/manuals/chapters/running.adoc
Normal file
35
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
== Running OsmoPCU
|
||||||
|
|
||||||
|
The OsmoPCU executable (`osmo-pcu`) offers the following command-line
|
||||||
|
options:
|
||||||
|
|
||||||
|
|
||||||
|
=== SYNOPSIS
|
||||||
|
|
||||||
|
*osmo-pcu* [-h|-V] [-D] [-c 'CONFIGFILE'] [-r 'PRIO'] [-m 'MCC'] [-n 'MNC'] [-i A.B.C.D]
|
||||||
|
|
||||||
|
|
||||||
|
=== OPTIONS
|
||||||
|
|
||||||
|
*-h, --help*::
|
||||||
|
Print a short help message about the supported options
|
||||||
|
*-V, --version*::
|
||||||
|
Print the compile-time version number of the program
|
||||||
|
*-D, --daemonize*::
|
||||||
|
Fork the process as a daemon into background.
|
||||||
|
*-c, --config-file 'CONFIGFILE'*::
|
||||||
|
Specify the file and path name of the configuration file to be
|
||||||
|
used. If none is specified, use `osmo-pcu.cfg` in the current
|
||||||
|
working directory.
|
||||||
|
*-r, --realtime 'PRIO'*::
|
||||||
|
Enable use of the Linux kernel realtime priority scheduler with
|
||||||
|
the specified priority.
|
||||||
|
It is recommended you use this option on low-performance
|
||||||
|
embedded systems or systems that encounter high non-GSM/GPRS
|
||||||
|
load.
|
||||||
|
*-m, --mcc 'MCC'*::
|
||||||
|
Use the given MCC instead of that provided by BTS via PCU socket
|
||||||
|
*-n, --mnc 'MNC'*::
|
||||||
|
Use the given MNC instead of that provided by BTS via PCU socket
|
||||||
|
*-i, --gsmtap-ip 'A.B.C.D'*::
|
||||||
|
Send Um interface trace via GSMTAP to specified IP address
|
||||||
501
doc/manuals/gb/bssgp.adoc
Normal file
501
doc/manuals/gb/bssgp.adoc
Normal file
@@ -0,0 +1,501 @@
|
|||||||
|
[[bssgp]]
|
||||||
|
== BSS GPRS Protocol (BSSGP)
|
||||||
|
|
||||||
|
=== List of Messages
|
||||||
|
|
||||||
|
The following tables list the BSSGP messages used by OsmoPCU, grouped
|
||||||
|
by their level of compliance with 3GPP TS 48.018.
|
||||||
|
|
||||||
|
==== Messages Compliant With TS 48.018
|
||||||
|
|
||||||
|
.Messages compliant with TS 48.018
|
||||||
|
[options="header",cols="10%,10%,20%,35%,5%,20%"]
|
||||||
|
|===
|
||||||
|
| TS 48.018 § | type code (hex) | This document § | Message | <-/-> | Received/Sent by OsmoPCU
|
||||||
|
6+<| *RL and BSSGP SAP Messages:*
|
||||||
|
| 10.2.1 | 0x00 | <<dl_unit_data>> | DL-UNITDATA | <- | Received
|
||||||
|
| 10.2.2 | 0x01 | <<ul_unit_data>> | UL-UNITDATA | -> | Sent
|
||||||
|
| 10.2.3 | 0x02 | <<ra_capab>> | RA-CAPABILITY | <- | Received
|
||||||
|
6+<| *GMM SAP Messages:*
|
||||||
|
| 10.3.1 | 0x06 | <<paging_ps>> | PAGING PS | <- | Received
|
||||||
|
| 10.3.2 | 0x07 | <<paging_cs>> | PAGING CS | <- | Received
|
||||||
|
| 10.3.7 | 0x0c | <<susp_ack>> | SUSPEND-ACK | <- | Received
|
||||||
|
| 10.3.8 | 0x0d | <<susp_nack>> | SUSPEND-NACK | <- | Received
|
||||||
|
| 10.3.10 | 0x0f | <<res_ack>> | RESUME-ACK | <- | Received
|
||||||
|
| 10.3.11 | 0x10 | <<res_nack>> | RESUME-NACK | <- | Received
|
||||||
|
6+<| *NM SAP Messages:*
|
||||||
|
| 10.4.9 | 0x21 | <<block_ack>> | BVC-BLOCK-ACK | <- | Received
|
||||||
|
| 10.4.12 | 0x22 | <<bvc_reset>> | BVC-RESET | <-/-> | Received/Sent
|
||||||
|
| 10.4.13 | 0x23 | <<reset_ack>> | BVC-RESET-ACK | <- | Received
|
||||||
|
| 10.4.10 | 0x24 | <<bvc_unblock>> | BVC-UNBLOCK | -> | Sent
|
||||||
|
| 10.4.11 | 0x25 | <<unblock_ack>> | BVC-UNBLOCK-ACK | <- | Received
|
||||||
|
| 10.4.4 | 0x26 | <<flow_bvc>> | FLOW-CONTROL-BVC | -> | Sent
|
||||||
|
| 10.4.5 | 0x27 | <<flow_bvc_ack>> | FLOW-CONTROL-BVC-ACK | <- | Received
|
||||||
|
| 10.4.7 | 0x29 | <<flow_ms_ack>> | FLOW-CONTROL-MS-ACK | <- | Received
|
||||||
|
| 10.4.1 | 0x2a | <<flush_ll>> | FLUSH-LL | <- | Received
|
||||||
|
| 10.4.15 | 0x40 | <<invoke_trace>> | SGSN-INVOKE-TRACE | <- | Received
|
||||||
|
| 10.4.14 | 0x41 | <<bssgp_status>> | STATUS | <-/-> | Received/Sent
|
||||||
|
|===
|
||||||
|
|
||||||
|
==== Messages Specific to OsmoPCU
|
||||||
|
|
||||||
|
There are no OsmoPCU specific BSSGP messages.
|
||||||
|
|
||||||
|
[[not_impl]]
|
||||||
|
==== Messages Not Implemented by OsmoPCU
|
||||||
|
|
||||||
|
.3GPP TS 48.018 messages not implemented by OsmoPCU
|
||||||
|
[options="header",cols="10%,10%,80%"]
|
||||||
|
|===
|
||||||
|
| TS 48.018 § | type code (hex) | Message
|
||||||
|
3+<| *RL (relay) and BSSGP SAP Messages:*
|
||||||
|
| 10.2.4 | 0x03 | PTM-UNITDATA
|
||||||
|
3+<| *GMM (GPRS mobility management) SAP Messages:*
|
||||||
|
| 10.3.3 | 0x08 | RA-CAPABILITY-UPDATE
|
||||||
|
| 10.3.4 | 0x09 | RA-CAPABILITY-UPDATE-ACK
|
||||||
|
| 10.3.5 | 0x0a | RADIO-STATUS
|
||||||
|
| 10.3.6 | 0x0b | SUSPEND
|
||||||
|
| 10.3.9 | 0x0e | RESUME
|
||||||
|
3+<| *NM (network management) SAP Messages:*
|
||||||
|
| 10.4.8 | 0x20 | BVC-BLOCK
|
||||||
|
| 10.4.6 | 0x28 | FLOW-CONTROL-MS
|
||||||
|
| 10.4.2 | 0x2b | FLUSH-LL-ACK
|
||||||
|
| 10.4.3 | 0x2c | LLC-DISCARDED
|
||||||
|
3+<| *PFM (packet flow management) SAP Messages:*
|
||||||
|
| 10.4.16 | 0x50 | DOWNLOAD-BSS-PFC
|
||||||
|
| 10.4.17 | 0x51 | CREATE-BSS-PFC
|
||||||
|
| 10.4.18 | 0x52 | CREATE-BSS-PFC-ACK
|
||||||
|
| 10.4.19 | 0x53 | CREATE-BSS-PFC-NACK
|
||||||
|
| 10.4.20 | 0x54 | MODIFY-BSS-PFC
|
||||||
|
| 10.4.21 | 0x55 | MODIFY-BSS-PFC-ACK
|
||||||
|
| 10.4.22 | 0x56 | DELETE-BSS-PFC
|
||||||
|
| 10.4.23 | 0x57 | DELETE-BSS-PFC-ACK
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
=== Details on Compliant BSSGP Messages
|
||||||
|
|
||||||
|
[[dl_unit_data]]
|
||||||
|
==== DL-UNITDATA
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.2.1, with the following
|
||||||
|
limitations:
|
||||||
|
|
||||||
|
* OsmoPCU does not support QoS
|
||||||
|
* all optional IEs except for IMSI and old TLLI are ignored.
|
||||||
|
|
||||||
|
._DL-UNITDATA_ IE limitations
|
||||||
|
[options="header",cols="10%,30%,60%"]
|
||||||
|
|===
|
||||||
|
| TS 48.018 § | IE Name | Handling
|
||||||
|
| 11.3.28 | QoS Profile | _ignored_
|
||||||
|
| 11.3.22 | MS Radio Access Capability | _ignored_
|
||||||
|
| 11.3.27 | Priority | _ignored_
|
||||||
|
| 11.3.11 | DRX Parameters | _ignored_
|
||||||
|
| 1.3.42 | PFI | _ignored_
|
||||||
|
| 11.3.19 | LSA Information | _ignored_
|
||||||
|
| 11.3.47 | Service UTRAN CCO | _ignored_
|
||||||
|
|===
|
||||||
|
|
||||||
|
[[ul_unit_data]]
|
||||||
|
==== UL-UNITDATA
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.2.2, with the following limitations:
|
||||||
|
|
||||||
|
* OsmoPCU does not send optional IEs - PFI (§ 12.3.42) and LSA
|
||||||
|
Identifier List (§ 11.3.18).
|
||||||
|
* QoS Profile (§ 11.3.28) IE is always set to 0x04.
|
||||||
|
|
||||||
|
[[ra_capab]]
|
||||||
|
==== RA-CAPABILITY
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the moment.
|
||||||
|
|
||||||
|
[[paging_ps]]
|
||||||
|
==== PAGING PS
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.3.1, with the following
|
||||||
|
limitations:
|
||||||
|
|
||||||
|
* only IMSI and P-TMSI are parsed by OsmoPCU.
|
||||||
|
|
||||||
|
._DL-UNITDATA_ IE limitations
|
||||||
|
[options="header",cols="10%,30%,60%"]
|
||||||
|
|===
|
||||||
|
| TS 48.018 § | IE Name | Handling
|
||||||
|
| 11.3.11 | DRX Parameters | _ignored_
|
||||||
|
| 11.3.6 | BVCI | _ignored_
|
||||||
|
| 11.3.17 | Location Are | _ignored_
|
||||||
|
| 11.3.31 | Routeing Area | _ignored_
|
||||||
|
| 11.3.3 | BSS Area Indication | _ignored_
|
||||||
|
| 11.3.42 | PFI | _ignored_
|
||||||
|
| 11.3.43 | ABQP | _ignored_
|
||||||
|
| 11.3.28 | QoS Profile | _ignored_
|
||||||
|
| 11.3.36 | P-TMSI | treated as mandatory (in case of absence paging with 0-length P-TMSI will be sent)
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[[paging_cs]]
|
||||||
|
==== PAGING CS
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[susp_ack]]
|
||||||
|
==== SUSPEND-ACK
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[susp_nack]]
|
||||||
|
==== SUSPEND-NACK
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[res_ack]]
|
||||||
|
==== RESUME-ACK
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[res_nack]]
|
||||||
|
==== RESUME-NACK
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[block_ack]]
|
||||||
|
==== BVC-BLOCK-ACK
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[bvc_reset]]
|
||||||
|
==== BVC-RESET
|
||||||
|
|
||||||
|
OsmoPCU never transmits optional Feature bitmap (3GPP TS 48.018 §
|
||||||
|
11.3.40) IE.
|
||||||
|
|
||||||
|
Receiving BVC RESET will cause OsmoPCU to respond with "Unknown BVCI"
|
||||||
|
status message.
|
||||||
|
|
||||||
|
[[reset_ack]]
|
||||||
|
==== BVC-RESET-ACK
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.4.13.
|
||||||
|
|
||||||
|
After receiving it OsmoPCU completes the RESET procedure for BVC
|
||||||
|
according to 3GPP TS 48.018 § 8.4.
|
||||||
|
|
||||||
|
[[unblock_ack]]
|
||||||
|
==== BVC-UNBLOCK-ACK
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.4.11.
|
||||||
|
|
||||||
|
After receiving it OsmoPCU completes the RESET procedure for BVC
|
||||||
|
according to 3GPP TS 48.018 § 8.3.
|
||||||
|
|
||||||
|
[[bvc_unblock]]
|
||||||
|
==== BVC-UNBLOCK
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.4.10 and is send by
|
||||||
|
OsmoPCU as part of UNBLOCK procedure described in 3GPP TS 48.018 § 8.3.
|
||||||
|
|
||||||
|
[[flow_ms_ack]]
|
||||||
|
==== FLOW-CONTROL-MS-ACK
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[flow_bvc_ack]]
|
||||||
|
==== FLOW-CONTROL-BVC-ACK
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[flow_bvc]]
|
||||||
|
==== FLOW-CONTROL-BVC
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.4.4, with the following
|
||||||
|
limitations:
|
||||||
|
|
||||||
|
* OsmoPCU does not support Current Bucket Level (CBL) feature so
|
||||||
|
Bucket_Full Ratio (TS 48.018 § 11.3.46) IE is not transmitted as part
|
||||||
|
of this message.
|
||||||
|
|
||||||
|
[[flush_ll]]
|
||||||
|
==== FLUSH-LL
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[invoke_trace]]
|
||||||
|
==== SGSN-INVOKE-TRACE
|
||||||
|
|
||||||
|
This message is received and logged but ignored by OsmoPCU at the
|
||||||
|
moment.
|
||||||
|
|
||||||
|
[[bssgp_status]]
|
||||||
|
==== STATUS
|
||||||
|
|
||||||
|
This message conforms to 3GPP TS 48.018 § 10.4.14.
|
||||||
|
|
||||||
|
=== Information Elements Overview
|
||||||
|
|
||||||
|
All of the IEs handled by OsmoPCU are listed below, with limitations
|
||||||
|
and additions to 3GPP TS 48.018 specified in more detail.
|
||||||
|
|
||||||
|
==== IEs Conforming to 3GPP TS 48.018
|
||||||
|
|
||||||
|
The following Information Elements are accepted by OsmoPCU. Not all
|
||||||
|
IEs are actually evaluated.
|
||||||
|
|
||||||
|
.IEs conforming to 3GPP TS 48.018
|
||||||
|
[options="header",cols="5%,10%,40%,5%,40%"]
|
||||||
|
|===
|
||||||
|
| tag (hex) | TS 48.018 § | IE name | <-/-> | Received/Sent by OsmoPCU
|
||||||
|
| 0x00 | 11.3.1 | Alignment Octets | <-/-> | Received/Sent
|
||||||
|
| 0x01 | 11.3.2 | Bmax default MS | -> | Sent
|
||||||
|
| 0x02 | 11.3.3 | BSS Area Indication | <- | Received
|
||||||
|
| 0x03 | 11.3.4 | Bucket Leak Rate | -> | Sent
|
||||||
|
| 0x04 | 11.3.6 | BVCI | <-/-> | Received/Sent
|
||||||
|
| 0x05 | 11.3.5 | BVC Bucket Size | -> | Sent
|
||||||
|
| 0x06 | 11.3.7 | BVC Measurement | -> | Sent
|
||||||
|
| 0x07 | 11.3.8 | Cause | <-/-> | Received/Sent
|
||||||
|
| 0x08 | 11.3.9 | Cell Identifier | -> | Sent
|
||||||
|
| 0x09 | 11.3.10 | Channel needed | <- | Received
|
||||||
|
| 0x0a | 11.3.11 | DRX Parameters | <- | Received
|
||||||
|
| 0x0b | 11.3.12 | eMLPP-Priority | <- | Received
|
||||||
|
| 0x0c | 11.3.13 | Flush Action | <- | Received
|
||||||
|
| 0x0d | 11.3.14 | IMSI | <-/-> | Received/Sent
|
||||||
|
| 0x0e | 11.3.15 | LLC-PDU | <-/-> | Received/Sent
|
||||||
|
| 0x0f | 11.3.16 | LLC Frames Discarded | -> | Sent
|
||||||
|
| 0x10 | 11.3.17 | Location Area | <- | Received
|
||||||
|
| 0x11 | 11.3.20 | Mobile Id | <- | Received
|
||||||
|
| 0x12 | 11.3.21 | MS Bucket Size | -> | Sent
|
||||||
|
| 0x13 | 11.3.22 | MS Radio Access Capability | <- | Received
|
||||||
|
| 0x14 | 11.3.23 | OMC Id | <- | Received
|
||||||
|
| 0x15 | 11.3.24 | PDU In Error | <-/-> | Received/Sent
|
||||||
|
| 0x16 | 11.3.25 | PDU Lifetime | <- | Received
|
||||||
|
| 0x17 | 11.3.27 | Priority | <- | Received
|
||||||
|
| 0x19 | 11.3.29 | Radio Cause | -> | Sent
|
||||||
|
| 0x1a | 11.3.30 | RA-Cap-UPD-Cause | -> | Sent
|
||||||
|
| 0x1b | 11.3.31 | Routeing Area | <-/-> | Received/Sent
|
||||||
|
| 0x1c | 11.3.32 | R_default_MS | -> | Sent
|
||||||
|
| 0x1d | 11.3.33 | Suspend Reference Number | <-/-> | Received/Sent
|
||||||
|
| 0x1e | 11.3.34 | Tag | <-/-> | Received/Sent
|
||||||
|
| 0x1f | 11.3.35 | TLLI | <-/-> | Received/Sent
|
||||||
|
| 0x20 | 11.3.36 | TMSI | <-/-> | Received/Sent
|
||||||
|
| 0x21 | 11.3.37 | Trace Reference | <- | Received
|
||||||
|
| 0x22 | 11.3.38 | Trace Type | <- | Received
|
||||||
|
| 0x23 | 11.3.39 | TransactionId | <- | Received
|
||||||
|
| 0x24 | 11.3.40 | Trigger Id | <- | Received
|
||||||
|
| 0x25 | 11.3.41 | Number of octets affected | -> | Sent
|
||||||
|
| 0x26 | 11.3.18 | LSA Identifier List | -> | Sent
|
||||||
|
| 0x27 | 11.3.19 | LSA Information | <- | Received
|
||||||
|
| 0x28 | 11.3.42 | Packet Flow Identifier | <-/-> | Received/Sent
|
||||||
|
| 0x3a | 11.3.43 | Aggregate BSS QoS Profile (ABQP) | <-/-> | Received/Sent
|
||||||
|
| 0x3b | 11.3.45 | Feature Bitmap | <-/-> | Received/Sent
|
||||||
|
| 0x3c | 11.3.46 | Bucket_Full Ratio | -> | Sent
|
||||||
|
| 0x3d | 11.3.47 | Service UTRAN CCO (Cell Change Order) | <- | Received
|
||||||
|
|===
|
||||||
|
|
||||||
|
==== IEs Not Conforming to 3GPP TS 48.018
|
||||||
|
|
||||||
|
.IEs not conforming to 3GPP TS 48.018
|
||||||
|
[options="header",cols="5%,10%,30%,55%"]
|
||||||
|
|===
|
||||||
|
| tag (hex) | TS 48.018 § | IE name | Description
|
||||||
|
| 0x18 | 11.3.28 | QoS Profile | Received value is ignored. Sent value is hard-coded to 0x4 (3 octets).
|
||||||
|
|===
|
||||||
|
|
||||||
|
==== Additional Attributes and Parameters
|
||||||
|
|
||||||
|
There are no OsmoPCU specific additional Attributes and Parameters.
|
||||||
|
|
||||||
|
=== Details on IEs
|
||||||
|
|
||||||
|
==== BSS Area Indication
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU.
|
||||||
|
|
||||||
|
==== Bucket Leak Rate
|
||||||
|
|
||||||
|
The value used by OsmoPCU for this IE can be set through configuration
|
||||||
|
file or vty via "flow-control force-ms-leak-rate <1-6553500>" command.
|
||||||
|
|
||||||
|
==== BVC Bucket Size
|
||||||
|
|
||||||
|
The value used by OsmoPCU for this IE can be set through configuration file or vty via
|
||||||
|
"flow-control force-bvc-bucket-size <1-6553500>" command.
|
||||||
|
|
||||||
|
==== Channel needed
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<paging_cs>> for details.
|
||||||
|
|
||||||
|
==== DRX Parameters
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU.
|
||||||
|
|
||||||
|
==== eMLPP-Priority
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<paging_cs>> for details.
|
||||||
|
|
||||||
|
==== Flush Action
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<flush_ll>> for details.
|
||||||
|
|
||||||
|
==== LLC Frames Discarded
|
||||||
|
|
||||||
|
This IE is not available because entire message which contains it
|
||||||
|
(LLC-DISCARDED) is not implemented by OsmoPCU - see for <<not_impl>>
|
||||||
|
details.
|
||||||
|
|
||||||
|
==== Location Area
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU.
|
||||||
|
|
||||||
|
==== Mobile Id
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<invoke_trace>> for details.
|
||||||
|
|
||||||
|
==== MS Bucket Size
|
||||||
|
|
||||||
|
The value used by OsmoPCU for this IE can be set through configuration
|
||||||
|
file or vty via "flow-control force-ms-bucket-size <1-6553500>"
|
||||||
|
command.
|
||||||
|
|
||||||
|
==== MS Radio Access Capability
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU.
|
||||||
|
|
||||||
|
==== OMC Id
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<invoke_trace>> for details.
|
||||||
|
|
||||||
|
==== Priority
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU.
|
||||||
|
|
||||||
|
==== QoS Profile
|
||||||
|
|
||||||
|
No QoS is supported by OsmoPCU so this IE is ignored or safe default
|
||||||
|
used when mandatory.
|
||||||
|
|
||||||
|
==== Radio Cause
|
||||||
|
|
||||||
|
This IE is not available because entire message which contains it
|
||||||
|
(RADIO-STATUS) is not implemented by OsmoPCU - see for <<not_impl>>
|
||||||
|
details.
|
||||||
|
|
||||||
|
==== RA-Cap-UPD-Cause
|
||||||
|
|
||||||
|
This IE is not available because entire message which contains it
|
||||||
|
(RA-CAPABILITY-UPDATE-ACK) is not implemented by OsmoPCU - see for
|
||||||
|
<<not_impl>> details.
|
||||||
|
|
||||||
|
==== Routeing Area
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU upon receiving.
|
||||||
|
|
||||||
|
The messages which might require this IE to be send are not
|
||||||
|
implemented by OsmoPCU - see for <<not_impl>> details.
|
||||||
|
|
||||||
|
==== Suspend Reference Number
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU upon receiving.
|
||||||
|
|
||||||
|
The messages which might require this IE to be send are not
|
||||||
|
implemented by OsmoPCU - see for <<not_impl>> details.
|
||||||
|
|
||||||
|
==== Tag
|
||||||
|
|
||||||
|
This IE currently only used by OsmoPCU for Flow Control procedure (TS
|
||||||
|
48.018 § 8.2). In other cases it's either ignored or unavailable.
|
||||||
|
|
||||||
|
==== Trace Reference
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<invoke_trace>> for details.
|
||||||
|
|
||||||
|
==== Trace Type
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<invoke_trace>> for details.
|
||||||
|
|
||||||
|
==== TransactionId
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<invoke_trace>> for details.
|
||||||
|
|
||||||
|
==== Trigger Id
|
||||||
|
|
||||||
|
This IE is ignored because entire message which contains it is ignored
|
||||||
|
by OsmoPCU - see <<invoke_trace>> for details.
|
||||||
|
|
||||||
|
==== Number of octets affected
|
||||||
|
|
||||||
|
This IE is not available because the messages which contains it
|
||||||
|
(FLUSH-LL-ACK and LLC-DISCARDE) are not implemented by OsmoPCU - see
|
||||||
|
for <<not_impl>> details.
|
||||||
|
|
||||||
|
==== LSA Information
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU.
|
||||||
|
|
||||||
|
==== LSA Identifier List
|
||||||
|
|
||||||
|
This IE is not implemented by OsmoPCU.
|
||||||
|
|
||||||
|
==== Packet Flow Identifier
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU upon receiving.
|
||||||
|
|
||||||
|
The messages which might require this IE to be send are not
|
||||||
|
implemented by OsmoPCU - see for <<not_impl>> details.
|
||||||
|
|
||||||
|
==== Aggregate BSS QoS Profile (ABQP)
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU upon receiving.
|
||||||
|
|
||||||
|
The messages which might require this IE to be send are not
|
||||||
|
implemented by OsmoPCU - see for <<not_impl>> details.
|
||||||
|
|
||||||
|
==== Feature Bitmap
|
||||||
|
|
||||||
|
This IE is not implemented by OsmoPCU.
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU when received.
|
||||||
|
|
||||||
|
Absence of Feature Bitmap automatically disables optional features for
|
||||||
|
Network Service Entity (NSE) communicating with OsmoPCU.
|
||||||
|
|
||||||
|
==== Bucket_Full Ratio
|
||||||
|
|
||||||
|
This IE is not implemented by OsmoPCU.
|
||||||
|
|
||||||
|
==== Service UTRAN CCO (Cell Change Order)
|
||||||
|
|
||||||
|
This IE is ignored by OsmoPCU.
|
||||||
|
|
||||||
|
=== Gb BSSGP Initialization / PCU bring-up
|
||||||
|
|
||||||
|
The BSSGP initialization directly follows NS connection establishment
|
||||||
|
described in <<ns_init>>.
|
||||||
|
|
||||||
|
OsmoPCU allocates a BVC context for the BVCI given by OsmoBTS, which
|
||||||
|
in turn receives it from OsmoBSC or OsmoNITB via OML procedures.
|
||||||
|
|
||||||
|
In addition to the BVCI identifying the OsmoPCU side of BSSGP
|
||||||
|
connection, there is also special BVCI which is accepted by OsmoPCU in
|
||||||
|
accordance with 3GPP TS 48.018 § 5.4.1: BVCI = 0 represents signaling data
|
||||||
|
between SGSN and PCU in contrast to PTP (Peer-To-Peer) user's data.
|
||||||
|
|
||||||
|
The mapping between BSSGP PDUs and signaling or PTP BVCIs is available
|
||||||
|
in 3GPP TS 48.018 Table 5.4.
|
||||||
27
doc/manuals/gb/gb-startup.msc
Normal file
27
doc/manuals/gb/gb-startup.msc
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
msc {
|
||||||
|
hscale="1.2";
|
||||||
|
bsc [label="BSC"], bts [label="BTS"], pcu [label="PCU"], sgsn [label="SGSN"];
|
||||||
|
|
||||||
|
|||;
|
||||||
|
bts box bsc [label="A-bis OML connection"];
|
||||||
|
bsc => bts [label="Set OML Attrbibutes (NSVC,CELL)"];
|
||||||
|
bts rbox pcu [label="PCU Unix Domain Socket"];
|
||||||
|
pcu => bts [label="connect to PCU socket"];
|
||||||
|
pcu <: bts [label="Config. parameters"];
|
||||||
|
pcu rbox pcu [label="bind/connect UDP socket"];
|
||||||
|
pcu note sgsn [label="NS-over-IP (UDP port 23000)"];
|
||||||
|
pcu => sgsn [label="NS RESET"];
|
||||||
|
pcu <= sgsn [label="NS RESET ACK"];
|
||||||
|
...;
|
||||||
|
pcu => sgsn [label="NS UNBLOCK"];
|
||||||
|
pcu <= sgsn [label="NS UNBLOCK ACK"];
|
||||||
|
pcu box sgsn [label="NS link established"];
|
||||||
|
...;
|
||||||
|
pcu => sgsn [label="BVC RESET"];
|
||||||
|
pcu <= sgsn [label="BVC RESET ACK"];
|
||||||
|
...;
|
||||||
|
pcu => sgsn [label="BVC UNBLOCK"];
|
||||||
|
pcu <= sgsn [label="BVC UNBLOCK ACK"];
|
||||||
|
pcu box sgsn [label="BSSGP link established"];
|
||||||
|
|||;
|
||||||
|
}
|
||||||
278
doc/manuals/gb/ns.adoc
Normal file
278
doc/manuals/gb/ns.adoc
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
== Network Service (NS)
|
||||||
|
|
||||||
|
=== List of Messages
|
||||||
|
|
||||||
|
The following tables list the NS messages used by osmo-pcu and osmo-gbproxy, grouped by their level of
|
||||||
|
compliance with 3GPP TS 48.016.
|
||||||
|
|
||||||
|
==== Messages Compliant With 3GPP TS 48.016
|
||||||
|
|
||||||
|
The NS protocol is implemented inside libosmogb so none of the messages below are sent by OsmoPCU explicitly.
|
||||||
|
Instead corresponding functions from libosmogb are called which send and receive messages as necessary. See <<ns_init>> for details
|
||||||
|
on establishing NS connection.
|
||||||
|
|
||||||
|
.Messages compliant with 3GPP TS 48.016
|
||||||
|
[options="header",cols="10%,10%,20%,35%,5%,20%"]
|
||||||
|
|===
|
||||||
|
| TS 48.016 § | type code (hex) | This document § | Message | <-/-> | Received/Sent by OsmoPCU
|
||||||
|
| 9.2.1 | 0x0a | <<ns_alive>> | NS-ALIVE | <-/-> | Received/Sent
|
||||||
|
| 9.2.2 | 0x0b | <<ns_alive_ack>> | NS-ALIVE-ACK | <-/-> | Received/Sent
|
||||||
|
| 9.2.3 | 0x04 | <<ns_block>> | NS-BLOCK | <-/-> | Received/Sent
|
||||||
|
| 9.2.4 | 0x05 | <<ns_block_ack>> | NS-BLOCK-ACK | <-/-> | Received/Sent
|
||||||
|
| 9.2.5 | 0x02 | <<ns_reset>> | NS-RESET | <-/-> | Received/Sent
|
||||||
|
| 9.2.6 | 0x03 | <<ns_reset_ack>> | NS-RESET-ACK | <-/-> | Received/Sent
|
||||||
|
| 9.2.7 | 0x08 | <<ns_status>> | NS-STATUS | <-/-> | Received/Sent
|
||||||
|
| 9.2.8 | 0x06 | <<ns_unblock>> | NS-UNBLOCK | <-/-> | Received/Sent
|
||||||
|
| 9.2.9 | 0x07 | <<ns_unblock_ack>> | NS-UNBLOCK-ACK | <-/-> | Received/Sent
|
||||||
|
| 9.2.10 | 0x00 | <<ns_unit_data>> | NS-UNITDATA | <-/-> | Received/Sent
|
||||||
|
| 9.3.1 | 0x0c | <<sns_ack>> | SNS-ACK | <-/-> | Received/Sent
|
||||||
|
| 9.3.2 | 0x0d | <<sns_add>> | SNS-ADD | <-/-> | Received/Sent
|
||||||
|
| 9.3.3 | 0x0e | <<sns_changeweight>> | SNS-CHANGEWEIGHT | <-/-> | Received/Sent
|
||||||
|
| 9.3.4 | 0x0f | <<sns_config>> | SNS-CONFIG | <-/-> | Received/Sent
|
||||||
|
| 9.3.5 | 0x10 | <<sns_config_ack>> | SNS-CONFIG | <-/-> | Received/Sent
|
||||||
|
| 9.3.6 | 0x11 | <<sns_delete>> | SNS-DELETE | <-/-> | Received/Sent
|
||||||
|
| 9.3.7 | 0x12 | <<sns_size>> | SNS-SIZE | <-/-> | Received/Sent
|
||||||
|
| 9.3.8 | 0x13 | <<sns_size_ack>> | SNS-SIZE-ACK | <-/-> | Received/Sent
|
||||||
|
|===
|
||||||
|
|
||||||
|
==== Messages Specific to OsmoPCU
|
||||||
|
|
||||||
|
There are no OsmoPCU specific NS messages.
|
||||||
|
|
||||||
|
==== Messages Not Implemented by OsmoPCU
|
||||||
|
|
||||||
|
All the NS protocol messages from 3GPP TS 48.016 are implemented in OsmoPCU.
|
||||||
|
|
||||||
|
=== Details on Compliant NS Messages
|
||||||
|
|
||||||
|
[[ns_unit_data]]
|
||||||
|
==== NS-UNITDATA
|
||||||
|
|
||||||
|
This PDU transfers one NS SDU (specified in 3GPP TS 08.18) between
|
||||||
|
OsmoPCU and SGSN. Upon receiving it OsmoPCU passes it to BSSGP
|
||||||
|
implementation to handle. It is also sent by BSSGP as necessary - see
|
||||||
|
<<bssgp>> for details.
|
||||||
|
|
||||||
|
It contains BVCI (<<ie_bvci>>) and NS SDU (<<ie_nssdu>>) IEs.
|
||||||
|
|
||||||
|
[[ns_reset]]
|
||||||
|
==== NS-RESET
|
||||||
|
|
||||||
|
This message is send by OsmoPCU in order to initiate reset procedure
|
||||||
|
described in 3GPP TS 48.016 § 7.3. The expected reply is NS-RESET-ACK
|
||||||
|
(<<ns_reset_ack>>) message. If no expected reply is received in 3
|
||||||
|
seconds than the sending is retried up to 3 times. When this message
|
||||||
|
is received it is replied with NS-RESET-ACK (<<ns_reset_ack>>).
|
||||||
|
It might be ignored under conditions described in 3GPP TS 48.016 § 7.3.1.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.5 specification.
|
||||||
|
|
||||||
|
It contains Cause (<<ie_cause>>), NSVCI (<<ie_nsvci>>) and NSEI (<<ie_nsei>>) IEs.
|
||||||
|
|
||||||
|
[[ns_reset_ack]]
|
||||||
|
==== NS-RESET-ACK
|
||||||
|
|
||||||
|
This message is sent as a response to proper NS-RESET (<<ns_reset>>)
|
||||||
|
message initiating reset procedure.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.6 specification.
|
||||||
|
|
||||||
|
It contains NSVCI (<<ie_nsvci>>) and NSEI (<<ie_nsei>>) IEs.
|
||||||
|
|
||||||
|
[[ns_block]]
|
||||||
|
==== NS-BLOCK
|
||||||
|
|
||||||
|
Upon receiving this message corresponding NS-VC is marked as blocked
|
||||||
|
by OsmoPCU and NS-BLOCK-ACK (<<ns_block_ack>>) reply is transmitted.
|
||||||
|
When this message is sent by OsmoPCU corresponding NS-BLOCK-ACK
|
||||||
|
(<<ns_block_ack>>) reply is expected before NS-VC is actually marked
|
||||||
|
as blocked. This behavior follows the blocking procedure described in
|
||||||
|
3GPP TS 48.016 § 7.2.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.3 specification.
|
||||||
|
|
||||||
|
It contains Cause (<<ie_cause>>) and NSVCI (<<ie_nsvci>>) IEs.
|
||||||
|
|
||||||
|
[[ns_block_ack]]
|
||||||
|
==== NS-BLOCK-ACK
|
||||||
|
|
||||||
|
This message is sent by OsmoPCU automatically upon reception of
|
||||||
|
correct NS-BLOCK (<<ns_block>>) message. It is expected as a reply
|
||||||
|
for NS-BLOCK (<<ns_block>>) message sent by OsmoPCU.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.4 specification.
|
||||||
|
|
||||||
|
It contains NSVCI (<<ie_nsvci>>) IE.
|
||||||
|
|
||||||
|
[[ns_unblock]]
|
||||||
|
==== NS-UNBLOCK
|
||||||
|
|
||||||
|
Upon receiving this message corresponding NS-VC is unblocked by
|
||||||
|
OsmoPCU and NS-UNBLOCK-ACK (<<ns_unblock_ack>>) reply is sent. When
|
||||||
|
this message is sent by OsmoPCU corresponding NS-UNBLOCK-ACK
|
||||||
|
(<<ns_unblock_ack>>) reply is expected before NS-VC is actually marked
|
||||||
|
as unblocked. This behavior follows the blocking procedure described
|
||||||
|
in 3GPP TS 48.016 § 7.2.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.8 specification.
|
||||||
|
|
||||||
|
[[ns_unblock_ack]]
|
||||||
|
==== NS-UNBLOCK-ACK
|
||||||
|
|
||||||
|
Receiving this message notifies OsmoPCU that NS-VC unblocking request
|
||||||
|
is confirmed and thus NS-VC is marked as unblocked. This message is
|
||||||
|
also sent as a reply to NS-UNBLOCK (<<ns_unblock>>) message.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.9 specification.
|
||||||
|
|
||||||
|
[[ns_status]]
|
||||||
|
==== NS-STATUS
|
||||||
|
|
||||||
|
This message is sent to inform other party about error conditions as a
|
||||||
|
response to various unexpected PDUs or PDUs with unexpected/missing
|
||||||
|
data. If this message is received for unknown NS-VC it is ignored in
|
||||||
|
accordance with 3GPP TS 48.016 § 7.5.1, otherwise the error cause is
|
||||||
|
logged if present in NS-STATUS.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.7 specification.
|
||||||
|
|
||||||
|
It contains Cause (<<ie_cause>>) and might (depending on actual error)
|
||||||
|
contain NSVCI (<<ie_nsvci>>), NS PDU (<<ie_nspdu>>) and BVCI
|
||||||
|
(<<ie_bvci>>) IEs.
|
||||||
|
|
||||||
|
[[ns_alive]]
|
||||||
|
==== NS-ALIVE
|
||||||
|
|
||||||
|
This message is sent periodically to test connectivity according to
|
||||||
|
3GPP TS 48.016 § 4.5.3. The expected response is NS-ALIVE-ACK
|
||||||
|
(<<ns_alive_ack>>). If no such response arrives within given amount of
|
||||||
|
time (3 seconds) than another NS-ALIVE message is sent and failed test
|
||||||
|
attempt is recorded. After 10 failed attempts NS connection is
|
||||||
|
considered dead and OsmoPCU tries to reconnect.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.1 specification.
|
||||||
|
|
||||||
|
[[ns_alive_ack]]
|
||||||
|
==== NS-ALIVE-ACK
|
||||||
|
|
||||||
|
This message is sent automatically in reply to NS-ALIVE (<<ns_alive>>)
|
||||||
|
message.
|
||||||
|
|
||||||
|
The message conforms to 3GPP TS 48.016 § 9.2.2 specification.
|
||||||
|
|
||||||
|
[[sns_ack]]
|
||||||
|
==== SNS-ACK
|
||||||
|
|
||||||
|
[[sns_add]]
|
||||||
|
==== SNS-ADD
|
||||||
|
|
||||||
|
[[sns_changeweight]]
|
||||||
|
==== SNS-CHANGEWEIGHT
|
||||||
|
|
||||||
|
[[sns_config]]
|
||||||
|
==== SNS-CONFIG
|
||||||
|
|
||||||
|
[[sns_config_ack]]
|
||||||
|
==== SNS-CONFIG-ACK
|
||||||
|
|
||||||
|
[[sns_delete]]
|
||||||
|
==== SNS-DELETE
|
||||||
|
|
||||||
|
[[ssn_size]]
|
||||||
|
==== SNS-SIZE
|
||||||
|
|
||||||
|
[[sns_size_ack]]
|
||||||
|
==== SNS-SIZE-ACK
|
||||||
|
|
||||||
|
|
||||||
|
=== Information Elements Overview
|
||||||
|
|
||||||
|
All of the IEs handled by OsmoPCU are listed below, with limitations and
|
||||||
|
additions to 3GPP TS 48.016 specified in more detail.
|
||||||
|
|
||||||
|
==== IEs Conforming to 3GPP TS 48.016
|
||||||
|
|
||||||
|
The following Information Elements are accepted by OsmoPCU.
|
||||||
|
|
||||||
|
.IEs conforming to 3GPP TS 48.016
|
||||||
|
[options="header",cols="5%,10%,40%,5%,40%"]
|
||||||
|
|===
|
||||||
|
| tag (hex) | TS 48.016 § | IE name | <-/-> | Received/Sent by OsmoPCU
|
||||||
|
| 0x03 | 10.3.1 | BVCI | <-/-> | Received/Sent
|
||||||
|
| 0x00 | 10.3.2 | Cause | <-/-> | Received/Sent
|
||||||
|
| - | 10.3.2a | End Flag | <-/-> | Received/Sent
|
||||||
|
| 0x0b | 10.3.2b | IP Address | <-/-> | Received/Sent
|
||||||
|
| 0x05 | 10.3.2c | List of IP4 Elements | <-/-> | Received/Sent
|
||||||
|
| 0x06 | 10.3.2d | List of IP6 Elements | <-/-> | Received/Sent
|
||||||
|
| 0x07 | 10.3.2e | Maximum Number of NS-VCs | <-/-> | Received/Sent
|
||||||
|
| 0x08 | 10.3.2f | Number of IP4 Endpoints | <-/-> | Received/Sent
|
||||||
|
| 0x09 | 10.3.2g | Number of IP6 Endpoints | <-/-> | Received/Sent
|
||||||
|
| 0x02 | 10.3.3 | NS PDU | <-/-> | Received/Sent
|
||||||
|
| 0x01 | 10.3.5 | NSVCI | <-/-> | Received/Sent
|
||||||
|
| 0x04 | 10.3.6 | NSEI | <-/-> | Received/Sent
|
||||||
|
| - | 10.3.7 | PDU Type | <-/-> | Received/Sent
|
||||||
|
| 0x0a | 10.3.7a | Reset Flag | <-/-> | Received/Sent
|
||||||
|
| - | 10.3.8 | Spare Octet | <-/-> | Received/Sent
|
||||||
|
| - | 10.3.10 | Transaction ID | <-/-> | Received/Sent
|
||||||
|
|===
|
||||||
|
|
||||||
|
==== IEs Not Conforming to 3GPP TS 48.016
|
||||||
|
|
||||||
|
.IEs conforming to 3GPP TS 48.016
|
||||||
|
[options="header",cols="5%,10%,40%,5%,40%"]
|
||||||
|
|===
|
||||||
|
| tag (hex) | TS 48.016 § | IE name | <-/-> | Notice
|
||||||
|
| - | 10.3.9 | NS-SDU Control Bits | <-/-> | Not implemented yet
|
||||||
|
|===
|
||||||
|
|
||||||
|
All other IEs defined in 3GPP TS 48.016 § 10.3 are supported by OsmoPCU.
|
||||||
|
|
||||||
|
==== Additional Attributes and Parameters
|
||||||
|
|
||||||
|
There are no OsmoPCU specific additional Attributes and Parameters.
|
||||||
|
|
||||||
|
=== Details on IEs
|
||||||
|
|
||||||
|
[[ie_cause]]
|
||||||
|
==== Cause
|
||||||
|
|
||||||
|
This IE contains reason for a procedure or error as described in 3GPP TS 48.016 § 10.3.2.
|
||||||
|
|
||||||
|
[[ie_nsvci]]
|
||||||
|
==== NSVCI
|
||||||
|
|
||||||
|
This IE represents NSVCI identity described in <<ident>> and 3GPP TS 48.016 § 10.3.5.
|
||||||
|
|
||||||
|
[[ie_nspdu]]
|
||||||
|
==== NS PDU
|
||||||
|
|
||||||
|
This IE contains PDU (possibly truncated) which cause error described
|
||||||
|
in NS-STATUS message (<<ns_status>>) as described in 3GPP TS 48.016 §
|
||||||
|
10.3.3.
|
||||||
|
|
||||||
|
[[ie_nssdu]]
|
||||||
|
==== NS SDU
|
||||||
|
|
||||||
|
This IE contains BSSGP data - see <<bssgp>> for details.
|
||||||
|
|
||||||
|
[[ie_bvci]]
|
||||||
|
==== BVCI
|
||||||
|
|
||||||
|
This IE represents BSSGP identity described in <<ident>> and 3GPP TS 48.016
|
||||||
|
§ 10.3.1.
|
||||||
|
|
||||||
|
[[ie_nsei]]
|
||||||
|
==== NSEI
|
||||||
|
|
||||||
|
This IE represents NSEI identity described in <<ident>> and 3GPP TS 48.016 §
|
||||||
|
10.3.6.
|
||||||
|
|
||||||
|
[[ns_init]]
|
||||||
|
=== Gb NS Initialization / PCU bring-up
|
||||||
|
|
||||||
|
OsmoPCU binds and connects an UDP socket for NS using port numbers and IP
|
||||||
|
information given by OsmoBTS via the PCU socket. OsmoBTS in turn
|
||||||
|
receives this information from the BSC vi A-bis OML.
|
||||||
|
|
||||||
|
Following successful initialization of the UDP socket, the reset
|
||||||
|
procedure is initiated as described in <<ns_reset>>.
|
||||||
46
doc/manuals/osmopcu-gb-docinfo.xml
Normal file
46
doc/manuals/osmopcu-gb-docinfo.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<authorgroup>
|
||||||
|
<author>
|
||||||
|
<firstname>Max</firstname>
|
||||||
|
<surname>Suraev</surname>
|
||||||
|
<email>msuraev@sysmocom.de</email>
|
||||||
|
<authorinitials>MS</authorinitials>
|
||||||
|
<affiliation>
|
||||||
|
<shortaffil>sysmocom</shortaffil>
|
||||||
|
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||||
|
<jobtitle>Software Developer</jobtitle>
|
||||||
|
</affiliation>
|
||||||
|
</author>
|
||||||
|
<author>
|
||||||
|
<firstname>Harald</firstname>
|
||||||
|
<surname>Welte</surname>
|
||||||
|
<email>hwelte@sysmocom.de</email>
|
||||||
|
<authorinitials>HW</authorinitials>
|
||||||
|
<affiliation>
|
||||||
|
<shortaffil>sysmocom</shortaffil>
|
||||||
|
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||||
|
<jobtitle>Managing Director</jobtitle>
|
||||||
|
</affiliation>
|
||||||
|
</author>
|
||||||
|
</authorgroup>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2015-2021</year>
|
||||||
|
<holder>sysmocom - s.f.m.c. GmbH</holder>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>
|
||||||
|
Permission is granted to copy, distribute and/or modify this
|
||||||
|
document under the terms of the GNU Free Documentation License,
|
||||||
|
Version 1.3 or any later version published by the Free Software
|
||||||
|
Foundation; with no Invariant Sections, no Front-Cover Texts,
|
||||||
|
and no Back-Cover Texts. A copy of the license is included in
|
||||||
|
the section entitled "GNU Free Documentation License".
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The Asciidoc source code of this manual can be found at
|
||||||
|
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
|
||||||
|
http://git.osmocom.org/osmo-gsm-manuals/
|
||||||
|
</ulink>
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
95
doc/manuals/osmopcu-gb.adoc
Normal file
95
doc/manuals/osmopcu-gb.adoc
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
:gfdl-enabled:
|
||||||
|
|
||||||
|
OsmoPCU Gb Protocol Specification
|
||||||
|
=================================
|
||||||
|
Harald Welte <hwelte@sysmocom.de>
|
||||||
|
|
||||||
|
== Introduction
|
||||||
|
|
||||||
|
This document describes the Gb interface of *OsmoPCU*. Based on 3GPP TS
|
||||||
|
48.016 and 48.018, this document indicates which of the 3GPP specified Gb
|
||||||
|
messages and IEs are implemented according to 3GPP specifications, which of
|
||||||
|
these are not or not fully implemented, as well as OsmoPCU-specific extensions
|
||||||
|
to the Gb interface not specified by 3GPP.
|
||||||
|
|
||||||
|
Extensions to the Gb interface specific to OsmoPCU are detailed in this
|
||||||
|
document. For details on the messages and IEs that comply with above-mentioned
|
||||||
|
3GPP specifications, please refer to those documents.
|
||||||
|
|
||||||
|
.3GPP document versions referred to by this document
|
||||||
|
[cols="20%,80%"]
|
||||||
|
|===
|
||||||
|
|3GPP TS 08.56 | version 8.0.1 Release 1999
|
||||||
|
|3GPP TS 08.58 | version 8.6.0 Release 1999
|
||||||
|
|3GPP TS 08.60 | version 8.2.1 Release 1999
|
||||||
|
|3GPP TS 12.21 | version 8.0.0 Release 1999
|
||||||
|
|3GPP TS 48.016 | version 15.0.0 Release 15
|
||||||
|
|3GPP TS 48.018 | version 15.0.0 Release 15
|
||||||
|
|===
|
||||||
|
|
||||||
|
.IETF documents referred to by his document
|
||||||
|
[cols="20%,80%"]
|
||||||
|
|===
|
||||||
|
|IETF RFC 768 | User Datagram Protocol
|
||||||
|
|IETF RFC 791 | Internet Protocol
|
||||||
|
|===
|
||||||
|
|
||||||
|
== Overview
|
||||||
|
|
||||||
|
The OsmoPCU Gb interface consists of the NS (Network Services) and
|
||||||
|
BSSGP (Base Station Subsystem Gateway Protocol), encapsulated in UDP
|
||||||
|
(User Datagram Protocol) and IP (Internet Protocol) version 4.
|
||||||
|
Use of other underlying protocols (e. g. Frame Relay) is not supported.
|
||||||
|
|
||||||
|
.UDP port numbers used by OsmoPCU Gb/IP
|
||||||
|
[options="header",width="50%",cols="35%,65%"]
|
||||||
|
|===
|
||||||
|
|TCP Port Number|Usage
|
||||||
|
|23000|NS over UDP (default port)
|
||||||
|
|===
|
||||||
|
|
||||||
|
The NS-over-UDP link is established in the PCU -> SGSN direction, i.e.
|
||||||
|
the PCU is running as client while the SGSN is running as server.
|
||||||
|
|
||||||
|
Establishment of the NS-over-UDP link is only possible after OsmoPCU
|
||||||
|
has been configured via the *PCU socket* interface from OsmoBTS.
|
||||||
|
|
||||||
|
OsmoBTS in turn receives relevant configuration parameters from
|
||||||
|
OsmoBSC or the BSC component inside OsmoNITB.
|
||||||
|
|
||||||
|
.Overview of Gb link establishment
|
||||||
|
["mscgen"]
|
||||||
|
----
|
||||||
|
include::{srcdir}/gb/gb-startup.msc[]
|
||||||
|
----
|
||||||
|
|
||||||
|
[[ident]]
|
||||||
|
=== Identities
|
||||||
|
|
||||||
|
The Gb interface identities of the PCU are configured via BSC ->
|
||||||
|
OsmoBTS -> PCU Socket. They consist of
|
||||||
|
|
||||||
|
NSEI:: NS Equipment Identifier
|
||||||
|
NSVCI:: NS Virtual Connection Identifier
|
||||||
|
BVCI:: BSSGP Virtual Connection Identifier
|
||||||
|
|
||||||
|
For an explanation of those identifiers and their use in the NS and
|
||||||
|
BSSGP protocols, please see the relevant 3GPP specifications for NS (TS 48.016)
|
||||||
|
and BSSGP (TS 48.018).
|
||||||
|
|
||||||
|
In most cases, all above identities belong to different namespaces and
|
||||||
|
must be unique within their respective namespace and within the SGSN
|
||||||
|
they connect to.
|
||||||
|
|
||||||
|
This means that typically each OsmoPCU has one unique set of NSEI,
|
||||||
|
NSVCI and BVCI in your network.
|
||||||
|
|
||||||
|
include::{srcdir}/gb/ns.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/gb/bssgp.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/port_numbers.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/glossary.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/gfdl.adoc[]
|
||||||
35
doc/manuals/osmopcu-usermanual-docinfo.xml
Normal file
35
doc/manuals/osmopcu-usermanual-docinfo.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<authorgroup>
|
||||||
|
<author>
|
||||||
|
<firstname>Harald</firstname>
|
||||||
|
<surname>Welte</surname>
|
||||||
|
<email>hwelte@sysmocom.de</email>
|
||||||
|
<authorinitials>HW</authorinitials>
|
||||||
|
<affiliation>
|
||||||
|
<shortaffil>sysmocom</shortaffil>
|
||||||
|
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||||
|
<jobtitle>Managing Director</jobtitle>
|
||||||
|
</affiliation>
|
||||||
|
</author>
|
||||||
|
</authorgroup>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2013-2021</year>
|
||||||
|
<holder>sysmocom - s.f.m.c. GmbH</holder>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>
|
||||||
|
Permission is granted to copy, distribute and/or modify this
|
||||||
|
document under the terms of the GNU Free Documentation License,
|
||||||
|
Version 1.3 or any later version published by the Free Software
|
||||||
|
Foundation; with no Invariant Sections, no Front-Cover Texts,
|
||||||
|
and no Back-Cover Texts. A copy of the license is included in
|
||||||
|
the section entitled "GNU Free Documentation License".
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The Asciidoc source code of this manual can be found at
|
||||||
|
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
|
||||||
|
http://git.osmocom.org/osmo-gsm-manuals/
|
||||||
|
</ulink>
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
34
doc/manuals/osmopcu-usermanual.adoc
Normal file
34
doc/manuals/osmopcu-usermanual.adoc
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
:gfdl-enabled:
|
||||||
|
|
||||||
|
OsmoPCU User Manual
|
||||||
|
===================
|
||||||
|
Harald Welte <hwelte@sysmocom.de>
|
||||||
|
|
||||||
|
|
||||||
|
include::./common/chapters/preface.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/overview.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/running.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/vty.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/logging.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/configuration.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/counters.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/gb.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/qos-dscp-pcp.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/vty_cpu_sched.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/port_numbers.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/bibliography.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/glossary.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/gfdl.adoc[]
|
||||||
29
doc/manuals/osmopcu-vty-reference.xml
Normal file
29
doc/manuals/osmopcu-vty-reference.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
ex:ts=2:sw=42sts=2:et
|
||||||
|
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
|
-->
|
||||||
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
|
||||||
|
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
|
||||||
|
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml">
|
||||||
|
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
|
||||||
|
]>
|
||||||
|
|
||||||
|
<book>
|
||||||
|
<info>
|
||||||
|
|
||||||
|
<title>OsmoPCU VTY Reference</title>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2014-2021</year>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
<!-- Main chapters-->
|
||||||
|
&chapter-vty;
|
||||||
|
</book>
|
||||||
17
doc/manuals/regen_doc.sh
Executable file
17
doc/manuals/regen_doc.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh -x
|
||||||
|
|
||||||
|
if [ -z "$DOCKER_PLAYGROUND" ]; then
|
||||||
|
echo "You need to set DOCKER_PLAYGROUND"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT=$(realpath "$0")
|
||||||
|
MANUAL_DIR=$(dirname "$SCRIPT")
|
||||||
|
|
||||||
|
COMMIT=${COMMIT:-$(git log -1 --format=format:%H)}
|
||||||
|
|
||||||
|
cd "$DOCKER_PLAYGROUND/scripts" || exit 1
|
||||||
|
|
||||||
|
OSMO_PCU_BRANCH=$COMMIT ./regen_doc.sh osmo-pcu 4240 \
|
||||||
|
"$MANUAL_DIR/chapters/counters_generated.adoc" \
|
||||||
|
"$MANUAL_DIR/vty/osmo-pcu_vty_reference.xml"
|
||||||
9
doc/manuals/vty/osmo-pcu_vty_additions.xml
Normal file
9
doc/manuals/vty/osmo-pcu_vty_additions.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
|
||||||
|
<node id='14'>
|
||||||
|
<child_of id='4' />
|
||||||
|
<name>PCU Configuration Node</name>
|
||||||
|
<description>The main PCU configuration including the timeslot
|
||||||
|
assignment algorithm and other parameters.</description>
|
||||||
|
</node>
|
||||||
|
</vtydoc>
|
||||||
|
|
||||||
@@ -14,16 +14,16 @@ Notes:
|
|||||||
Queue of next frames to be transmitted.
|
Queue of next frames to be transmitted.
|
||||||
|
|
||||||
States:
|
States:
|
||||||
GPRS_RLCMAC_ASSIGN
|
TBF_ST_ASSIGN
|
||||||
After a downlink TBF is created, it resides in this state until the
|
After a downlink TBF is created, it resides in this state until the
|
||||||
block flow can start. This is required to give the mobile time to listen
|
block flow can start. This is required to give the mobile time to listen
|
||||||
to connect to downlink PDCH.
|
to connect to downlink PDCH.
|
||||||
|
|
||||||
GPRS_RLCMAC_FLOW,
|
TBF_ST_FLOW,
|
||||||
During packet flow, this state indicates downlink and uplink TBF block
|
During packet flow, this state indicates downlink and uplink TBF block
|
||||||
flow.
|
flow.
|
||||||
|
|
||||||
GPRS_RLCMAC_FINISHED,
|
TBF_ST_FINISHED,
|
||||||
Uplink TBF:
|
Uplink TBF:
|
||||||
After final block is received AND all other blocks are completely
|
After final block is received AND all other blocks are completely
|
||||||
received, the state is entered. The PACKET CONTROL ACK is still not
|
received, the state is entered. The PACKET CONTROL ACK is still not
|
||||||
@@ -33,11 +33,11 @@ States:
|
|||||||
downlink blocks are acknowledged yet. (Counter N3015 is counted on each
|
downlink blocks are acknowledged yet. (Counter N3015 is counted on each
|
||||||
poll request.)
|
poll request.)
|
||||||
|
|
||||||
GPRS_RLCMAC_WAIT_RELEASE,
|
TBF_ST_WAIT_RELEASE,
|
||||||
The all blocks on downlink TBF have been acked by mobile. The penalty
|
The all blocks on downlink TBF have been acked by mobile. The penalty
|
||||||
timer T3192 is running on mobile.
|
timer T3192 is running on mobile.
|
||||||
|
|
||||||
GPRS_RLCMAC_RELEASING,
|
TBF_ST_RELEASING,
|
||||||
Wait for TFI/USF to be re-used. This state is entered when a counter
|
Wait for TFI/USF to be re-used. This state is entered when a counter
|
||||||
reaches it's maximum and T3169 is running.
|
reaches it's maximum and T3169 is running.
|
||||||
|
|
||||||
@@ -117,13 +117,17 @@ Control TS:
|
|||||||
|
|
||||||
Polling:
|
Polling:
|
||||||
In order to poll uplink control block from MS, a special poll state and
|
In order to poll uplink control block from MS, a special poll state and
|
||||||
frame number is stored at TBF. The scheduler reads that value and will not
|
frame number is stored at PDCH UL Controller. The scheduler reads that value
|
||||||
assign uplink ressource for other TBFs at that frame number.
|
and will not assign uplink resource for other TBFs at that frame number.
|
||||||
|
|
||||||
When there is no uplink transmission received on the block, a timeout is
|
On receipt of an Uplink RLCMAC block, it's the duty of each specific message
|
||||||
indicated by layer 1 interface. There are two ways of checking timeout:
|
handler to release the expectancies previously stored in the PDCH UL
|
||||||
- The received frame is bad (BFI).
|
Controller. After the specific handler returns, the generic return path will
|
||||||
- The GSM indicates that the block should have been already received.
|
expire all reserved events up to the curent FN for that PDCH, eventually
|
||||||
|
calling timeout functions on TBFs and SBAs owning those reservations. Hence,
|
||||||
|
the layer 1 interface is expected to provide DATA.ind for each FN block,
|
||||||
|
containing data=0 if decoding failed, in order to keep the clock up-to-date in
|
||||||
|
upper layers.
|
||||||
|
|
||||||
Because polling requires uplink response from MS, the polling must be
|
Because polling requires uplink response from MS, the polling must be
|
||||||
performed at control TS.
|
performed at control TS.
|
||||||
@@ -131,13 +135,21 @@ Polling:
|
|||||||
|
|
||||||
Data structures of TBFs and PDCHs:
|
Data structures of TBFs and PDCHs:
|
||||||
|
|
||||||
There is a global structure for BTS.
|
There is a global structure for PCU (struct gprs_pcu the_pcu).
|
||||||
|
|
||||||
The BTS structure has 8 TRX structures.
|
A BTS is created and put into PCU's list for each PCUIF INFO_IND with a unique
|
||||||
|
bts_nr received.
|
||||||
|
|
||||||
|
Each BTS structure has 8 TRX structures.
|
||||||
|
|
||||||
Each TRX structure has 8 PDCH structures, one for each timeslot.
|
Each TRX structure has 8 PDCH structures, one for each timeslot.
|
||||||
|
|
||||||
There are two linked lists of TBF instances:
|
Each BTS structure has a list of MS (struct GprsMs).
|
||||||
|
|
||||||
|
Each MS can have 1 UL TBF and 1 DL TBF, and 1 TBF is always assigned to an
|
||||||
|
existing GprsMS structure.
|
||||||
|
|
||||||
|
Each BTS also has two linked lists of TBF instances:
|
||||||
- uplink TBFs
|
- uplink TBFs
|
||||||
- downlink TBFs
|
- downlink TBFs
|
||||||
|
|
||||||
@@ -147,7 +159,7 @@ Data structures of TBFs and PDCHs:
|
|||||||
- an array of 8 PDCH structures, one for each assigned timeslot
|
- an array of 8 PDCH structures, one for each assigned timeslot
|
||||||
- in case of uplink TBF: an array of 8 USFs, one for each assigned timeslot
|
- in case of uplink TBF: an array of 8 USFs, one for each assigned timeslot
|
||||||
|
|
||||||
Each PDCH structure also has:
|
Each TRX and all it's PDCH structures also have:
|
||||||
- an array of 32 uplink TBFs that are assigned to this PDCH
|
- an array of 32 uplink TBFs that are assigned to this PDCH
|
||||||
- an array of 32 downlink TBFs that are assigned to this PDCH
|
- an array of 32 downlink TBFs that are assigned to this PDCH
|
||||||
|
|
||||||
@@ -158,4 +170,6 @@ Data structures of TBFs and PDCHs:
|
|||||||
On release of a TBF, the link to this PDCH is removed from all assigned
|
On release of a TBF, the link to this PDCH is removed from all assigned
|
||||||
PDCHs. The TBF is removed from the list of TBFs. The TBF is destroyed.
|
PDCHs. The TBF is removed from the list of TBFs. The TBF is destroyed.
|
||||||
|
|
||||||
|
GprsMs structures are kept alive for a while once they become unused, in order to
|
||||||
|
have a cache of MS related information it would otherwise need to acquire
|
||||||
|
again once it interacts with the PCU again.
|
||||||
2
include/Makefile.am
Normal file
2
include/Makefile.am
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
noinst_HEADERS = \
|
||||||
|
osmocom/pcu/pcuif_proto.h
|
||||||
273
include/osmocom/pcu/pcuif_proto.h
Normal file
273
include/osmocom/pcu/pcuif_proto.h
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
#ifndef _PCUIF_PROTO_H
|
||||||
|
#define _PCUIF_PROTO_H
|
||||||
|
|
||||||
|
#include <osmocom/gsm/l1sap.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#define PCU_SOCK_DEFAULT "/tmp/pcu_bts"
|
||||||
|
|
||||||
|
#define PCU_IF_VERSION 0x0a
|
||||||
|
#define TXT_MAX_LEN 128
|
||||||
|
|
||||||
|
/* msg_type */
|
||||||
|
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
|
||||||
|
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
|
||||||
|
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
|
||||||
|
#define PCU_IF_MSG_SUSP_REQ 0x03 /* BTS forwards GPRS SUSP REQ to PCU */
|
||||||
|
#define PCU_IF_MSG_APP_INFO_REQ 0x04 /* BTS asks PCU to transmit APP INFO via PACCH */
|
||||||
|
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
|
||||||
|
#define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */
|
||||||
|
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
|
||||||
|
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
|
||||||
|
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
|
||||||
|
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
|
||||||
|
#define PCU_IF_MSG_INTERF_IND 0x53 /* interference report */
|
||||||
|
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
|
||||||
|
#define PCU_IF_MSG_TXT_IND 0x70 /* Text indication for BTS */
|
||||||
|
#define PCU_IF_MSG_CONTAINER 0x80 /* Transparent container message */
|
||||||
|
|
||||||
|
/* msg_type coming from BSC (inside PCU_IF_MSG_CONTAINER) */
|
||||||
|
#define PCU_IF_MSG_ANR_REQ 0x81 /* Automatic Neighbor Registration Request */
|
||||||
|
#define PCU_IF_MSG_ANR_CNF 0x82 /* Automatic Neighbor Registration Confirmation (meas results) */
|
||||||
|
|
||||||
|
/* sapi */
|
||||||
|
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
|
||||||
|
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
|
||||||
|
#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
|
||||||
|
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
|
||||||
|
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
|
||||||
|
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
|
||||||
|
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
|
||||||
|
#define PCU_IF_SAPI_AGCH_DT 0x08 /* assignment on AGCH but with additional TLLI */
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
|
||||||
|
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
|
||||||
|
#define PCU_IF_FLAG_CS1 (1 << 16)
|
||||||
|
#define PCU_IF_FLAG_CS2 (1 << 17)
|
||||||
|
#define PCU_IF_FLAG_CS3 (1 << 18)
|
||||||
|
#define PCU_IF_FLAG_CS4 (1 << 19)
|
||||||
|
#define PCU_IF_FLAG_MCS1 (1 << 20)
|
||||||
|
#define PCU_IF_FLAG_MCS2 (1 << 21)
|
||||||
|
#define PCU_IF_FLAG_MCS3 (1 << 22)
|
||||||
|
#define PCU_IF_FLAG_MCS4 (1 << 23)
|
||||||
|
#define PCU_IF_FLAG_MCS5 (1 << 24)
|
||||||
|
#define PCU_IF_FLAG_MCS6 (1 << 25)
|
||||||
|
#define PCU_IF_FLAG_MCS7 (1 << 26)
|
||||||
|
#define PCU_IF_FLAG_MCS8 (1 << 27)
|
||||||
|
#define PCU_IF_FLAG_MCS9 (1 << 28)
|
||||||
|
|
||||||
|
/* NSVC address type */
|
||||||
|
#define PCU_IF_ADDR_TYPE_UNSPEC 0x00 /* No address - empty entry */
|
||||||
|
#define PCU_IF_ADDR_TYPE_IPV4 0x04 /* IPv4 address */
|
||||||
|
#define PCU_IF_ADDR_TYPE_IPV6 0x29 /* IPv6 address */
|
||||||
|
|
||||||
|
#define PCU_IF_NUM_NSVC 2
|
||||||
|
|
||||||
|
enum gsm_pcu_if_text_type {
|
||||||
|
PCU_VERSION,
|
||||||
|
PCU_OML_ALERT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gsm_pcu_if_txt_ind {
|
||||||
|
uint8_t type; /* gsm_pcu_if_text_type */
|
||||||
|
char text[TXT_MAX_LEN]; /* Text to be transmitted to BTS */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_data {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint8_t len;
|
||||||
|
uint8_t data[162];
|
||||||
|
uint32_t fn;
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint8_t block_nr;
|
||||||
|
int8_t rssi;
|
||||||
|
uint16_t ber10k; /* !< \brief BER in units of 0.01% */
|
||||||
|
int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
|
||||||
|
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* data confirmation with direct tlli (instead of raw mac block with tlli) */
|
||||||
|
struct gsm_pcu_if_data_cnf_dt {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint32_t tlli;
|
||||||
|
uint32_t fn;
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint8_t block_nr;
|
||||||
|
int8_t rssi;
|
||||||
|
uint16_t ber10k; /* !< \brief BER in units of 0.01% */
|
||||||
|
int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
|
||||||
|
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_rts_req {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint8_t spare[3];
|
||||||
|
uint32_t fn;
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint8_t block_nr;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_rach_ind {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint16_t ra;
|
||||||
|
int16_t qta;
|
||||||
|
uint32_t fn;
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t is_11bit;
|
||||||
|
uint8_t burst_type;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_info_ts {
|
||||||
|
uint8_t tsc;
|
||||||
|
uint8_t h;
|
||||||
|
uint8_t hsn;
|
||||||
|
uint8_t maio;
|
||||||
|
uint8_t ma_bit_len;
|
||||||
|
uint8_t ma[8];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_info_trx {
|
||||||
|
uint16_t arfcn;
|
||||||
|
uint8_t pdch_mask; /* PDCH timeslot mask */
|
||||||
|
uint8_t spare;
|
||||||
|
uint32_t hlayer1;
|
||||||
|
struct gsm_pcu_if_info_ts ts[8]; /* timeslots per TRX */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_info_ind {
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t flags;
|
||||||
|
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
|
||||||
|
uint8_t bsic;
|
||||||
|
/* RAI */
|
||||||
|
uint16_t mcc, mnc;
|
||||||
|
uint8_t mnc_3_digits;
|
||||||
|
uint16_t lac, rac;
|
||||||
|
/* NSE */
|
||||||
|
uint16_t nsei;
|
||||||
|
uint8_t nse_timer[7];
|
||||||
|
uint8_t cell_timer[11];
|
||||||
|
/* cell */
|
||||||
|
uint16_t cell_id;
|
||||||
|
uint16_t repeat_time;
|
||||||
|
uint8_t repeat_count;
|
||||||
|
uint16_t bvci;
|
||||||
|
uint8_t t3142;
|
||||||
|
uint8_t t3169;
|
||||||
|
uint8_t t3191;
|
||||||
|
uint8_t t3193_10ms;
|
||||||
|
uint8_t t3195;
|
||||||
|
uint8_t n3101;
|
||||||
|
uint8_t n3103;
|
||||||
|
uint8_t n3105;
|
||||||
|
uint8_t cv_countdown;
|
||||||
|
uint16_t dl_tbf_ext;
|
||||||
|
uint16_t ul_tbf_ext;
|
||||||
|
uint8_t initial_cs;
|
||||||
|
uint8_t initial_mcs;
|
||||||
|
/* NSVC */
|
||||||
|
uint16_t nsvci[PCU_IF_NUM_NSVC];
|
||||||
|
uint16_t local_port[PCU_IF_NUM_NSVC];
|
||||||
|
uint16_t remote_port[PCU_IF_NUM_NSVC];
|
||||||
|
uint8_t address_type[PCU_IF_NUM_NSVC];
|
||||||
|
union {
|
||||||
|
struct in_addr v4;
|
||||||
|
struct in6_addr v6;
|
||||||
|
} remote_ip[PCU_IF_NUM_NSVC];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_act_req {
|
||||||
|
uint8_t activate;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint8_t spare;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_time_ind {
|
||||||
|
uint32_t fn;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if_pag_req {
|
||||||
|
uint8_t sapi;
|
||||||
|
uint8_t chan_needed;
|
||||||
|
uint8_t identity_lv[9];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* BTS tells PCU to [once] send given application data via PACCH to all UE with active TBF */
|
||||||
|
struct gsm_pcu_if_app_info_req {
|
||||||
|
uint8_t application_type; /* 4bit field, see TS 44.060 11.2.47 */
|
||||||
|
uint8_t len; /* length of data */
|
||||||
|
uint8_t data[162]; /* random size choice; ETWS needs 56 bytes */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* BTS tells PCU about a GPRS SUSPENSION REQUEST received on DCCH */
|
||||||
|
struct gsm_pcu_if_susp_req {
|
||||||
|
uint32_t tlli;
|
||||||
|
uint8_t ra_id[6];
|
||||||
|
uint8_t cause;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* Interference measurements on PDCH timeslots */
|
||||||
|
struct gsm_pcu_if_interf_ind {
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t spare[3];
|
||||||
|
uint32_t fn;
|
||||||
|
uint8_t interf[8];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* Contains messages transmitted BSC<->PCU, potentially forwarded by BTS via IPA/PCU */
|
||||||
|
struct gsm_pcu_if_container {
|
||||||
|
uint8_t msg_type;
|
||||||
|
uint8_t spare;
|
||||||
|
uint16_t length; /* network byte order */
|
||||||
|
uint8_t data[0];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* Used inside container: */
|
||||||
|
struct gsm_pcu_if_anr_req {
|
||||||
|
uint8_t num_cells;
|
||||||
|
uint16_t cell_list[96]; /* struct gsm48_cell_desc */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
/* PCU confirms back with measurements of target cells */
|
||||||
|
struct gsm_pcu_if_anr_cnf {
|
||||||
|
uint8_t num_cells;
|
||||||
|
uint16_t cell_list[32]; /* struct gsm48_cell_desc */
|
||||||
|
uint8_t rxlev_list[32]; /* value 0xff: invalid */
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct gsm_pcu_if {
|
||||||
|
/* context based information */
|
||||||
|
uint8_t msg_type; /* message type */
|
||||||
|
uint8_t bts_nr; /* bts number */
|
||||||
|
uint8_t spare[2];
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct gsm_pcu_if_data data_req;
|
||||||
|
struct gsm_pcu_if_data data_cnf;
|
||||||
|
struct gsm_pcu_if_data_cnf_dt data_cnf_dt;
|
||||||
|
struct gsm_pcu_if_data data_ind;
|
||||||
|
struct gsm_pcu_if_susp_req susp_req;
|
||||||
|
struct gsm_pcu_if_rts_req rts_req;
|
||||||
|
struct gsm_pcu_if_rach_ind rach_ind;
|
||||||
|
struct gsm_pcu_if_txt_ind txt_ind;
|
||||||
|
struct gsm_pcu_if_info_ind info_ind;
|
||||||
|
struct gsm_pcu_if_act_req act_req;
|
||||||
|
struct gsm_pcu_if_time_ind time_ind;
|
||||||
|
struct gsm_pcu_if_pag_req pag_req;
|
||||||
|
struct gsm_pcu_if_app_info_req app_info_req;
|
||||||
|
struct gsm_pcu_if_interf_ind interf_ind;
|
||||||
|
struct gsm_pcu_if_container container;
|
||||||
|
} u;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
#endif /* _PCUIF_PROTO_H */
|
||||||
26
osmoappdesc.py
Normal file
26
osmoappdesc.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
# 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. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
app_configs = {
|
||||||
|
"osmo-pcu": ["doc/examples/osmo-pcu.cfg"]
|
||||||
|
}
|
||||||
|
|
||||||
|
apps = [(4240, "src/osmo-pcu", "OsmoPCU", "osmo-pcu"),
|
||||||
|
]
|
||||||
|
|
||||||
|
vty_command = ["src/osmo-pcu", "-c", "doc/examples/osmo-pcu.cfg"]
|
||||||
|
|
||||||
|
vty_app = apps[0]
|
||||||
188
src/Makefile.am
188
src/Makefile.am
@@ -18,61 +18,191 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
|
AUTOMAKE_OPTIONS = subdir-objects
|
||||||
|
AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGSM_CFLAGS)
|
||||||
|
|
||||||
|
if ENABLE_SYSMODSP
|
||||||
|
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ENABLE_LC15BTS_PHY
|
||||||
|
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ENABLE_OC2GBTS_PHY
|
||||||
|
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
|
||||||
|
endif
|
||||||
|
|
||||||
AM_CXXFLAGS = -Wall -ldl -pthread
|
AM_CXXFLAGS = -Wall -ldl -pthread
|
||||||
|
AM_LDFLAGS = -lrt
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libgprs.la
|
noinst_LTLIBRARIES = libgprs.la
|
||||||
|
|
||||||
libgprs_la_SOURCES = \
|
libgprs_la_SOURCES = \
|
||||||
gprs_debug.cpp \
|
gprs_debug.cpp \
|
||||||
csn1.cpp \
|
csn1.c \
|
||||||
gsm_rlcmac.cpp \
|
gsm_rlcmac.c \
|
||||||
gprs_bssgp_pcu.cpp \
|
gprs_bssgp_pcu.c \
|
||||||
|
gprs_bssgp_rim.c \
|
||||||
gprs_rlcmac.cpp \
|
gprs_rlcmac.cpp \
|
||||||
gprs_rlcmac_data.cpp \
|
|
||||||
gprs_rlcmac_sched.cpp \
|
gprs_rlcmac_sched.cpp \
|
||||||
gsm_timer.cpp \
|
gprs_rlcmac_meas.cpp \
|
||||||
bitvector.cpp \
|
gprs_rlcmac_ts_alloc.cpp \
|
||||||
|
gprs_ms.c \
|
||||||
|
gprs_ms_storage.cpp \
|
||||||
|
gprs_pcu.c \
|
||||||
pcu_l1_if.cpp \
|
pcu_l1_if.cpp \
|
||||||
pcu_vty.c
|
pcu_vty.c \
|
||||||
|
pcu_vty_functions.cpp \
|
||||||
if ENABLE_SYSMOBTS
|
mslot_class.c \
|
||||||
libgprs_la_SOURCES += \
|
nacc_fsm.c \
|
||||||
sysmo_sock.cpp
|
neigh_cache.c \
|
||||||
else
|
tbf.cpp \
|
||||||
libgprs_la_SOURCES += \
|
tbf_fsm.c \
|
||||||
openbts_sock.cpp
|
tbf_ul.cpp \
|
||||||
endif
|
tbf_dl.cpp \
|
||||||
|
bts.cpp \
|
||||||
noinst_PROGRAMS = \
|
bts_anr_fsm.c \
|
||||||
RLCMACTest
|
ms_anr_fsm.c \
|
||||||
|
pdch.cpp \
|
||||||
|
pdch_ul_controller.c \
|
||||||
|
encoding.cpp \
|
||||||
|
sba.c \
|
||||||
|
decoding.cpp \
|
||||||
|
llc.cpp \
|
||||||
|
rlc.cpp \
|
||||||
|
osmobts_sock.c \
|
||||||
|
gprs_codel.c \
|
||||||
|
coding_scheme.c \
|
||||||
|
egprs_rlc_compression.cpp \
|
||||||
|
gprs_rlcmac_sched.cpp
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
osmo-pcu
|
osmo-pcu
|
||||||
|
|
||||||
|
noinst_PROGRAMS =
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
gprs_debug.h \
|
gprs_debug.h \
|
||||||
csn1.h \
|
csn1.h \
|
||||||
gsm_rlcmac.h \
|
gsm_rlcmac.h \
|
||||||
gprs_bssgp_pcu.h \
|
gprs_bssgp_pcu.h \
|
||||||
|
gprs_bssgp_rim.h \
|
||||||
gprs_rlcmac.h \
|
gprs_rlcmac.h \
|
||||||
pcuif_proto.h \
|
gprs_ms.h \
|
||||||
|
gprs_ms_storage.h \
|
||||||
|
gprs_pcu.h \
|
||||||
pcu_l1_if.h \
|
pcu_l1_if.h \
|
||||||
gsm_timer.h \
|
pcu_vty.h \
|
||||||
bitvector.h \
|
pcu_vty_functions.h \
|
||||||
pcu_vty.h
|
mslot_class.h \
|
||||||
|
nacc_fsm.h \
|
||||||
RLCMACTest_SOURCES = RLCMACTest.cpp
|
neigh_cache.h \
|
||||||
RLCMACTest_LDADD = \
|
tbf.h \
|
||||||
libgprs.la \
|
tbf_fsm.h \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
tbf_ul.h \
|
||||||
$(COMMON_LA)
|
tbf_dl.h \
|
||||||
|
bts.h \
|
||||||
|
bts_anr_fsm.h \
|
||||||
|
ms_anr_fsm.h \
|
||||||
|
pdch.h \
|
||||||
|
pdch_ul_controller.h \
|
||||||
|
encoding.h \
|
||||||
|
sba.h \
|
||||||
|
rlc.h \
|
||||||
|
decoding.h \
|
||||||
|
llc.h \
|
||||||
|
pcu_utils.h \
|
||||||
|
cxx_linuxlist.h \
|
||||||
|
gprs_codel.h \
|
||||||
|
coding_scheme.h \
|
||||||
|
egprs_rlc_compression.h \
|
||||||
|
wireshark_compat.h
|
||||||
|
|
||||||
osmo_pcu_SOURCES = pcu_main.cpp
|
osmo_pcu_SOURCES = pcu_main.cpp
|
||||||
|
|
||||||
|
if ENABLE_SYSMODSP
|
||||||
|
AM_CPPFLAGS += -I$(srcdir)/osmo-bts-sysmo -I$(SYSMOBTS_INCDIR)
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_if.c \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_if.h \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_hw.c \
|
||||||
|
osmo-bts-sysmo/femtobts.c \
|
||||||
|
osmo-bts-sysmo/femtobts.h
|
||||||
|
|
||||||
|
noinst_HEADERS += \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_if.h \
|
||||||
|
osmo-bts-sysmo/femtobts.h
|
||||||
|
|
||||||
|
noinst_PROGRAMS += \
|
||||||
|
osmo-pcu-remote
|
||||||
|
|
||||||
|
osmo_pcu_SOURCES += \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_if.c \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_hw.c \
|
||||||
|
osmo-bts-sysmo/femtobts.c
|
||||||
|
|
||||||
|
osmo_pcu_remote_SOURCES = \
|
||||||
|
pcu_main.cpp \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_if.c \
|
||||||
|
osmo-bts-sysmo/sysmo_l1_fwd.c \
|
||||||
|
osmo-bts-sysmo/femtobts.c
|
||||||
|
|
||||||
|
osmo_pcu_remote_LDADD = \
|
||||||
|
libgprs.la \
|
||||||
|
$(LIBOSMOGB_LIBS) \
|
||||||
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(LIBOSMOCTRL_LIBS) \
|
||||||
|
$(LIBOSMOGSM_LIBS) \
|
||||||
|
$(COMMON_LA)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ENABLE_LC15BTS_PHY
|
||||||
|
AM_CPPFLAGS += $(LITECELL15_CFLAGS) -I$(srcdir)/osmo-bts-litecell15
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
osmo-bts-litecell15/lc15_l1_if.c \
|
||||||
|
osmo-bts-litecell15/lc15_l1_if.h \
|
||||||
|
osmo-bts-litecell15/lc15_l1_hw.c \
|
||||||
|
osmo-bts-litecell15/lc15bts.c \
|
||||||
|
osmo-bts-litecell15/lc15bts.h
|
||||||
|
|
||||||
|
noinst_HEADERS += \
|
||||||
|
osmo-bts-litecell15/lc15_l1_if.h \
|
||||||
|
osmo-bts-litecell15/lc15bts.h
|
||||||
|
|
||||||
|
osmo_pcu_SOURCES += \
|
||||||
|
osmo-bts-litecell15/lc15_l1_if.c \
|
||||||
|
osmo-bts-litecell15/lc15_l1_hw.c \
|
||||||
|
osmo-bts-litecell15/lc15bts.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
if ENABLE_OC2GBTS_PHY
|
||||||
|
AM_CPPFLAGS += -I$(OC2G_INCDIR) -I$(srcdir)/osmo-bts-oc2g
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
osmo-bts-oc2g/oc2g_l1_if.c \
|
||||||
|
osmo-bts-oc2g/oc2g_l1_if.h \
|
||||||
|
osmo-bts-oc2g/oc2g_l1_hw.c \
|
||||||
|
osmo-bts-oc2g/oc2gbts.c \
|
||||||
|
osmo-bts-oc2g/oc2gbts.h
|
||||||
|
|
||||||
|
noinst_HEADERS += \
|
||||||
|
osmo-bts-oc2g/oc2g_l1_if.h \
|
||||||
|
osmo-bts-oc2g/oc2gbts.h
|
||||||
|
|
||||||
|
osmo_pcu_SOURCES += \
|
||||||
|
osmo-bts-oc2g/oc2g_l1_if.c \
|
||||||
|
osmo-bts-oc2g/oc2g_l1_hw.c \
|
||||||
|
osmo-bts-oc2g/oc2gbts.c
|
||||||
|
endif
|
||||||
|
|
||||||
osmo_pcu_LDADD = \
|
osmo_pcu_LDADD = \
|
||||||
libgprs.la \
|
libgprs.la \
|
||||||
$(LIBOSMOGB_LIBS) \
|
$(LIBOSMOGB_LIBS) \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(LIBOSMOCTRL_LIBS) \
|
||||||
$(LIBOSMOGSM_LIBS) \
|
$(LIBOSMOGSM_LIBS) \
|
||||||
$(COMMON_LA)
|
$(COMMON_LA)
|
||||||
|
|
||||||
|
|||||||
@@ -1,205 +0,0 @@
|
|||||||
/* RLCMACTest.cpp
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011 Ivan Klyuchnikov
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//#include <BitVector.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include "csn1.h"
|
|
||||||
#include "gsm_rlcmac.h"
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void printSizeofRLCMAC()
|
|
||||||
{
|
|
||||||
cout << "sizeof RlcMacUplink_t " << sizeof(RlcMacUplink_t) << endl;
|
|
||||||
cout << "sizeof Packet_Cell_Change_Failure_t " << sizeof(Packet_Cell_Change_Failure_t) << endl;
|
|
||||||
cout << "sizeof Packet_Control_Acknowledgement_t " << sizeof(Packet_Control_Acknowledgement_t) << endl;
|
|
||||||
cout << "sizeof Packet_Downlink_Ack_Nack_t " << sizeof(Packet_Downlink_Ack_Nack_t) << endl;
|
|
||||||
cout << "sizeof EGPRS_PD_AckNack_t " << sizeof(EGPRS_PD_AckNack_t) << endl;
|
|
||||||
cout << "sizeof Packet_Uplink_Dummy_Control_Block_t " << sizeof(Packet_Uplink_Dummy_Control_Block_t) << endl;
|
|
||||||
cout << "sizeof Packet_Measurement_Report_t " << sizeof(Packet_Measurement_Report_t) << endl;
|
|
||||||
cout << "sizeof Packet_Resource_Request_t " << sizeof(Packet_Resource_Request_t) << endl;
|
|
||||||
cout << "sizeof Packet_Mobile_TBF_Status_t " << sizeof(Packet_Mobile_TBF_Status_t) << endl;
|
|
||||||
cout << "sizeof Packet_PSI_Status_t " << sizeof(Packet_PSI_Status_t) << endl;
|
|
||||||
cout << "sizeof Packet_Enh_Measurement_Report_t " << sizeof(Packet_Enh_Measurement_Report_t) << endl;
|
|
||||||
cout << "sizeof Packet_Cell_Change_Notification_t " << sizeof(Packet_Cell_Change_Notification_t) << endl;
|
|
||||||
cout << "sizeof Packet_SI_Status_t " << sizeof(Packet_SI_Status_t) << endl;
|
|
||||||
cout << "sizeof Additional_MS_Rad_Access_Cap_t " << sizeof(Additional_MS_Rad_Access_Cap_t) << endl;
|
|
||||||
cout << "sizeof Packet_Pause_t " << sizeof(Packet_Pause_t) << endl;
|
|
||||||
|
|
||||||
cout << "sizeof RlcMacDownlink_t " << sizeof(RlcMacDownlink_t) << endl;
|
|
||||||
cout << "sizeof Packet_Access_Reject_t " << sizeof(Packet_Access_Reject_t) << endl;
|
|
||||||
cout << "sizeof Packet_Cell_Change_Order_t " << sizeof(Packet_Cell_Change_Order_t) << endl;
|
|
||||||
cout << "sizeof Packet_Downlink_Assignment_t " << sizeof(Packet_Downlink_Assignment_t) << endl;
|
|
||||||
cout << "sizeof Packet_Measurement_Order_Reduced_t " << sizeof(Packet_Measurement_Order_Reduced_t) << endl;
|
|
||||||
cout << "sizeof Packet_Neighbour_Cell_Data_t " << sizeof(Packet_Neighbour_Cell_Data_t) << endl;
|
|
||||||
cout << "sizeof Packet_Serving_Cell_Data_t " << sizeof(Packet_Serving_Cell_Data_t) << endl;
|
|
||||||
cout << "sizeof Packet_Paging_Request_t " << sizeof(Packet_Paging_Request_t) << endl;
|
|
||||||
cout << "sizeof Packet_PDCH_Release_t " << sizeof(Packet_PDCH_Release_t) << endl;
|
|
||||||
cout << "sizeof Packet_Polling_Request_t " << sizeof(Packet_Polling_Request_t) << endl;
|
|
||||||
cout << "sizeof Packet_Power_Control_Timing_Advance_t " << sizeof(Packet_Power_Control_Timing_Advance_t) << endl;
|
|
||||||
cout << "sizeof Packet_PRACH_Parameters_t " << sizeof(Packet_PRACH_Parameters_t) << endl;
|
|
||||||
cout << "sizeof Packet_Queueing_Notification_t " << sizeof(Packet_Queueing_Notification_t) << endl;
|
|
||||||
cout << "sizeof Packet_Timeslot_Reconfigure_t " << sizeof(Packet_Timeslot_Reconfigure_t) << endl;
|
|
||||||
cout << "sizeof Packet_TBF_Release_t " << sizeof(Packet_TBF_Release_t) << endl;
|
|
||||||
cout << "sizeof Packet_Uplink_Ack_Nack_t " << sizeof(Packet_Uplink_Ack_Nack_t) << endl;
|
|
||||||
cout << "sizeof Packet_Uplink_Assignment_t " << sizeof(Packet_Uplink_Assignment_t) << endl;
|
|
||||||
cout << "sizeof Packet_Cell_Change_Continue_t " << sizeof(Packet_Cell_Change_Continue_t) << endl;
|
|
||||||
cout << "sizeof Packet_Handover_Command_t " << sizeof(Packet_Handover_Command_t) << endl;
|
|
||||||
cout << "sizeof Packet_PhysicalInformation_t " << sizeof(Packet_PhysicalInformation_t) << endl;
|
|
||||||
cout << "sizeof Packet_Downlink_Dummy_Control_Block_t " << sizeof(Packet_Downlink_Dummy_Control_Block_t) << endl;
|
|
||||||
cout << "sizeof PSI1_t " << sizeof(PSI1_t) << endl;
|
|
||||||
cout << "sizeof PSI2_t " << sizeof(PSI2_t) << endl;
|
|
||||||
cout << "sizeof PSI3_t " << sizeof(PSI3_t) << endl;
|
|
||||||
cout << "sizeof PSI3_BIS_t " << sizeof(PSI3_BIS_t) << endl;
|
|
||||||
cout << "sizeof PSI4_t " << sizeof(PSI4_t) << endl;
|
|
||||||
cout << "sizeof PSI13_t " << sizeof(PSI13_t) << endl;
|
|
||||||
cout << "sizeof PSI5_t " << sizeof(PSI5_t) << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void testRlcMacDownlink()
|
|
||||||
{
|
|
||||||
struct bitvec *resultVector = bitvec_alloc(23);
|
|
||||||
bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
|
||||||
|
|
||||||
std::string testData[] = {
|
|
||||||
"4e082500e3f1a81d080820800b2b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment
|
|
||||||
"48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
|
|
||||||
"47240c00400000000000000079eb2ac9402b2b2b2b2b2b", // Packet Uplink Ack Nack
|
|
||||||
"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b" // Packet Uplink Assignment
|
|
||||||
"4913e00850884013a8048b2b2b2b2b2b2b2b2b2b2b2b2b"
|
|
||||||
"412430007fffffffffffffffefd19c7ba12b2b2b2b2b2b"
|
|
||||||
"41942b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
|
|
||||||
};
|
|
||||||
|
|
||||||
int testDataSize = sizeof(testData)/sizeof(testData[0]);
|
|
||||||
|
|
||||||
cout << " DOWNLINK " << endl;
|
|
||||||
for (int i = 0; i < testDataSize; i++)
|
|
||||||
{
|
|
||||||
bitvec *vector = bitvec_alloc(23);
|
|
||||||
bitvec_unhex(vector, testData[i].c_str());
|
|
||||||
cout << "vector1 = ";
|
|
||||||
for (int i = 0; i < 23; i++)
|
|
||||||
{
|
|
||||||
cout << hex << (unsigned)*(vector->data + i);
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
RlcMacDownlink_t * data = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
|
|
||||||
cout << "=========Start DECODE===========" << endl;
|
|
||||||
decode_gsm_rlcmac_downlink(vector, data);
|
|
||||||
cout << "+++++++++Finish DECODE++++++++++" << endl;
|
|
||||||
cout << "=========Start ENCODE=============" << endl;
|
|
||||||
encode_gsm_rlcmac_downlink(resultVector, data);
|
|
||||||
cout << "+++++++++Finish ENCODE+++++++++++" << endl;
|
|
||||||
cout << "vector1 = ";
|
|
||||||
for (int i = 0; i < 23; i++)
|
|
||||||
{
|
|
||||||
cout << (unsigned)*(vector->data + i);
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
cout << "vector2 = ";
|
|
||||||
for (int i = 0; i < 23; i++)
|
|
||||||
{
|
|
||||||
cout << (unsigned)*(resultVector->data + i);
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
if (memcmp(vector->data, resultVector->data, 23) == 0)
|
|
||||||
{
|
|
||||||
cout << "vector1 == vector2 : TRUE" << endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "vector1 == vector2 : FALSE" << endl;
|
|
||||||
}
|
|
||||||
bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
|
||||||
bitvec_free(vector);
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void testRlcMacUplink()
|
|
||||||
{
|
|
||||||
struct bitvec *resultVector = bitvec_alloc(23);
|
|
||||||
bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
|
||||||
|
|
||||||
std::string testData[] = {
|
|
||||||
"400e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
|
|
||||||
"400b8020000000000000002480e00b2b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
|
|
||||||
"4016713dc094270ca2ae57ef909006aa0fc0001f80222b" // Packet Resource Request
|
|
||||||
"400a9020000000000000003010012a0800132b2b2b2b2b"
|
|
||||||
};
|
|
||||||
|
|
||||||
int testDataSize = sizeof(testData)/sizeof(testData[0]);
|
|
||||||
|
|
||||||
|
|
||||||
cout << " UPLINK " << endl;
|
|
||||||
for (int i = 0; i < testDataSize; i++)
|
|
||||||
{
|
|
||||||
bitvec *vector = bitvec_alloc(23);
|
|
||||||
bitvec_unhex(vector, testData[i].c_str());
|
|
||||||
cout << "vector1 = ";
|
|
||||||
for (int i = 0; i < 23; i++)
|
|
||||||
{
|
|
||||||
cout << hex << (unsigned)*(vector->data + i);
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
RlcMacUplink_t * data = (RlcMacUplink_t *)malloc(sizeof(RlcMacUplink_t));
|
|
||||||
cout << "=========Start DECODE===========" << endl;
|
|
||||||
decode_gsm_rlcmac_uplink(vector, data);
|
|
||||||
cout << "+++++++++Finish DECODE++++++++++" << endl;
|
|
||||||
cout << "=========Start ENCODE=============" << endl;
|
|
||||||
encode_gsm_rlcmac_uplink(resultVector, data);
|
|
||||||
cout << "+++++++++Finish ENCODE+++++++++++" << endl;
|
|
||||||
cout << "vector1 = ";
|
|
||||||
for (int i = 0; i < 23; i++)
|
|
||||||
{
|
|
||||||
cout << (unsigned)*(vector->data + i);
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
cout << "vector2 = ";
|
|
||||||
for (int i = 0; i < 23; i++)
|
|
||||||
{
|
|
||||||
cout << (unsigned)*(resultVector->data + i);
|
|
||||||
}
|
|
||||||
cout << endl;
|
|
||||||
if (memcmp(vector->data, resultVector->data, 23) == 0)
|
|
||||||
{
|
|
||||||
cout << "vector1 == vector2 : TRUE" << endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "vector1 == vector2 : FALSE" << endl;
|
|
||||||
}
|
|
||||||
bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
|
||||||
bitvec_free(vector);
|
|
||||||
free(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
//printSizeofRLCMAC();
|
|
||||||
testRlcMacDownlink();
|
|
||||||
testRlcMacUplink();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
/* bitvector.cpp
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! \addtogroup bitvector
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! \file bitvector.cpp
|
|
||||||
* \brief Additional functions for Osmocom bit vector abstraction.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <bitvector.h>
|
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
void *bv_tall_ctx;
|
|
||||||
|
|
||||||
struct bitvec *bitvec_alloc(unsigned size)
|
|
||||||
{
|
|
||||||
struct bitvec *bv = talloc_zero(bv_tall_ctx, struct bitvec);
|
|
||||||
bv->data_len = size;
|
|
||||||
bv->cur_bit = 0;
|
|
||||||
bv->data = talloc_zero_array(bv_tall_ctx, uint8_t, size);
|
|
||||||
return bv;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bitvec_free(struct bitvec *bv)
|
|
||||||
{
|
|
||||||
talloc_free(bv->data);
|
|
||||||
talloc_free(bv);
|
|
||||||
}
|
|
||||||
|
|
||||||
int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0; i < bv->data_len; i++)
|
|
||||||
{
|
|
||||||
buffer[i] = bv->data[i];
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (i = 0; i < bv->data_len; i++)
|
|
||||||
{
|
|
||||||
bv->data[i] = buffer[i];
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int bitvec_unhex(struct bitvec *bv, const char* src)
|
|
||||||
{
|
|
||||||
unsigned val;
|
|
||||||
unsigned write_index = 0;
|
|
||||||
unsigned digits = bv->data_len*2;
|
|
||||||
for (unsigned i=0; i<digits; i++) {
|
|
||||||
if (sscanf(src+i, "%1x", &val) < 1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
bitvec_write_field(bv, write_index,val, 4);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint64_t ui = 0;
|
|
||||||
bv->cur_bit = read_index;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
int bit = bitvec_get_bit_pos((const struct bitvec *)bv, bv->cur_bit);
|
|
||||||
if (bit < 0)
|
|
||||||
return bit;
|
|
||||||
if (bit)
|
|
||||||
ui |= ((uint64_t)1 << (len - i - 1));
|
|
||||||
bv->cur_bit++;
|
|
||||||
}
|
|
||||||
read_index += len;
|
|
||||||
return ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
|
|
||||||
{
|
|
||||||
int i, rc;
|
|
||||||
bv->cur_bit = write_index;
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
int bit = 0;
|
|
||||||
if (val & ((uint64_t)1 << (len - i - 1)))
|
|
||||||
bit = 1;
|
|
||||||
rc = bitvec_set_bit(bv, (bit_value)bit);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
write_index += len;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
1342
src/bts.cpp
Normal file
1342
src/bts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
370
src/bts.h
Normal file
370
src/bts.h
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
/* bts.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pdch.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
|
#include <osmocom/core/stat_item.h>
|
||||||
|
#include <osmocom/core/tdef.h>
|
||||||
|
#include <osmocom/gprs/gprs_ns2.h>
|
||||||
|
#include <osmocom/gsm/l1sap.h>
|
||||||
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||||
|
#include <osmocom/gsm/gsm48_rest_octets.h>
|
||||||
|
#include <osmocom/gsm/gsm48.h>
|
||||||
|
#include "mslot_class.h"
|
||||||
|
#include "gsm_rlcmac.h"
|
||||||
|
#include "gprs_pcu.h"
|
||||||
|
#include "bts_anr_fsm.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "tbf.h"
|
||||||
|
#include "coding_scheme.h"
|
||||||
|
|
||||||
|
struct GprsMs;
|
||||||
|
struct gprs_rlcmac_bts;
|
||||||
|
|
||||||
|
struct gprs_rlcmac_trx {
|
||||||
|
void *fl1h;
|
||||||
|
uint16_t arfcn;
|
||||||
|
struct gprs_rlcmac_pdch pdch[8];
|
||||||
|
|
||||||
|
/* back pointers */
|
||||||
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
uint8_t trx_no;
|
||||||
|
|
||||||
|
/* list of uplink TBFs */
|
||||||
|
struct llist_head ul_tbfs; /* list of gprs_rlcmac_tbf */
|
||||||
|
/* list of downlink TBFs */
|
||||||
|
struct llist_head dl_tbfs; /* list of gprs_rlcmac_tbf */
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
void bts_trx_init(struct gprs_rlcmac_trx *trx, struct gprs_rlcmac_bts *bts, uint8_t trx_no);
|
||||||
|
void bts_trx_reserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
|
||||||
|
void bts_trx_unreserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
|
||||||
|
void bts_trx_free_all_tbf(struct gprs_rlcmac_trx *trx);
|
||||||
|
|
||||||
|
void bts_update_tbf_ta(struct gprs_rlcmac_bts *bts, const char *p, uint32_t fn,
|
||||||
|
uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CTR_TBF_DL_ALLOCATED,
|
||||||
|
CTR_TBF_DL_FREED,
|
||||||
|
CTR_TBF_DL_ABORTED,
|
||||||
|
CTR_TBF_UL_ALLOCATED,
|
||||||
|
CTR_TBF_UL_FREED,
|
||||||
|
CTR_TBF_UL_ABORTED,
|
||||||
|
CTR_TBF_REUSED,
|
||||||
|
CTR_TBF_ALLOC_ALGO_A,
|
||||||
|
CTR_TBF_ALLOC_ALGO_B,
|
||||||
|
CTR_TBF_ALLOC_FAIL,
|
||||||
|
CTR_TBF_ALLOC_FAIL_NO_TFI,
|
||||||
|
CTR_TBF_ALLOC_FAIL_NO_USF,
|
||||||
|
CTR_TBF_ALLOC_FAIL_NO_SLOT_COMBI,
|
||||||
|
CTR_TBF_ALLOC_FAIL_NO_SLOT_AVAIL,
|
||||||
|
CTR_RLC_SENT,
|
||||||
|
CTR_RLC_RESENT,
|
||||||
|
CTR_RLC_RESTARTED,
|
||||||
|
CTR_RLC_STALLED,
|
||||||
|
CTR_RLC_NACKED,
|
||||||
|
CTR_RLC_FINAL_BLOCK_RESENT,
|
||||||
|
CTR_RLC_ASS_TIMEDOUT,
|
||||||
|
CTR_RLC_ASS_FAILED,
|
||||||
|
CTR_RLC_ACK_TIMEDOUT,
|
||||||
|
CTR_RLC_ACK_FAILED,
|
||||||
|
CTR_RLC_REL_TIMEDOUT,
|
||||||
|
CTR_RLC_LATE_BLOCK,
|
||||||
|
CTR_RLC_SENT_DUMMY,
|
||||||
|
CTR_RLC_SENT_CONTROL,
|
||||||
|
CTR_RLC_DL_BYTES,
|
||||||
|
CTR_RLC_DL_PAYLOAD_BYTES,
|
||||||
|
CTR_RLC_UL_BYTES,
|
||||||
|
CTR_RLC_UL_PAYLOAD_BYTES,
|
||||||
|
CTR_DECODE_ERRORS,
|
||||||
|
CTR_SBA_ALLOCATED,
|
||||||
|
CTR_SBA_FREED,
|
||||||
|
CTR_SBA_TIMEDOUT,
|
||||||
|
CTR_LLC_FRAME_TIMEDOUT,
|
||||||
|
CTR_LLC_FRAME_DROPPED,
|
||||||
|
CTR_LLC_FRAME_SCHED,
|
||||||
|
CTR_LLC_DL_BYTES,
|
||||||
|
CTR_LLC_UL_BYTES,
|
||||||
|
CTR_RACH_REQUESTS,
|
||||||
|
CTR_11BIT_RACH_REQUESTS,
|
||||||
|
CTR_SPB_UL_FIRST_SEGMENT,
|
||||||
|
CTR_SPB_UL_SECOND_SEGMENT,
|
||||||
|
CTR_SPB_DL_FIRST_SEGMENT,
|
||||||
|
CTR_SPB_DL_SECOND_SEGMENT,
|
||||||
|
CTR_IMMEDIATE_ASSIGN_UL_TBF,
|
||||||
|
CTR_IMMEDIATE_ASSIGN_REJ,
|
||||||
|
CTR_IMMEDIATE_ASSIGN_DL_TBF,
|
||||||
|
CTR_CHANNEL_REQUEST_DESCRIPTION,
|
||||||
|
CTR_PKT_UL_ASSIGNMENT,
|
||||||
|
CTR_PKT_ACCESS_REJ,
|
||||||
|
CTR_PKT_DL_ASSIGNMENT,
|
||||||
|
CTR_PKT_CELL_CHG_NOTIFICATION,
|
||||||
|
CTR_PKT_CELL_CHG_CONTINUE,
|
||||||
|
CTR_PKT_NEIGH_CELL_DATA,
|
||||||
|
CTR_PKT_MEAS_ORDER,
|
||||||
|
CTR_RLC_RECV_CONTROL,
|
||||||
|
CTR_PUA_POLL_TIMEDOUT,
|
||||||
|
CTR_PUA_POLL_FAILED,
|
||||||
|
CTR_PDA_POLL_TIMEDOUT,
|
||||||
|
CTR_PDA_POLL_FAILED,
|
||||||
|
CTR_PUAN_POLL_TIMEDOUT,
|
||||||
|
CTR_PUAN_POLL_FAILED,
|
||||||
|
CTR_PDAN_POLL_TIMEDOUT,
|
||||||
|
CTR_PDAN_POLL_FAILED,
|
||||||
|
CTR_GPRS_DL_CS1,
|
||||||
|
CTR_GPRS_DL_CS2,
|
||||||
|
CTR_GPRS_DL_CS3,
|
||||||
|
CTR_GPRS_DL_CS4,
|
||||||
|
CTR_EGPRS_DL_MCS1,
|
||||||
|
CTR_EGPRS_DL_MCS2,
|
||||||
|
CTR_EGPRS_DL_MCS3,
|
||||||
|
CTR_EGPRS_DL_MCS4,
|
||||||
|
CTR_EGPRS_DL_MCS5,
|
||||||
|
CTR_EGPRS_DL_MCS6,
|
||||||
|
CTR_EGPRS_DL_MCS7,
|
||||||
|
CTR_EGPRS_DL_MCS8,
|
||||||
|
CTR_EGPRS_DL_MCS9,
|
||||||
|
CTR_GPRS_UL_CS1,
|
||||||
|
CTR_GPRS_UL_CS2,
|
||||||
|
CTR_GPRS_UL_CS3,
|
||||||
|
CTR_GPRS_UL_CS4,
|
||||||
|
CTR_EGPRS_UL_MCS1,
|
||||||
|
CTR_EGPRS_UL_MCS2,
|
||||||
|
CTR_EGPRS_UL_MCS3,
|
||||||
|
CTR_EGPRS_UL_MCS4,
|
||||||
|
CTR_EGPRS_UL_MCS5,
|
||||||
|
CTR_EGPRS_UL_MCS6,
|
||||||
|
CTR_EGPRS_UL_MCS7,
|
||||||
|
CTR_EGPRS_UL_MCS8,
|
||||||
|
CTR_EGPRS_UL_MCS9,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STAT_MS_PRESENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* RACH.ind parameters (to be parsed) */
|
||||||
|
struct rach_ind_params {
|
||||||
|
enum ph_burst_type burst_type;
|
||||||
|
bool is_11bit;
|
||||||
|
uint16_t ra;
|
||||||
|
uint8_t trx_nr;
|
||||||
|
uint8_t ts_nr;
|
||||||
|
uint32_t rfn;
|
||||||
|
int16_t qta;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* [EGPRS Packet] Channel Request parameters (parsed) */
|
||||||
|
struct chan_req_params {
|
||||||
|
unsigned int egprs_mslot_class;
|
||||||
|
unsigned int priority;
|
||||||
|
bool single_block;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GprsMsStorage;
|
||||||
|
struct pcu_l1_meas;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I represent a GSM BTS. I have one or more TRX, I know the current
|
||||||
|
* GSM time and I have controllers that help with allocating resources
|
||||||
|
* on my TRXs.
|
||||||
|
*/
|
||||||
|
struct gprs_rlcmac_bts {
|
||||||
|
uint8_t nr; /* bts_nr */
|
||||||
|
struct llist_head list; /* queued in pcu->bts_list */
|
||||||
|
bool active;
|
||||||
|
struct osmo_cell_global_id_ps cgi_ps;
|
||||||
|
uint8_t bsic;
|
||||||
|
uint8_t cs_mask; /* Allowed CS mask from BTS */
|
||||||
|
uint16_t mcs_mask; /* Allowed MCS mask from BTS */
|
||||||
|
struct { /* information stored from last received PCUIF info_ind message */
|
||||||
|
uint8_t initial_cs;
|
||||||
|
uint8_t initial_mcs;
|
||||||
|
} pcuif_info_ind;
|
||||||
|
uint8_t initial_cs_dl, initial_cs_ul;
|
||||||
|
uint8_t initial_mcs_dl, initial_mcs_ul;
|
||||||
|
/* Timer defintions */
|
||||||
|
struct osmo_tdef *T_defs_bts; /* timers controlled by BTS, received through PCUIF */
|
||||||
|
uint8_t n3101;
|
||||||
|
uint8_t n3103;
|
||||||
|
uint8_t n3105;
|
||||||
|
struct gprs_rlcmac_trx trx[8];
|
||||||
|
|
||||||
|
uint8_t si1[GSM_MACBLOCK_LEN];
|
||||||
|
bool si1_is_set;
|
||||||
|
uint8_t si2[GSM_MACBLOCK_LEN];
|
||||||
|
bool si2_is_set;
|
||||||
|
struct gsm_sysinfo_freq si2_bcch_cell_list[1024];
|
||||||
|
uint8_t si3[GSM_MACBLOCK_LEN];
|
||||||
|
bool si3_is_set;
|
||||||
|
uint8_t si13[GSM_MACBLOCK_LEN];
|
||||||
|
struct osmo_gsm48_si13_info si13_ro_decoded;
|
||||||
|
bool si13_is_set;
|
||||||
|
|
||||||
|
/* State for dynamic algorithm selection */
|
||||||
|
int multislot_disabled;
|
||||||
|
|
||||||
|
/* Packet Application Information (3GPP TS 44.060 11.2.47, usually ETWS primary message). We don't need to store
|
||||||
|
* more than one message, because they get sent so rarely. */
|
||||||
|
struct msgb *app_info;
|
||||||
|
uint32_t app_info_pending; /* Count of MS with active TBF, to which we did not send app_info yet */
|
||||||
|
|
||||||
|
/* main nsei */
|
||||||
|
struct gprs_ns2_nse *nse;
|
||||||
|
|
||||||
|
/* back pointer to PCU object */
|
||||||
|
struct gprs_pcu *pcu;
|
||||||
|
|
||||||
|
uint32_t cur_fn;
|
||||||
|
int cur_blk_fn;
|
||||||
|
uint8_t max_cs_dl, max_cs_ul;
|
||||||
|
uint8_t max_mcs_dl, max_mcs_ul;
|
||||||
|
struct rate_ctr_group *ratectrs;
|
||||||
|
struct osmo_stat_item_group *statg;
|
||||||
|
|
||||||
|
struct GprsMsStorage *ms_store;
|
||||||
|
|
||||||
|
struct bts_anr_fsm_ctx *anr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct paging_req_cs {
|
||||||
|
uint8_t chan_needed;
|
||||||
|
uint32_t tlli; /* GSM_RESERVED_TMSI if not present */
|
||||||
|
bool mi_tmsi_present;
|
||||||
|
struct osmo_mobile_identity mi_tmsi;
|
||||||
|
bool mi_imsi_present;
|
||||||
|
struct osmo_mobile_identity mi_imsi;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts *bts, uint8_t ms_class, uint8_t egprs_ms_class);
|
||||||
|
int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req, struct GprsMs *ms);
|
||||||
|
|
||||||
|
uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn);
|
||||||
|
|
||||||
|
struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
|
||||||
|
struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
|
||||||
|
|
||||||
|
void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, uint16_t pgroup);
|
||||||
|
|
||||||
|
void bts_set_current_frame_number(struct gprs_rlcmac_bts *bts, uint32_t frame_number);
|
||||||
|
void bts_set_current_block_frame_number(struct gprs_rlcmac_bts *bts, int frame_number);
|
||||||
|
static inline uint32_t bts_current_frame_number(const struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
return bts->cur_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir,
|
||||||
|
uint8_t *_trx, int8_t use_trx);
|
||||||
|
|
||||||
|
int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
|
||||||
|
int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
|
||||||
|
int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn);
|
||||||
|
|
||||||
|
void bts_send_gsmtap(struct gprs_rlcmac_bts *bts,
|
||||||
|
enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
|
||||||
|
uint8_t ts_no, uint8_t channel, uint32_t fn,
|
||||||
|
const uint8_t *data, unsigned int len);
|
||||||
|
void bts_send_gsmtap_meas(struct gprs_rlcmac_bts *bts,
|
||||||
|
enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
|
||||||
|
uint8_t ts_no, uint8_t channel, uint32_t fn,
|
||||||
|
const uint8_t *data, unsigned int len, struct pcu_l1_meas *meas);
|
||||||
|
void bts_send_gsmtap_rach(struct gprs_rlcmac_bts *bts,
|
||||||
|
enum pcu_gsmtap_category categ, uint8_t channel,
|
||||||
|
const struct rach_ind_params *rip);
|
||||||
|
|
||||||
|
struct GprsMsStorage *bts_ms_store(const struct gprs_rlcmac_bts *bts);
|
||||||
|
|
||||||
|
struct GprsMs *bts_ms_by_tlli(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli);
|
||||||
|
|
||||||
|
static inline struct rate_ctr_group *bts_rate_counters(struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
return bts->ratectrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct osmo_stat_item_group *bts_stat_items(struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
return bts->statg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bts_do_rate_ctr_inc(const struct gprs_rlcmac_bts *bts, unsigned int ctr_id) {
|
||||||
|
rate_ctr_inc(rate_ctr_group_get_ctr(bts->ratectrs, ctr_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bts_do_rate_ctr_add(const struct gprs_rlcmac_bts *bts, unsigned int ctr_id, int inc) {
|
||||||
|
rate_ctr_add(rate_ctr_group_get_ctr(bts->ratectrs, ctr_id), inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void bts_stat_item_add(struct gprs_rlcmac_bts *bts, unsigned int stat_id, int inc) {
|
||||||
|
struct osmo_stat_item *item = osmo_stat_item_group_get_item(bts->statg, stat_id);
|
||||||
|
int32_t val = osmo_stat_item_get_last(item);
|
||||||
|
osmo_stat_item_set(item, val + inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts *bts_alloc(struct gprs_pcu *pcu, uint8_t bts_nr);
|
||||||
|
|
||||||
|
struct gprs_rlcmac_sba *bts_alloc_sba(struct gprs_rlcmac_bts *bts, uint8_t ta);
|
||||||
|
|
||||||
|
void bts_recalc_initial_cs(struct gprs_rlcmac_bts *bts);
|
||||||
|
void bts_recalc_initial_mcs(struct gprs_rlcmac_bts *bts);
|
||||||
|
void bts_recalc_max_cs(struct gprs_rlcmac_bts *bts);
|
||||||
|
void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts);
|
||||||
|
struct GprsMs *bts_ms_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
|
||||||
|
uint8_t bts_max_cs_dl(const struct gprs_rlcmac_bts *bts);
|
||||||
|
uint8_t bts_max_cs_ul(const struct gprs_rlcmac_bts *bts);
|
||||||
|
uint8_t bts_max_mcs_dl(const struct gprs_rlcmac_bts *bts);
|
||||||
|
uint8_t bts_max_mcs_ul(const struct gprs_rlcmac_bts *bts);
|
||||||
|
void bts_set_max_cs_dl(struct gprs_rlcmac_bts *bts, uint8_t cs_dl);
|
||||||
|
void bts_set_max_cs_ul(struct gprs_rlcmac_bts *bts, uint8_t cs_ul);
|
||||||
|
void bts_set_max_mcs_dl(struct gprs_rlcmac_bts *bts, uint8_t mcs_dl);
|
||||||
|
void bts_set_max_mcs_ul(struct gprs_rlcmac_bts *bts, uint8_t mcs_ul);
|
||||||
|
bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts *bts, enum CodingScheme cs);
|
||||||
|
const struct llist_head* bts_ms_list(struct gprs_rlcmac_bts *bts);
|
||||||
|
uint8_t bts_get_ms_pwr_alpha(const struct gprs_rlcmac_bts *bts);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
388
src/bts_anr_fsm.c
Normal file
388
src/bts_anr_fsm.c
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
/* bts_anr_fsm.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <talloc.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
|
#include <osmocom/ctrl/control_cmd.h>
|
||||||
|
#include <osmocom/ctrl/control_if.h>
|
||||||
|
|
||||||
|
#include <osmocom/gsm/gsm48.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||||
|
|
||||||
|
#include <bts_anr_fsm.h>
|
||||||
|
#include <ms_anr_fsm.h>
|
||||||
|
#include <gprs_rlcmac.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
#include <gprs_ms.h>
|
||||||
|
#include <encoding.h>
|
||||||
|
#include <bts.h>
|
||||||
|
#include <neigh_cache.h>
|
||||||
|
|
||||||
|
#define X(s) (1 << (s))
|
||||||
|
|
||||||
|
/* Ask the MS to measure up to 5 neighbors at a time */
|
||||||
|
#define ANR_MAX_NEIGH_SUBSET 5
|
||||||
|
|
||||||
|
static const struct osmo_tdef_state_timeout bts_anr_fsm_timeouts[32] = {
|
||||||
|
[BTS_ANR_ST_DISABLED] = {},
|
||||||
|
[BTS_ANR_ST_ENABLED] = { .T = PCU_TDEF_ANR_SCHED_TBF },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
|
||||||
|
* The actual timeout value is in turn obtained from conn->T_defs.
|
||||||
|
* Assumes local variable fi exists. */
|
||||||
|
|
||||||
|
#define bts_anr_fsm_state_chg(fi, NEXT_STATE) \
|
||||||
|
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
|
||||||
|
bts_anr_fsm_timeouts, \
|
||||||
|
((struct bts_anr_fsm_ctx*)(fi->priv))->bts->pcu->T_defs, \
|
||||||
|
-1)
|
||||||
|
|
||||||
|
const struct value_string bts_anr_fsm_event_names[] = {
|
||||||
|
{ BTS_ANR_EV_RX_ANR_REQ, "RX_ANR_REQ" },
|
||||||
|
{ BTS_ANR_EV_SCHED_MS_MEAS, "SCHED_MS_MEAS" },
|
||||||
|
{ BTS_ANR_EV_MS_MEAS_COMPL, "MS_MEAS_COMPL" },
|
||||||
|
{ BTS_ANR_EV_MS_MEAS_ABORTED, "MS_MEAS_ABORTED" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void copy_sort_arfcn_bsic(struct bts_anr_fsm_ctx *ctx, const struct gsm48_cell_desc *cell_list, unsigned int num_cells)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(num_cells <= ARRAY_SIZE(ctx->cell_list));
|
||||||
|
uint16_t last_min_arfcn = 0;
|
||||||
|
uint8_t last_min_bsic = 0;
|
||||||
|
ctx->num_cells = 0;
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->bts;
|
||||||
|
|
||||||
|
/* Copy over ARFCN+BSIC in an ARFCN then BSIC ascending ordered way */
|
||||||
|
while (ctx->num_cells < num_cells) {
|
||||||
|
bool found = false;
|
||||||
|
uint16_t curr_min_arfcn = 0xffff;
|
||||||
|
uint8_t curr_min_bsic = 0xff;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_cells; i++) {
|
||||||
|
uint16_t arfcn = (cell_list[i].arfcn_hi << 8) | cell_list[i].arfcn_lo;
|
||||||
|
uint8_t bsic = (cell_list[i].ncc << 3) | cell_list[i].bcc;
|
||||||
|
if ((arfcn > last_min_arfcn || (arfcn == last_min_arfcn && bsic > last_min_bsic)) &&
|
||||||
|
(arfcn < curr_min_arfcn || (arfcn == curr_min_arfcn && bsic < curr_min_bsic))) {
|
||||||
|
found = true;
|
||||||
|
curr_min_arfcn = arfcn;
|
||||||
|
curr_min_bsic = bsic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
break; /* we are done before copying all, probably due to duplicated arfcn in list */
|
||||||
|
|
||||||
|
/* Copy lower ARFCN+BSIC to dst */
|
||||||
|
if (curr_min_arfcn != bts->trx[0].arfcn || curr_min_bsic != bts->bsic) {
|
||||||
|
ctx->cell_list[ctx->num_cells] = (struct arfcn_bsic){
|
||||||
|
.arfcn = curr_min_arfcn,
|
||||||
|
.bsic = curr_min_bsic,
|
||||||
|
};
|
||||||
|
ctx->num_cells++;
|
||||||
|
LOGPFSML(ctx->fi, LOGL_DEBUG, "Added neigh cell to ANR list: ARFCN=%u BSIC=%u\n",
|
||||||
|
curr_min_arfcn, curr_min_bsic);
|
||||||
|
} else {
|
||||||
|
LOGPFSML(ctx->fi, LOGL_DEBUG, "Skip neigh cell to ANR list (itself): ARFCN=%u BSIC=%u\n",
|
||||||
|
curr_min_arfcn, curr_min_bsic);
|
||||||
|
}
|
||||||
|
last_min_arfcn = curr_min_arfcn;
|
||||||
|
last_min_bsic = curr_min_bsic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rx_new_cell_list(struct bts_anr_fsm_ctx *ctx, const struct gsm_pcu_if_anr_req *anr_req)
|
||||||
|
{
|
||||||
|
unsigned int num_cells = anr_req->num_cells;
|
||||||
|
if (anr_req->num_cells > ARRAY_SIZE(anr_req->cell_list)) {
|
||||||
|
LOGPFSML(ctx->fi, LOGL_ERROR, "Too many cells received %u > %zu (max), trimming it\n",
|
||||||
|
anr_req->num_cells, ARRAY_SIZE(anr_req->cell_list));
|
||||||
|
num_cells = ARRAY_SIZE(anr_req->cell_list);
|
||||||
|
}
|
||||||
|
copy_sort_arfcn_bsic(ctx, (const struct gsm48_cell_desc *)anr_req->cell_list, num_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct GprsMs *select_candidate_ms(struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
struct llist_head *tmp;
|
||||||
|
/* prio top to bottom: 0,1,2: */
|
||||||
|
struct GprsMs *ms_dl_tbf_assign = NULL;
|
||||||
|
|
||||||
|
/* We'll need a DL TBF. Take with higher priority an MS which already
|
||||||
|
* has one, otherwise one in process of acquiring one. In last place an
|
||||||
|
* MS which has no DL-TBF yet. */
|
||||||
|
llist_for_each(tmp, bts_ms_list(bts)) {
|
||||||
|
struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
|
||||||
|
if (ms->anr) /* Don't pick MS already busy doing ANR */
|
||||||
|
continue;
|
||||||
|
if (!ms->dl_tbf)
|
||||||
|
continue;
|
||||||
|
switch (tbf_state((struct gprs_rlcmac_tbf*)ms->dl_tbf)) {
|
||||||
|
case TBF_ST_FLOW: /* Pick active DL-TBF as best option, early return: */
|
||||||
|
return ms;
|
||||||
|
case TBF_ST_ASSIGN:
|
||||||
|
ms_dl_tbf_assign = ms;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms_dl_tbf_assign)
|
||||||
|
return ms_dl_tbf_assign;
|
||||||
|
|
||||||
|
llist_for_each(tmp, bts_ms_list(bts)) {
|
||||||
|
struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
|
||||||
|
if (ms->anr) /* Don't pick MS already busy doing ANR */
|
||||||
|
continue;
|
||||||
|
if (!ms->dl_tbf) {
|
||||||
|
/* Trigger a Pkt Dl Assignment and do ANR procedure once it is active: */
|
||||||
|
struct gprs_rlcmac_dl_tbf *new_dl_tbf;
|
||||||
|
int rc;
|
||||||
|
rc = tbf_new_dl_assignment(ms->bts, ms, &new_dl_tbf);
|
||||||
|
if (rc < 0)
|
||||||
|
continue;
|
||||||
|
/* Fill the TBF with some LLC Dummy Command, since everyone expectes we send something to that DL TBF... */
|
||||||
|
uint16_t delay_csec = 0xffff;
|
||||||
|
/* The shortest dummy command (the spec requests at least 6 octets) */
|
||||||
|
const uint8_t llc_dummy_command[] = {
|
||||||
|
0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
|
||||||
|
};
|
||||||
|
dl_tbf_append_data(new_dl_tbf, delay_csec, &llc_dummy_command[0], ARRAY_SIZE(llc_dummy_command));
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build up cell list subset for this MS to measure: */
|
||||||
|
static size_t take_next_cell_list_chunk(struct bts_anr_fsm_ctx *ctx, struct arfcn_bsic ms_cell_li[MAX_NEIGH_LIST_LEN])
|
||||||
|
{
|
||||||
|
unsigned int subset_len = ANR_MAX_NEIGH_SUBSET;
|
||||||
|
if (ctx->num_cells <= subset_len) {
|
||||||
|
memcpy(ms_cell_li, ctx->cell_list, ctx->num_cells * sizeof(ctx->cell_list[0]));
|
||||||
|
subset_len = ctx->num_cells;
|
||||||
|
} else if ((ctx->num_cells - ctx->next_cell) >= subset_len) {
|
||||||
|
memcpy(ms_cell_li, &ctx->cell_list[ctx->next_cell], subset_len * sizeof(ctx->cell_list[0]));
|
||||||
|
ctx->next_cell = (ctx->next_cell + subset_len) % ctx->num_cells;
|
||||||
|
} else {
|
||||||
|
unsigned int len = (ctx->num_cells - ctx->next_cell);
|
||||||
|
memcpy(ms_cell_li, &ctx->cell_list[ctx->next_cell], len * sizeof(ctx->cell_list[0]));
|
||||||
|
memcpy(&ms_cell_li[len], &ctx->cell_list[0], subset_len - len);
|
||||||
|
ctx->next_cell = subset_len - len;
|
||||||
|
}
|
||||||
|
return subset_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sched_ms_meas_report(struct bts_anr_fsm_ctx *ctx, const struct arfcn_bsic* cell_list,
|
||||||
|
unsigned int num_cells)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->bts;
|
||||||
|
struct GprsMs *ms;
|
||||||
|
struct arfcn_bsic ms_cell_li[MAX_NEIGH_LIST_LEN];
|
||||||
|
/* HERE we'll:
|
||||||
|
* 1- Select a TBF candidate in the BTS
|
||||||
|
* 2- Pick a subset from ctx->cell_list (increasing index round buffer in array)
|
||||||
|
* 3- Send event to it to schedule the meas report [osmo_fsm_inst_dispatch(ms->meas_rep_fsm, MEAS_REP_EV_SCHEDULE, cell_sublist)]
|
||||||
|
* 4- Wait for event BTS_ANR_EV_MEAS_REP containing "Packet Measurement Report" as data
|
||||||
|
* 5- Filter out the list and submit it back over PCUIF */
|
||||||
|
|
||||||
|
/* First poor-man impl: pick first MS having a FLOW TBF: */
|
||||||
|
ms = select_candidate_ms(bts);
|
||||||
|
if (!ms) {
|
||||||
|
LOGPFSML(ctx->fi, LOGL_INFO, "Unable to find MS to start ANR measurements\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOGPMS(ms, DANR, LOGL_DEBUG, "Selected for ANR measurements\n");
|
||||||
|
|
||||||
|
/* Build up cell list subset for this MS to measure: */
|
||||||
|
if (!cell_list) {
|
||||||
|
num_cells = take_next_cell_list_chunk(ctx, &ms_cell_li[0]);
|
||||||
|
cell_list = &ms_cell_li[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms_anr_start(ms, cell_list, num_cells) < 0)
|
||||||
|
LOGPFSML(ctx->fi, LOGL_ERROR, "Unable to start ANR measurements on MS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_ms_meas_report(struct bts_anr_fsm_ctx *ctx, const struct ms_anr_ev_meas_compl* result)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->bts;
|
||||||
|
pcu_tx_anr_cnf(bts, result->cell_list, result->meas_list, result->num_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// FSM states //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
static void st_disabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
|
||||||
|
struct llist_head *tmp;
|
||||||
|
|
||||||
|
/* Abort ongoing scheduled ms_anr_fsm: */
|
||||||
|
llist_for_each(tmp, bts_ms_list(ctx->bts)) {
|
||||||
|
struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
|
||||||
|
/* Remark: ms_anr_fsm_abort does NOT send BTS_ANR_EV_MS_MEAS_ABORTED back at us */
|
||||||
|
if (ms->anr)
|
||||||
|
ms_anr_fsm_abort(ms->anr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_disabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
|
||||||
|
const struct gsm_pcu_if_anr_req *anr_req;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case BTS_ANR_EV_RX_ANR_REQ:
|
||||||
|
anr_req = (const struct gsm_pcu_if_anr_req *)data;
|
||||||
|
rx_new_cell_list(ctx, anr_req);
|
||||||
|
if (ctx->num_cells > 0)
|
||||||
|
bts_anr_fsm_state_chg(fi, BTS_ANR_ST_ENABLED);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
|
||||||
|
sched_ms_meas_report(ctx, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct bts_anr_fsm_ctx *ctx = (struct bts_anr_fsm_ctx *)fi->priv;
|
||||||
|
const struct gsm_pcu_if_anr_req *anr_req;
|
||||||
|
struct ms_anr_ev_abort *ev_abort_data;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case BTS_ANR_EV_RX_ANR_REQ:
|
||||||
|
anr_req = (const struct gsm_pcu_if_anr_req *)data;
|
||||||
|
rx_new_cell_list(ctx, anr_req);
|
||||||
|
if (ctx->num_cells == 0)
|
||||||
|
bts_anr_fsm_state_chg(fi, BTS_ANR_ST_DISABLED);
|
||||||
|
break;
|
||||||
|
case BTS_ANR_EV_SCHED_MS_MEAS:
|
||||||
|
sched_ms_meas_report(ctx, NULL, 0);
|
||||||
|
break;
|
||||||
|
case BTS_ANR_EV_MS_MEAS_ABORTED:
|
||||||
|
ev_abort_data = (struct ms_anr_ev_abort*)data;
|
||||||
|
sched_ms_meas_report(ctx, ev_abort_data->cell_list, ev_abort_data->num_cells);
|
||||||
|
break;
|
||||||
|
case BTS_ANR_EV_MS_MEAS_COMPL:
|
||||||
|
handle_ms_meas_report(ctx, (const struct ms_anr_ev_meas_compl*)data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*TODO: we need to track how many chunks are created, how many are in progress, how many are completed, etc. */
|
||||||
|
static int bts_anr_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||||
|
{
|
||||||
|
unsigned long timeout;
|
||||||
|
|
||||||
|
switch (fi->T) {
|
||||||
|
case PCU_TDEF_ANR_SCHED_TBF:
|
||||||
|
/* Re-schedule the timer */
|
||||||
|
timeout = osmo_tdef_get(((struct bts_anr_fsm_ctx*)(fi->priv))->bts->pcu->T_defs,
|
||||||
|
fi->T, OSMO_TDEF_S, -1);
|
||||||
|
osmo_timer_schedule(&fi->timer, timeout, 0);
|
||||||
|
/* Dispatch the schedule TBF MEAS event */
|
||||||
|
osmo_fsm_inst_dispatch(fi, BTS_ANR_EV_SCHED_MS_MEAS, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct osmo_fsm_state bts_anr_fsm_states[] = {
|
||||||
|
[BTS_ANR_ST_DISABLED] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(BTS_ANR_EV_RX_ANR_REQ),
|
||||||
|
.out_state_mask =
|
||||||
|
X(BTS_ANR_ST_ENABLED),
|
||||||
|
.name = "DISABLED",
|
||||||
|
.onenter = st_disabled_on_enter,
|
||||||
|
.action = st_disabled,
|
||||||
|
},
|
||||||
|
[BTS_ANR_ST_ENABLED] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(BTS_ANR_EV_RX_ANR_REQ) |
|
||||||
|
X(BTS_ANR_EV_SCHED_MS_MEAS) |
|
||||||
|
X(BTS_ANR_EV_MS_MEAS_COMPL) |
|
||||||
|
X(BTS_ANR_EV_MS_MEAS_ABORTED),
|
||||||
|
.out_state_mask =
|
||||||
|
X(BTS_ANR_ST_DISABLED),
|
||||||
|
.name = "ENABLED",
|
||||||
|
.onenter = st_enabled_on_enter,
|
||||||
|
.action = st_enabled,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct osmo_fsm bts_anr_fsm = {
|
||||||
|
.name = "BTS_ANR",
|
||||||
|
.states = bts_anr_fsm_states,
|
||||||
|
.num_states = ARRAY_SIZE(bts_anr_fsm_states),
|
||||||
|
.timer_cb = bts_anr_fsm_timer_cb,
|
||||||
|
.log_subsys = DANR,
|
||||||
|
.event_names = bts_anr_fsm_event_names,
|
||||||
|
};
|
||||||
|
|
||||||
|
static __attribute__((constructor)) void bts_anr_fsm_init(void)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(osmo_fsm_register(&bts_anr_fsm) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bts_anr_fsm_ctx_talloc_destructor(struct bts_anr_fsm_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->fi) {
|
||||||
|
osmo_fsm_inst_free(ctx->fi);
|
||||||
|
ctx->fi = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bts_anr_fsm_ctx *bts_anr_fsm_alloc(struct gprs_rlcmac_bts* bts)
|
||||||
|
{
|
||||||
|
struct bts_anr_fsm_ctx *ctx = talloc_zero(bts, struct bts_anr_fsm_ctx);
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
talloc_set_destructor(ctx, bts_anr_fsm_ctx_talloc_destructor);
|
||||||
|
|
||||||
|
ctx->bts = bts;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "BTS-%u", bts->nr);
|
||||||
|
ctx->fi = osmo_fsm_inst_alloc(&bts_anr_fsm, ctx, ctx, LOGL_INFO, buf);
|
||||||
|
if (!ctx->fi)
|
||||||
|
goto free_ret;
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
free_ret:
|
||||||
|
talloc_free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
65
src/bts_anr_fsm.h
Normal file
65
src/bts_anr_fsm.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/* bts_anr_fsm.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/fsm.h>
|
||||||
|
#include <osmocom/gsm/gsm23003.h>
|
||||||
|
|
||||||
|
#include <pcu_utils.h>
|
||||||
|
|
||||||
|
#define MAX_NEIGH_LIST_LEN 96
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts;
|
||||||
|
|
||||||
|
enum bts_anr_fsm_event {
|
||||||
|
BTS_ANR_EV_RX_ANR_REQ, /* data: struct gsm_pcu_if_anr_req* */
|
||||||
|
BTS_ANR_EV_SCHED_MS_MEAS,
|
||||||
|
BTS_ANR_EV_MS_MEAS_COMPL, /* data: struct ms_anr_ev_meas_compl* */
|
||||||
|
BTS_ANR_EV_MS_MEAS_ABORTED, /* data: struct ms_anr_ev_meas_abort* */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum bts_anr_fsm_states {
|
||||||
|
BTS_ANR_ST_DISABLED,
|
||||||
|
BTS_ANR_ST_ENABLED
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bts_anr_fsm_ctx {
|
||||||
|
struct osmo_fsm_inst *fi;
|
||||||
|
struct gprs_rlcmac_bts *bts; /* back pointer */
|
||||||
|
struct arfcn_bsic cell_list[MAX_NEIGH_LIST_LEN]; /* ordered by ascending ARFCN */
|
||||||
|
unsigned int num_cells;
|
||||||
|
unsigned int next_cell; /* Next cell list subset starts from this index */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* passed as data in BTS_ANR_EV_MS_MEAS_COMPL */
|
||||||
|
|
||||||
|
struct ms_anr_ev_meas_compl {
|
||||||
|
const struct arfcn_bsic *cell_list; /* len() = num_cells */
|
||||||
|
const uint8_t *meas_list; /* len() = num_cells, value 0xff means invalid */
|
||||||
|
unsigned int num_cells;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* passed as data in BTS_ANR_EV_MS_MEAS_ABORT */
|
||||||
|
struct ms_anr_ev_abort {
|
||||||
|
const struct arfcn_bsic* cell_list;
|
||||||
|
unsigned int num_cells;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bts_anr_fsm_ctx *bts_anr_fsm_alloc(struct gprs_rlcmac_bts* bts);
|
||||||
408
src/coding_scheme.c
Normal file
408
src/coding_scheme.c
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
/* coding_scheme.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 by sysmocom s.f.m.c. GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
|
#include "coding_scheme.h"
|
||||||
|
|
||||||
|
const struct value_string mcs_names[] = {
|
||||||
|
{ UNKNOWN, "UNKNOWN" },
|
||||||
|
{ CS1, "CS-1" },
|
||||||
|
{ CS2, "CS-2" },
|
||||||
|
{ CS3, "CS-3" },
|
||||||
|
{ CS4, "CS-4" },
|
||||||
|
{ MCS1, "MCS-1" },
|
||||||
|
{ MCS2, "MCS-2" },
|
||||||
|
{ MCS3, "MCS-3" },
|
||||||
|
{ MCS4, "MCS-4" },
|
||||||
|
{ MCS5, "MCS-5" },
|
||||||
|
{ MCS6, "MCS-6" },
|
||||||
|
{ MCS7, "MCS-7" },
|
||||||
|
{ MCS8, "MCS-8" },
|
||||||
|
{ MCS9, "MCS-9" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Family {
|
||||||
|
FAMILY_INVALID,
|
||||||
|
FAMILY_A,
|
||||||
|
FAMILY_B,
|
||||||
|
FAMILY_C,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
struct {
|
||||||
|
uint8_t bytes;
|
||||||
|
uint8_t ext_bits;
|
||||||
|
uint8_t data_header_bits;
|
||||||
|
} uplink, downlink;
|
||||||
|
uint8_t data_bytes;
|
||||||
|
uint8_t optional_padding_bits;
|
||||||
|
enum HeaderType data_hdr;
|
||||||
|
enum Family family;
|
||||||
|
} mcs_info[NUM_SCHEMES] = {
|
||||||
|
{{0, 0}, {0, 0}, 0, 0,
|
||||||
|
HEADER_INVALID, FAMILY_INVALID},
|
||||||
|
{{23, 0}, {23, 0}, 20, 0,
|
||||||
|
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||||
|
{{33, 7}, {33, 7}, 30, 0,
|
||||||
|
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||||
|
{{39, 3}, {39, 3}, 36, 0,
|
||||||
|
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||||
|
{{53, 7}, {53, 7}, 50, 0,
|
||||||
|
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||||
|
|
||||||
|
{{26, 1}, {26, 1}, 22, 0,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
|
||||||
|
{{32, 1}, {32, 1}, 28, 0,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_3, FAMILY_B},
|
||||||
|
{{41, 1}, {41, 1}, 37, 48,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_3, FAMILY_A},
|
||||||
|
{{48, 1}, {48, 1}, 44, 0,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
|
||||||
|
|
||||||
|
{{60, 7}, {59, 6}, 56, 0,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_2, FAMILY_B},
|
||||||
|
{{78, 7}, {77, 6}, 74, 48,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_2, FAMILY_A},
|
||||||
|
{{118, 2}, {117, 4}, 56, 0,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_1, FAMILY_B},
|
||||||
|
{{142, 2}, {141, 4}, 68, 0,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
|
||||||
|
{{154, 2}, {153, 4}, 74, 0,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *mcs_name(enum CodingScheme val) {
|
||||||
|
return get_value_string(mcs_names, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcs_is_gprs(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return CS1 <= cs && cs <= CS4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcs_is_edge(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return MCS1 <= cs && cs <= MCS9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcs_is_edge_gmsk(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
if (mcs_is_edge(cs))
|
||||||
|
return cs <= MCS4;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return 3GPP TS 44.060 §12.10d (EDGE) or Table 11.2.28.2 (GPRS) Channel Coding Command value */
|
||||||
|
uint8_t mcs_chan_code(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
if (mcs_is_gprs(cs))
|
||||||
|
return cs - CS1;
|
||||||
|
|
||||||
|
if (mcs_is_edge(cs))
|
||||||
|
return cs - MCS1;
|
||||||
|
|
||||||
|
/* Defaults to (M)CS1 */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CodingScheme mcs_get_by_size_ul(unsigned size)
|
||||||
|
{
|
||||||
|
switch (size) {
|
||||||
|
case 23: return CS1;
|
||||||
|
case 27: return MCS1;
|
||||||
|
case 33: return MCS2;
|
||||||
|
case 34: return CS2;
|
||||||
|
case 40: return CS3;
|
||||||
|
case 42: return MCS3;
|
||||||
|
case 49: return MCS4;
|
||||||
|
case 54: return CS4;
|
||||||
|
case 61: return MCS5;
|
||||||
|
case 79: return MCS6;
|
||||||
|
case 119: return MCS7;
|
||||||
|
case 143: return MCS8;
|
||||||
|
case 155: return MCS9;
|
||||||
|
default: return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CodingScheme mcs_get_gprs_by_num(unsigned num)
|
||||||
|
{
|
||||||
|
if (num < 1 || num > 4)
|
||||||
|
return UNKNOWN;
|
||||||
|
return CS1 + (num - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CodingScheme mcs_get_egprs_by_num(unsigned num)
|
||||||
|
{
|
||||||
|
if (num < 1 || num > 9)
|
||||||
|
return UNKNOWN;
|
||||||
|
return MCS1 + (num - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcs_is_valid(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return UNKNOWN < cs && cs <= MCS9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case GPRS: return mcs_is_gprs(cs);
|
||||||
|
case EGPRS_GMSK: return mcs_is_edge_gmsk(cs);
|
||||||
|
case EGPRS: return mcs_is_edge(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o)
|
||||||
|
{
|
||||||
|
return (mcs_is_gprs(cs) && mcs_is_gprs(o)) || (mcs_is_edge(cs) && mcs_is_edge(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_size_ul(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].uplink.bytes + (mcs_spare_bits_ul(cs) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_size_dl(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].downlink.bytes + (mcs_spare_bits_dl(cs) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_used_size_ul(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
|
||||||
|
return mcs_info[cs].uplink.bytes;
|
||||||
|
else
|
||||||
|
return mcs_size_ul(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_used_size_dl(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
|
||||||
|
return mcs_info[cs].downlink.bytes;
|
||||||
|
else
|
||||||
|
return mcs_size_dl(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_max_bytes_ul(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].uplink.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_max_bytes_dl(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].downlink.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_spare_bits_ul(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].uplink.ext_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_spare_bits_dl(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].downlink.ext_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].data_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mcs_opt_padding_bits(enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
return mcs_info[cs].optional_padding_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode)
|
||||||
|
{
|
||||||
|
if (!mcs_is_compat_kind(*cs, mode))
|
||||||
|
/* This should not happen. TODO: Use assert? */
|
||||||
|
return;
|
||||||
|
|
||||||
|
enum CodingScheme new_cs = *cs + 1;
|
||||||
|
if (!mcs_is_compat_kind(new_cs, mode))
|
||||||
|
/* Clipping, do not change the value */
|
||||||
|
return;
|
||||||
|
|
||||||
|
*cs = new_cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode)
|
||||||
|
{
|
||||||
|
if (!mcs_is_compat_kind(*cs, mode))
|
||||||
|
/* This should not happen. TODO: Use assert? */
|
||||||
|
return;
|
||||||
|
|
||||||
|
enum CodingScheme new_cs = *cs - 1;
|
||||||
|
if (!mcs_is_compat_kind(new_cs, mode))
|
||||||
|
/* Clipping, do not change the value */
|
||||||
|
return;
|
||||||
|
|
||||||
|
*cs = new_cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mcs_inc(enum CodingScheme *cs)
|
||||||
|
{
|
||||||
|
if (mcs_is_gprs(*cs) && *cs == CS4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mcs_is_edge(*cs) && *cs == MCS9)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!mcs_is_valid(*cs))
|
||||||
|
return;
|
||||||
|
|
||||||
|
*cs = *cs + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mcs_dec(enum CodingScheme *cs)
|
||||||
|
{
|
||||||
|
if (mcs_is_gprs(*cs) && *cs == CS1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mcs_is_edge(*cs) && *cs == MCS1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!mcs_is_valid(*cs))
|
||||||
|
return;
|
||||||
|
|
||||||
|
*cs = *cs - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o)
|
||||||
|
{
|
||||||
|
if (cs == o)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (mcs_info[cs].family == FAMILY_INVALID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return mcs_info[cs].family == mcs_info[o].family;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing)
|
||||||
|
{
|
||||||
|
switch (*cs) {
|
||||||
|
case MCS7: *need_stuffing = false; *cs = MCS5; break;
|
||||||
|
case MCS8: *need_stuffing = true; *cs = MCS6; break;
|
||||||
|
case MCS9: *need_stuffing = false; *cs = MCS6; break;
|
||||||
|
default: *need_stuffing = false; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
struct {
|
||||||
|
uint8_t data_header_bits;
|
||||||
|
} uplink, downlink;
|
||||||
|
uint8_t data_block_header_bits;
|
||||||
|
uint8_t num_blocks;
|
||||||
|
const char *name;
|
||||||
|
} hdr_type_info[NUM_HEADER_TYPES] = {
|
||||||
|
{ { 0 }, { 0 }, 0, 0, "INVALID" },
|
||||||
|
{ { 1 * 8 + 0 }, { 1 * 8 + 0 }, 0, 0, "CONTROL" },
|
||||||
|
{ { 3 * 8 + 0 }, { 3 * 8 + 0 }, 0, 1, "GPRS_DATA" },
|
||||||
|
{ { 5 * 8 + 6 }, { 5 * 8 + 0 }, 2, 2, "EGPRS_DATA_TYPE1" },
|
||||||
|
{ { 4 * 8 + 5 }, { 3 * 8 + 4 }, 2, 1, "EGPRS_DATA_TYPE2" },
|
||||||
|
{ { 3 * 8 + 7 }, { 3 * 8 + 7 }, 2, 1, "EGPRS_DATA_TYPE3" },
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HeaderType mcs_header_type(enum CodingScheme mcs)
|
||||||
|
{
|
||||||
|
return mcs_info[mcs].data_hdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t num_data_blocks(enum HeaderType ht)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||||
|
return hdr_type_info[ht].num_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t num_data_header_bits_UL(enum HeaderType ht)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||||
|
return hdr_type_info[ht].uplink.data_header_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t num_data_header_bits_DL(enum HeaderType ht)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||||
|
return hdr_type_info[ht].downlink.data_header_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t num_data_block_header_bits(enum HeaderType ht)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||||
|
return hdr_type_info[ht].data_block_header_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct value_string mode_names[] = {
|
||||||
|
{ GPRS, "GPRS" },
|
||||||
|
{ EGPRS_GMSK, "EGPRS_GMSK-only"},
|
||||||
|
{ EGPRS, "EGPRS"},
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *mode_name(enum mcs_kind val) {
|
||||||
|
return get_value_string(mode_names, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: take into account padding and special cases of commanded MCS (MCS-6-9 and MCS-5-7) */
|
||||||
|
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(mcs_is_edge(initial_mcs));
|
||||||
|
OSMO_ASSERT(mcs_is_edge(commanded_mcs));
|
||||||
|
OSMO_ASSERT(NUM_SCHEMES - MCS1 == 9);
|
||||||
|
|
||||||
|
if (resegment_bit) { /* 3GPP TS 44.060 Table 8.1.1.1, reflected over antidiagonal */
|
||||||
|
enum CodingScheme egprs_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
|
||||||
|
{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
|
||||||
|
{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
|
||||||
|
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
|
||||||
|
{ MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
|
||||||
|
{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||||
|
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
|
||||||
|
{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||||
|
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8 },
|
||||||
|
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
|
||||||
|
};
|
||||||
|
return egprs_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
|
||||||
|
} else { /* 3GPP TS 44.060 Table 8.1.1.2, reflected over antidiagonal */
|
||||||
|
enum CodingScheme egprs_no_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
|
||||||
|
{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
|
||||||
|
{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
|
||||||
|
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
|
||||||
|
{ MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
|
||||||
|
{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||||
|
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
|
||||||
|
{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||||
|
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8 },
|
||||||
|
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
|
||||||
|
};
|
||||||
|
return egprs_no_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/coding_scheme.h
Normal file
99
src/coding_scheme.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/* coding_scheme.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2019 by sysmocom s.f.m.c. GmbH
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
enum CodingScheme {
|
||||||
|
UNKNOWN,
|
||||||
|
/* GPRS Coding Schemes: */
|
||||||
|
CS1, CS2, CS3, CS4,
|
||||||
|
/* EDGE/EGPRS Modulation and Coding Schemes: */
|
||||||
|
MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
|
||||||
|
NUM_SCHEMES
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mcs_kind {
|
||||||
|
GPRS,
|
||||||
|
EGPRS_GMSK,
|
||||||
|
EGPRS,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum egprs_arq_type {
|
||||||
|
EGPRS_ARQ1 = 0,
|
||||||
|
EGPRS_ARQ2 = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct value_string mcs_names[];
|
||||||
|
const char *mcs_name(enum CodingScheme val);
|
||||||
|
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit);
|
||||||
|
|
||||||
|
bool mcs_is_gprs(enum CodingScheme cs);
|
||||||
|
bool mcs_is_edge(enum CodingScheme cs);
|
||||||
|
bool mcs_is_edge_gmsk(enum CodingScheme cs);
|
||||||
|
|
||||||
|
uint8_t mcs_chan_code(enum CodingScheme cs);
|
||||||
|
|
||||||
|
enum CodingScheme mcs_get_by_size_ul(unsigned size);
|
||||||
|
enum CodingScheme mcs_get_gprs_by_num(unsigned num);
|
||||||
|
enum CodingScheme mcs_get_egprs_by_num(unsigned num);
|
||||||
|
bool mcs_is_valid(enum CodingScheme cs);
|
||||||
|
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o);
|
||||||
|
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode);
|
||||||
|
|
||||||
|
uint8_t mcs_size_ul(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_size_dl(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_used_size_ul(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_used_size_dl(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_max_bytes_ul(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_max_bytes_dl(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_spare_bits_ul(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_spare_bits_dl(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs);
|
||||||
|
uint8_t mcs_opt_padding_bits(enum CodingScheme cs);
|
||||||
|
|
||||||
|
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode);
|
||||||
|
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode);
|
||||||
|
void mcs_inc(enum CodingScheme *cs);
|
||||||
|
void mcs_dec(enum CodingScheme *cs);
|
||||||
|
|
||||||
|
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o);
|
||||||
|
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing);
|
||||||
|
|
||||||
|
enum HeaderType {
|
||||||
|
HEADER_INVALID,
|
||||||
|
HEADER_GPRS_CONTROL,
|
||||||
|
HEADER_GPRS_DATA,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_1,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_2,
|
||||||
|
HEADER_EGPRS_DATA_TYPE_3,
|
||||||
|
NUM_HEADER_TYPES
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HeaderType mcs_header_type(enum CodingScheme mcs);
|
||||||
|
|
||||||
|
uint8_t num_data_blocks(enum HeaderType ht);
|
||||||
|
uint8_t num_data_header_bits_UL(enum HeaderType ht);
|
||||||
|
uint8_t num_data_header_bits_DL(enum HeaderType ht);
|
||||||
|
uint8_t num_data_block_header_bits(enum HeaderType ht);
|
||||||
|
|
||||||
|
const char *mode_name(enum mcs_kind val);
|
||||||
File diff suppressed because it is too large
Load Diff
155
src/csn1.h
155
src/csn1.h
@@ -25,15 +25,11 @@
|
|||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <bitvector.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdlib>
|
|
||||||
#ifndef _PACKET_CSN1_H_
|
#ifndef _PACKET_CSN1_H_
|
||||||
#define _PACKET_CSN1_H_
|
#define _PACKET_CSN1_H_
|
||||||
|
|
||||||
|
#include <osmocom/core/bitvec.h>
|
||||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
#include "wireshark_compat.h"
|
||||||
//#define max(a,b) (((a)>(b))?(a):(b))
|
|
||||||
|
|
||||||
/* Error codes */
|
/* Error codes */
|
||||||
#define CSN_OK 0
|
#define CSN_OK 0
|
||||||
@@ -48,15 +44,6 @@
|
|||||||
#define CSN_ERROR_MESSAGE_TOO_LONG -9
|
#define CSN_ERROR_MESSAGE_TOO_LONG -9
|
||||||
#define CSN_ERROR_ -10
|
#define CSN_ERROR_ -10
|
||||||
|
|
||||||
#define FALSE (0)
|
|
||||||
typedef signed int gint32;
|
|
||||||
typedef signed short gint16;
|
|
||||||
typedef int gint;
|
|
||||||
typedef gint gboolean;
|
|
||||||
typedef unsigned char guint8;
|
|
||||||
typedef unsigned short guint16;
|
|
||||||
typedef unsigned int guint32;
|
|
||||||
typedef unsigned long guint64;
|
|
||||||
/* CallBack return status */
|
/* CallBack return status */
|
||||||
typedef gint16 CSN_CallBackStatus_t;
|
typedef gint16 CSN_CallBackStatus_t;
|
||||||
|
|
||||||
@@ -73,6 +60,7 @@ typedef gint16 CSN_CallBackStatus_t;
|
|||||||
#ifndef ElementsOf
|
#ifndef ElementsOf
|
||||||
#define ElementsOf(array) (sizeof(array) / sizeof(array[0]))
|
#define ElementsOf(array) (sizeof(array) / sizeof(array[0]))
|
||||||
#endif
|
#endif
|
||||||
|
typedef void(*void_fn_t)(void);
|
||||||
|
|
||||||
/* Context holding CSN1 parameters */
|
/* Context holding CSN1 parameters */
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -82,7 +70,9 @@ typedef struct
|
|||||||
gint direction; /* 0 - decode; 1 - encode */
|
gint direction; /* 0 - decode; 1 - encode */
|
||||||
} csnStream_t;
|
} csnStream_t;
|
||||||
|
|
||||||
typedef gint16 (*StreamSerializeFcn_t)(csnStream_t* ar, bitvec *vector, unsigned& readIndex, void* data);
|
typedef gint16 (*StreamSerializeFcn_t)(csnStream_t* ar, struct bitvec *vector, unsigned *readIndex, void* data);
|
||||||
|
typedef CSN_CallBackStatus_t (*DissectorCallbackFcn_t)(struct bitvec *vector, unsigned *readIndex, void* param1, void* param2);
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
CSN_END = 0,
|
CSN_END = 0,
|
||||||
@@ -99,6 +89,7 @@ typedef enum
|
|||||||
CSN_VARIABLE_BITMAP_1, /* <bitmap: bit**> i.e. to the end of message (R99) */
|
CSN_VARIABLE_BITMAP_1, /* <bitmap: bit**> i.e. to the end of message (R99) */
|
||||||
CSN_LEFT_ALIGNED_VAR_BMP, /* As variable bitmap but the result is left aligned (R99) */
|
CSN_LEFT_ALIGNED_VAR_BMP, /* As variable bitmap but the result is left aligned (R99) */
|
||||||
CSN_LEFT_ALIGNED_VAR_BMP_1,/* As above only size is to the end of message (R99) */
|
CSN_LEFT_ALIGNED_VAR_BMP_1,/* As above only size is to the end of message (R99) */
|
||||||
|
CSN_PADDING_BITS, /* Padding bits fill to the end of the buffer */
|
||||||
CSN_VARIABLE_ARRAY, /* Array with length specified in parameter: <N: bit(4)> <list: octet(N + offset)> */
|
CSN_VARIABLE_ARRAY, /* Array with length specified in parameter: <N: bit(4)> <list: octet(N + offset)> */
|
||||||
CSN_VARIABLE_TARRAY, /* Type Array with length specified in parameter: <N: bit(x)> <Type>*N */
|
CSN_VARIABLE_TARRAY, /* Type Array with length specified in parameter: <N: bit(x)> <Type>*N */
|
||||||
CSN_VARIABLE_TARRAY_OFFSET,/* As above but with offset. The offset is stored as third parameter of CSN_DESCR (descr.value) */
|
CSN_VARIABLE_TARRAY_OFFSET,/* As above but with offset. The offset is stored as third parameter of CSN_DESCR (descr.value) */
|
||||||
@@ -127,7 +118,7 @@ typedef enum
|
|||||||
*
|
*
|
||||||
* i:
|
* i:
|
||||||
* Depending on the contents of the type parameter, the parameter "i" may have following meaning:
|
* Depending on the contents of the type parameter, the parameter "i" may have following meaning:
|
||||||
* - specifies the number of bits for the CSN_UINT type
|
* - specifies the number of bits for the CSN_UINT or CSN_UINT_OR_NULL types
|
||||||
* - the offset for an array size by which the size is incremented
|
* - the offset for an array size by which the size is incremented
|
||||||
* for the CSN_VAR_ARRAY type
|
* for the CSN_VAR_ARRAY type
|
||||||
* - the length of each element of an array for the CSN_REC_ARRAY type
|
* - the length of each element of an array for the CSN_REC_ARRAY type
|
||||||
@@ -142,6 +133,7 @@ typedef enum
|
|||||||
* CSN_VAR_BITMAP, CSN_LEFT_VAR_BMP, and CSN_LEFT_BMP_1 types
|
* CSN_VAR_BITMAP, CSN_LEFT_VAR_BMP, and CSN_LEFT_BMP_1 types
|
||||||
* - the offset to param1 for the CSN_CALLBACK type
|
* - the offset to param1 for the CSN_CALLBACK type
|
||||||
* - ERRORCODE used by the CSN_ERROR type
|
* - ERRORCODE used by the CSN_ERROR type
|
||||||
|
* - the bit-length of the LENGTH field in a CSN_SERIALISE type
|
||||||
*
|
*
|
||||||
* descr
|
* descr
|
||||||
* This parameter has different meaning depending on the value of the type parameter:
|
* This parameter has different meaning depending on the value of the type parameter:
|
||||||
@@ -167,6 +159,10 @@ typedef enum
|
|||||||
* - an offset to the variable Exist for CSN_NEXT_EXIST and CSN_NEXT_EXIST_LH types
|
* - an offset to the variable Exist for CSN_NEXT_EXIST and CSN_NEXT_EXIST_LH types
|
||||||
* - an offset to param2 in the CSN_CALLBACK type
|
* - an offset to param2 in the CSN_CALLBACK type
|
||||||
*
|
*
|
||||||
|
* may_be_null
|
||||||
|
* TRUE: if dissection may be attempted at an offset beyond the length of existing data bits
|
||||||
|
* FALSE: othewise
|
||||||
|
*
|
||||||
* sz
|
* sz
|
||||||
* - is the name of the parameter within the descr where their unpacked or packed value shall be stored or fetched.
|
* - is the name of the parameter within the descr where their unpacked or packed value shall be stored or fetched.
|
||||||
* This paramater is pointed out by the offset parameter in the same CSN_DESCR variable as the sz.
|
* This paramater is pointed out by the offset parameter in the same CSN_DESCR variable as the sz.
|
||||||
@@ -184,23 +180,21 @@ typedef struct
|
|||||||
gint16 i;
|
gint16 i;
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
void* ptr;
|
const void* ptr;
|
||||||
guint32 value;
|
guint32 value;
|
||||||
} descr;
|
} descr;
|
||||||
unsigned offset;
|
unsigned offset;
|
||||||
|
gboolean may_be_null;
|
||||||
const char* sz;
|
const char* sz;
|
||||||
union
|
guint32 value;
|
||||||
{
|
void_fn_t aux_fn;
|
||||||
StreamSerializeFcn_t fcn;
|
|
||||||
guint32 value;
|
|
||||||
int* hf_ptr;
|
|
||||||
} serialize;
|
|
||||||
} CSN_DESCR;
|
} CSN_DESCR;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
guint8 bits;
|
guint8 bits;
|
||||||
guint8 value;
|
guint8 value;
|
||||||
|
gboolean keep_bits;
|
||||||
CSN_DESCR descr;
|
CSN_DESCR descr;
|
||||||
} CSN_ChoiceElement_t;
|
} CSN_ChoiceElement_t;
|
||||||
|
|
||||||
@@ -223,16 +217,16 @@ void csnStreamInit(csnStream_t* ar,gint BitOffset,gint BitCount);
|
|||||||
* RETURNS: int Number of bits left to be unpacked. Negative Error code if failed to unpack all bits
|
* RETURNS: int Number of bits left to be unpacked. Negative Error code if failed to unpack all bits
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
gint16 csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsigned& readIndex, void* data);
|
gint16 csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, struct bitvec *vector, unsigned *readIndex, void* data);
|
||||||
|
|
||||||
gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsigned& readIndex, void* data);
|
gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, struct bitvec *vector, unsigned *writeIndex, void* data);
|
||||||
|
|
||||||
/* CSN struct macro's */
|
/* CSN struct macro's */
|
||||||
#define CSN_DESCR_BEGIN(_STRUCT)\
|
#define CSN_DESCR_BEGIN(_STRUCT)\
|
||||||
CSN_DESCR CSNDESCR_##_STRUCT[] = {
|
CSN_DESCR CSNDESCR_##_STRUCT[] = {
|
||||||
|
|
||||||
#define CSN_DESCR_END(_STRUCT)\
|
#define CSN_DESCR_END(_STRUCT)\
|
||||||
{CSN_END, 0, {0}, 0, "", {(StreamSerializeFcn_t)0}} };
|
{CSN_END, 0, {0}, 0, FALSE, "", 0, NULL} };
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* CSN_ERROR(Par1, Par2, Par3)
|
* CSN_ERROR(Par1, Par2, Par3)
|
||||||
@@ -243,7 +237,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par3: Error code
|
* Par3: Error code
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define CSN_ERROR(_STRUCT, _Text, _ERRCODE)\
|
#define CSN_ERROR(_STRUCT, _Text, _ERRCODE)\
|
||||||
{CSN_TRAP_ERROR, _ERRCODE, {(void*)_Text}, 0, _Text, {(StreamSerializeFcn_t)0}}
|
{CSN_TRAP_ERROR, _ERRCODE, {_Text}, 0, FALSE, _Text, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_BIT(Par1, Par2)
|
* M_BIT(Par1, Par2)
|
||||||
@@ -252,7 +246,17 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par2: C structure element name
|
* Par2: C structure element name
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_BIT(_STRUCT, _MEMBER)\
|
#define M_BIT(_STRUCT, _MEMBER)\
|
||||||
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* M_BIT_OR_NULL(Par1, Par2)
|
||||||
|
* Similar to the M_BIT except that not only bit 0 or 1 but also the
|
||||||
|
* end of the message may be encountered when looking for the next element in
|
||||||
|
* the message.
|
||||||
|
* Covers the case {null | 0 | 1}
|
||||||
|
*****************************************************************************/
|
||||||
|
#define M_BIT_OR_NULL(_STRUCT, _MEMBER)\
|
||||||
|
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_NEXT_EXIST(Par1, Par2, Par3)
|
* M_NEXT_EXIST(Par1, Par2, Par3)
|
||||||
@@ -264,7 +268,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* element(s) does not exist
|
* element(s) does not exist
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_NEXT_EXIST(_STRUCT, _MEMBER, _NoOfExisting)\
|
#define M_NEXT_EXIST(_STRUCT, _MEMBER, _NoOfExisting)\
|
||||||
{CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_NEXT_EXIST_LH(Par1, Par2, Par3)
|
* M_NEXT_EXIST_LH(Par1, Par2, Par3)
|
||||||
@@ -274,7 +278,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* pattern 0x2B is performed on the read bit before the decision is made.
|
* pattern 0x2B is performed on the read bit before the decision is made.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_NEXT_EXIST_LH(_STRUCT, _MEMBER, _NoOfExisting)\
|
#define M_NEXT_EXIST_LH(_STRUCT, _MEMBER, _NoOfExisting)\
|
||||||
{CSN_NEXT_EXIST_LH, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_NEXT_EXIST_LH, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_NEXT_EXIST_OR_NULL(Par1, Par2, Par3)
|
* M_NEXT_EXIST_OR_NULL(Par1, Par2, Par3)
|
||||||
@@ -284,7 +288,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Covers the case {null | 0 | 1 < IE >}
|
* Covers the case {null | 0 | 1 < IE >}
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_NEXT_EXIST_OR_NULL(_STRUCT, _MEMBER, _NoOfExisting)\
|
#define M_NEXT_EXIST_OR_NULL(_STRUCT, _MEMBER, _NoOfExisting)\
|
||||||
{CSN_NEXT_EXIST, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_NEXT_EXIST, _NoOfExisting, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_NEXT_EXIST_OR_NULL_LH(Par1, Par2, Par3)
|
* M_NEXT_EXIST_OR_NULL_LH(Par1, Par2, Par3)
|
||||||
@@ -294,7 +298,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Covers the case {null | L | H < IE >}
|
* Covers the case {null | L | H < IE >}
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_NEXT_EXIST_OR_NULL_LH(_STRUCT, _MEMBER, _NoOfExisting)\
|
#define M_NEXT_EXIST_OR_NULL_LH(_STRUCT, _MEMBER, _NoOfExisting)\
|
||||||
{CSN_NEXT_EXIST_LH, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_NEXT_EXIST_LH, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_UINT(Par1, Par2, Par3)
|
* M_UINT(Par1, Par2, Par3)
|
||||||
@@ -304,7 +308,17 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par3: number of bits used to code the element (between 1 and 32)
|
* Par3: number of bits used to code the element (between 1 and 32)
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_UINT(_STRUCT, _MEMBER, _BITS)\
|
#define M_UINT(_STRUCT, _MEMBER, _BITS)\
|
||||||
{CSN_UINT, _BITS, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* M_UINT_OR_NULL(Par1, Par2, Par3)
|
||||||
|
* Similar to the M_UINT except that not only the request set of bits but also the
|
||||||
|
* end of the message may be encountered when looking for the next element in
|
||||||
|
* the message.
|
||||||
|
* Covers the case {null | 0 | 1 < IE >}
|
||||||
|
*****************************************************************************/
|
||||||
|
#define M_UINT_OR_NULL(_STRUCT, _MEMBER, _BITS)\
|
||||||
|
{CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_UINT(Par1, Par2, Par3)
|
* M_UINT(Par1, Par2, Par3)
|
||||||
@@ -314,7 +328,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* received CSN.1 message
|
* received CSN.1 message
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_UINT_LH(_STRUCT, _MEMBER, _BITS)\
|
#define M_UINT_LH(_STRUCT, _MEMBER, _BITS)\
|
||||||
{CSN_UINT_LH, _BITS, {(void*)1}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_UINT_LH, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_UINT_OFFSET(Par1, Par2, Par3, Par4)
|
* M_UINT_OFFSET(Par1, Par2, Par3, Par4)
|
||||||
@@ -325,7 +339,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: value added to the returned integer (offset)
|
* Par4: value added to the returned integer (offset)
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_UINT_OFFSET(_STRUCT, _MEMBER, _BITS, _OFFSET)\
|
#define M_UINT_OFFSET(_STRUCT, _MEMBER, _BITS, _OFFSET)\
|
||||||
{CSN_UINT_OFFSET, _BITS, {(void*)_OFFSET}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_UINT_OFFSET, _BITS, {(void*)_OFFSET}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_UINT_ARRAY(Par1, Par2, Par3, Par4)
|
* M_UINT_ARRAY(Par1, Par2, Par3, Par4)
|
||||||
@@ -336,7 +350,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: number of elements in the array (fixed integer value)
|
* Par4: number of elements in the array (fixed integer value)
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCount)\
|
#define M_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCount)\
|
||||||
{CSN_UINT_ARRAY, _BITS, {(void*)_ElementCount}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_UINT_ARRAY, _BITS, {(void*)_ElementCount}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_VAR_UINT_ARRAY(Par1, Par2, Par3, Par4)
|
* M_VAR_UINT_ARRAY(Par1, Par2, Par3, Par4)
|
||||||
@@ -348,7 +362,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* structure member holding the length value
|
* structure member holding the length value
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_VAR_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCountField)\
|
#define M_VAR_UINT_ARRAY(_STRUCT, _MEMBER, _BITS, _ElementCountField)\
|
||||||
{CSN_UINT_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)1}}
|
{CSN_UINT_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 1, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_VAR_ARRAY(Par1, Par2, Par3, Par4)
|
* M_VAR_ARRAY(Par1, Par2, Par3, Par4)
|
||||||
@@ -359,7 +373,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: offset that is added to the Par3 to get the actual size of the array
|
* Par4: offset that is added to the Par3 to get the actual size of the array
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_VAR_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
#define M_VAR_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
||||||
{CSN_VARIABLE_ARRAY, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_VARIABLE_ARRAY, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_VAR_TARRAY(Par1, Par2, Par3, Par4)
|
* M_VAR_TARRAY(Par1, Par2, Par3, Par4)
|
||||||
@@ -370,14 +384,14 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: name of the structure member holding the size of the array
|
* Par4: name of the structure member holding the size of the array
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_VAR_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
#define M_VAR_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
||||||
{CSN_VARIABLE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
|
{CSN_VARIABLE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_VAR_TARRAY_OFFSET(Par1, Par2, Par3, Par4)
|
* M_VAR_TARRAY_OFFSET(Par1, Par2, Par3, Par4)
|
||||||
* Same as M_VAR_TARRAY with offset
|
* Same as M_VAR_TARRAY with offset
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_VAR_TARRAY_OFFSET(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
#define M_VAR_TARRAY_OFFSET(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
||||||
{CSN_VARIABLE_TARRAY_OFFSET, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
|
{CSN_VARIABLE_TARRAY_OFFSET, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_REC_ARRAY(Par1, Par2, Par3, Par4)
|
* M_REC_ARRAY(Par1, Par2, Par3, Par4)
|
||||||
@@ -395,7 +409,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: length of each element in bits
|
* Par4: length of each element in bits
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_REC_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _BITS)\
|
#define M_REC_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _BITS)\
|
||||||
{CSN_RECURSIVE_ARRAY, _BITS, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_RECURSIVE_ARRAY, _BITS, {(const void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_VAR_TYPE_ARRAY(Par1, Par2, Par3, Par4)
|
* M_VAR_TYPE_ARRAY(Par1, Par2, Par3, Par4)
|
||||||
@@ -406,7 +420,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: number of elements in the array (fixed integer value)
|
* Par4: number of elements in the array (fixed integer value)
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_TYPE_ARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCount)\
|
#define M_TYPE_ARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCount)\
|
||||||
{CSN_TYPE_ARRAY, _ElementCount, {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
|
{CSN_TYPE_ARRAY, _ElementCount, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_REC_TARRAY(Par1, Par2, Par3, Par4)
|
* M_REC_TARRAY(Par1, Par2, Par3, Par4)
|
||||||
@@ -418,7 +432,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: will hold the number of element in the array after unpacking
|
* Par4: will hold the number of element in the array after unpacking
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
#define M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
||||||
{CSN_RECURSIVE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
|
{CSN_RECURSIVE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_REC_TARRAY1(Par1, Par2, Par3, Par4)
|
* M_REC_TARRAY1(Par1, Par2, Par3, Par4)
|
||||||
@@ -426,7 +440,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* <list> ::= <type> {1 <type>} ** 0 ;
|
* <list> ::= <type> {1 <type>} ** 0 ;
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_REC_TARRAY_1(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
#define M_REC_TARRAY_1(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
||||||
{CSN_RECURSIVE_TARRAY_1, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
|
{CSN_RECURSIVE_TARRAY_1, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_REC_TARRAY2(Par1, Par2, Par3, Par4)
|
* M_REC_TARRAY2(Par1, Par2, Par3, Par4)
|
||||||
@@ -434,7 +448,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* <lists> ::= <type> { 0 <type> } ** 1 ;
|
* <lists> ::= <type> { 0 <type> } ** 1 ;
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_REC_TARRAY_2(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
#define M_REC_TARRAY_2(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
||||||
{CSN_RECURSIVE_TARRAY_2, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)sizeof(_MEMBER_TYPE)}}
|
{CSN_RECURSIVE_TARRAY_2, offsetof(_STRUCT, _ElementCountField), {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, sizeof(_MEMBER_TYPE), (void_fn_t)ElementsOf(((_STRUCT*)0)->_MEMBER)}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_TYPE(Par1, Par2, Par3)
|
* M_TYPE(Par1, Par2, Par3)
|
||||||
@@ -445,7 +459,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par3: type of member
|
* Par3: type of member
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_TYPE(_STRUCT, _MEMBER, _MEMBER_TYPE)\
|
#define M_TYPE(_STRUCT, _MEMBER, _MEMBER_TYPE)\
|
||||||
{CSN_TYPE, 0, {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_UNION(Par1, Par2)
|
* M_UNION(Par1, Par2)
|
||||||
@@ -458,14 +472,14 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par2: number of possible choice in the union
|
* Par2: number of possible choice in the union
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_UNION(_STRUCT, _COUNT)\
|
#define M_UNION(_STRUCT, _COUNT)\
|
||||||
{CSN_UNION, _COUNT, {0}, offsetof(_STRUCT, UnionType), "UnionType", {(StreamSerializeFcn_t)0}}
|
{CSN_UNION, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_UNION_LH(Par1, Par2)
|
* M_UNION_LH(Par1, Par2)
|
||||||
* Same as M_UNION but masked with background value 0x2B
|
* Same as M_UNION but masked with background value 0x2B
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_UNION_LH(_STRUCT, _COUNT)\
|
#define M_UNION_LH(_STRUCT, _COUNT)\
|
||||||
{CSN_UNION_LH, _COUNT, {0}, offsetof(_STRUCT, UnionType), "UnionType", {(StreamSerializeFcn_t)0}}
|
{CSN_UNION_LH, _COUNT, {0}, offsetof(_STRUCT, UnionType), FALSE, "UnionType", 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_CHOICE(Par1, Par2, Par3, Par4)
|
* M_CHOICE(Par1, Par2, Par3, Par4)
|
||||||
@@ -476,11 +490,14 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* is the part of the message. In the CSN_CHOICE case, this rule does not
|
* is the part of the message. In the CSN_CHOICE case, this rule does not
|
||||||
* apply. There is free but predefined mapping of the element of the union and
|
* apply. There is free but predefined mapping of the element of the union and
|
||||||
* the value which addresses this element.
|
* the value which addresses this element.
|
||||||
* The value of the address is called a selector.
|
* The value of the address is called a selector. Up to 256 (UCHAR_MAX) unique
|
||||||
|
* selectors can be handled, longer choice list would cause CSN_ERROR_IN_SCRIPT.
|
||||||
* After unpacking, this value is then converted to the sequential number of the
|
* After unpacking, this value is then converted to the sequential number of the
|
||||||
* element in the union and stored in the UnionType variable.
|
* element in the union and stored in the UnionType variable (Par2).
|
||||||
* Par1: C structure name
|
* Par1: C structure name
|
||||||
* Par2: C structure element name
|
* Par2: C structure field name holding sequential number of the chosen element.
|
||||||
|
* BEWARE! Never use an enumerated type here, because its length is
|
||||||
|
* compiler/machine dependent, while decoder would cast it to guint8.
|
||||||
* Par3: address of an array of type CSN_ChoiceElement_t where all possible
|
* Par3: address of an array of type CSN_ChoiceElement_t where all possible
|
||||||
* values of the selector are provided, together with the selector
|
* values of the selector are provided, together with the selector
|
||||||
* length expressed in bits and the address of the CSN_DESCR type
|
* length expressed in bits and the address of the CSN_DESCR type
|
||||||
@@ -494,7 +511,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par4: number of possible choices in the union
|
* Par4: number of possible choices in the union
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_CHOICE(_STRUCT, _MEMBER, _CHOICE, _ElementCount)\
|
#define M_CHOICE(_STRUCT, _MEMBER, _CHOICE, _ElementCount)\
|
||||||
{CSN_CHOICE, _ElementCount, {(void*)_CHOICE}, offsetof(_STRUCT, _MEMBER), #_CHOICE, {(StreamSerializeFcn_t)0}}
|
{CSN_CHOICE, _ElementCount, {(const void*)_CHOICE}, offsetof(_STRUCT, _MEMBER), FALSE, #_CHOICE, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_FIXED(Par1, Par2, Par3)
|
* M_FIXED(Par1, Par2, Par3)
|
||||||
@@ -506,23 +523,23 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* the message the unpacking procedure is aborted
|
* the message the unpacking procedure is aborted
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_FIXED(_STRUCT, _BITS, _BITVALUE)\
|
#define M_FIXED(_STRUCT, _BITS, _BITVALUE)\
|
||||||
{CSN_FIXED, _BITS, {0}, _BITVALUE, #_BITVALUE, {(StreamSerializeFcn_t)0}}
|
{CSN_FIXED, _BITS, {0}, _BITVALUE, FALSE, #_BITVALUE, 0, NULL}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_SERIALIZE(Par1, Par2, Par3)
|
* M_SERIALIZE(Par1, Par2, Par3)
|
||||||
* Allows using a complete free format of data being encoded or decoded.
|
* Allows using a complete free format of data being encoded or decoded.
|
||||||
* When the M_SERIALIZE is uncounted during encoding or decoding of a message
|
* When the M_SERIALIZE is encounted during encoding or decoding of a message
|
||||||
* the CSNstream program passes the control over to the specified function
|
* the CSNstream program passes the control over to the specified function
|
||||||
* together with all necessary parameters about the current position within
|
* together with all necessary parameters about the current position within
|
||||||
* the message being unpacked or packed. When transferring of "serialized"
|
* the message being unpacked or packed. When transferring of "serialized"
|
||||||
* data to or from the message is finished by the function the CSNstream gets
|
* data to or from the message is finished by the function the CSNstream gets
|
||||||
* back control over the data stream and continues to work with the message.
|
* back control over the data stream and continues to work with the message.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_SERIALIZE(_STRUCT, _MEMBER, _SERIALIZEFCN)\
|
#define M_SERIALIZE(_STRUCT, _MEMBER, _LENGTH_LEN, _SERIALIZEFCN)\
|
||||||
{CSN_SERIALIZE, 1, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {_SERIALIZEFCN}}
|
{CSN_SERIALIZE, _LENGTH_LEN, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, (void_fn_t)_SERIALIZEFCN}
|
||||||
|
|
||||||
#define M_CALLBACK(_STRUCT, _CSNCALLBACKFCN, _PARAM1, _PARAM2)\
|
#define M_CALLBACK(_STRUCT, _CSNCALLBACKFCN, _PARAM1, _PARAM2)\
|
||||||
{CSN_CALLBACK, offsetof(_STRUCT, _PARAM1), {_CSNCALLBACKFCN}, offsetof(_STRUCT, _PARAM2), "CallBack_"#_CSNCALLBACKFCN, {(StreamSerializeFcn_t)0}}
|
{CSN_CALLBACK, offsetof(_STRUCT, _PARAM1), {0}, offsetof(_STRUCT, _PARAM2), FALSE, "CallBack_"#_CSNCALLBACKFCN, 0, (void_fn_t)_CSNCALLBACKFCN}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* M_BITMAP(Par1, Par2, Par3)
|
* M_BITMAP(Par1, Par2, Par3)
|
||||||
@@ -533,38 +550,42 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||||||
* Par3: length of the bitmap expressed in bits
|
* Par3: length of the bitmap expressed in bits
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#define M_BITMAP(_STRUCT, _MEMBER, _BITS)\
|
#define M_BITMAP(_STRUCT, _MEMBER, _BITS)\
|
||||||
{CSN_BITMAP, _BITS, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_BITMAP, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/* variable length, right aligned bitmap i.e. _ElementCountField = 11 => 00000111 11111111 */
|
/* variable length, right aligned bitmap i.e. _ElementCountField = 11 => 00000111 11111111 */
|
||||||
#define M_VAR_BITMAP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
#define M_VAR_BITMAP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
||||||
{CSN_VARIABLE_BITMAP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_VARIABLE_BITMAP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/* variable length, right aligned bitmap filling the rest of message
|
/* variable length, right aligned bitmap filling the rest of message
|
||||||
* - when unpacking the _ElementCountField will be set in runtime
|
* - when unpacking the _ElementCountField will be set in runtime
|
||||||
* - when packing _ElementCountField contains the size of bitmap
|
* - when packing _ElementCountField contains the size of bitmap
|
||||||
*/
|
*/
|
||||||
#define M_VAR_BITMAP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
#define M_VAR_BITMAP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
||||||
{CSN_VARIABLE_BITMAP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_VARIABLE_BITMAP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/* variable length, left aligned bitmap i.e. _ElementCountField = 11 => 11111111 11100000 */
|
/* variable length, left aligned bitmap i.e. _ElementCountField = 11 => 11111111 11100000 */
|
||||||
#define M_LEFT_VAR_BMP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
#define M_LEFT_VAR_BMP(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
||||||
{CSN_LEFT_ALIGNED_VAR_BMP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_LEFT_ALIGNED_VAR_BMP, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
/* variable length, left aligned bitmap filling the rest of message
|
/* variable length, left aligned bitmap filling the rest of message
|
||||||
*- when unpacking the _ElementCountField will be set in runtime
|
*- when unpacking the _ElementCountField will be set in runtime
|
||||||
* - when packing _ElementCountField contains the size of bitmap
|
* - when packing _ElementCountField contains the size of bitmap
|
||||||
*/
|
*/
|
||||||
#define M_LEFT_VAR_BMP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
#define M_LEFT_VAR_BMP_1(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
||||||
{CSN_LEFT_ALIGNED_VAR_BMP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
{CSN_LEFT_ALIGNED_VAR_BMP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
#define M_NULL(_STRUCT, _MEMBER)\
|
/* todo: dissect padding bits looking for unexpected extensions */
|
||||||
{CSN_NULL, 0, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
#define M_PADDING_BITS(_STRUCT)\
|
||||||
|
{CSN_PADDING_BITS, 0, {0}, 0, TRUE, "Padding", 0, NULL}
|
||||||
|
|
||||||
|
#define M_NULL(_STRUCT, _MEMBER, _SKIP_BITS)\
|
||||||
|
{CSN_NULL, _SKIP_BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||||
|
|
||||||
#define M_THIS_EXIST(_STRUCT)\
|
#define M_THIS_EXIST(_STRUCT)\
|
||||||
{CSN_EXIST, 0, {0}, offsetof(_STRUCT, Exist), "Exist", {(StreamSerializeFcn_t)0}}
|
{CSN_EXIST, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", 0, NULL}
|
||||||
|
|
||||||
#define M_THIS_EXIST_LH(_STRUCT)\
|
#define M_THIS_EXIST_LH(_STRUCT)\
|
||||||
{CSN_EXIST_LH, 0, {0}, offsetof(_STRUCT, Exist), "Exist", {(StreamSerializeFcn_t)0}}
|
{CSN_EXIST_LH, 0, {0}, offsetof(_STRUCT, Exist), FALSE, "Exist", 0, NULL}
|
||||||
|
|
||||||
/* return value 0 if ok else discontionue the unpacking */
|
/* return value 0 if ok else discontionue the unpacking */
|
||||||
typedef gint16 (*CsnCallBackFcn_t)(void* pv ,...);
|
typedef gint16 (*CsnCallBackFcn_t)(void* pv ,...);
|
||||||
|
|||||||
133
src/cxx_linuxlist.h
Normal file
133
src/cxx_linuxlist.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/* cxx_linuxlist.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||||
|
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct LListHead {
|
||||||
|
typedef T entry_t;
|
||||||
|
|
||||||
|
/* This must match the declaration of struct llist_head */
|
||||||
|
LListHead<T> *next;
|
||||||
|
LListHead<T> *prev;
|
||||||
|
|
||||||
|
LListHead() : m_back(0) { INIT_LLIST_HEAD(this); }
|
||||||
|
LListHead(T* entry) : m_back(entry) {
|
||||||
|
next = (LListHead<T> *)LLIST_POISON1;
|
||||||
|
prev = (LListHead<T> *)LLIST_POISON2;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *entry() {return m_back;}
|
||||||
|
const T *entry() const {return m_back;}
|
||||||
|
|
||||||
|
llist_head &llist() {
|
||||||
|
return *static_cast<llist_head *>(static_cast<void *>(this));
|
||||||
|
}
|
||||||
|
const llist_head &llist() const {
|
||||||
|
return *static_cast<const llist_head *>(static_cast<const void *>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T *const m_back;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Define a family of casting functions */
|
||||||
|
template <typename T>
|
||||||
|
llist_head &llist(LListHead<T> &l)
|
||||||
|
{
|
||||||
|
return l->llist();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const llist_head &llist(const LListHead<T> &l)
|
||||||
|
{
|
||||||
|
return l->llist();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
llist_head *llptr(LListHead<T> *l)
|
||||||
|
{
|
||||||
|
return &(l->llist());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const llist_head *llptr(const LListHead<T> *l)
|
||||||
|
{
|
||||||
|
return &(l->llist());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Define type-safe wrapper for the existing linux_list.h functions */
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_add(LListHead<T> *new_, LListHead<T> *head)
|
||||||
|
{
|
||||||
|
llist_add(llptr(new_), llptr(head));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_add_tail(LListHead<T> *new_, LListHead<T> *head)
|
||||||
|
{
|
||||||
|
llist_add_tail(llptr(new_), llptr(head));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_del(LListHead<T> *entry)
|
||||||
|
{
|
||||||
|
llist_del(llptr(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_del_init(LListHead<T> *entry)
|
||||||
|
{
|
||||||
|
llist_del_init(llptr(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_move(LListHead<T> *list, LListHead<T> *head)
|
||||||
|
{
|
||||||
|
llist_move(llptr(list), llptr(head));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_move_tail(LListHead<T> *list, LListHead<T> *head)
|
||||||
|
{
|
||||||
|
llist_move_tail(llptr(list), llptr(head));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline int llist_empty(const LListHead<T> *head)
|
||||||
|
{
|
||||||
|
return llist_empty(llptr(head));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_splice(LListHead<T> *list, LListHead<T> *head)
|
||||||
|
{
|
||||||
|
llist_splice(llptr(list), llptr(head));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void llist_splice_init(LListHead<T> *list, LListHead<T> *head)
|
||||||
|
{
|
||||||
|
llist_splice_init(llptr(list), llptr(head));
|
||||||
|
}
|
||||||
799
src/decoding.cpp
Normal file
799
src/decoding.cpp
Normal file
@@ -0,0 +1,799 @@
|
|||||||
|
/* decoding
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#include <decoding.h>
|
||||||
|
#include <rlc.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
#include <egprs_rlc_compression.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/bitcomp.h>
|
||||||
|
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define LENGTH_TO_END 255
|
||||||
|
/*!
|
||||||
|
* \returns num extensions fields (num frames == offset) on success,
|
||||||
|
* -errno otherwise.
|
||||||
|
*/
|
||||||
|
static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
|
||||||
|
unsigned int *offs,
|
||||||
|
bool is_last_block,
|
||||||
|
Decoding::RlcData *chunks, unsigned int chunks_size)
|
||||||
|
{
|
||||||
|
const struct rlc_li_field_egprs *li;
|
||||||
|
uint8_t e;
|
||||||
|
unsigned int num_chunks = 0;
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
while (!e) {
|
||||||
|
if (*offs > data_len) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||||
|
"but no more data\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get new E */
|
||||||
|
li = (struct rlc_li_field_egprs *)&data[*offs];
|
||||||
|
e = li->e;
|
||||||
|
*offs += 1;
|
||||||
|
|
||||||
|
if (!chunks)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (num_chunks == chunks_size) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||||
|
"but no more chunks possible\n");
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
if (li->li == 0 && num_chunks == 0) {
|
||||||
|
/* TS 44.060, table 10.4.14a.1, row 2a */
|
||||||
|
/* TS 44.060, table 10.4.14a.1, row 4 */
|
||||||
|
chunks[num_chunks].length = 0;
|
||||||
|
chunks[num_chunks].is_complete = true;
|
||||||
|
} else if (li->li == 127 && li->e == 1) {
|
||||||
|
/* TS 44.060, table 10.4.14a.1, row 3 & 5 */
|
||||||
|
/* only filling bytes left */
|
||||||
|
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||||
|
"only filling bytes with extension octet: LI=%d, E=%d, count=%d\n",
|
||||||
|
li->li, li->e, num_chunks);
|
||||||
|
break;
|
||||||
|
} else if (li->li > 0) {
|
||||||
|
/* TS 44.060, table 10.4.14a.1, row 1 & 2b */
|
||||||
|
chunks[num_chunks].length = li->li;
|
||||||
|
chunks[num_chunks].is_complete = true;
|
||||||
|
} else {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI contains "
|
||||||
|
"invalid extension octet: LI=%d, E=%d, count=%d\n",
|
||||||
|
li->li, li->e, num_chunks);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||||
|
"extension octet: LI=%d, E=%d, count=%d\n",
|
||||||
|
li->li, li->e, num_chunks);
|
||||||
|
num_chunks += 1;
|
||||||
|
|
||||||
|
if (e == 1) {
|
||||||
|
/* There is space after the last chunk, add a final one */
|
||||||
|
if (num_chunks == chunks_size) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||||
|
"UL DATA LI possibly extended, "
|
||||||
|
"but no more chunks possible\n");
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks[num_chunks].length = LENGTH_TO_END;
|
||||||
|
chunks[num_chunks].is_complete = is_last_block;
|
||||||
|
num_chunks += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
|
||||||
|
unsigned int *offs,
|
||||||
|
bool is_last_block,
|
||||||
|
Decoding::RlcData *chunks, unsigned int chunks_size)
|
||||||
|
{
|
||||||
|
const struct rlc_li_field *li;
|
||||||
|
uint8_t m, e;
|
||||||
|
unsigned int num_chunks = 0;
|
||||||
|
|
||||||
|
e = 0;
|
||||||
|
while (!e) {
|
||||||
|
if (*offs > data_len) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||||
|
"but no more data\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get new E */
|
||||||
|
li = (const struct rlc_li_field *)&data[*offs];
|
||||||
|
e = li->e;
|
||||||
|
m = li->m;
|
||||||
|
*offs += 1;
|
||||||
|
|
||||||
|
if (li->li == 0) {
|
||||||
|
/* TS 44.060, 10.4.14, par 6 */
|
||||||
|
e = 1;
|
||||||
|
m = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TS 44.060, table 10.4.13.1 */
|
||||||
|
if (m == 0 && e == 0) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA "
|
||||||
|
"ignored, because M='0' and E='0'.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chunks)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (num_chunks == chunks_size) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||||
|
"but no more chunks possible\n");
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (li->li == 0)
|
||||||
|
/* e is 1 here */
|
||||||
|
chunks[num_chunks].length = LENGTH_TO_END;
|
||||||
|
else
|
||||||
|
chunks[num_chunks].length = li->li;
|
||||||
|
|
||||||
|
chunks[num_chunks].is_complete = li->li || is_last_block;
|
||||||
|
|
||||||
|
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||||
|
"extension octet: LI=%d, M=%d, E=%d, count=%d\n",
|
||||||
|
li->li, li->m, li->e, num_chunks);
|
||||||
|
num_chunks += 1;
|
||||||
|
|
||||||
|
if (e == 1 && m == 1) {
|
||||||
|
if (num_chunks == chunks_size) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||||
|
"but no more chunks possible\n");
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
/* TS 44.060, 10.4.13.1, row 4 */
|
||||||
|
chunks[num_chunks].length = LENGTH_TO_END;
|
||||||
|
chunks[num_chunks].is_complete = is_last_block;
|
||||||
|
num_chunks += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::rlc_data_from_ul_data(
|
||||||
|
const struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
|
||||||
|
const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
|
||||||
|
uint32_t *tlli)
|
||||||
|
{
|
||||||
|
uint8_t e;
|
||||||
|
unsigned int data_len = rdbi->data_len;
|
||||||
|
int num_chunks = 0, i;
|
||||||
|
unsigned int offs = 0;
|
||||||
|
bool is_last_block = (rdbi->cv == 0);
|
||||||
|
|
||||||
|
if (!chunks)
|
||||||
|
chunks_size = 0;
|
||||||
|
|
||||||
|
e = rdbi->e;
|
||||||
|
if (e) {
|
||||||
|
if (chunks_size > 0) {
|
||||||
|
/* Block without LI means it only contains data of one LLC PDU */
|
||||||
|
chunks[num_chunks].offset = offs;
|
||||||
|
chunks[num_chunks].length = LENGTH_TO_END;
|
||||||
|
chunks[num_chunks].is_complete = is_last_block;
|
||||||
|
num_chunks += 1;
|
||||||
|
} else if (chunks) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "No extension, "
|
||||||
|
"but no more chunks possible\n");
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
} else if (mcs_is_edge(cs)) {
|
||||||
|
/* if E is not set (LI follows), EGPRS */
|
||||||
|
num_chunks = parse_extensions_egprs(data, data_len, &offs,
|
||||||
|
is_last_block,
|
||||||
|
chunks, chunks_size);
|
||||||
|
} else {
|
||||||
|
/* if E is not set (LI follows), GPRS */
|
||||||
|
num_chunks = parse_extensions_gprs(data, data_len, &offs,
|
||||||
|
is_last_block,
|
||||||
|
chunks, chunks_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_chunks < 0)
|
||||||
|
return num_chunks;
|
||||||
|
|
||||||
|
/* TLLI */
|
||||||
|
if (rdbi->ti) {
|
||||||
|
uint32_t tlli_enc;
|
||||||
|
if (offs + 4 > data_len) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of block "
|
||||||
|
"border\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
|
||||||
|
if (mcs_is_gprs(cs))
|
||||||
|
/* The TLLI is encoded in big endian for GPRS (see
|
||||||
|
* TS 44.060, figure 10.2.2.1, note) */
|
||||||
|
*tlli = be32toh(tlli_enc);
|
||||||
|
else
|
||||||
|
/* The TLLI is encoded in little endian for EGPRS (see
|
||||||
|
* TS 44.060, figure 10.3a.2.1, note 2) */
|
||||||
|
*tlli = le32toh(tlli_enc);
|
||||||
|
|
||||||
|
offs += sizeof(tlli_enc);
|
||||||
|
} else {
|
||||||
|
*tlli = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PFI */
|
||||||
|
if (rdbi->pi) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
|
||||||
|
"please disable in SYSTEM INFORMATION\n");
|
||||||
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
/* TODO: Skip all extensions with E=0 (see TS 44.060, 10.4.11 */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunks_size == 0)
|
||||||
|
return num_chunks;
|
||||||
|
|
||||||
|
/* LLC */
|
||||||
|
for (i = 0; i < num_chunks; i++) {
|
||||||
|
chunks[i].offset = offs;
|
||||||
|
if (chunks[i].length == LENGTH_TO_END) {
|
||||||
|
if (offs == data_len) {
|
||||||
|
/* There is no place for an additional chunk,
|
||||||
|
* so drop it (this may happen with EGPRS since
|
||||||
|
* there is no M flag. */
|
||||||
|
num_chunks -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chunks[i].length = data_len - offs;
|
||||||
|
}
|
||||||
|
offs += chunks[i].length;
|
||||||
|
if (offs > data_len) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block "
|
||||||
|
"border, chunk idx: %d, offset: %u, size: %d, data_len: %u\n",
|
||||||
|
i, offs, chunks[i].length, data_len);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
|
||||||
|
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
|
||||||
|
continue;
|
||||||
|
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class)
|
||||||
|
continue;
|
||||||
|
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
|
||||||
|
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
|
||||||
|
continue;
|
||||||
|
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_EGPRS_multislot_class)
|
||||||
|
continue;
|
||||||
|
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.EGPRS_multislot_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show_rbb needs to be an array with 65 elements
|
||||||
|
* The index of the array is the bit position in the rbb
|
||||||
|
* (show_rbb[63] relates to BSN ssn-1)
|
||||||
|
*/
|
||||||
|
void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
uint8_t bit;
|
||||||
|
|
||||||
|
bit = !!(rbb[i/8] & (1<<(7-i%8)));
|
||||||
|
show_rbb[i] = bit ? 'R' : 'I';
|
||||||
|
}
|
||||||
|
|
||||||
|
show_rbb[64] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < rbb->cur_bit; i++) {
|
||||||
|
uint8_t bit;
|
||||||
|
bit = bitvec_get_bit_pos(rbb, i);
|
||||||
|
show_rbb[i] = bit == 1 ? 'R' : 'I';
|
||||||
|
}
|
||||||
|
|
||||||
|
show_rbb[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data, enum CodingScheme cs)
|
||||||
|
{
|
||||||
|
unsigned int cur_bit = 0;
|
||||||
|
switch(mcs_header_type(cs)) {
|
||||||
|
case HEADER_GPRS_DATA :
|
||||||
|
cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
|
||||||
|
break;
|
||||||
|
case HEADER_EGPRS_DATA_TYPE_3 :
|
||||||
|
cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs);
|
||||||
|
break;
|
||||||
|
case HEADER_EGPRS_DATA_TYPE_2 :
|
||||||
|
cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs);
|
||||||
|
break;
|
||||||
|
case HEADER_EGPRS_DATA_TYPE_1 :
|
||||||
|
cur_bit = rlc_parse_ul_data_header_egprs_type_1(rlc, data, cs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGP(DRLCMACDL, LOGL_ERROR,
|
||||||
|
"Decoding of uplink %s data blocks not yet supported.\n",
|
||||||
|
mcs_name(cs));
|
||||||
|
return -ENOTSUP;
|
||||||
|
};
|
||||||
|
|
||||||
|
return cur_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::rlc_parse_ul_data_header_egprs_type_3(
|
||||||
|
struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data,
|
||||||
|
const enum CodingScheme &cs)
|
||||||
|
{
|
||||||
|
int punct, punct2, with_padding, cps;
|
||||||
|
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||||
|
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
|
||||||
|
|
||||||
|
egprs3 = static_cast < struct gprs_rlc_ul_header_egprs_3 * >
|
||||||
|
((void *)data);
|
||||||
|
|
||||||
|
cps = (egprs3->cps_hi << 0) | (egprs3->cps_lo << 2);
|
||||||
|
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||||
|
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||||
|
|
||||||
|
rlc->r = egprs3->r;
|
||||||
|
rlc->si = egprs3->si;
|
||||||
|
rlc->tfi = (egprs3->tfi_hi << 0) | (egprs3->tfi_lo << 2);
|
||||||
|
rlc->cps = cps;
|
||||||
|
rlc->rsb = egprs3->rsb;
|
||||||
|
|
||||||
|
rlc->num_data_blocks = 1;
|
||||||
|
rlc->block_info[0].cv = egprs3->cv;
|
||||||
|
rlc->block_info[0].pi = egprs3->pi;
|
||||||
|
rlc->block_info[0].spb = egprs3->spb;
|
||||||
|
rlc->block_info[0].bsn =
|
||||||
|
(egprs3->bsn1_hi << 0) | (egprs3->bsn1_lo << 5);
|
||||||
|
|
||||||
|
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||||
|
offs = rlc->data_offs_bits[0] / 8;
|
||||||
|
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
|
||||||
|
e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
|
||||||
|
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||||
|
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||||
|
cur_bit += 2;
|
||||||
|
/* skip data area */
|
||||||
|
cur_bit += mcs_max_data_block_bytes(cs) * 8;
|
||||||
|
|
||||||
|
return cur_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::rlc_parse_ul_data_header_egprs_type_2(
|
||||||
|
struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data,
|
||||||
|
const enum CodingScheme &cs)
|
||||||
|
{
|
||||||
|
const struct gprs_rlc_ul_header_egprs_2 *egprs2;
|
||||||
|
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||||
|
int punct, punct2, with_padding, cps;
|
||||||
|
|
||||||
|
egprs2 = static_cast < struct gprs_rlc_ul_header_egprs_2 * >
|
||||||
|
((void *)data);
|
||||||
|
|
||||||
|
cps = (egprs2->cps_hi << 0) | (egprs2->cps_lo << 2);
|
||||||
|
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||||
|
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||||
|
|
||||||
|
rlc->r = egprs2->r;
|
||||||
|
rlc->si = egprs2->si;
|
||||||
|
rlc->tfi = (egprs2->tfi_hi << 0) | (egprs2->tfi_lo << 2);
|
||||||
|
rlc->cps = cps;
|
||||||
|
rlc->rsb = egprs2->rsb;
|
||||||
|
|
||||||
|
rlc->num_data_blocks = 1;
|
||||||
|
rlc->block_info[0].cv = egprs2->cv;
|
||||||
|
rlc->block_info[0].pi = egprs2->pi;
|
||||||
|
rlc->block_info[0].bsn =
|
||||||
|
(egprs2->bsn1_hi << 0) | (egprs2->bsn1_lo << 5);
|
||||||
|
|
||||||
|
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||||
|
|
||||||
|
offs = rlc->data_offs_bits[0] / 8;
|
||||||
|
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 7);
|
||||||
|
|
||||||
|
e_ti_header = (data[offs] & 0x60) >> 5;
|
||||||
|
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||||
|
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||||
|
cur_bit += 2;
|
||||||
|
|
||||||
|
/* skip data area */
|
||||||
|
cur_bit += mcs_max_data_block_bytes(cs) * 8;
|
||||||
|
|
||||||
|
return cur_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::rlc_parse_ul_data_header_egprs_type_1(
|
||||||
|
struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data, const enum CodingScheme &cs)
|
||||||
|
{
|
||||||
|
struct gprs_rlc_ul_header_egprs_1 *egprs1;
|
||||||
|
unsigned int e_ti_header, cur_bit = 0, offs;
|
||||||
|
int punct, punct2, with_padding;
|
||||||
|
|
||||||
|
egprs1 = static_cast < struct gprs_rlc_ul_header_egprs_1 * >
|
||||||
|
((void *)data);
|
||||||
|
gprs_rlc_mcs_cps_decode(egprs1->cps, cs, &punct, &punct2,
|
||||||
|
&with_padding);
|
||||||
|
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||||
|
|
||||||
|
rlc->r = egprs1->r;
|
||||||
|
rlc->si = egprs1->si;
|
||||||
|
rlc->tfi = (egprs1->tfi_hi << 0) | (egprs1->tfi_lo << 2);
|
||||||
|
rlc->cps = egprs1->cps;
|
||||||
|
rlc->rsb = egprs1->rsb;
|
||||||
|
rlc->num_data_blocks = 2;
|
||||||
|
rlc->block_info[0].cv = egprs1->cv;
|
||||||
|
rlc->block_info[0].pi = egprs1->pi;
|
||||||
|
rlc->block_info[0].bsn =
|
||||||
|
(egprs1->bsn1_hi << 0) | (egprs1->bsn1_lo << 5);
|
||||||
|
|
||||||
|
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||||
|
offs = rlc->data_offs_bits[0] / 8;
|
||||||
|
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 0);
|
||||||
|
|
||||||
|
e_ti_header = data[offs - 1] >> 6;
|
||||||
|
rlc->block_info[0].e = (e_ti_header & 0x01);
|
||||||
|
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||||
|
cur_bit += 2;
|
||||||
|
|
||||||
|
rlc->block_info[1].cv = egprs1->cv;
|
||||||
|
rlc->block_info[1].pi = egprs1->pi;
|
||||||
|
rlc->block_info[1].bsn = rlc->block_info[0].bsn +
|
||||||
|
((egprs1->bsn2_hi << 0) | (egprs1->bsn2_lo << 2));
|
||||||
|
rlc->block_info[1].bsn = rlc->block_info[1].bsn & (RLC_EGPRS_SNS - 1);
|
||||||
|
|
||||||
|
if ((rlc->block_info[1].bsn != rlc->block_info[0].bsn) &&
|
||||||
|
(rlc->block_info[0].cv == 0))
|
||||||
|
rlc->block_info[0].cv = 1;
|
||||||
|
|
||||||
|
cur_bit = rlc->data_offs_bits[1] - 2;
|
||||||
|
|
||||||
|
offs = rlc->data_offs_bits[1] / 8;
|
||||||
|
OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 2);
|
||||||
|
|
||||||
|
e_ti_header = (data[offs] & (0x03));
|
||||||
|
rlc->block_info[1].e = (e_ti_header & 0x01);
|
||||||
|
rlc->block_info[1].ti = !!(e_ti_header & 0x02);
|
||||||
|
cur_bit += 2;
|
||||||
|
/* skip data area */
|
||||||
|
cur_bit += mcs_max_data_block_bytes(cs) * 8;
|
||||||
|
|
||||||
|
return cur_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data, const enum CodingScheme &cs)
|
||||||
|
{
|
||||||
|
const struct rlc_ul_header *gprs;
|
||||||
|
unsigned int cur_bit = 0;
|
||||||
|
|
||||||
|
gprs = static_cast < struct rlc_ul_header * >
|
||||||
|
((void *)data);
|
||||||
|
|
||||||
|
gprs_rlc_data_info_init_ul(rlc, cs, false);
|
||||||
|
|
||||||
|
rlc->r = gprs->r;
|
||||||
|
rlc->si = gprs->si;
|
||||||
|
rlc->tfi = gprs->tfi;
|
||||||
|
rlc->cps = 0;
|
||||||
|
rlc->rsb = 0;
|
||||||
|
rlc->num_data_blocks = 1;
|
||||||
|
rlc->block_info[0].cv = gprs->cv;
|
||||||
|
rlc->block_info[0].pi = gprs->pi;
|
||||||
|
rlc->block_info[0].bsn = gprs->bsn;
|
||||||
|
rlc->block_info[0].e = gprs->e;
|
||||||
|
rlc->block_info[0].ti = gprs->ti;
|
||||||
|
rlc->block_info[0].spb = 0;
|
||||||
|
cur_bit += rlc->data_offs_bits[0];
|
||||||
|
/* skip data area */
|
||||||
|
cur_bit += mcs_max_data_block_bytes(cs) * 8;
|
||||||
|
|
||||||
|
return cur_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Copy LSB bitstream RLC data block to byte aligned buffer.
|
||||||
|
*
|
||||||
|
* Note that the bitstream is encoded in LSB first order, so the two octets
|
||||||
|
* 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3
|
||||||
|
* (LSB has bit position 1). This is a different order than the one used by
|
||||||
|
* CSN.1.
|
||||||
|
*
|
||||||
|
* \param data_block_idx The block index, 0..1 for header type 1, 0 otherwise
|
||||||
|
* \param src A pointer to the start of the RLC block (incl. the header)
|
||||||
|
* \param buffer A data area of a least the size of the RLC block
|
||||||
|
* \returns the number of bytes copied
|
||||||
|
*/
|
||||||
|
unsigned int Decoding::rlc_copy_to_aligned_buffer(
|
||||||
|
const struct gprs_rlc_data_info *rlc,
|
||||||
|
unsigned int data_block_idx,
|
||||||
|
const uint8_t *src, uint8_t *buffer)
|
||||||
|
{
|
||||||
|
unsigned int hdr_bytes;
|
||||||
|
unsigned int extra_bits;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
uint8_t c, last_c;
|
||||||
|
uint8_t *dst;
|
||||||
|
const struct gprs_rlc_data_block_info *rdbi;
|
||||||
|
|
||||||
|
OSMO_ASSERT(data_block_idx < rlc->num_data_blocks);
|
||||||
|
rdbi = &rlc->block_info[data_block_idx];
|
||||||
|
|
||||||
|
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
|
||||||
|
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
|
||||||
|
|
||||||
|
if (extra_bits == 0) {
|
||||||
|
/* It is aligned already */
|
||||||
|
memmove(buffer, src + hdr_bytes, rdbi->data_len);
|
||||||
|
return rdbi->data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst = buffer;
|
||||||
|
src = src + hdr_bytes;
|
||||||
|
last_c = *(src++);
|
||||||
|
|
||||||
|
for (i = 0; i < rdbi->data_len; i++) {
|
||||||
|
c = src[i];
|
||||||
|
*(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits));
|
||||||
|
last_c = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rdbi->data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get a pointer to byte aligned RLC data.
|
||||||
|
*
|
||||||
|
* Since the RLC data may not be byte aligned to the RLC block data such that a
|
||||||
|
* single RLC data byte is spread over two RLC block bytes, this function
|
||||||
|
* eventually uses the provided buffer as data storage.
|
||||||
|
*
|
||||||
|
* \param src A pointer to the start of the RLC block (incl. the header)
|
||||||
|
* \param buffer A data area of a least the size of the RLC block
|
||||||
|
* \returns A pointer to the RLC data start within src if it is aligned, and
|
||||||
|
* buffer otherwise.
|
||||||
|
*/
|
||||||
|
const uint8_t *Decoding::rlc_get_data_aligned(
|
||||||
|
const struct gprs_rlc_data_info *rlc,
|
||||||
|
unsigned int data_block_idx,
|
||||||
|
const uint8_t *src, uint8_t *buffer)
|
||||||
|
{
|
||||||
|
unsigned int hdr_bytes;
|
||||||
|
unsigned int extra_bits;
|
||||||
|
|
||||||
|
OSMO_ASSERT(data_block_idx < ARRAY_SIZE(rlc->data_offs_bits));
|
||||||
|
|
||||||
|
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
|
||||||
|
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
|
||||||
|
|
||||||
|
if (extra_bits == 0)
|
||||||
|
/* It is aligned already, return a pointer that refers to the
|
||||||
|
* original data. */
|
||||||
|
return src + hdr_bytes;
|
||||||
|
|
||||||
|
Decoding::rlc_copy_to_aligned_buffer(rlc, data_block_idx, src, buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_final_ack(bitvec *bits, int *bsn_begin, int *bsn_end,
|
||||||
|
gprs_rlc_dl_window *window)
|
||||||
|
{
|
||||||
|
int num_blocks, i;
|
||||||
|
|
||||||
|
num_blocks = window->mod_sns(window->v_s() - window->v_a());
|
||||||
|
for (i = 0; i < num_blocks; i++)
|
||||||
|
bitvec_set_bit(bits, ONE);
|
||||||
|
|
||||||
|
*bsn_begin = window->v_a();
|
||||||
|
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
|
||||||
|
return num_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
|
||||||
|
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
|
||||||
|
{
|
||||||
|
int urbb_len = desc->URBB_LENGTH;
|
||||||
|
int crbb_len = 0;
|
||||||
|
int num_blocks = 0;
|
||||||
|
struct bitvec urbb;
|
||||||
|
int i;
|
||||||
|
bool have_bitmap;
|
||||||
|
int implicitly_acked_blocks;
|
||||||
|
int ssn = desc->STARTING_SEQUENCE_NUMBER;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (desc->FINAL_ACK_INDICATION)
|
||||||
|
return handle_final_ack(bits, bsn_begin, bsn_end, window);
|
||||||
|
|
||||||
|
if (desc->Exist_CRBB)
|
||||||
|
crbb_len = desc->CRBB_LENGTH;
|
||||||
|
|
||||||
|
have_bitmap = (urbb_len + crbb_len) > 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bow & bitmap present:
|
||||||
|
* V(A)-> [ 11111...11111 0 SSN-> BBBBB...BBBBB ] (SSN+Nbits) .... V(S)
|
||||||
|
* bow & not bitmap present:
|
||||||
|
* V(A)-> [ 11111...11111 ] . SSN .... V(S)
|
||||||
|
* not bow & bitmap present:
|
||||||
|
* V(A)-> ... [ 0 SSN-> BBBBB...BBBBB ](SSN+N) .... V(S)
|
||||||
|
* not bow & not bitmap present:
|
||||||
|
* V(A)-> ... [] . SSN .... V(S)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (desc->BEGINNING_OF_WINDOW) {
|
||||||
|
implicitly_acked_blocks = window->mod_sns(ssn - 1 - window->v_a());
|
||||||
|
|
||||||
|
for (i = 0; i < implicitly_acked_blocks; i++)
|
||||||
|
bitvec_set_bit(bits, ONE);
|
||||||
|
|
||||||
|
num_blocks += implicitly_acked_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_bitmap)
|
||||||
|
goto aborted;
|
||||||
|
|
||||||
|
/* next bit refers to V(Q) and thus is always zero (and not
|
||||||
|
* transmitted) */
|
||||||
|
bitvec_set_bit(bits, ZERO);
|
||||||
|
num_blocks += 1;
|
||||||
|
|
||||||
|
if (crbb_len > 0) {
|
||||||
|
int old_len = bits->cur_bit;
|
||||||
|
|
||||||
|
LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exists, "
|
||||||
|
"CRBB LEN = %d and Starting color code = %d",
|
||||||
|
desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE);
|
||||||
|
rc = egprs_compress::decompress_crbb(desc->CRBB_LENGTH,
|
||||||
|
desc->CRBB_STARTING_COLOR_CODE, desc->CRBB, bits);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||||
|
"Failed to decode CRBB: length %d, data '%s'\n",
|
||||||
|
desc->CRBB_LENGTH, osmo_hexdump(
|
||||||
|
desc->CRBB, (desc->CRBB_LENGTH + 7)/8));
|
||||||
|
/* We don't know the SSN offset for the URBB,
|
||||||
|
* return what we have so far and assume the
|
||||||
|
* bitmap has stopped here */
|
||||||
|
goto aborted;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||||
|
"CRBB len: %d, decoded len: %d, cc: %d, crbb: '%s'\n",
|
||||||
|
desc->CRBB_LENGTH, bits->cur_bit - old_len,
|
||||||
|
desc->CRBB_STARTING_COLOR_CODE,
|
||||||
|
osmo_hexdump(
|
||||||
|
desc->CRBB, (desc->CRBB_LENGTH + 7)/8)
|
||||||
|
);
|
||||||
|
|
||||||
|
num_blocks += (bits->cur_bit - old_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
urbb.cur_bit = 0;
|
||||||
|
urbb.data = (uint8_t *)desc->URBB;
|
||||||
|
urbb.data_len = sizeof(desc->URBB);
|
||||||
|
|
||||||
|
for (i = urbb_len; i > 0; i--) {
|
||||||
|
/*
|
||||||
|
* Set bit at the appropriate position (see 3GPP TS
|
||||||
|
* 44.060 12.3.1)
|
||||||
|
*/
|
||||||
|
int is_ack = bitvec_get_bit_pos(&urbb, i-1);
|
||||||
|
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
|
||||||
|
}
|
||||||
|
num_blocks += urbb_len;
|
||||||
|
|
||||||
|
aborted:
|
||||||
|
*bsn_begin = window->v_a();
|
||||||
|
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
|
||||||
|
|
||||||
|
return num_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Decoding::decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc,
|
||||||
|
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
|
||||||
|
{
|
||||||
|
int urbb_len = RLC_GPRS_WS;
|
||||||
|
int num_blocks;
|
||||||
|
struct bitvec urbb;
|
||||||
|
|
||||||
|
if (desc->FINAL_ACK_INDICATION)
|
||||||
|
return handle_final_ack(bits, bsn_begin, bsn_end, window);
|
||||||
|
|
||||||
|
*bsn_begin = window->v_a();
|
||||||
|
*bsn_end = desc->STARTING_SEQUENCE_NUMBER;
|
||||||
|
|
||||||
|
num_blocks = window->mod_sns(*bsn_end - *bsn_begin);
|
||||||
|
|
||||||
|
if (num_blocks < 0 || num_blocks > urbb_len) {
|
||||||
|
*bsn_end = *bsn_begin;
|
||||||
|
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||||
|
"Invalid GPRS Ack/Nack window %d:%d (length %d)\n",
|
||||||
|
*bsn_begin, *bsn_end, num_blocks);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
urbb.cur_bit = 0;
|
||||||
|
urbb.data = (uint8_t *)desc->RECEIVED_BLOCK_BITMAP;
|
||||||
|
urbb.data_len = sizeof(desc->RECEIVED_BLOCK_BITMAP);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TS 44.060, 12.3:
|
||||||
|
* BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64.
|
||||||
|
* The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128.
|
||||||
|
*
|
||||||
|
* We are only interested in the range from V(A) to SSN-1 which is
|
||||||
|
* num_blocks large. The RBB is laid out as
|
||||||
|
* [SSN-1] [SSN-2] ... [V(A)] ... [SSN-64]
|
||||||
|
* so we want to start with [V(A)] and go backwards until we reach
|
||||||
|
* [SSN-1] to get the needed BSNs in an increasing order. Note that
|
||||||
|
* the bit numbers are counted from the end of the buffer.
|
||||||
|
*/
|
||||||
|
for (int i = num_blocks; i > 0; i--) {
|
||||||
|
int is_ack = bitvec_get_bit_pos(&urbb, urbb_len - i);
|
||||||
|
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_blocks;
|
||||||
|
}
|
||||||
102
src/decoding.h
Normal file
102
src/decoding.h
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/* decoding
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gsm_rlcmac.h"
|
||||||
|
#include "coding_scheme.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct bitvec;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
class Decoding {
|
||||||
|
public:
|
||||||
|
/* represents (parts) LLC PDUs within one RLC Data block */
|
||||||
|
struct RlcData {
|
||||||
|
uint8_t offset;
|
||||||
|
uint8_t length;
|
||||||
|
bool is_complete; /* if this PDU ends in this block */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rlc_data_from_ul_data(
|
||||||
|
const struct gprs_rlc_data_block_info *rdbi,
|
||||||
|
enum CodingScheme cs, const uint8_t *data, RlcData *chunks,
|
||||||
|
unsigned int chunks_size, uint32_t *tlli);
|
||||||
|
|
||||||
|
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
|
||||||
|
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
|
||||||
|
static int rlc_parse_ul_data_header_egprs_type_3(
|
||||||
|
struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data,
|
||||||
|
const enum CodingScheme &cs);
|
||||||
|
static int rlc_parse_ul_data_header_egprs_type_2(
|
||||||
|
struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data,
|
||||||
|
const enum CodingScheme &cs);
|
||||||
|
static int rlc_parse_ul_data_header_egprs_type_1(
|
||||||
|
struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data,
|
||||||
|
const enum CodingScheme &cs);
|
||||||
|
static int rlc_parse_ul_data_header_gprs(
|
||||||
|
struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data,
|
||||||
|
const enum CodingScheme &cs);
|
||||||
|
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||||
|
const uint8_t *data, enum CodingScheme cs);
|
||||||
|
static unsigned int rlc_copy_to_aligned_buffer(
|
||||||
|
const struct gprs_rlc_data_info *rlc,
|
||||||
|
unsigned int data_block_idx,
|
||||||
|
const uint8_t *src, uint8_t *buffer);
|
||||||
|
static const uint8_t *rlc_get_data_aligned(
|
||||||
|
const struct gprs_rlc_data_info *rlc,
|
||||||
|
unsigned int data_block_idx,
|
||||||
|
const uint8_t *src, uint8_t *buffer);
|
||||||
|
static int decode_egprs_acknack_bits(
|
||||||
|
const EGPRS_AckNack_Desc_t *desc,
|
||||||
|
struct bitvec *bits, int *bsn_begin, int *bsn_end,
|
||||||
|
struct gprs_rlc_dl_window *window);
|
||||||
|
static int decode_gprs_acknack_bits(
|
||||||
|
const Ack_Nack_Description_t *desc,
|
||||||
|
bitvec *bits, int *bsn_begin, int *bsn_end,
|
||||||
|
gprs_rlc_dl_window *window);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* #ifdef __cplusplus */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
|
||||||
|
uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
696
src/egprs_rlc_compression.cpp
Normal file
696
src/egprs_rlc_compression.cpp
Normal file
@@ -0,0 +1,696 @@
|
|||||||
|
/* egprs_rlc_compression.h
|
||||||
|
* Routines for EGPRS RLC bitmap compression handling
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
#include <decoding.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
#include <gprs_rlcmac.h>
|
||||||
|
#include <egprs_rlc_compression.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/talloc.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
#include <osmocom/core/stats.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EGPRS_CODEWORDS 79 /* total number of codewords */
|
||||||
|
|
||||||
|
struct egprs_compress_node{
|
||||||
|
struct egprs_compress_node *left;
|
||||||
|
struct egprs_compress_node *right;
|
||||||
|
int run_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void *tall_pcu_ctx;
|
||||||
|
|
||||||
|
egprs_compress *egprs_compress::s_instance = 0;
|
||||||
|
|
||||||
|
egprs_compress_node *egprs_compress::create_tree_node(void *parent)
|
||||||
|
{
|
||||||
|
egprs_compress_node *new_node;
|
||||||
|
|
||||||
|
new_node = talloc_zero(parent, egprs_compress_node);
|
||||||
|
new_node->left = NULL;
|
||||||
|
new_node->right = NULL;
|
||||||
|
new_node->run_length = -1;
|
||||||
|
return new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
egprs_compress *egprs_compress::instance()
|
||||||
|
{
|
||||||
|
if (!egprs_compress::s_instance)
|
||||||
|
egprs_compress::s_instance = new egprs_compress;
|
||||||
|
return egprs_compress::s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expands the given tree by incorporating
|
||||||
|
* the given codewords.
|
||||||
|
* \param root[in] Root of ones or zeros tree
|
||||||
|
* \param cdwd[in] Array of code words
|
||||||
|
* number of codewords is EGPRS_CODEWORDS
|
||||||
|
*/
|
||||||
|
void egprs_compress::build_codewords(egprs_compress_node *root, const char *cdwd[])
|
||||||
|
{
|
||||||
|
egprs_compress_node *iter;
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
for (idx = 0; idx < EGPRS_CODEWORDS; idx++) {
|
||||||
|
len = strlen((const char *)cdwd[idx]);
|
||||||
|
iter = root;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (cdwd[idx][i] == '0') {
|
||||||
|
if (!iter->left)
|
||||||
|
iter->left = create_tree_node(root);
|
||||||
|
iter = iter->left;
|
||||||
|
} else {
|
||||||
|
if (!iter->right)
|
||||||
|
iter->right = create_tree_node(root);
|
||||||
|
iter = iter->right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (iter) {
|
||||||
|
/* The first 64 run lengths are 0, 1, 2, ..., 63
|
||||||
|
* and the following ones are 64, 128, 192 described in
|
||||||
|
* section 9.1.10 of 3gpp 44.060 */
|
||||||
|
if (idx < 64)
|
||||||
|
iter->run_length = idx;
|
||||||
|
else
|
||||||
|
iter->run_length = (idx - 63) * 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Terminating codes for uninterrupted sequences of 0 and 1 up to 64 bit length
|
||||||
|
* according to TS 44.060 9.1.10
|
||||||
|
*/
|
||||||
|
static const unsigned t4_term[2][64] = {
|
||||||
|
{
|
||||||
|
0b0000110111,
|
||||||
|
0b10,
|
||||||
|
0b11,
|
||||||
|
0b010,
|
||||||
|
0b011,
|
||||||
|
0b0011,
|
||||||
|
0b0010,
|
||||||
|
0b00011,
|
||||||
|
0b000101,
|
||||||
|
0b000100,
|
||||||
|
0b0000100,
|
||||||
|
0b0000101,
|
||||||
|
0b0000111,
|
||||||
|
0b00000100,
|
||||||
|
0b00000111,
|
||||||
|
0b000011000,
|
||||||
|
0b0000010111,
|
||||||
|
0b0000011000,
|
||||||
|
0b0000001000,
|
||||||
|
0b00001100111,
|
||||||
|
0b00001101000,
|
||||||
|
0b00001101100,
|
||||||
|
0b00000110111,
|
||||||
|
0b00000101000,
|
||||||
|
0b00000010111,
|
||||||
|
0b00000011000,
|
||||||
|
0b000011001010,
|
||||||
|
0b000011001011,
|
||||||
|
0b000011001100,
|
||||||
|
0b000011001101,
|
||||||
|
0b000001101000,
|
||||||
|
0b000001101001,
|
||||||
|
0b000001101010,
|
||||||
|
0b000001101011,
|
||||||
|
0b000011010010,
|
||||||
|
0b000011010011,
|
||||||
|
0b000011010100,
|
||||||
|
0b000011010101,
|
||||||
|
0b000011010110,
|
||||||
|
0b000011010111,
|
||||||
|
0b000001101100,
|
||||||
|
0b000001101101,
|
||||||
|
0b000011011010,
|
||||||
|
0b000011011011,
|
||||||
|
0b000001010100,
|
||||||
|
0b000001010101,
|
||||||
|
0b000001010110,
|
||||||
|
0b000001010111,
|
||||||
|
0b000001100100,
|
||||||
|
0b000001100101,
|
||||||
|
0b000001010010,
|
||||||
|
0b000001010011,
|
||||||
|
0b000000100100,
|
||||||
|
0b000000110111,
|
||||||
|
0b000000111000,
|
||||||
|
0b000000100111,
|
||||||
|
0b000000101000,
|
||||||
|
0b000001011000,
|
||||||
|
0b000001011001,
|
||||||
|
0b000000101011,
|
||||||
|
0b000000101100,
|
||||||
|
0b000001011010,
|
||||||
|
0b000001100110,
|
||||||
|
0b000001100111
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0b00110101,
|
||||||
|
0b000111,
|
||||||
|
0b0111,
|
||||||
|
0b1000,
|
||||||
|
0b1011,
|
||||||
|
0b1100,
|
||||||
|
0b1110,
|
||||||
|
0b1111,
|
||||||
|
0b10011,
|
||||||
|
0b10100,
|
||||||
|
0b00111,
|
||||||
|
0b01000,
|
||||||
|
0b001000,
|
||||||
|
0b000011,
|
||||||
|
0b110100,
|
||||||
|
0b110101,
|
||||||
|
0b101010,
|
||||||
|
0b101011,
|
||||||
|
0b0100111,
|
||||||
|
0b0001100,
|
||||||
|
0b0001000,
|
||||||
|
0b0010111,
|
||||||
|
0b0000011,
|
||||||
|
0b0000100,
|
||||||
|
0b0101000,
|
||||||
|
0b0101011,
|
||||||
|
0b0010011,
|
||||||
|
0b0100100,
|
||||||
|
0b0011000,
|
||||||
|
0b00000010,
|
||||||
|
0b00000011,
|
||||||
|
0b00011010,
|
||||||
|
0b00011011,
|
||||||
|
0b00010010,
|
||||||
|
0b00010011,
|
||||||
|
0b00010100,
|
||||||
|
0b00010101,
|
||||||
|
0b00010110,
|
||||||
|
0b00010111,
|
||||||
|
0b00101000,
|
||||||
|
0b00101001,
|
||||||
|
0b00101010,
|
||||||
|
0b00101011,
|
||||||
|
0b00101100,
|
||||||
|
0b00101101,
|
||||||
|
0b00000100,
|
||||||
|
0b00000101,
|
||||||
|
0b00001010,
|
||||||
|
0b00001011,
|
||||||
|
0b01010010,
|
||||||
|
0b01010011,
|
||||||
|
0b01010100,
|
||||||
|
0b01010101,
|
||||||
|
0b00100100,
|
||||||
|
0b00100101,
|
||||||
|
0b01011000,
|
||||||
|
0b01011001,
|
||||||
|
0b01011010,
|
||||||
|
0b01011011,
|
||||||
|
0b01001010,
|
||||||
|
0b01001011,
|
||||||
|
0b00110010,
|
||||||
|
0b00110011,
|
||||||
|
0b00110100
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static const unsigned t4_term_length[2][64] = {
|
||||||
|
{10, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
|
||||||
|
{8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned t4_min_term_length[] = {2, 4};
|
||||||
|
static const unsigned t4_min_make_up_length[] = {10, 5};
|
||||||
|
|
||||||
|
static const unsigned t4_max_term_length[] = {12, 8};
|
||||||
|
static const unsigned t4_max_make_up_length[] = {13, 9};
|
||||||
|
|
||||||
|
static const unsigned t4_make_up_length[2][15] = {
|
||||||
|
{10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13},
|
||||||
|
{5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned t4_make_up_ind[15] = {64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960};
|
||||||
|
|
||||||
|
static const unsigned t4_make_up[2][15] = {
|
||||||
|
{
|
||||||
|
0b0000001111,
|
||||||
|
0b000011001000,
|
||||||
|
0b000011001001,
|
||||||
|
0b000001011011,
|
||||||
|
0b000000110011,
|
||||||
|
0b000000110100,
|
||||||
|
0b000000110101,
|
||||||
|
0b0000001101100,
|
||||||
|
0b0000001101101,
|
||||||
|
0b0000001001010,
|
||||||
|
0b0000001001011,
|
||||||
|
0b0000001001100,
|
||||||
|
0b0000001001101,
|
||||||
|
0b0000001110010,
|
||||||
|
0b0000001110011
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0b11011,
|
||||||
|
0b10010,
|
||||||
|
0b010111,
|
||||||
|
0b0110111,
|
||||||
|
0b00110110,
|
||||||
|
0b00110111,
|
||||||
|
0b01100100,
|
||||||
|
0b01100101,
|
||||||
|
0b01101000,
|
||||||
|
0b01100111,
|
||||||
|
0b011001100,
|
||||||
|
0b011001101,
|
||||||
|
0b011010010,
|
||||||
|
0b011010011,
|
||||||
|
0b011010100
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The code words for one run length and zero
|
||||||
|
* run length are described in table 9.1.10.1
|
||||||
|
* of 3gpp 44.060
|
||||||
|
*/
|
||||||
|
const char *one_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||||
|
"00110101",
|
||||||
|
"000111",
|
||||||
|
"0111",
|
||||||
|
"1000",
|
||||||
|
"1011",
|
||||||
|
"1100",
|
||||||
|
"1110",
|
||||||
|
"1111",
|
||||||
|
"10011",
|
||||||
|
"10100",
|
||||||
|
"00111",
|
||||||
|
"01000",
|
||||||
|
"001000",
|
||||||
|
"000011",
|
||||||
|
"110100",
|
||||||
|
"110101",
|
||||||
|
"101010",
|
||||||
|
"101011",
|
||||||
|
"0100111",
|
||||||
|
"0001100",
|
||||||
|
"0001000",
|
||||||
|
"0010111",
|
||||||
|
"0000011",
|
||||||
|
"0000100",
|
||||||
|
"0101000",
|
||||||
|
"0101011",
|
||||||
|
"0010011",
|
||||||
|
"0100100",
|
||||||
|
"0011000",
|
||||||
|
"00000010",
|
||||||
|
"00000011",
|
||||||
|
"00011010",
|
||||||
|
"00011011",
|
||||||
|
"00010010",
|
||||||
|
"00010011",
|
||||||
|
"00010100",
|
||||||
|
"00010101",
|
||||||
|
"00010110",
|
||||||
|
"00010111",
|
||||||
|
"00101000",
|
||||||
|
"00101001",
|
||||||
|
"00101010",
|
||||||
|
"00101011",
|
||||||
|
"00101100",
|
||||||
|
"00101101",
|
||||||
|
"00000100",
|
||||||
|
"00000101",
|
||||||
|
"00001010",
|
||||||
|
"00001011",
|
||||||
|
"01010010",
|
||||||
|
"01010011",
|
||||||
|
"01010100",
|
||||||
|
"01010101",
|
||||||
|
"00100100",
|
||||||
|
"00100101",
|
||||||
|
"01011000",
|
||||||
|
"01011001",
|
||||||
|
"01011010",
|
||||||
|
"01011011",
|
||||||
|
"01001010",
|
||||||
|
"01001011",
|
||||||
|
"00110010",
|
||||||
|
"00110011",
|
||||||
|
"00110100",
|
||||||
|
"11011",
|
||||||
|
"10010",
|
||||||
|
"010111",
|
||||||
|
"0110111",
|
||||||
|
"00110110",
|
||||||
|
"00110111",
|
||||||
|
"01100100",
|
||||||
|
"01100101",
|
||||||
|
"01101000",
|
||||||
|
"01100111",
|
||||||
|
"011001100",
|
||||||
|
"011001101",
|
||||||
|
"011010010",
|
||||||
|
"011010011",
|
||||||
|
"011010100"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||||
|
"0000110111",
|
||||||
|
"10",
|
||||||
|
"11",
|
||||||
|
"010",
|
||||||
|
"011",
|
||||||
|
"0011",
|
||||||
|
"0010",
|
||||||
|
"00011",
|
||||||
|
"000101",
|
||||||
|
"000100",
|
||||||
|
"0000100",
|
||||||
|
"0000101",
|
||||||
|
"0000111",
|
||||||
|
"00000100",
|
||||||
|
"00000111",
|
||||||
|
"000011000",
|
||||||
|
"0000010111",
|
||||||
|
"0000011000",
|
||||||
|
"0000001000",
|
||||||
|
"00001100111",
|
||||||
|
"00001101000",
|
||||||
|
"00001101100",
|
||||||
|
"00000110111",
|
||||||
|
"00000101000",
|
||||||
|
"00000010111",
|
||||||
|
"00000011000",
|
||||||
|
"000011001010",
|
||||||
|
"000011001011",
|
||||||
|
"000011001100",
|
||||||
|
"000011001101",
|
||||||
|
"000001101000",
|
||||||
|
"000001101001",
|
||||||
|
"000001101010",
|
||||||
|
"000001101011",
|
||||||
|
"000011010010",
|
||||||
|
"000011010011",
|
||||||
|
"000011010100",
|
||||||
|
"000011010101",
|
||||||
|
"000011010110",
|
||||||
|
"000011010111",
|
||||||
|
"000001101100",
|
||||||
|
"000001101101",
|
||||||
|
"000011011010",
|
||||||
|
"000011011011",
|
||||||
|
"000001010100",
|
||||||
|
"000001010101",
|
||||||
|
"000001010110",
|
||||||
|
"000001010111",
|
||||||
|
"000001100100",
|
||||||
|
"000001100101",
|
||||||
|
"000001010010",
|
||||||
|
"000001010011",
|
||||||
|
"000000100100",
|
||||||
|
"000000110111",
|
||||||
|
"000000111000",
|
||||||
|
"000000100111",
|
||||||
|
"000000101000",
|
||||||
|
"000001011000",
|
||||||
|
"000001011001",
|
||||||
|
"000000101011",
|
||||||
|
"000000101100",
|
||||||
|
"000001011010",
|
||||||
|
"000001100110",
|
||||||
|
"000001100111",
|
||||||
|
"0000001111",
|
||||||
|
"000011001000",
|
||||||
|
"000011001001",
|
||||||
|
"000001011011",
|
||||||
|
"000000110011",
|
||||||
|
"000000110100",
|
||||||
|
"000000110101",
|
||||||
|
"0000001101100",
|
||||||
|
"0000001101101",
|
||||||
|
"0000001001010",
|
||||||
|
"0000001001011",
|
||||||
|
"0000001001100",
|
||||||
|
"0000001001101",
|
||||||
|
"0000001110010",
|
||||||
|
"0000001110011"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Calculate runlength of a codeword
|
||||||
|
* \param root[in] Root of Ones or Zeros tree
|
||||||
|
* \param bmbuf[in] Received compressed bitmap buf
|
||||||
|
* \param length[in] Length of bitmap buf in bits
|
||||||
|
* \param bit_pos[in] The start bit pos to read codeword
|
||||||
|
* \param len_codewd[in] Length of code word
|
||||||
|
* \param rlen[out] Calculated run length
|
||||||
|
*/
|
||||||
|
static int search_runlen(
|
||||||
|
egprs_compress_node *root,
|
||||||
|
const uint8_t *bmbuf,
|
||||||
|
uint8_t length,
|
||||||
|
uint8_t bit_pos,
|
||||||
|
uint8_t *len_codewd,
|
||||||
|
uint16_t *rlen)
|
||||||
|
{
|
||||||
|
egprs_compress_node *iter;
|
||||||
|
uint8_t dir;
|
||||||
|
|
||||||
|
iter = root;
|
||||||
|
*len_codewd = 0;
|
||||||
|
|
||||||
|
while (iter->run_length == -1) {
|
||||||
|
if ((!iter->left) && (!iter->right))
|
||||||
|
return -1;
|
||||||
|
if (bit_pos >= length)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* get the bit value at the bitpos and put it in right most of dir */
|
||||||
|
dir = (bmbuf[bit_pos/8] >> (7 - (bit_pos & 0x07))) & 0x01;
|
||||||
|
bit_pos++;
|
||||||
|
(*len_codewd)++;
|
||||||
|
if (!dir && (iter->left != NULL))
|
||||||
|
iter = iter->left;
|
||||||
|
else if (dir && (iter->right != NULL))
|
||||||
|
iter = iter->right;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LOGP(DRLCMACUL, LOGL_DEBUG, "Run_length = %d\n", iter->run_length);
|
||||||
|
*rlen = iter->run_length;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decompress received block bitmap
|
||||||
|
* \param compress_bmap_len[in] Compressed bitmap length
|
||||||
|
* \param start[in] Starting Color Code, true if bitmap starts with a run
|
||||||
|
* length of ones, false if zeros; see 9.1.10, 3GPP 44.060.
|
||||||
|
* \param orig_crbb_buf[in] Received block crbb bitmap
|
||||||
|
* \param dest[out] Uncompressed bitvector
|
||||||
|
*/
|
||||||
|
int egprs_compress::decompress_crbb(
|
||||||
|
int8_t compress_bmap_len,
|
||||||
|
bool start,
|
||||||
|
const uint8_t *orig_crbb_buf,
|
||||||
|
bitvec *dest)
|
||||||
|
{
|
||||||
|
int8_t remaining_bmap_len = compress_bmap_len;
|
||||||
|
uint8_t bit_pos = 0;
|
||||||
|
uint8_t data;
|
||||||
|
egprs_compress_node *list = NULL;
|
||||||
|
uint8_t nbits = 0; /* number of bits of codeword */
|
||||||
|
uint16_t run_length = 0;
|
||||||
|
uint16_t cbmaplen = 0; /* compressed bitmap part after decompression */
|
||||||
|
unsigned wp = dest->cur_bit;
|
||||||
|
int rc = 0;
|
||||||
|
egprs_compress *compress = instance();
|
||||||
|
|
||||||
|
while (remaining_bmap_len > 0) {
|
||||||
|
if (start) {
|
||||||
|
data = 0xff;
|
||||||
|
list = compress->ones_list;
|
||||||
|
} else {
|
||||||
|
data = 0x00;
|
||||||
|
list = compress->zeros_list;
|
||||||
|
}
|
||||||
|
rc = search_runlen(list, orig_crbb_buf, compress_bmap_len,
|
||||||
|
bit_pos, &nbits, &run_length);
|
||||||
|
if (rc == -1)
|
||||||
|
return -1;
|
||||||
|
/* If run length > 64, need makeup and terminating code */
|
||||||
|
if (run_length < 64)
|
||||||
|
start = !start;
|
||||||
|
cbmaplen = cbmaplen + run_length;
|
||||||
|
|
||||||
|
/* put run length of Ones in uncompressed bitmap */
|
||||||
|
while (run_length != 0) {
|
||||||
|
if (run_length > 8) {
|
||||||
|
bitvec_write_field(dest, &wp, data, 8);
|
||||||
|
run_length = run_length - 8;
|
||||||
|
} else {
|
||||||
|
bitvec_write_field(dest, &wp, data, run_length);
|
||||||
|
run_length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bit_pos = bit_pos + nbits;
|
||||||
|
remaining_bmap_len = remaining_bmap_len - nbits;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void egprs_compress::decode_tree_init()
|
||||||
|
{
|
||||||
|
ones_list = create_tree_node(tall_pcu_ctx);
|
||||||
|
zeros_list = create_tree_node(tall_pcu_ctx);
|
||||||
|
build_codewords(ones_list, one_run_len_code_list);
|
||||||
|
build_codewords(zeros_list, zero_run_len_code_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
egprs_compress::egprs_compress()
|
||||||
|
{
|
||||||
|
decode_tree_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compress received block bitmap
|
||||||
|
* \param run_len_cnt[in] Count of number of 1's and 0's
|
||||||
|
* \param codewrd_bitmap[in] Code word for coresponding run length.
|
||||||
|
* \param crbb_vec[out] compressed bitvector.
|
||||||
|
*/
|
||||||
|
static void compress_bitmap(
|
||||||
|
uint16_t *run_len_cnt, /* cnt: run length count */
|
||||||
|
uint16_t *codewrd_bitmap, /* code word */
|
||||||
|
int16_t *codewrd_len, /* number of bits in the code word */
|
||||||
|
bitvec *crbb_vec, /* bitmap buffer to put code word in */
|
||||||
|
bool start)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
unsigned writeIndex = crbb_vec->cur_bit;
|
||||||
|
*codewrd_bitmap = 0;
|
||||||
|
*codewrd_len = 0;
|
||||||
|
if (*run_len_cnt >= 64) {
|
||||||
|
for (i = 0; i < 15; i++) {
|
||||||
|
if (t4_make_up_ind[i] == *run_len_cnt) {
|
||||||
|
*codewrd_bitmap = t4_make_up[start][i];
|
||||||
|
*codewrd_len = t4_make_up_length[start][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*codewrd_bitmap = t4_term[start][*run_len_cnt];
|
||||||
|
*codewrd_len = t4_term_length[start][*run_len_cnt];
|
||||||
|
}
|
||||||
|
bitvec_write_field(crbb_vec, &writeIndex, *codewrd_bitmap, *codewrd_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compress received block bitmap */
|
||||||
|
int egprs_compress::osmo_t4_compress(struct bitvec *bv)
|
||||||
|
{
|
||||||
|
uint8_t crbb_len = 0;
|
||||||
|
uint8_t uclen_crbb = 0;
|
||||||
|
uint8_t crbb_bitmap[127] = {'\0'};
|
||||||
|
bool start = (bv->data[0] & 0x80)>>7;
|
||||||
|
struct bitvec crbb_vec;
|
||||||
|
|
||||||
|
crbb_vec.data = crbb_bitmap;
|
||||||
|
crbb_vec.cur_bit = 0;
|
||||||
|
crbb_vec.data_len = 127;
|
||||||
|
bv->data_len = bv->cur_bit;
|
||||||
|
bv->cur_bit = 0;
|
||||||
|
if (egprs_compress::compress_rbb(bv, &crbb_vec, &uclen_crbb, 23*8)) {
|
||||||
|
memcpy(bv->data, crbb_bitmap, (crbb_len+7)/8);
|
||||||
|
bv->cur_bit = crbb_len;
|
||||||
|
bv->data_len = (crbb_len+7)/8;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("Encode failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief compression algorithm using T4 encoding
|
||||||
|
* the compressed bitmap's are copied in crbb_bitmap
|
||||||
|
* \param[in] rbb_vec bit vector to be encoded
|
||||||
|
* \return 1 if compression is success or 0 for failure
|
||||||
|
*/
|
||||||
|
int egprs_compress::compress_rbb(
|
||||||
|
struct bitvec *urbb_vec,
|
||||||
|
struct bitvec *crbb_vec,
|
||||||
|
uint8_t *uclen_crbb, /* Uncompressed bitmap len in CRBB */
|
||||||
|
uint8_t max_bits) /* max remaining bits */
|
||||||
|
{
|
||||||
|
bool run_len_bit;
|
||||||
|
int buflen = urbb_vec->cur_bit;
|
||||||
|
int total_bits = urbb_vec->cur_bit;
|
||||||
|
uint16_t rlen;
|
||||||
|
uint16_t temprl = 0;
|
||||||
|
uint16_t cbmap = 0; /* Compressed code word */
|
||||||
|
int16_t nbits; /* Length of code word */
|
||||||
|
uint16_t uclen = 0;
|
||||||
|
int16_t clen = 0;
|
||||||
|
bool start; /* Starting color code see 9.1.10, 3GPP 44.060 */
|
||||||
|
urbb_vec->cur_bit = 0;
|
||||||
|
run_len_bit = (urbb_vec->data[0] & 0x80)>>7;
|
||||||
|
while (buflen > 0) {
|
||||||
|
temprl = 0;
|
||||||
|
/* Find Run length */
|
||||||
|
if (run_len_bit == 1)
|
||||||
|
rlen = bitvec_rl_curbit(urbb_vec, true, total_bits);
|
||||||
|
else
|
||||||
|
rlen = bitvec_rl_curbit(urbb_vec, false, total_bits);
|
||||||
|
buflen = buflen - rlen;
|
||||||
|
/* if rlen > 64 need Makeup code word */
|
||||||
|
/*Compress the bits */
|
||||||
|
if (run_len_bit == 0) {
|
||||||
|
start = 0;
|
||||||
|
if (rlen >= 64) {
|
||||||
|
temprl = (rlen/64)*64;
|
||||||
|
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||||
|
crbb_vec, start);
|
||||||
|
clen = clen + nbits;
|
||||||
|
}
|
||||||
|
temprl = MOD64(rlen);
|
||||||
|
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||||
|
crbb_vec, start);
|
||||||
|
/* next time the run length will be Ones */
|
||||||
|
run_len_bit = 1;
|
||||||
|
} else {
|
||||||
|
start = 1;
|
||||||
|
if (rlen >= 64) {
|
||||||
|
temprl = (rlen/64)*64;
|
||||||
|
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||||
|
crbb_vec, start);
|
||||||
|
clen = clen + nbits;
|
||||||
|
}
|
||||||
|
temprl = MOD64(rlen);
|
||||||
|
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||||
|
crbb_vec, start);
|
||||||
|
|
||||||
|
/* next time the run length will be Zeros */
|
||||||
|
run_len_bit = 0;
|
||||||
|
}
|
||||||
|
uclen = uclen + rlen;
|
||||||
|
clen = clen + nbits;
|
||||||
|
/*compressed bitmap exceeds the buffer space */
|
||||||
|
if (clen > max_bits) {
|
||||||
|
uclen = uclen - rlen;
|
||||||
|
clen = clen - nbits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crbb_vec->cur_bit = clen;
|
||||||
|
*uclen_crbb = uclen;
|
||||||
|
if (clen >= uclen)
|
||||||
|
/* No Gain is observed, So no need to compress */
|
||||||
|
return 0;
|
||||||
|
LOGP(DRLCMACUL, LOGL_DEBUG, "CRBB bitmap = %s\n", osmo_hexdump(crbb_vec->data, (crbb_vec->cur_bit+7)/8));
|
||||||
|
/* Add compressed bitmap to final buffer */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
33
src/egprs_rlc_compression.h
Normal file
33
src/egprs_rlc_compression.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/* egprs_rlc_compression.h
|
||||||
|
* Routines for EGPRS RLC bitmap compression handling
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct egprs_compress_node;
|
||||||
|
#define MOD64(X) (((X) + 64) & 0x3F)
|
||||||
|
|
||||||
|
/* Singleton to manage the EGPRS compression algorithm. */
|
||||||
|
class egprs_compress
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static int decompress_crbb(int8_t compress_bmap_len,
|
||||||
|
bool start, const uint8_t *orig_buf,
|
||||||
|
bitvec *dest);
|
||||||
|
egprs_compress();
|
||||||
|
int osmo_t4_compress(struct bitvec *bv);
|
||||||
|
static int compress_rbb(struct bitvec *urbb_vec, struct bitvec *crbb_vec,
|
||||||
|
uint8_t *uclen_crbb, uint8_t max_bits);
|
||||||
|
|
||||||
|
private:
|
||||||
|
egprs_compress_node *ones_list;
|
||||||
|
egprs_compress_node *zeros_list;
|
||||||
|
|
||||||
|
void decode_tree_init(void);
|
||||||
|
static egprs_compress *s_instance;
|
||||||
|
static egprs_compress*instance();
|
||||||
|
egprs_compress_node *create_tree_node(void *);
|
||||||
|
void build_codewords(egprs_compress_node *root, const char *cdwd[]);
|
||||||
|
/* singleton class, so this private destructor is left unimplemented. */
|
||||||
|
~egprs_compress();
|
||||||
|
};
|
||||||
1860
src/encoding.cpp
Normal file
1860
src/encoding.cpp
Normal file
File diff suppressed because it is too large
Load Diff
141
src/encoding.h
Normal file
141
src/encoding.h
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/* encoding.cpp
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#include <osmocom/gsm/l1sap.h>
|
||||||
|
#include "coding_scheme.h"
|
||||||
|
#include "gsm_rlcmac.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct gprs_rlcmac_tbf;
|
||||||
|
struct bitvec;
|
||||||
|
struct gprs_llc;
|
||||||
|
struct gprs_rlc_data_block_info;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
/**
|
||||||
|
* I help with encoding data into CSN1 messages.
|
||||||
|
* TODO: Nobody can remember a function signature like this. One should
|
||||||
|
* fill out a struct with the request parameters and then hand the struct
|
||||||
|
* to the code.
|
||||||
|
*/
|
||||||
|
class Encoding {
|
||||||
|
public:
|
||||||
|
static int write_immediate_assignment(
|
||||||
|
const struct gprs_rlcmac_pdch *pdch,
|
||||||
|
struct gprs_rlcmac_tbf *tbf,
|
||||||
|
bitvec * dest, bool downlink, uint16_t ra,
|
||||||
|
uint32_t ref_fn, uint8_t ta,
|
||||||
|
uint8_t usf, bool polling,
|
||||||
|
uint32_t fn, uint8_t alpha, uint8_t gamma,
|
||||||
|
int8_t ta_idx,
|
||||||
|
enum ph_burst_type burst_type);
|
||||||
|
|
||||||
|
static int write_immediate_assignment_reject(
|
||||||
|
bitvec *dest, uint16_t ra,
|
||||||
|
uint32_t ref_fn,
|
||||||
|
enum ph_burst_type burst_type,
|
||||||
|
uint8_t t3142
|
||||||
|
);
|
||||||
|
|
||||||
|
static void write_packet_uplink_assignment(
|
||||||
|
RlcMacDownlink_t * block, uint8_t old_tfi,
|
||||||
|
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
|
||||||
|
const struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t rrbp,
|
||||||
|
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
|
||||||
|
bool use_egprs);
|
||||||
|
|
||||||
|
static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
|
||||||
|
bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
|
||||||
|
const struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll, uint8_t rrbp,
|
||||||
|
uint8_t alpha, uint8_t gamma,
|
||||||
|
int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
|
||||||
|
|
||||||
|
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
|
||||||
|
|
||||||
|
static void write_packet_access_reject(bitvec * dest, uint32_t tlli,
|
||||||
|
unsigned long t3172_ms);
|
||||||
|
|
||||||
|
static void write_packet_uplink_ack(
|
||||||
|
bitvec * dest, struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
|
||||||
|
uint8_t rrbp);
|
||||||
|
|
||||||
|
static int write_paging_request(bitvec * dest, const struct osmo_mobile_identity *mi);
|
||||||
|
|
||||||
|
static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
|
||||||
|
uint8_t *identity, uint8_t chan_needed);
|
||||||
|
|
||||||
|
static unsigned write_packet_paging_request(bitvec * dest);
|
||||||
|
|
||||||
|
static int rlc_write_dl_data_header(
|
||||||
|
const struct gprs_rlc_data_info *rlc,
|
||||||
|
uint8_t *data);
|
||||||
|
static unsigned int rlc_copy_from_aligned_buffer(
|
||||||
|
const struct gprs_rlc_data_info *rlc,
|
||||||
|
unsigned int data_block_idx,
|
||||||
|
uint8_t *dst, const uint8_t *buffer);
|
||||||
|
|
||||||
|
enum AppendResult {
|
||||||
|
AR_NEED_MORE_BLOCKS,
|
||||||
|
AR_COMPLETED_SPACE_LEFT,
|
||||||
|
AR_COMPLETED_BLOCK_FILLED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static AppendResult rlc_data_to_dl_append(
|
||||||
|
struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
|
||||||
|
gprs_llc *llc, int *offset, int *num_chunks,
|
||||||
|
uint8_t *data, bool is_final, int *count_payload);
|
||||||
|
static void rlc_data_to_dl_append_egprs_li_padding(
|
||||||
|
const struct gprs_rlc_data_block_info *rdbi,
|
||||||
|
int *offset, int *num_chunks, uint8_t *data_block);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ifdef __cplusplus */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void write_packet_neighbour_cell_data(RlcMacDownlink_t *block,
|
||||||
|
bool tfi_is_dl, uint8_t tfi, uint8_t container_id,
|
||||||
|
uint8_t container_idx, PNCDContainer_t *container);
|
||||||
|
|
||||||
|
void write_packet_cell_change_continue(RlcMacDownlink_t *block, uint8_t poll, uint8_t rrbp,
|
||||||
|
bool tfi_is_dl, uint8_t tfi, bool exist_id,
|
||||||
|
uint16_t arfcn, uint8_t bsic, uint8_t container_id);
|
||||||
|
|
||||||
|
void write_packet_measurement_order(RlcMacDownlink_t *block, uint8_t poll, uint8_t rrbp,
|
||||||
|
bool tfi_assigned, bool tfi_is_dl, uint8_t tfi,
|
||||||
|
uint32_t tlli, uint8_t pmo_idx, uint8_t pmo_count,
|
||||||
|
uint8_t nco, bool exist_nc, uint8_t non_drx_period,
|
||||||
|
uint8_t nc_report_period_i, uint8_t nc_report_period_t,
|
||||||
|
const NC_Frequency_list_t *NC_Frequency_list);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
1332
src/gprs_bssgp_pcu.c
Normal file
1332
src/gprs_bssgp_pcu.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,525 +0,0 @@
|
|||||||
/* gprs_bssgp_pcu.cpp
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <gprs_rlcmac.h>
|
|
||||||
#include <gprs_bssgp_pcu.h>
|
|
||||||
#include <pcu_l1_if.h>
|
|
||||||
|
|
||||||
struct sgsn_instance *sgsn;
|
|
||||||
void *tall_bsc_ctx;
|
|
||||||
struct bssgp_bvc_ctx *bctx = NULL;
|
|
||||||
struct gprs_nsvc *nsvc = NULL;
|
|
||||||
extern uint16_t spoof_mcc, spoof_mnc;
|
|
||||||
|
|
||||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
|
|
||||||
{
|
|
||||||
struct bssgp_ud_hdr *budh;
|
|
||||||
|
|
||||||
int8_t tfi; /* must be signed */
|
|
||||||
uint32_t tlli;
|
|
||||||
int i, j;
|
|
||||||
uint8_t *data;
|
|
||||||
uint16_t len;
|
|
||||||
struct gprs_rlcmac_tbf *tbf;
|
|
||||||
|
|
||||||
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
|
|
||||||
tlli = ntohl(budh->tlli);
|
|
||||||
|
|
||||||
/* LLC_PDU is mandatory IE */
|
|
||||||
if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
|
|
||||||
{
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tlli);
|
|
||||||
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
|
|
||||||
len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
|
|
||||||
if (len > sizeof(tbf->llc_frame))
|
|
||||||
{
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli);
|
|
||||||
return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read IMSI. if no IMSI exists, use first paging block (any paging),
|
|
||||||
* because during attachment the IMSI might not be known, so the MS
|
|
||||||
* will listen to all paging blocks. */
|
|
||||||
char imsi[16] = "000";
|
|
||||||
if (TLVP_PRESENT(tp, BSSGP_IE_IMSI))
|
|
||||||
{
|
|
||||||
uint8_t imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
|
|
||||||
uint8_t *bcd_imsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_IMSI);
|
|
||||||
if ((bcd_imsi[0] & 0x08))
|
|
||||||
imsi_len = imsi_len * 2 - 1;
|
|
||||||
else
|
|
||||||
imsi_len = (imsi_len - 1) * 2;
|
|
||||||
for (i = 0, j = 0; j < imsi_len && j < 16; j++)
|
|
||||||
{
|
|
||||||
if (!(j & 1)) {
|
|
||||||
imsi[j] = (bcd_imsi[i] >> 4) + '0';
|
|
||||||
i++;
|
|
||||||
} else
|
|
||||||
imsi[j] = (bcd_imsi[i] & 0xf) + '0';
|
|
||||||
}
|
|
||||||
imsi[j] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse ms radio access capability */
|
|
||||||
uint8_t ms_class = 0;
|
|
||||||
if (TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
|
|
||||||
{
|
|
||||||
bitvec *block;
|
|
||||||
uint8_t cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
|
|
||||||
uint8_t *cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
|
|
||||||
unsigned rp = 0;
|
|
||||||
|
|
||||||
block = bitvec_alloc(cap_len);
|
|
||||||
bitvec_unpack(block, cap);
|
|
||||||
bitvec_read_field(block, rp, 4); // Access Technology Type
|
|
||||||
bitvec_read_field(block, rp, 7); // Length of Access Capabilities
|
|
||||||
bitvec_read_field(block, rp, 3); // RF Power Capability
|
|
||||||
if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
|
|
||||||
bitvec_read_field(block, rp, 7); // A5 Bits
|
|
||||||
bitvec_read_field(block, rp, 1); // ES IND
|
|
||||||
bitvec_read_field(block, rp, 1); // PS
|
|
||||||
bitvec_read_field(block, rp, 1); // VGCS
|
|
||||||
bitvec_read_field(block, rp, 1); // VBS
|
|
||||||
if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
|
|
||||||
if (bitvec_read_field(block, rp, 1)) // HSCSD Present
|
|
||||||
bitvec_read_field(block, rp, 5); // Class
|
|
||||||
if (bitvec_read_field(block, rp, 1)) { // GPRS Present
|
|
||||||
ms_class = bitvec_read_field(block, rp, 5); // Class
|
|
||||||
bitvec_read_field(block, rp, 1); // Ext.
|
|
||||||
}
|
|
||||||
if (bitvec_read_field(block, rp, 1)) // SMS Present
|
|
||||||
bitvec_read_field(block, rp, 4); // SMS Value
|
|
||||||
bitvec_read_field(block, rp, 4); // SMS Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* get lifetime */
|
|
||||||
uint16_t delay_csec = 0xffff;
|
|
||||||
if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME))
|
|
||||||
{
|
|
||||||
uint8_t lt_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
|
|
||||||
uint16_t *lt = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_PDU_LIFETIME);
|
|
||||||
if (lt_len == 2)
|
|
||||||
delay_csec = ntohs(*lt);
|
|
||||||
else
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
|
|
||||||
"PDU_LIFETIME IE\n");
|
|
||||||
} else
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP missing mandatory "
|
|
||||||
"PDU_LIFETIME IE\n");
|
|
||||||
|
|
||||||
LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
|
|
||||||
|
|
||||||
/* check for existing TBF */
|
|
||||||
if ((tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
|
|
||||||
LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli);
|
|
||||||
if (tbf->state == GPRS_RLCMAC_WAIT_RELEASE) {
|
|
||||||
LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state "
|
|
||||||
"(T3193), so reuse TBF\n");
|
|
||||||
memcpy(tbf->llc_frame, data, len);
|
|
||||||
tbf->llc_length = len;
|
|
||||||
memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset
|
|
||||||
rlc states */
|
|
||||||
if (!tbf->ms_class && ms_class)
|
|
||||||
tbf->ms_class = ms_class;
|
|
||||||
tbf_update(tbf);
|
|
||||||
gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL);
|
|
||||||
} else {
|
|
||||||
/* the TBF exists, so we must write it in the queue
|
|
||||||
* we prepend lifetime in front of PDU */
|
|
||||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
|
||||||
struct timeval *tv;
|
|
||||||
struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv),
|
|
||||||
"llc_pdu_queue");
|
|
||||||
if (!llc_msg)
|
|
||||||
return -ENOMEM;
|
|
||||||
tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
|
|
||||||
if (bts->force_llc_lifetime)
|
|
||||||
delay_csec = bts->force_llc_lifetime;
|
|
||||||
/* keep timestap at 0 for infinite delay */
|
|
||||||
if (delay_csec != 0xffff) {
|
|
||||||
/* calculate timestamp of timeout */
|
|
||||||
gettimeofday(tv, NULL);
|
|
||||||
tv->tv_usec += (delay_csec % 100) * 10000;
|
|
||||||
tv->tv_sec += delay_csec / 100;
|
|
||||||
if (tv->tv_usec > 999999) {
|
|
||||||
tv->tv_usec -= 1000000;
|
|
||||||
tv->tv_sec++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memcpy(msgb_put(llc_msg, len), data, len);
|
|
||||||
msgb_enqueue(&tbf->llc_queue, llc_msg);
|
|
||||||
/* set ms class for updating TBF */
|
|
||||||
if (!tbf->ms_class && ms_class)
|
|
||||||
tbf->ms_class = ms_class;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uint8_t trx, ts, use_trx, first_ts, ta, ss;
|
|
||||||
struct gprs_rlcmac_tbf *old_tbf;
|
|
||||||
|
|
||||||
/* check for uplink data, so we copy our informations */
|
|
||||||
tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
|
|
||||||
if (tbf && tbf->dir.ul.contention_resolution_done
|
|
||||||
&& !tbf->dir.ul.final_ack_sent) {
|
|
||||||
use_trx = tbf->trx;
|
|
||||||
first_ts = tbf->first_ts;
|
|
||||||
ta = tbf->ta;
|
|
||||||
ss = 0;
|
|
||||||
old_tbf = tbf;
|
|
||||||
} else {
|
|
||||||
use_trx = -1;
|
|
||||||
first_ts = -1;
|
|
||||||
ta = 0; /* FIXME: initial TA */
|
|
||||||
ss = 1; /* PCH assignment only allows one timeslot */
|
|
||||||
old_tbf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new TBF (any TRX)
|
|
||||||
tfi = tfi_alloc(GPRS_RLCMAC_DL_TBF, &trx, &ts, use_trx, first_ts);
|
|
||||||
if (tfi < 0) {
|
|
||||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
|
|
||||||
/* FIXME: send reject */
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
/* set number of downlink slots according to multislot class */
|
|
||||||
tbf = tbf_alloc(tbf, GPRS_RLCMAC_DL_TBF, tfi, trx, ts, ms_class,
|
|
||||||
ss);
|
|
||||||
if (!tbf) {
|
|
||||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
|
|
||||||
/* FIXME: send reject */
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
tbf->tlli = tlli;
|
|
||||||
tbf->tlli_valid = 1;
|
|
||||||
tbf->ta = ta;
|
|
||||||
|
|
||||||
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
|
|
||||||
|
|
||||||
/* new TBF, so put first frame */
|
|
||||||
memcpy(tbf->llc_frame, data, len);
|
|
||||||
tbf->llc_length = len;
|
|
||||||
|
|
||||||
/* trigger downlink assignment and set state to ASSIGN.
|
|
||||||
* we don't use old_downlink, so the possible uplink is used
|
|
||||||
* to trigger downlink assignment. if there is no uplink,
|
|
||||||
* AGCH is used. */
|
|
||||||
gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
|
|
||||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
|
|
||||||
{
|
|
||||||
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
|
||||||
uint8_t pdu_type = bgph->pdu_type;
|
|
||||||
unsigned rc = 0;
|
|
||||||
|
|
||||||
if (!bctx)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* If traffic is received on a BVC that is marked as blocked, the
|
|
||||||
* received PDU shall not be accepted and a STATUS PDU (Cause value:
|
|
||||||
* BVC Blocked) shall be sent to the peer entity on the signalling BVC */
|
|
||||||
if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS)
|
|
||||||
{
|
|
||||||
uint16_t bvci = msgb_bvci(msg);
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "rx BVC_S_BLOCKED\n");
|
|
||||||
return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (pdu_type) {
|
|
||||||
case BSSGP_PDUT_DL_UNITDATA:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
|
|
||||||
gprs_bssgp_pcu_rx_dl_ud(msg, tp);
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_PAGING_PS:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_PAGING_CS:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
|
|
||||||
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Receive a BSSGP PDU from a SGSN on a SIGNALLING BVCI */
|
|
||||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
|
|
||||||
{
|
|
||||||
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
|
||||||
int rc = 0;
|
|
||||||
switch (bgph->pdu_type) {
|
|
||||||
case BSSGP_PDUT_STATUS:
|
|
||||||
/* Some exception has occurred */
|
|
||||||
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
|
|
||||||
/* FIXME: send NM_STATUS.ind to NM */
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_SUSPEND_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_SUSPEND_NACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_BVC_RESET_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_PAGING_PS:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_PAGING_CS:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_RESUME_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_RESUME_NACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_FLUSH_LL:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_BVC_BLOCK_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
|
|
||||||
break;
|
|
||||||
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
|
|
||||||
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
|
|
||||||
{
|
|
||||||
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
|
||||||
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
|
|
||||||
struct tlv_parsed tp;
|
|
||||||
uint8_t pdu_type = bgph->pdu_type;
|
|
||||||
uint16_t ns_bvci = msgb_bvci(msg);
|
|
||||||
int data_len;
|
|
||||||
int rc = 0;
|
|
||||||
struct bssgp_bvc_ctx *bctx;
|
|
||||||
|
|
||||||
if (pdu_type == BSSGP_PDUT_STATUS) {
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u received STATUS\n",
|
|
||||||
msgb_nsei(msg), ns_bvci);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
|
|
||||||
|
|
||||||
/* UNITDATA BSSGP headers have TLLI in front */
|
|
||||||
if (pdu_type != BSSGP_PDUT_UL_UNITDATA && pdu_type != BSSGP_PDUT_DL_UNITDATA)
|
|
||||||
{
|
|
||||||
data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
|
|
||||||
rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data_len = msgb_bssgp_len(msg) - sizeof(*budh);
|
|
||||||
rc = bssgp_tlv_parse(&tp, budh->data, data_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* look-up or create the BTS context for this BVC */
|
|
||||||
bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
|
|
||||||
|
|
||||||
if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET_ACK)
|
|
||||||
{
|
|
||||||
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
|
|
||||||
"type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
|
|
||||||
pdu_type);
|
|
||||||
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bctx)
|
|
||||||
{
|
|
||||||
log_set_context(BSC_CTX_BVC, bctx);
|
|
||||||
rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
|
|
||||||
rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], msgb_bssgp_len(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ns_bvci == BVCI_SIGNALLING)
|
|
||||||
{
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
|
|
||||||
rc = gprs_bssgp_pcu_rx_sign(msg, &tp, bctx);
|
|
||||||
}
|
|
||||||
else if (ns_bvci == BVCI_PTM)
|
|
||||||
{
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTM bssgp_tx_status\n");
|
|
||||||
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
|
|
||||||
rc = gprs_bssgp_pcu_rx_ptp(msg, &tp, bctx);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
switch (event) {
|
|
||||||
case GPRS_NS_EVT_UNIT_DATA:
|
|
||||||
/* hand the message into the BSSGP implementation */
|
|
||||||
rc = gprs_bssgp_pcu_rcvmsg(msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGP(DPCU, LOGL_NOTICE, "RLCMAC: Unknown event %u from NS\n", event);
|
|
||||||
if (msg)
|
|
||||||
talloc_free(msg);
|
|
||||||
rc = -EIO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nsvc_unblocked = 0;
|
|
||||||
|
|
||||||
static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
|
|
||||||
void *handler_data, void *signal_data)
|
|
||||||
{
|
|
||||||
struct ns_signal_data *nssd;
|
|
||||||
|
|
||||||
if (subsys != SS_L_NS)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
nssd = (struct ns_signal_data *)signal_data;
|
|
||||||
if (nssd->nsvc != nsvc) {
|
|
||||||
LOGP(DPCU, LOGL_ERROR, "Signal received of unknown NSVC\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (signal) {
|
|
||||||
case S_NS_UNBLOCK:
|
|
||||||
if (!nsvc_unblocked) {
|
|
||||||
nsvc_unblocked = 1;
|
|
||||||
LOGP(DPCU, LOGL_NOTICE, "NS-VC is unblocked.\n");
|
|
||||||
bssgp_tx_bvc_reset(bctx, bctx->bvci,
|
|
||||||
BSSGP_CAUSE_PROTO_ERR_UNSPEC);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case S_NS_BLOCK:
|
|
||||||
if (nsvc_unblocked) {
|
|
||||||
nsvc_unblocked = 0;
|
|
||||||
LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create BSSGP/NS layer instances */
|
|
||||||
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
|
|
||||||
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
|
|
||||||
uint16_t rac, uint16_t cell_id)
|
|
||||||
{
|
|
||||||
struct sockaddr_in dest;
|
|
||||||
|
|
||||||
if (bctx)
|
|
||||||
return 0; /* if already created, must return 0: no error */
|
|
||||||
|
|
||||||
bssgp_nsi = gprs_ns_instantiate(&sgsn_ns_cb, NULL);
|
|
||||||
if (!bssgp_nsi) {
|
|
||||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
gprs_ns_nsip_listen(bssgp_nsi);
|
|
||||||
|
|
||||||
dest.sin_family = AF_INET;
|
|
||||||
dest.sin_port = htons(sgsn_port);
|
|
||||||
dest.sin_addr.s_addr = htonl(sgsn_ip);
|
|
||||||
|
|
||||||
nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
|
|
||||||
if (!nsvc) {
|
|
||||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NSVCt\n");
|
|
||||||
gprs_ns_destroy(bssgp_nsi);
|
|
||||||
bssgp_nsi = NULL;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bctx = btsctx_alloc(bvci, nsei);
|
|
||||||
if (!bctx) {
|
|
||||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to create BSSGP context\n");
|
|
||||||
nsvc = NULL;
|
|
||||||
gprs_ns_destroy(bssgp_nsi);
|
|
||||||
bssgp_nsi = NULL;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
bctx->ra_id.mcc = spoof_mcc ? : mcc;
|
|
||||||
bctx->ra_id.mnc = spoof_mnc ? : mnc;
|
|
||||||
bctx->ra_id.lac = lac;
|
|
||||||
bctx->ra_id.rac = rac;
|
|
||||||
bctx->cell_id = cell_id;
|
|
||||||
|
|
||||||
osmo_signal_register_handler(SS_L_NS, nsvc_signal_cb, NULL);
|
|
||||||
|
|
||||||
// bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gprs_bssgp_destroy(void)
|
|
||||||
{
|
|
||||||
if (!bssgp_nsi)
|
|
||||||
return;
|
|
||||||
|
|
||||||
osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
|
|
||||||
|
|
||||||
nsvc = NULL;
|
|
||||||
|
|
||||||
/* FIXME: move this to libgb: btsctx_free() */
|
|
||||||
llist_del(&bctx->list);
|
|
||||||
talloc_free(bctx);
|
|
||||||
bctx = NULL;
|
|
||||||
|
|
||||||
/* FIXME: blocking... */
|
|
||||||
|
|
||||||
gprs_ns_destroy(bssgp_nsi);
|
|
||||||
bssgp_nsi = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/* gprs_bssgp_pcu.h
|
/* gprs_bssgp_pcu.h
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -20,44 +21,88 @@
|
|||||||
#ifndef GPRS_BSSGP_PCU_H
|
#ifndef GPRS_BSSGP_PCU_H
|
||||||
#define GPRS_BSSGP_PCU_H
|
#define GPRS_BSSGP_PCU_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#endif
|
||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
#include <osmocom/core/rate_ctr.h>
|
#include <osmocom/core/rate_ctr.h>
|
||||||
#include <osmocom/core/logging.h>
|
#include <osmocom/core/logging.h>
|
||||||
#include <osmocom/core/signal.h>
|
#include <osmocom/core/signal.h>
|
||||||
#include <osmocom/core/application.h>
|
#include <osmocom/core/application.h>
|
||||||
#include <osmocom/gprs/gprs_ns.h>
|
#include <osmocom/gprs/gprs_ns2.h>
|
||||||
#include <osmocom/gprs/gprs_bssgp.h>
|
#include <osmocom/gprs/gprs_bssgp.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp_bss.h>
|
||||||
#include <osmocom/gprs/gprs_msgb.h>
|
#include <osmocom/gprs/gprs_msgb.h>
|
||||||
|
|
||||||
int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause);
|
|
||||||
|
|
||||||
int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli, const uint8_t *qos_profile, struct msgb *llc_pdu);
|
|
||||||
|
|
||||||
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
|
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
|
||||||
}
|
|
||||||
#include <gprs_debug.h>
|
#include <gprs_debug.h>
|
||||||
|
|
||||||
#define QOS_PROFILE 0
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define QOS_PROFILE 4
|
||||||
#define BSSGP_HDR_LEN 53
|
#define BSSGP_HDR_LEN 53
|
||||||
#define NS_HDR_LEN 4
|
#define NS_HDR_LEN 4
|
||||||
#define IE_LLC_PDU 14
|
#define IE_LLC_PDU 14
|
||||||
|
|
||||||
extern struct bssgp_bvc_ctx *bctx;
|
struct gprs_bssgp_pcu {
|
||||||
|
struct bssgp_bvc_ctx *bctx;
|
||||||
|
|
||||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp);
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
|
||||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
|
struct osmo_timer_list bvc_timer;
|
||||||
|
|
||||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
|
/* state: is the NSVC unblocked? */
|
||||||
|
int nsvc_unblocked;
|
||||||
|
|
||||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
|
/* state: true if bvc signalling needs to be reseted or waiting for reset ack */
|
||||||
|
int bvc_sig_reset;
|
||||||
|
/* state: true if bvc ptp needs to be reseted or waiting for reset ack */
|
||||||
|
int bvc_reset;
|
||||||
|
/* state: true if bvc ptp is unblocked */
|
||||||
|
int bvc_unblocked;
|
||||||
|
|
||||||
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
|
/* Flow control */
|
||||||
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
|
struct timespec queue_delay_sum;
|
||||||
uint16_t rac, uint16_t cell_id);
|
unsigned queue_delay_count;
|
||||||
|
uint8_t fc_tag;
|
||||||
|
unsigned queue_frames_sent;
|
||||||
|
unsigned queue_bytes_recv;
|
||||||
|
unsigned queue_frames_recv;
|
||||||
|
|
||||||
void gprs_bssgp_destroy(void);
|
/** callbacks below */
|
||||||
|
|
||||||
|
/* The BSSGP has been unblocked */
|
||||||
|
void (*on_unblock_ack)(struct gprs_bssgp_pcu *pcu);
|
||||||
|
|
||||||
|
/* When BSSGP data arrives. The msgb is not only for reference */
|
||||||
|
void (*on_dl_unit_data)(struct gprs_bssgp_pcu *pcu, struct msgb *msg,
|
||||||
|
struct tlv_parsed *tp);
|
||||||
|
};
|
||||||
|
|
||||||
|
int gprs_gp_send_cb(void *ctx, struct msgb *msg);
|
||||||
|
int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
|
||||||
|
void gprs_bssgp_update_queue_delay(const struct timespec *tv_recv,
|
||||||
|
const struct timespec *tv_now);
|
||||||
|
void gprs_bssgp_update_frames_sent();
|
||||||
|
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv);
|
||||||
|
|
||||||
|
struct gprs_bssgp_pcu *gprs_bssgp_init(
|
||||||
|
struct gprs_rlcmac_bts *bts,
|
||||||
|
uint16_t nsei, uint16_t bvci,
|
||||||
|
uint16_t mcc, uint16_t mnc, bool mnc_3_digits,
|
||||||
|
uint16_t lac, uint16_t rac, uint16_t cell_id);
|
||||||
|
|
||||||
|
int gprs_ns_update_config(struct gprs_rlcmac_bts *bts, uint16_t nsei,
|
||||||
|
const struct osmo_sockaddr *local,
|
||||||
|
const struct osmo_sockaddr *remote,
|
||||||
|
uint16_t *nsvci, uint16_t valid);
|
||||||
|
|
||||||
|
void gprs_bssgp_destroy(struct gprs_rlcmac_bts *bts);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // GPRS_BSSGP_PCU_H
|
#endif // GPRS_BSSGP_PCU_H
|
||||||
|
|||||||
294
src/gprs_bssgp_rim.c
Normal file
294
src/gprs_bssgp_rim.c
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
/* gprs_bssgp_pcu.cpp
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <osmocom/gprs/gprs_bssgp.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
#include <osmocom/gsm/tlv.h>
|
||||||
|
#include <osmocom/gprs/gprs_ns.h>
|
||||||
|
#include <osmocom/core/prim.h>
|
||||||
|
#include <pcu_l1_if.h>
|
||||||
|
#include <gprs_rlcmac.h>
|
||||||
|
#include <bts.h>
|
||||||
|
|
||||||
|
#include "gprs_debug.h"
|
||||||
|
#include "gprs_pcu.h"
|
||||||
|
#include "bts.h"
|
||||||
|
#include "gprs_ms.h"
|
||||||
|
#include "nacc_fsm.h"
|
||||||
|
|
||||||
|
#define LOGPRIM(nsei, level, fmt, args...) \
|
||||||
|
LOGP(DRIM, level, "(NSEI=%u) " fmt, nsei, ## args)
|
||||||
|
|
||||||
|
static inline void gprs_ra_id_ci_to_cgi_ps(struct osmo_cell_global_id_ps *cgi_ps,
|
||||||
|
const struct gprs_ra_id *raid, uint16_t cid)
|
||||||
|
{
|
||||||
|
*cgi_ps = (struct osmo_cell_global_id_ps) {
|
||||||
|
.rai.lac.plmn.mcc = raid->mcc,
|
||||||
|
.rai.lac.plmn.mnc = raid->mnc,
|
||||||
|
.rai.lac.plmn.mnc_3_digits = raid->mnc_3_digits,
|
||||||
|
.rai.lac.lac = raid->lac,
|
||||||
|
.rai.rac = raid->rac,
|
||||||
|
.cell_identity = cid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mirror RIM routing information of a given PDU, see also 3GPP TS 48.018, section 8c.1.4.3 */
|
||||||
|
static void mirror_rim_routing_info(struct bssgp_ran_information_pdu *to_pdu,
|
||||||
|
const struct bssgp_ran_information_pdu *from_pdu)
|
||||||
|
{
|
||||||
|
memcpy(&to_pdu->routing_info_dest, &from_pdu->routing_info_src, sizeof(to_pdu->routing_info_dest));
|
||||||
|
memcpy(&to_pdu->routing_info_src, &from_pdu->routing_info_dest, sizeof(to_pdu->routing_info_src));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill NACC application container with data (cell identifier, sysinfo) */
|
||||||
|
#define SI_HDR_LEN 2
|
||||||
|
static void fill_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *app_cont, const struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
struct bssgp_bvc_ctx *bctx = the_pcu->bssgp.bctx;
|
||||||
|
|
||||||
|
gprs_ra_id_ci_to_cgi_ps(&app_cont->reprt_cell, &bctx->ra_id, bctx->cell_id);
|
||||||
|
app_cont->num_si = 0;
|
||||||
|
|
||||||
|
/* Note: The BTS struct stores the system information including its pseudo header. The RIM application
|
||||||
|
* container defines the system information without pseudo header, so we need to chop it off. */
|
||||||
|
|
||||||
|
if (bts->si1_is_set) {
|
||||||
|
app_cont->si[app_cont->num_si] = bts->si1 + SI_HDR_LEN;
|
||||||
|
app_cont->num_si++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bts->si3_is_set) {
|
||||||
|
app_cont->si[app_cont->num_si] = bts->si3 + SI_HDR_LEN;
|
||||||
|
app_cont->num_si++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bts->si13_is_set) {
|
||||||
|
app_cont->si[app_cont->num_si] = bts->si13 + SI_HDR_LEN;
|
||||||
|
app_cont->num_si++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: It is possible that the resulting PDU will not contain any system information, even if this is
|
||||||
|
* an unlikely case since the BTS immediately updates the system information after startup. The
|
||||||
|
* specification permits to send zero system information, see also: 3GPP TS 48.018 section 11.3.63.2.1 */
|
||||||
|
|
||||||
|
if (!bts->si1_is_set || !bts->si3_is_set || !bts->si13_is_set)
|
||||||
|
LOGP(DNACC, LOGL_INFO, "TX RAN INFO RESPONSE (NACC) %s: Some SI are missing:%s%s%s\n",
|
||||||
|
osmo_cgi_ps_name(&app_cont->reprt_cell),
|
||||||
|
bts->si1_is_set ? "" : " SI1",
|
||||||
|
bts->si3_is_set ? "" : " SI3",
|
||||||
|
bts->si13_is_set ? "" : " SI13");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a RAN INFORMATION PDU that contains the requested system information */
|
||||||
|
static void format_response_pdu(struct bssgp_ran_information_pdu *resp_pdu,
|
||||||
|
const struct bssgp_ran_information_pdu *req_pdu,
|
||||||
|
const struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
memset(resp_pdu, 0, sizeof(*resp_pdu));
|
||||||
|
mirror_rim_routing_info(resp_pdu, req_pdu);
|
||||||
|
|
||||||
|
resp_pdu->decoded.rim_cont = (struct bssgp_ran_inf_rim_cont) {
|
||||||
|
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
|
||||||
|
.seq_num = 1, /* single report has only one message in response */
|
||||||
|
.pdu_ind = {
|
||||||
|
.pdu_type_ext = RIM_PDU_TYPE_SING_REP,
|
||||||
|
},
|
||||||
|
.prot_ver = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
fill_app_cont_nacc(&resp_pdu->decoded.rim_cont.u.app_cont_nacc, bts);
|
||||||
|
resp_pdu->decoded_present = true;
|
||||||
|
resp_pdu->rim_cont_iei = BSSGP_IE_RI_RIM_CONTAINER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Format a RAN INFORMATION ERROR PDU */
|
||||||
|
static void format_response_pdu_err(struct bssgp_ran_information_pdu *resp_pdu,
|
||||||
|
const struct bssgp_ran_information_pdu *req_pdu)
|
||||||
|
{
|
||||||
|
memset(resp_pdu, 0, sizeof(*resp_pdu));
|
||||||
|
mirror_rim_routing_info(resp_pdu, req_pdu);
|
||||||
|
|
||||||
|
resp_pdu->decoded.err_rim_cont = (struct bssgp_ran_inf_err_rim_cont) {
|
||||||
|
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
|
||||||
|
.prot_ver = 1,
|
||||||
|
.err_pdu = req_pdu->rim_cont,
|
||||||
|
.err_pdu_len = req_pdu->rim_cont_len,
|
||||||
|
};
|
||||||
|
|
||||||
|
resp_pdu->decoded_present = true;
|
||||||
|
resp_pdu->rim_cont_iei = BSSGP_IE_RI_ERROR_RIM_COINTAINER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_ran_info_response_nacc(const struct bssgp_ran_inf_app_cont_nacc *nacc, struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
struct si_cache_value val;
|
||||||
|
struct si_cache_entry *entry;
|
||||||
|
struct llist_head *tmp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
LOGP(DRIM, LOGL_INFO, "Rx RAN-INFO cell=%s type=%sBCCH num_si=%d\n",
|
||||||
|
osmo_cgi_ps_name(&nacc->reprt_cell),
|
||||||
|
nacc->type_psi ? "P" : "", nacc->num_si);
|
||||||
|
|
||||||
|
val.type_psi = nacc->type_psi;
|
||||||
|
val.si_len = 0;
|
||||||
|
for (i = 0; i < nacc->num_si; i++) {
|
||||||
|
size_t len = val.type_psi ? BSSGP_RIM_PSI_LEN : BSSGP_RIM_SI_LEN;
|
||||||
|
memcpy(&val.si_buf[val.si_len], nacc->si[i], len);
|
||||||
|
val.si_len += len;
|
||||||
|
}
|
||||||
|
entry = si_cache_add(bts->pcu->si_cache, &nacc->reprt_cell, &val);
|
||||||
|
|
||||||
|
llist_for_each(tmp, bts_ms_list(bts)) {
|
||||||
|
struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
|
||||||
|
if (!ms->nacc)
|
||||||
|
continue;
|
||||||
|
if (ms->nacc->fi->state != NACC_ST_WAIT_REQUEST_SI)
|
||||||
|
continue;
|
||||||
|
if (osmo_cgi_ps_cmp(&nacc->reprt_cell, &ms->nacc->cgi_ps) != 0)
|
||||||
|
continue;
|
||||||
|
osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_SI, entry);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_ran_info_request(const struct bssgp_ran_information_pdu *pdu,
|
||||||
|
struct gprs_rlcmac_bts *bts, uint16_t nsei)
|
||||||
|
{
|
||||||
|
const struct bssgp_ran_inf_req_rim_cont *ri_req = &pdu->decoded.req_rim_cont;
|
||||||
|
const struct bssgp_ran_inf_req_app_cont_nacc *nacc_req;
|
||||||
|
struct bssgp_ran_information_pdu resp_pdu;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* Check if we support the application ID */
|
||||||
|
if (ri_req->app_id != BSSGP_RAN_INF_APP_ID_NACC) {
|
||||||
|
LOGPRIM(nsei, LOGL_ERROR,
|
||||||
|
"Rx RAN-INFO-REQ for cell %s with unknown/wrong application ID %s -- reject.\n",
|
||||||
|
osmo_cgi_ps_name(&bts->cgi_ps), bssgp_ran_inf_app_id_str(ri_req->app_id));
|
||||||
|
format_response_pdu_err(&resp_pdu, pdu);
|
||||||
|
rc = -ENOTSUP;
|
||||||
|
goto tx_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
nacc_req = &ri_req->u.app_cont_nacc;
|
||||||
|
rc = osmo_cgi_ps_cmp(&bts->cgi_ps, &nacc_req->reprt_cell);
|
||||||
|
if (rc != 0) {
|
||||||
|
LOGPRIM(nsei, LOGL_ERROR, "reporting cell in RIM application container %s "
|
||||||
|
"does not match destination cell in RIM routing info %s -- rejected.\n",
|
||||||
|
osmo_cgi_ps_name(&nacc_req->reprt_cell),
|
||||||
|
osmo_cgi_ps_name2(&bts->cgi_ps));
|
||||||
|
format_response_pdu_err(&resp_pdu, pdu);
|
||||||
|
} else {
|
||||||
|
LOGPRIM(nsei, LOGL_INFO, "Responding to RAN INFORMATION REQUEST %s ...\n",
|
||||||
|
osmo_cgi_ps_name(&nacc_req->reprt_cell));
|
||||||
|
format_response_pdu(&resp_pdu, pdu, bts);
|
||||||
|
}
|
||||||
|
|
||||||
|
tx_ret:
|
||||||
|
bssgp_tx_rim(&resp_pdu, nsei);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_ran_info_response(const struct bssgp_ran_information_pdu *pdu, struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
const struct bssgp_ran_inf_rim_cont *ran_info = &pdu->decoded.rim_cont;
|
||||||
|
char ri_src_str[64];
|
||||||
|
|
||||||
|
/* Check if we support the application ID */
|
||||||
|
if (ran_info->app_id != BSSGP_RAN_INF_APP_ID_NACC) {
|
||||||
|
LOGP(DRIM, LOGL_ERROR,
|
||||||
|
"Rx RAN-INFO for cell %s with unknown/wrong application ID %s received -- reject.\n",
|
||||||
|
osmo_cgi_ps_name(&bts->cgi_ps), bssgp_ran_inf_app_id_str(ran_info->app_id));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ran_info->app_err) {
|
||||||
|
LOGP(DRIM, LOGL_ERROR,
|
||||||
|
"%s Rx RAN-INFO with an app error! cause: %s\n",
|
||||||
|
bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &pdu->routing_info_src),
|
||||||
|
bssgp_nacc_cause_str(ran_info->u.app_err_cont_nacc.nacc_cause));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_ran_info_response_nacc(&ran_info->u.app_cont_nacc, bts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_rim(struct osmo_bssgp_prim *bp)
|
||||||
|
{
|
||||||
|
struct msgb *msg = bp->oph.msg;
|
||||||
|
uint16_t nsei = msgb_nsei(msg);
|
||||||
|
struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu;
|
||||||
|
struct bssgp_ran_information_pdu resp_pdu;
|
||||||
|
struct osmo_cell_global_id_ps dst_addr;
|
||||||
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
|
||||||
|
OSMO_ASSERT (bp->oph.sap == SAP_BSSGP_RIM);
|
||||||
|
|
||||||
|
/* At the moment we only support GERAN, so we block all other network
|
||||||
|
* types here. */
|
||||||
|
if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) {
|
||||||
|
LOGPRIM(nsei, LOGL_ERROR,
|
||||||
|
"Only GERAN supported, destination cell is not a GERAN cell -- rejected.\n");
|
||||||
|
return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the RIM pdu is really addressed to this PCU. In case we
|
||||||
|
* receive a RIM PDU for a cell that is not parented by this PCU we
|
||||||
|
* are supposed to reject it with a BSSGP STATUS.
|
||||||
|
* see also: 3GPP TS 48.018, section 8c.3.1.2 */
|
||||||
|
gprs_ra_id_ci_to_cgi_ps(&dst_addr, &pdu->routing_info_dest.geran.raid,
|
||||||
|
pdu->routing_info_dest.geran.cid);
|
||||||
|
bts = gprs_pcu_get_bts_by_cgi_ps(the_pcu, &dst_addr);
|
||||||
|
if (!bts) {
|
||||||
|
LOGPRIM(nsei, LOGL_ERROR, "Destination cell %s unknown to this pcu\n",
|
||||||
|
osmo_cgi_ps_name(&dst_addr));
|
||||||
|
return bssgp_tx_status(BSSGP_CAUSE_UNKN_DST, NULL, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the incoming RIM PDU is parseable, if not we must report
|
||||||
|
* an error to the controlling BSS 3GPP TS 48.018, 8c.3.4 and 8c.3.4.2 */
|
||||||
|
if (!pdu->decoded_present) {
|
||||||
|
LOGPRIM(nsei, LOGL_ERROR, "Erroneous RIM PDU received for cell %s -- reject.\n",
|
||||||
|
osmo_cgi_ps_name(&dst_addr));
|
||||||
|
format_response_pdu_err(&resp_pdu, pdu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle incoming RIM container */
|
||||||
|
switch (pdu->rim_cont_iei) {
|
||||||
|
case BSSGP_IE_RI_REQ_RIM_CONTAINER:
|
||||||
|
return handle_ran_info_request(pdu, bts, nsei);
|
||||||
|
case BSSGP_IE_RI_RIM_CONTAINER:
|
||||||
|
return handle_ran_info_response(pdu, bts);
|
||||||
|
case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
|
||||||
|
case BSSGP_IE_RI_ACK_RIM_CONTAINER:
|
||||||
|
case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
|
||||||
|
LOGPRIM(nsei, LOGL_ERROR, "RIM PDU not handled by this application\n");
|
||||||
|
return -EINVAL;
|
||||||
|
default:
|
||||||
|
/* This should never happen. If the RIM PDU is parsed correctly, then the rim_cont_iei will
|
||||||
|
* be set to one of the cases above and if parsing fails this switch statement is guarded
|
||||||
|
* by the check on decoded_present above */
|
||||||
|
OSMO_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
24
src/gprs_bssgp_rim.h
Normal file
24
src/gprs_bssgp_rim.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/* gprs_bssgp_rim.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct osmo_bssgp_prim;
|
||||||
|
|
||||||
|
int handle_rim(struct osmo_bssgp_prim *bp);
|
||||||
180
src/gprs_codel.c
Normal file
180
src/gprs_codel.c
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/* gprs_codel.cpp
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||||
|
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gprs_codel.h"
|
||||||
|
#include "gprs_debug.h"
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/timer_compat.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static void control_law(struct gprs_codel *state, struct timespec *delta)
|
||||||
|
{
|
||||||
|
/* 256 / sqrt(x), limited to 255 */
|
||||||
|
static uint8_t inv_sqrt_tab[] = {255,
|
||||||
|
255, 181, 147, 128, 114, 104, 96, 90, 85, 80, 77, 73, 71, 68,
|
||||||
|
66, 64, 62, 60, 58, 57, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
|
||||||
|
45, 45, 44, 43, 43, 42, 42, 41, 40, 40, 39, 39, 39, 38, 38, 37,
|
||||||
|
37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 33, 32, 32,
|
||||||
|
32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28,
|
||||||
|
28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26,
|
||||||
|
26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24,
|
||||||
|
24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22,
|
||||||
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21,
|
||||||
|
21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||||
|
20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
|
||||||
|
19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18,
|
||||||
|
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17,
|
||||||
|
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||||
|
17, 17, 17, 17
|
||||||
|
};
|
||||||
|
uint_fast32_t delta_usecs;
|
||||||
|
uint_fast32_t inv_sqrt;
|
||||||
|
div_t q;
|
||||||
|
|
||||||
|
if (state->count >= ARRAY_SIZE(inv_sqrt_tab))
|
||||||
|
inv_sqrt = 16;
|
||||||
|
else
|
||||||
|
inv_sqrt = inv_sqrt_tab[state->count];
|
||||||
|
|
||||||
|
/* delta = state->interval / sqrt(count) */
|
||||||
|
delta_usecs = state->interval.tv_sec * 1000000 + state->interval.tv_nsec/1000;
|
||||||
|
delta_usecs = delta_usecs * inv_sqrt / 256;
|
||||||
|
|
||||||
|
q = div(delta_usecs, 1000000);
|
||||||
|
delta->tv_sec = q.quot;
|
||||||
|
delta->tv_nsec = q.rem * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_codel_init(struct gprs_codel *state)
|
||||||
|
{
|
||||||
|
static const struct gprs_codel init_state = {0};
|
||||||
|
|
||||||
|
*state = init_state;
|
||||||
|
gprs_codel_set_interval(state, -1);
|
||||||
|
gprs_codel_set_maxpacket(state, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms)
|
||||||
|
{
|
||||||
|
div_t q;
|
||||||
|
|
||||||
|
if (interval_ms <= 0)
|
||||||
|
interval_ms = GPRS_CODEL_DEFAULT_INTERVAL_MS;
|
||||||
|
|
||||||
|
q = div(interval_ms, 1000);
|
||||||
|
state->interval.tv_sec = q.quot;
|
||||||
|
state->interval.tv_nsec = q.rem * 1000000;
|
||||||
|
|
||||||
|
/* target ~ 5% of interval */
|
||||||
|
q = div(interval_ms * 13 / 256, 1000);
|
||||||
|
state->target.tv_sec = q.quot;
|
||||||
|
state->target.tv_nsec = q.rem * 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (maxpacket < 0)
|
||||||
|
maxpacket = GPRS_CODEL_DEFAULT_MAXPACKET;
|
||||||
|
|
||||||
|
state->maxpacket = maxpacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an broken up variant of the algorithm being described in
|
||||||
|
* http://queue.acm.org/appendices/codel.html
|
||||||
|
*/
|
||||||
|
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
|
||||||
|
const struct timespec *now, int bytes)
|
||||||
|
{
|
||||||
|
struct timespec sojourn_time;
|
||||||
|
struct timespec delta;
|
||||||
|
|
||||||
|
if (recv == NULL)
|
||||||
|
goto stop_dropping;
|
||||||
|
|
||||||
|
timespecsub(now, recv, &sojourn_time);
|
||||||
|
|
||||||
|
if (timespeccmp(&sojourn_time, &state->target, <))
|
||||||
|
goto stop_dropping;
|
||||||
|
|
||||||
|
if (bytes >= 0 && (unsigned)bytes <= state->maxpacket)
|
||||||
|
goto stop_dropping;
|
||||||
|
|
||||||
|
if (!timespecisset(&state->first_above_time)) {
|
||||||
|
timespecadd(now, &state->interval, &state->first_above_time);
|
||||||
|
goto not_ok_to_drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timespeccmp(now, &state->first_above_time, <))
|
||||||
|
goto not_ok_to_drop;
|
||||||
|
|
||||||
|
/* Ok to drop */
|
||||||
|
|
||||||
|
if (!state->dropping) {
|
||||||
|
int recently = 0;
|
||||||
|
int in_drop_cycle = 0;
|
||||||
|
if (timespecisset(&state->drop_next)) {
|
||||||
|
timespecsub(now, &state->drop_next, &delta);
|
||||||
|
in_drop_cycle = timespeccmp(&delta, &state->interval, <);
|
||||||
|
recently = in_drop_cycle;
|
||||||
|
}
|
||||||
|
if (!recently) {
|
||||||
|
timespecsub(now, &state->first_above_time, &delta);
|
||||||
|
recently = !timespeccmp(&delta, &state->interval, <);
|
||||||
|
};
|
||||||
|
if (!recently)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
state->dropping = 1;
|
||||||
|
|
||||||
|
if (in_drop_cycle && state->count > 2)
|
||||||
|
state->count -= 2;
|
||||||
|
else
|
||||||
|
state->count = 1;
|
||||||
|
|
||||||
|
state->drop_next = *now;
|
||||||
|
} else {
|
||||||
|
if (timespeccmp(now, &state->drop_next, <))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
state->count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
control_law(state, &delta);
|
||||||
|
timespecadd(&state->drop_next, &delta, &state->drop_next);
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
LOGP(DRLCMAC, LOGL_INFO,
|
||||||
|
"CoDel decided to drop packet, window = %d.%03dms, count = %d\n",
|
||||||
|
(int)delta.tv_sec, (int)(delta.tv_nsec / 1000000), state->count);
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
stop_dropping:
|
||||||
|
timespecclear(&state->first_above_time);
|
||||||
|
not_ok_to_drop:
|
||||||
|
state->dropping = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
108
src/gprs_codel.h
Normal file
108
src/gprs_codel.h
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/* gprs_codel.h
|
||||||
|
*
|
||||||
|
* This is an implementation of the CoDel algorithm based on the reference
|
||||||
|
* pseudocode (see http://queue.acm.org/appendices/codel.html).
|
||||||
|
* Instead of abstracting the queue itself, the following implementation
|
||||||
|
* provides a time stamp based automaton. The main work is done by a single
|
||||||
|
* decision function which updates the state and tells whether to pass or to
|
||||||
|
* drop a packet after it has been taken from the queue.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||||
|
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* Spec default values */
|
||||||
|
#define GPRS_CODEL_DEFAULT_INTERVAL_MS 100
|
||||||
|
#define GPRS_CODEL_DEFAULT_MAXPACKET 512
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct gprs_codel {
|
||||||
|
int dropping;
|
||||||
|
unsigned count;
|
||||||
|
struct timespec first_above_time;
|
||||||
|
struct timespec drop_next;
|
||||||
|
struct timespec target;
|
||||||
|
struct timespec interval;
|
||||||
|
unsigned maxpacket;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Decide about packet drop and update CoDel state
|
||||||
|
*
|
||||||
|
* This function takes timing information and decides whether the packet in
|
||||||
|
* question should be dropped in order to keep related queue in a 'good' state.
|
||||||
|
* The function is meant to be called when the packet is dequeued.
|
||||||
|
*
|
||||||
|
* The CoDel state is updated by this function.
|
||||||
|
*
|
||||||
|
* \param state A pointer to the CoDel state of this queue
|
||||||
|
* \param recv The time when the packet has entered the queue,
|
||||||
|
* use NULL if dequeueing was not possible because the queue is
|
||||||
|
* empty
|
||||||
|
* \param now The current (dequeueing) time
|
||||||
|
* \param bytes The number of bytes currently stored in the queue (-1 if
|
||||||
|
* unknown)
|
||||||
|
*
|
||||||
|
* \return != 0 if the packet should be dropped, 0 otherwise
|
||||||
|
*/
|
||||||
|
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
|
||||||
|
const struct timespec *now, int bytes);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Initialise CoDel state
|
||||||
|
*
|
||||||
|
* This function initialises the CoDel state object. It sets the interval time
|
||||||
|
* to the default value (GPRS_CODEL_DEFAULT_INTERVAL_MS).
|
||||||
|
*
|
||||||
|
* \param state A pointer to the CoDel state of this queue
|
||||||
|
*/
|
||||||
|
void gprs_codel_init(struct gprs_codel *state);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Set interval time
|
||||||
|
*
|
||||||
|
* This function changes the interval time.
|
||||||
|
* The target time is derived from the interval time as proposed in the spec
|
||||||
|
* (5% of interval time).
|
||||||
|
*
|
||||||
|
* \param state A pointer to the CoDel state of this queue
|
||||||
|
* \param interval_ms The initial interval in ms to be used (<= 0 selects the
|
||||||
|
* default value)
|
||||||
|
*/
|
||||||
|
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Set max packet size
|
||||||
|
*
|
||||||
|
* This function changes the maxpacket value. If no more than this number of
|
||||||
|
* bytes are still stored in the queue, no dropping will be done.
|
||||||
|
*
|
||||||
|
* \param state A pointer to the CoDel state of this queue
|
||||||
|
* \param maxpacket The value in bytes
|
||||||
|
*/
|
||||||
|
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/* gprs_debug.cpp
|
/* gprs_debug.cpp
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -17,55 +18,144 @@
|
|||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdarg.h>
|
extern "C" {
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/utils.h>
|
#include <osmocom/core/utils.h>
|
||||||
#include <osmocom/core/logging.h>
|
#include <osmocom/core/logging.h>
|
||||||
|
}
|
||||||
|
|
||||||
#include <gprs_debug.h>
|
#include <gprs_debug.h>
|
||||||
|
|
||||||
/* default categories */
|
/* default categories */
|
||||||
|
|
||||||
static const struct log_info_cat default_categories[] = {
|
static const struct log_info_cat default_categories[] = {
|
||||||
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
|
[DCSN1] = {
|
||||||
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_INFO, 1},
|
.name = "DCSN1",
|
||||||
{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
|
.color = "\033[1;31m",
|
||||||
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
|
.description = "Concrete Syntax Notation One (CSN1)",
|
||||||
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
|
.loglevel = LOGL_NOTICE,
|
||||||
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
|
.enabled = 0,
|
||||||
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
|
},
|
||||||
{"DRLCMACBW", "\033[1;31m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
|
[DL1IF] = {
|
||||||
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
|
.name = "DL1IF",
|
||||||
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
|
.color = "\033[1;32m",
|
||||||
};
|
.description = "GPRS PCU L1 interface (L1IF)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
enum {
|
.enabled = 1,
|
||||||
_FLT_ALL = LOG_FILTER_ALL, /* libosmocore */
|
},
|
||||||
FLT_IMSI = 1,
|
[DRLCMAC] = {
|
||||||
FLT_NSVC = 2,
|
.name = "DRLCMAC",
|
||||||
FLT_BVC = 3,
|
.color = "\033[0;33m",
|
||||||
|
.description = "GPRS RLC/MAC layer (RLCMAC)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DRLCMACDATA] = {
|
||||||
|
.name = "DRLCMACDATA",
|
||||||
|
.color = "\033[0;33m",
|
||||||
|
.description = "GPRS RLC/MAC layer Data (RLCMAC)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DRLCMACDL] = {
|
||||||
|
.name = "DRLCMACDL",
|
||||||
|
.color = "\033[1;33m",
|
||||||
|
.description = "GPRS RLC/MAC layer Downlink (RLCMAC)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DRLCMACUL] = {
|
||||||
|
.name = "DRLCMACUL",
|
||||||
|
.color = "\033[1;36m",
|
||||||
|
.description = "GPRS RLC/MAC layer Uplink (RLCMAC)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DRLCMACSCHED] = {
|
||||||
|
.name = "DRLCMACSCHED",
|
||||||
|
.color = "\033[0;36m",
|
||||||
|
.description = "GPRS RLC/MAC layer Scheduling (RLCMAC)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DRLCMACMEAS] = {
|
||||||
|
.name = "DRLCMACMEAS",
|
||||||
|
.color = "\033[1;31m",
|
||||||
|
.description = "GPRS RLC/MAC layer Measurements (RLCMAC)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DTBF] = {
|
||||||
|
.name = "DTBF",
|
||||||
|
.color = "\033[1;34m",
|
||||||
|
.description = "Temporary Block Flow (TBF)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DTBFDL] = {
|
||||||
|
.name = "DTBFDL",
|
||||||
|
.color = "\033[1;34m",
|
||||||
|
.description = "Temporary Block Flow (TBF) Downlink",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DTBFUL] = {
|
||||||
|
.name = "DTBFUL",
|
||||||
|
.color = "\033[1;34m",
|
||||||
|
.description = "Temporary Block Flow (TBF) Uplink",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DNS] = {
|
||||||
|
.name = "DNS",
|
||||||
|
.color = "\033[1;34m",
|
||||||
|
.description = "GPRS Network Service Protocol (NS)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DPCU] = {
|
||||||
|
.name = "DPCU",
|
||||||
|
.color = "\033[1;35m",
|
||||||
|
.description = "GPRS Packet Control Unit (PCU)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DNACC] = {
|
||||||
|
.name = "DNACC",
|
||||||
|
.color = "\033[1;37m",
|
||||||
|
.description = "Network Assisted Cell Change (NACC)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DRIM] = {
|
||||||
|
.name = "DRIM",
|
||||||
|
.color = "\033[1;38m",
|
||||||
|
.description = "RAN Information Management (RIM)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DANR] = {
|
||||||
|
.name = "DANR",
|
||||||
|
.color = "\033[1;37m",
|
||||||
|
.description = "Automatic Neighbor Registration (ANR)",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int filter_fn(const struct log_context *ctx,
|
static int filter_fn(const struct log_context *ctx,
|
||||||
struct log_target *tar)
|
struct log_target *tar)
|
||||||
{
|
{
|
||||||
const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_NSVC];
|
const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_NSVC];
|
||||||
const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_BVC];
|
const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_BVC];
|
||||||
|
|
||||||
/* Filter on the NS Virtual Connection */
|
/* Filter on the NS Virtual Connection */
|
||||||
if ((tar->filter_map & (1 << FLT_NSVC)) != 0
|
if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
|
||||||
&& nsvc && (nsvc == tar->filter_data[FLT_NSVC]))
|
&& nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Filter on the BVC */
|
/* Filter on the BVC */
|
||||||
if ((tar->filter_map & (1 << FLT_BVC)) != 0
|
if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
|
||||||
&& bvc && (bvc == tar->filter_data[FLT_BVC]))
|
&& bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -17,14 +17,20 @@
|
|||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef GPRS_DEBUG_H
|
#pragma once
|
||||||
#define GPRS_DEBUG_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <osmocom/core/linuxlist.h>
|
#endif
|
||||||
#include <osmocom/core/logging.h>
|
#include <osmocom/core/logging.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* we used to have DBSSGP definded in each application, and applications telling
|
||||||
|
* libosmogb which sub-system to use. That creates problems and has been deprecated */
|
||||||
|
#define DBSSGP DLBSSGP
|
||||||
|
|
||||||
/* Debug Areas of the code */
|
/* Debug Areas of the code */
|
||||||
enum {
|
enum {
|
||||||
DCSN1,
|
DCSN1,
|
||||||
@@ -34,39 +40,16 @@ enum {
|
|||||||
DRLCMACDL,
|
DRLCMACDL,
|
||||||
DRLCMACUL,
|
DRLCMACUL,
|
||||||
DRLCMACSCHED,
|
DRLCMACSCHED,
|
||||||
DRLCMACBW,
|
DRLCMACMEAS,
|
||||||
DBSSGP,
|
DTBF,
|
||||||
|
DTBFDL,
|
||||||
|
DTBFUL,
|
||||||
|
DNS,
|
||||||
DPCU,
|
DPCU,
|
||||||
|
DNACC,
|
||||||
|
DRIM,
|
||||||
|
DANR,
|
||||||
aDebug_LastEntry
|
aDebug_LastEntry
|
||||||
};
|
};
|
||||||
|
|
||||||
/* context */
|
|
||||||
#define BSC_CTX_SUBSCR 1
|
|
||||||
#define BSC_CTX_NSVC 4
|
|
||||||
#define BSC_CTX_BVC 5
|
|
||||||
|
|
||||||
/* target */
|
|
||||||
|
|
||||||
enum {
|
|
||||||
//DEBUG_FILTER_ALL = 1 << 0,
|
|
||||||
LOG_FILTER_IMSI = 1 << 1,
|
|
||||||
LOG_FILTER_NSVC = 1 << 2,
|
|
||||||
LOG_FILTER_BVC = 1 << 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* we don't need a header dependency for this... */
|
|
||||||
|
|
||||||
struct gprs_nsvc;
|
|
||||||
struct bssgp_bvc_ctx;
|
|
||||||
|
|
||||||
void log_set_imsi_filter(struct log_target *target, const char *imsi);
|
|
||||||
void log_set_nsvc_filter(struct log_target *target,
|
|
||||||
struct gprs_nsvc *nsvc);
|
|
||||||
void log_set_bvc_filter(struct log_target *target,
|
|
||||||
struct bssgp_bvc_ctx *bctx);
|
|
||||||
|
|
||||||
extern const struct log_info gprs_log_info;
|
extern const struct log_info gprs_log_info;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // GPRS_DEBUG_H
|
|
||||||
|
|||||||
1040
src/gprs_ms.c
Normal file
1040
src/gprs_ms.c
Normal file
File diff suppressed because it is too large
Load Diff
265
src/gprs_ms.h
Normal file
265
src/gprs_ms.h
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
/* gprs_ms.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2020 by Sysmocom s.f.m.c. GmbH
|
||||||
|
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct gprs_codel;
|
||||||
|
|
||||||
|
#include "llc.h"
|
||||||
|
#include "tbf.h"
|
||||||
|
#include "tbf_ul.h"
|
||||||
|
#include "tbf_dl.h"
|
||||||
|
#include "pcu_l1_if.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <osmocom/core/timer.h>
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
|
|
||||||
|
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||||
|
#include <osmocom/gsm/gsm48.h>
|
||||||
|
|
||||||
|
#include "coding_scheme.h"
|
||||||
|
#include <gsm_rlcmac.h>
|
||||||
|
#include "pcu_utils.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
enum ms_counter_id {
|
||||||
|
MS_CTR_DL_CTRL_MSG_SCHED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts;
|
||||||
|
struct gprs_rlcmac_trx;
|
||||||
|
struct GprsMs;
|
||||||
|
|
||||||
|
struct gpr_ms_callback {
|
||||||
|
void (*ms_idle)(struct GprsMs *);
|
||||||
|
void (*ms_active)(struct GprsMs *);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GprsMs {
|
||||||
|
struct llist_head list; /* list of all GprsMs */
|
||||||
|
struct gpr_ms_callback cb;
|
||||||
|
bool app_info_pending;
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
struct gprs_rlcmac_ul_tbf *ul_tbf;
|
||||||
|
struct gprs_rlcmac_dl_tbf *dl_tbf;
|
||||||
|
struct llist_head old_tbfs; /* list of gprs_rlcmac_tbf */
|
||||||
|
|
||||||
|
uint32_t tlli;
|
||||||
|
uint32_t new_ul_tlli;
|
||||||
|
uint32_t new_dl_tlli;
|
||||||
|
|
||||||
|
/* store IMSI for look-up and PCH retransmission */
|
||||||
|
char imsi[OSMO_IMSI_BUF_SIZE];
|
||||||
|
uint8_t ta;
|
||||||
|
uint8_t ms_class;
|
||||||
|
uint8_t egprs_ms_class;
|
||||||
|
/* current coding scheme */
|
||||||
|
enum CodingScheme current_cs_ul;
|
||||||
|
enum CodingScheme current_cs_dl;
|
||||||
|
|
||||||
|
struct gprs_llc_queue llc_queue;
|
||||||
|
|
||||||
|
bool is_idle;
|
||||||
|
int ref;
|
||||||
|
struct osmo_timer_list timer;
|
||||||
|
unsigned delay;
|
||||||
|
|
||||||
|
int64_t last_cs_not_low;
|
||||||
|
|
||||||
|
struct pcu_l1_meas l1_meas;
|
||||||
|
unsigned nack_rate_dl;
|
||||||
|
uint8_t reserved_dl_slots;
|
||||||
|
uint8_t reserved_ul_slots;
|
||||||
|
struct gprs_rlcmac_trx *current_trx;
|
||||||
|
|
||||||
|
struct gprs_codel *codel_state;
|
||||||
|
enum mcs_kind mode;
|
||||||
|
|
||||||
|
struct rate_ctr_group *ctrs;
|
||||||
|
struct nacc_fsm_ctx *nacc;
|
||||||
|
struct ms_anr_fsm_ctx *anr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli);
|
||||||
|
|
||||||
|
int ms_first_common_ts(const struct GprsMs *ms);
|
||||||
|
void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
|
||||||
|
uint8_t ul_slots, uint8_t dl_slots);
|
||||||
|
struct GprsMs *ms_ref(struct GprsMs *ms);
|
||||||
|
void ms_unref(struct GprsMs *ms);
|
||||||
|
void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode);
|
||||||
|
void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_);
|
||||||
|
void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_);
|
||||||
|
void ms_set_ta(struct GprsMs *ms, uint8_t ta_);
|
||||||
|
|
||||||
|
enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind);
|
||||||
|
enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms);
|
||||||
|
enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms);
|
||||||
|
void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme);
|
||||||
|
|
||||||
|
void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate);
|
||||||
|
uint8_t ms_current_pacch_slots(const struct GprsMs *ms);
|
||||||
|
|
||||||
|
void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms);
|
||||||
|
|
||||||
|
void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
|
||||||
|
void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
|
||||||
|
|
||||||
|
void ms_set_tlli(struct GprsMs *ms, uint32_t tlli);
|
||||||
|
bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli);
|
||||||
|
void ms_set_imsi(struct GprsMs *ms, const char *imsi);
|
||||||
|
|
||||||
|
void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas);
|
||||||
|
|
||||||
|
struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir);
|
||||||
|
static inline struct gprs_rlcmac_ul_tbf *ms_ul_tbf(const struct GprsMs *ms) {return ms->ul_tbf;}
|
||||||
|
static inline struct gprs_rlcmac_dl_tbf *ms_dl_tbf(const struct GprsMs *ms) {return ms->dl_tbf;}
|
||||||
|
|
||||||
|
|
||||||
|
void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb);
|
||||||
|
|
||||||
|
int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif);
|
||||||
|
bool ms_nacc_rts(const struct GprsMs *ms);
|
||||||
|
struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
|
||||||
|
|
||||||
|
int ms_anr_start(struct GprsMs *ms, const struct arfcn_bsic* cell_list, unsigned int num_cells);
|
||||||
|
bool ms_anr_rts(const struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
|
||||||
|
struct msgb *ms_anr_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
|
||||||
|
bool ms_anr_tbf_keep_open(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf);
|
||||||
|
|
||||||
|
static inline bool ms_is_idle(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return !ms->ul_tbf && !ms->dl_tbf && !ms->ref && llist_empty(&ms->old_tbfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return &ms->llc_queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool ms_need_dl_tbf(struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
if (ms_dl_tbf(ms) != NULL &&
|
||||||
|
tbf_state((const struct gprs_rlcmac_tbf *)ms_dl_tbf(ms)) != TBF_ST_WAIT_RELEASE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return llc_queue_size(ms_llc_queue(ms)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t ms_tlli(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
if (ms->new_ul_tlli != GSM_RESERVED_TMSI)
|
||||||
|
return ms->new_ul_tlli;
|
||||||
|
if (ms->tlli != GSM_RESERVED_TMSI)
|
||||||
|
return ms->tlli;
|
||||||
|
|
||||||
|
return ms->new_dl_tlli;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool ms_check_tlli(struct GprsMs *ms, uint32_t tlli)
|
||||||
|
{
|
||||||
|
return tlli != GSM_RESERVED_TMSI &&
|
||||||
|
(tlli == ms->tlli || tlli == ms->new_ul_tlli || tlli == ms->new_dl_tlli);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *ms_imsi(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->imsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool ms_imsi_valid(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->imsi[0] != '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t ms_ta(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->ta;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t ms_ms_class(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->ms_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t ms_egprs_ms_class(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->egprs_ms_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum CodingScheme ms_current_cs_ul(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->current_cs_ul;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum mcs_kind ms_mode(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ms_set_timeout(struct GprsMs *ms, unsigned secs)
|
||||||
|
{
|
||||||
|
ms->delay = secs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct gprs_codel *ms_codel_state(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->codel_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned ms_nack_rate_dl(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->nack_rate_dl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t ms_reserved_dl_slots(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->reserved_dl_slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t ms_reserved_ul_slots(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->reserved_ul_slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct gprs_rlcmac_trx *ms_current_trx(const struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
return ms->current_trx;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOGPMS(ms, category, level, fmt, args...) \
|
||||||
|
LOGP(category, level, "MS(TLLI=0x%08x, IMSI=%s, TA=%" PRIu8 ", %" PRIu8 "/%" PRIu8 ",%s%s) " fmt, \
|
||||||
|
ms_tlli(ms), ms_imsi(ms), ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms), \
|
||||||
|
ms_ul_tbf(ms) ? " UL": "", \
|
||||||
|
ms_dl_tbf(ms) ? " DL": "", \
|
||||||
|
## args)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
115
src/gprs_ms_storage.cpp
Normal file
115
src/gprs_ms_storage.cpp
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/* gprs_ms_storage.cpp
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||||
|
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "gprs_ms_storage.h"
|
||||||
|
|
||||||
|
#include "tbf.h"
|
||||||
|
#include "bts.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
#include <osmocom/gsm/gsm48.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GPRS_UNDEFINED_IMSI "000"
|
||||||
|
|
||||||
|
static void ms_storage_ms_idle_cb(struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
llist_del(&ms->list);
|
||||||
|
if (ms->bts)
|
||||||
|
bts_stat_item_add(ms->bts, STAT_MS_PRESENT, -1);
|
||||||
|
if (ms_is_idle(ms))
|
||||||
|
talloc_free(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ms_storage_ms_active_cb(struct GprsMs *ms)
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gpr_ms_callback ms_storage_ms_cb = {
|
||||||
|
.ms_idle = ms_storage_ms_idle_cb,
|
||||||
|
.ms_active = ms_storage_ms_active_cb,
|
||||||
|
};
|
||||||
|
|
||||||
|
GprsMsStorage::GprsMsStorage(struct gprs_rlcmac_bts *bts) :
|
||||||
|
m_bts(bts)
|
||||||
|
{
|
||||||
|
INIT_LLIST_HEAD(&m_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
GprsMsStorage::~GprsMsStorage()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GprsMsStorage::cleanup()
|
||||||
|
{
|
||||||
|
struct llist_head *pos, *tmp;
|
||||||
|
|
||||||
|
llist_for_each_safe(pos, tmp, &m_list) {
|
||||||
|
struct GprsMs *ms = llist_entry(pos, typeof(*ms), list);
|
||||||
|
ms_set_callback(ms, NULL);
|
||||||
|
ms_storage_ms_idle_cb(ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi) const
|
||||||
|
{
|
||||||
|
struct llist_head *tmp;
|
||||||
|
GprsMs *ms;
|
||||||
|
|
||||||
|
if (tlli != GSM_RESERVED_TMSI || old_tlli != GSM_RESERVED_TMSI) {
|
||||||
|
llist_for_each(tmp, &m_list) {
|
||||||
|
ms = llist_entry(tmp, typeof(*ms), list);
|
||||||
|
if (ms_check_tlli(ms, tlli))
|
||||||
|
return ms;
|
||||||
|
if (ms_check_tlli(ms, old_tlli))
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not found by TLLI */
|
||||||
|
|
||||||
|
if (imsi && imsi[0] && strcmp(imsi, GPRS_UNDEFINED_IMSI) != 0) {
|
||||||
|
llist_for_each(tmp, &m_list) {
|
||||||
|
ms = llist_entry(tmp, typeof(*ms), list);
|
||||||
|
if (strcmp(imsi, ms_imsi(ms)) == 0)
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GprsMs *GprsMsStorage::create_ms()
|
||||||
|
{
|
||||||
|
GprsMs *ms;
|
||||||
|
|
||||||
|
ms = ms_alloc(m_bts, GSM_RESERVED_TMSI);
|
||||||
|
|
||||||
|
ms_set_callback(ms, &ms_storage_ms_cb);
|
||||||
|
llist_add(&ms->list, &m_list);
|
||||||
|
if (m_bts)
|
||||||
|
bts_stat_item_add(m_bts, STAT_MS_PRESENT, 1);
|
||||||
|
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
44
src/gprs_ms_storage.h
Normal file
44
src/gprs_ms_storage.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/* gprs_ms_storage.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||||
|
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gprs_ms.h"
|
||||||
|
#include "tbf.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts;
|
||||||
|
|
||||||
|
struct GprsMsStorage {
|
||||||
|
public:
|
||||||
|
GprsMsStorage(struct gprs_rlcmac_bts *bts);
|
||||||
|
~GprsMsStorage();
|
||||||
|
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = GSM_RESERVED_TMSI, const char *imsi = NULL) const;
|
||||||
|
GprsMs *create_ms();
|
||||||
|
|
||||||
|
const struct llist_head* ms_list() const {return &m_list;}
|
||||||
|
private:
|
||||||
|
struct gprs_rlcmac_bts *m_bts;
|
||||||
|
struct llist_head m_list; /* list of struct GprsMs */
|
||||||
|
};
|
||||||
196
src/gprs_pcu.c
Normal file
196
src/gprs_pcu.c
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
#include <osmocom/ctrl/ports.h>
|
||||||
|
|
||||||
|
#include "gprs_pcu.h"
|
||||||
|
#include "bts.h"
|
||||||
|
|
||||||
|
struct gprs_pcu *the_pcu;
|
||||||
|
|
||||||
|
static struct osmo_tdef T_defs_pcu[] = {
|
||||||
|
{ .T=3190, .default_val=5, .unit=OSMO_TDEF_S, .desc="Return to packet idle mode after Packet DL Assignment on CCCH (s)", .val=0},
|
||||||
|
{ .T=3141, .default_val=10, .unit=OSMO_TDEF_S, .desc="Timeout for contention resolution procedure (s)", .val=0 },
|
||||||
|
{ .T=PCU_TDEF_NEIGH_RESOLVE_TO, .default_val=1000, .unit=OSMO_TDEF_MS, .desc="[ARFCN+BSIC]->[RAC+CI] resolution timeout (ms)", .val=0 },
|
||||||
|
{ .T=PCU_TDEF_SI_RESOLVE_TO, .default_val=1000, .unit=OSMO_TDEF_MS, .desc="RIM RAN-INFO response timeout (ms)", .val=0 },
|
||||||
|
{ .T=PCU_TDEF_NEIGH_CACHE_ALIVE, .default_val=5, .unit=OSMO_TDEF_S, .desc="[ARFCN+BSIC]->[RAC+CI] resolution cache entry storage timeout (s)", .val=0 },
|
||||||
|
{ .T=PCU_TDEF_SI_CACHE_ALIVE, .default_val=5, .unit=OSMO_TDEF_S, .desc="[RAC+CI]->[SI] resolution cache entry storage timeout (s)", .val=0 },
|
||||||
|
{ .T=PCU_TDEF_ANR_SCHED_TBF, .default_val=10, .unit=OSMO_TDEF_S, .desc="[ANR] Frequency to schedule a new Measurement Report on any MS (s)", .val=0 },
|
||||||
|
{ .T=PCU_TDEF_ANR_MS_TIMEOUT, .default_val=60, .unit=OSMO_TDEF_S, .desc="[ANR] Time to wait for Packet Measurement Report (s)", .val=0 },
|
||||||
|
{ .T=-101, .default_val=30, .unit=OSMO_TDEF_S, .desc="BSSGP (un)blocking procedures timer (s)", .val=0 },
|
||||||
|
{ .T=-102, .default_val=30, .unit=OSMO_TDEF_S, .desc="BSSGP reset procedure timer (s)", .val=0 },
|
||||||
|
{ .T=-2000, .default_val=2, .unit=OSMO_TDEF_MS, .desc="Delay release of UL TBF after tx Packet Access Reject (PACCH) (ms)", .val=0 },
|
||||||
|
{ .T=-2001, .default_val=2, .unit=OSMO_TDEF_S, .desc="PACCH assignment timeout (s)", .val=0 },
|
||||||
|
{ .T=-2002, .default_val=200, .unit=OSMO_TDEF_MS, .desc="Waiting after IMM.ASS confirm timer (ms)", .val=0 },
|
||||||
|
{ .T=-2030, .default_val=60, .unit=OSMO_TDEF_S, .desc="Time to keep an idle MS object alive (s)", .val=0 }, /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
|
||||||
|
{ .T=-2031, .default_val=2000, .unit=OSMO_TDEF_MS, .desc="Time to keep an idle DL TBF alive (ms)", .val=0 },
|
||||||
|
{ .T=0, .default_val=0, .unit=OSMO_TDEF_S, .desc=NULL, .val=0 } /* empty item at the end */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gprs_pcu_talloc_destructor(struct gprs_pcu *pcu)
|
||||||
|
{
|
||||||
|
neigh_cache_free(pcu->neigh_cache);
|
||||||
|
si_cache_free(pcu->si_cache);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gprs_pcu *gprs_pcu_alloc(void *ctx)
|
||||||
|
{
|
||||||
|
struct gprs_pcu *pcu;
|
||||||
|
|
||||||
|
pcu = (struct gprs_pcu *)talloc_zero(ctx, struct gprs_pcu);
|
||||||
|
OSMO_ASSERT(pcu);
|
||||||
|
talloc_set_destructor(pcu, gprs_pcu_talloc_destructor);
|
||||||
|
|
||||||
|
pcu->vty.fc_interval = 1;
|
||||||
|
pcu->vty.max_cs_ul = MAX_GPRS_CS;
|
||||||
|
pcu->vty.max_cs_dl = MAX_GPRS_CS;
|
||||||
|
pcu->vty.max_mcs_ul = MAX_EDGE_MCS;
|
||||||
|
pcu->vty.max_mcs_dl = MAX_EDGE_MCS;
|
||||||
|
pcu->vty.force_alpha = (uint8_t)-1; /* don't force by default, use BTS SI13 provided value */
|
||||||
|
pcu->vty.dl_tbf_preemptive_retransmission = true;
|
||||||
|
/* By default resegmentation is supported in DL can also be configured
|
||||||
|
* through VTY */
|
||||||
|
pcu->vty.dl_arq_type = EGPRS_ARQ1;
|
||||||
|
pcu->vty.cs_adj_enabled = true;
|
||||||
|
pcu->vty.cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */
|
||||||
|
pcu->vty.cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
|
||||||
|
pcu->vty.cs_downgrade_threshold = 200;
|
||||||
|
/* CS-1 to CS-4 */
|
||||||
|
pcu->vty.cs_lqual_ranges[0].low = -256;
|
||||||
|
pcu->vty.cs_lqual_ranges[0].high = 6;
|
||||||
|
pcu->vty.cs_lqual_ranges[1].low = 5;
|
||||||
|
pcu->vty.cs_lqual_ranges[1].high = 8;
|
||||||
|
pcu->vty.cs_lqual_ranges[2].low = 7;
|
||||||
|
pcu->vty.cs_lqual_ranges[2].high = 13;
|
||||||
|
pcu->vty.cs_lqual_ranges[3].low = 12;
|
||||||
|
pcu->vty.cs_lqual_ranges[3].high = 256;
|
||||||
|
/* MCS-1 to MCS-9 */
|
||||||
|
/* Default thresholds are referenced from literature */
|
||||||
|
/* Fig. 2.3, Chapter 2, Optimizing Wireless Communication Systems, Springer (2009) */
|
||||||
|
pcu->vty.mcs_lqual_ranges[0].low = -256;
|
||||||
|
pcu->vty.mcs_lqual_ranges[0].high = 6;
|
||||||
|
pcu->vty.mcs_lqual_ranges[1].low = 5;
|
||||||
|
pcu->vty.mcs_lqual_ranges[1].high = 8;
|
||||||
|
pcu->vty.mcs_lqual_ranges[2].low = 7;
|
||||||
|
pcu->vty.mcs_lqual_ranges[2].high = 13;
|
||||||
|
pcu->vty.mcs_lqual_ranges[3].low = 12;
|
||||||
|
pcu->vty.mcs_lqual_ranges[3].high = 15;
|
||||||
|
pcu->vty.mcs_lqual_ranges[4].low = 14;
|
||||||
|
pcu->vty.mcs_lqual_ranges[4].high = 17;
|
||||||
|
pcu->vty.mcs_lqual_ranges[5].low = 16;
|
||||||
|
pcu->vty.mcs_lqual_ranges[5].high = 18;
|
||||||
|
pcu->vty.mcs_lqual_ranges[6].low = 17;
|
||||||
|
pcu->vty.mcs_lqual_ranges[6].high = 20;
|
||||||
|
pcu->vty.mcs_lqual_ranges[7].low = 19;
|
||||||
|
pcu->vty.mcs_lqual_ranges[7].high = 24;
|
||||||
|
pcu->vty.mcs_lqual_ranges[8].low = 23;
|
||||||
|
pcu->vty.mcs_lqual_ranges[8].high = 256;
|
||||||
|
pcu->vty.ns_dialect = GPRS_NS2_DIALECT_IPACCESS;
|
||||||
|
pcu->vty.ns_ip_dscp = -1;
|
||||||
|
pcu->vty.ns_priority = -1;
|
||||||
|
/* TODO: increase them when CRBB decoding is implemented */
|
||||||
|
pcu->vty.ws_base = 64;
|
||||||
|
pcu->vty.ws_pdch = 0;
|
||||||
|
pcu->vty.llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
|
||||||
|
pcu->vty.llc_idle_ack_csec = 10;
|
||||||
|
pcu->vty.neigh_ctrl_addr = talloc_strdup(pcu, "127.0.0.1");
|
||||||
|
pcu->vty.neigh_ctrl_port = OSMO_CTRL_PORT_BSC_NEIGH;
|
||||||
|
|
||||||
|
pcu->T_defs = T_defs_pcu;
|
||||||
|
osmo_tdefs_reset(pcu->T_defs);
|
||||||
|
|
||||||
|
INIT_LLIST_HEAD(&pcu->bts_list);
|
||||||
|
|
||||||
|
pcu->neigh_cache = neigh_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_NEIGH_CACHE_ALIVE, OSMO_TDEF_S, -1));
|
||||||
|
pcu->si_cache = si_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_SI_CACHE_ALIVE, OSMO_TDEF_S, -1));
|
||||||
|
|
||||||
|
return pcu;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts_nr)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *pos;
|
||||||
|
llist_for_each_entry(pos, &pcu->bts_list, list) {
|
||||||
|
if (pos->nr == bts_nr)
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *pos;
|
||||||
|
llist_for_each_entry(pos, &pcu->bts_list, list) {
|
||||||
|
if (osmo_cgi_ps_cmp(&pos->cgi_ps, cgi_ps) == 0)
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
|
||||||
|
the_pcu->vty.initial_cs_dl = cs_dl;
|
||||||
|
the_pcu->vty.initial_cs_ul = cs_ul;
|
||||||
|
|
||||||
|
llist_for_each_entry(bts, &pcu->bts_list, list) {
|
||||||
|
bts_recalc_initial_cs(bts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
|
||||||
|
the_pcu->vty.initial_mcs_dl = mcs_dl;
|
||||||
|
the_pcu->vty.initial_mcs_ul = mcs_ul;
|
||||||
|
|
||||||
|
llist_for_each_entry(bts, &pcu->bts_list, list) {
|
||||||
|
bts_recalc_initial_mcs(bts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_pcu_set_max_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
|
||||||
|
the_pcu->vty.max_cs_dl = cs_dl;
|
||||||
|
the_pcu->vty.max_cs_ul = cs_ul;
|
||||||
|
|
||||||
|
llist_for_each_entry(bts, &pcu->bts_list, list) {
|
||||||
|
bts_recalc_max_cs(bts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void gprs_pcu_set_max_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts;
|
||||||
|
|
||||||
|
the_pcu->vty.max_mcs_dl = mcs_dl;
|
||||||
|
the_pcu->vty.max_mcs_ul = mcs_ul;
|
||||||
|
|
||||||
|
llist_for_each_entry(bts, &pcu->bts_list, list) {
|
||||||
|
bts_recalc_max_mcs(bts);
|
||||||
|
}
|
||||||
|
}
|
||||||
148
src/gprs_pcu.h
Normal file
148
src/gprs_pcu.h
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/gsmtap_util.h>
|
||||||
|
|
||||||
|
#include "gprs_bssgp_pcu.h"
|
||||||
|
#include "coding_scheme.h"
|
||||||
|
|
||||||
|
#include "neigh_cache.h"
|
||||||
|
|
||||||
|
#define LLC_CODEL_DISABLE 0
|
||||||
|
#define LLC_CODEL_USE_DEFAULT (-1)
|
||||||
|
|
||||||
|
#define MAX_EDGE_MCS 9
|
||||||
|
#define MAX_GPRS_CS 4
|
||||||
|
|
||||||
|
#define PCU_TDEF_NEIGH_RESOLVE_TO (-1)
|
||||||
|
#define PCU_TDEF_SI_RESOLVE_TO (-2)
|
||||||
|
#define PCU_TDEF_NEIGH_CACHE_ALIVE (-10)
|
||||||
|
#define PCU_TDEF_SI_CACHE_ALIVE (-11)
|
||||||
|
#define PCU_TDEF_ANR_SCHED_TBF (-20)
|
||||||
|
#define PCU_TDEF_ANR_MS_TIMEOUT (-21)
|
||||||
|
|
||||||
|
/* see bts->gsmtap_categ_mask */
|
||||||
|
enum pcu_gsmtap_category {
|
||||||
|
PCU_GSMTAP_C_DL_UNKNOWN = 0, /* unknown or undecodable downlink blocks */
|
||||||
|
PCU_GSMTAP_C_DL_DUMMY = 1, /* downlink dummy blocks */
|
||||||
|
PCU_GSMTAP_C_DL_CTRL = 2, /* downlink control blocks */
|
||||||
|
PCU_GSMTAP_C_DL_DATA_GPRS = 3, /* downlink GPRS data blocks */
|
||||||
|
PCU_GSMTAP_C_DL_DATA_EGPRS = 4, /* downlink EGPRS data blocks */
|
||||||
|
PCU_GSMTAP_C_DL_PTCCH = 5, /* downlink PTCCH blocks */
|
||||||
|
PCU_GSMTAP_C_DL_AGCH = 6, /* downlink AGCH blocks */
|
||||||
|
PCU_GSMTAP_C_DL_PCH = 7, /* downlink PCH blocks */
|
||||||
|
|
||||||
|
PCU_GSMTAP_C_UL_UNKNOWN = 15, /* unknown or undecodable uplink blocks */
|
||||||
|
PCU_GSMTAP_C_UL_DUMMY = 16, /* uplink dummy blocks */
|
||||||
|
PCU_GSMTAP_C_UL_CTRL = 17, /* uplink control blocks */
|
||||||
|
PCU_GSMTAP_C_UL_DATA_GPRS = 18, /* uplink GPRS data blocks */
|
||||||
|
PCU_GSMTAP_C_UL_DATA_EGPRS = 19, /* uplink EGPRS data blocks */
|
||||||
|
PCU_GSMTAP_C_UL_RACH = 20, /* uplink RACH bursts */
|
||||||
|
PCU_GSMTAP_C_UL_PTCCH = 21, /* uplink PTCCH bursts */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts;
|
||||||
|
struct GprsMs;
|
||||||
|
struct gprs_rlcmac_tbf;
|
||||||
|
|
||||||
|
typedef int (*alloc_algorithm_func_t)(struct gprs_rlcmac_bts *bts,
|
||||||
|
struct gprs_rlcmac_tbf *tbf,
|
||||||
|
bool single, int8_t use_tbf);
|
||||||
|
|
||||||
|
struct gprs_pcu {
|
||||||
|
|
||||||
|
/* Path to be used for the pcu-bts socket */
|
||||||
|
char *pcu_sock_path;
|
||||||
|
|
||||||
|
struct { /* Config Values set by VTY */
|
||||||
|
uint8_t fc_interval;
|
||||||
|
uint16_t fc_bucket_time;
|
||||||
|
uint32_t fc_bvc_bucket_size;
|
||||||
|
uint32_t fc_bvc_leak_rate;
|
||||||
|
uint32_t fc_ms_bucket_size;
|
||||||
|
uint32_t fc_ms_leak_rate;
|
||||||
|
bool force_initial_cs; /* false=use from BTS true=use from VTY */
|
||||||
|
bool force_initial_mcs; /* false=use from BTS true=use from VTY */
|
||||||
|
uint8_t initial_cs_dl, initial_cs_ul;
|
||||||
|
uint8_t initial_mcs_dl, initial_mcs_ul;
|
||||||
|
uint8_t max_cs_dl, max_cs_ul;
|
||||||
|
uint8_t max_mcs_dl, max_mcs_ul;
|
||||||
|
uint8_t force_two_phase;
|
||||||
|
uint8_t force_alpha, gamma;
|
||||||
|
bool dl_tbf_preemptive_retransmission;
|
||||||
|
enum egprs_arq_type dl_arq_type; /* EGPRS_ARQ1 to support resegmentation in DL, EGPRS_ARQ2 for no reseg */
|
||||||
|
bool cs_adj_enabled; /* whether cs_adj_{upper,lower}_limit are used to adjust DL CS */
|
||||||
|
uint8_t cs_adj_upper_limit; /* downgrade DL CS if error rate above its value */
|
||||||
|
uint8_t cs_adj_lower_limit; /* upgrade DL CS if error rate below its value */
|
||||||
|
/* downgrade DL CS when less than specified octets are left in tx queue. Optimization, see paper:
|
||||||
|
"Theoretical Analysis of GPRS Throughput and Delay" */
|
||||||
|
uint16_t cs_downgrade_threshold;
|
||||||
|
/* Link quality range for each UL (M)CS. Below or above, next/prev (M)CS is selected. */
|
||||||
|
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
|
||||||
|
struct {int16_t low; int16_t high; } mcs_lqual_ranges[MAX_EDGE_MCS];
|
||||||
|
enum gprs_ns2_dialect ns_dialect; /* Are we talking Gb with IP-SNS (true) or classic Gb? */
|
||||||
|
int ns_ip_dscp;
|
||||||
|
int ns_priority;
|
||||||
|
uint16_t ws_base;
|
||||||
|
uint16_t ws_pdch; /* increase WS by this value per PDCH */
|
||||||
|
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
|
||||||
|
uint32_t llc_discard_csec;
|
||||||
|
uint32_t llc_idle_ack_csec;
|
||||||
|
uint32_t llc_codel_interval_msec; /* 0=disabled, -1=use default interval */
|
||||||
|
/* Remote BSS resolution sevice (CTRL iface) */
|
||||||
|
char *neigh_ctrl_addr;
|
||||||
|
uint16_t neigh_ctrl_port;
|
||||||
|
} vty;
|
||||||
|
|
||||||
|
struct gsmtap_inst *gsmtap;
|
||||||
|
uint32_t gsmtap_categ_mask;
|
||||||
|
|
||||||
|
struct llist_head bts_list; /* list of gprs_rlcmac_tbf */
|
||||||
|
|
||||||
|
struct gprs_ns2_inst *nsi;
|
||||||
|
|
||||||
|
alloc_algorithm_func_t alloc_algorithm;
|
||||||
|
|
||||||
|
struct gprs_bssgp_pcu bssgp;
|
||||||
|
|
||||||
|
struct osmo_tdef *T_defs; /* timers controlled by PCU */
|
||||||
|
|
||||||
|
struct neigh_cache *neigh_cache; /* ARFC+BSIC -> CGI PS cache */
|
||||||
|
struct si_cache *si_cache; /* ARFC+BSIC -> CGI PS cache */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern struct gprs_pcu *the_pcu;
|
||||||
|
|
||||||
|
struct gprs_pcu *gprs_pcu_alloc(void *ctx);
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts_nr);
|
||||||
|
struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps);
|
||||||
|
|
||||||
|
void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul);
|
||||||
|
void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul);
|
||||||
|
void gprs_pcu_set_max_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul);
|
||||||
|
void gprs_pcu_set_max_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul);
|
||||||
1483
src/gprs_rlcmac.cpp
1483
src/gprs_rlcmac.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
|||||||
/* gprs_rlcmac.h
|
/* gprs_rlcmac.h
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -20,14 +21,21 @@
|
|||||||
#ifndef GPRS_RLCMAC_H
|
#ifndef GPRS_RLCMAC_H
|
||||||
#define GPRS_RLCMAC_H
|
#define GPRS_RLCMAC_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#include <stdbool.h>
|
||||||
#include <bitvector.h>
|
#include <stdint.h>
|
||||||
#include <gsm_rlcmac.h>
|
|
||||||
#include <gsm_timer.h>
|
|
||||||
|
|
||||||
|
#include <pcu_l1_if.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#endif
|
||||||
#include <osmocom/core/linuxlist.h>
|
#include <osmocom/core/linuxlist.h>
|
||||||
#include <osmocom/core/timer.h>
|
#include <osmocom/core/timer.h>
|
||||||
|
#include <osmocom/core/bitvec.h>
|
||||||
|
#include <osmocom/gsm/gsm48.h>
|
||||||
|
#include <osmocom/pcu/pcuif_proto.h>
|
||||||
|
#include "gsm_rlcmac.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -37,177 +45,13 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
//#define DEBUG_DL_ASS_IDLE
|
//#define DEBUG_DL_ASS_IDLE
|
||||||
|
|
||||||
/*
|
#define DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
|
||||||
* PDCH instanc
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct gprs_rlcmac_tbf;
|
struct gprs_rlcmac_tbf;
|
||||||
|
struct gprs_rlcmac_bts;
|
||||||
struct gprs_rlcmac_pdch {
|
struct GprsMs;
|
||||||
uint8_t enable; /* TS is enabled */
|
|
||||||
uint8_t tsc; /* TSC of this slot */
|
|
||||||
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
|
|
||||||
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
|
|
||||||
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
|
|
||||||
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
|
|
||||||
struct llist_head paging_list; /* list of paging messages */
|
|
||||||
uint32_t last_rts_fn; /* store last frame number of RTS */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gprs_rlcmac_trx {
|
|
||||||
uint16_t arfcn;
|
|
||||||
struct gprs_rlcmac_pdch pdch[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gprs_rlcmac_bts {
|
|
||||||
uint8_t cs1;
|
|
||||||
uint8_t cs2;
|
|
||||||
uint8_t cs3;
|
|
||||||
uint8_t cs4;
|
|
||||||
uint8_t initial_cs;
|
|
||||||
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
|
|
||||||
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
|
|
||||||
uint8_t t3142;
|
|
||||||
uint8_t t3169;
|
|
||||||
uint8_t t3191;
|
|
||||||
uint16_t t3193_msec;
|
|
||||||
uint8_t t3195;
|
|
||||||
uint8_t n3101;
|
|
||||||
uint8_t n3103;
|
|
||||||
uint8_t n3105;
|
|
||||||
struct gprs_rlcmac_trx trx[8];
|
|
||||||
int (*alloc_algorithm)(struct gprs_rlcmac_tbf *old_tbf,
|
|
||||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
|
|
||||||
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
/*
|
|
||||||
* TBF instance
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LLC_MAX_LEN 1543
|
|
||||||
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
|
|
||||||
#define RLC_MAX_WS 64 /* max window size */
|
|
||||||
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
|
|
||||||
|
|
||||||
#define Tassign_agch 0,800000/* FIXME: we need a confirm from BTS */
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_state {
|
|
||||||
GPRS_RLCMAC_NULL = 0, /* new created TBF */
|
|
||||||
GPRS_RLCMAC_ASSIGN, /* wait for downlink assignment */
|
|
||||||
GPRS_RLCMAC_FLOW, /* RLC/MAC flow, ressource needed */
|
|
||||||
GPRS_RLCMAC_FINISHED, /* flow finished, wait for release */
|
|
||||||
GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
|
|
||||||
GPRS_RLCMAC_RELEASING, /* releasing, wait to free TBI/USF */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_poll_state {
|
|
||||||
GPRS_RLCMAC_POLL_NONE = 0,
|
|
||||||
GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_dl_ass_state {
|
|
||||||
GPRS_RLCMAC_DL_ASS_NONE = 0,
|
|
||||||
GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
|
|
||||||
GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_ul_ass_state {
|
|
||||||
GPRS_RLCMAC_UL_ASS_NONE = 0,
|
|
||||||
GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
|
|
||||||
GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_ul_ack_state {
|
|
||||||
GPRS_RLCMAC_UL_ACK_NONE = 0,
|
|
||||||
GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
|
|
||||||
GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_direction {
|
|
||||||
GPRS_RLCMAC_DL_TBF,
|
|
||||||
GPRS_RLCMAC_UL_TBF
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gprs_rlcmac_tbf {
|
|
||||||
struct llist_head list;
|
|
||||||
enum gprs_rlcmac_tbf_state state;
|
|
||||||
enum gprs_rlcmac_tbf_direction direction;
|
|
||||||
uint8_t tfi;
|
|
||||||
uint32_t tlli;
|
|
||||||
uint8_t tlli_valid;
|
|
||||||
uint8_t trx;
|
|
||||||
uint16_t arfcn;
|
|
||||||
uint8_t tsc;
|
|
||||||
uint8_t first_ts; /* first TS used by TBF */
|
|
||||||
uint8_t first_common_ts; /* first TS that the phone can send and
|
|
||||||
reveive simultaniously */
|
|
||||||
uint8_t control_ts; /* timeslot control messages and polling */
|
|
||||||
uint8_t ms_class;
|
|
||||||
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
|
|
||||||
uint16_t ta;
|
|
||||||
uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
|
|
||||||
uint16_t llc_index; /* current write/read position of frame */
|
|
||||||
uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
|
|
||||||
struct llist_head llc_queue; /* queued LLC DL data */
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
|
|
||||||
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
|
|
||||||
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
|
|
||||||
|
|
||||||
enum gprs_rlcmac_tbf_poll_state poll_state;
|
|
||||||
uint32_t poll_fn; /* frame number to poll */
|
|
||||||
|
|
||||||
uint16_t ws; /* window size */
|
|
||||||
uint16_t sns; /* sequence number space */
|
|
||||||
|
|
||||||
/* Please note that all variables here will be reset when changing
|
|
||||||
* from WAIT RELEASE back to FLOW state (re-use of TBF).
|
|
||||||
* All states that need reset must be in this struct, so this is why
|
|
||||||
* variables are in both (dl and ul) structs and not outside union.
|
|
||||||
*/
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
uint16_t bsn; /* block sequence number */
|
|
||||||
uint16_t v_s; /* send state */
|
|
||||||
uint16_t v_a; /* ack state */
|
|
||||||
char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
|
|
||||||
int32_t tx_counter; /* count all transmitted blocks */
|
|
||||||
uint8_t n3105; /* N3105 counter */
|
|
||||||
} dl;
|
|
||||||
struct {
|
|
||||||
uint16_t bsn; /* block sequence number */
|
|
||||||
uint16_t v_r; /* receive state */
|
|
||||||
uint16_t v_q; /* receive window state */
|
|
||||||
char v_n[RLC_MAX_SNS/2]; /* receive state array */
|
|
||||||
int32_t rx_counter; /* count all received blocks */
|
|
||||||
uint8_t n3103; /* N3103 counter */
|
|
||||||
uint8_t usf[8]; /* list USFs per PDCH (timeslot) */
|
|
||||||
uint8_t contention_resolution_done; /* set after done */
|
|
||||||
uint8_t final_ack_sent; /* set if we sent final ack */
|
|
||||||
} ul;
|
|
||||||
} dir;
|
|
||||||
uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
|
|
||||||
uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len of history */
|
|
||||||
|
|
||||||
struct osmo_timer_list timer;
|
|
||||||
unsigned int T; /* Txxxx number */
|
|
||||||
unsigned int num_T_exp; /* number of consecutive T expirations */
|
|
||||||
|
|
||||||
struct osmo_gsm_timer_list gsm_timer;
|
|
||||||
unsigned int fT; /* fTxxxx number */
|
|
||||||
unsigned int num_fT_exp; /* number of consecutive fT expirations */
|
|
||||||
|
|
||||||
struct timeval bw_tv; /* timestamp for bandwidth calculation */
|
|
||||||
uint32_t bw_octets; /* number of octets transmitted since bw_tv */
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
|
|
||||||
extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* paging entry
|
* paging entry
|
||||||
*/
|
*/
|
||||||
@@ -217,34 +61,30 @@ struct gprs_rlcmac_paging {
|
|||||||
uint8_t identity_lv[9];
|
uint8_t identity_lv[9];
|
||||||
};
|
};
|
||||||
|
|
||||||
int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, uint8_t *_ts,
|
/*
|
||||||
uint8_t use_trx, uint8_t first_ts);
|
* coding scheme info
|
||||||
|
*/
|
||||||
|
struct gprs_rlcmac_cs {
|
||||||
|
uint8_t block_length;
|
||||||
|
uint8_t block_data;
|
||||||
|
uint8_t block_payload;
|
||||||
|
};
|
||||||
|
|
||||||
struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_tbf *old_tbf,
|
/* TS allocation internal functions */
|
||||||
enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
|
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots);
|
||||||
uint8_t first_ts, uint8_t ms_class, uint8_t single_slot);
|
|
||||||
|
|
||||||
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts,
|
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||||
enum gprs_rlcmac_tbf_direction dir);
|
uint16_t lost);
|
||||||
|
|
||||||
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
|
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
|
||||||
enum gprs_rlcmac_tbf_direction dir);
|
|
||||||
|
|
||||||
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
|
int gprs_rlcmac_meas_rep(GprsMs *ms, Packet_Measurement_Report_t *pmr);
|
||||||
|
|
||||||
void tbf_free(struct gprs_rlcmac_tbf *tbf);
|
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
|
||||||
|
|
||||||
int tbf_update(struct gprs_rlcmac_tbf *tbf);
|
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
|
||||||
|
|
||||||
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
|
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
|
||||||
|
|
||||||
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
|
|
||||||
enum gprs_rlcmac_tbf_state state);
|
|
||||||
|
|
||||||
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
|
|
||||||
unsigned int seconds, unsigned int microseconds);
|
|
||||||
|
|
||||||
void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf);
|
|
||||||
|
|
||||||
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
|
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
|
||||||
enum gprs_rlcmac_block_type {
|
enum gprs_rlcmac_block_type {
|
||||||
@@ -254,83 +94,30 @@ enum gprs_rlcmac_block_type {
|
|||||||
GPRS_RLCMAC_RESERVED = 0x3
|
GPRS_RLCMAC_RESERVED = 0x3
|
||||||
};
|
};
|
||||||
|
|
||||||
int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
|
|
||||||
uint32_t fn);
|
|
||||||
|
|
||||||
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
|
|
||||||
uint32_t fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
|
|
||||||
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
|
|
||||||
uint32_t poll_fn);
|
|
||||||
|
|
||||||
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
|
|
||||||
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
|
|
||||||
struct gprs_rlcmac_tbf *tbf, uint8_t poll);
|
|
||||||
|
|
||||||
void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
|
|
||||||
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
|
|
||||||
uint8_t final);
|
|
||||||
|
|
||||||
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
|
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
|
||||||
|
|
||||||
void tbf_timer_cb(void *_tbf);
|
struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req);
|
||||||
|
|
||||||
int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
|
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||||
|
uint8_t trx, uint8_t ts,
|
||||||
int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
|
|
||||||
|
|
||||||
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
|
|
||||||
uint32_t fn);
|
|
||||||
|
|
||||||
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
|
|
||||||
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
|
|
||||||
|
|
||||||
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
|
|
||||||
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
|
|
||||||
|
|
||||||
void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf,
|
|
||||||
struct gprs_rlcmac_tbf *old_tbf, char *imsi);
|
|
||||||
|
|
||||||
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
|
|
||||||
uint8_t ssn, uint8_t *rbb);
|
|
||||||
|
|
||||||
unsigned write_packet_paging_request(bitvec * dest);
|
|
||||||
|
|
||||||
unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
|
|
||||||
uint8_t *identity, uint8_t chan_needed);
|
|
||||||
|
|
||||||
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
|
|
||||||
uint8_t *data, uint8_t len);
|
|
||||||
|
|
||||||
struct msgb *gprs_rlcmac_send_data_block_acknowledged(
|
|
||||||
struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
|
|
||||||
|
|
||||||
struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
|
|
||||||
uint32_t fn);
|
|
||||||
|
|
||||||
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|
||||||
uint32_t fn, uint8_t block_nr);
|
uint32_t fn, uint8_t block_nr);
|
||||||
|
|
||||||
int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv);
|
|
||||||
|
|
||||||
struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
|
|
||||||
struct gprs_rlcmac_pdch *pdch);
|
|
||||||
|
|
||||||
struct msgb *gprs_rlcmac_send_packet_paging_request(
|
|
||||||
struct gprs_rlcmac_pdch *pdch);
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
|
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
|
int8_t use_trx);
|
||||||
|
|
||||||
int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
|
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
|
int8_t use_trx);
|
||||||
|
|
||||||
|
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||||
|
int8_t use_trx);
|
||||||
|
|
||||||
|
int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi, uint16_t pgroup);
|
||||||
|
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#endif // GPRS_RLCMAC_H
|
#endif // GPRS_RLCMAC_H
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
198
src/gprs_rlcmac_meas.cpp
Normal file
198
src/gprs_rlcmac_meas.cpp
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
/* Measurements
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/timer_compat.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <gprs_rlcmac.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
#include <pcu_l1_if.h>
|
||||||
|
#include <tbf.h>
|
||||||
|
#include <tbf_dl.h>
|
||||||
|
#include <gprs_ms.h>
|
||||||
|
#include <ms_anr_fsm.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* downlink measurement
|
||||||
|
*/
|
||||||
|
/* TODO: trigger the measurement report from the pollcontroller and use it for flow control */
|
||||||
|
|
||||||
|
/* received Measurement Report */
|
||||||
|
int gprs_rlcmac_meas_rep(GprsMs *ms, Packet_Measurement_Report_t *pmr)
|
||||||
|
{
|
||||||
|
NC_Measurement_Report_t *ncr;
|
||||||
|
NC_Measurements_t *nc;
|
||||||
|
int i;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
LOGPMS(ms, DRLCMACMEAS, LOGL_INFO, "Rx Measurement Report:");
|
||||||
|
|
||||||
|
switch (pmr->UnionType) {
|
||||||
|
case 0:
|
||||||
|
ncr = &pmr->u.NC_Measurement_Report;
|
||||||
|
LOGPC(DRLCMACMEAS, LOGL_INFO, " NC%u Serv %d dbm",
|
||||||
|
ncr->NC_MODE + 1,
|
||||||
|
ncr->Serving_Cell_Data.RXLEV_SERVING_CELL - 110);
|
||||||
|
for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) {
|
||||||
|
nc = &ncr->NC_Measurements[i];
|
||||||
|
LOGPC(DRLCMACMEAS, LOGL_DEBUG, ", Neigh %u %d dbm",
|
||||||
|
nc->FREQUENCY_N, nc->RXLEV_N - 110);
|
||||||
|
}
|
||||||
|
LOGPC(DRLCMACMEAS, LOGL_INFO, "\n");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
LOGPC(DRLCMACMEAS, LOGL_INFO,
|
||||||
|
" <EXT Reporting not supported!>\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms->anr && ms->anr->fi->state == MS_ANR_ST_WAIT_PKT_MEAS_REPORT)
|
||||||
|
rc = osmo_fsm_inst_dispatch(ms->anr->fi, MS_ANR_EV_RX_PKT_MEAS_REPORT, pmr);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* uplink measurement
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* RSSI values received from MS */
|
||||||
|
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi)
|
||||||
|
{
|
||||||
|
struct timespec now_tv, *rssi_tv = &tbf->meas.rssi_tv;
|
||||||
|
struct timespec elapsed;
|
||||||
|
|
||||||
|
tbf->meas.rssi_sum += rssi;
|
||||||
|
tbf->meas.rssi_num++;
|
||||||
|
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now_tv);
|
||||||
|
|
||||||
|
timespecsub(&now_tv, rssi_tv, &elapsed);
|
||||||
|
if (elapsed.tv_sec < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
gprs_rlcmac_rssi_rep(tbf);
|
||||||
|
|
||||||
|
/* reset rssi values and timestamp */
|
||||||
|
memcpy(rssi_tv, &now_tv, sizeof(*rssi_tv));
|
||||||
|
tbf->meas.rssi_sum = 0;
|
||||||
|
tbf->meas.rssi_num = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Give RSSI report */
|
||||||
|
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
|
||||||
|
{
|
||||||
|
/* No measurement values */
|
||||||
|
if (!tbf->meas.rssi_num)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
LOGPMS(tbf->ms(), DRLCMACMEAS, LOGL_INFO, "UL RSSI: %d dBm\n",
|
||||||
|
tbf->meas.rssi_sum / tbf->meas.rssi_num);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lost frames
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Lost frames reported from RLCMAC layer */
|
||||||
|
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||||
|
uint16_t lost)
|
||||||
|
{
|
||||||
|
struct timespec now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv;
|
||||||
|
struct timespec elapsed;
|
||||||
|
uint16_t sum = received + lost;
|
||||||
|
|
||||||
|
/* No measurement values */
|
||||||
|
if (!sum)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d "
|
||||||
|
"Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum);
|
||||||
|
|
||||||
|
tbf->m_bw.dl_loss_received += received;
|
||||||
|
tbf->m_bw.dl_loss_lost += lost;
|
||||||
|
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now_tv);
|
||||||
|
timespecsub(&now_tv, loss_tv, &elapsed);
|
||||||
|
if (elapsed.tv_sec < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
gprs_rlcmac_lost_rep(tbf);
|
||||||
|
|
||||||
|
/* reset lost values and timestamp */
|
||||||
|
memcpy(loss_tv, &now_tv, sizeof(*loss_tv));
|
||||||
|
tbf->m_bw.dl_loss_received = 0;
|
||||||
|
tbf->m_bw.dl_loss_lost = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Give Lost report */
|
||||||
|
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
|
||||||
|
{
|
||||||
|
uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received;
|
||||||
|
|
||||||
|
/* No measurement values */
|
||||||
|
if (!sum)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
|
||||||
|
"%d%%\n", tbf->imsi(), tbf->tlli(),
|
||||||
|
tbf->m_bw.dl_loss_lost * 100 / sum);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* downlink bandwidth
|
||||||
|
*/
|
||||||
|
|
||||||
|
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
|
||||||
|
{
|
||||||
|
struct timespec now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv;
|
||||||
|
struct timespec elapsed;
|
||||||
|
|
||||||
|
tbf->m_bw.dl_bw_octets += octets;
|
||||||
|
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now_tv);
|
||||||
|
timespecsub(&now_tv, bw_tv, &elapsed);
|
||||||
|
if (elapsed.tv_sec < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tbf->m_bw.dl_throughput = (tbf->m_bw.dl_bw_octets << 10) / ((elapsed.tv_sec << 10) + (elapsed.tv_nsec >> 20));
|
||||||
|
LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
|
||||||
|
"%d KBits/s\n", tbf->imsi(), tbf->tlli(), tbf->m_bw.dl_throughput);
|
||||||
|
|
||||||
|
/* reset bandwidth values timestamp */
|
||||||
|
memcpy(bw_tv, &now_tv, sizeof(*bw_tv));
|
||||||
|
tbf->m_bw.dl_bw_octets = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -20,166 +20,350 @@
|
|||||||
#include <gprs_bssgp_pcu.h>
|
#include <gprs_bssgp_pcu.h>
|
||||||
#include <gprs_rlcmac.h>
|
#include <gprs_rlcmac.h>
|
||||||
#include <pcu_l1_if.h>
|
#include <pcu_l1_if.h>
|
||||||
|
#include <bts.h>
|
||||||
|
#include <tbf.h>
|
||||||
|
#include <tbf_ul.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
#include <gprs_ms.h>
|
||||||
|
#include <rlc.h>
|
||||||
|
#include <sba.h>
|
||||||
|
#include <pdch.h>
|
||||||
|
#include "pcu_utils.h"
|
||||||
|
|
||||||
uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
|
extern "C" {
|
||||||
struct gprs_rlcmac_tbf **poll_tbf,
|
#include <osmocom/core/gsmtap.h>
|
||||||
struct gprs_rlcmac_tbf **ul_ass_tbf,
|
|
||||||
struct gprs_rlcmac_tbf **dl_ass_tbf,
|
|
||||||
struct gprs_rlcmac_tbf **ul_ack_tbf)
|
|
||||||
{
|
|
||||||
struct gprs_rlcmac_tbf *tbf;
|
|
||||||
uint32_t poll_fn;
|
|
||||||
|
|
||||||
/* check special TBF for events */
|
|
||||||
poll_fn = fn + 4;
|
|
||||||
if ((block_nr % 3) == 2)
|
|
||||||
poll_fn ++;
|
|
||||||
poll_fn = poll_fn % 2715648;
|
|
||||||
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
|
|
||||||
/* this trx, this ts */
|
|
||||||
if (tbf->trx != trx || tbf->control_ts != ts)
|
|
||||||
continue;
|
|
||||||
/* polling for next uplink block */
|
|
||||||
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
|
|
||||||
&& tbf->poll_fn == poll_fn)
|
|
||||||
*poll_tbf = tbf;
|
|
||||||
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
|
|
||||||
*ul_ack_tbf = tbf;
|
|
||||||
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
|
||||||
*dl_ass_tbf = tbf;
|
|
||||||
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
|
||||||
*ul_ass_tbf = tbf;
|
|
||||||
}
|
|
||||||
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
|
|
||||||
/* this trx, this ts */
|
|
||||||
if (tbf->trx != trx || tbf->control_ts != ts)
|
|
||||||
continue;
|
|
||||||
/* polling for next uplink block */
|
|
||||||
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
|
|
||||||
&& tbf->poll_fn == poll_fn)
|
|
||||||
*poll_tbf = tbf;
|
|
||||||
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
|
||||||
*dl_ass_tbf = tbf;
|
|
||||||
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
|
||||||
*ul_ass_tbf = tbf;
|
|
||||||
}
|
|
||||||
|
|
||||||
return poll_fn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
|
struct tbf_sched_candidates {
|
||||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
|
struct gprs_rlcmac_tbf *ul_ass;
|
||||||
|
struct gprs_rlcmac_tbf *dl_ass;
|
||||||
|
struct gprs_rlcmac_tbf *nacc;
|
||||||
|
struct gprs_rlcmac_tbf *anr;
|
||||||
|
struct gprs_rlcmac_ul_tbf *ul_ack;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void get_ctrl_msg_tbf_candidates(const struct gprs_rlcmac_pdch *pdch,
|
||||||
|
struct tbf_sched_candidates *tbf_cand)
|
||||||
{
|
{
|
||||||
struct gprs_rlcmac_tbf *tbf;
|
struct gprs_rlcmac_ul_tbf *ul_tbf;
|
||||||
uint8_t usf = 0x07;
|
struct gprs_rlcmac_dl_tbf *dl_tbf;
|
||||||
|
struct llist_item *pos;
|
||||||
|
|
||||||
|
llist_for_each_entry(pos, &pdch->trx->ul_tbfs, list) {
|
||||||
|
ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
|
||||||
|
OSMO_ASSERT(ul_tbf);
|
||||||
|
/* this trx, this ts */
|
||||||
|
if (!ul_tbf->is_control_ts(pdch->ts_no))
|
||||||
|
continue;
|
||||||
|
if (ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_SEND_ACK))
|
||||||
|
tbf_cand->ul_ack = ul_tbf;
|
||||||
|
if (ul_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
|
||||||
|
tbf_cand->dl_ass = ul_tbf;
|
||||||
|
if (ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||||
|
|| ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
|
||||||
|
tbf_cand->ul_ass = ul_tbf;
|
||||||
|
/* NACC ready to send. TFI assigned is needed to send messages */
|
||||||
|
if (ul_tbf->is_tfi_assigned() && ms_nacc_rts(ul_tbf->ms()))
|
||||||
|
tbf_cand->nacc = ul_tbf;
|
||||||
|
/* Don't pick ->anr here, only DL TBFs are useful */
|
||||||
|
/* FIXME: Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all
|
||||||
|
states? */
|
||||||
|
}
|
||||||
|
llist_for_each_entry(pos, &pdch->trx->dl_tbfs, list) {
|
||||||
|
dl_tbf = as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
|
||||||
|
OSMO_ASSERT(dl_tbf);
|
||||||
|
/* this trx, this ts */
|
||||||
|
if (!dl_tbf->is_control_ts(pdch->ts_no))
|
||||||
|
continue;
|
||||||
|
if (dl_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
|
||||||
|
tbf_cand->dl_ass = dl_tbf;
|
||||||
|
if (dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||||
|
|| dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
|
||||||
|
tbf_cand->ul_ass = dl_tbf;
|
||||||
|
/* NACC ready to send. TFI assigned is needed to send messages */
|
||||||
|
if (dl_tbf->is_tfi_assigned() && ms_nacc_rts(dl_tbf->ms()))
|
||||||
|
tbf_cand->nacc = dl_tbf;
|
||||||
|
if (ms_anr_rts(dl_tbf->ms(), dl_tbf))
|
||||||
|
tbf_cand->anr = dl_tbf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gprs_rlcmac_ul_tbf *sched_select_uplink(struct gprs_rlcmac_pdch *pdch, bool require_gprs_only)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_ul_tbf *tbf;
|
||||||
uint8_t i, tfi;
|
uint8_t i, tfi;
|
||||||
|
|
||||||
/* select uplink ressource */
|
/* select uplink resource */
|
||||||
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
|
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
|
||||||
i++, tfi = (tfi + 1) & 31) {
|
i++, tfi = (tfi + 1) & 31) {
|
||||||
tbf = pdch->ul_tbf[tfi];
|
tbf = pdch->ul_tbf_by_tfi(tfi);
|
||||||
/* no TBF for this tfi, go next */
|
/* no TBF for this tfi, go next */
|
||||||
if (!tbf)
|
if (!tbf)
|
||||||
continue;
|
continue;
|
||||||
/* no UL ressources needed, go next */
|
/* no UL resources needed, go next */
|
||||||
/* we don't need to give ressources in FINISHED state,
|
/* we don't need to give resources in FINISHED state,
|
||||||
* because we have received all blocks and only poll
|
* because we have received all blocks and only poll
|
||||||
* for packet control ack. */
|
* for packet control ack. */
|
||||||
if (tbf->state != GPRS_RLCMAC_FLOW)
|
if (tbf->state_is_not(TBF_ST_FLOW))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* use this USF */
|
if (require_gprs_only && tbf->is_egprs_enabled())
|
||||||
usf = tbf->dir.ul.usf[ts];
|
continue;
|
||||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
|
|
||||||
"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
|
/* use this USF (tbf) */
|
||||||
"required uplink ressource of UL TBF=%d\n", trx, ts, fn,
|
/* next TBF to handle resource is the next one */
|
||||||
block_nr, usf, tfi);
|
|
||||||
/* next TBF to handle ressource is the next one */
|
|
||||||
pdch->next_ul_tfi = (tfi + 1) & 31;
|
pdch->next_ul_tfi = (tfi + 1) & 31;
|
||||||
break;
|
return tbf;
|
||||||
}
|
}
|
||||||
|
|
||||||
return usf;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
|
struct msgb *sched_app_info(struct gprs_rlcmac_tbf *tbf) {
|
||||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
|
struct gprs_rlcmac_bts *bts;
|
||||||
struct gprs_rlcmac_tbf *ul_ass_tbf,
|
|
||||||
struct gprs_rlcmac_tbf *dl_ass_tbf,
|
|
||||||
struct gprs_rlcmac_tbf *ul_ack_tbf)
|
|
||||||
{
|
|
||||||
struct msgb *msg = NULL;
|
struct msgb *msg = NULL;
|
||||||
struct gprs_rlcmac_tbf *tbf = NULL;
|
|
||||||
|
|
||||||
/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
|
if (!tbf || !tbf->ms()->app_info_pending)
|
||||||
if (ul_ass_tbf) {
|
|
||||||
tbf = ul_ass_tbf;
|
|
||||||
msg = gprs_rlcmac_send_packet_uplink_assignment(tbf, fn);
|
|
||||||
}
|
|
||||||
/* schedule PACKET DOWNLINK ASSIGNMENT (2nd priotiry) */
|
|
||||||
if (!msg && dl_ass_tbf) {
|
|
||||||
tbf = dl_ass_tbf;
|
|
||||||
msg = gprs_rlcmac_send_packet_downlink_assignment(tbf, fn);
|
|
||||||
}
|
|
||||||
/* schedule PACKET UPLINK ACK (3rd priority) */
|
|
||||||
if (!msg && ul_ack_tbf) {
|
|
||||||
tbf = ul_ack_tbf;
|
|
||||||
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
|
|
||||||
}
|
|
||||||
/* any message */
|
|
||||||
if (msg) {
|
|
||||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
|
|
||||||
"message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n",
|
|
||||||
(tbf->direction == GPRS_RLCMAC_UL_TBF)
|
|
||||||
? "UL" : "DL", tbf->tfi, trx, ts);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
/* schedule PACKET PAGING REQUEST */
|
|
||||||
if (llist_empty(&pdch->paging_list))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
msg = gprs_rlcmac_send_packet_paging_request(pdch);
|
|
||||||
if (msg)
|
|
||||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
|
|
||||||
"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
|
|
||||||
|
|
||||||
|
bts = tbf->bts;
|
||||||
|
|
||||||
|
if (bts->app_info) {
|
||||||
|
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Sending Packet Application Information message\n");
|
||||||
|
msg = msgb_copy(bts->app_info, "app_info_msg_sched");
|
||||||
|
} else
|
||||||
|
LOGP(DRLCMACSCHED, LOGL_ERROR, "MS has app_info_pending flag set, but no Packet Application Information"
|
||||||
|
" message stored in BTS!\n");
|
||||||
|
|
||||||
|
tbf->ms()->app_info_pending = false;
|
||||||
|
bts->app_info_pending--;
|
||||||
|
|
||||||
|
if (!bts->app_info_pending) {
|
||||||
|
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Packet Application Information successfully sent to all MS with active"
|
||||||
|
" TBF\n");
|
||||||
|
msgb_free(bts->app_info);
|
||||||
|
bts->app_info = NULL;
|
||||||
|
}
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
|
static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_t fn,
|
||||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
|
uint8_t block_nr, struct tbf_sched_candidates *tbfs)
|
||||||
{
|
{
|
||||||
struct msgb *msg = NULL;
|
struct msgb *msg = NULL;
|
||||||
struct gprs_rlcmac_tbf *tbf = NULL;
|
struct gprs_rlcmac_tbf *tbf = NULL;
|
||||||
uint8_t i, tfi;
|
struct gprs_rlcmac_tbf *next_list[] = { tbfs->ul_ass,
|
||||||
|
tbfs->dl_ass,
|
||||||
|
tbfs->ul_ack,
|
||||||
|
tbfs->nacc,
|
||||||
|
tbfs->anr };
|
||||||
|
uint8_t ts = pdch->ts_no;
|
||||||
|
|
||||||
/* select downlink ressource */
|
/* Send Packet Application Information first (ETWS primary notifications) */
|
||||||
|
msg = sched_app_info(tbfs->dl_ass);
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
|
||||||
|
tbf = next_list[(pdch->next_ctrl_prio + i) % ARRAY_SIZE(next_list)];
|
||||||
|
if (!tbf)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assignments for the same direction have lower precedence,
|
||||||
|
* because they may kill the TBF when the CONTROL ACK is
|
||||||
|
* received, thus preventing the others from being processed.
|
||||||
|
*/
|
||||||
|
if (tbf == tbfs->ul_ass && tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
|
||||||
|
msg = tbfs->ul_ass->create_packet_access_reject();
|
||||||
|
else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||||
|
msg = tbfs->ul_ass->create_ul_ass(fn, ts);
|
||||||
|
else if (tbf == tbfs->dl_ass && tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||||
|
msg = tbfs->dl_ass->create_dl_ass(fn, ts);
|
||||||
|
else if (tbf == tbfs->ul_ack)
|
||||||
|
msg = tbfs->ul_ack->create_ul_ack(fn, ts);
|
||||||
|
else if (tbf == tbfs->nacc)
|
||||||
|
msg = ms_nacc_create_rlcmac_msg(tbf->ms(), tbf, fn, ts);
|
||||||
|
else if (tbf == tbfs->anr)
|
||||||
|
msg = ms_anr_create_rlcmac_msg(tbf->ms(), tbf, fn, ts);
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
tbf = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdch->next_ctrl_prio = (pdch->next_ctrl_prio + 1) % ARRAY_SIZE(next_list);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
/*
|
||||||
|
* If one of these is left, the response (CONTROL ACK) from the
|
||||||
|
* MS will kill the current TBF, only one of them can be
|
||||||
|
* non-NULL
|
||||||
|
*/
|
||||||
|
if (tbfs->dl_ass) {
|
||||||
|
tbf = tbfs->dl_ass;
|
||||||
|
msg = tbfs->dl_ass->create_dl_ass(fn, ts);
|
||||||
|
} else if (tbfs->ul_ass) {
|
||||||
|
tbf = tbfs->ul_ass;
|
||||||
|
msg = tbfs->ul_ass->create_ul_ass(fn, ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* any message */
|
||||||
|
if (msg) {
|
||||||
|
if (!tbf) {
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_ERROR, "FN=%" PRIu32
|
||||||
|
" Control message to be scheduled, but no TBF\n", fn);
|
||||||
|
msgb_free(msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
tbf->rotate_in_list();
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
|
||||||
|
" Scheduling control message at RTS for %s\n",
|
||||||
|
fn, tbf_name(tbf));
|
||||||
|
rate_ctr_inc(rate_ctr_group_get_ctr(tbf->ms()->ctrs, MS_CTR_DL_CTRL_MSG_SCHED));
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* schedule PACKET PAGING REQUEST, if any are pending */
|
||||||
|
msg = pdch->packet_paging_request();
|
||||||
|
if (msg) {
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
|
||||||
|
" Scheduling paging request message at RTS\n", fn);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_dl_tbf *tbf,
|
||||||
|
uint8_t ts, uint32_t fn, int age)
|
||||||
|
{
|
||||||
|
const gprs_rlc_dl_window *w = static_cast<gprs_rlc_dl_window *>(tbf->window());
|
||||||
|
unsigned long msecs_t3190 = osmo_tdef_get(the_pcu->T_defs, 3190, OSMO_TDEF_MS, -1);
|
||||||
|
unsigned long dl_tbf_idle_msec = osmo_tdef_get(the_pcu->T_defs, -2031, OSMO_TDEF_MS, -1);
|
||||||
|
int age_thresh1 = msecs_to_frames(200);
|
||||||
|
int age_thresh2 = msecs_to_frames(OSMO_MIN(msecs_t3190/2, dl_tbf_idle_msec));
|
||||||
|
|
||||||
|
if (tbf->is_control_ts(ts) && tbf->need_control_ts())
|
||||||
|
return DL_PRIO_CONTROL;
|
||||||
|
|
||||||
|
if (tbf->is_control_ts(ts) && age > age_thresh2 && age_thresh2 > 0)
|
||||||
|
return DL_PRIO_HIGH_AGE;
|
||||||
|
|
||||||
|
if ((tbf->state_is(TBF_ST_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
|
||||||
|
return DL_PRIO_NEW_DATA;
|
||||||
|
|
||||||
|
if (tbf->is_control_ts(ts) && age > age_thresh1 && tbf->keep_open(fn))
|
||||||
|
return DL_PRIO_LOW_AGE;
|
||||||
|
|
||||||
|
if (!w->window_empty())
|
||||||
|
return DL_PRIO_SENT_DATA;
|
||||||
|
|
||||||
|
return DL_PRIO_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if next data block of a TBF can be encoded in GMSK [(M)CS1-4]. */
|
||||||
|
static bool can_produce_gmsk_data_block_next(struct gprs_rlcmac_dl_tbf *tbf, enum tbf_dl_prio prio)
|
||||||
|
{
|
||||||
|
const gprs_rlc_dl_window *w;
|
||||||
|
|
||||||
|
/* GPRS TBFs can always send GMSK */
|
||||||
|
if (!tbf->is_egprs_enabled())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
switch (prio) {
|
||||||
|
case DL_PRIO_CONTROL:
|
||||||
|
/* Control blocks are always CS-1 */
|
||||||
|
return true;
|
||||||
|
case DL_PRIO_NEW_DATA:
|
||||||
|
/* We can send any new data (no block generated yet) using any
|
||||||
|
* MCS. However, we don't (yet) support resegmenting already
|
||||||
|
* sent blocks (NACKed blocks in this case) into lower MCS of
|
||||||
|
* the same family. See OS#4966 */
|
||||||
|
w = static_cast<gprs_rlc_dl_window *>(tbf->window());
|
||||||
|
return w->resend_needed() < 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_pdch *pdch, uint32_t fn,
|
||||||
|
uint8_t block_nr, enum mcs_kind req_mcs_kind, bool *is_egprs)
|
||||||
|
{
|
||||||
|
struct msgb *msg = NULL;
|
||||||
|
struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
|
||||||
|
enum tbf_dl_prio prio, max_prio = DL_PRIO_NONE;
|
||||||
|
uint8_t ts = pdch->ts_no;
|
||||||
|
|
||||||
|
uint8_t i, tfi, prio_tfi;
|
||||||
|
int age;
|
||||||
|
|
||||||
|
/* select downlink resource */
|
||||||
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
|
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
|
||||||
i++, tfi = (tfi + 1) & 31) {
|
i++, tfi = (tfi + 1) & 31) {
|
||||||
tbf = pdch->dl_tbf[tfi];
|
tbf = pdch->dl_tbf_by_tfi(tfi);
|
||||||
/* no TBF for this tfi, go next */
|
/* no TBF for this tfi, go next */
|
||||||
if (!tbf)
|
if (!tbf)
|
||||||
continue;
|
continue;
|
||||||
/* no DL TBF, go next */
|
/* no DL TBF, go next */
|
||||||
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
|
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
|
||||||
continue;
|
continue;
|
||||||
/* no DL ressources needed, go next */
|
/* no DL resources needed, go next */
|
||||||
if (tbf->state != GPRS_RLCMAC_FLOW
|
if (tbf->state_is_not(TBF_ST_FLOW)
|
||||||
&& tbf->state != GPRS_RLCMAC_FINISHED)
|
&& tbf->state_is_not(TBF_ST_FINISHED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
|
/* waiting for CCCH IMM.ASS confirm */
|
||||||
"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
|
if (tbf->m_wait_confirm)
|
||||||
/* next TBF to handle ressource is the next one */
|
continue;
|
||||||
pdch->next_dl_tfi = (tfi + 1) & 31;
|
|
||||||
|
/* If a GPRS (CS1-4) Dl block is required, skip EGPRS(_GSMK) tbfs: */
|
||||||
|
if (req_mcs_kind == GPRS && tbf->is_egprs_enabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
age = tbf->frames_since_last_poll(fn);
|
||||||
|
|
||||||
|
/* compute priority */
|
||||||
|
prio = tbf_compute_priority(bts, tbf, ts, fn, age);
|
||||||
|
if (prio == DL_PRIO_NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* If a GPRS (CS1-4/MCS1-4) Dl block is required, downgrade MCS
|
||||||
|
* below instead of skipping. However, downgrade can only be
|
||||||
|
* done on new data BSNs (not yet sent) and control blocks. */
|
||||||
|
if (req_mcs_kind == EGPRS_GMSK && !can_produce_gmsk_data_block_next(tbf, prio)) {
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
|
||||||
|
" Cannot downgrade EGPRS TBF with prio %d for %s\n",
|
||||||
|
fn, prio, tbf_name(tbf));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the TBF with the highest priority */
|
||||||
|
if (prio > max_prio) {
|
||||||
|
prio_tfi = tfi;
|
||||||
|
prio_tbf = tbf;
|
||||||
|
max_prio = prio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prio_tbf) {
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
|
||||||
|
" Scheduling data message at RTS for DL TFI=%d prio=%d mcs_mode_restrict=%s\n",
|
||||||
|
fn, prio_tfi, max_prio, mode_name(req_mcs_kind));
|
||||||
|
/* next TBF to handle resource is the next one */
|
||||||
|
pdch->next_dl_tfi = (prio_tfi + 1) & 31;
|
||||||
/* generate DL data block */
|
/* generate DL data block */
|
||||||
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
|
msg = prio_tbf->create_dl_acked_block(fn, ts, req_mcs_kind);
|
||||||
ts);
|
*is_egprs = prio_tbf->is_egprs_enabled();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
static uint8_t rlcmac_dl_idle[23] = {
|
|
||||||
|
static const uint8_t rlcmac_dl_idle[23] = {
|
||||||
0x47, /* control without optional header octets, no polling, USF=111 */
|
0x47, /* control without optional header octets, no polling, USF=111 */
|
||||||
0x94, /* dummy downlink control message, paging mode 00 */
|
0x94, /* dummy downlink control message, paging mode 00 */
|
||||||
0x2b, /* no persistance level, 7 bits spare pattern */
|
0x2b, /* no persistance level, 7 bits spare pattern */
|
||||||
@@ -187,7 +371,7 @@ static uint8_t rlcmac_dl_idle[23] = {
|
|||||||
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
|
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msgb *sched_dummy(void)
|
static struct msgb *sched_dummy(void)
|
||||||
{
|
{
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
|
|
||||||
@@ -199,22 +383,56 @@ struct msgb *sched_dummy(void)
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
static inline void tap_n_acc(const struct msgb *msg, struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
|
||||||
|
uint32_t fn, enum pcu_gsmtap_category cat)
|
||||||
|
{
|
||||||
|
if (!msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch(cat) {
|
||||||
|
case PCU_GSMTAP_C_DL_CTRL:
|
||||||
|
bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_CONTROL);
|
||||||
|
bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
|
||||||
|
msg->len);
|
||||||
|
break;
|
||||||
|
case PCU_GSMTAP_C_DL_DATA_GPRS:
|
||||||
|
case PCU_GSMTAP_C_DL_DATA_EGPRS:
|
||||||
|
bts_do_rate_ctr_inc(bts, CTR_RLC_SENT);
|
||||||
|
bts_send_gsmtap(bts, cat, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
|
||||||
|
msg->len);
|
||||||
|
break;
|
||||||
|
case PCU_GSMTAP_C_DL_DUMMY:
|
||||||
|
bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_DUMMY);
|
||||||
|
bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
|
||||||
|
msg->len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||||
|
uint8_t trx, uint8_t ts,
|
||||||
uint32_t fn, uint8_t block_nr)
|
uint32_t fn, uint8_t block_nr)
|
||||||
{
|
{
|
||||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
|
||||||
struct gprs_rlcmac_pdch *pdch;
|
struct gprs_rlcmac_pdch *pdch;
|
||||||
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
|
struct tbf_sched_candidates tbf_cand = {0};
|
||||||
*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
|
struct gprs_rlcmac_tbf *poll_tbf;
|
||||||
uint8_t usf = 0x7;
|
struct gprs_rlcmac_ul_tbf *usf_tbf = NULL;
|
||||||
|
struct gprs_rlcmac_sba *sba;
|
||||||
|
uint8_t usf;
|
||||||
struct msgb *msg = NULL;
|
struct msgb *msg = NULL;
|
||||||
uint32_t poll_fn;
|
uint32_t poll_fn;
|
||||||
|
enum pcu_gsmtap_category gsmtap_cat;
|
||||||
|
bool tx_is_egprs = false;
|
||||||
|
bool require_gprs_only;
|
||||||
|
enum mcs_kind req_mcs_kind; /* Restrict CS/MCS if DL Data block is to be sent */
|
||||||
|
|
||||||
if (trx >= 8 || ts >= 8)
|
if (trx >= 8 || ts >= 8)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
pdch = &bts->trx[trx].pdch[ts];
|
pdch = &bts->trx[trx].pdch[ts];
|
||||||
|
|
||||||
if (!pdch->enable) {
|
if (!pdch->is_enabled()) {
|
||||||
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
|
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
|
||||||
"TRX=%d TS=%d\n", trx, ts);
|
"TRX=%d TS=%d\n", trx, ts);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@@ -223,42 +441,92 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|||||||
/* store last frame number of RTS */
|
/* store last frame number of RTS */
|
||||||
pdch->last_rts_fn = fn;
|
pdch->last_rts_fn = fn;
|
||||||
|
|
||||||
poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
|
/* require_gprs_only: Prioritize USF for GPRS-only MS here,
|
||||||
&dl_ass_tbf, &ul_ack_tbf);
|
* since anyway we'll need to tx a Dl block with CS1-4 due to
|
||||||
/* check uplink ressource for polling */
|
* synchronization requirements. See 3GPP TS 03.64 version
|
||||||
if (poll_tbf)
|
* 8.12.0
|
||||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
|
*/
|
||||||
"TS=%d FN=%d block_nr=%d scheduling free USF for "
|
require_gprs_only = (pdch->fn_without_cs14 == MS_RESYNC_NUM_FRAMES - 1);
|
||||||
"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
|
if (require_gprs_only) {
|
||||||
block_nr, poll_fn,
|
LOGP(DRLCMACSCHED, LOGL_DEBUG, "TRX=%d TS=%d FN=%d "
|
||||||
(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
|
"synchronization frame (every 18 frames), only CS1-4 allowed",
|
||||||
? "UL" : "DL", poll_tbf->tfi);
|
trx, ts, fn);
|
||||||
/* use free USF */
|
req_mcs_kind = GPRS; /* only GPRS CS1-4 allowed, all MS need to be able to decode it */
|
||||||
/* else, we search for uplink ressource */
|
} else {
|
||||||
else
|
req_mcs_kind = EGPRS; /* all kinds are fine */
|
||||||
usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
|
}
|
||||||
|
|
||||||
|
/* polling for next uplink block */
|
||||||
|
poll_fn = rts_next_fn(fn, block_nr);
|
||||||
|
/* check for sba */
|
||||||
|
if ((sba = pdch_ulc_get_sba(pdch->ulc, poll_fn))) {
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: "
|
||||||
|
"FN=%d block_nr=%d scheduling free USF for "
|
||||||
|
"single block allocation at FN=%d\n", fn, block_nr, sba->fn);
|
||||||
|
/* else, check uplink resource for polling */
|
||||||
|
} else if ((poll_tbf = pdch_ulc_get_tbf_poll(pdch->ulc, poll_fn))) {
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: FN=%d "
|
||||||
|
"block_nr=%d scheduling free USF for polling at FN=%d of %s\n",
|
||||||
|
fn, block_nr, poll_fn, tbf_name(poll_tbf));
|
||||||
|
/* If POLL TBF is UL and already has a USF assigned on this TS,
|
||||||
|
* let's set its USF in the DL msg. This is not really needed,
|
||||||
|
* but it helps understand better the flow when looking at
|
||||||
|
* pcaps. */
|
||||||
|
if (poll_tbf->direction == GPRS_RLCMAC_UL_TBF && as_ul_tbf(poll_tbf)->m_usf[ts] != USF_INVALID)
|
||||||
|
usf_tbf = as_ul_tbf(poll_tbf);
|
||||||
|
/* else, search for uplink tbf */
|
||||||
|
} else if ((usf_tbf = sched_select_uplink(pdch, require_gprs_only))) {
|
||||||
|
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: FN=%d "
|
||||||
|
"block_nr=%d scheduling USF=%d for %s, expect answer on UL FN=%d\n",
|
||||||
|
fn, block_nr, usf_tbf->m_usf[pdch->ts_no], tbf_name(usf_tbf), poll_fn);
|
||||||
|
pdch_ulc_reserve_tbf_usf(pdch->ulc, poll_fn, usf_tbf);
|
||||||
|
}
|
||||||
|
/* If MS selected for USF is GPRS-only, then it will only be
|
||||||
|
* able to read USF if dl block uses GMSK * (CS1-4, MCS1-4) */
|
||||||
|
if (usf_tbf && req_mcs_kind == EGPRS && ms_mode(usf_tbf->ms()) != EGPRS)
|
||||||
|
req_mcs_kind = EGPRS_GMSK;
|
||||||
|
|
||||||
|
get_ctrl_msg_tbf_candidates(pdch, &tbf_cand);
|
||||||
|
|
||||||
/* Prio 1: select control message */
|
/* Prio 1: select control message */
|
||||||
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
|
if ((msg = sched_select_ctrl_msg(pdch, fn, block_nr, &tbf_cand))) {
|
||||||
dl_ass_tbf, ul_ack_tbf);
|
gsmtap_cat = PCU_GSMTAP_C_DL_CTRL;
|
||||||
|
}
|
||||||
/* Prio 2: select data message for downlink */
|
/* Prio 2: select data message for downlink */
|
||||||
if (!msg)
|
else if((msg = sched_select_downlink(bts, pdch, fn, block_nr, req_mcs_kind, &tx_is_egprs))) {
|
||||||
msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
|
gsmtap_cat = tx_is_egprs ? PCU_GSMTAP_C_DL_DATA_EGPRS :
|
||||||
|
PCU_GSMTAP_C_DL_DATA_GPRS;
|
||||||
|
}
|
||||||
/* Prio 3: send dummy contol message */
|
/* Prio 3: send dummy contol message */
|
||||||
if (!msg)
|
else if ((msg = sched_dummy())) {
|
||||||
msg = sched_dummy();
|
/* increase counter */
|
||||||
|
gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY;
|
||||||
if (!msg)
|
} else {
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_is_egprs && pdch->has_gprs_only_tbf_attached()) {
|
||||||
|
pdch->fn_without_cs14 += 1;
|
||||||
|
} else {
|
||||||
|
pdch->fn_without_cs14 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* msg is now available */
|
/* msg is now available */
|
||||||
|
bts_do_rate_ctr_add(bts, CTR_RLC_DL_BYTES, msgb_length(msg));
|
||||||
|
|
||||||
/* set USF */
|
/* set USF */
|
||||||
|
OSMO_ASSERT(msgb_length(msg) > 0);
|
||||||
|
usf = usf_tbf ? usf_tbf->m_usf[ts] : USF_UNUSED;
|
||||||
msg->data[0] = (msg->data[0] & 0xf8) | usf;
|
msg->data[0] = (msg->data[0] & 0xf8) | usf;
|
||||||
|
|
||||||
|
/* Used to measure the leak rate, count all blocks */
|
||||||
|
gprs_bssgp_update_frames_sent();
|
||||||
|
|
||||||
|
/* Send to GSMTAP */
|
||||||
|
tap_n_acc(msg, bts, trx, ts, fn, gsmtap_cat);
|
||||||
|
|
||||||
/* send PDTCH/PACCH to L1 */
|
/* send PDTCH/PACCH to L1 */
|
||||||
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
|
pcu_l1if_tx_pdtch(msg, bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
1026
src/gprs_rlcmac_ts_alloc.cpp
Normal file
1026
src/gprs_rlcmac_ts_alloc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
537
src/gsm_rlcmac.h
537
src/gsm_rlcmac.h
File diff suppressed because it is too large
Load Diff
@@ -1,226 +0,0 @@
|
|||||||
/* gsm_timer.cpp
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* These store the amount of frame number that we wait until next timer expires. */
|
|
||||||
static int nearest;
|
|
||||||
static int *nearest_p;
|
|
||||||
|
|
||||||
/*! \addtogroup gsm_timer
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! \file gsm_timer.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <gsm_timer.h>
|
|
||||||
#include <pcu_l1_if.h>
|
|
||||||
|
|
||||||
static struct rb_root timer_root = RB_ROOT;
|
|
||||||
|
|
||||||
static void __add_gsm_timer(struct osmo_gsm_timer_list *timer)
|
|
||||||
{
|
|
||||||
struct rb_node **new_node = &(timer_root.rb_node);
|
|
||||||
struct rb_node *parent = NULL;
|
|
||||||
|
|
||||||
while (*new_node) {
|
|
||||||
struct osmo_gsm_timer_list *this_timer;
|
|
||||||
|
|
||||||
this_timer = container_of(*new_node, struct osmo_gsm_timer_list, node);
|
|
||||||
|
|
||||||
parent = *new_node;
|
|
||||||
if (timer->fn < this_timer->fn)
|
|
||||||
new_node = &((*new_node)->rb_left);
|
|
||||||
else
|
|
||||||
new_node = &((*new_node)->rb_right);
|
|
||||||
}
|
|
||||||
|
|
||||||
rb_link_node(&timer->node, parent, new_node);
|
|
||||||
rb_insert_color(&timer->node, &timer_root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! \brief add a new timer to the timer management
|
|
||||||
* \param[in] timer the timer that should be added
|
|
||||||
*/
|
|
||||||
void osmo_gsm_timer_add(struct osmo_gsm_timer_list *timer)
|
|
||||||
{
|
|
||||||
osmo_gsm_timer_del(timer);
|
|
||||||
timer->active = 1;
|
|
||||||
INIT_LLIST_HEAD(&timer->list);
|
|
||||||
__add_gsm_timer(timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! \brief schedule a gsm timer at a given future relative time
|
|
||||||
* \param[in] timer the to-be-added timer
|
|
||||||
* \param[in] number of frames from now
|
|
||||||
*
|
|
||||||
* This function can be used to (re-)schedule a given timer at a
|
|
||||||
* specified number of frames in the future. It will
|
|
||||||
* internally add it to the timer management data structures, thus
|
|
||||||
* osmo_timer_add() is automatically called.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
osmo_gsm_timer_schedule(struct osmo_gsm_timer_list *timer, int fn)
|
|
||||||
{
|
|
||||||
int current_fn;
|
|
||||||
|
|
||||||
current_fn = get_current_fn();
|
|
||||||
timer->fn = current_fn + fn;
|
|
||||||
osmo_gsm_timer_add(timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! \brief delete a gsm timer from timer management
|
|
||||||
* \param[in] timer the to-be-deleted timer
|
|
||||||
*
|
|
||||||
* This function can be used to delete a previously added/scheduled
|
|
||||||
* timer from the timer management code.
|
|
||||||
*/
|
|
||||||
void osmo_gsm_timer_del(struct osmo_gsm_timer_list *timer)
|
|
||||||
{
|
|
||||||
if (timer->active) {
|
|
||||||
timer->active = 0;
|
|
||||||
rb_erase(&timer->node, &timer_root);
|
|
||||||
/* make sure this is not already scheduled for removal. */
|
|
||||||
if (!llist_empty(&timer->list))
|
|
||||||
llist_del_init(&timer->list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! \brief check if given timer is still pending
|
|
||||||
* \param[in] timer the to-be-checked timer
|
|
||||||
* \return 1 if pending, 0 otherwise
|
|
||||||
*
|
|
||||||
* This function can be used to determine whether a given timer
|
|
||||||
* has alredy expired (returns 0) or is still pending (returns 1)
|
|
||||||
*/
|
|
||||||
int osmo_gsm_timer_pending(struct osmo_gsm_timer_list *timer)
|
|
||||||
{
|
|
||||||
return timer->active;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if we have a nearest frame number return the delta between the current
|
|
||||||
* FN and the FN of the nearest timer.
|
|
||||||
* If the nearest timer timed out return NULL and then we will
|
|
||||||
* dispatch everything after the select
|
|
||||||
*/
|
|
||||||
int *osmo_gsm_timers_nearest(void)
|
|
||||||
{
|
|
||||||
/* nearest_p is exactly what we need already: NULL if nothing is
|
|
||||||
* waiting, {0,0} if we must dispatch immediately, and the correct
|
|
||||||
* delay if we need to wait */
|
|
||||||
return nearest_p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_nearest(int *cand, int *current)
|
|
||||||
{
|
|
||||||
if (*cand != LONG_MAX) {
|
|
||||||
if (*cand > *current)
|
|
||||||
nearest = *cand - *current;
|
|
||||||
else {
|
|
||||||
/* loop again inmediately */
|
|
||||||
nearest = 0;
|
|
||||||
}
|
|
||||||
nearest_p = &nearest;
|
|
||||||
} else {
|
|
||||||
nearest_p = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find the nearest FN and update s_nearest_time
|
|
||||||
*/
|
|
||||||
void osmo_gsm_timers_prepare(void)
|
|
||||||
{
|
|
||||||
struct rb_node *node;
|
|
||||||
int current_fn;
|
|
||||||
|
|
||||||
current_fn = get_current_fn();
|
|
||||||
|
|
||||||
node = rb_first(&timer_root);
|
|
||||||
if (node) {
|
|
||||||
struct osmo_gsm_timer_list *this_timer;
|
|
||||||
this_timer = container_of(node, struct osmo_gsm_timer_list, node);
|
|
||||||
update_nearest(&this_timer->fn, ¤t_fn);
|
|
||||||
} else {
|
|
||||||
nearest_p = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* fire all timers... and remove them
|
|
||||||
*/
|
|
||||||
int osmo_gsm_timers_update(void)
|
|
||||||
{
|
|
||||||
int current_fn;
|
|
||||||
struct rb_node *node;
|
|
||||||
struct llist_head timer_eviction_list;
|
|
||||||
struct osmo_gsm_timer_list *this_timer;
|
|
||||||
int work = 0;
|
|
||||||
|
|
||||||
current_fn = get_current_fn();
|
|
||||||
|
|
||||||
INIT_LLIST_HEAD(&timer_eviction_list);
|
|
||||||
for (node = rb_first(&timer_root); node; node = rb_next(node)) {
|
|
||||||
this_timer = container_of(node, struct osmo_gsm_timer_list, node);
|
|
||||||
|
|
||||||
if (this_timer->fn > current_fn)
|
|
||||||
break;
|
|
||||||
|
|
||||||
llist_add(&this_timer->list, &timer_eviction_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The callbacks might mess with our list and in this case
|
|
||||||
* even llist_for_each_entry_safe is not safe to use. To allow
|
|
||||||
* osmo_gsm_timer_del to be called from within the callback we need
|
|
||||||
* to restart the iteration for each element scheduled for removal.
|
|
||||||
*
|
|
||||||
* The problematic scenario is the following: Given two timers A
|
|
||||||
* and B that have expired at the same time. Thus, they are both
|
|
||||||
* in the eviction list in this order: A, then B. If we remove
|
|
||||||
* timer B from the A's callback, we continue with B in the next
|
|
||||||
* iteration step, leading to an access-after-release.
|
|
||||||
*/
|
|
||||||
restart:
|
|
||||||
llist_for_each_entry(this_timer, &timer_eviction_list, list) {
|
|
||||||
osmo_gsm_timer_del(this_timer);
|
|
||||||
this_timer->cb(this_timer->data);
|
|
||||||
work = 1;
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
|
|
||||||
return work;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmo_gsm_timers_check(void)
|
|
||||||
{
|
|
||||||
struct rb_node *node;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (node = rb_first(&timer_root); node; node = rb_next(node)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! }@ */
|
|
||||||
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/* gsm_timer.h
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! \defgroup timer GSM timers
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! \file gsm_timer.h
|
|
||||||
* \brief GSM timer handling routines
|
|
||||||
*/
|
|
||||||
#ifndef GSM_TIMER_H
|
|
||||||
#define GSM_TIMER_H
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/core/linuxlist.h>
|
|
||||||
#include <osmocom/core/linuxrbtree.h>
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Timer management:
|
|
||||||
* - Create a struct osmo_gsm_timer_list
|
|
||||||
* - Fill out timeout and use add_gsm_timer or
|
|
||||||
* use schedule_gsm_timer to schedule a timer in
|
|
||||||
* x frames from now...
|
|
||||||
* - Use del_gsm_timer to remove the timer
|
|
||||||
*
|
|
||||||
* Internally:
|
|
||||||
* - We hook into select.c to give a frame number of the
|
|
||||||
* nearest timer. On already passed timers we give
|
|
||||||
* it a 0 to immediately fire after the select.
|
|
||||||
* - update_gsm_timers will call the callbacks and remove
|
|
||||||
* the timers.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/*! \brief A structure representing a single instance of a gsm timer */
|
|
||||||
struct osmo_gsm_timer_list {
|
|
||||||
struct rb_node node; /*!< \brief rb-tree node header */
|
|
||||||
struct llist_head list; /*!< \brief internal list header */
|
|
||||||
int fn; /*!< \brief expiration frame number */
|
|
||||||
unsigned int active : 1; /*!< \brief is it active? */
|
|
||||||
|
|
||||||
void (*cb)(void*); /*!< \brief call-back called at timeout */
|
|
||||||
void *data; /*!< \brief user data for callback */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* timer management
|
|
||||||
*/
|
|
||||||
|
|
||||||
void osmo_gsm_timer_add(struct osmo_gsm_timer_list *timer);
|
|
||||||
|
|
||||||
void osmo_gsm_timer_schedule(struct osmo_gsm_timer_list *timer, int fn);
|
|
||||||
|
|
||||||
void osmo_gsm_timer_del(struct osmo_gsm_timer_list *timer);
|
|
||||||
|
|
||||||
int osmo_gsm_timer_pending(struct osmo_gsm_timer_list *timer);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* internal timer list management
|
|
||||||
*/
|
|
||||||
int *osmo_gsm_timers_nearest(void);
|
|
||||||
void osmo_gsm_timers_prepare(void);
|
|
||||||
int osmo_gsm_timers_update(void);
|
|
||||||
int osmo_gsm_timers_check(void);
|
|
||||||
|
|
||||||
/*! }@ */
|
|
||||||
|
|
||||||
#endif // GSM_TIMER_H
|
|
||||||
256
src/llc.cpp
Normal file
256
src/llc.cpp
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
/* Copied from tbf.cpp
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <bts.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "pcu_utils.h"
|
||||||
|
|
||||||
|
/* reset LLC frame */
|
||||||
|
void gprs_llc::reset()
|
||||||
|
{
|
||||||
|
m_index = 0;
|
||||||
|
m_length = 0;
|
||||||
|
|
||||||
|
memset(frame, 0x42, sizeof(frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_llc::reset_frame_space()
|
||||||
|
{
|
||||||
|
m_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
|
||||||
|
void gprs_llc::put_dummy_frame(size_t req_len)
|
||||||
|
{
|
||||||
|
/* The shortest dummy command (the spec requests at least 6 octets) */
|
||||||
|
static const uint8_t llc_dummy_command[] = {
|
||||||
|
0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
|
||||||
|
};
|
||||||
|
static const size_t max_dummy_command_len = 79;
|
||||||
|
|
||||||
|
put_frame(llc_dummy_command, sizeof(llc_dummy_command));
|
||||||
|
|
||||||
|
if (req_len > max_dummy_command_len)
|
||||||
|
req_len = max_dummy_command_len;
|
||||||
|
|
||||||
|
/* Add further stuffing, if the requested length exceeds the minimum
|
||||||
|
* dummy command length */
|
||||||
|
if (m_length < req_len) {
|
||||||
|
memset(&frame[m_length], 0x2b, req_len - m_length);
|
||||||
|
m_length = req_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_llc::put_frame(const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
/* only put frames when we are empty */
|
||||||
|
OSMO_ASSERT(m_index == 0 && m_length == 0);
|
||||||
|
append_frame(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_llc::append_frame(const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
/* TODO: bounds check */
|
||||||
|
memcpy(frame + m_length, data, len);
|
||||||
|
m_length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_llc::init()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
if (len < 2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
|
||||||
|
/* It is not an LLC UI frame, see TS 44.064, 6.3 */
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void llc_queue_init(struct gprs_llc_queue *q)
|
||||||
|
{
|
||||||
|
INIT_LLIST_HEAD(&q->m_queue);
|
||||||
|
q->m_queue_size = 0;
|
||||||
|
q->m_queue_octets = 0;
|
||||||
|
q->m_avg_queue_delay = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const struct timespec *expire_time)
|
||||||
|
{
|
||||||
|
MetaInfo *meta_storage;
|
||||||
|
|
||||||
|
osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit);
|
||||||
|
|
||||||
|
m_queue_size += 1;
|
||||||
|
m_queue_octets += msgb_length(llc_msg);
|
||||||
|
|
||||||
|
meta_storage = (MetaInfo *)&llc_msg->cb[0];
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
|
||||||
|
meta_storage->expire_time = *expire_time;
|
||||||
|
|
||||||
|
msgb_enqueue(&m_queue, llc_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
|
||||||
|
while ((msg = msgb_dequeue(&q->m_queue))) {
|
||||||
|
if (bts)
|
||||||
|
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
|
||||||
|
msgb_free(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
q->m_queue_size = 0;
|
||||||
|
q->m_queue_octets = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o)
|
||||||
|
{
|
||||||
|
struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
|
||||||
|
struct llist_head new_queue;
|
||||||
|
size_t queue_size = 0;
|
||||||
|
size_t queue_octets = 0;
|
||||||
|
INIT_LLIST_HEAD(&new_queue);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (msg1 == NULL)
|
||||||
|
msg1 = msgb_dequeue(&q->m_queue);
|
||||||
|
|
||||||
|
if (msg2 == NULL)
|
||||||
|
msg2 = msgb_dequeue(&o->m_queue);
|
||||||
|
|
||||||
|
if (msg1 == NULL && msg2 == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (msg1 == NULL) {
|
||||||
|
msg = msg2;
|
||||||
|
msg2 = NULL;
|
||||||
|
} else if (msg2 == NULL) {
|
||||||
|
msg = msg1;
|
||||||
|
msg1 = NULL;
|
||||||
|
} else {
|
||||||
|
const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
|
||||||
|
const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
|
||||||
|
|
||||||
|
if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
|
||||||
|
msg = msg1;
|
||||||
|
msg1 = NULL;
|
||||||
|
} else {
|
||||||
|
msg = msg2;
|
||||||
|
msg2 = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msgb_enqueue(&new_queue, msg);
|
||||||
|
queue_size += 1;
|
||||||
|
queue_octets += msgb_length(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSMO_ASSERT(llist_empty(&q->m_queue));
|
||||||
|
OSMO_ASSERT(llist_empty(&o->m_queue));
|
||||||
|
|
||||||
|
o->m_queue_size = 0;
|
||||||
|
o->m_queue_octets = 0;
|
||||||
|
|
||||||
|
llist_splice_init(&new_queue, &q->m_queue);
|
||||||
|
q->m_queue_size = queue_size;
|
||||||
|
q->m_queue_octets = queue_octets;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ALPHA 0.5f
|
||||||
|
|
||||||
|
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
struct timespec *tv, tv_now, tv_result;
|
||||||
|
uint32_t lifetime;
|
||||||
|
const MetaInfo *meta_storage;
|
||||||
|
|
||||||
|
msg = msgb_dequeue(&m_queue);
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
meta_storage = (MetaInfo *)&msg->cb[0];
|
||||||
|
|
||||||
|
if (info)
|
||||||
|
*info = meta_storage;
|
||||||
|
|
||||||
|
m_queue_size -= 1;
|
||||||
|
m_queue_octets -= msgb_length(msg);
|
||||||
|
|
||||||
|
/* take the second time */
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
|
||||||
|
tv = (struct timespec *)&msg->data[sizeof(*tv)];
|
||||||
|
timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
|
||||||
|
|
||||||
|
lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
|
||||||
|
m_avg_queue_delay = m_avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gprs_llc_queue::calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
|
||||||
|
{
|
||||||
|
uint16_t delay_csec;
|
||||||
|
if (bts->pcu->vty.force_llc_lifetime)
|
||||||
|
delay_csec = bts->pcu->vty.force_llc_lifetime;
|
||||||
|
else
|
||||||
|
delay_csec = pdu_delay_csec;
|
||||||
|
|
||||||
|
/* keep timestamp at 0 for infinite delay */
|
||||||
|
if (delay_csec == 0xffff) {
|
||||||
|
memset(tv, 0, sizeof(*tv));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate timestamp of timeout */
|
||||||
|
struct timespec now, csec;
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
csecs_to_timespec(delay_csec, &csec);
|
||||||
|
|
||||||
|
timespecadd(&now, &csec, tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gprs_llc_queue::is_frame_expired(const struct timespec *tv_now,
|
||||||
|
const struct timespec *tv)
|
||||||
|
{
|
||||||
|
/* Timeout is infinite */
|
||||||
|
if (tv->tv_sec == 0 && tv->tv_nsec == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return timespeccmp(tv_now, tv, >);
|
||||||
|
}
|
||||||
134
src/llc.h
Normal file
134
src/llc.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define LLC_MAX_LEN 1543
|
||||||
|
|
||||||
|
struct gprs_rlcmac_bts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I represent the LLC data to a MS
|
||||||
|
*/
|
||||||
|
struct gprs_llc {
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
static bool is_user_data_frame(uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void reset();
|
||||||
|
void reset_frame_space();
|
||||||
|
|
||||||
|
void put_frame(const uint8_t *data, size_t len);
|
||||||
|
void put_dummy_frame(size_t req_len);
|
||||||
|
void append_frame(const uint8_t *data, size_t len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
|
||||||
|
uint16_t m_index; /* current write/read position of frame */
|
||||||
|
uint16_t m_length; /* len of current DL LLC_frame, 0 == no frame */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MetaInfo {
|
||||||
|
struct timespec recv_time;
|
||||||
|
struct timespec expire_time;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* I store the LLC frames that come from the SGSN.
|
||||||
|
*/
|
||||||
|
struct gprs_llc_queue {
|
||||||
|
#ifdef __cplusplus
|
||||||
|
static void calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,
|
||||||
|
struct timespec *tv);
|
||||||
|
static bool is_frame_expired(const struct timespec *now,
|
||||||
|
const struct timespec *tv);
|
||||||
|
static bool is_user_data_frame(uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
void enqueue(struct msgb *llc_msg, const struct timespec *expire_time);
|
||||||
|
struct msgb *dequeue(const MetaInfo **info = 0);
|
||||||
|
#endif
|
||||||
|
uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */
|
||||||
|
size_t m_queue_size;
|
||||||
|
size_t m_queue_octets;
|
||||||
|
struct llist_head m_queue; /* queued LLC DL data */
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
void llc_queue_init(struct gprs_llc_queue *q);
|
||||||
|
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts);
|
||||||
|
void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o);
|
||||||
|
|
||||||
|
static inline uint16_t llc_chunk_size(const struct gprs_llc *llc)
|
||||||
|
{
|
||||||
|
return llc->m_length - llc->m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t llc_remaining_space(const struct gprs_llc *llc)
|
||||||
|
{
|
||||||
|
return LLC_MAX_LEN - llc->m_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t llc_frame_length(const struct gprs_llc *llc)
|
||||||
|
{
|
||||||
|
return llc->m_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void llc_consume(struct gprs_llc *llc, size_t len)
|
||||||
|
{
|
||||||
|
llc->m_index += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void llc_consume_data(struct gprs_llc *llc, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
/* copy and increment index */
|
||||||
|
memcpy(data, llc->frame + llc->m_index, len);
|
||||||
|
llc_consume(llc, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool llc_fits_in_current_frame(const struct gprs_llc *llc, uint8_t chunk_size)
|
||||||
|
{
|
||||||
|
return llc->m_length + chunk_size <= LLC_MAX_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t llc_queue_size(const struct gprs_llc_queue *q)
|
||||||
|
{
|
||||||
|
return q->m_queue_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t llc_queue_octets(const struct gprs_llc_queue *q)
|
||||||
|
{
|
||||||
|
return q->m_queue_octets;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
757
src/ms_anr_fsm.c
Normal file
757
src/ms_anr_fsm.c
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
/* ms_anr_fsm.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <talloc.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
|
#include <osmocom/ctrl/control_cmd.h>
|
||||||
|
#include <osmocom/ctrl/control_if.h>
|
||||||
|
|
||||||
|
#include <osmocom/gsm/gsm48.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||||
|
|
||||||
|
#include <ms_anr_fsm.h>
|
||||||
|
#include <gprs_rlcmac.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
#include <gprs_ms.h>
|
||||||
|
#include <encoding.h>
|
||||||
|
#include <bts.h>
|
||||||
|
|
||||||
|
#define X(s) (1 << (s))
|
||||||
|
|
||||||
|
/* We add safety timer to any FSM since ending up into some unexpected scenario
|
||||||
|
* can keep the FSM alive and hence the TBF kept open forever */
|
||||||
|
static const struct osmo_tdef_state_timeout ms_anr_fsm_timeouts[32] = {
|
||||||
|
[MS_ANR_ST_INITIAL] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
[MS_ANR_ST_TX_PKT_MEAS_RESET1] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
[MS_ANR_ST_WAIT_CTRL_ACK1] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
[MS_ANR_ST_TX_PKT_MEAS_ORDER] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
[MS_ANR_ST_WAIT_PKT_MEAS_REPORT] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
[MS_ANR_ST_TX_PKT_MEAS_RESET2] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
[MS_ANR_ST_WAIT_CTRL_ACK2] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
[MS_ANR_ST_DONE] = { .T = PCU_TDEF_ANR_MS_TIMEOUT },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
|
||||||
|
* The actual timeout value is in turn obtained from conn->T_defs.
|
||||||
|
* Assumes local variable fi exists. */
|
||||||
|
#define ms_anr_fsm_state_chg(fi, NEXT_STATE) \
|
||||||
|
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
|
||||||
|
ms_anr_fsm_timeouts, \
|
||||||
|
((struct ms_anr_fsm_ctx*)(fi->priv))->ms->bts->pcu->T_defs, \
|
||||||
|
-1)
|
||||||
|
|
||||||
|
const struct value_string ms_anr_fsm_event_names[] = {
|
||||||
|
{ MS_ANR_EV_START, "START" },
|
||||||
|
{ MS_ANR_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
|
||||||
|
{ MS_ANR_EV_RX_PKT_MEAS_REPORT, "RX_PKT_MEAS_REPORT" },
|
||||||
|
{ MS_ANR_EV_RX_PKT_CTRL_ACK_MSG, "RX_PKT_CTRL_ACK_MSG" },
|
||||||
|
{ MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT, "RX_PKT_CTRL_ACK_TIMEOUT" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TS 44 060 11.2.9b Packet Measurement Order */
|
||||||
|
static struct msgb *create_packet_meas_order(struct ms_anr_fsm_ctx *ctx,
|
||||||
|
const struct gprs_rlcmac_tbf *tbf,
|
||||||
|
uint8_t nco, uint8_t pmo_idx, uint8_t pmo_count,
|
||||||
|
const NC_Frequency_list_t *freq_li)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
int rc;
|
||||||
|
RlcMacDownlink_t *mac_control_block;
|
||||||
|
struct GprsMs *ms = tbf_ms(tbf);
|
||||||
|
bool tfi_asigned, tfi_is_dl;
|
||||||
|
uint8_t tfi;
|
||||||
|
bool exist_nc;
|
||||||
|
uint8_t non_drx_period, nc_report_period_i, nc_report_period_t;
|
||||||
|
|
||||||
|
if (tbf_is_tfi_assigned(tbf)) {
|
||||||
|
tfi_asigned = true;
|
||||||
|
tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
|
||||||
|
tfi = tbf_tfi(tbf);
|
||||||
|
} else {
|
||||||
|
tfi_asigned = false;
|
||||||
|
tfi_is_dl = false;
|
||||||
|
tfi = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_meas_order");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Initialize a bit vector that uses allocated msgb as the data buffer. */
|
||||||
|
struct bitvec bv = {
|
||||||
|
.data = msgb_put(msg, GSM_MACBLOCK_LEN),
|
||||||
|
.data_len = GSM_MACBLOCK_LEN,
|
||||||
|
};
|
||||||
|
bitvec_unhex(&bv, DUMMY_VEC);
|
||||||
|
|
||||||
|
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
|
||||||
|
|
||||||
|
/* First message, set NC Meas Params. As per spec:
|
||||||
|
* "If parameters for the NC measurements are not included, a previous
|
||||||
|
* Packet Measurement Order message belonging to the same set of messages
|
||||||
|
* shall still be valid." */
|
||||||
|
exist_nc = pmo_idx == 0;
|
||||||
|
non_drx_period = 2; /* default value, still need to check */
|
||||||
|
nc_report_period_i = 5;//0;
|
||||||
|
nc_report_period_t = 5;//0;
|
||||||
|
|
||||||
|
|
||||||
|
write_packet_measurement_order(mac_control_block, 0, 0, tfi_asigned, tfi_is_dl,tfi, ms_tlli(ms),
|
||||||
|
pmo_idx, pmo_count, nco, exist_nc, non_drx_period,
|
||||||
|
nc_report_period_i, nc_report_period_t, freq_li);
|
||||||
|
LOGP(DANR, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Measurement Order +++++++++++++++++++++++++\n");
|
||||||
|
rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DANR, LOGL_ERROR, "Encoding of Packet Measurement Order Data failed (%d)\n", rc);
|
||||||
|
goto free_ret;
|
||||||
|
}
|
||||||
|
LOGP(DANR, LOGL_DEBUG, "------------------------- TX : Packet Measurement Order -------------------------\n");
|
||||||
|
rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_MEAS_ORDER]);
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
free_ret:
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
msgb_free(msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_REMOVE_FREQ_PER_MSG 16
|
||||||
|
#define MAX_ADD_FREQ_PER_MSG 5
|
||||||
|
static void build_nc_freq_list(struct ms_anr_fsm_ctx *ctx, NC_Frequency_list_t *freq_li,
|
||||||
|
const uint16_t *freq_to_remove, unsigned *freq_to_remove_idx, unsigned freq_to_remove_cnt,
|
||||||
|
const struct arfcn_bsic *freq_to_add, unsigned *freq_to_add_idx, unsigned freq_to_add_cnt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned to_remove_this_message;
|
||||||
|
LOGP(DANR, LOGL_DEBUG, "Build NC Frequency List:\n");
|
||||||
|
|
||||||
|
/* First, remove all ARFCNs from BS(GPRS): */
|
||||||
|
if (*freq_to_remove_idx < freq_to_remove_cnt) {
|
||||||
|
to_remove_this_message = OSMO_MIN(freq_to_remove_cnt - *freq_to_remove_idx, MAX_REMOVE_FREQ_PER_MSG);
|
||||||
|
freq_li->Exist_REMOVED_FREQ = 1;
|
||||||
|
freq_li->NR_OF_REMOVED_FREQ = to_remove_this_message; /* offset of 1 applied already by CSN1 encoder */
|
||||||
|
for (i = 0; i < to_remove_this_message; i++) {
|
||||||
|
LOGP(DANR, LOGL_DEBUG, "Remove_Frequency[%d] INDEX=%u\n", i, freq_to_remove[*freq_to_remove_idx]);
|
||||||
|
freq_li->Removed_Freq_Index[i].REMOVED_FREQ_INDEX = freq_to_remove[(*freq_to_remove_idx)++];
|
||||||
|
}
|
||||||
|
/* We want in general to first remove all frequencies, and only once we
|
||||||
|
* are done removing, starting adding new ones */
|
||||||
|
if (*freq_to_remove_idx < freq_to_remove_cnt) {
|
||||||
|
freq_li->Count_Add_Frequency = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
to_remove_this_message = 0;
|
||||||
|
freq_li->Exist_REMOVED_FREQ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then, add selected ones for ANR. ctx->cell_list has ARFCNs stored in ascending order */
|
||||||
|
freq_li->Count_Add_Frequency = OSMO_MIN(freq_to_add_cnt - *freq_to_add_idx,
|
||||||
|
MAX_ADD_FREQ_PER_MSG - to_remove_this_message/4);
|
||||||
|
for (i = 0; i < freq_li->Count_Add_Frequency; i++) {
|
||||||
|
freq_li->Add_Frequency[i].START_FREQUENCY = freq_to_add[*freq_to_add_idx].arfcn;
|
||||||
|
freq_li->Add_Frequency[i].BSIC = freq_to_add[*freq_to_add_idx].bsic;
|
||||||
|
freq_li->Add_Frequency[i].Exist_Cell_Selection = 0;
|
||||||
|
freq_li->Add_Frequency[i].NR_OF_FREQUENCIES = 0; /* TODO: optimize here checking if we can fit more with DIFF */
|
||||||
|
freq_li->Add_Frequency[i].FREQ_DIFF_LENGTH = 0;
|
||||||
|
LOGP(DANR, LOGL_DEBUG, "Add_Frequency[%d] START_FREQ=%u BSIC=%u\n", i,
|
||||||
|
freq_li->Add_Frequency[i].START_FREQUENCY,
|
||||||
|
freq_li->Add_Frequency[i].BSIC);
|
||||||
|
(*freq_to_add_idx)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int build_multipart_packet_meas_order(struct ms_anr_fsm_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||||
|
unsigned int i, j;
|
||||||
|
/* TODO: decide early whether to use si2 or si5, and pick is related BA-IND */
|
||||||
|
struct gsm_sysinfo_freq *bcch_freq_list = bts->si2_bcch_cell_list;
|
||||||
|
unsigned int bcch_freq_list_len = ARRAY_SIZE(bts->si2_bcch_cell_list);
|
||||||
|
unsigned int bcch_freq_list_cnt = 0; // Number of freqs in Neigh List */
|
||||||
|
|
||||||
|
unsigned int freq_to_remove_cnt = 0, freq_to_add_cnt = 0;
|
||||||
|
uint16_t freq_to_remove[1024]; /* freq list index */
|
||||||
|
struct arfcn_bsic freq_to_add[1024];
|
||||||
|
|
||||||
|
/* First calculate amount of REMOVE and ADD freq entries, to calculate
|
||||||
|
* required number of bits and hence number of RLCMAC messages */
|
||||||
|
ctx->nc_measurement_list_len = 0;
|
||||||
|
for (i = 0; i < bcch_freq_list_len; i++) {
|
||||||
|
bool bcch_freq_marked = !!bcch_freq_list[i].mask;
|
||||||
|
|
||||||
|
if (bcch_freq_marked) {
|
||||||
|
/* Freqs from BCCH list occupy one slot in the Neighbour
|
||||||
|
* List, even if removed later by NC_FreqList in Pkt
|
||||||
|
* Meas Order */
|
||||||
|
if (ctx->nc_measurement_list_len < ARRAY_SIZE(ctx->nc_measurement_list)) {
|
||||||
|
ctx->nc_measurement_list[ctx->nc_measurement_list_len] = i;
|
||||||
|
ctx->nc_measurement_list_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the ARFCN is in our target ANR subset,
|
||||||
|
* otherwise mark it from removal using Pkt Meas Order */
|
||||||
|
bcch_freq_list_cnt++;
|
||||||
|
bool found = false;
|
||||||
|
for (j = 0; j < ctx->num_cells; j++) {
|
||||||
|
/* early termination, arfcns are in ascending order */
|
||||||
|
if (ctx->cell_list[j].arfcn > i)
|
||||||
|
break;
|
||||||
|
if (ctx->cell_list[j].arfcn == i) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
freq_to_remove[freq_to_remove_cnt] = bcch_freq_list_cnt - 1;
|
||||||
|
freq_to_remove_cnt++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (j = 0; j < ctx->num_cells; j++) {
|
||||||
|
/* early termination, arfcns are in ascending order */
|
||||||
|
if (ctx->cell_list[j].arfcn > i)
|
||||||
|
break;
|
||||||
|
if (ctx->cell_list[j].arfcn == i) {
|
||||||
|
freq_to_add[freq_to_add_cnt] = ctx->cell_list[j];
|
||||||
|
freq_to_add_cnt++;
|
||||||
|
/* Don't break here, there may be several ARFCN=N with different BSIC */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGPFSML(ctx->fi, LOGL_DEBUG, "NC_freq_list to_remove=%u to_add=%u\n", freq_to_remove_cnt, freq_to_add_cnt);
|
||||||
|
|
||||||
|
/* Added frequency through Pkt Meas Order NC Freq list are indexed after existing arfcns from BA(GPRS) */
|
||||||
|
for (i = 0; i < freq_to_add_cnt; i++) {
|
||||||
|
if (ctx->nc_measurement_list_len >= ARRAY_SIZE(ctx->nc_measurement_list))
|
||||||
|
break;
|
||||||
|
ctx->nc_measurement_list[ctx->nc_measurement_list_len] = freq_to_add[i].arfcn;
|
||||||
|
ctx->nc_measurement_list_len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pmo_index;
|
||||||
|
uint8_t pmo_count = 0;
|
||||||
|
unsigned int freq_to_remove_idx = 0, freq_to_add_idx = 0;
|
||||||
|
NC_Frequency_list_t freq_li[8];
|
||||||
|
do {
|
||||||
|
OSMO_ASSERT(pmo_count < ARRAY_SIZE(freq_li)); /* TODO: print something here*/
|
||||||
|
build_nc_freq_list(ctx, &freq_li[pmo_count],
|
||||||
|
freq_to_remove, &freq_to_remove_idx, freq_to_remove_cnt,
|
||||||
|
freq_to_add, &freq_to_add_idx, freq_to_add_cnt);
|
||||||
|
pmo_count++;
|
||||||
|
} while (freq_to_remove_idx < freq_to_remove_cnt || freq_to_add_idx < freq_to_add_cnt);
|
||||||
|
|
||||||
|
/* Now build messages */
|
||||||
|
for (pmo_index = 0; pmo_index < pmo_count; pmo_index++) {
|
||||||
|
struct msgb *msg = create_packet_meas_order(ctx, ctx->tbf, NC_1, pmo_index, pmo_count - 1, &freq_li[pmo_index]);
|
||||||
|
llist_add_tail(&msg->list, &ctx->meas_order_queue);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TS 44 060 11.2.9b Packet Measurement Order */
|
||||||
|
static struct msgb *create_packet_meas_order_reset(struct ms_anr_fsm_ctx *ctx,
|
||||||
|
struct ms_anr_ev_create_rlcmac_msg_ctx *data,
|
||||||
|
uint32_t *new_poll_fn)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
int rc;
|
||||||
|
RlcMacDownlink_t *mac_control_block;
|
||||||
|
struct gprs_rlcmac_tbf *tbf = ctx->tbf;
|
||||||
|
struct GprsMs *ms = tbf_ms(tbf);
|
||||||
|
bool tfi_asigned, tfi_is_dl;
|
||||||
|
uint8_t tfi;
|
||||||
|
uint8_t pmo_idx = 0, pmo_count = 0;
|
||||||
|
uint8_t nco = NC_RESET;
|
||||||
|
unsigned int rrbp;
|
||||||
|
|
||||||
|
if (tbf_is_tfi_assigned(tbf)) {
|
||||||
|
tfi_asigned = true;
|
||||||
|
tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
|
||||||
|
tfi = tbf_tfi(tbf);
|
||||||
|
} else {
|
||||||
|
tfi_asigned = false;
|
||||||
|
tfi_is_dl = false;
|
||||||
|
tfi = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = tbf_check_polling(tbf, data->fn, data->ts, new_poll_fn, &rrbp);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DANR, LOGL_ERROR, "Failed registering poll for Packet Measurement Order (reset) (%d)\n", rc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_meas_order");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Initialize a bit vector that uses allocated msgb as the data buffer. */
|
||||||
|
struct bitvec bv = {
|
||||||
|
.data = msgb_put(msg, GSM_MACBLOCK_LEN),
|
||||||
|
.data_len = GSM_MACBLOCK_LEN,
|
||||||
|
};
|
||||||
|
bitvec_unhex(&bv, DUMMY_VEC);
|
||||||
|
|
||||||
|
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
|
||||||
|
|
||||||
|
write_packet_measurement_order(mac_control_block, 1, rrbp, tfi_asigned, tfi_is_dl,tfi, ms_tlli(ms),
|
||||||
|
pmo_idx, pmo_count, nco, false, 0, 0, 0, NULL);
|
||||||
|
LOGP(DANR, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Measurement Order (reset) FN=%" PRIu32 " +++++++++++++++++++++++++\n", data->fn);
|
||||||
|
rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DANR, LOGL_ERROR, "Encoding of Packet Measurement Order Data failed (%d)\n", rc);
|
||||||
|
goto free_ret;
|
||||||
|
}
|
||||||
|
LOGP(DANR, LOGL_DEBUG, "------------------------- TX : Packet Measurement Order (reset) POLL_FN=%" PRIu32 "-------------------------\n", *new_poll_fn);
|
||||||
|
rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_MEAS_ORDER]);
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
|
||||||
|
tbf_set_polling(tbf, *new_poll_fn, data->ts, PDCH_ULC_POLL_MEAS_ORDER);
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
free_ret:
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
msgb_free(msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_meas_report(struct ms_anr_fsm_ctx *ctx, uint8_t *meas, const Packet_Measurement_Report_t *pmr)
|
||||||
|
{
|
||||||
|
//TODO: transmit meas back to BSC
|
||||||
|
const NC_Measurement_Report_t *ncr;
|
||||||
|
int i, j;
|
||||||
|
memset(meas, 0xff, ctx->num_cells);
|
||||||
|
|
||||||
|
switch (pmr->UnionType) {
|
||||||
|
case 0: break;
|
||||||
|
case 1: /* EXT Reporting, should not happen */
|
||||||
|
default:
|
||||||
|
LOGPFSML(ctx->fi, LOGL_NOTICE, "EXT Reporting not supported!\n");
|
||||||
|
osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ncr = &pmr->u.NC_Measurement_Report;
|
||||||
|
LOGPFSML(ctx->fi, LOGL_NOTICE, "Rx MEAS REPORT %u neighbours\n", ncr->NUMBER_OF_NC_MEASUREMENTS);
|
||||||
|
|
||||||
|
for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) {
|
||||||
|
const NC_Measurements_t *nc = &ncr->NC_Measurements[i];
|
||||||
|
/* infer ARFCN from FREQUENCY_N using previously calculated data: */
|
||||||
|
OSMO_ASSERT(nc->FREQUENCY_N < ARRAY_SIZE(ctx->nc_measurement_list));
|
||||||
|
uint16_t arfcn = ctx->nc_measurement_list[nc->FREQUENCY_N];
|
||||||
|
LOGPFSML(ctx->fi, LOGL_DEBUG, "Neigh arfcn_index=%u arfcn=%u bsic=%d %d dBm\n",
|
||||||
|
nc->FREQUENCY_N, arfcn, nc->Exist_BSIC_N ? nc->BSIC_N : -1, nc->RXLEV_N - 110);
|
||||||
|
if (!nc->Exist_BSIC_N)
|
||||||
|
continue; /* Skip measurement without BSIC, since there could be several cells with same ARFCN */
|
||||||
|
for (j = 0; j < ctx->num_cells; j++) {
|
||||||
|
if (ctx->cell_list[j].arfcn != arfcn || ctx->cell_list[j].bsic != nc->BSIC_N)
|
||||||
|
continue;
|
||||||
|
meas[j] = nc->RXLEV_N;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j == ctx->num_cells) {
|
||||||
|
LOGPFSML(ctx->fi, LOGL_NOTICE,
|
||||||
|
"Neigh arfcn_index=%u arfcn=%u bsic=%u %d dBm not found in target cell list!\n",
|
||||||
|
nc->FREQUENCY_N, arfcn, nc->BSIC_N, nc->RXLEV_N - 110);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// FSM states //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
//struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||||
|
const struct ms_anr_ev_start *start_data;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case MS_ANR_EV_START:
|
||||||
|
start_data = (const struct ms_anr_ev_start *)data;
|
||||||
|
/* Copy over cell list on which to ask for measurements */
|
||||||
|
OSMO_ASSERT(start_data->tbf);
|
||||||
|
ctx->tbf = start_data->tbf;
|
||||||
|
OSMO_ASSERT(start_data->num_cells <= ARRAY_SIZE(ctx->cell_list));
|
||||||
|
ctx->num_cells = start_data->num_cells;
|
||||||
|
if (start_data->num_cells)
|
||||||
|
memcpy(ctx->cell_list, start_data->cell_list, start_data->num_cells * sizeof(start_data->cell_list[0]));
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_tx_pkt_meas_reset1(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case MS_ANR_EV_CREATE_RLCMAC_MSG:
|
||||||
|
/* Set NC to RESET and drop NC_Freq_list for MS to go back to
|
||||||
|
network defaults. */
|
||||||
|
data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data;
|
||||||
|
data_ctx->msg = create_packet_meas_order_reset(ctx, data_ctx, &ctx->poll_fn);
|
||||||
|
if (!data_ctx->msg) {
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx->poll_ts = data_ctx->ts;
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_CTRL_ACK1);
|
||||||
|
break;
|
||||||
|
case MS_ANR_EV_RX_PKT_MEAS_REPORT:
|
||||||
|
/* Ignore Meas Report from previously (potentially unfinished) prcoedures */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_wait_ctrl_ack1(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
//struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case MS_ANR_EV_RX_PKT_CTRL_ACK_MSG:
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_ORDER);
|
||||||
|
break;
|
||||||
|
case MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT:
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET1);
|
||||||
|
break;
|
||||||
|
case MS_ANR_EV_RX_PKT_MEAS_REPORT:
|
||||||
|
/* Ignore Meas Report from previously (potentially unfinished) prcoedures */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_tx_pkt_meas_order_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
build_multipart_packet_meas_order(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_tx_pkt_meas_order(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case MS_ANR_EV_CREATE_RLCMAC_MSG:
|
||||||
|
data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data;
|
||||||
|
/* Set NC to 1 to force MS to send us Meas Rep */
|
||||||
|
data_ctx->msg = llist_first_entry(&ctx->meas_order_queue, struct msgb, list);
|
||||||
|
if (!data_ctx->msg) {
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
llist_del(&data_ctx->msg->list);
|
||||||
|
if (llist_empty(&ctx->meas_order_queue)) /* DONE */
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_PKT_MEAS_REPORT);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_wait_rx_pkt_meas_report_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
/* DO NOTHING */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void st_wait_rx_pkt_meas_report(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
uint8_t *meas = alloca(ctx->num_cells);
|
||||||
|
struct ms_anr_ev_meas_compl ev_compl = {
|
||||||
|
.cell_list = ctx->cell_list,
|
||||||
|
.meas_list = meas,
|
||||||
|
.num_cells = ctx->num_cells,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case MS_ANR_EV_RX_PKT_MEAS_REPORT:
|
||||||
|
handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data);
|
||||||
|
osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl);
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_TX_PKT_MEAS_RESET2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_tx_pkt_meas_reset2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
struct ms_anr_ev_create_rlcmac_msg_ctx *data_ctx;
|
||||||
|
uint8_t *meas = alloca(ctx->num_cells);
|
||||||
|
struct ms_anr_ev_meas_compl ev_compl = {
|
||||||
|
.cell_list = ctx->cell_list,
|
||||||
|
.meas_list = meas,
|
||||||
|
.num_cells = ctx->num_cells,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case MS_ANR_EV_CREATE_RLCMAC_MSG:
|
||||||
|
/* Set NC to RESET and drop NC_Freq_list for MS to go back to
|
||||||
|
network defaults. */
|
||||||
|
data_ctx = (struct ms_anr_ev_create_rlcmac_msg_ctx *)data;
|
||||||
|
data_ctx->msg = create_packet_meas_order_reset(ctx, data_ctx, &ctx->poll_fn);
|
||||||
|
if (!data_ctx->msg) {
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx->poll_ts = data_ctx->ts;
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_WAIT_CTRL_ACK2);
|
||||||
|
break;
|
||||||
|
case MS_ANR_EV_RX_PKT_MEAS_REPORT:
|
||||||
|
handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data);
|
||||||
|
osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_wait_ctrl_ack2(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
uint8_t *meas = alloca(ctx->num_cells);
|
||||||
|
struct ms_anr_ev_meas_compl ev_compl = {
|
||||||
|
.cell_list = ctx->cell_list,
|
||||||
|
.meas_list = meas,
|
||||||
|
.num_cells = ctx->num_cells,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case MS_ANR_EV_RX_PKT_CTRL_ACK_MSG:
|
||||||
|
ms_anr_fsm_state_chg(fi, MS_ANR_ST_DONE);
|
||||||
|
break;
|
||||||
|
case MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT:
|
||||||
|
ms_anr_fsm_state_chg(fi, fi->state == MS_ANR_ST_WAIT_CTRL_ACK1 ?
|
||||||
|
MS_ANR_ST_TX_PKT_MEAS_RESET1 :
|
||||||
|
MS_ANR_ST_TX_PKT_MEAS_RESET2);
|
||||||
|
break;
|
||||||
|
case MS_ANR_EV_RX_PKT_MEAS_REPORT:
|
||||||
|
/* We may keep receiving meas report from MS while waiting to
|
||||||
|
* receive the CTRL ACK: */
|
||||||
|
handle_meas_report(ctx, meas, (const Packet_Measurement_Report_t *)data);
|
||||||
|
osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_COMPL, &ev_compl);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_done_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ms_anr_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = (struct ms_anr_fsm_ctx *)fi->priv;
|
||||||
|
|
||||||
|
/* after cleanup() finishes, FSM termination calls osmo_fsm_inst_free,
|
||||||
|
so we need to avoid double-freeing it during ctx talloc free
|
||||||
|
destructor */
|
||||||
|
talloc_reparent(ctx, ctx->ms, ctx->fi);
|
||||||
|
ctx->fi = NULL;
|
||||||
|
|
||||||
|
/* remove references from owning MS and free entire ctx */
|
||||||
|
ctx->ms->anr = NULL;
|
||||||
|
|
||||||
|
if (cause != OSMO_FSM_TERM_REGULAR && cause != OSMO_FSM_TERM_REQUEST) {
|
||||||
|
/* Signal to bts_anr_fsm that orchestrates us that we failed, so
|
||||||
|
* that it can schedule the procedure again */
|
||||||
|
struct ms_anr_ev_abort ev_data = {
|
||||||
|
.cell_list = &ctx->cell_list[0],
|
||||||
|
.num_cells = ctx->num_cells,
|
||||||
|
};
|
||||||
|
osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_ABORTED, &ev_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
talloc_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ms_anr_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||||
|
{
|
||||||
|
switch (fi->T) {
|
||||||
|
case PCU_TDEF_ANR_MS_TIMEOUT:
|
||||||
|
LOGPFSML(fi, LOGL_NOTICE, "ms_anr_fsm got stuck, freeing it. This probably indicates a bug somehwere (if not in state WAIT_PKT_MEAS_REPORT)\n");
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct osmo_fsm_state ms_anr_fsm_states[] = {
|
||||||
|
[MS_ANR_ST_INITIAL] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(MS_ANR_EV_START),
|
||||||
|
.out_state_mask =
|
||||||
|
X(MS_ANR_ST_TX_PKT_MEAS_RESET1),
|
||||||
|
.name = "INITIAL",
|
||||||
|
.action = st_initial,
|
||||||
|
},
|
||||||
|
[MS_ANR_ST_TX_PKT_MEAS_RESET1] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(MS_ANR_EV_CREATE_RLCMAC_MSG) |
|
||||||
|
X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
|
||||||
|
.out_state_mask =
|
||||||
|
X(MS_ANR_ST_WAIT_CTRL_ACK1),
|
||||||
|
.name = "TX_PKT_MEAS_RESET1",
|
||||||
|
.action = st_tx_pkt_meas_reset1,
|
||||||
|
},
|
||||||
|
[MS_ANR_ST_WAIT_CTRL_ACK1] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(MS_ANR_EV_RX_PKT_CTRL_ACK_MSG) |
|
||||||
|
X(MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT) |
|
||||||
|
X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
|
||||||
|
.out_state_mask =
|
||||||
|
X(MS_ANR_ST_TX_PKT_MEAS_RESET1) |
|
||||||
|
X(MS_ANR_ST_TX_PKT_MEAS_ORDER),
|
||||||
|
.name = "WAIT_CTRL_ACK1",
|
||||||
|
.action = st_wait_ctrl_ack1,
|
||||||
|
},
|
||||||
|
[MS_ANR_ST_TX_PKT_MEAS_ORDER] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(MS_ANR_EV_CREATE_RLCMAC_MSG),
|
||||||
|
.out_state_mask =
|
||||||
|
X(MS_ANR_ST_WAIT_PKT_MEAS_REPORT),
|
||||||
|
.name = "TX_PKT_MEAS_ORDER",
|
||||||
|
.onenter = st_tx_pkt_meas_order_on_enter,
|
||||||
|
.action = st_tx_pkt_meas_order,
|
||||||
|
},
|
||||||
|
[MS_ANR_ST_WAIT_PKT_MEAS_REPORT] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
|
||||||
|
.out_state_mask =
|
||||||
|
X(MS_ANR_ST_TX_PKT_MEAS_RESET2),
|
||||||
|
.name = "WAIT_PKT_MEAS_REPORT",
|
||||||
|
.onenter = st_wait_rx_pkt_meas_report_on_enter,
|
||||||
|
.action = st_wait_rx_pkt_meas_report,
|
||||||
|
},
|
||||||
|
[MS_ANR_ST_TX_PKT_MEAS_RESET2] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(MS_ANR_EV_CREATE_RLCMAC_MSG) |
|
||||||
|
X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
|
||||||
|
.out_state_mask =
|
||||||
|
X(MS_ANR_ST_WAIT_CTRL_ACK2),
|
||||||
|
.name = "TX_PKT_MEAS_RESET2",
|
||||||
|
.action = st_tx_pkt_meas_reset2,
|
||||||
|
},
|
||||||
|
[MS_ANR_ST_WAIT_CTRL_ACK2] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(MS_ANR_EV_RX_PKT_CTRL_ACK_MSG) |
|
||||||
|
X(MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT) |
|
||||||
|
X(MS_ANR_EV_RX_PKT_MEAS_REPORT),
|
||||||
|
.out_state_mask =
|
||||||
|
X(MS_ANR_ST_TX_PKT_MEAS_RESET2) |
|
||||||
|
X(MS_ANR_ST_DONE),
|
||||||
|
.name = "WAIT_CTRL_ACK2",
|
||||||
|
.action = st_wait_ctrl_ack2,
|
||||||
|
},
|
||||||
|
[MS_ANR_ST_DONE] = {
|
||||||
|
.in_event_mask = 0,
|
||||||
|
.out_state_mask = 0,
|
||||||
|
.name = "DONE",
|
||||||
|
.onenter = st_done_on_enter,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct osmo_fsm ms_anr_fsm = {
|
||||||
|
.name = "MS_ANR",
|
||||||
|
.states = ms_anr_fsm_states,
|
||||||
|
.num_states = ARRAY_SIZE(ms_anr_fsm_states),
|
||||||
|
.timer_cb = ms_anr_fsm_timer_cb,
|
||||||
|
.cleanup = ms_anr_fsm_cleanup,
|
||||||
|
.log_subsys = DANR,
|
||||||
|
.event_names = ms_anr_fsm_event_names,
|
||||||
|
};
|
||||||
|
|
||||||
|
static __attribute__((constructor)) void ms_anr_fsm_init(void)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(osmo_fsm_register(&ms_anr_fsm) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ms_anr_fsm_ctx_talloc_destructor(struct ms_anr_fsm_ctx *ctx)
|
||||||
|
{
|
||||||
|
/* if ctx->fi != NULL it means we come directly from talloc_free(ctx)
|
||||||
|
* without having passed through ms_anr_fsm_cleanup() as part of
|
||||||
|
* osmo_fsm_inst_term(). In this case, clean up manually similarly to
|
||||||
|
* ms_anr_fsm_cleanup() and free the ctx->fi. */
|
||||||
|
if (ctx->fi) {
|
||||||
|
/* Signal to bts_anr_fsm that orchestrates us that we failed, so
|
||||||
|
* that it can schedule the procedure again */
|
||||||
|
struct ms_anr_ev_abort ev_data = {
|
||||||
|
.cell_list = &ctx->cell_list[0],
|
||||||
|
.num_cells = ctx->num_cells,
|
||||||
|
};
|
||||||
|
osmo_fsm_inst_dispatch(ctx->ms->bts->anr->fi, BTS_ANR_EV_MS_MEAS_ABORTED, &ev_data);
|
||||||
|
osmo_fsm_inst_free(ctx->fi);
|
||||||
|
ctx->fi = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ms_anr_fsm_ctx *ms_anr_fsm_alloc(struct GprsMs* ms)
|
||||||
|
{
|
||||||
|
struct ms_anr_fsm_ctx *ctx = talloc_zero(ms, struct ms_anr_fsm_ctx);
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
talloc_set_destructor(ctx, ms_anr_fsm_ctx_talloc_destructor);
|
||||||
|
|
||||||
|
ctx->ms = ms;
|
||||||
|
INIT_LLIST_HEAD(&ctx->meas_order_queue);
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "TLLI-0x%08x", ms_tlli(ms));
|
||||||
|
ctx->fi = osmo_fsm_inst_alloc(&ms_anr_fsm, ctx, ctx, LOGL_INFO, buf);
|
||||||
|
if (!ctx->fi)
|
||||||
|
goto free_ret;
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
free_ret:
|
||||||
|
talloc_free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used by bts_anr_fsm to abort ongoing procedure without need of being informed
|
||||||
|
* back by BTS_ANR_EV_MS_MEAS_ABORTED */
|
||||||
|
void ms_anr_fsm_abort(struct ms_anr_fsm_ctx *ctx)
|
||||||
|
{
|
||||||
|
osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||||
|
}
|
||||||
90
src/ms_anr_fsm.h
Normal file
90
src/ms_anr_fsm.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/* ms_anr_fsm.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/fsm.h>
|
||||||
|
#include <osmocom/gsm/gsm23003.h>
|
||||||
|
|
||||||
|
#include "pcu_utils.h"
|
||||||
|
|
||||||
|
struct GprsMs;
|
||||||
|
struct gprs_rlcmac_tbf;
|
||||||
|
|
||||||
|
#define MAX_NEIGH_LIST_LEN 96
|
||||||
|
#define MAX_NEIGH_MEAS_LIST_LEN 32
|
||||||
|
|
||||||
|
enum ms_anr_fsm_event {
|
||||||
|
MS_ANR_EV_START, /* data: struct ms_anr_ev_start */
|
||||||
|
MS_ANR_EV_CREATE_RLCMAC_MSG, /* data: struct anr_ev_create_rlcmac_msg_ctx* */
|
||||||
|
MS_ANR_EV_RX_PKT_MEAS_REPORT, /* data: Packet_Measurement_Report_t */
|
||||||
|
MS_ANR_EV_RX_PKT_CTRL_ACK_MSG,
|
||||||
|
MS_ANR_EV_RX_PKT_CTRL_ACK_TIMEOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ms_anr_fsm_states {
|
||||||
|
MS_ANR_ST_INITIAL,
|
||||||
|
MS_ANR_ST_TX_PKT_MEAS_RESET1,
|
||||||
|
MS_ANR_ST_WAIT_CTRL_ACK1,
|
||||||
|
MS_ANR_ST_TX_PKT_MEAS_ORDER,
|
||||||
|
MS_ANR_ST_WAIT_PKT_MEAS_REPORT,
|
||||||
|
MS_ANR_ST_TX_PKT_MEAS_RESET2,
|
||||||
|
MS_ANR_ST_WAIT_CTRL_ACK2,
|
||||||
|
MS_ANR_ST_DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ms_anr_fsm_ctx {
|
||||||
|
struct osmo_fsm_inst *fi;
|
||||||
|
struct GprsMs* ms; /* back pointer */
|
||||||
|
struct gprs_rlcmac_tbf *tbf; /* target tbf to create messages for, selected upon first MS_ANR_EV_CREATE_RLCMAC_MSG */
|
||||||
|
struct arfcn_bsic cell_list[MAX_NEIGH_MEAS_LIST_LEN]; /* ordered by ascending ARFCN */
|
||||||
|
unsigned int num_cells;
|
||||||
|
struct llist_head meas_order_queue; /* list of msgb PMO_IDX=0..PMO_COUNT */
|
||||||
|
uint16_t nc_measurement_list[MAX_NEIGH_LIST_LEN]; /* Used to infer ARFCN from Frequency index received at Measurement Report */
|
||||||
|
unsigned int nc_measurement_list_len;
|
||||||
|
uint32_t poll_fn; /* Scheduled poll FN to CTRL ACK the Pkt Meas Order (reset) */
|
||||||
|
uint8_t poll_ts; /* Scheduled poll TS to CTRL ACK the Pkt Meas Order (reset */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* passed as data in MS_ANR_EV_START */
|
||||||
|
struct ms_anr_ev_start {
|
||||||
|
struct gprs_rlcmac_tbf *tbf; /* target DL TBF to create messages for */
|
||||||
|
const struct arfcn_bsic* cell_list;
|
||||||
|
unsigned int num_cells;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* passed as data in MS_ANR_EV_CREATE_RLCMAC_MSG */
|
||||||
|
struct ms_anr_ev_create_rlcmac_msg_ctx {
|
||||||
|
//struct gprs_rlcmac_tbf *tbf; /* target DL TBF to create messages for */
|
||||||
|
uint32_t fn; /* FN where the created DL ctrl block is to be sent */
|
||||||
|
uint8_t ts; /* TS where the created DL ctrl block is to be sent */
|
||||||
|
struct msgb *msg; /* to be filled by FSM during event processing */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ms_anr_fsm_ctx *ms_anr_fsm_alloc(struct GprsMs* ms);
|
||||||
|
void ms_anr_fsm_abort(struct ms_anr_fsm_ctx *ctx);
|
||||||
|
|
||||||
|
/* 3GPP TS44.060 Table 11.2.4.2: PACKET CELL CHANGE ORDER
|
||||||
|
* 3GPP TS 45.008 10.1.4 Network controlled Cell re-selection*/
|
||||||
|
enum network_control_order {
|
||||||
|
NC_0 = 0x00,
|
||||||
|
NC_1 = 0x01,
|
||||||
|
NC_2 = 0x02,
|
||||||
|
NC_RESET = 0x03
|
||||||
|
};
|
||||||
297
src/mslot_class.c
Normal file
297
src/mslot_class.c
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
/* mslot_class.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
* Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mslot_class.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/bits.h>
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* 3GPP TS 45.002 Annex B Table B.1 */
|
||||||
|
|
||||||
|
struct gprs_ms_multislot_class {
|
||||||
|
uint8_t rx, tx, sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
|
||||||
|
uint8_t ta, tb, ra, rb; /* Minimum Number of Slots */
|
||||||
|
uint8_t type; /* Type of Mobile */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct gprs_ms_multislot_class gprs_ms_multislot_class[] = {
|
||||||
|
/* M-S Class | Max # of slots | Min # of slots | Type */
|
||||||
|
/* | Rx Tx Sum | Tta Ttb Tra Trb | */
|
||||||
|
/* N/A */ { MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
|
||||||
|
/* 1 */ { 1, 1, 2, 3, 2, 4, 2, 1 },
|
||||||
|
/* 2 */ { 2, 1, 3, 3, 2, 3, 1, 1 },
|
||||||
|
/* 3 */ { 2, 2, 3, 3, 2, 3, 1, 1 },
|
||||||
|
/* 4 */ { 3, 1, 4, 3, 1, 3, 1, 1 },
|
||||||
|
/* 5 */ { 2, 2, 4, 3, 1, 3, 1, 1 },
|
||||||
|
/* 6 */ { 3, 2, 4, 3, 1, 3, 1, 1 },
|
||||||
|
/* 7 */ { 3, 3, 4, 3, 1, 3, 1, 1 },
|
||||||
|
/* 8 */ { 4, 1, 5, 3, 1, 2, 1, 1 },
|
||||||
|
/* 9 */ { 3, 2, 5, 3, 1, 2, 1, 1 },
|
||||||
|
/* 10 */ { 4, 2, 5, 3, 1, 2, 1, 1 },
|
||||||
|
/* 11 */ { 4, 3, 5, 3, 1, 2, 1, 1 },
|
||||||
|
/* 12 */ { 4, 4, 5, 2, 1, 2, 1, 1 },
|
||||||
|
/* 13 */ { 3, 3, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
|
||||||
|
/* 14 */ { 4, 4, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
|
||||||
|
/* 15 */ { 5, 5, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
|
||||||
|
/* 16 */ { 6, 6, MS_NA, MS_NA, MS_A, 2, MS_A, 2 },
|
||||||
|
/* 17 */ { 7, 7, MS_NA, MS_NA, MS_A, 1, 0, 2 },
|
||||||
|
/* 18 */ { 8, 8, MS_NA, MS_NA, 0, 0, 0, 2 },
|
||||||
|
/* 19 */ { 6, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 20 */ { 6, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 21 */ { 6, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 22 */ { 6, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 23 */ { 6, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 24 */ { 8, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 25 */ { 8, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 26 */ { 8, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 27 */ { 8, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 28 */ { 8, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 29 */ { 8, 8, MS_NA, 2, MS_B, 2, MS_C, 1 },
|
||||||
|
/* 30 */ { 5, 1, 6, 2, 1, 1, 1, 1 },
|
||||||
|
/* 31 */ { 5, 2, 6, 2, 1, 1, 1, 1 },
|
||||||
|
/* 32 */ { 5, 3, 6, 2, 1, 1, 1, 1 },
|
||||||
|
/* 33 */ { 5, 4, 6, 2, 1, 1, 1, 1 },
|
||||||
|
/* 34 */ { 5, 5, 6, 2, 1, 1, 1, 1 },
|
||||||
|
/* 35 */ { 5, 1, 6, 2, 1, MS_TO, 1, 1 },
|
||||||
|
/* 36 */ { 5, 2, 6, 2, 1, MS_TO, 1, 1 },
|
||||||
|
/* 37 */ { 5, 3, 6, 2, 1, MS_TO, 1, 1 },
|
||||||
|
/* 38 */ { 5, 4, 6, 2, 1, MS_TO, 1, 1 },
|
||||||
|
/* 39 */ { 5, 5, 6, 2, 1, MS_TO, 1, 1 },
|
||||||
|
/* 40 */ { 6, 1, 7, 1, 1, 1, MS_TO, 1 },
|
||||||
|
/* 41 */ { 6, 2, 7, 1, 1, 1, MS_TO, 1 },
|
||||||
|
/* 42 */ { 6, 3, 7, 1, 1, 1, MS_TO, 1 },
|
||||||
|
/* 43 */ { 6, 4, 7, 1, 1, 1, MS_TO, 1 },
|
||||||
|
/* 44 */ { 6, 5, 7, 1, 1, 1, MS_TO, 1 },
|
||||||
|
/* 45 */ { 6, 6, 7, 1, 1, 1, MS_TO, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const struct gprs_ms_multislot_class *get_mslot_table(uint8_t ms_cl)
|
||||||
|
{
|
||||||
|
uint8_t index = ms_cl ? ms_cl : DEFAULT_MSLOT_CLASS;
|
||||||
|
|
||||||
|
if (ms_cl >= ARRAY_SIZE(gprs_ms_multislot_class))
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
return &gprs_ms_multislot_class[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_max()
|
||||||
|
{
|
||||||
|
return ARRAY_SIZE(gprs_ms_multislot_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_get_ta(uint8_t ms_cl)
|
||||||
|
{
|
||||||
|
return get_mslot_table(ms_cl)->ta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Set it to 1 if FH is implemented and enabled
|
||||||
|
* MS_A and MS_B are 0 iff FH is disabled and there is no Tx/Rx change.
|
||||||
|
* This is never the case with the current implementation, so 1 will always be used. */
|
||||||
|
uint8_t mslot_class_get_tb(uint8_t ms_cl)
|
||||||
|
{
|
||||||
|
const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
|
||||||
|
|
||||||
|
switch (t->tb) {
|
||||||
|
case MS_A:
|
||||||
|
return 0;
|
||||||
|
case MS_B:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return t->tb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta)
|
||||||
|
{
|
||||||
|
const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
|
||||||
|
|
||||||
|
switch (t->ra) {
|
||||||
|
case MS_TO:
|
||||||
|
return ta + 1;
|
||||||
|
default:
|
||||||
|
return t->ra;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta)
|
||||||
|
{
|
||||||
|
const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
|
||||||
|
|
||||||
|
switch (t->rb) {
|
||||||
|
case MS_A:
|
||||||
|
return 0;
|
||||||
|
case MS_C:
|
||||||
|
return 1;
|
||||||
|
case MS_TO:
|
||||||
|
return ta;
|
||||||
|
default:
|
||||||
|
return t->rb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_get_tx(uint8_t ms_cl)
|
||||||
|
{
|
||||||
|
return get_mslot_table(ms_cl)->tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_get_rx(uint8_t ms_cl)
|
||||||
|
{
|
||||||
|
return get_mslot_table(ms_cl)->rx;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_get_sum(uint8_t ms_cl)
|
||||||
|
{
|
||||||
|
return get_mslot_table(ms_cl)->sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mslot_class_get_type(uint8_t ms_cl)
|
||||||
|
{
|
||||||
|
return get_mslot_table(ms_cl)->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Fill in RX mask table for a given MS Class
|
||||||
|
*
|
||||||
|
* \param[in] ms_cl MS Class pointer
|
||||||
|
* \param[in] num_tx Number of TX slots to consider
|
||||||
|
* \param[out] rx_mask RX mask table
|
||||||
|
*/
|
||||||
|
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask)
|
||||||
|
{
|
||||||
|
static const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" };
|
||||||
|
uint8_t Tx = mslot_class_get_tx(mslot_class), /* Max number of Tx slots */
|
||||||
|
Sum = mslot_class_get_sum(mslot_class), /* Max number of Tx + Rx slots */
|
||||||
|
Type = mslot_class_get_type(mslot_class), /* Type of Mobile */
|
||||||
|
Tta = mslot_class_get_ta(mslot_class), /* Minimum number of slots */
|
||||||
|
Ttb = mslot_class_get_tb(mslot_class),
|
||||||
|
/* FIXME: use actual TA offset for computation - make sure to adjust "1 + MS_TO" accordingly
|
||||||
|
see also "Offset required" bit in 3GPP TS 24.008 §10.5.1.7 */
|
||||||
|
Tra = mslot_class_get_ra(mslot_class, 0),
|
||||||
|
Trb = mslot_class_get_rb(mslot_class, 0);
|
||||||
|
|
||||||
|
if (num_tx == 1) /* it's enough to log this once per TX slot set iteration */
|
||||||
|
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||||
|
"Rx=%d Tx=%d Sum Rx+Tx=%s, Tta=%s Ttb=%d, Tra=%d Trb=%d, Type=%d\n",
|
||||||
|
mslot_class_get_rx(mslot_class), Tx,
|
||||||
|
(Sum == MS_NA) ? "N/A" : digit[Sum],
|
||||||
|
(Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type);
|
||||||
|
|
||||||
|
if (Type == 1) {
|
||||||
|
rx_mask[MASK_TT] = (0x100 >> OSMO_MAX(Ttb, Tta)) - 1;
|
||||||
|
rx_mask[MASK_TT] &= ~((1 << (Trb + num_tx)) - 1);
|
||||||
|
rx_mask[MASK_TR] = (0x100 >> Ttb) - 1;
|
||||||
|
rx_mask[MASK_TR] &= ~((1 << (OSMO_MAX(Trb, Tra) + num_tx)) - 1);
|
||||||
|
} else {
|
||||||
|
/* Class type 2 MS have independant RX and TX */
|
||||||
|
rx_mask[MASK_TT] = 0xff;
|
||||||
|
rx_mask[MASK_TR] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
rx_mask[MASK_TT] = (rx_mask[MASK_TT] << 3) | (rx_mask[MASK_TT] >> 5);
|
||||||
|
rx_mask[MASK_TR] = (rx_mask[MASK_TR] << 3) | (rx_mask[MASK_TR] >> 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look for USF, don't use USF=7 */
|
||||||
|
int8_t find_free_usf(uint8_t usf_map)
|
||||||
|
{
|
||||||
|
uint8_t usf;
|
||||||
|
|
||||||
|
if (usf_map == (1 << 7) - 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (usf = 0; usf < 7; usf++) {
|
||||||
|
if (!(usf_map & (1 << usf)))
|
||||||
|
return usf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look for USF, don't use USF=7 */
|
||||||
|
int8_t find_free_tfi(uint32_t tfi_map)
|
||||||
|
{
|
||||||
|
int8_t tfi;
|
||||||
|
|
||||||
|
if (tfi_map == NO_FREE_TFI)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (tfi = 0; tfi < 32; tfi++) {
|
||||||
|
if (!(tfi_map & (((uint32_t)1) << tfi)))
|
||||||
|
return tfi;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void masked_override_with(char *buf, uint8_t mask, char set_char)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; mask; i++, mask >>= 1)
|
||||||
|
if (mask & 1)
|
||||||
|
buf[i] = set_char;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask)
|
||||||
|
{
|
||||||
|
snprintf(buf, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(dl_mask, 'D'));
|
||||||
|
masked_override_with(buf, ul_mask, 'U');
|
||||||
|
masked_override_with(buf, ul_mask & dl_mask, 'C');
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t mslot_wrap_window(uint16_t win)
|
||||||
|
{
|
||||||
|
return (win | win >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem)
|
||||||
|
{
|
||||||
|
bool was_set = bits[elem/32] & (((uint32_t)1) << (elem % 32));
|
||||||
|
bits[elem/32] |= (((uint32_t)1) << (elem % 32));
|
||||||
|
|
||||||
|
return was_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Filter out bad slots
|
||||||
|
*
|
||||||
|
* \param[in] mask TS selection mask
|
||||||
|
* \param[in] ul_slots set of UL timeslots
|
||||||
|
* \param[in] dl_slots set of DL timeslots
|
||||||
|
* \param[in] rx_valid_win Mask for valid RX window value
|
||||||
|
* \returns negative error code or RX window on success
|
||||||
|
*/
|
||||||
|
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win)
|
||||||
|
{
|
||||||
|
uint8_t rx_good;
|
||||||
|
uint16_t rx_bad = (uint16_t)(0xFF & ~mask) << ul_slots;
|
||||||
|
|
||||||
|
/* TODO: CHECK this calculation -> separate function for unit testing */
|
||||||
|
rx_bad = (rx_bad | (rx_bad >> 8)) & 0xFF;
|
||||||
|
rx_good = dl_slots & ~rx_bad;
|
||||||
|
if (!rx_good)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return rx_good & rx_valid_win;
|
||||||
|
}
|
||||||
62
src/mslot_class.h
Normal file
62
src/mslot_class.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/* mslot_class.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||||
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||||
|
* Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* 3GPP TS 05.02 Annex B.1 */
|
||||||
|
|
||||||
|
#define MS_NA 255 /* N/A */
|
||||||
|
#define MS_A 254 /* 1 with hopping, 0 without */
|
||||||
|
#define MS_B 253 /* 1 with hopping, 0 without (change Rx to Tx)*/
|
||||||
|
#define MS_C 252 /* 1 with hopping, 0 without (change Tx to Rx)*/
|
||||||
|
#define MS_TO 251 /* 31 symbol periods (this can be provided by a TA offset, i.e. a minimum TA value) */
|
||||||
|
|
||||||
|
#define DEFAULT_MSLOT_CLASS 12
|
||||||
|
|
||||||
|
#define NO_FREE_TFI 0xffffffff
|
||||||
|
|
||||||
|
enum { MASK_TT = 0, MASK_TR = 1 };
|
||||||
|
|
||||||
|
/* multislot class selection routines */
|
||||||
|
uint8_t mslot_class_get_ta(uint8_t ms_cl);
|
||||||
|
uint8_t mslot_class_get_tb(uint8_t ms_cl);
|
||||||
|
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta);
|
||||||
|
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta);
|
||||||
|
uint8_t mslot_class_get_tx(uint8_t ms_cl);
|
||||||
|
uint8_t mslot_class_get_rx(uint8_t ms_cl);
|
||||||
|
uint8_t mslot_class_get_sum(uint8_t ms_cl);
|
||||||
|
uint8_t mslot_class_get_type(uint8_t ms_cl);
|
||||||
|
uint8_t mslot_class_max();
|
||||||
|
|
||||||
|
/* multislot allocation helper routines */
|
||||||
|
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask);
|
||||||
|
int8_t find_free_usf(uint8_t usf_map);
|
||||||
|
int8_t find_free_tfi(uint32_t tfi_map);
|
||||||
|
void masked_override_with(char *buf, uint8_t mask, char set_char);
|
||||||
|
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask);
|
||||||
|
uint16_t mslot_wrap_window(uint16_t win);
|
||||||
|
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem);
|
||||||
|
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win);
|
||||||
849
src/nacc_fsm.c
Normal file
849
src/nacc_fsm.c
Normal file
@@ -0,0 +1,849 @@
|
|||||||
|
/* nacc_fsm.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <talloc.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/rate_ctr.h>
|
||||||
|
#include <osmocom/ctrl/control_cmd.h>
|
||||||
|
#include <osmocom/ctrl/control_if.h>
|
||||||
|
|
||||||
|
#include <osmocom/gsm/gsm48.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||||
|
|
||||||
|
#include <nacc_fsm.h>
|
||||||
|
#include <gprs_rlcmac.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
#include <gprs_ms.h>
|
||||||
|
#include <encoding.h>
|
||||||
|
#include <bts.h>
|
||||||
|
#include <neigh_cache.h>
|
||||||
|
|
||||||
|
#define X(s) (1 << (s))
|
||||||
|
|
||||||
|
/* Infer CTRL id (seqnum) for a given tgt arfcn+bsic (bsic range: 0-63) */
|
||||||
|
#define arfcn_bsic_2_ctrl_id(arfcn, bsic) ((arfcn) * 100 + (bsic))
|
||||||
|
|
||||||
|
static const struct osmo_tdef_state_timeout nacc_fsm_timeouts[32] = {
|
||||||
|
[NACC_ST_INITIAL] = {},
|
||||||
|
[NACC_ST_WAIT_RESOLVE_RAC_CI] = { .T = PCU_TDEF_NEIGH_RESOLVE_TO },
|
||||||
|
[NACC_ST_WAIT_REQUEST_SI] = { .T = PCU_TDEF_SI_RESOLVE_TO },
|
||||||
|
[NACC_ST_TX_NEIGHBOUR_DATA] = {},
|
||||||
|
[NACC_ST_TX_CELL_CHG_CONTINUE] = {},
|
||||||
|
[NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK] = {}, /* Timeout through event controlled by tbf::poll_timeout() */
|
||||||
|
[NACC_ST_DONE] = {},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
|
||||||
|
* The actual timeout value is in turn obtained from conn->T_defs.
|
||||||
|
* Assumes local variable fi exists. */
|
||||||
|
|
||||||
|
#define nacc_fsm_state_chg(fi, NEXT_STATE) \
|
||||||
|
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
|
||||||
|
nacc_fsm_timeouts, \
|
||||||
|
((struct nacc_fsm_ctx*)(fi->priv))->ms->bts->pcu->T_defs, \
|
||||||
|
-1)
|
||||||
|
|
||||||
|
const struct value_string nacc_fsm_event_names[] = {
|
||||||
|
{ NACC_EV_RX_CELL_CHG_NOTIFICATION, "RX_CELL_CHG_NOTIFICATION" },
|
||||||
|
{ NACC_EV_RX_RAC_CI, "RX_RAC_CI" },
|
||||||
|
{ NACC_EV_RX_SI, "RX_SI" },
|
||||||
|
{ NACC_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
|
||||||
|
{ NACC_EV_RX_CELL_CHG_CONTINUE_ACK, "RX_CELL_CHG_CONTINUE_ACK"},
|
||||||
|
{ NACC_EV_TIMEOUT_CELL_CHG_CONTINUE, "TIMEOUT_CELL_CHG_CONTINUE" },
|
||||||
|
{ 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TS 44 060 11.2.9e Packet Neighbour Cell Data */
|
||||||
|
static struct msgb *create_packet_neighbour_cell_data(struct nacc_fsm_ctx *ctx,
|
||||||
|
const struct gprs_rlcmac_tbf *tbf,
|
||||||
|
bool *all_si_info_sent)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
int rc;
|
||||||
|
RlcMacDownlink_t *mac_control_block;
|
||||||
|
struct GprsMs *ms = tbf_ms(tbf);
|
||||||
|
OSMO_ASSERT(tbf_is_tfi_assigned(tbf));
|
||||||
|
uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
|
||||||
|
uint8_t tfi = tbf_tfi(tbf);
|
||||||
|
uint8_t container_id = 0;
|
||||||
|
PNCDContainer_t container;
|
||||||
|
size_t max_len, len_to_write;
|
||||||
|
uint8_t *cont_buf;
|
||||||
|
uint8_t si_type = ctx->si_info.type_psi ? 0x01 : 0x0;
|
||||||
|
|
||||||
|
memset(&container, 0, sizeof(container));
|
||||||
|
if (ctx->container_idx == 0) {
|
||||||
|
container.UnionType = 1; /* with ID */
|
||||||
|
container.u.PNCD_Container_With_ID.ARFCN = ctx->neigh_key.tgt_arfcn;
|
||||||
|
container.u.PNCD_Container_With_ID.BSIC = ctx->neigh_key.tgt_bsic;
|
||||||
|
cont_buf = &container.u.PNCD_Container_With_ID.CONTAINER[0];
|
||||||
|
max_len = sizeof(container.u.PNCD_Container_With_ID.CONTAINER) - 1;
|
||||||
|
} else {
|
||||||
|
container.UnionType = 0; /* without ID */
|
||||||
|
cont_buf = &container.u.PNCD_Container_Without_ID.CONTAINER[0];
|
||||||
|
max_len = sizeof(container.u.PNCD_Container_Without_ID.CONTAINER) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len_to_write = ctx->si_info.si_len - ctx->si_info_bytes_sent;
|
||||||
|
|
||||||
|
if (len_to_write == 0) {
|
||||||
|
/* We sent all info on last message filing it exactly, we now send a zeroed one to finish */
|
||||||
|
*all_si_info_sent = true;
|
||||||
|
*cont_buf = (si_type << 5) | 0x00;
|
||||||
|
} else if (len_to_write >= max_len) {
|
||||||
|
/* We fill the rlcmac block, we'll need more messages */
|
||||||
|
*all_si_info_sent = false;
|
||||||
|
*cont_buf = (si_type << 5) | 0x1F;
|
||||||
|
memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], max_len);
|
||||||
|
ctx->si_info_bytes_sent += max_len;
|
||||||
|
} else {
|
||||||
|
/* Last block, we don't fill it exactly */
|
||||||
|
*all_si_info_sent = true;
|
||||||
|
*cont_buf = (si_type << 5) | (len_to_write & 0x1F);
|
||||||
|
memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], len_to_write);
|
||||||
|
ctx->si_info_bytes_sent += len_to_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = msgb_alloc(GSM_MACBLOCK_LEN, "neighbour_cell_data");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Initialize a bit vector that uses allocated msgb as the data buffer. */
|
||||||
|
struct bitvec bv = {
|
||||||
|
.data = msgb_put(msg, GSM_MACBLOCK_LEN),
|
||||||
|
.data_len = GSM_MACBLOCK_LEN,
|
||||||
|
};
|
||||||
|
bitvec_unhex(&bv, DUMMY_VEC);
|
||||||
|
|
||||||
|
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
|
||||||
|
|
||||||
|
write_packet_neighbour_cell_data(mac_control_block,
|
||||||
|
tfi_is_dl, tfi, container_id,
|
||||||
|
ctx->container_idx, &container);
|
||||||
|
LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Neighbour Cell Data +++++++++++++++++++++++++\n");
|
||||||
|
rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Neighbour Cell Data failed (%d)\n", rc);
|
||||||
|
goto free_ret;
|
||||||
|
}
|
||||||
|
LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Neighbour Cell Data -------------------------\n");
|
||||||
|
rate_ctr_inc(rate_ctr_group_get_ctr(bts_rate_counters(ms->bts), CTR_PKT_NEIGH_CELL_DATA));
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
|
||||||
|
ctx->container_idx++;
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
free_ret:
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
msgb_free(msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TS 44 060 11.2.2a Packet Cell Change Continue */
|
||||||
|
static struct msgb *create_packet_cell_chg_continue(const struct nacc_fsm_ctx *ctx,
|
||||||
|
const struct nacc_ev_create_rlcmac_msg_ctx *data,
|
||||||
|
uint32_t *new_poll_fn)
|
||||||
|
{
|
||||||
|
struct msgb *msg;
|
||||||
|
int rc;
|
||||||
|
RlcMacDownlink_t *mac_control_block;
|
||||||
|
struct gprs_rlcmac_tbf *tbf = data->tbf;
|
||||||
|
struct GprsMs *ms = tbf_ms(tbf);
|
||||||
|
unsigned int rrbp;
|
||||||
|
|
||||||
|
rc = tbf_check_polling(tbf, data->fn, data->ts, new_poll_fn, &rrbp);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DTBF, LOGL_ERROR, "Failed registering poll for Pkt Cell Chg Continue (%d)\n", rc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_cell_chg_continue");
|
||||||
|
if (!msg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Initialize a bit vector that uses allocated msgb as the data buffer. */
|
||||||
|
struct bitvec bv = {
|
||||||
|
.data = msgb_put(msg, GSM_MACBLOCK_LEN),
|
||||||
|
.data_len = GSM_MACBLOCK_LEN,
|
||||||
|
};
|
||||||
|
bitvec_unhex(&bv, DUMMY_VEC);
|
||||||
|
|
||||||
|
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);
|
||||||
|
|
||||||
|
OSMO_ASSERT(tbf_is_tfi_assigned(tbf));
|
||||||
|
uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
|
||||||
|
uint8_t tfi = tbf_tfi(tbf);
|
||||||
|
uint8_t container_id = 0;
|
||||||
|
write_packet_cell_change_continue(mac_control_block, 1, rrbp, tfi_is_dl, tfi, true,
|
||||||
|
ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic, container_id);
|
||||||
|
LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Cell Change Continue +++++++++++++++++++++++++\n");
|
||||||
|
rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Cell Change Continue failed (%d)\n", rc);
|
||||||
|
goto free_ret;
|
||||||
|
}
|
||||||
|
LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Cell Change Continue -------------------------\n");
|
||||||
|
rate_ctr_inc(rate_ctr_group_get_ctr(bts_rate_counters(ms->bts), CTR_PKT_CELL_CHG_CONTINUE));
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
tbf_set_polling(tbf, *new_poll_fn, data->ts, PDCH_ULC_POLL_CELL_CHG_CONTINUE);
|
||||||
|
return msg;
|
||||||
|
|
||||||
|
free_ret:
|
||||||
|
talloc_free(mac_control_block);
|
||||||
|
msgb_free(msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_rim_ran_info_req(const struct nacc_fsm_ctx *ctx, struct bssgp_ran_information_pdu *pdu)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||||
|
|
||||||
|
*pdu = (struct bssgp_ran_information_pdu){
|
||||||
|
.routing_info_dest = {
|
||||||
|
.discr = BSSGP_RIM_ROUTING_INFO_GERAN,
|
||||||
|
.geran = {
|
||||||
|
.raid = {
|
||||||
|
.mcc = ctx->cgi_ps.rai.lac.plmn.mcc,
|
||||||
|
.mnc = ctx->cgi_ps.rai.lac.plmn.mnc,
|
||||||
|
.mnc_3_digits = ctx->cgi_ps.rai.lac.plmn.mnc_3_digits,
|
||||||
|
.lac = ctx->cgi_ps.rai.lac.lac,
|
||||||
|
.rac = ctx->cgi_ps.rai.rac,
|
||||||
|
},
|
||||||
|
.cid = ctx->cgi_ps.cell_identity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.routing_info_src = {
|
||||||
|
.discr = BSSGP_RIM_ROUTING_INFO_GERAN,
|
||||||
|
.geran = {
|
||||||
|
.raid = {
|
||||||
|
.mcc = bts->cgi_ps.rai.lac.plmn.mcc,
|
||||||
|
.mnc = bts->cgi_ps.rai.lac.plmn.mnc,
|
||||||
|
.mnc_3_digits = bts->cgi_ps.rai.lac.plmn.mnc_3_digits,
|
||||||
|
.lac = bts->cgi_ps.rai.lac.lac,
|
||||||
|
.rac = bts->cgi_ps.rai.rac,
|
||||||
|
},
|
||||||
|
.cid = bts->cgi_ps.cell_identity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.rim_cont_iei = BSSGP_IE_RI_REQ_RIM_CONTAINER,
|
||||||
|
.decoded_present = true,
|
||||||
|
.decoded = {
|
||||||
|
.req_rim_cont = {
|
||||||
|
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
|
||||||
|
.seq_num = 1,
|
||||||
|
.pdu_ind = {
|
||||||
|
.ack_requested = 0,
|
||||||
|
.pdu_type_ext = RIM_PDU_TYPE_SING_REP,
|
||||||
|
},
|
||||||
|
.prot_ver = 1,
|
||||||
|
.son_trans_app_id = NULL,
|
||||||
|
.son_trans_app_id_len = 0,
|
||||||
|
.u = {
|
||||||
|
.app_cont_nacc = {
|
||||||
|
.reprt_cell = ctx->cgi_ps,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_neigh_key_from_bts_pkt_cell_chg_not(struct neigh_cache_entry_key *neigh_key,
|
||||||
|
const struct gprs_rlcmac_bts *bts,
|
||||||
|
const Packet_Cell_Change_Notification_t *notif)
|
||||||
|
{
|
||||||
|
switch (notif->Target_Cell.UnionType) {
|
||||||
|
case 0: /* GSM */
|
||||||
|
neigh_key->local_lac = bts->cgi_ps.rai.lac.lac;
|
||||||
|
neigh_key->local_ci = bts->cgi_ps.cell_identity;
|
||||||
|
neigh_key->tgt_arfcn = notif->Target_Cell.u.Target_Cell_GSM_Notif.ARFCN;
|
||||||
|
neigh_key->tgt_bsic = notif->Target_Cell.u.Target_Cell_GSM_Notif.BSIC;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SI_HDR_LEN 2
|
||||||
|
static void bts_fill_si_cache_value(const struct gprs_rlcmac_bts *bts, struct si_cache_value *val)
|
||||||
|
{
|
||||||
|
val->type_psi = false;
|
||||||
|
val->si_len = 0;
|
||||||
|
if (bts->si1_is_set) {
|
||||||
|
osmo_static_assert(sizeof(bts->si1) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si1_header_size);
|
||||||
|
memcpy(&val->si_buf[val->si_len], bts->si1 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
|
||||||
|
val->si_len += BSSGP_RIM_SI_LEN;
|
||||||
|
}
|
||||||
|
if (bts->si3_is_set) {
|
||||||
|
osmo_static_assert(sizeof(bts->si3) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si3_header_size);
|
||||||
|
memcpy(&val->si_buf[val->si_len], bts->si3 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
|
||||||
|
val->si_len += BSSGP_RIM_SI_LEN;
|
||||||
|
}
|
||||||
|
if (bts->si13_is_set) {
|
||||||
|
osmo_static_assert(sizeof(bts->si13) - SI_HDR_LEN == BSSGP_RIM_SI_LEN, _si13_header_size);
|
||||||
|
memcpy(&val->si_buf[val->si_len], bts->si13 + SI_HDR_LEN, BSSGP_RIM_SI_LEN);
|
||||||
|
val->si_len += BSSGP_RIM_SI_LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called on event NACC_EV_RX_CELL_CHG_NOTIFICATION on states after
|
||||||
|
* WAIT_RESOLVE_RAC_CI. Ignore duplicate messages, transition back if target
|
||||||
|
* cell changed.
|
||||||
|
*/
|
||||||
|
static void handle_retrans_pkt_cell_chg_notif(struct nacc_fsm_ctx *ctx, const Packet_Cell_Change_Notification_t *notif)
|
||||||
|
{
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||||
|
struct neigh_cache_entry_key neigh_key;
|
||||||
|
|
||||||
|
if (fill_neigh_key_from_bts_pkt_cell_chg_not(&neigh_key, bts, notif) < 0) {
|
||||||
|
LOGPFSML(ctx->fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
|
||||||
|
notif->Target_Cell.UnionType);
|
||||||
|
if (ctx->fi->state != NACC_ST_TX_CELL_CHG_CONTINUE)
|
||||||
|
nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* If tgt cell changed, restart resolving it */
|
||||||
|
if (!neigh_cache_entry_key_eq(&ctx->neigh_key, &neigh_key)) {
|
||||||
|
ctx->neigh_key = neigh_key;
|
||||||
|
nacc_fsm_state_chg(ctx->fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
|
||||||
|
}
|
||||||
|
/* else: ignore it, it's a dup, carry on what we were doing */
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// FSM states //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||||
|
Packet_Cell_Change_Notification_t *notif;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
|
||||||
|
notif = (Packet_Cell_Change_Notification_t *)data;
|
||||||
|
if (fill_neigh_key_from_bts_pkt_cell_chg_not(&ctx->neigh_key, bts, notif) < 0) {
|
||||||
|
LOGPFSML(fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
|
||||||
|
notif->Target_Cell.UnionType);
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||||
|
} else {
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_wait_resolve_rac_ci_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||||
|
struct gprs_pcu *pcu = bts->pcu;
|
||||||
|
const struct osmo_cell_global_id_ps *cgi_ps;
|
||||||
|
struct ctrl_cmd *cmd = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* First try to find the value in the cache */
|
||||||
|
cgi_ps = neigh_cache_lookup_value(pcu->neigh_cache, &ctx->neigh_key);
|
||||||
|
if (cgi_ps) {
|
||||||
|
ctx->cgi_ps = *cgi_ps;
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CGI-PS not in cache, resolve it using BSC Neighbor Resolution CTRL interface */
|
||||||
|
|
||||||
|
LOGPFSML(fi, LOGL_DEBUG, "No CGI-PS found in cache, resolving " NEIGH_CACHE_ENTRY_KEY_FMT "...\n",
|
||||||
|
NEIGH_CACHE_ENTRY_KEY_ARGS(&ctx->neigh_key));
|
||||||
|
|
||||||
|
/* We may have changed to this state previously (eg: we are handling
|
||||||
|
* another Pkt cell Change Notify with different target). Avoid
|
||||||
|
* re-creating the socket in that case. */
|
||||||
|
if (ctx->neigh_ctrl_conn->write_queue.bfd.fd == -1) {
|
||||||
|
rc = osmo_sock_init2_ofd(&ctx->neigh_ctrl_conn->write_queue.bfd,
|
||||||
|
AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
|
||||||
|
NULL, 0, pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port,
|
||||||
|
OSMO_SOCK_F_CONNECT);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR,
|
||||||
|
"Failed to establish CTRL (neighbor resolution) connection to BSC r=%s:%u\n\n",
|
||||||
|
pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port);
|
||||||
|
goto err_term;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = ctrl_cmd_create(ctx, CTRL_TYPE_GET);
|
||||||
|
if (!cmd) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR, "CTRL msg creation failed\n");
|
||||||
|
goto err_term;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd->id = talloc_asprintf(cmd, "%u", arfcn_bsic_2_ctrl_id(ctx->neigh_key.tgt_arfcn,
|
||||||
|
ctx->neigh_key.tgt_bsic));
|
||||||
|
cmd->variable = talloc_asprintf(cmd, "neighbor_resolve_cgi_ps_from_lac_ci.%d.%d.%d.%d",
|
||||||
|
ctx->neigh_key.local_lac, ctx->neigh_key.local_ci,
|
||||||
|
ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);
|
||||||
|
rc = ctrl_cmd_send(&ctx->neigh_ctrl_conn->write_queue, cmd);
|
||||||
|
if (rc) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR, "CTRL msg sent failed: %d\n", rc);
|
||||||
|
goto err_term;
|
||||||
|
}
|
||||||
|
|
||||||
|
talloc_free(cmd);
|
||||||
|
return;
|
||||||
|
|
||||||
|
err_term:
|
||||||
|
talloc_free(cmd);
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_wait_resolve_rac_ci(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
const Packet_Cell_Change_Notification_t *notif;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
|
||||||
|
notif = (const Packet_Cell_Change_Notification_t *)data;
|
||||||
|
handle_retrans_pkt_cell_chg_notif(ctx, notif);
|
||||||
|
break;
|
||||||
|
case NACC_EV_RX_RAC_CI:
|
||||||
|
/* Assumption: ctx->cgi_ps has been filled by caller of the event */
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* At this point, we expect correct tgt cell info to be already in ctx->cgi_ps */
|
||||||
|
static void st_wait_request_si_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||||
|
struct gprs_pcu *pcu = bts->pcu;
|
||||||
|
struct bssgp_ran_information_pdu pdu;
|
||||||
|
const struct si_cache_value *si;
|
||||||
|
struct gprs_rlcmac_bts *bts_i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* First check if the CGI-PS addresses a cell managed by this PCU. If
|
||||||
|
* that's the case, we already have the info and there's no need to go
|
||||||
|
* the RIM way since we'd end up to this same PCU on the other end anyway.
|
||||||
|
*/
|
||||||
|
llist_for_each_entry(bts_i, &the_pcu->bts_list, list) {
|
||||||
|
if (bts_i == bts) /* Makes no sense targeting the same cell */
|
||||||
|
continue;
|
||||||
|
if (osmo_cgi_ps_cmp(&ctx->cgi_ps, &bts_i->cgi_ps) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOGPFSML(fi, LOGL_DEBUG, "neighbor CGI-PS %s addresses local BTS %d\n",
|
||||||
|
osmo_cgi_ps_name(&ctx->cgi_ps), bts_i->nr);
|
||||||
|
bts_fill_si_cache_value(bts, &ctx->si_info);
|
||||||
|
/* Tell the PCU scheduler we are ready to go, from here one we
|
||||||
|
* are polled/driven by the scheduler */
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First check if we have SI info for the target cell in cache */
|
||||||
|
si = si_cache_lookup_value(pcu->si_cache, &ctx->cgi_ps);
|
||||||
|
if (si) {
|
||||||
|
/* Copy info since cache can be deleted at any point */
|
||||||
|
memcpy(&ctx->si_info, si, sizeof(ctx->si_info));
|
||||||
|
/* Tell the PCU scheduler we are ready to go, from here one we
|
||||||
|
* are polled/driven by the scheduler */
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SI info not in cache, resolve it using RIM procedure against SGSN */
|
||||||
|
if (fill_rim_ran_info_req(ctx, &pdu) < 0) {
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGPFSML(fi, LOGL_INFO, "Tx RIM RAN-INFO to request SI of %s\n",
|
||||||
|
osmo_cgi_ps_name(&ctx->cgi_ps));
|
||||||
|
rc = bssgp_tx_rim(&pdu, gprs_ns2_nse_nsei(ctx->ms->bts->nse));
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGPFSML(fi, LOGL_ERROR, "Failed transmitting RIM RAN-INFO %s PDU: %d\n",
|
||||||
|
osmo_cgi_ps_name(&ctx->cgi_ps), rc);
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void st_wait_request_si(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
const Packet_Cell_Change_Notification_t *notif;
|
||||||
|
struct si_cache_entry *entry;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
|
||||||
|
notif = (const Packet_Cell_Change_Notification_t *)data;
|
||||||
|
handle_retrans_pkt_cell_chg_notif(ctx, notif);
|
||||||
|
break;
|
||||||
|
case NACC_EV_RX_SI:
|
||||||
|
entry = (struct si_cache_entry *)data;
|
||||||
|
/* Copy info since cache can be deleted at any point */
|
||||||
|
memcpy(&ctx->si_info, &entry->value, sizeof(ctx->si_info));
|
||||||
|
/* Tell the PCU scheduler we are ready to go, from here one we
|
||||||
|
* are polled/driven by the scheduler */
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* At this point, we already received all required SI information to send stored
|
||||||
|
* in struct nacc_fsm_ctx. We now wait for scheduler to ask us to construct
|
||||||
|
* RLCMAC DL CTRL messages to move FSM states forward
|
||||||
|
*/
|
||||||
|
static void st_tx_neighbour_data_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
ctx->si_info_bytes_sent = 0;
|
||||||
|
ctx->container_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_tx_neighbour_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
const Packet_Cell_Change_Notification_t *notif;
|
||||||
|
struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;
|
||||||
|
bool all_si_info_sent;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
|
||||||
|
notif = (const Packet_Cell_Change_Notification_t *)data;
|
||||||
|
handle_retrans_pkt_cell_chg_notif(ctx, notif);
|
||||||
|
break;
|
||||||
|
case NACC_EV_CREATE_RLCMAC_MSG:
|
||||||
|
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;
|
||||||
|
data_ctx->msg = create_packet_neighbour_cell_data(ctx, data_ctx->tbf, &all_si_info_sent);
|
||||||
|
if (!data_ctx->msg) {
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (all_si_info_sent) /* DONE */
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* st_cell_chg_continue_on_enter:
|
||||||
|
* At this point, we already sent all Pkt Cell Neighbour Change rlcmac
|
||||||
|
* blocks, and we only need to wait to be scheduled again to send PKT
|
||||||
|
* CELL CHANGE NOTIFICATION and then we are done
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void st_cell_chg_continue(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
const Packet_Cell_Change_Notification_t *notif;
|
||||||
|
struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
|
||||||
|
notif = (const Packet_Cell_Change_Notification_t *)data;
|
||||||
|
handle_retrans_pkt_cell_chg_notif(ctx, notif);
|
||||||
|
break;
|
||||||
|
case NACC_EV_CREATE_RLCMAC_MSG:
|
||||||
|
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;
|
||||||
|
data_ctx->msg = create_packet_cell_chg_continue(ctx, data_ctx, &ctx->continue_poll_fn);
|
||||||
|
if (data_ctx->msg) {
|
||||||
|
ctx->continue_poll_ts = data_ctx->ts;
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void st_wait_cell_chg_continue_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
const Packet_Cell_Change_Notification_t *notif;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
|
||||||
|
notif = (const Packet_Cell_Change_Notification_t *)data;
|
||||||
|
handle_retrans_pkt_cell_chg_notif(ctx, notif);
|
||||||
|
break;
|
||||||
|
case NACC_EV_TIMEOUT_CELL_CHG_CONTINUE:
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
break;
|
||||||
|
case NACC_EV_RX_CELL_CHG_CONTINUE_ACK:
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_DONE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OSMO_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void st_done_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||||
|
{
|
||||||
|
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nacc_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
|
||||||
|
/* after cleanup() finishes, FSM termination calls osmo_fsm_inst_free,
|
||||||
|
so we need to avoid double-freeing it during ctx talloc free
|
||||||
|
destructor */
|
||||||
|
talloc_reparent(ctx, ctx->ms, ctx->fi);
|
||||||
|
ctx->fi = NULL;
|
||||||
|
|
||||||
|
/* remove references from owning MS and free entire ctx */
|
||||||
|
ctx->ms->nacc = NULL;
|
||||||
|
talloc_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nacc_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||||
|
{
|
||||||
|
switch (fi->T) {
|
||||||
|
case PCU_TDEF_NEIGH_RESOLVE_TO:
|
||||||
|
case PCU_TDEF_SI_RESOLVE_TO:
|
||||||
|
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct osmo_fsm_state nacc_fsm_states[] = {
|
||||||
|
[NACC_ST_INITIAL] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(NACC_EV_RX_CELL_CHG_NOTIFICATION),
|
||||||
|
.out_state_mask =
|
||||||
|
X(NACC_ST_WAIT_RESOLVE_RAC_CI),
|
||||||
|
.name = "INITIAL",
|
||||||
|
.action = st_initial,
|
||||||
|
},
|
||||||
|
[NACC_ST_WAIT_RESOLVE_RAC_CI] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
|
||||||
|
X(NACC_EV_RX_RAC_CI),
|
||||||
|
.out_state_mask =
|
||||||
|
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
|
||||||
|
X(NACC_ST_WAIT_REQUEST_SI) |
|
||||||
|
X(NACC_ST_TX_CELL_CHG_CONTINUE),
|
||||||
|
.name = "WAIT_RESOLVE_RAC_CI",
|
||||||
|
.onenter = st_wait_resolve_rac_ci_on_enter,
|
||||||
|
.action = st_wait_resolve_rac_ci,
|
||||||
|
},
|
||||||
|
[NACC_ST_WAIT_REQUEST_SI] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
|
||||||
|
X(NACC_EV_RX_SI),
|
||||||
|
.out_state_mask =
|
||||||
|
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
|
||||||
|
X(NACC_ST_TX_NEIGHBOUR_DATA) |
|
||||||
|
X(NACC_ST_TX_CELL_CHG_CONTINUE),
|
||||||
|
.name = "WAIT_REQUEST_SI",
|
||||||
|
.onenter = st_wait_request_si_on_enter,
|
||||||
|
.action = st_wait_request_si,
|
||||||
|
},
|
||||||
|
[NACC_ST_TX_NEIGHBOUR_DATA] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
|
||||||
|
X(NACC_EV_CREATE_RLCMAC_MSG),
|
||||||
|
.out_state_mask =
|
||||||
|
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
|
||||||
|
X(NACC_ST_TX_CELL_CHG_CONTINUE),
|
||||||
|
.name = "TX_NEIGHBOUR_DATA",
|
||||||
|
.onenter = st_tx_neighbour_data_on_enter,
|
||||||
|
.action = st_tx_neighbour_data,
|
||||||
|
},
|
||||||
|
[NACC_ST_TX_CELL_CHG_CONTINUE] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
|
||||||
|
X(NACC_EV_CREATE_RLCMAC_MSG),
|
||||||
|
.out_state_mask =
|
||||||
|
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
|
||||||
|
X(NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK),
|
||||||
|
.name = "TX_CELL_CHG_CONTINUE",
|
||||||
|
.action = st_cell_chg_continue,
|
||||||
|
},
|
||||||
|
[NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK] = {
|
||||||
|
.in_event_mask =
|
||||||
|
X(NACC_EV_RX_CELL_CHG_NOTIFICATION) |
|
||||||
|
X(NACC_EV_RX_CELL_CHG_CONTINUE_ACK) |
|
||||||
|
X(NACC_EV_TIMEOUT_CELL_CHG_CONTINUE),
|
||||||
|
.out_state_mask =
|
||||||
|
X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
|
||||||
|
X(NACC_ST_TX_CELL_CHG_CONTINUE) |
|
||||||
|
X(NACC_ST_DONE),
|
||||||
|
.name = "WAIT_CELL_CHG_CONTINUE_ACK",
|
||||||
|
.action = st_wait_cell_chg_continue_ack,
|
||||||
|
},
|
||||||
|
[NACC_ST_DONE] = {
|
||||||
|
.in_event_mask = 0,
|
||||||
|
.out_state_mask = 0,
|
||||||
|
.name = "DONE",
|
||||||
|
.onenter = st_done_on_enter,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct osmo_fsm nacc_fsm = {
|
||||||
|
.name = "NACC",
|
||||||
|
.states = nacc_fsm_states,
|
||||||
|
.num_states = ARRAY_SIZE(nacc_fsm_states),
|
||||||
|
.timer_cb = nacc_fsm_timer_cb,
|
||||||
|
.cleanup = nacc_fsm_cleanup,
|
||||||
|
.log_subsys = DNACC,
|
||||||
|
.event_names = nacc_fsm_event_names,
|
||||||
|
};
|
||||||
|
|
||||||
|
static __attribute__((constructor)) void nacc_fsm_init(void)
|
||||||
|
{
|
||||||
|
OSMO_ASSERT(osmo_fsm_register(&nacc_fsm) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nacc_fsm_ctrl_reply_cb(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)data;
|
||||||
|
char *tmp = NULL, *tok, *saveptr;
|
||||||
|
unsigned int exp_id;
|
||||||
|
|
||||||
|
LOGPFSML(ctx->fi, LOGL_NOTICE, "Received CTRL message: type=%d %s %s: %s\n",
|
||||||
|
cmd->type, cmd->variable, cmd->id, osmo_escape_str(cmd->reply, -1));
|
||||||
|
|
||||||
|
if (cmd->type != CTRL_TYPE_GET_REPLY || !cmd->reply) {
|
||||||
|
nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate it's the seqnum from our last GET cmd, and not from older
|
||||||
|
* one we may have requested in case MS decided to resend Pkt Cell
|
||||||
|
* Change Notify with a different tgt cell:
|
||||||
|
*/
|
||||||
|
exp_id = arfcn_bsic_2_ctrl_id(ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);
|
||||||
|
if ((unsigned int)atoi(cmd->id) != exp_id) {
|
||||||
|
LOGPFSML(ctx->fi, LOGL_INFO,
|
||||||
|
"Received CTRL message with id=%s doesn't match our expected last id=%d, ignoring\n",
|
||||||
|
cmd->id, exp_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Potentially validate cmd->variable contains same params as we
|
||||||
|
sent, and that cmd->id matches the original set. We may want to keep
|
||||||
|
the original cmd around by setting cmd->defer=1 when sending it. */
|
||||||
|
|
||||||
|
tmp = talloc_strdup(cmd, cmd->reply);
|
||||||
|
if (!tmp)
|
||||||
|
goto free_ret;
|
||||||
|
|
||||||
|
if (!(tok = strtok_r(tmp, "-", &saveptr)))
|
||||||
|
goto free_ret;
|
||||||
|
ctx->cgi_ps.rai.lac.plmn.mcc = atoi(tok);
|
||||||
|
|
||||||
|
if (!(tok = strtok_r(NULL, "-", &saveptr)))
|
||||||
|
goto free_ret;
|
||||||
|
ctx->cgi_ps.rai.lac.plmn.mnc = atoi(tok);
|
||||||
|
|
||||||
|
if (!(tok = strtok_r(NULL, "-", &saveptr)))
|
||||||
|
goto free_ret;
|
||||||
|
ctx->cgi_ps.rai.lac.lac = atoi(tok);
|
||||||
|
|
||||||
|
if (!(tok = strtok_r(NULL, "-", &saveptr)))
|
||||||
|
goto free_ret;
|
||||||
|
ctx->cgi_ps.rai.rac = atoi(tok);
|
||||||
|
|
||||||
|
if (!(tok = strtok_r(NULL, "\0", &saveptr)))
|
||||||
|
goto free_ret;
|
||||||
|
ctx->cgi_ps.cell_identity = atoi(tok);
|
||||||
|
|
||||||
|
/* Cache the cgi_ps so we can avoid requesting again same resolution for a while */
|
||||||
|
neigh_cache_add(ctx->ms->bts->pcu->neigh_cache, &ctx->neigh_key, &ctx->cgi_ps);
|
||||||
|
|
||||||
|
osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, NULL);
|
||||||
|
return;
|
||||||
|
|
||||||
|
free_ret:
|
||||||
|
talloc_free(tmp);
|
||||||
|
nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nacc_fsm_ctx_talloc_destructor(struct nacc_fsm_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->fi) {
|
||||||
|
osmo_fsm_inst_free(ctx->fi);
|
||||||
|
ctx->fi = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->neigh_ctrl_conn) {
|
||||||
|
if (ctx->neigh_ctrl_conn->write_queue.bfd.fd != -1) {
|
||||||
|
osmo_wqueue_clear(&ctx->neigh_ctrl_conn->write_queue);
|
||||||
|
osmo_fd_unregister(&ctx->neigh_ctrl_conn->write_queue.bfd);
|
||||||
|
close(ctx->neigh_ctrl_conn->write_queue.bfd.fd);
|
||||||
|
ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms)
|
||||||
|
{
|
||||||
|
struct nacc_fsm_ctx *ctx = talloc_zero(ms, struct nacc_fsm_ctx);
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
talloc_set_destructor(ctx, nacc_fsm_ctx_talloc_destructor);
|
||||||
|
|
||||||
|
ctx->ms = ms;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "TLLI-0x%08x", ms_tlli(ms));
|
||||||
|
ctx->fi = osmo_fsm_inst_alloc(&nacc_fsm, ctx, ctx, LOGL_INFO, buf);
|
||||||
|
if (!ctx->fi)
|
||||||
|
goto free_ret;
|
||||||
|
|
||||||
|
ctx->neigh_ctrl = ctrl_handle_alloc(ctx, ctx, NULL);
|
||||||
|
ctx->neigh_ctrl->reply_cb = nacc_fsm_ctrl_reply_cb;
|
||||||
|
ctx->neigh_ctrl_conn = osmo_ctrl_conn_alloc(ctx, ctx->neigh_ctrl);
|
||||||
|
if (!ctx->neigh_ctrl_conn)
|
||||||
|
goto free_ret;
|
||||||
|
/* Older versions of osmo_ctrl_conn_alloc didn't properly initialize fd to -1,
|
||||||
|
* so make sure to do it here otherwise fd may be valid fd 0 and cause trouble */
|
||||||
|
ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
|
||||||
|
llist_add(&ctx->neigh_ctrl_conn->list_entry, &ctx->neigh_ctrl->ccon_list);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
free_ret:
|
||||||
|
talloc_free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
71
src/nacc_fsm.h
Normal file
71
src/nacc_fsm.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/* nacc_fsm.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <osmocom/core/fsm.h>
|
||||||
|
#include <osmocom/gsm/gsm23003.h>
|
||||||
|
|
||||||
|
#include <neigh_cache.h>
|
||||||
|
|
||||||
|
struct GprsMs;
|
||||||
|
struct gprs_rlcmac_tbf;
|
||||||
|
|
||||||
|
enum nacc_fsm_event {
|
||||||
|
NACC_EV_RX_CELL_CHG_NOTIFICATION, /* data: Packet_Cell_Change_Notification_t* */
|
||||||
|
NACC_EV_RX_RAC_CI, /* no data passed, RAC_CI became available in neigh_cache */
|
||||||
|
NACC_EV_RX_SI, /* data: struct si_cache_entry* */
|
||||||
|
NACC_EV_CREATE_RLCMAC_MSG, /* data: struct nacc_ev_create_rlcmac_msg_ctx* */
|
||||||
|
NACC_EV_RX_CELL_CHG_CONTINUE_ACK,
|
||||||
|
NACC_EV_TIMEOUT_CELL_CHG_CONTINUE, /* Poll Timeout */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum nacc_fsm_states {
|
||||||
|
NACC_ST_INITIAL,
|
||||||
|
NACC_ST_WAIT_RESOLVE_RAC_CI,
|
||||||
|
NACC_ST_WAIT_REQUEST_SI,
|
||||||
|
NACC_ST_TX_NEIGHBOUR_DATA,
|
||||||
|
NACC_ST_TX_CELL_CHG_CONTINUE,
|
||||||
|
NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK,
|
||||||
|
NACC_ST_DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nacc_fsm_ctx {
|
||||||
|
struct osmo_fsm_inst *fi;
|
||||||
|
struct GprsMs* ms; /* back pointer */
|
||||||
|
struct ctrl_handle *neigh_ctrl;
|
||||||
|
struct ctrl_connection *neigh_ctrl_conn;
|
||||||
|
struct neigh_cache_entry_key neigh_key; /* target cell info from MS */
|
||||||
|
struct osmo_cell_global_id_ps cgi_ps; /* target cell info resolved from req_{arfcn+bsic} */
|
||||||
|
struct si_cache_value si_info; /* SI info resolved from SGSN, to be sent to MS */
|
||||||
|
size_t si_info_bytes_sent; /* How many bytes out of si_info->si_len were already sent to MS */
|
||||||
|
size_t container_idx; /* Next container_idx to assign when sending Packet Neighbor Data message */
|
||||||
|
uint32_t continue_poll_fn; /* Scheduled poll FN to CTRL ACK the Pkt Cell Chg Continue */
|
||||||
|
uint8_t continue_poll_ts; /* Scheduled poll TS to CTRL ACK the Pkt Cell Chg Continue */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* passed as data in NACC_EV_CREATE_RLCMAC_MSG */
|
||||||
|
struct nacc_ev_create_rlcmac_msg_ctx {
|
||||||
|
struct gprs_rlcmac_tbf *tbf; /* target tbf to create messages for */
|
||||||
|
uint32_t fn; /* FN where the created DL ctrl block is to be sent */
|
||||||
|
uint8_t ts; /* TS where the created DL ctrl block is to be sent */
|
||||||
|
struct msgb *msg; /* to be filled by FSM during event processing */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms);
|
||||||
289
src/neigh_cache.c
Normal file
289
src/neigh_cache.c
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
/* si_cache.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <talloc.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
|
||||||
|
#include <neigh_cache.h>
|
||||||
|
#include <gprs_debug.h>
|
||||||
|
|
||||||
|
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);
|
||||||
|
static void neigh_cache_cleanup_cb(void *data)
|
||||||
|
{
|
||||||
|
struct timespec now, threshold;
|
||||||
|
struct neigh_cache *cache = (struct neigh_cache *)data;
|
||||||
|
struct neigh_cache_entry *it, *tmp;
|
||||||
|
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
/* Instead of adding keep_time_intval to each, substract it from now once */
|
||||||
|
timespecsub(&now, &cache->keep_time_intval, &threshold);
|
||||||
|
|
||||||
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||||
|
if (timespeccmp(&threshold, &it->update_ts, <))
|
||||||
|
break;
|
||||||
|
LOGP(DNACC, LOGL_DEBUG,
|
||||||
|
"neigh_cache: Removing entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
|
||||||
|
NEIGH_CACHE_ENTRY_KEY_ARGS(&it->key), osmo_cgi_ps_name(&it->value));
|
||||||
|
llist_del(&it->list);
|
||||||
|
talloc_free(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
neigh_cache_schedule_cleanup(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
|
||||||
|
{
|
||||||
|
struct neigh_cache_entry *it;
|
||||||
|
struct timespec now, threshold, result;
|
||||||
|
|
||||||
|
/* First item is the one with oldest update_ts */
|
||||||
|
it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);
|
||||||
|
if (!it)
|
||||||
|
return;
|
||||||
|
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
|
||||||
|
|
||||||
|
if (timespeccmp(&now, &threshold, >=)) {
|
||||||
|
/* Too late, let's flush asynchonously so newly added isn't
|
||||||
|
* immediatelly freed before return. */
|
||||||
|
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
|
||||||
|
} else {
|
||||||
|
timespecsub(&threshold, &now, &result);
|
||||||
|
}
|
||||||
|
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
|
||||||
|
{
|
||||||
|
struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);
|
||||||
|
OSMO_ASSERT(cache);
|
||||||
|
INIT_LLIST_HEAD(&cache->list);
|
||||||
|
osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);
|
||||||
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||||
|
return cache;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec)
|
||||||
|
{
|
||||||
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||||
|
neigh_cache_schedule_cleanup(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
|
||||||
|
const struct neigh_cache_entry_key *key,
|
||||||
|
const struct osmo_cell_global_id_ps *value)
|
||||||
|
{
|
||||||
|
struct neigh_cache_entry *it;
|
||||||
|
|
||||||
|
/* First check if it already exists. If so, simply update timer+value */
|
||||||
|
it = neigh_cache_lookup_entry(cache, key);
|
||||||
|
if (!it) {
|
||||||
|
LOGP(DNACC, LOGL_DEBUG,
|
||||||
|
"neigh_cache: Inserting new entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
|
||||||
|
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(value));
|
||||||
|
it = talloc_zero(cache, struct neigh_cache_entry);
|
||||||
|
OSMO_ASSERT(it);
|
||||||
|
memcpy(&it->key, key, sizeof(it->key));
|
||||||
|
} else {
|
||||||
|
LOGP(DNACC, LOGL_DEBUG,
|
||||||
|
"neigh_cache: Updating entry " NEIGH_CACHE_ENTRY_KEY_FMT " => (%s -> %s)\n",
|
||||||
|
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(&it->value), osmo_cgi_ps_name2(value));
|
||||||
|
/* remove item, we'll add it to the end to have them sorted by last update */
|
||||||
|
llist_del(&it->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&it->value, value, sizeof(it->value));
|
||||||
|
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
|
||||||
|
llist_add_tail(&it->list, &cache->list);
|
||||||
|
neigh_cache_schedule_cleanup(cache);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
|
||||||
|
const struct neigh_cache_entry_key *key)
|
||||||
|
{
|
||||||
|
struct neigh_cache_entry *tmp;
|
||||||
|
llist_for_each_entry(tmp, &cache->list, list) {
|
||||||
|
if (neigh_cache_entry_key_eq(&tmp->key, key))
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
|
||||||
|
const struct neigh_cache_entry_key *key)
|
||||||
|
{
|
||||||
|
struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
|
||||||
|
if (it)
|
||||||
|
return &it->value;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void neigh_cache_free(struct neigh_cache *cache)
|
||||||
|
{
|
||||||
|
struct neigh_cache_entry *it, *tmp;
|
||||||
|
if (!cache)
|
||||||
|
return;
|
||||||
|
|
||||||
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||||
|
llist_del(&it->list);
|
||||||
|
talloc_free(it);
|
||||||
|
}
|
||||||
|
osmo_timer_del(&cache->cleanup_timer);
|
||||||
|
talloc_free(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// SI CACHE
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
static void si_cache_schedule_cleanup(struct si_cache *cache);
|
||||||
|
static void si_cache_cleanup_cb(void *data)
|
||||||
|
{
|
||||||
|
struct timespec now, threshold;
|
||||||
|
struct si_cache *cache = (struct si_cache *)data;
|
||||||
|
struct si_cache_entry *it, *tmp;
|
||||||
|
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
/* Instead of adding keep_time_intval to each, substract it from now once */
|
||||||
|
timespecsub(&now, &cache->keep_time_intval, &threshold);
|
||||||
|
|
||||||
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||||
|
if (timespeccmp(&threshold, &it->update_ts, <))
|
||||||
|
break;
|
||||||
|
LOGP(DNACC, LOGL_DEBUG, "si_cache: Removing entry %s\n",
|
||||||
|
osmo_cgi_ps_name(&it->key));
|
||||||
|
llist_del(&it->list);
|
||||||
|
talloc_free(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
si_cache_schedule_cleanup(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void si_cache_schedule_cleanup(struct si_cache *cache)
|
||||||
|
{
|
||||||
|
struct si_cache_entry *it;
|
||||||
|
struct timespec now, threshold, result;
|
||||||
|
|
||||||
|
/* First item is the one with oldest update_ts */
|
||||||
|
it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);
|
||||||
|
if (!it)
|
||||||
|
return;
|
||||||
|
|
||||||
|
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
|
||||||
|
|
||||||
|
if (timespeccmp(&now, &threshold, >=)) {
|
||||||
|
/* Too late, let's flush asynchonously so newly added isn't
|
||||||
|
* immediatelly freed before return. */
|
||||||
|
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
|
||||||
|
} else {
|
||||||
|
timespecsub(&threshold, &now, &result);
|
||||||
|
}
|
||||||
|
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
|
||||||
|
{
|
||||||
|
struct si_cache *cache = talloc_zero(ctx, struct si_cache);
|
||||||
|
OSMO_ASSERT(cache);
|
||||||
|
INIT_LLIST_HEAD(&cache->list);
|
||||||
|
osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);
|
||||||
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec)
|
||||||
|
{
|
||||||
|
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||||
|
si_cache_schedule_cleanup(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct si_cache_entry *si_cache_add(struct si_cache *cache,
|
||||||
|
const struct osmo_cell_global_id_ps *key,
|
||||||
|
const struct si_cache_value *value)
|
||||||
|
{
|
||||||
|
struct si_cache_entry *it;
|
||||||
|
|
||||||
|
/* First check if it already exists. If so, simply update timer+value */
|
||||||
|
it = si_cache_lookup_entry(cache, key);
|
||||||
|
if (!it) {
|
||||||
|
LOGP(DNACC, LOGL_DEBUG, "si_cache: Inserting new entry %s\n",
|
||||||
|
osmo_cgi_ps_name(key));
|
||||||
|
it = talloc_zero(cache, struct si_cache_entry);
|
||||||
|
OSMO_ASSERT(it);
|
||||||
|
memcpy(&it->key, key, sizeof(it->key));
|
||||||
|
} else {
|
||||||
|
LOGP(DNACC, LOGL_DEBUG, "si_cache: Updating entry %s\n",
|
||||||
|
osmo_cgi_ps_name(&it->key));
|
||||||
|
/* remove item, we'll add it to the end to have them sorted by last update */
|
||||||
|
llist_del(&it->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&it->value, value, sizeof(it->value));
|
||||||
|
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
|
||||||
|
llist_add_tail(&it->list, &cache->list);
|
||||||
|
si_cache_schedule_cleanup(cache);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
|
||||||
|
const struct osmo_cell_global_id_ps *key)
|
||||||
|
{
|
||||||
|
struct si_cache_entry *tmp;
|
||||||
|
llist_for_each_entry(tmp, &cache->list, list) {
|
||||||
|
if (osmo_cgi_ps_cmp(&tmp->key, key) == 0)
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
|
||||||
|
const struct osmo_cell_global_id_ps *key)
|
||||||
|
{
|
||||||
|
struct si_cache_entry *it = si_cache_lookup_entry(cache, key);
|
||||||
|
if (it)
|
||||||
|
return &it->value;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void si_cache_free(struct si_cache *cache)
|
||||||
|
{
|
||||||
|
struct si_cache_entry *it, *tmp;
|
||||||
|
if (!cache)
|
||||||
|
return;
|
||||||
|
|
||||||
|
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||||
|
llist_del(&it->list);
|
||||||
|
talloc_free(it);
|
||||||
|
}
|
||||||
|
osmo_timer_del(&cache->cleanup_timer);
|
||||||
|
talloc_free(cache);
|
||||||
|
}
|
||||||
112
src/neigh_cache.h
Normal file
112
src/neigh_cache.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/* neigh_cache.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/linuxlist.h>
|
||||||
|
#include <osmocom/core/timer.h>
|
||||||
|
|
||||||
|
#include <osmocom/gsm/gsm23003.h>
|
||||||
|
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// NEIGH CACHE
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
/* ARFC+BSIC -> CGI PS cache */
|
||||||
|
struct neigh_cache {
|
||||||
|
struct llist_head list; /* list of neigh_cache_entry items */
|
||||||
|
struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
|
||||||
|
struct timespec keep_time_intval;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct neigh_cache_entry_key {
|
||||||
|
uint16_t local_lac;
|
||||||
|
uint16_t local_ci;
|
||||||
|
uint16_t tgt_arfcn;
|
||||||
|
uint8_t tgt_bsic;
|
||||||
|
};
|
||||||
|
#define NEIGH_CACHE_ENTRY_KEY_FMT "%" PRIu16 "-%" PRIu16 "-%" PRIu16 "-%" PRIu8
|
||||||
|
#define NEIGH_CACHE_ENTRY_KEY_ARGS(key) (key)->local_lac, (key)->local_ci, (key)->tgt_arfcn, (key)->tgt_bsic
|
||||||
|
|
||||||
|
static inline bool neigh_cache_entry_key_eq(const struct neigh_cache_entry_key *a,
|
||||||
|
const struct neigh_cache_entry_key *b)
|
||||||
|
{
|
||||||
|
return a->local_lac == b->local_lac &&
|
||||||
|
a->local_ci == b->local_ci &&
|
||||||
|
a->tgt_arfcn == b->tgt_arfcn &&
|
||||||
|
a->tgt_bsic == b->tgt_bsic;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct neigh_cache_entry {
|
||||||
|
struct llist_head list; /* to be included in neigh_cache->list */
|
||||||
|
struct timespec update_ts;
|
||||||
|
struct neigh_cache_entry_key key;
|
||||||
|
struct osmo_cell_global_id_ps value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec);
|
||||||
|
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec);
|
||||||
|
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
|
||||||
|
const struct neigh_cache_entry_key *key,
|
||||||
|
const struct osmo_cell_global_id_ps *value);
|
||||||
|
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
|
||||||
|
const struct neigh_cache_entry_key *key);
|
||||||
|
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
|
||||||
|
const struct neigh_cache_entry_key *key);
|
||||||
|
void neigh_cache_free(struct neigh_cache *cache);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
// SI CACHE
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
/* CGI-PS-> SI cache */
|
||||||
|
struct si_cache {
|
||||||
|
struct llist_head list; /* list of si_cache_entry items */
|
||||||
|
struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
|
||||||
|
struct timespec keep_time_intval;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct si_cache_value {
|
||||||
|
uint8_t si_buf[BSSGP_RIM_PSI_LEN * 127]; /* 3GPP TS 48.018 11.3.63.2.1 */
|
||||||
|
size_t si_len;
|
||||||
|
bool type_psi;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct si_cache_entry {
|
||||||
|
struct llist_head list; /* to be included in si_cache->list */
|
||||||
|
struct timespec update_ts;
|
||||||
|
struct osmo_cell_global_id_ps key;
|
||||||
|
struct si_cache_value value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec);
|
||||||
|
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec);
|
||||||
|
struct si_cache_entry *si_cache_add(struct si_cache *cache,
|
||||||
|
const struct osmo_cell_global_id_ps *key,
|
||||||
|
const struct si_cache_value *value);
|
||||||
|
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
|
||||||
|
const struct osmo_cell_global_id_ps *key);
|
||||||
|
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
|
||||||
|
const struct osmo_cell_global_id_ps *key);
|
||||||
|
void si_cache_free(struct si_cache *cache);
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
/* openbts_sock.cpp
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <gprs_rlcmac.h>
|
|
||||||
#include <gprs_bssgp_pcu.h>
|
|
||||||
#include <pcu_l1_if.h>
|
|
||||||
#include <gprs_debug.h>
|
|
||||||
#include <bitvector.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/write_queue.h>
|
|
||||||
#include <osmocom/core/socket.h>
|
|
||||||
#include <osmocom/core/timer.h>
|
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
|
||||||
#include <pcuif_proto.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void *tall_pcu_ctx;
|
|
||||||
|
|
||||||
struct femtol1_hdl {
|
|
||||||
struct gsm_time gsm_time;
|
|
||||||
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
|
|
||||||
uint32_t dsp_trace_f;
|
|
||||||
uint16_t clk_cal;
|
|
||||||
struct llist_head wlc_list;
|
|
||||||
|
|
||||||
void *priv; /* user reference */
|
|
||||||
|
|
||||||
struct osmo_timer_list alive_timer;
|
|
||||||
unsigned int alive_prim_cnt;
|
|
||||||
|
|
||||||
struct osmo_fd read_ofd; /* osmo file descriptors */
|
|
||||||
struct osmo_wqueue write_q;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint16_t arfcn;
|
|
||||||
uint8_t tn;
|
|
||||||
uint8_t tsc;
|
|
||||||
uint16_t ta;
|
|
||||||
} channel_info;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct l1fwd_hdl {
|
|
||||||
struct sockaddr_storage remote_sa;
|
|
||||||
socklen_t remote_sa_len;
|
|
||||||
|
|
||||||
struct osmo_wqueue udp_wq;
|
|
||||||
|
|
||||||
struct femtol1_hdl *fl1h;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
|
|
||||||
|
|
||||||
// TODO: We should move this parameters to config file.
|
|
||||||
#define PCU_L1_IF_PORT 5944
|
|
||||||
|
|
||||||
/* OpenBTS socket functions */
|
|
||||||
|
|
||||||
int pcu_sock_send(struct msgb *msg)
|
|
||||||
{
|
|
||||||
osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* data has arrived on the udp socket */
|
|
||||||
static int udp_read_cb(struct osmo_fd *ofd)
|
|
||||||
{
|
|
||||||
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
|
|
||||||
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
|
|
||||||
struct femtol1_hdl *fl1h = l1fh->fl1h;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!msg)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
msg->l1h = msg->data;
|
|
||||||
|
|
||||||
l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
|
|
||||||
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
|
|
||||||
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
|
|
||||||
if (rc < 0) {
|
|
||||||
perror("read from udp");
|
|
||||||
msgb_free(msg);
|
|
||||||
return rc;
|
|
||||||
} else if (rc == 0) {
|
|
||||||
perror("len=0 read from udp");
|
|
||||||
msgb_free(msg);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
msgb_put(msg, rc);
|
|
||||||
|
|
||||||
struct gsm_pcu_if *pcu_prim = (gsm_pcu_if *)(msg->l1h);
|
|
||||||
rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* callback when we can write to the UDP socket */
|
|
||||||
static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
|
|
||||||
|
|
||||||
//LOGP(DPCU, LOGL_ERROR, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_length(msg));
|
|
||||||
|
|
||||||
rc = sendto(ofd->fd, msgb_data(msg), msgb_length(msg), 0,
|
|
||||||
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
|
|
||||||
if (rc < 0) {
|
|
||||||
LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
return rc;
|
|
||||||
} else if (rc < (int)msgb_length(msg)) {
|
|
||||||
LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
|
|
||||||
"%u < %u\n", rc, msgb_length(msg));
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pcu_l1if_open()
|
|
||||||
{
|
|
||||||
struct femtol1_hdl *fl1h;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* allocate new femtol1_handle */
|
|
||||||
fl1h = talloc_zero(tall_pcu_ctx, struct femtol1_hdl);
|
|
||||||
INIT_LLIST_HEAD(&fl1h->wlc_list);
|
|
||||||
|
|
||||||
l1fh->fl1h = fl1h;
|
|
||||||
fl1h->priv = l1fh;
|
|
||||||
|
|
||||||
struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
|
|
||||||
osmo_wqueue_init(queue, 10);
|
|
||||||
queue->bfd.when |= BSC_FD_READ;
|
|
||||||
queue->bfd.data = l1fh;
|
|
||||||
queue->bfd.priv_nr = 0;
|
|
||||||
|
|
||||||
/* Open UDP */
|
|
||||||
struct osmo_wqueue *wq = &l1fh->udp_wq;
|
|
||||||
|
|
||||||
osmo_wqueue_init(wq, 10);
|
|
||||||
wq->write_cb = udp_write_cb;
|
|
||||||
wq->read_cb = udp_read_cb;
|
|
||||||
wq->bfd.when |= BSC_FD_READ;
|
|
||||||
wq->bfd.data = l1fh;
|
|
||||||
wq->bfd.priv_nr = 0;
|
|
||||||
rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
|
|
||||||
IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
|
|
||||||
OSMO_SOCK_F_BIND);
|
|
||||||
if (rc < 0) {
|
|
||||||
perror("sock_init");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pcu_l1if_close(void)
|
|
||||||
{
|
|
||||||
gprs_bssgp_destroy();
|
|
||||||
|
|
||||||
/* FIXME: cleanup l1if */
|
|
||||||
talloc_free(l1fh->fl1h);
|
|
||||||
}
|
|
||||||
206
src/osmo-bts-litecell15/lc15_l1_hw.c
Normal file
206
src/osmo-bts-litecell15/lc15_l1_hw.c
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
/* Interface handler for Nuran Wireless Litecell 1.5 L1 (real hardware) */
|
||||||
|
|
||||||
|
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||||
|
* based on:
|
||||||
|
* femto_l1_hw.c
|
||||||
|
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||||
|
*
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <osmocom/core/talloc.h>
|
||||||
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/core/select.h>
|
||||||
|
#include <osmocom/core/write_queue.h>
|
||||||
|
#include <osmocom/core/timer.h>
|
||||||
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
|
|
||||||
|
#include <nrw/litecell15/litecell15.h>
|
||||||
|
#include <nrw/litecell15/gsml1prim.h>
|
||||||
|
#include <nrw/litecell15/gsml1const.h>
|
||||||
|
#include <nrw/litecell15/gsml1types.h>
|
||||||
|
|
||||||
|
#include "gprs_debug.h"
|
||||||
|
#include "lc15bts.h"
|
||||||
|
#include "lc15_l1_if.h"
|
||||||
|
|
||||||
|
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/litecell15_dsp2arm_trx"
|
||||||
|
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/litecell15_arm2dsp_trx"
|
||||||
|
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm_trx"
|
||||||
|
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp_trx"
|
||||||
|
|
||||||
|
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm_trx"
|
||||||
|
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp_trx"
|
||||||
|
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
|
||||||
|
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
|
||||||
|
|
||||||
|
static const char *rd_devnames[] = {
|
||||||
|
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
||||||
|
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
||||||
|
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
|
||||||
|
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *wr_devnames[] = {
|
||||||
|
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
||||||
|
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
||||||
|
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
|
||||||
|
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* callback when there's something to read from the l1 msg_queue */
|
||||||
|
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||||
|
{
|
||||||
|
//struct msgb *msg = l1p_msgb_alloc();
|
||||||
|
struct msgb *msg = msgb_alloc_headroom(sizeof(Litecell15_Prim_t) + 128,
|
||||||
|
128, "1l_fd");
|
||||||
|
struct lc15l1_hdl *fl1h = ofd->data;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
msg->l1h = msg->data;
|
||||||
|
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
|
||||||
|
if (rc < 0) {
|
||||||
|
if (rc != -1)
|
||||||
|
LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
msgb_free(msg);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
msgb_put(msg, rc);
|
||||||
|
|
||||||
|
switch (ofd->priv_nr) {
|
||||||
|
case MQ_SYS_WRITE:
|
||||||
|
if (rc != sizeof(Litecell15_Prim_t))
|
||||||
|
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||||
|
"sizeof(Litecell15_Prim_t)\n", rc);
|
||||||
|
return l1if_handle_sysprim(fl1h, msg);
|
||||||
|
case MQ_L1_WRITE:
|
||||||
|
case MQ_TCH_WRITE:
|
||||||
|
case MQ_PDTCH_WRITE:
|
||||||
|
if (rc != sizeof(GsmL1_Prim_t))
|
||||||
|
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||||
|
"sizeof(GsmL1_Prim_t)\n", rc);
|
||||||
|
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
|
||||||
|
default:
|
||||||
|
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||||
|
LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||||
|
ofd->priv_nr);
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* callback when we can write to one of the l1 msg_queue devices */
|
||||||
|
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return rc;
|
||||||
|
} else if (rc < msg->len) {
|
||||||
|
LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
|
||||||
|
"%u < %u\n", rc, msg->len);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
|
||||||
|
/* Step 1: Open all msg_queue file descriptors */
|
||||||
|
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||||
|
struct osmo_wqueue *wq = &hdl->write_q[q];
|
||||||
|
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], hdl->hw_info.trx_nr);
|
||||||
|
buf[sizeof(buf)-1] = '\0';
|
||||||
|
|
||||||
|
rc = open(buf, O_RDONLY);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||||
|
buf, strerror(errno));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
osmo_fd_setup(read_ofd, rc, OSMO_FD_READ, l1if_fd_cb, hdl, q);
|
||||||
|
rc = osmo_fd_register(read_ofd);
|
||||||
|
if (rc < 0) {
|
||||||
|
close(read_ofd->fd);
|
||||||
|
read_ofd->fd = -1;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], hdl->hw_info.trx_nr);
|
||||||
|
buf[sizeof(buf)-1] = '\0';
|
||||||
|
|
||||||
|
rc = open(buf, O_WRONLY);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||||
|
buf, strerror(errno));
|
||||||
|
goto out_read;
|
||||||
|
}
|
||||||
|
osmo_wqueue_init(wq, 10);
|
||||||
|
wq->write_cb = l1fd_write_cb;
|
||||||
|
osmo_fd_setup(write_ofd, rc, OSMO_FD_WRITE, osmo_wqueue_bfd_cb, hdl, q);
|
||||||
|
rc = osmo_fd_register(write_ofd);
|
||||||
|
if (rc < 0) {
|
||||||
|
close(write_ofd->fd);
|
||||||
|
write_ofd->fd = -1;
|
||||||
|
goto out_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_read:
|
||||||
|
close(hdl->read_ofd[q].fd);
|
||||||
|
osmo_fd_unregister(&hdl->read_ofd[q]);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int l1if_transport_close(int q, struct lc15l1_hdl *hdl)
|
||||||
|
{
|
||||||
|
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||||
|
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||||
|
|
||||||
|
osmo_fd_unregister(read_ofd);
|
||||||
|
close(read_ofd->fd);
|
||||||
|
read_ofd->fd = -1;
|
||||||
|
|
||||||
|
osmo_fd_unregister(write_ofd);
|
||||||
|
close(write_ofd->fd);
|
||||||
|
write_ofd->fd = -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user