mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
synced 2025-11-03 05:33:31 +00:00
Compare commits
2008 Commits
jolly/outd
...
2021q4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d03c50024 | ||
|
|
11f72dfbcb | ||
|
|
56b7c64298 | ||
|
|
683ce64039 | ||
|
|
e30153ea10 | ||
|
|
0dcbc07682 | ||
|
|
5deac1404d | ||
|
|
13866f02a2 | ||
|
|
afd9393a2d | ||
|
|
17bfbedbc7 | ||
|
|
bd1b90f141 | ||
|
|
84f2b51a37 | ||
|
|
dbdf84eaff | ||
|
|
7eb9e69506 | ||
|
|
715aeb4ebc | ||
|
|
ace3b1bdc1 | ||
|
|
54a126f6d4 | ||
|
|
853cdf85eb | ||
|
|
978071bce9 | ||
|
|
d9066272ec | ||
|
|
fb904fbbd9 | ||
|
|
304b10a8b5 | ||
|
|
20dfa54508 | ||
|
|
ef8a730f6d | ||
|
|
19b3392166 | ||
|
|
13961c9b7a | ||
|
|
f8a93cb882 | ||
|
|
ba5683194a | ||
|
|
cc77eed9d9 | ||
|
|
10b29153b7 | ||
|
|
f61acd75c4 | ||
|
|
dbd3b78a9b | ||
|
|
dff399fa42 | ||
|
|
a02f945479 | ||
|
|
d3d46de278 | ||
|
|
de0eeafd2e | ||
|
|
063296883f | ||
|
|
92cbe4aee0 | ||
|
|
b6babc39dc | ||
|
|
48df600bfa | ||
|
|
812a7d3fa3 | ||
|
|
769e28114f | ||
|
|
bf129c1437 | ||
|
|
14015124ed | ||
|
|
43fc4a8690 | ||
|
|
7ce56d7c64 | ||
|
|
858f038c1c | ||
|
|
7e8d5ab4c4 | ||
|
|
dd28f82747 | ||
|
|
9ecdc11eb6 | ||
|
|
1859ec38cc | ||
|
|
ebdc0d8c17 | ||
|
|
089d734cd1 | ||
|
|
c90e6f8de1 | ||
|
|
fef3da24ae | ||
|
|
9f43c65c99 | ||
|
|
affd6a7f7a | ||
|
|
e50b5f1e3f | ||
|
|
8a9eec345e | ||
|
|
89a995a439 | ||
|
|
a2ef802dba | ||
|
|
77e2ff32d9 | ||
|
|
9c84c88259 | ||
|
|
f5cb4acb14 | ||
|
|
9b9f5efb09 | ||
|
|
196f36b75a | ||
|
|
592239630b | ||
|
|
b0aba59143 | ||
|
|
b3291bc0e3 | ||
|
|
880cbd3a8f | ||
|
|
b9fede74ef | ||
|
|
ffa2918bc5 | ||
|
|
32744c8916 | ||
|
|
ffe998ce9d | ||
|
|
38a9c873bc | ||
|
|
85aa87b61f | ||
|
|
27a4e7371c | ||
|
|
78ddfbc413 | ||
|
|
4a1c561ce8 | ||
|
|
0043005afb | ||
|
|
7a2b65ed2c | ||
|
|
201dbe04f7 | ||
|
|
8318d9c25d | ||
|
|
0dbf6f2a54 | ||
|
|
e080808cfa | ||
|
|
b5bad20731 | ||
|
|
abed2e326d | ||
|
|
2282b50e5c | ||
|
|
cee6b122c2 | ||
|
|
e9db5c7387 | ||
|
|
a27fb3fce3 | ||
|
|
c8d2166b86 | ||
|
|
422636d752 | ||
|
|
d72f46f020 | ||
|
|
c276e4996d | ||
|
|
864a41496c | ||
|
|
ea7cb48c9c | ||
|
|
fce67bc7fa | ||
|
|
eeae776345 | ||
|
|
0e3083daf5 | ||
|
|
402451b308 | ||
|
|
6c81adda45 | ||
|
|
5557c0af80 | ||
|
|
35d51ca4e3 | ||
|
|
1d663d9277 | ||
|
|
bf7bde1cbb | ||
|
|
25d60cafc4 | ||
|
|
0f88bcdebf | ||
|
|
767144f7b7 | ||
|
|
e376fd57cf | ||
|
|
3bbb3cc1f2 | ||
|
|
a89a23881f | ||
|
|
c4fe1f97b4 | ||
|
|
d06ec27856 | ||
|
|
f48de627f4 | ||
|
|
a161bf48bd | ||
|
|
ea8dbddab1 | ||
|
|
3e48cfd9f3 | ||
|
|
405d2d10ec | ||
|
|
3225290d77 | ||
|
|
907f037339 | ||
|
|
628d881247 | ||
|
|
fbc1baa139 | ||
|
|
afe189e802 | ||
|
|
9d67e72e85 | ||
|
|
c65c9e56e1 | ||
|
|
5bc6560efc | ||
|
|
49a2f404e8 | ||
|
|
432d4f3b89 | ||
|
|
ab8fba3a20 | ||
|
|
6ad11a6990 | ||
|
|
b0ead922a1 | ||
|
|
284711d627 | ||
|
|
131deb059f | ||
|
|
9dacf0b35b | ||
|
|
8c4f978483 | ||
|
|
62e06f92e9 | ||
|
|
f45ede640b | ||
|
|
cfb61d9536 | ||
|
|
55f600b702 | ||
|
|
efcb046ce1 | ||
|
|
c32c4a3648 | ||
|
|
65bba93afa | ||
|
|
720e19e7f3 | ||
|
|
33e8007100 | ||
|
|
88f34812df | ||
|
|
b5fece959f | ||
|
|
bc139a4af4 | ||
|
|
e54f148ce9 | ||
|
|
3f79470453 | ||
|
|
3bd6488889 | ||
|
|
d8e8ea9c8f | ||
|
|
112c63e9b4 | ||
|
|
4163361906 | ||
|
|
d3c7591304 | ||
|
|
4df959d305 | ||
|
|
978396732b | ||
|
|
3f561bfbfe | ||
|
|
945be91032 | ||
|
|
81db7334da | ||
|
|
890de986ce | ||
|
|
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
|
||||
*.lo
|
||||
*.a
|
||||
*.sw?
|
||||
Makefile.in
|
||||
Makefile
|
||||
.deps
|
||||
src/cscope*
|
||||
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
@@ -12,11 +14,57 @@ config.guess
|
||||
config.sub
|
||||
config.status
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
libtool
|
||||
*libtool
|
||||
ltmain.sh
|
||||
*~
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
9
TODO-RELEASE
Normal file
9
TODO-RELEASE
Normal file
@@ -0,0 +1,9 @@
|
||||
# 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
|
||||
248
configure.ac
248
configure.ac
@@ -1,13 +1,24 @@
|
||||
dnl Process this file with autoconf to produce a configure script
|
||||
AC_INIT([osmo-pcu],
|
||||
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])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CXXFLAGS="$CXXFLAGS -std=gnu++03"
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
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
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_CC
|
||||
@@ -15,25 +26,236 @@ AC_PROG_CXX
|
||||
AC_PROG_INSTALL
|
||||
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
|
||||
AC_HEADER_STDC
|
||||
|
||||
dnl Checks for typedefs, structures and compiler characteristics
|
||||
|
||||
dnl checks for libraries
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
|
||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING([--enable-sanitize], [Compile with address sanitizer enabled], )],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
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(sysmocom-bts,
|
||||
AC_HELP_STRING([--enable-sysmocom-bts],
|
||||
[enable code for sysmocom femto-bts [default=no]]),
|
||||
[enable_sysmocom_bts="yes"],[enable_sysmocom_bts="no"])
|
||||
AC_MSG_RESULT([$enable_sysmocom_bts])
|
||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[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.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.6.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(
|
||||
include/Makefile
|
||||
src/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
tests/Makefile
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-pcu.spec
|
||||
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.6.0
|
||||
BuildRequires: pkgconfig(libosmogb) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.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
|
||||
1180
debian/changelog
vendored
Normal file
1180
debian/changelog
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
9
|
||||
46
debian/control
vendored
Normal file
46
debian/control
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
Source: osmo-pcu
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
dh-autoreconf,
|
||||
autotools-dev,
|
||||
pkg-config,
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.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.
|
||||
133
debian/copyright
vendored
Normal file
133
debian/copyright
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
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.c
|
||||
src/csn1_dec.c
|
||||
src/csn1_enc.c
|
||||
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
|
||||
492
doc/manuals/chapters/configuration.adoc
Normal file
492
doc/manuals/chapters/configuration.adoc
Normal file
@@ -0,0 +1,492 @@
|
||||
== 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 Service. This service is nowadays provided by means
|
||||
of PCUIF container messages, which are transparently forwarded in both directions
|
||||
by the BTS using the IPA multiplex of the OML connection against the BSC. No
|
||||
specific configuration is required in any of the involved nodes, they should
|
||||
behave properly out of the box.
|
||||
|
||||
These neighbor address resolutions (<ARFCN + BSIC> => <RAI + CI>) are by default
|
||||
cached for a while, in order to avoid querying the BSC frequently. As a result,
|
||||
the resolution time is also optimized.
|
||||
|
||||
.Example: Configure Neighbor Resolution cache and timeouts
|
||||
----
|
||||
pcu
|
||||
timer X1 500 <1>
|
||||
timer X0 60 <2>
|
||||
----
|
||||
<1> Time out if the BSC doesn't answer our resolution request after 500 ms
|
||||
<2> Keep resolved neighbor addresses cached for 60 seconds
|
||||
|
||||
===== OsmoBSC CTRL interface (deprecated)
|
||||
|
||||
CAUTION: This interface is nowadays considered deprecated and should not be used
|
||||
anymore. Any related VTY options should be dropped from configuration files, to
|
||||
let OsmoPCU use the new interface instead. This section is kept here for a while
|
||||
as a reference for old deployments using old versions of the programs.
|
||||
|
||||
This Neighbor Address Resolution Service was initially implemented by means of a
|
||||
separate CTRL interface (see OsmoBSC User Manual), where OsmoPCU would create a
|
||||
CTRL connection to the BSC each time an address resolution was required.
|
||||
|
||||
Older versions of OsmoBSC may not support the current Neighbor Address
|
||||
Resolution Service over the IPA multiplex (see above). For those cases, OsmoPCU
|
||||
can be configured to use the old deprecated CTRL interface.
|
||||
|
||||
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.
|
||||
|
||||
.Example: Configure Neighbor Resolution CTRL interface against OsmoBSC
|
||||
----
|
||||
pcu
|
||||
neighbor resolution 172.18.13.10 4248 <1>
|
||||
----
|
||||
<1> Port 4248 is the default and hence could be omitted in this case
|
||||
|
||||
==== 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.
|
||||
|
||||
[[gsmtap]]
|
||||
=== Configuring GSMTAP tracing
|
||||
|
||||
In addition to being able to obtain pcap protocol traces of the NS/BSSGP
|
||||
communication and the text-based logging from the OsmoPCU software, there is
|
||||
also the capability of tracing all communication on the radio interface related
|
||||
to PS. To do so, OsmoPCU can encapsulate MAC blocks (23-155 byte messages at the
|
||||
L2-L1 interface depending on coding scheme) into _GSMTAP_ and send them via
|
||||
UDP/IP. At that point, they can be captured with utilities like *tcpdump* or
|
||||
*tshark* for further analysis by the *wireshark* protocol analyzer.
|
||||
|
||||
In order to activate this feature, you first need to make sure to specify
|
||||
the remote address of _GSMTAP_ host in the configuration file. In most
|
||||
cases, using 127.0.0.1 for passing the messages over the loopback (`lo`)
|
||||
device will be sufficient:
|
||||
|
||||
.Example: Enabling GSMTAP Um-frame logging to localhost
|
||||
----
|
||||
pcu
|
||||
gsmtap-remote-host 127.0.0.1 <1>
|
||||
----
|
||||
<1> Destination address for _GSMTAP_ Um-frames
|
||||
|
||||
NOTE: Changing this parameter at run-time will not affect the existing
|
||||
_GSMTAP_ connection, full program restart is required.
|
||||
|
||||
NOTE: Command line parameters `-i` and `--gsmtap-ip` have been deprecated.
|
||||
|
||||
OsmoPCU can selectively trace such messages based on different categories, for
|
||||
both Ul and Dl. For a complete list of cateogry values, please refer to the
|
||||
_OsmoPCU VTY reference manual_ <<vty-ref-osmopcu>>.
|
||||
|
||||
For example, to enable GSMTAP tracing for all DL EGPRS rlcmac data blocks, you
|
||||
can use the `gsmtap-category dl-data-egprs` command at the `pcu` node of the
|
||||
OsmoPCU VTY.
|
||||
|
||||
.Example: Enabling GSMTAP for for all DL EGPRS rlcmac data blocks
|
||||
----
|
||||
OsmoPCU> enable
|
||||
OsmoPCU# configure terminal
|
||||
OsmoPCU(config)# pcu
|
||||
OsmoPCU(pcu)# gsmtap-category dl-data-egprs
|
||||
OsmoPCU(trx)# write <1>
|
||||
----
|
||||
<1> the `write` command will make the configuration persistent in the
|
||||
configuration file. This is not required if you wish to enable GSMTAP
|
||||
only in the current session of OsmoPCU.
|
||||
|
||||
De-activation can be performed similarly by using the `no gsmtap-category
|
||||
dl-data-egprs` command at the `pcu` node of the OsmoPCU VTY.
|
||||
|
||||
It may be useful to enable all categories with a few exceptions, or vice versa
|
||||
disable everything using one command. For this purpose, the VTY provides
|
||||
`gsmtap-category enable-all` and `gsmtap-category disable-all` commands.
|
||||
|
||||
.Example: Enabling all categoriess except _dl-dummy_
|
||||
----
|
||||
pcu
|
||||
gsmtap-category enable-all <1>
|
||||
no gsmtap-category dl-dummy <2>
|
||||
----
|
||||
<1> Enable all available SAPIs
|
||||
<2> Exclude DL RLCMAC blocks
|
||||
|
||||
From the moment they are enabled via VTY, GSMTAP messages will be
|
||||
generated and sent in UDP encapsulation to the IANA-registered UDP port
|
||||
for GSMTAP (4729) of the specified remote address.
|
||||
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[]
|
||||
208
doc/manuals/chapters/counters_generated.adoc
Normal file
208
doc/manuals/chapters/counters_generated.adoc
Normal file
@@ -0,0 +1,208 @@
|
||||
|
||||
// autogenerated by show asciidoc counters
|
||||
These counters and their description are based on OsmoPCU 0.9.0.244-de96 (OsmoPCU).
|
||||
|
||||
=== Rate Counters
|
||||
|
||||
// generating tables for rate_ctr_group
|
||||
// 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)
|
||||
| packets:out:drop | <<ns:nsvc_packets:out:drop>> | Dropped Packets (Out)
|
||||
| bytes:in | <<ns:nsvc_bytes:in>> | Bytes at NS Level ( In)
|
||||
| bytes:out | <<ns:nsvc_bytes:out>> | Bytes at NS Level (Out)
|
||||
| bytes:out:drop | <<ns:nsvc_bytes:out:drop>> | Dropped Bytes (Out)
|
||||
| blocked | <<ns:nsvc_blocked>> | NS-VC Block count
|
||||
| unblocked | <<ns:nsvc_unblocked>> | NS-VC Unblock 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 NSE Peer Statistics
|
||||
.ns:nse - NSE Peer Statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| packets:in | <<ns:nse_packets:in>> | Packets at NS Level ( In)
|
||||
| packets:out | <<ns:nse_packets:out>> | Packets at NS Level (Out)
|
||||
| packets:out:drop | <<ns:nse_packets:out:drop>> | Dropped Packets (Out)
|
||||
| bytes:in | <<ns:nse_bytes:in>> | Bytes at NS Level ( In)
|
||||
| bytes:out | <<ns:nse_bytes:out>> | Bytes at NS Level (Out)
|
||||
| bytes:out:drop | <<ns:nse_bytes:out:drop>> | Dropped Bytes (Out)
|
||||
| blocked | <<ns:nse_blocked>> | NS-VC Block count
|
||||
| unblocked | <<ns:nse_unblocked>> | NS-VC Unblock count
|
||||
| dead | <<ns:nse_dead>> | NS-VC gone dead count
|
||||
| replaced | <<ns:nse_replaced>> | NS-VC replaced other count
|
||||
| nsei-chg | <<ns:nse_nsei-chg>> | NS-VC changed NSEI count
|
||||
| inv-nsvci | <<ns:nse_inv-nsvci>> | NS-VCI was invalid count
|
||||
| inv-nsei | <<ns:nse_inv-nsei>> | NSEI was invalid count
|
||||
| lost:alive | <<ns:nse_lost:alive>> | ALIVE ACK missing count
|
||||
| lost:reset | <<ns:nse_lost:reset>> | RESET ACK missing count
|
||||
|===
|
||||
// rate_ctr_group table SGSN Statistics
|
||||
.pcu:sgsn - SGSN Statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description
|
||||
| rx_paging_cs | <<pcu:sgsn_rx_paging_cs>> | Amount of paging CS requests received
|
||||
| rx_paging_ps | <<pcu:sgsn_rx_paging_ps>> | Amount of paging PS requests received
|
||||
|===
|
||||
// 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 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:alloc:failed | <<bts_tbf:alloc:failed>> | TBF Alloc Failure (any reason)
|
||||
| tbf:alloc:failed:no_tfi | <<bts_tbf:alloc:failed:no_tfi>> | TBF Alloc Failure (TFIs exhausted)
|
||||
| tbf:alloc:failed:no_usf | <<bts_tbf:alloc:failed:no_usf>> | TBF Alloc Failure (USFs exhausted)
|
||||
| tbf:alloc:failed:no_slot_combi | <<bts_tbf:alloc:failed:no_slot_combi>> | TBF Alloc Failure (No valid UL/DL slot combination found)
|
||||
| tbf:alloc:failed:no_slot_avail | <<bts_tbf:alloc:failed:no_slot_avail>> | TBF Alloc Failure (No slot available)
|
||||
| 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
|
||||
| pch:requests | <<bts_pch:requests>> | PCH requests sent
|
||||
| pch:requests:timeout | <<bts_pch:requests:timeout>> | PCH requests timeout
|
||||
| rach:requests | <<bts_rach:requests>> | RACH requests received
|
||||
| rach:requests:11bit | <<bts_rach:requests:11bit>> | 11BIT_RACH requests received
|
||||
| rach:requests:one_phase | <<bts_rach:requests:one_phase>> | One phase packet access with request for single TS UL
|
||||
| rach:requests:two_phase | <<bts_rach:requests:two_phase>> | Single block packet request for two phase packet access
|
||||
| rach:requests:unexpected | <<bts_rach:requests:unexpected>> | RACH Request with unexpected content received
|
||||
| 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_ul:one_phase | <<bts_immediate:assignment_ul:one_phase>> | Immediate Assign UL (one phase packet access)
|
||||
| immediate:assignment_ul:two_phase | <<bts_immediate:assignment_ul:two_phase>> | Immediate Assign UL (two phase packet access)
|
||||
| immediate:assignment_ul:contention_resolution_success | <<bts_immediate:assignment_ul:contention_resolution_success>> | First RLC Block (PDU) on the PDTCH from the MS received
|
||||
| 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
|
||||
| pkt:cell_chg_notification | <<bts_pkt:cell_chg_notification>> | Packet Cell Change Notification
|
||||
| pkt:cell_chg_continue | <<bts_pkt:cell_chg_continue>> | Packet Cell Change Continue
|
||||
| pkt:neigh_cell_data | <<bts_pkt:neigh_cell_data>> | Packet Neighbour Cell Data
|
||||
| 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
|
||||
|===
|
||||
=== Osmo Stat Items
|
||||
|
||||
// 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
|
||||
|===
|
||||
NS Bind Statistics
|
||||
// osmo_stat_item_group table NS Bind Statistics
|
||||
.ns.bind - NS Bind Statistics
|
||||
[options="header"]
|
||||
|===
|
||||
| Name | Reference | Description | Unit
|
||||
| tx_backlog_length | <<ns.bind_tx_backlog_length>> | Transmit backlog length | packets
|
||||
|===
|
||||
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 |
|
||||
| pdch.available | <<bts_pdch.available>> | PDCH available |
|
||||
| pdch.occupied | <<bts_pdch.occupied>> | PDCH occupied (all) |
|
||||
| pdch.occupied.gprs | <<bts_pdch.occupied.gprs>> | PDCH occupied (GPRS) |
|
||||
| pdch.occupied.egprs | <<bts_pdch.occupied.egprs>> | PDCH occupied (EGPRS) |
|
||||
|===
|
||||
// there are no ungrouped osmo_counters
|
||||
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.
|
||||
27
doc/manuals/chapters/running.adoc
Normal file
27
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,27 @@
|
||||
== 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.
|
||||
*-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
|
||||
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.
|
||||
|
||||
States:
|
||||
GPRS_RLCMAC_ASSIGN
|
||||
TBF_ST_ASSIGN
|
||||
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
|
||||
to connect to downlink PDCH.
|
||||
|
||||
GPRS_RLCMAC_FLOW,
|
||||
TBF_ST_FLOW,
|
||||
During packet flow, this state indicates downlink and uplink TBF block
|
||||
flow.
|
||||
|
||||
GPRS_RLCMAC_FINISHED,
|
||||
TBF_ST_FINISHED,
|
||||
Uplink TBF:
|
||||
After final block is received AND all other blocks are completely
|
||||
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
|
||||
poll request.)
|
||||
|
||||
GPRS_RLCMAC_WAIT_RELEASE,
|
||||
TBF_ST_WAIT_RELEASE,
|
||||
The all blocks on downlink TBF have been acked by mobile. The penalty
|
||||
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
|
||||
reaches it's maximum and T3169 is running.
|
||||
|
||||
@@ -52,7 +52,7 @@ When downlink LLC PDU is received:
|
||||
Attach PDU to LLC Frame of TBF.
|
||||
Put TBF back into FLOW state.
|
||||
Done.
|
||||
If dowlink TBF does not exists for given TLLI, or in RELEASING state:
|
||||
If downlink TBF does not exists for given TLLI, or in RELEASING state:
|
||||
Create new downlink TBF.
|
||||
Attach PDU to LLC Frame of TBF.
|
||||
If uplink TBF exists for given TLLI, but not in RELEASING state:
|
||||
@@ -117,13 +117,17 @@ Control TS:
|
||||
|
||||
Polling:
|
||||
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
|
||||
assign uplink ressource for other TBFs at that frame number.
|
||||
frame number is stored at PDCH UL Controller. The scheduler reads that value
|
||||
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
|
||||
indicated by layer 1 interface. There are two ways of checking timeout:
|
||||
- The received frame is bad (BFI).
|
||||
- The GSM indicates that the block should have been already received.
|
||||
On receipt of an Uplink RLCMAC block, it's the duty of each specific message
|
||||
handler to release the expectancies previously stored in the PDCH UL
|
||||
Controller. After the specific handler returns, the generic return path will
|
||||
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
|
||||
performed at control TS.
|
||||
@@ -131,13 +135,21 @@ Polling:
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
- downlink TBFs
|
||||
|
||||
@@ -147,7 +159,7 @@ Data structures of TBFs and PDCHs:
|
||||
- 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
|
||||
|
||||
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 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
|
||||
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
|
||||
284
include/osmocom/pcu/pcuif_proto.h
Normal file
284
include/osmocom/pcu/pcuif_proto.h
Normal file
@@ -0,0 +1,284 @@
|
||||
#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_NEIGH_ADDR_REQ 0x81 /* Neighbor Address Resolution Request */
|
||||
#define PCU_IF_MSG_NEIGH_ADDR_CNF 0x82 /* Neighbor Address Resolution Confirmation */
|
||||
|
||||
/* 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: NOTE: values must be network byte order here! ***/
|
||||
/* Neighbor Address Resolution Request */
|
||||
struct gsm_pcu_if_neigh_addr_req {
|
||||
uint16_t local_lac;
|
||||
uint16_t local_ci;
|
||||
uint16_t tgt_arfcn;
|
||||
uint8_t tgt_bsic;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Neighbor Address Resolution Confirmation */
|
||||
struct gsm_pcu_if_neigh_addr_cnf {
|
||||
struct gsm_pcu_if_neigh_addr_req orig_req;
|
||||
uint8_t err_code; /* 0 success, !0 failed & below unset */
|
||||
/* RAI + CI (CGI-PS): */
|
||||
struct __attribute__ ((packed)) {
|
||||
uint16_t mcc;
|
||||
uint16_t mnc;
|
||||
uint8_t mnc_3_digits;
|
||||
uint16_t lac;
|
||||
uint8_t rac;
|
||||
uint16_t cell_identity;
|
||||
} cgi_ps;
|
||||
} __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]
|
||||
194
src/Makefile.am
194
src/Makefile.am
@@ -18,61 +18,197 @@
|
||||
# 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_LDFLAGS = -lrt
|
||||
|
||||
noinst_LTLIBRARIES = libgprs.la
|
||||
|
||||
libgprs_la_SOURCES = \
|
||||
gprs_debug.cpp \
|
||||
csn1.cpp \
|
||||
gsm_rlcmac.cpp \
|
||||
gprs_bssgp_pcu.cpp \
|
||||
csn1.c \
|
||||
csn1_dec.c \
|
||||
csn1_enc.c \
|
||||
gsm_rlcmac.c \
|
||||
gprs_bssgp_pcu.c \
|
||||
gprs_bssgp_rim.c \
|
||||
gprs_rlcmac.cpp \
|
||||
gprs_rlcmac_data.cpp \
|
||||
gprs_rlcmac_sched.cpp \
|
||||
gsm_timer.cpp \
|
||||
bitvector.cpp \
|
||||
gprs_rlcmac_meas.cpp \
|
||||
gprs_rlcmac_ts_alloc.cpp \
|
||||
gprs_ms.c \
|
||||
gprs_ms_storage.cpp \
|
||||
gprs_pcu.c \
|
||||
pcu_l1_if.cpp \
|
||||
pcu_vty.c
|
||||
|
||||
if ENABLE_SYSMOBTS
|
||||
libgprs_la_SOURCES += \
|
||||
sysmo_sock.cpp
|
||||
else
|
||||
libgprs_la_SOURCES += \
|
||||
openbts_sock.cpp
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
RLCMACTest
|
||||
pcu_vty.c \
|
||||
pcu_vty_functions.cpp \
|
||||
mslot_class.c \
|
||||
nacc_fsm.c \
|
||||
neigh_cache.c \
|
||||
tbf.cpp \
|
||||
tbf_fsm.c \
|
||||
tbf_ul.cpp \
|
||||
tbf_ul_ack_fsm.c \
|
||||
tbf_ul_ass_fsm.c \
|
||||
tbf_dl.cpp \
|
||||
tbf_dl_ass_fsm.c \
|
||||
bts.cpp \
|
||||
bts_pch_timer.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 = \
|
||||
osmo-pcu
|
||||
|
||||
noinst_PROGRAMS =
|
||||
|
||||
noinst_HEADERS = \
|
||||
gprs_debug.h \
|
||||
csn1.h \
|
||||
gsm_rlcmac.h \
|
||||
gprs_bssgp_pcu.h \
|
||||
gprs_bssgp_rim.h \
|
||||
gprs_rlcmac.h \
|
||||
pcuif_proto.h \
|
||||
gprs_ms.h \
|
||||
gprs_ms_storage.h \
|
||||
gprs_pcu.h \
|
||||
pcu_l1_if.h \
|
||||
gsm_timer.h \
|
||||
bitvector.h \
|
||||
pcu_vty.h
|
||||
|
||||
RLCMACTest_SOURCES = RLCMACTest.cpp
|
||||
RLCMACTest_LDADD = \
|
||||
libgprs.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
pcu_vty.h \
|
||||
pcu_vty_functions.h \
|
||||
mslot_class.h \
|
||||
nacc_fsm.h \
|
||||
neigh_cache.h \
|
||||
tbf.h \
|
||||
tbf_fsm.h \
|
||||
tbf_ul.h \
|
||||
tbf_ul_ack_fsm.h \
|
||||
tbf_ul_ass_fsm.h \
|
||||
tbf_dl.h \
|
||||
tbf_dl_ass_fsm.h \
|
||||
bts.h \
|
||||
bts_pch_timer.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
|
||||
|
||||
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 = \
|
||||
libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(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;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/* bitvector.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.
|
||||
*/
|
||||
|
||||
#ifndef BITVECTOR_H
|
||||
#define BITVECTOR_H
|
||||
|
||||
/*! \defgroup bitvector Bit vectors
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*! \file bitvector.h
|
||||
* \brief Additional functions for Osmocom bit vector abstraction.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/bitvec.h>
|
||||
}
|
||||
|
||||
struct bitvec *bitvec_alloc(unsigned size);
|
||||
void bitvec_free(struct bitvec *bv);
|
||||
int bitvec_unhex(struct bitvec *bv, const char* src);
|
||||
int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
|
||||
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
|
||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len);
|
||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
|
||||
|
||||
/*! }@ */
|
||||
|
||||
#endif // BITVECTOR_H
|
||||
1416
src/bts.cpp
Normal file
1416
src/bts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
387
src/bts.h
Normal file
387
src/bts.h
Normal file
@@ -0,0 +1,387 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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/core/time_cc.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"
|
||||
#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_PDCH_ALL_ALLOCATED,
|
||||
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_PCH_REQUESTS,
|
||||
CTR_PCH_REQUESTS_ALREADY,
|
||||
CTR_PCH_REQUESTS_TIMEDOUT,
|
||||
CTR_RACH_REQUESTS,
|
||||
CTR_RACH_REQUESTS_11BIT,
|
||||
CTR_RACH_REQUESTS_ONE_PHASE,
|
||||
CTR_RACH_REQUESTS_TWO_PHASE,
|
||||
CTR_RACH_REQUESTS_UNEXPECTED,
|
||||
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_UL_TBF_ONE_PHASE,
|
||||
CTR_IMMEDIATE_ASSIGN_UL_TBF_TWO_PHASE,
|
||||
CTR_IMMEDIATE_ASSIGN_UL_TBF_CONTENTION_RESOLUTION_SUCCESS,
|
||||
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_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,
|
||||
STAT_PDCH_AVAILABLE,
|
||||
STAT_PDCH_OCCUPIED,
|
||||
STAT_PDCH_OCCUPIED_GPRS,
|
||||
STAT_PDCH_OCCUPIED_EGPRS,
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
/* List of struct bts_pch_timer for active PCH pagings */
|
||||
struct llist_head pch_timer;
|
||||
|
||||
struct osmo_time_cc all_allocated_pdch;
|
||||
};
|
||||
|
||||
#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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#define bts_stat_item_inc(bts, stat_id) bts_stat_item_add(bts, stat_id, 1)
|
||||
|
||||
#define bts_stat_item_dec(bts, stat_id) bts_stat_item_add(bts, stat_id, -1)
|
||||
|
||||
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);
|
||||
bool bts_all_pdch_allocated(const struct gprs_rlcmac_bts *bts);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
121
src/bts_pch_timer.c
Normal file
121
src/bts_pch_timer.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Oliver Smith
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_pcu.h>
|
||||
#include <bts_pch_timer.h>
|
||||
#include <gprs_ms.h>
|
||||
|
||||
static struct bts_pch_timer *bts_pch_timer_get_by_ptmsi(struct gprs_rlcmac_bts *bts, uint32_t ptmsi)
|
||||
{
|
||||
struct bts_pch_timer *p;
|
||||
OSMO_ASSERT(ptmsi != GSM_RESERVED_TMSI);
|
||||
|
||||
llist_for_each_entry(p, &bts->pch_timer, entry) {
|
||||
if (p->ptmsi != GSM_RESERVED_TMSI && p->ptmsi == ptmsi)
|
||||
return p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi)
|
||||
{
|
||||
struct bts_pch_timer *p;
|
||||
|
||||
llist_for_each_entry(p, &bts->pch_timer, entry) {
|
||||
if (strcmp(p->imsi, imsi) == 0)
|
||||
return p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bts_pch_timer_remove(struct bts_pch_timer *p)
|
||||
{
|
||||
osmo_timer_del(&p->T3113);
|
||||
llist_del(&p->entry);
|
||||
|
||||
LOGP(DPCU, LOGL_DEBUG, "PCH paging timer stopped for IMSI=%s\n", p->imsi);
|
||||
talloc_free(p);
|
||||
}
|
||||
|
||||
static void T3113_callback(void *data)
|
||||
{
|
||||
struct bts_pch_timer *p = data;
|
||||
|
||||
LOGP(DPCU, LOGL_INFO, "PCH paging timeout for IMSI=%s\n", p->imsi);
|
||||
bts_do_rate_ctr_inc(p->bts, CTR_PCH_REQUESTS_TIMEDOUT);
|
||||
bts_pch_timer_remove(p);
|
||||
}
|
||||
|
||||
void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
|
||||
const char *imsi)
|
||||
{
|
||||
struct bts_pch_timer *p;
|
||||
struct osmo_tdef *tdef;
|
||||
|
||||
p = talloc_zero(bts, struct bts_pch_timer);
|
||||
llist_add_tail(&p->entry, &bts->pch_timer);
|
||||
p->bts = bts;
|
||||
OSMO_STRLCPY_ARRAY(p->imsi, imsi);
|
||||
p->ptmsi = (mi_paging->type == GSM_MI_TYPE_TMSI) ? mi_paging->tmsi : GSM_RESERVED_TMSI;
|
||||
|
||||
tdef = osmo_tdef_get_entry(the_pcu->T_defs, 3113);
|
||||
OSMO_ASSERT(tdef);
|
||||
osmo_timer_setup(&p->T3113, T3113_callback, p);
|
||||
osmo_timer_schedule(&p->T3113, tdef->val, 0);
|
||||
|
||||
if (log_check_level(DPCU, LOGL_DEBUG)) {
|
||||
char str[64];
|
||||
osmo_mobile_identity_to_str_buf(str, sizeof(str), mi_paging);
|
||||
LOGP(DPCU, LOGL_DEBUG, "PCH paging timer started for MI=%s IMSI=%s\n", str, p->imsi);
|
||||
}
|
||||
}
|
||||
|
||||
void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms)
|
||||
{
|
||||
struct bts_pch_timer *p = NULL;
|
||||
uint32_t tlli = ms_tlli(ms);
|
||||
const char *imsi = ms_imsi(ms);
|
||||
|
||||
/* First try matching by TMSI if available in MS */
|
||||
if (tlli != GSM_RESERVED_TMSI)
|
||||
p = bts_pch_timer_get_by_ptmsi(bts, tlli);
|
||||
/* Otherwise try matching by IMSI if available in MS */
|
||||
if (!p && imsi[0] != '\0')
|
||||
p = bts_pch_timer_get_by_imsi(bts, imsi);
|
||||
if (p)
|
||||
bts_pch_timer_remove(p);
|
||||
}
|
||||
|
||||
void bts_pch_timer_stop_all(struct gprs_rlcmac_bts *bts)
|
||||
{
|
||||
struct bts_pch_timer *p, *n;
|
||||
|
||||
llist_for_each_entry_safe(p, n, &bts->pch_timer, entry) {
|
||||
bts_pch_timer_remove(p);
|
||||
}
|
||||
}
|
||||
49
src/bts_pch_timer.h
Normal file
49
src/bts_pch_timer.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Oliver Smith
|
||||
*
|
||||
* 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 Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
|
||||
#include <bts.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bts_pch_timer {
|
||||
struct llist_head entry;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct osmo_timer_list T3113;
|
||||
uint32_t ptmsi; /* GSM_RESERVED_TMSI if not available */
|
||||
char imsi[OSMO_IMSI_BUF_SIZE];
|
||||
};
|
||||
|
||||
struct GprsMs;
|
||||
|
||||
void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
|
||||
const char *imsi);
|
||||
void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms);
|
||||
void bts_pch_timer_stop_all(struct gprs_rlcmac_bts *bts);
|
||||
struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
404
src/coding_scheme.c
Normal file
404
src/coding_scheme.c
Normal file
@@ -0,0 +1,404 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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)];
|
||||
}
|
||||
}
|
||||
95
src/coding_scheme.h
Normal file
95
src/coding_scheme.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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);
|
||||
103
src/csn1.c
Normal file
103
src/csn1.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/* csn1_enc.c
|
||||
* Routines for CSN1 dissection in wireshark.
|
||||
*
|
||||
* Copyright (C) 2011 Ivan Klyuchnikov
|
||||
*
|
||||
* By Vincent Helfre, based on original code by Jari Sassi
|
||||
* with the gracious authorization of STE
|
||||
* Copyright (c) 2011 ST-Ericsson
|
||||
*
|
||||
* $Id: packet-csn1.c 39140 2011-09-25 22:01:50Z wmeier $
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include "csn1.h"
|
||||
#include <gprs_debug.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
const unsigned char ixBitsTab[] = {0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5};
|
||||
|
||||
/* Returns no_of_bits (up to 8) masked with 0x2B */
|
||||
guint8
|
||||
get_masked_bits8(struct bitvec *vector, unsigned *readIndex, gint bit_offset, const gint no_of_bits)
|
||||
{
|
||||
static const guint8 maskBits[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF};
|
||||
//gint byte_offset = bit_offset >> 3; /* divide by 8 */
|
||||
gint relative_bit_offset = bit_offset & 0x07; /* modulo 8 */
|
||||
guint8 result;
|
||||
gint bit_shift = 8 - relative_bit_offset - (gint) no_of_bits;
|
||||
*readIndex -= relative_bit_offset;
|
||||
if (bit_shift >= 0)
|
||||
{
|
||||
result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> bit_shift;
|
||||
*readIndex-= bit_shift;
|
||||
result &= maskBits[no_of_bits];
|
||||
}
|
||||
else
|
||||
{
|
||||
guint8 hight_part = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) & maskBits[8 - relative_bit_offset];
|
||||
hight_part = (guint8) (hight_part << (-bit_shift));
|
||||
result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> (8 + bit_shift);
|
||||
*readIndex = *readIndex - (8 - (-bit_shift));
|
||||
result |= hight_part;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ================================================================================================
|
||||
* set initial/start values in help data structure used for packing/unpacking operation
|
||||
* ================================================================================================
|
||||
*/
|
||||
void
|
||||
csnStreamInit(csnStream_t* ar, gint bit_offset, gint remaining_bits_len)
|
||||
{
|
||||
ar->remaining_bits_len = remaining_bits_len;
|
||||
ar->bit_offset = bit_offset;
|
||||
ar->direction = 0;
|
||||
}
|
||||
|
||||
static const struct value_string csn1_error_names[] = {
|
||||
{ CSN_OK, "General 0" },
|
||||
{ CSN_ERROR_GENERAL, "General -1" },
|
||||
{ CSN_ERROR_DATA_NOT_VALID, "DATA_NOT VALID" },
|
||||
{ CSN_ERROR_IN_SCRIPT, "IN SCRIPT" },
|
||||
{ CSN_ERROR_INVALID_UNION_INDEX, "INVALID UNION INDEX" },
|
||||
{ CSN_ERROR_NEED_MORE_BITS_TO_UNPACK, "NEED_MORE BITS TO UNPACK" },
|
||||
{ CSN_ERROR_ILLEGAL_BIT_VALUE, "ILLEGAL BIT VALUE" },
|
||||
{ CSN_ERROR_INTERNAL, "INTERNAL" },
|
||||
{ CSN_ERROR_STREAM_NOT_SUPPORTED, "STREAM_NOT_SUPPORTED" },
|
||||
{ CSN_ERROR_MESSAGE_TOO_LONG, "MESSAGE_TOO_LONG" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
gint16 ProcessError_impl(const char *file, int line, unsigned *readIndex,
|
||||
const char* sz, gint16 err, const CSN_DESCR* pDescr)
|
||||
{
|
||||
/* Don't add trailing newline, top caller is responsible for appending it */
|
||||
if (err != CSN_OK)
|
||||
LOGPSRC(DCSN1, LOGL_ERROR, file, line, "%s: error %s (%d) at %s (idx %u)",
|
||||
sz, get_value_string(csn1_error_names, err), err,
|
||||
pDescr ? pDescr->sz : "-", *readIndex);
|
||||
return err;
|
||||
}
|
||||
2603
src/csn1.cpp
2603
src/csn1.cpp
File diff suppressed because it is too large
Load Diff
253
src/csn1.h
253
src/csn1.h
@@ -19,21 +19,13 @@
|
||||
* 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>
|
||||
#ifndef _PACKET_CSN1_H_
|
||||
#define _PACKET_CSN1_H_
|
||||
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
//#define max(a,b) (((a)>(b))?(a):(b))
|
||||
#include <osmocom/core/bitvec.h>
|
||||
#include "wireshark_compat.h"
|
||||
|
||||
/* Error codes */
|
||||
#define CSN_OK 0
|
||||
@@ -48,15 +40,6 @@
|
||||
#define CSN_ERROR_MESSAGE_TOO_LONG -9
|
||||
#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 */
|
||||
typedef gint16 CSN_CallBackStatus_t;
|
||||
|
||||
@@ -73,6 +56,7 @@ typedef gint16 CSN_CallBackStatus_t;
|
||||
#ifndef ElementsOf
|
||||
#define ElementsOf(array) (sizeof(array) / sizeof(array[0]))
|
||||
#endif
|
||||
typedef void(*void_fn_t)(void);
|
||||
|
||||
/* Context holding CSN1 parameters */
|
||||
typedef struct
|
||||
@@ -82,7 +66,9 @@ typedef struct
|
||||
gint direction; /* 0 - decode; 1 - encode */
|
||||
} 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
|
||||
{
|
||||
CSN_END = 0,
|
||||
@@ -99,6 +85,7 @@ typedef enum
|
||||
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_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_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) */
|
||||
@@ -127,7 +114,7 @@ typedef enum
|
||||
*
|
||||
* i:
|
||||
* 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
|
||||
* for the CSN_VAR_ARRAY type
|
||||
* - the length of each element of an array for the CSN_REC_ARRAY type
|
||||
@@ -142,6 +129,7 @@ typedef enum
|
||||
* CSN_VAR_BITMAP, CSN_LEFT_VAR_BMP, and CSN_LEFT_BMP_1 types
|
||||
* - the offset to param1 for the CSN_CALLBACK type
|
||||
* - ERRORCODE used by the CSN_ERROR type
|
||||
* - the bit-length of the LENGTH field in a CSN_SERIALISE type
|
||||
*
|
||||
* descr
|
||||
* This parameter has different meaning depending on the value of the type parameter:
|
||||
@@ -167,6 +155,10 @@ typedef enum
|
||||
* - 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
|
||||
*
|
||||
* may_be_null
|
||||
* TRUE: if dissection may be attempted at an offset beyond the length of existing data bits
|
||||
* FALSE: othewise
|
||||
*
|
||||
* sz
|
||||
* - 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.
|
||||
@@ -184,23 +176,21 @@ typedef struct
|
||||
gint16 i;
|
||||
union
|
||||
{
|
||||
void* ptr;
|
||||
const void* ptr;
|
||||
guint32 value;
|
||||
} descr;
|
||||
unsigned offset;
|
||||
gboolean may_be_null;
|
||||
const char* sz;
|
||||
union
|
||||
{
|
||||
StreamSerializeFcn_t fcn;
|
||||
guint32 value;
|
||||
int* hf_ptr;
|
||||
} serialize;
|
||||
guint32 value;
|
||||
void_fn_t aux_fn;
|
||||
} CSN_DESCR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint8 bits;
|
||||
guint8 value;
|
||||
gboolean keep_bits;
|
||||
CSN_DESCR descr;
|
||||
} CSN_ChoiceElement_t;
|
||||
|
||||
@@ -223,16 +213,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
|
||||
******************************************************************************/
|
||||
|
||||
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 */
|
||||
#define CSN_DESCR_BEGIN(_STRUCT)\
|
||||
CSN_DESCR CSNDESCR_##_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)
|
||||
@@ -243,7 +233,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par3: Error code
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -252,11 +242,21 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par2: C structure element name
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* Indicates whether the next element or a group of elements defined in the
|
||||
* Indicates whether the next element or a group of elements defined in the
|
||||
* structure is present or not.
|
||||
* Par1: C structure name
|
||||
* Par2: C structure element name
|
||||
@@ -264,37 +264,37 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* element(s) does not exist
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* similar to the M_NEXT_EXIST except that instead of bit 0/1 which is fetched
|
||||
* from the message in order to find out whether the next element/elements are
|
||||
* present in the message, the logical operation XOR with the background
|
||||
* present in the message, the logical operation XOR with the background
|
||||
* pattern 0x2B is performed on the read bit before the decision is made.
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* Similar to the M_NEXT_EXIST 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
|
||||
* of the message may be encountered when looking for the next element in the
|
||||
* message.
|
||||
* Covers the case {null | 0 | 1 < IE >}
|
||||
* Covers the case {null | 0 | 1 < IE >}
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* Similar to the M_NEXT_EXIST_LH 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 | L | H < IE >}
|
||||
* Covers the case {null | L | H < IE >}
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -304,17 +304,27 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par3: number of bits used to code the element (between 1 and 32)
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* This macro has the same functionality as M_UINT except that in addition the
|
||||
* logical "exclusive or" operation with the background value "0x2B" is
|
||||
* performed before the final value of the integer number is delivered from the
|
||||
* logical "exclusive or" operation with the background value "0x2B" is
|
||||
* performed before the final value of the integer number is delivered from the
|
||||
* received CSN.1 message
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -325,7 +335,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par4: value added to the returned integer (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)
|
||||
@@ -336,7 +346,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par4: number of elements in the array (fixed integer value)
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -348,7 +358,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* structure member holding the length value
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -359,7 +369,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
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -370,32 +380,32 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par4: name of the structure member holding the size of the array
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* Same as M_VAR_TARRAY with offset
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* similar to the M_VAR_ARRAY. The difference is that the size of the array is
|
||||
* similar to the M_VAR_ARRAY. The difference is that the size of the array is
|
||||
* not known in advance and it has to be calculated during unpacking. Its value
|
||||
* is stored in a variable which belongs to the same structure as the array.
|
||||
* A zero element terminates the array. The CSN.1 syntax describes it
|
||||
* is stored in a variable which belongs to the same structure as the array.
|
||||
* A zero element terminates the array. The CSN.1 syntax describes it
|
||||
* recursively as:
|
||||
* <array> ::={1 <element> <array>| 0}
|
||||
* <array> ::={1 <element> <array>| 0}
|
||||
*
|
||||
* Par1: C structure name
|
||||
* Par2: C structure element name
|
||||
* Par3: name of the structure member where the calculated the size of the
|
||||
* Par3: name of the structure member where the calculated the size of the
|
||||
* array will be stored
|
||||
* Par4: length of each element in 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)
|
||||
@@ -406,7 +416,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par4: number of elements in the array (fixed integer value)
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -418,7 +428,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par4: will hold the number of element in the array after unpacking
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -426,7 +436,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* <list> ::= <type> {1 <type>} ** 0 ;
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -434,7 +444,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* <lists> ::= <type> { 0 <type> } ** 1 ;
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
@@ -445,130 +455,161 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par3: type of member
|
||||
*****************************************************************************/
|
||||
#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_TYPE_OR_NULL(Par1, Par2, Par3)
|
||||
* Similar to the M_TYPE 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_TYPE_OR_NULL(_STRUCT, _MEMBER, _MEMBER_TYPE)\
|
||||
{CSN_TYPE, 0, {(const void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
|
||||
|
||||
/******************************************************************************
|
||||
* M_UNION(Par1, Par2)
|
||||
* Informs the CSN.1 library that a union follows and how many possible choices
|
||||
* there are in the union. The actual value of the choice, which points out the
|
||||
* chosen element of the union is stored in the uint8 variable and is usually
|
||||
* called UnionType. The elements of the union have to be listed directly after
|
||||
* chosen element of the union is stored in the uint8 variable and is usually
|
||||
* called UnionType. The elements of the union have to be listed directly after
|
||||
* the M_UNION statement.
|
||||
* Par1: C structure name
|
||||
* Par2: number of possible choice in the union
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* Same as M_UNION but masked with background value 0x2B
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* Similar to the M_UNION. In the M_UNION the selected element of all possible
|
||||
* choices in the union is referred as a sequential numbers, i.e., the first
|
||||
* choice is addressed as choice 0 the second as choice 1, the third as choice
|
||||
* Similar to the M_UNION. In the M_UNION the selected element of all possible
|
||||
* choices in the union is referred as a sequential numbers, i.e., the first
|
||||
* choice is addressed as choice 0 the second as choice 1, the third as choice
|
||||
* 2 and so on, both in the encoded message and in the variable UnionType which
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* values of the selector are provided, together with the selector
|
||||
* length expressed in bits and the address of the CSN_DESCR type
|
||||
* where the element is defined. For every element in the union
|
||||
* there is one line in the Choice variable. These lines have to
|
||||
* appear in the _CHOICE in the same order as the elements in the
|
||||
* union. The element of the union selected in the message through
|
||||
* the _CHOICE parameter is after unpacking translated to the
|
||||
* corresponding sequential number of this element and stored in
|
||||
* values of the selector are provided, together with the selector
|
||||
* length expressed in bits and the address of the CSN_DESCR type
|
||||
* where the element is defined. For every element in the union
|
||||
* there is one line in the Choice variable. These lines have to
|
||||
* appear in the _CHOICE in the same order as the elements in the
|
||||
* union. The element of the union selected in the message through
|
||||
* the _CHOICE parameter is after unpacking translated to the
|
||||
* corresponding sequential number of this element and stored in
|
||||
* the variable pointed out by the _MEMBER
|
||||
* Par4: number of possible choices in the union
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* Defines a fixed value of type integer which should be fetched from or stored
|
||||
* Defines a fixed value of type integer which should be fetched from or stored
|
||||
* in the message
|
||||
* Par1: C structure name
|
||||
* Par2: gives the length of the fixed number in bits.
|
||||
* Par3: the value of the number. If the expected value is not present in
|
||||
* Par3: the value of the number. If the expected value is not present in
|
||||
* the message the unpacking procedure is aborted
|
||||
*****************************************************************************/
|
||||
#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)
|
||||
* 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
|
||||
* the CSNstream program passes the control over to the specified function
|
||||
* together with all necessary parameters about the current position within
|
||||
* the message being unpacked or packed. When transferring of "serialized"
|
||||
* data to or from the message is finished by the function the CSNstream gets
|
||||
* Allows using a complete free format of data being encoded or decoded.
|
||||
* When the M_SERIALIZE is encounted during encoding or decoding of a message
|
||||
* the CSNstream program passes the control over to the specified function
|
||||
* together with all necessary parameters about the current position within
|
||||
* the message being unpacked or packed. When transferring of "serialized"
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
#define M_SERIALIZE(_STRUCT, _MEMBER, _SERIALIZEFCN)\
|
||||
{CSN_SERIALIZE, 1, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {_SERIALIZEFCN}}
|
||||
#define M_SERIALIZE(_STRUCT, _MEMBER, _LENGTH_LEN, _SERIALIZEFCN)\
|
||||
{CSN_SERIALIZE, _LENGTH_LEN, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, (void_fn_t)_SERIALIZEFCN}
|
||||
|
||||
#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)
|
||||
* Defines a type which consists of a bitmap. The size of the bitmap in bits
|
||||
* Defines a type which consists of a bitmap. The size of the bitmap in bits
|
||||
* is fixed and provided by the parameter Par3
|
||||
* Par1: C structure name
|
||||
* Par2: C structure element name
|
||||
* Par3: length of the bitmap expressed in 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 */
|
||||
#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
|
||||
* - when unpacking the _ElementCountField will be set in runtime
|
||||
* - when packing _ElementCountField contains the size of bitmap
|
||||
*/
|
||||
#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 */
|
||||
#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
|
||||
*- when unpacking the _ElementCountField will be set in runtime
|
||||
* - when packing _ElementCountField contains the size of bitmap
|
||||
*/
|
||||
#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)\
|
||||
{CSN_NULL, 0, {0}, offsetof(_STRUCT, _MEMBER), #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
/* todo: dissect padding bits looking for unexpected extensions */
|
||||
#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)\
|
||||
{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)\
|
||||
{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 */
|
||||
typedef gint16 (*CsnCallBackFcn_t)(void* pv ,...);
|
||||
|
||||
#define CSNDESCR(_FuncType) CSNDESCR_##_FuncType
|
||||
|
||||
#define pvDATA(_pv, _offset) ((void*) ((unsigned char*)_pv + _offset))
|
||||
#define pui8DATA(_pv, _offset) ((guint8*) pvDATA(_pv, _offset))
|
||||
#define pui16DATA(_pv, _offset) ((guint16*) pvDATA(_pv, _offset))
|
||||
#define pui32DATA(_pv, _offset) ((guint32*) pvDATA(_pv, _offset))
|
||||
#define pui64DATA(_pv, _offset) ((guint64*) pvDATA(_pv, _offset))
|
||||
/* used to tag existence of next element in variable length lists */
|
||||
#define STANDARD_TAG 1
|
||||
#define REVERSED_TAG 0
|
||||
|
||||
gint16 ProcessError_impl(const char *file, int line, unsigned *readIndex,
|
||||
const char* sz, gint16 err, const CSN_DESCR* pDescr);
|
||||
#define ProcessError(readIndex, sz, err, pDescr) \
|
||||
ProcessError_impl(__FILE__, __LINE__, readIndex, sz, err, pDescr)
|
||||
|
||||
#endif /*_PACKET_CSN1_H_*/
|
||||
|
||||
1424
src/csn1_dec.c
Normal file
1424
src/csn1_dec.c
Normal file
File diff suppressed because it is too large
Load Diff
1301
src/csn1_enc.c
Normal file
1301
src/csn1_enc.c
Normal file
File diff suppressed because it is too large
Load Diff
129
src/cxx_linuxlist.h
Normal file
129
src/cxx_linuxlist.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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));
|
||||
}
|
||||
783
src/decoding.cpp
Normal file
783
src/decoding.cpp
Normal file
@@ -0,0 +1,783 @@
|
||||
/* 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.
|
||||
*/
|
||||
#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 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;
|
||||
}
|
||||
97
src/decoding.h
Normal file
97
src/decoding.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* 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.
|
||||
*/
|
||||
#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 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();
|
||||
};
|
||||
1802
src/encoding.cpp
Normal file
1802
src/encoding.cpp
Normal file
File diff suppressed because it is too large
Load Diff
127
src/encoding.h
Normal file
127
src/encoding.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/* 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.
|
||||
*/
|
||||
#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 encode_rbb(const char *show_rbb, bitvec *rbb);
|
||||
|
||||
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_access_reject(struct bitvec *dest, uint32_t tlli, unsigned long t3172_ms);
|
||||
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);
|
||||
|
||||
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,
|
||||
uint8_t control_ack);
|
||||
|
||||
void write_packet_uplink_ack(struct bitvec *dest, struct gprs_rlcmac_ul_tbf *tbf,
|
||||
bool is_final, uint8_t rrbp);
|
||||
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
1358
src/gprs_bssgp_pcu.c
Normal file
1358
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
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* 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
|
||||
@@ -11,53 +12,100 @@
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GPRS_BSSGP_PCU_H
|
||||
#define GPRS_BSSGP_PCU_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/signal.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_bss.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);
|
||||
}
|
||||
|
||||
#include <gprs_debug.h>
|
||||
|
||||
#define QOS_PROFILE 0
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define QOS_PROFILE 4
|
||||
#define BSSGP_HDR_LEN 53
|
||||
#define NS_HDR_LEN 4
|
||||
#define IE_LLC_PDU 14
|
||||
|
||||
extern struct bssgp_bvc_ctx *bctx;
|
||||
enum sgsn_counter_id {
|
||||
SGSN_CTR_RX_PAGING_CS,
|
||||
SGSN_CTR_RX_PAGING_PS,
|
||||
};
|
||||
|
||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp);
|
||||
struct gprs_bssgp_pcu {
|
||||
struct bssgp_bvc_ctx *bctx;
|
||||
|
||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
|
||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
|
||||
struct osmo_timer_list bvc_timer;
|
||||
|
||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
|
||||
struct rate_ctr_group *ctrs;
|
||||
|
||||
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);
|
||||
/* state: is the NSVC unblocked? */
|
||||
int nsvc_unblocked;
|
||||
|
||||
void gprs_bssgp_destroy(void);
|
||||
/* 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;
|
||||
|
||||
/* Flow control */
|
||||
struct timespec queue_delay_sum;
|
||||
unsigned queue_delay_count;
|
||||
uint8_t fc_tag;
|
||||
unsigned queue_frames_sent;
|
||||
unsigned queue_bytes_recv;
|
||||
unsigned queue_frames_recv;
|
||||
|
||||
/** 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
|
||||
|
||||
285
src/gprs_bssgp_rim.c
Normal file
285
src/gprs_bssgp_rim.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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 && nacc_fsm_is_waiting_si_resolution(ms->nacc, &nacc->reprt_cell))
|
||||
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;
|
||||
}
|
||||
20
src/gprs_bssgp_rim.h
Normal file
20
src/gprs_bssgp_rim.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct osmo_bssgp_prim;
|
||||
|
||||
int handle_rim(struct osmo_bssgp_prim *bp);
|
||||
176
src/gprs_codel.c
Normal file
176
src/gprs_codel.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
104
src/gprs_codel.h
Normal file
104
src/gprs_codel.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -11,61 +12,139 @@
|
||||
* 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 <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
}
|
||||
|
||||
#include <gprs_debug.h>
|
||||
|
||||
/* default categories */
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
|
||||
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_INFO, 1},
|
||||
{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
|
||||
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
|
||||
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
|
||||
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
|
||||
{"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},
|
||||
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
|
||||
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
|
||||
};
|
||||
|
||||
enum {
|
||||
_FLT_ALL = LOG_FILTER_ALL, /* libosmocore */
|
||||
FLT_IMSI = 1,
|
||||
FLT_NSVC = 2,
|
||||
FLT_BVC = 3,
|
||||
[DCSN1] = {
|
||||
.name = "DCSN1",
|
||||
.color = "\033[1;31m",
|
||||
.description = "Concrete Syntax Notation One (CSN1)",
|
||||
.loglevel = LOGL_NOTICE,
|
||||
.enabled = 0,
|
||||
},
|
||||
[DL1IF] = {
|
||||
.name = "DL1IF",
|
||||
.color = "\033[1;32m",
|
||||
.description = "GPRS PCU L1 interface (L1IF)",
|
||||
.loglevel = LOGL_NOTICE,
|
||||
.enabled = 1,
|
||||
},
|
||||
[DRLCMAC] = {
|
||||
.name = "DRLCMAC",
|
||||
.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,
|
||||
},
|
||||
};
|
||||
|
||||
static int filter_fn(const struct log_context *ctx,
|
||||
struct log_target *tar)
|
||||
{
|
||||
const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_NSVC];
|
||||
const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_BVC];
|
||||
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[LOG_CTX_GB_BVC];
|
||||
|
||||
/* Filter on the NS Virtual Connection */
|
||||
if ((tar->filter_map & (1 << FLT_NSVC)) != 0
|
||||
&& nsvc && (nsvc == tar->filter_data[FLT_NSVC]))
|
||||
if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
|
||||
&& nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
|
||||
return 1;
|
||||
|
||||
/* Filter on the BVC */
|
||||
if ((tar->filter_map & (1 << FLT_BVC)) != 0
|
||||
&& bvc && (bvc == tar->filter_data[FLT_BVC]))
|
||||
if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
|
||||
&& bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -11,20 +11,22 @@
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef GPRS_DEBUG_H
|
||||
#define GPRS_DEBUG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#endif
|
||||
#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 */
|
||||
enum {
|
||||
DCSN1,
|
||||
@@ -34,39 +36,15 @@ enum {
|
||||
DRLCMACDL,
|
||||
DRLCMACUL,
|
||||
DRLCMACSCHED,
|
||||
DRLCMACBW,
|
||||
DBSSGP,
|
||||
DRLCMACMEAS,
|
||||
DTBF,
|
||||
DTBFDL,
|
||||
DTBFUL,
|
||||
DNS,
|
||||
DPCU,
|
||||
DNACC,
|
||||
DRIM,
|
||||
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;
|
||||
|
||||
|
||||
|
||||
#endif // GPRS_DEBUG_H
|
||||
|
||||
984
src/gprs_ms.c
Normal file
984
src/gprs_ms.c
Normal file
@@ -0,0 +1,984 @@
|
||||
/* gprs_ms.c
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_ms.h"
|
||||
#include "bts.h"
|
||||
#include "tbf.h"
|
||||
#include "tbf_ul.h"
|
||||
#include "gprs_debug.h"
|
||||
#include "gprs_codel.h"
|
||||
#include "pcu_utils.h"
|
||||
#include "nacc_fsm.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include "coding_scheme.h"
|
||||
|
||||
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
static unsigned int next_ms_ctr_group_id;
|
||||
|
||||
static const struct rate_ctr_desc ms_ctr_description[] = {
|
||||
[MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc ms_ctrg_desc = {
|
||||
.group_name_prefix = "pcu:ms",
|
||||
.group_description = "MS Statistics",
|
||||
.class_id = OSMO_STATS_CLASS_SUBSCRIBER,
|
||||
.num_ctr = ARRAY_SIZE(ms_ctr_description),
|
||||
.ctr_desc = ms_ctr_description,
|
||||
};
|
||||
|
||||
static int64_t now_msec()
|
||||
{
|
||||
struct timespec ts;
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
void gprs_default_cb_ms_idle(struct GprsMs *ms)
|
||||
{
|
||||
talloc_free(ms);
|
||||
}
|
||||
|
||||
void gprs_default_cb_ms_active(struct GprsMs *ms)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
static struct gpr_ms_callback gprs_default_cb = {
|
||||
.ms_idle = gprs_default_cb_ms_idle,
|
||||
.ms_active = gprs_default_cb_ms_active,
|
||||
};
|
||||
|
||||
static void ms_release_timer_cb(void *data)
|
||||
{
|
||||
struct GprsMs *ms = (struct GprsMs *) data;
|
||||
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
|
||||
|
||||
if (ms->timer.data) {
|
||||
ms->timer.data = NULL;
|
||||
ms_unref(ms);
|
||||
}
|
||||
}
|
||||
|
||||
static int ms_talloc_destructor(struct GprsMs *ms);
|
||||
struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
|
||||
{
|
||||
struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
|
||||
|
||||
talloc_set_destructor(ms, ms_talloc_destructor);
|
||||
|
||||
ms->bts = bts;
|
||||
ms->cb = gprs_default_cb;
|
||||
ms->tlli = tlli;
|
||||
ms->new_ul_tlli = GSM_RESERVED_TMSI;
|
||||
ms->new_dl_tlli = GSM_RESERVED_TMSI;
|
||||
ms->ta = GSM48_TA_INVALID;
|
||||
ms->current_cs_ul = UNKNOWN;
|
||||
ms->current_cs_dl = UNKNOWN;
|
||||
ms->is_idle = true;
|
||||
INIT_LLIST_HEAD(&ms->list);
|
||||
INIT_LLIST_HEAD(&ms->old_tbfs);
|
||||
|
||||
int codel_interval = LLC_CODEL_USE_DEFAULT;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
|
||||
|
||||
ms->imsi[0] = '\0';
|
||||
memset(&ms->timer, 0, sizeof(ms->timer));
|
||||
ms->timer.cb = ms_release_timer_cb;
|
||||
llc_queue_init(&ms->llc_queue);
|
||||
|
||||
ms_set_mode(ms, GPRS);
|
||||
|
||||
if (ms->bts)
|
||||
codel_interval = the_pcu->vty.llc_codel_interval_msec;
|
||||
|
||||
if (codel_interval) {
|
||||
if (codel_interval == LLC_CODEL_USE_DEFAULT)
|
||||
codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
|
||||
ms->codel_state = talloc(ms, struct gprs_codel);
|
||||
gprs_codel_init(ms->codel_state);
|
||||
gprs_codel_set_interval(ms->codel_state, codel_interval);
|
||||
}
|
||||
ms->last_cs_not_low = now_msec();
|
||||
ms->app_info_pending = false;
|
||||
|
||||
ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
|
||||
if (!ms->ctrs)
|
||||
goto free_ret;
|
||||
|
||||
return ms;
|
||||
free_ret:
|
||||
talloc_free(ms);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ms_talloc_destructor(struct GprsMs *ms)
|
||||
{
|
||||
struct llist_item *pos, *tmp;
|
||||
|
||||
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
|
||||
|
||||
ms_set_reserved_slots(ms, NULL, 0, 0);
|
||||
|
||||
if (osmo_timer_pending(&ms->timer))
|
||||
osmo_timer_del(&ms->timer);
|
||||
|
||||
if (ms->ul_tbf) {
|
||||
tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
|
||||
ms->ul_tbf = NULL;
|
||||
}
|
||||
|
||||
if (ms->dl_tbf) {
|
||||
tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
|
||||
ms->dl_tbf = NULL;
|
||||
}
|
||||
|
||||
llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
|
||||
struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
|
||||
tbf_set_ms(tbf, NULL);
|
||||
}
|
||||
|
||||
llc_queue_clear(&ms->llc_queue, ms->bts);
|
||||
|
||||
if (ms->ctrs)
|
||||
rate_ctr_group_free(ms->ctrs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
|
||||
{
|
||||
if (cb)
|
||||
ms->cb = *cb;
|
||||
else
|
||||
ms->cb = gprs_default_cb;
|
||||
}
|
||||
|
||||
static void ms_update_status(struct GprsMs *ms)
|
||||
{
|
||||
if (ms->ref > 0)
|
||||
return;
|
||||
|
||||
if (ms_is_idle(ms) && !ms->is_idle) {
|
||||
ms->is_idle = true;
|
||||
ms->cb.ms_idle(ms);
|
||||
/* this can be deleted by now, do not access it */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ms_is_idle(ms) && ms->is_idle) {
|
||||
ms->is_idle = false;
|
||||
ms->cb.ms_active(ms);
|
||||
}
|
||||
}
|
||||
|
||||
struct GprsMs *ms_ref(struct GprsMs *ms)
|
||||
{
|
||||
ms->ref += 1;
|
||||
return ms;
|
||||
}
|
||||
|
||||
void ms_unref(struct GprsMs *ms)
|
||||
{
|
||||
OSMO_ASSERT(ms->ref >= 0);
|
||||
ms->ref -= 1;
|
||||
if (ms->ref == 0)
|
||||
ms_update_status(ms);
|
||||
}
|
||||
|
||||
static void ms_release_timer_start(struct GprsMs *ms)
|
||||
{
|
||||
if (ms->delay == 0)
|
||||
return;
|
||||
|
||||
LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
|
||||
|
||||
if (!ms->timer.data)
|
||||
ms->timer.data = ms_ref(ms);
|
||||
|
||||
osmo_timer_schedule(&ms->timer, ms->delay, 0);
|
||||
}
|
||||
|
||||
static void ms_release_timer_stop(struct GprsMs *ms)
|
||||
{
|
||||
if (!ms->timer.data)
|
||||
return;
|
||||
|
||||
LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
|
||||
|
||||
osmo_timer_del(&ms->timer);
|
||||
ms->timer.data = NULL;
|
||||
ms_unref(ms);
|
||||
}
|
||||
|
||||
void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
|
||||
{
|
||||
ms->mode = mode;
|
||||
|
||||
switch (ms->mode) {
|
||||
case GPRS:
|
||||
if (!mcs_is_gprs(ms->current_cs_ul)) {
|
||||
ms->current_cs_ul = mcs_get_gprs_by_num(
|
||||
ms->bts->initial_cs_ul);
|
||||
if (!mcs_is_valid(ms->current_cs_ul))
|
||||
ms->current_cs_ul = CS1;
|
||||
}
|
||||
if (!mcs_is_gprs(ms->current_cs_dl)) {
|
||||
ms->current_cs_dl = mcs_get_gprs_by_num(
|
||||
ms->bts->initial_cs_dl);
|
||||
if (!mcs_is_valid(ms->current_cs_dl))
|
||||
ms->current_cs_dl = CS1;
|
||||
}
|
||||
break;
|
||||
|
||||
case EGPRS_GMSK:
|
||||
if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
|
||||
ms->current_cs_ul = mcs_get_egprs_by_num(
|
||||
ms->bts->initial_mcs_ul);
|
||||
if (!mcs_is_valid(ms->current_cs_ul))
|
||||
ms->current_cs_ul = MCS1;
|
||||
}
|
||||
if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
|
||||
ms->current_cs_dl = mcs_get_egprs_by_num(
|
||||
ms->bts->initial_mcs_dl);
|
||||
if (!mcs_is_valid(ms->current_cs_dl))
|
||||
ms->current_cs_dl = MCS1;
|
||||
}
|
||||
break;
|
||||
case EGPRS:
|
||||
if (!mcs_is_edge(ms->current_cs_ul)) {
|
||||
ms->current_cs_ul = mcs_get_egprs_by_num(
|
||||
ms->bts->initial_mcs_ul);
|
||||
if (!mcs_is_valid(ms->current_cs_ul))
|
||||
ms->current_cs_ul = MCS1;
|
||||
}
|
||||
if (!mcs_is_edge(ms->current_cs_dl)) {
|
||||
ms->current_cs_dl = mcs_get_egprs_by_num(
|
||||
ms->bts->initial_mcs_dl);
|
||||
if (!mcs_is_valid(ms->current_cs_dl))
|
||||
ms->current_cs_dl = MCS1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
|
||||
{
|
||||
if (ms->ul_tbf == tbf)
|
||||
return;
|
||||
|
||||
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
|
||||
|
||||
ms_ref(ms);
|
||||
|
||||
if (ms->ul_tbf)
|
||||
llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
|
||||
|
||||
ms->ul_tbf = tbf;
|
||||
|
||||
if (tbf)
|
||||
ms_release_timer_stop(ms);
|
||||
|
||||
ms_unref(ms);
|
||||
}
|
||||
|
||||
static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
|
||||
{
|
||||
if (ms->dl_tbf == tbf)
|
||||
return;
|
||||
|
||||
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
|
||||
|
||||
ms_ref(ms);
|
||||
|
||||
if (ms->dl_tbf)
|
||||
llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
|
||||
|
||||
ms->dl_tbf = tbf;
|
||||
|
||||
if (tbf)
|
||||
ms_release_timer_stop(ms);
|
||||
|
||||
ms_unref(ms);
|
||||
}
|
||||
|
||||
void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
|
||||
ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
|
||||
else
|
||||
ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
|
||||
}
|
||||
|
||||
void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
|
||||
ms->ul_tbf = NULL;
|
||||
} else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
|
||||
ms->dl_tbf = NULL;
|
||||
} else {
|
||||
bool found = false;
|
||||
|
||||
struct llist_item *pos, *tmp;
|
||||
llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
|
||||
struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
|
||||
if (tmp_tbf == tbf) {
|
||||
llist_del(&pos->list);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Protect against recursive calls via set_ms() */
|
||||
if (!found)
|
||||
return;
|
||||
}
|
||||
|
||||
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
|
||||
tbf_name(tbf));
|
||||
|
||||
if (tbf_ms(tbf) == ms)
|
||||
tbf_set_ms(tbf, NULL);
|
||||
|
||||
if (!ms->dl_tbf && !ms->ul_tbf) {
|
||||
ms_set_reserved_slots(ms, NULL, 0, 0);
|
||||
|
||||
if (ms_tlli(ms) != 0)
|
||||
ms_release_timer_start(ms);
|
||||
}
|
||||
|
||||
ms_update_status(ms);
|
||||
}
|
||||
|
||||
void ms_reset(struct GprsMs *ms)
|
||||
{
|
||||
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
|
||||
|
||||
ms_release_timer_stop(ms);
|
||||
|
||||
ms->tlli = GSM_RESERVED_TMSI;
|
||||
ms->new_dl_tlli = ms->tlli;
|
||||
ms->new_ul_tlli = ms->tlli;
|
||||
ms->imsi[0] = '\0';
|
||||
}
|
||||
|
||||
static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
|
||||
{
|
||||
OSMO_ASSERT(old_ms != ms);
|
||||
|
||||
if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
|
||||
osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
|
||||
|
||||
if (!ms_ms_class(ms) && ms_ms_class(old_ms))
|
||||
ms_set_ms_class(ms, ms_ms_class(old_ms));
|
||||
|
||||
if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
|
||||
ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
|
||||
|
||||
llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
|
||||
|
||||
ms_reset(old_ms);
|
||||
}
|
||||
|
||||
void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
|
||||
{
|
||||
OSMO_ASSERT(old_ms != ms);
|
||||
|
||||
ms_ref(old_ms);
|
||||
|
||||
/* Clean up the old MS object */
|
||||
/* TODO: Use timer? */
|
||||
if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
|
||||
tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
|
||||
if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
|
||||
tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
|
||||
|
||||
ms_merge_old_ms(ms, old_ms);
|
||||
|
||||
ms_unref(old_ms);
|
||||
}
|
||||
|
||||
void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
|
||||
{
|
||||
if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
|
||||
return;
|
||||
|
||||
if (tlli != ms->new_dl_tlli) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
|
||||
"not yet confirmed\n",
|
||||
ms_tlli(ms), tlli);
|
||||
ms->new_ul_tlli = tlli;
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
|
||||
"already confirmed partly\n",
|
||||
ms->tlli, tlli);
|
||||
|
||||
ms->tlli = tlli;
|
||||
ms->new_dl_tlli = GSM_RESERVED_TMSI;
|
||||
ms->new_ul_tlli = GSM_RESERVED_TMSI;
|
||||
}
|
||||
|
||||
bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
|
||||
{
|
||||
if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
|
||||
return false;
|
||||
|
||||
if (tlli != ms->new_ul_tlli) {
|
||||
/* The MS has not sent a message with the new TLLI, which may
|
||||
* happen according to the spec [TODO: add reference]. */
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
|
||||
"partly confirmed\n", tlli);
|
||||
/* Use the network's idea of TLLI as candidate, this does not
|
||||
* change the result value of tlli() */
|
||||
ms->new_dl_tlli = tlli;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
|
||||
|
||||
ms->tlli = tlli;
|
||||
ms->new_dl_tlli = GSM_RESERVED_TMSI;
|
||||
ms->new_ul_tlli = GSM_RESERVED_TMSI;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ms_set_imsi(struct GprsMs *ms, const char *imsi)
|
||||
{
|
||||
if (!imsi) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (imsi[0] && strlen(imsi) < 3) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
|
||||
imsi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(imsi, ms->imsi) == 0)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
|
||||
ms_tlli(ms), ms->imsi, imsi);
|
||||
|
||||
struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
|
||||
/* Check if we are going to store a different MS object with already
|
||||
existing IMSI. This is probably a bug in code calling this function,
|
||||
since it should take care of this explicitly */
|
||||
if (old_ms) {
|
||||
/* We cannot find ms->ms by IMSI since we know that it has a
|
||||
* different IMSI */
|
||||
OSMO_ASSERT(old_ms != ms);
|
||||
|
||||
LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
|
||||
"IMSI '%s' was already assigned to another "
|
||||
"MS object: TLLI = 0x%08x, that IMSI will be removed\n",
|
||||
imsi, ms_tlli(old_ms));
|
||||
|
||||
ms_merge_and_clear_ms(ms, old_ms);
|
||||
}
|
||||
|
||||
|
||||
osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
|
||||
}
|
||||
|
||||
uint16_t ms_paging_group(struct GprsMs *ms)
|
||||
{
|
||||
uint16_t pgroup;
|
||||
if (!ms_imsi_is_valid(ms))
|
||||
return 0; /* 000 is the special "all paging" group */
|
||||
if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
|
||||
LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
|
||||
return 0;
|
||||
}
|
||||
return pgroup;
|
||||
}
|
||||
|
||||
void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
|
||||
{
|
||||
if (ta_ == ms->ta)
|
||||
return;
|
||||
|
||||
if (gsm48_ta_is_valid(ta_)) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
|
||||
ms_tlli(ms), ms->ta, ta_);
|
||||
ms->ta = ta_;
|
||||
} else
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
|
||||
"value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
|
||||
}
|
||||
|
||||
void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
|
||||
{
|
||||
if (ms_class_ == ms->ms_class)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
|
||||
ms_tlli(ms), ms->ms_class, ms_class_);
|
||||
|
||||
ms->ms_class = ms_class_;
|
||||
}
|
||||
|
||||
void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
|
||||
{
|
||||
if (ms_class_ == ms->egprs_ms_class)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
|
||||
ms_tlli(ms), ms->egprs_ms_class, ms_class_);
|
||||
|
||||
ms->egprs_ms_class = ms_class_;
|
||||
|
||||
if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
|
||||
LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
|
||||
"Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
|
||||
bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
|
||||
mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
|
||||
ms_mode(ms) != EGPRS)
|
||||
{
|
||||
ms_set_mode(ms, EGPRS_GMSK);
|
||||
} else {
|
||||
ms_set_mode(ms, EGPRS);
|
||||
}
|
||||
LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
|
||||
}
|
||||
|
||||
void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
{
|
||||
int64_t now;
|
||||
enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
|
||||
OSMO_ASSERT(max_cs_dl);
|
||||
|
||||
if (error_rate < 0)
|
||||
return;
|
||||
|
||||
now = now_msec();
|
||||
|
||||
/* TODO: Check for TBF direction */
|
||||
/* TODO: Support different CS values for UL and DL */
|
||||
|
||||
ms->nack_rate_dl = error_rate;
|
||||
|
||||
if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
|
||||
if (mcs_chan_code(ms->current_cs_dl) > 0) {
|
||||
mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): High error rate %d%%, "
|
||||
"reducing CS level to %s\n",
|
||||
ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
|
||||
ms->last_cs_not_low = now;
|
||||
}
|
||||
} else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
|
||||
if (ms->current_cs_dl < max_cs_dl) {
|
||||
if (now - ms->last_cs_not_low > 1000) {
|
||||
mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"increasing DL CS level to %s\n",
|
||||
ms_imsi(ms), error_rate,
|
||||
mcs_name(ms->current_cs_dl));
|
||||
ms->last_cs_not_low = now;
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"ignored (within blocking period)\n",
|
||||
ms_imsi(ms), error_rate);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"MS (IMSI %s): Medium error rate %d%%, ignored\n",
|
||||
ms_imsi(ms), error_rate);
|
||||
ms->last_cs_not_low = now;
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
|
||||
{
|
||||
enum CodingScheme cs;
|
||||
OSMO_ASSERT(ms->bts != NULL);
|
||||
|
||||
if (mcs_is_gprs(ms->current_cs_ul)) {
|
||||
if (!bts_max_cs_ul(ms->bts)) {
|
||||
return CS4;
|
||||
}
|
||||
|
||||
return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
|
||||
}
|
||||
|
||||
cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
|
||||
if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
|
||||
cs = MCS4;
|
||||
return cs;
|
||||
}
|
||||
|
||||
void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
|
||||
{
|
||||
ms->current_cs_dl = scheme;
|
||||
}
|
||||
|
||||
enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
|
||||
{
|
||||
enum CodingScheme cs;
|
||||
OSMO_ASSERT(ms->bts != NULL);
|
||||
|
||||
if (mcs_is_gprs(ms->current_cs_dl)) {
|
||||
if (!bts_max_cs_dl(ms->bts)) {
|
||||
return CS4;
|
||||
}
|
||||
|
||||
return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
|
||||
}
|
||||
|
||||
cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
|
||||
if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
|
||||
cs = MCS4;
|
||||
return cs;
|
||||
}
|
||||
|
||||
void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
|
||||
{
|
||||
enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
|
||||
|
||||
int old_link_qual;
|
||||
int low;
|
||||
int high;
|
||||
enum CodingScheme new_cs_ul = ms->current_cs_ul;
|
||||
uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
|
||||
|
||||
if (!max_cs_ul) {
|
||||
LOGP(DRLCMACMEAS, LOGL_ERROR,
|
||||
"max_cs_ul cannot be derived (current UL CS: %s)\n",
|
||||
mcs_name(ms->current_cs_ul));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ms->current_cs_ul) {
|
||||
LOGP(DRLCMACMEAS, LOGL_ERROR,
|
||||
"Unable to update UL (M)CS because it's not set: %s\n",
|
||||
mcs_name(ms->current_cs_ul));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!meas->have_link_qual) {
|
||||
LOGP(DRLCMACMEAS, LOGL_ERROR,
|
||||
"Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
|
||||
mcs_name(ms->current_cs_ul));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mcs_is_gprs(ms->current_cs_ul)) {
|
||||
if (current_cs >= MAX_GPRS_CS)
|
||||
current_cs = MAX_GPRS_CS - 1;
|
||||
low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
|
||||
high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
|
||||
} else if (mcs_is_edge(ms->current_cs_ul)) {
|
||||
if (current_cs >= MAX_EDGE_MCS)
|
||||
current_cs = MAX_EDGE_MCS - 1;
|
||||
low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
|
||||
high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
|
||||
} else {
|
||||
LOGP(DRLCMACMEAS, LOGL_ERROR,
|
||||
"Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
|
||||
mcs_name(ms->current_cs_ul));
|
||||
return;
|
||||
}
|
||||
|
||||
/* To avoid rapid changes of the coding scheme, we also take
|
||||
* the old link quality value into account (if present). */
|
||||
if (ms->l1_meas.have_link_qual)
|
||||
old_link_qual = ms->l1_meas.link_qual;
|
||||
else
|
||||
old_link_qual = meas->link_qual;
|
||||
|
||||
if (meas->link_qual < low && old_link_qual < low)
|
||||
mcs_dec_kind(&new_cs_ul, ms_mode(ms));
|
||||
else if (meas->link_qual > high && old_link_qual > high &&
|
||||
ms->current_cs_ul < max_cs_ul)
|
||||
mcs_inc_kind(&new_cs_ul, ms_mode(ms));
|
||||
|
||||
if (ms->current_cs_ul != new_cs_ul) {
|
||||
LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
|
||||
"Link quality %ddB (old %ddB) left window [%d, %d], "
|
||||
"modifying uplink CS level: %s -> %s\n",
|
||||
meas->link_qual, old_link_qual,
|
||||
low, high,
|
||||
mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
|
||||
|
||||
ms->current_cs_ul = new_cs_ul;
|
||||
}
|
||||
}
|
||||
|
||||
void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
ms_update_cs_ul(ms, meas);
|
||||
|
||||
if (meas->have_rssi)
|
||||
pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
|
||||
if (meas->have_bto)
|
||||
pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
|
||||
if (meas->have_ber)
|
||||
pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
|
||||
if (meas->have_link_qual)
|
||||
pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
|
||||
|
||||
if (meas->have_ms_rx_qual)
|
||||
pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
|
||||
if (meas->have_ms_c_value)
|
||||
pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
|
||||
if (meas->have_ms_sign_var)
|
||||
pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
|
||||
|
||||
if (meas->have_ms_i_level) {
|
||||
for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
|
||||
if (meas->ts[i].have_ms_i_level)
|
||||
pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
|
||||
else
|
||||
ms->l1_meas.ts[i].have_ms_i_level = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
|
||||
enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
|
||||
{
|
||||
enum CodingScheme orig_cs = ms->current_cs_dl;
|
||||
struct gprs_rlcmac_bts *bts = ms->bts;
|
||||
size_t unencoded_octets;
|
||||
enum CodingScheme cs;
|
||||
|
||||
/* It could be that a TBF requests a GPRS CS despite the MS currently
|
||||
being upgraded to EGPRS (hence reporting MCS). That could happen
|
||||
because the TBF was created early in the process where we didn't have
|
||||
yet enough information about the MS, and only AFTER it was created we
|
||||
upgraded the MS to be EGPRS capable.
|
||||
As a result, when the MS is queried for the target CS here, we could be
|
||||
returning an MCS despite the current TBF being established as GPRS,
|
||||
but we rather stick to the TBF type we assigned to the MS rather than
|
||||
magically sending EGPRS data blocks to a GPRS TBF.
|
||||
It could also be that the caller requests specific MCS kind
|
||||
explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
|
||||
if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
|
||||
cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
|
||||
bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
|
||||
bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
|
||||
MCS1;
|
||||
} else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
|
||||
int i;
|
||||
cs = orig_cs > MCS4 ? MCS4 : orig_cs;
|
||||
cs -= (MCS1 - CS1); /* MCSx -> CSx */
|
||||
/* Find suitable CS starting from equivalent MCS which is supported by BTS: */
|
||||
for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
|
||||
OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
|
||||
cs = CS1 + i;
|
||||
} else {
|
||||
cs = orig_cs;
|
||||
}
|
||||
|
||||
if (orig_cs != cs)
|
||||
LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
|
||||
"DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
|
||||
mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
|
||||
|
||||
unencoded_octets = llc_queue_octets(&ms->llc_queue);
|
||||
|
||||
/* If the DL TBF is active, add number of unencoded chunk octets */
|
||||
if (ms->dl_tbf)
|
||||
unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
|
||||
|
||||
/* There are many unencoded octets, don't reduce */
|
||||
if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
|
||||
return cs;
|
||||
|
||||
/* RF conditions are good, don't reduce */
|
||||
if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
|
||||
return cs;
|
||||
|
||||
/* The throughput would probably be better if the CS level was reduced */
|
||||
mcs_dec_kind(&cs, ms_mode(ms));
|
||||
|
||||
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
|
||||
if (cs == CS2)
|
||||
mcs_dec_kind(&cs, ms_mode(ms));
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
int ms_first_common_ts(const struct GprsMs *ms)
|
||||
{
|
||||
if (ms->dl_tbf)
|
||||
return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
|
||||
|
||||
if (ms->ul_tbf)
|
||||
return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t ms_dl_slots(const struct GprsMs *ms)
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
if (ms->dl_tbf)
|
||||
slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
|
||||
|
||||
if (ms->ul_tbf)
|
||||
slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
uint8_t ms_ul_slots(const struct GprsMs *ms)
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
if (ms->dl_tbf)
|
||||
slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
|
||||
|
||||
if (ms->ul_tbf)
|
||||
slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
|
||||
bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
|
||||
|
||||
if (!is_dl_active && !is_ul_active)
|
||||
return 0;
|
||||
|
||||
/* see TS 44.060, 8.1.1.2.2 */
|
||||
if (is_dl_active && !is_ul_active)
|
||||
slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
|
||||
else if (!is_dl_active && is_ul_active)
|
||||
slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
|
||||
else
|
||||
slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
|
||||
tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
|
||||
|
||||
/* Assume a multislot class 1 device */
|
||||
/* TODO: For class 2 devices, this could be removed */
|
||||
slots = pcu_lsb(slots);
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
|
||||
uint8_t ul_slots, uint8_t dl_slots)
|
||||
{
|
||||
if (ms->current_trx) {
|
||||
bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
|
||||
ms->reserved_dl_slots);
|
||||
bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
|
||||
ms->reserved_ul_slots);
|
||||
ms->reserved_dl_slots = 0;
|
||||
ms->reserved_ul_slots = 0;
|
||||
}
|
||||
ms->current_trx = trx;
|
||||
if (trx) {
|
||||
ms->reserved_dl_slots = dl_slots;
|
||||
ms->reserved_ul_slots = ul_slots;
|
||||
bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
|
||||
ms->reserved_dl_slots);
|
||||
bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
|
||||
ms->reserved_ul_slots);
|
||||
}
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
|
||||
{
|
||||
switch (dir) {
|
||||
case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
|
||||
case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
|
||||
{
|
||||
if (!ms->nacc)
|
||||
ms->nacc = nacc_fsm_alloc(ms);
|
||||
if (!ms->nacc)
|
||||
return -EINVAL;
|
||||
return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
|
||||
}
|
||||
|
||||
bool ms_nacc_rts(const struct GprsMs *ms)
|
||||
{
|
||||
if (!ms->nacc)
|
||||
return false;
|
||||
if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
|
||||
ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
|
||||
{
|
||||
int rc;
|
||||
struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
|
||||
|
||||
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
|
||||
.tbf = tbf,
|
||||
.fn = fn,
|
||||
.ts = ts,
|
||||
.msg = NULL,
|
||||
};
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
|
||||
if (rc != 0 || !data_ctx.msg)
|
||||
return NULL;
|
||||
return data_ctx.msg;
|
||||
}
|
||||
255
src/gprs_ms.h
Normal file
255
src/gprs_ms.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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 <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 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);
|
||||
uint16_t ms_paging_group(struct GprsMs *ms);
|
||||
|
||||
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);
|
||||
|
||||
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_is_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
|
||||
109
src/gprs_ms_storage.cpp
Normal file
109
src/gprs_ms_storage.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_ms_storage.h"
|
||||
|
||||
#include "tbf.h"
|
||||
#include "bts.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
}
|
||||
|
||||
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] != '\0') {
|
||||
llist_for_each(tmp, &m_list) {
|
||||
ms = llist_entry(tmp, typeof(*ms), list);
|
||||
if (ms_imsi_is_valid(ms) && 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;
|
||||
}
|
||||
40
src/gprs_ms_storage.h
Normal file
40
src/gprs_ms_storage.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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 */
|
||||
};
|
||||
212
src/gprs_pcu.c
Normal file
212
src/gprs_pcu.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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=3113, .default_val=7, .unit=OSMO_TDEF_S, .desc="Timeout for paging", .val=0 },
|
||||
{ .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=3172, .default_val=5000, .unit=OSMO_TDEF_MS, .desc="Wait Indication used in Imm Ass Reject during TBF Establishment (PACCH)", .val=0, .min_val = 0, .max_val = 255000 }, /* TS 44.060 7.1.3.2.1 */
|
||||
{ .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=-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=0, .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 void _update_stats_timer_cb(void *data)
|
||||
{
|
||||
struct gprs_pcu *pcu = (struct gprs_pcu *)data;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
|
||||
llist_for_each_entry(bts, &pcu->bts_list, list)
|
||||
osmo_time_cc_set_flag(&bts->all_allocated_pdch, bts_all_pdch_allocated(bts));
|
||||
|
||||
osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
|
||||
}
|
||||
|
||||
static int gprs_pcu_talloc_destructor(struct gprs_pcu *pcu)
|
||||
{
|
||||
if (osmo_timer_pending(&pcu->update_stats_timer))
|
||||
osmo_timer_del(&pcu->update_stats_timer);
|
||||
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 = NULL; /* don't use CTRL iface for Neigh Addr Resolution */
|
||||
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));
|
||||
|
||||
osmo_timer_setup(&pcu->update_stats_timer, _update_stats_timer_cb, pcu);
|
||||
osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
150
src/gprs_pcu.h
Normal file
150
src/gprs_pcu.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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 <osmocom/core/timer.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)
|
||||
|
||||
/* 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;
|
||||
char *gsmtap_remote_host;
|
||||
|
||||
struct llist_head bts_list; /* list of gprs_rlcmac_bts */
|
||||
|
||||
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 */
|
||||
|
||||
struct osmo_timer_list update_stats_timer; /* Used to update some time_cc stats periodically */
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
1490
src/gprs_rlcmac.cpp
1490
src/gprs_rlcmac.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
/* gprs_rlcmac.h
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* 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
|
||||
@@ -11,23 +12,26 @@
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef GPRS_RLCMAC_H
|
||||
#define GPRS_RLCMAC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <bitvector.h>
|
||||
#include <gsm_rlcmac.h>
|
||||
#include <gsm_timer.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pcu_l1_if.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <osmocom/core/linuxlist.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
|
||||
|
||||
@@ -37,177 +41,13 @@ extern "C" {
|
||||
*/
|
||||
//#define DEBUG_DL_ASS_IDLE
|
||||
|
||||
/*
|
||||
* PDCH instanc
|
||||
*/
|
||||
#define DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
|
||||
|
||||
struct gprs_rlcmac_tbf;
|
||||
|
||||
struct gprs_rlcmac_pdch {
|
||||
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;
|
||||
struct gprs_rlcmac_bts;
|
||||
struct GprsMs;
|
||||
|
||||
#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
|
||||
*/
|
||||
@@ -217,120 +57,63 @@ struct gprs_rlcmac_paging {
|
||||
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,
|
||||
enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
|
||||
uint8_t first_ts, uint8_t ms_class, uint8_t single_slot);
|
||||
/* TS allocation internal functions */
|
||||
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots);
|
||||
|
||||
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts,
|
||||
enum gprs_rlcmac_tbf_direction dir);
|
||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||
uint16_t lost);
|
||||
|
||||
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
|
||||
enum gprs_rlcmac_tbf_direction dir);
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
|
||||
|
||||
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
|
||||
enum gprs_rlcmac_block_type {
|
||||
GPRS_RLCMAC_DATA_BLOCK = 0x0,
|
||||
GPRS_RLCMAC_CONTROL_BLOCK = 0x1,
|
||||
GPRS_RLCMAC_CONTROL_BLOCK = 0x1,
|
||||
GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x2,
|
||||
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);
|
||||
|
||||
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_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,
|
||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts,
|
||||
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" {
|
||||
#endif
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
int8_t use_trx);
|
||||
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GPRS_RLCMAC_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
190
src/gprs_rlcmac_meas.cpp
Normal file
190
src/gprs_rlcmac_meas.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
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 <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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -11,175 +11,344 @@
|
||||
* 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_bssgp_pcu.h>
|
||||
#include <gprs_rlcmac.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,
|
||||
struct gprs_rlcmac_tbf **poll_tbf,
|
||||
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;
|
||||
extern "C" {
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
}
|
||||
|
||||
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
|
||||
struct tbf_sched_candidates {
|
||||
struct gprs_rlcmac_tbf *ul_ass;
|
||||
struct gprs_rlcmac_tbf *dl_ass;
|
||||
struct gprs_rlcmac_tbf *nacc;
|
||||
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;
|
||||
uint8_t usf = 0x07;
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
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 (tbf_ul_ack_rts(ul_tbf))
|
||||
tbf_cand->ul_ack = ul_tbf;
|
||||
if (tbf_dl_ass_rts(ul_tbf))
|
||||
tbf_cand->dl_ass = ul_tbf;
|
||||
if (tbf_ul_ass_rts(ul_tbf))
|
||||
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;
|
||||
/* 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 (tbf_dl_ass_rts(dl_tbf))
|
||||
tbf_cand->dl_ass = dl_tbf;
|
||||
if (tbf_ul_ass_rts(dl_tbf))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* select uplink ressource */
|
||||
/* select uplink resource */
|
||||
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
|
||||
i++, tfi = (tfi + 1) & 31) {
|
||||
tbf = pdch->ul_tbf[tfi];
|
||||
tbf = pdch->ul_tbf_by_tfi(tfi);
|
||||
/* no TBF for this tfi, go next */
|
||||
if (!tbf)
|
||||
continue;
|
||||
/* no UL ressources needed, go next */
|
||||
/* we don't need to give ressources in FINISHED state,
|
||||
/* no UL resources needed, go next */
|
||||
/* we don't need to give resources in FINISHED state,
|
||||
* because we have received all blocks and only poll
|
||||
* for packet control ack. */
|
||||
if (tbf->state != GPRS_RLCMAC_FLOW)
|
||||
if (tbf->state_is_not(TBF_ST_FLOW))
|
||||
continue;
|
||||
|
||||
/* use this USF */
|
||||
usf = tbf->dir.ul.usf[ts];
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
|
||||
"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
|
||||
"required uplink ressource of UL TBF=%d\n", trx, ts, fn,
|
||||
block_nr, usf, tfi);
|
||||
/* next TBF to handle ressource is the next one */
|
||||
if (require_gprs_only && tbf->is_egprs_enabled())
|
||||
continue;
|
||||
|
||||
/* use this USF (tbf) */
|
||||
/* next TBF to handle resource is the next one */
|
||||
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,
|
||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
|
||||
struct gprs_rlcmac_tbf *ul_ass_tbf,
|
||||
struct gprs_rlcmac_tbf *dl_ass_tbf,
|
||||
struct gprs_rlcmac_tbf *ul_ack_tbf)
|
||||
{
|
||||
struct msgb *sched_app_info(struct gprs_rlcmac_tbf *tbf) {
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct msgb *msg = NULL;
|
||||
struct gprs_rlcmac_tbf *tbf = NULL;
|
||||
|
||||
/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
|
||||
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))
|
||||
if (!tbf || !tbf->ms()->app_info_pending)
|
||||
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;
|
||||
}
|
||||
|
||||
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
|
||||
static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_t fn,
|
||||
uint8_t block_nr, struct tbf_sched_candidates *tbfs)
|
||||
{
|
||||
struct msgb *msg = 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 };
|
||||
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(TBF_UL_ASS_SEND_ASS_REJ))
|
||||
msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
|
||||
else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
|
||||
else if (tbf == tbfs->dl_ass && tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, fn, ts);
|
||||
else if (tbf == tbfs->ul_ack)
|
||||
msg = tbf_ul_ack_create_rlcmac_msg(tbfs->ul_ack, fn, ts);
|
||||
else if (tbf == tbfs->nacc) {
|
||||
msg = ms_nacc_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 = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, fn, ts);
|
||||
} else if (tbfs->ul_ass) {
|
||||
tbf = tbfs->ul_ass;
|
||||
msg = tbf_ul_ass_create_rlcmac_msg(tbfs->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_poll_for_dl_ack_nack())
|
||||
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_dl_data_msg(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;
|
||||
i++, tfi = (tfi + 1) & 31) {
|
||||
tbf = pdch->dl_tbf[tfi];
|
||||
tbf = pdch->dl_tbf_by_tfi(tfi);
|
||||
/* no TBF for this tfi, go next */
|
||||
if (!tbf)
|
||||
continue;
|
||||
/* no DL TBF, go next */
|
||||
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
|
||||
continue;
|
||||
/* no DL ressources needed, go next */
|
||||
if (tbf->state != GPRS_RLCMAC_FLOW
|
||||
&& tbf->state != GPRS_RLCMAC_FINISHED)
|
||||
/* no DL resources needed, go next */
|
||||
if (tbf->state_is_not(TBF_ST_FLOW)
|
||||
&& tbf->state_is_not(TBF_ST_FINISHED))
|
||||
continue;
|
||||
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
|
||||
"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
|
||||
/* next TBF to handle ressource is the next one */
|
||||
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 */
|
||||
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
|
||||
ts);
|
||||
break;
|
||||
msg = prio_tbf->create_dl_acked_block(fn, ts, req_mcs_kind);
|
||||
*is_egprs = prio_tbf->is_egprs_enabled();
|
||||
}
|
||||
|
||||
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 */
|
||||
0x94, /* dummy downlink control message, paging mode 00 */
|
||||
0x2b, /* no persistance level, 7 bits spare pattern */
|
||||
@@ -187,7 +356,7 @@ static uint8_t rlcmac_dl_idle[23] = {
|
||||
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
|
||||
};
|
||||
|
||||
struct msgb *sched_dummy(void)
|
||||
static struct msgb *sched_dummy(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
@@ -199,66 +368,171 @@ struct msgb *sched_dummy(void)
|
||||
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)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
|
||||
*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
|
||||
uint8_t usf = 0x7;
|
||||
struct tbf_sched_candidates tbf_cand = {0};
|
||||
struct gprs_rlcmac_tbf *poll_tbf;
|
||||
struct gprs_rlcmac_ul_tbf *usf_tbf = NULL;
|
||||
struct gprs_rlcmac_sba *sba;
|
||||
uint8_t usf;
|
||||
struct msgb *msg = NULL;
|
||||
uint32_t poll_fn;
|
||||
enum pcu_gsmtap_category gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY; /* init: make gcc happy */
|
||||
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)
|
||||
return -EINVAL;
|
||||
pdch = &bts->trx[trx].pdch[ts];
|
||||
|
||||
if (!pdch->enable) {
|
||||
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
|
||||
"TRX=%d TS=%d\n", trx, ts);
|
||||
if (!pdch->is_enabled()) {
|
||||
LOGPDCH(pdch, DRLCMACSCHED, LOGL_INFO, "Received RTS on disabled TS\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* store last frame number of RTS */
|
||||
pdch->last_rts_fn = fn;
|
||||
|
||||
poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
|
||||
&dl_ass_tbf, &ul_ack_tbf);
|
||||
/* check uplink ressource for polling */
|
||||
if (poll_tbf)
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
|
||||
"TS=%d FN=%d block_nr=%d scheduling free USF for "
|
||||
"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
|
||||
block_nr, poll_fn,
|
||||
(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
? "UL" : "DL", poll_tbf->tfi);
|
||||
/* use free USF */
|
||||
/* else, we search for uplink ressource */
|
||||
else
|
||||
usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
|
||||
/* require_gprs_only: Prioritize USF for GPRS-only MS here,
|
||||
* since anyway we'll need to tx a Dl block with CS1-4 due to
|
||||
* synchronization requirements. See 3GPP TS 03.64 version
|
||||
* 8.12.0
|
||||
*/
|
||||
require_gprs_only = (pdch->fn_without_cs14 == MS_RESYNC_NUM_FRAMES - 1);
|
||||
if (require_gprs_only) {
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "TRX=%d TS=%d FN=%d "
|
||||
"synchronization frame (every 18 frames), only CS1-4 allowed",
|
||||
trx, ts, fn);
|
||||
req_mcs_kind = GPRS; /* only GPRS CS1-4 allowed, all MS need to be able to decode it */
|
||||
} else {
|
||||
req_mcs_kind = EGPRS; /* all kinds are fine */
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
|
||||
dl_ass_tbf, ul_ack_tbf);
|
||||
|
||||
if ((msg = sched_select_ctrl_msg(pdch, fn, block_nr, &tbf_cand))) {
|
||||
gsmtap_cat = PCU_GSMTAP_C_DL_CTRL;
|
||||
}
|
||||
/* Prio 2: select data message for downlink */
|
||||
if (!msg)
|
||||
msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
|
||||
else if((msg = sched_select_dl_data_msg(bts, pdch, fn, block_nr, req_mcs_kind, &tx_is_egprs))) {
|
||||
gsmtap_cat = tx_is_egprs ? PCU_GSMTAP_C_DL_DATA_EGPRS :
|
||||
PCU_GSMTAP_C_DL_DATA_GPRS;
|
||||
}
|
||||
/* Prio 3: send dummy control message if need to poll or USF */
|
||||
else {
|
||||
/* If there's no TBF attached to this PDCH, we can submit an empty
|
||||
* data_req since there's nothing to transmit nor to poll/USF. This
|
||||
* way we help BTS energy saving (on TRX!=C0) by sending nothing
|
||||
* instead of a dummy block. The early return is done here and
|
||||
* not at the start of the function because the condition below
|
||||
* (num_tbfs==0) may not be enough, because temporary dummy TBFs
|
||||
* created to send Imm Ass Rej (see handle_tbf_reject()) don't
|
||||
* have a TFI assigned and hence are not attached to the PDCH
|
||||
* TS, so they don't show up in the count below.
|
||||
*/
|
||||
const unsigned num_tbfs = pdch->num_tbfs(GPRS_RLCMAC_DL_TBF)
|
||||
+ pdch->num_tbfs(GPRS_RLCMAC_UL_TBF);
|
||||
bool skip_idle = (num_tbfs == 0);
|
||||
#ifdef ENABLE_DIRECT_PHY
|
||||
/* In DIRECT_PHY mode we want to always submit something to L1 in
|
||||
* TRX0, since BTS is not preparing dummy bursts on idle TS for us */
|
||||
skip_idle = skip_idle && trx != 0;
|
||||
#endif
|
||||
if (!skip_idle && (msg = sched_dummy())) {
|
||||
/* increase counter */
|
||||
gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY;
|
||||
} else {
|
||||
msg = NULL; /* submit empty frame */
|
||||
}
|
||||
}
|
||||
|
||||
/* Prio 3: send dummy contol message */
|
||||
if (!msg)
|
||||
msg = sched_dummy();
|
||||
if (tx_is_egprs && pdch->has_gprs_only_tbf_attached()) {
|
||||
pdch->fn_without_cs14 += 1;
|
||||
} else {
|
||||
pdch->fn_without_cs14 = 0;
|
||||
}
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
/* msg is now available */
|
||||
/* Used to measure the leak rate, count all blocks */
|
||||
gprs_bssgp_update_frames_sent();
|
||||
|
||||
/* set USF */
|
||||
msg->data[0] = (msg->data[0] & 0xf8) | usf;
|
||||
if (msg) {
|
||||
/* msg is now available */
|
||||
bts_do_rate_ctr_add(bts, CTR_RLC_DL_BYTES, msgb_length(msg));
|
||||
|
||||
/* send PDTCH/PACCH to L1 */
|
||||
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
|
||||
/* 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;
|
||||
|
||||
/* Send to GSMTAP */
|
||||
tap_n_acc(msg, bts, trx, ts, fn, gsmtap_cat);
|
||||
}
|
||||
|
||||
/* send PDTCH/PACCH to L1. msg=NULL accepted too (idle block) */
|
||||
pcu_l1if_tx_pdtch(msg, bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
1016
src/gprs_rlcmac_ts_alloc.cpp
Normal file
1016
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
549
src/gsm_rlcmac.h
549
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
|
||||
252
src/llc.cpp
Normal file
252
src/llc.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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, >);
|
||||
}
|
||||
130
src/llc.h
Normal file
130
src/llc.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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
|
||||
293
src/mslot_class.c
Normal file
293
src/mslot_class.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
58
src/mslot_class.h
Normal file
58
src/mslot_class.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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);
|
||||
900
src/nacc_fsm.c
Normal file
900
src/nacc_fsm.c
Normal file
@@ -0,0 +1,900 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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 nacc_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);
|
||||
LOGPTBFDL(tbf, LOGL_DEBUG, "Scheduled 'Packet Cell Change Continue' polling on PACCH (FN=%d, TS=%d)\n",
|
||||
*new_poll_fn, data->ts);
|
||||
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 int send_neigh_addr_req_ctrl_iface(struct nacc_fsm_ctx *ctx)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||
struct gprs_pcu *pcu = bts->pcu;
|
||||
struct ctrl_cmd *cmd = NULL;
|
||||
int rc;
|
||||
|
||||
/* 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(ctx->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(ctx->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(ctx->fi, LOGL_ERROR, "CTRL msg sent failed: %d\n", rc);
|
||||
goto err_term;
|
||||
}
|
||||
|
||||
talloc_free(cmd);
|
||||
return 0;
|
||||
|
||||
err_term:
|
||||
talloc_free(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int send_neigh_addr_req(struct nacc_fsm_ctx *ctx)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
|
||||
|
||||
/* If PCU was configured to use the old CTRL interface, use it: */
|
||||
if (ctx->neigh_ctrl_conn)
|
||||
return send_neigh_addr_req_ctrl_iface(ctx);
|
||||
|
||||
/* Otherwise, by default the new PCUIF over IPA Abis multiplex proto should be used: */
|
||||
return pcu_tx_neigh_addr_res_req(bts, &ctx->neigh_key);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
|
||||
if (send_neigh_addr_req(ctx) < 0)
|
||||
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:
|
||||
/* data is NULL upon failure */
|
||||
if (data) {
|
||||
ctx->cgi_ps = *(struct osmo_cell_global_id_ps *)data;
|
||||
nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);
|
||||
} else {
|
||||
nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
|
||||
}
|
||||
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;
|
||||
struct osmo_cell_global_id_ps cgi_ps;
|
||||
|
||||
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) {
|
||||
osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, NULL);
|
||||
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;
|
||||
cgi_ps.rai.lac.plmn.mcc = atoi(tok);
|
||||
|
||||
if (!(tok = strtok_r(NULL, "-", &saveptr)))
|
||||
goto free_ret;
|
||||
cgi_ps.rai.lac.plmn.mnc = atoi(tok);
|
||||
|
||||
if (!(tok = strtok_r(NULL, "-", &saveptr)))
|
||||
goto free_ret;
|
||||
cgi_ps.rai.lac.lac = atoi(tok);
|
||||
|
||||
if (!(tok = strtok_r(NULL, "-", &saveptr)))
|
||||
goto free_ret;
|
||||
cgi_ps.rai.rac = atoi(tok);
|
||||
|
||||
if (!(tok = strtok_r(NULL, "\0", &saveptr)))
|
||||
goto free_ret;
|
||||
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, &cgi_ps);
|
||||
|
||||
osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, &cgi_ps);
|
||||
return;
|
||||
|
||||
free_ret:
|
||||
talloc_free(tmp);
|
||||
osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RAC_CI, NULL);
|
||||
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;
|
||||
|
||||
/* If CTRL ip present, use the old CTRL interface for neighbor resolution */
|
||||
if (ms->bts->pcu->vty.neigh_ctrl_addr) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool nacc_fsm_is_waiting_addr_resolution(const struct nacc_fsm_ctx *ctx,
|
||||
const struct neigh_cache_entry_key *neigh_key)
|
||||
{
|
||||
if (ctx->fi->state != NACC_ST_WAIT_RESOLVE_RAC_CI)
|
||||
return false;
|
||||
return neigh_cache_entry_key_eq(&ctx->neigh_key, neigh_key);
|
||||
}
|
||||
|
||||
bool nacc_fsm_is_waiting_si_resolution(const struct nacc_fsm_ctx *ctx,
|
||||
const struct osmo_cell_global_id_ps *cgi_ps)
|
||||
{
|
||||
if (ctx->fi->state != NACC_ST_WAIT_REQUEST_SI)
|
||||
return false;
|
||||
return !osmo_cgi_ps_cmp(&ctx->cgi_ps, cgi_ps);
|
||||
}
|
||||
|
||||
bool nacc_fsm_exp_ctrl_ack(const struct nacc_fsm_ctx *ctx, uint32_t fn, uint8_t ts)
|
||||
{
|
||||
return ctx->fi->state == NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK &&
|
||||
ctx->continue_poll_fn == fn &&
|
||||
ctx->continue_poll_ts == ts;
|
||||
}
|
||||
75
src/nacc_fsm.h
Normal file
75
src/nacc_fsm.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* 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.
|
||||
*/
|
||||
#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, /* RAC_CI became available in neigh_cache. NULL on failure, pointer to ctx->cgi_ps on success */
|
||||
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);
|
||||
|
||||
bool nacc_fsm_is_waiting_addr_resolution(const struct nacc_fsm_ctx *ctx,
|
||||
const struct neigh_cache_entry_key *neigh_key);
|
||||
|
||||
bool nacc_fsm_is_waiting_si_resolution(const struct nacc_fsm_ctx *ctx,
|
||||
const struct osmo_cell_global_id_ps *cgi_ps);
|
||||
|
||||
bool nacc_fsm_exp_ctrl_ack(const struct nacc_fsm_ctx *ctx, uint32_t fn, uint8_t ts);
|
||||
285
src/neigh_cache.c
Normal file
285
src/neigh_cache.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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);
|
||||
}
|
||||
108
src/neigh_cache.h
Normal file
108
src/neigh_cache.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* 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.
|
||||
*/
|
||||
#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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user