mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
synced 2025-11-03 13:43:32 +00:00
Compare commits
589 Commits
jolly/test
...
jerlbeck/w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88782ddb8f | ||
|
|
31af0ad11b | ||
|
|
c8cbfc2c98 | ||
|
|
ae0a799f44 | ||
|
|
91ff7d1864 | ||
|
|
9659d59307 | ||
|
|
cf6ae9d12f | ||
|
|
af75ce8e15 | ||
|
|
be4a08b58a | ||
|
|
60f77033ad | ||
|
|
e77d49f2a2 | ||
|
|
ac49d0943a | ||
|
|
16d29c7da4 | ||
|
|
b6b3c7eb27 | ||
|
|
1c95bd383e | ||
|
|
159d4de370 | ||
|
|
c62216b4fc | ||
|
|
6835cead8c | ||
|
|
6e013a136a | ||
|
|
af387e2199 | ||
|
|
444bc82081 | ||
|
|
23c4b3f158 | ||
|
|
af45473cd5 | ||
|
|
cef20ae67a | ||
|
|
ee31090b2e | ||
|
|
64921d217b | ||
|
|
56f99d19c3 | ||
|
|
e0b21f41c2 | ||
|
|
257b630216 | ||
|
|
e91bd3babd | ||
|
|
b139598b1c | ||
|
|
076f5c794d | ||
|
|
537b149828 | ||
|
|
4a6fe534ce | ||
|
|
2b349b5d33 | ||
|
|
ebebad1c92 | ||
|
|
56af6d55ed | ||
|
|
f76fedeed5 | ||
|
|
fea17f8b8c | ||
|
|
af9a39d954 | ||
|
|
28c40b1757 | ||
|
|
3449a61032 | ||
|
|
1c3b8998bc | ||
|
|
ac28905082 | ||
|
|
04e72d34f5 | ||
|
|
6eed1911fd | ||
|
|
b31f5ef699 | ||
|
|
d4ad731bae | ||
|
|
4f666bc113 | ||
|
|
7f79f0d332 | ||
|
|
77da35515c | ||
|
|
7b3675bf7a | ||
|
|
0f352a6f22 | ||
|
|
bf9042203d | ||
|
|
7af53e61f0 | ||
|
|
88fb6136fb | ||
|
|
e21b79cb21 | ||
|
|
f16a069fd7 | ||
|
|
5979fe9d8a | ||
|
|
400ec02e8a | ||
|
|
40da3e17e5 | ||
|
|
5a2b8be3f5 | ||
|
|
2b558857dd | ||
|
|
a8c2aaf6f0 | ||
|
|
69c9bfa089 | ||
|
|
b2439bbb8a | ||
|
|
3a10dbd564 | ||
|
|
e0853cdf42 | ||
|
|
5879c6493f | ||
|
|
47a57f6f86 | ||
|
|
61205a7e65 | ||
|
|
57cf69a18c | ||
|
|
dd08ac86e6 | ||
|
|
bae33a7001 | ||
|
|
5e46a20e03 | ||
|
|
c135b878cd | ||
|
|
1139ec1d0f | ||
|
|
3db617f14a | ||
|
|
efe62a7395 | ||
|
|
14376a73a5 | ||
|
|
506f156f7a | ||
|
|
1653f837e3 | ||
|
|
20b7ba7501 | ||
|
|
cc9358f95a | ||
|
|
16c6ecc365 | ||
|
|
5f494b8415 | ||
|
|
9ae282372c | ||
|
|
ed46afda6f | ||
|
|
ea65c72d06 | ||
|
|
c91c18e6ef | ||
|
|
e0c734dcfe | ||
|
|
07111668d4 | ||
|
|
4944c195d4 | ||
|
|
5cd496d208 | ||
|
|
83426b20a3 | ||
|
|
617c7127f4 | ||
|
|
23f93a15ca | ||
|
|
ec478756cc | ||
|
|
9380f5d218 | ||
|
|
ac89a555fa | ||
|
|
699b8dca49 | ||
|
|
ccc34e4d30 | ||
|
|
c6d4ceeda6 | ||
|
|
9ec49e2411 | ||
|
|
fa464bbce9 | ||
|
|
e565564bc9 | ||
|
|
cb1b494c89 | ||
|
|
6d86628e5b | ||
|
|
e2e004e7a9 | ||
|
|
ace7b570a0 | ||
|
|
f1379346f7 | ||
|
|
34cf156b80 | ||
|
|
9cc783a87d | ||
|
|
a99d95e3af | ||
|
|
e500e2e5d1 | ||
|
|
b671dbfe94 | ||
|
|
1e50a3dade | ||
|
|
70b96aa232 | ||
|
|
07eb655244 | ||
|
|
1eae96ca2f | ||
|
|
626369c2fb | ||
|
|
409efa1ec8 | ||
|
|
411686402b | ||
|
|
04a108617a | ||
|
|
e1d2b3568a | ||
|
|
da1a79ef5b | ||
|
|
51b1151044 | ||
|
|
94cde130ca | ||
|
|
e4bcb62dbf | ||
|
|
20f6fd1b63 | ||
|
|
b4584ff6c4 | ||
|
|
0808f68601 | ||
|
|
25db7c6116 | ||
|
|
7bf9f49728 | ||
|
|
0ae4313800 | ||
|
|
d0aee85b29 | ||
|
|
09fdf6622a | ||
|
|
37e896dff1 | ||
|
|
b33e675e5a | ||
|
|
8158ea7288 | ||
|
|
144a1d0516 | ||
|
|
8322d08071 | ||
|
|
a17fccbcf4 | ||
|
|
1751c62c98 | ||
|
|
f47f68a9d8 | ||
|
|
62e96a3535 | ||
|
|
a700dd9e11 | ||
|
|
17214bb06d | ||
|
|
befc760f86 | ||
|
|
489a2b35d8 | ||
|
|
10ed79553a | ||
|
|
1d0a52a349 | ||
|
|
6dbe822062 | ||
|
|
b3f713bd7b | ||
|
|
3c91cb881d | ||
|
|
a098c19b55 | ||
|
|
d9e102472a | ||
|
|
1db67e0a35 | ||
|
|
9200ce6019 | ||
|
|
ddfc0d5763 | ||
|
|
d3eac2867a | ||
|
|
1c68abaffa | ||
|
|
71e55118f5 | ||
|
|
7b9f825ae8 | ||
|
|
b0e5eaf59a | ||
|
|
9a2845d491 | ||
|
|
0e50ce6145 | ||
|
|
767193e20b | ||
|
|
d1cb41bfd0 | ||
|
|
5752285bc5 | ||
|
|
b75e23143b | ||
|
|
4f459799e3 | ||
|
|
be0cbc1b7e | ||
|
|
87597358cf | ||
|
|
9399046729 | ||
|
|
e43460b50f | ||
|
|
5367086175 | ||
|
|
dfef28de88 | ||
|
|
67c385046d | ||
|
|
fecece0e59 | ||
|
|
e04e0b0a20 | ||
|
|
6eeb7c7e74 | ||
|
|
6e4ccec6c4 | ||
|
|
0288cdb0a8 | ||
|
|
3d62fc55d8 | ||
|
|
87d7341fbe | ||
|
|
29d91e9271 | ||
|
|
801d6fe40c | ||
|
|
0494c75a53 | ||
|
|
0d39dc92b5 | ||
|
|
1f33294b1c | ||
|
|
2acfbebfd3 | ||
|
|
c0c580c414 | ||
|
|
56e8d71090 | ||
|
|
00fc6b13f2 | ||
|
|
90de3a7ffe | ||
|
|
d58b711eec | ||
|
|
cc12f02658 | ||
|
|
d0261b72de | ||
|
|
0c1c8778df | ||
|
|
0a0b5dcb32 | ||
|
|
502bd1feea | ||
|
|
e25b5b91f6 | ||
|
|
2cbe80b53e | ||
|
|
3bed5d11d2 | ||
|
|
cbb1e70554 | ||
|
|
c495209122 | ||
|
|
005ee7f862 | ||
|
|
2493c660e9 | ||
|
|
a3e4509ff9 | ||
|
|
eceb910fef | ||
|
|
95340242ed | ||
|
|
612e93e360 | ||
|
|
39645b824a | ||
|
|
1e96af6325 | ||
|
|
7c44415d78 | ||
|
|
0eabffdc35 | ||
|
|
1842c921b3 | ||
|
|
adcdf150a6 | ||
|
|
a41a71e2d4 | ||
|
|
297edf754f | ||
|
|
08fe76a893 | ||
|
|
5e9f40d3d9 | ||
|
|
18fef10641 | ||
|
|
f5c97476de | ||
|
|
49f26bf6e8 | ||
|
|
07e39302ec | ||
|
|
0f58af6627 | ||
|
|
635d47c78c | ||
|
|
1c15573012 | ||
|
|
efd5dbb168 | ||
|
|
510d7d36ed | ||
|
|
e481815583 | ||
|
|
08e57c8366 | ||
|
|
341689d6c5 | ||
|
|
ad1fcccf60 | ||
|
|
ca102af92b | ||
|
|
eb100244b0 | ||
|
|
418a4230e0 | ||
|
|
cf706b0775 | ||
|
|
e2732e2f59 | ||
|
|
f72fcfe219 | ||
|
|
7e994e392d | ||
|
|
6a8a1dcda2 | ||
|
|
d1d1633121 | ||
|
|
cd44ec41c5 | ||
|
|
f55e58f5cf | ||
|
|
350f64d9e2 | ||
|
|
4f3c420852 | ||
|
|
b2886f1a0d | ||
|
|
93d1711011 | ||
|
|
6a16e0c092 | ||
|
|
351a573396 | ||
|
|
3016888ee0 | ||
|
|
532a4b54f5 | ||
|
|
1dac2ebb71 | ||
|
|
b8f260176e | ||
|
|
6c813fc9bc | ||
|
|
538ac5b574 | ||
|
|
2354402b7a | ||
|
|
057c285cd7 | ||
|
|
1b3864fc47 | ||
|
|
4bbe3349c2 | ||
|
|
48aa0b0d99 | ||
|
|
0d12a2fa89 | ||
|
|
fe6e2e4a08 | ||
|
|
078bb713e1 | ||
|
|
2207c5e4ef | ||
|
|
febf1a0ac9 | ||
|
|
54044b0635 | ||
|
|
b59d61b4b4 | ||
|
|
e8915b9d9d | ||
|
|
2c98f1db78 | ||
|
|
c421e8aaf2 | ||
|
|
35cc1c0ff3 | ||
|
|
f3405e5b03 | ||
|
|
6d8884de49 | ||
|
|
77e58f602d | ||
|
|
5d77f14904 | ||
|
|
17a1d5e162 | ||
|
|
1e0c61032f | ||
|
|
cf1fae7f38 | ||
|
|
73191a443f | ||
|
|
fc03bbe078 | ||
|
|
9167055ed2 | ||
|
|
8f3520579a | ||
|
|
f81e2f7621 | ||
|
|
59fe8f79cc | ||
|
|
d8f339592d | ||
|
|
a09e33cdeb | ||
|
|
e5109ba1f0 | ||
|
|
3d0cc2f97d | ||
|
|
a004799699 | ||
|
|
b293043e2d | ||
|
|
80367aae17 | ||
|
|
772415fd8a | ||
|
|
afa72f5210 | ||
|
|
85c1ea5cb6 | ||
|
|
3ce011f44f | ||
|
|
d54d9f5c75 | ||
|
|
146514e180 | ||
|
|
55844795be | ||
|
|
7c3751b10b | ||
|
|
f4a1ec6ce7 | ||
|
|
c2fab7a6ff | ||
|
|
0a940087b2 | ||
|
|
2bf3446f7a | ||
|
|
746b390201 | ||
|
|
1cd9d886e6 | ||
|
|
fe2dcc8aec | ||
|
|
f3eec04655 | ||
|
|
765736dc77 | ||
|
|
7a16d46fdc | ||
|
|
ccde4c462d | ||
|
|
b03d427b08 | ||
|
|
73193110f2 | ||
|
|
1fe69323ad | ||
|
|
3fd2ddf1a2 | ||
|
|
dd4af8045f | ||
|
|
e45c19b3e8 | ||
|
|
882fc9b174 | ||
|
|
f34f34495b | ||
|
|
df022f6cb2 | ||
|
|
c7b998cc73 | ||
|
|
4af30533f0 | ||
|
|
f37e514a96 | ||
|
|
8f399de135 | ||
|
|
ba26368040 | ||
|
|
6f791d049a | ||
|
|
6f0796a33a | ||
|
|
fdcdde2756 | ||
|
|
402cdcd02f | ||
|
|
2f1974b5ac | ||
|
|
1b517342ae | ||
|
|
705653b2d7 | ||
|
|
aadfc2e121 | ||
|
|
9c623892f5 | ||
|
|
a42b2ad5ed | ||
|
|
550bb88a9e | ||
|
|
88553abf4d | ||
|
|
c92b964e2d | ||
|
|
b3a87ced5a | ||
|
|
81e8f0a8a2 | ||
|
|
752a3b2baa | ||
|
|
5e94cd4fde | ||
|
|
cc5a4cbe9b | ||
|
|
48df40d2a4 | ||
|
|
f1786a375f | ||
|
|
6f7cb2cb4f | ||
|
|
0f2b5fc749 | ||
|
|
5241c1a018 | ||
|
|
52ea8a0d87 | ||
|
|
e6e605ba86 | ||
|
|
c3f4330fa3 | ||
|
|
f86fb7a953 | ||
|
|
8a31f9e016 | ||
|
|
11f2d58dbd | ||
|
|
3cbf9e040c | ||
|
|
3c95776805 | ||
|
|
7a5f3c2153 | ||
|
|
7f3e662b34 | ||
|
|
cbb00ebd04 | ||
|
|
423dd2286b | ||
|
|
4c06d9155c | ||
|
|
c15d5cc230 | ||
|
|
9977e1545a | ||
|
|
6ab5b24be4 | ||
|
|
7decedcbf8 | ||
|
|
faf3ef45b3 | ||
|
|
e9b1ebba9d | ||
|
|
270f7fce1d | ||
|
|
f1593b7c49 | ||
|
|
ab6cd921e3 | ||
|
|
9a76968ec4 | ||
|
|
9c5539d46d | ||
|
|
86dc355a33 | ||
|
|
bc15570651 | ||
|
|
e358ff8fa4 | ||
|
|
df6b4f52e0 | ||
|
|
9eb8ace260 | ||
|
|
12c039cdb2 | ||
|
|
a6ba67cb3a | ||
|
|
8b16ae30fe | ||
|
|
c03e38291a | ||
|
|
1577779526 | ||
|
|
ef93bdb19b | ||
|
|
64b49bc3ce | ||
|
|
40fc8f9e46 | ||
|
|
b18aedcc50 | ||
|
|
9525567d77 | ||
|
|
6b5660c19f | ||
|
|
321f3c3104 | ||
|
|
092478f294 | ||
|
|
5a7f636ce4 | ||
|
|
c6382ddd6e | ||
|
|
6058220d2a | ||
|
|
58db60c68e | ||
|
|
c3d5325fc8 | ||
|
|
c70aae4697 | ||
|
|
937e2a6919 | ||
|
|
e5dc2a0ac5 | ||
|
|
796270bf83 | ||
|
|
9241fd0957 | ||
|
|
e9429b5d3e | ||
|
|
55cf994c29 | ||
|
|
b1302b083e | ||
|
|
32f9a59ab4 | ||
|
|
e23102602c | ||
|
|
b3d5ee2934 | ||
|
|
51e093bd1c | ||
|
|
985fd114f2 | ||
|
|
fce431cf3a | ||
|
|
aa35ba7584 | ||
|
|
857281f7ff | ||
|
|
d26318e4a7 | ||
|
|
4e8b50cd8d | ||
|
|
0e0f2f5faf | ||
|
|
a6e47c7f54 | ||
|
|
5697b4ccfa | ||
|
|
acb5427bda | ||
|
|
be57081721 | ||
|
|
b7840466ce | ||
|
|
a1da251c10 | ||
|
|
096f6f9f39 | ||
|
|
758dc12c9f | ||
|
|
28e5378b55 | ||
|
|
9948514086 | ||
|
|
77e05971b4 | ||
|
|
bc1626e5de | ||
|
|
b809866be5 | ||
|
|
5464c9baf2 | ||
|
|
34f6e5ebe6 | ||
|
|
474685e26e | ||
|
|
bd449f57a7 | ||
|
|
870c601f1d | ||
|
|
6b88cddc21 | ||
|
|
8021644e9d | ||
|
|
ec80f82824 | ||
|
|
fc498c9e7b | ||
|
|
875fc895a8 | ||
|
|
e1a075ab59 | ||
|
|
53a336f0e5 | ||
|
|
8d0e489484 | ||
|
|
15bb1a2a51 | ||
|
|
5da2014f13 | ||
|
|
7a344716a6 | ||
|
|
cb5c49b581 | ||
|
|
750ca67ce9 | ||
|
|
842808848c | ||
|
|
5a9658168a | ||
|
|
396f4161cb | ||
|
|
c1ae22694c | ||
|
|
90b87ea5e6 | ||
|
|
180def907b | ||
|
|
1997787c52 | ||
|
|
b98dd9e240 | ||
|
|
93e048fe27 | ||
|
|
158776411b | ||
|
|
f537298cca | ||
|
|
61a0a04d26 | ||
|
|
3dc56a3b34 | ||
|
|
ae03f22199 | ||
|
|
22b31190cb | ||
|
|
46bcb8d59d | ||
|
|
02beed5e98 | ||
|
|
2db7e7e7db | ||
|
|
24c1a5ba29 | ||
|
|
42de18f347 | ||
|
|
d9262b3b55 | ||
|
|
40cfaa6837 | ||
|
|
7292373f92 | ||
|
|
70ddde6929 | ||
|
|
f63cabd931 | ||
|
|
a54bbbbf02 | ||
|
|
dea63b96e0 | ||
|
|
05f8efc1a2 | ||
|
|
4f753c64d6 | ||
|
|
65be4808af | ||
|
|
6673005215 | ||
|
|
782f6ddf99 | ||
|
|
86300bbeea | ||
|
|
af8094d799 | ||
|
|
aa9c326d7e | ||
|
|
fcbc702112 | ||
|
|
d11290b90b | ||
|
|
6f9f434463 | ||
|
|
9ae367f639 | ||
|
|
b65e08a7be | ||
|
|
545876550f | ||
|
|
6796ed23ab | ||
|
|
09ef27ae04 | ||
|
|
4ed1dae533 | ||
|
|
1702f102af | ||
|
|
7ce21eb042 | ||
|
|
34bd8bdf30 | ||
|
|
9f0c1d216a | ||
|
|
cedf890928 | ||
|
|
621dc2fd01 | ||
|
|
111614a994 | ||
|
|
d6ef5345e5 | ||
|
|
63f29d6f73 | ||
|
|
6ca0a9076e | ||
|
|
9446485e3d | ||
|
|
f0984897a5 | ||
|
|
24e98d039d | ||
|
|
17b0d83a1f | ||
|
|
ba5543fbf3 | ||
|
|
b78adcdd11 | ||
|
|
9b30c7f46e | ||
|
|
b6acfdaa24 | ||
|
|
67ed34eedb | ||
|
|
1b33361bab | ||
|
|
b19d7268c3 | ||
|
|
1af73f6c81 | ||
|
|
ee7a535608 | ||
|
|
485860cc31 | ||
|
|
d507ce6b85 | ||
|
|
8d7a632eef | ||
|
|
4ffc260869 | ||
|
|
9f521cd4af | ||
|
|
b0250ebeac | ||
|
|
698b612188 | ||
|
|
ae947fcf67 | ||
|
|
344ff48756 | ||
|
|
173a240a7e | ||
|
|
45396a99d9 | ||
|
|
bfdd5f285b | ||
|
|
948a3d62b1 | ||
|
|
b0a0075845 | ||
|
|
11a748935e | ||
|
|
8481a0553d | ||
|
|
743bafa50c | ||
|
|
96efa70a9e | ||
|
|
02ab4a8803 | ||
|
|
96173aec93 | ||
|
|
1c344e2668 | ||
|
|
7380babdba | ||
|
|
0946f99b08 | ||
|
|
455613076a | ||
|
|
4f6a4e5d6d | ||
|
|
964ddb6aa0 | ||
|
|
9e21d84f1e | ||
|
|
099535a1a7 | ||
|
|
8692128365 | ||
|
|
bb20b2c64c | ||
|
|
bcafdf8d90 | ||
|
|
443c822f77 | ||
|
|
d868928888 | ||
|
|
d1d114f5b7 | ||
|
|
31d0df92ac | ||
|
|
17c31ce173 | ||
|
|
dcc9c39529 | ||
|
|
70862c9409 | ||
|
|
26da8361e9 | ||
|
|
483f77a275 | ||
|
|
985806030d | ||
|
|
52c911b3b4 | ||
|
|
2023bd18b6 | ||
|
|
d6bd91e4e5 | ||
|
|
9d938388f6 | ||
|
|
bc1e52cfbf | ||
|
|
741481d3e0 | ||
|
|
4ea940787e | ||
|
|
416ce69550 | ||
|
|
c0f1644c88 | ||
|
|
e8d9a5fa54 | ||
|
|
b67a8a348a | ||
|
|
90d5df4ae7 | ||
|
|
a9744debd9 | ||
|
|
dfe17d7f91 | ||
|
|
e13298d093 | ||
|
|
bb00704871 | ||
|
|
421fe79e39 | ||
|
|
939bfaefec | ||
|
|
f14ddb7830 | ||
|
|
997d2ac3fe | ||
|
|
cf0265a112 | ||
|
|
49be8bce50 | ||
|
|
148fb9a3bc | ||
|
|
98fe945a0d | ||
|
|
249c7e9431 | ||
|
|
40bd0c4b57 | ||
|
|
4b984b14a3 | ||
|
|
ed70cb733c | ||
|
|
d8157c07df | ||
|
|
90692f93cf | ||
|
|
90f08efe58 | ||
|
|
a30f47613a | ||
|
|
51c57045e5 |
20
.gitignore
vendored
20
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
*.o
|
||||
*.lo
|
||||
*.a
|
||||
*.sw?
|
||||
Makefile.in
|
||||
Makefile
|
||||
.deps
|
||||
@@ -22,3 +23,22 @@ core
|
||||
core.*
|
||||
|
||||
osmoappdesc.pyc
|
||||
|
||||
# binaries
|
||||
src/osmo-pcu
|
||||
src/osmo-pcu-remote
|
||||
|
||||
# tests
|
||||
.dirstamp
|
||||
tests/atconfig
|
||||
tests/package.m4
|
||||
tests/alloc/AllocTest
|
||||
tests/rlcmac/RLCMACTest
|
||||
tests/tbf/TbfTest
|
||||
tests/types/TypesTest
|
||||
tests/ms/MsTest
|
||||
tests/llist/LListTest
|
||||
tests/codel/codel_test
|
||||
tests/emu/pcu_emu
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
|
||||
13
Makefile.am
13
Makefile.am
@@ -1,16 +1,5 @@
|
||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
|
||||
SUBDIRS = src examples
|
||||
SUBDIRS = src examples tests
|
||||
EXTRA_DIST = osmoappdesc.py
|
||||
|
||||
if ENABLE_VTY_TESTS
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
osmotestvty.py -p $(top_srcdir) -w $(builddir) -v
|
||||
osmotestconfig.py -p $(top_srcdir) -w $(builddir) -v
|
||||
else
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
@echo "Not running python-based tests (determined at configure-time)"
|
||||
endif
|
||||
|
||||
check-local: $(BUILT_SOURCES)
|
||||
$(MAKE) $(AM_MAKEFLAGS) python-tests
|
||||
|
||||
42
TODO
Normal file
42
TODO
Normal file
@@ -0,0 +1,42 @@
|
||||
* 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.
|
||||
|
||||
|
||||
* Possible race condition:
|
||||
When scheduling a Downlink Assignment on the UL-TBF we need to make
|
||||
sure that the assignment is sent _before_ the final ack. With my fairness
|
||||
changes it gets more likely that this event is trigerred.
|
||||
|
||||
* Optimize:
|
||||
After receiving an ACK/NACK.. schedule another one if the window
|
||||
is kind of stalled anyway. This way we avoid resending frames that
|
||||
might have already arrived. It could increase the throughput..
|
||||
|
||||
Do not re-transmit after we got ack/nacked and where in the resending
|
||||
mode... and increase the window.
|
||||
|
||||
<0004> tbf.cpp:907 - Sending new block at BSN 111
|
||||
...
|
||||
tbf.cpp:858 - Restarting at BSN 48, because all window is stalled.
|
||||
...
|
||||
tbf.cpp:1383 - V(B): (V(A)=59)"NNAAAAAAANAAAAANNAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXX"(V(S)-1=111) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid
|
||||
.. retransmitting the nacked.. and then the ones that migh have
|
||||
already arrived
|
||||
<0004> tbf.cpp:834 TBF(TFI=0 TLLI=0xd7b78810 DIR=DL) downlink (V(A)==59 .. V(S)==112)
|
||||
<0004> tbf.cpp:840 - Resending BSN 111
|
||||
|
||||
|
||||
Figure out scheduling issue. Why do we reach the 20 re-transmits and
|
||||
stil haven't received the ACK/NACK? was it scheduled? The whole
|
||||
scheduler could be re-worked to be more determestic.. and answer
|
||||
questions like if it has been sent or not
|
||||
@@ -1,9 +1,10 @@
|
||||
dnl Process this file with autoconf to produce a configure script
|
||||
AC_INIT([osmo-pcu],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[osmocom-pcu@lists.osmocom.org])
|
||||
[osmocom-net-gprs@lists.osmocom.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
@@ -60,4 +61,5 @@ AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
|
||||
AC_OUTPUT(
|
||||
src/Makefile
|
||||
examples/Makefile
|
||||
tests/Makefile
|
||||
Makefile)
|
||||
|
||||
16
contrib/sysmopcu.service
Normal file
16
contrib/sysmopcu.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=sysmocom sysmoPCU
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg -e
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
|
||||
# The msg queues must be read fast enough
|
||||
CPUSchedulingPolicy=rr
|
||||
CPUSchedulingPriority=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,6 +1,6 @@
|
||||
pcu
|
||||
flow-control-interval 10
|
||||
cs 4
|
||||
alloc-algorithm b
|
||||
cs 2
|
||||
alloc-algorithm dynamic
|
||||
alpha 0
|
||||
gamma 0
|
||||
|
||||
@@ -25,6 +25,7 @@ AM_CPPFLAGS += -DENABLE_SYSMODSP
|
||||
endif
|
||||
|
||||
AM_CXXFLAGS = -Wall -ldl -pthread
|
||||
AM_LDFLAGS = -lrt
|
||||
|
||||
noinst_LTLIBRARIES = libgprs.la
|
||||
|
||||
@@ -34,13 +35,27 @@ libgprs_la_SOURCES = \
|
||||
gsm_rlcmac.cpp \
|
||||
gprs_bssgp_pcu.cpp \
|
||||
gprs_rlcmac.cpp \
|
||||
gprs_rlcmac_data.cpp \
|
||||
gprs_rlcmac_sched.cpp \
|
||||
gprs_rlcmac_meas.cpp \
|
||||
gprs_rlcmac_ts_alloc.cpp \
|
||||
gprs_ms.cpp \
|
||||
gprs_ms_storage.cpp \
|
||||
gsm_timer.cpp \
|
||||
bitvector.cpp \
|
||||
pcu_l1_if.cpp \
|
||||
pcu_vty.c
|
||||
pcu_vty.c \
|
||||
pcu_vty_functions.cpp \
|
||||
tbf.cpp \
|
||||
tbf_ul.cpp \
|
||||
tbf_dl.cpp \
|
||||
bts.cpp \
|
||||
poll_controller.cpp \
|
||||
encoding.cpp \
|
||||
sba.cpp \
|
||||
decoding.cpp \
|
||||
llc.cpp \
|
||||
rlc.cpp \
|
||||
gprs_codel.c
|
||||
|
||||
if ENABLE_SYSMOBTS
|
||||
libgprs_la_SOURCES += \
|
||||
@@ -50,12 +65,11 @@ libgprs_la_SOURCES += \
|
||||
openbts_sock.cpp
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
RLCMACTest
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-pcu
|
||||
|
||||
noinst_PROGRAMS =
|
||||
|
||||
if ENABLE_SYSMODSP
|
||||
noinst_PROGRAMS += \
|
||||
osmo-pcu-remote
|
||||
@@ -67,19 +81,27 @@ noinst_HEADERS = \
|
||||
gsm_rlcmac.h \
|
||||
gprs_bssgp_pcu.h \
|
||||
gprs_rlcmac.h \
|
||||
gprs_ms.h \
|
||||
gprs_ms_storage.h \
|
||||
pcuif_proto.h \
|
||||
pcu_l1_if.h \
|
||||
gsm_timer.h \
|
||||
bitvector.h \
|
||||
pcu_vty.h \
|
||||
pcu_vty_functions.h \
|
||||
sysmo_l1_if.h \
|
||||
femtobts.h
|
||||
|
||||
RLCMACTest_SOURCES = RLCMACTest.cpp
|
||||
RLCMACTest_LDADD = \
|
||||
libgprs.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
femtobts.h \
|
||||
tbf.h \
|
||||
bts.h \
|
||||
poll_controller.h \
|
||||
encoding.h \
|
||||
sba.h \
|
||||
rlc.h \
|
||||
decoding.h \
|
||||
llc.h \
|
||||
pcu_utils.h \
|
||||
cxx_linuxlist.h \
|
||||
gprs_codel.h
|
||||
|
||||
osmo_pcu_SOURCES = pcu_main.cpp
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ void bitvec_free(struct bitvec *bv)
|
||||
talloc_free(bv);
|
||||
}
|
||||
|
||||
int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
|
||||
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned int i = 0;
|
||||
for (i = 0; i < bv->data_len; i++)
|
||||
{
|
||||
buffer[i] = bv->data[i];
|
||||
@@ -57,9 +57,9 @@ int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
|
||||
return i;
|
||||
}
|
||||
|
||||
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
|
||||
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned int i = 0;
|
||||
for (i = 0; i < bv->data_len; i++)
|
||||
{
|
||||
bv->data[i] = buffer[i];
|
||||
@@ -84,7 +84,7 @@ int bitvec_unhex(struct bitvec *bv, const char* src)
|
||||
|
||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len)
|
||||
{
|
||||
int i;
|
||||
unsigned int i;
|
||||
uint64_t ui = 0;
|
||||
bv->cur_bit = read_index;
|
||||
|
||||
@@ -103,7 +103,8 @@ uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len
|
||||
|
||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
|
||||
{
|
||||
int i, rc;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
bv->cur_bit = write_index;
|
||||
for (i = 0; i < len; i++) {
|
||||
int bit = 0;
|
||||
|
||||
@@ -35,8 +35,8 @@ extern "C" {
|
||||
struct bitvec *bitvec_alloc(unsigned size);
|
||||
void bitvec_free(struct bitvec *bv);
|
||||
int bitvec_unhex(struct bitvec *bv, const char* src);
|
||||
int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
|
||||
int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
|
||||
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
|
||||
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
|
||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len);
|
||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
|
||||
|
||||
|
||||
1281
src/bts.cpp
Normal file
1281
src/bts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
434
src/bts.h
Normal file
434
src/bts.h
Normal file
@@ -0,0 +1,434 @@
|
||||
/* bts.h
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
}
|
||||
|
||||
#include "poll_controller.h"
|
||||
#include "sba.h"
|
||||
#include "tbf.h"
|
||||
#include "gprs_ms_storage.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LLC_CODEL_DISABLE 0
|
||||
#define LLC_CODEL_USE_DEFAULT (-1)
|
||||
|
||||
struct BTS;
|
||||
struct GprsMs;
|
||||
|
||||
/*
|
||||
* PDCH instance
|
||||
*/
|
||||
struct gprs_rlcmac_pdch {
|
||||
#ifdef __cplusplus
|
||||
struct gprs_rlcmac_paging *dequeue_paging();
|
||||
struct msgb *packet_paging_request();
|
||||
|
||||
void add_paging(struct gprs_rlcmac_paging *pag);
|
||||
|
||||
void free_resources();
|
||||
|
||||
bool is_enabled() const;
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
/* dispatching of messages */
|
||||
int rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
|
||||
struct pcu_l1_meas *meas);
|
||||
|
||||
gprs_rlcmac_bts *bts_data() const;
|
||||
BTS *bts() const;
|
||||
uint8_t trx_no() const;
|
||||
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi);
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi);
|
||||
|
||||
void attach_tbf(gprs_rlcmac_tbf *tbf);
|
||||
void detach_tbf(gprs_rlcmac_tbf *tbf);
|
||||
|
||||
unsigned num_tbfs(enum gprs_rlcmac_tbf_direction dir) const;
|
||||
|
||||
void reserve(enum gprs_rlcmac_tbf_direction dir);
|
||||
void unreserve(enum gprs_rlcmac_tbf_direction dir);
|
||||
unsigned num_reserved(enum gprs_rlcmac_tbf_direction dir) const;
|
||||
|
||||
uint8_t assigned_usf() const;
|
||||
uint32_t assigned_tfi(enum gprs_rlcmac_tbf_direction dir) const;
|
||||
#endif
|
||||
|
||||
uint8_t m_is_enabled; /* TS is enabled */
|
||||
uint8_t tsc; /* TSC of this slot */
|
||||
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
|
||||
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
|
||||
uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */
|
||||
struct llist_head paging_list; /* list of paging messages */
|
||||
uint32_t last_rts_fn; /* store last frame number of RTS */
|
||||
|
||||
/* back pointers */
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t ts_no;
|
||||
|
||||
#ifdef __cplusplus
|
||||
private:
|
||||
int rcv_data_block_acknowledged(uint8_t *data, uint8_t len,
|
||||
struct pcu_l1_meas *meas);
|
||||
int rcv_control_block(bitvec *rlc_block, uint32_t fn);
|
||||
|
||||
void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
|
||||
void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn);
|
||||
void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn);
|
||||
void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);
|
||||
gprs_rlcmac_tbf *tbf_from_list_by_tfi(struct llist_head *tbf_list, uint8_t tfi,
|
||||
enum gprs_rlcmac_tbf_direction dir);
|
||||
gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi,
|
||||
enum gprs_rlcmac_tbf_direction dir);
|
||||
#endif
|
||||
|
||||
uint8_t m_num_tbfs[2];
|
||||
uint8_t m_num_reserved[2];
|
||||
uint8_t m_assigned_usf; /* bit set */
|
||||
uint32_t m_assigned_tfi[2]; /* bit set */
|
||||
struct gprs_rlcmac_tbf *m_tbfs[2][32];
|
||||
};
|
||||
|
||||
struct gprs_rlcmac_trx {
|
||||
void *fl1h;
|
||||
uint16_t arfcn;
|
||||
struct gprs_rlcmac_pdch pdch[8];
|
||||
|
||||
/* back pointers */
|
||||
struct 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
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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 max_cs_dl, max_cs_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 gprs_rlcmac_trx trx[8];
|
||||
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts,
|
||||
struct GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
|
||||
int use_tbf);
|
||||
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
|
||||
uint8_t force_two_phase;
|
||||
uint8_t alpha, gamma;
|
||||
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
|
||||
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[4];
|
||||
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
|
||||
|
||||
/* TBF handling, make private or move into TBFController */
|
||||
/* list of uplink TBFs */
|
||||
struct llist_head ul_tbfs;
|
||||
/* list of downlink TBFs */
|
||||
struct llist_head dl_tbfs;
|
||||
|
||||
/* State for dynamic algorithm selection */
|
||||
int multislot_disabled;
|
||||
|
||||
/**
|
||||
* Point back to the C++ object. This is used during the transition
|
||||
* period.
|
||||
*/
|
||||
struct BTS *bts;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
/**
|
||||
* 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_UL_ALLOCATED,
|
||||
CTR_TBF_UL_FREED,
|
||||
CTR_TBF_REUSED,
|
||||
CTR_TBF_ALLOC_ALGO_A,
|
||||
CTR_TBF_ALLOC_ALGO_B,
|
||||
CTR_RLC_SENT,
|
||||
CTR_RLC_RESENT,
|
||||
CTR_RLC_RESTARTED,
|
||||
CTR_RLC_STALLED,
|
||||
CTR_RLC_NACKED,
|
||||
CTR_RLC_ASS_TIMEDOUT,
|
||||
CTR_RLC_ACK_TIMEDOUT,
|
||||
CTR_RLC_REL_TIMEDOUT,
|
||||
CTR_RLC_LATE_BLOCK,
|
||||
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_RACH_REQUESTS,
|
||||
};
|
||||
|
||||
enum {
|
||||
TIMER_T3190_MSEC = 5000,
|
||||
};
|
||||
|
||||
BTS();
|
||||
~BTS();
|
||||
|
||||
static BTS* main_bts();
|
||||
|
||||
struct gprs_rlcmac_bts *bts_data();
|
||||
SBAController *sba();
|
||||
|
||||
/** 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;
|
||||
|
||||
/** add paging to paging queue(s) */
|
||||
int add_paging(uint8_t chan_needed, uint8_t *identity_lv);
|
||||
|
||||
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);
|
||||
|
||||
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
|
||||
|
||||
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
|
||||
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
|
||||
|
||||
void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
|
||||
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);
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
void tbf_dl_created();
|
||||
void tbf_dl_freed();
|
||||
void tbf_ul_created();
|
||||
void tbf_ul_freed();
|
||||
void tbf_reused();
|
||||
void tbf_alloc_algo_a();
|
||||
void tbf_alloc_algo_b();
|
||||
void rlc_sent();
|
||||
void rlc_resent();
|
||||
void rlc_restarted();
|
||||
void rlc_stalled();
|
||||
void rlc_nacked();
|
||||
void rlc_ass_timedout();
|
||||
void rlc_ack_timedout();
|
||||
void rlc_rel_timedout();
|
||||
void rlc_late_block();
|
||||
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 rach_frame();
|
||||
|
||||
/*
|
||||
* Below for C interface for the VTY
|
||||
*/
|
||||
struct rate_ctr_group *rate_counters() const;
|
||||
|
||||
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;
|
||||
|
||||
GprsMsStorage m_ms_store;
|
||||
|
||||
private:
|
||||
/* disable copying to avoid slicing */
|
||||
BTS(const BTS&);
|
||||
BTS& operator=(const BTS&);
|
||||
};
|
||||
|
||||
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 BTS *gprs_rlcmac_pdch::bts() const
|
||||
{
|
||||
return trx->bts;
|
||||
}
|
||||
|
||||
inline unsigned gprs_rlcmac_pdch::num_tbfs(enum gprs_rlcmac_tbf_direction dir) const
|
||||
{
|
||||
return m_num_tbfs[dir];
|
||||
}
|
||||
|
||||
inline unsigned gprs_rlcmac_pdch::num_reserved(
|
||||
enum gprs_rlcmac_tbf_direction dir) const
|
||||
{
|
||||
return gprs_rlcmac_pdch::m_num_reserved[dir];
|
||||
}
|
||||
|
||||
inline uint8_t gprs_rlcmac_pdch::assigned_usf() const
|
||||
{
|
||||
return m_assigned_usf;
|
||||
}
|
||||
|
||||
inline uint32_t gprs_rlcmac_pdch::assigned_tfi(
|
||||
enum gprs_rlcmac_tbf_direction dir) const
|
||||
{
|
||||
return m_assigned_tfi[dir];
|
||||
}
|
||||
|
||||
inline struct rate_ctr_group *BTS::rate_counters() const
|
||||
{
|
||||
return m_ratectrs;
|
||||
}
|
||||
|
||||
#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_ul_created, CTR_TBF_UL_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
|
||||
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(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_ass_timedout, CTR_RLC_ASS_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
|
||||
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_INLINE(rach_frame, CTR_RACH_REQUESTS);
|
||||
|
||||
#undef CREATE_COUNT_INLINE
|
||||
|
||||
|
||||
inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
|
||||
{
|
||||
return trx->bts->bts_data();
|
||||
}
|
||||
|
||||
inline uint8_t gprs_rlcmac_pdch::trx_no() const
|
||||
{
|
||||
return trx->trx_no;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
struct gprs_rlcmac_bts *bts_main_data();
|
||||
struct rate_ctr_group *bts_main_data_stats();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
inline bool gprs_rlcmac_pdch::is_enabled() const
|
||||
{
|
||||
return m_is_enabled;
|
||||
}
|
||||
#endif
|
||||
51
src/csn1.cpp
51
src/csn1.cpp
@@ -32,6 +32,8 @@
|
||||
#include <cstdlib>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include "csn1.h"
|
||||
#include <gprs_debug.h>
|
||||
|
||||
@@ -87,6 +89,7 @@ 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 char* ErrCodes[] =
|
||||
@@ -118,44 +121,6 @@ ProcessError( unsigned readIndex, const char* sz, gint16 err, const CSN_DESCR* p
|
||||
return err;
|
||||
}
|
||||
|
||||
//#if 0
|
||||
static const char* CSN_DESCR_type[]=
|
||||
{
|
||||
"CSN_END",
|
||||
"CSN_BIT",
|
||||
"CSN_UINT",
|
||||
"CSN_TYPE",
|
||||
"CSN_CHOICE",
|
||||
"CSN_UNION",
|
||||
"CSN_UNION_LH",
|
||||
"CSN_UINT_ARRAY",
|
||||
"CSN_TYPE_ARRAY",
|
||||
"CSN_BITMAP",
|
||||
"CSN_VARIABLE_BITMAP",
|
||||
"CSN_VARIABLE_BITMAP_1",
|
||||
"CSN_LEFT_ALIGNED_VAR_BMP",
|
||||
"CSN_LEFT_ALIGNED_VAR_BMP_1",
|
||||
"CSN_VARIABLE_ARRAY",
|
||||
"CSN_VARIABLE_TARRAY",
|
||||
"CSN_VARIABLE_TARRAY_OFFSET",
|
||||
"CSN_RECURSIVE_ARRAY",
|
||||
"CSN_RECURSIVE_TARRAY",
|
||||
"CSN_RECURSIVE_TARRAY_1",
|
||||
"CSN_RECURSIVE_TARRAY_2",
|
||||
"CSN_EXIST",
|
||||
"CSN_EXIST_LH",
|
||||
"CSN_NEXT_EXIST",
|
||||
"CSN_NEXT_EXIST_LH",
|
||||
"CSN_NULL",
|
||||
"CSN_FIXED",
|
||||
"CSN_CALLBACK",
|
||||
"CSN_UINT_OFFSET",
|
||||
"CSN_UINT_LH",
|
||||
"CSN_SERIALIZE",
|
||||
"CSN_TRAP_ERROR"
|
||||
"CSN_???"
|
||||
};
|
||||
//#endif
|
||||
|
||||
/**
|
||||
* ================================================================================================
|
||||
@@ -579,11 +544,11 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
|
||||
guint8 length = bitvec_read_field(vector, readIndex, length_len);
|
||||
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s length = %d | ", pDescr->sz , (int)length);
|
||||
arT.direction = 1;
|
||||
bit_offset += length_len;
|
||||
remaining_bits_len -= length_len;
|
||||
|
||||
csnStreamInit(&arT, bit_offset, length);
|
||||
arT.direction = 1;
|
||||
Status = serialize(&arT, vector, readIndex, pvDATA(data, pDescr->offset));
|
||||
|
||||
if (Status >= 0)
|
||||
@@ -899,7 +864,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
|
||||
guint64 ui64 = bitvec_read_field(vector, readIndex, no_of_bits);
|
||||
pui64 = pui64DATA(data, pDescr->offset);
|
||||
*pui64 = ui64;
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui64);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1179,13 +1144,13 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
|
||||
guint8 bits_to_handle = remaining_bits_len%8;
|
||||
if (bits_to_handle > 0)
|
||||
{
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%u|", bitvec_read_field(vector, readIndex, bits_to_handle));
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, bits_to_handle));
|
||||
remaining_bits_len -= bits_to_handle;
|
||||
bit_offset += bits_to_handle;
|
||||
}
|
||||
else if (bits_to_handle == 0)
|
||||
{
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%u|", bitvec_read_field(vector, readIndex, 8));
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, 8));
|
||||
remaining_bits_len -= 8;
|
||||
bit_offset += 8;
|
||||
}
|
||||
@@ -2154,7 +2119,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
||||
{
|
||||
pui64 = pui64DATA(data, pDescr->offset);
|
||||
bitvec_write_field(vector, writeIndex, *pui64, no_of_bits);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui64);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
133
src/cxx_linuxlist.h
Normal file
133
src/cxx_linuxlist.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* cxx_linuxlist.h
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct LListHead {
|
||||
typedef T entry_t;
|
||||
|
||||
/* This must match the declaration of struct llist_head */
|
||||
LListHead<T> *next;
|
||||
LListHead<T> *prev;
|
||||
|
||||
LListHead() : m_back(0) { INIT_LLIST_HEAD(this); }
|
||||
LListHead(T* entry) : m_back(entry) {
|
||||
next = (LListHead<T> *)LLIST_POISON1;
|
||||
prev = (LListHead<T> *)LLIST_POISON2;
|
||||
}
|
||||
|
||||
T *entry() {return m_back;}
|
||||
const T *entry() const {return m_back;}
|
||||
|
||||
llist_head &llist() {
|
||||
return *static_cast<llist_head *>(static_cast<void *>(this));
|
||||
}
|
||||
const llist_head &llist() const {
|
||||
return *static_cast<const llist_head *>(static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
private:
|
||||
T *const m_back;
|
||||
};
|
||||
|
||||
/* Define a family of casting functions */
|
||||
template <typename T>
|
||||
llist_head &llist(LListHead<T> &l)
|
||||
{
|
||||
return l->llist();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const llist_head &llist(const LListHead<T> &l)
|
||||
{
|
||||
return l->llist();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
llist_head *llptr(LListHead<T> *l)
|
||||
{
|
||||
return &(l->llist());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const llist_head *llptr(const LListHead<T> *l)
|
||||
{
|
||||
return &(l->llist());
|
||||
}
|
||||
|
||||
/* Define type-safe wrapper for the existing linux_list.h functions */
|
||||
template <typename T>
|
||||
inline void llist_add(LListHead<T> *new_, LListHead<T> *head)
|
||||
{
|
||||
llist_add(llptr(new_), llptr(head));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void llist_add_tail(LListHead<T> *new_, LListHead<T> *head)
|
||||
{
|
||||
llist_add_tail(llptr(new_), llptr(head));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void llist_del(LListHead<T> *entry)
|
||||
{
|
||||
llist_del(llptr(entry));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void llist_del_init(LListHead<T> *entry)
|
||||
{
|
||||
llist_del_init(llptr(entry));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void llist_move(LListHead<T> *list, LListHead<T> *head)
|
||||
{
|
||||
llist_move(llptr(list), llptr(head));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void llist_move_tail(LListHead<T> *list, LListHead<T> *head)
|
||||
{
|
||||
llist_move_tail(llptr(list), llptr(head));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline int llist_empty(const LListHead<T> *head)
|
||||
{
|
||||
return llist_empty(llptr(head));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void llist_splice(LListHead<T> *list, LListHead<T> *head)
|
||||
{
|
||||
llist_splice(llptr(list), llptr(head));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void llist_splice_init(LListHead<T> *list, LListHead<T> *head)
|
||||
{
|
||||
llist_splice_init(llptr(list), llptr(head));
|
||||
}
|
||||
101
src/decoding.cpp
Normal file
101
src/decoding.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/* decoding
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <decoding.h>
|
||||
#include <rlc.h>
|
||||
#include <gprs_debug.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
int Decoding::tlli_from_ul_data(const uint8_t *data, uint8_t len,
|
||||
uint32_t *tlli)
|
||||
{
|
||||
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
|
||||
struct rlc_li_field *li;
|
||||
uint8_t e;
|
||||
uint32_t _tlli;
|
||||
|
||||
if (!rh->ti)
|
||||
return -EINVAL;
|
||||
|
||||
data += 3;
|
||||
len -= 3;
|
||||
e = rh->e;
|
||||
/* if E is not set (LI follows) */
|
||||
while (!e) {
|
||||
if (!len) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||
"but no more data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* get new E */
|
||||
li = (struct rlc_li_field *)data;
|
||||
if (li->e == 0) /* if LI==0, E is interpreted as '1' */
|
||||
e = 1;
|
||||
else
|
||||
e = li->e;
|
||||
data++;
|
||||
len--;
|
||||
}
|
||||
if (len < 4) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame "
|
||||
"border\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy(&_tlli, data, 4);
|
||||
*tlli = ntohl(_tlli);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
|
||||
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
|
||||
continue;
|
||||
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class)
|
||||
continue;
|
||||
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* show_rbb needs to be an array with 65 elements
|
||||
* The index of the array is the bit position in the rbb
|
||||
* (show_rbb[63] relates to BSN ssn-1)
|
||||
*/
|
||||
void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
|
||||
{
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint8_t bit;
|
||||
|
||||
bit = !!(rbb[i/8] & (1<<(7-i%8)));
|
||||
show_rbb[i] = bit ? 'R' : 'I';
|
||||
}
|
||||
|
||||
show_rbb[64] = '\0';
|
||||
}
|
||||
33
src/decoding.h
Normal file
33
src/decoding.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* decoding
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gsm_rlcmac.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class Decoding {
|
||||
public:
|
||||
static int tlli_from_ul_data(const uint8_t *data, uint8_t len,
|
||||
uint32_t *tlli);
|
||||
static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
|
||||
|
||||
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
|
||||
};
|
||||
463
src/encoding.cpp
Normal file
463
src/encoding.cpp
Normal file
@@ -0,0 +1,463 @@
|
||||
/* encoding.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <encoding.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
#include <gprs_debug.h>
|
||||
|
||||
// GSM 04.08 9.1.18 Immediate assignment
|
||||
int Encoding::write_immediate_assignment(
|
||||
struct gprs_rlcmac_bts *bts,
|
||||
bitvec * dest, uint8_t downlink, uint8_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
|
||||
uint8_t tfi, uint8_t usf, uint32_t tlli,
|
||||
uint8_t polling, uint32_t fn, uint8_t single_block, uint8_t alpha,
|
||||
uint8_t gamma, int8_t ta_idx)
|
||||
{
|
||||
unsigned wp = 0;
|
||||
uint8_t plen;
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,4); // Skip Indicator
|
||||
bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator
|
||||
bitvec_write_field(dest, wp,0x3F,8); // Immediate Assignment Message Type
|
||||
|
||||
// 10.5.2.25b Dedicated mode or TBF
|
||||
bitvec_write_field(dest, wp,0x0,1); // spare
|
||||
bitvec_write_field(dest, wp,0x0,1); // TMA : Two-message assignment: No meaning
|
||||
bitvec_write_field(dest, wp,downlink,1); // Downlink : Downlink assignment to mobile in packet idle mode
|
||||
bitvec_write_field(dest, wp,0x1,1); // T/D : TBF or dedicated mode: this message assigns a Temporary Block Flow (TBF).
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,4); // Page Mode
|
||||
|
||||
// GSM 04.08 10.5.2.25a Packet Channel Description
|
||||
bitvec_write_field(dest, wp,0x1,5); // Channel type
|
||||
bitvec_write_field(dest, wp,ts,3); // TN
|
||||
bitvec_write_field(dest, wp,tsc,3); // TSC
|
||||
bitvec_write_field(dest, wp,0x0,3); // non-hopping RF channel configuraion
|
||||
bitvec_write_field(dest, wp,arfcn,10); // ARFCN
|
||||
|
||||
//10.5.2.30 Request Reference
|
||||
bitvec_write_field(dest, wp,ra,8); // RA
|
||||
bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1'
|
||||
bitvec_write_field(dest, wp,ref_fn % 51,6); // T3
|
||||
bitvec_write_field(dest, wp,ref_fn % 26,5); // T2
|
||||
|
||||
// 10.5.2.40 Timing Advance
|
||||
bitvec_write_field(dest, wp,0x0,2); // spare
|
||||
bitvec_write_field(dest, wp,ta,6); // Timing Advance value
|
||||
|
||||
// No mobile allocation in non-hopping systems.
|
||||
// A zero-length LV. Just write L=0.
|
||||
bitvec_write_field(dest, wp,0,8);
|
||||
|
||||
if ((wp % 8)) {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS without rest "
|
||||
"octets is not multiple of 8 bits, PLEASE FIX!\n");
|
||||
exit (0);
|
||||
}
|
||||
plen = wp / 8;
|
||||
|
||||
if (downlink)
|
||||
{
|
||||
// GSM 04.08 10.5.2.16 IA Rest Octets
|
||||
bitvec_write_field(dest, wp, 3, 2); // "HH"
|
||||
bitvec_write_field(dest, wp, 1, 2); // "01" Packet Downlink Assignment
|
||||
bitvec_write_field(dest, wp,tlli,32); // TLLI
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TFI : on
|
||||
bitvec_write_field(dest, wp,tfi,5); // TFI
|
||||
bitvec_write_field(dest, wp,0x0,1); // RLC acknowledged mode
|
||||
if (alpha) {
|
||||
bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
|
||||
bitvec_write_field(dest, wp,alpha,4); // ALPHA
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
|
||||
}
|
||||
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
|
||||
bitvec_write_field(dest, wp,polling,1); // Polling Bit
|
||||
bitvec_write_field(dest, wp,!polling,1); // TA_VALID ???
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
|
||||
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
|
||||
}
|
||||
if (polling) {
|
||||
bitvec_write_field(dest, wp,0x1,1); // TBF Starting TIME present
|
||||
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
|
||||
bitvec_write_field(dest, wp,fn % 51,6); // T3
|
||||
bitvec_write_field(dest, wp,fn % 26,5); // T2
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x0,1); // TBF Starting TIME present
|
||||
}
|
||||
bitvec_write_field(dest, wp,0x0,1); // P0 not present
|
||||
// bitvec_write_field(dest, wp,0x1,1); // P0 not present
|
||||
// bitvec_write_field(dest, wp,0xb,4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// GMS 04.08 10.5.2.37b 10.5.2.16
|
||||
bitvec_write_field(dest, wp, 3, 2); // "HH"
|
||||
bitvec_write_field(dest, wp, 0, 2); // "0" Packet Uplink Assignment
|
||||
if (single_block) {
|
||||
bitvec_write_field(dest, wp, 0, 1); // Block Allocation : Single Block Allocation
|
||||
if (alpha) {
|
||||
bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
|
||||
bitvec_write_field(dest, wp,alpha,4); // ALPHA = present
|
||||
} else
|
||||
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
|
||||
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
|
||||
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
|
||||
}
|
||||
bitvec_write_field(dest, wp, 1, 1); // TBF_STARTING_TIME_FLAG
|
||||
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
|
||||
bitvec_write_field(dest, wp,fn % 51,6); // T3
|
||||
bitvec_write_field(dest, wp,fn % 26,5); // T2
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 1, 1); // Block Allocation : Not Single Block Allocation
|
||||
bitvec_write_field(dest, wp, tfi, 5); // TFI_ASSIGNMENT Temporary Flow Identity
|
||||
bitvec_write_field(dest, wp, 0, 1); // POLLING
|
||||
bitvec_write_field(dest, wp, 0, 1); // ALLOCATION_TYPE: dynamic
|
||||
bitvec_write_field(dest, wp, usf, 3); // USF
|
||||
bitvec_write_field(dest, wp, 0, 1); // USF_GRANULARITY
|
||||
bitvec_write_field(dest, wp, 0, 1); // "0" power control: Not Present
|
||||
bitvec_write_field(dest, wp, bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND
|
||||
bitvec_write_field(dest, wp, 1, 1); // TLLI_BLOCK_CHANNEL_CODING
|
||||
if (alpha) {
|
||||
bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
|
||||
bitvec_write_field(dest, wp,alpha,4); // ALPHA
|
||||
} else
|
||||
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
|
||||
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
|
||||
/* note: there is no choise for TAI and no starting time */
|
||||
bitvec_write_field(dest, wp, 0, 1); // switch TIMING_ADVANCE_INDEX = off
|
||||
bitvec_write_field(dest, wp, 0, 1); // TBF_STARTING_TIME_FLAG
|
||||
}
|
||||
}
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
/* generate uplink assignment */
|
||||
void Encoding::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 alpha,
|
||||
uint8_t gamma, int8_t ta_idx)
|
||||
{
|
||||
// TODO We should use our implementation of encode RLC/MAC Control messages.
|
||||
unsigned wp = 0;
|
||||
uint8_t ts;
|
||||
|
||||
bitvec_write_field(dest, wp,0x1,2); // Payload Type
|
||||
bitvec_write_field(dest, wp,0x0,2); // Uplink block with TDMA framenumber (N+13)
|
||||
bitvec_write_field(dest, wp,poll,1); // Suppl/Polling Bit
|
||||
bitvec_write_field(dest, wp,0x0,3); // Uplink state flag
|
||||
bitvec_write_field(dest, wp,0xa,6); // MESSAGE TYPE
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,2); // Page Mode
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch PERSIST_LEVEL: off
|
||||
if (use_tlli) {
|
||||
bitvec_write_field(dest, wp,0x2,2); // switch TLLI : on
|
||||
bitvec_write_field(dest, wp,tlli,32); // TLLI
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TFI : on
|
||||
bitvec_write_field(dest, wp,old_downlink,1); // 0=UPLINK TFI, 1=DL TFI
|
||||
bitvec_write_field(dest, wp,old_tfi,5); // TFI
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,1); // Message escape
|
||||
bitvec_write_field(dest, wp,tbf->current_cs()-1, 2); // CHANNEL_CODING_COMMAND
|
||||
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
|
||||
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
|
||||
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
|
||||
}
|
||||
|
||||
#if 1
|
||||
bitvec_write_field(dest, wp,0x1,1); // Frequency Parameters information elements = present
|
||||
bitvec_write_field(dest, wp,tbf->tsc(),3); // Training Sequence Code (TSC)
|
||||
bitvec_write_field(dest, wp,0x0,2); // ARFCN = present
|
||||
bitvec_write_field(dest, wp,tbf->trx->arfcn,10); // ARFCN
|
||||
#else
|
||||
bitvec_write_field(dest, wp,0x0,1); // Frequency Parameters = off
|
||||
#endif
|
||||
|
||||
bitvec_write_field(dest, wp,0x1,2); // Dynamic Allocation
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,1); // Extended Dynamic Allocation = off
|
||||
bitvec_write_field(dest, wp,0x0,1); // P0 = off
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,1); // USF_GRANULARITY
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TFI : on
|
||||
bitvec_write_field(dest, wp,tbf->tfi(),5);// TFI
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,1); //
|
||||
bitvec_write_field(dest, wp,0x0,1); // TBF Starting Time = off
|
||||
if (alpha || gamma) {
|
||||
bitvec_write_field(dest, wp,0x1,1); // Timeslot Allocation with Power Control
|
||||
bitvec_write_field(dest, wp,alpha,4); // ALPHA
|
||||
} else
|
||||
bitvec_write_field(dest, wp,0x0,1); // Timeslot Allocation
|
||||
|
||||
for (ts = 0; ts < 8; ts++) {
|
||||
if (tbf->pdch[ts]) {
|
||||
bitvec_write_field(dest, wp,0x1,1); // USF_TN(i): on
|
||||
bitvec_write_field(dest, wp,tbf->m_usf[ts],3); // USF_TN(i)
|
||||
if (alpha || gamma)
|
||||
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
|
||||
} else
|
||||
bitvec_write_field(dest, wp,0x0,1); // USF_TN(i): off
|
||||
}
|
||||
// bitvec_write_field(dest, wp,0x0,1); // Measurement Mapping struct not present
|
||||
}
|
||||
|
||||
|
||||
/* generate downlink assignment */
|
||||
void Encoding::write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
|
||||
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts)
|
||||
{
|
||||
// Packet downlink assignment TS 44.060 11.2.7
|
||||
|
||||
uint8_t tn;
|
||||
|
||||
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
|
||||
block->RRBP = 0x0; // N+13
|
||||
block->SP = poll; // RRBP field is valid
|
||||
block->USF = 0x0; // Uplink state flag
|
||||
|
||||
block->u.Packet_Downlink_Assignment.MESSAGE_TYPE = 0x2; // Packet Downlink Assignment
|
||||
block->u.Packet_Downlink_Assignment.PAGE_MODE = 0x0; // Normal Paging
|
||||
|
||||
block->u.Packet_Downlink_Assignment.Exist_PERSISTENCE_LEVEL = 0x0; // PERSISTENCE_LEVEL: off
|
||||
|
||||
block->u.Packet_Downlink_Assignment.ID.UnionType = 0x0; // TFI = on
|
||||
block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.UnionType = old_downlink; // 0=UPLINK TFI, 1=DL TFI
|
||||
block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.u.UPLINK_TFI = old_tfi; // TFI
|
||||
|
||||
block->u.Packet_Downlink_Assignment.MAC_MODE = 0x0; // Dynamic Allocation
|
||||
block->u.Packet_Downlink_Assignment.RLC_MODE = 0x0; // RLC acknowledged mode
|
||||
block->u.Packet_Downlink_Assignment.CONTROL_ACK = tbf->was_releasing; // NW establishes no new DL TBF for the MS with running timer T3192
|
||||
block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0; // timeslot(s)
|
||||
for (tn = 0; tn < 8; tn++) {
|
||||
if (tbf->pdch[tn])
|
||||
block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION |= 0x80 >> tn; // timeslot(s)
|
||||
}
|
||||
|
||||
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x1; // TIMING_ADVANCE_VALUE = on
|
||||
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE = tbf->ta(); // TIMING_ADVANCE_VALUE
|
||||
if (ta_idx < 0) {
|
||||
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x0; // TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x1; // TIMING_ADVANCE_INDEX = on
|
||||
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_INDEX = ta_idx; // TIMING_ADVANCE_INDEX
|
||||
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_TIMESLOT_NUMBER = ta_ts; // TIMING_ADVANCE_TS
|
||||
}
|
||||
|
||||
block->u.Packet_Downlink_Assignment.Exist_P0_and_BTS_PWR_CTRL_MODE = 0x0; // POWER CONTROL = off
|
||||
|
||||
block->u.Packet_Downlink_Assignment.Exist_Frequency_Parameters = 0x1; // Frequency Parameters = on
|
||||
block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC = tbf->tsc(); // Training Sequence Code (TSC)
|
||||
block->u.Packet_Downlink_Assignment.Frequency_Parameters.UnionType = 0x0; // ARFCN = on
|
||||
block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN = tbf->trx->arfcn; // ARFCN
|
||||
|
||||
block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT = 0x1; // DOWNLINK TFI ASSIGNMENT = on
|
||||
block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT = tbf->tfi(); // TFI
|
||||
|
||||
block->u.Packet_Downlink_Assignment.Exist_Power_Control_Parameters = 0x1; // Power Control Parameters = on
|
||||
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = alpha; // ALPHA
|
||||
|
||||
for (tn = 0; tn < 8; tn++)
|
||||
{
|
||||
if (tbf->pdch[tn])
|
||||
{
|
||||
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x1; // Slot[i] = on
|
||||
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].GAMMA_TN = gamma; // GAMMA_TN
|
||||
}
|
||||
else
|
||||
{
|
||||
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x0; // Slot[i] = off
|
||||
}
|
||||
}
|
||||
|
||||
block->u.Packet_Downlink_Assignment.Exist_TBF_Starting_Time = 0x0; // TBF Starting TIME = off
|
||||
block->u.Packet_Downlink_Assignment.Exist_Measurement_Mapping = 0x0; // Measurement_Mapping = off
|
||||
block->u.Packet_Downlink_Assignment.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off
|
||||
}
|
||||
|
||||
/* generate paging request */
|
||||
int Encoding::write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len)
|
||||
{
|
||||
unsigned wp = 0;
|
||||
int plen;
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,4); // Skip Indicator
|
||||
bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator
|
||||
bitvec_write_field(dest, wp,0x21,8); // Paging Request Message Type
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,4); // Page Mode
|
||||
bitvec_write_field(dest, wp,0x0,4); // Channel Needed
|
||||
|
||||
// Mobile Identity
|
||||
bitvec_write_field(dest, wp,ptmsi_len+1,8); // Mobile Identity length
|
||||
bitvec_write_field(dest, wp,0xf,4); // unused
|
||||
bitvec_write_field(dest, wp,0x4,4); // PTMSI type
|
||||
for (int i = 0; i < ptmsi_len; i++)
|
||||
{
|
||||
bitvec_write_field(dest, wp,ptmsi[i],8); // PTMSI
|
||||
}
|
||||
if ((wp % 8)) {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR, "Length of PAG.REQ without rest "
|
||||
"octets is not multiple of 8 bits, PLEASE FIX!\n");
|
||||
exit (0);
|
||||
}
|
||||
plen = wp / 8;
|
||||
bitvec_write_field(dest, wp,0x0,1); // "L" NLN(PCH) = off
|
||||
bitvec_write_field(dest, wp,0x0,1); // "L" Priority1 = off
|
||||
bitvec_write_field(dest, wp,0x1,1); // "L" Priority2 = off
|
||||
bitvec_write_field(dest, wp,0x0,1); // "L" Group Call information = off
|
||||
bitvec_write_field(dest, wp,0x0,1); // "H" Packet Page Indication 1 = packet paging procedure
|
||||
bitvec_write_field(dest, wp,0x1,1); // "H" Packet Page Indication 2 = packet paging procedure
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index of the array show_rbb is the bit position inside the rbb
|
||||
* (show_rbb[63] relates to BSN ssn-1)
|
||||
*/
|
||||
void Encoding::encode_rbb(const char *show_rbb, uint8_t *rbb)
|
||||
{
|
||||
uint8_t rbb_byte = 0;
|
||||
|
||||
// RECEIVE_BLOCK_BITMAP
|
||||
for (int i = 0; i < 64; i++) {
|
||||
/* Set bit at the appropriate position (see 3GPP TS 04.60 9.1.8.1) */
|
||||
if (show_rbb[i] == 'R')
|
||||
rbb_byte |= 1<< (7-(i%8));
|
||||
|
||||
if((i%8) == 7) {
|
||||
rbb[i/8] = rbb_byte;
|
||||
rbb_byte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* generate uplink ack */
|
||||
void Encoding::write_packet_uplink_ack(struct gprs_rlcmac_bts *bts,
|
||||
RlcMacDownlink_t * block, struct gprs_rlcmac_ul_tbf *tbf,
|
||||
uint8_t final)
|
||||
{
|
||||
// Packet Uplink Ack/Nack TS 44.060 11.2.28
|
||||
|
||||
char rbb[65];
|
||||
|
||||
tbf->m_window.update_rbb(rbb);
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Encoding Ack/Nack for %s "
|
||||
"(final=%d)\n", tbf_name(tbf), final);
|
||||
|
||||
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
|
||||
block->RRBP = 0x0; // N+13
|
||||
block->SP = final; // RRBP field is valid, if it is final ack
|
||||
block->USF = 0x0; // Uplink state flag
|
||||
|
||||
block->u.Packet_Uplink_Ack_Nack.MESSAGE_TYPE = 0x9; // Packet Downlink Assignment
|
||||
block->u.Packet_Uplink_Ack_Nack.PAGE_MODE = 0x0; // Normal Paging
|
||||
block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI = tbf->tfi(); // Uplink TFI
|
||||
|
||||
block->u.Packet_Uplink_Ack_Nack.UnionType = 0x0; // PU_AckNack_GPRS = on
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = tbf->current_cs() - 1; // CS1
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION = final; // FINAL ACK INDICATION
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->m_window.ssn(); // STARTING_SEQUENCE_NUMBER
|
||||
|
||||
encode_rbb(rbb, block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP);
|
||||
|
||||
/* rbb is not NULL terminated */
|
||||
rbb[64] = 0;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- V(N): \"%s\" R=Received "
|
||||
"I=Invalid\n", rbb);
|
||||
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.UnionType = 0x0; // Fixed Allocation Dummy = on
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.u.FixedAllocationDummy = 0x0; // Fixed Allocation Dummy
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off
|
||||
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_CONTENTION_RESOLUTION_TLLI = 0x1;
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.CONTENTION_RESOLUTION_TLLI = tbf->tlli();
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Packet_Timing_Advance = 0x0;
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Extension_Bits = 0x0;
|
||||
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Power_Control_Parameters = 0x0;
|
||||
}
|
||||
|
||||
unsigned Encoding::write_packet_paging_request(bitvec * dest)
|
||||
{
|
||||
unsigned wp = 0;
|
||||
|
||||
bitvec_write_field(dest, wp,0x1,2); // Payload Type
|
||||
bitvec_write_field(dest, wp,0x0,3); // No polling
|
||||
bitvec_write_field(dest, wp,0x0,3); // Uplink state flag
|
||||
bitvec_write_field(dest, wp,0x22,6); // MESSAGE TYPE
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,2); // Page Mode
|
||||
|
||||
bitvec_write_field(dest, wp,0x0,1); // No PERSISTENCE_LEVEL
|
||||
bitvec_write_field(dest, wp,0x0,1); // No NLN
|
||||
|
||||
return wp;
|
||||
}
|
||||
|
||||
unsigned Encoding::write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
|
||||
uint8_t *identity, uint8_t chan_needed)
|
||||
{
|
||||
bitvec_write_field(dest, wp,0x1,1); // Repeated Page info exists
|
||||
|
||||
bitvec_write_field(dest, wp,0x1,1); // RR connection paging
|
||||
|
||||
if ((identity[0] & 0x07) == 4) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // TMSI
|
||||
identity++;
|
||||
len--;
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x0,1); // MI
|
||||
bitvec_write_field(dest, wp,len,4); // MI len
|
||||
}
|
||||
while (len) {
|
||||
bitvec_write_field(dest, wp,*identity++,8); // MI data
|
||||
len--;
|
||||
}
|
||||
bitvec_write_field(dest, wp,chan_needed,2); // CHANNEL_NEEDED
|
||||
bitvec_write_field(dest, wp,0x0,1); // No eMLPP_PRIORITY
|
||||
|
||||
return wp;
|
||||
}
|
||||
|
||||
68
src/encoding.h
Normal file
68
src/encoding.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* encoding.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <gsm_rlcmac.h>
|
||||
|
||||
struct gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_tbf;
|
||||
struct bitvec;
|
||||
|
||||
/**
|
||||
* I help with encoding data into CSN1 messages.
|
||||
* TODO: Nobody can remember a function signature like this. One should
|
||||
* fill out a struct with the request parameters and then hand the struct
|
||||
* to the code.
|
||||
*/
|
||||
class Encoding {
|
||||
public:
|
||||
static int write_immediate_assignment(
|
||||
struct gprs_rlcmac_bts *bts,
|
||||
bitvec * dest, uint8_t downlink, uint8_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
|
||||
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
|
||||
uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
|
||||
int8_t ta_idx);
|
||||
|
||||
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 alpha,
|
||||
uint8_t gamma, int8_t ta_idx);
|
||||
|
||||
static void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
|
||||
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts);
|
||||
|
||||
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
|
||||
|
||||
static void write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, RlcMacDownlink_t * block, struct gprs_rlcmac_ul_tbf *tbf,
|
||||
uint8_t final);
|
||||
|
||||
static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
|
||||
|
||||
static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
|
||||
uint8_t *identity, uint8_t chan_needed);
|
||||
|
||||
static unsigned write_packet_paging_request(bitvec * dest);
|
||||
};
|
||||
@@ -238,19 +238,6 @@ const struct value_string femtobts_tch_pl_names[] = {
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string femtobts_clksrc_names[] = {
|
||||
{ SuperFemto_ClkSrcId_None, "None" },
|
||||
{ SuperFemto_ClkSrcId_Ocxo, "ocxo" },
|
||||
{ SuperFemto_ClkSrcId_Tcxo, "tcxo" },
|
||||
{ SuperFemto_ClkSrcId_External, "ext" },
|
||||
{ SuperFemto_ClkSrcId_GpsPps, "gps" },
|
||||
{ SuperFemto_ClkSrcId_Trx, "trx" },
|
||||
{ SuperFemto_ClkSrcId_Rx, "rx" },
|
||||
{ SuperFemto_ClkSrcId_Edge, "edge" },
|
||||
{ SuperFemto_ClkSrcId_NetList, "nwl" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string femtobts_dir_names[] = {
|
||||
{ GsmL1_Dir_TxDownlink, "TxDL" },
|
||||
{ GsmL1_Dir_TxUplink, "TxUL" },
|
||||
|
||||
@@ -33,8 +33,6 @@ const struct value_string femtobts_tracef_names[29];
|
||||
|
||||
const struct value_string femtobts_tch_pl_names[15];
|
||||
|
||||
const struct value_string femtobts_clksrc_names[8];
|
||||
|
||||
const struct value_string femtobts_dir_names[6];
|
||||
|
||||
enum pdch_cs {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* gprs_bssgp_pcu.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -20,16 +21,27 @@
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
|
||||
#define BSSGP_TIMER_T1 30 /* Guards the (un)blocking procedures */
|
||||
#define BSSGP_TIMER_T2 30 /* Guards the reset procedure */
|
||||
|
||||
/* Tuning parameters for BSSGP flow control */
|
||||
#define FC_DEFAULT_LIFE_TIME_SECS 10 /* experimental value, 10s */
|
||||
#define FC_MS_BUCKET_SIZE_BY_BMAX(bmax) ((bmax) / 2 + 500) /* experimental */
|
||||
#define FC_FALLBACK_BVC_BUCKET_SIZE 2000 /* e.g. on R = 0, value taken from PCAP files */
|
||||
#define FC_MS_MAX_RX_SLOTS 4 /* limit MS default R to 4 TS per MS */
|
||||
|
||||
/* Constants for BSSGP flow control */
|
||||
#define FC_MAX_BUCKET_LEAK_RATE (6553500 / 8) /* Byte/s */
|
||||
#define FC_MAX_BUCKET_SIZE 6553500 /* Octets */
|
||||
|
||||
static struct gprs_bssgp_pcu the_pcu = { 0, };
|
||||
|
||||
struct sgsn_instance *sgsn;
|
||||
extern void *tall_pcu_ctx;
|
||||
struct bssgp_bvc_ctx *bctx = NULL;
|
||||
struct gprs_nsvc *nsvc = NULL;
|
||||
static int bvc_sig_reset = 0, bvc_reset = 0, bvc_unblocked = 0;
|
||||
extern uint16_t spoof_mcc, spoof_mnc;
|
||||
|
||||
struct osmo_timer_list bvc_timer;
|
||||
|
||||
static void bvc_timeout(void *_priv);
|
||||
|
||||
static int parse_imsi(struct tlv_parsed *tp, char *imsi)
|
||||
@@ -61,16 +73,54 @@ static int parse_imsi(struct tlv_parsed *tp, char *imsi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
|
||||
static int parse_ra_cap_ms_class(struct tlv_parsed *tp)
|
||||
{
|
||||
bitvec *block;
|
||||
unsigned rp = 0;
|
||||
uint8_t ms_class = 0;
|
||||
uint8_t cap_len;
|
||||
uint8_t *cap;
|
||||
|
||||
if (!TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
|
||||
return ms_class;
|
||||
|
||||
cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
|
||||
cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
|
||||
|
||||
block = bitvec_alloc(cap_len);
|
||||
bitvec_unpack(block, cap);
|
||||
bitvec_read_field(block, rp, 4); // Access Technology Type
|
||||
bitvec_read_field(block, rp, 7); // Length of Access Capabilities
|
||||
bitvec_read_field(block, rp, 3); // RF Power Capability
|
||||
if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
|
||||
bitvec_read_field(block, rp, 7); // A5 Bits
|
||||
bitvec_read_field(block, rp, 1); // ES IND
|
||||
bitvec_read_field(block, rp, 1); // PS
|
||||
bitvec_read_field(block, rp, 1); // VGCS
|
||||
bitvec_read_field(block, rp, 1); // VBS
|
||||
if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
|
||||
if (bitvec_read_field(block, rp, 1)) // HSCSD Present
|
||||
bitvec_read_field(block, rp, 5); // Class
|
||||
if (bitvec_read_field(block, rp, 1)) { // GPRS Present
|
||||
ms_class = bitvec_read_field(block, rp, 5); // Class
|
||||
bitvec_read_field(block, rp, 1); // Ext.
|
||||
}
|
||||
if (bitvec_read_field(block, rp, 1)) // SMS Present
|
||||
bitvec_read_field(block, rp, 4); // SMS Value
|
||||
}
|
||||
|
||||
bitvec_free(block);
|
||||
return ms_class;
|
||||
}
|
||||
|
||||
static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
|
||||
{
|
||||
struct bssgp_ud_hdr *budh;
|
||||
|
||||
int8_t tfi; /* must be signed */
|
||||
|
||||
uint32_t tlli;
|
||||
uint32_t tlli_old = 0;
|
||||
uint8_t *data;
|
||||
uint16_t len;
|
||||
struct gprs_rlcmac_tbf *tbf;
|
||||
char imsi[16] = "000";
|
||||
|
||||
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
|
||||
@@ -85,7 +135,7 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
|
||||
|
||||
data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
|
||||
len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
|
||||
if (len > sizeof(tbf->llc_frame))
|
||||
if (len > sizeof(gprs_llc::frame))
|
||||
{
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli);
|
||||
return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
|
||||
@@ -97,38 +147,8 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
|
||||
parse_imsi(tp, imsi);
|
||||
|
||||
/* parse ms radio access capability */
|
||||
uint8_t ms_class = 0;
|
||||
if (TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
|
||||
{
|
||||
bitvec *block;
|
||||
uint8_t cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
|
||||
uint8_t *cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
|
||||
unsigned rp = 0;
|
||||
uint8_t ms_class = parse_ra_cap_ms_class(tp);
|
||||
|
||||
block = bitvec_alloc(cap_len);
|
||||
bitvec_unpack(block, cap);
|
||||
bitvec_read_field(block, rp, 4); // Access Technology Type
|
||||
bitvec_read_field(block, rp, 7); // Length of Access Capabilities
|
||||
bitvec_read_field(block, rp, 3); // RF Power Capability
|
||||
if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
|
||||
bitvec_read_field(block, rp, 7); // A5 Bits
|
||||
bitvec_read_field(block, rp, 1); // ES IND
|
||||
bitvec_read_field(block, rp, 1); // PS
|
||||
bitvec_read_field(block, rp, 1); // VGCS
|
||||
bitvec_read_field(block, rp, 1); // VBS
|
||||
if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
|
||||
if (bitvec_read_field(block, rp, 1)) // HSCSD Present
|
||||
bitvec_read_field(block, rp, 5); // Class
|
||||
if (bitvec_read_field(block, rp, 1)) { // GPRS Present
|
||||
ms_class = bitvec_read_field(block, rp, 5); // Class
|
||||
bitvec_read_field(block, rp, 1); // Ext.
|
||||
}
|
||||
if (bitvec_read_field(block, rp, 1)) // SMS Present
|
||||
bitvec_read_field(block, rp, 4); // SMS Value
|
||||
bitvec_read_field(block, rp, 4); // SMS Value
|
||||
}
|
||||
bitvec_free(block);
|
||||
}
|
||||
/* get lifetime */
|
||||
uint16_t delay_csec = 0xffff;
|
||||
if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME))
|
||||
@@ -144,123 +164,22 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP missing mandatory "
|
||||
"PDU_LIFETIME IE\n");
|
||||
|
||||
LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
|
||||
|
||||
/* check for existing TBF */
|
||||
if ((tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
|
||||
LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli);
|
||||
if (tbf->state == GPRS_RLCMAC_WAIT_RELEASE) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state "
|
||||
"(T3193), so reuse TBF\n");
|
||||
memcpy(tbf->llc_frame, data, len);
|
||||
tbf->llc_length = len;
|
||||
memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset
|
||||
rlc states */
|
||||
tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep
|
||||
to flags */
|
||||
tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
|
||||
if (!tbf->ms_class && ms_class)
|
||||
tbf->ms_class = ms_class;
|
||||
tbf_update(tbf);
|
||||
gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL);
|
||||
} else {
|
||||
/* the TBF exists, so we must write it in the queue
|
||||
* we prepend lifetime in front of PDU */
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct timeval *tv;
|
||||
struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv),
|
||||
"llc_pdu_queue");
|
||||
if (!llc_msg)
|
||||
return -ENOMEM;
|
||||
tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
|
||||
if (bts->force_llc_lifetime)
|
||||
delay_csec = bts->force_llc_lifetime;
|
||||
/* keep timestap at 0 for infinite delay */
|
||||
if (delay_csec != 0xffff) {
|
||||
/* calculate timestamp of timeout */
|
||||
gettimeofday(tv, NULL);
|
||||
tv->tv_usec += (delay_csec % 100) * 10000;
|
||||
tv->tv_sec += delay_csec / 100;
|
||||
if (tv->tv_usec > 999999) {
|
||||
tv->tv_usec -= 1000000;
|
||||
tv->tv_sec++;
|
||||
}
|
||||
}
|
||||
memcpy(msgb_put(llc_msg, len), data, len);
|
||||
msgb_enqueue(&tbf->llc_queue, llc_msg);
|
||||
/* set ms class for updating TBF */
|
||||
if (!tbf->ms_class && ms_class)
|
||||
tbf->ms_class = ms_class;
|
||||
}
|
||||
} else {
|
||||
uint8_t trx, ta, ss;
|
||||
int8_t use_trx;
|
||||
struct gprs_rlcmac_tbf *old_tbf;
|
||||
int rc;
|
||||
|
||||
/* check for uplink data, so we copy our informations */
|
||||
tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
|
||||
if (tbf && tbf->dir.ul.contention_resolution_done
|
||||
&& !tbf->dir.ul.final_ack_sent) {
|
||||
use_trx = tbf->trx;
|
||||
ta = tbf->ta;
|
||||
ss = 0;
|
||||
old_tbf = tbf;
|
||||
} else {
|
||||
use_trx = -1;
|
||||
/* we already have an uplink TBF, so we use that TA */
|
||||
if (tbf)
|
||||
ta = tbf->ta;
|
||||
else {
|
||||
/* recall TA */
|
||||
rc = recall_timing_advance(tlli);
|
||||
if (rc < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown"
|
||||
", assuming 0\n");
|
||||
ta = 0;
|
||||
} else
|
||||
ta = rc;
|
||||
}
|
||||
ss = 1; /* PCH assignment only allows one timeslot */
|
||||
old_tbf = NULL;
|
||||
}
|
||||
|
||||
// Create new TBF (any TRX)
|
||||
tfi = tfi_alloc(GPRS_RLCMAC_DL_TBF, &trx, use_trx);
|
||||
if (tfi < 0) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
|
||||
/* FIXME: send reject */
|
||||
return -EBUSY;
|
||||
}
|
||||
/* set number of downlink slots according to multislot class */
|
||||
tbf = tbf_alloc(tbf, GPRS_RLCMAC_DL_TBF, tfi, trx, ms_class,
|
||||
ss);
|
||||
if (!tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
|
||||
/* FIXME: send reject */
|
||||
return -EBUSY;
|
||||
}
|
||||
tbf->tlli = tlli;
|
||||
tbf->tlli_valid = 1;
|
||||
tbf->ta = ta;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
|
||||
|
||||
/* new TBF, so put first frame */
|
||||
memcpy(tbf->llc_frame, data, len);
|
||||
tbf->llc_length = len;
|
||||
|
||||
/* trigger downlink assignment and set state to ASSIGN.
|
||||
* we don't use old_downlink, so the possible uplink is used
|
||||
* to trigger downlink assignment. if there is no uplink,
|
||||
* AGCH is used. */
|
||||
gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi);
|
||||
/* get optional TLLI old */
|
||||
if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
|
||||
{
|
||||
uint8_t tlli_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
|
||||
uint16_t *e_tlli_old = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_TLLI);
|
||||
if (tlli_len == 2)
|
||||
tlli_old = ntohs(*e_tlli_old);
|
||||
else
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
|
||||
"TLLI (old) IE\n");
|
||||
}
|
||||
|
||||
/* store IMSI for debugging purpose */
|
||||
strncpy(tbf->meas.imsi, imsi, sizeof(tbf->meas.imsi) - 1);
|
||||
LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
|
||||
|
||||
return 0;
|
||||
return gprs_rlcmac_dl_tbf::handle(the_pcu.bts, tlli, tlli_old, imsi,
|
||||
ms_class, delay_csec, data, len);
|
||||
}
|
||||
|
||||
int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
|
||||
@@ -286,7 +205,7 @@ int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
|
||||
}
|
||||
|
||||
/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
|
||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
|
||||
static int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
|
||||
{
|
||||
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
||||
uint8_t pdu_type = bgph->pdu_type;
|
||||
@@ -308,6 +227,8 @@ int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_
|
||||
switch (pdu_type) {
|
||||
case BSSGP_PDUT_DL_UNITDATA:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
|
||||
if (the_pcu.on_dl_unit_data)
|
||||
the_pcu.on_dl_unit_data(&the_pcu, msg, tp);
|
||||
gprs_bssgp_pcu_rx_dl_ud(msg, tp);
|
||||
break;
|
||||
case BSSGP_PDUT_PAGING_PS:
|
||||
@@ -334,66 +255,70 @@ int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_
|
||||
}
|
||||
|
||||
/* Receive a BSSGP PDU from a SGSN on a SIGNALLING BVCI */
|
||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
|
||||
static int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
|
||||
{
|
||||
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
||||
int rc = 0;
|
||||
int bvci = bctx ? bctx->bvci : -1;
|
||||
switch (bgph->pdu_type) {
|
||||
case BSSGP_PDUT_STATUS:
|
||||
/* Some exception has occurred */
|
||||
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
|
||||
DEBUGP(DBSSGP, "BSSGP BVCI=%d Rx BVC STATUS\n", bvci);
|
||||
/* FIXME: send NM_STATUS.ind to NM */
|
||||
break;
|
||||
case BSSGP_PDUT_SUSPEND_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_SUSPEND_NACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_BVC_RESET_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
|
||||
if (!bvc_sig_reset)
|
||||
bvc_sig_reset = 1;
|
||||
else
|
||||
bvc_reset = 1;
|
||||
bvc_timeout(NULL);
|
||||
break;
|
||||
case BSSGP_PDUT_PAGING_PS:
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
|
||||
gprs_bssgp_pcu_rx_paging_ps(msg, tp);
|
||||
break;
|
||||
case BSSGP_PDUT_PAGING_CS:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
|
||||
break;
|
||||
case BSSGP_PDUT_RESUME_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_RESUME_NACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_FLUSH_LL:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
|
||||
break;
|
||||
case BSSGP_PDUT_BVC_BLOCK_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
|
||||
bvc_unblocked = 1;
|
||||
bvc_timeout(NULL);
|
||||
break;
|
||||
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
|
||||
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
||||
break;
|
||||
case BSSGP_PDUT_SUSPEND_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_SUSPEND_NACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_BVC_RESET_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
|
||||
if (!the_pcu.bvc_sig_reset)
|
||||
the_pcu.bvc_sig_reset = 1;
|
||||
else
|
||||
the_pcu.bvc_reset = 1;
|
||||
bvc_timeout(NULL);
|
||||
break;
|
||||
case BSSGP_PDUT_PAGING_PS:
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
|
||||
gprs_bssgp_pcu_rx_paging_ps(msg, tp);
|
||||
break;
|
||||
case BSSGP_PDUT_PAGING_CS:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
|
||||
break;
|
||||
case BSSGP_PDUT_RESUME_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_RESUME_NACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_FLUSH_LL:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
|
||||
break;
|
||||
case BSSGP_PDUT_BVC_BLOCK_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
|
||||
break;
|
||||
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
|
||||
the_pcu.bvc_unblocked = 1;
|
||||
if (the_pcu.on_unblock_ack)
|
||||
the_pcu.on_unblock_ack(&the_pcu);
|
||||
bvc_timeout(NULL);
|
||||
break;
|
||||
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%d Rx PDU type 0x%02x unknown\n",
|
||||
bvci, bgph->pdu_type);
|
||||
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
|
||||
static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
|
||||
{
|
||||
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
||||
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
|
||||
@@ -404,11 +329,10 @@ int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
|
||||
int rc = 0;
|
||||
struct bssgp_bvc_ctx *bctx;
|
||||
|
||||
if (pdu_type == BSSGP_PDUT_STATUS) {
|
||||
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u received STATUS\n",
|
||||
msgb_nsei(msg), ns_bvci);
|
||||
return 0;
|
||||
}
|
||||
if (pdu_type == BSSGP_PDUT_STATUS)
|
||||
/* Pass the message to the generic BSSGP parser, which handles
|
||||
* STATUS message in either direction. */
|
||||
return bssgp_rcvmsg(msg);
|
||||
|
||||
/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
|
||||
|
||||
@@ -463,8 +387,66 @@ int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void handle_nm_status(struct osmo_bssgp_prim *bp)
|
||||
{
|
||||
enum gprs_bssgp_cause cause;
|
||||
|
||||
LOGP(DPCU, LOGL_DEBUG,
|
||||
"Got NM-STATUS.ind, BVCI=%d, NSEI=%d\n",
|
||||
bp->bvci, bp->nsei);
|
||||
|
||||
if (!TLVP_PRESENT(bp->tp, BSSGP_IE_CAUSE))
|
||||
return;
|
||||
|
||||
cause = (enum gprs_bssgp_cause)*TLVP_VAL(bp->tp, BSSGP_IE_CAUSE);
|
||||
|
||||
if (cause != BSSGP_CAUSE_BVCI_BLOCKED &&
|
||||
cause != BSSGP_CAUSE_UNKNOWN_BVCI)
|
||||
return;
|
||||
|
||||
if (!TLVP_PRESENT(bp->tp, BSSGP_IE_BVCI))
|
||||
return;
|
||||
|
||||
if (gprs_bssgp_pcu_current_bctx()->bvci != bp->bvci) {
|
||||
LOGP(DPCU, LOGL_NOTICE,
|
||||
"Received BSSGP STATUS message for an unknown BVCI (%d), "
|
||||
"ignored\n",
|
||||
bp->bvci);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cause) {
|
||||
case BSSGP_CAUSE_BVCI_BLOCKED:
|
||||
if (the_pcu.bvc_unblocked) {
|
||||
the_pcu.bvc_unblocked = 0;
|
||||
bvc_timeout(NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case BSSGP_CAUSE_UNKNOWN_BVCI:
|
||||
if (the_pcu.bvc_reset) {
|
||||
the_pcu.bvc_reset = 0;
|
||||
bvc_timeout(NULL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct osmo_bssgp_prim *bp;
|
||||
bp = container_of(oph, struct osmo_bssgp_prim, oph);
|
||||
|
||||
switch (oph->sap) {
|
||||
case SAP_BSSGP_NM:
|
||||
if (oph->primitive == PRIM_NM_STATUS)
|
||||
handle_nm_status(bp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -484,7 +466,6 @@ static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msg
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int nsvc_unblocked = 0;
|
||||
|
||||
static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
@@ -495,87 +476,286 @@ static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
|
||||
return -EINVAL;
|
||||
|
||||
nssd = (struct ns_signal_data *)signal_data;
|
||||
if (nssd->nsvc != nsvc) {
|
||||
if (nssd->nsvc != the_pcu.nsvc) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Signal received of unknown NSVC\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (signal) {
|
||||
case S_NS_UNBLOCK:
|
||||
if (!nsvc_unblocked) {
|
||||
nsvc_unblocked = 1;
|
||||
if (!the_pcu.nsvc_unblocked) {
|
||||
the_pcu.nsvc_unblocked = 1;
|
||||
LOGP(DPCU, LOGL_NOTICE, "NS-VC %d is unblocked.\n",
|
||||
nsvc->nsvci);
|
||||
bvc_sig_reset = 0;
|
||||
bvc_reset = 0;
|
||||
bvc_unblocked = 0;
|
||||
the_pcu.nsvc->nsvci);
|
||||
the_pcu.bvc_sig_reset = 0;
|
||||
the_pcu.bvc_reset = 0;
|
||||
the_pcu.bvc_unblocked = 0;
|
||||
bvc_timeout(NULL);
|
||||
}
|
||||
break;
|
||||
case S_NS_BLOCK:
|
||||
if (nsvc_unblocked) {
|
||||
nsvc_unblocked = 0;
|
||||
if (osmo_timer_pending(&bvc_timer))
|
||||
osmo_timer_del(&bvc_timer);
|
||||
bvc_sig_reset = 0;
|
||||
bvc_reset = 0;
|
||||
bvc_unblocked = 0;
|
||||
if (the_pcu.nsvc_unblocked) {
|
||||
the_pcu.nsvc_unblocked = 0;
|
||||
osmo_timer_del(&the_pcu.bvc_timer);
|
||||
the_pcu.bvc_sig_reset = 0;
|
||||
the_pcu.bvc_reset = 0;
|
||||
the_pcu.bvc_unblocked = 0;
|
||||
LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
|
||||
}
|
||||
break;
|
||||
case S_NS_ALIVE_EXP:
|
||||
LOGP(DPCU, LOGL_NOTICE, "Tns alive expired too often, "
|
||||
"re-starting RESET procedure\n");
|
||||
gprs_ns_reconnect(nssd->nsvc);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned count_pdch(const struct gprs_rlcmac_bts *bts)
|
||||
{
|
||||
size_t trx_no, ts_no;
|
||||
unsigned num_pdch = 0;
|
||||
|
||||
for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); ++trx_no) {
|
||||
const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
|
||||
|
||||
for (ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) {
|
||||
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
|
||||
|
||||
if (pdch->is_enabled())
|
||||
num_pdch += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return num_pdch;
|
||||
}
|
||||
|
||||
static uint32_t gprs_bssgp_max_leak_rate(unsigned cs, int num_pdch)
|
||||
{
|
||||
static const uint32_t max_lr_per_ts[4] = {
|
||||
20 * (1000 / 20), /* CS-1: 20 byte payload per 20ms */
|
||||
30 * (1000 / 20), /* CS-2: 30 byte payload per 20ms */
|
||||
36 * (1000 / 20), /* CS-3: 36 byte payload per 20ms */
|
||||
50 * (1000 / 20), /* CS-4: 50 byte payload per 20ms */
|
||||
};
|
||||
|
||||
if (cs > ARRAY_SIZE(max_lr_per_ts))
|
||||
cs = 1;
|
||||
|
||||
return max_lr_per_ts[cs-1] * num_pdch;
|
||||
}
|
||||
|
||||
static uint32_t compute_bucket_size(struct gprs_rlcmac_bts *bts,
|
||||
uint32_t leak_rate, uint32_t fallback)
|
||||
{
|
||||
uint32_t bucket_size = 0;
|
||||
uint16_t bucket_time = bts->fc_bucket_time;
|
||||
|
||||
if (bucket_time == 0)
|
||||
bucket_time = bts->force_llc_lifetime;
|
||||
|
||||
if (bucket_time == 0xffff)
|
||||
bucket_size = FC_MAX_BUCKET_SIZE;
|
||||
|
||||
if (bucket_size == 0 && bucket_time && leak_rate)
|
||||
bucket_size = (uint64_t)leak_rate * bucket_time / 100;
|
||||
|
||||
if (bucket_size == 0 && leak_rate)
|
||||
bucket_size = leak_rate * FC_DEFAULT_LIFE_TIME_SECS;
|
||||
|
||||
if (bucket_size == 0)
|
||||
bucket_size = fallback;
|
||||
|
||||
if (bucket_size > FC_MAX_BUCKET_SIZE)
|
||||
bucket_size = FC_MAX_BUCKET_SIZE;
|
||||
|
||||
return bucket_size;
|
||||
}
|
||||
|
||||
static uint32_t get_and_reset_avg_queue_delay(void)
|
||||
{
|
||||
struct timeval *delay_sum = &the_pcu.queue_delay_sum;
|
||||
uint32_t delay_sum_ms = delay_sum->tv_sec * 1000 +
|
||||
delay_sum->tv_usec / 1000000;
|
||||
uint32_t avg_delay_ms = 0;
|
||||
|
||||
if (the_pcu.queue_delay_count > 0)
|
||||
avg_delay_ms = delay_sum_ms / the_pcu.queue_delay_count;
|
||||
|
||||
/* Reset accumulator */
|
||||
delay_sum->tv_sec = delay_sum->tv_usec = 0;
|
||||
the_pcu.queue_delay_count = 0;
|
||||
|
||||
return avg_delay_ms;
|
||||
}
|
||||
|
||||
int gprs_bssgp_tx_fc_bvc(void)
|
||||
{
|
||||
if (!bctx) {
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
uint32_t bucket_size; /* oct */
|
||||
uint32_t ms_bucket_size; /* oct */
|
||||
uint32_t leak_rate; /* oct/s */
|
||||
uint32_t ms_leak_rate; /* oct/s */
|
||||
uint32_t avg_delay_ms;
|
||||
int num_pdch = -1;
|
||||
int max_cs_dl;
|
||||
|
||||
if (!the_pcu.bctx) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
|
||||
return -EIO;
|
||||
}
|
||||
/* FIXME: use real values */
|
||||
return bssgp_tx_fc_bvc(bctx, 1, 6553500, 819100, 50000, 50000,
|
||||
NULL, NULL);
|
||||
// return bssgp_tx_fc_bvc(bctx, 1, 84000, 25000, 48000, 45000,
|
||||
// NULL, NULL);
|
||||
bts = bts_main_data();
|
||||
|
||||
if (bts->cs_adj_enabled) {
|
||||
max_cs_dl = bts->max_cs_dl;
|
||||
if (!max_cs_dl) {
|
||||
if (bts->cs4)
|
||||
max_cs_dl = 4;
|
||||
else if (bts->cs3)
|
||||
max_cs_dl = 3;
|
||||
else if (bts->cs2)
|
||||
max_cs_dl = 2;
|
||||
else
|
||||
max_cs_dl = 1;
|
||||
}
|
||||
} else {
|
||||
max_cs_dl = bts->initial_cs_dl;
|
||||
}
|
||||
|
||||
bucket_size = bts->fc_bvc_bucket_size;
|
||||
leak_rate = bts->fc_bvc_leak_rate;
|
||||
ms_bucket_size = bts->fc_ms_bucket_size;
|
||||
ms_leak_rate = bts->fc_ms_leak_rate;
|
||||
|
||||
if (leak_rate == 0) {
|
||||
if (num_pdch < 0)
|
||||
num_pdch = count_pdch(bts);
|
||||
|
||||
leak_rate = gprs_bssgp_max_leak_rate(max_cs_dl, num_pdch);
|
||||
|
||||
LOGP(DBSSGP, LOGL_DEBUG,
|
||||
"Computed BVC leak rate = %d, num_pdch = %d, cs = %d\n",
|
||||
leak_rate, num_pdch, max_cs_dl);
|
||||
};
|
||||
|
||||
if (ms_leak_rate == 0) {
|
||||
int ms_num_pdch;
|
||||
int max_pdch = gprs_alloc_max_dl_slots_per_ms(bts);
|
||||
|
||||
if (num_pdch < 0)
|
||||
num_pdch = count_pdch(bts);
|
||||
|
||||
ms_num_pdch = num_pdch;
|
||||
if (max_pdch > FC_MS_MAX_RX_SLOTS)
|
||||
max_pdch = FC_MS_MAX_RX_SLOTS;
|
||||
if (ms_num_pdch > max_pdch)
|
||||
ms_num_pdch = max_pdch;
|
||||
|
||||
ms_leak_rate = gprs_bssgp_max_leak_rate(max_cs_dl, ms_num_pdch);
|
||||
|
||||
/* TODO: To properly support multiple TRX, the per MS leak rate
|
||||
* should be derived from the max number of PDCH TS per TRX.
|
||||
*/
|
||||
LOGP(DBSSGP, LOGL_DEBUG,
|
||||
"Computed MS default leak rate = %d, ms_num_pdch = %d, cs = %d\n",
|
||||
ms_leak_rate, ms_num_pdch, max_cs_dl);
|
||||
};
|
||||
|
||||
/* TODO: Force leak_rate to 0 on buffer bloat */
|
||||
|
||||
if (bucket_size == 0)
|
||||
bucket_size = compute_bucket_size(bts, leak_rate,
|
||||
FC_FALLBACK_BVC_BUCKET_SIZE);
|
||||
|
||||
if (ms_bucket_size == 0)
|
||||
ms_bucket_size = compute_bucket_size(bts, ms_leak_rate,
|
||||
FC_MS_BUCKET_SIZE_BY_BMAX(bucket_size));
|
||||
|
||||
if (leak_rate > FC_MAX_BUCKET_LEAK_RATE)
|
||||
leak_rate = FC_MAX_BUCKET_LEAK_RATE;
|
||||
|
||||
if (ms_leak_rate > FC_MAX_BUCKET_LEAK_RATE)
|
||||
ms_leak_rate = FC_MAX_BUCKET_LEAK_RATE;
|
||||
|
||||
/* Avg queue delay monitoring */
|
||||
avg_delay_ms = get_and_reset_avg_queue_delay();
|
||||
|
||||
/* Update tag */
|
||||
the_pcu.fc_tag += 1;
|
||||
|
||||
LOGP(DBSSGP, LOGL_DEBUG,
|
||||
"Sending FLOW CONTROL BVC, Bmax = %d, R = %d, Bmax_MS = %d, "
|
||||
"R_MS = %d, avg_dly = %d\n",
|
||||
bucket_size, leak_rate, ms_bucket_size, ms_leak_rate,
|
||||
avg_delay_ms);
|
||||
|
||||
return bssgp_tx_fc_bvc(the_pcu.bctx, the_pcu.fc_tag,
|
||||
bucket_size, leak_rate,
|
||||
ms_bucket_size, ms_leak_rate,
|
||||
NULL, &avg_delay_ms);
|
||||
}
|
||||
|
||||
static void bvc_timeout(void *_priv)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
|
||||
if (!bvc_sig_reset) {
|
||||
if (!the_pcu.bvc_sig_reset) {
|
||||
LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI 0\n");
|
||||
bssgp_tx_bvc_reset(bctx, 0, BSSGP_CAUSE_OML_INTERV);
|
||||
osmo_timer_schedule(&bvc_timer, 1, 0);
|
||||
bssgp_tx_bvc_reset(the_pcu.bctx, 0, BSSGP_CAUSE_OML_INTERV);
|
||||
osmo_timer_schedule(&the_pcu.bvc_timer, BSSGP_TIMER_T2, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bvc_reset) {
|
||||
if (!the_pcu.bvc_reset) {
|
||||
LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI %d\n",
|
||||
bctx->bvci);
|
||||
bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_OML_INTERV);
|
||||
osmo_timer_schedule(&bvc_timer, 1, 0);
|
||||
the_pcu.bctx->bvci);
|
||||
bssgp_tx_bvc_reset(the_pcu.bctx, the_pcu.bctx->bvci, BSSGP_CAUSE_OML_INTERV);
|
||||
osmo_timer_schedule(&the_pcu.bvc_timer, BSSGP_TIMER_T2, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bvc_unblocked) {
|
||||
if (!the_pcu.bvc_unblocked) {
|
||||
LOGP(DBSSGP, LOGL_INFO, "Sending unblock on BVCI %d\n",
|
||||
bctx->bvci);
|
||||
bssgp_tx_bvc_unblock(bctx);
|
||||
osmo_timer_schedule(&bvc_timer, 1, 0);
|
||||
the_pcu.bctx->bvci);
|
||||
bssgp_tx_bvc_unblock(the_pcu.bctx);
|
||||
osmo_timer_schedule(&the_pcu.bvc_timer, BSSGP_TIMER_T1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DBSSGP, LOGL_DEBUG, "Sending flow control info on BVCI %d\n",
|
||||
bctx->bvci);
|
||||
the_pcu.bctx->bvci);
|
||||
gprs_bssgp_tx_fc_bvc();
|
||||
osmo_timer_schedule(&bvc_timer, bts->fc_interval, 0);
|
||||
osmo_timer_schedule(&the_pcu.bvc_timer, the_pcu.bts->fc_interval, 0);
|
||||
}
|
||||
|
||||
int gprs_ns_reconnect(struct gprs_nsvc *nsvc)
|
||||
{
|
||||
struct gprs_nsvc *nsvc2;
|
||||
|
||||
if (!bssgp_nsi) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "NS instance does not exist\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nsvc != the_pcu.nsvc) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "NSVC is invalid\n");
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
nsvc2 = gprs_ns_nsip_connect(bssgp_nsi, &nsvc->ip.bts_addr,
|
||||
nsvc->nsei, nsvc->nsvci);
|
||||
|
||||
if (!nsvc2) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to reconnect NSVC\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create BSSGP/NS layer instances */
|
||||
int gprs_bssgp_create(uint16_t local_port, uint32_t sgsn_ip,
|
||||
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, uint16_t lac, uint16_t rac,
|
||||
uint16_t cell_id)
|
||||
@@ -587,13 +767,16 @@ int gprs_bssgp_create(uint16_t local_port, uint32_t sgsn_ip,
|
||||
mnc = ((mnc & 0xf00) >> 8) * 100 + ((mnc & 0x0f0) >> 4) * 10 + (mnc & 0x00f);
|
||||
cell_id = ntohs(cell_id);
|
||||
|
||||
if (bctx)
|
||||
return 0; /* if already created, must return 0: no error */
|
||||
/* if already created... return the current address */
|
||||
if (the_pcu.bctx)
|
||||
return &the_pcu;
|
||||
|
||||
the_pcu.bts = bts;
|
||||
|
||||
bssgp_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_pcu_ctx);
|
||||
if (!bssgp_nsi) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
|
||||
return -EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
gprs_ns_vty_init(bssgp_nsi);
|
||||
bssgp_nsi->nsip.local_port = local_port;
|
||||
@@ -602,43 +785,41 @@ int gprs_bssgp_create(uint16_t local_port, uint32_t sgsn_ip,
|
||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to create socket\n");
|
||||
gprs_ns_destroy(bssgp_nsi);
|
||||
bssgp_nsi = NULL;
|
||||
return -EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = htons(sgsn_port);
|
||||
dest.sin_addr.s_addr = htonl(sgsn_ip);
|
||||
|
||||
nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
|
||||
if (!nsvc) {
|
||||
the_pcu.nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
|
||||
if (!the_pcu.nsvc) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NSVCt\n");
|
||||
gprs_ns_destroy(bssgp_nsi);
|
||||
bssgp_nsi = NULL;
|
||||
return -EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bctx = btsctx_alloc(bvci, nsei);
|
||||
if (!bctx) {
|
||||
the_pcu.bctx = btsctx_alloc(bvci, nsei);
|
||||
if (!the_pcu.bctx) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "Failed to create BSSGP context\n");
|
||||
nsvc = NULL;
|
||||
the_pcu.nsvc = NULL;
|
||||
gprs_ns_destroy(bssgp_nsi);
|
||||
bssgp_nsi = NULL;
|
||||
return -EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
bctx->ra_id.mcc = spoof_mcc ? : mcc;
|
||||
bctx->ra_id.mnc = spoof_mnc ? : mnc;
|
||||
bctx->ra_id.lac = lac;
|
||||
bctx->ra_id.rac = rac;
|
||||
bctx->cell_id = cell_id;
|
||||
the_pcu.bctx->ra_id.mcc = spoof_mcc ? : mcc;
|
||||
the_pcu.bctx->ra_id.mnc = spoof_mnc ? : mnc;
|
||||
the_pcu.bctx->ra_id.lac = lac;
|
||||
the_pcu.bctx->ra_id.rac = rac;
|
||||
the_pcu.bctx->cell_id = cell_id;
|
||||
|
||||
osmo_signal_register_handler(SS_L_NS, nsvc_signal_cb, NULL);
|
||||
|
||||
// bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC);
|
||||
|
||||
bvc_timer.cb = bvc_timeout;
|
||||
the_pcu.bvc_timer.cb = bvc_timeout;
|
||||
|
||||
|
||||
return 0;
|
||||
return &the_pcu;
|
||||
}
|
||||
|
||||
void gprs_bssgp_destroy(void)
|
||||
@@ -646,21 +827,40 @@ void gprs_bssgp_destroy(void)
|
||||
if (!bssgp_nsi)
|
||||
return;
|
||||
|
||||
if (osmo_timer_pending(&bvc_timer))
|
||||
osmo_timer_del(&bvc_timer);
|
||||
osmo_timer_del(&the_pcu.bvc_timer);
|
||||
|
||||
osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
|
||||
|
||||
nsvc = NULL;
|
||||
the_pcu.nsvc = NULL;
|
||||
|
||||
/* FIXME: move this to libgb: btsctx_free() */
|
||||
llist_del(&bctx->list);
|
||||
talloc_free(bctx);
|
||||
bctx = NULL;
|
||||
llist_del(&the_pcu.bctx->list);
|
||||
talloc_free(the_pcu.bctx);
|
||||
the_pcu.bctx = NULL;
|
||||
|
||||
/* FIXME: blocking... */
|
||||
the_pcu.nsvc_unblocked = 0;
|
||||
the_pcu.bvc_sig_reset = 0;
|
||||
the_pcu.bvc_reset = 0;
|
||||
the_pcu.bvc_unblocked = 0;
|
||||
|
||||
gprs_ns_destroy(bssgp_nsi);
|
||||
bssgp_nsi = NULL;
|
||||
}
|
||||
|
||||
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
|
||||
{
|
||||
return the_pcu.bctx;
|
||||
}
|
||||
|
||||
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
|
||||
const struct timeval *tv_now)
|
||||
{
|
||||
struct timeval *delay_sum = &the_pcu.queue_delay_sum;
|
||||
struct timeval tv_delay;
|
||||
|
||||
timersub(tv_now, tv_recv, &tv_delay);
|
||||
timeradd(delay_sum, &tv_delay, delay_sum);
|
||||
|
||||
the_pcu.queue_delay_count += 1;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* gprs_bssgp_pcu.h
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -41,21 +42,49 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
|
||||
#define NS_HDR_LEN 4
|
||||
#define IE_LLC_PDU 14
|
||||
|
||||
extern struct bssgp_bvc_ctx *bctx;
|
||||
struct gprs_rlcmac_bts;
|
||||
|
||||
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp);
|
||||
struct gprs_bssgp_pcu {
|
||||
struct gprs_nsvc *nsvc;
|
||||
struct bssgp_bvc_ctx *bctx;
|
||||
|
||||
int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
|
||||
int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx);
|
||||
struct osmo_timer_list bvc_timer;
|
||||
|
||||
int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
|
||||
int nsvc_unblocked;
|
||||
|
||||
int gprs_bssgp_create(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, uint16_t lac, uint16_t rac,
|
||||
uint16_t cell_id);
|
||||
int bvc_sig_reset;
|
||||
int bvc_reset;
|
||||
int bvc_unblocked;
|
||||
|
||||
/* Flow control */
|
||||
struct timeval queue_delay_sum;
|
||||
unsigned queue_delay_count;
|
||||
uint8_t fc_tag;
|
||||
|
||||
/** callbacks below */
|
||||
|
||||
/* The BSSGP has been unblocked */
|
||||
void (*on_unblock_ack)(struct gprs_bssgp_pcu *pcu);
|
||||
|
||||
/* When BSSGP data arrives. The msgb is not only for reference */
|
||||
void (*on_dl_unit_data)(struct gprs_bssgp_pcu *pcu, struct msgb *msg,
|
||||
struct tlv_parsed *tp);
|
||||
};
|
||||
|
||||
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,
|
||||
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);
|
||||
|
||||
#endif // GPRS_BSSGP_PCU_H
|
||||
|
||||
179
src/gprs_codel.c
Normal file
179
src/gprs_codel.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/* gprs_codel.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "gprs_codel.h"
|
||||
#include "gprs_debug.h"
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
static void control_law(struct gprs_codel *state, struct timeval *delta)
|
||||
{
|
||||
/* 256 / sqrt(x), limited to 255 */
|
||||
static uint8_t inv_sqrt_tab[] = {255,
|
||||
255, 181, 147, 128, 114, 104, 96, 90, 85, 80, 77, 73, 71, 68,
|
||||
66, 64, 62, 60, 58, 57, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
|
||||
45, 45, 44, 43, 43, 42, 42, 41, 40, 40, 39, 39, 39, 38, 38, 37,
|
||||
37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 33, 33, 32, 32,
|
||||
32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 29, 28,
|
||||
28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26,
|
||||
26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21,
|
||||
21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||
20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
|
||||
19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18,
|
||||
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
|
||||
17, 17, 17, 17
|
||||
};
|
||||
uint_fast32_t delta_usecs;
|
||||
uint_fast32_t inv_sqrt;
|
||||
div_t q;
|
||||
|
||||
if (state->count >= ARRAY_SIZE(inv_sqrt_tab))
|
||||
inv_sqrt = 16;
|
||||
else
|
||||
inv_sqrt = inv_sqrt_tab[state->count];
|
||||
|
||||
/* delta = state->interval / sqrt(count) */
|
||||
delta_usecs = state->interval.tv_sec * 1000000 + state->interval.tv_usec;
|
||||
delta_usecs = delta_usecs * inv_sqrt / 256;
|
||||
|
||||
q = div(delta_usecs, 1000000);
|
||||
delta->tv_sec = q.quot;
|
||||
delta->tv_usec = q.rem;
|
||||
}
|
||||
|
||||
void gprs_codel_init(struct gprs_codel *state)
|
||||
{
|
||||
static const struct gprs_codel init_state = {0};
|
||||
|
||||
*state = init_state;
|
||||
gprs_codel_set_interval(state, -1);
|
||||
gprs_codel_set_maxpacket(state, -1);
|
||||
}
|
||||
|
||||
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms)
|
||||
{
|
||||
div_t q;
|
||||
|
||||
if (interval_ms <= 0)
|
||||
interval_ms = GPRS_CODEL_DEFAULT_INTERVAL_MS;
|
||||
|
||||
q = div(interval_ms, 1000);
|
||||
state->interval.tv_sec = q.quot;
|
||||
state->interval.tv_usec = q.rem * 1000;
|
||||
|
||||
/* target ~ 5% of interval */
|
||||
q = div(interval_ms * 13 / 256, 1000);
|
||||
state->target.tv_sec = q.quot;
|
||||
state->target.tv_usec = q.rem * 1000;
|
||||
}
|
||||
|
||||
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket)
|
||||
{
|
||||
|
||||
if (maxpacket < 0)
|
||||
maxpacket = GPRS_CODEL_DEFAULT_MAXPACKET;
|
||||
|
||||
state->maxpacket = maxpacket;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an broken up variant of the algorithm being described in
|
||||
* http://queue.acm.org/appendices/codel.html
|
||||
*/
|
||||
int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
|
||||
const struct timeval *now, int bytes)
|
||||
{
|
||||
struct timeval sojourn_time;
|
||||
struct timeval delta;
|
||||
|
||||
if (recv == NULL)
|
||||
goto stop_dropping;
|
||||
|
||||
timersub(now, recv, &sojourn_time);
|
||||
|
||||
if (timercmp(&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);
|
||||
goto not_ok_to_drop;
|
||||
}
|
||||
|
||||
if (timercmp(now, &state->first_above_time, <))
|
||||
goto not_ok_to_drop;
|
||||
|
||||
/* Ok to drop */
|
||||
|
||||
if (!state->dropping) {
|
||||
int recently = 0;
|
||||
int in_drop_cycle = 0;
|
||||
if (timerisset(&state->drop_next)) {
|
||||
timersub(now, &state->drop_next, &delta);
|
||||
in_drop_cycle = timercmp(&delta, &state->interval, <);
|
||||
recently = in_drop_cycle;
|
||||
}
|
||||
if (!recently) {
|
||||
timersub(now, &state->first_above_time, &delta);
|
||||
recently = !timercmp(&delta, &state->interval, <);
|
||||
};
|
||||
if (!recently)
|
||||
return 0;
|
||||
|
||||
state->dropping = 1;
|
||||
|
||||
if (in_drop_cycle && state->count > 2)
|
||||
state->count -= 2;
|
||||
else
|
||||
state->count = 1;
|
||||
|
||||
state->drop_next = *now;
|
||||
} else {
|
||||
if (timercmp(now, &state->drop_next, <))
|
||||
return 0;
|
||||
|
||||
state->count += 1;
|
||||
}
|
||||
|
||||
control_law(state, &delta);
|
||||
timeradd(&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);
|
||||
#endif
|
||||
return 1;
|
||||
|
||||
stop_dropping:
|
||||
timerclear(&state->first_above_time);
|
||||
not_ok_to_drop:
|
||||
state->dropping = 0;
|
||||
return 0;
|
||||
}
|
||||
108
src/gprs_codel.h
Normal file
108
src/gprs_codel.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* gprs_codel.h
|
||||
*
|
||||
* This is an implementation of the CoDel algorithm based on the reference
|
||||
* pseudocode (see http://queue.acm.org/appendices/codel.html).
|
||||
* Instead of abstracting the queue itself, the following implementation
|
||||
* provides a time stamp based automaton. The main work is done by a single
|
||||
* decision function which updates the state and tells whether to pass or to
|
||||
* drop a packet after it has been taken from the queue.
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
/* Spec default values */
|
||||
#define GPRS_CODEL_DEFAULT_INTERVAL_MS 100
|
||||
#define GPRS_CODEL_DEFAULT_MAXPACKET 512
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct gprs_codel {
|
||||
int dropping;
|
||||
unsigned count;
|
||||
struct timeval first_above_time;
|
||||
struct timeval drop_next;
|
||||
struct timeval target;
|
||||
struct timeval interval;
|
||||
unsigned maxpacket;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Decide about packet drop and update CoDel state
|
||||
*
|
||||
* This function takes timing information and decides whether the packet in
|
||||
* question should be dropped in order to keep related queue in a 'good' state.
|
||||
* The function is meant to be called when the packet is dequeued.
|
||||
*
|
||||
* The CoDel state is updated by this function.
|
||||
*
|
||||
* \param state A pointer to the CoDel state of this queue
|
||||
* \param recv The time when the packet has entered the queue,
|
||||
* use NULL if dequeueing was not possible because the queue is
|
||||
* empty
|
||||
* \param now The current (dequeueing) time
|
||||
* \param bytes The number of bytes currently stored in the queue (-1 if
|
||||
* unknown)
|
||||
*
|
||||
* \return != 0 if the packet should be dropped, 0 otherwise
|
||||
*/
|
||||
int gprs_codel_control(struct gprs_codel *state, const struct timeval *recv,
|
||||
const struct timeval *now, int bytes);
|
||||
|
||||
/*!
|
||||
* \brief Initialise CoDel state
|
||||
*
|
||||
* This function initialises the CoDel state object. It sets the interval time
|
||||
* to the default value (GPRS_CODEL_DEFAULT_INTERVAL_MS).
|
||||
*
|
||||
* \param state A pointer to the CoDel state of this queue
|
||||
*/
|
||||
void gprs_codel_init(struct gprs_codel *state);
|
||||
|
||||
/*!
|
||||
* \brief Set interval time
|
||||
*
|
||||
* This function changes the interval time.
|
||||
* The target time is derived from the interval time as proposed in the spec
|
||||
* (5% of interval time).
|
||||
*
|
||||
* \param state A pointer to the CoDel state of this queue
|
||||
* \param interval_ms The initial interval in ms to be used (<= 0 selects the
|
||||
* default value)
|
||||
*/
|
||||
void gprs_codel_set_interval(struct gprs_codel *state, int interval_ms);
|
||||
|
||||
/*!
|
||||
* \brief Set max packet size
|
||||
*
|
||||
* This function changes the maxpacket value. If no more than this number of
|
||||
* bytes are still stored in the queue, no dropping will be done.
|
||||
*
|
||||
* \param state A pointer to the CoDel state of this queue
|
||||
* \param maxpacket The value in bytes
|
||||
*/
|
||||
void gprs_codel_set_maxpacket(struct gprs_codel *state, int maxpacket);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -41,6 +41,7 @@ static const struct log_info_cat default_categories[] = {
|
||||
{"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},
|
||||
{"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},
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@ enum {
|
||||
DRLCMACUL,
|
||||
DRLCMACSCHED,
|
||||
DRLCMACMEAS,
|
||||
DNS,
|
||||
DBSSGP,
|
||||
DPCU,
|
||||
aDebug_LastEntry
|
||||
|
||||
671
src/gprs_ms.cpp
Normal file
671
src/gprs_ms.cpp
Normal file
@@ -0,0 +1,671 @@
|
||||
/* 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 "bts.h"
|
||||
#include "tbf.h"
|
||||
#include "gprs_debug.h"
|
||||
#include "gprs_codel.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.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(0),
|
||||
m_ms_class(0),
|
||||
m_current_cs_ul(1),
|
||||
m_current_cs_dl(1),
|
||||
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)
|
||||
{
|
||||
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();
|
||||
if (m_bts) {
|
||||
m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
|
||||
if (m_current_cs_ul < 1)
|
||||
m_current_cs_ul = 1;
|
||||
|
||||
m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
|
||||
if (m_current_cs_dl < 1)
|
||||
m_current_cs_dl = 1;
|
||||
|
||||
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::attach_tbf(struct gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
|
||||
else
|
||||
attach_ul_tbf(static_cast<gprs_rlcmac_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;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
|
||||
tlli(), m_ta, ta_);
|
||||
|
||||
m_ta = 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::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
int64_t now;
|
||||
uint8_t max_cs_dl = 4;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (error_rate < 0)
|
||||
return;
|
||||
|
||||
now = now_msec();
|
||||
|
||||
if (bts_data->max_cs_dl)
|
||||
max_cs_dl = bts_data->max_cs_dl;
|
||||
|
||||
/* 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 > 1) {
|
||||
m_current_cs_dl -= 1;
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): High error rate %d%%, "
|
||||
"reducing CS level to %d\n",
|
||||
imsi(), error_rate, m_current_cs_dl);
|
||||
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 += 1;
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"increasing DL CS level to %d\n",
|
||||
imsi(), error_rate, m_current_cs_dl);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
uint8_t max_cs_ul = 4;
|
||||
unsigned i;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (bts_data->max_cs_ul)
|
||||
max_cs_ul = bts_data->max_cs_ul;
|
||||
|
||||
if (meas->have_link_qual) {
|
||||
int old_link_qual = meas->link_qual;
|
||||
int low = bts_data->cs_lqual_ranges[current_cs_ul()-1].low;
|
||||
int high = bts_data->cs_lqual_ranges[current_cs_ul()-1].high;
|
||||
uint8_t new_cs_ul = m_current_cs_ul;
|
||||
|
||||
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 = m_current_cs_ul - 1;
|
||||
else if (meas->link_qual > high && old_link_qual > high &&
|
||||
m_current_cs_ul < max_cs_ul)
|
||||
new_cs_ul = m_current_cs_ul + 1;
|
||||
|
||||
if (m_current_cs_ul != new_cs_ul) {
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): "
|
||||
"Link quality %ddB (%ddB) left window [%d, %d], "
|
||||
"modifying uplink CS level: %d -> %d\n",
|
||||
imsi(), meas->link_qual, old_link_qual,
|
||||
low, high,
|
||||
m_current_cs_ul, new_cs_ul);
|
||||
|
||||
m_current_cs_ul = new_cs_ul;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GprsMs::current_cs_dl() const
|
||||
{
|
||||
uint8_t 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 -= 1;
|
||||
|
||||
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
|
||||
if (cs == 2)
|
||||
cs -= 1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
258
src/gprs_ms.h
Normal file
258
src/gprs_ms.h
Normal file
@@ -0,0 +1,258 @@
|
||||
/* gprs_ms.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
|
||||
|
||||
struct gprs_rlcmac_tbf;
|
||||
struct gprs_rlcmac_dl_tbf;
|
||||
struct gprs_rlcmac_ul_tbf;
|
||||
struct gprs_codel;
|
||||
|
||||
#include "cxx_linuxlist.h"
|
||||
#include "llc.h"
|
||||
#include "tbf.h"
|
||||
#include "pcu_l1_if.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/timer.h>
|
||||
}
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.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();
|
||||
|
||||
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;
|
||||
void set_ms_class(uint8_t ms_class);
|
||||
|
||||
uint8_t current_cs_ul() const;
|
||||
uint8_t current_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;
|
||||
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;
|
||||
|
||||
/* internal use */
|
||||
static void timeout(void *priv_);
|
||||
|
||||
protected:
|
||||
void update_status();
|
||||
GprsMs *ref();
|
||||
void unref();
|
||||
void start_timer();
|
||||
void stop_timer();
|
||||
|
||||
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;
|
||||
/* current coding scheme */
|
||||
uint8_t m_current_cs_ul;
|
||||
uint8_t 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;
|
||||
};
|
||||
|
||||
inline bool GprsMs::is_idle() const
|
||||
{
|
||||
return !m_ul_tbf && !m_dl_tbf && !m_ref && llist_empty(&m_old_tbfs);
|
||||
}
|
||||
|
||||
inline bool GprsMs::need_dl_tbf() const
|
||||
{
|
||||
if (dl_tbf() != NULL && dl_tbf()->state_is_not(GPRS_RLCMAC_WAIT_RELEASE))
|
||||
return false;
|
||||
|
||||
return llc_queue()->size() > 0;
|
||||
}
|
||||
|
||||
inline uint32_t GprsMs::tlli() const
|
||||
{
|
||||
return m_new_ul_tlli ? m_new_ul_tlli :
|
||||
m_tlli ? m_tlli :
|
||||
m_new_dl_tlli;
|
||||
}
|
||||
|
||||
inline bool GprsMs::check_tlli(uint32_t tlli)
|
||||
{
|
||||
return tlli != 0 &&
|
||||
(tlli == m_tlli || tlli == m_new_ul_tlli || tlli == m_new_dl_tlli);
|
||||
}
|
||||
|
||||
inline const char *GprsMs::imsi() const
|
||||
{
|
||||
return m_imsi;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::ta() const
|
||||
{
|
||||
return m_ta;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::ms_class() const
|
||||
{
|
||||
return m_ms_class;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::current_cs_ul() const
|
||||
{
|
||||
return m_current_cs_ul;
|
||||
}
|
||||
|
||||
inline void GprsMs::set_timeout(unsigned secs)
|
||||
{
|
||||
m_delay = secs;
|
||||
}
|
||||
|
||||
inline gprs_llc_queue *GprsMs::llc_queue()
|
||||
{
|
||||
return &m_llc_queue;
|
||||
}
|
||||
|
||||
inline const gprs_llc_queue *GprsMs::llc_queue() const
|
||||
{
|
||||
return &m_llc_queue;
|
||||
}
|
||||
|
||||
inline gprs_codel *GprsMs::codel_state() const
|
||||
{
|
||||
return m_codel_state;
|
||||
}
|
||||
|
||||
inline unsigned GprsMs::nack_rate_dl() const
|
||||
{
|
||||
return m_nack_rate_dl;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::reserved_dl_slots() const
|
||||
{
|
||||
return m_reserved_dl_slots;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
112
src/gprs_ms_storage.cpp
Normal file
112
src/gprs_ms_storage.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/* gprs_ms_storage.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_ms_storage.h"
|
||||
|
||||
#include "tbf.h"
|
||||
#include "gprs_debug.h"
|
||||
|
||||
#define GPRS_UNDEFINED_IMSI "000"
|
||||
|
||||
GprsMsStorage::GprsMsStorage(BTS *bts) :
|
||||
m_bts(bts)
|
||||
{
|
||||
}
|
||||
|
||||
GprsMsStorage::~GprsMsStorage()
|
||||
{
|
||||
LListHead<GprsMs> *pos, *tmp;
|
||||
|
||||
llist_for_each_safe(pos, tmp, &m_list) {
|
||||
GprsMs *ms = pos->entry();
|
||||
ms->set_callback(NULL);
|
||||
ms_idle(ms);
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMsStorage::ms_idle(class GprsMs *ms)
|
||||
{
|
||||
llist_del(&ms->list());
|
||||
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
|
||||
{
|
||||
GprsMs *ms;
|
||||
LListHead<GprsMs> *pos;
|
||||
|
||||
if (tlli || old_tlli) {
|
||||
llist_for_each(pos, &m_list) {
|
||||
ms = pos->entry();
|
||||
if (ms->check_tlli(tlli))
|
||||
return ms;
|
||||
if (ms->check_tlli(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)
|
||||
return ms;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GprsMs *GprsMsStorage::create_ms()
|
||||
{
|
||||
GprsMs *ms;
|
||||
|
||||
ms = new GprsMs(m_bts, 0);
|
||||
|
||||
ms->set_callback(this);
|
||||
llist_add(&ms->list(), &m_list);
|
||||
|
||||
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);
|
||||
|
||||
return ms;
|
||||
}
|
||||
47
src/gprs_ms_storage.h
Normal file
47
src/gprs_ms_storage.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* gprs_ms_storage.h
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gprs_ms.h"
|
||||
#include "cxx_linuxlist.h"
|
||||
#include "tbf.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct BTS;
|
||||
|
||||
class GprsMsStorage : public GprsMs::Callback {
|
||||
public:
|
||||
GprsMsStorage(BTS *bts);
|
||||
~GprsMsStorage();
|
||||
|
||||
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 *create_ms();
|
||||
|
||||
const LListHead<GprsMs>& ms_list() const {return m_list;}
|
||||
private:
|
||||
BTS *m_bts;
|
||||
LListHead<GprsMs> m_list;
|
||||
};
|
||||
1890
src/gprs_rlcmac.cpp
1890
src/gprs_rlcmac.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
/* gprs_rlcmac.h
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -31,229 +32,19 @@ extern "C" {
|
||||
}
|
||||
#endif
|
||||
|
||||
/* generate a diagram for debugging timing issues */
|
||||
//#define DEBUG_DIAGRAM
|
||||
|
||||
/* This special feature will delay assignment of downlink TBF by one second,
|
||||
* in case there is already a TBF.
|
||||
* This is usefull to debug downlink establishment during packet idle mode.
|
||||
*/
|
||||
//#define DEBUG_DL_ASS_IDLE
|
||||
|
||||
/*
|
||||
* PDCH instanc
|
||||
*/
|
||||
|
||||
struct gprs_rlcmac_tbf;
|
||||
|
||||
struct gprs_rlcmac_pdch {
|
||||
uint8_t enable; /* TS is enabled */
|
||||
uint8_t tsc; /* TSC of this slot */
|
||||
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
|
||||
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
|
||||
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
|
||||
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
|
||||
struct llist_head paging_list; /* list of paging messages */
|
||||
uint32_t last_rts_fn; /* store last frame number of RTS */
|
||||
};
|
||||
|
||||
struct gprs_rlcmac_trx {
|
||||
void *fl1h;
|
||||
uint16_t arfcn;
|
||||
struct gprs_rlcmac_pdch pdch[8];
|
||||
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
|
||||
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
|
||||
};
|
||||
|
||||
struct gprs_rlcmac_bts {
|
||||
uint8_t bsic;
|
||||
uint8_t fc_interval;
|
||||
uint8_t cs1;
|
||||
uint8_t cs2;
|
||||
uint8_t cs3;
|
||||
uint8_t cs4;
|
||||
uint8_t initial_cs_dl, initial_cs_ul;
|
||||
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
|
||||
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
|
||||
uint8_t t3142;
|
||||
uint8_t t3169;
|
||||
uint8_t t3191;
|
||||
uint16_t t3193_msec;
|
||||
uint8_t t3195;
|
||||
uint8_t n3101;
|
||||
uint8_t n3103;
|
||||
uint8_t n3105;
|
||||
struct gprs_rlcmac_trx trx[8];
|
||||
int (*alloc_algorithm)(struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
|
||||
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
|
||||
uint8_t force_two_phase;
|
||||
uint8_t alpha, gamma;
|
||||
};
|
||||
|
||||
extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts;
|
||||
struct BTS;
|
||||
struct GprsMs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
/*
|
||||
* TBF instance
|
||||
*/
|
||||
|
||||
#define LLC_MAX_LEN 1543
|
||||
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
|
||||
#define RLC_MAX_WS 64 /* max window size */
|
||||
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
|
||||
|
||||
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
|
||||
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
|
||||
|
||||
enum gprs_rlcmac_tbf_state {
|
||||
GPRS_RLCMAC_NULL = 0, /* new created TBF */
|
||||
GPRS_RLCMAC_ASSIGN, /* wait for downlink assignment */
|
||||
GPRS_RLCMAC_FLOW, /* RLC/MAC flow, resource needed */
|
||||
GPRS_RLCMAC_FINISHED, /* flow finished, wait for release */
|
||||
GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
|
||||
GPRS_RLCMAC_RELEASING, /* releasing, wait to free TBI/USF */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_poll_state {
|
||||
GPRS_RLCMAC_POLL_NONE = 0,
|
||||
GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_dl_ass_state {
|
||||
GPRS_RLCMAC_DL_ASS_NONE = 0,
|
||||
GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
|
||||
GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_ul_ass_state {
|
||||
GPRS_RLCMAC_UL_ASS_NONE = 0,
|
||||
GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
|
||||
GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_ul_ack_state {
|
||||
GPRS_RLCMAC_UL_ACK_NONE = 0,
|
||||
GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
|
||||
GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_direction {
|
||||
GPRS_RLCMAC_DL_TBF,
|
||||
GPRS_RLCMAC_UL_TBF
|
||||
};
|
||||
|
||||
#define GPRS_RLCMAC_FLAG_CCCH 0 /* assignment on CCCH */
|
||||
#define GPRS_RLCMAC_FLAG_PACCH 1 /* assignment on PACCH */
|
||||
#define GPRS_RLCMAC_FLAG_UL_DATA 2 /* uplink data received */
|
||||
#define GPRS_RLCMAC_FLAG_DL_ACK 3 /* downlink acknowledge received */
|
||||
#define GPRS_RLCMAC_FLAG_TO_UL_ACK 4
|
||||
#define GPRS_RLCMAC_FLAG_TO_DL_ACK 5
|
||||
#define GPRS_RLCMAC_FLAG_TO_UL_ASS 6
|
||||
#define GPRS_RLCMAC_FLAG_TO_DL_ASS 7
|
||||
#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
|
||||
|
||||
struct gprs_rlcmac_tbf {
|
||||
struct llist_head list;
|
||||
enum gprs_rlcmac_tbf_state state;
|
||||
uint32_t state_flags;
|
||||
enum gprs_rlcmac_tbf_direction direction;
|
||||
uint8_t tfi;
|
||||
uint32_t tlli;
|
||||
uint8_t tlli_valid;
|
||||
uint8_t trx;
|
||||
uint16_t arfcn;
|
||||
uint8_t tsc;
|
||||
uint8_t first_ts; /* first TS used by TBF */
|
||||
uint8_t first_common_ts; /* first TS that the phone can send and
|
||||
reveive simultaniously */
|
||||
uint8_t control_ts; /* timeslot control messages and polling */
|
||||
uint8_t ms_class;
|
||||
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
|
||||
uint16_t ta;
|
||||
uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
|
||||
uint16_t llc_index; /* current write/read position of frame */
|
||||
uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
|
||||
struct llist_head llc_queue; /* queued LLC DL data */
|
||||
|
||||
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
|
||||
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
|
||||
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
|
||||
|
||||
enum gprs_rlcmac_tbf_poll_state poll_state;
|
||||
uint32_t poll_fn; /* frame number to poll */
|
||||
|
||||
uint16_t ws; /* window size */
|
||||
uint16_t sns; /* sequence number space */
|
||||
|
||||
/* Please note that all variables here will be reset when changing
|
||||
* from WAIT RELEASE back to FLOW state (re-use of TBF).
|
||||
* All states that need reset must be in this struct, so this is why
|
||||
* variables are in both (dl and ul) structs and not outside union.
|
||||
*/
|
||||
union {
|
||||
struct {
|
||||
uint16_t bsn; /* block sequence number */
|
||||
uint16_t v_s; /* send state */
|
||||
uint16_t v_a; /* ack state */
|
||||
char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
|
||||
int32_t tx_counter; /* count all transmitted blocks */
|
||||
char imsi[16]; /* store IMSI for PCH retransmission */
|
||||
uint8_t wait_confirm; /* wait for CCCH IMM.ASS cnf */
|
||||
} dl;
|
||||
struct {
|
||||
uint16_t bsn; /* block sequence number */
|
||||
uint16_t v_r; /* receive state */
|
||||
uint16_t v_q; /* receive window state */
|
||||
char v_n[RLC_MAX_SNS/2]; /* receive state array */
|
||||
int32_t rx_counter; /* count all received blocks */
|
||||
uint8_t n3103; /* N3103 counter */
|
||||
uint8_t usf[8]; /* list USFs per PDCH (timeslot) */
|
||||
uint8_t contention_resolution_done; /* set after done */
|
||||
uint8_t final_ack_sent; /* set if we sent final ack */
|
||||
} ul;
|
||||
} dir;
|
||||
uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
|
||||
uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len of history */
|
||||
|
||||
uint8_t n3105; /* N3105 counter */
|
||||
|
||||
struct osmo_timer_list timer;
|
||||
unsigned int T; /* Txxxx number */
|
||||
unsigned int num_T_exp; /* number of consecutive T expirations */
|
||||
|
||||
struct osmo_gsm_timer_list gsm_timer;
|
||||
unsigned int fT; /* fTxxxx number */
|
||||
unsigned int num_fT_exp; /* number of consecutive fT expirations */
|
||||
|
||||
struct {
|
||||
char imsi[16];
|
||||
|
||||
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
|
||||
uint32_t dl_bw_octets; /* number of octets since bw_tv */
|
||||
|
||||
struct timeval rssi_tv; /* timestamp for rssi calculation */
|
||||
int32_t rssi_sum; /* sum of rssi values */
|
||||
int rssi_num; /* number of rssi values added since rssi_tv */
|
||||
|
||||
struct timeval dl_loss_tv; /* timestamp for loss calculation */
|
||||
uint16_t dl_loss_lost; /* sum of lost packets */
|
||||
uint16_t dl_loss_received; /* sum of received packets */
|
||||
|
||||
} meas;
|
||||
|
||||
uint8_t cs; /* current coding scheme */
|
||||
|
||||
#ifdef DEBUG_DIAGRAM
|
||||
int diag; /* number where TBF is presented in diagram */
|
||||
int diag_new; /* used to format output of new TBF */
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
|
||||
extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
|
||||
extern struct llist_head gprs_rlcmac_sbas; /* list of single block allocs */
|
||||
|
||||
/*
|
||||
* paging entry
|
||||
*/
|
||||
@@ -263,17 +54,6 @@ struct gprs_rlcmac_paging {
|
||||
uint8_t identity_lv[9];
|
||||
};
|
||||
|
||||
/*
|
||||
* single block allocation entry
|
||||
*/
|
||||
struct gprs_rlcmac_sba {
|
||||
struct llist_head list;
|
||||
uint8_t trx;
|
||||
uint8_t ts;
|
||||
uint32_t fn;
|
||||
uint8_t ta;
|
||||
};
|
||||
|
||||
/*
|
||||
* coding scheme info
|
||||
*/
|
||||
@@ -283,18 +63,10 @@ struct gprs_rlcmac_cs {
|
||||
uint8_t block_payload;
|
||||
};
|
||||
|
||||
extern struct gprs_rlcmac_cs gprs_rlcmac_cs[];
|
||||
|
||||
#ifdef DEBUG_DIAGRAM
|
||||
void debug_diagram(int diag, const char *format, ...);
|
||||
#else
|
||||
#define debug_diagram(a, b, args...) ;
|
||||
#endif
|
||||
|
||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
|
||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||
uint16_t lost);
|
||||
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf);
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
|
||||
|
||||
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr);
|
||||
|
||||
@@ -302,40 +74,7 @@ int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
|
||||
|
||||
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets);
|
||||
|
||||
int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
|
||||
|
||||
struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn);
|
||||
|
||||
int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx,
|
||||
int8_t use_trx);
|
||||
|
||||
struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_tbf *old_tbf,
|
||||
enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
|
||||
uint8_t ms_class, uint8_t single_slot);
|
||||
|
||||
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx,
|
||||
enum gprs_rlcmac_tbf_direction dir);
|
||||
|
||||
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
|
||||
enum gprs_rlcmac_tbf_direction dir);
|
||||
|
||||
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
|
||||
|
||||
void tbf_free(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
int tbf_update(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
|
||||
enum gprs_rlcmac_tbf_state state);
|
||||
|
||||
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
|
||||
unsigned int seconds, unsigned int microseconds);
|
||||
|
||||
void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf);
|
||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
|
||||
|
||||
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
|
||||
enum gprs_rlcmac_block_type {
|
||||
@@ -345,97 +84,34 @@ enum gprs_rlcmac_block_type {
|
||||
GPRS_RLCMAC_RESERVED = 0x3
|
||||
};
|
||||
|
||||
int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
|
||||
uint32_t fn, int8_t rssi);
|
||||
|
||||
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
|
||||
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
|
||||
uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
|
||||
int8_t ta_idx);
|
||||
|
||||
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
|
||||
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
|
||||
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
|
||||
uint8_t gamma, int8_t ta_idx);
|
||||
|
||||
void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
|
||||
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts);
|
||||
|
||||
|
||||
|
||||
void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
|
||||
uint8_t final);
|
||||
|
||||
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
|
||||
|
||||
void tbf_timer_cb(void *_tbf);
|
||||
|
||||
int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
int gprs_rlcmac_sba_timeout(struct gprs_rlcmac_sba *sba);
|
||||
|
||||
int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
|
||||
|
||||
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
|
||||
uint32_t fn);
|
||||
|
||||
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
|
||||
|
||||
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
|
||||
|
||||
void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf,
|
||||
struct gprs_rlcmac_tbf *old_tbf, char *imsi);
|
||||
|
||||
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
|
||||
uint8_t ssn, uint8_t *rbb);
|
||||
|
||||
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
|
||||
const char *imsi);
|
||||
|
||||
unsigned write_packet_paging_request(bitvec * dest);
|
||||
|
||||
unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
|
||||
uint8_t *identity, uint8_t chan_needed);
|
||||
|
||||
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
|
||||
uint8_t *data, uint8_t len, int8_t rssi);
|
||||
|
||||
struct msgb *gprs_rlcmac_send_data_block_acknowledged(
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
|
||||
|
||||
struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
|
||||
uint32_t fn);
|
||||
|
||||
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
|
||||
int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn);
|
||||
|
||||
int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv);
|
||||
|
||||
struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
|
||||
struct gprs_rlcmac_pdch *pdch);
|
||||
|
||||
struct msgb *gprs_rlcmac_send_packet_paging_request(
|
||||
struct gprs_rlcmac_pdch *pdch);
|
||||
|
||||
int remember_timing_advance(uint32_t tlli, uint8_t ta);
|
||||
|
||||
int recall_timing_advance(uint32_t tlli);
|
||||
|
||||
int flush_timing_advance(void);
|
||||
int gprs_alloc_max_dl_slots_per_ms(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t ms_class = 0);
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
|
||||
struct GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
|
||||
int use_trx);
|
||||
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
|
||||
struct GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
|
||||
int use_trx);
|
||||
|
||||
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts,
|
||||
struct GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
|
||||
int use_trx);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <tbf.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
@@ -27,6 +28,7 @@
|
||||
/*
|
||||
* downlink measurement
|
||||
*/
|
||||
#warning "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)
|
||||
@@ -99,7 +101,7 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
|
||||
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);
|
||||
tbf->tlli(), tbf->meas.rssi_sum / tbf->meas.rssi_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -110,10 +112,10 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
|
||||
*/
|
||||
|
||||
/* Lost frames reported from RLCMAC layer */
|
||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
|
||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||
uint16_t lost)
|
||||
{
|
||||
struct timeval now_tv, *loss_tv = &tbf->meas.dl_loss_tv;
|
||||
struct timeval now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv;
|
||||
uint32_t elapsed;
|
||||
uint16_t sum = received + lost;
|
||||
|
||||
@@ -122,10 +124,10 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
|
||||
return -EINVAL;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d "
|
||||
"Lost: %4d Sum: %4d\n", tbf->tlli, received, lost, sum);
|
||||
"Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum);
|
||||
|
||||
tbf->meas.dl_loss_received += received;
|
||||
tbf->meas.dl_loss_lost += lost;
|
||||
tbf->m_bw.dl_loss_received += received;
|
||||
tbf->m_bw.dl_loss_lost += lost;
|
||||
|
||||
gettimeofday(&now_tv, NULL);
|
||||
elapsed = ((now_tv.tv_sec - loss_tv->tv_sec) << 7)
|
||||
@@ -137,24 +139,24 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
|
||||
|
||||
/* reset lost values and timestamp */
|
||||
memcpy(loss_tv, &now_tv, sizeof(struct timeval));
|
||||
tbf->meas.dl_loss_received = 0;
|
||||
tbf->meas.dl_loss_lost = 0;
|
||||
tbf->m_bw.dl_loss_received = 0;
|
||||
tbf->m_bw.dl_loss_lost = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Give Lost report */
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf)
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
|
||||
{
|
||||
uint16_t sum = tbf->meas.dl_loss_lost + tbf->meas.dl_loss_received;
|
||||
uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received;
|
||||
|
||||
/* No measurement values */
|
||||
if (!sum)
|
||||
return -EINVAL;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
|
||||
"%d%%\n", tbf->meas.imsi, tbf->tlli,
|
||||
tbf->meas.dl_loss_lost * 100 / sum);
|
||||
"%d%%\n", tbf->imsi(), tbf->tlli(),
|
||||
tbf->m_bw.dl_loss_lost * 100 / sum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -164,12 +166,12 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf)
|
||||
* downlink bandwidth
|
||||
*/
|
||||
|
||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets)
|
||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
|
||||
{
|
||||
struct timeval now_tv, *bw_tv = &tbf->meas.dl_bw_tv;
|
||||
struct timeval now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv;
|
||||
uint32_t elapsed;
|
||||
|
||||
tbf->meas.dl_bw_octets += octets;
|
||||
tbf->m_bw.dl_bw_octets += octets;
|
||||
|
||||
gettimeofday(&now_tv, NULL);
|
||||
elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7)
|
||||
@@ -178,12 +180,12 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets)
|
||||
return 0;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
|
||||
"%d KBits/s\n", tbf->meas.imsi, tbf->tlli,
|
||||
tbf->meas.dl_bw_octets / elapsed);
|
||||
"%d KBits/s\n", tbf->imsi(), tbf->tlli(),
|
||||
tbf->m_bw.dl_bw_octets / elapsed);
|
||||
|
||||
/* reset bandwidth values timestamp */
|
||||
memcpy(bw_tv, &now_tv, sizeof(struct timeval));
|
||||
tbf->meas.dl_bw_octets = 0;
|
||||
tbf->m_bw.dl_bw_octets = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,14 +20,21 @@
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
|
||||
uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
|
||||
#include "pcu_utils.h"
|
||||
|
||||
static uint32_t sched_poll(struct gprs_rlcmac_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_tbf **ul_ack_tbf)
|
||||
struct gprs_rlcmac_ul_tbf **ul_ack_tbf)
|
||||
{
|
||||
struct gprs_rlcmac_tbf *tbf;
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
struct llist_pods *lpods;
|
||||
uint32_t poll_fn;
|
||||
|
||||
/* check special TBF for events */
|
||||
@@ -35,83 +42,67 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
|
||||
if ((block_nr % 3) == 2)
|
||||
poll_fn ++;
|
||||
poll_fn = poll_fn % 2715648;
|
||||
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
|
||||
llist_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
|
||||
/* this trx, this ts */
|
||||
if (tbf->trx != trx || tbf->control_ts != ts)
|
||||
if (ul_tbf->trx->trx_no != trx || ul_tbf->control_ts != ts)
|
||||
continue;
|
||||
/* polling for next uplink block */
|
||||
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
|
||||
&& tbf->poll_fn == poll_fn)
|
||||
*poll_tbf = tbf;
|
||||
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
|
||||
*ul_ack_tbf = tbf;
|
||||
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
||||
*dl_ass_tbf = tbf;
|
||||
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
*ul_ass_tbf = tbf;
|
||||
if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
|
||||
&& ul_tbf->poll_fn == poll_fn)
|
||||
*poll_tbf = ul_tbf;
|
||||
if (ul_tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
|
||||
*ul_ack_tbf = ul_tbf;
|
||||
if (ul_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
||||
*dl_ass_tbf = ul_tbf;
|
||||
if (ul_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
*ul_ass_tbf = ul_tbf;
|
||||
#warning "Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all states?"
|
||||
}
|
||||
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
|
||||
llist_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
|
||||
/* this trx, this ts */
|
||||
if (tbf->trx != trx || tbf->control_ts != ts)
|
||||
if (dl_tbf->trx->trx_no != trx || dl_tbf->control_ts != ts)
|
||||
continue;
|
||||
/* polling for next uplink block */
|
||||
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
|
||||
&& tbf->poll_fn == poll_fn)
|
||||
*poll_tbf = tbf;
|
||||
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
||||
*dl_ass_tbf = tbf;
|
||||
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
*ul_ass_tbf = tbf;
|
||||
if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
|
||||
&& dl_tbf->poll_fn == poll_fn)
|
||||
*poll_tbf = dl_tbf;
|
||||
if (dl_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
||||
*dl_ass_tbf = dl_tbf;
|
||||
if (dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
*ul_ass_tbf = dl_tbf;
|
||||
}
|
||||
|
||||
return poll_fn;
|
||||
}
|
||||
|
||||
uint32_t sched_sba(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
uint32_t sba_fn;
|
||||
struct gprs_rlcmac_sba *sba;
|
||||
|
||||
/* check special TBF for events */
|
||||
sba_fn = fn + 4;
|
||||
if ((block_nr % 3) == 2)
|
||||
sba_fn ++;
|
||||
sba_fn = sba_fn % 2715648;
|
||||
sba = sba_find(trx, ts, sba_fn);
|
||||
if (sba)
|
||||
return sba_fn;
|
||||
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t 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)
|
||||
{
|
||||
struct gprs_rlcmac_tbf *tbf;
|
||||
struct gprs_rlcmac_ul_tbf *tbf;
|
||||
uint8_t usf = 0x07;
|
||||
uint8_t i, tfi;
|
||||
|
||||
/* select uplink ressource */
|
||||
/* select uplink resource */
|
||||
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
|
||||
i++, tfi = (tfi + 1) & 31) {
|
||||
tbf = pdch->ul_tbf[tfi];
|
||||
tbf = pdch->ul_tbf_by_tfi(tfi);
|
||||
/* no TBF for this tfi, go next */
|
||||
if (!tbf)
|
||||
continue;
|
||||
/* no UL ressources needed, go next */
|
||||
/* we don't need to give ressources in FINISHED state,
|
||||
/* no UL resources needed, go next */
|
||||
/* we don't need to give resources in FINISHED state,
|
||||
* because we have received all blocks and only poll
|
||||
* for packet control ack. */
|
||||
if (tbf->state != GPRS_RLCMAC_FLOW)
|
||||
if (tbf->state_is_not(GPRS_RLCMAC_FLOW))
|
||||
continue;
|
||||
|
||||
/* use this USF */
|
||||
usf = tbf->dir.ul.usf[ts];
|
||||
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 ressource of UL TBF=%d\n", trx, ts, fn,
|
||||
"required uplink resource of UL TFI=%d\n", trx, ts, fn,
|
||||
block_nr, usf, tfi);
|
||||
/* next TBF to handle ressource is the next one */
|
||||
/* next TBF to handle resource is the next one */
|
||||
pdch->next_ul_tfi = (tfi + 1) & 31;
|
||||
break;
|
||||
}
|
||||
@@ -119,41 +110,51 @@ uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
return usf;
|
||||
}
|
||||
|
||||
struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
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_tbf *ul_ack_tbf)
|
||||
struct gprs_rlcmac_ul_tbf *ul_ack_tbf)
|
||||
{
|
||||
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 };
|
||||
|
||||
/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
|
||||
if (ul_ass_tbf) {
|
||||
tbf = ul_ass_tbf;
|
||||
msg = gprs_rlcmac_send_packet_uplink_assignment(tbf, fn);
|
||||
}
|
||||
/* schedule PACKET DOWNLINK ASSIGNMENT (2nd priotiry) */
|
||||
if (!msg && dl_ass_tbf) {
|
||||
tbf = dl_ass_tbf;
|
||||
msg = gprs_rlcmac_send_packet_downlink_assignment(tbf, fn);
|
||||
}
|
||||
/* schedule PACKET UPLINK ACK (3rd priority) */
|
||||
if (!msg && ul_ack_tbf) {
|
||||
tbf = ul_ack_tbf;
|
||||
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
|
||||
tbf = next_list[(pdch->next_ctrl_prio + i) % 3];
|
||||
if (!tbf)
|
||||
continue;
|
||||
|
||||
if (tbf == ul_ass_tbf)
|
||||
msg = ul_ass_tbf->create_ul_ass(fn);
|
||||
else if (tbf == dl_ass_tbf)
|
||||
msg = dl_ass_tbf->create_dl_ass(fn);
|
||||
else if (tbf == ul_ack_tbf)
|
||||
msg = ul_ack_tbf->create_ul_ack(fn);
|
||||
else
|
||||
abort();
|
||||
|
||||
if (!msg) {
|
||||
tbf = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
pdch->next_ctrl_prio += i + 1;
|
||||
pdch->next_ctrl_prio %= 3;
|
||||
break;
|
||||
}
|
||||
|
||||
/* any message */
|
||||
if (msg) {
|
||||
tbf->rotate_in_list();
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
|
||||
"message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n",
|
||||
(tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
? "UL" : "DL", tbf->tfi, trx, ts);
|
||||
"message at RTS for %s (TRX=%d, TS=%d)\n",
|
||||
tbf_name(tbf), trx, ts);
|
||||
return msg;
|
||||
}
|
||||
/* schedule PACKET PAGING REQUEST */
|
||||
if (!llist_empty(&pdch->paging_list))
|
||||
msg = gprs_rlcmac_send_packet_paging_request(pdch);
|
||||
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);
|
||||
@@ -163,45 +164,88 @@ struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
|
||||
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)
|
||||
{
|
||||
struct msgb *msg = NULL;
|
||||
struct gprs_rlcmac_tbf *tbf = NULL;
|
||||
uint8_t i, tfi;
|
||||
struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
|
||||
enum {
|
||||
DL_PRIO_NONE,
|
||||
DL_PRIO_SENT_DATA, /* the data has been sent and not (yet) nacked */
|
||||
DL_PRIO_LOW_AGE, /* the age has reached the first threshold */
|
||||
DL_PRIO_NEW_DATA, /* the data has not been sent yet or nacked */
|
||||
DL_PRIO_HIGH_AGE, /* the age has reached the second threshold */
|
||||
DL_PRIO_CONTROL, /* a control block needs to be sent */
|
||||
} prio, max_prio = DL_PRIO_NONE;
|
||||
|
||||
/* select downlink ressource */
|
||||
uint8_t i, tfi, prio_tfi;
|
||||
int age;
|
||||
const int age_thresh1 = msecs_to_frames(200);
|
||||
const int high_prio_msecs =
|
||||
OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec);
|
||||
const int age_thresh2 = msecs_to_frames(high_prio_msecs);
|
||||
|
||||
/* select downlink resource */
|
||||
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
|
||||
i++, tfi = (tfi + 1) & 31) {
|
||||
tbf = pdch->dl_tbf[tfi];
|
||||
tbf = pdch->dl_tbf_by_tfi(tfi);
|
||||
/* no TBF for this tfi, go next */
|
||||
if (!tbf)
|
||||
continue;
|
||||
/* no DL TBF, go next */
|
||||
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
|
||||
continue;
|
||||
/* no DL ressources needed, go next */
|
||||
if (tbf->state != GPRS_RLCMAC_FLOW
|
||||
&& tbf->state != GPRS_RLCMAC_FINISHED)
|
||||
/* no DL resources needed, go next */
|
||||
if (tbf->state_is_not(GPRS_RLCMAC_FLOW)
|
||||
&& tbf->state_is_not(GPRS_RLCMAC_FINISHED))
|
||||
continue;
|
||||
|
||||
/* waiting for CCCH IMM.ASS confirm */
|
||||
if (tbf->dir.dl.wait_confirm)
|
||||
if (tbf->m_wait_confirm)
|
||||
continue;
|
||||
|
||||
age = tbf->frames_since_last_poll(fn);
|
||||
|
||||
/* compute priority */
|
||||
if (tbf->is_control_ts(ts) && tbf->need_control_ts())
|
||||
prio = DL_PRIO_CONTROL;
|
||||
else if (tbf->is_control_ts(ts) &&
|
||||
age > age_thresh2 && age_thresh2 > 0)
|
||||
prio = DL_PRIO_HIGH_AGE;
|
||||
else if ((tbf->state_is(GPRS_RLCMAC_FLOW) && tbf->have_data()) ||
|
||||
tbf->m_window.resend_needed() >= 0)
|
||||
prio = DL_PRIO_NEW_DATA;
|
||||
else if (tbf->is_control_ts(ts) &&
|
||||
age > age_thresh1 && tbf->keep_open(fn))
|
||||
prio = DL_PRIO_LOW_AGE;
|
||||
else if (!tbf->m_window.window_empty())
|
||||
prio = DL_PRIO_SENT_DATA;
|
||||
else
|
||||
continue;
|
||||
|
||||
/* get the TBF with the highest priority */
|
||||
if (prio > max_prio) {
|
||||
prio_tfi = tfi;
|
||||
prio_tbf = tbf;
|
||||
max_prio = prio;
|
||||
}
|
||||
}
|
||||
|
||||
if (prio_tbf) {
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
|
||||
"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
|
||||
/* next TBF to handle ressource is the next one */
|
||||
pdch->next_dl_tfi = (tfi + 1) & 31;
|
||||
"RTS for DL TFI=%d (TRX=%d, TS=%d) prio=%d\n",
|
||||
prio_tfi, trx, ts, max_prio);
|
||||
/* next TBF to handle resource is the next one */
|
||||
pdch->next_dl_tfi = (prio_tfi + 1) & 31;
|
||||
/* generate DL data block */
|
||||
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
|
||||
ts);
|
||||
break;
|
||||
msg = prio_tbf->create_dl_acked_block(fn, ts);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
static uint8_t rlcmac_dl_idle[23] = {
|
||||
|
||||
static const uint8_t rlcmac_dl_idle[23] = {
|
||||
0x47, /* control without optional header octets, no polling, USF=111 */
|
||||
0x94, /* dummy downlink control message, paging mode 00 */
|
||||
0x2b, /* no persistance level, 7 bits spare pattern */
|
||||
@@ -209,7 +253,7 @@ static uint8_t rlcmac_dl_idle[23] = {
|
||||
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
|
||||
};
|
||||
|
||||
struct msgb *sched_dummy(void)
|
||||
static struct msgb *sched_dummy(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
@@ -221,22 +265,25 @@ struct msgb *sched_dummy(void)
|
||||
return msg;
|
||||
}
|
||||
|
||||
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
|
||||
*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
|
||||
*ul_ass_tbf = NULL;
|
||||
struct gprs_rlcmac_ul_tbf *ul_ack_tbf = NULL;
|
||||
uint8_t usf = 0x7;
|
||||
struct msgb *msg = NULL;
|
||||
uint32_t poll_fn, sba_fn;
|
||||
|
||||
#warning "ARFCN... it is already in the TRX..... is it consistent with it?"
|
||||
|
||||
if (trx >= 8 || ts >= 8)
|
||||
return -EINVAL;
|
||||
pdch = &bts->trx[trx].pdch[ts];
|
||||
|
||||
if (!pdch->enable) {
|
||||
if (!pdch->is_enabled()) {
|
||||
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
|
||||
"TRX=%d TS=%d\n", trx, ts);
|
||||
return -EIO;
|
||||
@@ -245,25 +292,24 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
/* store last frame number of RTS */
|
||||
pdch->last_rts_fn = fn;
|
||||
|
||||
poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
|
||||
poll_fn = sched_poll(bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
|
||||
&dl_ass_tbf, &ul_ack_tbf);
|
||||
/* check uplink ressource for polling */
|
||||
/* 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 TFI=%d\n", trx, ts, fn,
|
||||
"polling at FN=%d of %s\n", trx, ts, fn,
|
||||
block_nr, poll_fn,
|
||||
(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
? "UL" : "DL", poll_tbf->tfi);
|
||||
tbf_name(poll_tbf));
|
||||
/* use free USF */
|
||||
/* else. check for sba */
|
||||
else if ((sba_fn = sched_sba(trx, ts, fn, block_nr) != 0xffffffff))
|
||||
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 ressource */
|
||||
/* else, we search for uplink resource */
|
||||
else
|
||||
usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
|
||||
|
||||
@@ -273,7 +319,7 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
|
||||
/* Prio 2: select data message for downlink */
|
||||
if (!msg)
|
||||
msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
|
||||
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
|
||||
|
||||
/* Prio 3: send dummy contol message */
|
||||
if (!msg)
|
||||
|
||||
1100
src/gprs_rlcmac_ts_alloc.cpp
Normal file
1100
src/gprs_rlcmac_ts_alloc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <assert.h>
|
||||
#include <gprs_debug.h>
|
||||
using namespace std;
|
||||
|
||||
@@ -5436,6 +5437,7 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
|
||||
}
|
||||
unsigned dataLen = 23 - readIndex/8;
|
||||
LOGPC(DRLCMACDATA, LOGL_NOTICE, "DATA[%u] = ", dataLen);
|
||||
assert(dataLen <= 20);
|
||||
for (unsigned i = 0; i < dataLen; i++)
|
||||
{
|
||||
data->RLC_DATA[i] = bitvec_read_field(vector, readIndex, 8);
|
||||
@@ -5498,6 +5500,7 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
|
||||
}
|
||||
unsigned dataNumOctets = 23 - writeIndex/8;
|
||||
LOGPC(DRLCMACDATA, LOGL_NOTICE, "DATA[%u] = ", dataNumOctets);
|
||||
assert(dataNumOctets <= 20);
|
||||
for (unsigned i = 0; i < dataNumOctets; i++)
|
||||
{
|
||||
bitvec_write_field(vector, writeIndex, data->RLC_DATA[i], 8);
|
||||
|
||||
@@ -33,9 +33,21 @@ static int *nearest_p;
|
||||
#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.
|
||||
*/
|
||||
static 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);
|
||||
|
||||
253
src/llc.cpp
Normal file
253
src/llc.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/* Copied from tbf.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <tbf.h>
|
||||
#include <bts.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
}
|
||||
|
||||
/* reset LLC frame */
|
||||
void gprs_llc::reset()
|
||||
{
|
||||
m_index = 0;
|
||||
m_length = 0;
|
||||
|
||||
memset(frame, 0x42, sizeof(frame));
|
||||
}
|
||||
|
||||
void gprs_llc::reset_frame_space()
|
||||
{
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
|
||||
void gprs_llc::put_dummy_frame(size_t req_len)
|
||||
{
|
||||
/* The shortest dummy command (the spec requests at least 6 octets) */
|
||||
static const uint8_t llc_dummy_command[] = {
|
||||
0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
|
||||
};
|
||||
static const size_t max_dummy_command_len = 79;
|
||||
|
||||
put_frame(llc_dummy_command, sizeof(llc_dummy_command));
|
||||
|
||||
if (req_len > max_dummy_command_len)
|
||||
req_len = max_dummy_command_len;
|
||||
|
||||
/* Add further stuffing, if the requested length exceeds the minimum
|
||||
* dummy command length */
|
||||
while (m_length < req_len)
|
||||
frame[m_length++] = 0x2b;
|
||||
}
|
||||
|
||||
void gprs_llc::put_frame(const uint8_t *data, size_t len)
|
||||
{
|
||||
/* only put frames when we are empty */
|
||||
OSMO_ASSERT(m_index == 0 && m_length == 0);
|
||||
append_frame(data, len);
|
||||
}
|
||||
|
||||
void gprs_llc::append_frame(const uint8_t *data, size_t len)
|
||||
{
|
||||
/* TODO: bounds check */
|
||||
memcpy(frame + m_length, data, len);
|
||||
m_length += len;
|
||||
}
|
||||
|
||||
void gprs_llc::init()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
|
||||
{
|
||||
if (len < 2)
|
||||
return false;
|
||||
|
||||
if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
|
||||
return false;
|
||||
|
||||
if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
|
||||
/* It is not an LLC UI frame, see TS 44.064, 6.3 */
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gprs_llc_queue::init()
|
||||
{
|
||||
INIT_LLIST_HEAD(&m_queue);
|
||||
m_queue_size = 0;
|
||||
m_queue_octets = 0;
|
||||
m_avg_queue_delay = 0;
|
||||
}
|
||||
|
||||
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const MetaInfo *info)
|
||||
{
|
||||
static const MetaInfo def_meta = {{0}};
|
||||
MetaInfo *meta_storage;
|
||||
|
||||
osmo_static_assert(sizeof(*info) <= 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;
|
||||
|
||||
msgb_enqueue(&m_queue, llc_msg);
|
||||
}
|
||||
|
||||
void gprs_llc_queue::clear(BTS *bts)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
while ((msg = msgb_dequeue(&m_queue))) {
|
||||
if (bts)
|
||||
bts->llc_dropped_frame();
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
m_queue_size = 0;
|
||||
m_queue_octets = 0;
|
||||
}
|
||||
|
||||
void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
|
||||
{
|
||||
struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
|
||||
struct llist_head new_queue;
|
||||
size_t queue_size = 0;
|
||||
size_t queue_octets = 0;
|
||||
INIT_LLIST_HEAD(&new_queue);
|
||||
|
||||
while (1) {
|
||||
if (msg1 == NULL)
|
||||
msg1 = msgb_dequeue(&m_queue);
|
||||
|
||||
if (msg2 == NULL)
|
||||
msg2 = msgb_dequeue(&o->m_queue);
|
||||
|
||||
if (msg1 == NULL && msg2 == NULL)
|
||||
break;
|
||||
|
||||
if (msg1 == NULL) {
|
||||
msg = msg2;
|
||||
msg2 = NULL;
|
||||
} else if (msg2 == NULL) {
|
||||
msg = msg1;
|
||||
msg1 = NULL;
|
||||
} else {
|
||||
const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
|
||||
const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
|
||||
|
||||
if (timercmp(&mi2->recv_time, &mi1->recv_time, >)) {
|
||||
msg = msg1;
|
||||
msg1 = NULL;
|
||||
} else {
|
||||
msg = msg2;
|
||||
msg2 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
msgb_enqueue(&new_queue, msg);
|
||||
queue_size += 1;
|
||||
queue_octets += msgb_length(msg);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(llist_empty(&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;
|
||||
}
|
||||
|
||||
#define ALPHA 0.5f
|
||||
|
||||
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct timeval *tv, tv_now, tv_result;
|
||||
uint32_t lifetime;
|
||||
const MetaInfo *meta_storage;
|
||||
|
||||
msg = msgb_dequeue(&m_queue);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
meta_storage = (MetaInfo *)&msg->cb[0];
|
||||
|
||||
if (info)
|
||||
*info = meta_storage;
|
||||
|
||||
m_queue_size -= 1;
|
||||
m_queue_octets -= msgb_length(msg);
|
||||
|
||||
/* take the second time */
|
||||
gettimeofday(&tv_now, NULL);
|
||||
tv = (struct timeval *)&msg->data[sizeof(*tv)];
|
||||
timersub(&tv_now, &meta_storage->recv_time, &tv_result);
|
||||
|
||||
lifetime = tv_result.tv_sec*1000 + tv_result.tv_usec/1000;
|
||||
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)
|
||||
{
|
||||
uint16_t delay_csec;
|
||||
if (bts->bts_data()->force_llc_lifetime)
|
||||
delay_csec = bts->bts_data()->force_llc_lifetime;
|
||||
else
|
||||
delay_csec = pdu_delay_csec;
|
||||
|
||||
/* keep timestamp at 0 for infinite delay */
|
||||
if (delay_csec == 0xffff) {
|
||||
memset(tv, 0, sizeof(*tv));
|
||||
return;
|
||||
}
|
||||
|
||||
/* calculate timestamp of timeout */
|
||||
struct timeval now, csec;
|
||||
gettimeofday(&now, NULL);
|
||||
csec.tv_usec = (delay_csec % 100) * 10000;
|
||||
csec.tv_sec = delay_csec / 100;
|
||||
|
||||
timeradd(&now, &csec, tv);
|
||||
}
|
||||
|
||||
bool gprs_llc_queue::is_frame_expired(const struct timeval *tv_now,
|
||||
const struct timeval *tv)
|
||||
{
|
||||
/* Timeout is infinite */
|
||||
if (tv->tv_sec == 0 && tv->tv_usec == 0)
|
||||
return false;
|
||||
|
||||
return timercmp(tv_now, tv, >);
|
||||
}
|
||||
136
src/llc.h
Normal file
136
src/llc.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
}
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define LLC_MAX_LEN 1543
|
||||
|
||||
struct BTS;
|
||||
struct timeval;
|
||||
struct msgb;
|
||||
|
||||
/**
|
||||
* I represent the LLC data to a MS
|
||||
*/
|
||||
struct gprs_llc {
|
||||
static bool is_user_data_frame(uint8_t *data, size_t len);
|
||||
|
||||
void init();
|
||||
void reset();
|
||||
void reset_frame_space();
|
||||
|
||||
void put_frame(const uint8_t *data, size_t len);
|
||||
void put_dummy_frame(size_t req_len);
|
||||
void append_frame(const uint8_t *data, size_t len);
|
||||
|
||||
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;
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
static bool is_user_data_frame(uint8_t *data, size_t len);
|
||||
|
||||
void init();
|
||||
|
||||
void enqueue(struct msgb *llc_msg, const MetaInfo *info = 0);
|
||||
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:
|
||||
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 */
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline uint16_t gprs_llc::chunk_size() const
|
||||
{
|
||||
return m_length - m_index;
|
||||
}
|
||||
|
||||
inline uint16_t gprs_llc::remaining_space() const
|
||||
{
|
||||
return LLC_MAX_LEN - m_length;
|
||||
}
|
||||
|
||||
inline uint16_t gprs_llc::frame_length() const
|
||||
{
|
||||
return m_length;
|
||||
}
|
||||
|
||||
inline void gprs_llc::consume(size_t len)
|
||||
{
|
||||
m_index += len;
|
||||
}
|
||||
|
||||
inline void gprs_llc::consume(uint8_t *data, size_t len)
|
||||
{
|
||||
/* copy and increment index */
|
||||
memcpy(data, frame + m_index, len);
|
||||
consume(len);
|
||||
}
|
||||
|
||||
inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
|
||||
{
|
||||
return m_length + chunk_size <= LLC_MAX_LEN;
|
||||
}
|
||||
|
||||
inline size_t gprs_llc_queue::size() const
|
||||
{
|
||||
return this ? m_queue_size : 0;
|
||||
}
|
||||
|
||||
inline size_t gprs_llc_queue::octets() const
|
||||
{
|
||||
return this ? m_queue_octets : 0;
|
||||
}
|
||||
@@ -79,7 +79,10 @@ struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
|
||||
|
||||
int pcu_sock_send(struct msgb *msg)
|
||||
{
|
||||
osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
|
||||
if (osmo_wqueue_enqueue(&l1fh->udp_wq, msg) != 0) {
|
||||
LOGP(DPCU, LOGL_ERROR, "PCU write queue full. Dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -88,7 +91,6 @@ static int udp_read_cb(struct osmo_fd *ofd)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
|
||||
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
|
||||
struct femtol1_hdl *fl1h = l1fh->fl1h;
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
@@ -183,4 +185,6 @@ void pcu_l1if_close(void)
|
||||
|
||||
/* FIXME: cleanup l1if */
|
||||
talloc_free(l1fh->fl1h);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ extern "C" {
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <pcuif_proto.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
|
||||
// FIXME: move this, when changed from c++ to c.
|
||||
extern "C" {
|
||||
@@ -48,19 +50,6 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
// Variable for storage current FN.
|
||||
int frame_number;
|
||||
|
||||
int get_current_fn()
|
||||
{
|
||||
return frame_number;
|
||||
}
|
||||
|
||||
void set_current_fn(int fn)
|
||||
{
|
||||
frame_number = fn;
|
||||
}
|
||||
|
||||
/*
|
||||
* PCU messages
|
||||
*/
|
||||
@@ -136,7 +125,7 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
if (bts->trx[trx].fl1h)
|
||||
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
|
||||
@@ -152,7 +141,7 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
if (bts->trx[trx].fl1h)
|
||||
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
|
||||
@@ -174,7 +163,7 @@ void pcu_l1if_tx_agch(bitvec * block, int plen)
|
||||
pcu_tx_data_req(0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, 23);
|
||||
}
|
||||
|
||||
void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi)
|
||||
void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi)
|
||||
{
|
||||
uint8_t data[23+3]; /* prefix PLEN */
|
||||
|
||||
@@ -191,21 +180,31 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi)
|
||||
pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
|
||||
}
|
||||
|
||||
// FIXME: remove this, when changed from c++ to c.
|
||||
static void pcu_l1if_tx_bcch(uint8_t *data, int len)
|
||||
extern "C" void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
|
||||
{
|
||||
pcu_tx_data_req(0, 0, PCU_IF_SAPI_BCCH, 0, 0, 0, data, len);
|
||||
BTS::main_bts()->set_current_block_frame_number(fn, 0);
|
||||
}
|
||||
|
||||
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
|
||||
uint8_t len, uint32_t fn, int8_t rssi)
|
||||
extern "C" void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
|
||||
{
|
||||
return gprs_rlcmac_rcv_block(trx, ts, data, len, fn, rssi);
|
||||
/* access bursts may arrive some bursts earlier */
|
||||
BTS::main_bts()->set_current_block_frame_number(fn, 5);
|
||||
}
|
||||
|
||||
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *data,
|
||||
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
|
||||
pdch = &bts_main_data()->trx[trx_no].pdch[ts_no];
|
||||
return pdch->rcv_block(data, len, fn, meas);
|
||||
}
|
||||
|
||||
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
|
||||
{
|
||||
int rc = 0;
|
||||
pcu_l1_meas meas;
|
||||
meas.set_rssi(data_ind->rssi);
|
||||
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
|
||||
"block=%d data=%s\n", data_ind->sapi,
|
||||
@@ -216,7 +215,7 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
|
||||
case PCU_IF_SAPI_PDTCH:
|
||||
rc = pcu_rx_data_ind_pdtch(data_ind->trx_nr, data_ind->ts_nr,
|
||||
data_ind->data, data_ind->len, data_ind->fn,
|
||||
data_ind->rssi);
|
||||
&meas);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
|
||||
@@ -237,8 +236,7 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
|
||||
switch (data_cnf->sapi) {
|
||||
case PCU_IF_SAPI_PCH:
|
||||
if (data_cnf->data[2] == 0x3f)
|
||||
rc = gprs_rlcmac_imm_ass_cnf(data_cnf->data,
|
||||
data_cnf->fn);
|
||||
BTS::main_bts()->rcv_imm_ass_cnf(data_cnf->data, data_cnf->fn);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with "
|
||||
@@ -253,7 +251,8 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
|
||||
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
return gprs_rlcmac_rcv_rts_block(trx, ts, arfcn, fn, block_nr);
|
||||
return gprs_rlcmac_rcv_rts_block(bts_main_data(),
|
||||
trx, ts, arfcn, fn, block_nr);
|
||||
}
|
||||
|
||||
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
|
||||
@@ -297,7 +296,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
|
||||
|
||||
switch (rach_ind->sapi) {
|
||||
case PCU_IF_SAPI_RACH:
|
||||
rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
|
||||
rc = BTS::main_bts()->rcv_rach(
|
||||
rach_ind->ra, rach_ind->fn,
|
||||
rach_ind->qta);
|
||||
break;
|
||||
default:
|
||||
@@ -309,39 +309,10 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int flush_pdch(struct gprs_rlcmac_pdch *pdch, uint8_t trx, uint8_t ts)
|
||||
{
|
||||
uint8_t tfi;
|
||||
struct gprs_rlcmac_tbf *tbf;
|
||||
struct gprs_rlcmac_paging *pag;
|
||||
struct gprs_rlcmac_sba *sba, *sba2;
|
||||
|
||||
/* kick all TBF on slot */
|
||||
for (tfi = 0; tfi < 32; tfi++) {
|
||||
tbf = pdch->ul_tbf[tfi];
|
||||
if (tbf)
|
||||
tbf_free(tbf);
|
||||
tbf = pdch->dl_tbf[tfi];
|
||||
if (tbf)
|
||||
tbf_free(tbf);
|
||||
}
|
||||
/* flush all pending paging messages */
|
||||
while ((pag = gprs_rlcmac_dequeue_paging(pdch)))
|
||||
talloc_free(pag);
|
||||
|
||||
llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) {
|
||||
if (sba->trx == trx && sba->ts == ts) {
|
||||
llist_del(&sba->list);
|
||||
talloc_free(sba);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
struct gprs_bssgp_pcu *pcu;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct in_addr ia;
|
||||
int rc = 0;
|
||||
@@ -363,14 +334,11 @@ bssgp_failed:
|
||||
/* free all TBF */
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
|
||||
for (ts = 0; ts < 8; ts++) {
|
||||
if (bts->trx[trx].pdch[ts].enable)
|
||||
flush_pdch(&bts->trx[trx].pdch[ts],
|
||||
trx, ts);
|
||||
}
|
||||
for (ts = 0; ts < 8; ts++)
|
||||
bts->trx[trx].pdch[ts].free_resources();
|
||||
}
|
||||
gprs_bssgp_destroy();
|
||||
return 0;
|
||||
exit(0);
|
||||
}
|
||||
LOGP(DL1IF, LOGL_INFO, "BTS available\n");
|
||||
LOGP(DL1IF, LOGL_DEBUG, " mcc=%x\n", info_ind->mcc);
|
||||
@@ -424,12 +392,12 @@ bssgp_failed:
|
||||
ia.s_addr = htonl(info_ind->remote_ip[0]);
|
||||
LOGP(DL1IF, LOGL_DEBUG, " remote_ip=%s\n", inet_ntoa(ia));
|
||||
|
||||
rc = gprs_bssgp_create(info_ind->local_port[0],
|
||||
pcu = gprs_bssgp_create_and_connect(bts, info_ind->local_port[0],
|
||||
info_ind->remote_ip[0], info_ind->remote_port[0],
|
||||
info_ind->nsei, info_ind->nsvci[0], info_ind->bvci,
|
||||
info_ind->mcc, info_ind->mnc, info_ind->lac, info_ind->rac,
|
||||
info_ind->cell_id);
|
||||
if (rc < 0) {
|
||||
if (!pcu) {
|
||||
LOGP(DL1IF, LOGL_NOTICE, "SGSN not available\n");
|
||||
goto bssgp_failed;
|
||||
}
|
||||
@@ -486,7 +454,7 @@ bssgp_failed:
|
||||
pdch = &bts->trx[trx].pdch[ts];
|
||||
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
|
||||
/* FIXME: activate dynamically at RLCMAC */
|
||||
if (!pdch->enable) {
|
||||
if (!pdch->is_enabled()) {
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
if ((info_ind->flags &
|
||||
PCU_IF_FLAG_SYSMO))
|
||||
@@ -494,17 +462,16 @@ bssgp_failed:
|
||||
bts->trx[trx].fl1h, ts);
|
||||
#endif
|
||||
pcu_tx_act_req(trx, ts, 1);
|
||||
INIT_LLIST_HEAD(&pdch->paging_list);
|
||||
pdch->enable = 1;
|
||||
pdch->enable();
|
||||
}
|
||||
pdch->tsc = info_ind->trx[trx].tsc[ts];
|
||||
LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
|
||||
trx, ts);
|
||||
} else {
|
||||
if (pdch->enable) {
|
||||
if (pdch->is_enabled()) {
|
||||
pcu_tx_act_req(trx, ts, 0);
|
||||
pdch->enable = 0;
|
||||
flush_pdch(pdch, trx, ts);
|
||||
pdch->free_resources();
|
||||
pdch->disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,9 +482,6 @@ bssgp_failed:
|
||||
|
||||
static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
|
||||
{
|
||||
struct gprs_rlcmac_tbf *tbf;
|
||||
struct gprs_rlcmac_sba *sba, *sba2;
|
||||
uint32_t elapsed;
|
||||
uint8_t fn13 = time_ind->fn % 13;
|
||||
|
||||
/* omit frame numbers not starting at a MAC block */
|
||||
@@ -527,33 +491,7 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
|
||||
// LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
|
||||
// time_ind->fn % 52);
|
||||
|
||||
set_current_fn(time_ind->fn);
|
||||
|
||||
/* check for poll timeout */
|
||||
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
|
||||
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
|
||||
elapsed = (frame_number + 2715648 - tbf->poll_fn)
|
||||
% 2715648;
|
||||
if (elapsed >= 20 && elapsed < 2715400)
|
||||
gprs_rlcmac_poll_timeout(tbf);
|
||||
}
|
||||
}
|
||||
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
|
||||
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
|
||||
elapsed = (frame_number + 2715648 - tbf->poll_fn)
|
||||
% 2715648;
|
||||
if (elapsed >= 20 && elapsed < 2715400)
|
||||
gprs_rlcmac_poll_timeout(tbf);
|
||||
}
|
||||
}
|
||||
llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) {
|
||||
elapsed = (frame_number + 2715648 - sba->fn) % 2715648;
|
||||
if (elapsed >= 20 && elapsed < 2715400) {
|
||||
/* sba will be freed here */
|
||||
gprs_rlcmac_sba_timeout(sba);
|
||||
}
|
||||
}
|
||||
|
||||
BTS::main_bts()->set_current_frame_number(time_ind->fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -562,7 +500,7 @@ static int pcu_rx_pag_req(struct gsm_pcu_if_pag_req *pag_req)
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d "
|
||||
"length=%d\n", pag_req->chan_needed, pag_req->identity_lv[0]);
|
||||
|
||||
return gprs_rlcmac_add_paging(pag_req->chan_needed,
|
||||
return BTS::main_bts()->add_paging(pag_req->chan_needed,
|
||||
pag_req->identity_lv);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,16 +31,88 @@ extern "C" {
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
int get_current_fn();
|
||||
/*
|
||||
* L1 Measurement values
|
||||
*/
|
||||
|
||||
struct pcu_l1_meas_ts {
|
||||
unsigned have_ms_i_level:1;
|
||||
|
||||
int16_t ms_i_level; /* I_LEVEL in dB */
|
||||
|
||||
#ifdef __cplusplus
|
||||
pcu_l1_meas_ts& set_ms_i_level(int16_t v) {
|
||||
ms_i_level = v; have_ms_i_level = 1; return *this;
|
||||
}
|
||||
|
||||
pcu_l1_meas_ts() :
|
||||
have_ms_i_level(0)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pcu_l1_meas {
|
||||
unsigned have_rssi:1;
|
||||
unsigned have_ber:1;
|
||||
unsigned have_bto:1;
|
||||
unsigned have_link_qual:1;
|
||||
unsigned have_ms_rx_qual:1;
|
||||
unsigned have_ms_c_value:1;
|
||||
unsigned have_ms_sign_var:1;
|
||||
unsigned have_ms_i_level:1;
|
||||
|
||||
int8_t rssi; /* RSSI in dBm */
|
||||
uint8_t ber; /* Bit error rate in % */
|
||||
int16_t bto; /* Burst timing offset in quarter bits */
|
||||
int16_t link_qual; /* Link quality in dB */
|
||||
int16_t ms_rx_qual; /* MS RXQUAL value in % */
|
||||
int16_t ms_c_value; /* C value in dB */
|
||||
int16_t ms_sign_var; /* SIGN_VAR in dB */
|
||||
|
||||
struct pcu_l1_meas_ts ts[8];
|
||||
|
||||
#ifdef __cplusplus
|
||||
pcu_l1_meas& set_rssi(int8_t v) { rssi = v; have_rssi = 1; return *this;}
|
||||
pcu_l1_meas& set_ber(uint8_t v) { ber = v; have_ber = 1; return *this;}
|
||||
pcu_l1_meas& set_bto(int16_t v) { bto = v; have_bto = 1; return *this;}
|
||||
pcu_l1_meas& set_link_qual(int16_t v) {
|
||||
link_qual = v; have_link_qual = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_rx_qual(int16_t v) {
|
||||
ms_rx_qual = v; have_ms_rx_qual = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_c_value(int16_t v) {
|
||||
ms_c_value = v; have_ms_c_value = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_sign_var(int16_t v) {
|
||||
ms_sign_var = v; have_ms_sign_var = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_i_level(size_t idx, int16_t v) {
|
||||
ts[idx].set_ms_i_level(v); have_ms_i_level = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas() :
|
||||
have_rssi(0),
|
||||
have_ber(0),
|
||||
have_bto(0),
|
||||
have_link_qual(0),
|
||||
have_ms_rx_qual(0),
|
||||
have_ms_c_value(0),
|
||||
have_ms_sign_var(0),
|
||||
have_ms_i_level(0)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
void pcu_l1if_tx_agch(bitvec * block, int len);
|
||||
|
||||
void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi);
|
||||
void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi);
|
||||
|
||||
int pcu_l1if_open(void);
|
||||
void pcu_l1if_close(void);
|
||||
@@ -50,15 +122,18 @@ int pcu_sock_send(struct msgb *msg);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
extern "C" {
|
||||
#endif
|
||||
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
|
||||
uint8_t len, uint32_t fn, int8_t rssi);
|
||||
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas);
|
||||
|
||||
void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
|
||||
void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // PCU_L1_IF_H
|
||||
|
||||
@@ -27,27 +27,23 @@
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <bts.h>
|
||||
extern "C" {
|
||||
#include "pcu_vty.h"
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_bts *gprs_rlcmac_bts;
|
||||
extern struct gprs_nsvc *nsvc;
|
||||
uint16_t spoof_mcc = 0, spoof_mnc = 0;
|
||||
static int config_given = 0;
|
||||
static const char *config_file = "osmo-pcu.cfg";
|
||||
static char *config_file = strdup("osmo-pcu.cfg");
|
||||
extern struct vty_app_info pcu_vty_info;
|
||||
void *tall_pcu_ctx;
|
||||
extern void *bv_tall_ctx;
|
||||
static int quit = 0;
|
||||
static int rt_prio = -1;
|
||||
|
||||
#ifdef DEBUG_DIAGRAM
|
||||
extern struct timeval diagram_time;
|
||||
#endif
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf( "Some useful options:\n"
|
||||
@@ -75,10 +71,11 @@ static void handle_options(int argc, char **argv)
|
||||
{ "mnc", 1, 0, 'n' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "realtime", 1, 0, 'r' },
|
||||
{ "exit", 0, 0, 'e' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:m:n:Vr:",
|
||||
c = getopt_long(argc, argv, "hc:m:n:Vr:e",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
@@ -89,6 +86,7 @@ static void handle_options(int argc, char **argv)
|
||||
exit(0);
|
||||
break;
|
||||
case 'c':
|
||||
free(config_file);
|
||||
config_file = strdup(optarg);
|
||||
config_given = 1;
|
||||
break;
|
||||
@@ -105,6 +103,9 @@ static void handle_options(int argc, char **argv)
|
||||
case 'r':
|
||||
rt_prio = atoi(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
fprintf(stderr, "Warning: Option '-e' is deprecated!\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown option '%c'\n", c);
|
||||
exit(0);
|
||||
@@ -158,10 +159,7 @@ int main(int argc, char *argv[])
|
||||
return -ENOMEM;
|
||||
bv_tall_ctx = tall_pcu_ctx;
|
||||
|
||||
bts = gprs_rlcmac_bts = talloc_zero(tall_pcu_ctx,
|
||||
struct gprs_rlcmac_bts);
|
||||
if (!gprs_rlcmac_bts)
|
||||
return -ENOMEM;
|
||||
bts = bts_main_data();
|
||||
bts->fc_interval = 1;
|
||||
bts->initial_cs_dl = bts->initial_cs_ul = 1;
|
||||
bts->cs1 = 1;
|
||||
@@ -174,10 +172,30 @@ int main(int argc, char *argv[])
|
||||
bts->n3103 = 4;
|
||||
bts->n3105 = 8;
|
||||
bts->alpha = 0; /* a = 0.0 */
|
||||
bts->ms_idle_sec = 60; /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
|
||||
bts->cs_adj_enabled = 1;
|
||||
bts->cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */
|
||||
bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
|
||||
bts->max_cs_ul = 4;
|
||||
bts->max_cs_dl = 4;
|
||||
/* CS-1 to CS-4 */
|
||||
bts->cs_lqual_ranges[0].low = -256;
|
||||
bts->cs_lqual_ranges[0].high = 6;
|
||||
bts->cs_lqual_ranges[1].low = 5;
|
||||
bts->cs_lqual_ranges[1].high = 8;
|
||||
bts->cs_lqual_ranges[2].low = 7;
|
||||
bts->cs_lqual_ranges[2].high = 13;
|
||||
bts->cs_lqual_ranges[3].low = 12;
|
||||
bts->cs_lqual_ranges[3].high = 256;
|
||||
bts->cs_downgrade_threshold = 200;
|
||||
|
||||
bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
gprs_ns_set_log_ss(DNS);
|
||||
bssgp_set_log_ss(DBSSGP);
|
||||
|
||||
vty_init(&pcu_vty_info);
|
||||
pcu_vty_init(&gprs_log_info);
|
||||
@@ -206,7 +224,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (!bts->alloc_algorithm)
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
bts->alloc_algorithm = alloc_algorithm_dynamic;
|
||||
|
||||
rc = pcu_l1if_open();
|
||||
|
||||
@@ -239,19 +257,12 @@ int main(int argc, char *argv[])
|
||||
osmo_gsm_timers_update();
|
||||
|
||||
osmo_select_main(0);
|
||||
#ifdef DEBUG_DIAGRAM
|
||||
gettimeofday(&diagram_time, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
telnet_exit();
|
||||
|
||||
pcu_l1if_close();
|
||||
|
||||
flush_timing_advance();
|
||||
|
||||
talloc_free(gprs_rlcmac_bts);
|
||||
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
talloc_free(tall_pcu_ctx);
|
||||
|
||||
|
||||
26
src/pcu_utils.h
Normal file
26
src/pcu_utils.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
inline int msecs_to_frames(int msecs) {
|
||||
return (msecs * (1024 * 1000 / 4615)) / 1024;
|
||||
}
|
||||
|
||||
inline void csecs_to_timeval(unsigned csecs, struct timeval *tv) {
|
||||
tv->tv_sec = csecs / 100;
|
||||
tv->tv_usec = (csecs % 100) * 10000;
|
||||
}
|
||||
659
src/pcu_vty.c
659
src/pcu_vty.c
@@ -2,12 +2,19 @@
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include "pcu_vty.h"
|
||||
#include "gprs_rlcmac.h"
|
||||
#include "bts.h"
|
||||
#include "tbf.h"
|
||||
|
||||
enum node_type pcu_vty_go_parent(struct vty *vty)
|
||||
#include "pcu_vty_functions.h"
|
||||
|
||||
int pcu_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
#if 0
|
||||
@@ -37,71 +44,101 @@ int pcu_vty_is_config_node(struct vty *vty, int node)
|
||||
|
||||
static struct cmd_node pcu_node = {
|
||||
(enum node_type) PCU_NODE,
|
||||
"%s(pcu)#",
|
||||
"%s(config-pcu)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
gDEFUN(ournode_exit, ournode_exit_cmd, "exit",
|
||||
"Exit current node, go down to provious node")
|
||||
{
|
||||
switch (vty->node) {
|
||||
#if 0
|
||||
case TRXV_NODE:
|
||||
vty->node = PCU_NODE;
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
vty->index = trx->bts;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
gDEFUN(ournode_end, ournode_end_cmd, "end",
|
||||
"End current mode and change to enable mode")
|
||||
{
|
||||
switch (vty->node) {
|
||||
default:
|
||||
vty_config_unlock(vty);
|
||||
vty->node = ENABLE_NODE;
|
||||
vty->index = NULL;
|
||||
vty->index_sub = NULL;
|
||||
break;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_pcu(struct vty *vty)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
vty_out(vty, "pcu%s", VTY_NEWLINE);
|
||||
vty_out(vty, " flow-control-interval %d%s", bts->fc_interval,
|
||||
VTY_NEWLINE);
|
||||
if (bts->force_cs)
|
||||
if (bts->fc_bvc_bucket_size)
|
||||
vty_out(vty, " flow-control force-bvc-bucket-size %d%s",
|
||||
bts->fc_bvc_bucket_size, VTY_NEWLINE);
|
||||
if (bts->fc_bvc_leak_rate)
|
||||
vty_out(vty, " flow-control force-bvc-leak-rate %d%s",
|
||||
bts->fc_bvc_leak_rate, VTY_NEWLINE);
|
||||
if (bts->fc_ms_bucket_size)
|
||||
vty_out(vty, " flow-control force-ms-bucket-size %d%s",
|
||||
bts->fc_ms_bucket_size, VTY_NEWLINE);
|
||||
if (bts->fc_ms_leak_rate)
|
||||
vty_out(vty, " flow-control force-ms-leak-rate %d%s",
|
||||
bts->fc_ms_leak_rate, VTY_NEWLINE);
|
||||
if (bts->force_cs) {
|
||||
if (bts->initial_cs_ul == bts->initial_cs_dl)
|
||||
vty_out(vty, " cs %d%s", bts->initial_cs_dl,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " cs %d %d%s", bts->initial_cs_dl,
|
||||
bts->initial_cs_ul, VTY_NEWLINE);
|
||||
}
|
||||
if (bts->max_cs_dl && bts->max_cs_ul) {
|
||||
if (bts->max_cs_ul == bts->max_cs_dl)
|
||||
vty_out(vty, " cs max %d%s", bts->max_cs_dl,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " cs max %d %d%s", bts->max_cs_dl,
|
||||
bts->max_cs_ul, VTY_NEWLINE);
|
||||
}
|
||||
if (bts->cs_adj_enabled)
|
||||
vty_out(vty, " cs threshold %d %d%s",
|
||||
bts->cs_adj_lower_limit, bts->cs_adj_upper_limit,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " no cs threshold%s", VTY_NEWLINE);
|
||||
|
||||
if (bts->cs_downgrade_threshold)
|
||||
vty_out(vty, " cs downgrade-threshold %d%s",
|
||||
bts->cs_downgrade_threshold, VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " no cs downgrade-threshold%s", VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " cs link-quality-ranges cs1 %d cs2 %d %d cs3 %d %d cs4 %d%s",
|
||||
bts->cs_lqual_ranges[0].high,
|
||||
bts->cs_lqual_ranges[1].low,
|
||||
bts->cs_lqual_ranges[1].high,
|
||||
bts->cs_lqual_ranges[2].low,
|
||||
bts->cs_lqual_ranges[2].high,
|
||||
bts->cs_lqual_ranges[3].low,
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (bts->force_llc_lifetime == 0xffff)
|
||||
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
|
||||
else if (bts->force_llc_lifetime)
|
||||
vty_out(vty, " queue lifetime %d%s", bts->force_llc_lifetime,
|
||||
VTY_NEWLINE);
|
||||
if (bts->llc_discard_csec)
|
||||
vty_out(vty, " queue hysteresis %d%s", bts->llc_discard_csec,
|
||||
VTY_NEWLINE);
|
||||
if (bts->llc_idle_ack_csec)
|
||||
vty_out(vty, " queue idle-ack-delay %d%s", bts->llc_idle_ack_csec,
|
||||
VTY_NEWLINE);
|
||||
if (bts->llc_codel_interval_msec == LLC_CODEL_USE_DEFAULT)
|
||||
vty_out(vty, " queue codel%s", VTY_NEWLINE);
|
||||
else if (bts->llc_codel_interval_msec == LLC_CODEL_DISABLE)
|
||||
vty_out(vty, " no queue codel%s", VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " queue codel interval %d%s",
|
||||
bts->llc_codel_interval_msec/10, VTY_NEWLINE);
|
||||
|
||||
if (bts->alloc_algorithm == alloc_algorithm_a)
|
||||
vty_out(vty, " alloc-algorithm a%s", VTY_NEWLINE);
|
||||
if (bts->alloc_algorithm == alloc_algorithm_b)
|
||||
vty_out(vty, " alloc-algorithm b%s", VTY_NEWLINE);
|
||||
if (bts->alloc_algorithm == alloc_algorithm_dynamic)
|
||||
vty_out(vty, " alloc-algorithm dynamic%s", VTY_NEWLINE);
|
||||
if (bts->force_two_phase)
|
||||
vty_out(vty, " two-phase-access%s", VTY_NEWLINE);
|
||||
vty_out(vty, " alpha %d%s", bts->alpha, VTY_NEWLINE);
|
||||
vty_out(vty, " gamma %d%s", bts->gamma * 2, VTY_NEWLINE);
|
||||
if (bts->dl_tbf_idle_msec)
|
||||
vty_out(vty, " dl-tbf-idle-time %d%s", bts->dl_tbf_idle_msec,
|
||||
VTY_NEWLINE);
|
||||
|
||||
return pcu_vty_config_write_pcu_ext(vty);
|
||||
}
|
||||
|
||||
/* per-BTS configuration */
|
||||
@@ -121,20 +158,147 @@ DEFUN(cfg_pcu_fc_interval,
|
||||
"Interval between sending subsequent Flow Control PDUs\n"
|
||||
"Interval time in seconds\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_interval = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
#define FC_STR "BSSGP Flow Control configuration\n"
|
||||
#define FC_BMAX_STR(who) "Force a fixed value for the " who " bucket size\n"
|
||||
#define FC_LR_STR(who) "Force a fixed value for the " who " leak rate\n"
|
||||
|
||||
DEFUN(cfg_pcu_fc_bvc_bucket_size,
|
||||
cfg_pcu_fc_bvc_bucket_size_cmd,
|
||||
"flow-control force-bvc-bucket-size <1-6553500>",
|
||||
FC_STR FC_BMAX_STR("BVC") "Bucket size in octets\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_bvc_bucket_size = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_fc_bvc_bucket_size,
|
||||
cfg_pcu_no_fc_bvc_bucket_size_cmd,
|
||||
"no flow-control force-bvc-bucket-size",
|
||||
NO_STR FC_STR FC_BMAX_STR("BVC"))
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_bvc_bucket_size = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_fc_bvc_leak_rate,
|
||||
cfg_pcu_fc_bvc_leak_rate_cmd,
|
||||
"flow-control force-bvc-leak-rate <1-6553500>",
|
||||
FC_STR FC_LR_STR("BVC") "Leak rate in bit/s\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_bvc_leak_rate = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_fc_bvc_leak_rate,
|
||||
cfg_pcu_no_fc_bvc_leak_rate_cmd,
|
||||
"no flow-control force-bvc-leak-rate",
|
||||
NO_STR FC_STR FC_LR_STR("BVC"))
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_bvc_leak_rate = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_fc_ms_bucket_size,
|
||||
cfg_pcu_fc_ms_bucket_size_cmd,
|
||||
"flow-control force-ms-bucket-size <1-6553500>",
|
||||
FC_STR FC_BMAX_STR("default MS") "Bucket size in octets\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_ms_bucket_size = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_fc_ms_bucket_size,
|
||||
cfg_pcu_no_fc_ms_bucket_size_cmd,
|
||||
"no flow-control force-ms-bucket-size",
|
||||
NO_STR FC_STR FC_BMAX_STR("default MS"))
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_ms_bucket_size = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_fc_ms_leak_rate,
|
||||
cfg_pcu_fc_ms_leak_rate_cmd,
|
||||
"flow-control force-ms-leak-rate <1-6553500>",
|
||||
FC_STR FC_LR_STR("default MS") "Leak rate in bit/s\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_ms_leak_rate = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_fc_ms_leak_rate,
|
||||
cfg_pcu_no_fc_ms_leak_rate_cmd,
|
||||
"no flow-control force-ms-leak-rate",
|
||||
NO_STR FC_STR FC_LR_STR("default MS"))
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_ms_leak_rate = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define FC_BTIME_STR "Set target downlink maximum queueing time (only affects the advertised bucket size)\n"
|
||||
DEFUN(cfg_pcu_fc_bucket_time,
|
||||
cfg_pcu_fc_bucket_time_cmd,
|
||||
"flow-control bucket-time <1-65534>",
|
||||
FC_STR FC_BTIME_STR "Time in centi-seconds\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_bucket_time = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_fc_bucket_time,
|
||||
cfg_pcu_no_fc_bucket_time_cmd,
|
||||
"no flow-control bucket-time",
|
||||
NO_STR FC_STR FC_BTIME_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->fc_bucket_time = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CS_STR "Coding Scheme configuration\n"
|
||||
|
||||
DEFUN(cfg_pcu_cs,
|
||||
cfg_pcu_cs_cmd,
|
||||
"cs <1-4> [<1-4>]",
|
||||
"Set the Coding Scheme to be used, (overrides BTS config)\n"
|
||||
"Initial CS used\nAlternative uplink CS")
|
||||
CS_STR
|
||||
"Initial CS value to be used (overrides BTS config)\n"
|
||||
"Use a different initial CS value for the uplink")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint8_t cs = atoi(argv[0]);
|
||||
|
||||
bts->force_cs = 1;
|
||||
@@ -150,15 +314,49 @@ DEFUN(cfg_pcu_cs,
|
||||
DEFUN(cfg_pcu_no_cs,
|
||||
cfg_pcu_no_cs_cmd,
|
||||
"no cs",
|
||||
NO_STR "Don't force given Coding Scheme, (use BTS config)\n")
|
||||
NO_STR CS_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->force_cs = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_cs_max,
|
||||
cfg_pcu_cs_max_cmd,
|
||||
"cs max <1-4> [<1-4>]",
|
||||
CS_STR
|
||||
"Set maximum values for adaptive CS selection (overrides BTS config)\n"
|
||||
"Maximum CS value to be used\n"
|
||||
"Use a different maximum CS value for the uplink")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint8_t cs = atoi(argv[0]);
|
||||
|
||||
bts->max_cs_dl = cs;
|
||||
if (argc > 1)
|
||||
bts->max_cs_ul = atoi(argv[1]);
|
||||
else
|
||||
bts->max_cs_ul = cs;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_cs_max,
|
||||
cfg_pcu_no_cs_max_cmd,
|
||||
"no cs max",
|
||||
NO_STR CS_STR
|
||||
"Set maximum values for adaptive CS selection (overrides BTS config)\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->max_cs_dl = 0;
|
||||
bts->max_cs_ul = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define QUEUE_STR "Packet queue options\n"
|
||||
#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
|
||||
"(overrides the value given by SGSN)\n"
|
||||
@@ -168,8 +366,8 @@ DEFUN(cfg_pcu_queue_lifetime,
|
||||
"queue lifetime <1-65534>",
|
||||
QUEUE_STR LIFETIME_STR "Lifetime in centi-seconds")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
uint8_t csec = atoi(argv[0]);
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint16_t csec = atoi(argv[0]);
|
||||
|
||||
bts->force_llc_lifetime = csec;
|
||||
|
||||
@@ -181,7 +379,7 @@ DEFUN(cfg_pcu_queue_lifetime_inf,
|
||||
"queue lifetime infinite",
|
||||
QUEUE_STR LIFETIME_STR "Infinite lifetime")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->force_llc_lifetime = 0xffff;
|
||||
|
||||
@@ -194,21 +392,119 @@ DEFUN(cfg_pcu_no_queue_lifetime,
|
||||
NO_STR QUEUE_STR "Disable lifetime limit of LLC frame (use value given "
|
||||
"by SGSN)\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->force_llc_lifetime = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define QUEUE_HYSTERESIS_STR "Set lifetime hysteresis of LLC frame in centi-seconds " \
|
||||
"(continue discarding until lifetime-hysteresis is reached)\n"
|
||||
|
||||
DEFUN(cfg_pcu_queue_hysteresis,
|
||||
cfg_pcu_queue_hysteresis_cmd,
|
||||
"queue hysteresis <1-65535>",
|
||||
QUEUE_STR QUEUE_HYSTERESIS_STR "Hysteresis in centi-seconds")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint16_t csec = atoi(argv[0]);
|
||||
|
||||
bts->llc_discard_csec = csec;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_queue_hysteresis,
|
||||
cfg_pcu_no_queue_hysteresis_cmd,
|
||||
"no queue hysteresis",
|
||||
NO_STR QUEUE_STR QUEUE_HYSTERESIS_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->llc_discard_csec = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define QUEUE_CODEL_STR "Set CoDel queue management\n"
|
||||
|
||||
DEFUN(cfg_pcu_queue_codel,
|
||||
cfg_pcu_queue_codel_cmd,
|
||||
"queue codel",
|
||||
QUEUE_STR QUEUE_CODEL_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_queue_codel_interval,
|
||||
cfg_pcu_queue_codel_interval_cmd,
|
||||
"queue codel interval <1-1000>",
|
||||
QUEUE_STR QUEUE_CODEL_STR "Specify interval\n" "Interval in centi-seconds")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint16_t csec = atoi(argv[0]);
|
||||
|
||||
bts->llc_codel_interval_msec = 10*csec;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_queue_codel,
|
||||
cfg_pcu_no_queue_codel_cmd,
|
||||
"no queue codel",
|
||||
NO_STR QUEUE_STR QUEUE_CODEL_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->llc_codel_interval_msec = LLC_CODEL_DISABLE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#define QUEUE_IDLE_ACK_STR "Request an ACK after the last DL LLC frame in centi-seconds\n"
|
||||
|
||||
DEFUN(cfg_pcu_queue_idle_ack_delay,
|
||||
cfg_pcu_queue_idle_ack_delay_cmd,
|
||||
"queue idle-ack-delay <1-65535>",
|
||||
QUEUE_STR QUEUE_IDLE_ACK_STR "Idle ACK delay in centi-seconds")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint16_t csec = atoi(argv[0]);
|
||||
|
||||
bts->llc_idle_ack_csec = csec;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_queue_idle_ack_delay,
|
||||
cfg_pcu_no_queue_idle_ack_delay_cmd,
|
||||
"no queue idle-ack-delay",
|
||||
NO_STR QUEUE_STR QUEUE_IDLE_ACK_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->llc_idle_ack_csec = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_pcu_alloc,
|
||||
cfg_pcu_alloc_cmd,
|
||||
"alloc-algorithm (a|b)",
|
||||
"alloc-algorithm (a|b|dynamic)",
|
||||
"Select slot allocation algorithm to use when assigning timeslots on "
|
||||
"PACCH\nSingle slot is assigned only\nMultiple slots are assigned for "
|
||||
"semi-duplex operation")
|
||||
"PACCH\n"
|
||||
"Single slot is assigned only\n"
|
||||
"Multiple slots are assigned for semi-duplex operation\n"
|
||||
"Dynamically select the algorithm based on the system state\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
switch (argv[0][0]) {
|
||||
case 'a':
|
||||
@@ -217,6 +513,9 @@ DEFUN(cfg_pcu_alloc,
|
||||
case 'b':
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
break;
|
||||
default:
|
||||
bts->alloc_algorithm = alloc_algorithm_dynamic;
|
||||
break;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@@ -227,7 +526,7 @@ DEFUN(cfg_pcu_two_phase,
|
||||
"two-phase-access",
|
||||
"Force two phase access when MS requests single phase access\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->force_two_phase = 1;
|
||||
|
||||
@@ -239,7 +538,7 @@ DEFUN(cfg_pcu_no_two_phase,
|
||||
"no two-phase-access",
|
||||
NO_STR "Only use two phase access when requested my MS\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->force_two_phase = 0;
|
||||
|
||||
@@ -253,7 +552,7 @@ DEFUN(cfg_pcu_alpha,
|
||||
"NOTE: Be sure to set Alpha value at System information 13 too.\n"
|
||||
"Alpha in units of 0.1\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->alpha = atoi(argv[0]);
|
||||
|
||||
@@ -266,13 +565,226 @@ DEFUN(cfg_pcu_gamma,
|
||||
"Gamma parameter for MS power control in units of dB (see TS 05.08)\n"
|
||||
"Gamma in even unit of dBs\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->gamma = atoi(argv[0]) / 2;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_bts_stats,
|
||||
show_bts_stats_cmd,
|
||||
"show bts statistics",
|
||||
SHOW_STR "BTS related functionality\nStatistics\n")
|
||||
{
|
||||
vty_out_rate_ctr_group(vty, "", bts_main_data_stats());
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define IDLE_TIME_STR "keep an idle DL TBF alive for the time given\n"
|
||||
DEFUN(cfg_pcu_dl_tbf_idle_time,
|
||||
cfg_pcu_dl_tbf_idle_time_cmd,
|
||||
"dl-tbf-idle-time <1-5000>",
|
||||
IDLE_TIME_STR "idle time in msec")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->dl_tbf_idle_msec = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_dl_tbf_idle_time,
|
||||
cfg_pcu_no_dl_tbf_idle_time_cmd,
|
||||
"no dl-tbf-idle-time",
|
||||
NO_STR IDLE_TIME_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->dl_tbf_idle_msec = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define MS_IDLE_TIME_STR "keep an idle MS object alive for the time given\n"
|
||||
DEFUN(cfg_pcu_ms_idle_time,
|
||||
cfg_pcu_ms_idle_time_cmd,
|
||||
"ms-idle-time <1-7200>",
|
||||
MS_IDLE_TIME_STR "idle time in sec")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->ms_idle_sec = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_ms_idle_time,
|
||||
cfg_pcu_no_ms_idle_time_cmd,
|
||||
"no ms-idle-time",
|
||||
NO_STR MS_IDLE_TIME_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->ms_idle_sec = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CS_ERR_LIMITS_STR "set thresholds for error rate based CS adjustment\n"
|
||||
DEFUN(cfg_pcu_cs_err_limits,
|
||||
cfg_pcu_cs_err_limits_cmd,
|
||||
"cs threshold <0-100> <0-100>",
|
||||
CS_STR CS_ERR_LIMITS_STR "lower limit in %\n" "upper limit in %\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
uint8_t lower_limit = atoi(argv[0]);
|
||||
uint8_t upper_limit = atoi(argv[1]);
|
||||
|
||||
if (lower_limit > upper_limit) {
|
||||
vty_out(vty,
|
||||
"The lower limit must be less than or equal to the "
|
||||
"upper limit.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
bts->cs_adj_enabled = 1;
|
||||
bts->cs_adj_upper_limit = upper_limit;
|
||||
bts->cs_adj_lower_limit = lower_limit;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_cs_err_limits,
|
||||
cfg_pcu_no_cs_err_limits_cmd,
|
||||
"no cs threshold",
|
||||
NO_STR CS_STR CS_ERR_LIMITS_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->cs_adj_enabled = 0;
|
||||
bts->cs_adj_upper_limit = 100;
|
||||
bts->cs_adj_lower_limit = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CS_DOWNGRADE_STR "set threshold for data size based CS downgrade\n"
|
||||
DEFUN(cfg_pcu_cs_downgrade_thrsh,
|
||||
cfg_pcu_cs_downgrade_thrsh_cmd,
|
||||
"cs downgrade-threshold <1-10000>",
|
||||
CS_STR CS_DOWNGRADE_STR "downgrade if less octets left\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->cs_downgrade_threshold = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_cs_downgrade_thrsh,
|
||||
cfg_pcu_no_cs_downgrade_thrsh_cmd,
|
||||
"no cs downgrade-threshold",
|
||||
NO_STR CS_STR CS_DOWNGRADE_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->cs_downgrade_threshold = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_pcu_cs_lqual_ranges,
|
||||
cfg_pcu_cs_lqual_ranges_cmd,
|
||||
"cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35> cs3 <0-35> <0-35> cs4 <0-35>",
|
||||
CS_STR "Set link quality ranges\n"
|
||||
"Set quality range for CS-1 (high value only)\n"
|
||||
"CS-1 high (dB)\n"
|
||||
"Set quality range for CS-2\n"
|
||||
"CS-2 low (dB)\n"
|
||||
"CS-2 high (dB)\n"
|
||||
"Set quality range for CS-3\n"
|
||||
"CS-3 low (dB)\n"
|
||||
"CS-3 high (dB)\n"
|
||||
"Set quality range for CS-4 (low value only)\n"
|
||||
"CS-4 low (dB)\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
uint8_t cs1_high = atoi(argv[0]);
|
||||
uint8_t cs2_low = atoi(argv[1]);
|
||||
uint8_t cs2_high = atoi(argv[2]);
|
||||
uint8_t cs3_low = atoi(argv[3]);
|
||||
uint8_t cs3_high = atoi(argv[4]);
|
||||
uint8_t cs4_low = atoi(argv[5]);
|
||||
|
||||
bts->cs_lqual_ranges[0].high = cs1_high;
|
||||
bts->cs_lqual_ranges[1].low = cs2_low;
|
||||
bts->cs_lqual_ranges[1].high = cs2_high;
|
||||
bts->cs_lqual_ranges[2].low = cs3_low;
|
||||
bts->cs_lqual_ranges[2].high = cs3_high;
|
||||
bts->cs_lqual_ranges[3].low = cs4_low;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(show_tbf,
|
||||
show_tbf_cmd,
|
||||
"show tbf all",
|
||||
SHOW_STR "information about TBFs\n" "All TBFs\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
struct llist_head *tbf;
|
||||
|
||||
vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
|
||||
llist_for_each(tbf, &bts->ul_tbfs) {
|
||||
tbf_print_vty_info(vty, tbf);
|
||||
}
|
||||
|
||||
vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
|
||||
llist_for_each(tbf, &bts->dl_tbfs) {
|
||||
tbf_print_vty_info(vty, tbf);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_ms_all,
|
||||
show_ms_all_cmd,
|
||||
"show ms all",
|
||||
SHOW_STR "information about MSs\n" "All TBFs\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
return pcu_vty_show_ms_all(vty, bts);
|
||||
}
|
||||
|
||||
DEFUN(show_ms_tlli,
|
||||
show_ms_tlli_cmd,
|
||||
"show ms tlli TLLI",
|
||||
SHOW_STR "information about MSs\n" "Select MS by TLLI\n" "TLLI as hex\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
char *endp = NULL;
|
||||
unsigned long long tlli = strtoll(argv[0], &endp, 16);
|
||||
if ((endp != NULL && *endp != 0) || tlli > 0xffffffffULL) {
|
||||
vty_out(vty, "Invalid TLLI.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return pcu_vty_show_ms_by_tlli(vty, bts, (uint32_t)tlli);
|
||||
}
|
||||
|
||||
DEFUN(show_ms_imsi,
|
||||
show_ms_imsi_cmd,
|
||||
"show ms imsi IMSI",
|
||||
SHOW_STR "information about MSs\n" "Select MS by IMSI\n" "IMSI\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
return pcu_vty_show_ms_by_imsi(vty, bts, argv[0]);
|
||||
}
|
||||
|
||||
static const char pcu_copyright[] =
|
||||
"Copyright (C) 2012 by Ivan Kluchnikov <kluchnikovi@gmail.com> and \r\n"
|
||||
" Andreas Eversberg <jolly@eversberg.eu>\r\n"
|
||||
@@ -296,19 +808,52 @@ int pcu_vty_init(const struct log_info *cat)
|
||||
|
||||
install_node(&pcu_node, config_write_pcu);
|
||||
install_element(CONFIG_NODE, &cfg_pcu_cmd);
|
||||
install_default(PCU_NODE);
|
||||
vty_install_default(PCU_NODE);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_cs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_max_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_cs_max_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_err_limits_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_cs_err_limits_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_downgrade_thrsh_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_hysteresis_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_queue_hysteresis_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_codel_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_codel_interval_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_queue_codel_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_idle_ack_delay_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_queue_idle_ack_delay_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_alloc_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_two_phase_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_fc_interval_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_fc_bucket_time_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_fc_bucket_time_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_fc_bvc_bucket_size_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_fc_bvc_bucket_size_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_fc_bvc_leak_rate_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_fc_bvc_leak_rate_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_fc_ms_bucket_size_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_fc_ms_bucket_size_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_fc_ms_leak_rate_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_fc_ms_leak_rate_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_alpha_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_gamma_cmd);
|
||||
install_element(PCU_NODE, &ournode_end_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_dl_tbf_idle_time_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_idle_time_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_ms_idle_time_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_ms_idle_time_cmd);
|
||||
|
||||
install_element_ve(&show_bts_stats_cmd);
|
||||
install_element_ve(&show_tbf_cmd);
|
||||
install_element_ve(&show_ms_all_cmd);
|
||||
install_element_ve(&show_ms_tlli_cmd);
|
||||
install_element_ve(&show_ms_imsi_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,7 @@ enum pcu_vty_node {
|
||||
PCU_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
};
|
||||
|
||||
extern struct cmd_element ournode_exit_cmd;
|
||||
extern struct cmd_element ournode_end_cmd;
|
||||
|
||||
enum node_type pcu_vty_go_parent(struct vty *vty);
|
||||
int pcu_vty_go_parent(struct vty *vty);
|
||||
int pcu_vty_is_config_node(struct vty *vty, int node);
|
||||
|
||||
int pcu_vty_init(const struct log_info *cat);
|
||||
|
||||
152
src/pcu_vty_functions.cpp
Normal file
152
src/pcu_vty_functions.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/* pcu_vty_functions.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.
|
||||
*/
|
||||
/* OsmoBTS VTY interface */
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "pcu_vty_functions.h"
|
||||
#include "bts.h"
|
||||
#include "gprs_ms_storage.h"
|
||||
#include "gprs_ms.h"
|
||||
#include "cxx_linuxlist.h"
|
||||
|
||||
extern "C" {
|
||||
# include <osmocom/vty/command.h>
|
||||
# include <osmocom/vty/logging.h>
|
||||
# include <osmocom/vty/misc.h>
|
||||
}
|
||||
|
||||
int pcu_vty_config_write_pcu_ext(struct vty *vty)
|
||||
{
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
|
||||
{
|
||||
BTS *bts = bts_data->bts;
|
||||
LListHead<GprsMs> *ms_iter;
|
||||
|
||||
llist_for_each(ms_iter, &bts->ms_store().ms_list()) {
|
||||
GprsMs *ms = ms_iter->entry();
|
||||
|
||||
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%d, CS-DL=%d, LLC=%d, "
|
||||
"IMSI=%s%s",
|
||||
ms->tlli(),
|
||||
ms->ta(), ms->current_cs_ul(), ms->current_cs_dl(),
|
||||
ms->llc_queue()->size(),
|
||||
ms->imsi(),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int show_ms(struct vty *vty, GprsMs *ms)
|
||||
{
|
||||
unsigned i;
|
||||
LListHead<gprs_rlcmac_tbf> *i_tbf;
|
||||
|
||||
vty_out(vty, "MS TLLI=%08x, IMSI=%s%s", ms->tlli(), ms->imsi(), VTY_NEWLINE);
|
||||
vty_out(vty, " Timing advance (TA): %d%s", ms->ta(), VTY_NEWLINE);
|
||||
vty_out(vty, " Coding scheme uplink: CS-%d%s", ms->current_cs_ul(),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Coding scheme downlink: CS-%d%s", ms->current_cs_dl(),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " MS class: %d%s", ms->ms_class(), VTY_NEWLINE);
|
||||
vty_out(vty, " LLC queue length: %d%s", ms->llc_queue()->size(),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " LLC queue octets: %d%s", ms->llc_queue()->octets(),
|
||||
VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_rssi)
|
||||
vty_out(vty, " RSSI: %d dBm%s",
|
||||
ms->l1_meas()->rssi, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ber)
|
||||
vty_out(vty, " Bit error rate: %d %%%s",
|
||||
ms->l1_meas()->ber, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_link_qual)
|
||||
vty_out(vty, " Link quality: %d dB%s",
|
||||
ms->l1_meas()->link_qual, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_bto)
|
||||
vty_out(vty, " Burst timing offset: %d/4 bit%s",
|
||||
ms->l1_meas()->bto, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_rx_qual)
|
||||
vty_out(vty, " Downlink NACK rate: %d %%%s",
|
||||
ms->nack_rate_dl(), VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_rx_qual)
|
||||
vty_out(vty, " MS RX quality: %d %%%s",
|
||||
ms->l1_meas()->ms_rx_qual, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_c_value)
|
||||
vty_out(vty, " MS C value: %d dB%s",
|
||||
ms->l1_meas()->ms_c_value, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_sign_var)
|
||||
vty_out(vty, " MS SIGN variance: %d dB%s",
|
||||
ms->l1_meas()->ms_sign_var, VTY_NEWLINE);
|
||||
for (i = 0; i < ARRAY_SIZE(ms->l1_meas()->ts); ++i) {
|
||||
if (ms->l1_meas()->ts[i].have_ms_i_level)
|
||||
vty_out(vty, " MS I level (slot %d): %d dB%s",
|
||||
i, ms->l1_meas()->ts[i].ms_i_level, VTY_NEWLINE);
|
||||
}
|
||||
if (ms->ul_tbf())
|
||||
vty_out(vty, " Uplink TBF: TFI=%d, state=%s%s",
|
||||
ms->ul_tbf()->tfi(),
|
||||
ms->ul_tbf()->state_name(),
|
||||
VTY_NEWLINE);
|
||||
if (ms->dl_tbf())
|
||||
vty_out(vty, " Downlink TBF: TFI=%d, state=%s%s",
|
||||
ms->dl_tbf()->tfi(),
|
||||
ms->dl_tbf()->state_name(),
|
||||
VTY_NEWLINE);
|
||||
|
||||
llist_for_each(i_tbf, &ms->old_tbfs())
|
||||
vty_out(vty, " Old %-19s TFI=%d, state=%s%s",
|
||||
i_tbf->entry()->direction == GPRS_RLCMAC_UL_TBF ?
|
||||
"Uplink TBF:" : "Downlink TBF:",
|
||||
i_tbf->entry()->tfi(),
|
||||
i_tbf->entry()->state_name(),
|
||||
VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
|
||||
uint32_t tlli)
|
||||
{
|
||||
BTS *bts = bts_data->bts;
|
||||
GprsMs *ms = bts->ms_store().get_ms(tlli);
|
||||
if (!ms) {
|
||||
vty_out(vty, "Unknown TLLI %08x.%s", tlli, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
return show_ms(vty, ms);
|
||||
}
|
||||
|
||||
int pcu_vty_show_ms_by_imsi(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
|
||||
const char *imsi)
|
||||
{
|
||||
BTS *bts = bts_data->bts;
|
||||
GprsMs *ms = bts->ms_store().get_ms(0, 0, imsi);
|
||||
if (!ms) {
|
||||
vty_out(vty, "Unknown IMSI '%s'.%s", imsi, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
return show_ms(vty, ms);
|
||||
}
|
||||
39
src/pcu_vty_functions.h
Normal file
39
src/pcu_vty_functions.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* pcu_vty_functions.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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct vty;
|
||||
struct gprs_rlcmac_bts;
|
||||
|
||||
int pcu_vty_config_write_pcu_ext(struct vty *vty);
|
||||
int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
|
||||
int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
|
||||
uint32_t tlli);
|
||||
int pcu_vty_show_ms_by_imsi(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
|
||||
const char *imsi);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
64
src/poll_controller.cpp
Normal file
64
src/poll_controller.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/* poll_controller.h
|
||||
*
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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 <poll_controller.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
|
||||
PollController::PollController(BTS& bts)
|
||||
: m_bts(bts)
|
||||
{}
|
||||
|
||||
void PollController::expireTimedout(int frame_number, unsigned max_delay)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = m_bts.bts_data();
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
struct gprs_rlcmac_sba *sba, *sba2;
|
||||
struct llist_pods *lpods;
|
||||
uint32_t elapsed;
|
||||
|
||||
llist_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
|
||||
if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
|
||||
elapsed = (frame_number + 2715648 - ul_tbf->poll_fn)
|
||||
% 2715648;
|
||||
if (elapsed > max_delay && elapsed < 2715400)
|
||||
ul_tbf->poll_timeout();
|
||||
}
|
||||
}
|
||||
llist_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
|
||||
if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
|
||||
elapsed = (frame_number + 2715648 - dl_tbf->poll_fn)
|
||||
% 2715648;
|
||||
if (elapsed > max_delay && elapsed < 2715400)
|
||||
dl_tbf->poll_timeout();
|
||||
}
|
||||
}
|
||||
llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) {
|
||||
elapsed = (frame_number + 2715648 - sba->fn) % 2715648;
|
||||
if (elapsed > max_delay && elapsed < 2715400) {
|
||||
/* sba will be freed here */
|
||||
m_bts.sba()->timeout(sba);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
47
src/poll_controller.h
Normal file
47
src/poll_controller.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* poll_controller.h
|
||||
*
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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
|
||||
|
||||
struct gprs_rlcmac_bts;
|
||||
|
||||
struct BTS;
|
||||
|
||||
/**
|
||||
* I belong to a BTS and I am responsible for finding TBFs and
|
||||
* SBAs that should have been polled and execute the timeout
|
||||
* action on them.
|
||||
*/
|
||||
class PollController {
|
||||
public:
|
||||
PollController(BTS& bts);
|
||||
|
||||
/* check for poll timeout */
|
||||
void expireTimedout(int frame_number, unsigned max_delay);
|
||||
|
||||
private:
|
||||
BTS& m_bts;
|
||||
|
||||
private:
|
||||
/* disable copying to avoid slicing */
|
||||
PollController(const PollController&);
|
||||
PollController& operator=(const PollController&);
|
||||
};
|
||||
227
src/rlc.cpp
Normal file
227
src/rlc.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "tbf.h"
|
||||
#include "bts.h"
|
||||
#include "gprs_debug.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
}
|
||||
|
||||
|
||||
uint8_t *gprs_rlc_data::prepare(size_t block_data_len)
|
||||
{
|
||||
/* todo.. only set it once if it turns out to be a bottleneck */
|
||||
memset(block, 0x0, sizeof(block));
|
||||
memset(block, 0x2b, block_data_len);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void gprs_rlc_data::put_data(const uint8_t *data, size_t data_len)
|
||||
{
|
||||
memcpy(block, data, data_len);
|
||||
len = data_len;
|
||||
}
|
||||
|
||||
void gprs_rlc_v_b::reset()
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
|
||||
mark_invalid(i);
|
||||
}
|
||||
|
||||
void gprs_rlc_dl_window::reset()
|
||||
{
|
||||
m_v_s = 0;
|
||||
m_v_a = 0;
|
||||
m_v_b.reset();
|
||||
}
|
||||
|
||||
int gprs_rlc_dl_window::resend_needed()
|
||||
{
|
||||
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
|
||||
if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
|
||||
return bsn;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gprs_rlc_dl_window::mark_for_resend()
|
||||
{
|
||||
int resend = 0;
|
||||
|
||||
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
|
||||
if (m_v_b.is_unacked(bsn)) {
|
||||
/* mark to be re-send */
|
||||
m_v_b.mark_resend(bsn);
|
||||
resend += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return resend;
|
||||
}
|
||||
|
||||
int gprs_rlc_dl_window::count_unacked()
|
||||
{
|
||||
uint16_t unacked = 0;
|
||||
uint16_t bsn;
|
||||
|
||||
for (bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
|
||||
if (!m_v_b.is_acked(bsn))
|
||||
unacked += 1;
|
||||
}
|
||||
|
||||
return unacked;
|
||||
}
|
||||
|
||||
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns)
|
||||
{
|
||||
return (ssn - 1 - bitnum) & mod_sns;
|
||||
}
|
||||
|
||||
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint8_t ssn,
|
||||
uint16_t *lost, uint16_t *received)
|
||||
{
|
||||
/* SSN - 1 is in range V(A)..V(S)-1 */
|
||||
for (int bitpos = 0; bitpos < ws(); bitpos++) {
|
||||
uint16_t bsn = bitnum_to_bsn(bitpos, ssn, mod_sns());
|
||||
|
||||
if (bsn == ((v_a() - 1) & mod_sns()))
|
||||
break;
|
||||
|
||||
if (show_rbb[ws() - 1 - bitpos] == 'R') {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
|
||||
if (!m_v_b.is_acked(bsn))
|
||||
*received += 1;
|
||||
m_v_b.mark_acked(bsn);
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
|
||||
m_v_b.mark_nacked(bsn);
|
||||
bts->rlc_nacked();
|
||||
*lost += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int gprs_rlc_dl_window::move_window()
|
||||
{
|
||||
int i;
|
||||
uint16_t bsn;
|
||||
int moved = 0;
|
||||
|
||||
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
|
||||
if (m_v_b.is_acked(bsn)) {
|
||||
m_v_b.mark_invalid(bsn);
|
||||
moved += 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
return moved;
|
||||
}
|
||||
|
||||
void gprs_rlc_dl_window::show_state(char *show_v_b)
|
||||
{
|
||||
int i;
|
||||
uint16_t bsn;
|
||||
|
||||
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
|
||||
uint16_t index = bsn & mod_sns_half();
|
||||
switch(m_v_b.get_state(index)) {
|
||||
case GPRS_RLC_DL_BSN_INVALID:
|
||||
show_v_b[i] = 'I';
|
||||
break;
|
||||
case GPRS_RLC_DL_BSN_ACKED:
|
||||
show_v_b[i] = 'A';
|
||||
break;
|
||||
case GPRS_RLC_DL_BSN_RESEND:
|
||||
show_v_b[i] = 'X';
|
||||
break;
|
||||
case GPRS_RLC_DL_BSN_NACKED:
|
||||
show_v_b[i] = 'N';
|
||||
break;
|
||||
default:
|
||||
show_v_b[i] = '?';
|
||||
}
|
||||
}
|
||||
show_v_b[i] = '\0';
|
||||
}
|
||||
|
||||
void gprs_rlc_v_n::reset()
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i)
|
||||
m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
|
||||
}
|
||||
|
||||
/* Update the receive block bitmap */
|
||||
void gprs_rlc_ul_window::update_rbb(char *rbb)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < ws(); i++) {
|
||||
if (m_v_n.is_received(ssn()-1-i))
|
||||
rbb[ws()-1-i] = 'R';
|
||||
else
|
||||
rbb[ws()-1-i] = 'I';
|
||||
}
|
||||
}
|
||||
|
||||
/* Raise V(R) to highest received sequence number not received. */
|
||||
void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
|
||||
{
|
||||
uint16_t offset_v_r;
|
||||
offset_v_r = (bsn + 1 - v_r()) & mod_sns();
|
||||
/* Positive offset, so raise. */
|
||||
if (offset_v_r < (sns() >> 1)) {
|
||||
while (offset_v_r--) {
|
||||
if (offset_v_r) /* all except the received block */
|
||||
m_v_n.mark_missing(v_r());
|
||||
raise_v_r_to(1);
|
||||
}
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Raise V(Q) if possible. This is looped until there is a gap
|
||||
* (non received block) or the window is empty.
|
||||
*/
|
||||
uint16_t gprs_rlc_ul_window::raise_v_q()
|
||||
{
|
||||
uint16_t count = 0;
|
||||
|
||||
while (v_q() != v_r()) {
|
||||
if (!m_v_n.is_received(v_q()))
|
||||
break;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
|
||||
"V(Q) to %d\n", v_q(), (v_q() + 1) & mod_sns());
|
||||
raise_v_q(1);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
uint16_t gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
|
||||
{
|
||||
m_v_n.mark_received(bsn);
|
||||
raise_v_r(bsn);
|
||||
|
||||
return raise_v_q();
|
||||
}
|
||||
425
src/rlc.h
Normal file
425
src/rlc.h
Normal file
@@ -0,0 +1,425 @@
|
||||
/* rlc header descriptions
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
|
||||
#define RLC_MAX_WS 64 /* max window size */
|
||||
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
|
||||
|
||||
struct BTS;
|
||||
struct gprs_rlc_v_n;
|
||||
|
||||
/* The state of a BSN in the send/receive window */
|
||||
enum gprs_rlc_ul_bsn_state {
|
||||
GPRS_RLC_UL_BSN_INVALID,
|
||||
GPRS_RLC_UL_BSN_RECEIVED,
|
||||
GPRS_RLC_UL_BSN_MISSING,
|
||||
GPRS_RLC_UL_BSN_MAX,
|
||||
};
|
||||
|
||||
enum gprs_rlc_dl_bsn_state {
|
||||
GPRS_RLC_DL_BSN_INVALID,
|
||||
GPRS_RLC_DL_BSN_NACKED,
|
||||
GPRS_RLC_DL_BSN_ACKED,
|
||||
GPRS_RLC_DL_BSN_UNACKED,
|
||||
GPRS_RLC_DL_BSN_RESEND,
|
||||
GPRS_RLC_DL_BSN_MAX,
|
||||
};
|
||||
|
||||
|
||||
static inline uint16_t mod_sns_half()
|
||||
{
|
||||
return (RLC_MAX_SNS / 2) - 1;
|
||||
}
|
||||
|
||||
struct gprs_rlc_data {
|
||||
uint8_t *prepare(size_t block_data_length);
|
||||
void put_data(const uint8_t *data, size_t len);
|
||||
|
||||
/* block history */
|
||||
uint8_t block[RLC_MAX_LEN];
|
||||
/* block len of history */
|
||||
uint8_t len;
|
||||
|
||||
uint8_t cs;
|
||||
};
|
||||
|
||||
/*
|
||||
* I hold the currently transferred blocks and will provide
|
||||
* the routines to manipulate these arrays.
|
||||
*/
|
||||
struct gprs_rlc {
|
||||
gprs_rlc_data *block(int bsn);
|
||||
gprs_rlc_data m_blocks[RLC_MAX_SNS/2];
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: for GPRS/EDGE maybe make sns a template parameter
|
||||
* so we create specialized versions...
|
||||
*/
|
||||
struct gprs_rlc_v_b {
|
||||
/* Check for an individual frame */
|
||||
bool is_unacked(int bsn) const;
|
||||
bool is_nacked(int bsn) const;
|
||||
bool is_acked(int bsn) const;
|
||||
bool is_resend(int bsn) const;
|
||||
bool is_invalid(int bsn) const;
|
||||
gprs_rlc_dl_bsn_state get_state(int bsn) const;
|
||||
|
||||
/* Mark a RLC frame for something */
|
||||
void mark_unacked(int bsn);
|
||||
void mark_nacked(int bsn);
|
||||
void mark_acked(int bsn);
|
||||
void mark_resend(int bsn);
|
||||
void mark_invalid(int bsn);
|
||||
|
||||
void reset();
|
||||
|
||||
|
||||
private:
|
||||
bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const;
|
||||
void mark(int bsn, const gprs_rlc_dl_bsn_state state);
|
||||
|
||||
gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TODO: The UL/DL code could/should share a baseclass but
|
||||
* we are using llist_for_each_entry for the TBF which
|
||||
* requires everything which creates a requirement for a POD
|
||||
* type and in < C++11 something that is using even if the
|
||||
* most simple form of inheritance is not a POD anymore.
|
||||
*/
|
||||
struct gprs_rlc_dl_window {
|
||||
void reset();
|
||||
const uint16_t mod_sns() const;
|
||||
const uint16_t sns() const;
|
||||
const uint16_t ws() const;
|
||||
|
||||
bool window_stalled() const;
|
||||
bool window_empty() const;
|
||||
|
||||
void increment_send();
|
||||
void raise(int moves);
|
||||
|
||||
const uint16_t v_s() const;
|
||||
const uint16_t v_s_mod(int offset) const;
|
||||
const uint16_t v_a() const;
|
||||
const int16_t distance() const;
|
||||
|
||||
/* Methods to manage reception */
|
||||
int resend_needed();
|
||||
int mark_for_resend();
|
||||
void update(BTS *bts, char *show_rbb, uint8_t ssn,
|
||||
uint16_t *lost, uint16_t *received);
|
||||
int move_window();
|
||||
void show_state(char *show_rbb);
|
||||
int count_unacked();
|
||||
|
||||
uint16_t m_v_s; /* send state */
|
||||
uint16_t m_v_a; /* ack state */
|
||||
|
||||
gprs_rlc_v_b m_v_b;
|
||||
};
|
||||
|
||||
struct gprs_rlc_v_n {
|
||||
void reset();
|
||||
|
||||
void mark_received(int bsn);
|
||||
void mark_missing(int bsn);
|
||||
|
||||
bool is_received(int bsn) const;
|
||||
|
||||
gprs_rlc_ul_bsn_state state(int bsn) const;
|
||||
private:
|
||||
bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const;
|
||||
void mark(int bsn, const gprs_rlc_ul_bsn_state state);
|
||||
gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
|
||||
};
|
||||
|
||||
struct gprs_rlc_ul_window {
|
||||
const uint16_t mod_sns() const;
|
||||
const uint16_t sns() const;
|
||||
const uint16_t ws() const;
|
||||
|
||||
const uint16_t v_r() const;
|
||||
const uint16_t v_q() const;
|
||||
|
||||
const uint16_t ssn() const;
|
||||
|
||||
bool is_in_window(uint8_t bsn) const;
|
||||
|
||||
void update_rbb(char *rbb);
|
||||
void raise_v_r_to(int moves);
|
||||
void raise_v_r(const uint16_t bsn);
|
||||
uint16_t raise_v_q();
|
||||
|
||||
void raise_v_q(int);
|
||||
|
||||
uint16_t receive_bsn(const uint16_t bsn);
|
||||
|
||||
uint16_t m_v_r; /* receive state */
|
||||
uint16_t m_v_q; /* receive window state */
|
||||
|
||||
gprs_rlc_v_n m_v_n;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
/* TS 04.60 10.2.2 */
|
||||
struct rlc_ul_header {
|
||||
uint8_t r:1,
|
||||
si:1,
|
||||
cv:4,
|
||||
pt:2;
|
||||
uint8_t ti:1,
|
||||
tfi:5,
|
||||
pi:1,
|
||||
spare:1;
|
||||
uint8_t e:1,
|
||||
bsn:7;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rlc_dl_header {
|
||||
uint8_t usf:3,
|
||||
s_p:1,
|
||||
rrbp:2,
|
||||
pt:2;
|
||||
uint8_t fbi:1,
|
||||
tfi:5,
|
||||
pr:2;
|
||||
uint8_t e:1,
|
||||
bsn:7;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct rlc_li_field {
|
||||
uint8_t e:1,
|
||||
m:1,
|
||||
li:6;
|
||||
} __attribute__ ((packed));
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
|
||||
{
|
||||
return m_v_b[bsn & mod_sns_half()] == type;
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type)
|
||||
{
|
||||
m_v_b[bsn & mod_sns_half()] = type;
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_b::is_nacked(int bsn) const
|
||||
{
|
||||
return is_state(bsn, GPRS_RLC_DL_BSN_NACKED);
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_b::is_acked(int bsn) const
|
||||
{
|
||||
return is_state(bsn, GPRS_RLC_DL_BSN_ACKED);
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_b::is_unacked(int bsn) const
|
||||
{
|
||||
return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED);
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_b::is_resend(int bsn) const
|
||||
{
|
||||
return is_state(bsn, GPRS_RLC_DL_BSN_RESEND);
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_b::is_invalid(int bsn) const
|
||||
{
|
||||
return is_state(bsn, GPRS_RLC_DL_BSN_INVALID);
|
||||
}
|
||||
|
||||
inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const
|
||||
{
|
||||
return m_v_b[bsn & mod_sns_half()];
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_b::mark_resend(int bsn)
|
||||
{
|
||||
return mark(bsn, GPRS_RLC_DL_BSN_RESEND);
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_b::mark_unacked(int bsn)
|
||||
{
|
||||
return mark(bsn, GPRS_RLC_DL_BSN_UNACKED);
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_b::mark_acked(int bsn)
|
||||
{
|
||||
return mark(bsn, GPRS_RLC_DL_BSN_ACKED);
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_b::mark_nacked(int bsn)
|
||||
{
|
||||
return mark(bsn, GPRS_RLC_DL_BSN_NACKED);
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_b::mark_invalid(int bsn)
|
||||
{
|
||||
return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_dl_window::sns() const
|
||||
{
|
||||
return RLC_MAX_SNS;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_dl_window::ws() const
|
||||
{
|
||||
return RLC_MAX_WS;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_dl_window::mod_sns() const
|
||||
{
|
||||
return sns() - 1;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_dl_window::v_s() const
|
||||
{
|
||||
return m_v_s;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
|
||||
{
|
||||
return (m_v_s + offset) & mod_sns();
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_dl_window::v_a() const
|
||||
{
|
||||
return m_v_a;
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_dl_window::window_stalled() const
|
||||
{
|
||||
return ((m_v_s - m_v_a) & mod_sns()) == ws();
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_dl_window::window_empty() const
|
||||
{
|
||||
return m_v_s == m_v_a;
|
||||
}
|
||||
|
||||
inline void gprs_rlc_dl_window::increment_send()
|
||||
{
|
||||
m_v_s = (m_v_s + 1) & mod_sns();
|
||||
}
|
||||
|
||||
inline void gprs_rlc_dl_window::raise(int moves)
|
||||
{
|
||||
m_v_a = (m_v_a + moves) & mod_sns();
|
||||
}
|
||||
|
||||
inline const int16_t gprs_rlc_dl_window::distance() const
|
||||
{
|
||||
return (m_v_s - m_v_a) & mod_sns();
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_ul_window::is_in_window(uint8_t bsn) const
|
||||
{
|
||||
uint16_t offset_v_q;
|
||||
|
||||
/* current block relative to lowest unreceived block */
|
||||
offset_v_q = (bsn - m_v_q) & mod_sns();
|
||||
/* If out of window (may happen if blocks below V(Q) are received
|
||||
* again. */
|
||||
return offset_v_q < ws();
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_ul_window::sns() const
|
||||
{
|
||||
return RLC_MAX_SNS;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_ul_window::ws() const
|
||||
{
|
||||
return RLC_MAX_WS;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_ul_window::mod_sns() const
|
||||
{
|
||||
return sns() - 1;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_ul_window::v_r() const
|
||||
{
|
||||
return m_v_r;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_ul_window::v_q() const
|
||||
{
|
||||
return m_v_q;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_ul_window::ssn() const
|
||||
{
|
||||
return m_v_r;
|
||||
}
|
||||
|
||||
inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
|
||||
{
|
||||
m_v_r = (m_v_r + moves) & mod_sns();
|
||||
}
|
||||
|
||||
inline void gprs_rlc_ul_window::raise_v_q(int incr)
|
||||
{
|
||||
m_v_q = (m_v_q + incr) & mod_sns();
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_n::mark_received(int bsn)
|
||||
{
|
||||
return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED);
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_n::mark_missing(int bsn)
|
||||
{
|
||||
return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_n::is_received(int bsn) const
|
||||
{
|
||||
return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const
|
||||
{
|
||||
return m_v_n[bsn & mod_sns_half()] == type;
|
||||
}
|
||||
|
||||
inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type)
|
||||
{
|
||||
m_v_n[bsn & mod_sns_half()] = type;
|
||||
}
|
||||
|
||||
inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const
|
||||
{
|
||||
return m_v_n[bsn & mod_sns_half()];
|
||||
}
|
||||
|
||||
inline gprs_rlc_data *gprs_rlc::block(int bsn)
|
||||
{
|
||||
return &m_blocks[bsn & mod_sns_half()];
|
||||
}
|
||||
149
src/sba.cpp
Normal file
149
src/sba.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* sba.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sba.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <bts.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
/* starting time for assigning single slot
|
||||
* This offset must be a multiple of 13. */
|
||||
#define AGCH_START_OFFSET 52
|
||||
|
||||
SBAController::SBAController(BTS &bts)
|
||||
: m_bts(bts)
|
||||
{
|
||||
INIT_LLIST_HEAD(&m_sbas);
|
||||
}
|
||||
|
||||
int SBAController::alloc(
|
||||
uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta)
|
||||
{
|
||||
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct gprs_rlcmac_sba *sba;
|
||||
int8_t trx, ts;
|
||||
uint32_t fn;
|
||||
|
||||
sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba);
|
||||
if (!sba)
|
||||
return -ENOMEM;
|
||||
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
for (ts = 7; ts >= 0; ts--) {
|
||||
pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
|
||||
if (!pdch->is_enabled())
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (ts >= 0)
|
||||
break;
|
||||
}
|
||||
if (trx == 8) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
|
||||
talloc_free(sba);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
|
||||
|
||||
sba->trx_no = trx;
|
||||
sba->ts_no = ts;
|
||||
sba->fn = fn;
|
||||
sba->ta = ta;
|
||||
|
||||
llist_add(&sba->list, &m_sbas);
|
||||
m_bts.sba_allocated();
|
||||
|
||||
*_trx = trx;
|
||||
*_ts = ts;
|
||||
*_fn = fn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
gprs_rlcmac_sba *SBAController::find(uint8_t trx, uint8_t ts, uint32_t fn)
|
||||
{
|
||||
struct gprs_rlcmac_sba *sba;
|
||||
|
||||
llist_for_each_entry(sba, &m_sbas, list) {
|
||||
if (sba->trx_no == trx && sba->ts_no == ts && sba->fn == fn)
|
||||
return sba;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gprs_rlcmac_sba *SBAController::find(const gprs_rlcmac_pdch *pdch, uint32_t fn)
|
||||
{
|
||||
return find(pdch->trx_no(), pdch->ts_no, fn);
|
||||
}
|
||||
|
||||
uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
uint32_t sba_fn;
|
||||
struct gprs_rlcmac_sba *sba;
|
||||
|
||||
/* check special TBF for events */
|
||||
sba_fn = fn + 4;
|
||||
if ((block_nr % 3) == 2)
|
||||
sba_fn ++;
|
||||
sba_fn = sba_fn % 2715648;
|
||||
sba = find(trx, ts, sba_fn);
|
||||
if (sba)
|
||||
return sba_fn;
|
||||
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
int SBAController::timeout(struct gprs_rlcmac_sba *sba)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for SBA\n");
|
||||
m_bts.sba_timedout();
|
||||
free_sba(sba);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SBAController::free_sba(gprs_rlcmac_sba *sba)
|
||||
{
|
||||
m_bts.sba_freed();
|
||||
llist_del(&sba->list);
|
||||
talloc_free(sba);
|
||||
}
|
||||
|
||||
void SBAController::free_resources(struct gprs_rlcmac_pdch *pdch)
|
||||
{
|
||||
struct gprs_rlcmac_sba *sba, *sba2;
|
||||
const uint8_t trx_no = pdch->trx->trx_no;
|
||||
const uint8_t ts_no = pdch->ts_no;
|
||||
|
||||
llist_for_each_entry_safe(sba, sba2, &m_sbas, list) {
|
||||
if (sba->trx_no == trx_no && sba->ts_no == ts_no)
|
||||
free_sba(sba);
|
||||
}
|
||||
}
|
||||
69
src/sba.h
Normal file
69
src/sba.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
}
|
||||
|
||||
struct BTS;
|
||||
class PollController;
|
||||
struct gprs_rlcmac_sba;
|
||||
struct gprs_rlcmac_pdch;
|
||||
|
||||
/*
|
||||
* single block allocation entry
|
||||
*/
|
||||
struct gprs_rlcmac_sba {
|
||||
struct llist_head list;
|
||||
uint8_t trx_no;
|
||||
uint8_t ts_no;
|
||||
uint32_t fn;
|
||||
uint8_t ta;
|
||||
};
|
||||
|
||||
/**
|
||||
* I help to manage SingleBlockAssignment (SBA).
|
||||
*
|
||||
* TODO: Add a flush method..
|
||||
*/
|
||||
class SBAController {
|
||||
friend class PollController;
|
||||
public:
|
||||
SBAController(BTS &bts);
|
||||
|
||||
int alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
|
||||
gprs_rlcmac_sba *find(uint8_t trx, uint8_t ts, uint32_t fn);
|
||||
gprs_rlcmac_sba *find(const gprs_rlcmac_pdch *pdch, uint32_t fn);
|
||||
|
||||
uint32_t sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr);
|
||||
|
||||
int timeout(struct gprs_rlcmac_sba *sba);
|
||||
void free_resources(struct gprs_rlcmac_pdch *pdch);
|
||||
|
||||
void free_sba(gprs_rlcmac_sba *sba);
|
||||
|
||||
private:
|
||||
BTS &m_bts;
|
||||
llist_head m_sbas;
|
||||
};
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -36,7 +36,10 @@ static int l1if_req_pdch(struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
|
||||
|
||||
osmo_wqueue_enqueue(wqueue, msg);
|
||||
if (osmo_wqueue_enqueue(wqueue, msg) != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -163,22 +166,53 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
|
||||
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 femtol1_hdl *fl1h,
|
||||
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct pcu_l1_meas meas = {0};
|
||||
|
||||
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,
|
||||
data_ind->msgUnitParam.u8Size);
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s",
|
||||
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
|
||||
get_value_string(femtobts_l1sapi_names, data_ind->sapi),
|
||||
data_ind->hLayer2,
|
||||
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
|
||||
data_ind->msgUnitParam.u8Size));
|
||||
|
||||
LOGP(DL1IF, LOGL_INFO,
|
||||
"Rx PH-DATA.ind SAPI %d, size %d, lq %f, timing %d, FN %d, TN %d\n",
|
||||
data_ind->sapi, data_ind->msgUnitParam.u8Size,
|
||||
data_ind->measParam.fLinkQuality, data_ind->measParam.i16BurstTiming,
|
||||
data_ind->u32Fn, data_ind->u8Tn);
|
||||
|
||||
pcu_rx_block_time(data_ind->u16Arfcn, data_ind->u32Fn, data_ind->u8Tn);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
@@ -191,7 +225,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
||||
data_ind->msgUnitParam.u8Buffer + 1,
|
||||
data_ind->msgUnitParam.u8Size - 1,
|
||||
data_ind->u32Fn,
|
||||
(int8_t) (data_ind->measParam.fRssi));
|
||||
&meas);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
@@ -211,6 +245,13 @@ static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
|
||||
{
|
||||
uint8_t acc_delay;
|
||||
|
||||
LOGP(DL1IF, LOGL_INFO, "Rx PH-RA.ind SAPI %d, lq %f, timing %d, FN %d, TN %d\n",
|
||||
ra_ind->sapi,
|
||||
ra_ind->measParam.fLinkQuality, ra_ind->measParam.i16BurstTiming,
|
||||
ra_ind->u32Fn, ra_ind->u8Tn);
|
||||
|
||||
pcu_rx_ra_time(ra_ind->u16Arfcn, ra_ind->u32Fn, ra_ind->u8Tn);
|
||||
|
||||
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
||||
return 0;
|
||||
|
||||
@@ -221,6 +262,12 @@ static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
|
||||
acc_delay = 0;
|
||||
else
|
||||
acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
|
||||
|
||||
LOGP(DL1IF, LOGL_NOTICE, "got (P)RACH request, TA = %u (ignored)\n",
|
||||
acc_delay);
|
||||
|
||||
#warning "The (P)RACH request is just dropped here"
|
||||
|
||||
#if 0
|
||||
if (acc_delay > bts->max_ta) {
|
||||
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
|
||||
@@ -313,7 +360,10 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
||||
|
||||
|
||||
/* transmit */
|
||||
osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -73,9 +73,19 @@ struct msgb *sysp_msgb_alloc(void);
|
||||
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
|
||||
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
|
||||
|
||||
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg);
|
||||
int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg);
|
||||
|
||||
/* tch.c */
|
||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
||||
|
||||
/*
|
||||
* The implementation of these functions is selected by either compiling and
|
||||
* linking sysmo_l1_hw.c or sysmo_l1_fwd.c
|
||||
*/
|
||||
int l1if_transport_open(int q, struct femtol1_hdl *hdl);
|
||||
int l1if_transport_close(int q, struct femtol1_hdl *hdl);
|
||||
|
||||
#endif /* _SYSMO_L1_IF_H */
|
||||
|
||||
@@ -36,6 +36,8 @@ extern "C" {
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <pcuif_proto.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
@@ -80,9 +82,8 @@ int pcu_sock_send(struct msgb *msg)
|
||||
static void pcu_sock_close(struct pcu_sock_state *state, int lost)
|
||||
{
|
||||
struct osmo_fd *bfd = &state->conn_bfd;
|
||||
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_tbf *tbf;
|
||||
uint8_t trx, ts, tfi;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint8_t trx, ts;
|
||||
|
||||
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
|
||||
(lost) ? "LOST" : "closed");
|
||||
@@ -106,23 +107,13 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
|
||||
}
|
||||
#endif
|
||||
for (ts = 0; ts < 8; ts++)
|
||||
bts->trx[trx].pdch[ts].enable = 0;
|
||||
for (tfi = 0; tfi < 32; tfi++) {
|
||||
tbf = bts->trx[trx].ul_tbf[tfi];
|
||||
if (tbf)
|
||||
tbf_free(tbf);
|
||||
tbf = bts->trx[trx].dl_tbf[tfi];
|
||||
if (tbf)
|
||||
tbf_free(tbf);
|
||||
}
|
||||
bts->trx[trx].pdch[ts].disable();
|
||||
#warning "NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c for the reset."
|
||||
gprs_rlcmac_tbf::free_all(&bts->trx[trx]);
|
||||
}
|
||||
|
||||
gprs_bssgp_destroy();
|
||||
|
||||
if (lost) {
|
||||
state->timer.cb = pcu_sock_timeout;
|
||||
osmo_timer_schedule(&state->timer, 5, 0);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int pcu_sock_read(struct osmo_fd *bfd)
|
||||
@@ -304,8 +295,7 @@ void pcu_l1if_close(void)
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
if (osmo_timer_pending(&state->timer))
|
||||
osmo_timer_del(&state->timer);
|
||||
osmo_timer_del(&state->timer);
|
||||
|
||||
bfd = &state->conn_bfd;
|
||||
if (bfd->fd > 0)
|
||||
|
||||
1139
src/tbf.cpp
Normal file
1139
src/tbf.cpp
Normal file
File diff suppressed because it is too large
Load Diff
433
src/tbf.h
Normal file
433
src/tbf.h
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "gprs_rlcmac.h"
|
||||
#include "llc.h"
|
||||
#include "rlc.h"
|
||||
#include "cxx_linuxlist.h"
|
||||
#include <gprs_debug.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct bssgp_bvc_ctx;
|
||||
struct rlc_ul_header;
|
||||
struct msgb;
|
||||
struct pcu_l1_meas;
|
||||
class GprsMs;
|
||||
|
||||
/*
|
||||
* TBF instance
|
||||
*/
|
||||
|
||||
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
|
||||
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
|
||||
|
||||
enum gprs_rlcmac_tbf_state {
|
||||
GPRS_RLCMAC_NULL = 0, /* new created TBF */
|
||||
GPRS_RLCMAC_ASSIGN, /* wait for downlink assignment */
|
||||
GPRS_RLCMAC_FLOW, /* RLC/MAC flow, resource needed */
|
||||
GPRS_RLCMAC_FINISHED, /* flow finished, wait for release */
|
||||
GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
|
||||
GPRS_RLCMAC_RELEASING, /* releasing, wait to free TBI/USF */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_poll_state {
|
||||
GPRS_RLCMAC_POLL_NONE = 0,
|
||||
GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_dl_ass_state {
|
||||
GPRS_RLCMAC_DL_ASS_NONE = 0,
|
||||
GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
|
||||
GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_ul_ass_state {
|
||||
GPRS_RLCMAC_UL_ASS_NONE = 0,
|
||||
GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
|
||||
GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_ul_ack_state {
|
||||
GPRS_RLCMAC_UL_ACK_NONE = 0,
|
||||
GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
|
||||
GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
||||
};
|
||||
|
||||
enum gprs_rlcmac_tbf_direction {
|
||||
GPRS_RLCMAC_DL_TBF,
|
||||
GPRS_RLCMAC_UL_TBF
|
||||
};
|
||||
|
||||
#define GPRS_RLCMAC_FLAG_CCCH 0 /* assignment on CCCH */
|
||||
#define GPRS_RLCMAC_FLAG_PACCH 1 /* assignment on PACCH */
|
||||
#define GPRS_RLCMAC_FLAG_UL_DATA 2 /* uplink data received */
|
||||
#define GPRS_RLCMAC_FLAG_DL_ACK 3 /* downlink acknowledge received */
|
||||
#define GPRS_RLCMAC_FLAG_TO_UL_ACK 4
|
||||
#define GPRS_RLCMAC_FLAG_TO_DL_ACK 5
|
||||
#define GPRS_RLCMAC_FLAG_TO_UL_ASS 6
|
||||
#define GPRS_RLCMAC_FLAG_TO_DL_ASS 7
|
||||
#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
|
||||
|
||||
struct llist_pods {
|
||||
struct llist_head list;
|
||||
void *back;
|
||||
};
|
||||
|
||||
#define llist_pods_entry(ptr, type) \
|
||||
((type *)(container_of(ptr, struct llist_pods, list)->back))
|
||||
|
||||
/**
|
||||
* llist_pods_for_each_entry - like llist_for_each_entry, but uses
|
||||
* struct llist_pods ->back to access the entry.
|
||||
* This is necessary for non-PODS classes because container_of is
|
||||
* not guaranteed to work anymore. */
|
||||
#define llist_pods_for_each_entry(pos, head, member, lpods) \
|
||||
for (lpods = llist_entry((head)->next, typeof(struct llist_pods), list), \
|
||||
pos = ((typeof(pos))lpods->back), \
|
||||
prefetch(pos->member.list.next); \
|
||||
&lpods->list != (head); \
|
||||
lpods = llist_entry(lpods->list.next, typeof(struct llist_pods), list), \
|
||||
pos = ((typeof(pos))lpods->back),\
|
||||
prefetch(pos->member.list.next))
|
||||
|
||||
struct gprs_rlcmac_tbf {
|
||||
gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir);
|
||||
|
||||
static void free_all(struct gprs_rlcmac_trx *trx);
|
||||
static void free_all(struct gprs_rlcmac_pdch *pdch);
|
||||
|
||||
bool state_is(enum gprs_rlcmac_tbf_state rhs) const;
|
||||
bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const;
|
||||
void set_state(enum gprs_rlcmac_tbf_state new_state);
|
||||
const char *state_name() const;
|
||||
|
||||
const char *name() const;
|
||||
|
||||
struct msgb *create_dl_ass(uint32_t fn);
|
||||
struct msgb *create_ul_ass(uint32_t fn);
|
||||
|
||||
GprsMs *ms() const;
|
||||
void set_ms(GprsMs *ms);
|
||||
|
||||
uint8_t tsc() const;
|
||||
|
||||
int rlcmac_diag();
|
||||
|
||||
int update();
|
||||
void handle_timeout();
|
||||
void stop_timer();
|
||||
void stop_t3191();
|
||||
int establish_dl_tbf_on_pacch();
|
||||
|
||||
void poll_timeout();
|
||||
|
||||
/** tlli handling */
|
||||
uint32_t tlli() const;
|
||||
bool is_tlli_valid() const;
|
||||
|
||||
/** MS updating */
|
||||
void update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction);
|
||||
|
||||
uint8_t tfi() const;
|
||||
|
||||
const char *imsi() const;
|
||||
void assign_imsi(const char *imsi);
|
||||
uint8_t ta() const;
|
||||
void set_ta(uint8_t);
|
||||
uint8_t ms_class() const;
|
||||
void set_ms_class(uint8_t);
|
||||
uint8_t current_cs() const;
|
||||
gprs_llc_queue *llc_queue();
|
||||
const gprs_llc_queue *llc_queue() const;
|
||||
|
||||
time_t created_ts() const;
|
||||
uint8_t dl_slots() const;
|
||||
uint8_t ul_slots() const;
|
||||
|
||||
/* attempt to make things a bit more fair */
|
||||
void rotate_in_list();
|
||||
|
||||
LListHead<gprs_rlcmac_tbf>& ms_list() {return this->m_ms_list;}
|
||||
const LListHead<gprs_rlcmac_tbf>& ms_list() const {return this->m_ms_list;}
|
||||
|
||||
struct llist_pods list;
|
||||
uint32_t state_flags;
|
||||
enum gprs_rlcmac_tbf_direction direction;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t first_ts; /* first TS used by TBF */
|
||||
uint8_t first_common_ts; /* first TS that the phone can send and
|
||||
reveive simultaniously */
|
||||
uint8_t control_ts; /* timeslot control messages and polling */
|
||||
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
|
||||
|
||||
gprs_llc m_llc;
|
||||
|
||||
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
|
||||
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
|
||||
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
|
||||
|
||||
enum gprs_rlcmac_tbf_poll_state poll_state;
|
||||
uint32_t poll_fn; /* frame number to poll */
|
||||
|
||||
gprs_rlc m_rlc;
|
||||
|
||||
uint8_t n3105; /* N3105 counter */
|
||||
|
||||
struct osmo_timer_list timer;
|
||||
unsigned int T; /* Txxxx number */
|
||||
unsigned int num_T_exp; /* number of consecutive T expirations */
|
||||
|
||||
struct osmo_gsm_timer_list gsm_timer;
|
||||
unsigned int fT; /* fTxxxx number */
|
||||
unsigned int num_fT_exp; /* number of consecutive fT expirations */
|
||||
|
||||
struct Meas {
|
||||
struct timeval rssi_tv; /* timestamp for rssi calculation */
|
||||
int32_t rssi_sum; /* sum of rssi values */
|
||||
int rssi_num; /* number of rssi values added since rssi_tv */
|
||||
|
||||
Meas();
|
||||
} meas;
|
||||
|
||||
/* these should become protected but only after gprs_rlcmac_data.c
|
||||
* stops to iterate over all tbf in its current form */
|
||||
enum gprs_rlcmac_tbf_state state;
|
||||
|
||||
/* Remember if the tbf was in wait_release state when we want to
|
||||
* schedule a new dl assignment */
|
||||
uint8_t was_releasing;
|
||||
|
||||
/* Can/should we upgrade this tbf to use multiple slots? */
|
||||
uint8_t upgrade_to_multislot;
|
||||
|
||||
/* store the BTS this TBF belongs to */
|
||||
BTS *bts;
|
||||
|
||||
/*
|
||||
* private fields. We can't make it private as it is breaking the
|
||||
* llist macros.
|
||||
*/
|
||||
uint8_t m_tfi;
|
||||
time_t m_created_ts;
|
||||
|
||||
protected:
|
||||
gprs_rlcmac_bts *bts_data() const;
|
||||
|
||||
int extract_tlli(const uint8_t *data, const size_t len);
|
||||
void merge_and_clear_ms(GprsMs *old_ms);
|
||||
|
||||
static const char *tbf_state_name[6];
|
||||
|
||||
class GprsMs *m_ms;
|
||||
|
||||
/* Fields to take the TA/MS class values if no MS is associated */
|
||||
uint8_t m_ta;
|
||||
uint8_t m_ms_class;
|
||||
|
||||
private:
|
||||
LListHead<gprs_rlcmac_tbf> m_ms_list;
|
||||
|
||||
mutable char m_name_buf[60];
|
||||
};
|
||||
|
||||
|
||||
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
|
||||
int8_t use_trx, uint8_t ms_class,
|
||||
uint32_t tlli, uint8_t ta, GprsMs *ms);
|
||||
|
||||
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
||||
GprsMs *ms, int8_t use_trx,
|
||||
uint8_t ms_class, uint8_t single_slot);
|
||||
|
||||
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
||||
GprsMs *ms, int8_t use_trx,
|
||||
uint8_t ms_class, uint8_t single_slot);
|
||||
|
||||
void tbf_free(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
|
||||
unsigned int seconds, unsigned int microseconds);
|
||||
|
||||
inline bool gprs_rlcmac_tbf::state_is(enum gprs_rlcmac_tbf_state rhs) const
|
||||
{
|
||||
return state == rhs;
|
||||
}
|
||||
|
||||
inline bool gprs_rlcmac_tbf::state_is_not(enum gprs_rlcmac_tbf_state rhs) const
|
||||
{
|
||||
return state != rhs;
|
||||
}
|
||||
|
||||
const char *tbf_name(gprs_rlcmac_tbf *tbf);
|
||||
|
||||
inline const char *gprs_rlcmac_tbf::state_name() const
|
||||
{
|
||||
return tbf_state_name[state];
|
||||
}
|
||||
|
||||
inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n",
|
||||
tbf_name(this),
|
||||
tbf_state_name[state], tbf_state_name[new_state]);
|
||||
state = new_state;
|
||||
}
|
||||
|
||||
inline GprsMs *gprs_rlcmac_tbf::ms() const
|
||||
{
|
||||
return m_ms;
|
||||
}
|
||||
|
||||
inline bool gprs_rlcmac_tbf::is_tlli_valid() const
|
||||
{
|
||||
return tlli() != 0;
|
||||
}
|
||||
|
||||
inline uint8_t gprs_rlcmac_tbf::tfi() const
|
||||
{
|
||||
return m_tfi;
|
||||
}
|
||||
|
||||
inline time_t gprs_rlcmac_tbf::created_ts() const
|
||||
{
|
||||
return m_created_ts;
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
|
||||
gprs_rlcmac_dl_tbf(BTS *bts);
|
||||
|
||||
void cleanup();
|
||||
|
||||
/* dispatch Unitdata.DL messages */
|
||||
static int handle(struct gprs_rlcmac_bts *bts,
|
||||
const uint32_t tlli, const uint32_t old_tlli,
|
||||
const char *imsi, const uint8_t ms_class,
|
||||
const uint16_t delay_csec, const uint8_t *data, const uint16_t len);
|
||||
|
||||
int append_data(const uint8_t ms_class,
|
||||
const uint16_t pdu_delay_csec,
|
||||
const uint8_t *data, const uint16_t len);
|
||||
|
||||
int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
|
||||
struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
|
||||
void request_dl_ack();
|
||||
bool need_control_ts() const;
|
||||
bool have_data() const;
|
||||
int frames_since_last_poll(unsigned fn) const;
|
||||
int frames_since_last_drain(unsigned fn) const;
|
||||
bool keep_open(unsigned fn) const;
|
||||
int release();
|
||||
|
||||
bool is_control_ts(uint8_t ts) const {
|
||||
return ts == control_ts;
|
||||
}
|
||||
|
||||
/* TODO: add the gettimeofday as parameter */
|
||||
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
|
||||
|
||||
/* Please note that all variables here will be reset when changing
|
||||
* from WAIT RELEASE back to FLOW state (re-use of TBF).
|
||||
* All states that need reset must be in this struct, so this is why
|
||||
* variables are in both (dl and ul) structs and not outside union.
|
||||
*/
|
||||
gprs_rlc_dl_window m_window;
|
||||
int32_t m_tx_counter; /* count all transmitted blocks */
|
||||
uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
|
||||
bool m_dl_ack_requested;
|
||||
int32_t m_last_dl_poll_fn;
|
||||
int32_t m_last_dl_drained_fn;
|
||||
|
||||
struct BandWidth {
|
||||
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
|
||||
uint32_t dl_bw_octets; /* number of octets since bw_tv */
|
||||
|
||||
struct timeval dl_loss_tv; /* timestamp for loss calculation */
|
||||
uint16_t dl_loss_lost; /* sum of lost packets */
|
||||
uint16_t dl_loss_received; /* sum of received packets */
|
||||
|
||||
BandWidth();
|
||||
} m_bw;
|
||||
|
||||
protected:
|
||||
struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts);
|
||||
struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
|
||||
const int index);
|
||||
int update_window(const uint8_t ssn, const uint8_t *rbb);
|
||||
int maybe_start_new_window();
|
||||
bool dl_window_stalled() const;
|
||||
void reuse_tbf();
|
||||
void start_llc_timer();
|
||||
int analyse_errors(char *show_rbb, uint8_t ssn);
|
||||
void schedule_next_frame();
|
||||
|
||||
struct osmo_timer_list m_llc_timer;
|
||||
};
|
||||
|
||||
struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
|
||||
gprs_rlcmac_ul_tbf(BTS *bts);
|
||||
|
||||
struct msgb *create_ul_ack(uint32_t fn);
|
||||
|
||||
/* blocks were acked */
|
||||
int rcv_data_block_acknowledged(const uint8_t *data, size_t len,
|
||||
struct pcu_l1_meas *meas);
|
||||
|
||||
/* TODO: extract LLC class? */
|
||||
int assemble_forward_llc(const gprs_rlc_data *data);
|
||||
int snd_ul_ud();
|
||||
|
||||
/* Please note that all variables here will be reset when changing
|
||||
* from WAIT RELEASE back to FLOW state (re-use of TBF).
|
||||
* All states that need reset must be in this struct, so this is why
|
||||
* variables are in both (dl and ul) structs and not outside union.
|
||||
*/
|
||||
gprs_rlc_ul_window m_window;
|
||||
int32_t m_rx_counter; /* count all received blocks */
|
||||
uint8_t m_n3103; /* N3103 counter */
|
||||
uint8_t m_usf[8]; /* list USFs per PDCH (timeslot) */
|
||||
uint8_t m_contention_resolution_done; /* set after done */
|
||||
uint8_t m_final_ack_sent; /* set if we sent final ack */
|
||||
|
||||
protected:
|
||||
void maybe_schedule_uplink_acknack(const rlc_ul_header *rh);
|
||||
};
|
||||
|
||||
inline enum gprs_rlcmac_tbf_direction reverse(enum gprs_rlcmac_tbf_direction dir)
|
||||
{
|
||||
return (enum gprs_rlcmac_tbf_direction)
|
||||
((int)GPRS_RLCMAC_UL_TBF - (int)dir + (int)GPRS_RLCMAC_DL_TBF);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
||||
void tbf_print_vty_info(struct vty *vty, struct llist_head *tbf);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
948
src/tbf_dl.cpp
Normal file
948
src/tbf_dl.cpp
Normal file
@@ -0,0 +1,948 @@
|
||||
/* Copied from tbf.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
#include <rlc.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <gprs_codel.h>
|
||||
#include <decoding.h>
|
||||
|
||||
#include "pcu_utils.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
/* After sending these frames, we poll for ack/nack. */
|
||||
#define POLL_ACK_AFTER_FRAMES 20
|
||||
|
||||
|
||||
static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
|
||||
/* frame length data block max payload */
|
||||
{ 0, 0, 0 },
|
||||
{ 23, 23, 20 }, /* CS-1 */
|
||||
{ 34, 33, 30 }, /* CS-2 */
|
||||
{ 40, 39, 36 }, /* CS-3 */
|
||||
{ 54, 53, 50 }, /* CS-4 */
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
|
||||
uint8_t num_frames, uint32_t num_octets);
|
||||
}
|
||||
|
||||
static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
|
||||
const uint8_t ms_class)
|
||||
{
|
||||
if (!tbf->ms_class() && ms_class)
|
||||
tbf->set_ms_class(ms_class);
|
||||
}
|
||||
|
||||
static void llc_timer_cb(void *_tbf)
|
||||
{
|
||||
struct gprs_rlcmac_dl_tbf *tbf = (struct gprs_rlcmac_dl_tbf *)_tbf;
|
||||
|
||||
if (tbf->state_is_not(GPRS_RLCMAC_FLOW))
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
"%s LLC receive timeout, requesting DL ACK\n", tbf_name(tbf));
|
||||
|
||||
tbf->request_dl_ack();
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::cleanup()
|
||||
{
|
||||
osmo_timer_del(&m_llc_timer);
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::start_llc_timer()
|
||||
{
|
||||
if (bts_data()->llc_idle_ack_csec > 0) {
|
||||
struct timeval tv;
|
||||
|
||||
/* TODO: this ought to be within a constructor */
|
||||
m_llc_timer.data = this;
|
||||
m_llc_timer.cb = &llc_timer_cb;
|
||||
|
||||
csecs_to_timeval(bts_data()->llc_idle_ack_csec, &tv);
|
||||
osmo_timer_schedule(&m_llc_timer, tv.tv_sec, tv.tv_usec);
|
||||
}
|
||||
}
|
||||
|
||||
int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
|
||||
const uint16_t pdu_delay_csec,
|
||||
const uint8_t *data, const uint16_t len)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this));
|
||||
gprs_llc_queue::MetaInfo info;
|
||||
struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
|
||||
if (!llc_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
gprs_llc_queue::calc_pdu_lifetime(bts, pdu_delay_csec, &info.expire_time);
|
||||
gettimeofday(&info.recv_time, NULL);
|
||||
memcpy(msgb_put(llc_msg, len), data, len);
|
||||
llc_queue()->enqueue(llc_msg, &info);
|
||||
tbf_update_ms_class(this, ms_class);
|
||||
start_llc_timer();
|
||||
|
||||
if (state_is(GPRS_RLCMAC_WAIT_RELEASE)) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
"%s in WAIT RELEASE state "
|
||||
"(T3193), so reuse TBF\n", tbf_name(this));
|
||||
tbf_update_ms_class(this, ms_class);
|
||||
establish_dl_tbf_on_pacch();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
||||
const char *imsi,
|
||||
const uint32_t tlli, const uint32_t tlli_old,
|
||||
const uint8_t ms_class,
|
||||
struct gprs_rlcmac_dl_tbf **tbf)
|
||||
{
|
||||
uint8_t ss;
|
||||
int8_t use_trx;
|
||||
uint16_t ta = 0;
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL, *old_ul_tbf;
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
|
||||
GprsMs *ms;
|
||||
|
||||
/* check for uplink data, so we copy our informations */
|
||||
#warning "Do the same look up for IMSI, TLLI and OLD_TLLI"
|
||||
#warning "Refactor the below lines... into a new method"
|
||||
ms = bts->bts->ms_store().get_ms(tlli, tlli_old, imsi);
|
||||
if (ms) {
|
||||
ul_tbf = ms->ul_tbf();
|
||||
ta = ms->ta();
|
||||
}
|
||||
/* TODO: if (!ms) create MS before tbf_alloc is called? */
|
||||
|
||||
if (ul_tbf && ul_tbf->m_contention_resolution_done
|
||||
&& !ul_tbf->m_final_ack_sent) {
|
||||
use_trx = ul_tbf->trx->trx_no;
|
||||
ss = 0;
|
||||
old_ul_tbf = ul_tbf;
|
||||
} else {
|
||||
use_trx = -1;
|
||||
ss = 1; /* PCH assignment only allows one timeslot */
|
||||
old_ul_tbf = NULL;
|
||||
}
|
||||
|
||||
// Create new TBF (any TRX)
|
||||
#warning "Copy and paste with alloc_ul_tbf"
|
||||
/* set number of downlink slots according to multislot class */
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ms, use_trx, ms_class, ss);
|
||||
|
||||
if (!dl_tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
|
||||
dl_tbf->ms()->set_ta(ta);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(dl_tbf));
|
||||
|
||||
/* Store IMSI for later look-up and PCH retransmission */
|
||||
dl_tbf->assign_imsi(imsi);
|
||||
|
||||
/* trigger downlink assignment and set state to ASSIGN.
|
||||
* we don't use old_downlink, so the possible uplink is used
|
||||
* to trigger downlink assignment. if there is no uplink,
|
||||
* AGCH is used. */
|
||||
dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf);
|
||||
*tbf = dl_tbf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: split into unit test-able parts...
|
||||
*/
|
||||
int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts,
|
||||
const uint32_t tlli, const uint32_t tlli_old, const char *imsi,
|
||||
const uint8_t ms_class, const uint16_t delay_csec,
|
||||
const uint8_t *data, const uint16_t len)
|
||||
{
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
|
||||
int rc;
|
||||
GprsMs *ms, *ms_old;
|
||||
|
||||
/* check for existing TBF */
|
||||
ms = bts->bts->ms_store().get_ms(tlli, tlli_old, imsi);
|
||||
if (ms)
|
||||
dl_tbf = ms->dl_tbf();
|
||||
|
||||
if (ms && strlen(ms->imsi()) == 0) {
|
||||
ms_old = bts->bts->ms_store().get_ms(0, 0, imsi);
|
||||
if (ms_old && ms_old != ms) {
|
||||
/* The TLLI has changed (RAU), so there are two MS
|
||||
* objects for the same MS */
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"There is a new MS object for the same MS: "
|
||||
"(0x%08x, '%s') -> (0x%08x, '%s')\n",
|
||||
ms_old->tlli(), ms_old->imsi(),
|
||||
ms->tlli(), ms->imsi());
|
||||
|
||||
GprsMs::Guard guard_old(ms_old);
|
||||
|
||||
if (!dl_tbf && ms_old->dl_tbf()) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"%s IMSI %s: "
|
||||
"moving DL TBF to new MS object\n",
|
||||
dl_tbf->name(), imsi);
|
||||
dl_tbf = ms_old->dl_tbf();
|
||||
/* Move the DL TBF to the new MS */
|
||||
dl_tbf->set_ms(ms);
|
||||
}
|
||||
/* Clean up the old MS object */
|
||||
/* TODO: Put this into a separate function, use timer? */
|
||||
if (ms_old->ul_tbf() && ms_old->ul_tbf()->T == 0)
|
||||
tbf_free(ms_old->ul_tbf());
|
||||
if (ms_old->dl_tbf() && ms_old->dl_tbf()->T == 0)
|
||||
tbf_free(ms_old->dl_tbf());
|
||||
|
||||
ms->merge_old_ms(ms_old);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dl_tbf) {
|
||||
rc = tbf_new_dl_assignment(bts, imsi, tlli, tlli_old, ms_class,
|
||||
&dl_tbf);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = dl_tbf->append_data(ms_class, delay_csec, data, len);
|
||||
dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
|
||||
dl_tbf->assign_imsi(imsi);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct timeval tv_now, tv_now2;
|
||||
uint32_t octets = 0, frames = 0;
|
||||
struct timeval hyst_delta = {0, 0};
|
||||
const unsigned keep_small_thresh = 60;
|
||||
const gprs_llc_queue::MetaInfo *info;
|
||||
|
||||
if (bts_data()->llc_discard_csec)
|
||||
csecs_to_timeval(bts_data()->llc_discard_csec, &hyst_delta);
|
||||
|
||||
gettimeofday(&tv_now, NULL);
|
||||
timeradd(&tv_now, &hyst_delta, &tv_now2);
|
||||
|
||||
while ((msg = llc_queue()->dequeue(&info))) {
|
||||
const struct timeval *tv_disc = &info->expire_time;
|
||||
const struct timeval *tv_recv = &info->recv_time;
|
||||
|
||||
gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
|
||||
|
||||
if (ms() && ms()->codel_state()) {
|
||||
int bytes = llc_queue()->octets();
|
||||
if (gprs_codel_control(ms()->codel_state(),
|
||||
tv_recv, &tv_now, bytes))
|
||||
goto drop_frame;
|
||||
}
|
||||
|
||||
/* Is the age below the low water mark? */
|
||||
if (!gprs_llc_queue::is_frame_expired(&tv_now2, tv_disc))
|
||||
break;
|
||||
|
||||
/* Is the age below the high water mark */
|
||||
if (!gprs_llc_queue::is_frame_expired(&tv_now, tv_disc)) {
|
||||
/* Has the previous message not been dropped? */
|
||||
if (frames == 0)
|
||||
break;
|
||||
|
||||
/* Hysteresis mode, try to discard LLC messages until
|
||||
* the low water mark has been reached */
|
||||
|
||||
/* Check whether to abort the hysteresis mode */
|
||||
|
||||
/* Is the frame small, perhaps only a TCP ACK? */
|
||||
if (msg->len <= keep_small_thresh)
|
||||
break;
|
||||
|
||||
/* Is it a GMM message? */
|
||||
if (!gprs_llc::is_user_data_frame(msg->data, msg->len))
|
||||
break;
|
||||
}
|
||||
|
||||
bts->llc_timedout_frame();
|
||||
drop_frame:
|
||||
frames++;
|
||||
octets += msg->len;
|
||||
msgb_free(msg);
|
||||
bts->llc_dropped_frame();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frames) {
|
||||
LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU "
|
||||
"because lifetime limit reached, "
|
||||
"count=%u new_queue_size=%zu\n",
|
||||
tbf_name(this), frames, llc_queue()->size());
|
||||
if (frames > 0xff)
|
||||
frames = 0xff;
|
||||
if (octets > 0xffffff)
|
||||
octets = 0xffffff;
|
||||
if (bctx)
|
||||
bssgp_tx_llc_discarded(bctx, tlli(), frames, octets);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create DL data block
|
||||
* The messages are fragmented and forwarded as data blocks.
|
||||
*/
|
||||
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts)
|
||||
{
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. "
|
||||
"V(S)==%d)\n", tbf_name(this),
|
||||
m_window.v_a(), m_window.v_s());
|
||||
|
||||
do_resend:
|
||||
/* check if there is a block with negative acknowledgement */
|
||||
int resend_bsn = m_window.resend_needed();
|
||||
if (resend_bsn >= 0) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn);
|
||||
/* re-send block with negative aknowlegement */
|
||||
m_window.m_v_b.mark_unacked(resend_bsn);
|
||||
bts->rlc_resent();
|
||||
return create_dl_acked_block(fn, ts, resend_bsn);
|
||||
}
|
||||
|
||||
/* if the window has stalled, or transfer is complete,
|
||||
* send an unacknowledged block */
|
||||
if (state_is(GPRS_RLCMAC_FINISHED)) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
|
||||
"because all blocks have been transmitted.\n",
|
||||
m_window.v_a());
|
||||
bts->rlc_restarted();
|
||||
} else if (dl_window_stalled()) {
|
||||
LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
|
||||
"because all window is stalled.\n",
|
||||
m_window.v_a());
|
||||
bts->rlc_stalled();
|
||||
} else if (have_data()) {
|
||||
/* New blocks may be send */
|
||||
return create_new_bsn(fn, ts);
|
||||
} else if (!m_window.window_empty()) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
|
||||
"because all blocks have been transmitted (FLOW).\n",
|
||||
m_window.v_a());
|
||||
bts->rlc_restarted();
|
||||
} else {
|
||||
/* Nothing left to send, create dummy LLC commands */
|
||||
return create_new_bsn(fn, ts);
|
||||
}
|
||||
|
||||
/* If V(S) == V(A) and finished state, we would have received
|
||||
* acknowledgement of all transmitted block. In this case we
|
||||
* would have transmitted the final block, and received ack
|
||||
* from MS. But in this case we did not receive the final ack
|
||||
* indication from MS. This should never happen if MS works
|
||||
* correctly. */
|
||||
if (m_window.window_empty()) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, "
|
||||
"so we re-transmit final block!\n");
|
||||
/* we just send final block again */
|
||||
int16_t index = m_window.v_s_mod(-1);
|
||||
bts->rlc_resent();
|
||||
return create_dl_acked_block(fn, ts, index);
|
||||
}
|
||||
|
||||
/* cycle through all unacked blocks */
|
||||
int resend = m_window.mark_for_resend();
|
||||
|
||||
/* At this point there should be at least one unacked block
|
||||
* to be resent. If not, this is an software error. */
|
||||
if (resend == 0) {
|
||||
LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
|
||||
"There are no unacknowledged blocks, but V(A) "
|
||||
" != V(S). PLEASE FIX!\n");
|
||||
/* we just send final block again */
|
||||
int16_t index = m_window.v_s_mod(-1);
|
||||
return create_dl_acked_block(fn, ts, index);
|
||||
}
|
||||
goto do_resend;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::schedule_next_frame()
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (m_llc.frame_length() != 0)
|
||||
return;
|
||||
|
||||
/* dequeue next LLC frame, if any */
|
||||
msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"- Dequeue next LLC for %s (len=%d)\n",
|
||||
tbf_name(this), msg->len);
|
||||
|
||||
m_llc.put_frame(msg->data, msg->len);
|
||||
bts->llc_frame_sched();
|
||||
msgb_free(msg);
|
||||
m_last_dl_drained_fn = -1;
|
||||
}
|
||||
|
||||
struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts)
|
||||
{
|
||||
struct rlc_dl_header *rh;
|
||||
struct rlc_li_field *li;
|
||||
uint8_t *delimiter, *data, *e_pointer;
|
||||
uint16_t space, chunk;
|
||||
gprs_rlc_data *rlc_data;
|
||||
const uint16_t bsn = m_window.v_s();
|
||||
uint8_t cs = 1;
|
||||
|
||||
if (m_llc.frame_length() == 0)
|
||||
schedule_next_frame();
|
||||
|
||||
cs = current_cs();
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d, CS=%d\n",
|
||||
m_window.v_s(), cs);
|
||||
|
||||
OSMO_ASSERT(cs >= 1);
|
||||
OSMO_ASSERT(cs <= 4);
|
||||
|
||||
/* total length of block, including spare bits */
|
||||
const uint8_t block_length = gprs_rlcmac_cs[cs].block_length;
|
||||
/* length of usable data of block, w/o spare bits, inc. MAC */
|
||||
const uint8_t block_data_len = gprs_rlcmac_cs[cs].block_data;
|
||||
|
||||
/* now we still have untransmitted LLC data, so we fill mac block */
|
||||
rlc_data = m_rlc.block(bsn);
|
||||
data = rlc_data->prepare(block_data_len);
|
||||
rlc_data->cs = cs;
|
||||
|
||||
rh = (struct rlc_dl_header *)data;
|
||||
rh->pt = 0; /* Data Block */
|
||||
rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */
|
||||
rh->usf = 7; /* will be set at scheduler */
|
||||
rh->pr = 0; /* FIXME: power reduction */
|
||||
rh->tfi = m_tfi; /* TFI */
|
||||
rh->fbi = 0; /* Final Block Indicator, set late, if true */
|
||||
rh->bsn = bsn; /* Block Sequence Number */
|
||||
rh->e = 0; /* Extension bit, maybe set later */
|
||||
e_pointer = data + 2; /* points to E of current chunk */
|
||||
data += sizeof(*rh);
|
||||
delimiter = data; /* where next length header would be stored */
|
||||
space = block_data_len - sizeof(*rh);
|
||||
while (1) {
|
||||
if (m_llc.frame_length() == 0) {
|
||||
/* A header will need to by added, so we just need
|
||||
* space-1 octets */
|
||||
m_llc.put_dummy_frame(space - 1);
|
||||
|
||||
/* The data just drained, store the current fn */
|
||||
if (m_last_dl_drained_fn < 0)
|
||||
m_last_dl_drained_fn = fn;
|
||||
|
||||
/* It is not clear, when the next real data will
|
||||
* arrive, so request a DL ack/nack now */
|
||||
request_dl_ack();
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"-- Empty chunk, "
|
||||
"added LLC dummy command of size %d, "
|
||||
"drained_since=%d\n",
|
||||
m_llc.frame_length(), frames_since_last_drain(fn));
|
||||
}
|
||||
|
||||
chunk = m_llc.chunk_size();
|
||||
|
||||
/* if chunk will exceed block limit */
|
||||
if (chunk > space) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
||||
"larger than space (%d) left in block: copy "
|
||||
"only remaining space, and we are done\n",
|
||||
chunk, space);
|
||||
/* block is filled, so there is no extension */
|
||||
*e_pointer |= 0x01;
|
||||
/* fill only space */
|
||||
m_llc.consume(data, space);
|
||||
/* return data block as message */
|
||||
break;
|
||||
}
|
||||
/* if FINAL chunk would fit precisely in space left */
|
||||
if (chunk == space && llc_queue()->size() == 0 && !keep_open(fn))
|
||||
{
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
||||
"would exactly fit into space (%d): because "
|
||||
"this is a final block, we don't add length "
|
||||
"header, and we are done\n", chunk, space);
|
||||
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for "
|
||||
"%s that fits precisely in last block: "
|
||||
"len=%d\n", tbf_name(this), m_llc.frame_length());
|
||||
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
|
||||
/* block is filled, so there is no extension */
|
||||
*e_pointer |= 0x01;
|
||||
/* fill space */
|
||||
m_llc.consume(data, space);
|
||||
m_llc.reset();
|
||||
/* final block */
|
||||
rh->fbi = 1; /* we indicate final block */
|
||||
request_dl_ack();
|
||||
set_state(GPRS_RLCMAC_FINISHED);
|
||||
/* return data block as message */
|
||||
break;
|
||||
}
|
||||
/* if chunk would fit exactly in space left */
|
||||
if (chunk == space) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
||||
"would exactly fit into space (%d): add length "
|
||||
"header with LI=0, to make frame extend to "
|
||||
"next block, and we are done\n", chunk, space);
|
||||
/* make space for delimiter */
|
||||
if (delimiter != data)
|
||||
memmove(delimiter + 1, delimiter,
|
||||
data - delimiter);
|
||||
data++;
|
||||
space--;
|
||||
/* add LI with 0 length */
|
||||
li = (struct rlc_li_field *)delimiter;
|
||||
li->e = 1; /* not more extension */
|
||||
li->m = 0; /* shall be set to 0, in case of li = 0 */
|
||||
li->li = 0; /* chunk fills the complete space */
|
||||
// no need to set e_pointer nor increase delimiter
|
||||
/* fill only space, which is 1 octet less than chunk */
|
||||
m_llc.consume(data, space);
|
||||
/* return data block as message */
|
||||
break;
|
||||
}
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
|
||||
"than remaining space (%d): add length header to "
|
||||
"to delimit LLC frame\n", chunk, space);
|
||||
/* the LLC frame chunk ends in this block */
|
||||
/* make space for delimiter */
|
||||
if (delimiter != data)
|
||||
memmove(delimiter + 1, delimiter, data - delimiter);
|
||||
data++;
|
||||
space--;
|
||||
/* add LI to delimit frame */
|
||||
li = (struct rlc_li_field *)delimiter;
|
||||
li->e = 0; /* Extension bit, maybe set later */
|
||||
li->m = 0; /* will be set later, if there is more LLC data */
|
||||
li->li = chunk; /* length of chunk */
|
||||
e_pointer = delimiter; /* points to E of current delimiter */
|
||||
delimiter++;
|
||||
/* copy (rest of) LLC frame to space and reset later */
|
||||
m_llc.consume(data, chunk);
|
||||
data += chunk;
|
||||
space -= chunk;
|
||||
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
|
||||
"len=%d\n", tbf_name(this), m_llc.frame_length());
|
||||
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
|
||||
m_llc.reset();
|
||||
/* dequeue next LLC frame, if any */
|
||||
schedule_next_frame();
|
||||
/* if we have more data and we have space left */
|
||||
if (space > 0 && (m_llc.frame_length() || keep_open(fn))) {
|
||||
li->m = 1; /* we indicate more frames to follow */
|
||||
continue;
|
||||
}
|
||||
/* if we don't have more LLC frames */
|
||||
if (!m_llc.frame_length() && !keep_open(fn)) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
|
||||
"done.\n");
|
||||
li->e = 1; /* we cannot extend */
|
||||
|
||||
rh->fbi = 1; /* we indicate final block */
|
||||
request_dl_ack();
|
||||
set_state(GPRS_RLCMAC_FINISHED);
|
||||
break;
|
||||
}
|
||||
/* we have no space left */
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
|
||||
"done.\n");
|
||||
li->e = 1; /* we cannot extend */
|
||||
break;
|
||||
}
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n",
|
||||
osmo_hexdump(rlc_data->block, block_length));
|
||||
#warning "move this up?"
|
||||
rlc_data->len = block_length;
|
||||
/* raise send state and set ack state array */
|
||||
m_window.m_v_b.mark_unacked(bsn);
|
||||
m_window.increment_send();
|
||||
|
||||
return create_dl_acked_block(fn, ts, bsn);
|
||||
}
|
||||
|
||||
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
||||
const uint32_t fn, const uint8_t ts,
|
||||
const int index)
|
||||
{
|
||||
uint8_t *data;
|
||||
struct rlc_dl_header *rh;
|
||||
struct msgb *dl_msg;
|
||||
uint8_t len;
|
||||
bool need_poll;
|
||||
|
||||
/* get data and header from current block */
|
||||
data = m_rlc.block(index)->block;
|
||||
len = m_rlc.block(index)->len;
|
||||
rh = (struct rlc_dl_header *)data;
|
||||
|
||||
/* If the TBF has just started, relate frames_since_last_poll to the
|
||||
* current fn */
|
||||
if (m_last_dl_poll_fn < 0)
|
||||
m_last_dl_poll_fn = fn;
|
||||
|
||||
need_poll = state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
|
||||
/* Clear Polling, if still set in history buffer */
|
||||
rh->s_p = 0;
|
||||
|
||||
/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
|
||||
*/
|
||||
if (m_tx_counter >= POLL_ACK_AFTER_FRAMES || m_dl_ack_requested ||
|
||||
need_poll) {
|
||||
if (m_dl_ack_requested) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
|
||||
"polling, because is was requested explicitly "
|
||||
"(e.g. first final block sent).\n");
|
||||
} else if (need_poll) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
|
||||
"polling, because polling timed out.\n");
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
|
||||
"polling, because %d blocks sent.\n",
|
||||
POLL_ACK_AFTER_FRAMES);
|
||||
}
|
||||
/* scheduling not possible, because: */
|
||||
if (poll_state != GPRS_RLCMAC_POLL_NONE)
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
|
||||
"sheduled for %s, so we must wait for "
|
||||
"requesting downlink ack\n", tbf_name(this));
|
||||
else if (control_ts != ts)
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
|
||||
"sheduled in this TS %d, waiting for "
|
||||
"TS %d\n", ts, control_ts);
|
||||
else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648))
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
|
||||
"sheduled, because single block alllocation "
|
||||
"already exists\n");
|
||||
else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this "
|
||||
"TS %d\n", ts);
|
||||
m_tx_counter = 0;
|
||||
/* start timer whenever we send the final block */
|
||||
if (rh->fbi == 1)
|
||||
tbf_timer_start(this, 3191, bts_data()->t3191, 0);
|
||||
|
||||
/* schedule polling */
|
||||
poll_state = GPRS_RLCMAC_POLL_SCHED;
|
||||
poll_fn = (fn + 13) % 2715648;
|
||||
|
||||
/* Clear poll timeout flag */
|
||||
state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
|
||||
|
||||
/* Clear request flag */
|
||||
m_dl_ack_requested = false;
|
||||
|
||||
/* set polling in header */
|
||||
rh->rrbp = 0; /* N+13 */
|
||||
rh->s_p = 1; /* Polling */
|
||||
|
||||
m_last_dl_poll_fn = poll_fn;
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"%s Scheduled Ack/Nack polling on FN=%d, TS=%d\n",
|
||||
name(), poll_fn, ts);
|
||||
}
|
||||
}
|
||||
|
||||
/* return data block as message */
|
||||
dl_msg = msgb_alloc(len, "rlcmac_dl_data");
|
||||
if (!dl_msg)
|
||||
return NULL;
|
||||
|
||||
/* Increment TX-counter */
|
||||
m_tx_counter++;
|
||||
|
||||
memcpy(msgb_put(dl_msg, len), data, len);
|
||||
bts->rlc_sent();
|
||||
|
||||
return dl_msg;
|
||||
}
|
||||
|
||||
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns)
|
||||
{
|
||||
return (ssn - 1 - bitnum) & mod_sns;
|
||||
}
|
||||
|
||||
int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn)
|
||||
{
|
||||
gprs_rlc_data *rlc_data;
|
||||
uint16_t lost = 0, received = 0, skipped = 0;
|
||||
char info[65];
|
||||
memset(info, '.', sizeof(info));
|
||||
info[64] = 0;
|
||||
uint16_t bsn = 0;
|
||||
|
||||
/* SSN - 1 is in range V(A)..V(S)-1 */
|
||||
for (int bitpos = 0; bitpos < m_window.ws(); bitpos++) {
|
||||
bsn = bitnum_to_bsn(bitpos, ssn, m_window.mod_sns());
|
||||
|
||||
if (bsn == ((m_window.v_a() - 1) & m_window.mod_sns())) {
|
||||
info[bitpos] = '$';
|
||||
break;
|
||||
}
|
||||
|
||||
rlc_data = m_rlc.block(bsn);
|
||||
if (!rlc_data) {
|
||||
info[bitpos] = '0';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rlc_data->cs != current_cs()) {
|
||||
/* This block has already been encoded with a different
|
||||
* CS, so it doesn't help us to decide, whether the
|
||||
* current CS is ok. Ignore it. */
|
||||
info[bitpos] = 'x';
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (show_rbb[m_window.ws() - 1 - bitpos] == 'R') {
|
||||
if (!m_window.m_v_b.is_acked(bsn)) {
|
||||
received += 1;
|
||||
info[bitpos] = 'R';
|
||||
} else {
|
||||
info[bitpos] = 'r';
|
||||
}
|
||||
} else {
|
||||
info[bitpos] = 'L';
|
||||
lost += 1;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "%s DL analysis, range=%d:%d, lost=%d, recv=%d, "
|
||||
"skipped=%d, bsn=%d, info='%s'\n",
|
||||
name(), m_window.v_a(), m_window.v_s(), lost, received,
|
||||
skipped, bsn, info);
|
||||
|
||||
if (lost + received <= 1)
|
||||
return -1;
|
||||
|
||||
return lost * 100 / (lost + received);
|
||||
}
|
||||
|
||||
|
||||
int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
|
||||
{
|
||||
int16_t dist; /* must be signed */
|
||||
uint16_t lost = 0, received = 0;
|
||||
char show_rbb[65];
|
||||
char show_v_b[RLC_MAX_SNS + 1];
|
||||
const uint16_t mod_sns = m_window.mod_sns();
|
||||
int error_rate;
|
||||
|
||||
Decoding::extract_rbb(rbb, show_rbb);
|
||||
/* show received array in debug (bit 64..1) */
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\""
|
||||
"(BSN=%d) R=ACK I=NACK\n", (ssn - 64) & mod_sns,
|
||||
show_rbb, (ssn - 1) & mod_sns);
|
||||
|
||||
/* apply received array to receive state (SSN-64..SSN-1) */
|
||||
/* calculate distance of ssn from V(S) */
|
||||
dist = (m_window.v_s() - ssn) & mod_sns;
|
||||
/* check if distance is less than distance V(A)..V(S) */
|
||||
if (dist >= m_window.distance()) {
|
||||
/* this might happpen, if the downlink assignment
|
||||
* was not received by ms and the ack refers
|
||||
* to previous TBF
|
||||
* FIXME: we should implement polling for
|
||||
* control ack!*/
|
||||
LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
|
||||
"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
|
||||
return 1; /* indicate to free TBF */
|
||||
}
|
||||
|
||||
if (bts_data()->cs_adj_enabled && ms()) {
|
||||
error_rate = analyse_errors(show_rbb, ssn);
|
||||
ms()->update_error_rate(this, error_rate);
|
||||
}
|
||||
|
||||
m_window.update(bts, show_rbb, ssn,
|
||||
&lost, &received);
|
||||
|
||||
/* report lost and received packets */
|
||||
gprs_rlcmac_received_lost(this, received, lost);
|
||||
|
||||
/* raise V(A), if possible */
|
||||
m_window.raise(m_window.move_window());
|
||||
|
||||
/* show receive state array in debug (V(A)..V(S)-1) */
|
||||
m_window.show_state(show_v_b);
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\""
|
||||
"(V(S)-1=%d) A=Acked N=Nacked U=Unacked "
|
||||
"X=Resend-Unacked I=Invalid\n",
|
||||
m_window.v_a(), show_v_b,
|
||||
m_window.v_s_mod(-1));
|
||||
|
||||
if (state_is(GPRS_RLCMAC_FINISHED) && m_window.window_empty()) {
|
||||
LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of "
|
||||
"all blocks, but without final ack "
|
||||
"inidcation (don't worry)\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int gprs_rlcmac_dl_tbf::maybe_start_new_window()
|
||||
{
|
||||
release();
|
||||
|
||||
/* check for LLC PDU in the LLC Queue */
|
||||
if (llc_queue()->size() > 0)
|
||||
/* we have more data so we will re-use this tbf */
|
||||
establish_dl_tbf_on_pacch();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gprs_rlcmac_dl_tbf::release()
|
||||
{
|
||||
uint16_t received;
|
||||
|
||||
/* range V(A)..V(S)-1 */
|
||||
received = m_window.count_unacked();
|
||||
|
||||
/* report all outstanding packets as received */
|
||||
gprs_rlcmac_received_lost(this, received, 0);
|
||||
|
||||
set_state(GPRS_RLCMAC_WAIT_RELEASE);
|
||||
|
||||
/* start T3193 */
|
||||
tbf_timer_start(this, 3193,
|
||||
bts_data()->t3193_msec / 1000,
|
||||
(bts_data()->t3193_msec % 1000) * 1000);
|
||||
|
||||
/* reset rlc states */
|
||||
m_tx_counter = 0;
|
||||
m_wait_confirm = 0;
|
||||
m_window.reset();
|
||||
|
||||
/* keep to flags */
|
||||
state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
|
||||
state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb)
|
||||
{
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink acknowledge\n", tbf_name(this));
|
||||
|
||||
if (!final_ack)
|
||||
return update_window(ssn, rbb);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n");
|
||||
return maybe_start_new_window();
|
||||
}
|
||||
|
||||
bool gprs_rlcmac_dl_tbf::dl_window_stalled() const
|
||||
{
|
||||
return m_window.window_stalled();
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::request_dl_ack()
|
||||
{
|
||||
m_dl_ack_requested = true;
|
||||
}
|
||||
|
||||
bool gprs_rlcmac_dl_tbf::need_control_ts() const
|
||||
{
|
||||
if (poll_state != GPRS_RLCMAC_POLL_NONE)
|
||||
return false;
|
||||
|
||||
return state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK) ||
|
||||
m_tx_counter >= POLL_ACK_AFTER_FRAMES ||
|
||||
m_dl_ack_requested;
|
||||
}
|
||||
|
||||
bool gprs_rlcmac_dl_tbf::have_data() const
|
||||
{
|
||||
return m_llc.chunk_size() > 0 ||
|
||||
(llc_queue() && llc_queue()->size() > 0);
|
||||
}
|
||||
|
||||
int gprs_rlcmac_dl_tbf::frames_since_last_poll(unsigned fn) const
|
||||
{
|
||||
unsigned wrapped;
|
||||
if (m_last_dl_poll_fn < 0)
|
||||
return -1;
|
||||
|
||||
wrapped = (fn + 2715648 - m_last_dl_poll_fn) % 2715648;
|
||||
if (wrapped < 2715648/2)
|
||||
return wrapped;
|
||||
else
|
||||
return wrapped - 2715648;
|
||||
}
|
||||
|
||||
int gprs_rlcmac_dl_tbf::frames_since_last_drain(unsigned fn) const
|
||||
{
|
||||
unsigned wrapped;
|
||||
if (m_last_dl_drained_fn < 0)
|
||||
return -1;
|
||||
|
||||
wrapped = (fn + 2715648 - m_last_dl_drained_fn) % 2715648;
|
||||
if (wrapped < 2715648/2)
|
||||
return wrapped;
|
||||
else
|
||||
return wrapped - 2715648;
|
||||
}
|
||||
|
||||
bool gprs_rlcmac_dl_tbf::keep_open(unsigned fn) const
|
||||
{
|
||||
int keep_time_frames;
|
||||
|
||||
if (bts_data()->dl_tbf_idle_msec <= 0)
|
||||
return false;
|
||||
|
||||
keep_time_frames = msecs_to_frames(bts_data()->dl_tbf_idle_msec);
|
||||
return frames_since_last_drain(fn) <= keep_time_frames;
|
||||
}
|
||||
425
src/tbf_ul.cpp
Normal file
425
src/tbf_ul.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/* Copied from tbf.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
#include <rlc.h>
|
||||
#include <encoding.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <decoding.h>
|
||||
#include <pcu_l1_if.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
/* After receiving these frames, we send ack/nack. */
|
||||
#define SEND_ACK_AFTER_FRAMES 20
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
|
||||
/*
|
||||
* Store received block data in LLC message(s) and forward to SGSN
|
||||
* if complete.
|
||||
*/
|
||||
int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
|
||||
{
|
||||
const uint8_t *data = _data->block;
|
||||
uint8_t len = _data->len;
|
||||
const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data;
|
||||
uint8_t e, m;
|
||||
struct rlc_li_field *li;
|
||||
uint8_t frame_offset[16], offset = 0, chunk;
|
||||
int i, frames = 0;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
|
||||
|
||||
data += 3;
|
||||
len -= 3;
|
||||
e = rh->e; /* if extended */
|
||||
m = 1; /* more frames, that means: the first frame */
|
||||
|
||||
/* Parse frame offsets from length indicator(s), if any. */
|
||||
while (1) {
|
||||
if (frames == (int)sizeof(frame_offset)) {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in "
|
||||
"block\n", tbf_name(this));
|
||||
return -EINVAL;
|
||||
}
|
||||
frame_offset[frames++] = offset;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
|
||||
"%d\n", frames, offset);
|
||||
if (!len)
|
||||
break;
|
||||
/* M == 0 and E == 0 is not allowed in this version. */
|
||||
if (!m && !e) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA "
|
||||
"ignored, because M='0' and E='0'.\n",
|
||||
tbf_name(this));
|
||||
return 0;
|
||||
}
|
||||
/* no more frames in this segment */
|
||||
if (e) {
|
||||
break;
|
||||
}
|
||||
/* There is a new frame and an LI that delimits it. */
|
||||
if (m) {
|
||||
li = (struct rlc_li_field *)data;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
|
||||
li->li);
|
||||
/* Special case: LI == 0
|
||||
* If the last segment would fit precisely into the
|
||||
* rest of the RLC MAC block, there would be no way
|
||||
* to delimit that this segment ends and is not
|
||||
* continued in the next block.
|
||||
* The special LI (0) is used to force the segment to
|
||||
* extend into the next block, so it is delimited there.
|
||||
* This LI must be skipped. Also it is the last LI.
|
||||
*/
|
||||
if (li->li == 0) {
|
||||
data++;
|
||||
len--;
|
||||
m = 1; /* M is ignored, we know there is more */
|
||||
break; /* handle E as '1', so we break! */
|
||||
}
|
||||
e = li->e;
|
||||
m = li->m;
|
||||
offset += li->li;
|
||||
data++;
|
||||
len--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!m) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
|
||||
"data\n");
|
||||
}
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
|
||||
len);
|
||||
/* TLLI */
|
||||
if (rh->ti) {
|
||||
if (len < 4) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of "
|
||||
"frame border\n", tbf_name(this));
|
||||
return -EINVAL;
|
||||
}
|
||||
data += 4;
|
||||
len -= 4;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
|
||||
"%d\n", len);
|
||||
}
|
||||
|
||||
/* PFI */
|
||||
if (rh->pi) {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
|
||||
"please disable in SYSTEM INFORMATION\n");
|
||||
if (len < 1) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of "
|
||||
"frame border\n", tbf_name(this));
|
||||
return -EINVAL;
|
||||
}
|
||||
data++;
|
||||
len--;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
|
||||
"%d\n", len);
|
||||
}
|
||||
|
||||
/* Now we have:
|
||||
* - a list of frames offsets: frame_offset[]
|
||||
* - number of frames: i
|
||||
* - m == 0: Last frame carries spare data (end of TBF).
|
||||
*/
|
||||
|
||||
/* Check if last offset would exceed frame. */
|
||||
if (offset > len) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, "
|
||||
"because LI delimits data that exceeds block size.\n",
|
||||
tbf_name(this));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* create LLC frames */
|
||||
for (i = 0; i < frames; i++) {
|
||||
/* last frame ? */
|
||||
if (i == frames - 1) {
|
||||
/* no more data in last frame */
|
||||
if (!m)
|
||||
break;
|
||||
/* data until end of frame */
|
||||
chunk = len - frame_offset[i];
|
||||
} else {
|
||||
/* data until next frame */
|
||||
chunk = frame_offset[i + 1] - frame_offset[i];
|
||||
}
|
||||
if (!m_llc.fits_in_current_frame(chunk)) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds "
|
||||
"maximum size %u.\n", tbf_name(this),
|
||||
m_llc.remaining_space());
|
||||
chunk = m_llc.remaining_space();
|
||||
}
|
||||
m_llc.append_frame(data + frame_offset[i], chunk);
|
||||
m_llc.consume(chunk);
|
||||
/* not last frame. */
|
||||
if (i != frames - 1) {
|
||||
/* send frame to SGSN */
|
||||
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
|
||||
tbf_name(this) , m_llc.frame_length());
|
||||
snd_ul_ud();
|
||||
m_llc.reset();
|
||||
/* also check if CV==0, because the frame may fill up the
|
||||
* block precisely, then it is also complete. normally the
|
||||
* frame would be extended into the next block with a 0-length
|
||||
* delimiter added to this block. */
|
||||
} else if (rh->cv == 0) {
|
||||
/* send frame to SGSN */
|
||||
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame "
|
||||
"that fits precisely in last block: "
|
||||
"len=%d\n", tbf_name(this), m_llc.frame_length());
|
||||
snd_ul_ud();
|
||||
m_llc.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
|
||||
{
|
||||
int final = (state_is(GPRS_RLCMAC_FINISHED));
|
||||
struct msgb *msg;
|
||||
|
||||
if (final) {
|
||||
if (poll_state != GPRS_RLCMAC_POLL_NONE) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
|
||||
"sheduled for %s, so we must wait for "
|
||||
"final uplink ack...\n", tbf_name(this));
|
||||
return NULL;
|
||||
}
|
||||
if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
|
||||
"scheduled for single block allocation...\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
msg = msgb_alloc(23, "rlcmac_ul_ack");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
bitvec *ack_vec = bitvec_alloc(23);
|
||||
if (!ack_vec) {
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
bitvec_unhex(ack_vec,
|
||||
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
|
||||
Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final);
|
||||
encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
|
||||
bitvec_pack(ack_vec, msgb_put(msg, 23));
|
||||
bitvec_free(ack_vec);
|
||||
talloc_free(mac_control_block);
|
||||
|
||||
/* now we must set this flag, so we are allowed to assign downlink
|
||||
* TBF on PACCH. it is only allowed when TLLI is acknowledged. */
|
||||
m_contention_resolution_done = 1;
|
||||
|
||||
if (final) {
|
||||
poll_state = GPRS_RLCMAC_POLL_SCHED;
|
||||
poll_fn = (fn + 13) % 2715648;
|
||||
/* waiting for final acknowledge */
|
||||
ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
|
||||
m_final_ack_sent = 1;
|
||||
} else
|
||||
ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data,
|
||||
size_t len, struct pcu_l1_meas *meas)
|
||||
{
|
||||
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
|
||||
int rc;
|
||||
int8_t rssi = meas->have_rssi ? meas->rssi : 0;
|
||||
|
||||
const uint16_t mod_sns = m_window.mod_sns();
|
||||
const uint16_t ws = m_window.ws();
|
||||
|
||||
this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA);
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
|
||||
"V(R)=%d)\n", rh->tfi, this->m_window.v_q(),
|
||||
this->m_window.v_r());
|
||||
|
||||
/* process RSSI */
|
||||
gprs_rlcmac_rssi(this, rssi);
|
||||
|
||||
/* store measurement values */
|
||||
if (ms())
|
||||
ms()->update_l1_meas(meas);
|
||||
|
||||
/* get TLLI */
|
||||
if (!this->is_tlli_valid()) {
|
||||
if (!extract_tlli(data, len))
|
||||
return 0;
|
||||
/* already have TLLI, but we stille get another one */
|
||||
} else if (rh->ti) {
|
||||
uint32_t tlli;
|
||||
rc = Decoding::tlli_from_ul_data(data, len, &tlli);
|
||||
if (rc) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
|
||||
"of UL DATA TFI=%d.\n", rh->tfi);
|
||||
return 0;
|
||||
}
|
||||
if (tlli != this->tlli()) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
|
||||
"DATA TFI=%d. (Ignoring due to contention "
|
||||
"resolution)\n", rh->tfi);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* restart T3169 */
|
||||
tbf_timer_start(this, 3169, bts_data()->t3169, 0);
|
||||
|
||||
/* Increment RX-counter */
|
||||
this->m_rx_counter++;
|
||||
|
||||
if (!m_window.is_in_window(rh->bsn)) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
|
||||
"%d..%d (it's normal)\n", rh->bsn,
|
||||
m_window.v_q(),
|
||||
(m_window.v_q() + ws - 1) & mod_sns);
|
||||
maybe_schedule_uplink_acknack(rh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write block to buffer and set receive state array. */
|
||||
m_rlc.block(rh->bsn)->put_data(data, len);
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
|
||||
rh->bsn, m_window.v_q(),
|
||||
(m_window.v_q() + ws - 1) & mod_sns);
|
||||
|
||||
/* Raise V(Q) if possible, and retrieve LLC frames from blocks.
|
||||
* This is looped until there is a gap (non received block) or
|
||||
* the window is empty.*/
|
||||
const uint16_t v_q_beg = m_window.v_q();
|
||||
|
||||
const uint16_t count = m_window.receive_bsn(rh->bsn);
|
||||
|
||||
/* Retrieve LLC frames from blocks that are ready */
|
||||
for (uint16_t i = 0; i < count; ++i) {
|
||||
uint16_t index = (v_q_beg + i) & mod_sns;
|
||||
assemble_forward_llc(m_rlc.block(index));
|
||||
}
|
||||
|
||||
/* Check CV of last frame in buffer */
|
||||
if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
|
||||
&& this->m_window.v_q() == this->m_window.v_r()) { /* if complete */
|
||||
struct rlc_ul_header *last_rh = (struct rlc_ul_header *)
|
||||
m_rlc.block((m_window.v_r() - 1) & mod_sns)->block;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, "
|
||||
"last block: BSN=%d CV=%d\n", last_rh->bsn,
|
||||
last_rh->cv);
|
||||
if (last_rh->cv == 0) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL "
|
||||
"TBF\n");
|
||||
set_state(GPRS_RLCMAC_FINISHED);
|
||||
/* Reset N3103 counter. */
|
||||
this->m_n3103 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If TLLI is included or if we received half of the window, we send
|
||||
* an ack/nack */
|
||||
maybe_schedule_uplink_acknack(rh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
|
||||
{
|
||||
if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED)
|
||||
|| (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
|
||||
if (rh->si) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
|
||||
"because MS is stalled.\n");
|
||||
}
|
||||
if (rh->ti) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
|
||||
"because TLLI is included.\n");
|
||||
}
|
||||
if (state_is(GPRS_RLCMAC_FINISHED)) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
|
||||
"because last block has CV==0.\n");
|
||||
}
|
||||
if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
|
||||
"because %d frames received.\n",
|
||||
SEND_ACK_AFTER_FRAMES);
|
||||
}
|
||||
if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
|
||||
/* trigger sending at next RTS */
|
||||
ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
|
||||
} else {
|
||||
/* already triggered */
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is "
|
||||
"already triggered, don't schedule!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send Uplink unit-data to SGSN. */
|
||||
int gprs_rlcmac_ul_tbf::snd_ul_ud()
|
||||
{
|
||||
uint8_t qos_profile[3];
|
||||
struct msgb *llc_pdu;
|
||||
unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length();
|
||||
struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx();
|
||||
|
||||
LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length());
|
||||
if (!bctx) {
|
||||
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
|
||||
m_llc.reset_frame_space();
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
|
||||
uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length()));
|
||||
tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame);
|
||||
qos_profile[0] = QOS_PROFILE >> 16;
|
||||
qos_profile[1] = QOS_PROFILE >> 8;
|
||||
qos_profile[2] = QOS_PROFILE;
|
||||
bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu);
|
||||
|
||||
m_llc.reset_frame_space();
|
||||
return 0;
|
||||
}
|
||||
|
||||
140
tests/Makefile.am
Normal file
140
tests/Makefile.am
Normal file
@@ -0,0 +1,140 @@
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
|
||||
AM_LDFLAGS = -lrt
|
||||
|
||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test
|
||||
noinst_PROGRAMS = emu/pcu_emu
|
||||
|
||||
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
|
||||
rlcmac_RLCMACTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
alloc_AllocTest_SOURCES = alloc/AllocTest.cpp
|
||||
alloc_AllocTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
tbf_TbfTest_SOURCES = tbf/TbfTest.cpp
|
||||
tbf_TbfTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp \
|
||||
emu/openbsc_clone.c emu/openbsc_clone.h emu/gprs_tests.h \
|
||||
emu/test_pdp_activation.cpp
|
||||
emu_pcu_emu_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
types_TypesTest_SOURCES = types/TypesTest.cpp
|
||||
types_TypesTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
ms_MsTest_SOURCES = ms/MsTest.cpp
|
||||
ms_MsTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
ms_MsTest_LDFLAGS = \
|
||||
-Wl,-u,bssgp_prim_cb
|
||||
|
||||
llc_LlcTest_SOURCES = llc/LlcTest.cpp
|
||||
llc_LlcTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
llc_LlcTest_LDFLAGS = \
|
||||
-Wl,-u,bssgp_prim_cb
|
||||
|
||||
llist_LListTest_SOURCES = llist/LListTest.cpp
|
||||
llist_LListTest_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
codel_codel_test_SOURCES = codel/codel_test.c
|
||||
codel_codel_test_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
|
||||
:;{ \
|
||||
echo '# Signature of the current package.' && \
|
||||
echo 'm4_define([AT_PACKAGE_NAME],' && \
|
||||
echo ' [$(PACKAGE_NAME)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
|
||||
echo ' [$(PACKAGE_TARNAME)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_VERSION],' && \
|
||||
echo ' [$(PACKAGE_VERSION)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_STRING],' && \
|
||||
echo ' [$(PACKAGE_STRING)])' && \
|
||||
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
|
||||
echo ' [$(PACKAGE_BUGREPORT)])'; \
|
||||
echo 'm4_define([AT_PACKAGE_URL],' && \
|
||||
echo ' [$(PACKAGE_URL)])'; \
|
||||
} >'$(srcdir)/package.m4'
|
||||
|
||||
EXTRA_DIST = \
|
||||
testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
|
||||
rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err \
|
||||
alloc/AllocTest.ok alloc/AllocTest.err \
|
||||
tbf/TbfTest.ok tbf/TbfTest.err \
|
||||
types/TypesTest.ok types/TypesTest.err \
|
||||
ms/MsTest.ok ms/MsTest.err \
|
||||
llc/LlcTest.ok llc/LlcTest.err \
|
||||
llist/LListTest.ok llist/LListTest.err \
|
||||
codel/codel_test.ok
|
||||
|
||||
DISTCLEANFILES = atconfig
|
||||
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
|
||||
# Python testing
|
||||
if ENABLE_VTY_TESTS
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
|
||||
else
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
@echo "Not running python-based tests (determined at configure-time)"
|
||||
endif
|
||||
|
||||
check-local: $(BUILT_SOURCES) $(TESTSUITE)
|
||||
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
|
||||
$(MAKE) $(AM_MAKEFLAGS) python-tests
|
||||
|
||||
installcheck-local: atconfig $(TESTSUITE)
|
||||
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
|
||||
$(TESTSUITEFLAGS)
|
||||
|
||||
clean-local:
|
||||
test ! -f '$(TESTSUITE)' || \
|
||||
$(SHELL) '$(TESTSUITE)' --clean
|
||||
|
||||
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
|
||||
AUTOTEST = $(AUTOM4TE) --language=autotest
|
||||
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
|
||||
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
|
||||
mv $@.tmp $@
|
||||
821
tests/alloc/AllocTest.cpp
Normal file
821
tests/alloc/AllocTest.cpp
Normal file
@@ -0,0 +1,821 @@
|
||||
/* AllocTest.cpp
|
||||
*
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "gprs_rlcmac.h"
|
||||
#include "gprs_debug.h"
|
||||
#include "tbf.h"
|
||||
#include "bts.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
}
|
||||
|
||||
/* globals used by the code */
|
||||
void *tall_pcu_ctx;
|
||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
|
||||
|
||||
static gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts,
|
||||
GprsMs *ms, gprs_rlcmac_tbf_direction dir,
|
||||
uint8_t use_trx,
|
||||
uint8_t ms_class, uint8_t single_slot)
|
||||
{
|
||||
if (dir == GPRS_RLCMAC_UL_TBF)
|
||||
return tbf_alloc_ul_tbf(bts, ms, use_trx, ms_class, single_slot);
|
||||
else
|
||||
return tbf_alloc_dl_tbf(bts, ms, use_trx, ms_class, single_slot);
|
||||
}
|
||||
|
||||
static void check_tfi_usage(BTS *the_bts)
|
||||
{
|
||||
int pdch_no;
|
||||
struct gprs_rlcmac_bts *bts = the_bts->bts_data();
|
||||
|
||||
struct gprs_rlcmac_tbf *tfi_usage[8][8][2][32] = {{{{NULL}}}};
|
||||
struct llist_head *tbf_lists[2] = {
|
||||
&bts->ul_tbfs,
|
||||
&bts->dl_tbfs
|
||||
};
|
||||
|
||||
gprs_rlcmac_tbf *tbf;
|
||||
struct llist_pods *lpods;
|
||||
unsigned list_idx;
|
||||
struct gprs_rlcmac_tbf **tbf_var;
|
||||
|
||||
for (list_idx = 0; list_idx < ARRAY_SIZE(tbf_lists); list_idx += 1)
|
||||
{
|
||||
|
||||
llist_pods_for_each_entry(tbf, tbf_lists[list_idx], list, lpods) {
|
||||
for (pdch_no = 0; pdch_no < 8; pdch_no += 1) {
|
||||
struct gprs_rlcmac_pdch *pdch = tbf->pdch[pdch_no];
|
||||
if (pdch == NULL)
|
||||
continue;
|
||||
|
||||
tbf_var = &tfi_usage
|
||||
[tbf->trx->trx_no]
|
||||
[pdch_no]
|
||||
[tbf->direction]
|
||||
[tbf->tfi()];
|
||||
|
||||
OSMO_ASSERT(*tbf_var == NULL);
|
||||
if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
|
||||
OSMO_ASSERT(pdch->dl_tbf_by_tfi(
|
||||
tbf->tfi()) == tbf);
|
||||
OSMO_ASSERT(the_bts->dl_tbf_by_tfi(
|
||||
tbf->tfi(),
|
||||
tbf->trx->trx_no,
|
||||
pdch_no) == tbf);
|
||||
} else {
|
||||
OSMO_ASSERT(pdch->ul_tbf_by_tfi(
|
||||
tbf->tfi()) == tbf);
|
||||
OSMO_ASSERT(the_bts->ul_tbf_by_tfi(
|
||||
tbf->tfi(),
|
||||
tbf->trx->trx_no,
|
||||
pdch_no) == tbf);
|
||||
}
|
||||
*tbf_var = tbf;
|
||||
OSMO_ASSERT(pdch->assigned_tfi(tbf->direction) &
|
||||
(1 << tbf->tfi()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
|
||||
uint8_t slots, const int count)
|
||||
{
|
||||
int tfi;
|
||||
int i;
|
||||
uint8_t used_trx, tmp_trx;
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_tbf *tbfs[32*8+1] = { 0, };
|
||||
|
||||
printf("Testing alloc_a direction(%d)\n", dir);
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_a;
|
||||
|
||||
struct gprs_rlcmac_trx *trx = &bts->trx[0];
|
||||
for (i = 0; i < 8; i += 1)
|
||||
if (slots & (1 << i))
|
||||
trx->pdch[i].enable();
|
||||
|
||||
OSMO_ASSERT(count >= 0 && count <= (int)ARRAY_SIZE(tbfs));
|
||||
|
||||
/**
|
||||
* Currently alloc_a will only allocate from the first
|
||||
* PDCH and all possible usf's. We run out of usf's before
|
||||
* we are out of tfi's. Observe this and make sure that at
|
||||
* least this part is working okay.
|
||||
*/
|
||||
for (i = 0; i < (int)ARRAY_SIZE(tbfs); ++i) {
|
||||
tbfs[i] = tbf_alloc(bts, NULL, dir, -1, 0, 0);
|
||||
if (tbfs[i] == NULL)
|
||||
break;
|
||||
|
||||
used_trx = tbfs[i]->trx->trx_no;
|
||||
tfi = the_bts.tfi_find_free(dir, &tmp_trx, used_trx);
|
||||
OSMO_ASSERT(tbfs[i]->tfi() != tfi);
|
||||
}
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
|
||||
OSMO_ASSERT(i == count);
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
if (tbfs[i])
|
||||
tbf_free(tbfs[i]);
|
||||
|
||||
tbfs[tfi] = tbf_alloc(bts, NULL, dir, -1, 0, 0);
|
||||
OSMO_ASSERT(tbfs[tfi]);
|
||||
tbf_free(tbfs[tfi]);
|
||||
}
|
||||
|
||||
static void test_alloc_a()
|
||||
{
|
||||
/* slots 2 - 3 */
|
||||
test_alloc_a(GPRS_RLCMAC_DL_TBF, 0x0c, 32*2);
|
||||
test_alloc_a(GPRS_RLCMAC_UL_TBF, 0x0c, 14);
|
||||
|
||||
/* slots 1 - 5 */
|
||||
test_alloc_a(GPRS_RLCMAC_DL_TBF, 0x1e, 32*4);
|
||||
test_alloc_a(GPRS_RLCMAC_UL_TBF, 0x1e, 28);
|
||||
}
|
||||
|
||||
static void dump_assignment(struct gprs_rlcmac_tbf *tbf, const char *dir)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(tbf->pdch); ++i)
|
||||
if (tbf->pdch[i])
|
||||
printf("PDCH[%d] is used for %s\n", i, dir);
|
||||
printf("PDCH[%d] is control_ts for %s\n", tbf->control_ts, dir);
|
||||
printf("PDCH[%d] is first common for %s\n", tbf->first_common_ts, dir);
|
||||
}
|
||||
|
||||
static void test_alloc_b(int ms_class)
|
||||
{
|
||||
printf("Going to test multislot assignment MS_CLASS=%d\n", ms_class);
|
||||
/*
|
||||
* PDCH is on TS 6,7,8 and we start with a UL allocation and
|
||||
* then follow two DL allocations (once single, once normal).
|
||||
*
|
||||
* Uplink assigned and still available..
|
||||
*/
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t trx_no;
|
||||
|
||||
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
|
||||
|
||||
printf("Testing UL then DL assignment.\n");
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[5].enable();
|
||||
trx->pdch[6].enable();
|
||||
trx->pdch[7].enable();
|
||||
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 1);
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
OSMO_ASSERT(ul_tbf->ms());
|
||||
OSMO_ASSERT(ul_tbf->ms()->current_trx());
|
||||
trx_no = ul_tbf->ms()->current_trx()->trx_no;
|
||||
dump_assignment(ul_tbf, "UL");
|
||||
|
||||
/* assume final ack has not been sent */
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
dump_assignment(dl_tbf, "DL");
|
||||
|
||||
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
|
||||
tbf_free(dl_tbf);
|
||||
tbf_free(ul_tbf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with the other order.. first DL and then UL
|
||||
*/
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t trx_no;
|
||||
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
|
||||
printf("Testing DL then UL assignment followed by update\n");
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[5].enable();
|
||||
trx->pdch[6].enable();
|
||||
trx->pdch[7].enable();
|
||||
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 1);
|
||||
dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
OSMO_ASSERT(dl_tbf->ms());
|
||||
OSMO_ASSERT(dl_tbf->ms()->current_trx());
|
||||
trx_no = dl_tbf->ms()->current_trx()->trx_no;
|
||||
dump_assignment(dl_tbf, "DL");
|
||||
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0);
|
||||
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
|
||||
ul_tbf->m_contention_resolution_done = 1;
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
dump_assignment(ul_tbf, "UL");
|
||||
|
||||
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
|
||||
|
||||
/* now update the dl_tbf */
|
||||
dl_tbf->update();
|
||||
dump_assignment(dl_tbf, "DL");
|
||||
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
|
||||
tbf_free(dl_tbf);
|
||||
tbf_free(ul_tbf);
|
||||
}
|
||||
|
||||
/* Andreas osmocom-pcu example */
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
int tfi;
|
||||
uint8_t trx_no;
|
||||
|
||||
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
|
||||
|
||||
printf("Testing jolly example\n");
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[1].enable();
|
||||
trx->pdch[2].enable();
|
||||
trx->pdch[3].enable();
|
||||
trx->pdch[4].enable();
|
||||
|
||||
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
|
||||
OSMO_ASSERT(tfi >= 0);
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, .1, ms_class, 0);
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
OSMO_ASSERT(ul_tbf->ms());
|
||||
OSMO_ASSERT(ul_tbf->ms()->current_trx());
|
||||
trx_no = ul_tbf->ms()->current_trx()->trx_no;
|
||||
dump_assignment(ul_tbf, "UL");
|
||||
|
||||
/* assume final ack has not been sent */
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
dump_assignment(dl_tbf, "DL");
|
||||
|
||||
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
|
||||
tbf_free(dl_tbf);
|
||||
tbf_free(ul_tbf);
|
||||
}
|
||||
}
|
||||
|
||||
#define ENABLE_PDCH(ts_no, enable_flag, trx) \
|
||||
if (enable_flag) \
|
||||
trx->pdch[ts_no].enable();
|
||||
|
||||
static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7, int ms_class)
|
||||
{
|
||||
/* we can test the allocation failures differently */
|
||||
if (!ts0 && !ts1 && !ts2 && !ts3 && !ts4 && !ts5 && !ts6 && !ts7)
|
||||
return;
|
||||
|
||||
printf("Mass test: TS0(%c%c%c%c%c%c%c%c)TS7 MS_Class=%d\n",
|
||||
ts0 ? 'O' : 'x',
|
||||
ts1 ? 'O' : 'x',
|
||||
ts2 ? 'O' : 'x',
|
||||
ts3 ? 'O' : 'x',
|
||||
ts4 ? 'O' : 'x',
|
||||
ts5 ? 'O' : 'x',
|
||||
ts6 ? 'O' : 'x',
|
||||
ts7 ? 'O' : 'x', ms_class);
|
||||
fflush(stdout);
|
||||
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t trx_no;
|
||||
|
||||
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
ENABLE_PDCH(0, ts0, trx);
|
||||
ENABLE_PDCH(1, ts1, trx);
|
||||
ENABLE_PDCH(2, ts2, trx);
|
||||
ENABLE_PDCH(3, ts3, trx);
|
||||
ENABLE_PDCH(4, ts4, trx);
|
||||
ENABLE_PDCH(5, ts5, trx);
|
||||
ENABLE_PDCH(6, ts6, trx);
|
||||
ENABLE_PDCH(7, ts7, trx);
|
||||
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 1);
|
||||
OSMO_ASSERT(ul_tbf->ms());
|
||||
OSMO_ASSERT(ul_tbf->ms()->current_trx());
|
||||
trx_no = ul_tbf->ms()->current_trx()->trx_no;
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
|
||||
/* assume final ack has not been sent */
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
|
||||
/* verify that both are on the same ts */
|
||||
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
|
||||
tbf_free(dl_tbf);
|
||||
tbf_free(ul_tbf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with the other order.. first DL and then UL
|
||||
*/
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t trx_no;
|
||||
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
ENABLE_PDCH(0, ts0, trx);
|
||||
ENABLE_PDCH(1, ts1, trx);
|
||||
ENABLE_PDCH(2, ts2, trx);
|
||||
ENABLE_PDCH(3, ts3, trx);
|
||||
ENABLE_PDCH(4, ts4, trx);
|
||||
ENABLE_PDCH(5, ts5, trx);
|
||||
ENABLE_PDCH(6, ts6, trx);
|
||||
ENABLE_PDCH(7, ts7, trx);
|
||||
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 1);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
OSMO_ASSERT(dl_tbf->ms());
|
||||
OSMO_ASSERT(dl_tbf->ms()->current_trx());
|
||||
trx_no = dl_tbf->ms()->current_trx()->trx_no;
|
||||
dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
|
||||
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
|
||||
ul_tbf->m_contention_resolution_done = 1;
|
||||
|
||||
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
|
||||
|
||||
/* now update the dl_tbf */
|
||||
dl_tbf->update();
|
||||
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
|
||||
|
||||
OSMO_ASSERT(ul_tbf->ms_class() == ms_class);
|
||||
OSMO_ASSERT(dl_tbf->ms_class() == ms_class);
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
|
||||
tbf_free(dl_tbf);
|
||||
tbf_free(ul_tbf);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_all_alloc_b()
|
||||
{
|
||||
/* it is a bit crazy... */
|
||||
for (uint8_t ts0 = 0; ts0 < 2; ++ts0)
|
||||
for (uint8_t ts1 = 0; ts1 < 2; ++ts1)
|
||||
for (uint8_t ts2 = 0; ts2 < 2; ++ts2)
|
||||
for (uint8_t ts3 = 0; ts3 < 2; ++ts3)
|
||||
for (uint8_t ts4 = 0; ts4 < 2; ++ts4)
|
||||
for (uint8_t ts5 = 0; ts5 < 2; ++ts5)
|
||||
for (uint8_t ts6 = 0; ts6 < 2; ++ts6)
|
||||
for (uint8_t ts7 = 0; ts7 < 2; ++ts7)
|
||||
for (int ms_class = 0; ms_class < 30; ++ms_class)
|
||||
test_alloc_b(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class);
|
||||
}
|
||||
|
||||
static void test_alloc_b()
|
||||
{
|
||||
for (int i = 0; i < 30; ++i)
|
||||
test_alloc_b(i);
|
||||
|
||||
test_all_alloc_b();
|
||||
}
|
||||
|
||||
typedef int (*algo_t)(struct gprs_rlcmac_bts *bts,
|
||||
struct GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
|
||||
int use_trx);
|
||||
|
||||
static char get_dir_char(uint8_t mask, uint8_t tx, uint8_t rx, uint8_t busy)
|
||||
{
|
||||
int offs = busy ? 32 : 0;
|
||||
return (mask & tx & rx) ? 'C' + offs :
|
||||
(mask & tx) ? 'U' + offs :
|
||||
(mask & rx) ? 'D' + offs :
|
||||
'.';
|
||||
}
|
||||
|
||||
enum test_mode {
|
||||
TEST_MODE_UL_ONLY,
|
||||
TEST_MODE_DL_ONLY,
|
||||
TEST_MODE_UL_AND_DL,
|
||||
TEST_MODE_DL_AND_UL,
|
||||
TEST_MODE_DL_AFTER_UL,
|
||||
TEST_MODE_UL_AFTER_DL,
|
||||
};
|
||||
|
||||
static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
|
||||
enum test_mode mode)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
uint8_t trx_no = -1;
|
||||
|
||||
bts = the_bts->bts_data();
|
||||
|
||||
gprs_rlcmac_tbf *tbf = NULL;
|
||||
|
||||
if (ms && ms->current_trx())
|
||||
trx_no = ms->current_trx()->trx_no;
|
||||
|
||||
GprsMs::Guard guard1(ms);
|
||||
|
||||
/* Allocate what is needed first */
|
||||
switch (mode) {
|
||||
case TEST_MODE_UL_ONLY:
|
||||
case TEST_MODE_DL_AFTER_UL:
|
||||
case TEST_MODE_UL_AND_DL:
|
||||
if (ms && ms->ul_tbf())
|
||||
tbf_free(ms->ul_tbf());
|
||||
tbf = tbf_alloc_ul_tbf(bts, ms, trx_no, ms_class, 0);
|
||||
if (tbf == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
case TEST_MODE_DL_ONLY:
|
||||
case TEST_MODE_UL_AFTER_DL:
|
||||
case TEST_MODE_DL_AND_UL:
|
||||
if (ms && ms->dl_tbf())
|
||||
tbf_free(ms->dl_tbf());
|
||||
tbf = tbf_alloc_dl_tbf(bts, ms, trx_no, ms_class, 0);
|
||||
if (tbf == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(tbf);
|
||||
OSMO_ASSERT(tbf->ms());
|
||||
OSMO_ASSERT(ms == NULL || ms == tbf->ms());
|
||||
ms = tbf->ms();
|
||||
|
||||
GprsMs::Guard guard2(ms);
|
||||
|
||||
/* Continue with what is needed next */
|
||||
switch (mode) {
|
||||
case TEST_MODE_UL_ONLY:
|
||||
case TEST_MODE_DL_ONLY:
|
||||
/* We are done */
|
||||
break;
|
||||
|
||||
case TEST_MODE_DL_AFTER_UL:
|
||||
case TEST_MODE_UL_AND_DL:
|
||||
ms = alloc_tbfs(the_bts, ms, ms_class, TEST_MODE_DL_ONLY);
|
||||
break;
|
||||
|
||||
case TEST_MODE_UL_AFTER_DL:
|
||||
case TEST_MODE_DL_AND_UL:
|
||||
ms = alloc_tbfs(the_bts, ms, ms_class, TEST_MODE_UL_ONLY);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Optionally delete the TBF */
|
||||
switch (mode) {
|
||||
case TEST_MODE_DL_AFTER_UL:
|
||||
case TEST_MODE_UL_AFTER_DL:
|
||||
tbf_free(tbf);
|
||||
tbf = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ms && tbf)
|
||||
tbf_free(tbf);
|
||||
|
||||
return guard2.is_idle() ? NULL : ms;
|
||||
}
|
||||
|
||||
static unsigned alloc_many_tbfs(BTS *the_bts, unsigned min_class,
|
||||
unsigned max_class, enum test_mode mode)
|
||||
{
|
||||
unsigned counter;
|
||||
unsigned ms_class = min_class;
|
||||
|
||||
for (counter = 0; 1; counter += 1) {
|
||||
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
|
||||
uint8_t ul_slots = 0;
|
||||
uint8_t dl_slots = 0;
|
||||
uint8_t busy_slots = 0;
|
||||
unsigned i;
|
||||
int tfi = -1;
|
||||
int tfi2;
|
||||
uint8_t trx_no2;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
GprsMs *ms;
|
||||
enum gprs_rlcmac_tbf_direction dir;
|
||||
uint32_t tlli = counter + 0xc0000000;
|
||||
|
||||
ms = the_bts->ms_by_tlli(tlli);
|
||||
|
||||
ms = alloc_tbfs(the_bts, ms, ms_class, mode);
|
||||
if (!ms)
|
||||
break;
|
||||
|
||||
ms->set_tlli(tlli);
|
||||
|
||||
ul_tbf = ms->ul_tbf();
|
||||
dl_tbf = ms->dl_tbf();
|
||||
trx = ms->current_trx();
|
||||
|
||||
OSMO_ASSERT(ul_tbf || dl_tbf);
|
||||
|
||||
if (ul_tbf) {
|
||||
ul_slots = 1 << ul_tbf->first_common_ts;
|
||||
tfi = ul_tbf->tfi();
|
||||
dir = GPRS_RLCMAC_UL_TBF;
|
||||
} else {
|
||||
ul_slots = 1 << dl_tbf->first_common_ts;
|
||||
tfi = dl_tbf->tfi();
|
||||
dir = GPRS_RLCMAC_DL_TBF;
|
||||
}
|
||||
|
||||
for (i = 0; dl_tbf && i < ARRAY_SIZE(dl_tbf->pdch); i += 1)
|
||||
if (dl_tbf->pdch[i])
|
||||
dl_slots |= 1 << i;
|
||||
|
||||
for (i = 0; trx && i < ARRAY_SIZE(trx->pdch); i += 1) {
|
||||
struct gprs_rlcmac_pdch *pdch = &trx->pdch[i];
|
||||
|
||||
if (ul_tbf && dl_tbf)
|
||||
continue;
|
||||
|
||||
if (ul_tbf &&
|
||||
pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) != 0xffffffff)
|
||||
continue;
|
||||
|
||||
if (dl_tbf &&
|
||||
pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) != 0xffffffff)
|
||||
continue;
|
||||
|
||||
busy_slots |= 1 << i;
|
||||
}
|
||||
|
||||
printf(" TBF[%d] class %d reserves %c%c%c%c%c%c%c%c\n",
|
||||
tfi, ms_class,
|
||||
get_dir_char(0x01, ul_slots, dl_slots, busy_slots),
|
||||
get_dir_char(0x02, ul_slots, dl_slots, busy_slots),
|
||||
get_dir_char(0x04, ul_slots, dl_slots, busy_slots),
|
||||
get_dir_char(0x08, ul_slots, dl_slots, busy_slots),
|
||||
get_dir_char(0x10, ul_slots, dl_slots, busy_slots),
|
||||
get_dir_char(0x20, ul_slots, dl_slots, busy_slots),
|
||||
get_dir_char(0x40, ul_slots, dl_slots, busy_slots),
|
||||
get_dir_char(0x80, ul_slots, dl_slots, busy_slots));
|
||||
|
||||
if (tfi >= 0) {
|
||||
OSMO_ASSERT(ms->current_trx());
|
||||
tfi2 = the_bts->tfi_find_free(dir, &trx_no2,
|
||||
ms->current_trx()->trx_no);
|
||||
OSMO_ASSERT(tfi != tfi2);
|
||||
OSMO_ASSERT(tfi2 < 0 ||
|
||||
trx_no2 == ms->current_trx()->trx_no);
|
||||
}
|
||||
|
||||
ms_class += 1;
|
||||
if (ms_class > max_class)
|
||||
ms_class = min_class;
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
static void test_successive_allocation(algo_t algo, unsigned min_class,
|
||||
unsigned max_class, enum test_mode mode,
|
||||
unsigned expect_num, const char *text)
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
unsigned counter;
|
||||
|
||||
printf("Going to test assignment with many TBF, %s\n", text);
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = algo;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[3].enable();
|
||||
trx->pdch[4].enable();
|
||||
trx->pdch[5].enable();
|
||||
trx->pdch[6].enable();
|
||||
trx->pdch[7].enable();
|
||||
|
||||
counter = alloc_many_tbfs(&the_bts, min_class, max_class, mode);
|
||||
|
||||
printf(" Successfully allocated %d UL TBFs\n", counter);
|
||||
if (counter != expect_num)
|
||||
fprintf(stderr, " Expected %d TBFs for %s\n", expect_num, text);
|
||||
|
||||
OSMO_ASSERT(counter == expect_num);
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
}
|
||||
|
||||
static void test_many_connections(algo_t algo, unsigned expect_num,
|
||||
const char *text)
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
int counter1, counter2 = -1;
|
||||
unsigned i;
|
||||
enum test_mode mode_seq[] = {
|
||||
TEST_MODE_DL_AFTER_UL,
|
||||
TEST_MODE_UL_ONLY,
|
||||
TEST_MODE_DL_AFTER_UL,
|
||||
TEST_MODE_DL_ONLY,
|
||||
};
|
||||
|
||||
printf("Going to test assignment with many connections, %s\n", text);
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = algo;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[3].enable();
|
||||
trx->pdch[4].enable();
|
||||
trx->pdch[5].enable();
|
||||
trx->pdch[6].enable();
|
||||
trx->pdch[7].enable();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mode_seq); i += 1) {
|
||||
counter1 = alloc_many_tbfs(&the_bts, 1, 29, mode_seq[i]);
|
||||
fprintf(stderr, " Allocated %d TBFs (previously %d)\n",
|
||||
counter1, counter2);
|
||||
|
||||
check_tfi_usage(&the_bts);
|
||||
|
||||
/* This will stop earlier due to USF shortage */
|
||||
if (mode_seq[i] == TEST_MODE_UL_ONLY)
|
||||
continue;
|
||||
|
||||
if (counter2 >= 0) {
|
||||
if (counter1 < counter2)
|
||||
fprintf(stderr, " Expected %d >= %d in %s\n",
|
||||
counter1, counter2, text);
|
||||
OSMO_ASSERT(counter1 >= counter2);
|
||||
}
|
||||
|
||||
counter2 = counter1;
|
||||
}
|
||||
|
||||
printf(" Successfully allocated %d TBFs\n", counter1);
|
||||
if (counter1 != (int)expect_num)
|
||||
fprintf(stderr, " Expected %d TBFs for %s\n", expect_num, text);
|
||||
|
||||
OSMO_ASSERT(expect_num == (unsigned)counter1);
|
||||
}
|
||||
|
||||
static void test_successive_allocation()
|
||||
{
|
||||
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AND_DL,
|
||||
35, "algorithm A (UL and DL)");
|
||||
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AND_DL,
|
||||
32, "algorithm B class 10 (UL and DL)");
|
||||
test_successive_allocation(alloc_algorithm_b, 12, 12, TEST_MODE_UL_AND_DL,
|
||||
32, "algorithm B class 12 (UL and DL)");
|
||||
test_successive_allocation(alloc_algorithm_b, 1, 12, TEST_MODE_UL_AND_DL,
|
||||
32, "algorithm B class 1-12 (UL and DL)");
|
||||
test_successive_allocation(alloc_algorithm_b, 1, 29, TEST_MODE_UL_AND_DL,
|
||||
32, "algorithm B class 1-29 (UL and DL)");
|
||||
test_successive_allocation(alloc_algorithm_dynamic, 1, 29, TEST_MODE_UL_AND_DL,
|
||||
35, "algorithm dynamic class 1-29 (UL and DL)");
|
||||
|
||||
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AND_UL,
|
||||
35, "algorithm A (DL and UL)");
|
||||
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AND_UL,
|
||||
32, "algorithm B class 10 (DL and UL)");
|
||||
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AND_UL,
|
||||
32, "algorithm dynamic class 10 (DL and UL)");
|
||||
|
||||
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AFTER_UL,
|
||||
160, "algorithm A (DL after UL)");
|
||||
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AFTER_UL,
|
||||
32, "algorithm B class 10 (DL after UL)");
|
||||
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AFTER_UL,
|
||||
95, "algorithm dynamic class 10 (DL after UL)");
|
||||
|
||||
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AFTER_DL,
|
||||
35, "algorithm A (UL after DL)");
|
||||
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AFTER_DL,
|
||||
32, "algorithm B class 10 (UL after DL)");
|
||||
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_AFTER_DL,
|
||||
35, "algorithm dynamic class 10 (UL after DL)");
|
||||
|
||||
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_ONLY,
|
||||
35, "algorithm A (UL only)");
|
||||
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_ONLY,
|
||||
32, "algorithm B class 10 (UL only)");
|
||||
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_ONLY,
|
||||
35, "algorithm dynamic class 10 (UL only)");
|
||||
|
||||
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_ONLY,
|
||||
160, "algorithm A (DL ONLY)");
|
||||
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_ONLY,
|
||||
32, "algorithm B class 10 (DL ONLY)");
|
||||
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_ONLY,
|
||||
101, "algorithm dynamic class 10 (DL ONLY)");
|
||||
}
|
||||
|
||||
static void test_many_connections()
|
||||
{
|
||||
test_many_connections(alloc_algorithm_a, 160, "algorithm A");
|
||||
test_many_connections(alloc_algorithm_b, 32, "algorithm B");
|
||||
test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile AllocTest context");
|
||||
if (!tall_pcu_ctx)
|
||||
abort();
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
if (getenv("LOGL_DEBUG"))
|
||||
log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
|
||||
|
||||
test_alloc_a();
|
||||
test_alloc_b();
|
||||
test_successive_allocation();
|
||||
test_many_connections();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* stubs that should not be reached
|
||||
*/
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
894
tests/alloc/AllocTest.err
Normal file
894
tests/alloc/AllocTest.err
Normal file
@@ -0,0 +1,894 @@
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
- Failed to allocate a TS, no USF available
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
- Failed to allocate a TS, no USF available
|
||||
- Failed to allocate a TS, no USF available
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TS, no USF available
|
||||
- Failed to allocate a TS, no USF available
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
- Failed to allocate a TS, no USF available
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
- Failed to allocate a TS, no USF available
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
- Failed to allocate a TS, no USF available
|
||||
- Failed to allocate a TS, no USF available
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
- Failed to allocate a TS, no USF available
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
Allocated 160 TBFs (previously -1)
|
||||
- Failed to allocate a TS, no USF available
|
||||
Allocated 35 TBFs (previously 160)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
Allocated 160 TBFs (previously 160)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
Allocated 160 TBFs (previously 160)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
Allocated 32 TBFs (previously -1)
|
||||
No USF available
|
||||
Allocated 24 TBFs (previously 32)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
Allocated 32 TBFs (previously 32)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
Allocated 32 TBFs (previously 32)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to allocate a TFI
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
Allocated 97 TBFs (previously -1)
|
||||
- Failed to allocate a TS, no USF available
|
||||
Allocated 24 TBFs (previously 97)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
Allocated 160 TBFs (previously 97)
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
No TFI available.
|
||||
- Failed to find a usable TRX (TFI exhausted)
|
||||
Allocated 160 TBFs (previously 160)
|
||||
10795
tests/alloc/AllocTest.ok
Normal file
10795
tests/alloc/AllocTest.ok
Normal file
File diff suppressed because it is too large
Load Diff
147
tests/codel/codel_test.c
Normal file
147
tests/codel/codel_test.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/* Test routines for the CoDel implementation
|
||||
*
|
||||
* (C) 2015 by sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*/
|
||||
|
||||
#undef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#if 0
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#endif
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include "gprs_codel.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static int do_codel_control(struct gprs_codel *state, const struct timeval *recv,
|
||||
struct timeval *now, const struct timeval *delta_now, int count)
|
||||
{
|
||||
int drop;
|
||||
|
||||
drop = gprs_codel_control(state, recv, now, -1);
|
||||
if (drop) {
|
||||
printf("Dropping packet %d, "
|
||||
"recv = %d.%03d, now = %d.%03d, "
|
||||
"codel.count = %d\n",
|
||||
count,
|
||||
(int)recv->tv_sec, (int)recv->tv_usec/1000,
|
||||
(int)now->tv_sec, (int)now->tv_usec/1000,
|
||||
state->count);
|
||||
} else {
|
||||
timeradd(now, delta_now, now);
|
||||
}
|
||||
|
||||
return drop == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
static void test_codel(void)
|
||||
{
|
||||
struct gprs_codel codel;
|
||||
struct timeval now;
|
||||
struct timeval recv;
|
||||
const struct timeval delta_now = {0, 10000};
|
||||
const struct timeval init_delta_recv = {0, 5000};
|
||||
struct timeval delta_recv;
|
||||
unsigned count;
|
||||
unsigned sum = 0;
|
||||
unsigned dropped = 0;
|
||||
int drop;
|
||||
|
||||
printf("----- %s START\n", __func__);
|
||||
gprs_codel_init(&codel);
|
||||
gprs_codel_set_interval(&codel, 100);
|
||||
|
||||
timerclear(&now);
|
||||
timerclear(&recv);
|
||||
delta_recv = init_delta_recv;
|
||||
|
||||
for (count = 0; count < 20; count++, sum++) {
|
||||
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
|
||||
timeradd(&recv, &delta_recv, &recv);
|
||||
dropped += drop;
|
||||
}
|
||||
|
||||
printf("Dropped %d packets\n", dropped);
|
||||
OSMO_ASSERT(dropped == 0);
|
||||
OSMO_ASSERT(!codel.dropping);
|
||||
|
||||
for (count = 0; count < 20; count++, sum++) {
|
||||
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
|
||||
timeradd(&recv, &delta_recv, &recv);
|
||||
dropped += drop;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(dropped == 2);
|
||||
OSMO_ASSERT(codel.dropping);
|
||||
|
||||
/* slow down recv rate */
|
||||
delta_recv.tv_usec = delta_now.tv_usec;
|
||||
|
||||
for (count = 0; count < 75; count++, sum++) {
|
||||
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
|
||||
timeradd(&recv, &delta_recv, &recv);
|
||||
dropped += drop;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(dropped == 20);
|
||||
OSMO_ASSERT(codel.dropping);
|
||||
|
||||
for (count = 0; count < 50; count++, sum++) {
|
||||
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
|
||||
timeradd(&recv, &delta_recv, &recv);
|
||||
dropped += drop;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(dropped == 20);
|
||||
OSMO_ASSERT(!codel.dropping);
|
||||
OSMO_ASSERT(codel.count >= 20);
|
||||
|
||||
/* go back to old data rate */
|
||||
delta_recv = init_delta_recv;
|
||||
|
||||
for (count = 0; count < 20; count++, sum++) {
|
||||
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
|
||||
timeradd(&recv, &delta_recv, &recv);
|
||||
dropped += drop;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(dropped == 20);
|
||||
OSMO_ASSERT(!codel.dropping);
|
||||
|
||||
for (count = 0; count < 20; count++, sum++) {
|
||||
drop = do_codel_control(&codel, &recv, &now, &delta_now, sum);
|
||||
timeradd(&recv, &delta_recv, &recv);
|
||||
dropped += drop;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(dropped == 22);
|
||||
OSMO_ASSERT(codel.count >= 2);
|
||||
|
||||
printf("Dropped %d packets\n", dropped);
|
||||
|
||||
printf("----- %s END\n", __func__);
|
||||
}
|
||||
|
||||
static struct log_info info = {};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_log_level(osmo_stderr_target, LOGL_INFO);
|
||||
|
||||
printf("===== CoDel test START\n");
|
||||
test_codel();
|
||||
printf("===== CoDel test END\n\n");
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
29
tests/codel/codel_test.ok
Normal file
29
tests/codel/codel_test.ok
Normal file
@@ -0,0 +1,29 @@
|
||||
===== CoDel test START
|
||||
----- test_codel START
|
||||
Dropped 0 packets
|
||||
Dropping packet 21, recv = 0.105, now = 0.210, codel.count = 1
|
||||
Dropping packet 32, recv = 0.160, now = 0.310, codel.count = 2
|
||||
Dropping packet 41, recv = 0.210, now = 0.390, codel.count = 3
|
||||
Dropping packet 47, recv = 0.270, now = 0.440, codel.count = 4
|
||||
Dropping packet 53, recv = 0.330, now = 0.490, codel.count = 5
|
||||
Dropping packet 59, recv = 0.390, now = 0.540, codel.count = 6
|
||||
Dropping packet 64, recv = 0.440, now = 0.580, codel.count = 7
|
||||
Dropping packet 69, recv = 0.490, now = 0.620, codel.count = 8
|
||||
Dropping packet 73, recv = 0.530, now = 0.650, codel.count = 9
|
||||
Dropping packet 77, recv = 0.570, now = 0.680, codel.count = 10
|
||||
Dropping packet 81, recv = 0.610, now = 0.710, codel.count = 11
|
||||
Dropping packet 86, recv = 0.660, now = 0.750, codel.count = 12
|
||||
Dropping packet 89, recv = 0.690, now = 0.770, codel.count = 13
|
||||
Dropping packet 93, recv = 0.730, now = 0.800, codel.count = 14
|
||||
Dropping packet 97, recv = 0.770, now = 0.830, codel.count = 15
|
||||
Dropping packet 100, recv = 0.800, now = 0.850, codel.count = 16
|
||||
Dropping packet 104, recv = 0.840, now = 0.880, codel.count = 17
|
||||
Dropping packet 107, recv = 0.870, now = 0.900, codel.count = 18
|
||||
Dropping packet 111, recv = 0.910, now = 0.930, codel.count = 19
|
||||
Dropping packet 114, recv = 0.940, now = 0.950, codel.count = 20
|
||||
Dropping packet 186, recv = 1.555, now = 1.660, codel.count = 1
|
||||
Dropping packet 197, recv = 1.610, now = 1.760, codel.count = 2
|
||||
Dropped 22 packets
|
||||
----- test_codel END
|
||||
===== CoDel test END
|
||||
|
||||
65
tests/emu/gprs_tests.h
Normal file
65
tests/emu/gprs_tests.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef tests_h
|
||||
#define tests_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <string.h>
|
||||
|
||||
struct gprs_bssgp_pcu;
|
||||
struct tlv_parsed;
|
||||
struct msgb;
|
||||
|
||||
struct gprs_test {
|
||||
gprs_test(const char *name, const char *description,
|
||||
void (*start)(struct gprs_bssgp_pcu *),
|
||||
void (*data) (struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *parsed))
|
||||
: name(name)
|
||||
, description(description)
|
||||
, start(start)
|
||||
, data(data)
|
||||
{}
|
||||
|
||||
const char *name;
|
||||
const char *description;
|
||||
void (*start)(struct gprs_bssgp_pcu *);
|
||||
void (*data) (struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *);
|
||||
};
|
||||
|
||||
void gprs_test_success(struct gprs_bssgp_pcu *);
|
||||
|
||||
static inline struct msgb *create_msg(const uint8_t *data, size_t len)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(4096, 128, "create msg");
|
||||
msg->l3h = msgb_put(msg, len);
|
||||
memcpy(msg->l3h, data, len);
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
234
tests/emu/openbsc_clone.c
Normal file
234
tests/emu/openbsc_clone.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/* (C) 2009-2010 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 Affero 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 "openbsc_clone.h"
|
||||
|
||||
#include <gprs_debug.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/* Section 6.4 Commands and Responses */
|
||||
enum gprs_llc_u_cmd {
|
||||
GPRS_LLC_U_DM_RESP = 0x01,
|
||||
GPRS_LLC_U_DISC_CMD = 0x04,
|
||||
GPRS_LLC_U_UA_RESP = 0x06,
|
||||
GPRS_LLC_U_SABM_CMD = 0x07,
|
||||
GPRS_LLC_U_FRMR_RESP = 0x08,
|
||||
GPRS_LLC_U_XID = 0x0b,
|
||||
GPRS_LLC_U_NULL_CMD = 0x00,
|
||||
};
|
||||
|
||||
#define LLC_ALLOC_SIZE 16384
|
||||
#define UI_HDR_LEN 3
|
||||
#define N202 4
|
||||
#define CRC24_LENGTH 3
|
||||
|
||||
static const struct value_string llc_cmd_strs[] = {
|
||||
{ GPRS_LLC_NULL, "NULL" },
|
||||
{ GPRS_LLC_RR, "RR" },
|
||||
{ GPRS_LLC_ACK, "ACK" },
|
||||
{ GPRS_LLC_RNR, "RNR" },
|
||||
{ GPRS_LLC_SACK, "SACK" },
|
||||
{ GPRS_LLC_DM, "DM" },
|
||||
{ GPRS_LLC_DISC, "DISC" },
|
||||
{ GPRS_LLC_UA, "UA" },
|
||||
{ GPRS_LLC_SABM, "SABM" },
|
||||
{ GPRS_LLC_FRMR, "FRMR" },
|
||||
{ GPRS_LLC_XID, "XID" },
|
||||
{ GPRS_LLC_UI, "UI" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
|
||||
const uint8_t *llc_hdr, int len)
|
||||
{
|
||||
const uint8_t *ctrl = llc_hdr+1;
|
||||
|
||||
if (len <= CRC24_LENGTH)
|
||||
return -EIO;
|
||||
|
||||
ghp->crc_length = len - CRC24_LENGTH;
|
||||
|
||||
ghp->ack_req = 0;
|
||||
|
||||
/* Section 5.5: FCS */
|
||||
ghp->fcs = *(llc_hdr + len - 3);
|
||||
ghp->fcs |= *(llc_hdr + len - 2) << 8;
|
||||
ghp->fcs |= *(llc_hdr + len - 1) << 16;
|
||||
|
||||
/* Section 6.2.1: invalid PD field */
|
||||
if (llc_hdr[0] & 0x80)
|
||||
return -EIO;
|
||||
|
||||
/* This only works for the MS->SGSN direction */
|
||||
if (llc_hdr[0] & 0x40)
|
||||
ghp->is_cmd = 0;
|
||||
else
|
||||
ghp->is_cmd = 1;
|
||||
|
||||
ghp->sapi = llc_hdr[0] & 0xf;
|
||||
|
||||
/* Section 6.2.3: check for reserved SAPI */
|
||||
switch (ghp->sapi) {
|
||||
case 0:
|
||||
case 4:
|
||||
case 6:
|
||||
case 0xa:
|
||||
case 0xc:
|
||||
case 0xd:
|
||||
case 0xf:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((ctrl[0] & 0x80) == 0) {
|
||||
/* I (Information transfer + Supervisory) format */
|
||||
uint8_t k;
|
||||
|
||||
ghp->data = ctrl + 3;
|
||||
|
||||
if (ctrl[0] & 0x40)
|
||||
ghp->ack_req = 1;
|
||||
|
||||
ghp->seq_tx = (ctrl[0] & 0x1f) << 4;
|
||||
ghp->seq_tx |= (ctrl[1] >> 4);
|
||||
|
||||
ghp->seq_rx = (ctrl[1] & 0x7) << 6;
|
||||
ghp->seq_rx |= (ctrl[2] >> 2);
|
||||
|
||||
switch (ctrl[2] & 0x03) {
|
||||
case 0:
|
||||
ghp->cmd = GPRS_LLC_RR;
|
||||
break;
|
||||
case 1:
|
||||
ghp->cmd = GPRS_LLC_ACK;
|
||||
break;
|
||||
case 2:
|
||||
ghp->cmd = GPRS_LLC_RNR;
|
||||
break;
|
||||
case 3:
|
||||
ghp->cmd = GPRS_LLC_SACK;
|
||||
k = ctrl[3] & 0x1f;
|
||||
ghp->data += 1 + k;
|
||||
break;
|
||||
}
|
||||
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
|
||||
} else if ((ctrl[0] & 0xc0) == 0x80) {
|
||||
/* S (Supervisory) format */
|
||||
ghp->data = NULL;
|
||||
ghp->data_len = 0;
|
||||
|
||||
if (ctrl[0] & 0x20)
|
||||
ghp->ack_req = 1;
|
||||
ghp->seq_rx = (ctrl[0] & 0x7) << 6;
|
||||
ghp->seq_rx |= (ctrl[1] >> 2);
|
||||
|
||||
switch (ctrl[1] & 0x03) {
|
||||
case 0:
|
||||
ghp->cmd = GPRS_LLC_RR;
|
||||
break;
|
||||
case 1:
|
||||
ghp->cmd = GPRS_LLC_ACK;
|
||||
break;
|
||||
case 2:
|
||||
ghp->cmd = GPRS_LLC_RNR;
|
||||
break;
|
||||
case 3:
|
||||
ghp->cmd = GPRS_LLC_SACK;
|
||||
break;
|
||||
}
|
||||
} else if ((ctrl[0] & 0xe0) == 0xc0) {
|
||||
/* UI (Unconfirmed Inforamtion) format */
|
||||
ghp->cmd = GPRS_LLC_UI;
|
||||
ghp->data = ctrl + 2;
|
||||
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
|
||||
|
||||
ghp->seq_tx = (ctrl[0] & 0x7) << 6;
|
||||
ghp->seq_tx |= (ctrl[1] >> 2);
|
||||
if (ctrl[1] & 0x02) {
|
||||
ghp->is_encrypted = 1;
|
||||
/* FIXME: encryption */
|
||||
}
|
||||
if (ctrl[1] & 0x01) {
|
||||
/* FCS over hdr + all inf fields */
|
||||
} else {
|
||||
/* FCS over hdr + N202 octets (4) */
|
||||
if (ghp->crc_length > UI_HDR_LEN + N202)
|
||||
ghp->crc_length = UI_HDR_LEN + N202;
|
||||
}
|
||||
} else {
|
||||
/* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
|
||||
ghp->data = NULL;
|
||||
ghp->data_len = 0;
|
||||
|
||||
switch (ctrl[0] & 0xf) {
|
||||
case GPRS_LLC_U_NULL_CMD:
|
||||
ghp->cmd = GPRS_LLC_NULL;
|
||||
break;
|
||||
case GPRS_LLC_U_DM_RESP:
|
||||
ghp->cmd = GPRS_LLC_DM;
|
||||
break;
|
||||
case GPRS_LLC_U_DISC_CMD:
|
||||
ghp->cmd = GPRS_LLC_DISC;
|
||||
break;
|
||||
case GPRS_LLC_U_UA_RESP:
|
||||
ghp->cmd = GPRS_LLC_UA;
|
||||
break;
|
||||
case GPRS_LLC_U_SABM_CMD:
|
||||
ghp->cmd = GPRS_LLC_SABM;
|
||||
break;
|
||||
case GPRS_LLC_U_FRMR_RESP:
|
||||
ghp->cmd = GPRS_LLC_FRMR;
|
||||
break;
|
||||
case GPRS_LLC_U_XID:
|
||||
ghp->cmd = GPRS_LLC_XID;
|
||||
ghp->data = ctrl + 1;
|
||||
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
|
||||
break;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: parse sack frame */
|
||||
if (ghp->cmd == GPRS_LLC_SACK) {
|
||||
LOGP(DPCU, LOGL_NOTICE, "Unsupported SACK frame\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct tlv_definition gsm48_gmm_att_tlvdef = {
|
||||
.def = {
|
||||
[GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 },
|
||||
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
|
||||
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
|
||||
[GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 },
|
||||
[GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 },
|
||||
[GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
|
||||
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
|
||||
},
|
||||
};
|
||||
96
tests/emu/openbsc_clone.h
Normal file
96
tests/emu/openbsc_clone.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* (C) 2009-2010 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 Affero 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/>.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENBSC_CLONE_H
|
||||
#define OPENBSC_CLONE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum gprs_llc_cmd {
|
||||
GPRS_LLC_NULL,
|
||||
GPRS_LLC_RR,
|
||||
GPRS_LLC_ACK,
|
||||
GPRS_LLC_RNR,
|
||||
GPRS_LLC_SACK,
|
||||
GPRS_LLC_DM,
|
||||
GPRS_LLC_DISC,
|
||||
GPRS_LLC_UA,
|
||||
GPRS_LLC_SABM,
|
||||
GPRS_LLC_FRMR,
|
||||
GPRS_LLC_XID,
|
||||
GPRS_LLC_UI,
|
||||
};
|
||||
|
||||
struct gprs_llc_hdr_parsed {
|
||||
uint8_t sapi;
|
||||
uint8_t is_cmd:1,
|
||||
ack_req:1,
|
||||
is_encrypted:1;
|
||||
uint32_t seq_rx;
|
||||
uint32_t seq_tx;
|
||||
uint32_t fcs;
|
||||
uint32_t fcs_calc;
|
||||
const uint8_t *data;
|
||||
uint16_t data_len;
|
||||
uint16_t crc_length;
|
||||
enum gprs_llc_cmd cmd;
|
||||
};
|
||||
|
||||
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, const uint8_t *llc_hdr, int len);
|
||||
|
||||
/* Table 10.4 / 10.4a, GPRS Mobility Management (GMM) */
|
||||
#define GSM48_MT_GMM_ATTACH_ACK 0x02
|
||||
|
||||
/* Chapter 9.4.2 / Table 9.4.2 */
|
||||
struct gsm48_attach_ack {
|
||||
uint8_t att_result:4, /* 10.5.5.7 */
|
||||
force_stby:4; /* 10.5.5.1 */
|
||||
uint8_t ra_upd_timer; /* 10.5.7.3 */
|
||||
uint8_t radio_prio; /* 10.5.7.2 */
|
||||
struct gsm48_ra_id ra_id; /* 10.5.5.15 */
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
enum gsm48_gprs_ie_mm {
|
||||
GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */
|
||||
GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */
|
||||
GSM48_IE_GMM_ALLOC_PTMSI = 0x18, /* 10.5.1.4 */
|
||||
GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */
|
||||
GSM48_IE_GMM_AUTH_RAND = 0x21, /* 10.5.3.1 */
|
||||
GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */
|
||||
GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */
|
||||
GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */
|
||||
GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */
|
||||
GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */
|
||||
GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */
|
||||
GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */
|
||||
};
|
||||
|
||||
extern const struct tlv_definition gsm48_gmm_att_tlvdef;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
152
tests/emu/pcu_emu.cpp
Normal file
152
tests/emu/pcu_emu.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/* Code for a software PCU to test a SGSN.. */
|
||||
|
||||
/* (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <pcu_vty.h>
|
||||
}
|
||||
|
||||
#include "gprs_tests.h"
|
||||
|
||||
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <bts.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
static size_t current_test;
|
||||
|
||||
/* Extern data to please the underlying code */
|
||||
void *tall_pcu_ctx;
|
||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
|
||||
|
||||
extern void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu);
|
||||
extern void test_replay_gprs_data(struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed *);
|
||||
|
||||
extern void test_pdp_activation_start(struct gprs_bssgp_pcu *pcu);
|
||||
extern void test_pdp_activation_data(struct gprs_bssgp_pcu *, struct msgb *, struct tlv_parsed*);
|
||||
|
||||
struct gprs_test all_tests[] = {
|
||||
gprs_test("gprs_attach_with_tmsi",
|
||||
"A simple test that verifies that N(U) is "
|
||||
"increasing across various messages. This makes "
|
||||
"sure that no new LLE/LLME is created on the fly.",
|
||||
test_replay_gprs_attach,
|
||||
test_replay_gprs_data),
|
||||
gprs_test("gprs_full_attach_pdp_activation",
|
||||
"A simple test to do a GPRS attach and open a PDP "
|
||||
"context. Then goes to sleep and waits for you to ping "
|
||||
"the connection and hopefully re-produce a crash.",
|
||||
test_pdp_activation_start,
|
||||
test_pdp_activation_data),
|
||||
};
|
||||
|
||||
static void init_main_bts()
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
bts->fc_interval = 100;
|
||||
bts->initial_cs_dl = bts->initial_cs_ul = 1;
|
||||
bts->cs1 = 1;
|
||||
bts->t3142 = 20;
|
||||
bts->t3169 = 5;
|
||||
bts->t3191 = 5;
|
||||
bts->t3193_msec = 100;
|
||||
bts->t3195 = 5;
|
||||
bts->n3101 = 10;
|
||||
bts->n3103 = 4;
|
||||
bts->n3105 = 8;
|
||||
bts->alpha = 0; /* a = 0.0 */
|
||||
|
||||
if (!bts->alloc_algorithm)
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
}
|
||||
|
||||
static void bvci_unblocked(struct gprs_bssgp_pcu *pcu)
|
||||
{
|
||||
printf("BVCI unblocked. We can begin with test cases.\n");
|
||||
all_tests[current_test].start(pcu);
|
||||
}
|
||||
|
||||
static void bssgp_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
|
||||
{
|
||||
all_tests[current_test].data(pcu, msg, tp);
|
||||
}
|
||||
|
||||
void create_and_connect_bssgp(struct gprs_rlcmac_bts *bts,
|
||||
uint32_t sgsn_ip, uint16_t sgsn_port)
|
||||
{
|
||||
struct gprs_bssgp_pcu *pcu;
|
||||
|
||||
pcu = gprs_bssgp_create_and_connect(bts, 0, sgsn_ip, sgsn_port,
|
||||
20, 20, 20, 0x901, 0x99, 1, 0, 0);
|
||||
pcu->on_unblock_ack = bvci_unblocked;
|
||||
pcu->on_dl_unit_data = bssgp_data;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile Emu-PCU context");
|
||||
if (!tall_pcu_ctx)
|
||||
abort();
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
vty_init(&pcu_vty_info);
|
||||
pcu_vty_init(&gprs_log_info);
|
||||
|
||||
current_test = 0;
|
||||
|
||||
init_main_bts();
|
||||
create_and_connect_bssgp(bts_main_data(), INADDR_LOOPBACK, 23000);
|
||||
|
||||
for (;;)
|
||||
osmo_select_main(0);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test handling..
|
||||
*/
|
||||
void gprs_test_success(struct gprs_bssgp_pcu *pcu)
|
||||
{
|
||||
current_test += 1;
|
||||
if (current_test >= ARRAY_SIZE(all_tests)) {
|
||||
printf("All tests executed.\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
all_tests[current_test].start(pcu);
|
||||
}
|
||||
|
||||
/*
|
||||
* stubs that should not be reached
|
||||
*/
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
169
tests/emu/test_pdp_activation.cpp
Normal file
169
tests/emu/test_pdp_activation.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/* (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/backtrace.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
}
|
||||
|
||||
#include "openbsc_clone.h"
|
||||
#include "gprs_tests.h"
|
||||
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static const uint8_t attach[] = {
|
||||
0x0e, 0x00, 0x26,
|
||||
0x01, 0xc0, 0x01, 0x08, 0x01, 0x02, 0xe5, 0x80,
|
||||
0x71, 0x0d, 0x01, 0x05, 0xf4, 0x02, 0x30, 0xef,
|
||||
0x0e, 0x09, 0xf1, 0x07, 0x00, 0x01, 0x00, 0x0b,
|
||||
0x34, 0xc7, 0x03, 0x2a, 0xa0, 0x42, 0x7c, 0xad,
|
||||
0xe1, 0x18, 0x0b, 0xf8, 0xef, 0xfc
|
||||
};
|
||||
|
||||
static const uint8_t id_resp_imei[] = {
|
||||
0x0e, 0x00, 0x11,
|
||||
0x01, 0xc0, 0x05, 0x08, 0x16, 0x08, 0x3a, 0x49,
|
||||
0x50, 0x13, 0x28, 0x15, 0x80, 0x01, 0x21, 0x6c,
|
||||
0x22
|
||||
};
|
||||
|
||||
static const uint8_t id_resp_imsi[] = {
|
||||
0x0e, 0x00, 0x11,
|
||||
0x01, 0xc0, 0x09, 0x08, 0x16, 0x08, 0x99, 0x10,
|
||||
0x07, 0x00, 0x00, 0x00, 0x03, 0x49, 0xc7, 0x5b,
|
||||
0xb6
|
||||
};
|
||||
|
||||
static const uint8_t attach_complete[] = {
|
||||
0x0e, 0x00, 0x08,
|
||||
0x01, 0xc0, 0x0d, 0x08, 0x03, 0x55, 0x1c, 0xea
|
||||
};
|
||||
|
||||
static const uint8_t pdp_context[] = {
|
||||
0x0e, 0x00, 0x5a,
|
||||
0x01, 0xc0, 0x11, 0x0a, 0x41, 0x05, 0x03, 0x0c,
|
||||
0x00, 0x00, 0x1f, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x21, 0x28,
|
||||
0x12, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x05, 0x65, 0x70, 0x6c, 0x75, 0x73,
|
||||
0x02, 0x64, 0x65, 0x27, 0x2a, 0x80, 0xc0, 0x23,
|
||||
0x13, 0x01, 0x00, 0x00, 0x13, 0x05, 0x65, 0x70,
|
||||
0x6c, 0x75, 0x73, 0x08, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x80, 0x21, 0x10, 0x01,
|
||||
0x00, 0x00, 0x10, 0x81, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x83, 0x06, 0x00, 0x00, 0x00, 0x00, 0xcf,
|
||||
0x90, 0xcc
|
||||
};
|
||||
|
||||
static const uint8_t qos_profile[] = { 0x0, 0x0, 0x04 };
|
||||
static uint32_t tlli = 0xadf11821;
|
||||
|
||||
enum state {
|
||||
Test_Start,
|
||||
Test_IdRespIMEI,
|
||||
Test_IdRespIMSI,
|
||||
Test_AttachCompl,
|
||||
Test_PDPAct,
|
||||
Test_Done,
|
||||
};
|
||||
|
||||
static enum state current_state = Test_Start;
|
||||
|
||||
static void extract_tmsi_and_generate_tlli(struct msgb *msg, struct tlv_parsed *tp)
|
||||
{
|
||||
uint32_t tmsi;
|
||||
struct gprs_llc_hdr_parsed hp;
|
||||
struct tlv_parsed ack_tp;
|
||||
|
||||
gprs_llc_hdr_parse(&hp, TLVP_VAL(tp, BSSGP_IE_LLC_PDU),
|
||||
TLVP_LEN(tp, BSSGP_IE_LLC_PDU));
|
||||
msgb_gmmh(msg) = (unsigned char *) hp.data;
|
||||
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
|
||||
OSMO_ASSERT(gh->msg_type == GSM48_MT_GMM_ATTACH_ACK);
|
||||
struct gsm48_attach_ack *ack = (struct gsm48_attach_ack *) gh->data;
|
||||
tlv_parse(&ack_tp, &gsm48_gmm_att_tlvdef, ack->data,
|
||||
(msg->data + msg->len) - ack->data, 0, 0);
|
||||
|
||||
|
||||
OSMO_ASSERT(TLVP_PRESENT(&ack_tp, GSM48_IE_GMM_ALLOC_PTMSI));
|
||||
memcpy(&tmsi, TLVP_VAL(&ack_tp, GSM48_IE_GMM_ALLOC_PTMSI) + 1, 4);
|
||||
tmsi = ntohl(tmsi);
|
||||
tlli = gprs_tmsi2tlli(tmsi, TLLI_LOCAL);
|
||||
printf("New TLLI(0x%08x) based on tmsi(0x%x)\n", tlli, tmsi);
|
||||
}
|
||||
|
||||
void test_pdp_activation_start(struct gprs_bssgp_pcu *pcu)
|
||||
{
|
||||
struct msgb *msg = create_msg(attach, ARRAY_SIZE(attach));
|
||||
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, msg);
|
||||
current_state = Test_IdRespIMEI;
|
||||
}
|
||||
|
||||
|
||||
void test_pdp_activation_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
|
||||
{
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
|
||||
switch (current_state) {
|
||||
case Test_IdRespIMEI:
|
||||
data = id_resp_imei;
|
||||
len = ARRAY_SIZE(id_resp_imei);
|
||||
current_state = Test_IdRespIMSI;
|
||||
break;
|
||||
case Test_IdRespIMSI:
|
||||
data = id_resp_imsi;
|
||||
len = ARRAY_SIZE(id_resp_imsi);
|
||||
current_state = Test_AttachCompl;
|
||||
break;
|
||||
case Test_AttachCompl:
|
||||
data = attach_complete;
|
||||
len = ARRAY_SIZE(attach_complete);
|
||||
extract_tmsi_and_generate_tlli(msg, tp);
|
||||
current_state = Test_PDPAct;
|
||||
break;
|
||||
case Test_PDPAct:
|
||||
printf("PDP context is active or not...\n");
|
||||
return;
|
||||
break;
|
||||
case Test_Done:
|
||||
case Test_Start: /* fall through */
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
printf("Unknown state. %d\n", current_state);
|
||||
return;
|
||||
break;
|
||||
};
|
||||
|
||||
struct msgb *out = create_msg(data, len);
|
||||
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, out);
|
||||
|
||||
/* send it after the PDP... */
|
||||
if (current_state == Test_PDPAct) {
|
||||
out = create_msg(pdp_context, ARRAY_SIZE(pdp_context));
|
||||
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, out);
|
||||
}
|
||||
}
|
||||
89
tests/emu/test_replay_gprs_attach.cpp
Normal file
89
tests/emu/test_replay_gprs_attach.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/* (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/backtrace.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
}
|
||||
|
||||
#include "openbsc_clone.h"
|
||||
#include "gprs_tests.h"
|
||||
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* GPRS attach with a foreign TLLI */
|
||||
static const uint8_t gprs_attach_llc[] = {
|
||||
/* LLC-PDU IE */
|
||||
0x0e, 0x00, 0x2e,
|
||||
|
||||
0x01, 0xc0, 0x01, 0x08, 0x01, 0x02, 0xf5, 0x40,
|
||||
0x71, 0x08, 0x00, 0x05, 0xf4, 0x2d, 0xf1, 0x18,
|
||||
0x20, 0x62, 0xf2, 0x10, 0x09, 0x67, 0x00, 0x13,
|
||||
0x16, 0x73, 0x43, 0x2a, 0x80, 0x42, 0x00, 0x42,
|
||||
0x88, 0x0b, 0x04, 0x20, 0x04, 0x2e, 0x82, 0x30,
|
||||
0x42, 0x00, 0x40, 0xaa, 0xf3, 0x18
|
||||
};
|
||||
|
||||
static uint32_t next_wanted_nu;
|
||||
|
||||
void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu)
|
||||
{
|
||||
uint32_t tlli = 0xadf11820;
|
||||
const uint8_t qos_profile[] = { 0x0, 0x0, 0x04 };
|
||||
|
||||
next_wanted_nu = 0;
|
||||
struct msgb *msg = create_msg(gprs_attach_llc, ARRAY_SIZE(gprs_attach_llc));
|
||||
bssgp_tx_ul_ud(pcu->bctx, tlli, qos_profile, msg);
|
||||
}
|
||||
|
||||
void test_replay_gprs_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct tlv_parsed *tp)
|
||||
{
|
||||
struct bssgp_ud_hdr *budh;
|
||||
struct gprs_llc_hdr_parsed ph;
|
||||
uint32_t tlli;
|
||||
|
||||
if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
|
||||
return;
|
||||
|
||||
|
||||
gprs_llc_hdr_parse(&ph, TLVP_VAL(tp, BSSGP_IE_LLC_PDU),
|
||||
TLVP_LEN(tp, BSSGP_IE_LLC_PDU));
|
||||
|
||||
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
|
||||
tlli = ntohl(budh->tlli);
|
||||
|
||||
/* all messages we should get, should be for a foreign tlli */
|
||||
OSMO_ASSERT(gprs_tlli_type(tlli) == TLLI_FOREIGN);
|
||||
printf("TLLI(0x%08x) is foreign!\n", tlli);
|
||||
|
||||
OSMO_ASSERT(ph.cmd == GPRS_LLC_UI);
|
||||
OSMO_ASSERT(ph.sapi == 1);
|
||||
OSMO_ASSERT(ph.seq_tx == next_wanted_nu);
|
||||
next_wanted_nu += 1;
|
||||
|
||||
/* this test just wants to see messages... no further data is sent */
|
||||
if (next_wanted_nu == 6) {
|
||||
printf("GPRS attach with increasing N(U) done.\n");
|
||||
gprs_test_success(pcu);
|
||||
}
|
||||
}
|
||||
266
tests/llc/LlcTest.cpp
Normal file
266
tests/llc/LlcTest.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* LlcTest.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
}
|
||||
|
||||
#include "llc.h"
|
||||
#include "gprs_debug.h"
|
||||
|
||||
extern "C" {
|
||||
#include "pcu_vty.h"
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
}
|
||||
|
||||
|
||||
void *tall_pcu_ctx;
|
||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
|
||||
|
||||
static void enqueue_data(gprs_llc_queue *queue, const uint8_t *data, size_t len,
|
||||
gprs_llc_queue::MetaInfo *info = 0)
|
||||
{
|
||||
struct timeval *tv;
|
||||
uint8_t *msg_data;
|
||||
struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2,
|
||||
"llc_pdu_queue");
|
||||
|
||||
msg_data = (uint8_t *)msgb_put(llc_msg, len);
|
||||
|
||||
memcpy(msg_data, data, len);
|
||||
|
||||
queue->enqueue(llc_msg, info);
|
||||
}
|
||||
|
||||
static void dequeue_and_check(gprs_llc_queue *queue, const uint8_t *exp_data,
|
||||
size_t len, const gprs_llc_queue::MetaInfo *exp_info = 0)
|
||||
{
|
||||
struct msgb *llc_msg;
|
||||
uint8_t *msg_data;
|
||||
const gprs_llc_queue::MetaInfo *info_res;
|
||||
|
||||
llc_msg = queue->dequeue(&info_res);
|
||||
OSMO_ASSERT(llc_msg != NULL);
|
||||
|
||||
fprintf(stderr, "dequeued msg, length %d (expected %d), data %s\n",
|
||||
msgb_length(llc_msg), len, msgb_hexdump(llc_msg));
|
||||
|
||||
OSMO_ASSERT(msgb_length(llc_msg) == len);
|
||||
msg_data = msgb_data(llc_msg);
|
||||
|
||||
OSMO_ASSERT(memcmp(msg_data, exp_data, len) == 0);
|
||||
|
||||
if (exp_info)
|
||||
OSMO_ASSERT(memcmp(exp_info, info_res, sizeof(*exp_info)) == 0);
|
||||
|
||||
msgb_free(llc_msg);
|
||||
}
|
||||
|
||||
static void enqueue_data(gprs_llc_queue *queue, const char *message,
|
||||
gprs_llc_queue::MetaInfo *info = 0)
|
||||
{
|
||||
enqueue_data(queue, (uint8_t *)(message), strlen(message), info);
|
||||
}
|
||||
|
||||
static void dequeue_and_check(gprs_llc_queue *queue, const char *exp_message,
|
||||
const gprs_llc_queue::MetaInfo *exp_info = 0)
|
||||
{
|
||||
dequeue_and_check(queue,
|
||||
(uint8_t *)(exp_message), strlen(exp_message), exp_info);
|
||||
}
|
||||
|
||||
static void test_llc_queue()
|
||||
{
|
||||
gprs_llc_queue queue;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
queue.init();
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
enqueue_data(&queue, "LLC message");
|
||||
OSMO_ASSERT(queue.size() == 1);
|
||||
OSMO_ASSERT(queue.octets() == 11);
|
||||
|
||||
enqueue_data(&queue, "other LLC message");
|
||||
OSMO_ASSERT(queue.size() == 2);
|
||||
OSMO_ASSERT(queue.octets() == 28);
|
||||
|
||||
dequeue_and_check(&queue, "LLC message");
|
||||
OSMO_ASSERT(queue.size() == 1);
|
||||
OSMO_ASSERT(queue.octets() == 17);
|
||||
|
||||
dequeue_and_check(&queue, "other LLC message");
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
enqueue_data(&queue, "LLC");
|
||||
OSMO_ASSERT(queue.size() == 1);
|
||||
OSMO_ASSERT(queue.octets() == 3);
|
||||
|
||||
queue.clear(NULL);
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_llc_meta()
|
||||
{
|
||||
gprs_llc_queue queue;
|
||||
gprs_llc_queue::MetaInfo info1;
|
||||
gprs_llc_queue::MetaInfo info2;
|
||||
|
||||
info1.recv_time.tv_sec = 123456777;
|
||||
info1.recv_time.tv_usec = 123456;
|
||||
info1.expire_time.tv_sec = 123456789;
|
||||
info1.expire_time.tv_usec = 987654;
|
||||
|
||||
info2.recv_time.tv_sec = 987654321;
|
||||
info2.recv_time.tv_usec = 547352;
|
||||
info2.expire_time.tv_sec = 987654327;
|
||||
info2.expire_time.tv_usec = 867252;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
queue.init();
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
enqueue_data(&queue, "LLC message 1", &info1);
|
||||
enqueue_data(&queue, "LLC message 2", &info2);
|
||||
|
||||
dequeue_and_check(&queue, "LLC message 1", &info1);
|
||||
dequeue_and_check(&queue, "LLC message 2", &info2);
|
||||
|
||||
queue.clear(NULL);
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_llc_merge()
|
||||
{
|
||||
gprs_llc_queue queue1;
|
||||
gprs_llc_queue queue2;
|
||||
gprs_llc_queue::MetaInfo info = {0};
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
queue1.init();
|
||||
queue2.init();
|
||||
|
||||
info.recv_time.tv_sec += 1;
|
||||
enqueue_data(&queue1, "*A*", &info);
|
||||
|
||||
info.recv_time.tv_sec += 1;
|
||||
enqueue_data(&queue1, "*B*", &info);
|
||||
|
||||
info.recv_time.tv_sec += 1;
|
||||
enqueue_data(&queue2, "*C*", &info);
|
||||
|
||||
info.recv_time.tv_sec += 1;
|
||||
enqueue_data(&queue1, "*D*", &info);
|
||||
|
||||
info.recv_time.tv_sec += 1;
|
||||
enqueue_data(&queue2, "*E*", &info);
|
||||
|
||||
OSMO_ASSERT(queue1.size() == 3);
|
||||
OSMO_ASSERT(queue1.octets() == 9);
|
||||
OSMO_ASSERT(queue2.size() == 2);
|
||||
OSMO_ASSERT(queue2.octets() == 6);
|
||||
|
||||
queue2.move_and_merge(&queue1);
|
||||
|
||||
OSMO_ASSERT(queue1.size() == 0);
|
||||
OSMO_ASSERT(queue1.octets() == 0);
|
||||
OSMO_ASSERT(queue2.size() == 5);
|
||||
OSMO_ASSERT(queue2.octets() == 15);
|
||||
|
||||
dequeue_and_check(&queue2, "*A*");
|
||||
dequeue_and_check(&queue2, "*B*");
|
||||
dequeue_and_check(&queue2, "*C*");
|
||||
dequeue_and_check(&queue2, "*D*");
|
||||
dequeue_and_check(&queue2, "*E*");
|
||||
|
||||
OSMO_ASSERT(queue2.size() == 0);
|
||||
OSMO_ASSERT(queue2.octets() == 0);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
{"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1},
|
||||
};
|
||||
|
||||
static int filter_fn(const struct log_context *ctx,
|
||||
struct log_target *tar)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct log_info debug_log_info = {
|
||||
filter_fn,
|
||||
(struct log_info_cat*)default_categories,
|
||||
ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct vty_app_info pcu_vty_info = {0};
|
||||
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "LlcTest context");
|
||||
if (!tall_pcu_ctx)
|
||||
abort();
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
osmo_init_logging(&debug_log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
|
||||
|
||||
vty_init(&pcu_vty_info);
|
||||
pcu_vty_init(&debug_log_info);
|
||||
|
||||
test_llc_queue();
|
||||
test_llc_meta();
|
||||
test_llc_merge();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
9
tests/llc/LlcTest.err
Normal file
9
tests/llc/LlcTest.err
Normal file
@@ -0,0 +1,9 @@
|
||||
dequeued msg, length 11 (expected 11), data 4c 4c 43 20 6d 65 73 73 61 67 65
|
||||
dequeued msg, length 17 (expected 17), data 6f 74 68 65 72 20 4c 4c 43 20 6d 65 73 73 61 67 65
|
||||
dequeued msg, length 13 (expected 13), data 4c 4c 43 20 6d 65 73 73 61 67 65 20 31
|
||||
dequeued msg, length 13 (expected 13), data 4c 4c 43 20 6d 65 73 73 61 67 65 20 32
|
||||
dequeued msg, length 3 (expected 3), data 2a 41 2a
|
||||
dequeued msg, length 3 (expected 3), data 2a 42 2a
|
||||
dequeued msg, length 3 (expected 3), data 2a 43 2a
|
||||
dequeued msg, length 3 (expected 3), data 2a 44 2a
|
||||
dequeued msg, length 3 (expected 3), data 2a 45 2a
|
||||
6
tests/llc/LlcTest.ok
Normal file
6
tests/llc/LlcTest.ok
Normal file
@@ -0,0 +1,6 @@
|
||||
=== start test_llc_queue ===
|
||||
=== end test_llc_queue ===
|
||||
=== start test_llc_meta ===
|
||||
=== end test_llc_meta ===
|
||||
=== start test_llc_merge ===
|
||||
=== end test_llc_merge ===
|
||||
91
tests/llist/LListTest.cpp
Normal file
91
tests/llist/LListTest.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* LListTest.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* 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 "cxx_linuxlist.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
struct TestElem {
|
||||
const char *str;
|
||||
LListHead<TestElem> list;
|
||||
|
||||
TestElem(const char *s) : str(s), list(this) {};
|
||||
};
|
||||
|
||||
static void test_linux_list()
|
||||
{
|
||||
LListHead<TestElem> elems, *pos, *tmp;
|
||||
TestElem elem1("number one");
|
||||
TestElem elem2("number two");
|
||||
TestElem elem3("number three");
|
||||
int count = 0;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
OSMO_ASSERT(llist_empty(&elems));
|
||||
|
||||
llist_add_tail(&elem1.list, &elems);
|
||||
llist_add_tail(&elem2.list, &elems);
|
||||
llist_add_tail(&elem3.list, &elems);
|
||||
|
||||
OSMO_ASSERT(!llist_empty(&elems));
|
||||
|
||||
llist_for_each(pos, &elems) {
|
||||
count += 1;
|
||||
printf(" %i -> %s\n", count, pos->entry()->str);
|
||||
}
|
||||
OSMO_ASSERT(count == 3);
|
||||
|
||||
count = 0;
|
||||
llist_for_each_safe(pos, tmp, &elems) {
|
||||
count += 1;
|
||||
if (count == 2)
|
||||
llist_del(pos);
|
||||
|
||||
printf(" %i -> %s\n", count, pos->entry()->str);
|
||||
}
|
||||
OSMO_ASSERT(count == 3);
|
||||
|
||||
count = 0;
|
||||
llist_for_each(pos, &elems) {
|
||||
count += 1;
|
||||
OSMO_ASSERT(pos != &elem2.list);
|
||||
printf(" %i -> %s\n", count, pos->entry()->str);
|
||||
}
|
||||
OSMO_ASSERT(count == 2);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_linux_list();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
0
tests/llist/LListTest.err
Normal file
0
tests/llist/LListTest.err
Normal file
10
tests/llist/LListTest.ok
Normal file
10
tests/llist/LListTest.ok
Normal file
@@ -0,0 +1,10 @@
|
||||
=== start test_linux_list ===
|
||||
1 -> number one
|
||||
2 -> number two
|
||||
3 -> number three
|
||||
1 -> number one
|
||||
2 -> number two
|
||||
3 -> number three
|
||||
1 -> number one
|
||||
2 -> number three
|
||||
=== end test_linux_list ===
|
||||
569
tests/ms/MsTest.cpp
Normal file
569
tests/ms/MsTest.cpp
Normal file
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* MsTest.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* 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 "tbf.h"
|
||||
#include "gprs_debug.h"
|
||||
#include "gprs_ms.h"
|
||||
#include "gprs_ms_storage.h"
|
||||
#include "bts.h"
|
||||
|
||||
extern "C" {
|
||||
#include "pcu_vty.h"
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void *tall_pcu_ctx;
|
||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
|
||||
|
||||
static void test_ms_state()
|
||||
{
|
||||
uint32_t tlli = 0xffeeddbb;
|
||||
gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
ms = new GprsMs(NULL, tlli);
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
|
||||
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
||||
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
|
||||
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
|
||||
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
|
||||
|
||||
ms->attach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
|
||||
OSMO_ASSERT(ms->dl_tbf() == NULL);
|
||||
|
||||
ms->attach_tbf(dl_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
|
||||
|
||||
OSMO_ASSERT(ms->tbf(GPRS_RLCMAC_UL_TBF) == ul_tbf);
|
||||
OSMO_ASSERT(ms->tbf(GPRS_RLCMAC_DL_TBF) == dl_tbf);
|
||||
|
||||
ms->detach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
|
||||
|
||||
ms->detach_tbf(dl_tbf);
|
||||
/* The ms object is freed now */
|
||||
ms = NULL;
|
||||
|
||||
talloc_free(dl_tbf);
|
||||
talloc_free(ul_tbf);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_ms_callback()
|
||||
{
|
||||
uint32_t tlli = 0xffeeddbb;
|
||||
gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
static enum {UNKNOWN, IS_IDLE, IS_ACTIVE} last_cb = UNKNOWN;
|
||||
|
||||
struct MyCallback: public GprsMs::Callback {
|
||||
virtual void ms_idle(class GprsMs *ms) {
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
printf(" ms_idle() was called\n");
|
||||
last_cb = IS_IDLE;
|
||||
}
|
||||
virtual void ms_active(class GprsMs *ms) {
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
printf(" ms_active() was called\n");
|
||||
last_cb = IS_ACTIVE;
|
||||
}
|
||||
} cb;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
ms = new GprsMs(NULL, tlli);
|
||||
ms->set_callback(&cb);
|
||||
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
|
||||
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
||||
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
|
||||
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
|
||||
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
|
||||
|
||||
OSMO_ASSERT(last_cb == UNKNOWN);
|
||||
|
||||
ms->attach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
|
||||
OSMO_ASSERT(ms->dl_tbf() == NULL);
|
||||
OSMO_ASSERT(last_cb == IS_ACTIVE);
|
||||
|
||||
last_cb = UNKNOWN;
|
||||
|
||||
ms->attach_tbf(dl_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
|
||||
OSMO_ASSERT(last_cb == UNKNOWN);
|
||||
|
||||
ms->detach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf);
|
||||
OSMO_ASSERT(last_cb == UNKNOWN);
|
||||
|
||||
ms->detach_tbf(dl_tbf);
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == NULL);
|
||||
OSMO_ASSERT(last_cb == IS_IDLE);
|
||||
|
||||
last_cb = UNKNOWN;
|
||||
delete ms;
|
||||
|
||||
talloc_free(dl_tbf);
|
||||
talloc_free(ul_tbf);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_ms_replace_tbf()
|
||||
{
|
||||
uint32_t tlli = 0xffeeddbb;
|
||||
gprs_rlcmac_dl_tbf *dl_tbf[2];
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
static bool was_idle;
|
||||
|
||||
struct MyCallback: public GprsMs::Callback {
|
||||
virtual void ms_idle(class GprsMs *ms) {
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
printf(" ms_idle() was called\n");
|
||||
was_idle = true;
|
||||
}
|
||||
virtual void ms_active(class GprsMs *ms) {
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
printf(" ms_active() was called\n");
|
||||
}
|
||||
} cb;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
ms = new GprsMs(NULL, tlli);
|
||||
ms->set_callback(&cb);
|
||||
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
was_idle = false;
|
||||
|
||||
dl_tbf[0] = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
||||
new (dl_tbf[0]) gprs_rlcmac_dl_tbf(NULL);
|
||||
dl_tbf[1] = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
||||
new (dl_tbf[1]) gprs_rlcmac_dl_tbf(NULL);
|
||||
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
|
||||
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
|
||||
|
||||
ms->attach_tbf(dl_tbf[0]);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[0]);
|
||||
OSMO_ASSERT(llist_empty(&ms->old_tbfs()));
|
||||
OSMO_ASSERT(!was_idle);
|
||||
|
||||
ms->attach_tbf(dl_tbf[1]);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
|
||||
OSMO_ASSERT(!llist_empty(&ms->old_tbfs()));
|
||||
OSMO_ASSERT(!was_idle);
|
||||
|
||||
ms->attach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == ul_tbf);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
|
||||
OSMO_ASSERT(!llist_empty(&ms->old_tbfs()));
|
||||
OSMO_ASSERT(!was_idle);
|
||||
|
||||
ms->detach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
|
||||
OSMO_ASSERT(!llist_empty(&ms->old_tbfs()));
|
||||
OSMO_ASSERT(!was_idle);
|
||||
|
||||
ms->detach_tbf(dl_tbf[0]);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == dl_tbf[1]);
|
||||
OSMO_ASSERT(llist_empty(&ms->old_tbfs()));
|
||||
OSMO_ASSERT(!was_idle);
|
||||
|
||||
ms->detach_tbf(dl_tbf[1]);
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
OSMO_ASSERT(ms->ul_tbf() == NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() == NULL);
|
||||
OSMO_ASSERT(llist_empty(&ms->old_tbfs()));
|
||||
OSMO_ASSERT(was_idle);
|
||||
|
||||
delete ms;
|
||||
|
||||
talloc_free(dl_tbf[0]);
|
||||
talloc_free(dl_tbf[1]);
|
||||
talloc_free(ul_tbf);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_ms_change_tlli()
|
||||
{
|
||||
uint32_t start_tlli = 0xaa000000;
|
||||
uint32_t new_ms_tlli = 0xff001111;
|
||||
uint32_t other_sgsn_tlli = 0xff00eeee;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
ms = new GprsMs(NULL, start_tlli);
|
||||
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
|
||||
/* MS announces TLLI, SGSN uses it immediately */
|
||||
ms->set_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(start_tlli));
|
||||
|
||||
ms->confirm_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(!ms->check_tlli(start_tlli));
|
||||
|
||||
/* MS announces TLLI, SGSN uses it later */
|
||||
ms->set_tlli(start_tlli);
|
||||
ms->confirm_tlli(start_tlli);
|
||||
|
||||
ms->set_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(start_tlli));
|
||||
|
||||
ms->confirm_tlli(start_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(start_tlli));
|
||||
|
||||
ms->set_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(start_tlli));
|
||||
|
||||
ms->confirm_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(!ms->check_tlli(start_tlli));
|
||||
|
||||
/* MS announces TLLI, SGSN uses it later after another new TLLI */
|
||||
ms->set_tlli(start_tlli);
|
||||
ms->confirm_tlli(start_tlli);
|
||||
|
||||
ms->set_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(start_tlli));
|
||||
|
||||
ms->confirm_tlli(other_sgsn_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(other_sgsn_tlli));
|
||||
|
||||
ms->set_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(other_sgsn_tlli));
|
||||
|
||||
ms->confirm_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(!ms->check_tlli(start_tlli));
|
||||
OSMO_ASSERT(!ms->check_tlli(other_sgsn_tlli));
|
||||
|
||||
/* SGSN uses the new TLLI before it is announced by the MS (shouldn't
|
||||
* happen in normal use) */
|
||||
ms->set_tlli(start_tlli);
|
||||
ms->confirm_tlli(start_tlli);
|
||||
|
||||
ms->confirm_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == start_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(ms->check_tlli(start_tlli));
|
||||
|
||||
ms->set_tlli(new_ms_tlli);
|
||||
OSMO_ASSERT(ms->tlli() == new_ms_tlli);
|
||||
OSMO_ASSERT(ms->check_tlli(new_ms_tlli));
|
||||
OSMO_ASSERT(!ms->check_tlli(start_tlli));
|
||||
|
||||
delete ms;
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_ms_storage()
|
||||
{
|
||||
uint32_t tlli = 0xffeeddbb;
|
||||
const char *imsi1 = "001001987654321";
|
||||
const char *imsi2 = "001001987654322";
|
||||
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms, *ms_tmp;
|
||||
GprsMsStorage store(NULL);
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
|
||||
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
|
||||
|
||||
ms = store.get_ms(tlli + 0);
|
||||
OSMO_ASSERT(ms == NULL);
|
||||
|
||||
ms = store.create_ms(tlli + 0, GPRS_RLCMAC_UL_TBF);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
OSMO_ASSERT(ms->tlli() == tlli + 0);
|
||||
ms->set_imsi(imsi1);
|
||||
OSMO_ASSERT(strcmp(ms->imsi(), imsi1) == 0);
|
||||
|
||||
ms_tmp = store.get_ms(tlli + 0);
|
||||
OSMO_ASSERT(ms == ms_tmp);
|
||||
OSMO_ASSERT(ms->tlli() == tlli + 0);
|
||||
|
||||
ms_tmp = store.get_ms(0, 0, imsi1);
|
||||
OSMO_ASSERT(ms == ms_tmp);
|
||||
OSMO_ASSERT(strcmp(ms->imsi(), imsi1) == 0);
|
||||
ms_tmp = store.get_ms(0, 0, imsi2);
|
||||
OSMO_ASSERT(ms_tmp == NULL);
|
||||
|
||||
ms = store.create_ms(tlli + 1, GPRS_RLCMAC_UL_TBF);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
OSMO_ASSERT(ms->tlli() == tlli + 1);
|
||||
ms->set_imsi(imsi2);
|
||||
OSMO_ASSERT(strcmp(ms->imsi(), imsi2) == 0);
|
||||
|
||||
ms_tmp = store.get_ms(tlli + 1);
|
||||
OSMO_ASSERT(ms == ms_tmp);
|
||||
OSMO_ASSERT(ms->tlli() == tlli + 1);
|
||||
|
||||
ms_tmp = store.get_ms(0, 0, imsi1);
|
||||
OSMO_ASSERT(ms_tmp != NULL);
|
||||
OSMO_ASSERT(ms_tmp != ms);
|
||||
ms_tmp = store.get_ms(0, 0, imsi2);
|
||||
OSMO_ASSERT(ms == ms_tmp);
|
||||
OSMO_ASSERT(strcmp(ms->imsi(), imsi2) == 0);
|
||||
|
||||
/* delete ms */
|
||||
ms = store.get_ms(tlli + 0);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
ms->attach_tbf(ul_tbf);
|
||||
ms->detach_tbf(ul_tbf);
|
||||
ms = store.get_ms(tlli + 0);
|
||||
OSMO_ASSERT(ms == NULL);
|
||||
ms = store.get_ms(tlli + 1);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
|
||||
/* delete ms */
|
||||
ms = store.get_ms(tlli + 1);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
ms->attach_tbf(ul_tbf);
|
||||
ms->detach_tbf(ul_tbf);
|
||||
ms = store.get_ms(tlli + 1);
|
||||
OSMO_ASSERT(ms == NULL);
|
||||
|
||||
talloc_free(ul_tbf);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_ms_timeout()
|
||||
{
|
||||
uint32_t tlli = 0xffeeddbb;
|
||||
gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
static enum {UNKNOWN, IS_IDLE, IS_ACTIVE} last_cb = UNKNOWN;
|
||||
|
||||
struct MyCallback: public GprsMs::Callback {
|
||||
virtual void ms_idle(class GprsMs *ms) {
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
printf(" ms_idle() was called\n");
|
||||
last_cb = IS_IDLE;
|
||||
}
|
||||
virtual void ms_active(class GprsMs *ms) {
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
printf(" ms_active() was called\n");
|
||||
last_cb = IS_ACTIVE;
|
||||
}
|
||||
} cb;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
ms = new GprsMs(NULL, tlli);
|
||||
ms->set_callback(&cb);
|
||||
ms->set_timeout(1);
|
||||
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
|
||||
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
||||
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
|
||||
ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
|
||||
new (ul_tbf) gprs_rlcmac_ul_tbf(NULL);
|
||||
|
||||
OSMO_ASSERT(last_cb == UNKNOWN);
|
||||
|
||||
ms->attach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(last_cb == IS_ACTIVE);
|
||||
|
||||
last_cb = UNKNOWN;
|
||||
|
||||
ms->attach_tbf(dl_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(last_cb == UNKNOWN);
|
||||
|
||||
ms->detach_tbf(ul_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(last_cb == UNKNOWN);
|
||||
|
||||
ms->detach_tbf(dl_tbf);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
OSMO_ASSERT(last_cb == UNKNOWN);
|
||||
|
||||
usleep(1100000);
|
||||
osmo_timers_update();
|
||||
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
OSMO_ASSERT(last_cb == IS_IDLE);
|
||||
|
||||
last_cb = UNKNOWN;
|
||||
delete ms;
|
||||
talloc_free(dl_tbf);
|
||||
talloc_free(ul_tbf);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_ms_cs_selection()
|
||||
{
|
||||
BTS the_bts;
|
||||
gprs_rlcmac_bts *bts = the_bts.bts_data();
|
||||
uint32_t tlli = 0xffeeddbb;
|
||||
|
||||
gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
bts->initial_cs_dl = 4;
|
||||
bts->initial_cs_ul = 1;
|
||||
bts->cs_downgrade_threshold = 0;
|
||||
|
||||
ms = new GprsMs(&the_bts, tlli);
|
||||
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
|
||||
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
||||
new (dl_tbf) gprs_rlcmac_dl_tbf(NULL);
|
||||
|
||||
dl_tbf->set_ms(ms);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
|
||||
OSMO_ASSERT(ms->current_cs_dl() == 4);
|
||||
|
||||
bts->cs_downgrade_threshold = 200;
|
||||
|
||||
OSMO_ASSERT(ms->current_cs_dl() == 3);
|
||||
|
||||
talloc_free(dl_tbf);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
{"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1},
|
||||
};
|
||||
|
||||
static int filter_fn(const struct log_context *ctx,
|
||||
struct log_target *tar)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct log_info debug_log_info = {
|
||||
filter_fn,
|
||||
(struct log_info_cat*)default_categories,
|
||||
ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct vty_app_info pcu_vty_info = {0};
|
||||
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "MsTest context");
|
||||
if (!tall_pcu_ctx)
|
||||
abort();
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
osmo_init_logging(&debug_log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_log_level(osmo_stderr_target, LOGL_INFO);
|
||||
|
||||
vty_init(&pcu_vty_info);
|
||||
pcu_vty_init(&debug_log_info);
|
||||
|
||||
test_ms_state();
|
||||
test_ms_callback();
|
||||
test_ms_replace_tbf();
|
||||
test_ms_change_tlli();
|
||||
test_ms_storage();
|
||||
test_ms_timeout();
|
||||
test_ms_cs_selection();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
58
tests/ms/MsTest.err
Normal file
58
tests/ms/MsTest.err
Normal file
@@ -0,0 +1,58 @@
|
||||
Creating MS object, TLLI = 0xffeeddbb
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Destroying MS object, TLLI = 0xffeeddbb
|
||||
Creating MS object, TLLI = 0xffeeddbb
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Destroying MS object, TLLI = 0xffeeddbb
|
||||
Creating MS object, TLLI = 0xffeeddbb
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Destroying MS object, TLLI = 0xffeeddbb
|
||||
Creating MS object, TLLI = 0xaa000000
|
||||
Modifying MS object, UL TLLI: 0xaa000000 -> 0xff001111, not yet confirmed
|
||||
Modifying MS object, TLLI: 0xff001111 confirmed
|
||||
Modifying MS object, UL TLLI: 0xff001111 -> 0xaa000000, not yet confirmed
|
||||
Modifying MS object, TLLI: 0xaa000000 confirmed
|
||||
Modifying MS object, UL TLLI: 0xaa000000 -> 0xff001111, not yet confirmed
|
||||
Modifying MS object, TLLI: 0xff001111 confirmed
|
||||
Modifying MS object, UL TLLI: 0xff001111 -> 0xaa000000, not yet confirmed
|
||||
Modifying MS object, TLLI: 0xaa000000 confirmed
|
||||
Modifying MS object, UL TLLI: 0xaa000000 -> 0xff001111, not yet confirmed
|
||||
The MS object cannot fully confirm an unexpected TLLI: 0xff00eeee, partly confirmed
|
||||
Modifying MS object, TLLI: 0xff001111 confirmed
|
||||
Modifying MS object, UL TLLI: 0xff001111 -> 0xaa000000, not yet confirmed
|
||||
Modifying MS object, TLLI: 0xaa000000 confirmed
|
||||
The MS object cannot fully confirm an unexpected TLLI: 0xff001111, partly confirmed
|
||||
Modifying MS object, TLLI: 0xaa000000 -> 0xff001111, already confirmed partly
|
||||
Destroying MS object, TLLI = 0xff001111
|
||||
Creating MS object, TLLI = 0x00000000
|
||||
Modifying MS object, UL TLLI: 0x00000000 -> 0xffeeddbb, not yet confirmed
|
||||
Modifying MS object, TLLI = 0xffeeddbb, IMSI '' -> '001001987654321'
|
||||
Creating MS object, TLLI = 0x00000000
|
||||
Modifying MS object, UL TLLI: 0x00000000 -> 0xffeeddbc, not yet confirmed
|
||||
Modifying MS object, TLLI = 0xffeeddbc, IMSI '' -> '001001987654322'
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Destroying MS object, TLLI = 0xffeeddbb
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbc, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbc, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Destroying MS object, TLLI = 0xffeeddbc
|
||||
Creating MS object, TLLI = 0xffeeddbb
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=NULL)
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Timeout for MS object, TLLI = 0xffeeddbb
|
||||
Destroying MS object, TLLI = 0xffeeddbb
|
||||
Creating MS object, TLLI = 0xffeeddbb
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0xffeeddbb DIR=DL STATE=NULL)
|
||||
20
tests/ms/MsTest.ok
Normal file
20
tests/ms/MsTest.ok
Normal file
@@ -0,0 +1,20 @@
|
||||
=== start test_ms_state ===
|
||||
=== end test_ms_state ===
|
||||
=== start test_ms_callback ===
|
||||
ms_active() was called
|
||||
ms_idle() was called
|
||||
=== end test_ms_callback ===
|
||||
=== start test_ms_replace_tbf ===
|
||||
ms_active() was called
|
||||
ms_idle() was called
|
||||
=== end test_ms_replace_tbf ===
|
||||
=== start test_ms_change_tlli ===
|
||||
=== end test_ms_change_tlli ===
|
||||
=== start test_ms_storage ===
|
||||
=== end test_ms_storage ===
|
||||
=== start test_ms_timeout ===
|
||||
ms_active() was called
|
||||
ms_idle() was called
|
||||
=== end test_ms_timeout ===
|
||||
=== start test_ms_cs_selection ===
|
||||
=== end test_ms_cs_selection ===
|
||||
@@ -54,8 +54,8 @@ void printSizeofRLCMAC()
|
||||
|
||||
cout << "sizeof RlcMacDownlink_t " << sizeof(RlcMacDownlink_t) << endl;
|
||||
cout << "sizeof Packet_Access_Reject_t " << sizeof(Packet_Access_Reject_t) << endl;
|
||||
cout << "sizeof Packet_Cell_Change_Order_t " << sizeof(Packet_Cell_Change_Order_t) << endl;
|
||||
cout << "sizeof Packet_Downlink_Assignment_t " << sizeof(Packet_Downlink_Assignment_t) << endl;
|
||||
cout << "sizeof Packet_Cell_Change_Order_t " << sizeof(Packet_Cell_Change_Order_t) << endl;
|
||||
cout << "sizeof Packet_Downlink_Assignment_t " << sizeof(Packet_Downlink_Assignment_t) << endl;
|
||||
cout << "sizeof Packet_Measurement_Order_Reduced_t " << sizeof(Packet_Measurement_Order_Reduced_t) << endl;
|
||||
cout << "sizeof Packet_Neighbour_Cell_Data_t " << sizeof(Packet_Neighbour_Cell_Data_t) << endl;
|
||||
cout << "sizeof Packet_Serving_Cell_Data_t " << sizeof(Packet_Serving_Cell_Data_t) << endl;
|
||||
0
tests/rlcmac/RLCMACTest.err
Normal file
0
tests/rlcmac/RLCMACTest.err
Normal file
58
tests/rlcmac/RLCMACTest.ok
Normal file
58
tests/rlcmac/RLCMACTest.ok
Normal file
@@ -0,0 +1,58 @@
|
||||
DOWNLINK
|
||||
vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4724c040000000079eb2ac9402b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4724c040000000079eb2ac9402b2b2b2b2b2b
|
||||
vector2 = 4724c040000000079eb2ac9402b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
UPLINK
|
||||
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
vector2 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
vector1 == vector2 : TRUE
|
||||
1158
tests/tbf/TbfTest.cpp
Normal file
1158
tests/tbf/TbfTest.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2884
tests/tbf/TbfTest.err
Normal file
2884
tests/tbf/TbfTest.err
Normal file
File diff suppressed because it is too large
Load Diff
22
tests/tbf/TbfTest.ok
Normal file
22
tests/tbf/TbfTest.ok
Normal file
@@ -0,0 +1,22 @@
|
||||
=== start test_tbf_base ===
|
||||
=== end test_tbf_base ===
|
||||
=== start test_tbf_delayed_release ===
|
||||
=== end test_tbf_delayed_release ===
|
||||
=== start test_tbf_imsi ===
|
||||
=== end test_tbf_imsi ===
|
||||
=== start test_tbf_exhaustion ===
|
||||
=== end test_tbf_exhaustion ===
|
||||
=== start test_tbf_dl_llc_loss ===
|
||||
=== end test_tbf_dl_llc_loss ===
|
||||
=== start test_tbf_single_phase ===
|
||||
=== end test_tbf_single_phase ===
|
||||
=== start test_tbf_two_phase ===
|
||||
=== end test_tbf_two_phase ===
|
||||
=== start test_tbf_ra_update_rach ===
|
||||
=== end test_tbf_ra_update_rach ===
|
||||
=== start test_tbf_dl_flow_and_rach_two_phase ===
|
||||
=== end test_tbf_dl_flow_and_rach_two_phase ===
|
||||
=== start test_tbf_dl_flow_and_rach_single_phase ===
|
||||
=== end test_tbf_dl_flow_and_rach_single_phase ===
|
||||
=== start test_tbf_dl_reuse ===
|
||||
=== end test_tbf_dl_reuse ===
|
||||
58
tests/testsuite.at
Normal file
58
tests/testsuite.at
Normal file
@@ -0,0 +1,58 @@
|
||||
AT_INIT
|
||||
AT_BANNER([Regression tests])
|
||||
|
||||
|
||||
AT_SETUP([rlcmac])
|
||||
AT_KEYWORDS([rlcmac])
|
||||
cat $abs_srcdir/rlcmac/RLCMACTest.ok > expout
|
||||
cat $abs_srcdir/rlcmac/RLCMACTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/rlcmac/RLCMACTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([ts_alloc])
|
||||
AT_KEYWORDS([ts_alloc])
|
||||
cat $abs_srcdir/alloc/AllocTest.ok > expout
|
||||
cat $abs_srcdir/alloc/AllocTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/alloc/AllocTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([tbf])
|
||||
AT_KEYWORDS([tbf])
|
||||
cat $abs_srcdir/tbf/TbfTest.ok > expout
|
||||
cat $abs_srcdir/tbf/TbfTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/tbf/TbfTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([types])
|
||||
AT_KEYWORDS([types])
|
||||
cat $abs_srcdir/types/TypesTest.ok > expout
|
||||
cat $abs_srcdir/types/TypesTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/types/TypesTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([ms])
|
||||
AT_KEYWORDS([ms])
|
||||
cat $abs_srcdir/ms/MsTest.ok > expout
|
||||
cat $abs_srcdir/ms/MsTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/ms/MsTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([llc])
|
||||
AT_KEYWORDS([llc])
|
||||
cat $abs_srcdir/llc/LlcTest.ok > expout
|
||||
cat $abs_srcdir/llc/LlcTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/llc/LlcTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([llist])
|
||||
AT_KEYWORDS([llist])
|
||||
cat $abs_srcdir/llist/LListTest.ok > expout
|
||||
cat $abs_srcdir/llist/LListTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/llist/LListTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([codel])
|
||||
AT_KEYWORDS([codel])
|
||||
cat $abs_srcdir/codel/codel_test.ok > expout
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/codel/codel_test], [0], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
375
tests/types/TypesTest.cpp
Normal file
375
tests/types/TypesTest.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* TypesTest.cpp Test the primitive data types
|
||||
*
|
||||
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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 "bts.h"
|
||||
#include "tbf.h"
|
||||
#include "gprs_debug.h"
|
||||
#include "encoding.h"
|
||||
#include "decoding.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
}
|
||||
|
||||
#define OSMO_ASSERT_STR_EQ(a, b) \
|
||||
do { \
|
||||
if (strcmp(a, b)) { \
|
||||
printf("String mismatch:\nGot:\t%s\nWant:\t%s\n", a, b); \
|
||||
OSMO_ASSERT(false); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void *tall_pcu_ctx;
|
||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
|
||||
|
||||
static void test_llc(void)
|
||||
{
|
||||
{
|
||||
uint8_t data[LLC_MAX_LEN] = {1, 2, 3, 4, };
|
||||
uint8_t out;
|
||||
gprs_llc llc;
|
||||
llc.init();
|
||||
|
||||
OSMO_ASSERT(llc.chunk_size() == 0);
|
||||
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN);
|
||||
OSMO_ASSERT(llc.frame_length() == 0);
|
||||
|
||||
llc.put_frame(data, 2);
|
||||
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 2);
|
||||
OSMO_ASSERT(llc.frame_length() == 2);
|
||||
OSMO_ASSERT(llc.chunk_size() == 2);
|
||||
OSMO_ASSERT(llc.frame[0] == 1);
|
||||
OSMO_ASSERT(llc.frame[1] == 2);
|
||||
|
||||
llc.append_frame(&data[3], 1);
|
||||
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 3);
|
||||
OSMO_ASSERT(llc.frame_length() == 3);
|
||||
OSMO_ASSERT(llc.chunk_size() == 3);
|
||||
|
||||
/* consume two bytes */
|
||||
llc.consume(&out, 1);
|
||||
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 3);
|
||||
OSMO_ASSERT(llc.frame_length() == 3);
|
||||
OSMO_ASSERT(llc.chunk_size() == 2);
|
||||
|
||||
/* check that the bytes are as we expected */
|
||||
OSMO_ASSERT(llc.frame[0] == 1);
|
||||
OSMO_ASSERT(llc.frame[1] == 2);
|
||||
OSMO_ASSERT(llc.frame[2] == 4);
|
||||
|
||||
/* now fill the frame */
|
||||
llc.append_frame(data, llc.remaining_space() - 1);
|
||||
OSMO_ASSERT(llc.fits_in_current_frame(1));
|
||||
OSMO_ASSERT(!llc.fits_in_current_frame(2));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_rlc()
|
||||
{
|
||||
{
|
||||
struct gprs_rlc_data rlc = { 0, };
|
||||
memset(rlc.block, 0x23, RLC_MAX_LEN);
|
||||
uint8_t *p = rlc.prepare(20);
|
||||
OSMO_ASSERT(p == rlc.block);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
OSMO_ASSERT(p[i] == 0x2B);
|
||||
for (int i = 20; i < RLC_MAX_LEN; ++i)
|
||||
OSMO_ASSERT(p[i] == 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_rlc_v_b()
|
||||
{
|
||||
{
|
||||
gprs_rlc_v_b vb;
|
||||
vb.reset();
|
||||
|
||||
for (size_t i = 0; i < RLC_MAX_SNS; ++i)
|
||||
OSMO_ASSERT(vb.is_invalid(i));
|
||||
|
||||
vb.mark_unacked(23);
|
||||
OSMO_ASSERT(vb.is_unacked(23));
|
||||
|
||||
vb.mark_nacked(23);
|
||||
OSMO_ASSERT(vb.is_nacked(23));
|
||||
|
||||
vb.mark_acked(23);
|
||||
OSMO_ASSERT(vb.is_acked(23));
|
||||
|
||||
vb.mark_resend(23);
|
||||
OSMO_ASSERT(vb.is_resend(23));
|
||||
|
||||
vb.mark_invalid(23);
|
||||
OSMO_ASSERT(vb.is_invalid(23));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_rlc_v_n()
|
||||
{
|
||||
{
|
||||
gprs_rlc_v_n vn;
|
||||
vn.reset();
|
||||
|
||||
OSMO_ASSERT(!vn.is_received(0x23));
|
||||
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_INVALID);
|
||||
|
||||
vn.mark_received(0x23);
|
||||
OSMO_ASSERT(vn.is_received(0x23));
|
||||
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_RECEIVED);
|
||||
|
||||
vn.mark_missing(0x23);
|
||||
OSMO_ASSERT(!vn.is_received(0x23));
|
||||
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_MISSING);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_rlc_dl_ul_basic()
|
||||
{
|
||||
{
|
||||
gprs_rlc_dl_window dl_win = { 0, };
|
||||
OSMO_ASSERT(dl_win.window_empty());
|
||||
OSMO_ASSERT(!dl_win.window_stalled());
|
||||
OSMO_ASSERT(dl_win.distance() == 0);
|
||||
|
||||
dl_win.increment_send();
|
||||
OSMO_ASSERT(!dl_win.window_empty());
|
||||
OSMO_ASSERT(!dl_win.window_stalled());
|
||||
OSMO_ASSERT(dl_win.distance() == 1);
|
||||
|
||||
for (int i = 1; i < 64; ++i) {
|
||||
dl_win.increment_send();
|
||||
OSMO_ASSERT(!dl_win.window_empty());
|
||||
OSMO_ASSERT(dl_win.distance() == i + 1);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(dl_win.distance() == 64);
|
||||
OSMO_ASSERT(dl_win.window_stalled());
|
||||
|
||||
dl_win.raise(1);
|
||||
OSMO_ASSERT(dl_win.distance() == 63);
|
||||
OSMO_ASSERT(!dl_win.window_stalled());
|
||||
for (int i = 62; i >= 0; --i) {
|
||||
dl_win.raise(1);
|
||||
OSMO_ASSERT(dl_win.distance() == i);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(dl_win.distance() == 0);
|
||||
OSMO_ASSERT(dl_win.window_empty());
|
||||
|
||||
dl_win.increment_send();
|
||||
dl_win.increment_send();
|
||||
dl_win.increment_send();
|
||||
dl_win.increment_send();
|
||||
OSMO_ASSERT(dl_win.distance() == 4);
|
||||
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
dl_win.increment_send();
|
||||
dl_win.increment_send();
|
||||
dl_win.raise(2);
|
||||
OSMO_ASSERT(dl_win.distance() == 4);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gprs_rlc_ul_window ul_win = { 0, };
|
||||
int count;
|
||||
const char *rbb;
|
||||
char win_rbb[65];
|
||||
uint8_t bin_rbb[8];
|
||||
win_rbb[64] = '\0';
|
||||
|
||||
ul_win.m_v_n.reset();
|
||||
|
||||
OSMO_ASSERT(ul_win.is_in_window(0));
|
||||
OSMO_ASSERT(ul_win.is_in_window(63));
|
||||
OSMO_ASSERT(!ul_win.is_in_window(64));
|
||||
|
||||
OSMO_ASSERT(!ul_win.m_v_n.is_received(0));
|
||||
|
||||
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII";
|
||||
OSMO_ASSERT(ul_win.ssn() == 0);
|
||||
ul_win.update_rbb(win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
Encoding::encode_rbb(win_rbb, bin_rbb);
|
||||
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
|
||||
Decoding::extract_rbb(bin_rbb, win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
|
||||
/* simulate to have received 0, 1 and 5 */
|
||||
OSMO_ASSERT(ul_win.is_in_window(0));
|
||||
count = ul_win.receive_bsn(0);
|
||||
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
|
||||
OSMO_ASSERT(ul_win.v_q() == 1);
|
||||
OSMO_ASSERT(ul_win.v_r() == 1);
|
||||
OSMO_ASSERT(count == 1);
|
||||
|
||||
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIR";
|
||||
OSMO_ASSERT(ul_win.ssn() == 1);
|
||||
ul_win.update_rbb(win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
Encoding::encode_rbb(win_rbb, bin_rbb);
|
||||
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
|
||||
Decoding::extract_rbb(bin_rbb, win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
|
||||
OSMO_ASSERT(ul_win.is_in_window(1));
|
||||
count = ul_win.receive_bsn(1);
|
||||
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
|
||||
OSMO_ASSERT(ul_win.v_q() == 2);
|
||||
OSMO_ASSERT(ul_win.v_r() == 2);
|
||||
OSMO_ASSERT(count == 1);
|
||||
|
||||
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIRR";
|
||||
OSMO_ASSERT(ul_win.ssn() == 2);
|
||||
ul_win.update_rbb(win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
Encoding::encode_rbb(win_rbb, bin_rbb);
|
||||
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
|
||||
Decoding::extract_rbb(bin_rbb, win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
|
||||
OSMO_ASSERT(ul_win.is_in_window(5));
|
||||
count = ul_win.receive_bsn(5);
|
||||
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
|
||||
OSMO_ASSERT(ul_win.v_q() == 2);
|
||||
OSMO_ASSERT(ul_win.v_r() == 6);
|
||||
OSMO_ASSERT(count == 0);
|
||||
|
||||
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIRRIIIR";
|
||||
OSMO_ASSERT(ul_win.ssn() == 6);
|
||||
ul_win.update_rbb(win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
Encoding::encode_rbb(win_rbb, bin_rbb);
|
||||
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
|
||||
Decoding::extract_rbb(bin_rbb, win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
|
||||
OSMO_ASSERT(ul_win.is_in_window(65));
|
||||
OSMO_ASSERT(ul_win.is_in_window(2));
|
||||
OSMO_ASSERT(ul_win.m_v_n.is_received(5));
|
||||
count = ul_win.receive_bsn(65);
|
||||
OSMO_ASSERT(count == 0);
|
||||
OSMO_ASSERT(ul_win.m_v_n.is_received(5));
|
||||
OSMO_ASSERT(ul_win.v_q() == 2);
|
||||
OSMO_ASSERT(ul_win.v_r() == 66);
|
||||
|
||||
rbb = "IIIRIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIR";
|
||||
OSMO_ASSERT(ul_win.ssn() == 66);
|
||||
ul_win.update_rbb(win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
Encoding::encode_rbb(win_rbb, bin_rbb);
|
||||
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
|
||||
Decoding::extract_rbb(bin_rbb, win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
|
||||
OSMO_ASSERT(ul_win.is_in_window(2));
|
||||
OSMO_ASSERT(!ul_win.is_in_window(66));
|
||||
count = ul_win.receive_bsn(2);
|
||||
OSMO_ASSERT(count == 1);
|
||||
OSMO_ASSERT(ul_win.v_q() == 3);
|
||||
OSMO_ASSERT(ul_win.v_r() == 66);
|
||||
|
||||
OSMO_ASSERT(ul_win.is_in_window(66));
|
||||
count = ul_win.receive_bsn(66);
|
||||
OSMO_ASSERT(count == 0);
|
||||
OSMO_ASSERT(ul_win.v_q() == 3);
|
||||
OSMO_ASSERT(ul_win.v_r() == 67);
|
||||
|
||||
for (int i = 3; i <= 67; ++i) {
|
||||
ul_win.receive_bsn(i);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ul_win.v_q() == 68);
|
||||
OSMO_ASSERT(ul_win.v_r() == 68);
|
||||
|
||||
count = ul_win.receive_bsn(68);
|
||||
OSMO_ASSERT(ul_win.v_q() == 69);
|
||||
OSMO_ASSERT(ul_win.v_r() == 69);
|
||||
OSMO_ASSERT(count == 1);
|
||||
|
||||
/* now test the wrapping */
|
||||
OSMO_ASSERT(ul_win.is_in_window(4));
|
||||
OSMO_ASSERT(!ul_win.is_in_window(5));
|
||||
count = ul_win.receive_bsn(4);
|
||||
OSMO_ASSERT(count == 0);
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t lost = 0, recv = 0;
|
||||
char show_rbb[65];
|
||||
BTS dummy_bts;
|
||||
gprs_rlc_dl_window dl_win = { 0, };
|
||||
|
||||
dl_win.m_v_b.reset();
|
||||
|
||||
OSMO_ASSERT(dl_win.window_empty());
|
||||
OSMO_ASSERT(!dl_win.window_stalled());
|
||||
OSMO_ASSERT(dl_win.distance() == 0);
|
||||
|
||||
dl_win.increment_send();
|
||||
OSMO_ASSERT(!dl_win.window_empty());
|
||||
OSMO_ASSERT(!dl_win.window_stalled());
|
||||
OSMO_ASSERT(dl_win.distance() == 1);
|
||||
|
||||
for (int i = 0; i < 35; ++i) {
|
||||
dl_win.increment_send();
|
||||
OSMO_ASSERT(!dl_win.window_empty());
|
||||
OSMO_ASSERT(dl_win.distance() == i + 2);
|
||||
}
|
||||
|
||||
uint8_t rbb_cmp[8] = { 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff };
|
||||
Decoding::extract_rbb(rbb_cmp, show_rbb);
|
||||
printf("show_rbb: %s\n", show_rbb);
|
||||
|
||||
dl_win.update(&dummy_bts, show_rbb, 35, &lost, &recv);
|
||||
OSMO_ASSERT(lost == 0);
|
||||
OSMO_ASSERT(recv == 35);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
|
||||
printf("Making some basic type testing.\n");
|
||||
test_llc();
|
||||
test_rlc();
|
||||
test_rlc_v_b();
|
||||
test_rlc_v_n();
|
||||
test_rlc_dl_ul_basic();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* stubs that should not be reached
|
||||
*/
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
0
tests/types/TypesTest.err
Normal file
0
tests/types/TypesTest.err
Normal file
7
tests/types/TypesTest.ok
Normal file
7
tests/types/TypesTest.ok
Normal file
@@ -0,0 +1,7 @@
|
||||
Making some basic type testing.
|
||||
rbb: 00 00 00 00 00 00 00 00
|
||||
rbb: 00 00 00 00 00 00 00 01
|
||||
rbb: 00 00 00 00 00 00 00 03
|
||||
rbb: 00 00 00 00 00 00 00 31
|
||||
rbb: 10 00 00 00 00 00 00 01
|
||||
show_rbb: IIIIIIIIIIIIIIIIIIIIIIIIIIIIIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
Reference in New Issue
Block a user