forked from cellular-infrastructure/osmo-pcu
Compare commits
755 Commits
Author | SHA1 | Date |
---|---|---|
Holger Hans Peter Freyther | f3f1bde4fc | |
Max | 528ff3910f | |
Max | 2efdf69734 | |
Max | 9d5580b6dd | |
Holger Hans Peter Freyther | 4f8438a6cd | |
Harald Welte | 7f4841b3ac | |
Jacob Erlbeck | 7f28c97fcc | |
Jacob Erlbeck | be881c028f | |
Jacob Erlbeck | 64e7b83139 | |
Jacob Erlbeck | d6752491e1 | |
Jacob Erlbeck | be314d9a54 | |
Jacob Erlbeck | 14e26cbca3 | |
Jacob Erlbeck | 2d2efb13e7 | |
Jacob Erlbeck | 0f5c6956dd | |
Jacob Erlbeck | fbd82e4e9f | |
Jacob Erlbeck | b55f313735 | |
Jacob Erlbeck | 215e18c9d4 | |
Jacob Erlbeck | 2305afd86c | |
Jacob Erlbeck | 4cc46d3e2a | |
Jacob Erlbeck | 9e8593917f | |
Jacob Erlbeck | f1a7b8fc66 | |
Jacob Erlbeck | 7c72acaa94 | |
Jacob Erlbeck | 1ff70c26e3 | |
Jacob Erlbeck | 7d5157ee17 | |
Jacob Erlbeck | be80c3670e | |
Jacob Erlbeck | 2647a337a8 | |
Jacob Erlbeck | 18831c3ca9 | |
Jacob Erlbeck | 7505f1d636 | |
Jacob Erlbeck | 4e7424d47b | |
Jacob Erlbeck | 9876a3ba5d | |
Jacob Erlbeck | 6b356a58d1 | |
Jacob Erlbeck | f2694b74c9 | |
Jacob Erlbeck | 8eb17143f2 | |
Jacob Erlbeck | 81a04f7d79 | |
Jacob Erlbeck | 646da1ba8d | |
Jacob Erlbeck | 5a3c84d0fd | |
Jacob Erlbeck | 5f93f855a7 | |
Jacob Erlbeck | f04a5b33ec | |
Jacob Erlbeck | 0316dc6e48 | |
Jacob Erlbeck | 29e3a2f0f3 | |
Jacob Erlbeck | 56d06f3e1e | |
Jacob Erlbeck | 2ca86afdec | |
Jacob Erlbeck | 741d25cb6f | |
Jacob Erlbeck | f4bb42459c | |
Jacob Erlbeck | 4a07a3be11 | |
Jacob Erlbeck | 0df80be95e | |
Jacob Erlbeck | acf66fb456 | |
Jacob Erlbeck | a35122c496 | |
Jacob Erlbeck | 9f6867033f | |
Jacob Erlbeck | 36df7740dd | |
Jacob Erlbeck | 08c72fb4a9 | |
Jacob Erlbeck | 8cba7e9b6d | |
Jacob Erlbeck | 13965aed74 | |
Jacob Erlbeck | db88380b76 | |
Jacob Erlbeck | 3b1c553773 | |
Jacob Erlbeck | 9b3d7e0159 | |
Jacob Erlbeck | f9940ca8d7 | |
Jacob Erlbeck | 53bc1861c4 | |
Jacob Erlbeck | b4beb545f7 | |
Jacob Erlbeck | d7630f2256 | |
Jacob Erlbeck | 419b034975 | |
Jacob Erlbeck | b41262fa0b | |
Jacob Erlbeck | eb08f86c02 | |
Jacob Erlbeck | f2f24b0959 | |
Jacob Erlbeck | 192bf33ffb | |
Jacob Erlbeck | 2bef4dbd43 | |
Jacob Erlbeck | 3fdcb2b84e | |
Jacob Erlbeck | ae9c575d2c | |
Jacob Erlbeck | c2141c6064 | |
Jacob Erlbeck | 70955c765c | |
Jacob Erlbeck | a88d065606 | |
Jacob Erlbeck | 5058bd6e9e | |
Jacob Erlbeck | fec94d1c5c | |
Jacob Erlbeck | 14bb0947b4 | |
Jacob Erlbeck | 3a3b6a7e86 | |
Jacob Erlbeck | 53efa3eeb6 | |
Jacob Erlbeck | 314ec4db40 | |
Jacob Erlbeck | f0e403911d | |
Jacob Erlbeck | 6e9f9c20e9 | |
Jacob Erlbeck | cc34a5b43f | |
Jacob Erlbeck | 22f8087b98 | |
Jacob Erlbeck | fc1b3e6c90 | |
Jacob Erlbeck | f2ba4cbf51 | |
Jacob Erlbeck | 7e7a261de0 | |
Jacob Erlbeck | a47aaa4e7a | |
Jacob Erlbeck | 08f631c197 | |
Jacob Erlbeck | 369c2fb7b4 | |
Jacob Erlbeck | 166c9fc827 | |
Jacob Erlbeck | 7b57997874 | |
Jacob Erlbeck | cb7289094a | |
Jacob Erlbeck | 96ccea8436 | |
Jacob Erlbeck | 4c9e549aa3 | |
Jacob Erlbeck | 0d05805b76 | |
Jacob Erlbeck | ed2dbf6954 | |
Jacob Erlbeck | bf49f042d4 | |
Jacob Erlbeck | aa9daa1b9d | |
Jacob Erlbeck | 38f18698b3 | |
Jacob Erlbeck | 5ffbb2744f | |
Jacob Erlbeck | a24e1cd508 | |
Jacob Erlbeck | 37005a165d | |
Jacob Erlbeck | 580edbc326 | |
Jacob Erlbeck | 8e323b39f9 | |
Jacob Erlbeck | 869449cdd0 | |
Jacob Erlbeck | 8f8197f3fd | |
Jacob Erlbeck | a3a567e765 | |
Jacob Erlbeck | e1ca87f057 | |
Jacob Erlbeck | 93c55d04e5 | |
Jacob Erlbeck | 2b3121eebf | |
Jacob Erlbeck | 2e3a81e4b3 | |
Jacob Erlbeck | 5c75480e90 | |
Jacob Erlbeck | d88bb2e825 | |
Jacob Erlbeck | c362df25a2 | |
Jacob Erlbeck | 27dc941475 | |
Jacob Erlbeck | ead5e4724c | |
Jacob Erlbeck | 6e75bc7fe3 | |
Jacob Erlbeck | 845c01ef3f | |
Jacob Erlbeck | 554a835e90 | |
Jacob Erlbeck | 39c6c7f738 | |
Jacob Erlbeck | b3100e187b | |
Jacob Erlbeck | e8f5fe5255 | |
Jacob Erlbeck | ce1beb423c | |
Jacob Erlbeck | 784a0bd000 | |
Jacob Erlbeck | d87e1d6ab7 | |
Jacob Erlbeck | 6167925147 | |
Jacob Erlbeck | 4abc686d76 | |
Jacob Erlbeck | 392a545336 | |
Jacob Erlbeck | 4aa78a8bea | |
Jacob Erlbeck | 6c3dc61db5 | |
Jacob Erlbeck | 3b802e3c4a | |
Jacob Erlbeck | 690a734ebf | |
Jacob Erlbeck | 9e862e1e7f | |
Jacob Erlbeck | d0222cfe2d | |
Jacob Erlbeck | 409f980a18 | |
Jacob Erlbeck | 14e00f8f66 | |
Jacob Erlbeck | 5265f59525 | |
Jacob Erlbeck | 86b6f05d19 | |
Jacob Erlbeck | 5643f35fb4 | |
Jacob Erlbeck | 76d767cbe8 | |
Jacob Erlbeck | 953c78987a | |
Jacob Erlbeck | c3c58046c7 | |
Jacob Erlbeck | 111ebe84c2 | |
Jacob Erlbeck | eb93f592e5 | |
Jacob Erlbeck | f5898a0528 | |
Jacob Erlbeck | edfd7e3d94 | |
Jacob Erlbeck | acfb883011 | |
Jacob Erlbeck | 42aba81c2f | |
Harald Welte | 08e5d604d3 | |
Harald Welte | 19d1e9270d | |
Harald Welte | 218482769b | |
Harald Welte | d32cbbb130 | |
Holger Hans Peter Freyther | 8df447dc77 | |
Holger Hans Peter Freyther | b8a5426cf0 | |
Jacob Erlbeck | 2db0f08e08 | |
Jacob Erlbeck | 7c8d39a67b | |
Jacob Erlbeck | c8cbfc2c98 | |
Jacob Erlbeck | ae0a799f44 | |
Jacob Erlbeck | 91ff7d1864 | |
Jacob Erlbeck | 9659d59307 | |
Jacob Erlbeck | cf6ae9d12f | |
Jacob Erlbeck | af75ce8e15 | |
Jacob Erlbeck | be4a08b58a | |
Jacob Erlbeck | 60f77033ad | |
Jacob Erlbeck | e77d49f2a2 | |
Jacob Erlbeck | ac49d0943a | |
Jacob Erlbeck | 16d29c7da4 | |
Jacob Erlbeck | b6b3c7eb27 | |
Jacob Erlbeck | 1c95bd383e | |
Jacob Erlbeck | 159d4de370 | |
Jacob Erlbeck | c62216b4fc | |
Jacob Erlbeck | 6835cead8c | |
Jacob Erlbeck | 6e013a136a | |
Jacob Erlbeck | af387e2199 | |
Jacob Erlbeck | 444bc82081 | |
Jacob Erlbeck | 23c4b3f158 | |
Jacob Erlbeck | af45473cd5 | |
Jacob Erlbeck | cef20ae67a | |
Jacob Erlbeck | ee31090b2e | |
Jacob Erlbeck | 64921d217b | |
Jacob Erlbeck | 56f99d19c3 | |
Jacob Erlbeck | e0b21f41c2 | |
Jacob Erlbeck | 257b630216 | |
Jacob Erlbeck | e91bd3babd | |
Jacob Erlbeck | b139598b1c | |
Jacob Erlbeck | 076f5c794d | |
Jacob Erlbeck | 537b149828 | |
Jacob Erlbeck | 4a6fe534ce | |
Jacob Erlbeck | 2b349b5d33 | |
Jacob Erlbeck | ebebad1c92 | |
Jacob Erlbeck | 56af6d55ed | |
Jacob Erlbeck | f76fedeed5 | |
Jacob Erlbeck | fea17f8b8c | |
Jacob Erlbeck | af9a39d954 | |
Jacob Erlbeck | 28c40b1757 | |
Jacob Erlbeck | 3449a61032 | |
Jacob Erlbeck | 1c3b8998bc | |
Jacob Erlbeck | ac28905082 | |
Jacob Erlbeck | 04e72d34f5 | |
Jacob Erlbeck | 6eed1911fd | |
Jacob Erlbeck | b31f5ef699 | |
Jacob Erlbeck | d4ad731bae | |
Jacob Erlbeck | 4f666bc113 | |
Jacob Erlbeck | 7f79f0d332 | |
Jacob Erlbeck | 77da35515c | |
Jacob Erlbeck | 7b3675bf7a | |
Jacob Erlbeck | 0f352a6f22 | |
Jacob Erlbeck | bf9042203d | |
Jacob Erlbeck | 7af53e61f0 | |
Jacob Erlbeck | 88fb6136fb | |
Jacob Erlbeck | e21b79cb21 | |
Jacob Erlbeck | f16a069fd7 | |
Jacob Erlbeck | 5979fe9d8a | |
Jacob Erlbeck | 400ec02e8a | |
Jacob Erlbeck | 40da3e17e5 | |
Jacob Erlbeck | 5a2b8be3f5 | |
Jacob Erlbeck | 2b558857dd | |
Jacob Erlbeck | a8c2aaf6f0 | |
Jacob Erlbeck | 69c9bfa089 | |
Jacob Erlbeck | b2439bbb8a | |
Jacob Erlbeck | 3a10dbd564 | |
Jacob Erlbeck | e0853cdf42 | |
Jacob Erlbeck | 5879c6493f | |
Jacob Erlbeck | 47a57f6f86 | |
Jacob Erlbeck | 61205a7e65 | |
Jacob Erlbeck | 57cf69a18c | |
Jacob Erlbeck | dd08ac86e6 | |
Jacob Erlbeck | bae33a7001 | |
Jacob Erlbeck | 5e46a20e03 | |
Jacob Erlbeck | c135b878cd | |
Jacob Erlbeck | 1139ec1d0f | |
Jacob Erlbeck | 3db617f14a | |
Jacob Erlbeck | efe62a7395 | |
Jacob Erlbeck | 14376a73a5 | |
Jacob Erlbeck | 506f156f7a | |
Jacob Erlbeck | 1653f837e3 | |
Jacob Erlbeck | 20b7ba7501 | |
Jacob Erlbeck | cc9358f95a | |
Jacob Erlbeck | 16c6ecc365 | |
Jacob Erlbeck | 5f494b8415 | |
Jacob Erlbeck | 9ae282372c | |
Jacob Erlbeck | ed46afda6f | |
Jacob Erlbeck | ea65c72d06 | |
Jacob Erlbeck | c91c18e6ef | |
Jacob Erlbeck | e0c734dcfe | |
Jacob Erlbeck | 07111668d4 | |
Jacob Erlbeck | 4944c195d4 | |
Jacob Erlbeck | 5cd496d208 | |
Jacob Erlbeck | 83426b20a3 | |
Jacob Erlbeck | 617c7127f4 | |
Jacob Erlbeck | 23f93a15ca | |
Jacob Erlbeck | ec478756cc | |
Jacob Erlbeck | 9380f5d218 | |
Jacob Erlbeck | ac89a555fa | |
Jacob Erlbeck | 699b8dca49 | |
Jacob Erlbeck | ccc34e4d30 | |
Jacob Erlbeck | c6d4ceeda6 | |
Jacob Erlbeck | 9ec49e2411 | |
Jacob Erlbeck | fa464bbce9 | |
Jacob Erlbeck | e565564bc9 | |
Jacob Erlbeck | cb1b494c89 | |
Jacob Erlbeck | 6d86628e5b | |
Jacob Erlbeck | e2e004e7a9 | |
Jacob Erlbeck | ace7b570a0 | |
Jacob Erlbeck | f1379346f7 | |
Jacob Erlbeck | 34cf156b80 | |
Jacob Erlbeck | 9cc783a87d | |
Jacob Erlbeck | a99d95e3af | |
Jacob Erlbeck | e500e2e5d1 | |
Jacob Erlbeck | b671dbfe94 | |
Jacob Erlbeck | 1e50a3dade | |
Jacob Erlbeck | 70b96aa232 | |
Jacob Erlbeck | 07eb655244 | |
Jacob Erlbeck | 1eae96ca2f | |
Jacob Erlbeck | 626369c2fb | |
Jacob Erlbeck | 409efa1ec8 | |
Jacob Erlbeck | 411686402b | |
Jacob Erlbeck | 04a108617a | |
Jacob Erlbeck | e1d2b3568a | |
Jacob Erlbeck | da1a79ef5b | |
Jacob Erlbeck | 51b1151044 | |
Jacob Erlbeck | 94cde130ca | |
Jacob Erlbeck | e4bcb62dbf | |
Jacob Erlbeck | 20f6fd1b63 | |
Jacob Erlbeck | b4584ff6c4 | |
Jacob Erlbeck | 0808f68601 | |
Jacob Erlbeck | 25db7c6116 | |
Jacob Erlbeck | 7bf9f49728 | |
Jacob Erlbeck | 0ae4313800 | |
Jacob Erlbeck | d0aee85b29 | |
Jacob Erlbeck | 09fdf6622a | |
Jacob Erlbeck | 37e896dff1 | |
Jacob Erlbeck | b33e675e5a | |
Jacob Erlbeck | 8158ea7288 | |
Jacob Erlbeck | 144a1d0516 | |
Jacob Erlbeck | 8322d08071 | |
Jacob Erlbeck | a17fccbcf4 | |
Jacob Erlbeck | 1751c62c98 | |
Jacob Erlbeck | f47f68a9d8 | |
Jacob Erlbeck | 62e96a3535 | |
Jacob Erlbeck | a700dd9e11 | |
Jacob Erlbeck | 17214bb06d | |
Jacob Erlbeck | befc760f86 | |
Jacob Erlbeck | 489a2b35d8 | |
Jacob Erlbeck | 10ed79553a | |
Jacob Erlbeck | 1d0a52a349 | |
Jacob Erlbeck | 6dbe822062 | |
Jacob Erlbeck | b3f713bd7b | |
Jacob Erlbeck | 3c91cb881d | |
Jacob Erlbeck | a098c19b55 | |
Jacob Erlbeck | d9e102472a | |
Jacob Erlbeck | 1db67e0a35 | |
Jacob Erlbeck | 9200ce6019 | |
Jacob Erlbeck | ddfc0d5763 | |
Jacob Erlbeck | d3eac2867a | |
Jacob Erlbeck | 1c68abaffa | |
Jacob Erlbeck | 71e55118f5 | |
Jacob Erlbeck | 7b9f825ae8 | |
Jacob Erlbeck | b0e5eaf59a | |
Jacob Erlbeck | 9a2845d491 | |
Jacob Erlbeck | 0e50ce6145 | |
Jacob Erlbeck | 767193e20b | |
Holger Hans Peter Freyther | d1cb41bfd0 | |
Holger Hans Peter Freyther | 5752285bc5 | |
Holger Hans Peter Freyther | b75e23143b | |
Jacob Erlbeck | 4f459799e3 | |
Jacob Erlbeck | be0cbc1b7e | |
Jacob Erlbeck | 87597358cf | |
Jacob Erlbeck | 9399046729 | |
Jacob Erlbeck | e43460b50f | |
Jacob Erlbeck | 5367086175 | |
Jacob Erlbeck | dfef28de88 | |
Jacob Erlbeck | 67c385046d | |
Jacob Erlbeck | fecece0e59 | |
Jacob Erlbeck | e04e0b0a20 | |
Jacob Erlbeck | 6eeb7c7e74 | |
Jacob Erlbeck | 6e4ccec6c4 | |
Jacob Erlbeck | 0288cdb0a8 | |
Jacob Erlbeck | 3d62fc55d8 | |
Jacob Erlbeck | 87d7341fbe | |
Jacob Erlbeck | 29d91e9271 | |
Jacob Erlbeck | 801d6fe40c | |
Jacob Erlbeck | 0494c75a53 | |
Jacob Erlbeck | 0d39dc92b5 | |
Jacob Erlbeck | 1f33294b1c | |
Jacob Erlbeck | 2acfbebfd3 | |
Jacob Erlbeck | c0c580c414 | |
Jacob Erlbeck | 56e8d71090 | |
Jacob Erlbeck | 00fc6b13f2 | |
Jacob Erlbeck | 90de3a7ffe | |
Jacob Erlbeck | d58b711eec | |
Jacob Erlbeck | cc12f02658 | |
Jacob Erlbeck | d0261b72de | |
Jacob Erlbeck | 0c1c8778df | |
Jacob Erlbeck | 0a0b5dcb32 | |
Jacob Erlbeck | 502bd1feea | |
Jacob Erlbeck | e25b5b91f6 | |
Jacob Erlbeck | 2cbe80b53e | |
Jacob Erlbeck | 3bed5d11d2 | |
Jacob Erlbeck | cbb1e70554 | |
Jacob Erlbeck | c495209122 | |
Jacob Erlbeck | 005ee7f862 | |
Jacob Erlbeck | 2493c660e9 | |
Jacob Erlbeck | a3e4509ff9 | |
Jacob Erlbeck | eceb910fef | |
Jacob Erlbeck | 95340242ed | |
Jacob Erlbeck | 612e93e360 | |
Jacob Erlbeck | 39645b824a | |
Jacob Erlbeck | 1e96af6325 | |
Jacob Erlbeck | 7c44415d78 | |
Jacob Erlbeck | 0eabffdc35 | |
Jacob Erlbeck | 1842c921b3 | |
Jacob Erlbeck | adcdf150a6 | |
Jacob Erlbeck | a41a71e2d4 | |
Jacob Erlbeck | 297edf754f | |
Jacob Erlbeck | 08fe76a893 | |
Jacob Erlbeck | 5e9f40d3d9 | |
Jacob Erlbeck | 18fef10641 | |
Holger Hans Peter Freyther | f5c97476de | |
Holger Hans Peter Freyther | 49f26bf6e8 | |
Daniel Willmann | 07e39302ec | |
Daniel Willmann | 0f58af6627 | |
Daniel Willmann | 635d47c78c | |
Daniel Willmann | 1c15573012 | |
Daniel Willmann | efd5dbb168 | |
Daniel Willmann | 510d7d36ed | |
Daniel Willmann | e481815583 | |
Daniel Willmann | 08e57c8366 | |
Daniel Willmann | 341689d6c5 | |
Holger Hans Peter Freyther | ad1fcccf60 | |
Daniel Willmann | ca102af92b | |
Daniel Willmann | eb100244b0 | |
Daniel Willmann | 418a4230e0 | |
Daniel Willmann | cf706b0775 | |
Daniel Willmann | e2732e2f59 | |
Holger Hans Peter Freyther | f72fcfe219 | |
Daniel Willmann | 7e994e392d | |
Daniel Willmann | 6a8a1dcda2 | |
Daniel Willmann | d1d1633121 | |
Daniel Willmann | cd44ec41c5 | |
Daniel Willmann | f55e58f5cf | |
Daniel Willmann | 350f64d9e2 | |
Daniel Willmann | 4f3c420852 | |
Daniel Willmann | b2886f1a0d | |
Daniel Willmann | 93d1711011 | |
Daniel Willmann | 6a16e0c092 | |
Daniel Willmann | 351a573396 | |
Daniel Willmann | 3016888ee0 | |
Daniel Willmann | 532a4b54f5 | |
Daniel Willmann | 1dac2ebb71 | |
Daniel Willmann | b8f260176e | |
Daniel Willmann | 6c813fc9bc | |
Daniel Willmann | 538ac5b574 | |
Daniel Willmann | 2354402b7a | |
Daniel Willmann | 057c285cd7 | |
Daniel Willmann | 1b3864fc47 | |
Holger Hans Peter Freyther | 4bbe3349c2 | |
Daniel Willmann | 48aa0b0d99 | |
Daniel Willmann | 0d12a2fa89 | |
Daniel Willmann | fe6e2e4a08 | |
Daniel Willmann | 078bb713e1 | |
Daniel Willmann | 2207c5e4ef | |
Daniel Willmann | febf1a0ac9 | |
Daniel Willmann | 54044b0635 | |
Daniel Willmann | b59d61b4b4 | |
Holger Hans Peter Freyther | e8915b9d9d | |
Holger Hans Peter Freyther | 2c98f1db78 | |
Holger Hans Peter Freyther | c421e8aaf2 | |
Holger Hans Peter Freyther | 35cc1c0ff3 | |
Holger Hans Peter Freyther | f3405e5b03 | |
Daniel Willmann | 6d8884de49 | |
Daniel Willmann | 77e58f602d | |
Daniel Willmann | 5d77f14904 | |
Daniel Willmann | 17a1d5e162 | |
Daniel Willmann | 1e0c61032f | |
Daniel Willmann | cf1fae7f38 | |
Daniel Willmann | 73191a443f | |
Daniel Willmann | fc03bbe078 | |
Andreas Eversberg | 9167055ed2 | |
Holger Hans Peter Freyther | 8f3520579a | |
Holger Hans Peter Freyther | f81e2f7621 | |
Holger Hans Peter Freyther | 59fe8f79cc | |
Holger Hans Peter Freyther | d8f339592d | |
Holger Hans Peter Freyther | a09e33cdeb | |
Holger Hans Peter Freyther | e5109ba1f0 | |
Holger Hans Peter Freyther | 3d0cc2f97d | |
Holger Hans Peter Freyther | a004799699 | |
Holger Hans Peter Freyther | b293043e2d | |
Daniel Willmann | 80367aae17 | |
Daniel Willmann | 772415fd8a | |
Daniel Willmann | afa72f5210 | |
Holger Hans Peter Freyther | 85c1ea5cb6 | |
Daniel Willmann | 3ce011f44f | |
Daniel Willmann | d54d9f5c75 | |
Daniel Willmann | 146514e180 | |
Daniel Willmann | 55844795be | |
Daniel Willmann | 7c3751b10b | |
Daniel Willmann | f4a1ec6ce7 | |
Holger Hans Peter Freyther | c2fab7a6ff | |
Andreas Eversberg | 0a940087b2 | |
Holger Hans Peter Freyther | 2bf3446f7a | |
Holger Hans Peter Freyther | 746b390201 | |
Andreas Eversberg | 1cd9d886e6 | |
Andreas Eversberg | fe2dcc8aec | |
Holger Hans Peter Freyther | f3eec04655 | |
Andreas Eversberg | 765736dc77 | |
Andreas Eversberg | 7a16d46fdc | |
Andreas Eversberg | ccde4c462d | |
Andreas Eversberg | b03d427b08 | |
Holger Hans Peter Freyther | 73193110f2 | |
Holger Hans Peter Freyther | 1fe69323ad | |
Holger Hans Peter Freyther | 3fd2ddf1a2 | |
Holger Hans Peter Freyther | dd4af8045f | |
Holger Hans Peter Freyther | e45c19b3e8 | |
Holger Hans Peter Freyther | 882fc9b174 | |
Holger Hans Peter Freyther | f34f34495b | |
Holger Hans Peter Freyther | df022f6cb2 | |
Holger Hans Peter Freyther | c7b998cc73 | |
Holger Hans Peter Freyther | 4af30533f0 | |
Holger Hans Peter Freyther | f37e514a96 | |
Holger Hans Peter Freyther | 8f399de135 | |
Holger Hans Peter Freyther | ba26368040 | |
Holger Hans Peter Freyther | 6f791d049a | |
Daniel Willmann | 6f0796a33a | |
Daniel Willmann | fdcdde2756 | |
Ivan Kluchnikov | 402cdcd02f | |
Daniel Willmann | 2f1974b5ac | |
Ivan Kluchnikov | 1b517342ae | |
Holger Hans Peter Freyther | 705653b2d7 | |
Holger Hans Peter Freyther | aadfc2e121 | |
Daniel Willmann | 9c623892f5 | |
Holger Hans Peter Freyther | a42b2ad5ed | |
Holger Hans Peter Freyther | 550bb88a9e | |
Holger Hans Peter Freyther | 88553abf4d | |
Henning Heinold | c92b964e2d | |
Holger Hans Peter Freyther | b3a87ced5a | |
Holger Hans Peter Freyther | 81e8f0a8a2 | |
Holger Hans Peter Freyther | 752a3b2baa | |
Daniel Willmann | 5e94cd4fde | |
Daniel Willmann | cc5a4cbe9b | |
Daniel Willmann | 48df40d2a4 | |
Daniel Willmann | f1786a375f | |
Daniel Willmann | 6f7cb2cb4f | |
Daniel Willmann | 0f2b5fc749 | |
Daniel Willmann | 5241c1a018 | |
Daniel Willmann | 52ea8a0d87 | |
Daniel Willmann | e6e605ba86 | |
Daniel Willmann | c3f4330fa3 | |
Daniel Willmann | f86fb7a953 | |
Daniel Willmann | 8a31f9e016 | |
Holger Hans Peter Freyther | 11f2d58dbd | |
Holger Hans Peter Freyther | 3cbf9e040c | |
Holger Hans Peter Freyther | 3c95776805 | |
Holger Hans Peter Freyther | 7a5f3c2153 | |
Holger Hans Peter Freyther | 7f3e662b34 | |
Holger Hans Peter Freyther | cbb00ebd04 | |
Holger Hans Peter Freyther | 423dd2286b | |
Holger Hans Peter Freyther | 4c06d9155c | |
Holger Hans Peter Freyther | c15d5cc230 | |
Holger Hans Peter Freyther | 9977e1545a | |
Holger Hans Peter Freyther | 6ab5b24be4 | |
Holger Hans Peter Freyther | 7decedcbf8 | |
Holger Hans Peter Freyther | faf3ef45b3 | |
Holger Hans Peter Freyther | e9b1ebba9d | |
Holger Hans Peter Freyther | 270f7fce1d | |
Holger Hans Peter Freyther | f1593b7c49 | |
Holger Hans Peter Freyther | ab6cd921e3 | |
Holger Hans Peter Freyther | 9a76968ec4 | |
Holger Hans Peter Freyther | 9c5539d46d | |
Holger Hans Peter Freyther | 86dc355a33 | |
Holger Hans Peter Freyther | bc15570651 | |
Holger Hans Peter Freyther | e358ff8fa4 | |
Holger Hans Peter Freyther | df6b4f52e0 | |
Holger Hans Peter Freyther | 9eb8ace260 | |
Holger Hans Peter Freyther | 12c039cdb2 | |
Holger Hans Peter Freyther | a6ba67cb3a | |
Holger Hans Peter Freyther | 8b16ae30fe | |
Holger Hans Peter Freyther | c03e38291a | |
Holger Hans Peter Freyther | 1577779526 | |
Holger Hans Peter Freyther | ef93bdb19b | |
Holger Hans Peter Freyther | 64b49bc3ce | |
Holger Hans Peter Freyther | 40fc8f9e46 | |
Holger Hans Peter Freyther | b18aedcc50 | |
Holger Hans Peter Freyther | 9525567d77 | |
Holger Hans Peter Freyther | 6b5660c19f | |
Holger Hans Peter Freyther | 321f3c3104 | |
Holger Hans Peter Freyther | 092478f294 | |
Holger Hans Peter Freyther | 5a7f636ce4 | |
Holger Hans Peter Freyther | c6382ddd6e | |
Holger Hans Peter Freyther | 6058220d2a | |
Holger Hans Peter Freyther | 58db60c68e | |
Daniel Willmann | c3d5325fc8 | |
Holger Hans Peter Freyther | c70aae4697 | |
Holger Hans Peter Freyther | 937e2a6919 | |
Holger Hans Peter Freyther | e5dc2a0ac5 | |
Holger Hans Peter Freyther | 796270bf83 | |
Holger Hans Peter Freyther | 9241fd0957 | |
Holger Hans Peter Freyther | e9429b5d3e | |
Holger Hans Peter Freyther | 55cf994c29 | |
Holger Hans Peter Freyther | b1302b083e | |
Holger Hans Peter Freyther | 32f9a59ab4 | |
Holger Hans Peter Freyther | e23102602c | |
Holger Hans Peter Freyther | b3d5ee2934 | |
Holger Hans Peter Freyther | 51e093bd1c | |
Holger Hans Peter Freyther | 985fd114f2 | |
Holger Hans Peter Freyther | fce431cf3a | |
Holger Hans Peter Freyther | aa35ba7584 | |
Holger Hans Peter Freyther | 857281f7ff | |
Holger Hans Peter Freyther | d26318e4a7 | |
Holger Hans Peter Freyther | 4e8b50cd8d | |
Holger Hans Peter Freyther | 0e0f2f5faf | |
Holger Hans Peter Freyther | a6e47c7f54 | |
Holger Hans Peter Freyther | 5697b4ccfa | |
Holger Hans Peter Freyther | acb5427bda | |
Holger Hans Peter Freyther | be57081721 | |
Holger Hans Peter Freyther | b7840466ce | |
Holger Hans Peter Freyther | a1da251c10 | |
Holger Hans Peter Freyther | 096f6f9f39 | |
Holger Hans Peter Freyther | 758dc12c9f | |
Holger Hans Peter Freyther | 28e5378b55 | |
Holger Hans Peter Freyther | 9948514086 | |
Holger Hans Peter Freyther | 77e05971b4 | |
Holger Hans Peter Freyther | bc1626e5de | |
Holger Hans Peter Freyther | b809866be5 | |
Holger Hans Peter Freyther | 5464c9baf2 | |
Holger Hans Peter Freyther | 34f6e5ebe6 | |
Holger Hans Peter Freyther | 474685e26e | |
Holger Hans Peter Freyther | bd449f57a7 | |
Holger Hans Peter Freyther | 870c601f1d | |
Holger Hans Peter Freyther | 6b88cddc21 | |
Holger Hans Peter Freyther | 8021644e9d | |
Holger Hans Peter Freyther | ec80f82824 | |
Holger Hans Peter Freyther | fc498c9e7b | |
Holger Hans Peter Freyther | 875fc895a8 | |
Holger Hans Peter Freyther | e1a075ab59 | |
Holger Hans Peter Freyther | 53a336f0e5 | |
Holger Hans Peter Freyther | 8d0e489484 | |
Holger Hans Peter Freyther | 15bb1a2a51 | |
Holger Hans Peter Freyther | 5da2014f13 | |
Holger Hans Peter Freyther | 7a344716a6 | |
Holger Hans Peter Freyther | cb5c49b581 | |
Holger Hans Peter Freyther | 750ca67ce9 | |
Holger Hans Peter Freyther | 842808848c | |
Holger Hans Peter Freyther | 5a9658168a | |
Holger Hans Peter Freyther | 396f4161cb | |
Holger Hans Peter Freyther | c1ae22694c | |
Holger Hans Peter Freyther | 90b87ea5e6 | |
Holger Hans Peter Freyther | 180def907b | |
Holger Hans Peter Freyther | 1997787c52 | |
Holger Hans Peter Freyther | b98dd9e240 | |
Holger Hans Peter Freyther | 93e048fe27 | |
Holger Hans Peter Freyther | 158776411b | |
Holger Hans Peter Freyther | f537298cca | |
Holger Hans Peter Freyther | 61a0a04d26 | |
Holger Hans Peter Freyther | 3dc56a3b34 | |
Holger Hans Peter Freyther | ae03f22199 | |
Holger Hans Peter Freyther | 22b31190cb | |
Holger Hans Peter Freyther | 46bcb8d59d | |
Holger Hans Peter Freyther | 02beed5e98 | |
Holger Hans Peter Freyther | 2db7e7e7db | |
Holger Hans Peter Freyther | 24c1a5ba29 | |
Holger Hans Peter Freyther | 42de18f347 | |
Holger Hans Peter Freyther | d9262b3b55 | |
Holger Hans Peter Freyther | 40cfaa6837 | |
Holger Hans Peter Freyther | 7292373f92 | |
Holger Hans Peter Freyther | 70ddde6929 | |
Holger Hans Peter Freyther | f63cabd931 | |
Holger Hans Peter Freyther | a54bbbbf02 | |
Holger Hans Peter Freyther | dea63b96e0 | |
Holger Hans Peter Freyther | 05f8efc1a2 | |
Holger Hans Peter Freyther | 4f753c64d6 | |
Holger Hans Peter Freyther | 65be4808af | |
Holger Hans Peter Freyther | 6673005215 | |
Holger Hans Peter Freyther | 782f6ddf99 | |
Holger Hans Peter Freyther | 86300bbeea | |
Holger Hans Peter Freyther | af8094d799 | |
Holger Hans Peter Freyther | aa9c326d7e | |
Holger Hans Peter Freyther | fcbc702112 | |
Holger Hans Peter Freyther | d11290b90b | |
Holger Hans Peter Freyther | 6f9f434463 | |
Holger Hans Peter Freyther | 9ae367f639 | |
Holger Hans Peter Freyther | b65e08a7be | |
Holger Hans Peter Freyther | 545876550f | |
Holger Hans Peter Freyther | 6796ed23ab | |
Holger Hans Peter Freyther | 09ef27ae04 | |
Holger Hans Peter Freyther | 4ed1dae533 | |
Holger Hans Peter Freyther | 1702f102af | |
Holger Hans Peter Freyther | 7ce21eb042 | |
Holger Hans Peter Freyther | 34bd8bdf30 | |
Holger Hans Peter Freyther | 9f0c1d216a | |
Holger Hans Peter Freyther | cedf890928 | |
Holger Hans Peter Freyther | 621dc2fd01 | |
Holger Hans Peter Freyther | 111614a994 | |
Holger Hans Peter Freyther | d6ef5345e5 | |
Holger Hans Peter Freyther | 63f29d6f73 | |
Holger Hans Peter Freyther | 6ca0a9076e | |
Holger Hans Peter Freyther | 9446485e3d | |
Holger Hans Peter Freyther | f0984897a5 | |
Holger Hans Peter Freyther | 24e98d039d | |
Holger Hans Peter Freyther | 17b0d83a1f | |
Holger Hans Peter Freyther | ba5543fbf3 | |
Holger Hans Peter Freyther | b78adcdd11 | |
Holger Hans Peter Freyther | 9b30c7f46e | |
Holger Hans Peter Freyther | b6acfdaa24 | |
Holger Hans Peter Freyther | 67ed34eedb | |
Holger Hans Peter Freyther | 1b33361bab | |
Holger Hans Peter Freyther | b19d7268c3 | |
Holger Hans Peter Freyther | 1af73f6c81 | |
Holger Hans Peter Freyther | ee7a535608 | |
Holger Hans Peter Freyther | 485860cc31 | |
Holger Hans Peter Freyther | d507ce6b85 | |
Holger Hans Peter Freyther | 8d7a632eef | |
Holger Hans Peter Freyther | 4ffc260869 | |
Holger Hans Peter Freyther | 9f521cd4af | |
Holger Hans Peter Freyther | b0250ebeac | |
Holger Hans Peter Freyther | 698b612188 | |
Holger Hans Peter Freyther | ae947fcf67 | |
Holger Hans Peter Freyther | 344ff48756 | |
Holger Hans Peter Freyther | 173a240a7e | |
Holger Hans Peter Freyther | 45396a99d9 | |
Holger Hans Peter Freyther | bfdd5f285b | |
Holger Hans Peter Freyther | 948a3d62b1 | |
Holger Hans Peter Freyther | b0a0075845 | |
Holger Hans Peter Freyther | 11a748935e | |
Holger Hans Peter Freyther | 8481a0553d | |
Holger Hans Peter Freyther | 743bafa50c | |
Holger Hans Peter Freyther | 96efa70a9e | |
Holger Hans Peter Freyther | 02ab4a8803 | |
Holger Hans Peter Freyther | 96173aec93 | |
Holger Hans Peter Freyther | 1c344e2668 | |
Holger Hans Peter Freyther | 7380babdba | |
Holger Hans Peter Freyther | 0946f99b08 | |
Holger Hans Peter Freyther | 455613076a | |
Holger Hans Peter Freyther | 4f6a4e5d6d | |
Holger Hans Peter Freyther | 964ddb6aa0 | |
Holger Hans Peter Freyther | 9e21d84f1e | |
Holger Hans Peter Freyther | 099535a1a7 | |
Holger Hans Peter Freyther | 8692128365 | |
Holger Hans Peter Freyther | bb20b2c64c | |
Holger Hans Peter Freyther | bcafdf8d90 | |
Holger Hans Peter Freyther | 443c822f77 | |
Holger Hans Peter Freyther | d868928888 | |
Holger Hans Peter Freyther | d1d114f5b7 | |
Holger Hans Peter Freyther | 31d0df92ac | |
Holger Hans Peter Freyther | 17c31ce173 | |
Holger Hans Peter Freyther | dcc9c39529 | |
Holger Hans Peter Freyther | 70862c9409 | |
Holger Hans Peter Freyther | 26da8361e9 | |
Holger Hans Peter Freyther | 483f77a275 | |
Holger Hans Peter Freyther | 985806030d | |
Holger Hans Peter Freyther | 52c911b3b4 | |
Holger Hans Peter Freyther | 2023bd18b6 | |
Holger Hans Peter Freyther | d6bd91e4e5 | |
Holger Hans Peter Freyther | 9d938388f6 | |
Holger Hans Peter Freyther | bc1e52cfbf | |
Holger Hans Peter Freyther | 741481d3e0 | |
Holger Hans Peter Freyther | 4ea940787e | |
Holger Hans Peter Freyther | 416ce69550 | |
Holger Hans Peter Freyther | c0f1644c88 | |
Holger Hans Peter Freyther | e8d9a5fa54 | |
Holger Hans Peter Freyther | b67a8a348a | |
Holger Hans Peter Freyther | 90d5df4ae7 | |
Holger Hans Peter Freyther | a9744debd9 | |
Holger Hans Peter Freyther | dfe17d7f91 | |
Holger Hans Peter Freyther | e13298d093 | |
Holger Hans Peter Freyther | bb00704871 | |
Holger Hans Peter Freyther | 421fe79e39 | |
Holger Hans Peter Freyther | 939bfaefec | |
Holger Hans Peter Freyther | f14ddb7830 | |
Holger Hans Peter Freyther | 997d2ac3fe | |
Holger Hans Peter Freyther | cf0265a112 | |
Holger Hans Peter Freyther | 49be8bce50 | |
Holger Hans Peter Freyther | 148fb9a3bc | |
Holger Hans Peter Freyther | 98fe945a0d | |
Holger Hans Peter Freyther | 249c7e9431 | |
Holger Hans Peter Freyther | 40bd0c4b57 | |
Holger Hans Peter Freyther | 4b984b14a3 | |
Holger Hans Peter Freyther | ed70cb733c | |
Holger Hans Peter Freyther | d8157c07df | |
Holger Hans Peter Freyther | 90692f93cf | |
Holger Hans Peter Freyther | 90f08efe58 | |
Holger Hans Peter Freyther | a30f47613a | |
Holger Hans Peter Freyther | 51c57045e5 | |
Andreas Eversberg | a004e6a823 | |
Andreas Eversberg | 783aa4bcb8 | |
Ivan Kluchnikov | b6bb55d88c | |
Kat | 7dac4862bc | |
Holger Hans Peter Freyther | 72075f0e00 | |
Andreas Eversberg | 81a12be317 | |
Andreas Eversberg | 050ace2fb4 | |
Andreas Eversberg | 570b44b29b | |
Andreas Eversberg | 0b874b64ef | |
Harald Welte | 30a73d8544 | |
Ivan Kluchnikov | 4440845614 | |
Ivan Kluchnikov | 5dc29a51ef | |
Andreas Eversberg | 3afe56d7d8 | |
Holger Hans Peter Freyther | 827ed55c3b |
|
@ -1,9 +1,11 @@
|
|||
*.o
|
||||
*.lo
|
||||
*.a
|
||||
*.sw?
|
||||
Makefile.in
|
||||
Makefile
|
||||
.deps
|
||||
src/cscope*
|
||||
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
|
@ -20,3 +22,24 @@ ltmain.sh
|
|||
|
||||
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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
|
||||
SUBDIRS = src examples
|
||||
SUBDIRS = src examples tests
|
||||
EXTRA_DIST = osmoappdesc.py
|
||||
|
||||
|
|
|
@ -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
|
29
configure.ac
29
configure.ac
|
@ -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])])
|
||||
|
@ -26,23 +27,31 @@ PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
|
|||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
|
||||
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
|
||||
|
||||
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
|
||||
AC_ARG_ENABLE(sysmocom-bts,
|
||||
AC_HELP_STRING([--enable-sysmocom-bts],
|
||||
[enable code for sysmocom femto-bts [default=no]]),
|
||||
[enable_sysmocom_bts="yes"],[enable_sysmocom_bts="no"])
|
||||
AC_MSG_RESULT([$enable_sysmocom_bts])
|
||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
|
||||
|
||||
AC_MSG_CHECKING([whether to enable direct DSP access for PDCH of sysmocom-bts])
|
||||
AC_ARG_ENABLE(sysmocom-dsp,
|
||||
AC_HELP_STRING([--enable-sysmocom-dsp],
|
||||
[enable code for sysmocom DSP [default=no]]),
|
||||
[enable_sysmocom_dsp="yes"],[enable_sysmocom_dsp="no"])
|
||||
[enable_sysmocom_dsp="$enableval"],[enable_sysmocom_dsp="no"])
|
||||
AC_MSG_RESULT([$enable_sysmocom_dsp])
|
||||
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
|
||||
|
||||
AC_ARG_ENABLE([vty_tests],
|
||||
AC_HELP_STRING([--enable-vty-tests],
|
||||
[Include the VTY tests in make check [default=no]]),
|
||||
[enable_vty_tests="$enableval"],[enable_vty_tests="no"])
|
||||
if test "x$enable_vty_tests" = "xyes" ; then
|
||||
AM_PATH_PYTHON
|
||||
AC_CHECK_PROG(OSMOTESTVTY_CHECK,osmotestvty.py,yes)
|
||||
if test "x$OSMOTESTVTY_CHECK" != "xyes" ; then
|
||||
AC_MSG_ERROR([Please install osmocom-python to run the vty tests.])
|
||||
fi
|
||||
fi
|
||||
AC_MSG_CHECKING([whether to enable VTY tests])
|
||||
AC_MSG_RESULT([$enable_vty_tests])
|
||||
AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
|
||||
|
||||
AC_OUTPUT(
|
||||
src/Makefile
|
||||
examples/Makefile
|
||||
tests/Makefile
|
||||
Makefile)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com>
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
app_configs = {
|
||||
"osmo-pcu": ["examples/osmo-pcu.cfg"]
|
||||
}
|
||||
|
||||
apps = [(4240, "src/osmo-pcu", "Osmo-PCU", "osmo-pcu"),
|
||||
]
|
||||
|
||||
vty_command = ["src/osmo-pcu", "-c", "examples/osmo-pcu.cfg"]
|
||||
|
||||
vty_app = apps[0]
|
|
@ -25,6 +25,7 @@ AM_CPPFLAGS += -DENABLE_SYSMODSP
|
|||
endif
|
||||
|
||||
AM_CXXFLAGS = -Wall -ldl -pthread
|
||||
AM_LDFLAGS = -lrt
|
||||
|
||||
noinst_LTLIBRARIES = libgprs.la
|
||||
|
||||
|
@ -34,27 +35,35 @@ 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
|
||||
|
||||
if ENABLE_SYSMOBTS
|
||||
libgprs_la_SOURCES += \
|
||||
sysmo_sock.cpp
|
||||
else
|
||||
libgprs_la_SOURCES += \
|
||||
openbts_sock.cpp
|
||||
endif
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
RLCMACTest
|
||||
pcu_vty.c \
|
||||
pcu_vty_functions.cpp \
|
||||
tbf.cpp \
|
||||
tbf_ul.cpp \
|
||||
tbf_dl.cpp \
|
||||
bts.cpp \
|
||||
poll_controller.cpp \
|
||||
encoding.cpp \
|
||||
sba.cpp \
|
||||
decoding.cpp \
|
||||
llc.cpp \
|
||||
rlc.cpp \
|
||||
osmobts_sock.cpp \
|
||||
gprs_codel.c \
|
||||
gprs_coding_scheme.cpp
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-pcu
|
||||
|
||||
noinst_PROGRAMS =
|
||||
|
||||
if ENABLE_SYSMODSP
|
||||
noinst_PROGRAMS += \
|
||||
osmo-pcu-remote
|
||||
|
@ -66,19 +75,28 @@ 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 \
|
||||
gprs_coding_scheme.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;
|
||||
|
@ -116,3 +117,21 @@ int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, u
|
|||
write_index += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
bv->cur_bit = write_index;
|
||||
for (i = 0; i < len; i++) {
|
||||
bit_value bit = L;
|
||||
if (val & ((uint64_t)1 << (len - i - 1)))
|
||||
bit = H;
|
||||
rc = bitvec_set_bit(bv, bit);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
write_index += len;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -35,10 +35,11 @@ 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);
|
||||
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
|
||||
|
||||
/*! }@ */
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,499 @@
|
|||
/* 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/stat_item.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
}
|
||||
|
||||
#include "poll_controller.h"
|
||||
#include "sba.h"
|
||||
#include "tbf.h"
|
||||
#include "gprs_ms_storage.h"
|
||||
#include "gprs_coding_scheme.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);
|
||||
int rcv_block_gprs(uint8_t *data, uint32_t fn,
|
||||
struct pcu_l1_meas *meas, GprsCodingScheme cs);
|
||||
int rcv_data_block(uint8_t *data, uint32_t fn,
|
||||
struct pcu_l1_meas *meas, GprsCodingScheme cs);
|
||||
|
||||
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_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_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_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(
|
||||
LListHead<gprs_rlcmac_tbf> *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 initial_mcs_dl, initial_mcs_ul;
|
||||
uint8_t max_cs_dl, max_cs_ul;
|
||||
uint8_t max_mcs_dl, max_mcs_ul;
|
||||
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
|
||||
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
|
||||
uint32_t llc_discard_csec;
|
||||
uint32_t llc_idle_ack_csec;
|
||||
uint32_t llc_codel_interval_msec; /* 0=disabled, -1=use default interval */
|
||||
uint8_t t3142;
|
||||
uint8_t t3169;
|
||||
uint8_t t3191;
|
||||
uint16_t t3193_msec;
|
||||
uint8_t t3195;
|
||||
uint8_t n3101;
|
||||
uint8_t n3103;
|
||||
uint8_t n3105;
|
||||
struct gsmtap_inst *gsmtap;
|
||||
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;
|
||||
uint8_t egprs_enabled;
|
||||
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) */
|
||||
uint16_t ws_base;
|
||||
uint16_t ws_pdch; /* increase WS by this value per PDCH */
|
||||
|
||||
/* State for dynamic algorithm selection */
|
||||
int multislot_disabled;
|
||||
|
||||
/**
|
||||
* Point back to the C++ object. This is used during the transition
|
||||
* period.
|
||||
*/
|
||||
struct BTS *bts;
|
||||
};
|
||||
|
||||
#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_DL_ABORTED,
|
||||
CTR_TBF_UL_ALLOCATED,
|
||||
CTR_TBF_UL_FREED,
|
||||
CTR_TBF_UL_ABORTED,
|
||||
CTR_TBF_REUSED,
|
||||
CTR_TBF_ALLOC_ALGO_A,
|
||||
CTR_TBF_ALLOC_ALGO_B,
|
||||
CTR_TBF_FAILED_EGPRS_ONLY,
|
||||
CTR_RLC_SENT,
|
||||
CTR_RLC_RESENT,
|
||||
CTR_RLC_RESTARTED,
|
||||
CTR_RLC_STALLED,
|
||||
CTR_RLC_NACKED,
|
||||
CTR_RLC_ASS_TIMEDOUT,
|
||||
CTR_RLC_ASS_FAILED,
|
||||
CTR_RLC_ACK_TIMEDOUT,
|
||||
CTR_RLC_ACK_FAILED,
|
||||
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 {
|
||||
STAT_MS_PRESENT,
|
||||
};
|
||||
|
||||
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, uint8_t egprs_ms_class = 0);
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
void tbf_dl_created();
|
||||
void tbf_dl_freed();
|
||||
void tbf_dl_aborted();
|
||||
void tbf_ul_created();
|
||||
void tbf_ul_freed();
|
||||
void tbf_ul_aborted();
|
||||
void tbf_reused();
|
||||
void tbf_alloc_algo_a();
|
||||
void tbf_alloc_algo_b();
|
||||
void tbf_failed_egprs_only();
|
||||
void rlc_sent();
|
||||
void rlc_resent();
|
||||
void rlc_restarted();
|
||||
void rlc_stalled();
|
||||
void rlc_nacked();
|
||||
void rlc_ass_timedout();
|
||||
void rlc_ass_failed();
|
||||
void rlc_ack_timedout();
|
||||
void rlc_ack_failed();
|
||||
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();
|
||||
|
||||
void ms_present(int32_t n);
|
||||
int32_t ms_present_get();
|
||||
|
||||
/*
|
||||
* Below for C interface for the VTY
|
||||
*/
|
||||
struct rate_ctr_group *rate_counters() const;
|
||||
struct osmo_stat_item_group *stat_items() const;
|
||||
|
||||
LListHead<gprs_rlcmac_tbf>& ul_tbfs();
|
||||
LListHead<gprs_rlcmac_tbf>& dl_tbfs();
|
||||
private:
|
||||
int m_cur_fn;
|
||||
int m_cur_blk_fn;
|
||||
struct gprs_rlcmac_bts m_bts;
|
||||
PollController m_pollController;
|
||||
SBAController m_sba;
|
||||
struct rate_ctr_group *m_ratectrs;
|
||||
struct osmo_stat_item_group *m_statg;
|
||||
|
||||
GprsMsStorage m_ms_store;
|
||||
|
||||
/* list of uplink TBFs */
|
||||
LListHead<gprs_rlcmac_tbf> m_ul_tbfs;
|
||||
/* list of downlink TBFs */
|
||||
LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
|
||||
|
||||
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 LListHead<gprs_rlcmac_tbf>& BTS::ul_tbfs()
|
||||
{
|
||||
return m_ul_tbfs;
|
||||
}
|
||||
|
||||
inline LListHead<gprs_rlcmac_tbf>& BTS::dl_tbfs()
|
||||
{
|
||||
return m_dl_tbfs;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline struct osmo_stat_item_group *BTS::stat_items() const
|
||||
{
|
||||
return m_statg;
|
||||
}
|
||||
|
||||
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
|
||||
inline void BTS::func_name() {\
|
||||
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
|
||||
}
|
||||
|
||||
CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED)
|
||||
CREATE_COUNT_INLINE(tbf_dl_aborted, CTR_TBF_DL_ABORTED)
|
||||
CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
|
||||
CREATE_COUNT_INLINE(tbf_ul_aborted, CTR_TBF_UL_ABORTED)
|
||||
CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED)
|
||||
CREATE_COUNT_INLINE(tbf_alloc_algo_a, CTR_TBF_ALLOC_ALGO_A)
|
||||
CREATE_COUNT_INLINE(tbf_alloc_algo_b, CTR_TBF_ALLOC_ALGO_B)
|
||||
CREATE_COUNT_INLINE(tbf_failed_egprs_only, CTR_TBF_FAILED_EGPRS_ONLY)
|
||||
CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT)
|
||||
CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
|
||||
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
|
||||
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
|
||||
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
|
||||
CREATE_COUNT_INLINE(rlc_ass_timedout, CTR_RLC_ASS_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_ass_failed, CTR_RLC_ASS_FAILED);
|
||||
CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
|
||||
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
|
||||
CREATE_COUNT_INLINE(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
|
||||
|
||||
#define CREATE_STAT_INLINE(func_name, func_name_get, stat_name) \
|
||||
inline void BTS::func_name(int32_t val) {\
|
||||
osmo_stat_item_set(m_statg->items[stat_name], val); \
|
||||
} \
|
||||
inline int32_t BTS::func_name_get() {\
|
||||
return osmo_stat_item_get_last(m_statg->items[stat_name]); \
|
||||
}
|
||||
|
||||
CREATE_STAT_INLINE(ms_present, ms_present_get, STAT_MS_PRESENT);
|
||||
|
||||
#undef CREATE_STAT_INLINE
|
||||
|
||||
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();
|
||||
struct osmo_stat_item_group *bts_main_data_stat_items();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
inline bool gprs_rlcmac_pdch::is_enabled() const
|
||||
{
|
||||
return m_is_enabled;
|
||||
}
|
||||
#endif
|
60
src/csn1.cpp
60
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,12 @@ 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;
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "ptr = %p | offset = %d | ", (void *)data, (int)pDescr->offset);
|
||||
Status = serialize(&arT, vector, readIndex, pvDATA(data, pDescr->offset));
|
||||
|
||||
if (Status >= 0)
|
||||
|
@ -899,7 +865,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 +1145,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;
|
||||
}
|
||||
|
@ -1305,7 +1271,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
|
|||
csnStream_t arT = *ar;
|
||||
gint16 Status;
|
||||
csnStreamInit(&arT, bit_offset, remaining_bits_len);
|
||||
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
|
||||
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
|
||||
|
||||
if (Status >= 0)
|
||||
{ /* successful completion */
|
||||
|
@ -1366,7 +1332,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
|
|||
LOGPC(DCSN1, LOGL_NOTICE, "%s { | ", pDescr->sz);
|
||||
|
||||
csnStreamInit(&arT, bit_offset, remaining_bits_len);
|
||||
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
|
||||
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
|
||||
|
||||
if (Status >= 0)
|
||||
{ /* successful completion */
|
||||
|
@ -2154,7 +2120,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
|
||||
{
|
||||
|
@ -2562,7 +2528,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||
csnStream_t arT = *ar;
|
||||
gint16 Status;
|
||||
csnStreamInit(&arT, bit_offset, remaining_bits_len);
|
||||
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pvDATA(data, pDescr->offset));
|
||||
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pui8);
|
||||
|
||||
if (Status >= 0)
|
||||
{ /* successful completion */
|
||||
|
@ -2628,7 +2594,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||
ElementCount--;
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s { | ", pDescr->sz);
|
||||
csnStreamInit(&arT, bit_offset, remaining_bits_len);
|
||||
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pvDATA(data, pDescr->offset));
|
||||
Status = csnStreamEncoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, writeIndex, pui8);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s } | ", pDescr->sz);
|
||||
if (Status >= 0)
|
||||
{ /* successful completion */
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -0,0 +1,677 @@
|
|||
/* 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>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/bitcomp.h>
|
||||
}
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LENGTH_TO_END 255
|
||||
/*
|
||||
* \returns num extensions fields (num frames == offset) on success,
|
||||
* -errno otherwise.
|
||||
*/
|
||||
static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
|
||||
unsigned int *offs,
|
||||
bool is_last_block,
|
||||
Decoding::RlcData *chunks, unsigned int chunks_size)
|
||||
{
|
||||
const struct rlc_li_field_egprs *li;
|
||||
uint8_t e;
|
||||
unsigned int num_chunks = 0;
|
||||
|
||||
e = 0;
|
||||
while (!e) {
|
||||
if (*offs > data_len) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||
"but no more data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* get new E */
|
||||
li = (struct rlc_li_field_egprs *)&data[*offs];
|
||||
e = li->e;
|
||||
*offs += 1;
|
||||
|
||||
if (!chunks)
|
||||
continue;
|
||||
|
||||
if (num_chunks == chunks_size) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
if (li->li == 0 && num_chunks == 0 && li->e == 0) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 2a */
|
||||
chunks[num_chunks].length = 0;
|
||||
chunks[num_chunks].is_complete = true;
|
||||
} else if (li->li == 0 && num_chunks == 0 && li->e == 1) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 4 */
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
} else if (li->li == 127 && li->e == 1) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 3 & 5 */
|
||||
/* only filling bytes left */
|
||||
break;
|
||||
} else if (li->li > 0) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 1 & 2b */
|
||||
chunks[num_chunks].length = li->li;
|
||||
chunks[num_chunks].is_complete = true;
|
||||
} else {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI contains "
|
||||
"invalid extension octet: LI=%d, E=%d, count=%d\n",
|
||||
li->li, li->e, num_chunks);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num_chunks += 1;
|
||||
|
||||
if (e == 1) {
|
||||
/* There is space after the last chunk, add a final one */
|
||||
if (num_chunks == chunks_size) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"UL DATA LI possibly extended, "
|
||||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
num_chunks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return num_chunks;
|
||||
}
|
||||
|
||||
static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
|
||||
unsigned int *offs,
|
||||
bool is_last_block,
|
||||
Decoding::RlcData *chunks, unsigned int chunks_size)
|
||||
{
|
||||
const struct rlc_li_field *li;
|
||||
uint8_t m, e;
|
||||
unsigned int num_chunks = 0;
|
||||
|
||||
e = 0;
|
||||
while (!e) {
|
||||
if (*offs > data_len) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||
"but no more data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* get new E */
|
||||
li = (const struct rlc_li_field *)&data[*offs];
|
||||
e = li->e;
|
||||
m = li->m;
|
||||
*offs += 1;
|
||||
|
||||
if (li->li == 0) {
|
||||
/* TS 44.060, 10.4.14, par 6 */
|
||||
e = 1;
|
||||
m = 0;
|
||||
}
|
||||
|
||||
/* TS 44.060, table 10.4.13.1 */
|
||||
if (m == 0 && e == 0) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA "
|
||||
"ignored, because M='0' and E='0'.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!chunks)
|
||||
continue;
|
||||
|
||||
if (num_chunks == chunks_size) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (li->li == 0)
|
||||
/* e is 1 here */
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
else
|
||||
chunks[num_chunks].length = li->li;
|
||||
|
||||
chunks[num_chunks].is_complete = li->li || is_last_block;
|
||||
|
||||
num_chunks += 1;
|
||||
|
||||
if (e == 1 && m == 1) {
|
||||
if (num_chunks == chunks_size) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
|
||||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
/* TS 44.060, 10.4.13.1, row 4 */
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
num_chunks += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return num_chunks;
|
||||
}
|
||||
|
||||
int Decoding::rlc_data_from_ul_data(
|
||||
const struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
|
||||
uint32_t *tlli)
|
||||
{
|
||||
uint8_t e;
|
||||
unsigned int data_len = rdbi->data_len;
|
||||
int num_chunks = 0, i;
|
||||
unsigned int offs = 0;
|
||||
bool is_last_block = (rdbi->cv == 0);
|
||||
|
||||
if (!chunks)
|
||||
chunks_size = 0;
|
||||
|
||||
e = rdbi->e;
|
||||
if (e) {
|
||||
if (chunks_size > 0) {
|
||||
chunks[num_chunks].offset = offs;
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
num_chunks += 1;
|
||||
} else if (chunks) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "No extension, "
|
||||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
} else if (cs.isEgprs()) {
|
||||
/* if E is not set (LI follows), EGPRS */
|
||||
num_chunks = parse_extensions_egprs(data, data_len, &offs,
|
||||
is_last_block,
|
||||
chunks, chunks_size);
|
||||
} else {
|
||||
/* if E is not set (LI follows), GPRS */
|
||||
num_chunks = parse_extensions_gprs(data, data_len, &offs,
|
||||
is_last_block,
|
||||
chunks, chunks_size);
|
||||
}
|
||||
|
||||
if (num_chunks < 0)
|
||||
return num_chunks;
|
||||
|
||||
/* TLLI */
|
||||
if (rdbi->ti) {
|
||||
uint32_t tlli_enc;
|
||||
if (offs + 4 > data_len) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of block "
|
||||
"border\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
|
||||
if (cs.isGprs())
|
||||
/* The TLLI is encoded in big endian for GPRS (see
|
||||
* TS 44.060, figure 10.2.2.1, note) */
|
||||
*tlli = be32toh(tlli_enc);
|
||||
else
|
||||
/* The TLLI is encoded in little endian for EGPRS (see
|
||||
* TS 44.060, figure 10.3a.2.1, note 2) */
|
||||
*tlli = le32toh(tlli_enc);
|
||||
|
||||
offs += sizeof(tlli_enc);
|
||||
} else {
|
||||
*tlli = 0;
|
||||
}
|
||||
|
||||
/* PFI */
|
||||
if (rdbi->pi) {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
|
||||
"please disable in SYSTEM INFORMATION\n");
|
||||
return -ENOTSUP;
|
||||
|
||||
/* TODO: Skip all extensions with E=0 (see TS 44.060, 10.4.11 */
|
||||
}
|
||||
|
||||
if (chunks_size == 0)
|
||||
return num_chunks;
|
||||
|
||||
/* LLC */
|
||||
for (i = 0; i < num_chunks; i++) {
|
||||
chunks[i].offset = offs;
|
||||
if (chunks[i].length == LENGTH_TO_END) {
|
||||
if (offs == data_len) {
|
||||
/* There is no place for an additional chunk,
|
||||
* so drop it (this may happen with EGPRS since
|
||||
* there is no M flag. */
|
||||
num_chunks -= 1;
|
||||
break;;
|
||||
}
|
||||
chunks[i].length = data_len - offs;
|
||||
}
|
||||
offs += chunks[i].length;
|
||||
if (offs > data_len) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block "
|
||||
"border, chunk idx: %d, size: %d\n",
|
||||
i, chunks[i].length);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return num_chunks;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
|
||||
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
|
||||
continue;
|
||||
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_EGPRS_multislot_class)
|
||||
continue;
|
||||
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.EGPRS_multislot_class;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* show_rbb needs to be an array with 65 elements
|
||||
* The index of the array is the bit position in the rbb
|
||||
* (show_rbb[63] relates to BSN ssn-1)
|
||||
*/
|
||||
void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
|
||||
{
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint8_t bit;
|
||||
|
||||
bit = !!(rbb[i/8] & (1<<(7-i%8)));
|
||||
show_rbb[i] = bit ? 'R' : 'I';
|
||||
}
|
||||
|
||||
show_rbb[64] = '\0';
|
||||
}
|
||||
|
||||
void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < rbb->cur_bit; i++) {
|
||||
uint8_t bit;
|
||||
bit = bitvec_get_bit_pos(rbb, i);
|
||||
show_rbb[i] = bit == 1 ? 'R' : 'I';
|
||||
}
|
||||
|
||||
show_rbb[i] = '\0';
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, GprsCodingScheme cs)
|
||||
{
|
||||
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
|
||||
const struct rlc_ul_header *gprs;
|
||||
unsigned int e_ti_header;
|
||||
unsigned int cur_bit = 0;
|
||||
int punct, punct2, with_padding, cps;
|
||||
unsigned int offs;
|
||||
|
||||
switch(cs.headerTypeData()) {
|
||||
case GprsCodingScheme::HEADER_GPRS_DATA:
|
||||
gprs = static_cast<struct rlc_ul_header *>
|
||||
((void *)data);
|
||||
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, false);
|
||||
|
||||
rlc->r = gprs->r;
|
||||
rlc->si = gprs->si;
|
||||
rlc->tfi = gprs->tfi;
|
||||
rlc->cps = 0;
|
||||
rlc->rsb = 0;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = gprs->cv;
|
||||
rlc->block_info[0].pi = gprs->pi;
|
||||
rlc->block_info[0].bsn = gprs->bsn;
|
||||
rlc->block_info[0].e = gprs->e;
|
||||
rlc->block_info[0].ti = gprs->ti;
|
||||
rlc->block_info[0].spb = 0;
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0];
|
||||
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
break;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3:
|
||||
egprs3 = static_cast<struct gprs_rlc_ul_header_egprs_3 *>
|
||||
((void *)data);
|
||||
|
||||
cps = (egprs3->cps_a << 0) | (egprs3->cps_b << 2);
|
||||
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs3->r;
|
||||
rlc->si = egprs3->si;
|
||||
rlc->tfi = (egprs3->tfi_a << 0) | (egprs3->tfi_b << 2);
|
||||
rlc->cps = cps;
|
||||
rlc->rsb = egprs3->rsb;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = egprs3->cv;
|
||||
rlc->block_info[0].pi = egprs3->pi;
|
||||
rlc->block_info[0].spb = egprs3->spb;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs3->bsn1_a << 0) | (egprs3->bsn1_b << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
|
||||
|
||||
e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
|
||||
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
break;
|
||||
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1:
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2:
|
||||
/* TODO: Support both header types */
|
||||
/* fall through */
|
||||
default:
|
||||
LOGP(DRLCMACDL, LOGL_ERROR,
|
||||
"Decoding of uplink %s data blocks not yet supported.\n",
|
||||
cs.name());
|
||||
return -ENOTSUP;
|
||||
};
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copy LSB bitstream RLC data block to byte aligned buffer.
|
||||
*
|
||||
* Note that the bitstream is encoded in LSB first order, so the two octets
|
||||
* 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3
|
||||
* (LSB has bit position 1). This is a different order than the one used by
|
||||
* CSN.1.
|
||||
*
|
||||
* \param data_block_idx The block index, 0..1 for header type 1, 0 otherwise
|
||||
* \param src A pointer to the start of the RLC block (incl. the header)
|
||||
* \param buffer A data area of a least the size of the RLC block
|
||||
* \returns the number of bytes copied
|
||||
*/
|
||||
unsigned int Decoding::rlc_copy_to_aligned_buffer(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
unsigned int data_block_idx,
|
||||
const uint8_t *src, uint8_t *buffer)
|
||||
{
|
||||
unsigned int hdr_bytes;
|
||||
unsigned int extra_bits;
|
||||
unsigned int i;
|
||||
|
||||
uint8_t c, last_c;
|
||||
uint8_t *dst;
|
||||
const struct gprs_rlc_data_block_info *rdbi;
|
||||
|
||||
OSMO_ASSERT(data_block_idx < rlc->num_data_blocks);
|
||||
rdbi = &rlc->block_info[data_block_idx];
|
||||
|
||||
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
|
||||
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
|
||||
|
||||
if (extra_bits == 0) {
|
||||
/* It is aligned already */
|
||||
memmove(buffer, src + hdr_bytes, rdbi->data_len);
|
||||
return rdbi->data_len;
|
||||
}
|
||||
|
||||
dst = buffer;
|
||||
src = src + hdr_bytes;
|
||||
last_c = *(src++);
|
||||
|
||||
for (i = 0; i < rdbi->data_len; i++) {
|
||||
c = src[i];
|
||||
*(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits));
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
return rdbi->data_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get a pointer to byte aligned RLC data.
|
||||
*
|
||||
* Since the RLC data may not be byte aligned to the RLC block data such that a
|
||||
* single RLC data byte is spread over two RLC block bytes, this function
|
||||
* eventually uses the provided buffer as data storage.
|
||||
*
|
||||
* \param src A pointer to the start of the RLC block (incl. the header)
|
||||
* \param buffer A data area of a least the size of the RLC block
|
||||
* \returns A pointer to the RLC data start within src if it is aligned, and
|
||||
* buffer otherwise.
|
||||
*/
|
||||
const uint8_t *Decoding::rlc_get_data_aligned(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
unsigned int data_block_idx,
|
||||
const uint8_t *src, uint8_t *buffer)
|
||||
{
|
||||
unsigned int hdr_bytes;
|
||||
unsigned int extra_bits;
|
||||
|
||||
OSMO_ASSERT(data_block_idx < ARRAY_SIZE(rlc->data_offs_bits));
|
||||
|
||||
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
|
||||
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
|
||||
|
||||
if (extra_bits == 0)
|
||||
/* It is aligned already, return a pointer that refers to the
|
||||
* original data. */
|
||||
return src + hdr_bytes;
|
||||
|
||||
Decoding::rlc_copy_to_aligned_buffer(rlc, data_block_idx, src, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static int handle_final_ack(bitvec *bits, int *bsn_begin, int *bsn_end,
|
||||
gprs_rlc_dl_window *window)
|
||||
{
|
||||
int num_blocks, i;
|
||||
|
||||
num_blocks = window->mod_sns(window->v_s() - window->v_a());
|
||||
for (i = 0; i < num_blocks; i++)
|
||||
bitvec_set_bit(bits, ONE);
|
||||
|
||||
*bsn_begin = window->v_a();
|
||||
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
|
||||
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
|
||||
{
|
||||
int urbb_len = desc->URBB_LENGTH;
|
||||
int crbb_len = 0;
|
||||
int num_blocks = 0;
|
||||
struct bitvec urbb;
|
||||
int i;
|
||||
bool have_bitmap;
|
||||
int implicitly_acked_blocks;
|
||||
int ssn = desc->STARTING_SEQUENCE_NUMBER;
|
||||
int rc;
|
||||
|
||||
if (desc->FINAL_ACK_INDICATION)
|
||||
return handle_final_ack(bits, bsn_begin, bsn_end, window);
|
||||
|
||||
if (desc->Exist_CRBB)
|
||||
crbb_len = desc->CRBB_LENGTH;
|
||||
|
||||
have_bitmap = (urbb_len + crbb_len) > 0;
|
||||
|
||||
/*
|
||||
* bow & bitmap present:
|
||||
* V(A)-> [ 11111...11111 0 SSN-> BBBBB...BBBBB ] (SSN+Nbits) .... V(S)
|
||||
* bow & not bitmap present:
|
||||
* V(A)-> [ 11111...11111 ] . SSN .... V(S)
|
||||
* not bow & bitmap present:
|
||||
* V(A)-> ... [ 0 SSN-> BBBBB...BBBBB ](SSN+N) .... V(S)
|
||||
* not bow & not bitmap present:
|
||||
* V(A)-> ... [] . SSN .... V(S)
|
||||
*/
|
||||
|
||||
if (desc->BEGINNING_OF_WINDOW) {
|
||||
implicitly_acked_blocks = window->mod_sns(ssn - 1 - window->v_a());
|
||||
|
||||
for (i = 0; i < implicitly_acked_blocks; i++)
|
||||
bitvec_set_bit(bits, ONE);
|
||||
|
||||
num_blocks += implicitly_acked_blocks;
|
||||
}
|
||||
|
||||
if (!have_bitmap)
|
||||
goto aborted;
|
||||
|
||||
/* next bit refers to V(Q) and thus is always zero (and not
|
||||
* transmitted) */
|
||||
bitvec_set_bit(bits, ZERO);
|
||||
num_blocks += 1;
|
||||
|
||||
if (crbb_len > 0) {
|
||||
int old_len = bits->cur_bit;
|
||||
struct bitvec crbb;
|
||||
|
||||
crbb.data = (uint8_t *)desc->CRBB;
|
||||
crbb.data_len = sizeof(desc->CRBB);
|
||||
crbb.cur_bit = desc->CRBB_LENGTH;
|
||||
|
||||
rc = osmo_t4_decode(&crbb, desc->CRBB_STARTING_COLOR_CODE,
|
||||
bits);
|
||||
|
||||
if (rc < 0) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Failed to decode CRBB: "
|
||||
"length %d, data '%s'\n",
|
||||
desc->CRBB_LENGTH,
|
||||
osmo_hexdump(crbb.data, crbb.data_len));
|
||||
/* We don't know the SSN offset for the URBB,
|
||||
* return what we have so far and assume the
|
||||
* bitmap has stopped here */
|
||||
goto aborted;
|
||||
}
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"CRBB len: %d, decoded len: %d, cc: %d, crbb: '%s'\n",
|
||||
desc->CRBB_LENGTH, bits->cur_bit - old_len,
|
||||
desc->CRBB_STARTING_COLOR_CODE,
|
||||
osmo_hexdump(
|
||||
desc->CRBB, (desc->CRBB_LENGTH + 7)/8)
|
||||
);
|
||||
|
||||
num_blocks += (bits->cur_bit - old_len);
|
||||
}
|
||||
|
||||
urbb.cur_bit = 0;
|
||||
urbb.data = (uint8_t *)desc->URBB;
|
||||
urbb.data_len = sizeof(desc->URBB);
|
||||
|
||||
for (i = urbb_len; i > 0; i--) {
|
||||
/*
|
||||
* Set bit at the appropriate position (see 3GPP TS
|
||||
* 44.060 12.3.1)
|
||||
*/
|
||||
int is_ack = bitvec_get_bit_pos(&urbb, i-1);
|
||||
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
|
||||
}
|
||||
num_blocks += urbb_len;
|
||||
|
||||
aborted:
|
||||
*bsn_begin = window->v_a();
|
||||
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
|
||||
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
int Decoding::decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc,
|
||||
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
|
||||
{
|
||||
int urbb_len = RLC_GPRS_WS;
|
||||
int num_blocks;
|
||||
struct bitvec urbb;
|
||||
|
||||
if (desc->FINAL_ACK_INDICATION)
|
||||
return handle_final_ack(bits, bsn_begin, bsn_end, window);
|
||||
|
||||
*bsn_begin = window->v_a();
|
||||
*bsn_end = desc->STARTING_SEQUENCE_NUMBER;
|
||||
|
||||
num_blocks = window->mod_sns(*bsn_end - *bsn_begin);
|
||||
|
||||
if (num_blocks < 0 || num_blocks > urbb_len) {
|
||||
*bsn_end = *bsn_begin;
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Invalid GPRS Ack/Nack window %d:%d (length %d)\n",
|
||||
*bsn_begin, *bsn_end, num_blocks);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
urbb.cur_bit = 0;
|
||||
urbb.data = (uint8_t *)desc->RECEIVED_BLOCK_BITMAP;
|
||||
urbb.data_len = sizeof(desc->RECEIVED_BLOCK_BITMAP);
|
||||
|
||||
/*
|
||||
* TS 44.060, 12.3:
|
||||
* BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64.
|
||||
* The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128.
|
||||
*
|
||||
* We are only interested in the range from V(A) to SSN-1 which is
|
||||
* num_blocks large. The RBB is laid out as
|
||||
* [SSN-1] [SSN-2] ... [V(A)] ... [SSN-64]
|
||||
* so we want to start with [V(A)] and go backwards until we reach
|
||||
* [SSN-1] to get the needed BSNs in an increasing order. Note that
|
||||
* the bit numbers are counted from the end of the buffer.
|
||||
*/
|
||||
for (int i = num_blocks; i > 0; i--) {
|
||||
int is_ack = bitvec_get_bit_pos(&urbb, urbb_len - i);
|
||||
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
|
||||
}
|
||||
|
||||
return num_blocks;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* 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 "rlc.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct bitvec;
|
||||
|
||||
class Decoding {
|
||||
public:
|
||||
struct RlcData {
|
||||
uint8_t offset;
|
||||
uint8_t length;
|
||||
bool is_complete;
|
||||
};
|
||||
|
||||
static int rlc_data_from_ul_data(
|
||||
const struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, const uint8_t *data, RlcData *chunks,
|
||||
unsigned int chunks_size, uint32_t *tlli);
|
||||
static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
|
||||
static uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
|
||||
|
||||
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
|
||||
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
|
||||
|
||||
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, GprsCodingScheme cs);
|
||||
static unsigned int rlc_copy_to_aligned_buffer(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
unsigned int data_block_idx,
|
||||
const uint8_t *src, uint8_t *buffer);
|
||||
static const uint8_t *rlc_get_data_aligned(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
unsigned int data_block_idx,
|
||||
const uint8_t *src, uint8_t *buffer);
|
||||
static int decode_egprs_acknack_bits(
|
||||
const EGPRS_AckNack_Desc_t *desc,
|
||||
struct bitvec *bits, int *bsn_begin, int *bsn_end,
|
||||
struct gprs_rlc_dl_window *window);
|
||||
static int decode_gprs_acknack_bits(
|
||||
const Ack_Nack_Description_t *desc,
|
||||
bitvec *bits, int *bsn_begin, int *bsn_end,
|
||||
gprs_rlc_dl_window *window);
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,96 @@
|
|||
/* 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>
|
||||
#include <gprs_coding_scheme.h>
|
||||
|
||||
struct gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_tbf;
|
||||
struct bitvec;
|
||||
struct gprs_llc;
|
||||
struct gprs_rlc_data_block_info;
|
||||
|
||||
/**
|
||||
* 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_tbf *tbf,
|
||||
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 usf, uint8_t polling,
|
||||
uint32_t fn, 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 rrbp,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
|
||||
int8_t use_egprs);
|
||||
|
||||
static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
|
||||
bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
|
||||
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t rrbp,
|
||||
uint8_t alpha, uint8_t gamma,
|
||||
int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
|
||||
|
||||
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
|
||||
|
||||
static void write_packet_uplink_ack(
|
||||
struct gprs_rlcmac_bts *bts, bitvec * dest,
|
||||
struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
|
||||
uint8_t rrbp);
|
||||
|
||||
static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
|
||||
|
||||
static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
|
||||
uint8_t *identity, uint8_t chan_needed);
|
||||
|
||||
static unsigned write_packet_paging_request(bitvec * dest);
|
||||
|
||||
static int rlc_write_dl_data_header(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
uint8_t *data);
|
||||
static unsigned int rlc_copy_from_aligned_buffer(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
unsigned int data_block_idx,
|
||||
uint8_t *dst, const uint8_t *buffer);
|
||||
|
||||
enum AppendResult {
|
||||
AR_NEED_MORE_BLOCKS,
|
||||
AR_COMPLETED_SPACE_LEFT,
|
||||
AR_COMPLETED_BLOCK_FILLED,
|
||||
};
|
||||
|
||||
static AppendResult rlc_data_to_dl_append(
|
||||
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data,
|
||||
bool is_final);
|
||||
};
|
|
@ -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 {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,20 +42,54 @@ 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(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;
|
||||
unsigned queue_frames_sent;
|
||||
unsigned queue_bytes_recv;
|
||||
unsigned queue_frames_recv;
|
||||
|
||||
/** callbacks below */
|
||||
|
||||
/* The BSSGP has been unblocked */
|
||||
void (*on_unblock_ack)(struct gprs_bssgp_pcu *pcu);
|
||||
|
||||
/* When BSSGP data arrives. The msgb is not only for reference */
|
||||
void (*on_dl_unit_data)(struct gprs_bssgp_pcu *pcu, struct msgb *msg,
|
||||
struct tlv_parsed *tp);
|
||||
};
|
||||
|
||||
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);
|
||||
void gprs_bssgp_update_frames_sent();
|
||||
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv);
|
||||
|
||||
#endif // GPRS_BSSGP_PCU_H
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,286 @@
|
|||
/* gprs_coding_scheme.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_coding_scheme.h"
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
unsigned int bytes;
|
||||
unsigned int ext_bits;
|
||||
unsigned int data_header_bits;
|
||||
} uplink, downlink;
|
||||
unsigned int data_bytes;
|
||||
unsigned int optional_padding_bits;
|
||||
const char *name;
|
||||
GprsCodingScheme::HeaderType data_hdr;
|
||||
GprsCodingScheme::Family family;
|
||||
} mcs_info[GprsCodingScheme::NUM_SCHEMES] = {
|
||||
{{0, 0}, {0, 0}, 0, 0, "UNKNOWN",
|
||||
GprsCodingScheme::HEADER_INVALID, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{23, 0}, {23, 0}, 20, 0, "CS-1",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{33, 7}, {33, 7}, 30, 0, "CS-2",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{39, 3}, {39, 3}, 36, 0, "CS-3",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
{{53, 7}, {53, 7}, 50, 0, "CS-4",
|
||||
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
|
||||
|
||||
{{26, 1}, {26, 1}, 22, 0, "MCS-1",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
|
||||
{{32, 1}, {32, 1}, 28, 0, "MCS-2",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_B},
|
||||
{{41, 1}, {41, 1}, 37, 48, "MCS-3",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_A},
|
||||
{{48, 1}, {48, 1}, 44, 0, "MCS-4",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
|
||||
|
||||
{{60, 7}, {59, 6}, 56, 0, "MCS-5",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_B},
|
||||
{{78, 7}, {77, 6}, 74, 48, "MCS-6",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_A},
|
||||
{{118, 2}, {117, 4}, 56, 0, "MCS-7",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_B},
|
||||
{{142, 2}, {141, 4}, 68, 0, "MCS-8",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
|
||||
{{154, 2}, {153, 4}, 74, 0, "MCS-9",
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
int data_header_bits;
|
||||
} uplink, downlink;
|
||||
unsigned int data_block_header_bits;
|
||||
unsigned int num_blocks;
|
||||
const char *name;
|
||||
} hdr_type_info[GprsCodingScheme::NUM_HEADER_TYPES] = {
|
||||
{{0}, {0}, 0, 0, "INVALID"},
|
||||
{{1*8 + 0}, {1*8 + 0}, 0, 0, "CONTROL"},
|
||||
{{3*8 + 0}, {3*8 + 0}, 0, 1, "GPRS_DATA"},
|
||||
{{5*8 + 6}, {5*8 + 0}, 2, 2, "EGPRS_DATA_TYPE1"},
|
||||
{{4*8 + 5}, {3*8 + 4}, 2, 1, "EGPRS_DATA_TYPE2"},
|
||||
{{3*8 + 7}, {3*8 + 7}, 2, 1, "EGPRS_DATA_TYPE3"},
|
||||
};
|
||||
|
||||
GprsCodingScheme GprsCodingScheme::getBySizeUL(unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 23: return GprsCodingScheme(CS1);
|
||||
case 27: return GprsCodingScheme(MCS1);
|
||||
case 33: return GprsCodingScheme(MCS2);
|
||||
case 34: return GprsCodingScheme(CS2);
|
||||
case 40: return GprsCodingScheme(CS3);
|
||||
case 42: return GprsCodingScheme(MCS3);
|
||||
case 49: return GprsCodingScheme(MCS4);
|
||||
case 54: return GprsCodingScheme(CS4);
|
||||
case 61: return GprsCodingScheme(MCS5);
|
||||
case 79: return GprsCodingScheme(MCS6);
|
||||
case 119: return GprsCodingScheme(MCS7);
|
||||
case 143: return GprsCodingScheme(MCS8);
|
||||
case 155: return GprsCodingScheme(MCS9);
|
||||
}
|
||||
|
||||
return GprsCodingScheme(UNKNOWN);
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::sizeUL() const
|
||||
{
|
||||
return mcs_info[m_scheme].uplink.bytes + (spareBitsUL() ? 1 : 0);
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::usedSizeUL() const
|
||||
{
|
||||
if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
|
||||
return mcs_info[m_scheme].uplink.bytes;
|
||||
else
|
||||
return sizeUL();
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::maxBytesUL() const
|
||||
{
|
||||
return mcs_info[m_scheme].uplink.bytes;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::spareBitsUL() const
|
||||
{
|
||||
return mcs_info[m_scheme].uplink.ext_bits;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::sizeDL() const
|
||||
{
|
||||
return mcs_info[m_scheme].downlink.bytes + (spareBitsDL() ? 1 : 0);
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::usedSizeDL() const
|
||||
{
|
||||
if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
|
||||
return mcs_info[m_scheme].downlink.bytes;
|
||||
else
|
||||
return sizeDL();
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::maxBytesDL() const
|
||||
{
|
||||
return mcs_info[m_scheme].downlink.bytes;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::spareBitsDL() const
|
||||
{
|
||||
return mcs_info[m_scheme].downlink.ext_bits;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::maxDataBlockBytes() const
|
||||
{
|
||||
return mcs_info[m_scheme].data_bytes;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::optionalPaddingBits() const
|
||||
{
|
||||
return mcs_info[m_scheme].optional_padding_bits;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::numDataBlocks() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].num_blocks;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::numDataHeaderBitsUL() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].uplink.data_header_bits;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::numDataHeaderBitsDL() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].downlink.data_header_bits;
|
||||
}
|
||||
|
||||
unsigned int GprsCodingScheme::numDataBlockHeaderBits() const
|
||||
{
|
||||
return hdr_type_info[headerTypeData()].data_block_header_bits;
|
||||
}
|
||||
|
||||
const char *GprsCodingScheme::name() const
|
||||
{
|
||||
return mcs_info[m_scheme].name;
|
||||
}
|
||||
|
||||
GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeData() const
|
||||
{
|
||||
return mcs_info[m_scheme].data_hdr;
|
||||
}
|
||||
|
||||
GprsCodingScheme::Family GprsCodingScheme::family() const
|
||||
{
|
||||
return mcs_info[m_scheme].family;
|
||||
}
|
||||
|
||||
void GprsCodingScheme::inc(Mode mode)
|
||||
{
|
||||
if (!isCompatible(mode))
|
||||
/* This should not happen. TODO: Use assert? */
|
||||
return;
|
||||
|
||||
Scheme new_cs(Scheme(m_scheme + 1));
|
||||
if (!GprsCodingScheme(new_cs).isCompatible(mode))
|
||||
/* Clipping, do not change the value */
|
||||
return;
|
||||
|
||||
m_scheme = new_cs;
|
||||
}
|
||||
|
||||
void GprsCodingScheme::dec(Mode mode)
|
||||
{
|
||||
if (!isCompatible(mode))
|
||||
/* This should not happen. TODO: Use assert? */
|
||||
return;
|
||||
|
||||
Scheme new_cs(Scheme(m_scheme - 1));
|
||||
if (!GprsCodingScheme(new_cs).isCompatible(mode))
|
||||
/* Clipping, do not change the value */
|
||||
return;
|
||||
|
||||
m_scheme = new_cs;
|
||||
}
|
||||
|
||||
void GprsCodingScheme::inc()
|
||||
{
|
||||
if (isGprs() && m_scheme == CS4)
|
||||
return;
|
||||
|
||||
if (isEgprs() && m_scheme == MCS9)
|
||||
return;
|
||||
|
||||
if (!isValid())
|
||||
return;
|
||||
|
||||
m_scheme = Scheme(m_scheme + 1);
|
||||
}
|
||||
|
||||
void GprsCodingScheme::dec()
|
||||
{
|
||||
if (isGprs() && m_scheme == CS1)
|
||||
return;
|
||||
|
||||
if (isEgprs() && m_scheme == MCS1)
|
||||
return;
|
||||
|
||||
if (!isValid())
|
||||
return;
|
||||
|
||||
m_scheme = Scheme(m_scheme - 1);
|
||||
}
|
||||
|
||||
const char *GprsCodingScheme::modeName(Mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case GPRS: return "GPRS";
|
||||
case EGPRS_GMSK: return "EGPRS_GMSK-only";
|
||||
case EGPRS: return "EGPRS";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
bool GprsCodingScheme::isFamilyCompatible(GprsCodingScheme o) const
|
||||
{
|
||||
if (*this == o)
|
||||
return true;
|
||||
|
||||
if (family() == FAMILY_INVALID)
|
||||
return false;
|
||||
|
||||
return family() == o.family();
|
||||
}
|
||||
|
||||
bool GprsCodingScheme::isCombinable(GprsCodingScheme o) const
|
||||
{
|
||||
return numDataBlocks() == o.numDataBlocks();
|
||||
}
|
||||
|
||||
void GprsCodingScheme::decToSingleBlock(bool *needStuffing)
|
||||
{
|
||||
switch (m_scheme) {
|
||||
case MCS7: *needStuffing = false; m_scheme = MCS5; break;
|
||||
case MCS8: *needStuffing = true; m_scheme = MCS6; break;
|
||||
case MCS9: *needStuffing = false; m_scheme = MCS6; break;
|
||||
default: *needStuffing = false; break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
/* gprs_coding_scheme.h
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
class GprsCodingScheme {
|
||||
public:
|
||||
enum Scheme {
|
||||
UNKNOWN,
|
||||
CS1, CS2, CS3, CS4,
|
||||
MCS1, MCS2, MCS3, MCS4,
|
||||
MCS5, MCS6, MCS7, MCS8, MCS9,
|
||||
NUM_SCHEMES
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
GPRS,
|
||||
EGPRS_GMSK,
|
||||
EGPRS,
|
||||
};
|
||||
|
||||
enum HeaderType {
|
||||
HEADER_INVALID,
|
||||
HEADER_GPRS_CONTROL,
|
||||
HEADER_GPRS_DATA,
|
||||
HEADER_EGPRS_DATA_TYPE_1,
|
||||
HEADER_EGPRS_DATA_TYPE_2,
|
||||
HEADER_EGPRS_DATA_TYPE_3,
|
||||
NUM_HEADER_TYPES
|
||||
};
|
||||
|
||||
enum Family {
|
||||
FAMILY_INVALID,
|
||||
FAMILY_A,
|
||||
FAMILY_B,
|
||||
FAMILY_C,
|
||||
};
|
||||
|
||||
GprsCodingScheme(Scheme s = UNKNOWN);
|
||||
|
||||
operator bool() const {return m_scheme != UNKNOWN;}
|
||||
operator Scheme() const {return m_scheme;}
|
||||
unsigned int to_num() const;
|
||||
|
||||
GprsCodingScheme& operator =(Scheme s);
|
||||
GprsCodingScheme& operator =(GprsCodingScheme o);
|
||||
|
||||
bool isValid() const {return UNKNOWN <= m_scheme && m_scheme <= MCS9;}
|
||||
bool isGprs() const {return CS1 <= m_scheme && m_scheme <= CS4;}
|
||||
bool isEgprs() const {return m_scheme >= MCS1;}
|
||||
bool isEgprsGmsk() const {return isEgprs() && m_scheme <= MCS4;}
|
||||
bool isCompatible(Mode mode) const;
|
||||
bool isCompatible(GprsCodingScheme o) const;
|
||||
bool isFamilyCompatible(GprsCodingScheme o) const;
|
||||
bool isCombinable(GprsCodingScheme o) const;
|
||||
|
||||
void inc(Mode mode);
|
||||
void dec(Mode mode);
|
||||
void inc();
|
||||
void dec();
|
||||
void decToSingleBlock(bool *needStuffing);
|
||||
|
||||
unsigned int sizeUL() const;
|
||||
unsigned int sizeDL() const;
|
||||
unsigned int usedSizeUL() const;
|
||||
unsigned int usedSizeDL() const;
|
||||
unsigned int maxBytesUL() const;
|
||||
unsigned int maxBytesDL() const;
|
||||
unsigned int spareBitsUL() const;
|
||||
unsigned int spareBitsDL() const;
|
||||
unsigned int maxDataBlockBytes() const;
|
||||
unsigned int numDataBlocks() const;
|
||||
unsigned int numDataHeaderBitsUL() const;
|
||||
unsigned int numDataHeaderBitsDL() const;
|
||||
unsigned int numDataBlockHeaderBits() const;
|
||||
unsigned int optionalPaddingBits() const;
|
||||
const char *name() const;
|
||||
HeaderType headerTypeData() const;
|
||||
HeaderType headerTypeControl() const;
|
||||
Family family() const;
|
||||
|
||||
static GprsCodingScheme getBySizeUL(unsigned size);
|
||||
static GprsCodingScheme getGprsByNum(unsigned num);
|
||||
static GprsCodingScheme getEgprsByNum(unsigned num);
|
||||
|
||||
static const char *modeName(Mode mode);
|
||||
private:
|
||||
GprsCodingScheme(int s); /* fail on use */
|
||||
GprsCodingScheme& operator =(int s); /* fail on use */
|
||||
enum Scheme m_scheme;
|
||||
};
|
||||
|
||||
inline unsigned int GprsCodingScheme::to_num() const
|
||||
{
|
||||
if (isGprs())
|
||||
return (m_scheme - CS1) + 1;
|
||||
|
||||
if (isEgprs())
|
||||
return (m_scheme - MCS1) + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool GprsCodingScheme::isCompatible(Mode mode) const
|
||||
{
|
||||
switch (mode) {
|
||||
case GPRS: return isGprs();
|
||||
case EGPRS_GMSK: return isEgprsGmsk();
|
||||
case EGPRS: return isEgprs();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool GprsCodingScheme::isCompatible(GprsCodingScheme o) const
|
||||
{
|
||||
return (isGprs() && o.isGprs()) || (isEgprs() && o.isEgprs());
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeControl() const
|
||||
{
|
||||
return HEADER_GPRS_CONTROL;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::GprsCodingScheme(Scheme s)
|
||||
: m_scheme(s)
|
||||
{
|
||||
if (!isValid())
|
||||
m_scheme = UNKNOWN;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme& GprsCodingScheme::operator =(Scheme s)
|
||||
{
|
||||
m_scheme = s;
|
||||
|
||||
if (!isValid())
|
||||
m_scheme = UNKNOWN;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme& GprsCodingScheme::operator =(GprsCodingScheme o)
|
||||
{
|
||||
m_scheme = o.m_scheme;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme GprsCodingScheme::getGprsByNum(unsigned num)
|
||||
{
|
||||
if (num < 1 || num > 4)
|
||||
return GprsCodingScheme();
|
||||
|
||||
return GprsCodingScheme(Scheme(CS1 + (num - 1)));
|
||||
}
|
||||
|
||||
inline GprsCodingScheme GprsCodingScheme::getEgprsByNum(unsigned num)
|
||||
{
|
||||
if (num < 1 || num > 9)
|
||||
return GprsCodingScheme();
|
||||
|
||||
return GprsCodingScheme(Scheme(MCS1 + (num - 1)));
|
||||
}
|
||||
|
||||
/* The coding schemes form a partial ordering */
|
||||
inline bool operator ==(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return GprsCodingScheme::Scheme(a) == GprsCodingScheme::Scheme(b);
|
||||
}
|
||||
|
||||
inline bool operator !=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline bool operator <(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return a.isCompatible(b) &&
|
||||
GprsCodingScheme::Scheme(a) < GprsCodingScheme::Scheme(b);
|
||||
}
|
||||
|
||||
inline bool operator >(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return b < a;
|
||||
}
|
||||
|
||||
inline bool operator <=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return a == b || a < b;
|
||||
}
|
||||
|
||||
inline bool operator >=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return a == b || a > b;
|
||||
}
|
||||
|
|
@ -40,7 +40,8 @@ static const struct log_info_cat default_categories[] = {
|
|||
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Downlink (RLCMAC)", LOGL_NOTICE, 1},
|
||||
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_NOTICE, 1},
|
||||
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_NOTICE, 1},
|
||||
{"DRLCMACBW", "\033[1;31m", "GPRS RLC/MAC layer Bandwidth (RLCMAC)", LOGL_INFO, 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},
|
||||
};
|
||||
|
|
|
@ -38,7 +38,8 @@ enum {
|
|||
DRLCMACDL,
|
||||
DRLCMACUL,
|
||||
DRLCMACSCHED,
|
||||
DRLCMACBW,
|
||||
DRLCMACMEAS,
|
||||
DNS,
|
||||
DBSSGP,
|
||||
DPCU,
|
||||
aDebug_LastEntry
|
||||
|
|
|
@ -0,0 +1,821 @@
|
|||
/* 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 "pcu_utils.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_egprs_ms_class(0),
|
||||
m_is_idle(true),
|
||||
m_ref(0),
|
||||
m_list(this),
|
||||
m_delay(0),
|
||||
m_nack_rate_dl(0),
|
||||
m_reserved_dl_slots(0),
|
||||
m_reserved_ul_slots(0),
|
||||
m_current_trx(NULL),
|
||||
m_codel_state(NULL),
|
||||
m_mode(GprsCodingScheme::GPRS)
|
||||
{
|
||||
int codel_interval = LLC_CODEL_USE_DEFAULT;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
|
||||
|
||||
m_imsi[0] = 0;
|
||||
memset(&m_timer, 0, sizeof(m_timer));
|
||||
m_timer.cb = GprsMs::timeout;
|
||||
m_llc_queue.init();
|
||||
|
||||
set_mode(m_mode);
|
||||
|
||||
if (m_bts)
|
||||
codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
|
||||
|
||||
if (codel_interval) {
|
||||
if (codel_interval == LLC_CODEL_USE_DEFAULT)
|
||||
codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
|
||||
m_codel_state = talloc(this, struct gprs_codel);
|
||||
gprs_codel_init(m_codel_state);
|
||||
gprs_codel_set_interval(m_codel_state, codel_interval);
|
||||
}
|
||||
m_last_cs_not_low = now_msec();
|
||||
}
|
||||
|
||||
GprsMs::~GprsMs()
|
||||
{
|
||||
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
|
||||
|
||||
set_reserved_slots(NULL, 0, 0);
|
||||
|
||||
if (osmo_timer_pending(&m_timer))
|
||||
osmo_timer_del(&m_timer);
|
||||
|
||||
if (m_ul_tbf) {
|
||||
m_ul_tbf->set_ms(NULL);
|
||||
m_ul_tbf = NULL;
|
||||
}
|
||||
|
||||
if (m_dl_tbf) {
|
||||
m_dl_tbf->set_ms(NULL);
|
||||
m_dl_tbf = NULL;
|
||||
}
|
||||
|
||||
llist_for_each_safe(pos, tmp, &m_old_tbfs)
|
||||
pos->entry()->set_ms(NULL);
|
||||
|
||||
m_llc_queue.clear(m_bts);
|
||||
}
|
||||
|
||||
void* GprsMs::operator new(size_t size)
|
||||
{
|
||||
static void *tall_ms_ctx = NULL;
|
||||
if (!tall_ms_ctx)
|
||||
tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
|
||||
|
||||
return talloc_size(tall_ms_ctx, size);
|
||||
}
|
||||
|
||||
void GprsMs::operator delete(void* p)
|
||||
{
|
||||
talloc_free(p);
|
||||
}
|
||||
|
||||
GprsMs *GprsMs::ref()
|
||||
{
|
||||
m_ref += 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
void GprsMs::unref()
|
||||
{
|
||||
OSMO_ASSERT(m_ref >= 0);
|
||||
m_ref -= 1;
|
||||
if (m_ref == 0)
|
||||
update_status();
|
||||
}
|
||||
|
||||
void GprsMs::start_timer()
|
||||
{
|
||||
if (m_delay == 0)
|
||||
return;
|
||||
|
||||
if (!m_timer.data)
|
||||
m_timer.data = ref();
|
||||
|
||||
osmo_timer_schedule(&m_timer, m_delay, 0);
|
||||
}
|
||||
|
||||
void GprsMs::stop_timer()
|
||||
{
|
||||
if (!m_timer.data)
|
||||
return;
|
||||
|
||||
osmo_timer_del(&m_timer);
|
||||
m_timer.data = NULL;
|
||||
unref();
|
||||
}
|
||||
|
||||
void GprsMs::set_mode(GprsCodingScheme::Mode mode)
|
||||
{
|
||||
m_mode = mode;
|
||||
|
||||
if (!m_bts)
|
||||
return;
|
||||
|
||||
switch (m_mode) {
|
||||
case GprsCodingScheme::GPRS:
|
||||
if (!m_current_cs_ul.isGprs()) {
|
||||
m_current_cs_ul = GprsCodingScheme::getGprsByNum(
|
||||
m_bts->bts_data()->initial_cs_ul);
|
||||
if (!m_current_cs_ul.isValid())
|
||||
m_current_cs_ul = GprsCodingScheme::CS1;
|
||||
}
|
||||
if (!m_current_cs_dl.isGprs()) {
|
||||
m_current_cs_dl = GprsCodingScheme::getGprsByNum(
|
||||
m_bts->bts_data()->initial_cs_dl);
|
||||
if (!m_current_cs_dl.isValid())
|
||||
m_current_cs_dl = GprsCodingScheme::CS1;
|
||||
}
|
||||
break;
|
||||
|
||||
case GprsCodingScheme::EGPRS_GMSK:
|
||||
case GprsCodingScheme::EGPRS:
|
||||
if (!m_current_cs_ul.isEgprs()) {
|
||||
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
|
||||
m_bts->bts_data()->initial_mcs_ul);
|
||||
if (!m_current_cs_dl.isValid())
|
||||
m_current_cs_ul = GprsCodingScheme::MCS1;
|
||||
}
|
||||
if (!m_current_cs_dl.isEgprs()) {
|
||||
m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
|
||||
m_bts->bts_data()->initial_mcs_dl);
|
||||
if (!m_current_cs_dl.isValid())
|
||||
m_current_cs_dl = GprsCodingScheme::MCS1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
attach_dl_tbf(as_dl_tbf(tbf));
|
||||
else
|
||||
attach_ul_tbf(as_ul_tbf(tbf));
|
||||
}
|
||||
|
||||
void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
|
||||
{
|
||||
if (m_ul_tbf == tbf)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
|
||||
tlli(), tbf->name());
|
||||
|
||||
Guard guard(this);
|
||||
|
||||
if (m_ul_tbf)
|
||||
llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
|
||||
|
||||
m_ul_tbf = tbf;
|
||||
|
||||
if (tbf)
|
||||
stop_timer();
|
||||
}
|
||||
|
||||
void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
|
||||
{
|
||||
if (m_dl_tbf == tbf)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
|
||||
tlli(), tbf->name());
|
||||
|
||||
Guard guard(this);
|
||||
|
||||
if (m_dl_tbf)
|
||||
llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
|
||||
|
||||
m_dl_tbf = tbf;
|
||||
|
||||
if (tbf)
|
||||
stop_timer();
|
||||
}
|
||||
|
||||
void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
|
||||
m_ul_tbf = NULL;
|
||||
} else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
|
||||
m_dl_tbf = NULL;
|
||||
} else {
|
||||
bool found = false;
|
||||
|
||||
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
|
||||
llist_for_each_safe(pos, tmp, &m_old_tbfs) {
|
||||
if (pos->entry() == tbf) {
|
||||
llist_del(pos);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Protect against recursive calls via set_ms() */
|
||||
if (!found)
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
|
||||
tlli(), tbf->name());
|
||||
|
||||
if (tbf->ms() == this)
|
||||
tbf->set_ms(NULL);
|
||||
|
||||
if (!m_dl_tbf && !m_ul_tbf) {
|
||||
set_reserved_slots(NULL, 0, 0);
|
||||
|
||||
if (tlli() != 0)
|
||||
start_timer();
|
||||
}
|
||||
|
||||
update_status();
|
||||
}
|
||||
|
||||
void GprsMs::update_status()
|
||||
{
|
||||
if (m_ref > 0)
|
||||
return;
|
||||
|
||||
if (is_idle() && !m_is_idle) {
|
||||
m_is_idle = true;
|
||||
m_cb->ms_idle(this);
|
||||
/* this can be deleted by now, do not access it */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_idle() && m_is_idle) {
|
||||
m_is_idle = false;
|
||||
m_cb->ms_active(this);
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::reset()
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
|
||||
tlli(), imsi());
|
||||
|
||||
stop_timer();
|
||||
|
||||
m_tlli = 0;
|
||||
m_new_dl_tlli = 0;
|
||||
m_new_ul_tlli = 0;
|
||||
m_imsi[0] = '\0';
|
||||
}
|
||||
|
||||
void GprsMs::merge_old_ms(GprsMs *old_ms)
|
||||
{
|
||||
if (old_ms == this)
|
||||
return;
|
||||
|
||||
if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
|
||||
set_imsi(old_ms->imsi());
|
||||
|
||||
if (!ms_class() && old_ms->ms_class())
|
||||
set_ms_class(old_ms->ms_class());
|
||||
|
||||
m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
|
||||
|
||||
old_ms->reset();
|
||||
}
|
||||
|
||||
void GprsMs::set_tlli(uint32_t tlli)
|
||||
{
|
||||
if (tlli == m_tlli || tlli == m_new_ul_tlli)
|
||||
return;
|
||||
|
||||
if (tlli != m_new_dl_tlli) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
|
||||
"not yet confirmed\n",
|
||||
this->tlli(), tlli);
|
||||
m_new_ul_tlli = tlli;
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
|
||||
"already confirmed partly\n",
|
||||
m_tlli, tlli);
|
||||
|
||||
m_tlli = tlli;
|
||||
m_new_dl_tlli = 0;
|
||||
m_new_ul_tlli = 0;
|
||||
}
|
||||
|
||||
bool GprsMs::confirm_tlli(uint32_t tlli)
|
||||
{
|
||||
if (tlli == m_tlli || tlli == m_new_dl_tlli)
|
||||
return false;
|
||||
|
||||
if (tlli != m_new_ul_tlli) {
|
||||
/* The MS has not sent a message with the new TLLI, which may
|
||||
* happen according to the spec [TODO: add reference]. */
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
|
||||
"partly confirmed\n", tlli);
|
||||
/* Use the network's idea of TLLI as candidate, this does not
|
||||
* change the result value of tlli() */
|
||||
m_new_dl_tlli = tlli;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
|
||||
|
||||
m_tlli = tlli;
|
||||
m_new_dl_tlli = 0;
|
||||
m_new_ul_tlli = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GprsMs::set_imsi(const char *imsi)
|
||||
{
|
||||
if (!imsi) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (imsi[0] && strlen(imsi) < 3) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
|
||||
imsi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(imsi, m_imsi) == 0)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
|
||||
tlli(), m_imsi, imsi);
|
||||
|
||||
strncpy(m_imsi, imsi, sizeof(m_imsi));
|
||||
m_imsi[sizeof(m_imsi) - 1] = '\0';
|
||||
}
|
||||
|
||||
void GprsMs::set_ta(uint8_t ta_)
|
||||
{
|
||||
if (ta_ == m_ta)
|
||||
return;
|
||||
|
||||
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::set_egprs_ms_class(uint8_t ms_class_)
|
||||
{
|
||||
if (ms_class_ == m_egprs_ms_class)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
|
||||
tlli(), m_egprs_ms_class, ms_class_);
|
||||
|
||||
m_egprs_ms_class = ms_class_;
|
||||
}
|
||||
|
||||
void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
int64_t now;
|
||||
GprsCodingScheme max_cs_dl = this->max_cs_dl();
|
||||
|
||||
OSMO_ASSERT(max_cs_dl);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (error_rate < 0)
|
||||
return;
|
||||
|
||||
now = now_msec();
|
||||
|
||||
/* TODO: Check for TBF direction */
|
||||
/* TODO: Support different CS values for UL and DL */
|
||||
|
||||
m_nack_rate_dl = error_rate;
|
||||
|
||||
if (error_rate > bts_data->cs_adj_upper_limit) {
|
||||
if (m_current_cs_dl.to_num() > 1) {
|
||||
m_current_cs_dl.dec(mode());
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): High error rate %d%%, "
|
||||
"reducing CS level to %s\n",
|
||||
imsi(), error_rate, m_current_cs_dl.name());
|
||||
m_last_cs_not_low = now;
|
||||
}
|
||||
} else if (error_rate < bts_data->cs_adj_lower_limit) {
|
||||
if (m_current_cs_dl < max_cs_dl) {
|
||||
if (now - m_last_cs_not_low > 1000) {
|
||||
m_current_cs_dl.inc(mode());
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"increasing DL CS level to %s\n",
|
||||
imsi(), error_rate,
|
||||
m_current_cs_dl.name());
|
||||
m_last_cs_not_low = now;
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"ignored (within blocking period)\n",
|
||||
imsi(), error_rate);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"MS (IMSI %s): Medium error rate %d%%, ignored\n",
|
||||
imsi(), error_rate);
|
||||
m_last_cs_not_low = now;
|
||||
}
|
||||
}
|
||||
|
||||
GprsCodingScheme GprsMs::max_cs_ul() const
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (m_current_cs_ul.isGprs()) {
|
||||
if (!bts_data->max_cs_ul)
|
||||
return GprsCodingScheme(GprsCodingScheme::CS4);
|
||||
|
||||
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
|
||||
}
|
||||
|
||||
if (!m_current_cs_ul.isEgprs())
|
||||
return GprsCodingScheme(); /* UNKNOWN */
|
||||
|
||||
if (bts_data->max_mcs_ul)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
|
||||
else if (bts_data->max_cs_ul)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
|
||||
|
||||
return GprsCodingScheme(GprsCodingScheme::MCS4);
|
||||
}
|
||||
|
||||
GprsCodingScheme GprsMs::max_cs_dl() const
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (m_current_cs_dl.isGprs()) {
|
||||
if (!bts_data->max_cs_dl)
|
||||
return GprsCodingScheme(GprsCodingScheme::CS4);
|
||||
|
||||
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
|
||||
}
|
||||
|
||||
if (!m_current_cs_dl.isEgprs())
|
||||
return GprsCodingScheme(); /* UNKNOWN */
|
||||
|
||||
if (bts_data->max_mcs_dl)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
|
||||
else if (bts_data->max_cs_dl)
|
||||
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
|
||||
|
||||
return GprsCodingScheme(GprsCodingScheme::MCS4);
|
||||
}
|
||||
|
||||
void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
GprsCodingScheme max_cs_ul = this->max_cs_ul();
|
||||
|
||||
int old_link_qual;
|
||||
int low;
|
||||
int high;
|
||||
GprsCodingScheme new_cs_ul = m_current_cs_ul;
|
||||
unsigned current_cs_num = m_current_cs_ul.to_num();
|
||||
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (!max_cs_ul) {
|
||||
LOGP(DRLCMACDL, LOGL_ERROR,
|
||||
"max_cs_ul cannot be derived (current UL CS: %s)\n",
|
||||
m_current_cs_ul.name());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_current_cs_ul)
|
||||
return;
|
||||
|
||||
if (!meas->have_link_qual)
|
||||
return;
|
||||
|
||||
old_link_qual = meas->link_qual;
|
||||
|
||||
if (m_current_cs_ul.isGprs()) {
|
||||
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
|
||||
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
|
||||
} else if (m_current_cs_ul.isEgprs()) {
|
||||
/* TODO, use separate table */
|
||||
if (current_cs_num > 4)
|
||||
current_cs_num = 4;
|
||||
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
|
||||
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_l1_meas.have_link_qual)
|
||||
old_link_qual = m_l1_meas.link_qual;
|
||||
|
||||
if (meas->link_qual < low && old_link_qual < low)
|
||||
new_cs_ul.dec(mode());
|
||||
else if (meas->link_qual > high && old_link_qual > high &&
|
||||
m_current_cs_ul < max_cs_ul)
|
||||
new_cs_ul.inc(mode());
|
||||
|
||||
if (m_current_cs_ul != new_cs_ul) {
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): "
|
||||
"Link quality %ddB (%ddB) left window [%d, %d], "
|
||||
"modifying uplink CS level: %s -> %s\n",
|
||||
imsi(), meas->link_qual, old_link_qual,
|
||||
low, high,
|
||||
m_current_cs_ul.name(), new_cs_ul.name());
|
||||
|
||||
m_current_cs_ul = new_cs_ul;
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
update_cs_ul(meas);
|
||||
|
||||
if (meas->have_rssi)
|
||||
m_l1_meas.set_rssi(meas->rssi);
|
||||
if (meas->have_bto)
|
||||
m_l1_meas.set_bto(meas->bto);
|
||||
if (meas->have_ber)
|
||||
m_l1_meas.set_ber(meas->ber);
|
||||
if (meas->have_link_qual)
|
||||
m_l1_meas.set_link_qual(meas->link_qual);
|
||||
|
||||
if (meas->have_ms_rx_qual)
|
||||
m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
|
||||
if (meas->have_ms_c_value)
|
||||
m_l1_meas.set_ms_c_value(meas->ms_c_value);
|
||||
if (meas->have_ms_sign_var)
|
||||
m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
|
||||
|
||||
if (meas->have_ms_i_level) {
|
||||
for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
|
||||
if (meas->ts[i].have_ms_i_level)
|
||||
m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
|
||||
else
|
||||
m_l1_meas.ts[i].have_ms_i_level = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GprsCodingScheme GprsMs::current_cs_dl() const
|
||||
{
|
||||
GprsCodingScheme cs = m_current_cs_dl;
|
||||
size_t unencoded_octets;
|
||||
|
||||
if (!m_bts)
|
||||
return cs;
|
||||
|
||||
unencoded_octets = m_llc_queue.octets();
|
||||
|
||||
/* If the DL TBF is active, add number of unencoded chunk octets */
|
||||
if (m_dl_tbf)
|
||||
unencoded_octets += m_dl_tbf->m_llc.chunk_size();
|
||||
|
||||
/* There are many unencoded octets, don't reduce */
|
||||
if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
|
||||
return cs;
|
||||
|
||||
/* RF conditions are good, don't reduce */
|
||||
if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
|
||||
return cs;
|
||||
|
||||
/* The throughput would probably be better if the CS level was reduced */
|
||||
cs.dec(mode());
|
||||
|
||||
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
|
||||
if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
|
||||
cs.dec(mode());
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
int GprsMs::first_common_ts() const
|
||||
{
|
||||
if (m_dl_tbf)
|
||||
return m_dl_tbf->first_common_ts;
|
||||
|
||||
if (m_ul_tbf)
|
||||
return m_ul_tbf->first_common_ts;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t GprsMs::dl_slots() const
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
if (m_dl_tbf)
|
||||
slots |= m_dl_tbf->dl_slots();
|
||||
|
||||
if (m_ul_tbf)
|
||||
slots |= m_ul_tbf->dl_slots();
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
uint8_t GprsMs::ul_slots() const
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
if (m_dl_tbf)
|
||||
slots |= m_dl_tbf->ul_slots();
|
||||
|
||||
if (m_ul_tbf)
|
||||
slots |= m_ul_tbf->ul_slots();
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
uint8_t GprsMs::current_pacch_slots() const
|
||||
{
|
||||
uint8_t slots = 0;
|
||||
|
||||
bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
|
||||
bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
|
||||
|
||||
if (!is_dl_active && !is_ul_active)
|
||||
return 0;
|
||||
|
||||
/* see TS 44.060, 8.1.1.2.2 */
|
||||
if (is_dl_active && !is_ul_active)
|
||||
slots = m_dl_tbf->dl_slots();
|
||||
else if (!is_dl_active && is_ul_active)
|
||||
slots = m_ul_tbf->ul_slots();
|
||||
else
|
||||
slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
|
||||
|
||||
/* Assume a multislot class 1 device */
|
||||
/* TODO: For class 2 devices, this could be removed */
|
||||
slots = pcu_lsb(slots);
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
|
||||
uint8_t ul_slots, uint8_t dl_slots)
|
||||
{
|
||||
if (m_current_trx) {
|
||||
m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
|
||||
m_reserved_dl_slots);
|
||||
m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
|
||||
m_reserved_ul_slots);
|
||||
m_reserved_dl_slots = 0;
|
||||
m_reserved_ul_slots = 0;
|
||||
}
|
||||
m_current_trx = trx;
|
||||
if (trx) {
|
||||
m_reserved_dl_slots = dl_slots;
|
||||
m_reserved_ul_slots = ul_slots;
|
||||
m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
|
||||
m_reserved_dl_slots);
|
||||
m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
|
||||
m_reserved_ul_slots);
|
||||
}
|
||||
}
|
||||
|
||||
gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
|
||||
{
|
||||
switch (dir) {
|
||||
case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
|
||||
case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
/* 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();
|
||||
GprsCodingScheme::Mode mode() const;
|
||||
void set_mode(GprsCodingScheme::Mode mode);
|
||||
|
||||
const char *imsi() const;
|
||||
void set_imsi(const char *imsi);
|
||||
|
||||
uint8_t ta() const;
|
||||
void set_ta(uint8_t ta);
|
||||
uint8_t ms_class() const;
|
||||
uint8_t egprs_ms_class() const;
|
||||
void set_ms_class(uint8_t ms_class);
|
||||
void set_egprs_ms_class(uint8_t ms_class);
|
||||
|
||||
GprsCodingScheme current_cs_ul() const;
|
||||
GprsCodingScheme current_cs_dl() const;
|
||||
GprsCodingScheme max_cs_ul() const;
|
||||
GprsCodingScheme max_cs_dl() const;
|
||||
|
||||
int first_common_ts() const;
|
||||
uint8_t dl_slots() const;
|
||||
uint8_t ul_slots() const;
|
||||
uint8_t reserved_dl_slots() const;
|
||||
uint8_t reserved_ul_slots() const;
|
||||
uint8_t current_pacch_slots() const;
|
||||
gprs_rlcmac_trx *current_trx() const;
|
||||
void set_reserved_slots(gprs_rlcmac_trx *trx,
|
||||
uint8_t ul_slots, uint8_t dl_slots);
|
||||
|
||||
gprs_llc_queue *llc_queue();
|
||||
const gprs_llc_queue *llc_queue() const;
|
||||
gprs_codel *codel_state() const;
|
||||
|
||||
void set_timeout(unsigned secs);
|
||||
|
||||
void attach_tbf(gprs_rlcmac_tbf *tbf);
|
||||
void attach_ul_tbf(gprs_rlcmac_ul_tbf *tbf);
|
||||
void attach_dl_tbf(gprs_rlcmac_dl_tbf *tbf);
|
||||
|
||||
void detach_tbf(gprs_rlcmac_tbf *tbf);
|
||||
|
||||
void update_error_rate(gprs_rlcmac_tbf *tbf, int percent);
|
||||
|
||||
bool is_idle() const;
|
||||
bool need_dl_tbf() const;
|
||||
|
||||
void* operator new(size_t num);
|
||||
void operator delete(void* p);
|
||||
|
||||
LListHead<GprsMs>& list() {return this->m_list;}
|
||||
const LListHead<GprsMs>& list() const {return this->m_list;}
|
||||
const LListHead<gprs_rlcmac_tbf>& old_tbfs() const {return m_old_tbfs;}
|
||||
|
||||
void update_l1_meas(const pcu_l1_meas *meas);
|
||||
const pcu_l1_meas* l1_meas() const {return &m_l1_meas;};
|
||||
unsigned nack_rate_dl() const;
|
||||
|
||||
/* internal use */
|
||||
static void timeout(void *priv_);
|
||||
|
||||
protected:
|
||||
void update_status();
|
||||
GprsMs *ref();
|
||||
void unref();
|
||||
void start_timer();
|
||||
void stop_timer();
|
||||
void update_cs_ul(const pcu_l1_meas*);
|
||||
|
||||
private:
|
||||
BTS *m_bts;
|
||||
Callback * m_cb;
|
||||
gprs_rlcmac_ul_tbf *m_ul_tbf;
|
||||
gprs_rlcmac_dl_tbf *m_dl_tbf;
|
||||
LListHead<gprs_rlcmac_tbf> m_old_tbfs;
|
||||
|
||||
uint32_t m_tlli;
|
||||
uint32_t m_new_ul_tlli;
|
||||
uint32_t m_new_dl_tlli;
|
||||
|
||||
/* store IMSI for look-up and PCH retransmission */
|
||||
char m_imsi[16];
|
||||
uint8_t m_ta;
|
||||
uint8_t m_ms_class;
|
||||
uint8_t m_egprs_ms_class;
|
||||
/* current coding scheme */
|
||||
GprsCodingScheme m_current_cs_ul;
|
||||
GprsCodingScheme m_current_cs_dl;
|
||||
|
||||
gprs_llc_queue m_llc_queue;
|
||||
|
||||
bool m_is_idle;
|
||||
int m_ref;
|
||||
LListHead<GprsMs> m_list;
|
||||
struct osmo_timer_list m_timer;
|
||||
unsigned m_delay;
|
||||
|
||||
int64_t m_last_cs_not_low;
|
||||
|
||||
pcu_l1_meas m_l1_meas;
|
||||
unsigned m_nack_rate_dl;
|
||||
uint8_t m_reserved_dl_slots;
|
||||
uint8_t m_reserved_ul_slots;
|
||||
gprs_rlcmac_trx *m_current_trx;
|
||||
|
||||
struct gprs_codel *m_codel_state;
|
||||
GprsCodingScheme::Mode m_mode;
|
||||
};
|
||||
|
||||
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::egprs_ms_class() const
|
||||
{
|
||||
return m_egprs_ms_class;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme GprsMs::current_cs_ul() const
|
||||
{
|
||||
return m_current_cs_ul;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::Mode GprsMs::mode() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/* gprs_ms_storage.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "gprs_ms_storage.h"
|
||||
|
||||
#include "tbf.h"
|
||||
#include "bts.h"
|
||||
#include "gprs_debug.h"
|
||||
|
||||
#define GPRS_UNDEFINED_IMSI "000"
|
||||
|
||||
GprsMsStorage::GprsMsStorage(BTS *bts) :
|
||||
m_bts(bts)
|
||||
{
|
||||
}
|
||||
|
||||
GprsMsStorage::~GprsMsStorage()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void GprsMsStorage::cleanup()
|
||||
{
|
||||
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 (m_bts)
|
||||
m_bts->ms_present(m_bts->ms_present_get() - 1);
|
||||
if (ms->is_idle())
|
||||
delete ms;
|
||||
}
|
||||
|
||||
void GprsMsStorage::ms_active(class GprsMs *ms)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi) const
|
||||
{
|
||||
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);
|
||||
if (m_bts)
|
||||
m_bts->ms_present(m_bts->ms_present_get() + 1);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
GprsMs *GprsMsStorage::create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
|
||||
{
|
||||
GprsMs *ms = get_ms(tlli);
|
||||
|
||||
if (ms)
|
||||
return ms;
|
||||
|
||||
ms = create_ms();
|
||||
|
||||
if (dir == GPRS_RLCMAC_UL_TBF)
|
||||
ms->set_tlli(tlli);
|
||||
else
|
||||
ms->confirm_tlli(tlli);
|
||||
|
||||
return ms;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* 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();
|
||||
|
||||
void cleanup();
|
||||
|
||||
virtual void ms_idle(class GprsMs *);
|
||||
virtual void ms_active(class GprsMs *);
|
||||
|
||||
GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = 0, const char *imsi = 0) const;
|
||||
GprsMs *create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir);
|
||||
GprsMs *create_ms();
|
||||
|
||||
const LListHead<GprsMs>& ms_list() const {return m_list;}
|
||||
private:
|
||||
BTS *m_bts;
|
||||
LListHead<GprsMs> m_list;
|
||||
};
|
1784
src/gprs_rlcmac.cpp
1784
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,216 +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 timeval bw_tv; /* timestamp for bandwidth calculation */
|
||||
uint32_t bw_octets; /* number of octets transmitted since bw_tv */
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -250,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
|
||||
*/
|
||||
|
@ -270,46 +63,18 @@ struct gprs_rlcmac_cs {
|
|||
uint8_t block_payload;
|
||||
};
|
||||
|
||||
extern struct gprs_rlcmac_cs gprs_rlcmac_cs[];
|
||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||
uint16_t lost);
|
||||
|
||||
#ifdef DEBUG_DIAGRAM
|
||||
void debug_diagram(int diag, const char *format, ...);
|
||||
#else
|
||||
#define debug_diagram(a, b, args...) ;
|
||||
#endif
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
|
||||
|
||||
int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
|
||||
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr);
|
||||
|
||||
struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn);
|
||||
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
|
||||
|
||||
int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx,
|
||||
int8_t use_trx);
|
||||
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
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 {
|
||||
|
@ -319,89 +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);
|
||||
|
||||
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_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);
|
||||
|
||||
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 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
|
@ -0,0 +1,192 @@
|
|||
/* Measurements
|
||||
*
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <tbf.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
NC_Measurement_Report_t *ncr;
|
||||
NC_Measurements_t *nc;
|
||||
int i;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "Measuement Report of TLLI=0x%08x:",
|
||||
pmr->TLLI);
|
||||
|
||||
switch (pmr->UnionType) {
|
||||
case 0:
|
||||
ncr = &pmr->u.NC_Measurement_Report;
|
||||
LOGPC(DRLCMACMEAS, LOGL_INFO, " NC%u Serv %d dbm",
|
||||
ncr->NC_MODE + 1,
|
||||
ncr->Serving_Cell_Data.RXLEV_SERVING_CELL - 110);
|
||||
for (i = 0; i < ncr->NUMBER_OF_NC_MEASUREMENTS; i++) {
|
||||
nc = &ncr->NC_Measurements[i];
|
||||
LOGPC(DRLCMACMEAS, LOGL_DEBUG, ", Neigh %u %d dbm",
|
||||
nc->FREQUENCY_N, nc->RXLEV_N - 110);
|
||||
}
|
||||
LOGPC(DRLCMACMEAS, LOGL_INFO, "\n");
|
||||
|
||||
break;
|
||||
case 1:
|
||||
LOGPC(DRLCMACMEAS, LOGL_INFO,
|
||||
" <EXT Reporting not supported!>\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* uplink measurement
|
||||
*/
|
||||
|
||||
/* RSSI values received from MS */
|
||||
int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi)
|
||||
{
|
||||
struct timeval now_tv, *rssi_tv = &tbf->meas.rssi_tv;
|
||||
uint32_t elapsed;
|
||||
|
||||
tbf->meas.rssi_sum += rssi;
|
||||
tbf->meas.rssi_num++;
|
||||
|
||||
gettimeofday(&now_tv, NULL);
|
||||
elapsed = ((now_tv.tv_sec - rssi_tv->tv_sec) << 7)
|
||||
+ ((now_tv.tv_usec - rssi_tv->tv_usec) << 7) / 1000000;
|
||||
if (elapsed < 128)
|
||||
return 0;
|
||||
|
||||
gprs_rlcmac_rssi_rep(tbf);
|
||||
|
||||
/* reset rssi values and timestamp */
|
||||
memcpy(rssi_tv, &now_tv, sizeof(struct timeval));
|
||||
tbf->meas.rssi_sum = 0;
|
||||
tbf->meas.rssi_num = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Give RSSI report */
|
||||
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
/* No measurement values */
|
||||
if (!tbf->meas.rssi_num)
|
||||
return -EINVAL;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "UL RSSI of TLLI=0x%08x: %d dBm\n",
|
||||
tbf->tlli(), tbf->meas.rssi_sum / tbf->meas.rssi_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* lost frames
|
||||
*/
|
||||
|
||||
/* Lost frames reported from RLCMAC layer */
|
||||
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
|
||||
uint16_t lost)
|
||||
{
|
||||
struct timeval now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv;
|
||||
uint32_t elapsed;
|
||||
uint16_t sum = received + lost;
|
||||
|
||||
/* No measurement values */
|
||||
if (!sum)
|
||||
return -EINVAL;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d "
|
||||
"Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum);
|
||||
|
||||
tbf->m_bw.dl_loss_received += received;
|
||||
tbf->m_bw.dl_loss_lost += lost;
|
||||
|
||||
gettimeofday(&now_tv, NULL);
|
||||
elapsed = ((now_tv.tv_sec - loss_tv->tv_sec) << 7)
|
||||
+ ((now_tv.tv_usec - loss_tv->tv_usec) << 7) / 1000000;
|
||||
if (elapsed < 128)
|
||||
return 0;
|
||||
|
||||
gprs_rlcmac_lost_rep(tbf);
|
||||
|
||||
/* reset lost values and timestamp */
|
||||
memcpy(loss_tv, &now_tv, sizeof(struct timeval));
|
||||
tbf->m_bw.dl_loss_received = 0;
|
||||
tbf->m_bw.dl_loss_lost = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Give Lost report */
|
||||
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
|
||||
{
|
||||
uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received;
|
||||
|
||||
/* No measurement values */
|
||||
if (!sum)
|
||||
return -EINVAL;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
|
||||
"%d%%\n", tbf->imsi(), tbf->tlli(),
|
||||
tbf->m_bw.dl_loss_lost * 100 / sum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* downlink bandwidth
|
||||
*/
|
||||
|
||||
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
|
||||
{
|
||||
struct timeval now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv;
|
||||
uint32_t elapsed;
|
||||
|
||||
tbf->m_bw.dl_bw_octets += octets;
|
||||
|
||||
gettimeofday(&now_tv, NULL);
|
||||
elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7)
|
||||
+ ((now_tv.tv_usec - bw_tv->tv_usec) << 7) / 1000000;
|
||||
if (elapsed < 128)
|
||||
return 0;
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
|
||||
"%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->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(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;
|
||||
LListHead<gprs_rlcmac_tbf> *pos;
|
||||
uint32_t poll_fn;
|
||||
|
||||
/* check special TBF for events */
|
||||
|
@ -35,86 +42,71 @@ 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_for_each(pos, &bts->ul_tbfs()) {
|
||||
ul_tbf = as_ul_tbf(pos->entry());
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
/* this trx, this ts */
|
||||
if (tbf->trx != trx || tbf->control_ts != ts)
|
||||
if (ul_tbf->trx->trx_no != trx || !ul_tbf->is_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_for_each(pos, &bts->dl_tbfs()) {
|
||||
dl_tbf = as_dl_tbf(pos->entry());
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
/* this trx, this ts */
|
||||
if (tbf->trx != trx || tbf->control_ts != ts)
|
||||
if (dl_tbf->trx->trx_no != trx || !dl_tbf->is_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) {
|
||||
llist_del(&sba->list);
|
||||
talloc_free(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;
|
||||
}
|
||||
|
@ -122,41 +114,70 @@ 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);
|
||||
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
|
||||
tbf = next_list[(pdch->next_ctrl_prio + i) % 3];
|
||||
if (!tbf)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Assignments for the same direction have lower precedence,
|
||||
* because they may kill the TBF when the CONTOL ACK is
|
||||
* received, thus preventing the others from being processed.
|
||||
*/
|
||||
|
||||
if (tbf == ul_ass_tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
msg = ul_ass_tbf->create_ul_ass(fn, ts);
|
||||
else if (tbf == dl_ass_tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
msg = dl_ass_tbf->create_dl_ass(fn, ts);
|
||||
else if (tbf == ul_ack_tbf)
|
||||
msg = ul_ack_tbf->create_ul_ack(fn, ts);
|
||||
|
||||
if (!msg) {
|
||||
tbf = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
pdch->next_ctrl_prio += 1;
|
||||
pdch->next_ctrl_prio %= 3;
|
||||
break;
|
||||
}
|
||||
/* 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);
|
||||
|
||||
if (!msg) {
|
||||
/*
|
||||
* If one of these is left, the response (CONTROL ACK) from the
|
||||
* MS will kill the current TBF, only one of them can be
|
||||
* non-NULL
|
||||
*/
|
||||
if (dl_ass_tbf) {
|
||||
tbf = dl_ass_tbf;
|
||||
msg = dl_ass_tbf->create_dl_ass(fn, ts);
|
||||
} else if (ul_ass_tbf) {
|
||||
tbf = ul_ass_tbf;
|
||||
msg = ul_ass_tbf->create_ul_ass(fn, ts);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
@ -166,45 +187,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 */
|
||||
|
@ -212,7 +276,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;
|
||||
|
||||
|
@ -224,22 +288,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;
|
||||
|
@ -248,25 +315,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->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);
|
||||
|
||||
|
@ -276,7 +342,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)
|
||||
|
@ -287,8 +353,12 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|||
/* msg is now available */
|
||||
|
||||
/* set USF */
|
||||
OSMO_ASSERT(msgb_length(msg) > 0);
|
||||
msg->data[0] = (msg->data[0] & 0xf8) | usf;
|
||||
|
||||
/* Used to measure the leak rate, count all blocks */
|
||||
gprs_bssgp_update_frames_sent();
|
||||
|
||||
/* send PDTCH/PACCH to L1 */
|
||||
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
|
||||
|
||||
|
|
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);
|
||||
|
@ -5506,3 +5509,11 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
|
|||
LOGPC(DRLCMACDATA, LOGL_NOTICE, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t *data)
|
||||
{
|
||||
csnStream_t ar;
|
||||
unsigned readIndex = 0;
|
||||
csnStreamInit(&ar, 0, 8 * vector->data_len);
|
||||
/*ret =*/ csnStreamDecoder(&ar, CSNDESCR(MS_Radio_Access_capability_t), vector, readIndex, data);
|
||||
}
|
||||
|
|
|
@ -476,7 +476,7 @@ typedef struct
|
|||
|
||||
typedef struct
|
||||
{
|
||||
guint8 LENGTH;
|
||||
/* guint8 LENGTH; */
|
||||
EGPRS_AckNack_Desc_t Desc;
|
||||
} EGPRS_AckNack_w_len_t;
|
||||
|
||||
|
@ -5136,4 +5136,5 @@ typedef struct
|
|||
void encode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data);
|
||||
void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * data);
|
||||
void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t * data);
|
||||
void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t * data);
|
||||
#endif /* __PACKET_GSM_RLCMAC_H__ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, >);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
/* openbts_sock.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Ivan Klyuchnikov
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <bitvector.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <pcuif_proto.h>
|
||||
}
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
struct femtol1_hdl {
|
||||
struct gsm_time gsm_time;
|
||||
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
|
||||
uint32_t dsp_trace_f;
|
||||
uint16_t clk_cal;
|
||||
struct llist_head wlc_list;
|
||||
|
||||
void *priv; /* user reference */
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
unsigned int alive_prim_cnt;
|
||||
|
||||
struct osmo_fd read_ofd; /* osmo file descriptors */
|
||||
struct osmo_wqueue write_q;
|
||||
|
||||
struct {
|
||||
uint16_t arfcn;
|
||||
uint8_t tn;
|
||||
uint8_t tsc;
|
||||
uint16_t ta;
|
||||
} channel_info;
|
||||
|
||||
};
|
||||
|
||||
struct l1fwd_hdl {
|
||||
struct sockaddr_storage remote_sa;
|
||||
socklen_t remote_sa_len;
|
||||
|
||||
struct osmo_wqueue udp_wq;
|
||||
|
||||
struct femtol1_hdl *fl1h;
|
||||
};
|
||||
|
||||
struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
|
||||
|
||||
// TODO: We should move this parameters to config file.
|
||||
#define PCU_L1_IF_PORT 5944
|
||||
|
||||
/* OpenBTS socket functions */
|
||||
|
||||
int pcu_sock_send(struct msgb *msg)
|
||||
{
|
||||
osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* data has arrived on the udp socket */
|
||||
static int udp_read_cb(struct osmo_fd *ofd)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
|
||||
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
|
||||
struct femtol1_hdl *fl1h = l1fh->fl1h;
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->l1h = msg->data;
|
||||
|
||||
l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
|
||||
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
|
||||
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
|
||||
if (rc < 0) {
|
||||
perror("read from udp");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
} else if (rc == 0) {
|
||||
perror("len=0 read from udp");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
struct gsm_pcu_if *pcu_prim = (gsm_pcu_if *)(msg->l1h);
|
||||
rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* callback when we can write to the UDP socket */
|
||||
static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
|
||||
|
||||
//LOGP(DPCU, LOGL_ERROR, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_length(msg));
|
||||
|
||||
rc = sendto(ofd->fd, msgb_data(msg), msgb_length(msg), 0,
|
||||
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
|
||||
if (rc < 0) {
|
||||
LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
return rc;
|
||||
} else if (rc < (int)msgb_length(msg)) {
|
||||
LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
|
||||
"%u < %u\n", rc, msgb_length(msg));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcu_l1if_open()
|
||||
{
|
||||
struct femtol1_hdl *fl1h;
|
||||
int rc;
|
||||
|
||||
/* allocate new femtol1_handle */
|
||||
fl1h = talloc_zero(tall_pcu_ctx, struct femtol1_hdl);
|
||||
INIT_LLIST_HEAD(&fl1h->wlc_list);
|
||||
|
||||
l1fh->fl1h = fl1h;
|
||||
fl1h->priv = l1fh;
|
||||
|
||||
struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
|
||||
osmo_wqueue_init(queue, 10);
|
||||
queue->bfd.when |= BSC_FD_READ;
|
||||
queue->bfd.data = l1fh;
|
||||
queue->bfd.priv_nr = 0;
|
||||
|
||||
/* Open UDP */
|
||||
struct osmo_wqueue *wq = &l1fh->udp_wq;
|
||||
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = udp_write_cb;
|
||||
wq->read_cb = udp_read_cb;
|
||||
wq->bfd.when |= BSC_FD_READ;
|
||||
wq->bfd.data = l1fh;
|
||||
wq->bfd.priv_nr = 0;
|
||||
rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
|
||||
IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
|
||||
OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
perror("sock_init");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pcu_l1if_close(void)
|
||||
{
|
||||
gprs_bssgp_destroy();
|
||||
|
||||
/* FIXME: cleanup l1if */
|
||||
talloc_free(l1fh->fl1h);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/* sysmo_sock.cpp
|
||||
/* osmobts_sock.cpp
|
||||
*
|
||||
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
|
@ -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;
|
||||
|
||||
|
@ -44,7 +46,7 @@ int l1if_close_pdch(void *obj);
|
|||
}
|
||||
|
||||
/*
|
||||
* SYSMO-PCU socket functions
|
||||
* osmo-bts PCU socket functions
|
||||
*/
|
||||
|
||||
struct pcu_sock_state {
|
||||
|
@ -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");
|
||||
|
@ -99,28 +100,20 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
|
|||
|
||||
/* disable all slots, kick all TBFs */
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
if (bts->trx[trx].fl1h) {
|
||||
l1if_close_pdch(bts->trx[trx].fl1h);
|
||||
bts->trx[trx].fl1h = NULL;
|
||||
}
|
||||
#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)
|
||||
|
@ -231,6 +224,8 @@ int pcu_l1if_open(void)
|
|||
unsigned int namelen;
|
||||
int rc;
|
||||
|
||||
LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface to OsmoBTS\n");
|
||||
|
||||
state = pcu_sock_state;
|
||||
if (!state) {
|
||||
state = talloc_zero(tall_pcu_ctx, struct pcu_sock_state);
|
||||
|
@ -243,7 +238,7 @@ int pcu_l1if_open(void)
|
|||
|
||||
bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (bfd->fd < 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
|
||||
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU socket.\n");
|
||||
talloc_free(state);
|
||||
return -1;
|
||||
}
|
||||
|
@ -265,8 +260,8 @@ int pcu_l1if_open(void)
|
|||
#endif
|
||||
rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
|
||||
if (rc != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
|
||||
"socket, delaying... '%s'\n", local.sun_path);
|
||||
LOGP(DL1IF, LOGL_ERROR, "Failed to connect to the osmo-bts"
|
||||
" PCU socket, delaying... '%s'\n", local.sun_path);
|
||||
pcu_sock_state = state;
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
|
@ -287,7 +282,7 @@ int pcu_l1if_open(void)
|
|||
return rc;
|
||||
}
|
||||
|
||||
LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
|
||||
LOGP(DL1IF, LOGL_NOTICE, "osmo-bts PCU socket has been connected\n");
|
||||
|
||||
pcu_sock_state = state;
|
||||
|
||||
|
@ -302,8 +297,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)
|
|
@ -25,10 +25,13 @@
|
|||
#include <assert.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <arpa/inet.h>
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
}
|
||||
|
||||
#include <gprs_rlcmac.h>
|
||||
|
@ -36,11 +39,12 @@ 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" {
|
||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1);
|
||||
int l1if_close_pdch(void *obj);
|
||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap);
|
||||
int l1if_connect_pdch(void *obj, uint8_t ts);
|
||||
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
|
||||
|
@ -48,19 +52,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
|
||||
*/
|
||||
|
@ -135,15 +126,18 @@ static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
|
|||
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)
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
if (bts->trx[trx].fl1h) {
|
||||
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
|
||||
msg->data, msg->len);
|
||||
else
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
|
||||
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
|
||||
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
|
||||
msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
@ -151,15 +145,18 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|||
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)
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
if (bts->trx[trx].fl1h) {
|
||||
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
|
||||
msg->data, msg->len);
|
||||
else
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
|
||||
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
|
||||
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
|
||||
msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
@ -174,7 +171,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,31 +188,49 @@ 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)
|
||||
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);
|
||||
/* access bursts may arrive some bursts earlier */
|
||||
BTS::main_bts()->set_current_block_frame_number(fn, 5);
|
||||
}
|
||||
|
||||
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
|
||||
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)
|
||||
{
|
||||
int rc = 0;
|
||||
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, struct gsmtap_inst *gsmtap)
|
||||
{
|
||||
int rc;
|
||||
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,
|
||||
data_ind->arfcn, data_ind->block_nr,
|
||||
osmo_hexdump(data_ind->data, data_ind->len));
|
||||
|
||||
rc = gsmtap_send(gsmtap, data_ind->arfcn | GSMTAP_ARFCN_F_UPLINK, data_ind->ts_nr,
|
||||
GSMTAP_CHANNEL_PACCH, 0, data_ind->fn, 0, 0, data_ind->data, data_ind->len);
|
||||
if (rc < 0)
|
||||
LOGP(DL1IF, LOGL_ERROR, "Sending RX data via GSMTAP failed: %d\n", rc);
|
||||
|
||||
rc = 0;
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
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->data, data_ind->len, data_ind->fn,
|
||||
&meas);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
|
||||
|
@ -236,8 +251,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 "
|
||||
|
@ -252,7 +266,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)
|
||||
|
@ -296,7 +311,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:
|
||||
|
@ -308,40 +324,12 @@ 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;
|
||||
int trx, ts;
|
||||
int i;
|
||||
|
@ -361,14 +349,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);
|
||||
|
@ -419,13 +404,15 @@ bssgp_failed:
|
|||
LOGP(DL1IF, LOGL_DEBUG, " nsvci=%d\n", info_ind->nsvci[0]);
|
||||
LOGP(DL1IF, LOGL_DEBUG, " local_port=%d\n", info_ind->local_port[0]);
|
||||
LOGP(DL1IF, LOGL_DEBUG, " remote_port=%d\n", info_ind->remote_port[0]);
|
||||
LOGP(DL1IF, LOGL_DEBUG, " remote_ip=%d\n", info_ind->remote_ip[0]);
|
||||
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->remote_ip[0], info_ind->remote_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;
|
||||
}
|
||||
|
@ -464,7 +451,8 @@ bssgp_failed:
|
|||
if (!bts->trx[trx].fl1h)
|
||||
bts->trx[trx].fl1h = l1if_open_pdch(
|
||||
(void *)trx,
|
||||
info_ind->trx[trx].hlayer1);
|
||||
info_ind->trx[trx].hlayer1,
|
||||
bts->gsmtap);
|
||||
if (!bts->trx[trx].fl1h) {
|
||||
LOGP(DL1IF, LOGL_FATAL, "Failed to open direct "
|
||||
"DSP access for PDCH.\n");
|
||||
|
@ -476,14 +464,13 @@ bssgp_failed:
|
|||
"BTS. Please deactivate it!\n");
|
||||
exit(0);
|
||||
#endif
|
||||
#warning close TBD
|
||||
}
|
||||
|
||||
for (ts = 0; ts < 8; ts++) {
|
||||
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))
|
||||
|
@ -491,17 +478,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -512,8 +498,6 @@ bssgp_failed:
|
|||
|
||||
static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
|
||||
{
|
||||
struct gprs_rlcmac_tbf *tbf;
|
||||
uint32_t elapsed;
|
||||
uint8_t fn13 = time_ind->fn % 13;
|
||||
|
||||
/* omit frame numbers not starting at a MAC block */
|
||||
|
@ -523,24 +507,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 - tbf->poll_fn) % 2715648;
|
||||
if (elapsed >= 20 && elapsed < 200)
|
||||
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 - tbf->poll_fn) % 2715648;
|
||||
if (elapsed >= 20 && elapsed < 200)
|
||||
gprs_rlcmac_poll_timeout(tbf);
|
||||
}
|
||||
}
|
||||
|
||||
BTS::main_bts()->set_current_frame_number(time_ind->fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -549,17 +516,18 @@ 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);
|
||||
}
|
||||
|
||||
int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
|
||||
{
|
||||
int rc = 0;
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
switch (msg_type) {
|
||||
case PCU_IF_MSG_DATA_IND:
|
||||
rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
|
||||
rc = pcu_rx_data_ind(&pcu_prim->u.data_ind, bts->gsmtap);
|
||||
break;
|
||||
case PCU_IF_MSG_DATA_CNF:
|
||||
rc = pcu_rx_data_cnf(&pcu_prim->u.data_cnf);
|
||||
|
|
|
@ -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);
|
||||
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
|
||||
|
|
|
@ -26,25 +26,27 @@
|
|||
#include <unistd.h>
|
||||
#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>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/gsmtap_util.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;
|
||||
|
||||
#ifdef DEBUG_DIAGRAM
|
||||
extern struct timeval diagram_time;
|
||||
#endif
|
||||
static int rt_prio = -1;
|
||||
static char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
|
@ -56,6 +58,10 @@ static void print_help()
|
|||
"provided by BTS\n"
|
||||
" -n --mnc MNC use given MNC instead of value "
|
||||
"provided by BTS\n"
|
||||
" -V --version print version\n"
|
||||
" -r --realtime PRIO Use SCHED_RR with the specified "
|
||||
"priority\n"
|
||||
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -70,10 +76,13 @@ static void handle_options(int argc, char **argv)
|
|||
{ "mcc", 1, 0, 'm' },
|
||||
{ "mnc", 1, 0, 'n' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "realtime", 1, 0, 'r' },
|
||||
{ "exit", 0, 0, 'e' },
|
||||
{ "gsmtap-ip", 1, 0, 'i' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:m:n:V",
|
||||
c = getopt_long(argc, argv, "hc:m:n:Vr:e:i:",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -84,6 +93,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;
|
||||
|
@ -97,6 +107,15 @@ static void handle_options(int argc, char **argv)
|
|||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
case 'i':
|
||||
gsmtap_addr = optarg;
|
||||
break;
|
||||
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);
|
||||
|
@ -114,6 +133,7 @@ void sighandler(int sigset)
|
|||
|
||||
switch (sigset) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
/* If another signal is received afterwards, the program
|
||||
* is terminated without finishing shutdown process.
|
||||
*/
|
||||
|
@ -140,6 +160,7 @@ void sighandler(int sigset)
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct sched_param param;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
int rc;
|
||||
|
||||
|
@ -148,12 +169,10 @@ 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->initial_mcs_dl = bts->initial_mcs_ul = 1;
|
||||
bts->cs1 = 1;
|
||||
bts->t3142 = 20;
|
||||
bts->t3169 = 5;
|
||||
|
@ -163,11 +182,40 @@ int main(int argc, char *argv[])
|
|||
bts->n3101 = 10;
|
||||
bts->n3103 = 4;
|
||||
bts->n3105 = 8;
|
||||
bts->alpha = 10; /* a = 1.0 */
|
||||
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;
|
||||
bts->max_mcs_ul = 4;
|
||||
bts->max_mcs_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;
|
||||
|
||||
/* TODO: increase them when CRBB decoding is implemented */
|
||||
bts->ws_base = 64;
|
||||
bts->ws_pdch = 0;
|
||||
|
||||
bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
|
||||
bts->dl_tbf_idle_msec = 2000;
|
||||
bts->llc_idle_ack_csec = 10;
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
osmo_stats_init(tall_pcu_ctx);
|
||||
gprs_ns_set_log_ss(DNS);
|
||||
bssgp_set_log_ss(DBSSGP);
|
||||
|
||||
vty_init(&pcu_vty_info);
|
||||
pcu_vty_init(&gprs_log_info);
|
||||
|
@ -179,6 +227,13 @@ int main(int argc, char *argv[])
|
|||
exit(0);
|
||||
}
|
||||
|
||||
bts->gsmtap = gsmtap_source_init(gsmtap_addr, GSMTAP_UDP_PORT, 1);
|
||||
|
||||
if (bts->gsmtap)
|
||||
gsmtap_source_add_sink(bts->gsmtap);
|
||||
else
|
||||
fprintf(stderr, "Failed to initialize GSMTAP for %s\n", gsmtap_addr);
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0 && config_given) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n",
|
||||
|
@ -196,7 +251,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();
|
||||
|
||||
|
@ -211,23 +266,30 @@ int main(int argc, char *argv[])
|
|||
signal(SIGUSR1, sighandler);
|
||||
signal(SIGUSR2, sighandler);
|
||||
|
||||
/* enable realtime priority for us */
|
||||
if (rt_prio != -1) {
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = rt_prio;
|
||||
rc = sched_setscheduler(getpid(), SCHED_RR, ¶m);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Setting SCHED_RR priority(%d) failed: %s\n",
|
||||
param.sched_priority, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
while (!quit) {
|
||||
osmo_gsm_timers_check();
|
||||
osmo_gsm_timers_prepare();
|
||||
osmo_gsm_timers_update();
|
||||
|
||||
osmo_select_main(0);
|
||||
#ifdef DEBUG_DIAGRAM
|
||||
gettimeofday(&diagram_time, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
telnet_exit();
|
||||
|
||||
pcu_l1if_close();
|
||||
|
||||
talloc_free(gprs_rlcmac_bts);
|
||||
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
talloc_free(tall_pcu_ctx);
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline unsigned int pcu_bitcount(T x)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
for (count = 0; x; count += 1)
|
||||
x &= x - 1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T pcu_lsb(T x)
|
||||
{
|
||||
return x & -x;
|
||||
}
|
798
src/pcu_vty.c
798
src/pcu_vty.c
|
@ -2,12 +2,20 @@
|
|||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.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 +45,124 @@ 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);
|
||||
if (bts->egprs_enabled)
|
||||
vty_out(vty, " egprs only%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->initial_mcs_dl != 1 && bts->initial_mcs_ul != 1) {
|
||||
if (bts->initial_mcs_ul == bts->initial_mcs_dl)
|
||||
vty_out(vty, " mcs %d%s", bts->initial_mcs_dl,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " mcs %d %d%s", bts->initial_mcs_dl,
|
||||
bts->initial_mcs_ul, VTY_NEWLINE);
|
||||
}
|
||||
if (bts->max_mcs_dl && bts->max_mcs_ul) {
|
||||
if (bts->max_mcs_ul == bts->max_mcs_dl)
|
||||
vty_out(vty, " mcs max %d%s", bts->max_mcs_dl,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " mcs max %d %d%s", bts->max_mcs_dl,
|
||||
bts->max_mcs_ul, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
vty_out(vty, " window-size %d %d%s", bts->ws_base, bts->ws_pdch,
|
||||
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 */
|
||||
|
@ -115,26 +176,185 @@ DEFUN(cfg_pcu,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define EGPRS_STR "EGPRS configuration\n"
|
||||
|
||||
DEFUN(cfg_pcu_egprs,
|
||||
cfg_pcu_egprs_cmd,
|
||||
"egprs only",
|
||||
EGPRS_STR "Use EGPRS and disable plain GPRS\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->egprs_enabled = 1;
|
||||
|
||||
vty_out(vty, "%%Note that EGPRS support is in an experimental state "
|
||||
"and the PCU will currently fail to use a TBF if the MS is capable "
|
||||
"to do EGPRS. You may want to disable this feature by entering "
|
||||
"the \"no egprs\" command. "
|
||||
"Do not use this in production!%s", VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_egprs,
|
||||
cfg_pcu_no_egprs_cmd,
|
||||
"no egprs",
|
||||
NO_STR EGPRS_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->egprs_enabled = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_fc_interval,
|
||||
cfg_pcu_fc_interval_cmd,
|
||||
"flow-control-interval <1-10>",
|
||||
"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 +370,136 @@ 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;
|
||||
}
|
||||
|
||||
#define CS_MAX_STR "Set maximum values for adaptive CS selection (overrides BTS config)\n"
|
||||
DEFUN(cfg_pcu_cs_max,
|
||||
cfg_pcu_cs_max_cmd,
|
||||
"cs max <1-4> [<1-4>]",
|
||||
CS_STR
|
||||
CS_MAX_STR
|
||||
"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 CS_MAX_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->max_cs_dl = 0;
|
||||
bts->max_cs_ul = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define MCS_STR "Modulation and Coding Scheme configuration (EGPRS)\n"
|
||||
|
||||
DEFUN(cfg_pcu_mcs,
|
||||
cfg_pcu_mcs_cmd,
|
||||
"mcs <1-9> [<1-9>]",
|
||||
MCS_STR
|
||||
"Initial MCS value to be used (default 1)\n"
|
||||
"Use a different initial MCS value for the uplink")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint8_t cs = atoi(argv[0]);
|
||||
|
||||
bts->initial_mcs_dl = cs;
|
||||
if (argc > 1)
|
||||
bts->initial_mcs_ul = atoi(argv[1]);
|
||||
else
|
||||
bts->initial_mcs_ul = cs;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_mcs,
|
||||
cfg_pcu_no_mcs_cmd,
|
||||
"no mcs",
|
||||
NO_STR MCS_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->initial_mcs_dl = 1;
|
||||
bts->initial_mcs_ul = 1;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_mcs_max,
|
||||
cfg_pcu_mcs_max_cmd,
|
||||
"mcs max <1-9> [<1-9>]",
|
||||
MCS_STR
|
||||
CS_MAX_STR
|
||||
"Maximum MCS value to be used\n"
|
||||
"Use a different maximum MCS value for the uplink")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint8_t mcs = atoi(argv[0]);
|
||||
|
||||
bts->max_mcs_dl = mcs;
|
||||
if (argc > 1)
|
||||
bts->max_mcs_ul = atoi(argv[1]);
|
||||
else
|
||||
bts->max_mcs_ul = mcs;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_mcs_max,
|
||||
cfg_pcu_no_mcs_max_cmd,
|
||||
"no mcs max",
|
||||
NO_STR MCS_STR CS_MAX_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->max_mcs_dl = 0;
|
||||
bts->max_mcs_ul = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_window_size,
|
||||
cfg_pcu_window_size_cmd,
|
||||
"window-size <0-1024> [<0-256>]",
|
||||
"Window size configuration (b + N_PDCH * f)\n"
|
||||
"Base value (b)\n"
|
||||
"Factor for number of PDCH (f)")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
uint16_t b = atoi(argv[0]);
|
||||
|
||||
bts->ws_base = b;
|
||||
if (argc > 1) {
|
||||
uint16_t f = atoi(argv[1]);
|
||||
bts->ws_pdch = f;
|
||||
}
|
||||
|
||||
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 +509,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 +522,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 +535,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 +656,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 +669,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 +681,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 +695,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 +708,214 @@ 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();
|
||||
return pcu_vty_show_tbf_all(vty, bts);
|
||||
}
|
||||
|
||||
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"
|
||||
|
@ -293,22 +936,63 @@ int pcu_vty_init(const struct log_info *cat)
|
|||
// install_element_ve(&show_pcu_cmd);
|
||||
|
||||
logging_vty_add_cmds(cat);
|
||||
osmo_stats_vty_add_cmds(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_egprs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_egprs_cmd);
|
||||
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_mcs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_window_size_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);
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/* 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;
|
||||
}
|
||||
|
||||
static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
|
||||
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
|
||||
|
||||
vty_out(vty, "TBF: TFI=%d TLLI=0x%08x (%s) DIR=%s IMSI=%s%s", tbf->tfi(),
|
||||
tbf->tlli(), tbf->is_tlli_valid() ? "valid" : "invalid",
|
||||
tbf->direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL",
|
||||
tbf->imsi(), VTY_NEWLINE);
|
||||
vty_out(vty, " created=%lu state=%08x 1st_TS=%d 1st_cTS=%d ctrl_TS=%d "
|
||||
"MS_CLASS=%d/%d%s",
|
||||
tbf->created_ts(), tbf->state_flags, tbf->first_ts,
|
||||
tbf->first_common_ts, tbf->control_ts,
|
||||
tbf->ms_class(),
|
||||
tbf->ms() ? tbf->ms()->egprs_ms_class() : -1,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " TS_alloc=");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bool is_ctrl = tbf->is_control_ts(i);
|
||||
if (tbf->pdch[i])
|
||||
vty_out(vty, "%d%s ", i, is_ctrl ? "!" : "");
|
||||
}
|
||||
vty_out(vty, " CS=%s WS=%d",
|
||||
tbf->current_cs().name(), tbf->window()->ws());
|
||||
|
||||
if (ul_tbf) {
|
||||
gprs_rlc_ul_window *win = &ul_tbf->m_window;
|
||||
vty_out(vty, " V(Q)=%d V(R)=%d",
|
||||
win->v_q(), win->v_r());
|
||||
}
|
||||
if (dl_tbf) {
|
||||
gprs_rlc_dl_window *win = &dl_tbf->m_window;
|
||||
vty_out(vty, " V(A)=%d V(S)=%d nBSN=%d%s",
|
||||
win->v_a(), win->v_s(), win->resend_needed(),
|
||||
win->window_stalled() ? " STALLED" : "");
|
||||
}
|
||||
vty_out(vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
|
||||
{
|
||||
BTS *bts = bts_data->bts;
|
||||
LListHead<gprs_rlcmac_tbf> *ms_iter;
|
||||
|
||||
vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
|
||||
llist_for_each(ms_iter, &bts->ul_tbfs())
|
||||
tbf_print_vty_info(vty, ms_iter->entry());
|
||||
|
||||
vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
|
||||
llist_for_each(ms_iter, &bts->dl_tbfs())
|
||||
tbf_print_vty_info(vty, ms_iter->entry());
|
||||
|
||||
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=%s, CS-DL=%s, LLC=%d, "
|
||||
"IMSI=%s%s",
|
||||
ms->tlli(),
|
||||
ms->ta(), ms->current_cs_ul().name(),
|
||||
ms->current_cs_dl().name(),
|
||||
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;
|
||||
uint8_t slots;
|
||||
|
||||
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: %s%s", ms->current_cs_ul().name(),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Coding scheme downlink: %s%s", ms->current_cs_dl().name(),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Mode: %s%s",
|
||||
GprsCodingScheme::modeName(ms->mode()), VTY_NEWLINE);
|
||||
vty_out(vty, " MS class: %d%s", ms->ms_class(), VTY_NEWLINE);
|
||||
vty_out(vty, " EGPRS MS class: %d%s", ms->egprs_ms_class(), VTY_NEWLINE);
|
||||
vty_out(vty, " PACCH: ");
|
||||
slots = ms->current_pacch_slots();
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (slots & (1 << i))
|
||||
vty_out(vty, "%d ", i);
|
||||
vty_out(vty, "%s", 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);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* 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_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
|
||||
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
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef _PCUIF_PROTO_H
|
||||
#define _PCUIF_PROTO_H
|
||||
|
||||
#define PCU_IF_VERSION 0x04
|
||||
#define PCU_IF_VERSION 0x05
|
||||
|
||||
/* msg_type */
|
||||
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
|
||||
|
@ -49,6 +49,7 @@ struct gsm_pcu_if_data {
|
|||
uint8_t trx_nr;
|
||||
uint8_t ts_nr;
|
||||
uint8_t block_nr;
|
||||
int8_t rssi;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_rts_req {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/* 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_dl_tbf *dl_tbf;
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
struct gprs_rlcmac_sba *sba, *sba2;
|
||||
LListHead<gprs_rlcmac_tbf> *pos;
|
||||
uint32_t elapsed;
|
||||
|
||||
llist_for_each(pos, &m_bts.ul_tbfs()) {
|
||||
ul_tbf = as_ul_tbf(pos->entry());
|
||||
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_for_each(pos, &m_bts.dl_tbfs()) {
|
||||
dl_tbf = as_dl_tbf(pos->entry());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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&);
|
||||
};
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
#include <errno.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 = mod_sns(bsn + 1)) {
|
||||
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 = mod_sns(bsn + 1)) {
|
||||
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 = mod_sns(bsn + 1)) {
|
||||
if (!m_v_b.is_acked(bsn))
|
||||
unacked += 1;
|
||||
}
|
||||
|
||||
return unacked;
|
||||
}
|
||||
|
||||
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
|
||||
{
|
||||
return (ssn - 1 - bitnum);
|
||||
}
|
||||
|
||||
void gprs_rlc_dl_window::update(BTS *bts, const struct bitvec *rbb,
|
||||
uint16_t first_bsn, uint16_t *lost,
|
||||
uint16_t *received)
|
||||
{
|
||||
unsigned num_blocks = rbb->cur_bit;
|
||||
unsigned bsn;
|
||||
|
||||
/* first_bsn is in range V(A)..V(S) */
|
||||
|
||||
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
|
||||
bool is_ack;
|
||||
bsn = mod_sns(first_bsn + bitpos);
|
||||
if (bsn == mod_sns(v_a() - 1))
|
||||
break;
|
||||
|
||||
is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
|
||||
|
||||
if (is_ack) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint16_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 = mod_sns(bitnum_to_bsn(bitpos, ssn));
|
||||
|
||||
if (bsn == mod_sns(v_a() - 1))
|
||||
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 = mod_sns(bsn + 1)) {
|
||||
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 = mod_sns(bsn + 1)) {
|
||||
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;
|
||||
}
|
||||
|
||||
void gprs_rlc_window::set_sns(uint16_t sns)
|
||||
{
|
||||
OSMO_ASSERT(sns >= RLC_GPRS_SNS);
|
||||
OSMO_ASSERT(sns <= RLC_MAX_SNS);
|
||||
/* check for 2^n */
|
||||
OSMO_ASSERT((sns & (-sns)) == sns);
|
||||
m_sns = sns;
|
||||
}
|
||||
|
||||
void gprs_rlc_window::set_ws(uint16_t ws)
|
||||
{
|
||||
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
|
||||
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
|
||||
m_ws = ws;
|
||||
}
|
||||
|
||||
/* 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 = mod_sns(bsn + 1 - v_r());
|
||||
/* 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(), mod_sns(v_q() + 1));
|
||||
raise_v_q(1);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
|
||||
{
|
||||
m_v_n.mark_received(bsn);
|
||||
raise_v_r(bsn);
|
||||
}
|
||||
|
||||
bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
|
||||
{
|
||||
bool was_valid = m_v_n.is_received(bsn);
|
||||
m_v_n.mark_missing(bsn);
|
||||
|
||||
return was_valid;
|
||||
}
|
||||
|
||||
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding, unsigned int header_bits)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int padding_bits = with_padding ? cs.optionalPaddingBits() : 0;
|
||||
|
||||
memset(rlc, 0, sizeof(*rlc));
|
||||
|
||||
rlc->cs = cs;
|
||||
rlc->with_padding = with_padding;
|
||||
rlc->num_data_blocks = cs.numDataBlocks();
|
||||
|
||||
OSMO_ASSERT(rlc->num_data_blocks <= ARRAY_SIZE(rlc->block_info));
|
||||
|
||||
for (i = 0; i < rlc->num_data_blocks; i++) {
|
||||
gprs_rlc_data_block_info_init(&rlc->block_info[i], cs,
|
||||
with_padding);
|
||||
|
||||
rlc->data_offs_bits[i] =
|
||||
header_bits + padding_bits +
|
||||
(i+1) * cs.numDataBlockHeaderBits() +
|
||||
i * 8 * rlc->block_info[0].data_len;
|
||||
}
|
||||
}
|
||||
|
||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
{
|
||||
return gprs_rlc_data_header_init(rlc, cs, with_padding,
|
||||
cs.numDataHeaderBitsDL());
|
||||
}
|
||||
|
||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
{
|
||||
return gprs_rlc_data_header_init(rlc, cs, with_padding,
|
||||
cs.numDataHeaderBitsUL());
|
||||
}
|
||||
|
||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
{
|
||||
unsigned int data_len = cs.maxDataBlockBytes();
|
||||
if (with_padding)
|
||||
data_len -= cs.optionalPaddingBits() / 8;
|
||||
|
||||
rdbi->data_len = data_len;
|
||||
rdbi->bsn = 0;
|
||||
rdbi->ti = 0;
|
||||
rdbi->e = 1;
|
||||
rdbi->cv = 15;
|
||||
rdbi->pi = 0;
|
||||
rdbi->spb = 0;
|
||||
}
|
||||
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
|
||||
int with_padding)
|
||||
{
|
||||
switch (GprsCodingScheme::Scheme(cs)) {
|
||||
case GprsCodingScheme::MCS1: return 0b1011 + punct % 2;
|
||||
case GprsCodingScheme::MCS2: return 0b1001 + punct % 2;
|
||||
case GprsCodingScheme::MCS3: return (with_padding ? 0b0110 : 0b0011) +
|
||||
punct % 3;
|
||||
case GprsCodingScheme::MCS4: return 0b0000 + punct % 3;
|
||||
case GprsCodingScheme::MCS5: return 0b100 + punct % 2;
|
||||
case GprsCodingScheme::MCS6: return (with_padding ? 0b010 : 0b000) +
|
||||
punct % 2;
|
||||
case GprsCodingScheme::MCS7: return 0b10100 + 3 * (punct % 3) + punct2 % 3;
|
||||
case GprsCodingScheme::MCS8: return 0b01011 + 3 * (punct % 3) + punct2 % 3;
|
||||
case GprsCodingScheme::MCS9: return 0b00000 + 4 * (punct % 3) + punct2 % 3;
|
||||
default: ;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void gprs_rlc_mcs_cps_decode(unsigned int cps,
|
||||
GprsCodingScheme cs, int *punct, int *punct2, int *with_padding)
|
||||
{
|
||||
*punct2 = -1;
|
||||
*with_padding = 0;
|
||||
|
||||
switch (GprsCodingScheme::Scheme(cs)) {
|
||||
case GprsCodingScheme::MCS1:
|
||||
cps -= 0b1011; *punct = cps % 2; break;
|
||||
case GprsCodingScheme::MCS2:
|
||||
cps -= 0b1001; *punct = cps % 2; break;
|
||||
case GprsCodingScheme::MCS3:
|
||||
cps -= 0b0011; *punct = cps % 3; *with_padding = cps >= 3; break;
|
||||
case GprsCodingScheme::MCS4:
|
||||
cps -= 0b0000; *punct = cps % 3; break;
|
||||
case GprsCodingScheme::MCS5:
|
||||
cps -= 0b100; *punct = cps % 2; break;
|
||||
case GprsCodingScheme::MCS6:
|
||||
cps -= 0b000; *punct = cps % 2; *with_padding = cps >= 2; break;
|
||||
case GprsCodingScheme::MCS7:
|
||||
cps -= 0b10100; *punct = cps / 3; *punct2 = cps % 3; break;
|
||||
case GprsCodingScheme::MCS8:
|
||||
cps -= 0b01011; *punct = cps / 3; *punct2 = cps % 3; break;
|
||||
case GprsCodingScheme::MCS9:
|
||||
cps -= 0b00000; *punct = cps / 4; *punct2 = cps % 3; break;
|
||||
default: ;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,577 @@
|
|||
/* 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 "gprs_coding_scheme.h"
|
||||
|
||||
#include <osmocom/core/endian.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
|
||||
#define RLC_GPRS_WS 64 /* max window size */
|
||||
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
|
||||
#define RLC_EGPRS_MIN_WS 64 /* min window size */
|
||||
#define RLC_EGPRS_MAX_WS 1024 /* min window size */
|
||||
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
|
||||
#define RLC_EGPRS_MAX_BSN_DELTA 512
|
||||
#define RLC_MAX_SNS RLC_EGPRS_SNS
|
||||
#define RLC_MAX_WS RLC_EGPRS_MAX_WS
|
||||
#define RLC_MAX_LEN 74 /* MCS-9 data unit */
|
||||
|
||||
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_block_info {
|
||||
unsigned int data_len; /* EGPRS: N2, GPRS: N2-2, N-2 */
|
||||
unsigned int bsn;
|
||||
unsigned int ti;
|
||||
unsigned int e;
|
||||
unsigned int cv; /* FBI == 1 <=> CV == 0 */
|
||||
unsigned int pi;
|
||||
unsigned int spb;
|
||||
};
|
||||
|
||||
struct gprs_rlc_data_info {
|
||||
GprsCodingScheme cs;
|
||||
unsigned int r;
|
||||
unsigned int si;
|
||||
unsigned int tfi;
|
||||
unsigned int cps;
|
||||
unsigned int rsb;
|
||||
unsigned int usf;
|
||||
unsigned int es_p;
|
||||
unsigned int rrbp;
|
||||
unsigned int pr;
|
||||
unsigned int num_data_blocks;
|
||||
unsigned int with_padding;
|
||||
unsigned int data_offs_bits[2];
|
||||
struct gprs_rlc_data_block_info block_info[2];
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
struct gprs_rlc_data_block_info block_info;
|
||||
GprsCodingScheme cs;
|
||||
};
|
||||
|
||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
|
||||
int with_padding);
|
||||
void gprs_rlc_mcs_cps_decode(unsigned int cps, GprsCodingScheme cs,
|
||||
int *punct, int *punct2, int *with_padding);
|
||||
|
||||
/*
|
||||
* 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 base class.
|
||||
*/
|
||||
class gprs_rlc_window {
|
||||
public:
|
||||
gprs_rlc_window();
|
||||
|
||||
const uint16_t mod_sns() const;
|
||||
const uint16_t mod_sns(uint16_t bsn) const;
|
||||
const uint16_t sns() const;
|
||||
const uint16_t ws() const;
|
||||
|
||||
void set_sns(uint16_t sns);
|
||||
void set_ws(uint16_t ws);
|
||||
|
||||
protected:
|
||||
uint16_t m_sns;
|
||||
uint16_t m_ws;
|
||||
};
|
||||
|
||||
struct gprs_rlc_dl_window: public gprs_rlc_window {
|
||||
void reset();
|
||||
|
||||
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, uint16_t ssn,
|
||||
uint16_t *lost, uint16_t *received);
|
||||
void update(BTS *bts, const struct bitvec *rbb,
|
||||
uint16_t first_bsn, 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;
|
||||
|
||||
gprs_rlc_dl_window();
|
||||
};
|
||||
|
||||
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: public gprs_rlc_window {
|
||||
const uint16_t v_r() const;
|
||||
const uint16_t v_q() const;
|
||||
|
||||
const uint16_t ssn() const;
|
||||
|
||||
bool is_in_window(uint16_t bsn) const;
|
||||
bool is_received(uint16_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);
|
||||
|
||||
void receive_bsn(const uint16_t bsn);
|
||||
bool invalidate_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;
|
||||
|
||||
gprs_rlc_ul_window();
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
/* TS 04.60 10.2.2 */
|
||||
#if OSMO_IS_LITTLE_ENDIAN
|
||||
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));
|
||||
|
||||
struct rlc_li_field_egprs {
|
||||
uint8_t e:1,
|
||||
li:7;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_ul_header_egprs_3 {
|
||||
uint8_t r:1,
|
||||
si:1,
|
||||
cv:4,
|
||||
tfi_a:2;
|
||||
uint8_t tfi_b:3,
|
||||
bsn1_a:5;
|
||||
uint8_t bsn1_b:6,
|
||||
cps_a:2;
|
||||
uint8_t cps_b:2,
|
||||
spb:2,
|
||||
rsb:1,
|
||||
pi:1,
|
||||
spare:1,
|
||||
dummy:1;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_1 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
bsn2_a:7;
|
||||
uint8_t bsn2_b:3,
|
||||
cps:5;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_2 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
cps:3,
|
||||
dummy:4;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_3 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
cps:4,
|
||||
spb:2,
|
||||
dummy:1;
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
# error "Only little endian headers are supported yet. TODO: add missing structs"
|
||||
#endif
|
||||
}
|
||||
|
||||
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 gprs_rlc_window::gprs_rlc_window()
|
||||
: m_sns(RLC_GPRS_SNS)
|
||||
, m_ws(RLC_GPRS_WS)
|
||||
{
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_window::sns() const
|
||||
{
|
||||
return m_sns;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_window::ws() const
|
||||
{
|
||||
return m_ws;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_window::mod_sns() const
|
||||
{
|
||||
return sns() - 1;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
|
||||
{
|
||||
return bsn & mod_sns();
|
||||
}
|
||||
|
||||
inline gprs_rlc_dl_window::gprs_rlc_dl_window()
|
||||
: m_v_s(0)
|
||||
, m_v_a(0)
|
||||
{
|
||||
}
|
||||
|
||||
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 mod_sns(m_v_s + offset);
|
||||
}
|
||||
|
||||
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 (mod_sns(m_v_s - m_v_a)) == 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 gprs_rlc_ul_window::gprs_rlc_ul_window()
|
||||
: m_v_r(0)
|
||||
, m_v_q(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool gprs_rlc_ul_window::is_in_window(uint16_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 bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
|
||||
{
|
||||
uint16_t offset_v_r;
|
||||
|
||||
/* Offset to the end of the received window */
|
||||
offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
|
||||
return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
|
||||
}
|
||||
|
||||
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 = mod_sns(m_v_r + moves);
|
||||
}
|
||||
|
||||
inline void gprs_rlc_ul_window::raise_v_q(int incr)
|
||||
{
|
||||
m_v_q = mod_sns(m_v_q + incr);
|
||||
}
|
||||
|
||||
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()];
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,47 @@ 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));
|
||||
|
||||
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:
|
||||
|
@ -190,7 +218,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
|||
pcu_rx_data_ind_pdtch((long)fl1h->priv, data_ind->u8Tn,
|
||||
data_ind->msgUnitParam.u8Buffer + 1,
|
||||
data_ind->msgUnitParam.u8Size - 1,
|
||||
data_ind->u32Fn);
|
||||
data_ind->u32Fn,
|
||||
&meas);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
|
@ -210,6 +239,8 @@ static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
|
|||
{
|
||||
uint8_t acc_delay;
|
||||
|
||||
pcu_rx_ra_time(ra_ind->u16Arfcn, ra_ind->u32Fn, ra_ind->u8Tn);
|
||||
|
||||
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
||||
return 0;
|
||||
|
||||
|
@ -220,6 +251,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",
|
||||
|
@ -312,12 +349,15 @@ 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;
|
||||
}
|
||||
|
||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1)
|
||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
|
||||
{
|
||||
struct femtol1_hdl *fl1h;
|
||||
int rc;
|
||||
|
@ -338,9 +378,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
|
||||
if (fl1h->gsmtap)
|
||||
gsmtap_source_add_sink(fl1h->gsmtap);
|
||||
fl1h->gsmtap = gsmtap;
|
||||
|
||||
return fl1h;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* 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 DL transmission */
|
||||
GPRS_RLCMAC_WAIT_ASSIGN,/* wait for confirmation */
|
||||
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 {
|
||||
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, uint8_t ts);
|
||||
struct msgb *create_ul_ass(uint32_t fn, uint8_t ts);
|
||||
|
||||
GprsMs *ms() const;
|
||||
void set_ms(GprsMs *ms);
|
||||
|
||||
gprs_rlc_window *window();
|
||||
|
||||
uint8_t tsc() const;
|
||||
|
||||
int rlcmac_diag();
|
||||
|
||||
int update();
|
||||
void handle_timeout();
|
||||
void stop_timer();
|
||||
void stop_t3191();
|
||||
int establish_dl_tbf_on_pacch();
|
||||
|
||||
int check_polling(uint32_t fn, uint8_t ts,
|
||||
uint32_t *poll_fn, unsigned int *rrbp);
|
||||
void set_polling(uint32_t poll_fn, uint8_t ts);
|
||||
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;
|
||||
bool is_tfi_assigned() 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);
|
||||
GprsCodingScheme 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;
|
||||
|
||||
bool is_control_ts(uint8_t ts) const;
|
||||
|
||||
/* EGPRS */
|
||||
bool is_egprs_enabled() const;
|
||||
void enable_egprs();
|
||||
void disable_egprs();
|
||||
|
||||
/* 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;}
|
||||
|
||||
LListHead<gprs_rlcmac_tbf>& list();
|
||||
const LListHead<gprs_rlcmac_tbf>& list() const;
|
||||
|
||||
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 */
|
||||
uint8_t poll_ts; /* TS 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 set_tlli_from_ul(uint32_t new_tlli);
|
||||
void merge_and_clear_ms(GprsMs *old_ms);
|
||||
|
||||
static const char *tbf_state_name[7];
|
||||
|
||||
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_list;
|
||||
LListHead<gprs_rlcmac_tbf> m_ms_list;
|
||||
bool m_egprs_enabled;
|
||||
|
||||
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, uint8_t egprs_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 egprs_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 egprs_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 LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list()
|
||||
{
|
||||
return this->m_list;
|
||||
}
|
||||
|
||||
inline const LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list() const
|
||||
{
|
||||
return this->m_list;
|
||||
}
|
||||
|
||||
inline GprsMs *gprs_rlcmac_tbf::ms() const
|
||||
{
|
||||
return m_ms;
|
||||
}
|
||||
|
||||
inline bool gprs_rlcmac_tbf::is_tlli_valid() const
|
||||
{
|
||||
return tlli() != 0;
|
||||
}
|
||||
|
||||
inline bool gprs_rlcmac_tbf::is_tfi_assigned() const
|
||||
{
|
||||
/* The TBF is established or has been assigned by a IMM.ASS for
|
||||
* download */
|
||||
return state > GPRS_RLCMAC_ASSIGN;
|
||||
}
|
||||
|
||||
inline uint8_t gprs_rlcmac_tbf::tfi() const
|
||||
{
|
||||
return m_tfi;
|
||||
}
|
||||
|
||||
inline time_t gprs_rlcmac_tbf::created_ts() const
|
||||
{
|
||||
return m_created_ts;
|
||||
}
|
||||
|
||||
inline bool gprs_rlcmac_tbf::is_egprs_enabled() const
|
||||
{
|
||||
return m_egprs_enabled;
|
||||
}
|
||||
|
||||
inline void gprs_rlcmac_tbf::enable_egprs()
|
||||
{
|
||||
m_egprs_enabled = true;
|
||||
}
|
||||
|
||||
inline void gprs_rlcmac_tbf::disable_egprs()
|
||||
{
|
||||
m_egprs_enabled = false;
|
||||
}
|
||||
|
||||
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 uint8_t egprs_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);
|
||||
int rcvd_dl_ack(uint8_t final_ack, unsigned first_bsn, struct bitvec *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();
|
||||
int abort();
|
||||
|
||||
/* 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 ana_result {
|
||||
unsigned received_packets;
|
||||
unsigned lost_packets;
|
||||
unsigned received_bytes;
|
||||
unsigned lost_bytes;
|
||||
};
|
||||
|
||||
int take_next_bsn(uint32_t fn, int previous_bsn,
|
||||
bool *may_combine);
|
||||
bool restart_bsn_cycle();
|
||||
int create_new_bsn(const uint32_t fn, GprsCodingScheme cs);
|
||||
struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
|
||||
int index, int index2 = -1);
|
||||
int update_window(const uint8_t ssn, const uint8_t *rbb);
|
||||
int update_window(unsigned first_bsn, const struct bitvec *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, ana_result *res);
|
||||
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, uint8_t ts);
|
||||
|
||||
/* blocks were acked */
|
||||
int rcv_data_block_acknowledged(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
uint8_t *data, 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 gprs_rlc_data_info *rlc);
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
inline gprs_rlcmac_ul_tbf *as_ul_tbf(gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
return static_cast<gprs_rlcmac_ul_tbf *>(tbf);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline gprs_rlcmac_dl_tbf *as_dl_tbf(gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
if (tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
return static_cast<gprs_rlcmac_dl_tbf *>(tbf);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline gprs_rlc_window *gprs_rlcmac_tbf::window()
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case GPRS_RLCMAC_UL_TBF: return &as_ul_tbf(this)->m_window;
|
||||
case GPRS_RLCMAC_DL_TBF: return &as_dl_tbf(this)->m_window;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,391 @@
|
|||
/* 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 gprs_rlc_data_block_info *rdbi = &_data->block_info;
|
||||
GprsCodingScheme cs = _data->cs;
|
||||
|
||||
Decoding::RlcData frames[16], *frame;
|
||||
int i, num_frames = 0;
|
||||
uint32_t dummy_tlli;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
|
||||
|
||||
num_frames = Decoding::rlc_data_from_ul_data(
|
||||
rdbi, cs, data, &(frames[0]), sizeof(frames),
|
||||
&dummy_tlli);
|
||||
|
||||
/* create LLC frames */
|
||||
for (i = 0; i < num_frames; i++) {
|
||||
frame = frames + i;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset %d, "
|
||||
"length=%d, is_complete=%d\n",
|
||||
i + 1, frame->offset, frame->length, frame->is_complete);
|
||||
|
||||
m_llc.append_frame(data + frame->offset, frame->length);
|
||||
m_llc.consume(frame->length);
|
||||
|
||||
if (frame->is_complete) {
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
|
||||
{
|
||||
int final = (state_is(GPRS_RLCMAC_FINISHED));
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
unsigned int rrbp = 0;
|
||||
uint32_t new_poll_fn = 0;
|
||||
|
||||
if (final) {
|
||||
if (poll_state == GPRS_RLCMAC_POLL_SCHED &&
|
||||
ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
|
||||
"scheduled for %s, so we must wait for "
|
||||
"the final uplink ack...\n", tbf_name(this));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = check_polling(fn, ts, &new_poll_fn, &rrbp);
|
||||
if (rc < 0)
|
||||
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");
|
||||
Encoding::write_packet_uplink_ack(bts_data(), ack_vec, this, final, rrbp);
|
||||
bitvec_pack(ack_vec, msgb_put(msg, 23));
|
||||
bitvec_free(ack_vec);
|
||||
|
||||
/* 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) {
|
||||
set_polling(new_poll_fn, ts);
|
||||
/* 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 struct gprs_rlc_data_info *rlc,
|
||||
uint8_t *data, struct pcu_l1_meas *meas)
|
||||
{
|
||||
int8_t rssi = meas->have_rssi ? meas->rssi : 0;
|
||||
|
||||
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", rlc->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);
|
||||
|
||||
uint32_t new_tlli = 0;
|
||||
unsigned int block_idx;
|
||||
|
||||
/* restart T3169 */
|
||||
tbf_timer_start(this, 3169, bts_data()->t3169, 0);
|
||||
|
||||
/* Increment RX-counter */
|
||||
this->m_rx_counter++;
|
||||
|
||||
/* Loop over num_blocks */
|
||||
for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) {
|
||||
int num_chunks;
|
||||
uint8_t *rlc_data;
|
||||
const struct gprs_rlc_data_block_info *rdbi =
|
||||
&rlc->block_info[block_idx];
|
||||
bool need_rlc_data = false;
|
||||
struct gprs_rlc_data *block;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"%s: Got %s RLC data block: "
|
||||
"CV=%d, BSN=%d, SPB=%d, "
|
||||
"PI=%d, E=%d, TI=%d, bitoffs=%d\n",
|
||||
name(), rlc->cs.name(),
|
||||
rdbi->cv, rdbi->bsn, rdbi->spb,
|
||||
rdbi->pi, rdbi->e, rdbi->ti,
|
||||
rlc->data_offs_bits[block_idx]);
|
||||
|
||||
/* Check whether the block needs to be decoded */
|
||||
|
||||
if (!m_window.is_in_window(rdbi->bsn)) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
|
||||
"%d..%d (it's normal)\n", rdbi->bsn,
|
||||
m_window.v_q(),
|
||||
m_window.mod_sns(m_window.v_q() + ws - 1));
|
||||
} else if (m_window.is_received(rdbi->bsn)) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"- BSN %d already received\n", rdbi->bsn);
|
||||
} else {
|
||||
need_rlc_data = true;
|
||||
}
|
||||
|
||||
if (!is_tlli_valid()) {
|
||||
if (!rdbi->ti) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"%s: Missing TLLI within UL DATA.\n",
|
||||
name());
|
||||
continue;
|
||||
}
|
||||
need_rlc_data = true;
|
||||
}
|
||||
|
||||
if (!need_rlc_data)
|
||||
continue;
|
||||
|
||||
/* Store block and meta info to BSN buffer */
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
|
||||
rdbi->bsn, m_window.v_q(),
|
||||
m_window.mod_sns(m_window.v_q() + ws - 1));
|
||||
block = m_rlc.block(rdbi->bsn);
|
||||
block->block_info = *rdbi;
|
||||
block->cs = rlc->cs;
|
||||
OSMO_ASSERT(rdbi->data_len < sizeof(block->block));
|
||||
rlc_data = &(block->block[0]);
|
||||
/* TODO: Handle SPB != 0 -> Set length to 2*len, add offset if
|
||||
* 2nd part. Note that resegmentation is currently disabled
|
||||
* within the UL assignment.
|
||||
*/
|
||||
if (rdbi->spb) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Got SPB != 0 but resegmentation has been "
|
||||
"disabled, skipping %s data block with BSN %d, "
|
||||
"TFI=%d.\n", rlc->cs.name(), rdbi->bsn,
|
||||
rlc->tfi);
|
||||
continue;
|
||||
}
|
||||
|
||||
block->len =
|
||||
Decoding::rlc_copy_to_aligned_buffer(rlc, block_idx, data,
|
||||
rlc_data);
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"%s: data_length=%d, data=%s\n",
|
||||
name(), block->len, osmo_hexdump(rlc_data, block->len));
|
||||
|
||||
/* TODO: Handle SPB != 0 -> set state to partly received
|
||||
* (upper/lower) and continue with the loop, unless the other
|
||||
* part is already present.
|
||||
*/
|
||||
|
||||
/* Get/Handle TLLI */
|
||||
if (rdbi->ti) {
|
||||
num_chunks = Decoding::rlc_data_from_ul_data(
|
||||
rdbi, rlc->cs, rlc_data, NULL, 0, &new_tlli);
|
||||
|
||||
if (num_chunks < 0) {
|
||||
bts->decode_error();
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Failed to decode TLLI of %s UL DATA "
|
||||
"TFI=%d.\n", rlc->cs.name(), rlc->tfi);
|
||||
m_window.invalidate_bsn(rdbi->bsn);
|
||||
continue;
|
||||
}
|
||||
if (!this->is_tlli_valid()) {
|
||||
if (!new_tlli) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"%s: TLLI = 0 within UL DATA.\n",
|
||||
name());
|
||||
m_window.invalidate_bsn(rdbi->bsn);
|
||||
continue;
|
||||
}
|
||||
LOGP(DRLCMACUL, LOGL_INFO,
|
||||
"Decoded premier TLLI=0x%08x of "
|
||||
"UL DATA TFI=%d.\n", tlli(), rlc->tfi);
|
||||
set_tlli_from_ul(new_tlli);
|
||||
} else if (new_tlli && new_tlli != tlli()) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
|
||||
"DATA TFI=%d. (Ignoring due to contention "
|
||||
"resolution)\n", rlc->tfi);
|
||||
m_window.invalidate_bsn(rdbi->bsn);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
m_window.receive_bsn(rdbi->bsn);
|
||||
}
|
||||
|
||||
/* 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.raise_v_q();
|
||||
|
||||
/* Retrieve LLC frames from blocks that are ready */
|
||||
for (uint16_t i = 0; i < count; ++i) {
|
||||
uint16_t index = m_window.mod_sns(v_q_beg + i);
|
||||
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 gprs_rlc_data *block =
|
||||
m_rlc.block(m_window.mod_sns(m_window.v_r() - 1));
|
||||
const struct gprs_rlc_data_block_info *rdbi =
|
||||
&block->block_info;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, "
|
||||
"last block: BSN=%d CV=%d\n", rdbi->bsn,
|
||||
rdbi->cv);
|
||||
if (rdbi->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(rlc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(
|
||||
const gprs_rlc_data_info *rlc)
|
||||
{
|
||||
bool have_ti = rlc->block_info[0].ti ||
|
||||
(rlc->num_data_blocks > 1 && rlc->block_info[1].ti);
|
||||
|
||||
if (rlc->si || have_ti || state_is(GPRS_RLCMAC_FINISHED) ||
|
||||
(m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0)
|
||||
{
|
||||
if (rlc->si) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
|
||||
"because MS is stalled.\n");
|
||||
}
|
||||
if (have_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;
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
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 edge/EdgeTest
|
||||
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)
|
||||
|
||||
edge_EdgeTest_SOURCES = edge/EdgeTest.cpp
|
||||
edge_EdgeTest_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 \
|
||||
edge/EdgeTest.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 $@
|
|
@ -0,0 +1,823 @@
|
|||
/* 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 egprs_ms_class, uint8_t single_slot)
|
||||
{
|
||||
if (dir == GPRS_RLCMAC_UL_TBF)
|
||||
return tbf_alloc_ul_tbf(bts, ms, use_trx,
|
||||
ms_class, egprs_ms_class, single_slot);
|
||||
else
|
||||
return tbf_alloc_dl_tbf(bts, ms, use_trx,
|
||||
ms_class, egprs_ms_class, single_slot);
|
||||
}
|
||||
|
||||
static void check_tfi_usage(BTS *the_bts)
|
||||
{
|
||||
int pdch_no;
|
||||
|
||||
struct gprs_rlcmac_tbf *tfi_usage[8][8][2][32] = {{{{NULL}}}};
|
||||
LListHead<gprs_rlcmac_tbf> *tbf_lists[2] = {
|
||||
&the_bts->ul_tbfs(),
|
||||
&the_bts->dl_tbfs()
|
||||
};
|
||||
|
||||
LListHead<gprs_rlcmac_tbf> *pos;
|
||||
gprs_rlcmac_tbf *tbf;
|
||||
unsigned list_idx;
|
||||
struct gprs_rlcmac_tbf **tbf_var;
|
||||
|
||||
for (list_idx = 0; list_idx < ARRAY_SIZE(tbf_lists); list_idx += 1)
|
||||
{
|
||||
|
||||
llist_for_each(pos, tbf_lists[list_idx]) {
|
||||
tbf = pos->entry();
|
||||
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, 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[0] = tbf_alloc(bts, NULL, dir, -1, 0, 0, 0);
|
||||
OSMO_ASSERT(tbfs[0]);
|
||||
tbf_free(tbfs[0]);
|
||||
}
|
||||
|
||||
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, 0, 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, 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, 0, 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, 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, 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, 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, 0, 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, 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, 0, 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, 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, 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, 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(); }
|
||||
}
|
|
@ -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)
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,8 @@
|
|||
=== start test_coding_scheme ===
|
||||
=== end test_coding_scheme ===
|
||||
=== start test_rlc_info_init ===
|
||||
=== end test_rlc_info_init ===
|
||||
=== start test_rlc_unit_decoder ===
|
||||
=== end test_rlc_unit_decoder ===
|
||||
=== start test_rlc_unit_encoder ===
|
||||
=== end test_rlc_unit_encoder ===
|
|
@ -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
|
|
@ -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 },
|
||||
},
|
||||
};
|
|
@ -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
|
|
@ -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(); }
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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_INFO);
|
||||
|
||||
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(); }
|
||||
}
|
|
@ -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
|
|
@ -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 ===
|
|
@ -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,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 ===
|
|
@ -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().to_num() == 4);
|
||||
|
||||
bts->cs_downgrade_threshold = 200;
|
||||
|
||||
OSMO_ASSERT(ms->current_cs_dl().to_num() == 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(); }
|
||||
}
|
|
@ -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)
|
|
@ -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,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
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,45 @@
|
|||
=== start test_tbf_base ===
|
||||
=== end test_tbf_base ===
|
||||
=== start test_tbf_tlli_update ===
|
||||
=== end test_tbf_tlli_update ===
|
||||
=== start test_tbf_final_ack ===
|
||||
=== end test_tbf_final_ack ===
|
||||
=== start test_tbf_final_ack ===
|
||||
=== end test_tbf_final_ack ===
|
||||
=== 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 ===
|
||||
=== start test_tbf_gprs_egprs ===
|
||||
=== end test_tbf_gprs_egprs ===
|
||||
=== start test_tbf_ws ===
|
||||
=== end test_tbf_ws ===
|
||||
=== start test_tbf_egprs_two_phase ===
|
||||
=== end test_tbf_egprs_two_phase ===
|
||||
=== start test_tbf_egprs_dl ===
|
||||
Testing MCS-1
|
||||
Testing MCS-2
|
||||
Testing MCS-3
|
||||
Testing MCS-4
|
||||
Testing MCS-5
|
||||
Testing MCS-6
|
||||
Testing MCS-7
|
||||
Testing MCS-8
|
||||
Testing MCS-9
|
||||
=== end test_tbf_egprs_dl ===
|
|
@ -0,0 +1,65 @@
|
|||
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([edge])
|
||||
AT_KEYWORDS([edge])
|
||||
cat $abs_srcdir/edge/EdgeTest.ok > expout
|
||||
cat $abs_srcdir/edge/EdgeTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/edge/EdgeTest], [0], [expout], [ignore])
|
||||
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
|
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
* 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;
|
||||
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;
|
||||
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));
|
||||
ul_win.receive_bsn(0);
|
||||
count = ul_win.raise_v_q();
|
||||
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));
|
||||
ul_win.receive_bsn(1);
|
||||
count = ul_win.raise_v_q();
|
||||
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));
|
||||
ul_win.receive_bsn(5);
|
||||
count = ul_win.raise_v_q();
|
||||
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));
|
||||
ul_win.receive_bsn(65);
|
||||
count = ul_win.raise_v_q();
|
||||
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));
|
||||
ul_win.receive_bsn(2);
|
||||
count = ul_win.raise_v_q();
|
||||
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));
|
||||
ul_win.receive_bsn(66);
|
||||
count = ul_win.raise_v_q();
|
||||
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);
|
||||
ul_win.raise_v_q();
|
||||
}
|
||||
|
||||
OSMO_ASSERT(ul_win.v_q() == 68);
|
||||
OSMO_ASSERT(ul_win.v_r() == 68);
|
||||
|
||||
ul_win.receive_bsn(68);
|
||||
count = ul_win.raise_v_q();
|
||||
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));
|
||||
ul_win.receive_bsn(4);
|
||||
count = ul_win.raise_v_q();
|
||||
OSMO_ASSERT(count == 0);
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t lost = 0, recv = 0;
|
||||
char show_rbb[65];
|
||||
uint8_t bits_data[8];
|
||||
BTS dummy_bts;
|
||||
gprs_rlc_dl_window dl_win;
|
||||
bitvec bits;
|
||||
int bsn_begin, bsn_end, num_blocks;
|
||||
Ack_Nack_Description_t desc;
|
||||
|
||||
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 };
|
||||
bits.data = bits_data;
|
||||
bits.data_len = sizeof(bits_data);
|
||||
bits.cur_bit = 0;
|
||||
|
||||
memcpy(desc.RECEIVED_BLOCK_BITMAP, rbb_cmp,
|
||||
sizeof(desc.RECEIVED_BLOCK_BITMAP));
|
||||
desc.FINAL_ACK_INDICATION = 0;
|
||||
desc.STARTING_SEQUENCE_NUMBER = 35;
|
||||
|
||||
num_blocks = Decoding::decode_gprs_acknack_bits(
|
||||
&desc, &bits,
|
||||
&bsn_begin, &bsn_end, &dl_win);
|
||||
Decoding::extract_rbb(&bits, show_rbb);
|
||||
printf("show_rbb: %s\n", show_rbb);
|
||||
|
||||
dl_win.update(&dummy_bts, &bits, 0, &lost, &recv);
|
||||
OSMO_ASSERT(lost == 0);
|
||||
OSMO_ASSERT(recv == 35);
|
||||
OSMO_ASSERT(bsn_begin == 0);
|
||||
OSMO_ASSERT(bsn_end == 35);
|
||||
OSMO_ASSERT(num_blocks == 35);
|
||||
|
||||
dl_win.raise(dl_win.move_window());
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
dl_win.increment_send();
|
||||
OSMO_ASSERT(!dl_win.window_empty());
|
||||
OSMO_ASSERT(dl_win.distance() == 2 + i);
|
||||
}
|
||||
|
||||
uint8_t rbb_cmp2[8] = { 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x31 };
|
||||
bits.data = bits_data;
|
||||
bits.data_len = sizeof(bits_data);
|
||||
bits.cur_bit = 0;
|
||||
|
||||
memcpy(desc.RECEIVED_BLOCK_BITMAP, rbb_cmp2,
|
||||
sizeof(desc.RECEIVED_BLOCK_BITMAP));
|
||||
desc.FINAL_ACK_INDICATION = 0;
|
||||
desc.STARTING_SEQUENCE_NUMBER = 35 + 8;
|
||||
|
||||
num_blocks = Decoding::decode_gprs_acknack_bits(
|
||||
&desc, &bits,
|
||||
&bsn_begin, &bsn_end, &dl_win);
|
||||
Decoding::extract_rbb(&bits, show_rbb);
|
||||
printf("show_rbb: %s\n", show_rbb);
|
||||
|
||||
lost = recv = 0;
|
||||
dl_win.update(&dummy_bts, &bits, 0, &lost, &recv);
|
||||
OSMO_ASSERT(lost == 5);
|
||||
OSMO_ASSERT(recv == 3);
|
||||
OSMO_ASSERT(bitvec_get_bit_pos(&bits, 0) == 0);
|
||||
OSMO_ASSERT(bitvec_get_bit_pos(&bits, 7) == 1);
|
||||
OSMO_ASSERT(bsn_begin == 35);
|
||||
OSMO_ASSERT(bsn_end == 43);
|
||||
OSMO_ASSERT(num_blocks == 8);
|
||||
}
|
||||
}
|
||||
|
||||
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,0 +1,8 @@
|
|||
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: RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
show_rbb: IIRRIIIR
|
Loading…
Reference in New Issue