mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
synced 2025-11-03 05:33:31 +00:00
Compare commits
869 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
15
.gitignore
vendored
15
.gitignore
vendored
@@ -20,6 +20,7 @@ install-sh
|
||||
missing
|
||||
*libtool
|
||||
ltmain.sh
|
||||
*~
|
||||
|
||||
core
|
||||
core.*
|
||||
@@ -32,6 +33,7 @@ src/osmo-pcu-remote
|
||||
|
||||
# tests
|
||||
.dirstamp
|
||||
__pycache__/
|
||||
tests/atconfig
|
||||
tests/package.m4
|
||||
tests/*/*Test
|
||||
@@ -53,3 +55,16 @@ 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
|
||||
|
||||
15
Makefile.am
15
Makefile.am
@@ -1,9 +1,14 @@
|
||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
|
||||
SUBDIRS = include src examples tests
|
||||
EXTRA_DIST = osmoappdesc.py README.md
|
||||
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@
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = osmo-pcu.pc
|
||||
|
||||
@@ -82,3 +82,4 @@ Current limitations
|
||||
* No half-duplex class support (only semi-duplex)
|
||||
* No TA loop
|
||||
* No power loop
|
||||
* Multi-BTS support not tested
|
||||
|
||||
8
TODO
8
TODO
@@ -1,13 +1,5 @@
|
||||
* Make the TBF ul/dl list per BTS
|
||||
* Make the SBA per BTS
|
||||
* Make the tbf point to the BTS so we can kill plenty of parameters
|
||||
* Group more into in classes.. remove bts pointers.
|
||||
* Change functions with 100 parameters to get a struct as param
|
||||
* Move move into the TBF class
|
||||
* Replace trx/ts with pointers. E.g. a PDCH should know the trx
|
||||
it is on... then we can omit trx, ts and parameters and just pass
|
||||
the pdch.
|
||||
* On global free/reset... also flush the timing advance..
|
||||
* tbf/llc window code appears to be duplicated and nested in other
|
||||
methods. This needs to be cleaned.
|
||||
|
||||
|
||||
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
|
||||
107
configure.ac
107
configure.ac
@@ -9,6 +9,9 @@ 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])])
|
||||
|
||||
@@ -23,6 +26,11 @@ 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
|
||||
@@ -41,6 +49,7 @@ AC_ARG_ENABLE(sanitize,
|
||||
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
|
||||
@@ -73,10 +82,11 @@ then
|
||||
fi
|
||||
|
||||
dnl checks for libraries
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.12.0)
|
||||
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,
|
||||
@@ -138,6 +148,25 @@ if test "$enable_litecell15" = "yes"; then
|
||||
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]]),
|
||||
@@ -156,13 +185,77 @@ AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
|
||||
STD_DEFINES_AND_INCLUDES="-Wall"
|
||||
AC_SUBST(STD_DEFINES_AND_INCLUDES)
|
||||
|
||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||
# 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(
|
||||
osmo-pcu.pc
|
||||
include/Makefile
|
||||
src/Makefile
|
||||
examples/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
|
||||
@@ -1,5 +1,12 @@
|
||||
#!/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 !"
|
||||
@@ -41,6 +48,12 @@ elif [ "$with_dsp" = lc15 ]; then
|
||||
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"
|
||||
@@ -65,6 +78,11 @@ 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
|
||||
@@ -77,8 +95,13 @@ set -x
|
||||
autoreconf --install --force
|
||||
./configure $PCU_CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" AM_DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" \
|
||||
$MAKE distcheck \
|
||||
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
|
||||
@@ -1,17 +0,0 @@
|
||||
[Unit]
|
||||
Description=sysmocom sysmoPCU
|
||||
|
||||
[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
|
||||
Alias=osmo-pcu.service
|
||||
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
|
||||
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=sysmocom sysmoPCU
|
||||
Description=Osmocom osmo-pcu
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
@@ -14,4 +14,3 @@ CPUSchedulingPriority=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=sysmopcu.service
|
||||
991
debian/changelog
vendored
991
debian/changelog
vendored
@@ -1,3 +1,994 @@
|
||||
osmo-pcu (1.0.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Support uplink multi-slot allocations
|
||||
* tbf: Log timeslot allocation failure
|
||||
* bts: Count TBF TS allocation failure
|
||||
* pdch: Standarize and improve logging
|
||||
* tbf: Improve logging when TBF being allocated or no TBF avail
|
||||
* Remove uneeded ms param from alloc_algorithm_func_t func
|
||||
* bts: Add new stats to detect TBF allocation failure reasons
|
||||
* llc: use memset to fill llc dummy frame padding
|
||||
* tbd_dl: Don't re-initialize class field twice
|
||||
* tbf: log keep_open condition status
|
||||
* tbf_dl: Fix m_last_dl_drained_fn not set under some conditions
|
||||
* tbf_dl: fix FBI not set upon X2031 = 0
|
||||
* ms: clarify delayed MS release process related code and logging
|
||||
* gprs_ms: Use standarized logging on more messages
|
||||
* sched: sched_select_ctrl_msg(): Clean up param list and improve logging
|
||||
* sched: sched_select_downlink(): Clean up param list and improve logging
|
||||
* TODO-RELEASE: document requirement of master libosmocore
|
||||
* tbf: Log N310* counter increments
|
||||
* pdch: Silently ignore DATA.ind with len=0
|
||||
* direct_phy: Support submitting DATA.ind with len=0 to upper layers
|
||||
* pcu_utils.h: Fix trailing whitespace
|
||||
* Track TDMA clock with DATA.ind instead of TIME.ind
|
||||
* Introduce init() APIs for PDCH and TRX objects
|
||||
* tests: rlcmac: Fix C vs C++ linkage of extern symbol
|
||||
* pdch.h: Drop uneeded include bts.h
|
||||
* Improve DATA.ind logging
|
||||
* Improve logging in DATA.req and ACT.req
|
||||
* tbf: Fix wrong variable printed in log
|
||||
* pdch: Log FN when decoding UL Ctrl block
|
||||
* Add new PDCH UL Controller, drop SBAllocator class
|
||||
* Replace PollController with newly added PDCH UL Controller
|
||||
* sched: Use new PDCH UL Controller
|
||||
* bts: Detect FN jumps
|
||||
* cosmetic: tests/Makefile.am: Split content into several lines
|
||||
* tests: Introduce unit tests for PDCH UL Controller
|
||||
* tests: ulc: Show current bug with FN wrap around
|
||||
* ulc: Fix FN store order upon wrap around
|
||||
* sysmo: fix wrong FN jumps in rx RA.ind
|
||||
* direct_phy: Fix condition dropping rx DATA.ind payload in in
|
||||
* Fix: left shift cannot be repesented in type int
|
||||
* sched: Fix scheduling UL TBF not matching conditions
|
||||
* sched: Simplify usf selection code
|
||||
* Set matching USF if available when polling a UL TBF
|
||||
* pdch: Add mising pdch_ulc_release_node in Rx Cell Change Notif
|
||||
* pdch_ulc: Create helper API pdch_ulc_release_node
|
||||
* Track scheduled UL blocks through USF
|
||||
* Properly implement N3101
|
||||
* sba: Document AGCH_START_OFFSET after some experimental tests
|
||||
* pdch_ulc: Optimize rbtree FN search
|
||||
* Pick unreserved UL FN when allocating an SBA
|
||||
* pdch_ulc: Support picking RRBP other than N+13
|
||||
* Drop unused function tbf_check()
|
||||
* pdch_ulc: Store TBF poll reason
|
||||
* tbf: Get rid of unneeded poll_scheduled()
|
||||
* tbf: Allow multiple concurrent polls
|
||||
* Remove unneeded poll_state check
|
||||
* tbf: get rid of poll_state completely
|
||||
* Get rid of param 'poll' with constant value
|
||||
* tbf: Get rid of attribute poll_fn
|
||||
* tbf: Get rid of attribute poll_ts
|
||||
* RIM: Improve logging
|
||||
* sba: Drop unused function find_sba_rts
|
||||
* pdch: rcv_resource_request: Improve robustness
|
||||
* pdch: tbf_by_tfi(): Allow returning TBFs in state RELEASING
|
||||
* Stop abusing T3169
|
||||
* Make use of T3142 received from BTS
|
||||
* Use negative numbers for non-spec osmo-specific timers
|
||||
* ul_tbf: Clean up handle_tbf_reject()
|
||||
* Make WaitIndication T3172 configurable
|
||||
* sched: Simplify else-if condition
|
||||
* Clarify, document Assignment related timers
|
||||
* doc/tbf.txt: Update and improve some information
|
||||
* bts: constify arg in func bts_ms_store()
|
||||
* sched: Rename func to describe its used only for RLCMAC CTRL blocks
|
||||
* rim: Constify param in func
|
||||
* Simplify helper function tbf_select_slot_set()
|
||||
* alloc_algorithm_b: Rearrange variable initialization
|
||||
* Rename function s/tbf_alloc_ul/tbf_alloc_ul_pacch/
|
||||
* Split ul_tbf alloc on CCCH into new function
|
||||
* Implement T3141
|
||||
* tbf_ul: Use is_tlli_valid() API
|
||||
* Tx ul ack/nack: Avoid sending invalid/unknown TLLI
|
||||
* encoding: Use gsm48_ta_is_valid() API
|
||||
* encoding: Encode TA in UL ACK/NACK if available
|
||||
* sched: Clean up helper function and improve logging
|
||||
* Drop existing tbf->ms() check condition
|
||||
* ul_tbf: Simplify function rcv_data_block_acknowledged
|
||||
* ul_tbf: Fix accessing zeroed block when checking if transfer is complete
|
||||
* sched: Clean up param passing and improve logging
|
||||
* pdch: Use llist_first_entry() API
|
||||
* RIM: Refactor Rx path to decode stack in proper order
|
||||
* Clean false positive in newer GCC version checking guard of else clause
|
||||
* Use LOGPDCH macro in bts_add_paging()
|
||||
* Optimize PAGING-CS PDCH set selection when target MS is known
|
||||
* bts: Use ms_store when calculating set of target PDCHs for Pkt Paging Request
|
||||
* tbf: Log error path in setup() failing to assign control TS
|
||||
* Move TBF list from BTS to the TRX structure
|
||||
* MsTest: Set up tbf talloc destructor
|
||||
* tbf: Move existing tbf_state implementation to osmo_fsm
|
||||
* cosmetic: Fix typo s/TIMSI/TMSI/
|
||||
* gsm_rlcmac.c: Fix arg list of 2 callbacks
|
||||
* csn1: Implement CSN_CALLBACK type in encoder
|
||||
* bts: Fix typo in field name
|
||||
* Use new stat item/ctr getter APIs
|
||||
* pdch: Log pdch_ulc reason upon rx of pkt ctrl ack
|
||||
* pcuif: Support receiving System Information 2
|
||||
* pdch: Fix null MS access gprs_rlcmac_pdch::rcv_control_ack
|
||||
* pcuif_proto.h: Add new container message
|
||||
* Support proto IPAC_PROTO_EXT_PCU BSC<->PCU
|
||||
* pdch: Fix heap-use-after-free in pdch->ulc
|
||||
* Make gcc 11.1.0 false positivies happy
|
||||
* tbf: Drop impossible paths in create_dl_ass()
|
||||
* tests/tbf: Fix null pointer access if slowly stepping with gdb
|
||||
* Revert "coverity: fix null deref from recent UL TBF leak fix"
|
||||
* Revert "fix: handle NULL return of as_dl_tbf() and as_ul_tbf()"
|
||||
* Revert "Revert "Stop abusing T3169""
|
||||
* Move NULL and ASSIGN tbf_state transition to tbf_fsm
|
||||
* Move FLOW tbf_state transition to tbf_fsm.
|
||||
* tests: tbf: Fix dl_tbf polled for data without being in FLOW state
|
||||
* Move FINISHED tbf_state transition to tbf_fsm
|
||||
* Move WAIT_RELEASE tbf_state transition to tbf_fsm
|
||||
* Move RELEASING tbf_state transition to tbf_fsm
|
||||
* Move T3169 and T3195 to tbf_fsm
|
||||
* Drop duplicate log line
|
||||
* Put dl_tbf::cleanup into destructor
|
||||
* Drop logging last mas report before freeing TBF
|
||||
* Remove duplicate call to gprs_rlcmac_lost_rep
|
||||
* Move rate_ctr free to tbf subclass destructor
|
||||
* Get rid of tbf_dl:abort()
|
||||
* tbf_free: Get rid of uneeded tbf_state transition
|
||||
* Replace ul_ass_state with osmocom FSM
|
||||
* tbf: Reimplement rlcmac_diag() and make it available from C
|
||||
* tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_TO_UL_ASS
|
||||
* replace dl_ass_state with osmocom FSM
|
||||
* tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_TO_DL_ASS
|
||||
* tbf: Drop unuseful flag GPRS_RLCMAC_FLAG_UL_DATA
|
||||
* Move timer X2001 to tbf_fsm
|
||||
* Get rid of lots of code only used by tests
|
||||
* tbf: Merge handle_ack_nack() into rcvd_dl_ack()
|
||||
* Fix typos in comments documenting fsm st chg macro
|
||||
* tbf: Use type bool for upgrade_to_multislot
|
||||
* Move timer X2002 to tbf_fsm
|
||||
* tbf_dl: Clarify requirements for DL ACK/NACK
|
||||
* tbf.h: Improve documentation on several flags
|
||||
* Move tbf ul_ack_state to osmocom FSM
|
||||
* Simplify tbf::set_polling()
|
||||
* tbf: Move T3193 to tbf_state FSM
|
||||
* fix typo 's/dowlink/downlink/g'
|
||||
* cosmetic: Fix typo in comment
|
||||
* sched: energy saving: Avoid Tx dummy blocks on empty PDCH TS
|
||||
* Fix crash with dyn TS when using direct pcu
|
||||
* Use LOGPDCH macro to standarize log line
|
||||
* cosmetic: sysmo: Drop unneded comment line
|
||||
* bts: Use public getter instead of class member
|
||||
* sched: Lower log level of RTS on disabled pdch
|
||||
* pdch: Make sure pending ImmAssRej scheduled for disabled pdch are dropped
|
||||
* Support Neighbor Address Resolution over PCUIF IPA multiplex
|
||||
* nacc_fsm: Move logic checking if SI is being waited for to a func helper
|
||||
* scheduler: Skip Tx DL idle blocks in TRX0 when not in DIRECT_PHY mode
|
||||
* PTCCH: skip Tx DL idle blocks when possible
|
||||
* tbf_ul_ass_fsm.c: Fix missing state transition in FSM description
|
||||
* tbf: poll_timeout(): Validate expected poll reason
|
||||
* nacc: Introduce helper function nacc_fsm_exp_ctrl_ack()
|
||||
* tbf: refactor poll_timeout() with a switch statement
|
||||
* tests: TbfTest: Fix wrong behavior in test_tbf_dl_reuse()
|
||||
* pdch: refactor rcv_control_ack() with a switch statement
|
||||
* cosmetic: Fix missing space
|
||||
* assert if tbf pointer for POLL event is NULL
|
||||
* tbf_fsm: Ignore event DL_ACKNACK_MISS in state RELEASING
|
||||
* tests: RLCMACTest: Add one more sample RA capabilities to suite
|
||||
* rlcmac: Fix CSN1 definition for DownlinkDualCarrierCapability_r7_t in MS RA cap
|
||||
* tbf: Assert if FSM allocation fails
|
||||
* sched: Rename function
|
||||
* pdch: Only release ULC entry if rx ul block matches the expected one
|
||||
* pdch: Validate poll reason matches in rcv_control_(egprs)_dl_ack_nack()
|
||||
* pdch: PktResReq: Avoid releasing ULC entry if expecting something else on UL
|
||||
* Handle Final UL ACK/NACK Confirmation in tbf_fsm
|
||||
* tbf_fsm: rename state NULL -> NEW
|
||||
* pdch_ulc: Log POLL reason upon timeout
|
||||
* tbf_dl_ass_fsm: Move block msg generation conditions to rts() function
|
||||
* tbf_ul_ass_fsm: Fix use of incorrect log macro
|
||||
* tbf_fsm: Handle MAX_N3105 in state ASSIGN
|
||||
* tbf: Use define to flag control_ts unset special value
|
||||
* tbf: Document temporary change of control_ts and move code assigning it back to FSM
|
||||
* Return void in tbf_assign_control_ts()
|
||||
* pdch: rcv_data_block: Avoid releasing ULC entry if expecting something else on UL
|
||||
* tbf: Avoid keeping poll nodes in pdch_ulc of temporary control_ts used during PACCH assignment
|
||||
* tbf_ul_ass_fsm: Avoid retrying Pkt Ul Ass if tbf is not in state ASSIGN
|
||||
* Abort scheduling of pending Pkt Ul Ass if tbf goes into RELEASE step
|
||||
* tbf: Drop pending polls during free also on states != ASSIGN
|
||||
* pdch: Simplify code path allocating UL TBF
|
||||
* pdch: Log reason of expected POLL when receiving unexpected UL data
|
||||
* bts_rcv_rach(): Gather pointers to data objects early and use them later
|
||||
* bts_rcv_rach(): Split code paths for Ass and Ass Rej
|
||||
* Get rid of tbf tsc field
|
||||
* ts_alloc: Rename s/tbf_/tbf/
|
||||
* tbf: Update FSM names when TFI change during tbf_update()
|
||||
* tbf_fsm: Add assert verifying X2002 only triggers for DL TBF
|
||||
* tbf: Assert if update() is called on UL TBF
|
||||
* tbf: update(): return negative val on error
|
||||
* tbf: Drop unneeded braces in one line condition
|
||||
* cosmetic: Fix typo in comment
|
||||
* pcuif: Submit data_req with len=0 as idle frames
|
||||
* Split csn1.c into common, enc and dec files
|
||||
* csn1: Add unit test showing RadioAccess Capability decoding failure
|
||||
* csn1: Avoid failing if optional DownlinkDualCarrierCapability_r7 is missing
|
||||
* csn1: Avoid storing existence bit as true if content was actually NULL
|
||||
* csn1_dec.c: Fix stored bit in CSN_NEXT_EXIST_LH
|
||||
* bts: Fix misleading log line in bts_rcv_rach()
|
||||
* tbf_ul: Document context where tbf_alloc_ul_ccch() is used
|
||||
* bts: Rename 11bit RACH request counter
|
||||
* vty: Avoid crash in tbf_print_vty_info with null ptr ctrg
|
||||
* vty: Log tbf_state when showing a TBF
|
||||
* vty: show tbf: Drop unneeded check for non-null ms
|
||||
* bts: Introduce new RACH req counters for one/two phase access
|
||||
* bts: Improve logging to clarify RACH req is for 2 phase access
|
||||
* bts: Count RACH Request with unexpected content
|
||||
* tbf: Increase log level of line about unable to allocate poll for TBF
|
||||
* pdch: Improve log line and increase log level
|
||||
* tbf_ul: Set first_(common_)ts in handle_tbf_reject
|
||||
* tbf: Set m_created_ts in constructor
|
||||
* tbf: Mark initial first_(common_)ts with special value
|
||||
* tbf: Set tfi to initial special value
|
||||
* bts: Add counters for successful 1,2 phase pkt access
|
||||
* tbf_ul: Update FSM names for dummy reject TBFs
|
||||
* cosmetic: Add parenthesis around expression to clarify it
|
||||
* pdch::rcv_resource_request(): Use local var to store bts pointer
|
||||
* tbf_ul: Improve documentation of tbf_alloc_ul_pacch()
|
||||
* Add counter for successful contention resolution procedures
|
||||
* doc: Update counters_generated.adoc using osmo_vty_interact.py
|
||||
* bts_pch_timer: Fix timer working only for MI type IMSI
|
||||
* tests/alloc: Extend test_bts_pch_timer() to validate MI type TMSI
|
||||
* bts_pch_timer: Avoid resend Paging Request over PCUIF if T3113 is armed
|
||||
* pdch: Log line detaching TBF at start of the function
|
||||
* pdch: Log TS enable/disable transitions
|
||||
* pdch: Log DL TBF originating the new UL TBF
|
||||
* ts_alloc: Simplify tfi_find_free logic
|
||||
* ts_alloc: rename function to clraify what it does
|
||||
* ts_alloc: rename variable to clarify meaning
|
||||
* pdch: Update ms_reserved_slots in GprsMS when TS becomes disabled
|
||||
* pdch: Drop previous UL TBF from MS who sent PktResReq through SBA
|
||||
* pdch: Increase log level of line informing about TS control change
|
||||
* tbf_ul_ass_fsm: Log both TBFs if old TBF is handling assignment for new one
|
||||
* cosmetic: gprs_pcu.h: Fix typo in comment
|
||||
* tbf_dl_ass_fsm: Log both TBFs if old TBF is handling assignment for new one
|
||||
* tbf_dl_ass_fsm: Fix missing transition to NONE if DL TBF is nonexistent
|
||||
* tbf_{dl,ul}_ass_fsm.c: use proper macro to log tbf
|
||||
* pcu_main: Mark -r cmdline param as deprecated
|
||||
* vty: Introduce command 'gsmtap-remote-host' and 'gsmtap-category enable-all'
|
||||
* Fix MS ending up with assigned imsi 000
|
||||
* bts: Add counter availablePDCHAllocatedTime
|
||||
* tbf_dl_ass_fsm: Drop unsued X2000 timer callback
|
||||
* T_defs_pcu: Set default val for X2000 to 0 ms
|
||||
* Move T3172 T_defs_bts->T_defs_pcu to have it configurable in VTY
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* gprs_bssgp_pcu: rework BSSGP Reset messages to support SGSN originated BSSGP-RESET
|
||||
* gprs_bssgp_pcu: ensure only known BVCI can be resetted by the SGSN
|
||||
* gprs_bssgp_pcu: add comments to the pcu states
|
||||
|
||||
[ Harald Welte ]
|
||||
* pdch_ul_controller: Fix compiler warning on gcc-10.2
|
||||
* manual: remove revhistory, as we don't maintain it manually anyyway
|
||||
* manual: Update copyright years
|
||||
* vty: Add configuration for Gb DSCP and socket priority
|
||||
* manual: Include QoS chapter and add osmo-pcu specific example
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* gprs_rlcmac_sched: fix incorrect length for CTR_RLC_DL_BYTES
|
||||
* PCUIF protocol: add message definition for interference report
|
||||
* pcu_l1_if: ignore PDCH interference reports, do not log errors
|
||||
|
||||
[ Oliver Smith ]
|
||||
* test: add 'make update_exp' target
|
||||
* Add counters: pcu.sgsn.N.rx_paging_{cs,ps}
|
||||
* Add counters: pcu.bts.N.pch.requests
|
||||
* Add counters: pcu.bts.N.pch.requests.timeout
|
||||
* bts: delete pch_timer list in destructor
|
||||
* tests: make update_exp: build check_PROGRAMS first
|
||||
* debian/control: remove dh-systemd build-depend
|
||||
* Add stats: pcu.bts.N.pdch.available/occupied
|
||||
* Add stats: pcu.bts.N.pdch.occupied.gprs/egprs
|
||||
* pdch: has_gprs_only_tb_attached: use m_num_tbfs
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* T_defs_bts: remove unit from doc strings
|
||||
* Revert "Stop abusing T3169"
|
||||
* fix: handle NULL return of as_dl_tbf() and as_ul_tbf()
|
||||
* coverity: fix null deref from recent UL TBF leak fix
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* gprs_bssgp_pcu: Fix crash when configuring an existing ns bind
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 16:47:29 +0100
|
||||
|
||||
osmo-pcu (0.9.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Pass paging group instead of imsi where later is not needed
|
||||
* Allow Gb PAGING-PS without P-TMSI
|
||||
* Support Gb PAGING-CS
|
||||
* Support PAGING-CS and PAGING-PS on on PTP-BVCI
|
||||
* tests/rlcmac: print test name at the start
|
||||
* tests/rlcmac: Memzero decoded struct
|
||||
* tests/rlcmac: Fix missing commas with unexpected results
|
||||
* tests/rlcmac: Use osmo_hexdump to print buffers
|
||||
* tests/rlcmac: Don't check stderr output
|
||||
* tests/rlcmac: Add test to showcase that decode_gsm_ra_cap() fails
|
||||
* csn1: Extend CSN_SERIALIZE to allow 0 bit of length
|
||||
* csn1: Allow CHOICE elements to re-process the bits used for the choice
|
||||
* csn1: Fix pedantic compiler warnings in csn.1 dissectors
|
||||
* csn1: Drop format_p union from CSN_DESCR
|
||||
* gsm_rlcmac.cpp: Fix trailing whitespace
|
||||
* cosmetic: csn1.cpp: Fix whitespace
|
||||
* csn1.cpp: Rework ProcessError() function to print errors
|
||||
* rlcmac: Return error code from csn1 encoder/decoder
|
||||
* Check return code of rlcmac decode/encode functions
|
||||
* rlcmac: Transform a few LOGPC messages to LOGP
|
||||
* Fix trailing newline mess with LOGP(C) in rlcmac/csn1
|
||||
* llc_queue::{dequeue,enqueue}() refactor
|
||||
* gsm_rlcmac: fix Packet_Resource_Request_t: s/Slot/I_LEVEL_TN/
|
||||
* tests/llc: Change unrealistic time jump to avoid runtime error under ARM
|
||||
* Use clock_gettime(CLOCK_MONOTONIC) and timespec everywhere
|
||||
* Use downlink BSSGP RA Cap IE
|
||||
* tests/RLCMACTest: free allocated bitvectors
|
||||
* tests/RLCMACTest: Several fixes and improvements to RAcap tests
|
||||
* rlcmac: Don't pass array element to CSN1 descriptors
|
||||
* csn1: Validate recursive array max size during decoding
|
||||
* rlcmac: Fix bug receiving RA cap
|
||||
* rlcmac: Log names of de/encoded rlcmac packet types
|
||||
* rlcmac: Introduce MS Radio Access Capabilities 2 to fix related spare bits
|
||||
* cosmetic: rlcmac: Fix comment typo and whitespace introduced recently
|
||||
* rlcmac: Rename field to MS RA Cap2 in Additional_MS_Rad_Access_Cap_t
|
||||
* pcu_l1_if.cpp: Add missing header ctype.h
|
||||
* gsm_rlcmac: Use 'struct bitvec' instead of 'bitvec'
|
||||
* cosmetic: Do not indent header includes inside extern C block
|
||||
* gsm_rlcmac.cpp: Avoid declaring variable in for loop
|
||||
* csn1.h: Fix trailing whitespace
|
||||
* tbf.cpp: Include c++ <new> header required for new operator's replacement type
|
||||
* gsm_rlcmac: Disable unused CSN1 descriptors
|
||||
* Move gsm_rlcmac.cpp -> .c
|
||||
* rlcmac: support decode FDD_CELL_INFORMATION of "UTRAN FDD Description
|
||||
* rlcmac: add dissection of 2G->3G/4G PS handover
|
||||
* csn1: Fix Several typos & whitespace
|
||||
* csn1: verify enough bits present to decode whole CSN_UINT_ARRAY
|
||||
* csn1: Properly verify CSN_BITMAP length
|
||||
* csn1: Remove code block from CSN_NEXT_EXIST_LH
|
||||
* pcu_l1_if: Don't use GSMTAP_CHANNEL_PACCH when sending unknown gsmtap blocks
|
||||
* pdch: Avoid sending GSMTAP_CHANNEL_UNKOWN for rejected UL EGPRS data block
|
||||
* tbf: Avoid crash: don't set TBF window size if setup failed
|
||||
* bts: Rename mslot_class_from_ra
|
||||
* bts: Fix Decoding EGPRS MultislotClass from 11-bit EGPRS PACKET CHANNEL REQUEST
|
||||
* bts: Return uint8_t in egprs_mslot_class_from_ra()
|
||||
* Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||
* Expect ms object to exist before calling tbf_alloc_ul_tbf()
|
||||
* Expect ms object to exist before calling tbf_alloc_dl_tbf()
|
||||
* pdch: rcv_resource_request(): Clarify tbf_free only needed if MS used to exist beforehand
|
||||
* Drop unneeded arg 'ta' in tbf_alloc_ul()
|
||||
* bts: Drop specific functions to increase counters
|
||||
* bts: Drop specific functions to add values to counters
|
||||
* bts: Drop specific functions to add values to stats
|
||||
* pcu: tbf_ul: Clean up maybe_schedule_uplink_acknack()
|
||||
* sysmo: femtobts.h: Avoid redefining global variables
|
||||
* rlc: Drop unused function gprs_rlc_data::put_data
|
||||
* rlc: Move prepare() function out of gprs_rlc_data struct
|
||||
* tbf_ul: Fix UL ACK not sent to MS if intermediate UL block is lost
|
||||
* Get rid of class GprsCodingScheme
|
||||
* gsmtap: Set signal level and SNR fields
|
||||
* gprs_ms: Small clean ups in IMSI storage related code
|
||||
* gprs_ms: Transfer known EGPRS MS class when mergling old MS
|
||||
* tbf: Drop unneeded method set_tlli_from_ul
|
||||
* pdch.cpp: Fix logging line format in rcv_block wrong length
|
||||
* Set correct GSMTAP channel type for PDTCH messages returning error
|
||||
* decoding.cpp: Improve logging in malformed UL data parsing
|
||||
* tbf_dl: uint8_t is enough to store a TA value
|
||||
* encoding: Encode TA as unsigned and check validty against GSM48_TA_INVALID
|
||||
* encoding.cpp: Fix missing spacing in function param
|
||||
* pdch.cpp: Avoid dropping existing DL TBF during rcv_resource_request
|
||||
* pdch.cpp: Avoid resetting (egprs_)ms_class to unknown if not found in MS RadioAccCap
|
||||
* pdch.cpp: Fix wrong annoying log line about non-scheduled ResourceReq received
|
||||
* pdch.cpp: Store TLLI promptly on newly created TLLI in rcv_resource_request
|
||||
* Fix typo in log message
|
||||
* pdch: Drop unneeded notice log message in rcv pkt meas report
|
||||
* Introduce log macro helper LOGPMS
|
||||
* configure.ac: Fix trailing whitespace
|
||||
* doc: Update VTY reference xml file
|
||||
* Support setting rt-prio and cpu-affinity mask through VTY
|
||||
* pdch: rcv pkt meas rep: Allocate MS object early in path and use it
|
||||
* Fix recent typo preventing MS from registering
|
||||
* gitignore: Add __pychache__ dir
|
||||
* tbf: Don't log rlcmac_diag() output in separate lines
|
||||
* gprs_ms_storage.h: Set pointer to NULL instead of 0
|
||||
* Free all MS TBFs when receiving GPRS Suspension Request
|
||||
* cosmetic: fix indentation alignment
|
||||
* vty: Add 'show bts pdch' command
|
||||
* cosmetic: Fix indentation of for loops
|
||||
* cosmetic: Fix typo in comment
|
||||
* Fix crash accessing NULL tbf->pdch[first_ts]
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* Improve debug logging for alloc algos
|
||||
* Fix several calls to LOGPAL
|
||||
* Move gprs_rlcmac_ul_tbf::window to correct file
|
||||
* Move constructor gprs_rlcmac_dl_tbf::BandWidth to correct file
|
||||
* tbf: Make window() available to tbf base class
|
||||
* tbf: Implement enable_egprs() once
|
||||
* tbf: Set MS during constructor time
|
||||
* Move ul_tbf allocation code to correct file
|
||||
* Move dl_tbf allocation code to correct file
|
||||
* tbf: Drop unused function disable_egprs()
|
||||
* tests: ms: Pass correct pointer in constructor instead of NULL
|
||||
* tbf: Clean up gprs_rlcmac_dl_tbf::handle()
|
||||
* alloc_algo_b: Select TRX with least assigned TFIs during TBF alloc
|
||||
* bts: define egprs_enabled as bool
|
||||
* cosmetic: Fix ws between if keyword and parenthesis
|
||||
* tbf_dl: Update (egprs_)ms_class for already known MS
|
||||
* cosmetic: tests: pcu_emu: fix trailing whitespace
|
||||
* gprs_ms: Use proper function to get CS
|
||||
* Move BTS initial values inside bts.cpp
|
||||
* pcuif: Improve BTS-supported CS/MCS handling
|
||||
* Move EGPRS MS mode set to gprs_ms.cpp
|
||||
* Take into account BTS supported (M)CS values when retrieving the maximum
|
||||
* Enable egprs support through PCUIF from BTS/BSC
|
||||
* pdch: Process received CS1-4 data blocks regardless of egprs_enabled
|
||||
* tbf_dl: Don't fake EGPRS MS class when no related info is available
|
||||
* tbf_ul: Allow non-egprs phones if EGPRS is enabled
|
||||
* Get rid of bts->egprs_enabled
|
||||
* Fix configuration of initial_(m)cs
|
||||
* Fix mcs_is_valid(): UNKNOWN value is not a valid (M)CS
|
||||
* gprs_ms: Avoid enabling EGPRS if no MCS are supported
|
||||
* tbf_ul: Log mismatching TLLI on log message
|
||||
* Fix ctr reports: Remove ctr description from already removed counter
|
||||
* encoding: Fix duplicate word in log str
|
||||
* sched: Fix sending GSMTAP DL data blocks with unset USF
|
||||
* sched: Use correct GMSTAP category for EGPRS DL data blocks
|
||||
* Support multiplexing of GPRS and EGPRS TBFs in one PDCH
|
||||
* pdch: packet_paging_request: Put back non-fitting paging entry where where it was
|
||||
* pdch: Log hexdump of decde failure for dl rlcmac block
|
||||
* csn1: Fix readIndex pointer change in CSN_VARIABLE_ARRAY
|
||||
* csn1: Log CSN_VARIABLE_ARRAY values as hex
|
||||
* main: generate coredump and exit upon SIGABRT received
|
||||
* tbf: Log previous TS when changing Control TS
|
||||
* Implement downgrade to DL MCS1-4 when USF for GPRS_only MS
|
||||
* Dl TBF: Get rid of LLC UI dummy blocks following other data
|
||||
* rlcmac: Fix typo in MT_PACKET_CELL_CHANGE_NOTIFICATION value_string
|
||||
* gprs_rlcmac_sched: Use helper structure to store several tbf pointer params
|
||||
* sched: Convert code handling next_list array to be size independant
|
||||
* Convert GprsMS and helpers classes to C
|
||||
* tbf: Fix wrong verb used in log message
|
||||
* .gitignore: ignore files ending with ~
|
||||
* doc: Improve CS/MCS GPRS/EGPRS considerations in User Manual
|
||||
* tbf: remove 'software error' logs from tbf_free
|
||||
* ms: Replace struct var with rate_ctr
|
||||
* AllocTest: Avoid queuing tons of to-be-freed ms
|
||||
* gprs_ms: Mark ms_ctrg_desc static
|
||||
* Workaround ASan false positive runtime errors under some platforms
|
||||
* Split PCU global PCU object from BTS object
|
||||
* Move T_defs_pcu from BTS to PCU object
|
||||
* Move force_two_phase field from BTS to PCU
|
||||
* Move alpha,gamma fields from BTS to PCU
|
||||
* Move dl_tbf_preemptive_retransmission field from BTS to PCU
|
||||
* Move dl_arq_type field from BTS to PCU
|
||||
* Move cs_adj* fields from BTS to PCU
|
||||
* Move cs_downgrade_threshold field from BTS to PCU
|
||||
* Move (m)cs_lqual_ranges fields from BTS to PCU
|
||||
* Move ns_dialect field from BTS to PCU
|
||||
* Move fc_* fields from BTS to PCU
|
||||
* tests/tbf: Allocate PCU per test instead of globally
|
||||
* Move ws_* fields from BTS to PCU
|
||||
* Move llc_* fields from BTS to PCU
|
||||
* Fix configuration mess of initial_cs/mcs between PCUIF and VTY
|
||||
* Unify BTS into a C usable structure
|
||||
* Get rid of bts singletons
|
||||
* Rename 'bts_data' leftovers to 'bts'
|
||||
* bts: combine bts_{init,cleanup} into consturctor/destructor methods
|
||||
* Get rid of unused gsm_timer.{cpp,h}
|
||||
* Convert gprs_bssgp_pcu.cpp to C
|
||||
* Move tbf::free_all static methods to proper object files
|
||||
* Convert osmo_bts_sock.cpp to C
|
||||
* Allow multiple bts objects in PCU
|
||||
* bts: Store RAC+CI from info_ind
|
||||
* Get rid of singleton gprs_bssgp_pcu_current_bctx()
|
||||
* Initial handling support for RIM messages
|
||||
* gprs_pcu: Use libosmocore osmo_cgi_ps_cmp API
|
||||
* ms: Drop always-false check
|
||||
* sched: Check if egprs is enabled in TBF rather than MS being egprs capable
|
||||
* tbf: Drop always-true condition checking for MS
|
||||
* encoding: fix typos in comment
|
||||
* ms: Set proper initial MCS values setting mode EGPRS_GMSK
|
||||
* ms: Properly handle EGPRS_GMSK mode in ms_max_cs_dl/ul()
|
||||
* Fix Dl EGPRS data blocks being generated occasionally on GPRS TBFs
|
||||
* sched: Avoid picking TBF with nacked dl blocks when GMSK is required
|
||||
* tbf: Make tbf_ms() param const
|
||||
* Introduce NACC support
|
||||
* NACC: Fix crash freeing struct if CTRL conn was refused during alloc
|
||||
* NACC: delay CTRL conn socket init until it's needed
|
||||
* NACC: allow setting keep time for entries in neigh and si cache
|
||||
* NACC: Configure neighbor and SI resolution timeout values
|
||||
* NACC: Send only Pkt Cell Chg Continue if SI retrieve fails
|
||||
* doc: Mark PCU node red in network node diagram
|
||||
* doc: Introduce section documenting NACC support
|
||||
* nacc: Improve log line failing to establish CTRL neigh conn
|
||||
* Update TS 04.60 references to new TS 44.060
|
||||
* Drop comment about an already implemented TODO
|
||||
* Move src/tbf.txt to doc/
|
||||
* encoding: Fix comment description of S/P field
|
||||
* tbf: Reuse stored result in variable in check_polling()
|
||||
* tbf: Constify some methods
|
||||
* nacc: Fix typo in function name
|
||||
* nacc: Implement Pkt Cell Change Continue retransmission
|
||||
* nacc: Avoid RIM procedures targeting cells under same PCU
|
||||
* rlc.h: Fix struct bit fields on big endian systems
|
||||
* cosmetic: fix typo in comment
|
||||
* nacc_fsm: Move code filling struct to helper function
|
||||
* nacc_fsm: Remove NACC_EV_RX_SI from in_event_mask of some states
|
||||
* nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_RESOLVE_RAC_CI
|
||||
* nacc_fsm: nacc_fsm: Support receiving Pkt Cell Change Notify in state WAIT_REQUEST_SI
|
||||
* nacc_fsm: Support receiving Pkt Cell Chg Notif while in some advanced states
|
||||
* nacc_fsm: Improve log when sending RIM RAN-INFO to gather SI from remote cell
|
||||
* vty: Write 'neighbor resolution' config to file
|
||||
* cosmetic: fix line indentation
|
||||
* sched: Avoid selecting TBF to tx NACC Dl msg if no TFI is assigned
|
||||
* tests: Explicitly drop category from log
|
||||
* tests: Replace deprecated API log_set_print_filename
|
||||
* Use NULL as default value for pointer type
|
||||
* find_multi_slots: Avoid calling mslot_class_get_tx() on each iteration
|
||||
* find_multi_slots: Avoid multiple calls to mslot_class_get_rx()
|
||||
* find_multi_slots: Mark mslot_class properties const
|
||||
* find_multi_slots: Avoid multiple calls to mslot_class_get_type()
|
||||
* Use ALPHA value received in SI13 from PCUIF
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* pcu_l1_if.cpp: fix NULL-pointer dereference in imsi2paging_group()
|
||||
* gsm_timer: fix comparison of constant LONG_MAX with an integer
|
||||
* encoding: fix log_alert_exit(): do not treat error as format string
|
||||
* tests/alloc: fix implicit conversion from 'double' to 'int8_t'
|
||||
* gprs_bssgp_pcu: fix invalid use of non-static data member 'frame'
|
||||
* gprs_bssgp_pcu: fixup: fix length check in gprs_bssgp_pcu_rx_dl_ud()
|
||||
* csn1: fix csnStreamDecoder(): avoid conditional calls to bitvec_read_field()
|
||||
* VTY: get rid of pcu_vty_go_parent() / pcu_vty_is_config_node()
|
||||
* VTY: install talloc context introspection commands
|
||||
* pcu_sock: fix memleak, allocate pcu_sock_state on stack
|
||||
* pcu_sock: cosmetic: fix typo in a comment message
|
||||
* tbf: cosmetic: fix spacing in gprs_rlcmac_tbf::create_ul_ass()
|
||||
* tbf: fix NULL pointer dereference in create_[ul|dl]_ass()
|
||||
* encoding: assert return value of bitvec_set_u64()
|
||||
* csn1: fix some mistaken CSN.1 error names
|
||||
* csn1: fix csnStreamDecoder(): catch unknown CSN_CHOICE values
|
||||
* tests/rlcmac: mark Packet Polling Request as malformed
|
||||
* csn1: fix existNextElement(): use bitvec_get_bit_pos()
|
||||
* tests/rlcmac: additionally match debug output of the CSN.1 codec
|
||||
* csn1: get rid of C++ specific code, compile with GCC
|
||||
* csn1: fix csnStreamDecoder(): do not subtract no_of_bits twice
|
||||
* csn1: fix csnStreamDecoder(): always keep remaining_bits_len updated
|
||||
* csn1: fix csnStreamDecoder(): update bit_offset in CSN_EXIST{_LH}
|
||||
* csn1: bitvec_get_uint() may return a negative, use %d
|
||||
* csn1: use proper format specifier for unsigned integers
|
||||
* gsm_rlcmac: fix misleading LOGP statement in decode_gsm_ra_cap()
|
||||
* tests/rlcmac: fix malformed MS RA capability in testRAcap()
|
||||
* tests/rlcmac: also verify encoding of MS RA Capability
|
||||
* tests/rlcmac: add a new test vector for Packet Resource Request
|
||||
* csn1: fix csnStreamDecoder(): skip bits unhandled by serialize()
|
||||
* tests/rlcmac: also enable logging for DRLCMACDATA category
|
||||
* rlcmac: fix encode_gsm_*(): do not suppress encoding errors
|
||||
* csn1: fix: do not return 0 if no bits left in the buffer
|
||||
* BSSGP: cosmetic use OSMO_IMSI_BUF_SIZE from libosmocore
|
||||
* BSSGP: fix: properly encode P-TMSI in RR Paging Request
|
||||
* pdch: fix packet_paging_request(): properly print paging MI
|
||||
* pdch: cosmetic: use GSM_MI_TYPE_* constants from libosmocore
|
||||
* fix: properly include pure C headers from C++ code
|
||||
* l1if: fix pcu_rx_rach_ind(): use proper format string specifiers
|
||||
* sba: fix possible memleak in SBAController::alloc()
|
||||
* TBF/UL: fix rcv_data_block_acknowledged(): print the actual TLLI
|
||||
* fix egprs_mslot_class_from_ra(): multislot class may not be present
|
||||
* l1if: fix: s/pcu_rx_rach_ind_pdtch/pcu_rx_rach_ind_ptcch/g
|
||||
* csn1: fix M_CHOICE: restirct maximum length of the choice list
|
||||
* csn1: fix csnStreamEncoder(): also check length of the choice list
|
||||
* csn1: fix csnStreamEncoder(): always check the choice index
|
||||
* csn1: fix: never use enumerated types in codec structures
|
||||
* encoding: cosmetic: use RLC_MODE_ACKNOWLEDGED where possible
|
||||
* RLC/MAC: implement decoding of EGPRS Packet Channel Request
|
||||
* encoding: fix write_ia_rest_egprs_uplink_sba(): add missing CHECK(rc)
|
||||
* doc/manuals: fix typo in overview.adoc: s/Omsocom/Osmocom/g
|
||||
* bts: refactor handling and parsing of RACH.ind
|
||||
* BTS::parse_rach_ind(): properly handle EGPRS Packet Channel Request
|
||||
* bts: add send_gsmtap_rach(), also send PTCCH/U over GSMTAP
|
||||
* bts: fix send_gsmtap_rach(): properly pack 11 bit RA
|
||||
* bts: cosmetic: use DUMMY_VEC for padding where possible
|
||||
* encoding: drop log_alert_exit(), use OSMO_ASSERT() instead
|
||||
* encoding: assert() presence of Downlink TBF
|
||||
* direct-phy: fix handle_ph_ra_ind(): handle PH-RA.ind on PRACH SAPI
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
* pcu_l1_if: use proper format specifier for PCUIF version
|
||||
* pcu_l1_if: constify the argument of pcu_rx_info_ind()
|
||||
* pcu_l1_if: cosmetic: rename both 'trx'/'ts' to 'trx_nr'/'ts_nr'
|
||||
* pcu_l1_if: cosmetic: move struct 'gprs_rlcmac_pdch' into the for loop
|
||||
* pcu_l1_if: cosmetic: correct error message in pcu_rx_info_ind()
|
||||
* gsm_rlcmac: use consistent naming for [Extended] Packet Timing Advance
|
||||
* tbf: cosmetic: use GSM_MACBLOCK_LEN where possible
|
||||
* tbf: allocate the bitvec on stack in create_{dl,ul}_ass()
|
||||
* encoding: constify 'tbf' in UL/DL assignment functions
|
||||
* encoding: do not encode out of range Timing Advance values
|
||||
* encoding: fix RRBP field in write_packet_uplink_assignment()
|
||||
* encoding: use bool for use_egprs in write_packet_uplink_assignment()
|
||||
* encoding: pass pdch slot directly to encoding functions
|
||||
* encoding: clarify docstring for write_packet_downlink_assignment()
|
||||
* encoding: use CSN.1 codec to generate Packet Uplink Assignment
|
||||
* encoding: implement handing of hopping parameters
|
||||
* encoding: fix gen_freq_params(): do not check pdch twice
|
||||
* pcuif_proto: version 10: add frequency hopping parameters
|
||||
* pcu_l1_if: cosmetic: use ARRAY_SIZE() in pcu_rx_info_ind()
|
||||
* pcu_l1_if: correct logging level in pcu_rx_info_ind()
|
||||
* pcu_l1_if: cosmetic: make {local,remote}_sockaddr scoped variables
|
||||
* pcu_l1_if: use proper format string specifiers: %d -> %u
|
||||
* pcu_l1_if: print NSVC address in more common format
|
||||
* gprs_bssgp_pcu: make osmo_sockaddr local/sgsn arguments const
|
||||
* gprs_bssgp_pcu: fix possible memleak in gprs_nsvc_create_and_connect()
|
||||
* struct gprs_rlcmac_bts: remove unused 'nsei' field
|
||||
* gprs_bssgp_pcu: fix: do not crash on receipt of subsequent INFO.ind
|
||||
* doc/manuals: (re-)generate XML VTY reference automatically
|
||||
* fix tbf_select_slot_set(): use LOGP() instead of LOGPC()
|
||||
* main: remove line breaks in print_help(), increase spacing
|
||||
* main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
|
||||
* BSSGP: use tlvp_val8() in gprs_bssgp_pcu_rx_paging_cs()
|
||||
* BSSGP: constify argument 'tp' of gprs_bssgp_pcu_rx_paging_{cs,ps}
|
||||
* TLLI 0x00000000 is a valid TLLI, use 0xffffffff instead
|
||||
* gprs_rlcmac_sched: fix incorrect SBA frame number assignment
|
||||
* bts: fix uninitialized memaccess in BTS::send_gsmtap_rach()
|
||||
* bts: fix uninitialized memaccess in BTS::send_gsmtap()
|
||||
* tests/rlcmac: add more test vectors for Packet Resource Request
|
||||
* contrib/osmo-pcu.spec.in: require libosmo* version 1.4.0
|
||||
* contrib/osmo-pcu.spec.in: add missing libosmoctrl dependency
|
||||
* vty: register libosmocore's FSM introspection commands
|
||||
|
||||
[ Anders Broman ]
|
||||
* csn1: Update M_NULL CSN_DESCR to match wireshark
|
||||
* csn1: packet-csn1.c:179: warning: 'pui8' may be used uninitialized in this function
|
||||
* csn1: Fix warning with -Wmissing-prototypes
|
||||
* csn1: Try to fix cast discards '__attribute__((const))' qualifier from pointer target type
|
||||
* gsm_rlcmac.cpp: hanged all M_BIT macros to M_UINT, as M_BIT does not use the referenced hf.
|
||||
|
||||
[ Jeff Morriss ]
|
||||
* csn1: shuffle decrements of remaining_bits_len
|
||||
|
||||
[ Pascal Quantin ]
|
||||
* csn1: Fix an infinite loop in CSN.1 dissector when having more than 255 padding bits
|
||||
* gsm_rlcmac.h: Remove Uplink messages from the RlcMacDownlink_t structure
|
||||
* gsm_rlcmac: Enhance dissection of PSI1
|
||||
* gsm_rlcmac.cpp: Do not skip too many lines of the CSN_DESCR when the field is missing
|
||||
* gsm_rlcmac.cpp: fix an out of bounds access
|
||||
* gsm_rlcmac.cpp: fix another global-buffer-overflow error reported by ASAN
|
||||
* gsm_rlcmac.cpp: fix global-buffer-overflow error reported by ASAN
|
||||
|
||||
[ Guy Harris ]
|
||||
* csn1: Don't cast away constness
|
||||
|
||||
[ Alexis La Goutte ]
|
||||
* csn1: fix this statement may fall through [-Werror=implicit-fallthrough=] found by gcc7
|
||||
|
||||
[ Bill Meier ]
|
||||
* gsm_rlcmac.h: #if 0 unused stuff
|
||||
|
||||
[ Gerald Combs ]
|
||||
* gsm_rlcmac.h: Make sure we have a corresponding 'u' member to RlcMacDownlink_t for every call
|
||||
|
||||
[ Vincent Helfre ]
|
||||
* gsm_rlcmac: add dissection of NAS container
|
||||
* gsm_rlcmac: improve dissection of MS RA Capability IE
|
||||
|
||||
[ AndersBroman ]
|
||||
* gsm_rlcmac: Update : PACKET RESOURCE REQUEST to Release 14.0.0
|
||||
|
||||
[ Keith ]
|
||||
* Send UL-CTRL Packet to GSMTAP even if we fail to decode.
|
||||
* Don't check ul_control_block before decoding into it.
|
||||
|
||||
[ Harald Welte ]
|
||||
* csn1.c: Almost all of the logging is DEBUG, not NOTICE
|
||||
* TODO: remove those that have obviously been implemented 5+ years ago
|
||||
* bts.cpp: Increase constructor priority
|
||||
* Use osmo_fd_setup() whenever applicable
|
||||
* Use osmo_fd_*_{disable,enable}
|
||||
* gb manual: 08.16 -> 48.016 / 08.18 -> 48.018
|
||||
* gb manual: NS is implemented in libosmogb, not libosmocore
|
||||
* manuals/gb/ns.adoc: Update documentation regarding SNS capability
|
||||
* migrate to DLBSSGP as log sub-system for BSSGP
|
||||
|
||||
[ Eric ]
|
||||
* configure.ac: fix libtool issue with clang and sanitizer
|
||||
* tbf: add virtual destructor
|
||||
|
||||
[ Philipp Maier ]
|
||||
* gprs_debug: Use only LOGL_NOTICE as default loglevel
|
||||
* vty: add attributes to VTY commands indicating when they apply
|
||||
* pcu_main: add commandline option --vty-ref-xml
|
||||
* gprs_bssgp_rim: add serving BSS NACC application
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
* contrib/jenkins: don't build osmo-gsm-manuals
|
||||
* configure.ac: set -std=gnu11
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* use new osmo_mobile_identity api (avoid deprecation)
|
||||
* paging: pass struct osmo_mobile_identity, not encoded IE bytes
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* pcuif_proto: version 0xa: add support for IPv6 NSVCs
|
||||
* Revert "pcuif_proto: version 0xa: add support for IPv6 NSVCs"
|
||||
* pcuif_proto: version 10: add support for IPv6 NSVCs
|
||||
* Use the new NS2 lib
|
||||
* Rework NS configuration over the info indication
|
||||
* pcu_l1_if: fix misaligned assignment of remote address
|
||||
* NS2: follow the change of ownership
|
||||
* gprs_bssgp_pcu: follow ns2 library changes
|
||||
* NS2: rework handling of unknown primitive
|
||||
* ns2: follow ns2 dialect changes
|
||||
* ns2: follow changes to add a unique name to all binds
|
||||
* ns2: follow ns2 sns api changes
|
||||
* gprs_ns2: set default dialect to ipaccess
|
||||
* gprs_rlcmac_sched: don't leak a sched_dummy()
|
||||
* gprs_rlc_ts_alloc: ensure no rolling slots are allocated
|
||||
* follow gprs_ns2 API enum changes
|
||||
* gprs_ns2: migrate to the new vty syntax
|
||||
* gprs_bssgp: rework and rename ns_create_nsvc -> ns_configure_nse
|
||||
* gprs_bssgp: rename gprs_ns_config -> gprs_ns_update_config
|
||||
* gprs_bssgp: use gprs_ns2_sns_add_bind() to allow the NSE to use the binds for IP-SNS configuration
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 14:41:00 +0100
|
||||
|
||||
osmo-pcu (0.8.0) unstable; urgency=medium
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* tests: test encoding of egprs ul ack/nacks
|
||||
* tbf_dl: add comments to the scheduler
|
||||
* encoding: fix space, tabs
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* osmobts_sock.cpp: pcu_sock_cb(): use libosmocore's socket API
|
||||
* osmobts_sock.cpp: pcu_sock_read(): use stack buffer, not heap
|
||||
* osmobts_sock.cpp: pcu_sock_read(): further simplify the code
|
||||
* osmobts_sock.cpp: do not print the same debug message twice
|
||||
* VTY: refactor pcu_vty_show_ms_all(): use show_ms()
|
||||
* VTY: fix command 'show tbf all': properly filter TBFs
|
||||
* BSSGP: do not reject SUSPEND ACK / NACK messages
|
||||
* BSSGP: properly print BVCI for signalling messages (BVCI=0)
|
||||
* tests/tbf: suspend warnings about the link quality measurements
|
||||
* GprsMs::update_cs_ul(): clarify the meaning of old_link_qual
|
||||
* gprs_bssgp_destroy(): fix memleak and NULL-pointer dereference
|
||||
* PTCCH: implement basic message codec and API
|
||||
* PTCCH: properly handle RTS.req for PCU_IF_SAPI_PTCCH
|
||||
* pcuif_proto.h: extend RACH.ind with TRX / TS numbers
|
||||
* PTCCH: properly handle RACH.ind for PCU_IF_SAPI_PTCCH
|
||||
* VTY: add warning about changing PCU socket path at run-time
|
||||
* VTY: cosmetic: use osmo_talloc_replace_string()
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* cosmetic: tbf: Rename T and N arrays
|
||||
* Use proper API osmo_timer_setup() to set up timer struct
|
||||
* Introduce osmo_tdef infra and timer VTY commands
|
||||
* bts.cpp: Fix osmo_tdef initialization on older g++ compilers
|
||||
* Use osmo_tdef for BSSGP T1 and T2
|
||||
* Use osmo_tdef to implement T3190
|
||||
* tests: TbfTest: Unify stderr and stdout to ease debugging
|
||||
* Use osmo_tdef to implement ms-idle-time
|
||||
* Use osmo_tdef to implement dl-tbf-idle-time
|
||||
* pdch.cpp: Refactor bitvec param passing in rcv_control_block
|
||||
* pdch.cpp: Use pcu_l1_meas previously filled by lower layers
|
||||
* cosmetic: fix whitespace
|
||||
* Move out tbf subclasses from tbf.h to their own headers
|
||||
* Move tbf_{dl,ul} child constructors to respective .cpp files
|
||||
* tbf_dl: Setup m_llc_timer in constructor using osmocom API
|
||||
* tbf_dl.cpp: Remove dup call to tbf_update_ms_class() in state GPRS_RLCMAC_WAIT_RELEASE
|
||||
* vty: Fix osmo_tdef timers not listed in write config
|
||||
* Log RACH Requests using GSMTAP
|
||||
* Log AGCH and PCH blocks using GSMTAP
|
||||
* pcu_l1_if.cpp: Fix GSMTAP Imm Assign PCH wrong encoding
|
||||
* pcu_l1_if.cpp: Drop unneeded byte in Imm Ass PCH buffer
|
||||
* pcu_l1_if.cpp: Imm Assign PCH: clarify size of different items
|
||||
* pcu_l1_if.cpp: Replace value 23 with libosmocore's GSM_MACBLOCK_LEN
|
||||
* Fix assertion hit upon CCCH Paging Request
|
||||
* doc: vty: Update osmo-pcu_vty_reference.xml
|
||||
* Clarify (M)CS related VTY attributes
|
||||
* Remove dash from name used in VTY cmd prompt
|
||||
* tbf_dl.cpp: Fix typo in log line
|
||||
* pcu_l1_if: Check pag_req id_lv len fits buffer
|
||||
* prs_bssgp_pcu.cpp: Mark priv funcs as static and remove trailing whitespace
|
||||
* Fix trailing whitespace
|
||||
* fix typo in log message
|
||||
* Log BVCI PTP value upon msg recv
|
||||
* Split identity_lv param into mi+mi_len
|
||||
|
||||
[ Oliver Smith ]
|
||||
* doc: update generated VTY reference
|
||||
* tbf_dl: make preemptive retransmission optional
|
||||
* Forward ETWS Primary Notification to MS
|
||||
* tests/app_info: fix compiling with older g++
|
||||
* configure.ac: set C and C++ dialects
|
||||
|
||||
[ Harald Welte ]
|
||||
* manual: Fix copy+paste error
|
||||
* manual: Fix documentation missing "-D" command line option
|
||||
* manual: Add missing documentation for '-i' command line option
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 03 Jan 2020 19:40:02 +0100
|
||||
|
||||
osmo-pcu (0.7.0) unstable; urgency=medium
|
||||
|
||||
[ Rafael Diniz ]
|
||||
* Added support for daemonize to osmo-pcu.
|
||||
* Fix help message formatting of osmo-pcu.
|
||||
|
||||
[ Max ]
|
||||
* Don't install pcuif_proto.h header
|
||||
* Move C include to proper place
|
||||
* Add define for dummy burst string
|
||||
* Add encoding tests for Immediate Assignment
|
||||
* Clarify write_immediate_assignment() signature
|
||||
* Restructure IA Rest Octets encoders
|
||||
* Rewrite Packet Downlink Assignment
|
||||
* Rewrite EGPRS Packet Uplink IA Rest Octets for MBA
|
||||
* Rewrite EGPRS Packet Uplink IA Rest Octets for SBA
|
||||
* MCS: internalize 'family' parameter
|
||||
* EDGE tests: reduce code duplication
|
||||
* MCS: remove dead code
|
||||
* EDGE tests: remove no-op check
|
||||
* Use msgb_eq_data_print() in tests
|
||||
* Tighten lqual table limits check
|
||||
* Enable LGLOBAL logging for TBF tests
|
||||
* Log (M)CS UL update errors
|
||||
* MCS: move Coding Scheme enum outside of class definition
|
||||
* Make get_retx_mcs() into regular function
|
||||
* MCS: remove unused function
|
||||
* Debian: bump copyright year
|
||||
* Use unique NSEI/BVCI/NSVCI in TBF tests
|
||||
* MS store: move test helper to unit test
|
||||
* Explicitly clean up BTS singleton
|
||||
* MCS: move HeaderType enum outside of class definition
|
||||
* MCS: use value_string for conversion
|
||||
* TBF-DL: log MCS as string
|
||||
* Fix TA index encoder
|
||||
* MCS: move Mode enum outside of class definition
|
||||
* MCS: add mcs_is_*() helpers
|
||||
* MCS: add Channel Coding Command encoder
|
||||
* Fix Channel Coding Command for MCS
|
||||
* Rewrite Packet Uplink IA Rest Octets for MBA
|
||||
* Rewrite Packet Uplink IA Rest Octets for SBA
|
||||
* Use Timing Advance Index in UL assignments
|
||||
* TBF: update MCS counters
|
||||
* TBF-DL: cosmetic update for helper routines
|
||||
* Update IA Rest Octets encoding
|
||||
* TS alloc: expand tests log
|
||||
* vty: add commands to show TBF of a certain kind
|
||||
* Update MCS selection for retransmission
|
||||
* cosmetic: use const pointer for bts_data
|
||||
* Add test for MS mode and (M)CS settings
|
||||
* Use libosmocore for IMSI parsing
|
||||
|
||||
[ Harald Welte ]
|
||||
* Mark gprs_ns_reconnect() as static (not used outside of C file)
|
||||
* Optionally Use the NS Sub-Network-Service (SNS) on Gb
|
||||
* pcu_l1_if: Fix erroneous endian-swapping of the CellID
|
||||
* Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
|
||||
* gprs_debug: Use named initializers and explicit array indicies
|
||||
* bssgp: Fix dead code: PDUT_STATUS can never reach this part
|
||||
|
||||
[ JF Dionne ]
|
||||
* encoding: Fixes TMSI vs MI bit selection in repeated page info
|
||||
|
||||
[ Oliver Smith ]
|
||||
* tests: use -no-install libtool flag to avoid ./lt-* scripts
|
||||
* debian: create -doc subpackage with pdf manuals
|
||||
* contrib/jenkins.sh: run "make maintainer-clean"
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* Include pdch.h in bts.h even if we're not compiling C++
|
||||
* oc2g: Remove custom alarms
|
||||
* oc2g: Change log type (Litecell15->Oc2g)
|
||||
* jenkins.sh: Add oc2g build support
|
||||
* manuals: Add script to regenerate vty/counter documentation
|
||||
* manuals: Update VTY documentation
|
||||
|
||||
[ Jean-Francois Dionne ]
|
||||
* Initial commit for OC-2G support.
|
||||
|
||||
[ Minh-Quang Nguyen ]
|
||||
* OC-2G: Fix missing header
|
||||
* OC-2G: Fix TA adjustment
|
||||
* OC-2G: Always use positive TA information provided in PH-RA-IND
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* gprs_bssgp_pcu: make gprs_bssgp_ns_cb public
|
||||
* gprs_bssgp_pcu: explicit allocate & initialize bssgp_nsi instance
|
||||
* encoding: correct encoding of CRBB in ACK/NACK when not byte aligned
|
||||
* encoding: use `/* */` for comments instead of `#if 0 #endif`
|
||||
* egprs_rlc_compression: fix white spaces
|
||||
* tests/BitcompTest: fix wording in log message
|
||||
* rlc: replace int with uint16_t
|
||||
* Encoding: drop struct gprs_rlcmac_bts* from all functions
|
||||
* decompress_crbb: add length argument for search_runlen
|
||||
* Encoding: write_packet_ack_nack_desc_egprs: don't use a reference for rest_bits
|
||||
* bts.cpp: ensure left-shift operation does not exceed uint32_t
|
||||
* Encoding: use uint16_t when interacting with the window object
|
||||
* Encoding: ACK/NACK: always encode with length field present
|
||||
|
||||
[ Keith ]
|
||||
* Cosmetic: Osmcoom -> Osmocom
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* src/pcu_l1_if.cpp: fix: properly pass measurements from PCUIF
|
||||
* gprs_bssgp_pcu_rx_dl_ud(): fix: BSSGP_IE_IMSI is optional
|
||||
* gprs_bssgp_pcu.cpp: check return code of gsm48_mi_to_string()
|
||||
* gprs_bssgp_pcu_rx_dl_ud(): use OSMO_IMSI_BUF_SIZE
|
||||
|
||||
[ Thorsten Alteholz ]
|
||||
* fix spelling errors detected by lintian
|
||||
|
||||
[ Eric Wild ]
|
||||
* ubsan: fix shift
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds
|
||||
* Require newer libosmocore to avoid compile failures
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 07 Aug 2019 21:09:53 +0200
|
||||
|
||||
osmo-pcu (0.6.0) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* debian/rules: Don't overwrite .tarball-version
|
||||
* gprs_rlcmac_received_lost(): Fix regression / uninitialized now_tv
|
||||
* initial checkin of manuals to public repo
|
||||
* Add link to Asciidoc source code of manual
|
||||
* Initial place-holder for the new Gb/IP interface documentation
|
||||
* Gb message sequence chart: flip sides (SGSN should be right)
|
||||
* Gb message sequence chart: Add notion of PCU unix domain socket
|
||||
* Gb: Various spelling fixes
|
||||
* gb: Some language improvements, formatting changes
|
||||
* consistently use '3GPP TS' not sometimes 3GPP TS and sometimes TS.
|
||||
* gb/NS: Clarify the language regarding the UDP port numbers / socket
|
||||
* vty-ref: Update URI of docbook 5.0 schema
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Cleanup of systemd service files
|
||||
* configure.ac: Set CXXFLAGS during --enable-sanitize
|
||||
* Install systemd services with autotools
|
||||
* Move examples/ to doc/examples/
|
||||
* Install osmo-pcu.cfg to docdir/examples
|
||||
* Allow easily disabling GFDL references
|
||||
|
||||
[ Stefan Sperling ]
|
||||
* check for overlong unix socket paths
|
||||
|
||||
[ Max ]
|
||||
* deb: add missing copyright file
|
||||
* OsmoPCU: fix Gb documentation front page
|
||||
* OsmoPCU: expand NS documentation
|
||||
* OsmoPCU: expand BSSGP documentation
|
||||
* OsmoPCU: add MSC chart
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* Importing history from osmo-gsm-manuals.git
|
||||
* make clean: also remove generated image files
|
||||
* add 'make check' target
|
||||
* fix 'make clean': shell glob, ignore failure
|
||||
* refactor Makefile build rules, don't use the FORCE
|
||||
|
||||
[ Jonathan Brielmaier ]
|
||||
* fix various typos across all manuals
|
||||
|
||||
[ Philipp ]
|
||||
* configuration: fixing typos
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* OsmoPCU: add rate counter documentation
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* Change OpenBSC mentions to OsmoBSC where applicable
|
||||
|
||||
[ Oliver Smith ]
|
||||
* build manuals moved here from osmo-gsm-manuals.git
|
||||
* Fix DISTCHECK_CONFIGURE_FLAGS override
|
||||
* contrib/jenkins.sh: build and publish manuals
|
||||
* contrib: fix makedistcheck with disabled systemd
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Mon, 21 Jan 2019 19:03:52 +0100
|
||||
|
||||
osmo-pcu (0.5.1) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
|
||||
19
debian/control
vendored
19
debian/control
vendored
@@ -1,13 +1,13 @@
|
||||
Source: osmo-pcu
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
dh-autoreconf,
|
||||
dh-systemd (>= 1.5),
|
||||
autotools-dev,
|
||||
pkg-config,
|
||||
libosmocore-dev (>= 0.10.1)
|
||||
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
|
||||
@@ -19,7 +19,7 @@ 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 Osmcoom implementation of this network
|
||||
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.
|
||||
|
||||
@@ -32,6 +32,15 @@ Depends: osmo-pcu (= ${binary:Version}),
|
||||
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 Osmcoom implementation of this network
|
||||
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
4
debian/osmo-pcu.install
vendored
@@ -1,4 +1,4 @@
|
||||
etc/osmocom/osmo-pcu.cfg
|
||||
lib/systemd/system/osmo-pcu.service
|
||||
usr/bin/osmo-pcu
|
||||
usr/include/osmocom/pcu/pcuif_proto.h
|
||||
usr/lib/*/pkgconfig/osmo-pcu.pc
|
||||
usr/share/doc/osmo-pcu/examples/osmo-pcu/osmo-pcu.cfg
|
||||
|
||||
15
debian/osmo-pcu.service
vendored
15
debian/osmo-pcu.service
vendored
@@ -1,15 +0,0 @@
|
||||
[Unit]
|
||||
Description=Osmocom osmo-pcu
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
# Read quickly enough
|
||||
CPUSchedulingPolicy=rr
|
||||
CPUSchedulingPriority=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
11
debian/rules
vendored
11
debian/rules
vendored
@@ -14,10 +14,6 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-pcu-dbg
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
||||
|
||||
override_dh_clean:
|
||||
dh_clean
|
||||
$(RM) tests/package.m4
|
||||
@@ -26,3 +22,10 @@ override_dh_clean:
|
||||
# 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
|
||||
|
||||
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
|
||||
@@ -2,5 +2,4 @@ pcu
|
||||
flow-control-interval 10
|
||||
cs 2
|
||||
alloc-algorithm dynamic
|
||||
alpha 0
|
||||
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 resource 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
|
||||
|
||||
@@ -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.
|
||||
@@ -1,4 +0,0 @@
|
||||
pcuconfdir = $(sysconfdir)/osmocom
|
||||
pcuconf_DATA = osmo-pcu.cfg
|
||||
|
||||
EXTRA_DIST = osmo-pcu.cfg
|
||||
@@ -1,2 +1,2 @@
|
||||
nobase_include_HEADERS = \
|
||||
noinst_HEADERS = \
|
||||
osmocom/pcu/pcuif_proto.h
|
||||
|
||||
@@ -2,24 +2,33 @@
|
||||
#define _PCUIF_PROTO_H
|
||||
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define PCU_SOCK_DEFAULT "/tmp/pcu_bts"
|
||||
|
||||
#define PCU_IF_VERSION 0x09
|
||||
#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 */
|
||||
@@ -48,6 +57,13 @@
|
||||
#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,
|
||||
@@ -106,14 +122,25 @@ struct gsm_pcu_if_rach_ind {
|
||||
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 channels per TS */
|
||||
uint8_t pdch_mask; /* PDCH timeslot mask */
|
||||
uint8_t spare;
|
||||
uint8_t tsc[8]; /* TSC per channel */
|
||||
uint32_t hlayer1;
|
||||
struct gsm_pcu_if_info_ts ts[8]; /* timeslots per TRX */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_info_ind {
|
||||
@@ -148,10 +175,14 @@ struct gsm_pcu_if_info_ind {
|
||||
uint8_t initial_cs;
|
||||
uint8_t initial_mcs;
|
||||
/* NSVC */
|
||||
uint16_t nsvci[2];
|
||||
uint16_t local_port[2];
|
||||
uint16_t remote_port[2];
|
||||
uint32_t remote_ip[2];
|
||||
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 {
|
||||
@@ -171,6 +202,60 @@ struct gsm_pcu_if_pag_req {
|
||||
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 */
|
||||
@@ -182,6 +267,7 @@ struct gsm_pcu_if {
|
||||
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;
|
||||
@@ -189,6 +275,9 @@ struct gsm_pcu_if {
|
||||
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));
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/
|
||||
|
||||
Name: OsmoPCU
|
||||
Description: Osmocom PCU implementation
|
||||
Requires:
|
||||
Version: @VERSION@
|
||||
Cflags: -I${includedir}
|
||||
@@ -15,12 +15,12 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
app_configs = {
|
||||
"osmo-pcu": ["examples/osmo-pcu.cfg"]
|
||||
"osmo-pcu": ["doc/examples/osmo-pcu.cfg"]
|
||||
}
|
||||
|
||||
apps = [(4240, "src/osmo-pcu", "Osmo-PCU", "osmo-pcu"),
|
||||
apps = [(4240, "src/osmo-pcu", "OsmoPCU", "osmo-pcu"),
|
||||
]
|
||||
|
||||
vty_command = ["src/osmo-pcu", "-c", "examples/osmo-pcu.cfg"]
|
||||
vty_command = ["src/osmo-pcu", "-c", "doc/examples/osmo-pcu.cfg"]
|
||||
|
||||
vty_app = apps[0]
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
|
||||
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
|
||||
@@ -29,6 +29,10 @@ 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
|
||||
|
||||
@@ -36,35 +40,46 @@ 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_sched.cpp \
|
||||
gprs_rlcmac_meas.cpp \
|
||||
gprs_rlcmac_ts_alloc.cpp \
|
||||
gprs_ms.cpp \
|
||||
gprs_ms.c \
|
||||
gprs_ms_storage.cpp \
|
||||
gsm_timer.cpp \
|
||||
gprs_pcu.c \
|
||||
pcu_l1_if.cpp \
|
||||
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 \
|
||||
poll_controller.cpp \
|
||||
pdch_ul_controller.c \
|
||||
encoding.cpp \
|
||||
sba.cpp \
|
||||
sba.c \
|
||||
decoding.cpp \
|
||||
llc.cpp \
|
||||
rlc.cpp \
|
||||
osmobts_sock.cpp \
|
||||
osmobts_sock.c \
|
||||
gprs_codel.c \
|
||||
gprs_coding_scheme.cpp \
|
||||
egprs_rlc_compression.cpp
|
||||
coding_scheme.c \
|
||||
egprs_rlc_compression.cpp \
|
||||
gprs_rlcmac_sched.cpp
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-pcu
|
||||
@@ -76,18 +91,28 @@ noinst_HEADERS = \
|
||||
csn1.h \
|
||||
gsm_rlcmac.h \
|
||||
gprs_bssgp_pcu.h \
|
||||
gprs_bssgp_rim.h \
|
||||
gprs_rlcmac.h \
|
||||
gprs_ms.h \
|
||||
gprs_ms_storage.h \
|
||||
gprs_pcu.h \
|
||||
pcu_l1_if.h \
|
||||
gsm_timer.h \
|
||||
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 \
|
||||
poll_controller.h \
|
||||
pdch_ul_controller.h \
|
||||
encoding.h \
|
||||
sba.h \
|
||||
rlc.h \
|
||||
@@ -96,11 +121,9 @@ noinst_HEADERS = \
|
||||
pcu_utils.h \
|
||||
cxx_linuxlist.h \
|
||||
gprs_codel.h \
|
||||
gprs_coding_scheme.h \
|
||||
egprs_rlc_compression.h
|
||||
|
||||
nobase_include_HEADERS =
|
||||
osmocom/pcu/pcuif_proto.h
|
||||
coding_scheme.h \
|
||||
egprs_rlc_compression.h \
|
||||
wireshark_compat.h
|
||||
|
||||
osmo_pcu_SOURCES = pcu_main.cpp
|
||||
|
||||
@@ -136,6 +159,7 @@ osmo_pcu_remote_LDADD = \
|
||||
libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(COMMON_LA)
|
||||
endif
|
||||
@@ -160,10 +184,31 @@ osmo_pcu_SOURCES += \
|
||||
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)
|
||||
|
||||
|
||||
1275
src/bts.cpp
1275
src/bts.cpp
File diff suppressed because it is too large
Load Diff
828
src/bts.h
828
src/bts.h
@@ -2,6 +2,7 @@
|
||||
*
|
||||
* 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
|
||||
@@ -20,51 +21,35 @@
|
||||
|
||||
#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 <mslot_class.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
|
||||
}
|
||||
|
||||
#include <gsm_rlcmac.h>
|
||||
#include "poll_controller.h"
|
||||
#include "sba.h"
|
||||
#include "tbf.h"
|
||||
#include "gprs_ms_storage.h"
|
||||
#include "gprs_coding_scheme.h"
|
||||
#include <cxx_linuxlist.h>
|
||||
#include <pdch.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tbf.h"
|
||||
#include "coding_scheme.h"
|
||||
|
||||
#define LLC_CODEL_DISABLE 0
|
||||
#define LLC_CODEL_USE_DEFAULT (-1)
|
||||
#define MAX_GPRS_CS 9
|
||||
|
||||
/* 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_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 */
|
||||
};
|
||||
|
||||
struct BTS;
|
||||
struct GprsMs;
|
||||
struct gprs_rlcmac_bts;
|
||||
|
||||
struct gprs_rlcmac_trx {
|
||||
void *fl1h;
|
||||
@@ -72,524 +57,335 @@ struct gprs_rlcmac_trx {
|
||||
struct gprs_rlcmac_pdch pdch[8];
|
||||
|
||||
/* back pointers */
|
||||
struct BTS *bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
uint8_t trx_no;
|
||||
|
||||
#ifdef __cplusplus
|
||||
void reserve_slots(enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
|
||||
void unreserve_slots(enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
|
||||
#endif
|
||||
/* 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_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
|
||||
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
|
||||
|
||||
/**
|
||||
* This is the data from C. As soon as our minimal compiler is gcc 4.7
|
||||
* we can start to compile pcu_vty.c with c++ and remove the split.
|
||||
*/
|
||||
struct gprs_rlcmac_bts {
|
||||
bool active;
|
||||
uint8_t bsic;
|
||||
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;
|
||||
uint8_t cs1;
|
||||
uint8_t cs2;
|
||||
uint8_t cs3;
|
||||
uint8_t cs4;
|
||||
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_cs; /* 0=use from BTS 1=use from VTY */
|
||||
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 */
|
||||
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 gsmtap_inst *gsmtap;
|
||||
uint32_t gsmtap_categ_mask;
|
||||
struct gprs_rlcmac_trx trx[8];
|
||||
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
|
||||
bool single, int8_t use_tbf);
|
||||
|
||||
uint8_t force_two_phase;
|
||||
uint8_t alpha, gamma;
|
||||
uint8_t egprs_enabled;
|
||||
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
|
||||
uint8_t si13[GSM_MACBLOCK_LEN];
|
||||
bool si13_is_set;
|
||||
/* 0 to support resegmentation in DL, 1 for no reseg */
|
||||
uint8_t dl_arq_type;
|
||||
|
||||
uint32_t ms_idle_sec;
|
||||
uint8_t cs_adj_enabled;
|
||||
uint8_t cs_adj_upper_limit;
|
||||
uint8_t cs_adj_lower_limit;
|
||||
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
|
||||
struct {int16_t low; int16_t high; } mcs_lqual_ranges[MAX_GPRS_CS];
|
||||
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
|
||||
uint16_t ws_base;
|
||||
uint16_t ws_pdch; /* increase WS by this value per PDCH */
|
||||
|
||||
/* State for dynamic algorithm selection */
|
||||
int multislot_disabled;
|
||||
|
||||
/**
|
||||
* Point back to the C++ object. This is used during the transition
|
||||
* period.
|
||||
*/
|
||||
struct BTS *bts;
|
||||
|
||||
/* Path to be used for the pcu-bts socket */
|
||||
char *pcu_sock_path;
|
||||
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,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
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 BTS {
|
||||
public:
|
||||
enum {
|
||||
CTR_TBF_DL_ALLOCATED,
|
||||
CTR_TBF_DL_FREED,
|
||||
CTR_TBF_DL_ABORTED,
|
||||
CTR_TBF_UL_ALLOCATED,
|
||||
CTR_TBF_UL_FREED,
|
||||
CTR_TBF_UL_ABORTED,
|
||||
CTR_TBF_REUSED,
|
||||
CTR_TBF_ALLOC_ALGO_A,
|
||||
CTR_TBF_ALLOC_ALGO_B,
|
||||
CTR_TBF_FAILED_EGPRS_ONLY,
|
||||
CTR_RLC_SENT,
|
||||
CTR_RLC_RESENT,
|
||||
CTR_RLC_RESTARTED,
|
||||
CTR_RLC_STALLED,
|
||||
CTR_RLC_NACKED,
|
||||
CTR_RLC_FINAL_BLOCK_RESENT,
|
||||
CTR_RLC_ASS_TIMEDOUT,
|
||||
CTR_RLC_ASS_FAILED,
|
||||
CTR_RLC_ACK_TIMEDOUT,
|
||||
CTR_RLC_ACK_FAILED,
|
||||
CTR_RLC_REL_TIMEDOUT,
|
||||
CTR_RLC_LATE_BLOCK,
|
||||
CTR_RLC_SENT_DUMMY,
|
||||
CTR_RLC_SENT_CONTROL,
|
||||
CTR_RLC_DL_BYTES,
|
||||
CTR_RLC_DL_PAYLOAD_BYTES,
|
||||
CTR_RLC_UL_BYTES,
|
||||
CTR_RLC_UL_PAYLOAD_BYTES,
|
||||
CTR_DECODE_ERRORS,
|
||||
CTR_SBA_ALLOCATED,
|
||||
CTR_SBA_FREED,
|
||||
CTR_SBA_TIMEDOUT,
|
||||
CTR_LLC_FRAME_TIMEDOUT,
|
||||
CTR_LLC_FRAME_DROPPED,
|
||||
CTR_LLC_FRAME_SCHED,
|
||||
CTR_LLC_DL_BYTES,
|
||||
CTR_LLC_UL_BYTES,
|
||||
CTR_RACH_REQUESTS,
|
||||
CTR_11BIT_RACH_REQUESTS,
|
||||
CTR_SPB_UL_FIRST_SEGMENT,
|
||||
CTR_SPB_UL_SECOND_SEGMENT,
|
||||
CTR_SPB_DL_FIRST_SEGMENT,
|
||||
CTR_SPB_DL_SECOND_SEGMENT,
|
||||
CTR_IMMEDIATE_ASSIGN_UL_TBF,
|
||||
CTR_IMMEDIATE_ASSIGN_REJ,
|
||||
CTR_IMMEDIATE_ASSIGN_DL_TBF,
|
||||
CTR_CHANNEL_REQUEST_DESCRIPTION,
|
||||
CTR_PKT_UL_ASSIGNMENT,
|
||||
CTR_PKT_ACCESS_REJ,
|
||||
CTR_PKT_DL_ASSIGNMENT,
|
||||
CTR_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,
|
||||
};
|
||||
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];
|
||||
|
||||
enum {
|
||||
STAT_MS_PRESENT,
|
||||
};
|
||||
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;
|
||||
|
||||
enum {
|
||||
TIMER_T3190_MSEC = 5000,
|
||||
};
|
||||
/* State for dynamic algorithm selection */
|
||||
int multislot_disabled;
|
||||
|
||||
BTS();
|
||||
~BTS();
|
||||
/* 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 */
|
||||
|
||||
static BTS* main_bts();
|
||||
/* main nsei */
|
||||
struct gprs_ns2_nse *nse;
|
||||
|
||||
struct gprs_rlcmac_bts *bts_data();
|
||||
SBAController *sba();
|
||||
/* back pointer to PCU object */
|
||||
struct gprs_pcu *pcu;
|
||||
|
||||
/** TODO: change the number to unsigned */
|
||||
void set_current_frame_number(int frame_number);
|
||||
void set_current_block_frame_number(int frame_number, unsigned max_delay);
|
||||
int current_frame_number() const;
|
||||
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;
|
||||
|
||||
/** add paging to paging queue(s) */
|
||||
int add_paging(uint8_t chan_needed, uint8_t *identity_lv);
|
||||
struct GprsMsStorage *ms_store;
|
||||
|
||||
gprs_rlcmac_dl_tbf *dl_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
|
||||
gprs_rlcmac_ul_tbf *ul_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
|
||||
gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
|
||||
gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
|
||||
/* List of struct bts_pch_timer for active PCH pagings */
|
||||
struct llist_head pch_timer;
|
||||
|
||||
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx) const;
|
||||
|
||||
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
|
||||
|
||||
uint32_t rfn_to_fn(int32_t rfn);
|
||||
int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, bool is_11bit,
|
||||
enum ph_burst_type burst_type);
|
||||
|
||||
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
|
||||
|
||||
GprsMsStorage &ms_store();
|
||||
GprsMs *ms_by_tlli(uint32_t tlli, uint32_t old_tlli = 0);
|
||||
GprsMs *ms_by_imsi(const char *imsi);
|
||||
GprsMs *ms_alloc(uint8_t ms_class, uint8_t egprs_ms_class = 0);
|
||||
|
||||
void send_gsmtap(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);
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
void tbf_dl_created();
|
||||
void tbf_dl_freed();
|
||||
void tbf_dl_aborted();
|
||||
void tbf_ul_created();
|
||||
void tbf_ul_freed();
|
||||
void tbf_ul_aborted();
|
||||
void tbf_reused();
|
||||
void tbf_alloc_algo_a();
|
||||
void tbf_alloc_algo_b();
|
||||
void tbf_failed_egprs_only();
|
||||
void rlc_sent();
|
||||
void rlc_resent();
|
||||
void rlc_restarted();
|
||||
void rlc_stalled();
|
||||
void rlc_nacked();
|
||||
void rlc_final_block_resent();
|
||||
void rlc_ass_timedout();
|
||||
void rlc_ass_failed();
|
||||
void rlc_ack_timedout();
|
||||
void rlc_ack_failed();
|
||||
void rlc_rel_timedout();
|
||||
void rlc_late_block();
|
||||
void rlc_sent_dummy();
|
||||
void rlc_sent_control();
|
||||
void rlc_dl_bytes(int bytes);
|
||||
void rlc_dl_payload_bytes(int bytes);
|
||||
void rlc_ul_bytes(int bytes);
|
||||
void rlc_ul_payload_bytes(int bytes);
|
||||
void decode_error();
|
||||
void sba_allocated();
|
||||
void sba_freed();
|
||||
void sba_timedout();
|
||||
void llc_timedout_frame();
|
||||
void llc_dropped_frame();
|
||||
void llc_frame_sched();
|
||||
void llc_dl_bytes(int bytes);
|
||||
void llc_ul_bytes(int bytes);
|
||||
void rach_frame();
|
||||
void rach_frame_11bit();
|
||||
void spb_uplink_first_segment();
|
||||
void spb_uplink_second_segment();
|
||||
void spb_downlink_first_segment();
|
||||
void spb_downlink_second_segment();
|
||||
void immediate_assignment_ul_tbf();
|
||||
void immediate_assignment_reject();
|
||||
void immediate_assignment_dl_tbf();
|
||||
void channel_request_description();
|
||||
void pkt_ul_assignment();
|
||||
void pkt_access_reject();
|
||||
void pkt_dl_assignemnt();
|
||||
void rlc_rcvd_control();
|
||||
void pua_poll_timedout();
|
||||
void pua_poll_failed();
|
||||
void pda_poll_timedout();
|
||||
void pda_poll_failed();
|
||||
void pkt_ul_ack_nack_poll_timedout();
|
||||
void pkt_ul_ack_nack_poll_failed();
|
||||
void pkt_dl_ack_nack_poll_timedout();
|
||||
void pkt_dl_ack_nack_poll_failed();
|
||||
void gprs_dl_cs1();
|
||||
void gprs_dl_cs2();
|
||||
void gprs_dl_cs3();
|
||||
void gprs_dl_cs4();
|
||||
void egprs_dl_mcs1();
|
||||
void egprs_dl_mcs2();
|
||||
void egprs_dl_mcs3();
|
||||
void egprs_dl_mcs4();
|
||||
void egprs_dl_mcs5();
|
||||
void egprs_dl_mcs6();
|
||||
void egprs_dl_mcs7();
|
||||
void egprs_dl_mcs8();
|
||||
void egprs_dl_mcs9();
|
||||
void gprs_ul_cs1();
|
||||
void gprs_ul_cs2();
|
||||
void gprs_ul_cs3();
|
||||
void gprs_ul_cs4();
|
||||
void egprs_ul_mcs1();
|
||||
void egprs_ul_mcs2();
|
||||
void egprs_ul_mcs3();
|
||||
void egprs_ul_mcs4();
|
||||
void egprs_ul_mcs5();
|
||||
void egprs_ul_mcs6();
|
||||
void egprs_ul_mcs7();
|
||||
void egprs_ul_mcs8();
|
||||
void egprs_ul_mcs9();
|
||||
|
||||
void ms_present(int32_t n);
|
||||
int32_t ms_present_get();
|
||||
|
||||
/*
|
||||
* Below for C interface for the VTY
|
||||
*/
|
||||
struct rate_ctr_group *rate_counters() const;
|
||||
struct osmo_stat_item_group *stat_items() const;
|
||||
|
||||
LListHead<gprs_rlcmac_tbf>& ul_tbfs();
|
||||
LListHead<gprs_rlcmac_tbf>& dl_tbfs();
|
||||
private:
|
||||
int m_cur_fn;
|
||||
int m_cur_blk_fn;
|
||||
struct gprs_rlcmac_bts m_bts;
|
||||
PollController m_pollController;
|
||||
SBAController m_sba;
|
||||
struct rate_ctr_group *m_ratectrs;
|
||||
struct osmo_stat_item_group *m_statg;
|
||||
|
||||
GprsMsStorage m_ms_store;
|
||||
|
||||
/* list of uplink TBFs */
|
||||
LListHead<gprs_rlcmac_tbf> m_ul_tbfs;
|
||||
/* list of downlink TBFs */
|
||||
LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
|
||||
|
||||
/* disable copying to avoid slicing */
|
||||
BTS(const BTS&);
|
||||
BTS& operator=(const BTS&);
|
||||
struct osmo_time_cc all_allocated_pdch;
|
||||
};
|
||||
|
||||
inline int BTS::current_frame_number() const
|
||||
{
|
||||
return m_cur_fn;
|
||||
}
|
||||
|
||||
inline SBAController *BTS::sba()
|
||||
{
|
||||
return &m_sba;
|
||||
}
|
||||
|
||||
inline GprsMsStorage &BTS::ms_store()
|
||||
{
|
||||
return m_ms_store;
|
||||
}
|
||||
|
||||
inline GprsMs *BTS::ms_by_tlli(uint32_t tlli, uint32_t old_tlli)
|
||||
{
|
||||
return ms_store().get_ms(tlli, old_tlli);
|
||||
}
|
||||
|
||||
inline GprsMs *BTS::ms_by_imsi(const char *imsi)
|
||||
{
|
||||
return ms_store().get_ms(0, 0, imsi);
|
||||
}
|
||||
|
||||
inline LListHead<gprs_rlcmac_tbf>& BTS::ul_tbfs()
|
||||
{
|
||||
return m_ul_tbfs;
|
||||
}
|
||||
|
||||
inline LListHead<gprs_rlcmac_tbf>& BTS::dl_tbfs()
|
||||
{
|
||||
return m_dl_tbfs;
|
||||
}
|
||||
|
||||
inline struct rate_ctr_group *BTS::rate_counters() const
|
||||
{
|
||||
return m_ratectrs;
|
||||
}
|
||||
|
||||
inline struct osmo_stat_item_group *BTS::stat_items() const
|
||||
{
|
||||
return m_statg;
|
||||
}
|
||||
|
||||
#define CREATE_COUNT_ADD_INLINE(func_name, ctr_name) \
|
||||
inline void BTS::func_name(int inc) {\
|
||||
rate_ctr_add(&m_ratectrs->ctr[ctr_name], inc); \
|
||||
}
|
||||
|
||||
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
|
||||
inline void BTS::func_name() {\
|
||||
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
|
||||
}
|
||||
|
||||
CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED)
|
||||
CREATE_COUNT_INLINE(tbf_dl_aborted, CTR_TBF_DL_ABORTED)
|
||||
CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
|
||||
CREATE_COUNT_INLINE(tbf_ul_aborted, CTR_TBF_UL_ABORTED)
|
||||
CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED)
|
||||
CREATE_COUNT_INLINE(tbf_alloc_algo_a, CTR_TBF_ALLOC_ALGO_A)
|
||||
CREATE_COUNT_INLINE(tbf_alloc_algo_b, CTR_TBF_ALLOC_ALGO_B)
|
||||
CREATE_COUNT_INLINE(tbf_failed_egprs_only, CTR_TBF_FAILED_EGPRS_ONLY)
|
||||
CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT)
|
||||
CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
|
||||
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
|
||||
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
|
||||
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
|
||||
CREATE_COUNT_INLINE(rlc_final_block_resent, CTR_RLC_FINAL_BLOCK_RESENT);
|
||||
CREATE_COUNT_INLINE(rlc_ass_timedout, CTR_RLC_ASS_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_ass_failed, CTR_RLC_ASS_FAILED);
|
||||
CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
|
||||
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
|
||||
CREATE_COUNT_INLINE(rlc_sent_dummy, CTR_RLC_SENT_DUMMY);
|
||||
CREATE_COUNT_INLINE(rlc_sent_control, CTR_RLC_SENT_CONTROL);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_dl_bytes, CTR_RLC_DL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_dl_payload_bytes, CTR_RLC_DL_PAYLOAD_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_ul_bytes, CTR_RLC_UL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_ul_payload_bytes, CTR_RLC_UL_PAYLOAD_BYTES);
|
||||
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
|
||||
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
|
||||
CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
|
||||
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
|
||||
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
|
||||
CREATE_COUNT_ADD_INLINE(llc_dl_bytes, CTR_LLC_DL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(llc_ul_bytes, CTR_LLC_UL_BYTES);
|
||||
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
|
||||
CREATE_COUNT_INLINE(rach_frame_11bit, CTR_11BIT_RACH_REQUESTS);
|
||||
CREATE_COUNT_INLINE(spb_uplink_first_segment, CTR_SPB_UL_FIRST_SEGMENT);
|
||||
CREATE_COUNT_INLINE(spb_uplink_second_segment, CTR_SPB_UL_SECOND_SEGMENT);
|
||||
CREATE_COUNT_INLINE(spb_downlink_first_segment, CTR_SPB_DL_FIRST_SEGMENT);
|
||||
CREATE_COUNT_INLINE(spb_downlink_second_segment, CTR_SPB_DL_SECOND_SEGMENT);
|
||||
CREATE_COUNT_INLINE(immediate_assignment_ul_tbf, CTR_IMMEDIATE_ASSIGN_UL_TBF);
|
||||
CREATE_COUNT_INLINE(immediate_assignment_reject, CTR_IMMEDIATE_ASSIGN_REJ);
|
||||
CREATE_COUNT_INLINE(immediate_assignment_dl_tbf, CTR_IMMEDIATE_ASSIGN_DL_TBF);
|
||||
CREATE_COUNT_INLINE(channel_request_description, CTR_CHANNEL_REQUEST_DESCRIPTION);
|
||||
CREATE_COUNT_INLINE(pkt_ul_assignment, CTR_PKT_UL_ASSIGNMENT);
|
||||
CREATE_COUNT_INLINE(pkt_access_reject, CTR_PKT_ACCESS_REJ);
|
||||
CREATE_COUNT_INLINE(pkt_dl_assignemnt, CTR_PKT_DL_ASSIGNMENT);
|
||||
CREATE_COUNT_INLINE(rlc_rcvd_control, CTR_RLC_RECV_CONTROL);
|
||||
CREATE_COUNT_INLINE(pua_poll_timedout, CTR_PUA_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pua_poll_failed, CTR_PUA_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(pda_poll_timedout, CTR_PDA_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pda_poll_failed, CTR_PDA_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_timedout, CTR_PUAN_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_failed, CTR_PUAN_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_timedout, CTR_PDAN_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_failed, CTR_PDAN_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs1, CTR_GPRS_DL_CS1);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs2, CTR_GPRS_DL_CS2);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs3, CTR_GPRS_DL_CS3);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs4, CTR_GPRS_DL_CS4);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs1, CTR_EGPRS_DL_MCS1);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs2, CTR_EGPRS_DL_MCS2);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs3, CTR_EGPRS_DL_MCS3);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs4, CTR_EGPRS_DL_MCS4);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs5, CTR_EGPRS_DL_MCS5);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs6, CTR_EGPRS_DL_MCS6);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs7, CTR_EGPRS_DL_MCS7);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs8, CTR_EGPRS_DL_MCS8);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs9, CTR_EGPRS_DL_MCS9);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs1, CTR_GPRS_UL_CS1);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs2, CTR_GPRS_UL_CS2);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs3, CTR_GPRS_UL_CS3);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs4, CTR_GPRS_UL_CS4);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs1, CTR_EGPRS_UL_MCS1);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs2, CTR_EGPRS_UL_MCS2);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs3, CTR_EGPRS_UL_MCS3);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs4, CTR_EGPRS_UL_MCS4);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs5, CTR_EGPRS_UL_MCS5);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs6, CTR_EGPRS_UL_MCS6);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs7, CTR_EGPRS_UL_MCS7);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs8, CTR_EGPRS_UL_MCS8);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs9, CTR_EGPRS_UL_MCS9);
|
||||
|
||||
#undef CREATE_COUNT_INLINE
|
||||
|
||||
#define CREATE_STAT_INLINE(func_name, func_name_get, stat_name) \
|
||||
inline void BTS::func_name(int32_t val) {\
|
||||
osmo_stat_item_set(m_statg->items[stat_name], val); \
|
||||
} \
|
||||
inline int32_t BTS::func_name_get() {\
|
||||
return osmo_stat_item_get_last(m_statg->items[stat_name]); \
|
||||
}
|
||||
|
||||
CREATE_STAT_INLINE(ms_present, ms_present_get, STAT_MS_PRESENT);
|
||||
|
||||
#undef CREATE_STAT_INLINE
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct gprs_rlcmac_bts *bts_main_data();
|
||||
struct rate_ctr_group *bts_main_data_stats();
|
||||
struct osmo_stat_item_group *bts_main_data_stat_items();
|
||||
#ifdef __cplusplus
|
||||
|
||||
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
|
||||
408
src/coding_scheme.c
Normal file
408
src/coding_scheme.c
Normal file
@@ -0,0 +1,408 @@
|
||||
/* coding_scheme.c
|
||||
*
|
||||
* Copyright (C) 2019 by sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "coding_scheme.h"
|
||||
|
||||
const struct value_string mcs_names[] = {
|
||||
{ UNKNOWN, "UNKNOWN" },
|
||||
{ CS1, "CS-1" },
|
||||
{ CS2, "CS-2" },
|
||||
{ CS3, "CS-3" },
|
||||
{ CS4, "CS-4" },
|
||||
{ MCS1, "MCS-1" },
|
||||
{ MCS2, "MCS-2" },
|
||||
{ MCS3, "MCS-3" },
|
||||
{ MCS4, "MCS-4" },
|
||||
{ MCS5, "MCS-5" },
|
||||
{ MCS6, "MCS-6" },
|
||||
{ MCS7, "MCS-7" },
|
||||
{ MCS8, "MCS-8" },
|
||||
{ MCS9, "MCS-9" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum Family {
|
||||
FAMILY_INVALID,
|
||||
FAMILY_A,
|
||||
FAMILY_B,
|
||||
FAMILY_C,
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
uint8_t bytes;
|
||||
uint8_t ext_bits;
|
||||
uint8_t data_header_bits;
|
||||
} uplink, downlink;
|
||||
uint8_t data_bytes;
|
||||
uint8_t optional_padding_bits;
|
||||
enum HeaderType data_hdr;
|
||||
enum Family family;
|
||||
} mcs_info[NUM_SCHEMES] = {
|
||||
{{0, 0}, {0, 0}, 0, 0,
|
||||
HEADER_INVALID, FAMILY_INVALID},
|
||||
{{23, 0}, {23, 0}, 20, 0,
|
||||
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||
{{33, 7}, {33, 7}, 30, 0,
|
||||
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||
{{39, 3}, {39, 3}, 36, 0,
|
||||
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||
{{53, 7}, {53, 7}, 50, 0,
|
||||
HEADER_GPRS_DATA, FAMILY_INVALID},
|
||||
|
||||
{{26, 1}, {26, 1}, 22, 0,
|
||||
HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
|
||||
{{32, 1}, {32, 1}, 28, 0,
|
||||
HEADER_EGPRS_DATA_TYPE_3, FAMILY_B},
|
||||
{{41, 1}, {41, 1}, 37, 48,
|
||||
HEADER_EGPRS_DATA_TYPE_3, FAMILY_A},
|
||||
{{48, 1}, {48, 1}, 44, 0,
|
||||
HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
|
||||
|
||||
{{60, 7}, {59, 6}, 56, 0,
|
||||
HEADER_EGPRS_DATA_TYPE_2, FAMILY_B},
|
||||
{{78, 7}, {77, 6}, 74, 48,
|
||||
HEADER_EGPRS_DATA_TYPE_2, FAMILY_A},
|
||||
{{118, 2}, {117, 4}, 56, 0,
|
||||
HEADER_EGPRS_DATA_TYPE_1, FAMILY_B},
|
||||
{{142, 2}, {141, 4}, 68, 0,
|
||||
HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
|
||||
{{154, 2}, {153, 4}, 74, 0,
|
||||
HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
|
||||
};
|
||||
|
||||
const char *mcs_name(enum CodingScheme val) {
|
||||
return get_value_string(mcs_names, val);
|
||||
}
|
||||
|
||||
bool mcs_is_gprs(enum CodingScheme cs)
|
||||
{
|
||||
return CS1 <= cs && cs <= CS4;
|
||||
}
|
||||
|
||||
bool mcs_is_edge(enum CodingScheme cs)
|
||||
{
|
||||
return MCS1 <= cs && cs <= MCS9;
|
||||
}
|
||||
|
||||
bool mcs_is_edge_gmsk(enum CodingScheme cs)
|
||||
{
|
||||
if (mcs_is_edge(cs))
|
||||
return cs <= MCS4;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return 3GPP TS 44.060 §12.10d (EDGE) or Table 11.2.28.2 (GPRS) Channel Coding Command value */
|
||||
uint8_t mcs_chan_code(enum CodingScheme cs)
|
||||
{
|
||||
if (mcs_is_gprs(cs))
|
||||
return cs - CS1;
|
||||
|
||||
if (mcs_is_edge(cs))
|
||||
return cs - MCS1;
|
||||
|
||||
/* Defaults to (M)CS1 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum CodingScheme mcs_get_by_size_ul(unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 23: return CS1;
|
||||
case 27: return MCS1;
|
||||
case 33: return MCS2;
|
||||
case 34: return CS2;
|
||||
case 40: return CS3;
|
||||
case 42: return MCS3;
|
||||
case 49: return MCS4;
|
||||
case 54: return CS4;
|
||||
case 61: return MCS5;
|
||||
case 79: return MCS6;
|
||||
case 119: return MCS7;
|
||||
case 143: return MCS8;
|
||||
case 155: return MCS9;
|
||||
default: return UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
enum CodingScheme mcs_get_gprs_by_num(unsigned num)
|
||||
{
|
||||
if (num < 1 || num > 4)
|
||||
return UNKNOWN;
|
||||
return CS1 + (num - 1);
|
||||
}
|
||||
|
||||
enum CodingScheme mcs_get_egprs_by_num(unsigned num)
|
||||
{
|
||||
if (num < 1 || num > 9)
|
||||
return UNKNOWN;
|
||||
return MCS1 + (num - 1);
|
||||
}
|
||||
|
||||
bool mcs_is_valid(enum CodingScheme cs)
|
||||
{
|
||||
return UNKNOWN < cs && cs <= MCS9;
|
||||
}
|
||||
|
||||
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case GPRS: return mcs_is_gprs(cs);
|
||||
case EGPRS_GMSK: return mcs_is_edge_gmsk(cs);
|
||||
case EGPRS: return mcs_is_edge(cs);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o)
|
||||
{
|
||||
return (mcs_is_gprs(cs) && mcs_is_gprs(o)) || (mcs_is_edge(cs) && mcs_is_edge(o));
|
||||
}
|
||||
|
||||
uint8_t mcs_size_ul(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].uplink.bytes + (mcs_spare_bits_ul(cs) ? 1 : 0);
|
||||
}
|
||||
|
||||
uint8_t mcs_size_dl(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].downlink.bytes + (mcs_spare_bits_dl(cs) ? 1 : 0);
|
||||
}
|
||||
|
||||
uint8_t mcs_used_size_ul(enum CodingScheme cs)
|
||||
{
|
||||
if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
|
||||
return mcs_info[cs].uplink.bytes;
|
||||
else
|
||||
return mcs_size_ul(cs);
|
||||
}
|
||||
|
||||
uint8_t mcs_used_size_dl(enum CodingScheme cs)
|
||||
{
|
||||
if (mcs_info[cs].data_hdr == HEADER_GPRS_DATA)
|
||||
return mcs_info[cs].downlink.bytes;
|
||||
else
|
||||
return mcs_size_dl(cs);
|
||||
}
|
||||
|
||||
uint8_t mcs_max_bytes_ul(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].uplink.bytes;
|
||||
}
|
||||
|
||||
uint8_t mcs_max_bytes_dl(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].downlink.bytes;
|
||||
}
|
||||
|
||||
uint8_t mcs_spare_bits_ul(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].uplink.ext_bits;
|
||||
}
|
||||
|
||||
uint8_t mcs_spare_bits_dl(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].downlink.ext_bits;
|
||||
}
|
||||
|
||||
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].data_bytes;
|
||||
}
|
||||
|
||||
uint8_t mcs_opt_padding_bits(enum CodingScheme cs)
|
||||
{
|
||||
return mcs_info[cs].optional_padding_bits;
|
||||
}
|
||||
|
||||
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode)
|
||||
{
|
||||
if (!mcs_is_compat_kind(*cs, mode))
|
||||
/* This should not happen. TODO: Use assert? */
|
||||
return;
|
||||
|
||||
enum CodingScheme new_cs = *cs + 1;
|
||||
if (!mcs_is_compat_kind(new_cs, mode))
|
||||
/* Clipping, do not change the value */
|
||||
return;
|
||||
|
||||
*cs = new_cs;
|
||||
}
|
||||
|
||||
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode)
|
||||
{
|
||||
if (!mcs_is_compat_kind(*cs, mode))
|
||||
/* This should not happen. TODO: Use assert? */
|
||||
return;
|
||||
|
||||
enum CodingScheme new_cs = *cs - 1;
|
||||
if (!mcs_is_compat_kind(new_cs, mode))
|
||||
/* Clipping, do not change the value */
|
||||
return;
|
||||
|
||||
*cs = new_cs;
|
||||
}
|
||||
|
||||
void mcs_inc(enum CodingScheme *cs)
|
||||
{
|
||||
if (mcs_is_gprs(*cs) && *cs == CS4)
|
||||
return;
|
||||
|
||||
if (mcs_is_edge(*cs) && *cs == MCS9)
|
||||
return;
|
||||
|
||||
if (!mcs_is_valid(*cs))
|
||||
return;
|
||||
|
||||
*cs = *cs + 1;
|
||||
}
|
||||
|
||||
void mcs_dec(enum CodingScheme *cs)
|
||||
{
|
||||
if (mcs_is_gprs(*cs) && *cs == CS1)
|
||||
return;
|
||||
|
||||
if (mcs_is_edge(*cs) && *cs == MCS1)
|
||||
return;
|
||||
|
||||
if (!mcs_is_valid(*cs))
|
||||
return;
|
||||
|
||||
*cs = *cs - 1;
|
||||
}
|
||||
|
||||
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o)
|
||||
{
|
||||
if (cs == o)
|
||||
return true;
|
||||
|
||||
if (mcs_info[cs].family == FAMILY_INVALID)
|
||||
return false;
|
||||
|
||||
return mcs_info[cs].family == mcs_info[o].family;
|
||||
}
|
||||
|
||||
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing)
|
||||
{
|
||||
switch (*cs) {
|
||||
case MCS7: *need_stuffing = false; *cs = MCS5; break;
|
||||
case MCS8: *need_stuffing = true; *cs = MCS6; break;
|
||||
case MCS9: *need_stuffing = false; *cs = MCS6; break;
|
||||
default: *need_stuffing = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
uint8_t data_header_bits;
|
||||
} uplink, downlink;
|
||||
uint8_t data_block_header_bits;
|
||||
uint8_t num_blocks;
|
||||
const char *name;
|
||||
} hdr_type_info[NUM_HEADER_TYPES] = {
|
||||
{ { 0 }, { 0 }, 0, 0, "INVALID" },
|
||||
{ { 1 * 8 + 0 }, { 1 * 8 + 0 }, 0, 0, "CONTROL" },
|
||||
{ { 3 * 8 + 0 }, { 3 * 8 + 0 }, 0, 1, "GPRS_DATA" },
|
||||
{ { 5 * 8 + 6 }, { 5 * 8 + 0 }, 2, 2, "EGPRS_DATA_TYPE1" },
|
||||
{ { 4 * 8 + 5 }, { 3 * 8 + 4 }, 2, 1, "EGPRS_DATA_TYPE2" },
|
||||
{ { 3 * 8 + 7 }, { 3 * 8 + 7 }, 2, 1, "EGPRS_DATA_TYPE3" },
|
||||
};
|
||||
|
||||
enum HeaderType mcs_header_type(enum CodingScheme mcs)
|
||||
{
|
||||
return mcs_info[mcs].data_hdr;
|
||||
}
|
||||
|
||||
uint8_t num_data_blocks(enum HeaderType ht)
|
||||
{
|
||||
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||
return hdr_type_info[ht].num_blocks;
|
||||
}
|
||||
|
||||
uint8_t num_data_header_bits_UL(enum HeaderType ht)
|
||||
{
|
||||
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||
return hdr_type_info[ht].uplink.data_header_bits;
|
||||
}
|
||||
|
||||
uint8_t num_data_header_bits_DL(enum HeaderType ht)
|
||||
{
|
||||
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||
return hdr_type_info[ht].downlink.data_header_bits;
|
||||
}
|
||||
|
||||
uint8_t num_data_block_header_bits(enum HeaderType ht)
|
||||
{
|
||||
OSMO_ASSERT(ht < NUM_HEADER_TYPES);
|
||||
return hdr_type_info[ht].data_block_header_bits;
|
||||
}
|
||||
|
||||
const struct value_string mode_names[] = {
|
||||
{ GPRS, "GPRS" },
|
||||
{ EGPRS_GMSK, "EGPRS_GMSK-only"},
|
||||
{ EGPRS, "EGPRS"},
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *mode_name(enum mcs_kind val) {
|
||||
return get_value_string(mode_names, val);
|
||||
}
|
||||
|
||||
/* FIXME: take into account padding and special cases of commanded MCS (MCS-6-9 and MCS-5-7) */
|
||||
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit)
|
||||
{
|
||||
OSMO_ASSERT(mcs_is_edge(initial_mcs));
|
||||
OSMO_ASSERT(mcs_is_edge(commanded_mcs));
|
||||
OSMO_ASSERT(NUM_SCHEMES - MCS1 == 9);
|
||||
|
||||
if (resegment_bit) { /* 3GPP TS 44.060 Table 8.1.1.1, reflected over antidiagonal */
|
||||
enum CodingScheme egprs_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
|
||||
{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
|
||||
{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
|
||||
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
|
||||
{ MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
|
||||
{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
|
||||
{ MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8 },
|
||||
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9 },
|
||||
};
|
||||
return egprs_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
|
||||
} else { /* 3GPP TS 44.060 Table 8.1.1.2, reflected over antidiagonal */
|
||||
enum CodingScheme egprs_no_reseg[NUM_SCHEMES - MCS1][NUM_SCHEMES - MCS1] = {
|
||||
{ MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1 },
|
||||
{ MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2 },
|
||||
{ MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3 },
|
||||
{ MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4 },
|
||||
{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
|
||||
{ MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7 },
|
||||
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8 },
|
||||
{ MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9 },
|
||||
};
|
||||
return egprs_no_reseg[mcs_chan_code(initial_mcs)][mcs_chan_code(commanded_mcs)];
|
||||
}
|
||||
}
|
||||
99
src/coding_scheme.h
Normal file
99
src/coding_scheme.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/* coding_scheme.h
|
||||
*
|
||||
* Copyright (C) 2015-2019 by sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
enum CodingScheme {
|
||||
UNKNOWN,
|
||||
/* GPRS Coding Schemes: */
|
||||
CS1, CS2, CS3, CS4,
|
||||
/* EDGE/EGPRS Modulation and Coding Schemes: */
|
||||
MCS1, MCS2, MCS3, MCS4, MCS5, MCS6, MCS7, MCS8, MCS9,
|
||||
NUM_SCHEMES
|
||||
};
|
||||
|
||||
enum mcs_kind {
|
||||
GPRS,
|
||||
EGPRS_GMSK,
|
||||
EGPRS,
|
||||
};
|
||||
|
||||
enum egprs_arq_type {
|
||||
EGPRS_ARQ1 = 0,
|
||||
EGPRS_ARQ2 = 1
|
||||
};
|
||||
|
||||
extern const struct value_string mcs_names[];
|
||||
const char *mcs_name(enum CodingScheme val);
|
||||
enum CodingScheme get_retx_mcs(enum CodingScheme initial_mcs, enum CodingScheme commanded_mcs, bool resegment_bit);
|
||||
|
||||
bool mcs_is_gprs(enum CodingScheme cs);
|
||||
bool mcs_is_edge(enum CodingScheme cs);
|
||||
bool mcs_is_edge_gmsk(enum CodingScheme cs);
|
||||
|
||||
uint8_t mcs_chan_code(enum CodingScheme cs);
|
||||
|
||||
enum CodingScheme mcs_get_by_size_ul(unsigned size);
|
||||
enum CodingScheme mcs_get_gprs_by_num(unsigned num);
|
||||
enum CodingScheme mcs_get_egprs_by_num(unsigned num);
|
||||
bool mcs_is_valid(enum CodingScheme cs);
|
||||
bool mcs_is_compat(enum CodingScheme cs, enum CodingScheme o);
|
||||
bool mcs_is_compat_kind(enum CodingScheme cs, enum mcs_kind mode);
|
||||
|
||||
uint8_t mcs_size_ul(enum CodingScheme cs);
|
||||
uint8_t mcs_size_dl(enum CodingScheme cs);
|
||||
uint8_t mcs_used_size_ul(enum CodingScheme cs);
|
||||
uint8_t mcs_used_size_dl(enum CodingScheme cs);
|
||||
uint8_t mcs_max_bytes_ul(enum CodingScheme cs);
|
||||
uint8_t mcs_max_bytes_dl(enum CodingScheme cs);
|
||||
uint8_t mcs_spare_bits_ul(enum CodingScheme cs);
|
||||
uint8_t mcs_spare_bits_dl(enum CodingScheme cs);
|
||||
uint8_t mcs_max_data_block_bytes(enum CodingScheme cs);
|
||||
uint8_t mcs_opt_padding_bits(enum CodingScheme cs);
|
||||
|
||||
void mcs_inc_kind(enum CodingScheme *cs, enum mcs_kind mode);
|
||||
void mcs_dec_kind(enum CodingScheme *cs, enum mcs_kind mode);
|
||||
void mcs_inc(enum CodingScheme *cs);
|
||||
void mcs_dec(enum CodingScheme *cs);
|
||||
|
||||
bool mcs_is_family_compat(enum CodingScheme cs, enum CodingScheme o);
|
||||
void mcs_dec_to_single_block(enum CodingScheme *cs, bool *need_stuffing);
|
||||
|
||||
enum HeaderType {
|
||||
HEADER_INVALID,
|
||||
HEADER_GPRS_CONTROL,
|
||||
HEADER_GPRS_DATA,
|
||||
HEADER_EGPRS_DATA_TYPE_1,
|
||||
HEADER_EGPRS_DATA_TYPE_2,
|
||||
HEADER_EGPRS_DATA_TYPE_3,
|
||||
NUM_HEADER_TYPES
|
||||
};
|
||||
|
||||
enum HeaderType mcs_header_type(enum CodingScheme mcs);
|
||||
|
||||
uint8_t num_data_blocks(enum HeaderType ht);
|
||||
uint8_t num_data_header_bits_UL(enum HeaderType ht);
|
||||
uint8_t num_data_header_bits_DL(enum HeaderType ht);
|
||||
uint8_t num_data_block_header_bits(enum HeaderType ht);
|
||||
|
||||
const char *mode_name(enum mcs_kind val);
|
||||
107
src/csn1.c
Normal file
107
src/csn1.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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.
|
||||
*
|
||||
* 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 <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;
|
||||
}
|
||||
2669
src/csn1.cpp
2669
src/csn1.cpp
File diff suppressed because it is too large
Load Diff
220
src/csn1.h
220
src/csn1.h
@@ -28,15 +28,8 @@
|
||||
#ifndef _PACKET_CSN1_H_
|
||||
#define _PACKET_CSN1_H_
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/bitvec.h>
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
//#define max(a,b) (((a)>(b))?(a):(b))
|
||||
#include "wireshark_compat.h"
|
||||
|
||||
/* Error codes */
|
||||
#define CSN_OK 0
|
||||
@@ -51,16 +44,6 @@ extern "C" {
|
||||
#define CSN_ERROR_MESSAGE_TOO_LONG -9
|
||||
#define CSN_ERROR_ -10
|
||||
|
||||
#define FALSE (0)
|
||||
#define TRUE (1)
|
||||
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;
|
||||
|
||||
@@ -77,6 +60,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
|
||||
@@ -86,7 +70,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,
|
||||
@@ -194,24 +180,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;
|
||||
|
||||
@@ -234,16 +217,16 @@ void csnStreamInit(csnStream_t* ar,gint BitOffset,gint BitCount);
|
||||
* RETURNS: int Number of bits left to be unpacked. Negative Error code if failed to unpack all bits
|
||||
******************************************************************************/
|
||||
|
||||
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, FALSE, "", {(StreamSerializeFcn_t)0}} };
|
||||
{CSN_END, 0, {0}, 0, FALSE, "", 0, NULL} };
|
||||
|
||||
/******************************************************************************
|
||||
* CSN_ERROR(Par1, Par2, Par3)
|
||||
@@ -254,7 +237,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, FALSE, _Text, {(StreamSerializeFcn_t)0}}
|
||||
{CSN_TRAP_ERROR, _ERRCODE, {_Text}, 0, FALSE, _Text, 0, NULL}
|
||||
|
||||
/******************************************************************************
|
||||
* M_BIT(Par1, Par2)
|
||||
@@ -263,7 +246,7 @@ 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), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||
|
||||
/******************************************************************************
|
||||
* M_BIT_OR_NULL(Par1, Par2)
|
||||
@@ -273,11 +256,11 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Covers the case {null | 0 | 1}
|
||||
*****************************************************************************/
|
||||
#define M_BIT_OR_NULL(_STRUCT, _MEMBER)\
|
||||
{CSN_BIT, 0, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{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
|
||||
@@ -285,37 +268,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), FALSE, #_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), FALSE, #_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), TRUE, #_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), TRUE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{CSN_NEXT_EXIST_LH, _NoOfExisting, {(void*)1}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, 0, NULL}
|
||||
|
||||
/******************************************************************************
|
||||
* M_UINT(Par1, Par2, Par3)
|
||||
@@ -325,7 +308,7 @@ 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), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||
|
||||
/******************************************************************************
|
||||
* M_UINT_OR_NULL(Par1, Par2, Par3)
|
||||
@@ -335,17 +318,17 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Covers the case {null | 0 | 1 < IE >}
|
||||
*****************************************************************************/
|
||||
#define M_UINT_OR_NULL(_STRUCT, _MEMBER, _BITS)\
|
||||
{CSN_UINT, _BITS, {0}, offsetof(_STRUCT, _MEMBER), TRUE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{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), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{CSN_UINT_LH, _BITS, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||
|
||||
/******************************************************************************
|
||||
* M_UINT_OFFSET(Par1, Par2, Par3, Par4)
|
||||
@@ -356,7 +339,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), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{CSN_UINT_OFFSET, _BITS, {(void*)_OFFSET}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||
|
||||
/******************************************************************************
|
||||
* M_UINT_ARRAY(Par1, Par2, Par3, Par4)
|
||||
@@ -367,7 +350,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), FALSE, #_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)
|
||||
@@ -379,7 +362,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), FALSE, #_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)
|
||||
@@ -390,7 +373,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par4: offset that is added to the Par3 to get the actual size of the array
|
||||
*****************************************************************************/
|
||||
#define M_VAR_ARRAY(_STRUCT, _MEMBER, _ElementCountField, _OFFSET)\
|
||||
{CSN_VARIABLE_ARRAY, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_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)
|
||||
@@ -401,32 +384,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), FALSE, #_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), FALSE, #_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), FALSE, #_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)
|
||||
@@ -437,7 +420,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), FALSE, #_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)
|
||||
@@ -449,7 +432,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
* Par4: will hold the number of element in the array after unpacking
|
||||
*****************************************************************************/
|
||||
#define M_REC_TARRAY(_STRUCT, _MEMBER, _MEMBER_TYPE, _ElementCountField)\
|
||||
{CSN_RECURSIVE_TARRAY, offsetof(_STRUCT, _ElementCountField), {(void*)CSNDESCR_##_MEMBER_TYPE}, offsetof(_STRUCT, _MEMBER), FALSE, #_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)
|
||||
@@ -457,7 +440,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), FALSE, #_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)
|
||||
@@ -465,7 +448,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), FALSE, #_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)
|
||||
@@ -476,134 +459,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), FALSE, #_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), FALSE, "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), FALSE, "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), FALSE, #_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, FALSE, #_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.
|
||||
* 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
|
||||
* 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, _LENGTH_LEN, _SERIALIZEFCN)\
|
||||
{CSN_SERIALIZE, _LENGTH_LEN, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {_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), FALSE, "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), FALSE, #_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), FALSE, #_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), FALSE, #_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), FALSE, #_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), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
{CSN_LEFT_ALIGNED_VAR_BMP_1, _OFFSET, {(void*)offsetof(_STRUCT, _ElementCountField)}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, 0, NULL}
|
||||
|
||||
/* todo: dissect padding bits looking for unexpected extensions */
|
||||
#define M_PADDING_BITS(_STRUCT)\
|
||||
{CSN_PADDING_BITS, 0, {0}, 0, TRUE, "Padding", {(StreamSerializeFcn_t)0}}
|
||||
{CSN_PADDING_BITS, 0, {0}, 0, TRUE, "Padding", 0, NULL}
|
||||
|
||||
#define M_NULL(_STRUCT, _MEMBER)\
|
||||
{CSN_NULL, 0, {0}, offsetof(_STRUCT, _MEMBER), FALSE, #_MEMBER, {(StreamSerializeFcn_t)0}}
|
||||
#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), FALSE, "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), FALSE, "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_*/
|
||||
|
||||
1428
src/csn1_dec.c
Normal file
1428
src/csn1_dec.c
Normal file
File diff suppressed because it is too large
Load Diff
1305
src/csn1_enc.c
Normal file
1305
src/csn1_enc.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -77,7 +77,7 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
|
||||
/* 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 extention octet: LI=%d, E=%d, count=%d\n",
|
||||
"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) {
|
||||
@@ -92,7 +92,7 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
|
||||
}
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||
"extention octet: LI=%d, E=%d, count=%d\n",
|
||||
"extension octet: LI=%d, E=%d, count=%d\n",
|
||||
li->li, li->e, num_chunks);
|
||||
num_chunks += 1;
|
||||
|
||||
@@ -168,7 +168,7 @@ static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
|
||||
chunks[num_chunks].is_complete = li->li || is_last_block;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||
"extention octet: LI=%d, M=%d, E=%d, count=%d\n",
|
||||
"extension octet: LI=%d, M=%d, E=%d, count=%d\n",
|
||||
li->li, li->m, li->e, num_chunks);
|
||||
num_chunks += 1;
|
||||
|
||||
@@ -189,7 +189,7 @@ static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
|
||||
}
|
||||
|
||||
int Decoding::rlc_data_from_ul_data(
|
||||
const struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
const struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
|
||||
const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
|
||||
uint32_t *tlli)
|
||||
{
|
||||
@@ -215,7 +215,7 @@ int Decoding::rlc_data_from_ul_data(
|
||||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
} else if (cs.isEgprs()) {
|
||||
} 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,
|
||||
@@ -240,7 +240,7 @@ int Decoding::rlc_data_from_ul_data(
|
||||
}
|
||||
|
||||
memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
|
||||
if (cs.isGprs())
|
||||
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);
|
||||
@@ -282,8 +282,8 @@ int Decoding::rlc_data_from_ul_data(
|
||||
offs += chunks[i].length;
|
||||
if (offs > data_len) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block "
|
||||
"border, chunk idx: %d, size: %d\n",
|
||||
i, chunks[i].length);
|
||||
"border, chunk idx: %d, offset: %u, size: %d, data_len: %u\n",
|
||||
i, offs, chunks[i].length, data_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ int Decoding::rlc_data_from_ul_data(
|
||||
return num_chunks;
|
||||
}
|
||||
|
||||
uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||
uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -306,7 +306,7 @@ uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||
uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -326,18 +326,6 @@ uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t
|
||||
* The index of the array is the bit position in the rbb
|
||||
* (show_rbb[63] relates to BSN ssn-1)
|
||||
*/
|
||||
void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
|
||||
{
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint8_t bit;
|
||||
|
||||
bit = !!(rbb[i/8] & (1<<(7-i%8)));
|
||||
show_rbb[i] = bit ? 'R' : 'I';
|
||||
}
|
||||
|
||||
show_rbb[64] = '\0';
|
||||
}
|
||||
|
||||
void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -351,26 +339,26 @@ void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, GprsCodingScheme cs)
|
||||
const uint8_t *data, enum CodingScheme cs)
|
||||
{
|
||||
unsigned int cur_bit = 0;
|
||||
switch(cs.headerTypeData()) {
|
||||
case GprsCodingScheme::HEADER_GPRS_DATA :
|
||||
switch(mcs_header_type(cs)) {
|
||||
case HEADER_GPRS_DATA :
|
||||
cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
|
||||
break;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3 :
|
||||
case HEADER_EGPRS_DATA_TYPE_3 :
|
||||
cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs);
|
||||
break;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2 :
|
||||
case HEADER_EGPRS_DATA_TYPE_2 :
|
||||
cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs);
|
||||
break;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1 :
|
||||
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",
|
||||
cs.name());
|
||||
mcs_name(cs));
|
||||
return -ENOTSUP;
|
||||
};
|
||||
|
||||
@@ -380,7 +368,7 @@ int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_3(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs)
|
||||
const enum CodingScheme &cs)
|
||||
{
|
||||
int punct, punct2, with_padding, cps;
|
||||
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||
@@ -414,7 +402,7 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_3(
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
cur_bit += mcs_max_data_block_bytes(cs) * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
@@ -422,7 +410,7 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_3(
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_2(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs)
|
||||
const enum CodingScheme &cs)
|
||||
{
|
||||
const struct gprs_rlc_ul_header_egprs_2 *egprs2;
|
||||
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||
@@ -458,14 +446,14 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_2(
|
||||
cur_bit += 2;
|
||||
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
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 GprsCodingScheme &cs)
|
||||
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;
|
||||
@@ -517,13 +505,13 @@ int Decoding::rlc_parse_ul_data_header_egprs_type_1(
|
||||
rlc->block_info[1].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
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 GprsCodingScheme &cs)
|
||||
const uint8_t *data, const enum CodingScheme &cs)
|
||||
{
|
||||
const struct rlc_ul_header *gprs;
|
||||
unsigned int cur_bit = 0;
|
||||
@@ -547,7 +535,7 @@ int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
|
||||
rlc->block_info[0].spb = 0;
|
||||
cur_bit += rlc->data_offs_bits[0];
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
cur_bit += mcs_max_data_block_bytes(cs) * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
@@ -19,13 +19,23 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gsm_rlcmac.h>
|
||||
#include "rlc.h"
|
||||
#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 */
|
||||
@@ -37,31 +47,28 @@ public:
|
||||
|
||||
static int rlc_data_from_ul_data(
|
||||
const struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, const uint8_t *data, RlcData *chunks,
|
||||
enum CodingScheme cs, const uint8_t *data, RlcData *chunks,
|
||||
unsigned int chunks_size, uint32_t *tlli);
|
||||
static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
|
||||
static uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
|
||||
|
||||
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
|
||||
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
|
||||
static int rlc_parse_ul_data_header_egprs_type_3(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
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 GprsCodingScheme &cs);
|
||||
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 GprsCodingScheme &cs);
|
||||
const enum CodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header_gprs(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
const enum CodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, GprsCodingScheme cs);
|
||||
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,
|
||||
@@ -79,3 +86,16 @@ public:
|
||||
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
|
||||
|
||||
@@ -449,6 +449,7 @@ const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||
/* 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
|
||||
@@ -456,6 +457,7 @@ const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||
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)
|
||||
@@ -469,6 +471,9 @@ static int search_runlen(
|
||||
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++;
|
||||
@@ -498,7 +503,7 @@ int egprs_compress::decompress_crbb(
|
||||
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;
|
||||
@@ -509,7 +514,7 @@ int egprs_compress::decompress_crbb(
|
||||
int rc = 0;
|
||||
egprs_compress *compress = instance();
|
||||
|
||||
while (compress_bmap_len > 0) {
|
||||
while (remaining_bmap_len > 0) {
|
||||
if (start) {
|
||||
data = 0xff;
|
||||
list = compress->ones_list;
|
||||
@@ -517,7 +522,7 @@ int egprs_compress::decompress_crbb(
|
||||
data = 0x00;
|
||||
list = compress->zeros_list;
|
||||
}
|
||||
rc = search_runlen(list, orig_crbb_buf,
|
||||
rc = search_runlen(list, orig_crbb_buf, compress_bmap_len,
|
||||
bit_pos, &nbits, &run_length);
|
||||
if (rc == -1)
|
||||
return -1;
|
||||
@@ -525,6 +530,7 @@ int egprs_compress::decompress_crbb(
|
||||
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) {
|
||||
@@ -536,7 +542,7 @@ int egprs_compress::decompress_crbb(
|
||||
}
|
||||
}
|
||||
bit_pos = bit_pos + nbits;
|
||||
compress_bmap_len = compress_bmap_len - nbits;
|
||||
remaining_bmap_len = remaining_bmap_len - nbits;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -618,7 +624,7 @@ 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 */
|
||||
uint8_t max_bits) /* max remaining bits */
|
||||
{
|
||||
bool run_len_bit;
|
||||
int buflen = urbb_vec->cur_bit;
|
||||
@@ -684,8 +690,7 @@ int egprs_compress::compress_rbb(
|
||||
if (clen >= uclen)
|
||||
/* No Gain is observed, So no need to compress */
|
||||
return 0;
|
||||
else
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
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);
|
||||
uint8_t *uclen_crbb, uint8_t max_bits);
|
||||
|
||||
private:
|
||||
egprs_compress_node *ones_list;
|
||||
|
||||
1230
src/encoding.cpp
1230
src/encoding.cpp
File diff suppressed because it is too large
Load Diff
@@ -21,18 +21,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <gsm_rlcmac.h>
|
||||
#include <gprs_coding_scheme.h>
|
||||
extern "C" {
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_bts;
|
||||
#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
|
||||
@@ -42,47 +47,25 @@ struct gprs_rlc_data_block_info;
|
||||
class Encoding {
|
||||
public:
|
||||
static int write_immediate_assignment(
|
||||
const struct gprs_rlcmac_pdch *pdch,
|
||||
struct gprs_rlcmac_tbf *tbf,
|
||||
bitvec * dest, uint8_t downlink, uint16_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
|
||||
uint8_t tsc, uint8_t usf, uint8_t polling,
|
||||
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 =
|
||||
GSM_L1_BURST_TYPE_ACCESS_0,
|
||||
uint8_t sb = 1);
|
||||
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
|
||||
enum ph_burst_type burst_type,
|
||||
uint8_t t3142
|
||||
);
|
||||
|
||||
static void write_packet_uplink_assignment(
|
||||
struct gprs_rlcmac_bts *bts,
|
||||
bitvec * dest, uint8_t old_tfi,
|
||||
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
|
||||
struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t rrbp,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
|
||||
int8_t use_egprs);
|
||||
static void encode_rbb(const char *show_rbb, bitvec *rbb);
|
||||
|
||||
static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
|
||||
bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
|
||||
struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll, uint8_t rrbp,
|
||||
uint8_t alpha, uint8_t gamma,
|
||||
int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
|
||||
|
||||
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
|
||||
|
||||
static void write_packet_access_reject(
|
||||
bitvec * dest, uint32_t tlli);
|
||||
|
||||
static void write_packet_uplink_ack(
|
||||
struct gprs_rlcmac_bts *bts, bitvec * dest,
|
||||
struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
|
||||
uint8_t rrbp);
|
||||
|
||||
static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
|
||||
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);
|
||||
@@ -104,7 +87,45 @@ public:
|
||||
};
|
||||
|
||||
static AppendResult rlc_data_to_dl_append(
|
||||
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
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
|
||||
|
||||
1362
src/gprs_bssgp_pcu.c
Normal file
1362
src/gprs_bssgp_pcu.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -21,43 +21,57 @@
|
||||
#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>
|
||||
|
||||
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
|
||||
}
|
||||
|
||||
#include <gprs_debug.h>
|
||||
|
||||
#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
|
||||
|
||||
enum sgsn_counter_id {
|
||||
SGSN_CTR_RX_PAGING_CS,
|
||||
SGSN_CTR_RX_PAGING_PS,
|
||||
};
|
||||
|
||||
struct gprs_bssgp_pcu {
|
||||
struct gprs_nsvc *nsvc;
|
||||
struct bssgp_bvc_ctx *bctx;
|
||||
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
|
||||
struct osmo_timer_list bvc_timer;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
|
||||
/* state: is the NSVC unblocked? */
|
||||
int nsvc_unblocked;
|
||||
|
||||
/* 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 timeval queue_delay_sum;
|
||||
struct timespec queue_delay_sum;
|
||||
unsigned queue_delay_count;
|
||||
uint8_t fc_tag;
|
||||
unsigned queue_frames_sent;
|
||||
@@ -74,20 +88,28 @@ struct gprs_bssgp_pcu {
|
||||
struct tlv_parsed *tp);
|
||||
};
|
||||
|
||||
struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
|
||||
uint16_t local_port,
|
||||
uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
|
||||
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, bool mnc_3_digits,
|
||||
uint16_t lac, uint16_t rac, uint16_t cell_id);
|
||||
|
||||
void gprs_bssgp_destroy(void);
|
||||
int gprs_ns_reconnect(struct gprs_nsvc *nsvc);
|
||||
|
||||
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void);
|
||||
|
||||
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
|
||||
const struct timeval *tv_now);
|
||||
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
|
||||
|
||||
289
src/gprs_bssgp_rim.c
Normal file
289
src/gprs_bssgp_rim.c
Normal file
@@ -0,0 +1,289 @@
|
||||
/* gprs_bssgp_pcu.cpp
|
||||
*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/gprs/gprs_ns.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <bts.h>
|
||||
|
||||
#include "gprs_debug.h"
|
||||
#include "gprs_pcu.h"
|
||||
#include "bts.h"
|
||||
#include "gprs_ms.h"
|
||||
#include "nacc_fsm.h"
|
||||
|
||||
#define LOGPRIM(nsei, level, fmt, args...) \
|
||||
LOGP(DRIM, level, "(NSEI=%u) " fmt, nsei, ## args)
|
||||
|
||||
static inline void gprs_ra_id_ci_to_cgi_ps(struct osmo_cell_global_id_ps *cgi_ps,
|
||||
const struct gprs_ra_id *raid, uint16_t cid)
|
||||
{
|
||||
*cgi_ps = (struct osmo_cell_global_id_ps) {
|
||||
.rai.lac.plmn.mcc = raid->mcc,
|
||||
.rai.lac.plmn.mnc = raid->mnc,
|
||||
.rai.lac.plmn.mnc_3_digits = raid->mnc_3_digits,
|
||||
.rai.lac.lac = raid->lac,
|
||||
.rai.rac = raid->rac,
|
||||
.cell_identity = cid,
|
||||
};
|
||||
}
|
||||
|
||||
/* Mirror RIM routing information of a given PDU, see also 3GPP TS 48.018, section 8c.1.4.3 */
|
||||
static void mirror_rim_routing_info(struct bssgp_ran_information_pdu *to_pdu,
|
||||
const struct bssgp_ran_information_pdu *from_pdu)
|
||||
{
|
||||
memcpy(&to_pdu->routing_info_dest, &from_pdu->routing_info_src, sizeof(to_pdu->routing_info_dest));
|
||||
memcpy(&to_pdu->routing_info_src, &from_pdu->routing_info_dest, sizeof(to_pdu->routing_info_src));
|
||||
}
|
||||
|
||||
/* Fill NACC application container with data (cell identifier, sysinfo) */
|
||||
#define SI_HDR_LEN 2
|
||||
static void fill_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *app_cont, const struct gprs_rlcmac_bts *bts)
|
||||
{
|
||||
struct bssgp_bvc_ctx *bctx = the_pcu->bssgp.bctx;
|
||||
|
||||
gprs_ra_id_ci_to_cgi_ps(&app_cont->reprt_cell, &bctx->ra_id, bctx->cell_id);
|
||||
app_cont->num_si = 0;
|
||||
|
||||
/* Note: The BTS struct stores the system information including its pseudo header. The RIM application
|
||||
* container defines the system information without pseudo header, so we need to chop it off. */
|
||||
|
||||
if (bts->si1_is_set) {
|
||||
app_cont->si[app_cont->num_si] = bts->si1 + SI_HDR_LEN;
|
||||
app_cont->num_si++;
|
||||
}
|
||||
|
||||
if (bts->si3_is_set) {
|
||||
app_cont->si[app_cont->num_si] = bts->si3 + SI_HDR_LEN;
|
||||
app_cont->num_si++;
|
||||
}
|
||||
|
||||
if (bts->si13_is_set) {
|
||||
app_cont->si[app_cont->num_si] = bts->si13 + SI_HDR_LEN;
|
||||
app_cont->num_si++;
|
||||
}
|
||||
|
||||
/* Note: It is possible that the resulting PDU will not contain any system information, even if this is
|
||||
* an unlikely case since the BTS immediately updates the system information after startup. The
|
||||
* specification permits to send zero system information, see also: 3GPP TS 48.018 section 11.3.63.2.1 */
|
||||
|
||||
if (!bts->si1_is_set || !bts->si3_is_set || !bts->si13_is_set)
|
||||
LOGP(DNACC, LOGL_INFO, "TX RAN INFO RESPONSE (NACC) %s: Some SI are missing:%s%s%s\n",
|
||||
osmo_cgi_ps_name(&app_cont->reprt_cell),
|
||||
bts->si1_is_set ? "" : " SI1",
|
||||
bts->si3_is_set ? "" : " SI3",
|
||||
bts->si13_is_set ? "" : " SI13");
|
||||
}
|
||||
|
||||
/* Format a RAN INFORMATION PDU that contains the requested system information */
|
||||
static void format_response_pdu(struct bssgp_ran_information_pdu *resp_pdu,
|
||||
const struct bssgp_ran_information_pdu *req_pdu,
|
||||
const struct gprs_rlcmac_bts *bts)
|
||||
{
|
||||
memset(resp_pdu, 0, sizeof(*resp_pdu));
|
||||
mirror_rim_routing_info(resp_pdu, req_pdu);
|
||||
|
||||
resp_pdu->decoded.rim_cont = (struct bssgp_ran_inf_rim_cont) {
|
||||
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
|
||||
.seq_num = 1, /* single report has only one message in response */
|
||||
.pdu_ind = {
|
||||
.pdu_type_ext = RIM_PDU_TYPE_SING_REP,
|
||||
},
|
||||
.prot_ver = 1,
|
||||
};
|
||||
|
||||
fill_app_cont_nacc(&resp_pdu->decoded.rim_cont.u.app_cont_nacc, bts);
|
||||
resp_pdu->decoded_present = true;
|
||||
resp_pdu->rim_cont_iei = BSSGP_IE_RI_RIM_CONTAINER;
|
||||
}
|
||||
|
||||
/* Format a RAN INFORMATION ERROR PDU */
|
||||
static void format_response_pdu_err(struct bssgp_ran_information_pdu *resp_pdu,
|
||||
const struct bssgp_ran_information_pdu *req_pdu)
|
||||
{
|
||||
memset(resp_pdu, 0, sizeof(*resp_pdu));
|
||||
mirror_rim_routing_info(resp_pdu, req_pdu);
|
||||
|
||||
resp_pdu->decoded.err_rim_cont = (struct bssgp_ran_inf_err_rim_cont) {
|
||||
.app_id = BSSGP_RAN_INF_APP_ID_NACC,
|
||||
.prot_ver = 1,
|
||||
.err_pdu = req_pdu->rim_cont,
|
||||
.err_pdu_len = req_pdu->rim_cont_len,
|
||||
};
|
||||
|
||||
resp_pdu->decoded_present = true;
|
||||
resp_pdu->rim_cont_iei = BSSGP_IE_RI_ERROR_RIM_COINTAINER;
|
||||
}
|
||||
|
||||
static int handle_ran_info_response_nacc(const struct bssgp_ran_inf_app_cont_nacc *nacc, struct gprs_rlcmac_bts *bts)
|
||||
{
|
||||
struct si_cache_value val;
|
||||
struct si_cache_entry *entry;
|
||||
struct llist_head *tmp;
|
||||
int i;
|
||||
|
||||
LOGP(DRIM, LOGL_INFO, "Rx RAN-INFO cell=%s type=%sBCCH num_si=%d\n",
|
||||
osmo_cgi_ps_name(&nacc->reprt_cell),
|
||||
nacc->type_psi ? "P" : "", nacc->num_si);
|
||||
|
||||
val.type_psi = nacc->type_psi;
|
||||
val.si_len = 0;
|
||||
for (i = 0; i < nacc->num_si; i++) {
|
||||
size_t len = val.type_psi ? BSSGP_RIM_PSI_LEN : BSSGP_RIM_SI_LEN;
|
||||
memcpy(&val.si_buf[val.si_len], nacc->si[i], len);
|
||||
val.si_len += len;
|
||||
}
|
||||
entry = si_cache_add(bts->pcu->si_cache, &nacc->reprt_cell, &val);
|
||||
|
||||
llist_for_each(tmp, bts_ms_list(bts)) {
|
||||
struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
|
||||
if (ms->nacc && 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;
|
||||
}
|
||||
24
src/gprs_bssgp_rim.h
Normal file
24
src/gprs_bssgp_rim.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/* gprs_bssgp_rim.h
|
||||
*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct osmo_bssgp_prim;
|
||||
|
||||
int handle_rim(struct osmo_bssgp_prim *bp);
|
||||
@@ -22,12 +22,13 @@
|
||||
#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 timeval *delta)
|
||||
static void control_law(struct gprs_codel *state, struct timespec *delta)
|
||||
{
|
||||
/* 256 / sqrt(x), limited to 255 */
|
||||
static uint8_t inv_sqrt_tab[] = {255,
|
||||
@@ -57,12 +58,12 @@ static void control_law(struct gprs_codel *state, struct timeval *delta)
|
||||
inv_sqrt = inv_sqrt_tab[state->count];
|
||||
|
||||
/* delta = state->interval / sqrt(count) */
|
||||
delta_usecs = state->interval.tv_sec * 1000000 + state->interval.tv_usec;
|
||||
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_usec = q.rem;
|
||||
delta->tv_nsec = q.rem * 1000;
|
||||
}
|
||||
|
||||
void gprs_codel_init(struct gprs_codel *state)
|
||||
@@ -83,12 +84,12 @@ void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms)
|
||||
|
||||
q = div(interval_ms, 1000);
|
||||
state->interval.tv_sec = q.quot;
|
||||
state->interval.tv_usec = q.rem * 1000;
|
||||
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_usec = q.rem * 1000;
|
||||
state->target.tv_nsec = q.rem * 1000000;
|
||||
}
|
||||
|
||||
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket)
|
||||
@@ -104,29 +105,29 @@ void gprs_codel_set_maxpacket(struct gprs_codel *state, int 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 timeval *recv,
|
||||
const struct timeval *now, int bytes)
|
||||
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
|
||||
const struct timespec *now, int bytes)
|
||||
{
|
||||
struct timeval sojourn_time;
|
||||
struct timeval delta;
|
||||
struct timespec sojourn_time;
|
||||
struct timespec delta;
|
||||
|
||||
if (recv == NULL)
|
||||
goto stop_dropping;
|
||||
|
||||
timersub(now, recv, &sojourn_time);
|
||||
timespecsub(now, recv, &sojourn_time);
|
||||
|
||||
if (timercmp(&sojourn_time, &state->target, <))
|
||||
if (timespeccmp(&sojourn_time, &state->target, <))
|
||||
goto stop_dropping;
|
||||
|
||||
if (bytes >= 0 && (unsigned)bytes <= state->maxpacket)
|
||||
goto stop_dropping;
|
||||
|
||||
if (!timerisset(&state->first_above_time)) {
|
||||
timeradd(now, &state->interval, &state->first_above_time);
|
||||
if (!timespecisset(&state->first_above_time)) {
|
||||
timespecadd(now, &state->interval, &state->first_above_time);
|
||||
goto not_ok_to_drop;
|
||||
}
|
||||
|
||||
if (timercmp(now, &state->first_above_time, <))
|
||||
if (timespeccmp(now, &state->first_above_time, <))
|
||||
goto not_ok_to_drop;
|
||||
|
||||
/* Ok to drop */
|
||||
@@ -134,14 +135,14 @@ int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
|
||||
if (!state->dropping) {
|
||||
int recently = 0;
|
||||
int in_drop_cycle = 0;
|
||||
if (timerisset(&state->drop_next)) {
|
||||
timersub(now, &state->drop_next, &delta);
|
||||
in_drop_cycle = timercmp(&delta, &state->interval, <);
|
||||
if (timespecisset(&state->drop_next)) {
|
||||
timespecsub(now, &state->drop_next, &delta);
|
||||
in_drop_cycle = timespeccmp(&delta, &state->interval, <);
|
||||
recently = in_drop_cycle;
|
||||
}
|
||||
if (!recently) {
|
||||
timersub(now, &state->first_above_time, &delta);
|
||||
recently = !timercmp(&delta, &state->interval, <);
|
||||
timespecsub(now, &state->first_above_time, &delta);
|
||||
recently = !timespeccmp(&delta, &state->interval, <);
|
||||
};
|
||||
if (!recently)
|
||||
return 0;
|
||||
@@ -155,24 +156,24 @@ int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
|
||||
|
||||
state->drop_next = *now;
|
||||
} else {
|
||||
if (timercmp(now, &state->drop_next, <))
|
||||
if (timespeccmp(now, &state->drop_next, <))
|
||||
return 0;
|
||||
|
||||
state->count += 1;
|
||||
}
|
||||
|
||||
control_law(state, &delta);
|
||||
timeradd(&state->drop_next, &delta, &state->drop_next);
|
||||
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_usec / 1000), state->count);
|
||||
(int)delta.tv_sec, (int)(delta.tv_nsec / 1000000), state->count);
|
||||
#endif
|
||||
return 1;
|
||||
|
||||
stop_dropping:
|
||||
timerclear(&state->first_above_time);
|
||||
timespecclear(&state->first_above_time);
|
||||
not_ok_to_drop:
|
||||
state->dropping = 0;
|
||||
return 0;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Spec default values */
|
||||
#define GPRS_CODEL_DEFAULT_INTERVAL_MS 100
|
||||
@@ -40,10 +40,10 @@ extern "C" {
|
||||
struct gprs_codel {
|
||||
int dropping;
|
||||
unsigned count;
|
||||
struct timeval first_above_time;
|
||||
struct timeval drop_next;
|
||||
struct timeval target;
|
||||
struct timeval interval;
|
||||
struct timespec first_above_time;
|
||||
struct timespec drop_next;
|
||||
struct timespec target;
|
||||
struct timespec interval;
|
||||
unsigned maxpacket;
|
||||
};
|
||||
|
||||
@@ -66,8 +66,8 @@ struct gprs_codel {
|
||||
*
|
||||
* \return != 0 if the packet should be dropped, 0 otherwise
|
||||
*/
|
||||
int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
|
||||
const struct timeval *now, int bytes);
|
||||
int gprs_codel_control(struct gprs_codel *state, const struct timespec *recv,
|
||||
const struct timespec *now, int bytes);
|
||||
|
||||
/*!
|
||||
* \brief Initialise CoDel state
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
/* gprs_coding_scheme.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_coding_scheme.h"
|
||||
|
||||
/*
|
||||
* 44.060 Table 8.1.1.1 and Table 8.1.1.2
|
||||
* It has 3 level indexing. 0th level is ARQ type
|
||||
* 1st level is Original MCS( index 0 corresponds to MCS1 and so on)
|
||||
* 2nd level is MS MCS (index 0 corresponds to MCS1 and so on)
|
||||
*/
|
||||
enum GprsCodingScheme::Scheme GprsCodingScheme::egprs_mcs_retx_tbl[MAX_NUM_ARQ]
|
||||
[MAX_NUM_MCS][MAX_NUM_MCS] = {
|
||||
{
|
||||
{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}
|
||||
},
|
||||
{
|
||||
{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}
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
const char *name;
|
||||
GprsCodingScheme::HeaderType data_hdr;
|
||||
GprsCodingScheme::Family family;
|
||||
} mcs_info[GprsCodingScheme::NUM_SCHEMES] = {
|
||||
{{0, 0}, {0, 0}, 0, 0, "UNKNOWN",
|
||||
GprsCodingScheme::HEADER_INVALID, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{23, 0}, {23, 0}, 20, 0, "CS-1",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{33, 7}, {33, 7}, 30, 0, "CS-2",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{39, 3}, {39, 3}, 36, 0, "CS-3",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{53, 7}, {53, 7}, 50, 0, "CS-4",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
|
||||
{{26, 1}, {26, 1}, 22, 0, "MCS-1",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
|
||||
{{32, 1}, {32, 1}, 28, 0, "MCS-2",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_B},
|
||||
{{41, 1}, {41, 1}, 37, 48, "MCS-3",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_A},
|
||||
{{48, 1}, {48, 1}, 44, 0, "MCS-4",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
|
||||
|
||||
{{60, 7}, {59, 6}, 56, 0, "MCS-5",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_B},
|
||||
{{78, 7}, {77, 6}, 74, 48, "MCS-6",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_A},
|
||||
{{118, 2}, {117, 4}, 56, 0, "MCS-7",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_B},
|
||||
{{142, 2}, {141, 4}, 68, 0, "MCS-8",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
|
||||
{{154, 2}, {153, 4}, 74, 0, "MCS-9",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
|
||||
};
|
||||
|
||||
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[GprsCodingScheme::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"},
|
||||
};
|
||||
|
||||
GprsCodingScheme GprsCodingScheme::getBySizeUL(unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 23: return GprsCodingScheme(CS1);
|
||||
case 27: return GprsCodingScheme(MCS1);
|
||||
case 33: return GprsCodingScheme(MCS2);
|
||||
case 34: return GprsCodingScheme(CS2);
|
||||
case 40: return GprsCodingScheme(CS3);
|
||||
case 42: return GprsCodingScheme(MCS3);
|
||||
case 49: return GprsCodingScheme(MCS4);
|
||||
case 54: return GprsCodingScheme(CS4);
|
||||
case 61: return GprsCodingScheme(MCS5);
|
||||
case 79: return GprsCodingScheme(MCS6);
|
||||
case 119: return GprsCodingScheme(MCS7);
|
||||
case 143: return GprsCodingScheme(MCS8);
|
||||
case 155: return GprsCodingScheme(MCS9);
|
||||
}
|
||||
|
||||
return GprsCodingScheme(UNKNOWN);
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::sizeUL() const
|
||||
{
|
||||
return mcs_info[m_scheme].uplink.bytes + (spareBitsUL() ? 1 : 0);
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::usedSizeUL() const
|
||||
{
|
||||
if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
|
||||
return mcs_info[m_scheme].uplink.bytes;
|
||||
else
|
||||
return sizeUL();
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::maxBytesUL() const
|
||||
{
|
||||
return mcs_info[m_scheme].uplink.bytes;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::spareBitsUL() const
|
||||
{
|
||||
return mcs_info[m_scheme].uplink.ext_bits;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::sizeDL() const
|
||||
{
|
||||
return mcs_info[m_scheme].downlink.bytes + (spareBitsDL() ? 1 : 0);
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::usedSizeDL() const
|
||||
{
|
||||
if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
|
||||
return mcs_info[m_scheme].downlink.bytes;
|
||||
else
|
||||
return sizeDL();
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::maxBytesDL() const
|
||||
{
|
||||
return mcs_info[m_scheme].downlink.bytes;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::spareBitsDL() const
|
||||
{
|
||||
return mcs_info[m_scheme].downlink.ext_bits;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::maxDataBlockBytes() const
|
||||
{
|
||||
return mcs_info[m_scheme].data_bytes;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::optionalPaddingBits() const
|
||||
{
|
||||
return mcs_info[m_scheme].optional_padding_bits;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::numDataBlocks() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].num_blocks;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::numDataHeaderBitsUL() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].uplink.data_header_bits;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::numDataHeaderBitsDL() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].downlink.data_header_bits;
|
||||
}
|
||||
|
||||
uint8_t GprsCodingScheme::numDataBlockHeaderBits() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].data_block_header_bits;
|
||||
}
|
||||
|
||||
const char *GprsCodingScheme::name() const
|
||||
{
|
||||
return mcs_info[m_scheme].name;
|
||||
}
|
||||
|
||||
GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeData() const
|
||||
{
|
||||
return mcs_info[m_scheme].data_hdr;
|
||||
}
|
||||
|
||||
GprsCodingScheme::Family GprsCodingScheme::family() const
|
||||
{
|
||||
return mcs_info[m_scheme].family;
|
||||
}
|
||||
|
||||
void GprsCodingScheme::inc(Mode mode)
|
||||
{
|
||||
if (!isCompatible(mode))
|
||||
/* This should not happen. TODO: Use assert? */
|
||||
return;
|
||||
|
||||
Scheme new_cs(Scheme(m_scheme + 1));
|
||||
if (!GprsCodingScheme(new_cs).isCompatible(mode))
|
||||
/* Clipping, do not change the value */
|
||||
return;
|
||||
|
||||
m_scheme = new_cs;
|
||||
}
|
||||
|
||||
void GprsCodingScheme::dec(Mode mode)
|
||||
{
|
||||
if (!isCompatible(mode))
|
||||
/* This should not happen. TODO: Use assert? */
|
||||
return;
|
||||
|
||||
Scheme new_cs(Scheme(m_scheme - 1));
|
||||
if (!GprsCodingScheme(new_cs).isCompatible(mode))
|
||||
/* Clipping, do not change the value */
|
||||
return;
|
||||
|
||||
m_scheme = new_cs;
|
||||
}
|
||||
|
||||
void GprsCodingScheme::inc()
|
||||
{
|
||||
if (isGprs() && m_scheme == CS4)
|
||||
return;
|
||||
|
||||
if (isEgprs() && m_scheme == MCS9)
|
||||
return;
|
||||
|
||||
if (!isValid())
|
||||
return;
|
||||
|
||||
m_scheme = Scheme(m_scheme + 1);
|
||||
}
|
||||
|
||||
void GprsCodingScheme::dec()
|
||||
{
|
||||
if (isGprs() && m_scheme == CS1)
|
||||
return;
|
||||
|
||||
if (isEgprs() && m_scheme == MCS1)
|
||||
return;
|
||||
|
||||
if (!isValid())
|
||||
return;
|
||||
|
||||
m_scheme = Scheme(m_scheme - 1);
|
||||
}
|
||||
|
||||
const char *GprsCodingScheme::modeName(Mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case GPRS: return "GPRS";
|
||||
case EGPRS_GMSK: return "EGPRS_GMSK-only";
|
||||
case EGPRS: return "EGPRS";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
bool GprsCodingScheme::isFamilyCompatible(GprsCodingScheme o) const
|
||||
{
|
||||
if (*this == o)
|
||||
return true;
|
||||
|
||||
if (family() == FAMILY_INVALID)
|
||||
return false;
|
||||
|
||||
return family() == o.family();
|
||||
}
|
||||
|
||||
bool GprsCodingScheme::isCombinable(GprsCodingScheme o) const
|
||||
{
|
||||
return numDataBlocks() == o.numDataBlocks();
|
||||
}
|
||||
|
||||
void GprsCodingScheme::decToSingleBlock(bool *needStuffing)
|
||||
{
|
||||
switch (m_scheme) {
|
||||
case MCS7: *needStuffing = false; m_scheme = MCS5; break;
|
||||
case MCS8: *needStuffing = true; m_scheme = MCS6; break;
|
||||
case MCS9: *needStuffing = false; m_scheme = MCS6; break;
|
||||
default: *needStuffing = false; break;
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/* gprs_coding_scheme.h
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
}
|
||||
|
||||
class GprsCodingScheme {
|
||||
public:
|
||||
|
||||
#define MAX_NUM_ARQ 2 /* max. number of ARQ */
|
||||
#define MAX_NUM_MCS 9 /* max. number of MCS */
|
||||
#define EGPRS_ARQ1 0x0
|
||||
#define EGPRS_ARQ2 0x1
|
||||
|
||||
enum Scheme {
|
||||
UNKNOWN,
|
||||
CS1, CS2, CS3, CS4,
|
||||
MCS1, MCS2, MCS3, MCS4,
|
||||
MCS5, MCS6, MCS7, MCS8, MCS9,
|
||||
NUM_SCHEMES
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
GPRS,
|
||||
EGPRS_GMSK,
|
||||
EGPRS,
|
||||
};
|
||||
|
||||
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 Family {
|
||||
FAMILY_INVALID,
|
||||
FAMILY_A,
|
||||
FAMILY_B,
|
||||
FAMILY_C,
|
||||
};
|
||||
|
||||
GprsCodingScheme(Scheme s = UNKNOWN);
|
||||
|
||||
operator bool() const {return m_scheme != UNKNOWN;}
|
||||
operator Scheme() const {return m_scheme;}
|
||||
uint8_t to_num() const;
|
||||
|
||||
GprsCodingScheme& operator =(Scheme s);
|
||||
bool operator == (Scheme s) const;
|
||||
GprsCodingScheme& operator =(GprsCodingScheme o);
|
||||
|
||||
bool isValid() const {return UNKNOWN <= m_scheme && m_scheme <= MCS9;}
|
||||
bool isGprs() const {return CS1 <= m_scheme && m_scheme <= CS4;}
|
||||
bool isEgprs() const {return m_scheme >= MCS1;}
|
||||
bool isEgprsGmsk() const {return isEgprs() && m_scheme <= MCS4;}
|
||||
bool isCompatible(Mode mode) const;
|
||||
bool isCompatible(GprsCodingScheme o) const;
|
||||
bool isFamilyCompatible(GprsCodingScheme o) const;
|
||||
bool isCombinable(GprsCodingScheme o) const;
|
||||
|
||||
void inc(Mode mode);
|
||||
void dec(Mode mode);
|
||||
void inc();
|
||||
void dec();
|
||||
void decToSingleBlock(bool *needStuffing);
|
||||
|
||||
uint8_t sizeUL() const;
|
||||
uint8_t sizeDL() const;
|
||||
uint8_t usedSizeUL() const;
|
||||
uint8_t usedSizeDL() const;
|
||||
uint8_t maxBytesUL() const;
|
||||
uint8_t maxBytesDL() const;
|
||||
uint8_t spareBitsUL() const;
|
||||
uint8_t spareBitsDL() const;
|
||||
uint8_t maxDataBlockBytes() const;
|
||||
uint8_t numDataBlocks() const;
|
||||
uint8_t numDataHeaderBitsUL() const;
|
||||
uint8_t numDataHeaderBitsDL() const;
|
||||
uint8_t numDataBlockHeaderBits() const;
|
||||
uint8_t optionalPaddingBits() const;
|
||||
const char *name() const;
|
||||
HeaderType headerTypeData() const;
|
||||
HeaderType headerTypeControl() const;
|
||||
Family family() const;
|
||||
|
||||
static GprsCodingScheme getBySizeUL(unsigned size);
|
||||
static GprsCodingScheme getGprsByNum(unsigned num);
|
||||
static GprsCodingScheme getEgprsByNum(unsigned num);
|
||||
|
||||
static const char *modeName(Mode mode);
|
||||
static Scheme get_retx_mcs(const GprsCodingScheme mcs,
|
||||
const GprsCodingScheme retx_mcs,
|
||||
const unsigned arq_type);
|
||||
|
||||
static enum Scheme egprs_mcs_retx_tbl[MAX_NUM_ARQ]
|
||||
[MAX_NUM_MCS][MAX_NUM_MCS];
|
||||
private:
|
||||
GprsCodingScheme(int s); /* fail on use */
|
||||
GprsCodingScheme& operator =(int s); /* fail on use */
|
||||
enum Scheme m_scheme;
|
||||
};
|
||||
|
||||
inline uint8_t GprsCodingScheme::to_num() const
|
||||
{
|
||||
if (isGprs())
|
||||
return (m_scheme - CS1) + 1;
|
||||
|
||||
if (isEgprs())
|
||||
return (m_scheme - MCS1) + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool GprsCodingScheme::isCompatible(Mode mode) const
|
||||
{
|
||||
switch (mode) {
|
||||
case GPRS: return isGprs();
|
||||
case EGPRS_GMSK: return isEgprsGmsk();
|
||||
case EGPRS: return isEgprs();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool GprsCodingScheme::isCompatible(GprsCodingScheme o) const
|
||||
{
|
||||
return (isGprs() && o.isGprs()) || (isEgprs() && o.isEgprs());
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeControl() const
|
||||
{
|
||||
return HEADER_GPRS_CONTROL;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::GprsCodingScheme(Scheme s)
|
||||
: m_scheme(s)
|
||||
{
|
||||
if (!isValid())
|
||||
m_scheme = UNKNOWN;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme& GprsCodingScheme::operator =(Scheme s)
|
||||
{
|
||||
m_scheme = s;
|
||||
|
||||
if (!isValid())
|
||||
m_scheme = UNKNOWN;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme& GprsCodingScheme::operator =(GprsCodingScheme o)
|
||||
{
|
||||
m_scheme = o.m_scheme;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme GprsCodingScheme::getGprsByNum(unsigned num)
|
||||
{
|
||||
if (num < 1 || num > 4)
|
||||
return GprsCodingScheme();
|
||||
|
||||
return GprsCodingScheme(Scheme(CS1 + (num - 1)));
|
||||
}
|
||||
|
||||
inline GprsCodingScheme GprsCodingScheme::getEgprsByNum(unsigned num)
|
||||
{
|
||||
if (num < 1 || num > 9)
|
||||
return GprsCodingScheme();
|
||||
|
||||
return GprsCodingScheme(Scheme(MCS1 + (num - 1)));
|
||||
}
|
||||
|
||||
/* The coding schemes form a partial ordering */
|
||||
inline bool operator ==(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return GprsCodingScheme::Scheme(a) == GprsCodingScheme::Scheme(b);
|
||||
}
|
||||
|
||||
inline bool GprsCodingScheme::operator == (Scheme scheme) const
|
||||
{
|
||||
return this->m_scheme == scheme;
|
||||
}
|
||||
|
||||
inline bool operator !=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline bool operator <(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return a.isCompatible(b) &&
|
||||
GprsCodingScheme::Scheme(a) < GprsCodingScheme::Scheme(b);
|
||||
}
|
||||
|
||||
inline bool operator >(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
|
||||
inline bool operator <=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return a == b || a < b;
|
||||
}
|
||||
|
||||
inline bool operator >=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return a == b || a > b;
|
||||
}
|
||||
inline GprsCodingScheme::Scheme GprsCodingScheme::get_retx_mcs(
|
||||
const GprsCodingScheme mcs,
|
||||
const GprsCodingScheme demanded_mcs,
|
||||
const unsigned arq_type)
|
||||
{
|
||||
OSMO_ASSERT(mcs.to_num() > 0);
|
||||
OSMO_ASSERT(demanded_mcs.to_num() > 0);
|
||||
|
||||
return egprs_mcs_retx_tbl[arq_type][mcs.to_num() - 1]
|
||||
[demanded_mcs.to_num() - 1];
|
||||
}
|
||||
@@ -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
|
||||
@@ -17,27 +18,121 @@
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
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_NOTICE, 1},
|
||||
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_NOTICE, 1},
|
||||
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Downlink (RLCMAC)", LOGL_NOTICE, 1},
|
||||
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_NOTICE, 1},
|
||||
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_NOTICE, 1},
|
||||
{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
|
||||
{"DTBF","\033[1;34m", "Temporary Block Flow (TBF)", LOGL_INFO , 1},
|
||||
{"DTBFDL","\033[1;34m", "Temporary Block Flow (TBF) Downlink", LOGL_INFO , 1},
|
||||
{"DTBFUL","\033[1;34m", "Temporary Block Flow (TBF) Uplink", LOGL_INFO , 1},
|
||||
{"DNS","\033[1;34m", "GPRS Network Service Protocol (NS)", 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},
|
||||
[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,
|
||||
|
||||
@@ -27,6 +27,10 @@ extern "C" {
|
||||
};
|
||||
#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,
|
||||
@@ -41,8 +45,9 @@ enum {
|
||||
DTBFDL,
|
||||
DTBFUL,
|
||||
DNS,
|
||||
DBSSGP,
|
||||
DPCU,
|
||||
DNACC,
|
||||
DRIM,
|
||||
aDebug_LastEntry
|
||||
};
|
||||
|
||||
|
||||
988
src/gprs_ms.c
Normal file
988
src/gprs_ms.c
Normal file
@@ -0,0 +1,988 @@
|
||||
/* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_ms.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;
|
||||
}
|
||||
834
src/gprs_ms.cpp
834
src/gprs_ms.cpp
@@ -1,834 +0,0 @@
|
||||
/* gprs_ms.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_ms.h"
|
||||
#include <gprs_coding_scheme.h>
|
||||
#include "bts.h"
|
||||
#include "tbf.h"
|
||||
#include "gprs_debug.h"
|
||||
#include "gprs_codel.h"
|
||||
#include "pcu_utils.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
}
|
||||
|
||||
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
static int64_t now_msec()
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
struct GprsMsDefaultCallback: public GprsMs::Callback {
|
||||
virtual void ms_idle(class GprsMs *ms) {
|
||||
delete ms;
|
||||
}
|
||||
virtual void ms_active(class GprsMs *) {}
|
||||
};
|
||||
|
||||
static GprsMsDefaultCallback gprs_default_cb;
|
||||
|
||||
GprsMs::Guard::Guard(GprsMs *ms) :
|
||||
m_ms(ms ? ms->ref() : NULL)
|
||||
{
|
||||
}
|
||||
|
||||
GprsMs::Guard::~Guard()
|
||||
{
|
||||
if (m_ms)
|
||||
m_ms->unref();
|
||||
}
|
||||
|
||||
bool GprsMs::Guard::is_idle() const
|
||||
{
|
||||
if (!m_ms)
|
||||
return true;
|
||||
|
||||
return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
|
||||
}
|
||||
|
||||
void GprsMs::timeout(void *priv_)
|
||||
{
|
||||
GprsMs *ms = static_cast<GprsMs *>(priv_);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
|
||||
ms->tlli());
|
||||
|
||||
if (ms->m_timer.data) {
|
||||
ms->m_timer.data = NULL;
|
||||
ms->unref();
|
||||
}
|
||||
}
|
||||
|
||||
GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
|
||||
m_bts(bts),
|
||||
m_cb(&gprs_default_cb),
|
||||
m_ul_tbf(NULL),
|
||||
m_dl_tbf(NULL),
|
||||
m_tlli(tlli),
|
||||
m_new_ul_tlli(0),
|
||||
m_new_dl_tlli(0),
|
||||
m_ta(GSM48_TA_INVALID),
|
||||
m_ms_class(0),
|
||||
m_egprs_ms_class(0),
|
||||
m_is_idle(true),
|
||||
m_ref(0),
|
||||
m_list(this),
|
||||
m_delay(0),
|
||||
m_nack_rate_dl(0),
|
||||
m_reserved_dl_slots(0),
|
||||
m_reserved_ul_slots(0),
|
||||
m_current_trx(NULL),
|
||||
m_codel_state(NULL),
|
||||
m_mode(GprsCodingScheme::GPRS),
|
||||
m_dl_ctrl_msg(0)
|
||||
{
|
||||
int codel_interval = LLC_CODEL_USE_DEFAULT;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
|
||||
|
||||
m_imsi[0] = 0;
|
||||
memset(&m_timer, 0, sizeof(m_timer));
|
||||
m_timer.cb = GprsMs::timeout;
|
||||
m_llc_queue.init();
|
||||
|
||||
set_mode(m_mode);
|
||||
|
||||
if (m_bts)
|
||||
codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
|
||||
|
||||
if (codel_interval) {
|
||||
if (codel_interval == LLC_CODEL_USE_DEFAULT)
|
||||
codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
|
||||
m_codel_state = talloc(this, struct gprs_codel);
|
||||
gprs_codel_init(m_codel_state);
|
||||
gprs_codel_set_interval(m_codel_state, codel_interval);
|
||||
}
|
||||
m_last_cs_not_low = now_msec();
|
||||
}
|
||||
|
||||
GprsMs::~GprsMs()
|
||||
{
|
||||
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
|
||||
|
||||
set_reserved_slots(NULL, 0, 0);
|
||||
|
||||
if (osmo_timer_pending(&m_timer))
|
||||
osmo_timer_del(&m_timer);
|
||||
|
||||
if (m_ul_tbf) {
|
||||
m_ul_tbf->set_ms(NULL);
|
||||
m_ul_tbf = NULL;
|
||||
}
|
||||
|
||||
if (m_dl_tbf) {
|
||||
m_dl_tbf->set_ms(NULL);
|
||||
m_dl_tbf = NULL;
|
||||
}
|
||||
|
||||
llist_for_each_safe(pos, tmp, &m_old_tbfs)
|
||||
pos->entry()->set_ms(NULL);
|
||||
|
||||
m_llc_queue.clear(m_bts);
|
||||
}
|
||||
|
||||
void* GprsMs::operator new(size_t size)
|
||||
{
|
||||
static void *tall_ms_ctx = NULL;
|
||||
if (!tall_ms_ctx)
|
||||
tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
|
||||
|
||||
return talloc_size(tall_ms_ctx, size);
|
||||
}
|
||||
|
||||
void GprsMs::operator delete(void* p)
|
||||
{
|
||||
talloc_free(p);
|
||||
}
|
||||
|
||||
GprsMs *GprsMs::ref()
|
||||
{
|
||||
m_ref += 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
void GprsMs::unref()
|
||||
{
|
||||
OSMO_ASSERT(m_ref >= 0);
|
||||
m_ref -= 1;
|
||||
if (m_ref == 0)
|
||||
update_status();
|
||||
}
|
||||
|
||||
void GprsMs::start_timer()
|
||||
{
|
||||
if (m_delay == 0)
|
||||
return;
|
||||
|
||||
if (!m_timer.data)
|
||||
m_timer.data = ref();
|
||||
|
||||
osmo_timer_schedule(&m_timer, m_delay, 0);
|
||||
}
|
||||
|
||||
void GprsMs::stop_timer()
|
||||
{
|
||||
if (!m_timer.data)
|
||||
return;
|
||||
|
||||
osmo_timer_del(&m_timer);
|
||||
m_timer.data = NULL;
|
||||
unref();
|
||||
}
|
||||
|
||||
void GprsMs::set_mode(GprsCodingScheme::Mode mode)
|
||||
{
|
||||
m_mode = mode;
|
||||
|
||||
if (!m_bts)
|
||||
return;
|
||||
|
||||
switch (m_mode) {
|
||||
case GprsCodingScheme::GPRS:
|
||||
if (!m_current_cs_ul.isGprs()) {
|
||||
m_current_cs_ul = GprsCodingScheme::getGprsByNum(
|
||||
m_bts->bts_data()->initial_cs_ul);
|
||||
if (!m_current_cs_ul.isValid())
|
||||
m_current_cs_ul = GprsCodingScheme::CS1;
|
||||
}
|
||||
if (!m_current_cs_dl.isGprs()) {
|
||||
m_current_cs_dl = GprsCodingScheme::getGprsByNum(
|
||||
m_bts->bts_data()->initial_cs_dl);
|
||||
if (!m_current_cs_dl.isValid())
|
||||
m_current_cs_dl = GprsCodingScheme::CS1;
|
||||
}
|
||||
break;
|
||||
|
||||
case GprsCodingScheme::EGPRS_GMSK:
|
||||
case GprsCodingScheme::EGPRS:
|
||||
if (!m_current_cs_ul.isEgprs()) {
|
||||
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
|
||||
m_bts->bts_data()->initial_mcs_ul);
|
||||
if (!m_current_cs_ul.isValid())
|
||||
m_current_cs_ul = GprsCodingScheme::MCS1;
|
||||
}
|
||||
if (!m_current_cs_dl.isEgprs()) {
|
||||
m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
|
||||
m_bts->bts_data()->initial_mcs_dl);
|
||||
if (!m_current_cs_dl.isValid())
|
||||
m_current_cs_dl = GprsCodingScheme::MCS1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
attach_dl_tbf(as_dl_tbf(tbf));
|
||||
else
|
||||
attach_ul_tbf(as_ul_tbf(tbf));
|
||||
}
|
||||
|
||||
void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
|
||||
{
|
||||
if (m_ul_tbf == tbf)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
|
||||
tlli(), tbf->name());
|
||||
|
||||
Guard guard(this);
|
||||
|
||||
if (m_ul_tbf)
|
||||
llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
|
||||
|
||||
m_ul_tbf = tbf;
|
||||
|
||||
if (tbf)
|
||||
stop_timer();
|
||||
}
|
||||
|
||||
void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
|
||||
{
|
||||
if (m_dl_tbf == tbf)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
|
||||
tlli(), tbf->name());
|
||||
|
||||
Guard guard(this);
|
||||
|
||||
if (m_dl_tbf)
|
||||
llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
|
||||
|
||||
m_dl_tbf = tbf;
|
||||
|
||||
if (tbf)
|
||||
stop_timer();
|
||||
}
|
||||
|
||||
void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
|
||||
m_ul_tbf = NULL;
|
||||
} else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
|
||||
m_dl_tbf = NULL;
|
||||
} else {
|
||||
bool found = false;
|
||||
|
||||
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
|
||||
llist_for_each_safe(pos, tmp, &m_old_tbfs) {
|
||||
if (pos->entry() == tbf) {
|
||||
llist_del(pos);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Protect against recursive calls via set_ms() */
|
||||
if (!found)
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
|
||||
tlli(), tbf->name());
|
||||
|
||||
if (tbf->ms() == this)
|
||||
tbf->set_ms(NULL);
|
||||
|
||||
if (!m_dl_tbf && !m_ul_tbf) {
|
||||
set_reserved_slots(NULL, 0, 0);
|
||||
|
||||
if (tlli() != 0)
|
||||
start_timer();
|
||||
}
|
||||
|
||||
update_status();
|
||||
}
|
||||
|
||||
void GprsMs::update_status()
|
||||
{
|
||||
if (m_ref > 0)
|
||||
return;
|
||||
|
||||
if (is_idle() && !m_is_idle) {
|
||||
m_is_idle = true;
|
||||
m_cb->ms_idle(this);
|
||||
/* this can be deleted by now, do not access it */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_idle() && m_is_idle) {
|
||||
m_is_idle = false;
|
||||
m_cb->ms_active(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::reset()
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
|
||||
tlli(), imsi());
|
||||
|
||||
stop_timer();
|
||||
|
||||
m_tlli = 0;
|
||||
m_new_dl_tlli = 0;
|
||||
m_new_ul_tlli = 0;
|
||||
m_imsi[0] = '\0';
|
||||
}
|
||||
|
||||
void GprsMs::merge_old_ms(GprsMs *old_ms)
|
||||
{
|
||||
if (old_ms == this)
|
||||
return;
|
||||
|
||||
if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
|
||||
set_imsi(old_ms->imsi());
|
||||
|
||||
if (!ms_class() && old_ms->ms_class())
|
||||
set_ms_class(old_ms->ms_class());
|
||||
|
||||
m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
|
||||
|
||||
old_ms->reset();
|
||||
}
|
||||
|
||||
void GprsMs::set_tlli(uint32_t tlli)
|
||||
{
|
||||
if (tlli == m_tlli || tlli == m_new_ul_tlli)
|
||||
return;
|
||||
|
||||
if (tlli != m_new_dl_tlli) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
|
||||
"not yet confirmed\n",
|
||||
this->tlli(), tlli);
|
||||
m_new_ul_tlli = tlli;
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
|
||||
"already confirmed partly\n",
|
||||
m_tlli, tlli);
|
||||
|
||||
m_tlli = tlli;
|
||||
m_new_dl_tlli = 0;
|
||||
m_new_ul_tlli = 0;
|
||||
}
|
||||
|
||||
bool GprsMs::confirm_tlli(uint32_t tlli)
|
||||
{
|
||||
if (tlli == m_tlli || tlli == m_new_dl_tlli)
|
||||
return false;
|
||||
|
||||
if (tlli != m_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() */
|
||||
m_new_dl_tlli = tlli;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
|
||||
|
||||
m_tlli = tlli;
|
||||
m_new_dl_tlli = 0;
|
||||
m_new_ul_tlli = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GprsMs::set_imsi(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, m_imsi) == 0)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
|
||||
tlli(), m_imsi, imsi);
|
||||
|
||||
strncpy(m_imsi, imsi, sizeof(m_imsi));
|
||||
m_imsi[sizeof(m_imsi) - 1] = '\0';
|
||||
}
|
||||
|
||||
void GprsMs::set_ta(uint8_t ta_)
|
||||
{
|
||||
if (ta_ == m_ta)
|
||||
return;
|
||||
|
||||
if (gsm48_ta_is_valid(ta_)) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
|
||||
tlli(), m_ta, ta_);
|
||||
m_ta = ta_;
|
||||
} else
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
|
||||
"value %d kept)\n", tlli(), ta_, m_ta);
|
||||
}
|
||||
|
||||
void GprsMs::set_ms_class(uint8_t ms_class_)
|
||||
{
|
||||
if (ms_class_ == m_ms_class)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
|
||||
tlli(), m_ms_class, ms_class_);
|
||||
|
||||
m_ms_class = ms_class_;
|
||||
}
|
||||
|
||||
void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
|
||||
{
|
||||
if (ms_class_ == m_egprs_ms_class)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
|
||||
tlli(), m_egprs_ms_class, ms_class_);
|
||||
|
||||
m_egprs_ms_class = ms_class_;
|
||||
}
|
||||
|
||||
void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
int64_t now;
|
||||
GprsCodingScheme max_cs_dl = this->max_cs_dl();
|
||||
|
||||
OSMO_ASSERT(max_cs_dl);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (error_rate < 0)
|
||||
return;
|
||||
|
||||
now = now_msec();
|
||||
|
||||
/* TODO: Check for TBF direction */
|
||||
/* TODO: Support different CS values for UL and DL */
|
||||
|
||||
m_nack_rate_dl = error_rate;
|
||||
|
||||
if (error_rate > bts_data->cs_adj_upper_limit) {
|
||||
if (m_current_cs_dl.to_num() > 1) {
|
||||
m_current_cs_dl.dec(mode());
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): High error rate %d%%, "
|
||||
"reducing CS level to %s\n",
|
||||
imsi(), error_rate, m_current_cs_dl.name());
|
||||
m_last_cs_not_low = now;
|
||||
}
|
||||
} else if (error_rate < bts_data->cs_adj_lower_limit) {
|
||||
if (m_current_cs_dl < max_cs_dl) {
|
||||
if (now - m_last_cs_not_low > 1000) {
|
||||
m_current_cs_dl.inc(mode());
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"increasing DL CS level to %s\n",
|
||||
imsi(), error_rate,
|
||||
m_current_cs_dl.name());
|
||||
m_last_cs_not_low = now;
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"ignored (within blocking period)\n",
|
||||
imsi(), error_rate);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"MS (IMSI %s): Medium error rate %d%%, ignored\n",
|
||||
imsi(), error_rate);
|
||||
m_last_cs_not_low = now;
|
||||
}
|
||||
}
|
||||
|
||||
GprsCodingScheme GprsMs::max_cs_ul() const
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (m_current_cs_ul.isGprs()) {
|
||||
if (!bts_data->max_cs_ul)
|
||||
return GprsCodingScheme(GprsCodingScheme::CS4);
|
||||
|
||||
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
|
||||
}
|
||||
|
||||
if (!m_current_cs_ul.isEgprs())
|
||||
return GprsCodingScheme(); /* UNKNOWN */
|
||||
|
||||
if (bts_data->max_mcs_ul)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
|
||||
else if (bts_data->max_cs_ul)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
|
||||
|
||||
return GprsCodingScheme(GprsCodingScheme::MCS4);
|
||||
}
|
||||
|
||||
void GprsMs::set_current_cs_dl(GprsCodingScheme::Scheme scheme)
|
||||
{
|
||||
m_current_cs_dl = scheme;
|
||||
}
|
||||
|
||||
GprsCodingScheme GprsMs::max_cs_dl() const
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (m_current_cs_dl.isGprs()) {
|
||||
if (!bts_data->max_cs_dl)
|
||||
return GprsCodingScheme(GprsCodingScheme::CS4);
|
||||
|
||||
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
|
||||
}
|
||||
|
||||
if (!m_current_cs_dl.isEgprs())
|
||||
return GprsCodingScheme(); /* UNKNOWN */
|
||||
|
||||
if (bts_data->max_mcs_dl)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
|
||||
else if (bts_data->max_cs_dl)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
|
||||
|
||||
return GprsCodingScheme(GprsCodingScheme::MCS4);
|
||||
}
|
||||
|
||||
void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
GprsCodingScheme max_cs_ul = this->max_cs_ul();
|
||||
|
||||
int old_link_qual;
|
||||
int low;
|
||||
int high;
|
||||
GprsCodingScheme new_cs_ul = m_current_cs_ul;
|
||||
unsigned current_cs_num = m_current_cs_ul.to_num();
|
||||
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (!max_cs_ul) {
|
||||
LOGP(DRLCMACMEAS, LOGL_ERROR,
|
||||
"max_cs_ul cannot be derived (current UL CS: %s)\n",
|
||||
m_current_cs_ul.name());
|
||||
return;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(current_cs_num > 0);
|
||||
|
||||
if (!m_current_cs_ul)
|
||||
return;
|
||||
|
||||
if (!meas->have_link_qual)
|
||||
return;
|
||||
|
||||
old_link_qual = meas->link_qual;
|
||||
|
||||
if (m_current_cs_ul.isGprs()) {
|
||||
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
|
||||
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
|
||||
} else if (m_current_cs_ul.isEgprs()) {
|
||||
if (current_cs_num > MAX_GPRS_CS)
|
||||
current_cs_num = MAX_GPRS_CS;
|
||||
low = bts_data->mcs_lqual_ranges[current_cs_num-1].low;
|
||||
high = bts_data->mcs_lqual_ranges[current_cs_num-1].high;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_l1_meas.have_link_qual)
|
||||
old_link_qual = m_l1_meas.link_qual;
|
||||
|
||||
if (meas->link_qual < low && old_link_qual < low)
|
||||
new_cs_ul.dec(mode());
|
||||
else if (meas->link_qual > high && old_link_qual > high &&
|
||||
m_current_cs_ul < max_cs_ul)
|
||||
new_cs_ul.inc(mode());
|
||||
|
||||
if (m_current_cs_ul != new_cs_ul) {
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO,
|
||||
"MS (IMSI %s): "
|
||||
"Link quality %ddB (%ddB) left window [%d, %d], "
|
||||
"modifying uplink CS level: %s -> %s\n",
|
||||
imsi(), meas->link_qual, old_link_qual,
|
||||
low, high,
|
||||
m_current_cs_ul.name(), new_cs_ul.name());
|
||||
|
||||
m_current_cs_ul = new_cs_ul;
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
update_cs_ul(meas);
|
||||
|
||||
if (meas->have_rssi)
|
||||
m_l1_meas.set_rssi(meas->rssi);
|
||||
if (meas->have_bto)
|
||||
m_l1_meas.set_bto(meas->bto);
|
||||
if (meas->have_ber)
|
||||
m_l1_meas.set_ber(meas->ber);
|
||||
if (meas->have_link_qual)
|
||||
m_l1_meas.set_link_qual(meas->link_qual);
|
||||
|
||||
if (meas->have_ms_rx_qual)
|
||||
m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
|
||||
if (meas->have_ms_c_value)
|
||||
m_l1_meas.set_ms_c_value(meas->ms_c_value);
|
||||
if (meas->have_ms_sign_var)
|
||||
m_l1_meas.set_ms_sign_var(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)
|
||||
m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
|
||||
else
|
||||
m_l1_meas.ts[i].have_ms_i_level = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GprsCodingScheme GprsMs::current_cs_dl() const
|
||||
{
|
||||
GprsCodingScheme cs = m_current_cs_dl;
|
||||
size_t unencoded_octets;
|
||||
|
||||
if (!m_bts)
|
||||
return cs;
|
||||
|
||||
unencoded_octets = m_llc_queue.octets();
|
||||
|
||||
/* If the DL TBF is active, add number of unencoded chunk octets */
|
||||
if (m_dl_tbf)
|
||||
unencoded_octets += m_dl_tbf->m_llc.chunk_size();
|
||||
|
||||
/* There are many unencoded octets, don't reduce */
|
||||
if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
|
||||
return cs;
|
||||
|
||||
/* RF conditions are good, don't reduce */
|
||||
if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
|
||||
return cs;
|
||||
|
||||
/* The throughput would probably be better if the CS level was reduced */
|
||||
cs.dec(mode());
|
||||
|
||||
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
|
||||
if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
|
||||
cs.dec(mode());
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
int GprsMs::first_common_ts() const
|
||||
{
|
||||
if (m_dl_tbf)
|
||||
return m_dl_tbf->first_common_ts;
|
||||
|
||||
if (m_ul_tbf)
|
||||
return m_ul_tbf->first_common_ts;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t GprsMs::dl_slots() const
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
if (m_dl_tbf)
|
||||
slots |= m_dl_tbf->dl_slots();
|
||||
|
||||
if (m_ul_tbf)
|
||||
slots |= m_ul_tbf->dl_slots();
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
uint8_t GprsMs::ul_slots() const
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
if (m_dl_tbf)
|
||||
slots |= m_dl_tbf->ul_slots();
|
||||
|
||||
if (m_ul_tbf)
|
||||
slots |= m_ul_tbf->ul_slots();
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
uint8_t GprsMs::current_pacch_slots() const
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
|
||||
bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
|
||||
|
||||
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 = m_dl_tbf->dl_slots();
|
||||
else if (!is_dl_active && is_ul_active)
|
||||
slots = m_ul_tbf->ul_slots();
|
||||
else
|
||||
slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
|
||||
|
||||
/* Assume a multislot class 1 device */
|
||||
/* TODO: For class 2 devices, this could be removed */
|
||||
slots = pcu_lsb(slots);
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
|
||||
uint8_t ul_slots, uint8_t dl_slots)
|
||||
{
|
||||
if (m_current_trx) {
|
||||
m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
|
||||
m_reserved_dl_slots);
|
||||
m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
|
||||
m_reserved_ul_slots);
|
||||
m_reserved_dl_slots = 0;
|
||||
m_reserved_ul_slots = 0;
|
||||
}
|
||||
m_current_trx = trx;
|
||||
if (trx) {
|
||||
m_reserved_dl_slots = dl_slots;
|
||||
m_reserved_ul_slots = ul_slots;
|
||||
m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
|
||||
m_reserved_dl_slots);
|
||||
m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
|
||||
m_reserved_ul_slots);
|
||||
}
|
||||
}
|
||||
|
||||
gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
|
||||
{
|
||||
switch (dir) {
|
||||
case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
|
||||
case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
374
src/gprs_ms.h
374
src/gprs_ms.h
@@ -1,6 +1,6 @@
|
||||
/* gprs_ms.h
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* 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
|
||||
@@ -22,272 +22,238 @@
|
||||
|
||||
struct gprs_codel;
|
||||
|
||||
#include <gprs_coding_scheme.h>
|
||||
#include "cxx_linuxlist.h"
|
||||
#include "llc.h"
|
||||
#include "tbf.h"
|
||||
#include "tbf_ul.h"
|
||||
#include "tbf_dl.h"
|
||||
#include "pcu_l1_if.h"
|
||||
#include <gprs_coding_scheme.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
}
|
||||
#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>
|
||||
|
||||
struct BTS;
|
||||
struct gprs_rlcmac_trx;
|
||||
|
||||
class GprsMs {
|
||||
public:
|
||||
struct Callback {
|
||||
virtual void ms_idle(class GprsMs *) = 0;
|
||||
virtual void ms_active(class GprsMs *) = 0;
|
||||
};
|
||||
|
||||
class Guard {
|
||||
public:
|
||||
Guard(GprsMs *ms);
|
||||
~Guard();
|
||||
|
||||
bool is_idle() const;
|
||||
|
||||
private:
|
||||
GprsMs * const m_ms;
|
||||
};
|
||||
|
||||
GprsMs(BTS *bts, uint32_t tlli);
|
||||
~GprsMs();
|
||||
|
||||
void set_callback(Callback *cb) {m_cb = cb;}
|
||||
|
||||
void merge_old_ms(GprsMs *old_ms);
|
||||
|
||||
gprs_rlcmac_ul_tbf *ul_tbf() const {return m_ul_tbf;}
|
||||
gprs_rlcmac_dl_tbf *dl_tbf() const {return m_dl_tbf;}
|
||||
gprs_rlcmac_tbf *tbf(enum gprs_rlcmac_tbf_direction dir) const;
|
||||
uint32_t tlli() const;
|
||||
void set_tlli(uint32_t tlli);
|
||||
bool confirm_tlli(uint32_t tlli);
|
||||
bool check_tlli(uint32_t tlli);
|
||||
|
||||
void reset();
|
||||
GprsCodingScheme::Mode mode() const;
|
||||
void set_mode(GprsCodingScheme::Mode mode);
|
||||
|
||||
const char *imsi() const;
|
||||
void set_imsi(const char *imsi);
|
||||
|
||||
uint8_t ta() const;
|
||||
void set_ta(uint8_t ta);
|
||||
uint8_t ms_class() const;
|
||||
uint8_t egprs_ms_class() const;
|
||||
void set_ms_class(uint8_t ms_class);
|
||||
void set_egprs_ms_class(uint8_t ms_class);
|
||||
void set_current_cs_dl(GprsCodingScheme::Scheme scheme);
|
||||
|
||||
GprsCodingScheme current_cs_ul() const;
|
||||
GprsCodingScheme current_cs_dl() const;
|
||||
GprsCodingScheme max_cs_ul() const;
|
||||
GprsCodingScheme max_cs_dl() const;
|
||||
|
||||
int first_common_ts() const;
|
||||
uint8_t dl_slots() const;
|
||||
uint8_t ul_slots() const;
|
||||
uint8_t reserved_dl_slots() const;
|
||||
uint8_t reserved_ul_slots() const;
|
||||
uint8_t current_pacch_slots() const;
|
||||
gprs_rlcmac_trx *current_trx() const;
|
||||
void set_reserved_slots(gprs_rlcmac_trx *trx,
|
||||
uint8_t ul_slots, uint8_t dl_slots);
|
||||
|
||||
gprs_llc_queue *llc_queue();
|
||||
const gprs_llc_queue *llc_queue() const;
|
||||
gprs_codel *codel_state() const;
|
||||
|
||||
void set_timeout(unsigned secs);
|
||||
|
||||
void attach_tbf(gprs_rlcmac_tbf *tbf);
|
||||
void attach_ul_tbf(gprs_rlcmac_ul_tbf *tbf);
|
||||
void attach_dl_tbf(gprs_rlcmac_dl_tbf *tbf);
|
||||
|
||||
void detach_tbf(gprs_rlcmac_tbf *tbf);
|
||||
|
||||
void update_error_rate(gprs_rlcmac_tbf *tbf, int percent);
|
||||
|
||||
bool is_idle() const;
|
||||
bool need_dl_tbf() const;
|
||||
|
||||
void* operator new(size_t num);
|
||||
void operator delete(void* p);
|
||||
|
||||
LListHead<GprsMs>& list() {return this->m_list;}
|
||||
const LListHead<GprsMs>& list() const {return this->m_list;}
|
||||
const LListHead<gprs_rlcmac_tbf>& old_tbfs() const {return m_old_tbfs;}
|
||||
|
||||
void update_l1_meas(const pcu_l1_meas *meas);
|
||||
const pcu_l1_meas* l1_meas() const {return &m_l1_meas;};
|
||||
unsigned nack_rate_dl() const;
|
||||
unsigned dl_ctrl_msg() const;
|
||||
void update_dl_ctrl_msg();
|
||||
|
||||
/* internal use */
|
||||
static void timeout(void *priv_);
|
||||
|
||||
protected:
|
||||
void update_status();
|
||||
GprsMs *ref();
|
||||
void unref();
|
||||
void start_timer();
|
||||
void stop_timer();
|
||||
void update_cs_ul(const pcu_l1_meas*);
|
||||
|
||||
private:
|
||||
BTS *m_bts;
|
||||
Callback * m_cb;
|
||||
gprs_rlcmac_ul_tbf *m_ul_tbf;
|
||||
gprs_rlcmac_dl_tbf *m_dl_tbf;
|
||||
LListHead<gprs_rlcmac_tbf> m_old_tbfs;
|
||||
|
||||
uint32_t m_tlli;
|
||||
uint32_t m_new_ul_tlli;
|
||||
uint32_t m_new_dl_tlli;
|
||||
|
||||
/* store IMSI for look-up and PCH retransmission */
|
||||
char m_imsi[16];
|
||||
uint8_t m_ta;
|
||||
uint8_t m_ms_class;
|
||||
uint8_t m_egprs_ms_class;
|
||||
/* current coding scheme */
|
||||
GprsCodingScheme m_current_cs_ul;
|
||||
GprsCodingScheme m_current_cs_dl;
|
||||
|
||||
gprs_llc_queue m_llc_queue;
|
||||
|
||||
bool m_is_idle;
|
||||
int m_ref;
|
||||
LListHead<GprsMs> m_list;
|
||||
struct osmo_timer_list m_timer;
|
||||
unsigned m_delay;
|
||||
|
||||
int64_t m_last_cs_not_low;
|
||||
|
||||
pcu_l1_meas m_l1_meas;
|
||||
unsigned m_nack_rate_dl;
|
||||
uint8_t m_reserved_dl_slots;
|
||||
uint8_t m_reserved_ul_slots;
|
||||
gprs_rlcmac_trx *m_current_trx;
|
||||
|
||||
struct gprs_codel *m_codel_state;
|
||||
GprsCodingScheme::Mode m_mode;
|
||||
|
||||
unsigned m_dl_ctrl_msg;
|
||||
enum ms_counter_id {
|
||||
MS_CTR_DL_CTRL_MSG_SCHED,
|
||||
};
|
||||
|
||||
inline bool GprsMs::is_idle() const
|
||||
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 !m_ul_tbf && !m_dl_tbf && !m_ref && llist_empty(&m_old_tbfs);
|
||||
return !ms->ul_tbf && !ms->dl_tbf && !ms->ref && llist_empty(&ms->old_tbfs);
|
||||
}
|
||||
|
||||
inline bool GprsMs::need_dl_tbf() const
|
||||
static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
|
||||
{
|
||||
if (dl_tbf() != NULL && dl_tbf()->state_is_not(GPRS_RLCMAC_WAIT_RELEASE))
|
||||
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() > 0;
|
||||
return llc_queue_size(ms_llc_queue(ms)) > 0;
|
||||
}
|
||||
|
||||
inline uint32_t GprsMs::tlli() const
|
||||
static inline uint32_t ms_tlli(const struct GprsMs *ms)
|
||||
{
|
||||
return m_new_ul_tlli ? m_new_ul_tlli :
|
||||
m_tlli ? m_tlli :
|
||||
m_new_dl_tlli;
|
||||
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;
|
||||
}
|
||||
|
||||
inline bool GprsMs::check_tlli(uint32_t tlli)
|
||||
static inline bool ms_check_tlli(struct GprsMs *ms, uint32_t tlli)
|
||||
{
|
||||
return tlli != 0 &&
|
||||
(tlli == m_tlli || tlli == m_new_ul_tlli || tlli == m_new_dl_tlli);
|
||||
return tlli != GSM_RESERVED_TMSI &&
|
||||
(tlli == ms->tlli || tlli == ms->new_ul_tlli || tlli == ms->new_dl_tlli);
|
||||
}
|
||||
|
||||
inline const char *GprsMs::imsi() const
|
||||
static inline const char *ms_imsi(const struct GprsMs *ms)
|
||||
{
|
||||
return m_imsi;
|
||||
return ms->imsi;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::ta() const
|
||||
static inline bool ms_imsi_is_valid(const struct GprsMs *ms)
|
||||
{
|
||||
return m_ta;
|
||||
return ms->imsi[0] != '\0';
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::ms_class() const
|
||||
static inline uint8_t ms_ta(const struct GprsMs *ms)
|
||||
{
|
||||
return m_ms_class;
|
||||
return ms->ta;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::egprs_ms_class() const
|
||||
static inline uint8_t ms_ms_class(const struct GprsMs *ms)
|
||||
{
|
||||
return m_egprs_ms_class;
|
||||
return ms->ms_class;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme GprsMs::current_cs_ul() const
|
||||
static inline uint8_t ms_egprs_ms_class(const struct GprsMs *ms)
|
||||
{
|
||||
return m_current_cs_ul;
|
||||
return ms->egprs_ms_class;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::Mode GprsMs::mode() const
|
||||
static inline enum CodingScheme ms_current_cs_ul(const struct GprsMs *ms)
|
||||
{
|
||||
return m_mode;
|
||||
return ms->current_cs_ul;
|
||||
}
|
||||
|
||||
inline void GprsMs::set_timeout(unsigned secs)
|
||||
static inline enum mcs_kind ms_mode(const struct GprsMs *ms)
|
||||
{
|
||||
m_delay = secs;
|
||||
return ms->mode;
|
||||
}
|
||||
|
||||
inline gprs_llc_queue *GprsMs::llc_queue()
|
||||
static inline void ms_set_timeout(struct GprsMs *ms, unsigned secs)
|
||||
{
|
||||
return &m_llc_queue;
|
||||
ms->delay = secs;
|
||||
}
|
||||
|
||||
inline const gprs_llc_queue *GprsMs::llc_queue() const
|
||||
static inline struct gprs_codel *ms_codel_state(const struct GprsMs *ms)
|
||||
{
|
||||
return &m_llc_queue;
|
||||
return ms->codel_state;
|
||||
}
|
||||
|
||||
inline gprs_codel *GprsMs::codel_state() const
|
||||
static inline unsigned ms_nack_rate_dl(const struct GprsMs *ms)
|
||||
{
|
||||
return m_codel_state;
|
||||
return ms->nack_rate_dl;
|
||||
}
|
||||
|
||||
inline unsigned GprsMs::nack_rate_dl() const
|
||||
static inline uint8_t ms_reserved_dl_slots(const struct GprsMs *ms)
|
||||
{
|
||||
return m_nack_rate_dl;
|
||||
return ms->reserved_dl_slots;
|
||||
}
|
||||
|
||||
inline unsigned GprsMs::dl_ctrl_msg() const
|
||||
static inline uint8_t ms_reserved_ul_slots(const struct GprsMs *ms)
|
||||
{
|
||||
return m_dl_ctrl_msg;
|
||||
return ms->reserved_ul_slots;
|
||||
}
|
||||
|
||||
inline void GprsMs::update_dl_ctrl_msg()
|
||||
static inline struct gprs_rlcmac_trx *ms_current_trx(const struct GprsMs *ms)
|
||||
{
|
||||
m_dl_ctrl_msg++;
|
||||
return ms->current_trx;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::reserved_dl_slots() const
|
||||
{
|
||||
return m_reserved_dl_slots;
|
||||
}
|
||||
#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)
|
||||
|
||||
inline uint8_t GprsMs::reserved_ul_slots() const
|
||||
{
|
||||
return m_reserved_ul_slots;
|
||||
}
|
||||
|
||||
inline gprs_rlcmac_trx *GprsMs::current_trx() const
|
||||
{
|
||||
return m_current_trx;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,13 +26,32 @@
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
}
|
||||
|
||||
#define GPRS_UNDEFINED_IMSI "000"
|
||||
static void ms_storage_ms_idle_cb(struct GprsMs *ms)
|
||||
{
|
||||
llist_del(&ms->list);
|
||||
if (ms->bts)
|
||||
bts_stat_item_add(ms->bts, STAT_MS_PRESENT, -1);
|
||||
if (ms_is_idle(ms))
|
||||
talloc_free(ms);
|
||||
}
|
||||
|
||||
GprsMsStorage::GprsMsStorage(BTS *bts) :
|
||||
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()
|
||||
@@ -42,50 +61,36 @@ GprsMsStorage::~GprsMsStorage()
|
||||
|
||||
void GprsMsStorage::cleanup()
|
||||
{
|
||||
LListHead<GprsMs> *pos, *tmp;
|
||||
struct llist_head *pos, *tmp;
|
||||
|
||||
llist_for_each_safe(pos, tmp, &m_list) {
|
||||
GprsMs *ms = pos->entry();
|
||||
ms->set_callback(NULL);
|
||||
ms_idle(ms);
|
||||
struct GprsMs *ms = llist_entry(pos, typeof(*ms), list);
|
||||
ms_set_callback(ms, NULL);
|
||||
ms_storage_ms_idle_cb(ms);
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMsStorage::ms_idle(class GprsMs *ms)
|
||||
{
|
||||
llist_del(&ms->list());
|
||||
if (m_bts)
|
||||
m_bts->ms_present(m_bts->ms_present_get() - 1);
|
||||
if (ms->is_idle())
|
||||
delete ms;
|
||||
}
|
||||
|
||||
void GprsMsStorage::ms_active(class GprsMs *ms)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi) const
|
||||
{
|
||||
struct llist_head *tmp;
|
||||
GprsMs *ms;
|
||||
LListHead<GprsMs> *pos;
|
||||
|
||||
if (tlli || old_tlli) {
|
||||
llist_for_each(pos, &m_list) {
|
||||
ms = pos->entry();
|
||||
if (ms->check_tlli(tlli))
|
||||
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(old_tlli))
|
||||
if (ms_check_tlli(ms, old_tlli))
|
||||
return ms;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found by TLLI */
|
||||
|
||||
if (imsi && imsi[0] && strcmp(imsi, GPRS_UNDEFINED_IMSI) != 0) {
|
||||
llist_for_each(pos, &m_list) {
|
||||
ms = pos->entry();
|
||||
if (strcmp(imsi, ms->imsi()) == 0)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -97,29 +102,12 @@ GprsMs *GprsMsStorage::create_ms()
|
||||
{
|
||||
GprsMs *ms;
|
||||
|
||||
ms = new GprsMs(m_bts, 0);
|
||||
ms = ms_alloc(m_bts, GSM_RESERVED_TMSI);
|
||||
|
||||
ms->set_callback(this);
|
||||
llist_add(&ms->list(), &m_list);
|
||||
ms_set_callback(ms, &ms_storage_ms_cb);
|
||||
llist_add(&ms->list, &m_list);
|
||||
if (m_bts)
|
||||
m_bts->ms_present(m_bts->ms_present_get() + 1);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
GprsMs *GprsMsStorage::create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
|
||||
{
|
||||
GprsMs *ms = get_ms(tlli);
|
||||
|
||||
if (ms)
|
||||
return ms;
|
||||
|
||||
ms = create_ms();
|
||||
|
||||
if (dir == GPRS_RLCMAC_UL_TBF)
|
||||
ms->set_tlli(tlli);
|
||||
else
|
||||
ms->confirm_tlli(tlli);
|
||||
bts_stat_item_add(m_bts, STAT_MS_PRESENT, 1);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
@@ -21,29 +21,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "gprs_ms.h"
|
||||
#include "cxx_linuxlist.h"
|
||||
#include "tbf.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct BTS;
|
||||
struct gprs_rlcmac_bts;
|
||||
|
||||
class GprsMsStorage : public GprsMs::Callback {
|
||||
struct GprsMsStorage {
|
||||
public:
|
||||
GprsMsStorage(BTS *bts);
|
||||
GprsMsStorage(struct gprs_rlcmac_bts *bts);
|
||||
~GprsMsStorage();
|
||||
|
||||
void cleanup();
|
||||
|
||||
virtual void ms_idle(class GprsMs *);
|
||||
virtual void ms_active(class GprsMs *);
|
||||
|
||||
GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = 0, const char *imsi = 0) const;
|
||||
GprsMs *create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir);
|
||||
GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = GSM_RESERVED_TMSI, const char *imsi = NULL) const;
|
||||
GprsMs *create_ms();
|
||||
|
||||
const LListHead<GprsMs>& ms_list() const {return m_list;}
|
||||
const struct llist_head* ms_list() const {return &m_list;}
|
||||
private:
|
||||
BTS *m_bts;
|
||||
LListHead<GprsMs> m_list;
|
||||
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);
|
||||
@@ -18,7 +18,11 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
}
|
||||
|
||||
#include <pcu_l1_if.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <bts.h>
|
||||
@@ -28,17 +32,47 @@
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
|
||||
const char *imsi)
|
||||
int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi,
|
||||
uint16_t pgroup)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH)\n");
|
||||
bitvec *paging_request = bitvec_alloc(23, tall_pcu_ctx);
|
||||
bitvec_unhex(paging_request, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
int plen = Encoding::write_paging_request(paging_request, ptmsi, ptmsi_len);
|
||||
pcu_l1if_tx_pch(paging_request, plen, (char *)imsi);
|
||||
if (log_check_level(DRLCMAC, LOGL_NOTICE)) {
|
||||
char str[64];
|
||||
osmo_mobile_identity_to_str_buf(str, sizeof(str), mi);
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH) MI=%s\n", str);
|
||||
}
|
||||
bitvec *paging_request = bitvec_alloc(22, tall_pcu_ctx);
|
||||
bitvec_unhex(paging_request, DUMMY_VEC);
|
||||
int plen = Encoding::write_paging_request(paging_request, mi);
|
||||
if (plen <= 0) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "TX: [PCU -> BTS] Failed to encode Paging Request\n");
|
||||
return -1;
|
||||
}
|
||||
bts_do_rate_ctr_inc(bts, CTR_PCH_REQUESTS);
|
||||
pcu_l1if_tx_pch(bts, paging_request, plen, pgroup);
|
||||
bitvec_free(paging_request);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Encode Application Information Request to Packet Application Information (3GPP TS 44.060 11.2.47) */
|
||||
struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req) {
|
||||
struct msgb *msg;
|
||||
uint16_t msgb_len = req->len + 1;
|
||||
struct bitvec bv = {0, msgb_len, NULL};
|
||||
const enum bit_value page_mode[] = {ZERO, ZERO}; /* Normal Paging (3GPP TS 44.060 12.20) */
|
||||
|
||||
if (!req->len) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "Application Information Request with zero length received!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = msgb_alloc(msgb_len, "app_info_msg");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
bv.data = msgb_put(msg, msgb_len);
|
||||
bitvec_set_bits(&bv, page_mode, 2);
|
||||
bitvec_set_uint(&bv, req->application_type, 4);
|
||||
bitvec_set_bytes(&bv, req->data, req->len);
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -17,20 +17,25 @@
|
||||
* 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
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pcu_l1_if.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <gsm_rlcmac.h>
|
||||
#include <gsm_timer.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -40,6 +45,7 @@ extern "C" {
|
||||
*/
|
||||
//#define DEBUG_DL_ASS_IDLE
|
||||
|
||||
#define DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
|
||||
|
||||
struct gprs_rlcmac_tbf;
|
||||
struct gprs_rlcmac_bts;
|
||||
@@ -72,7 +78,7 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
|
||||
|
||||
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr);
|
||||
int gprs_rlcmac_meas_rep(GprsMs *ms, Packet_Measurement_Report_t *pmr);
|
||||
|
||||
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
|
||||
|
||||
@@ -83,35 +89,35 @@ 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_tx_ul_ud(gprs_rlcmac_tbf *tbf);
|
||||
|
||||
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
|
||||
const char *imsi);
|
||||
struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req);
|
||||
|
||||
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_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts,
|
||||
uint8_t ms_class = 0);
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
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_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
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 GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
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
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/timer_compat.h>
|
||||
}
|
||||
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <tbf.h>
|
||||
#include <tbf_dl.h>
|
||||
#include <gprs_ms.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
@@ -33,14 +37,13 @@
|
||||
/* TODO: trigger the measurement report from the pollcontroller and use it for flow control */
|
||||
|
||||
/* received Measurement Report */
|
||||
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr)
|
||||
int gprs_rlcmac_meas_rep(GprsMs *ms, Packet_Measurement_Report_t *pmr)
|
||||
{
|
||||
NC_Measurement_Report_t *ncr;
|
||||
NC_Measurements_t *nc;
|
||||
int i;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "Measuement Report of TLLI=0x%08x:",
|
||||
pmr->TLLI);
|
||||
LOGPMS(ms, DRLCMACMEAS, LOGL_INFO, "Rx Measurement Report:");
|
||||
|
||||
switch (pmr->UnionType) {
|
||||
case 0:
|
||||
@@ -102,8 +105,8 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
|
||||
if (!tbf->meas.rssi_num)
|
||||
return -EINVAL;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "UL RSSI of TLLI=0x%08x: %d dBm\n",
|
||||
tbf->tlli(), tbf->meas.rssi_sum / tbf->meas.rssi_num);
|
||||
LOGPMS(tbf->ms(), DRLCMACMEAS, LOGL_INFO, "UL RSSI: %d dBm\n",
|
||||
tbf->meas.rssi_sum / tbf->meas.rssi_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -131,6 +134,7 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||
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;
|
||||
@@ -188,4 +192,3 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
* 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>
|
||||
@@ -33,66 +34,57 @@ extern "C" {
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
}
|
||||
|
||||
static uint32_t sched_poll(BTS *bts,
|
||||
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_ul_tbf **ul_ack_tbf)
|
||||
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_ul_tbf *ul_tbf;
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
LListHead<gprs_rlcmac_tbf> *pos;
|
||||
uint32_t poll_fn;
|
||||
struct llist_item *pos;
|
||||
|
||||
/* check special TBF for events */
|
||||
poll_fn = fn + 4;
|
||||
if ((block_nr % 3) == 2)
|
||||
poll_fn ++;
|
||||
poll_fn = poll_fn % GSM_MAX_FN;
|
||||
llist_for_each(pos, &bts->ul_tbfs()) {
|
||||
ul_tbf = as_ul_tbf(pos->entry());
|
||||
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->trx->trx_no != trx || !ul_tbf->is_control_ts(ts))
|
||||
if (!ul_tbf->is_control_ts(pdch->ts_no))
|
||||
continue;
|
||||
/* polling for next uplink block */
|
||||
if (ul_tbf->poll_scheduled() && ul_tbf->poll_fn == poll_fn)
|
||||
*poll_tbf = ul_tbf;
|
||||
if (ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_SEND_ACK))
|
||||
*ul_ack_tbf = ul_tbf;
|
||||
if (ul_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
|
||||
*dl_ass_tbf = ul_tbf;
|
||||
if (ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
|| ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
|
||||
*ul_ass_tbf = ul_tbf;
|
||||
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(pos, &bts->dl_tbfs()) {
|
||||
dl_tbf = as_dl_tbf(pos->entry());
|
||||
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->trx->trx_no != trx || !dl_tbf->is_control_ts(ts))
|
||||
if (!dl_tbf->is_control_ts(pdch->ts_no))
|
||||
continue;
|
||||
/* polling for next uplink block */
|
||||
if (dl_tbf->poll_scheduled() && dl_tbf->poll_fn == poll_fn)
|
||||
*poll_tbf = dl_tbf;
|
||||
if (dl_tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_SEND_ASS))
|
||||
*dl_ass_tbf = dl_tbf;
|
||||
if (dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
|| dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
|
||||
*ul_ass_tbf = dl_tbf;
|
||||
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;
|
||||
}
|
||||
|
||||
return poll_fn;
|
||||
}
|
||||
|
||||
static uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
|
||||
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 usf = 0x07;
|
||||
uint8_t i, tfi;
|
||||
|
||||
/* select uplink resource */
|
||||
@@ -106,65 +98,94 @@ static uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
/* 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_is_not(GPRS_RLCMAC_FLOW))
|
||||
if (tbf->state_is_not(TBF_ST_FLOW))
|
||||
continue;
|
||||
|
||||
/* use this USF */
|
||||
usf = tbf->m_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 resource of UL TFI=%d\n", trx, ts, fn,
|
||||
block_nr, usf, tfi);
|
||||
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;
|
||||
}
|
||||
|
||||
static 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_ul_tbf *ul_ack_tbf)
|
||||
struct msgb *sched_app_info(struct gprs_rlcmac_tbf *tbf) {
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct msgb *msg = NULL;
|
||||
|
||||
if (!tbf || !tbf->ms()->app_info_pending)
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
struct gprs_rlcmac_tbf *next_list[3] = { ul_ass_tbf, dl_ass_tbf, ul_ack_tbf };
|
||||
struct gprs_rlcmac_tbf *next_list[] = { tbfs->ul_ass,
|
||||
tbfs->dl_ass,
|
||||
tbfs->ul_ack,
|
||||
tbfs->nacc };
|
||||
uint8_t ts = pdch->ts_no;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
|
||||
tbf = next_list[(pdch->next_ctrl_prio + i) % 3];
|
||||
if (!tbf)
|
||||
continue;
|
||||
/* Send Packet Application Information first (ETWS primary notifications) */
|
||||
msg = sched_app_info(tbfs->dl_ass);
|
||||
|
||||
/*
|
||||
* 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 == ul_ass_tbf && tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
|
||||
msg = ul_ass_tbf->create_packet_access_reject();
|
||||
else if (tbf == ul_ass_tbf && tbf->direction ==
|
||||
GPRS_RLCMAC_DL_TBF)
|
||||
if (tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
|
||||
msg = ul_ass_tbf->create_packet_access_reject();
|
||||
else
|
||||
msg = ul_ass_tbf->create_ul_ass(fn, ts);
|
||||
else if (tbf == dl_ass_tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
msg = dl_ass_tbf->create_dl_ass(fn, ts);
|
||||
else if (tbf == ul_ack_tbf)
|
||||
msg = ul_ack_tbf->create_ul_ack(fn, ts);
|
||||
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;
|
||||
|
||||
if (!msg) {
|
||||
tbf = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
pdch->next_ctrl_prio += 1;
|
||||
pdch->next_ctrl_prio %= 3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!msg) {
|
||||
@@ -173,37 +194,36 @@ static struct msgb *sched_select_ctrl_msg(
|
||||
* MS will kill the current TBF, only one of them can be
|
||||
* non-NULL
|
||||
*/
|
||||
if (dl_ass_tbf) {
|
||||
tbf = dl_ass_tbf;
|
||||
msg = dl_ass_tbf->create_dl_ass(fn, ts);
|
||||
} else if (ul_ass_tbf) {
|
||||
tbf = ul_ass_tbf;
|
||||
msg = ul_ass_tbf->create_ul_ass(fn, ts);
|
||||
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) {
|
||||
LOGP(DRLCMACSCHED, LOGL_ERROR,
|
||||
"Control message to be scheduled, but no TBF (TRX=%d, TS=%d)\n", trx, ts);
|
||||
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();
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
|
||||
"message at RTS for %s (TRX=%d, TS=%d)\n",
|
||||
tbf_name(tbf), trx, ts);
|
||||
/* Updates the dl ctrl msg counter for ms */
|
||||
tbf->ms()->update_dl_ctrl_msg();
|
||||
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) {
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
|
||||
"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
|
||||
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "FN=%" PRIu32
|
||||
" Scheduling paging request message at RTS\n", fn);
|
||||
return msg;
|
||||
}
|
||||
|
||||
@@ -213,17 +233,19 @@ static struct msgb *sched_select_ctrl_msg(
|
||||
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 = tbf->window();
|
||||
int age_thresh1 = msecs_to_frames(200),
|
||||
age_thresh2 = msecs_to_frames(OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec));
|
||||
const gprs_rlc_dl_window *w = static_cast<gprs_rlc_dl_window *>(tbf->window());
|
||||
unsigned long msecs_t3190 = osmo_tdef_get(the_pcu->T_defs, 3190, OSMO_TDEF_MS, -1);
|
||||
unsigned long dl_tbf_idle_msec = osmo_tdef_get(the_pcu->T_defs, -2031, OSMO_TDEF_MS, -1);
|
||||
int age_thresh1 = msecs_to_frames(200);
|
||||
int age_thresh2 = msecs_to_frames(OSMO_MIN(msecs_t3190/2, dl_tbf_idle_msec));
|
||||
|
||||
if (tbf->is_control_ts(ts) && tbf->need_control_ts())
|
||||
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(GPRS_RLCMAC_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
|
||||
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))
|
||||
@@ -235,13 +257,39 @@ static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts
|
||||
return DL_PRIO_NONE;
|
||||
}
|
||||
|
||||
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
|
||||
/* 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;
|
||||
@@ -257,12 +305,12 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
|
||||
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
|
||||
continue;
|
||||
/* no DL resources needed, go next */
|
||||
if (tbf->state_is_not(GPRS_RLCMAC_FLOW)
|
||||
&& tbf->state_is_not(GPRS_RLCMAC_FINISHED))
|
||||
if (tbf->state_is_not(TBF_ST_FLOW)
|
||||
&& tbf->state_is_not(TBF_ST_FINISHED))
|
||||
continue;
|
||||
|
||||
/* waiting for CCCH IMM.ASS confirm */
|
||||
if (tbf->m_wait_confirm)
|
||||
/* 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);
|
||||
@@ -272,6 +320,16 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
|
||||
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;
|
||||
@@ -281,13 +339,14 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
|
||||
}
|
||||
|
||||
if (prio_tbf) {
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
|
||||
"RTS for DL TFI=%d (TRX=%d, TS=%d) prio=%d\n",
|
||||
prio_tfi, trx, ts, max_prio);
|
||||
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 = prio_tbf->create_dl_acked_block(fn, ts);
|
||||
msg = prio_tbf->create_dl_acked_block(fn, ts, req_mcs_kind);
|
||||
*is_egprs = prio_tbf->is_egprs_enabled();
|
||||
}
|
||||
|
||||
return msg;
|
||||
@@ -313,7 +372,7 @@ static struct msgb *sched_dummy(void)
|
||||
return msg;
|
||||
}
|
||||
|
||||
static inline void tap_n_acc(const struct msgb *msg, const struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
|
||||
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)
|
||||
@@ -321,19 +380,19 @@ static inline void tap_n_acc(const struct msgb *msg, const struct gprs_rlcmac_bt
|
||||
|
||||
switch(cat) {
|
||||
case PCU_GSMTAP_C_DL_CTRL:
|
||||
bts->bts->rlc_sent_control();
|
||||
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
|
||||
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:
|
||||
bts->bts->rlc_sent();
|
||||
/* FIXME: distinguish between GPRS and EGPRS */
|
||||
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DATA_GPRS, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
|
||||
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->bts->rlc_sent_dummy();
|
||||
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
|
||||
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:
|
||||
@@ -346,79 +405,138 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
|
||||
*ul_ass_tbf = NULL;
|
||||
struct gprs_rlcmac_ul_tbf *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, sba_fn;
|
||||
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->is_enabled()) {
|
||||
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
|
||||
"TRX=%d TS=%d\n", trx, ts);
|
||||
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(bts->bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
|
||||
&dl_ass_tbf, &ul_ack_tbf);
|
||||
/* check uplink resource 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\n", trx, ts, fn,
|
||||
block_nr, poll_fn,
|
||||
tbf_name(poll_tbf));
|
||||
/* use free USF */
|
||||
/* else. check for sba */
|
||||
else if ((sba_fn = bts->bts->sba()->sched(trx, ts, fn, block_nr) != 0xffffffff))
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
|
||||
"TS=%d FN=%d block_nr=%d scheduling free USF for "
|
||||
"single block allocation at FN=%d\n", trx, ts, fn,
|
||||
block_nr, sba_fn);
|
||||
/* use free USF */
|
||||
/* else, we search for uplink resource */
|
||||
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);
|
||||
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_CTRL);
|
||||
|
||||
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(bts, trx, ts, fn, block_nr, pdch);
|
||||
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_DATA_GPRS);
|
||||
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) {
|
||||
/* increase counter */
|
||||
msg = sched_dummy();
|
||||
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_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 */
|
||||
bts->bts->rlc_dl_bytes(msg->data_len);
|
||||
|
||||
/* set USF */
|
||||
OSMO_ASSERT(msgb_length(msg) > 0);
|
||||
msg->data[0] = (msg->data[0] & 0xf8) | usf;
|
||||
|
||||
/* Used to measure the leak rate, count all blocks */
|
||||
gprs_bssgp_update_frames_sent();
|
||||
|
||||
/* send PDTCH/PACCH to L1 */
|
||||
pcu_l1if_tx_pdtch(msg, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
|
||||
if (msg) {
|
||||
/* msg is now available */
|
||||
bts_do_rate_ctr_add(bts, CTR_RLC_DL_BYTES, msgb_length(msg));
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <gprs_debug.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
#include <tbf_ul.h>
|
||||
#include <pdch.h>
|
||||
#include <gprs_ms.h>
|
||||
#include <pcu_utils.h>
|
||||
@@ -40,6 +41,13 @@ extern "C" {
|
||||
/* Consider a PDCH as idle if has at most this number of TBFs assigned to it */
|
||||
#define PDCH_IDLE_TBF_THRESH 1
|
||||
|
||||
#define LOGPSL(tbf, level, fmt, args...) LOGP(DRLCMAC, level, "[%s] " fmt, \
|
||||
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL", ## args)
|
||||
|
||||
#define LOGPAL(tbf, kind, single, trx_n, level, fmt, args...) LOGPSL(tbf, level, \
|
||||
"algo %s <%s> (suggested TRX: %d): " fmt, \
|
||||
kind, single ? "single" : "multi", trx_n, ## args)
|
||||
|
||||
static char *set_flag_chars(char *buf, uint8_t val, char set_char, char unset_char = 0)
|
||||
{
|
||||
int i;
|
||||
@@ -139,7 +147,7 @@ static int compute_usage_for_algo_a(const struct gprs_rlcmac_pdch *pdch, enum gp
|
||||
*/
|
||||
static int find_least_busy_pdch(const struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t mask,
|
||||
int (*fn)(const struct gprs_rlcmac_pdch *, enum gprs_rlcmac_tbf_direction dir),
|
||||
int *free_tfi = 0, int *free_usf = 0)
|
||||
int *free_tfi = NULL, int *free_usf = NULL)
|
||||
{
|
||||
unsigned ts;
|
||||
int min_used = INT_MAX;
|
||||
@@ -231,21 +239,21 @@ static void assign_dlink_tbf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_d
|
||||
attach_tbf_to_pdch(pdch, tbf);
|
||||
}
|
||||
|
||||
static int find_trx(const struct gprs_rlcmac_bts *bts_data, const GprsMs *ms, int8_t use_trx)
|
||||
static int find_trx(const struct gprs_rlcmac_bts *bts, const GprsMs *ms, int8_t use_trx)
|
||||
{
|
||||
unsigned trx_no;
|
||||
unsigned ts;
|
||||
|
||||
/* We must use the TRX currently actively used by an MS */
|
||||
if (ms && ms->current_trx())
|
||||
return ms->current_trx()->trx_no;
|
||||
if (ms && ms_current_trx(ms))
|
||||
return ms_current_trx(ms)->trx_no;
|
||||
|
||||
if (use_trx >= 0 && use_trx < 8)
|
||||
return use_trx;
|
||||
|
||||
/* Find the first TRX that has a PDCH with a free UL and DL TFI */
|
||||
for (trx_no = 0; trx_no < ARRAY_SIZE(bts_data->trx); trx_no += 1) {
|
||||
const struct gprs_rlcmac_trx *trx = &bts_data->trx[trx_no];
|
||||
for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
|
||||
const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
|
||||
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
||||
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
|
||||
if (!pdch->is_enabled())
|
||||
@@ -264,14 +272,14 @@ static int find_trx(const struct gprs_rlcmac_bts *bts_data, const GprsMs *ms, in
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts_data)
|
||||
static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts)
|
||||
{
|
||||
unsigned trx_no;
|
||||
unsigned ts;
|
||||
|
||||
/* Find the first PDCH with an unused DL TS */
|
||||
for (trx_no = 0; trx_no < ARRAY_SIZE(bts_data->trx); trx_no += 1) {
|
||||
const struct gprs_rlcmac_trx *trx = &bts_data->trx[trx_no];
|
||||
for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
|
||||
const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
|
||||
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
||||
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
|
||||
if (!pdch->is_enabled())
|
||||
@@ -290,20 +298,21 @@ static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts_data)
|
||||
/*! Return free TFI
|
||||
*
|
||||
* \param[in] bts Pointer to BTS struct
|
||||
* \param[in] trx Optional pointer to TRX struct
|
||||
* \param[in] ms Pointer to MS object
|
||||
* \param[in] dir DL or UL direction
|
||||
* \param[in] use_trx which TRX to use or -1 if it should be selected based on what MS uses
|
||||
* \param[out] trx_no_ TRX number on which TFI was found
|
||||
* \returns negative error code or 0 on success
|
||||
*/
|
||||
static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsMs *ms,
|
||||
static int tfi_find_free(const struct gprs_rlcmac_bts *bts, const GprsMs *ms,
|
||||
enum gprs_rlcmac_tbf_direction dir, int8_t use_trx, uint8_t *trx_no_)
|
||||
{
|
||||
const struct gprs_rlcmac_trx *trx;
|
||||
int tfi;
|
||||
uint8_t trx_no;
|
||||
|
||||
if (trx) {
|
||||
/* If MS is already doing stuff on a TRX, set use_trx to it: */
|
||||
if ((trx = ms_current_trx(ms))) {
|
||||
if (use_trx >= 0 && use_trx != trx->trx_no) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "- Requested incompatible TRX %d (current is %d)\n",
|
||||
use_trx, trx->trx_no);
|
||||
@@ -312,10 +321,7 @@ static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsM
|
||||
use_trx = trx->trx_no;
|
||||
}
|
||||
|
||||
if (use_trx == -1 && ms->current_trx())
|
||||
use_trx = ms->current_trx()->trx_no;
|
||||
|
||||
tfi = bts->tfi_find_free(dir, &trx_no, use_trx);
|
||||
tfi = bts_tfi_find_free(bts, dir, &trx_no, use_trx);
|
||||
if (tfi < 0)
|
||||
return -EBUSY;
|
||||
|
||||
@@ -330,13 +336,12 @@ static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsM
|
||||
* Assign single slot for uplink and downlink
|
||||
*
|
||||
* \param[in,out] bts Pointer to BTS struct
|
||||
* \param[in,out] ms_ Pointer to MS object
|
||||
* \param[in,out] tbf_ Pointer to TBF struct
|
||||
* \param[in,out] tbf Pointer to TBF struct
|
||||
* \param[in] single flag indicating if we should force single-slot allocation
|
||||
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
|
||||
* \returns negative error code or 0 on success
|
||||
*/
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
int8_t use_trx)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
@@ -347,26 +352,24 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
|
||||
int usf = -1;
|
||||
uint8_t mask = 0xff;
|
||||
const char *mask_reason = NULL;
|
||||
const GprsMs *ms = ms_;
|
||||
const gprs_rlcmac_tbf *tbf = tbf_;
|
||||
gprs_rlcmac_trx *trx = ms->current_trx();
|
||||
struct GprsMs *ms = tbf->ms();
|
||||
gprs_rlcmac_trx *trx = ms_current_trx(ms);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm A) for class "
|
||||
"%d\n", tbf->ms_class());
|
||||
LOGPAL(tbf, "A", single, use_trx, LOGL_DEBUG, "Alloc start\n");
|
||||
|
||||
trx_no = find_trx(bts, ms, use_trx);
|
||||
if (trx_no < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"- Failed to find a usable TRX (TFI exhausted)\n");
|
||||
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
|
||||
"failed to find a usable TRX (TFI exhausted)\n");
|
||||
return trx_no;
|
||||
}
|
||||
if (!trx)
|
||||
trx = &bts->trx[trx_no];
|
||||
|
||||
dl_slots = ms->reserved_dl_slots();
|
||||
ul_slots = ms->reserved_ul_slots();
|
||||
dl_slots = ms_reserved_dl_slots(ms);
|
||||
ul_slots = ms_reserved_ul_slots(ms);
|
||||
|
||||
ts = ms->first_common_ts();
|
||||
ts = ms_first_common_ts(ms);
|
||||
|
||||
if (ts >= 0) {
|
||||
mask_reason = "need to reuse TS";
|
||||
@@ -385,40 +388,38 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
|
||||
&tfi, &usf);
|
||||
|
||||
if (tbf->direction == GPRS_RLCMAC_UL_TBF && usf < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
|
||||
"to allocate a TS, no USF available\n");
|
||||
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
|
||||
"failed to allocate a TS, no USF available\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (ts < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
|
||||
"to allocate a TS, no TFI available\n");
|
||||
LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
|
||||
"failed to allocate a TS, no TFI available\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
pdch = &trx->pdch[ts];
|
||||
|
||||
/* The allocation will be successful, so the system state and tbf_/ms_
|
||||
/* The allocation will be successful, so the system state and tbf/ms
|
||||
* may be modified from now on. */
|
||||
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf_);
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink TS=%d TFI=%d USF=%d\n",
|
||||
ts, tfi, usf);
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
|
||||
LOGPSL(tbf, LOGL_DEBUG, "Assign uplink TS=%d TFI=%d USF=%d\n", ts, tfi, usf);
|
||||
assign_uplink_tbf_usf(pdch, ul_tbf, tfi, usf);
|
||||
} else {
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf_);
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d TFI=%d\n",
|
||||
ts, tfi);
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
|
||||
LOGPSL(tbf, LOGL_DEBUG, "Assign downlink TS=%d TFI=%d\n", ts, tfi);
|
||||
assign_dlink_tbf(pdch, dl_tbf, tfi);
|
||||
}
|
||||
|
||||
tbf_->trx = trx;
|
||||
tbf->trx = trx;
|
||||
/* the only one TS is the common TS */
|
||||
tbf_->first_ts = tbf_->first_common_ts = ts;
|
||||
ms_->set_reserved_slots(trx, 1 << ts, 1 << ts);
|
||||
tbf->first_ts = tbf->first_common_ts = ts;
|
||||
ms_set_reserved_slots(ms, trx, 1 << ts, 1 << ts);
|
||||
|
||||
tbf_->upgrade_to_multislot = 0;
|
||||
bts->bts->tbf_alloc_algo_a();
|
||||
tbf->upgrade_to_multislot = false;
|
||||
bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_A);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -511,9 +512,11 @@ static bool skip_slot(uint8_t mslot_class, bool check_tr,
|
||||
*/
|
||||
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots)
|
||||
{
|
||||
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 */
|
||||
max_slots, num_tx, mask_sel, pdch_slots, ul_ts, dl_ts;
|
||||
const uint8_t Rx = mslot_class_get_rx(mslot_class), /* Max number of Rx slots */
|
||||
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);
|
||||
uint8_t max_slots, num_rx, num_tx, mask_sel, pdch_slots, ul_ts, dl_ts;
|
||||
int16_t rx_window, tx_window;
|
||||
char slot_info[9] = {0};
|
||||
int max_capacity = -1;
|
||||
@@ -529,7 +532,7 @@ int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_slots = OSMO_MAX(mslot_class_get_rx(mslot_class), Tx);
|
||||
max_slots = OSMO_MAX(Rx, Tx);
|
||||
|
||||
if (*dl_slots == 0)
|
||||
*dl_slots = 0xff;
|
||||
@@ -551,81 +554,97 @@ int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *
|
||||
/* Check for each UL (TX) slot */
|
||||
|
||||
/* Iterate through possible numbers of TX slots */
|
||||
for (num_tx = 1; num_tx <= mslot_class_get_tx(mslot_class); num_tx += 1) {
|
||||
for (num_tx = 1; num_tx <= Tx; num_tx += 1) {
|
||||
uint16_t tx_valid_win = (1 << num_tx) - 1;
|
||||
uint8_t rx_mask[MASK_TR + 1];
|
||||
|
||||
mslot_fill_rx_mask(mslot_class, num_tx, rx_mask);
|
||||
|
||||
/* Rotate group of TX slots: UUU-----, -UUU----, ..., UU-----U */
|
||||
for (ul_ts = 0; ul_ts < 8; ul_ts += 1, tx_valid_win <<= 1) {
|
||||
uint16_t rx_valid_win;
|
||||
uint32_t checked_rx[256/32] = {0};
|
||||
/* Rotate group of TX slots: UUU-----, -UUU----, ..., UU-----U */
|
||||
for (ul_ts = 0; ul_ts < 8; ul_ts += 1, tx_valid_win <<= 1) {
|
||||
uint16_t rx_valid_win;
|
||||
uint32_t checked_rx[256/32] = {0};
|
||||
|
||||
/* Wrap valid window */
|
||||
tx_valid_win = mslot_wrap_window(tx_valid_win);
|
||||
/* Wrap valid window */
|
||||
tx_valid_win = mslot_wrap_window(tx_valid_win);
|
||||
|
||||
tx_window = tx_valid_win;
|
||||
/* for multislot type 1: don't split the window to wrap around.
|
||||
* E.g. 'UU-----U' is invalid for a 4 TN window. Except 8 TN window.
|
||||
* See 45.002 B.1 */
|
||||
if (Type == 1 && num_tx < 8 &&
|
||||
tx_valid_win & (1 << 0) && tx_valid_win & (1 << 7))
|
||||
continue;
|
||||
|
||||
/* Filter out unavailable slots */
|
||||
tx_window &= *ul_slots;
|
||||
tx_window = tx_valid_win;
|
||||
|
||||
/* Skip if the the first TS (ul_ts) is not in the set */
|
||||
if ((tx_window & (1 << ul_ts)) == 0)
|
||||
continue;
|
||||
/* Filter out unavailable slots */
|
||||
tx_window &= *ul_slots;
|
||||
|
||||
/* Skip if the the last TS (ul_ts+num_tx-1) is not in the set */
|
||||
if ((tx_window & (1 << ((ul_ts+num_tx-1) % 8))) == 0)
|
||||
continue;
|
||||
/* Skip if the the first TS (ul_ts) is not in the set */
|
||||
if ((tx_window & (1 << ul_ts)) == 0)
|
||||
continue;
|
||||
|
||||
rx_valid_win = (1 << OSMO_MIN(mslot_class_get_rx(mslot_class), Sum - num_tx)) - 1;
|
||||
/* Skip if the the last TS (ul_ts+num_tx-1) is not in the set */
|
||||
if ((tx_window & (1 << ((ul_ts+num_tx-1) % 8))) == 0)
|
||||
continue;
|
||||
|
||||
/* Rotate group of RX slots: DDD-----, -DDD----, ..., DD-----D */
|
||||
for (dl_ts = 0; dl_ts < 8; dl_ts += 1, rx_valid_win <<= 1) {
|
||||
/* Wrap valid window */
|
||||
rx_valid_win = (rx_valid_win | rx_valid_win >> 8) & 0xff;
|
||||
num_rx = OSMO_MIN(Rx, Sum - num_tx);
|
||||
rx_valid_win = (1 << num_rx) - 1;
|
||||
|
||||
/* Validate with both Tta/Ttb/Trb and Ttb/Tra/Trb */
|
||||
for (mask_sel = MASK_TT; mask_sel <= MASK_TR; mask_sel += 1) {
|
||||
int capacity;
|
||||
/* Rotate group of RX slots: DDD-----, -DDD----, ..., DD-----D */
|
||||
for (dl_ts = 0; dl_ts < 8; dl_ts += 1, rx_valid_win <<= 1) {
|
||||
/* Wrap valid window */
|
||||
rx_valid_win = (rx_valid_win | rx_valid_win >> 8) & 0xff;
|
||||
|
||||
rx_window = mslot_filter_bad(rx_mask[mask_sel], ul_ts, *dl_slots, rx_valid_win);
|
||||
if (rx_window < 0)
|
||||
continue;
|
||||
/* for multislot type 1: don't split the window to wrap around.
|
||||
* E.g. 'DD-----D' is invalid for a 4 TN window. Except 8 TN window.
|
||||
* See 45.002 B.1 */
|
||||
if (Type == 1 && num_rx < 8 &&
|
||||
(rx_valid_win & (1 << 0)) && (rx_valid_win & (1 << 7)))
|
||||
continue;
|
||||
|
||||
if (skip_slot(mslot_class, mask_sel != MASK_TT, rx_window, tx_window, checked_rx))
|
||||
continue;
|
||||
/* Validate with both Tta/Ttb/Trb and Ttb/Tra/Trb */
|
||||
for (mask_sel = MASK_TT; mask_sel <= MASK_TR; mask_sel += 1) {
|
||||
int capacity;
|
||||
|
||||
/* Compute capacity */
|
||||
capacity = compute_capacity(trx, rx_window, tx_window);
|
||||
rx_window = mslot_filter_bad(rx_mask[mask_sel], ul_ts, *dl_slots, rx_valid_win);
|
||||
if (rx_window < 0)
|
||||
continue;
|
||||
|
||||
if (skip_slot(mslot_class, mask_sel != MASK_TT, rx_window, tx_window, checked_rx))
|
||||
continue;
|
||||
|
||||
/* Compute capacity */
|
||||
capacity = compute_capacity(trx, rx_window, tx_window);
|
||||
|
||||
#ifdef ENABLE_TS_ALLOC_DEBUG
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
"- Considering DL/UL slots: (TS=0)\"%s\"(TS=7), "
|
||||
"capacity = %d\n",
|
||||
set_flag_chars(set_flag_chars(set_flag_chars(set_flag_chars(
|
||||
slot_info,
|
||||
rx_bad, 'x', '.'),
|
||||
rx_window, 'D'),
|
||||
tx_window, 'U'),
|
||||
rx_window & tx_window, 'C'),
|
||||
capacity);
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
"- Considering DL/UL slots: (TS=0)\"%s\"(TS=7), "
|
||||
"capacity = %d\n",
|
||||
set_flag_chars(set_flag_chars(set_flag_chars(set_flag_chars(
|
||||
slot_info,
|
||||
rx_bad, 'x', '.'),
|
||||
rx_window, 'D'),
|
||||
tx_window, 'U'),
|
||||
rx_window & tx_window, 'C'),
|
||||
capacity);
|
||||
#endif
|
||||
|
||||
if (capacity <= max_capacity)
|
||||
continue;
|
||||
if (capacity <= max_capacity)
|
||||
continue;
|
||||
|
||||
max_capacity = capacity;
|
||||
max_ul_slots = tx_window;
|
||||
max_dl_slots = rx_window;
|
||||
}
|
||||
}
|
||||
}
|
||||
max_capacity = capacity;
|
||||
max_ul_slots = tx_window;
|
||||
max_dl_slots = rx_window;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!max_ul_slots || !max_dl_slots) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"No valid UL/DL slot combination found\n");
|
||||
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_COMBI);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -640,12 +659,12 @@ int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *
|
||||
* \param[in] slots Timeslots in use
|
||||
* \param[in] reserved_slots Reserved timeslots
|
||||
* \param[out] slotcount Number of TS in use
|
||||
* \param[out] avail_count Number of reserved TS
|
||||
* \param[out] reserve_count Number of reserved TS
|
||||
*/
|
||||
static void update_slot_counters(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *avail_count)
|
||||
static void count_slots(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *reserve_count)
|
||||
{
|
||||
(*slotcount) = pcu_bitcount(slots);
|
||||
(*avail_count) = pcu_bitcount(reserved_slots);
|
||||
(*reserve_count) = pcu_bitcount(reserved_slots);
|
||||
}
|
||||
|
||||
/*! Return slot mask with single TS from a given UL/DL set according to TBF's direction, ts pointer is set to that TS
|
||||
@@ -689,7 +708,8 @@ static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx
|
||||
uint8_t reserved_ul_slots, uint8_t reserved_dl_slots,
|
||||
int8_t first_common_ts)
|
||||
{
|
||||
uint8_t sl = tbf->direction != GPRS_RLCMAC_DL_TBF ? ul_slots : dl_slots;
|
||||
bool is_ul = tbf->direction == GPRS_RLCMAC_UL_TBF;
|
||||
uint8_t sl = is_ul ? ul_slots : dl_slots;
|
||||
char slot_info[9] = { 0 };
|
||||
|
||||
if (single)
|
||||
@@ -697,59 +717,63 @@ static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx
|
||||
|
||||
if (!sl) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No %s slots available\n",
|
||||
tbf->direction != GPRS_RLCMAC_DL_TBF ? "uplink" : "downlink");
|
||||
is_ul ? "uplink" : "downlink");
|
||||
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_SLOT_AVAIL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tbf->direction != GPRS_RLCMAC_DL_TBF) {
|
||||
if (is_ul) {
|
||||
snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_ul_slots, 'u'));
|
||||
masked_override_with(slot_info, sl, 'U');
|
||||
LOGPC(DRLCMAC, LOGL_DEBUG, "- Selected UL");
|
||||
} else {
|
||||
snprintf(slot_info, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(reserved_dl_slots, 'd'));
|
||||
masked_override_with(slot_info, sl, 'D');
|
||||
LOGPC(DRLCMAC, LOGL_DEBUG, "- Selected DL");
|
||||
}
|
||||
|
||||
LOGPC(DRLCMAC, LOGL_DEBUG, " slots: (TS=0)\"%s\"(TS=7)%s\n", slot_info, single ? ", single" : "");
|
||||
LOGPC(DRLCMAC, LOGL_DEBUG, "Selected %s slots: (TS=0)\"%s\"(TS=7), %s\n",
|
||||
is_ul ? "UL" : "DL",
|
||||
slot_info, single ? "single" : "multi");
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
/*! Allocate USF according to a given UL TS mapping
|
||||
*
|
||||
* N. B: this is legacy implementation which ignores given selected_ul_slots
|
||||
* \param[in] trx Pointer to TRX object
|
||||
* \param[in] tbf Pointer to TBF object
|
||||
* \param[in] first_common_ts First TS which is common to both UL and DL
|
||||
* \param[in] selected_ul_slots set of UL timeslots selected for allocation
|
||||
* \param[in] dl_slots set of DL timeslots
|
||||
* \param[out] usf array for allocated USF
|
||||
* \returns updated UL TS or negative on error
|
||||
* \returns updated UL TS mask or negative on error
|
||||
*/
|
||||
static int allocate_usf(const gprs_rlcmac_trx *trx, int8_t first_common_ts, uint8_t selected_ul_slots, uint8_t dl_slots,
|
||||
int *usf)
|
||||
static int allocate_usf(const gprs_rlcmac_trx *trx, uint8_t selected_ul_slots, uint8_t dl_slots,
|
||||
int *usf_list)
|
||||
{
|
||||
int free_usf = -1, ts;
|
||||
uint8_t ul_slots = selected_ul_slots;
|
||||
uint8_t ul_slots = selected_ul_slots & dl_slots;
|
||||
unsigned int ts;
|
||||
|
||||
if (first_common_ts >= 0)
|
||||
ul_slots = 1 << first_common_ts;
|
||||
else
|
||||
ul_slots = ul_slots & dl_slots;
|
||||
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
|
||||
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
|
||||
int8_t free_usf;
|
||||
|
||||
ts = find_least_busy_pdch(trx, GPRS_RLCMAC_UL_TBF, ul_slots, compute_usage_by_num_tbfs, NULL, &free_usf);
|
||||
if (((1 << ts) & ul_slots) == 0)
|
||||
continue;
|
||||
|
||||
if (free_usf < 0 || ts < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
|
||||
return -EBUSY;
|
||||
free_usf = find_free_usf(pdch->assigned_usf());
|
||||
if (free_usf < 0) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
"- Skipping TS %d, because "
|
||||
"no USF available\n", ts);
|
||||
ul_slots &= (~(1 << ts)) & 0xff;
|
||||
continue;
|
||||
}
|
||||
usf_list[ts] = free_usf;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ts >= 0 && ts <= 8);
|
||||
|
||||
/* We will stick to that single UL slot, unreserve the others */
|
||||
ul_slots = 1 << ts;
|
||||
usf[ts] = free_usf;
|
||||
if (!ul_slots) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
|
||||
bts_do_rate_ctr_inc(trx->bts, CTR_TBF_ALLOC_FAIL_NO_USF);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return ul_slots;
|
||||
}
|
||||
@@ -769,11 +793,11 @@ static void update_ms_reserved_slots(gprs_rlcmac_trx *trx, GprsMs *ms, uint8_t r
|
||||
{
|
||||
char slot_info[9] = { 0 };
|
||||
|
||||
if (res_ul_slots == ms->reserved_ul_slots() && res_dl_slots == ms->reserved_dl_slots())
|
||||
if (res_ul_slots == ms_reserved_ul_slots(ms) && res_dl_slots == ms_reserved_dl_slots(ms))
|
||||
return;
|
||||
|
||||
/* The reserved slots have changed, update the MS */
|
||||
ms->set_reserved_slots(trx, res_ul_slots, res_dl_slots);
|
||||
ms_set_reserved_slots(ms, trx, res_ul_slots, res_dl_slots);
|
||||
|
||||
ts_format(slot_info, dl_slots, ul_slots);
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "- Reserved DL/UL slots: (TS=0)\"%s\"(TS=7)\n", slot_info);
|
||||
@@ -829,13 +853,12 @@ static void assign_dl_tbf_slots(struct gprs_rlcmac_dl_tbf *dl_tbf, gprs_rlcmac_t
|
||||
* Assign one uplink slot. (With free USF)
|
||||
*
|
||||
* \param[in,out] bts Pointer to BTS struct
|
||||
* \param[in,out] ms_ Pointer to MS object
|
||||
* \param[in,out] tbf_ Pointer to TBF struct
|
||||
* \param[in,out] tbf Pointer to TBF struct
|
||||
* \param[in] single flag indicating if we should force single-slot allocation
|
||||
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
|
||||
* \returns negative error code or 0 on success
|
||||
*/
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
int8_t use_trx)
|
||||
{
|
||||
uint8_t dl_slots;
|
||||
@@ -844,46 +867,39 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
|
||||
uint8_t reserved_ul_slots;
|
||||
int8_t first_common_ts;
|
||||
uint8_t slotcount = 0;
|
||||
uint8_t avail_count = 0, trx_no;
|
||||
uint8_t reserve_count = 0, trx_no;
|
||||
int first_ts = -1;
|
||||
int usf[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
|
||||
int rc;
|
||||
int tfi;
|
||||
const GprsMs *ms = ms_;
|
||||
const gprs_rlcmac_tbf *tbf = tbf_;
|
||||
struct GprsMs *ms = tbf->ms();
|
||||
gprs_rlcmac_trx *trx;
|
||||
|
||||
LOGPAL(tbf, "B", single, use_trx, LOGL_DEBUG, "Alloc start\n");
|
||||
|
||||
/* Step 1: Get current state from the MS object */
|
||||
|
||||
if (!ms) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "MS not set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dl_slots = ms->reserved_dl_slots();
|
||||
ul_slots = ms->reserved_ul_slots();
|
||||
first_common_ts = ms->first_common_ts();
|
||||
trx = ms->current_trx();
|
||||
reserved_dl_slots = ms_reserved_dl_slots(ms);
|
||||
reserved_ul_slots = ms_reserved_ul_slots(ms);
|
||||
first_common_ts = ms_first_common_ts(ms);
|
||||
|
||||
/* Step 2a: Find usable TRX and TFI */
|
||||
tfi = tfi_find_free(bts->bts, trx, ms, tbf->direction, use_trx, &trx_no);
|
||||
tfi = tfi_find_free(bts, ms, tbf->direction, use_trx, &trx_no);
|
||||
if (tfi < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "- Failed to allocate a TFI\n");
|
||||
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "failed to allocate a TFI\n");
|
||||
return tfi;
|
||||
}
|
||||
|
||||
/* Step 2b: Reserve slots on the TRX for the MS */
|
||||
if (!trx)
|
||||
trx = &bts->trx[trx_no];
|
||||
trx = &bts->trx[trx_no];
|
||||
|
||||
if (!dl_slots || !ul_slots) {
|
||||
rc = find_multi_slots(trx, ms->ms_class(), &ul_slots, &dl_slots);
|
||||
if (!reserved_dl_slots || !reserved_ul_slots) {
|
||||
rc = find_multi_slots(trx, ms_ms_class(ms), &reserved_ul_slots, &reserved_dl_slots);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
reserved_dl_slots = dl_slots;
|
||||
reserved_ul_slots = ul_slots;
|
||||
dl_slots = reserved_dl_slots;
|
||||
ul_slots = reserved_ul_slots;
|
||||
|
||||
/* Step 3a: Derive the slot set for the current TBF */
|
||||
rc = tbf_select_slot_set(tbf, trx, single, ul_slots, dl_slots, reserved_ul_slots, reserved_dl_slots,
|
||||
@@ -891,63 +907,59 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
first_ts = ffs(rc) - 1;
|
||||
|
||||
/* Step 3b: Derive the slot set for a given direction */
|
||||
if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
|
||||
dl_slots = rc;
|
||||
update_slot_counters(dl_slots, reserved_dl_slots, &slotcount, &avail_count);
|
||||
count_slots(dl_slots, reserved_dl_slots, &slotcount, &reserve_count);
|
||||
} else {
|
||||
rc = allocate_usf(trx, first_common_ts, rc, dl_slots, usf);
|
||||
rc = allocate_usf(trx, rc, dl_slots, usf);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* We will stick to that single UL slot, unreserve the others */
|
||||
ul_slots = rc;
|
||||
reserved_ul_slots = ul_slots;
|
||||
|
||||
update_slot_counters(ul_slots, reserved_ul_slots, &slotcount, &avail_count);
|
||||
count_slots(ul_slots, reserved_ul_slots, &slotcount, &reserve_count);
|
||||
}
|
||||
|
||||
first_ts = ffs(rc) - 1;
|
||||
first_common_ts = ffs(dl_slots & ul_slots) - 1;
|
||||
|
||||
if (first_common_ts < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No first common slots available\n");
|
||||
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first common slot unavailable\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (first_ts < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No first slot available\n");
|
||||
LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first slot unavailable\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (single && slotcount) {
|
||||
tbf_->upgrade_to_multislot = (avail_count > slotcount);
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Using single slot at TS %d for %s\n",
|
||||
first_ts,
|
||||
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
|
||||
tbf->upgrade_to_multislot = (reserve_count > slotcount);
|
||||
LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using single slot at TS %d\n", first_ts);
|
||||
} else {
|
||||
tbf_->upgrade_to_multislot = 0;
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Using %d slots for %s\n", slotcount,
|
||||
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
|
||||
tbf->upgrade_to_multislot = false;
|
||||
LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using %d slots\n", slotcount);
|
||||
}
|
||||
|
||||
/* The allocation will be successful, so the system state and tbf_/ms_
|
||||
/* The allocation will be successful, so the system state and tbf/ms
|
||||
* may be modified from now on. */
|
||||
|
||||
/* Step 4: Update MS and TBF and really allocate the resources */
|
||||
|
||||
update_ms_reserved_slots(trx, ms_, reserved_ul_slots, reserved_dl_slots, ul_slots, dl_slots);
|
||||
update_ms_reserved_slots(trx, ms, reserved_ul_slots, reserved_dl_slots, ul_slots, dl_slots);
|
||||
|
||||
tbf_->trx = trx;
|
||||
tbf_->first_common_ts = first_common_ts;
|
||||
tbf_->first_ts = first_ts;
|
||||
tbf->trx = trx;
|
||||
tbf->first_common_ts = first_common_ts;
|
||||
tbf->first_ts = first_ts;
|
||||
|
||||
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
assign_dl_tbf_slots(as_dl_tbf(tbf_), trx, dl_slots, tfi);
|
||||
assign_dl_tbf_slots(as_dl_tbf(tbf), trx, dl_slots, tfi);
|
||||
else
|
||||
assign_ul_tbf_slots(as_ul_tbf(tbf_), trx, ul_slots, tfi, usf);
|
||||
assign_ul_tbf_slots(as_ul_tbf(tbf), trx, ul_slots, tfi, usf);
|
||||
|
||||
bts->bts->tbf_alloc_algo_b();
|
||||
bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_B);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -961,13 +973,12 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcm
|
||||
* goal is to provide the highest possible bandwidth per MS.
|
||||
*
|
||||
* \param[in,out] bts Pointer to BTS struct
|
||||
* \param[in,out] ms_ Pointer to MS object
|
||||
* \param[in,out] tbf_ Pointer to TBF struct
|
||||
* \param[in,out] tbf Pointer to TBF struct
|
||||
* \param[in] single flag indicating if we should force single-slot allocation
|
||||
* \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
|
||||
* \returns negative error code or 0 on success
|
||||
*/
|
||||
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gprs_rlcmac_tbf *tbf_, bool single,
|
||||
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
|
||||
int8_t use_trx)
|
||||
{
|
||||
int rc;
|
||||
@@ -980,7 +991,7 @@ int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gpr
|
||||
}
|
||||
|
||||
if (!bts->multislot_disabled) {
|
||||
rc = alloc_algorithm_b(bts, ms_, tbf_, single, use_trx);
|
||||
rc = alloc_algorithm_b(bts, tbf, single, use_trx);
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
|
||||
@@ -989,7 +1000,7 @@ int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, GprsMs *ms_, struct gpr
|
||||
bts->multislot_disabled = 1;
|
||||
}
|
||||
|
||||
return alloc_algorithm_a(bts, ms_, tbf_, single, use_trx);
|
||||
return alloc_algorithm_a(bts, tbf, single, use_trx);
|
||||
}
|
||||
|
||||
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class)
|
||||
@@ -999,7 +1010,7 @@ int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms
|
||||
if (rx == MS_NA)
|
||||
rx = 4;
|
||||
|
||||
if (bts->alloc_algorithm == alloc_algorithm_a)
|
||||
if (the_pcu->alloc_algorithm == alloc_algorithm_a)
|
||||
return 1;
|
||||
|
||||
if (bts->multislot_disabled)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
530
src/gsm_rlcmac.h
530
src/gsm_rlcmac.h
File diff suppressed because it is too large
Load Diff
@@ -1,238 +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>
|
||||
#include <bts.h>
|
||||
|
||||
|
||||
static struct rb_root timer_root = RB_ROOT;
|
||||
|
||||
/*
|
||||
* TODO: make this depend on the BTS. This means that
|
||||
* all time functions schedule based on the BTS they
|
||||
* are scheduled on.
|
||||
*/
|
||||
int get_current_fn()
|
||||
{
|
||||
return BTS::main_bts()->current_frame_number();
|
||||
}
|
||||
|
||||
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,90 +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);
|
||||
|
||||
|
||||
/*
|
||||
* Get Current Frame Number
|
||||
*/
|
||||
int get_current_fn();
|
||||
|
||||
/*! }@ */
|
||||
|
||||
#endif // GSM_TIMER_H
|
||||
84
src/llc.cpp
84
src/llc.cpp
@@ -27,6 +27,8 @@ extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
}
|
||||
|
||||
#include "pcu_utils.h"
|
||||
|
||||
/* reset LLC frame */
|
||||
void gprs_llc::reset()
|
||||
{
|
||||
@@ -57,8 +59,10 @@ void gprs_llc::put_dummy_frame(size_t req_len)
|
||||
|
||||
/* Add further stuffing, if the requested length exceeds the minimum
|
||||
* dummy command length */
|
||||
while (m_length < req_len)
|
||||
frame[m_length++] = 0x2b;
|
||||
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)
|
||||
@@ -95,45 +99,46 @@ bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
void gprs_llc_queue::init()
|
||||
void llc_queue_init(struct gprs_llc_queue *q)
|
||||
{
|
||||
INIT_LLIST_HEAD(&m_queue);
|
||||
m_queue_size = 0;
|
||||
m_queue_octets = 0;
|
||||
m_avg_queue_delay = 0;
|
||||
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 MetaInfo *info)
|
||||
|
||||
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const struct timespec *expire_time)
|
||||
{
|
||||
static const MetaInfo def_meta = {{0}};
|
||||
MetaInfo *meta_storage;
|
||||
|
||||
osmo_static_assert(sizeof(*info) <= sizeof(llc_msg->cb), info_does_not_fit);
|
||||
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];
|
||||
*meta_storage = info ? *info : def_meta;
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
|
||||
meta_storage->expire_time = *expire_time;
|
||||
|
||||
msgb_enqueue(&m_queue, llc_msg);
|
||||
}
|
||||
|
||||
void gprs_llc_queue::clear(BTS *bts)
|
||||
void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
while ((msg = msgb_dequeue(&m_queue))) {
|
||||
while ((msg = msgb_dequeue(&q->m_queue))) {
|
||||
if (bts)
|
||||
bts->llc_dropped_frame();
|
||||
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
m_queue_size = 0;
|
||||
m_queue_octets = 0;
|
||||
q->m_queue_size = 0;
|
||||
q->m_queue_octets = 0;
|
||||
}
|
||||
|
||||
void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
|
||||
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;
|
||||
@@ -143,7 +148,7 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
|
||||
|
||||
while (1) {
|
||||
if (msg1 == NULL)
|
||||
msg1 = msgb_dequeue(&m_queue);
|
||||
msg1 = msgb_dequeue(&q->m_queue);
|
||||
|
||||
if (msg2 == NULL)
|
||||
msg2 = msgb_dequeue(&o->m_queue);
|
||||
@@ -161,7 +166,7 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
|
||||
const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
|
||||
const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
|
||||
|
||||
if (timercmp(&mi2->recv_time, &mi1->recv_time, >)) {
|
||||
if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
|
||||
msg = msg1;
|
||||
msg1 = NULL;
|
||||
} else {
|
||||
@@ -175,15 +180,15 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
|
||||
queue_octets += msgb_length(msg);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(llist_empty(&m_queue));
|
||||
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, &m_queue);
|
||||
m_queue_size = queue_size;
|
||||
m_queue_octets = queue_octets;
|
||||
llist_splice_init(&new_queue, &q->m_queue);
|
||||
q->m_queue_size = queue_size;
|
||||
q->m_queue_octets = queue_octets;
|
||||
}
|
||||
|
||||
#define ALPHA 0.5f
|
||||
@@ -191,7 +196,7 @@ void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
|
||||
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct timeval *tv, tv_now, tv_result;
|
||||
struct timespec *tv, tv_now, tv_result;
|
||||
uint32_t lifetime;
|
||||
const MetaInfo *meta_storage;
|
||||
|
||||
@@ -208,21 +213,21 @@ struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
|
||||
m_queue_octets -= msgb_length(msg);
|
||||
|
||||
/* take the second time */
|
||||
gettimeofday(&tv_now, NULL);
|
||||
tv = (struct timeval *)&msg->data[sizeof(*tv)];
|
||||
timersub(&tv_now, &meta_storage->recv_time, &tv_result);
|
||||
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_usec/1000;
|
||||
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(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv)
|
||||
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->bts_data()->force_llc_lifetime)
|
||||
delay_csec = bts->bts_data()->force_llc_lifetime;
|
||||
if (bts->pcu->vty.force_llc_lifetime)
|
||||
delay_csec = bts->pcu->vty.force_llc_lifetime;
|
||||
else
|
||||
delay_csec = pdu_delay_csec;
|
||||
|
||||
@@ -233,20 +238,19 @@ void gprs_llc_queue::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec,
|
||||
}
|
||||
|
||||
/* calculate timestamp of timeout */
|
||||
struct timeval now, csec;
|
||||
gettimeofday(&now, NULL);
|
||||
csec.tv_usec = (delay_csec % 100) * 10000;
|
||||
csec.tv_sec = delay_csec / 100;
|
||||
struct timespec now, csec;
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
csecs_to_timespec(delay_csec, &csec);
|
||||
|
||||
timeradd(&now, &csec, tv);
|
||||
timespecadd(&now, &csec, tv);
|
||||
}
|
||||
|
||||
bool gprs_llc_queue::is_frame_expired(const struct timeval *tv_now,
|
||||
const struct timeval *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_usec == 0)
|
||||
if (tv->tv_sec == 0 && tv->tv_nsec == 0)
|
||||
return false;
|
||||
|
||||
return timercmp(tv_now, tv, >);
|
||||
return timespeccmp(tv_now, tv, >);
|
||||
}
|
||||
|
||||
94
src/llc.h
94
src/llc.h
@@ -18,22 +18,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#define LLC_MAX_LEN 1543
|
||||
|
||||
struct BTS;
|
||||
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();
|
||||
@@ -43,92 +49,86 @@ struct gprs_llc {
|
||||
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);
|
||||
|
||||
void consume(size_t len);
|
||||
void consume(uint8_t *data, size_t len);
|
||||
|
||||
uint16_t chunk_size() const;
|
||||
uint16_t remaining_space() const;
|
||||
uint16_t frame_length() const;
|
||||
|
||||
bool fits_in_current_frame(uint8_t size) const;
|
||||
#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 {
|
||||
struct MetaInfo {
|
||||
struct timeval recv_time;
|
||||
struct timeval expire_time;
|
||||
};
|
||||
|
||||
static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec,
|
||||
struct timeval *tv);
|
||||
static bool is_frame_expired(const struct timeval *now,
|
||||
const struct timeval *tv);
|
||||
#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 init();
|
||||
|
||||
void enqueue(struct msgb *llc_msg, const MetaInfo *info = 0);
|
||||
void enqueue(struct msgb *llc_msg, const struct timespec *expire_time);
|
||||
struct msgb *dequeue(const MetaInfo **info = 0);
|
||||
void clear(BTS *bts);
|
||||
void move_and_merge(gprs_llc_queue *o);
|
||||
size_t size() const;
|
||||
size_t octets() const;
|
||||
|
||||
private:
|
||||
#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);
|
||||
|
||||
inline uint16_t gprs_llc::chunk_size() const
|
||||
static inline uint16_t llc_chunk_size(const struct gprs_llc *llc)
|
||||
{
|
||||
return m_length - m_index;
|
||||
return llc->m_length - llc->m_index;
|
||||
}
|
||||
|
||||
inline uint16_t gprs_llc::remaining_space() const
|
||||
static inline uint16_t llc_remaining_space(const struct gprs_llc *llc)
|
||||
{
|
||||
return LLC_MAX_LEN - m_length;
|
||||
return LLC_MAX_LEN - llc->m_length;
|
||||
}
|
||||
|
||||
inline uint16_t gprs_llc::frame_length() const
|
||||
static inline uint16_t llc_frame_length(const struct gprs_llc *llc)
|
||||
{
|
||||
return m_length;
|
||||
return llc->m_length;
|
||||
}
|
||||
|
||||
inline void gprs_llc::consume(size_t len)
|
||||
static inline void llc_consume(struct gprs_llc *llc, size_t len)
|
||||
{
|
||||
m_index += len;
|
||||
llc->m_index += len;
|
||||
}
|
||||
|
||||
inline void gprs_llc::consume(uint8_t *data, size_t len)
|
||||
static inline void llc_consume_data(struct gprs_llc *llc, uint8_t *data, size_t len)
|
||||
{
|
||||
/* copy and increment index */
|
||||
memcpy(data, frame + m_index, len);
|
||||
consume(len);
|
||||
memcpy(data, llc->frame + llc->m_index, len);
|
||||
llc_consume(llc, len);
|
||||
}
|
||||
|
||||
inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
|
||||
static inline bool llc_fits_in_current_frame(const struct gprs_llc *llc, uint8_t chunk_size)
|
||||
{
|
||||
return m_length + chunk_size <= LLC_MAX_LEN;
|
||||
return llc->m_length + chunk_size <= LLC_MAX_LEN;
|
||||
}
|
||||
|
||||
inline size_t gprs_llc_queue::size() const
|
||||
static inline size_t llc_queue_size(const struct gprs_llc_queue *q)
|
||||
{
|
||||
return m_queue_size;
|
||||
return q->m_queue_size;
|
||||
}
|
||||
|
||||
inline size_t gprs_llc_queue::octets() const
|
||||
static inline size_t llc_queue_octets(const struct gprs_llc_queue *q)
|
||||
{
|
||||
return m_queue_octets;
|
||||
return q->m_queue_octets;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
904
src/nacc_fsm.c
Normal file
904
src/nacc_fsm.c
Normal file
@@ -0,0 +1,904 @@
|
||||
/* nacc_fsm.c
|
||||
*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||
|
||||
#include <nacc_fsm.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_ms.h>
|
||||
#include <encoding.h>
|
||||
#include <bts.h>
|
||||
#include <neigh_cache.h>
|
||||
|
||||
#define X(s) (1 << (s))
|
||||
|
||||
/* Infer CTRL id (seqnum) for a given tgt arfcn+bsic (bsic range: 0-63) */
|
||||
#define arfcn_bsic_2_ctrl_id(arfcn, bsic) ((arfcn) * 100 + (bsic))
|
||||
|
||||
static const struct osmo_tdef_state_timeout nacc_fsm_timeouts[32] = {
|
||||
[NACC_ST_INITIAL] = {},
|
||||
[NACC_ST_WAIT_RESOLVE_RAC_CI] = { .T = PCU_TDEF_NEIGH_RESOLVE_TO },
|
||||
[NACC_ST_WAIT_REQUEST_SI] = { .T = PCU_TDEF_SI_RESOLVE_TO },
|
||||
[NACC_ST_TX_NEIGHBOUR_DATA] = {},
|
||||
[NACC_ST_TX_CELL_CHG_CONTINUE] = {},
|
||||
[NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK] = {}, /* Timeout through event controlled by tbf::poll_timeout() */
|
||||
[NACC_ST_DONE] = {},
|
||||
};
|
||||
|
||||
/* Transition to a state, using the T timer defined in 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;
|
||||
}
|
||||
79
src/nacc_fsm.h
Normal file
79
src/nacc_fsm.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* nacc_fsm.h
|
||||
*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
#include <neigh_cache.h>
|
||||
|
||||
struct GprsMs;
|
||||
struct gprs_rlcmac_tbf;
|
||||
|
||||
enum nacc_fsm_event {
|
||||
NACC_EV_RX_CELL_CHG_NOTIFICATION, /* data: Packet_Cell_Change_Notification_t* */
|
||||
NACC_EV_RX_RAC_CI, /* 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);
|
||||
289
src/neigh_cache.c
Normal file
289
src/neigh_cache.c
Normal file
@@ -0,0 +1,289 @@
|
||||
/* si_cache.c
|
||||
*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <talloc.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <neigh_cache.h>
|
||||
#include <gprs_debug.h>
|
||||
|
||||
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);
|
||||
static void neigh_cache_cleanup_cb(void *data)
|
||||
{
|
||||
struct timespec now, threshold;
|
||||
struct neigh_cache *cache = (struct neigh_cache *)data;
|
||||
struct neigh_cache_entry *it, *tmp;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
/* Instead of adding keep_time_intval to each, substract it from now once */
|
||||
timespecsub(&now, &cache->keep_time_intval, &threshold);
|
||||
|
||||
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||
if (timespeccmp(&threshold, &it->update_ts, <))
|
||||
break;
|
||||
LOGP(DNACC, LOGL_DEBUG,
|
||||
"neigh_cache: Removing entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
|
||||
NEIGH_CACHE_ENTRY_KEY_ARGS(&it->key), osmo_cgi_ps_name(&it->value));
|
||||
llist_del(&it->list);
|
||||
talloc_free(it);
|
||||
}
|
||||
|
||||
neigh_cache_schedule_cleanup(cache);
|
||||
}
|
||||
|
||||
static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
|
||||
{
|
||||
struct neigh_cache_entry *it;
|
||||
struct timespec now, threshold, result;
|
||||
|
||||
/* First item is the one with oldest update_ts */
|
||||
it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);
|
||||
if (!it)
|
||||
return;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
|
||||
|
||||
if (timespeccmp(&now, &threshold, >=)) {
|
||||
/* Too late, let's flush asynchonously so newly added isn't
|
||||
* immediatelly freed before return. */
|
||||
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
|
||||
} else {
|
||||
timespecsub(&threshold, &now, &result);
|
||||
}
|
||||
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
|
||||
}
|
||||
|
||||
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
|
||||
{
|
||||
struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);
|
||||
OSMO_ASSERT(cache);
|
||||
INIT_LLIST_HEAD(&cache->list);
|
||||
osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);
|
||||
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||
return cache;
|
||||
|
||||
}
|
||||
|
||||
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec)
|
||||
{
|
||||
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||
neigh_cache_schedule_cleanup(cache);
|
||||
}
|
||||
|
||||
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
|
||||
const struct neigh_cache_entry_key *key,
|
||||
const struct osmo_cell_global_id_ps *value)
|
||||
{
|
||||
struct neigh_cache_entry *it;
|
||||
|
||||
/* First check if it already exists. If so, simply update timer+value */
|
||||
it = neigh_cache_lookup_entry(cache, key);
|
||||
if (!it) {
|
||||
LOGP(DNACC, LOGL_DEBUG,
|
||||
"neigh_cache: Inserting new entry " NEIGH_CACHE_ENTRY_KEY_FMT " => %s\n",
|
||||
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(value));
|
||||
it = talloc_zero(cache, struct neigh_cache_entry);
|
||||
OSMO_ASSERT(it);
|
||||
memcpy(&it->key, key, sizeof(it->key));
|
||||
} else {
|
||||
LOGP(DNACC, LOGL_DEBUG,
|
||||
"neigh_cache: Updating entry " NEIGH_CACHE_ENTRY_KEY_FMT " => (%s -> %s)\n",
|
||||
NEIGH_CACHE_ENTRY_KEY_ARGS(key), osmo_cgi_ps_name(&it->value), osmo_cgi_ps_name2(value));
|
||||
/* remove item, we'll add it to the end to have them sorted by last update */
|
||||
llist_del(&it->list);
|
||||
}
|
||||
|
||||
memcpy(&it->value, value, sizeof(it->value));
|
||||
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
|
||||
llist_add_tail(&it->list, &cache->list);
|
||||
neigh_cache_schedule_cleanup(cache);
|
||||
return it;
|
||||
}
|
||||
|
||||
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
|
||||
const struct neigh_cache_entry_key *key)
|
||||
{
|
||||
struct neigh_cache_entry *tmp;
|
||||
llist_for_each_entry(tmp, &cache->list, list) {
|
||||
if (neigh_cache_entry_key_eq(&tmp->key, key))
|
||||
return tmp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
|
||||
const struct neigh_cache_entry_key *key)
|
||||
{
|
||||
struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
|
||||
if (it)
|
||||
return &it->value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void neigh_cache_free(struct neigh_cache *cache)
|
||||
{
|
||||
struct neigh_cache_entry *it, *tmp;
|
||||
if (!cache)
|
||||
return;
|
||||
|
||||
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||
llist_del(&it->list);
|
||||
talloc_free(it);
|
||||
}
|
||||
osmo_timer_del(&cache->cleanup_timer);
|
||||
talloc_free(cache);
|
||||
}
|
||||
|
||||
|
||||
////////////////////
|
||||
// SI CACHE
|
||||
///////////////////
|
||||
|
||||
static void si_cache_schedule_cleanup(struct si_cache *cache);
|
||||
static void si_cache_cleanup_cb(void *data)
|
||||
{
|
||||
struct timespec now, threshold;
|
||||
struct si_cache *cache = (struct si_cache *)data;
|
||||
struct si_cache_entry *it, *tmp;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
/* Instead of adding keep_time_intval to each, substract it from now once */
|
||||
timespecsub(&now, &cache->keep_time_intval, &threshold);
|
||||
|
||||
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||
if (timespeccmp(&threshold, &it->update_ts, <))
|
||||
break;
|
||||
LOGP(DNACC, LOGL_DEBUG, "si_cache: Removing entry %s\n",
|
||||
osmo_cgi_ps_name(&it->key));
|
||||
llist_del(&it->list);
|
||||
talloc_free(it);
|
||||
}
|
||||
|
||||
si_cache_schedule_cleanup(cache);
|
||||
}
|
||||
|
||||
static void si_cache_schedule_cleanup(struct si_cache *cache)
|
||||
{
|
||||
struct si_cache_entry *it;
|
||||
struct timespec now, threshold, result;
|
||||
|
||||
/* First item is the one with oldest update_ts */
|
||||
it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);
|
||||
if (!it)
|
||||
return;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);
|
||||
|
||||
if (timespeccmp(&now, &threshold, >=)) {
|
||||
/* Too late, let's flush asynchonously so newly added isn't
|
||||
* immediatelly freed before return. */
|
||||
result = (struct timespec){ .tv_sec = 0, .tv_nsec = 0 };
|
||||
} else {
|
||||
timespecsub(&threshold, &now, &result);
|
||||
}
|
||||
osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
|
||||
}
|
||||
|
||||
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
|
||||
{
|
||||
struct si_cache *cache = talloc_zero(ctx, struct si_cache);
|
||||
OSMO_ASSERT(cache);
|
||||
INIT_LLIST_HEAD(&cache->list);
|
||||
osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);
|
||||
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||
return cache;
|
||||
}
|
||||
|
||||
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec)
|
||||
{
|
||||
cache->keep_time_intval = (struct timespec){ .tv_sec = keep_time_sec, .tv_nsec = 0};
|
||||
si_cache_schedule_cleanup(cache);
|
||||
}
|
||||
|
||||
struct si_cache_entry *si_cache_add(struct si_cache *cache,
|
||||
const struct osmo_cell_global_id_ps *key,
|
||||
const struct si_cache_value *value)
|
||||
{
|
||||
struct si_cache_entry *it;
|
||||
|
||||
/* First check if it already exists. If so, simply update timer+value */
|
||||
it = si_cache_lookup_entry(cache, key);
|
||||
if (!it) {
|
||||
LOGP(DNACC, LOGL_DEBUG, "si_cache: Inserting new entry %s\n",
|
||||
osmo_cgi_ps_name(key));
|
||||
it = talloc_zero(cache, struct si_cache_entry);
|
||||
OSMO_ASSERT(it);
|
||||
memcpy(&it->key, key, sizeof(it->key));
|
||||
} else {
|
||||
LOGP(DNACC, LOGL_DEBUG, "si_cache: Updating entry %s\n",
|
||||
osmo_cgi_ps_name(&it->key));
|
||||
/* remove item, we'll add it to the end to have them sorted by last update */
|
||||
llist_del(&it->list);
|
||||
}
|
||||
|
||||
memcpy(&it->value, value, sizeof(it->value));
|
||||
OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);
|
||||
llist_add_tail(&it->list, &cache->list);
|
||||
si_cache_schedule_cleanup(cache);
|
||||
return it;
|
||||
}
|
||||
|
||||
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
|
||||
const struct osmo_cell_global_id_ps *key)
|
||||
{
|
||||
struct si_cache_entry *tmp;
|
||||
llist_for_each_entry(tmp, &cache->list, list) {
|
||||
if (osmo_cgi_ps_cmp(&tmp->key, key) == 0)
|
||||
return tmp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
|
||||
const struct osmo_cell_global_id_ps *key)
|
||||
{
|
||||
struct si_cache_entry *it = si_cache_lookup_entry(cache, key);
|
||||
if (it)
|
||||
return &it->value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void si_cache_free(struct si_cache *cache)
|
||||
{
|
||||
struct si_cache_entry *it, *tmp;
|
||||
if (!cache)
|
||||
return;
|
||||
|
||||
llist_for_each_entry_safe(it, tmp, &cache->list, list) {
|
||||
llist_del(&it->list);
|
||||
talloc_free(it);
|
||||
}
|
||||
osmo_timer_del(&cache->cleanup_timer);
|
||||
talloc_free(cache);
|
||||
}
|
||||
112
src/neigh_cache.h
Normal file
112
src/neigh_cache.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* neigh_cache.h
|
||||
*
|
||||
* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gprs/gprs_bssgp_rim.h>
|
||||
|
||||
////////////////////
|
||||
// NEIGH CACHE
|
||||
///////////////////
|
||||
|
||||
/* ARFC+BSIC -> CGI PS cache */
|
||||
struct neigh_cache {
|
||||
struct llist_head list; /* list of neigh_cache_entry items */
|
||||
struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
|
||||
struct timespec keep_time_intval;
|
||||
};
|
||||
|
||||
struct neigh_cache_entry_key {
|
||||
uint16_t local_lac;
|
||||
uint16_t local_ci;
|
||||
uint16_t tgt_arfcn;
|
||||
uint8_t tgt_bsic;
|
||||
};
|
||||
#define NEIGH_CACHE_ENTRY_KEY_FMT "%" PRIu16 "-%" PRIu16 "-%" PRIu16 "-%" PRIu8
|
||||
#define NEIGH_CACHE_ENTRY_KEY_ARGS(key) (key)->local_lac, (key)->local_ci, (key)->tgt_arfcn, (key)->tgt_bsic
|
||||
|
||||
static inline bool neigh_cache_entry_key_eq(const struct neigh_cache_entry_key *a,
|
||||
const struct neigh_cache_entry_key *b)
|
||||
{
|
||||
return a->local_lac == b->local_lac &&
|
||||
a->local_ci == b->local_ci &&
|
||||
a->tgt_arfcn == b->tgt_arfcn &&
|
||||
a->tgt_bsic == b->tgt_bsic;
|
||||
}
|
||||
|
||||
struct neigh_cache_entry {
|
||||
struct llist_head list; /* to be included in neigh_cache->list */
|
||||
struct timespec update_ts;
|
||||
struct neigh_cache_entry_key key;
|
||||
struct osmo_cell_global_id_ps value;
|
||||
};
|
||||
|
||||
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec);
|
||||
void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int keep_time_sec);
|
||||
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
|
||||
const struct neigh_cache_entry_key *key,
|
||||
const struct osmo_cell_global_id_ps *value);
|
||||
struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
|
||||
const struct neigh_cache_entry_key *key);
|
||||
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
|
||||
const struct neigh_cache_entry_key *key);
|
||||
void neigh_cache_free(struct neigh_cache *cache);
|
||||
|
||||
|
||||
////////////////////
|
||||
// SI CACHE
|
||||
///////////////////
|
||||
|
||||
/* CGI-PS-> SI cache */
|
||||
struct si_cache {
|
||||
struct llist_head list; /* list of si_cache_entry items */
|
||||
struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */
|
||||
struct timespec keep_time_intval;
|
||||
};
|
||||
|
||||
struct si_cache_value {
|
||||
uint8_t si_buf[BSSGP_RIM_PSI_LEN * 127]; /* 3GPP TS 48.018 11.3.63.2.1 */
|
||||
size_t si_len;
|
||||
bool type_psi;
|
||||
};
|
||||
|
||||
struct si_cache_entry {
|
||||
struct llist_head list; /* to be included in si_cache->list */
|
||||
struct timespec update_ts;
|
||||
struct osmo_cell_global_id_ps key;
|
||||
struct si_cache_value value;
|
||||
};
|
||||
|
||||
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec);
|
||||
void si_cache_set_keep_time_interval(struct si_cache *cache, unsigned int keep_time_sec);
|
||||
struct si_cache_entry *si_cache_add(struct si_cache *cache,
|
||||
const struct osmo_cell_global_id_ps *key,
|
||||
const struct si_cache_value *value);
|
||||
struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,
|
||||
const struct osmo_cell_global_id_ps *key);
|
||||
const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,
|
||||
const struct osmo_cell_global_id_ps *key);
|
||||
void si_cache_free(struct si_cache *cache);
|
||||
@@ -153,11 +153,7 @@ int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
|
||||
buf, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
read_ofd->fd = rc;
|
||||
read_ofd->priv_nr = q;
|
||||
read_ofd->data = hdl;
|
||||
read_ofd->cb = l1if_fd_cb;
|
||||
read_ofd->when = BSC_FD_READ;
|
||||
osmo_fd_setup(read_ofd, rc, OSMO_FD_READ, l1if_fd_cb, hdl, q);
|
||||
rc = osmo_fd_register(read_ofd);
|
||||
if (rc < 0) {
|
||||
close(read_ofd->fd);
|
||||
@@ -176,10 +172,7 @@ int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
|
||||
}
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = l1fd_write_cb;
|
||||
write_ofd->fd = rc;
|
||||
write_ofd->priv_nr = q;
|
||||
write_ofd->data = hdl;
|
||||
write_ofd->when = BSC_FD_WRITE;
|
||||
osmo_fd_setup(write_ofd, rc, OSMO_FD_WRITE, osmo_wqueue_bfd_cb, hdl, q);
|
||||
rc = osmo_fd_register(write_ofd);
|
||||
if (rc < 0) {
|
||||
close(write_ofd->fd);
|
||||
|
||||
@@ -148,6 +148,7 @@ static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
|
||||
GsmL1_PhReadyToSendInd_t *rts_ind)
|
||||
{
|
||||
struct gsm_time g_time;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
int rc = 0;
|
||||
|
||||
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
|
||||
@@ -156,13 +157,19 @@ static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
|
||||
g_time.t1, g_time.t2, g_time.t3,
|
||||
get_value_string(lc15bts_l1sapi_names, rts_ind->sapi));
|
||||
|
||||
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
|
||||
|
||||
|
||||
switch (rts_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
|
||||
rc = pcu_rx_rts_req_pdtch(bts, fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
rc = pcu_rx_rts_req_ptcch(bts, fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -186,47 +193,47 @@ static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
|
||||
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct pcu_l1_meas meas = {0};
|
||||
uint8_t *data;
|
||||
uint8_t data_len;
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
|
||||
DEBUGP(DL1IF, "(trx=%" PRIu8 ",ts=%u) FN=%u Rx PH-DATA.ind %s (hL2 %08x): %s\n",
|
||||
fl1h->trx_no, data_ind->u8Tn, data_ind->u32Fn,
|
||||
get_value_string(lc15bts_l1sapi_names, data_ind->sapi),
|
||||
data_ind->hLayer2,
|
||||
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
|
||||
data_ind->msgUnitParam.u8Size));
|
||||
|
||||
/*
|
||||
* TODO: Add proper bad frame handling here. This could be used
|
||||
* to switch the used CS. Avoid a crash with the PCU right now
|
||||
* feed "0 - 1" amount of data.
|
||||
*/
|
||||
if (data_ind->msgUnitParam.u8Size == 0)
|
||||
return -1;
|
||||
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
|
||||
|
||||
get_meas(&meas, &data_ind->measParam);
|
||||
bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
|
||||
bts_update_tbf_ta(bts, "PH-DATA", data_ind->u32Fn, fl1h->trx_no,
|
||||
data_ind->u8Tn, sign_qta2ta(meas.bto), false);
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
/* drop incomplete UL block */
|
||||
if (data_ind->msgUnitParam.u8Buffer[0]
|
||||
!= GsmL1_PdtchPlType_Full)
|
||||
break;
|
||||
/* PDTCH / PACCH frame handling */
|
||||
rc = pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
|
||||
data_ind->msgUnitParam.u8Buffer + 1,
|
||||
data_ind->msgUnitParam.u8Size - 1,
|
||||
data_ind->u32Fn,
|
||||
&meas);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
rc = -1;
|
||||
if (data_ind->msgUnitParam.u8Size != 0 &&
|
||||
data_ind->msgUnitParam.u8Buffer[0] == GsmL1_PdtchPlType_Full) {
|
||||
data = data_ind->msgUnitParam.u8Buffer + 1;
|
||||
data_len = data_ind->msgUnitParam.u8Size - 1;
|
||||
if (data_len == 0)
|
||||
data = NULL;
|
||||
} else {
|
||||
data = NULL;
|
||||
data_len = 0;
|
||||
}
|
||||
pdch = &bts->trx[fl1h->trx_no].pdch[data_ind->u8Tn];
|
||||
pcu_rx_data_ind_pdtch(bts, pdch, data, data_len, data_ind->u32Fn, &meas);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
|
||||
get_value_string(lc15bts_l1sapi_names, data_ind->sapi));
|
||||
LOGP(DL1IF, LOGL_NOTICE,
|
||||
"(trx=%" PRIu8 ",ts=%u) FN=%u Rx PH-DATA.ind for unknown L1 SAPI %s\n",
|
||||
fl1h->trx_no, data_ind->u8Tn, data_ind->u32Fn,
|
||||
get_value_string(lc15bts_l1sapi_names, data_ind->sapi));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -244,12 +251,29 @@ static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
|
||||
|
||||
static int handle_ph_ra_ind(struct lc15l1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
||||
return 0;
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-RA.ind");
|
||||
bts_update_tbf_ta("PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
|
||||
qta2ta(ra_ind->measParam.i16BurstTiming), true);
|
||||
|
||||
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
|
||||
|
||||
switch (ra_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Prach:
|
||||
bts_update_tbf_ta(bts, "PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
|
||||
qta2ta(ra_ind->measParam.i16BurstTiming), true);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
pcu_rx_rach_ind_ptcch(bts, fl1h->trx_no, ra_ind->u8Tn, ra_ind->u32Fn,
|
||||
ra_ind->measParam.i16BurstTiming);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-RA.ind for unknown L1 SAPI %s\n",
|
||||
get_value_string(lc15bts_l1sapi_names, ra_ind->sapi));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -374,4 +398,3 @@ int l1if_close_pdch(void *obj)
|
||||
talloc_free(fl1h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
206
src/osmo-bts-oc2g/oc2g_l1_hw.c
Normal file
206
src/osmo-bts-oc2g/oc2g_l1_hw.c
Normal file
@@ -0,0 +1,206 @@
|
||||
/* Interface handler for Nuran Wireless Litecell 1.5 L1 (real hardware) */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* femto_l1_hw.c
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <nrw/oc2g/oc2g.h>
|
||||
#include <nrw/oc2g/gsml1prim.h>
|
||||
#include <nrw/oc2g/gsml1const.h>
|
||||
#include <nrw/oc2g/gsml1types.h>
|
||||
|
||||
#include "gprs_debug.h"
|
||||
#include "oc2g_l1_if.h"
|
||||
#include "oc2gbts.h"
|
||||
|
||||
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/oc2g_dsp2arm_trx"
|
||||
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/oc2g_arm2dsp_trx"
|
||||
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm_trx"
|
||||
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp_trx"
|
||||
|
||||
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm_trx"
|
||||
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp_trx"
|
||||
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
|
||||
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
|
||||
|
||||
static const char *rd_devnames[] = {
|
||||
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
||||
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
||||
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
|
||||
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
|
||||
};
|
||||
|
||||
static const char *wr_devnames[] = {
|
||||
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
||||
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
||||
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
|
||||
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
|
||||
};
|
||||
|
||||
/* callback when there's something to read from the l1 msg_queue */
|
||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
//struct msgb *msg = l1p_msgb_alloc();
|
||||
struct msgb *msg = msgb_alloc_headroom(sizeof(Oc2g_Prim_t) + 128,
|
||||
128, "1l_fd");
|
||||
struct oc2gl1_hdl *fl1h = ofd->data;
|
||||
int rc;
|
||||
|
||||
msg->l1h = msg->data;
|
||||
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
|
||||
if (rc < 0) {
|
||||
if (rc != -1)
|
||||
LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
switch (ofd->priv_nr) {
|
||||
case MQ_SYS_WRITE:
|
||||
if (rc != sizeof(Oc2g_Prim_t))
|
||||
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||
"sizeof(Oc2g_Prim_t)\n", rc);
|
||||
return l1if_handle_sysprim(fl1h, msg);
|
||||
case MQ_L1_WRITE:
|
||||
case MQ_TCH_WRITE:
|
||||
case MQ_PDTCH_WRITE:
|
||||
if (rc != sizeof(GsmL1_Prim_t))
|
||||
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||
"sizeof(GsmL1_Prim_t)\n", rc);
|
||||
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
|
||||
default:
|
||||
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||
LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||
ofd->priv_nr);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/* callback when we can write to one of the l1 msg_queue devices */
|
||||
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
return rc;
|
||||
} else if (rc < msg->len) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
|
||||
"%u < %u\n", rc, msg->len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_transport_open(int q, struct oc2gl1_hdl *hdl)
|
||||
{
|
||||
int rc;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
/* Step 1: Open all msg_queue file descriptors */
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_wqueue *wq = &hdl->write_q[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], hdl->hw_info.trx_nr);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_RDONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
osmo_fd_setup(read_ofd, rc, OSMO_FD_READ, l1if_fd_cb, hdl, q);
|
||||
rc = osmo_fd_register(read_ofd);
|
||||
if (rc < 0) {
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], hdl->hw_info.trx_nr);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_WRONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
goto out_read;
|
||||
}
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = l1fd_write_cb;
|
||||
osmo_fd_setup(write_ofd, rc, OSMO_FD_WRITE, osmo_wqueue_bfd_cb, hdl, q);
|
||||
rc = osmo_fd_register(write_ofd);
|
||||
if (rc < 0) {
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
goto out_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_read:
|
||||
close(hdl->read_ofd[q].fd);
|
||||
osmo_fd_unregister(&hdl->read_ofd[q]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_transport_close(int q, struct oc2gl1_hdl *hdl)
|
||||
{
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
osmo_fd_unregister(read_ofd);
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
|
||||
osmo_fd_unregister(write_ofd);
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
405
src/osmo-bts-oc2g/oc2g_l1_if.c
Normal file
405
src/osmo-bts-oc2g/oc2g_l1_if.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* femto_l1_if.c
|
||||
*
|
||||
* 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 "oc2g_l1_if.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nrw/oc2g/oc2g.h>
|
||||
#include <nrw/oc2g/gsml1prim.h>
|
||||
#include <nrw/oc2g/gsml1const.h>
|
||||
#include <nrw/oc2g/gsml1types.h>
|
||||
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
#include <gprs_debug.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <osmocom/pcu/pcuif_proto.h>
|
||||
#include <bts.h>
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
uint32_t l1if_ts_to_hLayer2(uint8_t trx, uint8_t ts)
|
||||
{
|
||||
return (ts << 16) | (trx << 24);
|
||||
}
|
||||
|
||||
/* allocate a msgb containing a GsmL1_Prim_t */
|
||||
struct msgb *l1p_msgb_alloc(void)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
|
||||
|
||||
if (msg)
|
||||
msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int l1if_req_pdch(struct oc2gl1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
|
||||
|
||||
if (osmo_wqueue_enqueue(wqueue, msg) != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct oc2gl1_hdl *gl1)
|
||||
{
|
||||
prim->id = id;
|
||||
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq:
|
||||
//prim->u.mphInitReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphCloseReq:
|
||||
prim->u.mphCloseReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphConnectReq:
|
||||
prim->u.mphConnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphDisconnectReq:
|
||||
prim->u.mphDisconnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphActivateReq:
|
||||
prim->u.mphActivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphDeactivateReq:
|
||||
prim->u.mphDeactivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphConfigReq:
|
||||
prim->u.mphConfigReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphMeasureReq:
|
||||
prim->u.mphMeasureReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphInitCnf:
|
||||
case GsmL1_PrimId_MphCloseCnf:
|
||||
case GsmL1_PrimId_MphConnectCnf:
|
||||
case GsmL1_PrimId_MphDisconnectCnf:
|
||||
case GsmL1_PrimId_MphActivateCnf:
|
||||
case GsmL1_PrimId_MphDeactivateCnf:
|
||||
case GsmL1_PrimId_MphConfigCnf:
|
||||
case GsmL1_PrimId_MphMeasureCnf:
|
||||
break;
|
||||
case GsmL1_PrimId_MphTimeInd:
|
||||
break;
|
||||
case GsmL1_PrimId_MphSyncInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhEmptyFrameReq:
|
||||
prim->u.phEmptyFrameReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataReq:
|
||||
prim->u.phDataReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_PhConnectInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhReadyToSendInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhRaInd:
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "unknown L1 primitive %u\n", id);
|
||||
break;
|
||||
}
|
||||
return &prim->u;
|
||||
}
|
||||
|
||||
/* connect PDTCH */
|
||||
int l1if_connect_pdch(void *obj, uint8_t ts)
|
||||
{
|
||||
struct oc2gl1_hdl *fl1h = obj;
|
||||
struct msgb *msg = l1p_msgb_alloc();
|
||||
GsmL1_MphConnectReq_t *cr;
|
||||
|
||||
cr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConnectReq, fl1h);
|
||||
cr->u8Tn = ts;
|
||||
cr->logChComb = GsmL1_LogChComb_XIII;
|
||||
|
||||
return l1if_req_pdch(fl1h, msg);
|
||||
}
|
||||
|
||||
static int handle_ph_readytosend_ind(struct oc2gl1_hdl *fl1h,
|
||||
GsmL1_PhReadyToSendInd_t *rts_ind)
|
||||
{
|
||||
struct gsm_time g_time;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
int rc = 0;
|
||||
|
||||
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-RTS.ind %02u/%02u/%02u SAPI=%s\n",
|
||||
g_time.t1, g_time.t2, g_time.t3,
|
||||
get_value_string(oc2gbts_l1sapi_names, rts_ind->sapi));
|
||||
|
||||
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
|
||||
|
||||
switch (rts_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
rc = pcu_rx_rts_req_pdtch(bts, fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
rc = pcu_rx_rts_req_ptcch(bts, fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void get_meas(struct pcu_l1_meas *meas, const GsmL1_MeasParam_t *l1_meas)
|
||||
{
|
||||
meas->rssi = (int8_t) (l1_meas->fRssi);
|
||||
meas->have_rssi = 1;
|
||||
meas->ber = (uint8_t) (l1_meas->fBer * 100);
|
||||
meas->have_ber = 1;
|
||||
meas->bto = (int16_t) (l1_meas->i16BurstTiming);
|
||||
meas->have_bto = 1;
|
||||
meas->link_qual = (int16_t) (l1_meas->fLinkQuality);
|
||||
meas->have_link_qual = 1;
|
||||
}
|
||||
|
||||
static int handle_ph_data_ind(struct oc2gl1_hdl *fl1h,
|
||||
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct pcu_l1_meas meas = {0};
|
||||
uint8_t *data;
|
||||
uint8_t data_len;
|
||||
|
||||
DEBUGP(DL1IF, "(trx=%" PRIu8 ",ts=%u) FN=%u Rx PH-DATA.ind %s (hL2 %08x): %s\n",
|
||||
fl1h->trx_no, data_ind->u8Tn, data_ind->u32Fn,
|
||||
get_value_string(oc2gbts_l1sapi_names, data_ind->sapi),
|
||||
data_ind->hLayer2,
|
||||
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
|
||||
data_ind->msgUnitParam.u8Size));
|
||||
|
||||
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
|
||||
|
||||
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
|
||||
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
|
||||
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
|
||||
data_ind->msgUnitParam.u8Size-1);
|
||||
|
||||
get_meas(&meas, &data_ind->measParam);
|
||||
bts_update_tbf_ta(bts, "PH-DATA", data_ind->u32Fn, fl1h->trx_no,
|
||||
data_ind->u8Tn, sign_qta2ta(meas.bto), false);
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
/* PDTCH / PACCH frame handling */
|
||||
if (data_ind->msgUnitParam.u8Size != 0 &&
|
||||
data_ind->msgUnitParam.u8Buffer[0] == GsmL1_PdtchPlType_Full) {
|
||||
data = data_ind->msgUnitParam.u8Buffer + 1;
|
||||
data_len = data_ind->msgUnitParam.u8Size - 1;
|
||||
if (data_len == 0)
|
||||
data = NULL;
|
||||
} else {
|
||||
data = NULL;
|
||||
data_len = 0;
|
||||
}
|
||||
pdch = &bts->trx[fl1h->trx_no].pdch[data_ind->u8Tn];
|
||||
pcu_rx_data_ind_pdtch(bts, pdch, data, data_len, data_ind->u32Fn, &meas);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_NOTICE,
|
||||
"(trx=%" PRIu8 ",ts=%u) FN=%u Rx PH-DATA.ind for unknown L1 SAPI %s\n",
|
||||
fl1h->trx_no, data_ind->u8Tn, data_ind->u32Fn,
|
||||
get_value_string(oc2gbts_l1sapi_names, data_ind->sapi));
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define MIN_QUAL_RACH 5.0f
|
||||
|
||||
static int handle_ph_ra_ind(struct oc2gl1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
|
||||
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
||||
return 0;
|
||||
|
||||
LOGP(DL1IF, LOGL_DEBUG, "PH-RA-IND L1 qta=%d\n", ra_ind->measParam.i16BurstTiming);
|
||||
|
||||
bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list);
|
||||
|
||||
switch (ra_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Prach:
|
||||
bts_update_tbf_ta(bts, "PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
|
||||
qta2ta(ra_ind->measParam.i16BurstTiming), true);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
pcu_rx_rach_ind_ptcch(bts, fl1h->trx_no, ra_ind->u8Tn, ra_ind->u32Fn,
|
||||
ra_ind->measParam.i16BurstTiming);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-RA.ind for unknown L1 SAPI %s\n",
|
||||
get_value_string(oc2gbts_l1sapi_names, ra_ind->sapi));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* handle any random indication from the L1 */
|
||||
int l1if_handle_l1prim(int wq, struct oc2gl1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
|
||||
get_value_string(oc2gbts_l1prim_names, l1p->id), wq);
|
||||
|
||||
switch (l1p->id) {
|
||||
#if 0
|
||||
case GsmL1_PrimId_MphTimeInd:
|
||||
rc = handle_mph_time_ind(fl1h, &l1p->u.mphTimeInd);
|
||||
break;
|
||||
case GsmL1_PrimId_MphSyncInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhConnectInd:
|
||||
break;
|
||||
#endif
|
||||
case GsmL1_PrimId_PhReadyToSendInd:
|
||||
rc = handle_ph_readytosend_ind(fl1h, &l1p->u.phReadyToSendInd);
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataInd:
|
||||
rc = handle_ph_data_ind(fl1h, &l1p->u.phDataInd, msg);
|
||||
break;
|
||||
case GsmL1_PrimId_PhRaInd:
|
||||
rc = handle_ph_ra_ind(fl1h, &l1p->u.phRaInd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_handle_sysprim(struct oc2gl1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* send packet data request to L1 */
|
||||
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
||||
{
|
||||
struct oc2gl1_hdl *fl1h = obj;
|
||||
struct msgb *msg;
|
||||
GsmL1_Prim_t *l1p;
|
||||
GsmL1_PhDataReq_t *data_req;
|
||||
GsmL1_MsgUnitParam_t *msu_param;
|
||||
struct gsm_time g_time;
|
||||
|
||||
gsm_fn2gsmtime(&g_time, fn);
|
||||
|
||||
DEBUGP(DL1IF, "TX packet data %02u/%02u/%02u is_ptcch=%d ts=%d "
|
||||
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
|
||||
g_time.t3, is_ptcch, ts, block_nr, arfcn, len);
|
||||
|
||||
msg = l1p_msgb_alloc();
|
||||
l1p = msgb_l1prim(msg);
|
||||
l1p->id = GsmL1_PrimId_PhDataReq;
|
||||
data_req = &l1p->u.phDataReq;
|
||||
data_req->hLayer1 = (HANDLE)fl1h->hLayer1;
|
||||
data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
|
||||
data_req->subCh = GsmL1_SubCh_NA;
|
||||
data_req->u8BlockNbr = block_nr;
|
||||
data_req->u8Tn = ts;
|
||||
data_req->u32Fn = fn;
|
||||
msu_param = &data_req->msgUnitParam;
|
||||
msu_param->u8Size = len;
|
||||
memcpy(msu_param->u8Buffer, data, len);
|
||||
|
||||
gsmtap_send(fl1h->gsmtap, arfcn, data_req->u8Tn, GSMTAP_CHANNEL_PACCH,
|
||||
0, data_req->u32Fn, 0, 0,
|
||||
data_req->msgUnitParam.u8Buffer,
|
||||
data_req->msgUnitParam.u8Size);
|
||||
|
||||
|
||||
/* transmit */
|
||||
if (osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg) != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
|
||||
{
|
||||
struct oc2gl1_hdl *fl1h;
|
||||
int rc;
|
||||
|
||||
fl1h = talloc_zero(tall_pcu_ctx, struct oc2gl1_hdl);
|
||||
if (!fl1h)
|
||||
return NULL;
|
||||
|
||||
fl1h->hLayer1 = hlayer1;
|
||||
fl1h->trx_no = trx_no;
|
||||
/* hardware queues are numbered starting from 0 */
|
||||
fl1h->hw_info.trx_nr = trx_no;
|
||||
|
||||
DEBUGP(DL1IF, "PCU: Using TRX HW#%u\n", fl1h->hw_info.trx_nr);
|
||||
|
||||
rc = l1if_transport_open(MQ_PDTCH_WRITE, fl1h);
|
||||
if (rc < 0) {
|
||||
talloc_free(fl1h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
|
||||
if (fl1h->gsmtap)
|
||||
gsmtap_source_add_sink(fl1h->gsmtap);
|
||||
|
||||
return fl1h;
|
||||
}
|
||||
|
||||
int l1if_close_pdch(void *obj)
|
||||
{
|
||||
struct oc2gl1_hdl *fl1h = obj;
|
||||
if (fl1h)
|
||||
l1if_transport_close(MQ_PDTCH_WRITE, fl1h);
|
||||
talloc_free(fl1h);
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user