Compare commits
577 Commits
Author | SHA1 | Date |
---|---|---|
Mychaela N. Falconia | 945b12a210 | |
Mychaela N. Falconia | 73a593c9f6 | |
Mychaela N. Falconia | 6007133574 | |
Mychaela N. Falconia | 2854c90e9e | |
Mychaela N. Falconia | 565aca5b95 | |
Mychaela N. Falconia | f939a07bda | |
Mychaela N. Falconia | 7b5d49bd0b | |
Mychaela N. Falconia | b81ab57653 | |
Mychaela N. Falconia | 7b39de33ad | |
Vadim Yanitskiy | 427f0437e1 | |
Oliver Smith | 6222a73db8 | |
Vadim Yanitskiy | 5277965190 | |
Vadim Yanitskiy | d3d04f9edf | |
Harald Welte | 12fae9aeeb | |
Harald Welte | 361488f83f | |
Harald Welte | ead2c4b651 | |
Harald Welte | c14bf38732 | |
Harald Welte | 715653778c | |
Harald Welte | a87de3cd60 | |
Harald Welte | d67966b43e | |
Harald Welte | 1645a2a261 | |
Andreas Eversberg | f5c799e903 | |
Vadim Yanitskiy | d33d431a1d | |
Vadim Yanitskiy | aa3452cb80 | |
Vadim Yanitskiy | c4ea374e4a | |
Keith Whyte | f0875079a3 | |
Harald Welte | 247e52141f | |
Harald Welte | 56a73534c9 | |
Pau Espin | e394d9dc58 | |
Vadim Yanitskiy | 9f1723d420 | |
Andreas Eversberg | 23bac40d75 | |
Andreas Eversberg | 7e5ab270b4 | |
Andreas Eversberg | 9fe62b01a7 | |
Philipp Maier | de41b9a40b | |
Harald Welte | eb2c109dea | |
Philipp Maier | a5afe23ab6 | |
Philipp Maier | 1e74d29937 | |
Vadim Yanitskiy | f6bde0f521 | |
Vadim Yanitskiy | 0de8a853a6 | |
Vadim Yanitskiy | 4b1cac45f4 | |
Vadim Yanitskiy | 36a075b06d | |
Philipp Maier | 851a4e4cbd | |
Philipp Maier | 6542a1514f | |
Philipp Maier | 2b880d6474 | |
Philipp Maier | a7e7621690 | |
Philipp Maier | 2c0237a122 | |
Mychaela N. Falconia | 2d70a93734 | |
Oliver Smith | dbbc4a075f | |
Philipp Maier | a551d3920e | |
Philipp Maier | 239a7049b7 | |
Philipp Maier | d741299f96 | |
Philipp Maier | dbb6cf4c7e | |
Philipp Maier | d0c61dc1c2 | |
Philipp Maier | d07528148e | |
Mychaela N. Falconia | 08d0c09902 | |
Mychaela N. Falconia | 1d42fcd4ea | |
Pau Espin | 4680672d6d | |
Andreas Eversberg | e40939e432 | |
Andreas Eversberg | 1a4ff4fbfa | |
Andreas Eversberg | df15c2976e | |
Philipp Maier | 26f7a7474e | |
Philipp Maier | 122a66da2e | |
arehbein | ac85f6069f | |
Philipp Maier | 5c9d45407f | |
Philipp Maier | 143a6798f5 | |
Philipp Maier | 8a84ce4a88 | |
Philipp Maier | ebb2cad4ae | |
Philipp Maier | 896cb2207a | |
Oliver Smith | 3dacef93ca | |
Andreas Eversberg | e4225cf8a9 | |
Andreas Eversberg | e73dd7ce31 | |
Andreas Eversberg | bbc4745799 | |
Andreas Eversberg | 982e5aadf9 | |
Andreas Eversberg | f78c84b292 | |
Andreas Eversberg | 9a151091cc | |
Oliver Smith | f2cb4f09e3 | |
Oliver Smith | 95dafe8b22 | |
Philipp Maier | cc42444539 | |
Philipp Maier | 31ab400147 | |
Philipp Maier | 7ff2afc703 | |
Philipp Maier | 9c9289c9d9 | |
Andreas Eversberg | eb4030f152 | |
Philipp Maier | 6622a97bfb | |
Pau Espin | 656f135ed0 | |
Philipp Maier | 1950af7030 | |
Philipp Maier | 8142f7ab99 | |
Andreas Eversberg | 79c49328a1 | |
Andreas Eversberg | d2b951fe74 | |
Andreas Eversberg | 32a208781a | |
Andreas Eversberg | 917454dd9c | |
Andreas Eversberg | 7f5b63e5e1 | |
Eric Wild | 864e1a2d56 | |
Philipp Maier | b057535512 | |
Philipp Maier | f2649502fd | |
Philipp Maier | 4934ff5389 | |
Philipp Maier | cf890391ee | |
Daniel Willmann | f2c76035fb | |
Daniel Willmann | c20af05bfc | |
Daniel Willmann | 6113525d6b | |
Daniel Willmann | 8e38094664 | |
Harald Welte | ce70074476 | |
Harald Welte | 9d90eb9960 | |
Oliver Smith | 3d8426e4c4 | |
Oliver Smith | fa19b2d9db | |
Oliver Smith | ef0f7c25f0 | |
Harald Welte | 35c109fd8b | |
Harald Welte | 4b77d42ea6 | |
Pau Espin | c4c3a856ad | |
Pau Espin | 3d3e5afb52 | |
Pau Espin | 2b52ac5dbd | |
Pau Espin | 57a148f332 | |
Pau Espin | 5b42f98089 | |
Jan Engelhardt | 74c8eba5ad | |
Vadim Yanitskiy | 79fbccf293 | |
Pau Espin | 8933b8f7cd | |
Pau Espin | 59a0c91904 | |
Harald Welte | dcbc00d0c4 | |
Harald Welte | 1885e0422a | |
Harald Welte | 3d41fc6815 | |
Pau Espin | 90df035a8d | |
Pau Espin | d20a10c58b | |
Harald Welte | 28fea7746b | |
Michael Iedema | ee2589e484 | |
Vadim Yanitskiy | adb2e1666f | |
Vadim Yanitskiy | bb9cba0167 | |
Michael Iedema | 296678662a | |
Michael Iedema | 95a8d6f2ef | |
Philipp Maier | f5f31d34d1 | |
Oliver Smith | 2650b57400 | |
Harald Welte | a864378936 | |
Pau Espin | a6eda5ae01 | |
Pau Espin | 57ff57ac5c | |
Pau Espin | 92aee5109c | |
Pau Espin | d2d28d83a4 | |
Pau Espin | dd48e257ab | |
Pau Espin | 49924f0a79 | |
Pau Espin | 24e0217c2e | |
Pau Espin | c5f1d4f9d3 | |
Philipp Maier | ea0f1bd49e | |
Keith Whyte | df088b0ea9 | |
Keith Whyte | bbff304e2e | |
Harald Welte | 14277e3375 | |
Harald Welte | 41547559da | |
Harald Welte | 6bcc32bcfa | |
Keith Whyte | 2672a2a2a7 | |
Harald Welte | 4aea11befc | |
Harald Welte | 956656452d | |
Pau Espin | de169199b9 | |
Pau Espin | 216316a6c1 | |
Pau Espin | 97cae17ffd | |
Harald Welte | 949b8a22b0 | |
Harald Welte | 8c724c0551 | |
Harald Welte | b3952c60f1 | |
Oliver Smith | 0f404b600c | |
Harald Welte | 6e831b72d7 | |
Vadim Yanitskiy | 77a7cf429b | |
Pau Espin | 9af069d5da | |
Pau Espin | 91314c10af | |
Pau Espin | 3bdf59b227 | |
Vadim Yanitskiy | 4da31b8173 | |
Harald Welte | de3959e62c | |
Pau Espin | 424e50f761 | |
Philipp Maier | ef5b839e23 | |
Vadim Yanitskiy | 82ef2a24e3 | |
Pau Espin | 7766e6dc15 | |
Harald Welte | 6c24d6cb43 | |
Harald Welte | f62da4798c | |
Harald Welte | ca28856d74 | |
Vadim Yanitskiy | 297a3be6cb | |
Harald Welte | 072ec6f133 | |
Harald Welte | 2a8a34cd01 | |
Philipp Maier | 8d15001910 | |
Harald Welte | 2f80b93528 | |
Harald Welte | ab8e0666f4 | |
Harald Welte | 1f310f7b03 | |
Harald Welte | dbb429cfd3 | |
Harald Welte | 04fe6b5f05 | |
Harald Welte | cd90ed2dd5 | |
Philipp Maier | 8eae96fab3 | |
Harald Welte | 78861c0aef | |
Harald Welte | 95e2dfbc83 | |
Harald Welte | dd9ee98d7e | |
Harald Welte | 254b924536 | |
Harald Welte | 1f9228434e | |
Harald Welte | ca71296916 | |
Harald Welte | f776669df2 | |
Harald Welte | 182957bf69 | |
Harald Welte | 58538de143 | |
Harald Welte | ac290eec96 | |
Pau Espin | deb5c4f7ab | |
Harald Welte | 8fc8ceff16 | |
Pau Espin | 3b9bebfa24 | |
Pau Espin | ffc92d5b23 | |
Pau Espin | 6ffeb9af89 | |
Pau Espin | 8a4e3d91f8 | |
Pau Espin | 815117c246 | |
Pau Espin | dc55a5fa63 | |
Pau Espin | a2ff733ac0 | |
Pau Espin | 8737ad4f75 | |
Pau Espin | b8ea0ff521 | |
Pau Espin | 5196cd5641 | |
Pau Espin | 837fb6e471 | |
Pau Espin | 30e80d555c | |
Pau Espin | 4ffd2a9c9b | |
Harald Welte | bcfd7ea184 | |
Harald Welte | 96034fcced | |
Harald Welte | 47c247b940 | |
Harald Welte | ea7da44bdd | |
Harald Welte | 36166d0a8d | |
Pau Espin | 89c6b8a42e | |
Harald Welte | b90318808b | |
Oliver Smith | 62725d0b58 | |
Oliver Smith | a5eef0eb82 | |
Oliver Smith | 85887b2eee | |
Oliver Smith | 39dffb6c29 | |
Harald Welte | db1bc21d73 | |
Harald Welte | 4119e5124d | |
Harald Welte | 2f182e4047 | |
Harald Welte | a684b84f11 | |
Harald Welte | 6913b9a4e4 | |
Harald Welte | 5226e5b926 | |
Sylvain Munaut | eb55e2f8d9 | |
Sylvain Munaut | 4b45e9d1a6 | |
Eric Wild | 73a73c0381 | |
Harald Welte | 54c919ec48 | |
Harald Welte | 9104597d76 | |
Oliver Smith | 3c51482e3d | |
Eric Wild | ef1f327c96 | |
Oliver Smith | 65ae42c62c | |
Harald Welte | eed3916dee | |
Harald Welte | accd677414 | |
Harald Welte | 28898d1fa7 | |
Harald Welte | ccf84ea3dd | |
Harald Welte | 52e14129f2 | |
Harald Welte | 6f674dea42 | |
Harald Welte | 43436034ee | |
Harald Welte | c6c70766b3 | |
Harald Welte | 52b0d45b8a | |
Sylvain Munaut | b559a53263 | |
Harald Welte | b5af0991b3 | |
Harald Welte | 510ae993a0 | |
Neels Hofmeyr | a160e4bfdb | |
Pau Espin | c313d44272 | |
Vadim Yanitskiy | 4c2c088518 | |
Harald Welte | d4be696bbc | |
Vadim Yanitskiy | b43ce42411 | |
Vadim Yanitskiy | 69ae238f1e | |
Vadim Yanitskiy | 1c94f6a50e | |
Harald Welte | 7c1c8cc710 | |
Pau Espin | b6e28bf77b | |
Alexander Couzens | e11afdaa0e | |
Harald Welte | 30249a15d5 | |
Eric Wild | 51b610095d | |
Harald Welte | b4a7db0f36 | |
Debian Mobcom Maintainers | 418775c7a5 | |
Oliver Smith | 5318f43dae | |
Eric Wild | 6eb186c097 | |
Eric Wild | b8242720b8 | |
Harald Welte | 3e03bc2997 | |
Harald Welte | 02c5e9d1d0 | |
Harald Welte | 3d60dbd021 | |
Sylvain Munaut | bab7ae7e88 | |
Oliver Smith | c674e9fcdf | |
Harald Welte | c0a0ec494f | |
Max | 3a2aa0975a | |
Max | 480073a660 | |
Harald Welte | 176a1fbab6 | |
Stefan Sperling | 961776a2f9 | |
Pau Espin | de5758d307 | |
Pau Espin | 4179207e0e | |
Pau Espin | b5cfc6b019 | |
Pau Espin | dd95eb6480 | |
Stefan Sperling | 0d7d0b0a86 | |
Pau Espin | 47e9e63bd5 | |
Stefan Sperling | b24efa551d | |
Pau Espin | 9540f59b1d | |
Pau Espin | ed122f3c25 | |
Pau Espin | 7ad2b15bc7 | |
Pau Espin | 082876bb65 | |
Pau Espin | 2775798edb | |
Pau Espin | 3750dea968 | |
Pau Espin | 493c8e6008 | |
Neels Hofmeyr | fe6731181c | |
Pau Espin | 67902bbeb9 | |
Pau Espin | 56ae85fd52 | |
Harald Welte | a60f5e13b6 | |
Harald Welte | 82eb55e5a1 | |
Neels Hofmeyr | 4c57eef663 | |
Pau Espin | 026ff4574d | |
Pau Espin | a210684433 | |
Philipp Maier | e7761c6f6f | |
Philipp Maier | 05de362c81 | |
Philipp Maier | 5a236ce1d4 | |
Philipp Maier | c81b68f3e8 | |
Philipp Maier | 28eeb6bc93 | |
Philipp Maier | 7632278ea3 | |
Stefan Sperling | b0162077da | |
Pau Espin | 7edc25d9d5 | |
Pau Espin | cfe2260c00 | |
Pau Espin | 7b89f12e66 | |
Pau Espin | 60fd2a0e85 | |
Pau Espin | c04d8d2274 | |
Pau Espin | 43b5b69bb8 | |
Pau Espin | db0700a5a3 | |
Stefan Sperling | 49917c129b | |
Neels Hofmeyr | d3b8b69977 | |
Neels Hofmeyr | bb83e6f7eb | |
Pau Espin | 61460fd643 | |
Max | f446367a63 | |
Harald Welte | f2a1607e27 | |
Max | 7139352e67 | |
Harald Welte | d1dd22c38e | |
Alexander Couzens | 2d4888dd08 | |
Neels Hofmeyr | 2e05f883f9 | |
Harald Welte | 323d39d784 | |
Harald Welte | 513c8f4472 | |
Neels Hofmeyr | e2d33bf02d | |
Neels Hofmeyr | d3c81da398 | |
Alexander Couzens | 4200aa6b69 | |
Neels Hofmeyr | 01543a1ea3 | |
Max | d3292913ac | |
Max | 7f17b8c45d | |
Max | e3260722d7 | |
Pau Espin | 35003ec2ea | |
Pau Espin | 1307b7aa60 | |
Harald Welte | ff8eed24e7 | |
Harald Welte | a06b877437 | |
Pau Espin | 65529bc541 | |
Pau Espin | 9e992c2435 | |
Pau Espin | 524923a96a | |
Neels Hofmeyr | c633cfbb91 | |
Pau Espin | 7d1f53143e | |
Pau Espin | 62eddcdcd8 | |
Pau Espin | 05df2d65a7 | |
Pau Espin | b0c3a4a30f | |
Pablo Neira Ayuso | b26f2fd825 | |
Harald Welte | e416e2e09d | |
Harald Welte | cac78fe9e9 | |
Harald Welte | d517db06ce | |
Harald Welte | bf7976c0b0 | |
Jean-Francois Dionne | 5e87fdfcab | |
Pau Espin Pedrol | c42bf19cc5 | |
Harald Welte | 1e04d4b7a2 | |
Neels Hofmeyr | 3fdb287d65 | |
Harald Welte | 7861ebac84 | |
Philipp Maier | 027e119363 | |
Philipp Maier | d75bac40d1 | |
Alexander Couzens | beb10ef02a | |
Max | 8a3be282ab | |
Holger Hans Peter Freyther | 254745880b | |
Neels Hofmeyr | 30ffa7ade5 | |
Max | c9fa25e831 | |
Max | 4c4a1c2035 | |
Max | efb43c012b | |
Max | 5f3871e362 | |
Max | bf42e0a91f | |
Max | ac2ad3b437 | |
Max | 7c840be476 | |
Max | 78d0486ffd | |
Harald Welte | b0a4235805 | |
Harald Welte | 519217f0ea | |
Harald Welte | 14dd30a13e | |
Harald Welte | 34260c8923 | |
Harald Welte | ae3a993bd6 | |
Alexander Couzens | 35daa67763 | |
Harald Welte | ab3b5606fe | |
Arran Cudbard-Bell | 67b81eb6fe | |
Philipp Maier | 0c7d5f4a61 | |
Harald Welte | 7895e0456a | |
Max | f69060066c | |
Max | 02ceea8342 | |
Harald Welte | 7a228ebc60 | |
Harald Welte | a0108e78a9 | |
Harald Welte | 4ca5c53f7f | |
Harald Welte | f35d8898ab | |
Harald Welte | 1129040594 | |
Neels Hofmeyr | 017752fedc | |
Neels Hofmeyr | fdcb961137 | |
Neels Hofmeyr | 04c73cb83f | |
Yves Godin | 2c32f0a269 | |
Neels Hofmeyr | a0ff942e92 | |
Neels Hofmeyr | 24796f2147 | |
Neels Hofmeyr | f5d28602a7 | |
Max | 15d9b7929d | |
Max | fc47015d91 | |
Max | 6dab90f39f | |
Neels Hofmeyr | c77c2a6aa1 | |
Neels Hofmeyr | 085ab0bb9c | |
Harald Welte | 7f9d851d9f | |
Harald Welte | b4698ef0d9 | |
Harald Welte | 47bee5bed5 | |
Max | c1cf14cae8 | |
Max | c42c2ca8e0 | |
Max | 73b9bc72ac | |
Holger Hans Peter Freyther | 6479728586 | |
Harald Welte | 9ed7ca5b86 | |
Harald Welte | 12814b9636 | |
Harald Welte | b2d727a102 | |
Max | 8c119f7a05 | |
Max | 80f7c0465f | |
Max | e54d7bcef5 | |
Holger Hans Peter Freyther | d8026ec1b7 | |
Holger Hans Peter Freyther | 7c9337b775 | |
Neels Hofmeyr | 0db1d43c0d | |
Alexander Huemer | 86fc3c8787 | |
Holger Hans Peter Freyther | 3cef39b03c | |
Holger Hans Peter Freyther | 71bc9e2ac8 | |
Holger Hans Peter Freyther | fe01908428 | |
Harald Welte | b37c5d48f0 | |
Holger Hans Peter Freyther | 3a580f263a | |
Holger Hans Peter Freyther | 050d60a9a1 | |
Holger Hans Peter Freyther | 2f0dd0c019 | |
Holger Hans Peter Freyther | 9087859057 | |
Holger Hans Peter Freyther | adfa01f041 | |
Jacob Erlbeck | 1acf4cbd75 | |
Holger Hans Peter Freyther | 687046ba2f | |
Holger Hans Peter Freyther | 37fcd93a50 | |
Holger Hans Peter Freyther | d9d1b5c19f | |
Holger Hans Peter Freyther | b6d2834eef | |
Holger Hans Peter Freyther | e6ad794cad | |
Holger Hans Peter Freyther | c97b9e64e3 | |
Andreas Eversberg | f422a753a7 | |
Harald Welte | c9295ea2dd | |
Harald Welte | b65f58f6f9 | |
Harald Welte | 46fc7e2df7 | |
Harald Welte | f5efba40e7 | |
Harald Welte | c1edf604ae | |
Harald Welte | 783715b204 | |
Holger Hans Peter Freyther | 46c40b8e58 | |
Harald Welte | 3091e17571 | |
Harald Welte | e7e1b75564 | |
Harald Welte | f0d4a2213a | |
Harald Welte | 205e90314d | |
Holger Hans Peter Freyther | fb6e1e993e | |
Holger Hans Peter Freyther | bf6f1f4a37 | |
Holger Hans Peter Freyther | d67f3f04ab | |
Pablo Neira Ayuso | 754d5d4b6c | |
Holger Hans Peter Freyther | ecd0a36ce8 | |
Jacob Erlbeck | 98af3c3a6d | |
Jacob Erlbeck | 36106ae244 | |
Holger Hans Peter Freyther | f465a4c16f | |
Holger Hans Peter Freyther | f6415ea5a6 | |
Holger Hans Peter Freyther | ba9aa8941b | |
Holger Hans Peter Freyther | 901ef1cbbb | |
Jacob Erlbeck | 8fe1571ea9 | |
Jacob Erlbeck | 86dae84bed | |
Jacob Erlbeck | a4ec51e108 | |
Daniel Willmann | 859807261e | |
Holger Hans Peter Freyther | 63ddf4645c | |
Holger Hans Peter Freyther | 7ae8503370 | |
Holger Hans Peter Freyther | d65ccd50fa | |
Holger Hans Peter Freyther | 05a337c8b4 | |
Holger Hans Peter Freyther | ece5323fd3 | |
Holger Hans Peter Freyther | 91de2d0065 | |
Holger Hans Peter Freyther | e0bd2f6d4e | |
Jacob Erlbeck | 3ea52e4f4c | |
Jacob Erlbeck | 1c9dcc1236 | |
Jacob Erlbeck | 37f0695626 | |
Holger Hans Peter Freyther | e58d33153d | |
Harald Welte | 2bfc01dfd8 | |
Harald Welte | 51b796122c | |
Harald Welte | a98a6254bf | |
Harald Welte | 6eddd478e2 | |
Harald Welte | 51de9ca3a6 | |
Harald Welte | a3e9dd5757 | |
Harald Welte | e68055bf11 | |
Harald Welte | 10b41306db | |
Harald Welte | 84f67b2832 | |
Harald Welte | d331f862dc | |
Pablo Neira Ayuso | ef13269e07 | |
Pablo Neira Ayuso | cdda0a87e4 | |
Pablo Neira Ayuso | 33f7175764 | |
Holger Hans Peter Freyther | f69b8123f9 | |
Holger Hans Peter Freyther | 219a6a4add | |
Pablo Neira Ayuso | 2e11f5c7dc | |
Pablo Neira Ayuso | 4bab3bf415 | |
Pablo Neira Ayuso | a8c4871a2a | |
Pablo Neira Ayuso | b948701f8c | |
Holger Hans Peter Freyther | 3a5b4364e8 | |
Holger Hans Peter Freyther | 8cbd9f4d09 | |
Holger Hans Peter Freyther | a677cbc0cb | |
Holger Hans Peter Freyther | 9d4fe51424 | |
Alexander Huemer | 3eed8a115a | |
Holger Hans Peter Freyther | 5731dd74f7 | |
Holger Hans Peter Freyther | 4b6860db44 | |
Harald Welte | e7a3f43d86 | |
Andreas Eversberg | 41bc615c3f | |
Holger Hans Peter Freyther | cf5b08bcd2 | |
Harald Welte | d426d458ca | |
Daniel Willmann | bd3f854f3c | |
Holger Hans Peter Freyther | 2dfcd9ecc1 | |
Holger Hans Peter Freyther | cbb78ab998 | |
Pablo Neira Ayuso | a49c24d965 | |
Tobias Engel | bcb9531366 | |
Holger Hans Peter Freyther | bc5dcb11a7 | |
Tobias Engel | f684976724 | |
Dieter Spaar | 08c103251c | |
Holger Hans Peter Freyther | d72c478b06 | |
Holger Hans Peter Freyther | 5c65e79edc | |
Pablo Neira Ayuso | 84e5cb9b34 | |
Pablo Neira Ayuso | 7251604cce | |
Pablo Neira Ayuso | 1fd93bbc6e | |
Pablo Neira Ayuso | 7723b4e9a3 | |
Pablo Neira Ayuso | 953060001d | |
Pablo Neira Ayuso | 7ed92581ee | |
Pablo Neira Ayuso | 5c67fb5610 | |
Pablo Neira Ayuso | f42280b6a2 | |
Pablo Neira Ayuso | 18d6294efe | |
Pablo Neira Ayuso | 1e4019483f | |
Pablo Neira Ayuso | 5a8cca3a1c | |
Pablo Neira Ayuso | 6cc3f92ea4 | |
Pablo Neira Ayuso | 81ed759993 | |
Eric Butler | d11a5c1103 | |
Eric Butler | 91ffa2b5c1 | |
Holger Hans Peter Freyther | 9e8f1c0362 | |
Holger Hans Peter Freyther | 36bac9a8b1 | |
Diego Elio Pettenò | e5ae7f5217 | |
Diego Elio Pettenò | 6e389a3b2b | |
Diego Elio Pettenò | 56855350e9 | |
Diego Elio Pettenò | 05819cebc4 | |
Diego Elio Pettenò | 3dfe8bf80c | |
Diego Elio Pettenò | 51f7940d1f | |
Diego Elio Pettenò | 62f0e0612a | |
Holger Hans Peter Freyther | 584672e8f4 | |
Harald Welte | 36a85a41b0 | |
Holger Hans Peter Freyther | 180ce7eaad | |
Holger Hans Peter Freyther | df78e92596 | |
Andreas Eversberg | a659f4827e | |
Andreas Eversberg | 4308667a25 | |
Holger Hans Peter Freyther | 55467f0478 | |
Andreas Eversberg | d73c84670b | |
Holger Hans Peter Freyther | e194cd92a8 | |
Harald Welte | 0b5ffc1788 | |
Harald Welte | 833aa79ffa | |
Holger Hans Peter Freyther | 25c83611be | |
Harald Welte | 43f4e08841 | |
Andreas Eversberg | 8aaed05e21 | |
Andreas Eversberg | d88c5bf637 | |
Andreas Eversberg | 3c46044cf1 | |
Andreas Eversberg | 3744b873e5 | |
Andreas Eversberg | a7ff0019b3 | |
Harald Welte | fe05cf5b39 | |
Harald Welte | 494f290f6b | |
Harald Welte | 0cf5514616 | |
Harald Welte | e2bd68575f | |
Harald Welte | 356918fed4 | |
Alexander Huemer | 70e04c7ccc | |
Harald Welte | c2889512c4 | |
Daniel Willmann | a0d9331cb1 | |
Daniel Willmann | dc4479f27d | |
Harald Welte | 883cfe5be8 | |
Pablo Neira Ayuso | 218bb8f31e | |
Pablo Neira Ayuso | 32c883a96f | |
Pablo Neira Ayuso | 2220a0542c | |
Pablo Neira Ayuso | 8ad30c96d4 | |
Pablo Neira Ayuso | 8fc277b777 | |
Pablo Neira Ayuso | f099567ae3 | |
Pablo Neira Ayuso | 00af772339 | |
Harald Welte | 65a50893d5 | |
Harald Welte | ddfaca45fe | |
Harald Welte | fcb1fe8c62 | |
Harald Welte | 9b737df611 | |
Harald Welte | 346374a001 | |
Harald Welte | 6730187bd8 | |
Harald Welte | 41d0d84fd6 | |
Pablo Neira Ayuso | d6216405b7 | |
Harald Welte | f350e25394 | |
Harald Welte | cfc9f1f7cc | |
Harald Welte | 14078eaffe | |
Harald Welte | 3bc7885539 | |
Harald Welte | ce307b4420 | |
Harald Welte | 6341aa0595 | |
Harald Welte | fd44a5f2fe | |
Harald Welte | 2f69b890bc | |
Harald Welte | e4ec40a365 | |
Dieter Spaar | aa4942440a | |
Harald Welte | bc25bcaea1 |
|
@ -15,12 +15,37 @@ config.log
|
|||
config.status
|
||||
config.guess
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
missing
|
||||
ltmain.sh
|
||||
install-sh
|
||||
stamp-h1
|
||||
libtool
|
||||
#libosmo-abis-*
|
||||
tests/*_test
|
||||
*~
|
||||
|
||||
# libtool and e.g. arm-poky-linux-gnueabi-libtool
|
||||
*libtool
|
||||
|
||||
.tarball-version
|
||||
.version
|
||||
.dirstamp
|
||||
|
||||
# tests
|
||||
tests/atconfig
|
||||
tests/package.m4
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
tests/subchan_demux/.dirstamp
|
||||
tests/subchan_demux/subchan_demux_test
|
||||
tests/ipa_recv/ipa_recv_test
|
||||
tests/trau_pcu_ericsson/trau_pcu_ericsson_test
|
||||
tests/trau_sync/trau_sync_test
|
||||
|
||||
|
||||
|
||||
# vi/vim files
|
||||
*.sw?
|
||||
|
||||
contrib/libosmo-abis.spec
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=libosmo-abis
|
14
Makefile.am
14
Makefile.am
|
@ -1,14 +1,22 @@
|
|||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
SUBDIRS = include src tests
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libosmoabis.pc
|
||||
pkgconfig_DATA = libosmoabis.pc libosmotrau.pc
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
README.md \
|
||||
debian \
|
||||
git-version-gen \
|
||||
$(NULL)
|
||||
|
||||
@RELMAKE@
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
libosmo-abis - Osmocom Abis interface library
|
||||
=============================================
|
||||
|
||||
This repository contains a set of C-language libraries that form the
|
||||
A-bis interface library of [Osmocom](https://osmocom.org/) Open Source
|
||||
Mobile Communications projects such as OpenBSC / OsmoBSC.
|
||||
|
||||
Historically, a lot of this code was developed as part of the
|
||||
[OpenBSC](https://osmocom.org/projects/openbsc) project, but which are
|
||||
of a more generic nature and thus useful to (at least) other programs
|
||||
that we develop in the sphere of Free Software / Open Source mobile
|
||||
communications.
|
||||
|
||||
The libosmo-abis.git repository build multiple libraries:
|
||||
|
||||
* **libosmoabis** contains some abstraction layer over E1/T1 and IP
|
||||
based ETSI/3GPP A-bis interface. It can use mISDN and DAHDI as
|
||||
underlying driver/hardware.
|
||||
* **libosmotrau** contains routines related to A-bis TRAU frame handling
|
||||
|
||||
Homepage
|
||||
--------
|
||||
|
||||
The official homepage of the project is
|
||||
<https://osmocom.org/projects/libosmo-abis>
|
||||
|
||||
GIT Repository
|
||||
--------------
|
||||
|
||||
You can clone from the official libosmo-abis.git repository using
|
||||
|
||||
git clone https://gitea.osmocom.org/osmocom/libosmo-abis
|
||||
|
||||
There is a web interface at <https://gitea.osmocom.org/osmocom/libosmo-abis>
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
There is no Doxygen-generated API documentation yet for this library. It
|
||||
would be great to some day have it, comparable to libosmocore.
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
Discussions related to libosmo-abis are happening on the
|
||||
openbsc@lists.osmocom.org mailing list, please see
|
||||
<https://lists.osmocom.org/mailman/listinfo/openbsc> for subscription
|
||||
options and the list archive.
|
||||
|
||||
Please observe the [Osmocom Mailing List
|
||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||
when posting.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Our coding standards are described at
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
|
||||
|
||||
We use a Gerrit based patch submission/review process for managing
|
||||
contributions. Please see
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
|
||||
more details
|
||||
|
||||
The current patch queue for libosmo-abis can be seen at
|
||||
<https://gerrit.osmocom.org/#/q/project:libosmo-abis+status:open>
|
|
@ -0,0 +1,11 @@
|
|||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# In short:
|
||||
# LIBVERSION=c:r:a
|
||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
|
||||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
libosmotrau struct osmo_trau2rtp_state extended (ABI break)
|
||||
libosmogsm >1.9.0 rtp_extensions.h new header
|
163
configure.ac
163
configure.ac
|
@ -1,24 +1,56 @@
|
|||
AC_INIT([libosmo-abis],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[openbsc-devel@lists.openbsc.org])
|
||||
[openbsc@lists.osmocom.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.6 subdir-objects])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
dnl checks for programs
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
LT_INIT
|
||||
AC_PROG_LIBTOOL
|
||||
LT_INIT([pic-only])
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
AC_MSG_WARN([You need to install pkg-config])
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h)
|
||||
CFLAGS="$CFLAGS -Wall"
|
||||
CPPFLAGS="$CPPFLAGS -Wall"
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
# The following test is taken from WebKit's webkit.m4
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
|
@ -34,18 +66,121 @@ AC_SUBST(SYMBOL_VISIBILITY)
|
|||
dnl Generate the output
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec >= 1.9.0)
|
||||
PKG_CHECK_MODULES(ORTP, ortp >= 0.22.0)
|
||||
|
||||
AC_CHECK_HEADERS(dahdi/user.h,,AC_MSG_ERROR(DAHDI input driver headers missing))
|
||||
AC_ARG_ENABLE([dahdi],
|
||||
AC_HELP_STRING([--disable-dahdi],
|
||||
[disable support for DAHID ISDN (E1/T1) cards [default=yes]]),
|
||||
[enable_dahdi="$enableval"], [enable_dahdi="yes"])
|
||||
AM_CONDITIONAL(ENABLE_DAHDI, test "x$enable_dahdi" = "xyes")
|
||||
if test "x$enable_dahdi" = "xyes"; then
|
||||
AC_CHECK_HEADERS([dahdi/user.h],[],[AC_MSG_ERROR([DAHDI input driver enabled but DAHDI not found])])
|
||||
else
|
||||
AC_MSG_WARN([DAHDI input driver will not be built])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([e1d],
|
||||
[AS_HELP_STRING(
|
||||
[--enable-e1d],
|
||||
[Enable osmo-e1d driver support]
|
||||
)],
|
||||
[
|
||||
ENABLE_E1D=$enableval
|
||||
],
|
||||
[
|
||||
ENABLE_E1D="no"
|
||||
])
|
||||
AS_IF([test "x$ENABLE_E1D" = "xyes"], [
|
||||
PKG_CHECK_MODULES(LIBOSMOE1D, libosmo-e1d >= 0.5.0)
|
||||
])
|
||||
AM_CONDITIONAL(ENABLE_E1D, test "x$ENABLE_E1D" = "xyes")
|
||||
AC_SUBST(ENABLE_E1D)
|
||||
if test x"$ENABLE_E1D" = x"yes"
|
||||
then
|
||||
AC_DEFINE([HAVE_E1D], [1], [Enable osmo-e1d driver support])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
_cflags_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $ORTP_CFLAGS"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <ortp/ortp.h>]],
|
||||
[[ortp_set_log_level_mask(NULL, 0xffff);]]
|
||||
)],
|
||||
[AC_DEFINE([HAVE_ORTP_LOG_DOMAIN], [1],
|
||||
[ortp_set_log_level_mask requires domain parameter])],
|
||||
[AC_DEFINE([HAVE_ORTP_LOG_DOMAIN], [0],
|
||||
[ortp_set_log_level_mask has no domain parameter])])
|
||||
CFLAGS=$_cflags_save
|
||||
|
||||
_cflags_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $ORTP_CFLAGS -Werror"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <ortp/ortp.h>
|
||||
void fake_cb(struct _RtpSession *r, void *arg1, void *arg2, void *arg3) { return; }]],
|
||||
[[rtp_session_signal_connect(NULL, "", fake_cb, (void*) fake_cb);]]
|
||||
)],
|
||||
[AC_DEFINE([RTP_SIGNAL_PTR_CAST(arg)], [(void*)(arg)],
|
||||
[rtp_session_signal_connect requires pointer parameter])],
|
||||
[AC_DEFINE([RTP_SIGNAL_PTR_CAST(arg)], [(unsigned long)(arg)],
|
||||
[rtp_session_signal_connect requires ulong parameter])])
|
||||
CFLAGS=$_cflags_save
|
||||
|
||||
_cflags_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $ORTP_CFLAGS"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <ortp/ortp.h>]],
|
||||
[[OrtpMemoryFunctions memfn = { };]]
|
||||
)],
|
||||
[AC_DEFINE([HAVE_ORTP_MEM_FUNC], [1],
|
||||
[OrtpMemoryFunctions is available, will use talloc])],
|
||||
[AC_DEFINE([HAVE_ORTP_MEM_FUNC], [0],
|
||||
[OrtpMemoryFunctions is not available])])
|
||||
CFLAGS=$_cflags_save
|
||||
|
||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||
|
||||
AC_OUTPUT(
|
||||
libosmoabis.pc
|
||||
libosmotrau.pc
|
||||
include/Makefile
|
||||
include/osmocom/Makefile
|
||||
include/osmocom/abis/Makefile
|
||||
src/Makefile
|
||||
src/input/Makefile
|
||||
tests/Makefile
|
||||
Makefile)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/sh
|
||||
# jenkins build helper script for libosmo-abis. This is how we build on jenkins.osmocom.org
|
||||
|
||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
base="$PWD"
|
||||
deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
export deps inst
|
||||
|
||||
osmo-clean-workspace.sh
|
||||
|
||||
mkdir "$deps" || true
|
||||
|
||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
||||
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh osmo-e1d
|
||||
|
||||
set +x
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo " =============================== libosmo-abis ==============================="
|
||||
echo
|
||||
set -x
|
||||
|
||||
autoreconf --install --force
|
||||
./configure --enable-sanitize --enable-werror --enable-e1d
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE $PARALLEL_MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
$MAKE $PARALLEL_MAKE maintainer-clean
|
||||
|
||||
osmo-clean-workspace.sh
|
|
@ -0,0 +1,13 @@
|
|||
CFLAGS := -Wall `pkg-config --cflags libosmocore libosmogsm libosmotrau`
|
||||
LIBS := `pkg-config --libs libosmocore libosmogsm libosmotrau`
|
||||
|
||||
all: trau2rtp
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^
|
||||
|
||||
trau2rtp: trau2rtp.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
@rm -f *.o trau2rtp
|
|
@ -0,0 +1,67 @@
|
|||
this is a small utility program illustrating the use of the new (2020)
|
||||
TRAU related blocks like I.460 mux, TRAU frame synchronizer, TRAU frame
|
||||
decoder and trau2rtp conversion.
|
||||
|
||||
The only argument to the progrmam is the input file, which can either be
|
||||
|
||||
* a DAHDI device like /dev/dahdi/chan/004/002 to use TS2 on span/line 4
|
||||
* a pre-recorded capture file containing raw binary 64k timeslot data
|
||||
|
||||
The tool will automatically determine if a regular file or a DAHDI device
|
||||
was passed, and behave accordingly.
|
||||
|
||||
|
||||
== reading capture files
|
||||
|
||||
There are FR and EFR exampel captures included.
|
||||
|
||||
The input data (*.log.bz2) was generated using strace on an osmo-nitb process
|
||||
while a MO-to-MT call was running on two sub-slots of TS2.
|
||||
|
||||
The strace log is converted to a binary stream of the raw 64bit E1 slot
|
||||
using strace-write-parse.py
|
||||
|
||||
You can use the too like this:
|
||||
|
||||
* start osmo-gapk as a RTP to ALSA sink like
|
||||
osmo-gapk -I 127.0.0.1/9999 -f gsm -g rawpcm-s16le -A default # for FR
|
||||
osmo-gapk -I 127.0.0.1/9999 -f rtp-efr -g rawpcm-s16le -A default # for EFR
|
||||
|
||||
* run trau2rtp to read the bin file and generate RTP:
|
||||
./trau2rtp e1_ts2_fr.bin # for FR
|
||||
./trau2rtp e1_ts2_efr.bin # for EFR
|
||||
|
||||
|
||||
== Interfacing a DAHDI device
|
||||
|
||||
It can be operated in two modes:
|
||||
|
||||
a) local loop between sub-slots 1+2 (bit-offset 2 + 4), i.e. connecting the first
|
||||
two calls on a 'typical' TRX0 where the first sub-slot (bit-offset 0) is not
|
||||
used,
|
||||
|
||||
OR
|
||||
|
||||
b) interface between 64k TRAU slot and RTP. IP addresses + port numbers are
|
||||
compiled-in, you need to modify them accordingly.
|
||||
|
||||
|
||||
In mode "a", you can use it to have voice calls for low-level debugging without a media
|
||||
gateway.
|
||||
|
||||
In mode "b", you can e.g. play back audio from an exterenal RTP source and listen to it
|
||||
over the GSM attached mobile phone.
|
||||
|
||||
Command line for playing back EFR via RTP:
|
||||
./osmo-gapk -f amr-efr -i ../tests/ref-files/hhgttg_part1_5.s16.amr-efr -g rtp-efr -O 192.168.11.179/8002 -t -l
|
||||
|
||||
Command line for playing back FR via RTP:
|
||||
./osmo-gapk -f gsm -i ../tests/ref-files/hhgttg_part1_5.s16.gsm -g gsm -O 192.168.11.179/8002 -t -l
|
||||
|
||||
The '-l' option is experimental and requires gapk patch I2d552695dfb4cc96039838e79e0f5ae25a6737c8. If you want
|
||||
to use it with EFR, you need to change pq_file.c to skip the AMR file header when rewinding:
|
||||
|
||||
- fseek(state->fh, 0, SEEK_SET);
|
||||
+ fseek(state->fh, 6, SEEK_SET);
|
||||
|
||||
The code has been tested against BS-11 and RBS6000/DUG20 in both modes (loop vs. RTP) and for FR and EFR.
|
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# parses the output of strace and stroes the binary writes to a file
|
||||
|
||||
import re, binascii
|
||||
from struct import *
|
||||
|
||||
|
||||
p = re.compile('read\(\d+, "(.*)", \d+\) = \d+')
|
||||
|
||||
fi = open("e1_ts2_short.log", "r")
|
||||
fo = open("e1_ts2_short.bin", "wb")
|
||||
|
||||
for line in fi:
|
||||
m = p.match(line)
|
||||
data = m.group(1)
|
||||
snippets = [data[2+i:4+i] for i in range(0, len(data), 4)]
|
||||
for s in snippets:
|
||||
b = binascii.unhexlify(s)
|
||||
fo.write(b)
|
|
@ -0,0 +1,340 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dahdi/user.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
#include <osmocom/trau/trau_sync.h>
|
||||
#include <osmocom/trau/trau_frame.h>
|
||||
#include <osmocom/trau/trau_rtp.h>
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
#define D_BCHAN_TX_GRAN 160
|
||||
|
||||
/***********************************************************************
|
||||
* BEGIN CONFIGURATION
|
||||
***********************************************************************/
|
||||
/* HACK: Those do not have getopt but need to be modified in source code to match your enviroment */
|
||||
|
||||
/* do we just locally loop the calls from 1->2, or do we interface with RTP? */
|
||||
static bool g_local_loop = false;
|
||||
|
||||
/* remote IP address to which we send RTP data (unless g_local_loop mode) */
|
||||
const char *g_remote_host = "192.168.101.131";
|
||||
|
||||
/* remote UDP base port to which we send RTP data: +2 for every sub-slot */
|
||||
const int g_remote_port = 9000;
|
||||
|
||||
/* local UDP base port on which we receive RTP data: +2 for every sub-slot */
|
||||
const int g_local_port = 8000;
|
||||
|
||||
/* codec; can be OSMO_TRAU16_FT_FR or OSMO_TRAU16_FT_EFR */
|
||||
const enum osmo_trau_frame_type g_ftype = OSMO_TRAU16_FT_EFR;
|
||||
|
||||
/***********************************************************************
|
||||
* END CONFIGURATION
|
||||
***********************************************************************/
|
||||
|
||||
/* do we have a DAHDI chardev to which we can write RTP->TRAU conversion data/ */
|
||||
static bool g_ts_is_writable = false;
|
||||
|
||||
struct sc_state {
|
||||
unsigned int num;
|
||||
struct osmo_i460_subchan *i460_sc;
|
||||
struct osmo_fsm_inst *sync_fi;
|
||||
struct osmo_rtp_socket *rtps;
|
||||
struct osmo_trau2rtp_state t2r;
|
||||
};
|
||||
|
||||
struct state {
|
||||
int in_fd;
|
||||
struct osmo_i460_timeslot i460_ts;
|
||||
struct sc_state sc[4];
|
||||
};
|
||||
static struct state g_st;
|
||||
|
||||
#define LOGSC(sc, fmt, args...) printf("SC%u: " fmt, (sc)->num, ## args)
|
||||
|
||||
static struct sc_state *opposite_schan(struct sc_state *sc)
|
||||
{
|
||||
/* we blindly assume that there is a call between sub-channel 1 + sub-channel 2 */
|
||||
switch (sc->num) {
|
||||
case 1:
|
||||
return &g_st.sc[2];
|
||||
case 2:
|
||||
return &g_st.sc[1];
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */
|
||||
static void i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data,
|
||||
const ubit_t *bits, unsigned int num_bits)
|
||||
{
|
||||
struct sc_state *sc = user_data;
|
||||
//printf("I460: %s\n", osmo_ubit_dump(bits, num_bits));
|
||||
osmo_trau_sync_rx_ubits(sc->sync_fi, bits, num_bits);
|
||||
}
|
||||
|
||||
/* called for each synchronized TRAU frame received; decode frame + convert to RTP */
|
||||
static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
|
||||
{
|
||||
struct sc_state *sc = user_data;
|
||||
struct osmo_trau_frame fr;
|
||||
int rc;
|
||||
|
||||
LOGSC(sc, "Rx TRAU: %s\n", osmo_ubit_dump(bits, num_bits));
|
||||
if (!bits)
|
||||
goto skip;
|
||||
|
||||
rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL);
|
||||
if (rc != 0)
|
||||
goto skip;
|
||||
|
||||
uint8_t sid;
|
||||
switch (fr.type) {
|
||||
case OSMO_TRAU16_FT_FR:
|
||||
case OSMO_TRAU16_FT_EFR:
|
||||
sid = (fr.c_bits[13-1]) << 1 | (fr.c_bits[14-1] << 0);
|
||||
LOGSC(sc, "-> FT=%s, BFI=%u, SID=%u, TAF=%u DTXd=%u\n",
|
||||
osmo_trau_frame_type_name(fr.type), fr.c_bits[12-1], sid, fr.c_bits[15-1], fr.c_bits[17-1]);
|
||||
break;
|
||||
default:
|
||||
LOGSC(sc, "-> FT=%s\n", osmo_trau_frame_type_name(fr.type));
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_local_loop) {
|
||||
/* Mirror back to other sub-slot */
|
||||
struct sc_state *peer = opposite_schan(sc);
|
||||
if (peer) {
|
||||
struct msgb *msg = msgb_alloc(2*40*8, "mirror");
|
||||
fr.c_bits[12-1] = 1; /* C12 = good u-link frame */
|
||||
memset(&fr.c_bits[13-1], 1, 3); /* C13..C15: spare */
|
||||
fr.c_bits[16-1] = 1; /* C16 = SP[eech]; no DTX */
|
||||
memset(&fr.c_bits[6-1], 0, 6); /* C6..C11: tie alignment */
|
||||
fr.dir = OSMO_TRAU_DIR_DL;
|
||||
rc = osmo_trau_frame_encode(msgb_data(msg), 2*40*8, &fr);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
msgb_put(msg, rc);
|
||||
osmo_i460_mux_enqueue(peer->i460_sc, msg);
|
||||
}
|
||||
} else {
|
||||
/* Convert to RTP */
|
||||
if (fr.type != OSMO_TRAU16_FT_FR && fr.type != OSMO_TRAU16_FT_EFR)
|
||||
goto skip;
|
||||
|
||||
uint8_t rtpbuf[35];
|
||||
struct osmo_trau2rtp_state t2rs = {
|
||||
.type = fr.type,
|
||||
};
|
||||
memset(rtpbuf, 0, sizeof(rtpbuf));
|
||||
rc = osmo_trau2rtp(rtpbuf, sizeof(rtpbuf), &fr, &t2rs);
|
||||
LOGSC(sc, "Tx RTP: %s\n", osmo_hexdump(rtpbuf, rc));
|
||||
if (rc)
|
||||
osmo_rtp_send_frame_ext(sc->rtps, rtpbuf, rc, 160, false);
|
||||
else {
|
||||
osmo_rtp_skipped_frame(sc->rtps, 160);
|
||||
}
|
||||
return;
|
||||
}
|
||||
skip:
|
||||
if (!g_local_loop)
|
||||
osmo_rtp_skipped_frame(sc->rtps, 160);
|
||||
}
|
||||
|
||||
static int dahdi_set_bufinfo(int fd, int as_sigchan)
|
||||
{
|
||||
struct dahdi_bufferinfo bi;
|
||||
int x = 0;
|
||||
|
||||
if (ioctl(fd, DAHDI_GET_BUFINFO, &bi)) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Error getting bufinfo\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (as_sigchan) {
|
||||
bi.numbufs = 4;
|
||||
bi.bufsize = 512;
|
||||
} else {
|
||||
bi.numbufs = 8;
|
||||
bi.bufsize = D_BCHAN_TX_GRAN;
|
||||
bi.txbufpolicy = DAHDI_POLICY_WHEN_FULL;
|
||||
}
|
||||
|
||||
if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
|
||||
fprintf(stderr, "Error setting DAHDI bufinfo\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!as_sigchan) {
|
||||
if (ioctl(fd, DAHDI_AUDIOMODE, &x)) {
|
||||
fprintf(stderr, "Error setting DAHDI bufinfo\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
int one = 1;
|
||||
ioctl(fd, DAHDI_HDLCFCSMODE, &one);
|
||||
/* we cannot reliably check for the ioctl return value here
|
||||
* as this command will fail if the slot _already_ was a
|
||||
* signalling slot before :( */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mux_q_empty_cb(struct osmo_i460_subchan *schan, void *user_data);
|
||||
|
||||
/* RTP data was received on the socket */
|
||||
static void ortp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *payload,
|
||||
unsigned int payload_len, uint16_t seq_number, uint32_t timestamp, bool marker)
|
||||
{
|
||||
struct sc_state *sc = rs->priv;
|
||||
struct osmo_trau_frame fr;
|
||||
int rc;
|
||||
|
||||
LOGSC(sc, "RTP Rx: %s\n", osmo_hexdump_nospc(payload, payload_len));
|
||||
|
||||
sc->t2r.type = g_ftype;
|
||||
|
||||
memset(&fr, 0, sizeof(fr));
|
||||
fr.dir = OSMO_TRAU_DIR_DL;
|
||||
rc = osmo_rtp2trau(&fr, payload, payload_len, &sc->t2r);
|
||||
if (rc < 0) {
|
||||
LOGSC(sc, "Failed to convert RTP to TRAU");
|
||||
return;
|
||||
}
|
||||
|
||||
struct msgb *msg = msgb_alloc(2*40*8, "rtp2trau");
|
||||
rc = osmo_trau_frame_encode(msgb_data(msg), 2*40*8, &fr);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
msgb_put(msg, rc);
|
||||
osmo_i460_mux_enqueue(sc->i460_sc, msg);
|
||||
}
|
||||
|
||||
static void init(const char *fname)
|
||||
{
|
||||
struct stat st;
|
||||
int rc;
|
||||
|
||||
struct osmo_i460_schan_desc scd16_0 = {
|
||||
.rate = OSMO_I460_RATE_16k,
|
||||
.demux = {
|
||||
.num_bits = 40*8,
|
||||
.out_cb_bytes = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
rc = stat(fname, &st);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
if (S_ISCHR(st.st_mode)) {
|
||||
/* if this is a char device, we assume DAHDI */
|
||||
g_ts_is_writable = true;
|
||||
g_st.in_fd = open(fname, O_RDWR);
|
||||
OSMO_ASSERT(g_st.in_fd >= 0);
|
||||
dahdi_set_bufinfo(g_st.in_fd, false);
|
||||
} else {
|
||||
g_st.in_fd = open(fname, O_RDONLY);
|
||||
OSMO_ASSERT(g_st.in_fd >= 0);
|
||||
}
|
||||
|
||||
osmo_i460_ts_init(&g_st.i460_ts);
|
||||
|
||||
//for (i = 0; i < ARRAY_SIZE(g_st.sc); i++) {
|
||||
for (int i = 1; i < 3; i++) {
|
||||
struct sc_state *sc = &g_st.sc[i];
|
||||
sc->num = i;
|
||||
|
||||
scd16_0.bit_offset = i * 2;
|
||||
scd16_0.demux.user_data = sc;
|
||||
scd16_0.demux.out_cb_bits = i460_demux_bits_cb,
|
||||
scd16_0.mux.in_cb_queue_empty = mux_q_empty_cb;
|
||||
scd16_0.mux.user_data = sc;
|
||||
sc->i460_sc = osmo_i460_subchan_add(NULL, &g_st.i460_ts, &scd16_0);
|
||||
OSMO_ASSERT(sc->i460_sc != NULL);
|
||||
|
||||
char strbuf[16];
|
||||
snprintf(strbuf, sizeof(strbuf), "SC%u", sc->num);
|
||||
sc->sync_fi = osmo_trau_sync_alloc(NULL, strbuf, sync_frame_out_cb, OSMO_TRAU_SYNCP_16_FR_EFR, sc);
|
||||
OSMO_ASSERT(sc->sync_fi);
|
||||
sc->rtps = osmo_rtp_socket_create(NULL, OSMO_RTP_F_POLL);
|
||||
OSMO_ASSERT(sc->rtps);
|
||||
osmo_rtp_socket_set_pt(sc->rtps, RTP_PT_GSM_FULL);
|
||||
sc->rtps->rx_cb = ortp_rx_cb;
|
||||
sc->rtps->priv = sc;
|
||||
osmo_rtp_socket_bind(sc->rtps, "0.0.0.0", g_local_port + i*2);
|
||||
osmo_rtp_socket_connect(sc->rtps, g_remote_host, g_remote_port + i*2);
|
||||
//osmo_rtp_socket_autoconnect(sc->rtps);
|
||||
}
|
||||
}
|
||||
|
||||
static void mux_q_empty_cb(struct osmo_i460_subchan *schan, void *user_data)
|
||||
{
|
||||
struct sc_state *sc = user_data;
|
||||
struct msgb *msg = msgb_alloc(2*40*8, "mux-enq");
|
||||
struct osmo_trau_frame traufr = {
|
||||
.type = g_ftype,
|
||||
.dir = OSMO_TRAU_DIR_DL,
|
||||
};
|
||||
int rc;
|
||||
|
||||
LOGSC(sc, "EMPTY -> Generating Tx\n");
|
||||
|
||||
if (traufr.type == OSMO_TRAU16_FT_EFR) {
|
||||
traufr.c_bits[12-1] = 1; /* C12 = good u-link frame */
|
||||
traufr.c_bits[16-1] = 1; /* C16 = SP[eech]; no DTX */
|
||||
}
|
||||
|
||||
rc = osmo_trau_frame_encode(msgb_data(msg), 2*40*8, &traufr);
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
msgb_put(msg, rc);
|
||||
//LOGSC(sc, "Tx TRAU: %s\n", osmo_ubit_dump(cur, 40*8));
|
||||
osmo_i460_mux_enqueue(sc->i460_sc, msg);
|
||||
}
|
||||
|
||||
static void process(void)
|
||||
{
|
||||
uint8_t buf[D_BCHAN_TX_GRAN];
|
||||
int rc, nread;
|
||||
|
||||
while (rc = read(g_st.in_fd, buf, sizeof(buf))) {
|
||||
OSMO_ASSERT(rc == sizeof(buf));
|
||||
nread = rc;
|
||||
osmo_i460_demux_in(&g_st.i460_ts, buf, nread);
|
||||
|
||||
for (int i = 1; i < 3; i++) {
|
||||
struct sc_state *sc = &g_st.sc[i];
|
||||
int rc2 = osmo_rtp_socket_poll(sc->rtps);
|
||||
sc->rtps->rx_user_ts += 160;
|
||||
//printf("rtp_recv=%d (flags=%x)\n",rc2, sc->rtps->flags);
|
||||
}
|
||||
|
||||
/* write as many bytes as we just received */
|
||||
osmo_i460_mux_out(&g_st.i460_ts, buf, nread);
|
||||
if (g_ts_is_writable) {
|
||||
rc = write(g_st.in_fd, buf, nread);
|
||||
if (rc != nread)
|
||||
printf("rc=%d, nread=%d (%s)\n", rc, nread, strerror(errno));
|
||||
OSMO_ASSERT(rc == nread);
|
||||
}
|
||||
//usleep(20000);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging2(NULL, NULL);
|
||||
osmo_fsm_log_addr(false);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
|
||||
log_set_category_filter(osmo_stderr_target, DLMIB, true, LOGL_DEBUG);
|
||||
osmo_rtp_init(NULL);
|
||||
|
||||
init(argv[1]);
|
||||
process();
|
||||
}
|
|
@ -0,0 +1,530 @@
|
|||
libosmo-abis (1.5.1) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* dahdi: Log context (e1inpt_ts name) when opening timeslot devices
|
||||
* dahdi: Fix compilation with ancient DAHDI
|
||||
|
||||
[ Keith ]
|
||||
* DAHDI: Log hexdump of TX at level DEBUG, not ERROR
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* trau/osmo_ortp: support building without OrtpMemoryFunctions API
|
||||
* build: include git-version-gen into the release tarball
|
||||
|
||||
-- Vadim Yanitskiy <vyanitskiy@sysmocom.de> Sun, 28 Jan 2024 18:04:12 +0700
|
||||
|
||||
libosmo-abis (1.5.0) unstable; urgency=medium
|
||||
|
||||
[ Philipp Maier ]
|
||||
* e1_input: add define constant for full subslot (whole E1 timeslot)
|
||||
* trau_sync: allow multiple secondary sync patterns
|
||||
* trau_sync: add synchronization pattern for Ericsson RBS MCS9
|
||||
* trau_pcu_ericsson: change loglevel for CPS decoding
|
||||
* e1_input: add timeslot type NONE
|
||||
* e1_input: cleanup TS resources when timeslot type is changed
|
||||
* trau_pcu_ericsson_test: replace 64K CS1/CS2 testvectors
|
||||
* trau_pcu_ericsson: cosmetic: set hdr_good flag explictly to false
|
||||
* trau_pcu_ericsson: fix log line
|
||||
* trau_pcu_ericsson_test: fix comment, CS2 blocks are not CRC protected
|
||||
* trau_pcu_ericsson: add testvectors for MCS1-MCS8
|
||||
* trau_pcu_ericsson: add comment about uplink blocks
|
||||
* e1d: fix log output
|
||||
* e1d: clean up error logging in handle_ts_sign_read
|
||||
* e1d: maintain error log consistency in handle_ts_hdlc_read
|
||||
* e1d: fix sourcecode formatting
|
||||
* e1d: cosmetic: remove double whitespace
|
||||
* e1d: close fd when osmo_fd_setup fails
|
||||
* trau_pcu_ericsson: set uplink frame error bit correctly
|
||||
* e1d: do not set fd number to 0 after close
|
||||
* e1_input: add new driver callback function: line_create
|
||||
* e1d: initialize file descriptor numbers to -1 on startup
|
||||
* e1d: fix logic to detect if a timeslot is in use
|
||||
* e1d: get rid of strange file descriptor registered check
|
||||
* trau_pcu_ericsson: do not set unused D bits to 1
|
||||
* e1d: reconnect to osmo-e1d after connection loss
|
||||
|
||||
[ Andreas Eversberg ]
|
||||
* Add more L1 signals to common part
|
||||
* Add function to set Sa bits to common part
|
||||
* Add L1 signals to misdn driver
|
||||
* Add setting of Sa bits to misdn driver
|
||||
* Use correct bit order of RAW channels at misdn driver
|
||||
* misdn driver: replace printf and fprintf by logging functions
|
||||
* Rework mi_e1_line_update() and some of its sub routines
|
||||
* Send raw data with e1d as it arrives from application
|
||||
* e1d: Remove useless call of handle_ts_trau_write()
|
||||
* e1d: Add support for HDLC type channels
|
||||
* dahdi: Disable OSMO_FD_WRITE if HDLC/RAW write queue is empty
|
||||
* dahdi: Add missing msg->l2h
|
||||
* dahdi: Add msgb_free() to prevent memeory leaks
|
||||
|
||||
[ Oliver Smith ]
|
||||
* Cosmetic: osmo_ortp.h: fix typos
|
||||
* osmo_ortp.h: add RTP_PT_CSDATA
|
||||
* Run struct_endianness.py
|
||||
* debian: set compat level to 10
|
||||
|
||||
[ arehbein ]
|
||||
* Transition to use of 'telnet_init_default'
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* ipa: Call osmo_fd_unregister() before closing and changing bfd->fd
|
||||
|
||||
[ Mychaela N. Falconia ]
|
||||
* osmo_rtp2trau() for FR & EFR: correctly handle the no-data case
|
||||
* osmo_rtp2trau() for FR & EFR: set SP=0 in DL if the frame is a SID
|
||||
* osmo_rtp2trau() for FR & EFR UL: set C13 & C14 correctly
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* .gitignore: add new trau_{pcu_ericsson,sync} tests
|
||||
* cosmetic: struct e1inp_ts: fix coding style
|
||||
* cosmetic: e1inp_ipa_bts_rsl_close_n(): cache ipa_client_conn
|
||||
* fix use-after-free in ipaccess_bts_keepalive_fsm_alloc()
|
||||
* osmo_ortp: register a PayloadType for RTP_PT_CSDATA
|
||||
|
||||
[ Harald Welte ]
|
||||
* trau_sync: Add sync pattern for FA protocol of 3GPP TS 43.045
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 13:39:04 +0200
|
||||
|
||||
libosmo-abis (1.4.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* configure.ac: Set libosmo-e1d dependency version
|
||||
* cosmetic: Remove unneeded empty line
|
||||
* ipa: Allow users closing lower layer tcp/ipa connections
|
||||
* e1inp_line_ipa_rsl_ts(): Return null instead of reading out of bounds
|
||||
* ipaccess_close(): No need to lookup already available struct e1i_ts
|
||||
* ipaccess: Call line->ops->sign_link_down() only if set
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* libosmo{abis,trau}: add -no-undefined to *_la_LDFLAGS
|
||||
|
||||
[ Jan Engelhardt ]
|
||||
* pkgconf: require libosmocore
|
||||
|
||||
[ Harald Welte ]
|
||||
* input/unixsocket: Remove write delay timer
|
||||
* Support building with -Werror=strict-prototypes / -Werror=old-style-definition
|
||||
* trau_sync: Add the V.110 sync pattern
|
||||
* trau_sync: Fix typo tray_sync_pat_id -> trau_sync_pat_id
|
||||
|
||||
[ Oliver Smith ]
|
||||
* e1_input: enable tcp keepalive by default
|
||||
* ipaccess: require tcp keepalive related defines
|
||||
* ipaccess: update_fd_settings: use LOGL_ERROR
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* ipaccess: Clean up keepalive FSM properly
|
||||
* ipa: Add ipa_client_conn_open2 with connect timeout argument
|
||||
* ipaccess: Add connect timeout in e1inp_line
|
||||
* ipa: Guard against calling ipa_client_conn_open* multiple times
|
||||
|
||||
[ Philipp Maier ]
|
||||
* trau_sync: add Ericsson RBS GPRS TRAU synchronization pattern (16kbps)
|
||||
* trau_sync: support more than 40 bytes sync pattern.
|
||||
* trau_sync: add Ericsson RBS GPRS TRAU synchronization pattern (64kbps)
|
||||
* GPRS Trau frame encoder/decoder for Ericsson RBS
|
||||
* trau_pcu_ericsson: fix broken length check
|
||||
* trau_pcu_ericsson: also support ER_UL_CHMOD_NB_UNKN in 16k mode.
|
||||
|
||||
[ Eric ]
|
||||
* lapd_pcap: fix illegal VLA within struct
|
||||
|
||||
[ Andreas Eversberg ]
|
||||
* Fix support for HDLC/RAW type channels at mISDN.c
|
||||
* Add missing functions to send HDLC/RAW data
|
||||
* Remove mISDN header from received channel data
|
||||
* Initialize devinfo at misdn.c
|
||||
* Send raw data with mISDN as it arrives from application
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 13:15:21 +0100
|
||||
|
||||
libosmo-abis (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* lapd: don't add parenthesis around datalink name
|
||||
* input/ipaccess: Don't start zero-ms timer on every write
|
||||
* update git URLs (git -> https; gitea)
|
||||
* update git URLs (git -> https; gitea)
|
||||
* osmo_pcap_lapd_write: Fix write of uninitialized byte(s)
|
||||
|
||||
[ Oliver Smith ]
|
||||
* treewide: remove FSF address
|
||||
|
||||
[ Philipp Maier ]
|
||||
* ipaccess.c: register RSL/OML related osmo_fds for monitoring
|
||||
|
||||
[ Michael Iedema ]
|
||||
* stats: add RSL line number to TCP stats name
|
||||
* stats: add BTS index to IPA RSL TCP stats name
|
||||
* stats: add site_id scope to ipa-rsl tcp stat names
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* input/e1d: fix a memleak in handle_ts_raw_read()
|
||||
* input/e1d: use msgb_hexdump_l2() in handle_ts_raw_read()
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* input/ipaccess: Remove unneeded osmo_fd_write_enable()
|
||||
* input/ipaccess: Avoid extra poll() call when e1i_ts tx queue becomes empty
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 17:59:38 +0200
|
||||
|
||||
libosmo-abis (1.2.0) unstable; urgency=medium
|
||||
|
||||
[ Keith ]
|
||||
* Configure E1 pcap file per line
|
||||
* Log TRAU FSM at INFO not NOTICE
|
||||
* Fix up vty 'show' commands for E1 line/timeslots
|
||||
|
||||
[ Harald Welte ]
|
||||
* ipa: Introduce support for user-specific DSCP and priority
|
||||
* e1_input: Allow (vty) configuration of IP DSCP and socket priority
|
||||
* trau: Introduce osmo_rtp_socket_set_priority()
|
||||
|
||||
[ Philipp Maier ]
|
||||
* ipaccess: do not block ipaccess_line_update on failure
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Use new stat item/ctr getter APIs
|
||||
* osmo_ortp: Fix seqno reset to 0 upon ssrc_changed
|
||||
* e1_input: Fix line not removed from e1inp_line_list when freed
|
||||
* e1_input: Document e1inp_line_create()
|
||||
* ipaccess: Allow reconfiguring the ipa line during line_update()
|
||||
* ipaccess: e1inp_ipa_bts_rsl_connect: Fix memleak recreating ipa_client_conn
|
||||
* e1inp_input: Fix e1inp_line_clone removing original line from global list when freed
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 14:18:45 +0100
|
||||
|
||||
libosmo-abis (1.1.1) unstable; urgency=medium
|
||||
|
||||
* attempt to fix RPM spec file after recent soversion bump
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Wed, 24 Feb 2021 09:32:29 +0100
|
||||
|
||||
libosmo-abis (1.1.0) unstable; urgency=medium
|
||||
|
||||
[ Harald Welte ]
|
||||
* trau_frame: New API
|
||||
* trau_frame: Introduce support for Downlink Time Alignment
|
||||
* osmo_trau_frame_encode(): Check for sufficiently sized output buffer
|
||||
* Add new TRAU frame sync code
|
||||
* TRAU frame RTP conversion
|
||||
* Add 'trau2rtp' demo program illustrating the use of new TRAU code
|
||||
* Add missing build dependency to libosmocodec-dev
|
||||
* trau_frame: Fix computation of odd parity while encoding HR frames
|
||||
* trau_sync: Check return value of osmo_fsm_register()
|
||||
* trau_frame: Add missing break statement in osmo_trau_frame_encode()
|
||||
* Fix RPM spec file (.so.6 -> .so.9)
|
||||
* debian: Build libosmo-abis with osmo-e1d support
|
||||
* Revert "debian: Build libosmo-abis with osmo-e1d support"
|
||||
* Use OSMO_FD_* instead of deprecated BSC_FD_*
|
||||
* Use osmo_fd_setup() wherever applicable
|
||||
* e1_input: Support I.460 timeslot type
|
||||
* osmo_ortp: Don't print ERROR message for every missing RTP frame
|
||||
* migrate to osmo_fd_{read,write}_{enable,disable}()
|
||||
|
||||
[ Philipp Maier ]
|
||||
* trau_sync: prevent false positive synchronization on startup
|
||||
* trau_sync: make sync pattern configurable
|
||||
* vty: add attributes to VTY commands indicating when they apply
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* debian/control: change maintainer to the Osmocom team / mailing list
|
||||
* vty: use install_lib_element() and install_lib_element_ve()
|
||||
* cosmetic: fix spelling in logging messages: existAnt -> existEnt
|
||||
* ipaccess: fix verbosive logging in __handle_ts1_write()
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* tests: Disable stderr log color in trau_sync_test
|
||||
* contrib/jenkins: Enable parallel make in make distcheck
|
||||
* ipaccess: Use LOGPITS macro in __handle_ts1_write
|
||||
* ipaccess: Fix log error printed on wrong conditional branch
|
||||
* ipaccess: Fix wrong assertion in ipaccess_drop() when used by BTS code
|
||||
* tests: trau_sync: Explicitly drop category from log
|
||||
* gitignore: Ignore autofoo files *~
|
||||
|
||||
[ Oliver Smith ]
|
||||
* configure.ac: set -std=gnu11
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 23 Feb 2021 16:21:49 +0100
|
||||
|
||||
libosmo-abis (1.0.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* add/clean big-endian packed structs (struct_endianess.py)
|
||||
|
||||
[ Harald Welte ]
|
||||
* dahdi: Don't use perror() directly, use osmocom logging instead
|
||||
* introduce and use logging macros with context on E1 line / timeslot
|
||||
* e1d: Use HAVE_E1D in C source, not ENABLE_E1D
|
||||
* e1d: add missing forward-declaration of e1inp_e1d_init()
|
||||
* e1d: Don't use perror() directly, use osmocom logging instead
|
||||
* e1d: Remove EXCEPTFD handling
|
||||
* e1d: Remove bogus vty_show function.
|
||||
* e1d: Use line->port_nr to specify e1d interface/line
|
||||
* e1d: Use LOGPIL/LOGPITS logging macros to give context
|
||||
* e1d: Don't connect to e1d at program start time
|
||||
* e1d: Implement varions non-LAPD timeslot modes
|
||||
* Add rtp_test to show the double-bind bug of OS#4444
|
||||
* ortp: disable SO_REUSEADDR + SO_REUSEPORT
|
||||
* subchan_demux: Use 'ubit_t' for unpacked bit buffer; use const
|
||||
* trau_frame: use 'ubit_t' for unpacked bits
|
||||
* subchan_demux: Use ubit_t where appropriate
|
||||
* trau_frame.h: Fix definition of TRAU_FT_OM_UP
|
||||
* Fix subchan_demux_test compiler warning on some gcc versions
|
||||
* trau_frame: Fix AMR frame decoding
|
||||
* lapd: Always print context information when logging
|
||||
* fix compilation with --enable-e1d
|
||||
* lapd: Replace magic numbers with #defines
|
||||
* input/lapd.c: Enlarge message buffers for DL-SAP primitives
|
||||
* input/dahdi.c: Don't simply read beyond end of msgb
|
||||
* subchan_demux: Fix out-of-bounds write
|
||||
* e1d: Add new osmo_e1dp_client_ts_open() argument
|
||||
* e1d: Fix compilation after I4a088f91f23aaad05c5ab84a4783c1915d85aca6
|
||||
* contrib/jenkins.sh: Test builds with --enable-e1d
|
||||
* dahdi: Use osmo_revbytebits_buf() instead of local flip table
|
||||
* input/e1d: Fix support for TRAU slots
|
||||
* input/e1d: Add missing "RAW" timeslot support
|
||||
* e1_input_vty: Fix VTY help strings
|
||||
|
||||
[ Sylvain Munaut ]
|
||||
* e1d: Initial osmo-e1d support
|
||||
* e1_input: Allow to change the pcap fd and/or unset it
|
||||
* e1_input: Add VTY command to enable PCAP debug output
|
||||
|
||||
[ Oliver Smith ]
|
||||
* ipaccess.c: line_already_initialized: int -> bool
|
||||
* osmo_ortp: add osmo_rtp_socket_set_dscp()
|
||||
* contrib: import RPM spec
|
||||
* contrib: integrate RPM spec
|
||||
* Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
|
||||
* src/input/ipaccess.c: set TCP_NODELAY
|
||||
|
||||
[ Eric Wild ]
|
||||
* add ipa ping/pong keepalive for OML/RSL links between bts and bsc
|
||||
|
||||
[ Eric ]
|
||||
* configure.ac: fix libtool issue with clang and sanitizer
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* e1_input: refcount inc line during e1_sign_link_create, not during line update
|
||||
* ipaccess.c: Drop repeated ipaccess_keepalive_fsm_cleanup in write error path
|
||||
* ipaccess: Fix log formatting of RESP_ID IPA attributes
|
||||
* cosmetic: e1_input.h: Fix trailing whitespace
|
||||
* e1_input: Use osmo_use_count in e1inp_line
|
||||
* ipaccess: Drop e1inp_line reference in ipacess_drop()
|
||||
* ipacces: Fix e1inp_line reference put in ipaccess_close
|
||||
* ipaccess: Set bfd->data to NULL before releasing its reference
|
||||
* ipaccess_recvmsg: Clean up release steps upon error condition
|
||||
* ipaccess_recvmsg: Assert the new bfd from new line differs from the old one
|
||||
* ipaccess_recvmsg: Untangle code updating line
|
||||
* cosmetic: lapd: Fix trailing whitespace
|
||||
* lapd: Use lapd_dl_init2 instead of deprecated lapd_dl_init
|
||||
* tests: Use API e1inp_line_put2 instead of deprecated e1inp_line_put
|
||||
* ipaccess: Fix use-after-free in ipaccess_drop()
|
||||
|
||||
-- Harald Welte <laforge@osmocom.org> Thu, 13 Aug 2020 12:09:39 +0200
|
||||
|
||||
libosmo-abis (0.8.0) unstable; urgency=medium
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* ipa: ipa_server_link_close() add checks of link state
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* ipa: Allow setting local addr and port for struct ipa_client_conn
|
||||
|
||||
[ Harald Welte ]
|
||||
* dahdi: Use ioctl(DAHDI_SPECIFY) instead of legacy /dev/dahdi/%u
|
||||
* Enable DAHDI support by default; require --disable-dahdi otherwise
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* input/ipaccess.c: propagate errors from ipa_parse_unitid()
|
||||
* input/ipaccess.c: fix debug message on receipt of IPAC_MSGT_ID_GET
|
||||
* e1_input.c: make reference counting get() / put() more verbose
|
||||
* input/ipa_keepalive.c: make sure IPA keepalive FSM is registered
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 02 Jan 2020 20:53:32 +0100
|
||||
|
||||
libosmo-abis (0.7.0) unstable; urgency=medium
|
||||
|
||||
[ Max ]
|
||||
* Set local IP in ipa_server_link properly
|
||||
* Log peer's port in accept callback
|
||||
|
||||
[ Harald Welte ]
|
||||
* ipa: Make ipa_server_conn_destroy() re-entrant
|
||||
* Add IPA keep-alive FSM implementation
|
||||
* ipa_keepalive_fsm: Fix OSMO_IPA_KA_E_STOP allstate event
|
||||
* ipa_keepalive_fsm: Suppress error messages for INIT -> INIT transition
|
||||
* ipaccess.c: Avoid calling close(-1) on error path
|
||||
|
||||
[ Oliver Smith ]
|
||||
* tests: use -no-install libtool flag to avoid ./lt-* scripts
|
||||
* contrib/jenkins.sh: run "make maintainer-clean"
|
||||
|
||||
[ Sylvain Munaut ]
|
||||
* rtp: Add 'autoconnect' feature to the osmo_rtp_socket
|
||||
|
||||
[ Eric Wild ]
|
||||
* ipaccess: allow tcp keepalive for ipa clients
|
||||
* add TCP_USER_TIMEOUT to keepalive
|
||||
* extend the ipa keepalive fsm
|
||||
|
||||
[ Debian Mobcom Maintainers ]
|
||||
* spelling
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Sun, 21 Jul 2019 21:32:08 +0200
|
||||
|
||||
libosmo-abis (0.6.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* fix strncpy bugs in ipaccess.c, ipa_proxy.c
|
||||
* ipa: don't crash on missing IPA ID GET message
|
||||
|
||||
[ Harald Welte ]
|
||||
* Migrate from ipa_ccm_idtag_parse to ipa_ccm_id_resp_parse()
|
||||
* debian/rules: Don't overwrite .tarball-version
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* ipaccess: Use osmo_fd_setup to set up callbacks
|
||||
* ipaccess_rcvmsg: Fix bug introduced in previous commit
|
||||
* ipa: Simplify code in ipa_client_read
|
||||
* ipa: Simplify code in ipa_server_conn_read
|
||||
* ipa: Allow signalling fd destroyed in ipa_client_read
|
||||
* ipa: Allow signalling fd destroyed in ipa_server_conn_read
|
||||
* ipaccess: Return -EBADF when closing socket in ipaccess_bts_read_cb
|
||||
* ipaccess: Allow passing return code in e1inp_line_ops->sign_link()
|
||||
* debian: Fix libosmoabis soname package version
|
||||
* debian: Conflict libosmoabis6 with libosmoabis5
|
||||
* ipacces: Log correct trx_nr during IPA GET
|
||||
* ipaccess: Simplify handling of ipaccess e1line ts
|
||||
* e1_input.h: Set correct type for input_signal_data->link_type
|
||||
* osmo_ortp: Log domain and fix strings without endl char
|
||||
|
||||
[ Stefan Sperling ]
|
||||
* Properly deal with sockaddr_un socket path length limitations.
|
||||
* ensure unix socket paths are NUL-terminated for bind/connect
|
||||
|
||||
-- Harald Welte <laforge@gnumonks.org> Sat, 19 Jan 2019 22:20:46 +0100
|
||||
|
||||
libosmo-abis (0.5.1) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* ipaccess: ipaccess_rcvmsg: Drop unneeded memcpy
|
||||
* ipaccess: Avoid using released line and bfd in ipaccess_fd_cb
|
||||
* e1_input.c: Replace '.' in counter names with ':'
|
||||
|
||||
[ Stefan Sperling ]
|
||||
* fix double-free/use-after-free of pointers in struct e1inp_line
|
||||
|
||||
[ Philipp Maier ]
|
||||
* ortp: make sure the ortp scheduler is started
|
||||
* ortp: reset rtp session on SSRC changes
|
||||
* ortp: detect ssrc changes immediately
|
||||
* ortp: enable scheduled mode on rtp socket creation
|
||||
* Revert "ortp: make sure the ortp scheduler is started"
|
||||
* Revert "ortp: enable scheduled mode on rtp socket creation"
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 27 Jul 2018 18:15:18 +0200
|
||||
|
||||
libosmo-abis (0.5.0) unstable; urgency=medium
|
||||
|
||||
[ Max ]
|
||||
* Use value string check from osmo-ci
|
||||
* cosmetic: update ipaccess_bts_handle_ccm()
|
||||
* IPA: log remote address
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* cosmetic: ipa.c: use msgb_dequeue(), drop local var
|
||||
* jenkins: use osmo-clean-workspace.sh before and after build
|
||||
* vty: skip installing cmds now always installed by default
|
||||
* add --enable-sanitize config option
|
||||
* configure: add --enable-werror
|
||||
* jenkins.sh: use --enable-werror configure flag, not CFLAGS
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* debian/rules: show testsuite.log when tests are failing
|
||||
* unixsocket: fix a potential string overflow
|
||||
|
||||
[ Harald Welte ]
|
||||
* debian/copyright: fix upstream-name
|
||||
* Add SPDX-License-Identifier to all source files
|
||||
* Add a new osmo_rtp_set_source_desc() function to set the RTCP SDES items
|
||||
* debian/control: Fix Vcs-Browser
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* contrib: jenkins.sh: Disable doxygen in libosmocore build
|
||||
* e1_input.h: Remove dead declaration of unexistent API ipaccess_setup
|
||||
* configure.ac: Fix condition check for RTP_SIGNAL_PTR_CAST define
|
||||
* use osmo_init_logging2
|
||||
* git-version-gen: Check first for new tag format
|
||||
|
||||
[ Stefan Sperling ]
|
||||
* preserve 'when' flags of new osmo_fd in ipaccess_rcvmsg()
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 16:12:04 +0200
|
||||
|
||||
libosmo-abis (0.4.0) unstable; urgency=medium
|
||||
|
||||
* Move forward towards a new release.
|
||||
* libosmo-abis API change major: add parameter to struct
|
||||
input_signal_data
|
||||
* libosmo-trau API change major: add parameters to rx_cb()
|
||||
callack in osmo_ortp.h
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Fri, 25 Aug 2017 16:09:46 +0200
|
||||
|
||||
libosmo-abis (0.3.2) unstable; urgency=medium
|
||||
|
||||
* Bump so version to re-link libosmovty
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Tue, 03 Nov 2015 09:42:01 +0100
|
||||
|
||||
libosmo-abis (0.3.1) unstable; urgency=medium
|
||||
|
||||
* New upstream release of libosmo-abis
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@freyther.de> Sun, 18 Jan 2015 19:26:40 +0100
|
||||
|
||||
libosmo-abis (0.3.0) unstable; urgency=medium
|
||||
|
||||
* New upstream release of libosmo-abis
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@freyther.de> Mon, 08 Sep 2014 07:29:39 +0200
|
||||
|
||||
libosmo-abis (0.2.0) unstable; urgency=medium
|
||||
|
||||
* New upstream release of libosmo-abis
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@freyther.de> Mon, 08 Sep 2014 07:29:19 +0200
|
||||
|
||||
libosmo-abis (0.1.6) unstable; urgency=medium
|
||||
|
||||
* New upstream release of libosmo-abis
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@freyther.de> Mon, 20 Jan 2014 10:33:36 +0100
|
||||
|
||||
libosmo-abis (0.1.5+git1) unstable; urgency=low
|
||||
|
||||
* Rebuild 0.1.5 with the right libosmocore depedency
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@freyther.de> Thu, 12 Dec 2013 11:47:01 +0100
|
||||
|
||||
libosmo-abis (0.1.4) unstable; urgency=low
|
||||
|
||||
* New upstream release
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@freyther.de> Tue, 06 Nov 2012 13:33:45 +0100
|
||||
|
||||
libosmo-abis (0.1.3+git3-1) precise; urgency=low
|
||||
|
||||
* Fix version issue.
|
||||
|
||||
-- Eric Butler <eric@codebutler.com> Tue, 14 Aug 2012 20:50:50 -0700
|
||||
|
||||
libosmo-abis (0.1.3+git3) precise; urgency=low
|
||||
|
||||
Created new Ubuntu package.
|
||||
|
||||
-- Eric Butler <eric@codebutler.com> Tue, 14 Aug 2012 13:00:18 -0700
|
|
@ -0,0 +1 @@
|
|||
10
|
|
@ -0,0 +1,81 @@
|
|||
Source: libosmo-abis
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Section: libs
|
||||
Priority: optional
|
||||
Build-Depends: debhelper (>= 10),
|
||||
autotools-dev,
|
||||
autoconf,
|
||||
automake,
|
||||
dahdi-source,
|
||||
libtool,
|
||||
dh-autoreconf,
|
||||
libdpkg-perl,
|
||||
git,
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
pkg-config,
|
||||
libortp-dev
|
||||
Standards-Version: 3.9.7
|
||||
Vcs-Git: https://gitea.osmocom.org/osmocom/libosmo-abis
|
||||
Vcs-Browser: https://gitea.osmocom.org/osmocom/libosmo-abis
|
||||
Homepage: https://projects.osmocom.org/projects/libosmo-abis
|
||||
|
||||
Package: libosmo-abis
|
||||
Section: oldlibs
|
||||
Architecture: any
|
||||
Depends: libosmoabis13 (= ${binary:Version}), libosmotrau2 (= ${binary:Version}), ${misc:Depends}
|
||||
Multi-Arch: same
|
||||
Description: Legacy package for libosmo-abis
|
||||
libosmo-abis is an empty package helping in the transition to one
|
||||
package per DSO.
|
||||
|
||||
Package: libosmoabis13
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Description: GSM A-bis handling
|
||||
This library contains common/shared code regarding the A-bis interface between
|
||||
the GSM Base transceiver station (BTS) and the GSM Base station controller BSC.
|
||||
.
|
||||
It also implements drivers for mISDN and DAHDI based E1 cards, as well as some
|
||||
A-bis/IP dialects.
|
||||
|
||||
Package: libosmotrau2
|
||||
Section: libs
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Description: GSM trau handling
|
||||
This library implements the Transcoder and Rate Adaptation Unit (TRAU) for
|
||||
GSM systems. It is closely related to the libosmo-abis library.
|
||||
.
|
||||
TRAU performs transcoding function for speech channels and rate adaptation (RA)
|
||||
for data channels in the GSM network.
|
||||
|
||||
Package: libosmo-abis-dev
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libdevel
|
||||
Depends: ${misc:Depends},
|
||||
libosmotrau2 (= ${binary:Version}),
|
||||
libosmoabis13 (= ${binary:Version})
|
||||
Description: Development headers for A-bis interface
|
||||
The libosmo-abis library contains common/shared code regarding the A-bis
|
||||
interface between GSM BTS and BSC. This package in particular contains the
|
||||
development files needed to develop with the libosmoabis and libosmotrau
|
||||
libraries.
|
||||
|
||||
Package: libosmo-abis-dbg
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: debug
|
||||
Priority: extra
|
||||
Depends: libosmoabis13 (= ${binary:Version}),
|
||||
libosmotrau2 (= ${binary:Version}),
|
||||
${misc:Depends}
|
||||
Description: Debug symbols for A-bis interface
|
||||
The libosmo-abis library contains common/shared code regarding the A-bis
|
||||
interface between GSM BTS and BSC. This package in particular contains the
|
||||
debug symbols for the two libraries libosmoabis and libosmotrau.
|
|
@ -0,0 +1,83 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: libosmo-abis
|
||||
Source: https://gitea.osmocom.org/osmocom/libosmo-abis
|
||||
|
||||
Files: *
|
||||
Copyright: 2008 Daniel Willmann <daniel@totalueberwachung.de>
|
||||
2008-2012 Harald Welte <laforge@gnumonks.org>
|
||||
2009-2010 Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
2010,2014 On-Waves
|
||||
2011-2014 Pablo Neira Ayuso <pablo@gnumonks.org>
|
||||
2012 Tobias Engel
|
||||
2014 sysmocom - s.f.m.c. GmBH
|
||||
License: AGPL-3+
|
||||
|
||||
Files: src/input/lapd.c
|
||||
src/trau/osmo_ortp.c
|
||||
src/input/dahdi.c
|
||||
Copyright: 2008-2011 Harald Welte <laforge@gnumonks.org>
|
||||
2009 oystein@homelien.no
|
||||
2009 Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
2010 Digium and Matthew Fredrickson <creslin@digium.com>
|
||||
2011 Andreas Everberg <jolly@eversberg.eu>
|
||||
License: GPL-2+
|
||||
|
||||
Files: include/mISDNif.h
|
||||
Copyright: 2008 Karsten Keil <kkeil@novell.com>
|
||||
License: LGPL-2.1
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2012 Eric Butler <eric@codebutler.com>
|
||||
2012-2015 Holger Hans Peter Freyther <holger@moiji-mobile.com>
|
||||
2015 Ruben Undheim <ruben.undheim@gmail.com>
|
||||
License: AGPL-3+
|
||||
|
||||
|
||||
License: AGPL-3+
|
||||
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/>.
|
||||
|
||||
|
||||
License: GPL-2+
|
||||
This package 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, version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
|
||||
|
||||
License: LGPL-2.1
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation;
|
||||
version 2.1 of the License.
|
||||
.
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU Lesser General
|
||||
Public License version 2.1 can be found in "/usr/share/common-licenses/LGPL-2.1".
|
|
@ -0,0 +1,5 @@
|
|||
usr/include/*
|
||||
usr/lib/*/lib*.a
|
||||
usr/lib/*/lib*.so
|
||||
usr/lib/*/lib*.la
|
||||
usr/lib/*/pkgconfig/*
|
|
@ -0,0 +1 @@
|
|||
usr/lib/*/libosmoabis.so.*
|
|
@ -0,0 +1 @@
|
|||
usr/lib/*/libosmotrau.so.*
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
|
||||
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
|
||||
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
|
||||
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
|
||||
%:
|
||||
dh $@ --with autoreconf --fail-missing
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=libosmo-abis-dbg
|
||||
|
||||
override_dh_clean:
|
||||
dh_clean
|
||||
rm -f tests/package.m4 tests/testsuite .version
|
||||
|
||||
override_dh_auto_test:
|
||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
|
@ -93,8 +93,8 @@ if test -n "$v"
|
|||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
v=`git describe --abbrev=4 HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 --match='v*' HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
noinst_HEADERS=mISDNif.h internal.h
|
||||
|
||||
SUBDIRS=osmocom
|
||||
nobase_include_HEADERS = osmocom/abis/ipa.h osmocom/abis/trau_frame.h \
|
||||
osmocom/abis/ipa_proxy.h osmocom/abis/ipaccess.h osmocom/abis/abis.h \
|
||||
osmocom/abis/subchan_demux.h osmocom/abis/e1_input.h \
|
||||
osmocom/abis/lapd.h osmocom/abis/lapd_pcap.h osmocom/trau/osmo_ortp.h \
|
||||
osmocom/abis/unixsocket_proto.h \
|
||||
osmocom/trau/trau_frame.h \
|
||||
osmocom/trau/trau_sync.h \
|
||||
osmocom/trau/trau_pcu_ericsson.h \
|
||||
osmocom/trau/trau_rtp.h
|
||||
|
|
|
@ -3,16 +3,40 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Amount of data to write to a B-channel in every write() call */
|
||||
#define D_BCHAN_TX_GRAN 160
|
||||
|
||||
struct osmo_fd;
|
||||
struct e1inp_sign_link;
|
||||
struct e1inp_ts;
|
||||
|
||||
struct ipa_proto_pars {
|
||||
uint8_t dscp;
|
||||
uint8_t priority;
|
||||
};
|
||||
|
||||
struct ipa_pars {
|
||||
struct ipa_proto_pars oml;
|
||||
struct ipa_proto_pars rsl;
|
||||
};
|
||||
|
||||
/* global parameters of IPA input driver */
|
||||
extern struct ipa_pars g_e1inp_ipaccess_pars;
|
||||
|
||||
/* talloc context for libosmo-abis. */
|
||||
extern void *libosmo_abis_ctx;
|
||||
|
||||
/* use libosmo_abis_init, this is only for internal use. */
|
||||
void e1inp_init(void);
|
||||
|
||||
/* hsl requires these functions defined in ipaccess driver. */
|
||||
void e1inp_ipa_set_bind_addr(const char *ip_bind_addr);
|
||||
const char *e1inp_ipa_get_bind_addr(void);
|
||||
|
||||
/* ipaccess.c requires these functions defined here */
|
||||
struct msgb;
|
||||
void ipaccess_prepend_header(struct msgb *msg, int proto);
|
||||
struct msgb *ipa_msg_alloc(int headroom);
|
||||
void ipa_msg_push_header(struct msgb *msg, uint8_t proto);
|
||||
|
||||
int e1inp_int_snd_event(struct e1inp_ts *ts, struct e1inp_sign_link *link, int evt);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -193,6 +193,8 @@
|
|||
#define L1_SIGNAL_RDI_ON 0x0015
|
||||
#define L1_SIGNAL_SLIP_RX 0x0020
|
||||
#define L1_SIGNAL_SLIP_TX 0x0021
|
||||
#define L1_SIGNAL_SA_BITS 0x0100
|
||||
#define L1_SIGNAL_SA_MASK 0x00FF
|
||||
|
||||
/*
|
||||
* protocol ids
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
SUBDIRS=abis
|
|
@ -1,10 +0,0 @@
|
|||
osmoabis_HEADERS = abis.h \
|
||||
e1_input.h \
|
||||
lapd.h \
|
||||
subchan_demux.h \
|
||||
ipa.h \
|
||||
ipa_proxy.h \
|
||||
ipaccess.h \
|
||||
trau_frame.h
|
||||
|
||||
osmoabisdir = $(includedir)/osmocom/abis
|
|
@ -5,19 +5,31 @@
|
|||
#include <netinet/in.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/use_count.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
#include <osmocom/abis/subchan_demux.h>
|
||||
#include <osmocom/abis/lapd.h>
|
||||
|
||||
#define NUM_E1_TS 32
|
||||
#define E1INP_USE_DEFAULT (-1)
|
||||
|
||||
#define LOGPITS(e1ts, ss, level, fmt, args ...) \
|
||||
LOGP(ss, level, "E1TS(%u:%u) " fmt, (e1ts)->line->num, (e1ts)->num, ## args)
|
||||
|
||||
#define LOGPIL(e1l, ss, level, fmt, args ...) \
|
||||
LOGP(ss, level, "E1L(%u) " fmt, (e1l)->num, ## args)
|
||||
|
||||
enum e1inp_sign_type {
|
||||
E1INP_SIGN_NONE,
|
||||
E1INP_SIGN_OML,
|
||||
E1INP_SIGN_RSL,
|
||||
E1INP_SIGN_OSMO, /* IPA CCM OSMO sub-type */
|
||||
};
|
||||
const char *e1inp_signtype_name(enum e1inp_sign_type tp);
|
||||
extern const struct value_string e1inp_sign_type_names[5];
|
||||
|
||||
enum e1inp_ctr {
|
||||
E1I_CTR_HDLC_ABORT,
|
||||
|
@ -28,6 +40,7 @@ enum e1inp_ctr {
|
|||
};
|
||||
|
||||
struct e1inp_ts;
|
||||
struct vty;
|
||||
|
||||
struct e1inp_sign_link {
|
||||
/* list of signalling links */
|
||||
|
@ -38,7 +51,7 @@ struct e1inp_sign_link {
|
|||
|
||||
enum e1inp_sign_type type;
|
||||
|
||||
/* trx for msg->trx of received msgs */
|
||||
/* trx for msg->trx of received msgs */
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
/* msgb queue of to-be-transmitted msgs */
|
||||
|
@ -59,8 +72,12 @@ enum e1inp_ts_type {
|
|||
E1INP_TS_TYPE_NONE,
|
||||
E1INP_TS_TYPE_SIGN,
|
||||
E1INP_TS_TYPE_TRAU,
|
||||
E1INP_TS_TYPE_RAW,
|
||||
E1INP_TS_TYPE_HDLC,
|
||||
E1INP_TS_TYPE_I460,
|
||||
};
|
||||
const char *e1inp_tstype_name(enum e1inp_ts_type tp);
|
||||
extern const struct value_string e1inp_ts_type_names[];
|
||||
|
||||
/* A timeslot in the E1 interface */
|
||||
struct e1inp_ts {
|
||||
|
@ -70,6 +87,9 @@ struct e1inp_ts {
|
|||
/* to which line do we belong ? */
|
||||
struct e1inp_line *line;
|
||||
|
||||
/* LAPD instance, if any */
|
||||
struct lapd_instance *lapd;
|
||||
|
||||
union {
|
||||
struct {
|
||||
/* list of all signalling links on this TS */
|
||||
|
@ -85,6 +105,21 @@ struct e1inp_ts {
|
|||
/* subchannel muxer for frames to E1 */
|
||||
struct subch_mux mux;
|
||||
} trau;
|
||||
struct {
|
||||
/* call-back for every received frame */
|
||||
void (*recv_cb)(struct e1inp_ts *ts, struct msgb *msg);
|
||||
/* queue of pending to-be-transmitted msgbs */
|
||||
struct llist_head tx_queue;
|
||||
} raw;
|
||||
struct {
|
||||
/* call-back for every received frame */
|
||||
void (*recv_cb)(struct e1inp_ts *ts, struct msgb *msg);
|
||||
/* queue of pending to-be-transmitted msgbs */
|
||||
struct llist_head tx_queue;
|
||||
} hdlc;
|
||||
struct {
|
||||
struct osmo_i460_timeslot i460_ts;
|
||||
} i460;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
|
@ -94,24 +129,33 @@ struct e1inp_ts {
|
|||
struct {
|
||||
/* ip.access driver has one fd for each ts */
|
||||
struct osmo_fd fd;
|
||||
/* ipa keep-alive */
|
||||
struct osmo_fsm_inst *ka_fsm;
|
||||
} ipaccess;
|
||||
struct {
|
||||
/* DAHDI driver has one fd for each ts */
|
||||
struct osmo_fd fd;
|
||||
struct lapd_instance *lapd;
|
||||
} dahdi;
|
||||
struct {
|
||||
/* osmo-e1d driver has one fd for each ts */
|
||||
struct osmo_fd fd;
|
||||
} e1d;
|
||||
struct {
|
||||
struct osmo_fd fd;
|
||||
} rs232;
|
||||
} driver;
|
||||
|
||||
struct msgb *pending_msg;
|
||||
};
|
||||
|
||||
#define E1_SUBSLOT_FULL 0xff
|
||||
|
||||
struct gsm_e1_subslot {
|
||||
/* Number of E1 link */
|
||||
uint8_t e1_nr;
|
||||
/* Number of E1 TS inside E1 link */
|
||||
uint8_t e1_ts;
|
||||
/* Sub-slot within the E1 TS, 0xff if full TS */
|
||||
/* Sub-slot within the E1 TS, 0xff (E1_SUBSLOT_FULL) if full TS */
|
||||
uint8_t e1_ts_ss;
|
||||
};
|
||||
|
||||
|
@ -122,13 +166,33 @@ enum e1inp_line_role {
|
|||
E1INP_LINE_R_MAX
|
||||
};
|
||||
|
||||
/* Notes on setting/getting Sa bits (TABLE 5A and 5B of ITU-T G.704)
|
||||
* The sa_bits byte contains the Sa bits in TS 0, if multi frame is used:
|
||||
* bit 7 = Sa8, bit 6 = Sa7, bit 5 = Sa5, bit 4 = Sa4, bit 3 = Sa64, bit 2 = Sa63, bit 1 = Sa62, bit 0 = Sa61.
|
||||
* The sa_bits byte contains the Sa bits in TS 0, if double frame is used:
|
||||
* bit 7 = Sa8, bit 6 = Sa7, bit 5 = Sa5, bit 4 = Sa4, bit 0 = Sa6.
|
||||
* The received Sa bits are reported whenever they change (and are stable for some frames, depending on the
|
||||
* implementation). Before any report, all Sa bits are assumed to be set to 1.
|
||||
* Setting sa_bits byte will change the transmitted Sa bits on TS0.
|
||||
* Before setting them, all Sa bits are transmitted as 1.
|
||||
*/
|
||||
|
||||
struct e1inp_driver {
|
||||
struct llist_head list;
|
||||
const char *name;
|
||||
int (*want_write)(struct e1inp_ts *ts);
|
||||
int (*line_update)(struct e1inp_line *line);
|
||||
void (*close)(struct e1inp_sign_link *link);
|
||||
void (*vty_show)(struct vty *vty, struct e1inp_line *line);
|
||||
int default_delay;
|
||||
int has_keepalive;
|
||||
const char *bind_addr;
|
||||
|
||||
/* Set Sa bits to transmit in TS0 (MSB to LSB): Sa8 Sa7 Sa5 Sa4 Sa64 Sa63 Sa62 Sa61/Sa6 */
|
||||
int (*set_sa_bits)(struct e1inp_line *line, uint8_t sa_bits);
|
||||
|
||||
/* Optional callback to perform driver specific initialization when the line is created. */
|
||||
int (*line_create)(struct e1inp_line *line);
|
||||
};
|
||||
|
||||
struct e1inp_line_ops {
|
||||
|
@ -146,25 +210,47 @@ struct e1inp_line_ops {
|
|||
|
||||
struct e1inp_sign_link * (*sign_link_up)(void *unit_info, struct e1inp_line *line, enum e1inp_sign_type type);
|
||||
void (*sign_link_down)(struct e1inp_line *line);
|
||||
/* Called when a new message arrives. -EBADF must be returned if the osmo_fd in link (msg->dst) is destroyed. */
|
||||
int (*sign_link)(struct msgb *msg);
|
||||
};
|
||||
|
||||
struct e1inp_line {
|
||||
struct llist_head list;
|
||||
int refcnt;
|
||||
int refcnt; /* unusued, kept for ABI compat, use_count is used instead */
|
||||
|
||||
unsigned int num;
|
||||
const char *name;
|
||||
unsigned int port_nr;
|
||||
char *sock_path;
|
||||
struct rate_ctr_group *rate_ctr;
|
||||
|
||||
/* tcp keepalive configuration */
|
||||
int keepalive_num_probes; /* 0: disable, num, or E1INP_USE_DEFAULT */
|
||||
int keepalive_idle_timeout; /* secs, or E1INP_USE_DEFAULT */
|
||||
int keepalive_probe_interval; /* secs or E1INP_USE_DEFAULT */
|
||||
|
||||
/* ipa ping/pong keepalive params */
|
||||
struct ipa_keepalive_params *ipa_kap;
|
||||
|
||||
/* array of timestlots */
|
||||
struct e1inp_ts ts[NUM_E1_TS];
|
||||
unsigned int num_ts;
|
||||
|
||||
const struct e1inp_line_ops *ops;
|
||||
|
||||
struct e1inp_driver *driver;
|
||||
void *driver_data;
|
||||
|
||||
struct osmo_use_count use_count;
|
||||
|
||||
/* file name and file descriptor of pcap for this line */
|
||||
char *pcap_file;
|
||||
int pcap_fd;
|
||||
|
||||
unsigned int connect_timeout;
|
||||
};
|
||||
#define e1inp_line_ipa_oml_ts(line) (&line->ts[0])
|
||||
#define e1inp_line_ipa_rsl_ts(line, trx_id) (((1 + (trx_id)) < NUM_E1_TS) ? (&line->ts[1 + (trx_id)]) : NULL)
|
||||
|
||||
/* SS_L_INPUT signals */
|
||||
enum e1inp_signal_input {
|
||||
|
@ -175,17 +261,27 @@ enum e1inp_signal_input {
|
|||
S_L_INP_LINE_INIT,
|
||||
S_L_INP_LINE_ALARM,
|
||||
S_L_INP_LINE_NOALARM,
|
||||
S_L_INP_LINE_LOS,
|
||||
S_L_INP_LINE_NOLOS,
|
||||
S_L_INP_LINE_AIS,
|
||||
S_L_INP_LINE_NOAIS,
|
||||
S_L_INP_LINE_RAI,
|
||||
S_L_INP_LINE_NORAI,
|
||||
S_L_INP_LINE_SLIP_RX,
|
||||
S_L_INP_LINE_SLIP_TX,
|
||||
S_L_INP_LINE_SA_BITS,
|
||||
S_L_INP_LINE_LOF,
|
||||
S_L_INP_LINE_NOLOF,
|
||||
};
|
||||
|
||||
extern const struct value_string e1inp_signal_names[];
|
||||
|
||||
/* register a driver with the E1 core */
|
||||
int e1inp_driver_register(struct e1inp_driver *drv);
|
||||
|
||||
/* fine a previously registered driver */
|
||||
struct e1inp_driver *e1inp_driver_find(const char *name);
|
||||
|
||||
/* register a line with the E1 core */
|
||||
int e1inp_line_register(struct e1inp_line *line);
|
||||
|
||||
/* get a line by its ID */
|
||||
struct e1inp_line *e1inp_line_find(uint8_t e1_nr);
|
||||
|
||||
|
@ -193,13 +289,17 @@ struct e1inp_line *e1inp_line_find(uint8_t e1_nr);
|
|||
struct e1inp_line *e1inp_line_create(uint8_t e1_nr, const char *driver_name);
|
||||
|
||||
/* clone one existing E1 input line */
|
||||
struct e1inp_line *e1inp_line_clone(void *ctx, struct e1inp_line *line);
|
||||
struct e1inp_line *e1inp_line_clone(void *ctx, struct e1inp_line *line, const char *use);
|
||||
|
||||
/* increment refcount use of E1 input line */
|
||||
void e1inp_line_get(struct e1inp_line *line);
|
||||
void e1inp_line_get(struct e1inp_line *line) OSMO_DEPRECATED("Use e1inp_line_get2() instead");
|
||||
|
||||
/* decrement refcount use of E1 input line, release if unused */
|
||||
void e1inp_line_put(struct e1inp_line *line);
|
||||
void e1inp_line_put(struct e1inp_line *line) OSMO_DEPRECATED("Use e1inp_line_put2() instead");
|
||||
|
||||
/* Convenience macros for struct foo instances. These are strict about use count errors. */
|
||||
#define e1inp_line_get2(line, USE) OSMO_ASSERT( osmo_use_count_get_put(&(line)->use_count, USE, 1) == 0 );
|
||||
#define e1inp_line_put2(line, USE) OSMO_ASSERT( osmo_use_count_get_put(&(line)->use_count, USE, -1) == 0 );
|
||||
|
||||
/* bind operations to one E1 input line */
|
||||
void e1inp_line_bind_ops(struct e1inp_line *line, const struct e1inp_line_ops *ops);
|
||||
|
@ -221,14 +321,34 @@ int e1inp_ts_config_sign(struct e1inp_ts *ts, struct e1inp_line *line);
|
|||
/* configure and initialize one timeslot dedicated to TRAU frames. */
|
||||
int e1inp_ts_config_trau(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
int (*trau_rcv_cb)(struct subch_demux *dmx, int ch,
|
||||
uint8_t *data, int len, void *_priv));
|
||||
const ubit_t *data, int len, void *_priv));
|
||||
|
||||
/* Call from the Stack: configuration of this TS has changed */
|
||||
int e1inp_update_ts(struct e1inp_ts *ts);
|
||||
/* configure and initialize one timeslot dedicated to RAW frames */
|
||||
int e1inp_ts_config_raw(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
void (*raw_recv_cb)(struct e1inp_ts *ts,
|
||||
struct msgb *msg));
|
||||
|
||||
/* configure and initialize one timeslot dedicated to HDLC frames */
|
||||
int e1inp_ts_config_hdlc(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
void (*hdlc_recv_cb)(struct e1inp_ts *ts,
|
||||
struct msgb *msg));
|
||||
|
||||
/* configure and initialize one timeslot dedicated to nothing */
|
||||
int e1inp_ts_config_none(struct e1inp_ts *ts, struct e1inp_line *line);
|
||||
|
||||
/*
|
||||
* configure Sa bits on TS0, if supported by driver (TABLE 5A and 5B of ITU-T G.704)
|
||||
* sa_bits (MSB to LSB): Sa8 Sa7 Sa5 Sa4 Sa64 Sa63 Sa62 Sa61/Sa6
|
||||
*/
|
||||
int e1inp_ts_set_sa_bits(struct e1inp_line *line, uint8_t sa_bits);
|
||||
|
||||
/* obtain a string identifier/name for the given timeslot */
|
||||
void e1inp_ts_name(char *out, size_t out_len, const struct e1inp_ts *ts);
|
||||
|
||||
/* Receive a packet from the E1 driver */
|
||||
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
uint8_t tei, uint8_t sapi);
|
||||
int e1inp_rx_ts_lapd(struct e1inp_ts *e1i_ts, struct msgb *msg);
|
||||
|
||||
/* called by driver if it wants to transmit on a given TS */
|
||||
struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
|
||||
|
@ -237,33 +357,56 @@ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
|
|||
/* called by driver in case some kind of link state event */
|
||||
int e1inp_event(struct e1inp_ts *ts, int evt, uint8_t tei, uint8_t sapi);
|
||||
|
||||
/* L2->L3 */
|
||||
void e1inp_dlsap_up(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
|
||||
void *rx_cbdata);
|
||||
|
||||
/* Write LAPD frames to the fd. */
|
||||
void e1_set_pcap_fd(int fd);
|
||||
OSMO_DEPRECATED("Use e1_set_pcap_fd2() instead")
|
||||
int e1_set_pcap_fd(int fd);
|
||||
|
||||
int e1_set_pcap_fd2(struct e1inp_line *line, int fd);
|
||||
|
||||
/* called by TRAU muxer to obtain the destination mux entity */
|
||||
struct subch_mux *e1inp_get_mux(uint8_t e1_nr, uint8_t ts_nr);
|
||||
|
||||
/* on an IPA BTS, the BTS needs to establish the RSL connection much
|
||||
* later than the OML connection. */
|
||||
int e1inp_ipa_bts_rsl_connect(struct e1inp_line *line,
|
||||
const char *rem_addr, uint16_t rem_port);
|
||||
|
||||
int e1inp_ipa_bts_rsl_connect_n(struct e1inp_line *line,
|
||||
const char *rem_addr, uint16_t rem_port,
|
||||
uint8_t trx_nr);
|
||||
int e1inp_ipa_bts_rsl_close_n(struct e1inp_line *line, uint8_t trx_nr);
|
||||
|
||||
void e1inp_sign_link_destroy(struct e1inp_sign_link *link);
|
||||
int e1inp_line_update(struct e1inp_line *line);
|
||||
|
||||
int e1inp_vty_init(void);
|
||||
|
||||
struct gsm_network;
|
||||
int ipaccess_setup(struct gsm_network *gsmnet);
|
||||
int hsl_setup(struct gsm_network *gsmnet);
|
||||
/* activate superchannel or deactive to use timeslots. only valid for unixsocket driver */
|
||||
void e1inp_ericsson_set_altc(struct e1inp_line *unixlinue, int superchannel);
|
||||
|
||||
extern struct llist_head e1inp_driver_list;
|
||||
extern struct llist_head e1inp_line_list;
|
||||
|
||||
/* XXX */
|
||||
struct input_signal_data {
|
||||
int link_type;
|
||||
enum e1inp_sign_type link_type;
|
||||
uint8_t tei;
|
||||
uint8_t sapi;
|
||||
uint8_t ts_nr;
|
||||
/* received Sa bits in TS0 (MSB to LSB): Sa8 Sa7 Sa5 Sa4 Sa64 Sa63 Sa62 Sa61/Sa6 */
|
||||
uint8_t sa_bits;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct e1inp_line *line;
|
||||
};
|
||||
|
||||
int abis_sendmsg(struct msgb *msg);
|
||||
int abis_rsl_sendmsg(struct msgb *msg);
|
||||
|
||||
int e1inp_ts_send_raw(struct e1inp_ts *ts, struct msgb *msg);
|
||||
int e1inp_ts_send_hdlc(struct e1inp_ts *ts, struct msgb *msg);
|
||||
|
||||
#endif /* _E1_INPUT_H */
|
||||
|
|
|
@ -4,6 +4,13 @@
|
|||
#include <stdint.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
|
||||
struct e1inp_line;
|
||||
struct e1inp_ts;
|
||||
struct msgb;
|
||||
|
||||
struct ipa_server_link {
|
||||
struct e1inp_line *line;
|
||||
|
@ -12,58 +19,140 @@ struct ipa_server_link {
|
|||
uint16_t port;
|
||||
int (*accept_cb)(struct ipa_server_link *link, int fd);
|
||||
void *data;
|
||||
uint8_t dscp;
|
||||
uint8_t priority;
|
||||
};
|
||||
|
||||
struct ipa_server_link *ipa_server_link_create(void *ctx, struct e1inp_line *line, const char *addr, uint16_t port, int (*accept_cb)(struct ipa_server_link *link, int fd), void *data);
|
||||
struct ipa_server_link *
|
||||
ipa_server_link_create(void *ctx, struct e1inp_line *line, const char *addr,
|
||||
uint16_t port,
|
||||
int (*accept_cb)(struct ipa_server_link *link, int fd),
|
||||
void *data);
|
||||
void ipa_server_link_destroy(struct ipa_server_link *link);
|
||||
|
||||
int ipa_server_link_open(struct ipa_server_link *link);
|
||||
void ipa_server_link_close(struct ipa_server_link *link);
|
||||
|
||||
struct ipa_server_peer {
|
||||
struct ipa_server_conn {
|
||||
struct ipa_server_link *server;
|
||||
struct osmo_fd ofd;
|
||||
struct llist_head tx_queue;
|
||||
int (*cb)(struct ipa_server_peer *peer, struct msgb *msg);
|
||||
int (*closed_cb)(struct ipa_server_conn *peer);
|
||||
int (*ccm_cb)(struct ipa_server_conn *peer, struct msgb *msg,
|
||||
struct tlv_parsed *tlvp, struct ipaccess_unit *ud);
|
||||
/* Callback when ofd has something to be read. -EBADF must be returned if the osmo_fd is destroyed. */
|
||||
int (*cb)(struct ipa_server_conn *peer, struct msgb *msg);
|
||||
void *data;
|
||||
struct msgb *pending_msg;
|
||||
/* remote address information */
|
||||
const char *addr;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
struct ipa_server_peer *ipa_server_peer_create(void *ctx, struct ipa_server_link *link, int fd, int (*cb)(struct ipa_server_peer *peer, struct msgb *msg), void *data);
|
||||
void ipa_server_peer_destroy(struct ipa_server_peer *peer);
|
||||
struct ipa_server_conn *
|
||||
ipa_server_conn_create(void *ctx, struct ipa_server_link *link, int fd,
|
||||
int (*cb)(struct ipa_server_conn *peer, struct msgb *msg),
|
||||
int (*closed_cb)(struct ipa_server_conn *peer),
|
||||
void *data);
|
||||
void ipa_server_conn_destroy(struct ipa_server_conn *peer);
|
||||
|
||||
void ipa_server_peer_send(struct ipa_server_peer *peer, struct msgb *msg);
|
||||
void ipa_server_conn_send(struct ipa_server_conn *peer, struct msgb *msg);
|
||||
int ipa_server_conn_ccm(struct ipa_server_conn *conn, struct msgb *msg);
|
||||
|
||||
enum ipa_client_link_state {
|
||||
enum ipa_client_conn_state {
|
||||
IPA_CLIENT_LINK_STATE_NONE = 0,
|
||||
IPA_CLIENT_LINK_STATE_CONNECTING = 1,
|
||||
IPA_CLIENT_LINK_STATE_CONNECTED = 2,
|
||||
IPA_CLIENT_LINK_STATE_MAX
|
||||
};
|
||||
|
||||
struct ipa_client_link {
|
||||
struct ipa_client_conn {
|
||||
struct e1inp_line *line;
|
||||
struct osmo_fd *ofd;
|
||||
struct llist_head tx_queue;
|
||||
struct osmo_timer_list timer;
|
||||
enum ipa_client_link_state state;
|
||||
enum ipa_client_conn_state state;
|
||||
const char *addr;
|
||||
uint16_t port;
|
||||
int (*connect_cb)(struct ipa_client_link *link);
|
||||
int (*read_cb)(struct ipa_client_link *link, struct msgb *msg);
|
||||
int (*write_cb)(struct ipa_client_link *link);
|
||||
void (*updown_cb)(struct ipa_client_conn *link, int up);
|
||||
/* Callback when ofd has something to be read. -EBADF must be returned if the osmo_fd is destroyed. */
|
||||
int (*read_cb)(struct ipa_client_conn *link, struct msgb *msg);
|
||||
int (*write_cb)(struct ipa_client_conn *link);
|
||||
void *data;
|
||||
struct msgb *pending_msg;
|
||||
const char *local_addr;
|
||||
uint16_t local_port;
|
||||
uint8_t dscp;
|
||||
uint8_t priority;
|
||||
};
|
||||
|
||||
struct ipa_client_link *ipa_client_link_create(void *ctx, struct e1inp_ts *ts, const char *driver_name, int priv_nr, const char *addr, uint16_t port, int (*connect)(struct ipa_client_link *link), int (*read_cb)(struct ipa_client_link *link, struct msgb *msgb), int (*write_cb)(struct ipa_client_link *link), void *data);
|
||||
void ipa_client_link_destroy(struct ipa_client_link *link);
|
||||
struct ipa_client_conn *
|
||||
ipa_client_conn_create(void *ctx, struct e1inp_ts *ts, int priv_nr,
|
||||
const char *addr, uint16_t port,
|
||||
void (*updown)(struct ipa_client_conn *link, int),
|
||||
int (*read_cb)(struct ipa_client_conn *link, struct msgb *msgb),
|
||||
int (*write_cb)(struct ipa_client_conn *link),
|
||||
void *data) OSMO_DEPRECATED("Use ipa_client_conn_create2() instead");
|
||||
struct ipa_client_conn *
|
||||
ipa_client_conn_create2(void *ctx, struct e1inp_ts *ts,
|
||||
int priv_nr, const char *loc_addr, uint16_t loc_port,
|
||||
const char *rem_addr, uint16_t rem_port,
|
||||
void (*updown_cb)(struct ipa_client_conn *link, int up),
|
||||
int (*read_cb)(struct ipa_client_conn *link,
|
||||
struct msgb *msgb),
|
||||
int (*write_cb)(struct ipa_client_conn *link),
|
||||
void *data);
|
||||
void ipa_client_conn_destroy(struct ipa_client_conn *link);
|
||||
|
||||
int ipa_client_write_default_cb(struct ipa_client_link *link);
|
||||
int ipa_client_conn_open(struct ipa_client_conn *link);
|
||||
int ipa_client_conn_open2(struct ipa_client_conn *link, unsigned int connect_timeout);
|
||||
void ipa_client_conn_close(struct ipa_client_conn *link);
|
||||
|
||||
int ipa_client_link_open(struct ipa_client_link *link);
|
||||
void ipa_client_link_close(struct ipa_client_link *link);
|
||||
void ipa_client_conn_send(struct ipa_client_conn *link, struct msgb *msg);
|
||||
size_t ipa_client_conn_clear_queue(struct ipa_client_conn *link);
|
||||
|
||||
void ipa_client_link_send(struct ipa_client_link *link, struct msgb *msg);
|
||||
int ipaccess_bts_handle_ccm(struct ipa_client_conn *link,
|
||||
struct ipaccess_unit *dev, struct msgb *msg);
|
||||
|
||||
int ipa_msg_recv(int fd, struct msgb **rmsg);
|
||||
void ipa_msg_push_header(struct msgb *msg, uint8_t proto);
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* IPA Keep-Alive FSM
|
||||
***********************************************************************/
|
||||
|
||||
/*! parameters describing the keep-alive FSM (timeouts). */
|
||||
struct ipa_keepalive_params {
|
||||
/*! interval in which to send IPA CCM PING requests to the peer. */
|
||||
unsigned int interval;
|
||||
/*! time to wait for an IPA CCM PONG in response to a IPA CCM PING before giving up. */
|
||||
unsigned int wait_for_resp;
|
||||
};
|
||||
|
||||
typedef int ipa_keepalive_timeout_cb_t(struct osmo_fsm_inst *fi, void *conn);
|
||||
|
||||
typedef void ipa_keepalive_send_cb_t(struct osmo_fsm_inst *fi, void *conn, struct msgb *msg);
|
||||
|
||||
struct osmo_fsm_inst *ipa_client_conn_alloc_keepalive_fsm(struct ipa_client_conn *client,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id);
|
||||
|
||||
struct osmo_fsm_inst *ipa_server_conn_alloc_keepalive_fsm(struct ipa_server_conn *server,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id);
|
||||
|
||||
struct osmo_fsm_inst *ipa_generic_conn_alloc_keepalive_fsm(void *ctx, void* data,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id);
|
||||
|
||||
void ipa_keepalive_fsm_set_timeout_cb(struct osmo_fsm_inst *fi, ipa_keepalive_timeout_cb_t *cb);
|
||||
|
||||
void ipa_keepalive_fsm_set_send_cb(struct osmo_fsm_inst *fi, ipa_keepalive_send_cb_t *fn);
|
||||
|
||||
void ipa_keepalive_fsm_start(struct osmo_fsm_inst *fi);
|
||||
|
||||
void ipa_keepalive_fsm_stop(struct osmo_fsm_inst *fi);
|
||||
|
||||
void ipa_keepalive_fsm_pong_received(struct osmo_fsm_inst *fi);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,114 +1,10 @@
|
|||
#ifndef _OSMO_PROTO_IPACCESS_H
|
||||
#define _OSMO_PROTO_IPACCESS_H
|
||||
#ifndef _OSMO_ABIS_IPACCESS_H
|
||||
#define _OSMO_ABIS_IPACCESS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define IPA_TCP_PORT_OML 3002
|
||||
#define IPA_TCP_PORT_RSL 3003
|
||||
|
||||
struct ipaccess_head {
|
||||
uint16_t len; /* network byte order */
|
||||
uint8_t proto;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct ipaccess_head_ext {
|
||||
uint8_t proto;
|
||||
uint8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum ipaccess_proto {
|
||||
IPAC_PROTO_RSL = 0x00,
|
||||
IPAC_PROTO_IPACCESS = 0xfe,
|
||||
IPAC_PROTO_SCCP = 0xfd,
|
||||
IPAC_PROTO_OML = 0xff,
|
||||
|
||||
|
||||
/* OpenBSC extensions */
|
||||
IPAC_PROTO_OSMO = 0xee,
|
||||
IPAC_PROTO_MGCP_OLD = 0xfc,
|
||||
};
|
||||
|
||||
enum ipaccess_proto_ext {
|
||||
IPAC_PROTO_EXT_CTRL = 0x00,
|
||||
IPAC_PROTO_EXT_MGCP = 0x01,
|
||||
IPAC_PROTO_EXT_LAC = 0x02,
|
||||
};
|
||||
|
||||
enum ipaccess_msgtype {
|
||||
IPAC_MSGT_PING = 0x00,
|
||||
IPAC_MSGT_PONG = 0x01,
|
||||
IPAC_MSGT_ID_GET = 0x04,
|
||||
IPAC_MSGT_ID_RESP = 0x05,
|
||||
IPAC_MSGT_ID_ACK = 0x06,
|
||||
|
||||
/* OpenBSC extension */
|
||||
IPAC_MSGT_SCCP_OLD = 0xff,
|
||||
};
|
||||
|
||||
enum ipaccess_id_tags {
|
||||
IPAC_IDTAG_SERNR = 0x00,
|
||||
IPAC_IDTAG_UNITNAME = 0x01,
|
||||
IPAC_IDTAG_LOCATION1 = 0x02,
|
||||
IPAC_IDTAG_LOCATION2 = 0x03,
|
||||
IPAC_IDTAG_EQUIPVERS = 0x04,
|
||||
IPAC_IDTAG_SWVERSION = 0x05,
|
||||
IPAC_IDTAG_IPADDR = 0x06,
|
||||
IPAC_IDTAG_MACADDR = 0x07,
|
||||
IPAC_IDTAG_UNIT = 0x08,
|
||||
};
|
||||
|
||||
/*
|
||||
* Firmware specific header
|
||||
*/
|
||||
struct sdp_firmware {
|
||||
char magic[4];
|
||||
char more_magic[2];
|
||||
uint16_t more_more_magic;
|
||||
uint32_t header_length;
|
||||
uint32_t file_length;
|
||||
char sw_part[20];
|
||||
char text1[64];
|
||||
char time[12];
|
||||
char date[14];
|
||||
char text2[10];
|
||||
char version[20];
|
||||
uint16_t table_offset;
|
||||
/* stuff i don't know */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sdp_header_entry {
|
||||
uint16_t something1;
|
||||
char text1[64];
|
||||
char time[12];
|
||||
char date[14];
|
||||
char text2[10];
|
||||
char version[20];
|
||||
uint32_t length;
|
||||
uint32_t addr1;
|
||||
uint32_t addr2;
|
||||
uint32_t start;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipaccess_unit {
|
||||
uint16_t site_id;
|
||||
uint16_t bts_id;
|
||||
uint16_t trx_id;
|
||||
char *unit_name;
|
||||
char *equipvers;
|
||||
char *swversion;
|
||||
uint8_t mac_addr[6];
|
||||
char *location1;
|
||||
char *location2;
|
||||
char *serno;
|
||||
};
|
||||
|
||||
struct hsl_unit {
|
||||
uint8_t swversion;
|
||||
uint64_t serno;
|
||||
};
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
/* quick solution to get openBSC's ipaccess tools working. */
|
||||
extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what);
|
||||
|
||||
#endif /* _OSMO_PROTO_IPACCESS_H */
|
||||
#endif /* _OSMO_ABIS_IPACCESS_H */
|
||||
|
|
|
@ -4,26 +4,41 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/lapd_core.h>
|
||||
|
||||
typedef enum {
|
||||
LAPD_MPH_NONE = 0,
|
||||
struct lapd_profile {
|
||||
uint8_t k[64];
|
||||
int n200;
|
||||
int n201;
|
||||
int n202;
|
||||
int t200_sec, t200_usec;
|
||||
int t201_sec, t201_usec;
|
||||
int t202_sec, t202_usec;
|
||||
int t203_sec, t203_usec;
|
||||
int short_address;
|
||||
};
|
||||
|
||||
LAPD_MPH_ACTIVATE_IND,
|
||||
LAPD_MPH_DEACTIVATE_IND,
|
||||
|
||||
LAPD_DL_DATA_IND,
|
||||
LAPD_DL_UNITDATA_IND,
|
||||
|
||||
} lapd_mph_type;
|
||||
/* predefined lapd profiles (see lapd.c for definition) */
|
||||
extern const struct lapd_profile lapd_profile_isdn;
|
||||
extern const struct lapd_profile lapd_profile_abis;
|
||||
extern const struct lapd_profile lapd_profile_abis_ericsson;
|
||||
extern const struct lapd_profile lapd_profile_sat;
|
||||
|
||||
struct lapd_instance {
|
||||
struct llist_head list; /* list of LAPD instances */
|
||||
int network_side;
|
||||
|
||||
void (*transmit_cb)(uint8_t *data, int len, void *cbdata);
|
||||
void *cbdata;
|
||||
void (*transmit_cb)(struct msgb *msg, void *cbdata);
|
||||
void *transmit_cbdata;
|
||||
void (*receive_cb)(struct osmo_dlsap_prim *odp, uint8_t tei,
|
||||
uint8_t sapi, void *rx_cbdata);
|
||||
void *receive_cbdata;
|
||||
|
||||
struct lapd_profile profile; /* must be a copy */
|
||||
|
||||
struct llist_head tei_list; /* list of TEI in this LAPD instance */
|
||||
int pcap_fd; /* PCAP file descriptor */
|
||||
char *name; /* human-readable name */
|
||||
};
|
||||
|
||||
enum lapd_recv_errors {
|
||||
|
@ -34,20 +49,38 @@ enum lapd_recv_errors {
|
|||
LAPD_ERR_UNKNOWN_U_CMD,
|
||||
LAPD_ERR_UNKNOWN_TEI,
|
||||
LAPD_ERR_BAD_CMD,
|
||||
LAPD_ERR_NO_MEM,
|
||||
__LAPD_ERR_MAX
|
||||
};
|
||||
|
||||
extern uint8_t *lapd_receive(struct lapd_instance *li, uint8_t *data,
|
||||
unsigned int len, int *ilen, lapd_mph_type *prim,
|
||||
int *error);
|
||||
struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei);
|
||||
|
||||
extern void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
|
||||
uint8_t *data, unsigned int len);
|
||||
int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error);
|
||||
|
||||
void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
|
||||
struct msgb *msg);
|
||||
|
||||
struct lapd_instance *lapd_instance_alloc(int network_side,
|
||||
void (*tx_cb)(uint8_t *data, int len,
|
||||
void *cbdata), void *cbdata);
|
||||
void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
|
||||
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
|
||||
void *rx_cbdata), void *rx_cbdata,
|
||||
const struct lapd_profile *profile)
|
||||
OSMO_DEPRECATED("Use lapd_instance_alloc2() instead");
|
||||
|
||||
struct lapd_instance *lapd_instance_alloc2(int network_side,
|
||||
void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
|
||||
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
|
||||
void *rx_cbdata), void *rx_cbdata,
|
||||
const struct lapd_profile *profile, const char *name);
|
||||
|
||||
/* In rare cases (e.g. Ericsson's lapd dialect), it may be necessary to
|
||||
* exchange the lapd profile on the fly. lapd_instance_set_profile()
|
||||
* allwos to set the lapd profile on a lapd instance danymically to
|
||||
* one of the lapd profiles define above. */
|
||||
void lapd_instance_set_profile(struct lapd_instance *li,
|
||||
const struct lapd_profile *profile);
|
||||
|
||||
void lapd_instance_free(struct lapd_instance *li);
|
||||
|
||||
/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
|
||||
int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi);
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef _LAPD_PCAP_H_
|
||||
#define _LAPD_PCAP_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#define OSMO_LAPD_PCAP_INPUT 0
|
||||
#define OSMO_LAPD_PCAP_OUTPUT 1
|
||||
|
||||
int osmo_pcap_lapd_open(char *filename, mode_t mode);
|
||||
int osmo_pcap_lapd_set_fd(int fd);
|
||||
int osmo_pcap_lapd_write(int fd, int direction, struct msgb *msg);
|
||||
int osmo_pcap_lapd_close(int fd);
|
||||
|
||||
#endif
|
|
@ -22,80 +22,89 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
/*! \defgroup subchan_demux
|
||||
* \brief E1 sub-channel multiplexer/demultiplexer
|
||||
* @{
|
||||
*
|
||||
* \file subchan_demux.h
|
||||
*/
|
||||
|
||||
/*! \brief number of 16k sub-channels inside one 64k E1 timeslot */
|
||||
#define NR_SUBCH 4
|
||||
/*! \brief size of TRAU frames in bytes */
|
||||
#define TRAU_FRAME_SIZE 40
|
||||
/*! \brief size of TRAU farmes in bits */
|
||||
#define TRAU_FRAME_BITS (TRAU_FRAME_SIZE*8)
|
||||
|
||||
/***********************************************************************/
|
||||
/* DEMULTIPLEXER */
|
||||
/***********************************************************************/
|
||||
|
||||
/*! \brief one subchannel inside the demultplexer */
|
||||
struct demux_subch {
|
||||
uint8_t out_bitbuf[TRAU_FRAME_BITS];
|
||||
uint16_t out_idx; /* next bit to be written in out_bitbuf */
|
||||
/* number of consecutive zeros that we have received (for sync) */
|
||||
/*! \brief bit-buffer for output bits */
|
||||
ubit_t out_bitbuf[TRAU_FRAME_BITS];
|
||||
/*! \brief next bit to be written in out_bitbuf */
|
||||
uint16_t out_idx;
|
||||
/*! \brief number of consecutive zeros that we have received (for sync) */
|
||||
unsigned int consecutive_zeros;
|
||||
/* are we in TRAU frame sync or not? */
|
||||
/*! \brief are we in TRAU frame sync or not? */
|
||||
unsigned int in_sync;
|
||||
};
|
||||
|
||||
/*! \brief one instance of a subchannel demultiplexer */
|
||||
struct subch_demux {
|
||||
/* bitmask of currently active subchannels */
|
||||
/*! \brief bitmask of currently active subchannels */
|
||||
uint8_t chan_activ;
|
||||
/* one demux_subch struct for every subchannel */
|
||||
/*! \brief one demux_subch struct for every subchannel */
|
||||
struct demux_subch subch[NR_SUBCH];
|
||||
/* callback to be called once we have received a complete
|
||||
* frame on a given subchannel */
|
||||
int (*out_cb)(struct subch_demux *dmx, int ch, uint8_t *data, int len,
|
||||
/*! \brief callback to be called once we have received a
|
||||
* complete frame on a given subchannel */
|
||||
int (*out_cb)(struct subch_demux *dmx, int ch, const ubit_t *data, int len,
|
||||
void *);
|
||||
/* user-provided data, transparently passed to out_cb() */
|
||||
/*! \brief user-provided data, transparently passed to out_cb() */
|
||||
void *data;
|
||||
};
|
||||
|
||||
/* initialize one demultiplexer instance */
|
||||
int subch_demux_init(struct subch_demux *dmx);
|
||||
|
||||
/* feed 'len' number of muxed bytes into the demultiplexer */
|
||||
int subch_demux_in(struct subch_demux *dmx, uint8_t *data, int len);
|
||||
|
||||
/* activate decoding/processing for one subchannel */
|
||||
int subch_demux_activate(struct subch_demux *dmx, int subch);
|
||||
|
||||
/* deactivate decoding/processing for one subchannel */
|
||||
int subch_demux_deactivate(struct subch_demux *dmx, int subch);
|
||||
|
||||
/***********************************************************************/
|
||||
/* MULTIPLEXER */
|
||||
/***********************************************************************/
|
||||
|
||||
/* one element in the tx_queue of a muxer sub-channel */
|
||||
/*! \brief one element in the tx_queue of a muxer sub-channel */
|
||||
struct subch_txq_entry {
|
||||
/*! \brief internal linked list header */
|
||||
struct llist_head list;
|
||||
|
||||
unsigned int bit_len; /* total number of bits in 'bits' */
|
||||
unsigned int next_bit; /* next bit to be transmitted */
|
||||
unsigned int bit_len; /*!< \brief total number of bits in 'bits' */
|
||||
unsigned int next_bit; /*!< \brief next bit to be transmitted */
|
||||
|
||||
uint8_t bits[0]; /* one bit per byte */
|
||||
ubit_t bits[0]; /*!< \brief one bit per byte */
|
||||
};
|
||||
|
||||
/*! \brief one sub-channel inside a multiplexer */
|
||||
struct mux_subch {
|
||||
/*! \brief linked list of \ref subch_txq_entry */
|
||||
struct llist_head tx_queue;
|
||||
};
|
||||
|
||||
/* structure representing one instance of the subchannel muxer */
|
||||
/*! \brief one instance of the subchannel multiplexer */
|
||||
struct subch_mux {
|
||||
/*! \brief array of sub-channels inside the multiplexer */
|
||||
struct mux_subch subch[NR_SUBCH];
|
||||
};
|
||||
|
||||
/* initialize a subchannel muxer instance */
|
||||
int subchan_mux_init(struct subch_mux *mx);
|
||||
|
||||
/* request the output of 'len' multiplexed bytes */
|
||||
int subchan_mux_out(struct subch_mux *mx, uint8_t *data, int len);
|
||||
|
||||
/* enqueue some data into one sub-channel of the muxer */
|
||||
int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const uint8_t *data,
|
||||
int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const ubit_t *data,
|
||||
int len);
|
||||
|
||||
/* }@ */
|
||||
|
||||
#endif /* _SUBCH_DEMUX_H */
|
||||
|
|
|
@ -21,31 +21,41 @@
|
|||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
/* 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */
|
||||
/*! \defgroup trau_frame TRAU frame handling
|
||||
* @{
|
||||
*
|
||||
* \file trau_frame.h
|
||||
*/
|
||||
|
||||
/*! \brief Maximum number of C-bits in a TRAU frame:
|
||||
* 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */
|
||||
#define MAX_C_BITS 25
|
||||
/* 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */
|
||||
/*! \brief Maximum number of D-bits in a TRAU frame:
|
||||
* 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */
|
||||
#define MAX_D_BITS 288
|
||||
/* for all speech frames */
|
||||
/*! \brief Maximum number of T-bits in a TRAU frame for all speech frames */
|
||||
#define MAX_T_BITS 4
|
||||
/* for OM */
|
||||
/*! \brief Maximum number of S-bits in a TRAU frame for OM */
|
||||
#define MAX_S_BITS 6
|
||||
/* for E-data */
|
||||
/*! \brief Maximum number of M-bits in a TRAU frame for E-data */
|
||||
#define MAX_M_BITS 2
|
||||
|
||||
/*! \brief a decoded TRAU frame, extracted C/D/T/S/M bits */
|
||||
struct decoded_trau_frame {
|
||||
uint8_t c_bits[MAX_C_BITS];
|
||||
uint8_t d_bits[MAX_D_BITS];
|
||||
uint8_t t_bits[MAX_T_BITS];
|
||||
uint8_t s_bits[MAX_S_BITS];
|
||||
uint8_t m_bits[MAX_M_BITS];
|
||||
ubit_t c_bits[MAX_C_BITS];
|
||||
ubit_t d_bits[MAX_D_BITS];
|
||||
ubit_t t_bits[MAX_T_BITS];
|
||||
ubit_t s_bits[MAX_S_BITS];
|
||||
ubit_t m_bits[MAX_M_BITS];
|
||||
};
|
||||
|
||||
#define TRAU_FT_FR_UP 0x02 /* 0 0 0 1 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_FR_DOWN 0x1c /* 1 1 1 0 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_EFR 0x1a /* 1 1 0 1 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_AMR 0x06 /* 0 0 1 1 0 - 3.5.1.2 */
|
||||
#define TRAU_FT_OM_UP 0x07 /* 0 0 1 0 1 - 3.5.2 */
|
||||
#define TRAU_FT_OM_UP 0x05 /* 0 0 1 0 1 - 3.5.2 */
|
||||
#define TRAU_FT_OM_DOWN 0x1b /* 1 1 0 1 1 - 3.5.2 */
|
||||
#define TRAU_FT_DATA_UP 0x08 /* 0 1 0 0 0 - 3.5.3 */
|
||||
#define TRAU_FT_DATA_DOWN 0x16 /* 1 0 1 1 0 - 3.5.3 */
|
||||
|
@ -55,10 +65,12 @@ struct decoded_trau_frame {
|
|||
#define TRAU_FT_IDLE_DOWN 0x0e /* 0 1 1 1 0 - 3.5.5 */
|
||||
|
||||
|
||||
int decode_trau_frame(struct decoded_trau_frame *fr, const uint8_t *trau_bits);
|
||||
int encode_trau_frame(uint8_t *trau_bits, const struct decoded_trau_frame *fr);
|
||||
int decode_trau_frame(struct decoded_trau_frame *fr, const ubit_t *trau_bits);
|
||||
int encode_trau_frame(ubit_t *trau_bits, const struct decoded_trau_frame *fr);
|
||||
int trau_frame_up2down(struct decoded_trau_frame *fr);
|
||||
|
||||
uint8_t *trau_idle_frame(void);
|
||||
ubit_t *trau_idle_frame(void);
|
||||
|
||||
/* }@ */
|
||||
|
||||
#endif /* _TRAU_FRAME_H */
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
#ifndef UNIXSOCKET_PROTO_H
|
||||
#define UNIXSOCKET_PROTO_H
|
||||
|
||||
/* The unix socket protocol is using a 2 byte header
|
||||
* containg the version and type.
|
||||
*
|
||||
* header: | 1b version | 1b type |
|
||||
*
|
||||
* for data packets it would be
|
||||
*
|
||||
* data: | 0x1 | 0x0 | lapd ..|
|
||||
* control: | 0x1 | 0x1 | control payload |
|
||||
*
|
||||
* Atm there is only one control packet:
|
||||
* - set_altc (superchannel or timeslot)
|
||||
*
|
||||
* set_altc payload:
|
||||
* | 4b magic | 1b new_state|
|
||||
* | 0x23004200 | 0x0 | to timeslot
|
||||
* | 0x23004200 | 0x1 | to superchannel
|
||||
*/
|
||||
|
||||
#define UNIXSOCKET_PROTO_VERSION 0x1
|
||||
|
||||
enum {
|
||||
UNIXSOCKET_PROTO_DATA = 0x0,
|
||||
UNIXSOCKET_PROTO_CONTROL = 0x1,
|
||||
};
|
||||
|
||||
#endif /* UNIXSOCKET_PROTO_H */
|
|
@ -0,0 +1,112 @@
|
|||
#ifndef _OSMO_ORTP_H
|
||||
#define _OSMO_ORTP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
/* we cannot include ortp/ortp.h here, as they also use 'struct msgb' */
|
||||
struct _RtpSession;
|
||||
|
||||
/*! \brief default duration of a 20ms GSM codec frame */
|
||||
#define GSM_RTP_DURATION 160
|
||||
|
||||
/*! \brief standard payload type for GSM Full Rate (FR) */
|
||||
#define RTP_PT_GSM_FULL 3
|
||||
/*! \brief Osmocom pseudo-static payload type for Half Rate (HR) */
|
||||
#define RTP_PT_GSM_HALF 96
|
||||
/*! \brief Osmocom pseudo-static payload type for Enhanced Full Rate (EFR) */
|
||||
#define RTP_PT_GSM_EFR 97
|
||||
/*! \brief Osmocom pseudo-static payload type for Adaptive Multi Rate (AMR) */
|
||||
#define RTP_PT_AMR 98
|
||||
/*! \brief standard payload type for CSData (3GPP TS 48.103 table 5.4.2.2.1) */
|
||||
#define RTP_PT_CSDATA 120
|
||||
|
||||
#define GSM_VOICE_SAMPLE_RATE_HZ 8000
|
||||
#define GSM_VOICE_SAMPLES_PER_MS (GSM_VOICE_SAMPLE_RATE_HZ / 1000)
|
||||
#define GSM_VOICE_MULTIFRAME 26
|
||||
#define GSM_RTP_FRAME_DURATION_MS 20
|
||||
#define GSM_SAMPLES_PER_RTP_FRAME (GSM_RTP_FRAME_DURATION_MS * GSM_VOICE_SAMPLES_PER_MS)
|
||||
#define GSM_TDMA_FRAME_MS (120 / GSM_VOICE_MULTIFRAME)
|
||||
#define GSM_MS_TO_SAMPLES(ms) ((ms) * GSM_VOICE_SAMPLES_PER_MS)
|
||||
#define GSM_FN_TO_MS(fn) ((fn) * GSM_TDMA_FRAME_MS)
|
||||
|
||||
/*! \brief Parameter to osmo_rtp_socket_param_set() */
|
||||
enum osmo_rtp_param {
|
||||
OSMO_RTP_P_JITBUF = 1,
|
||||
OSMO_RTP_P_JIT_ADAP,
|
||||
};
|
||||
|
||||
/*! \brief Flag to indicate the socket is in polling-only mode */
|
||||
#define OSMO_RTP_F_POLL 0x0001
|
||||
#define OSMO_RTP_F_DISABLED 0x0002
|
||||
|
||||
/*! \brief A structure representing one RTP socket */
|
||||
struct osmo_rtp_socket {
|
||||
/*! \biref list header for global list of sockets */
|
||||
struct llist_head list;
|
||||
|
||||
/*! \brief libortp RTP session pointer */
|
||||
struct _RtpSession *sess;
|
||||
/*! \brief Osmo file descriptor for RTP socket FD */
|
||||
struct osmo_fd rtp_bfd;
|
||||
/*! \brief Osmo file descriptor for RTCP socket FD */
|
||||
struct osmo_fd rtcp_bfd;
|
||||
|
||||
/*! \brief callback for incoming data */
|
||||
void (*rx_cb)(struct osmo_rtp_socket *rs, const uint8_t *payload,
|
||||
unsigned int payload_len, uint16_t seq_number,
|
||||
uint32_t timestamp, bool marker);
|
||||
|
||||
/*! \brief Receive user timestamp, to be incremented by user */
|
||||
uint32_t rx_user_ts;
|
||||
|
||||
/*! \brief Transmit timestamp, incremented by library */
|
||||
uint32_t tx_timestamp;
|
||||
|
||||
/*! \brief Flags like OSMO_RTP_F_POLL */
|
||||
unsigned int flags;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
void osmo_rtp_init(void *ctx);
|
||||
struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags);
|
||||
int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port);
|
||||
int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port);
|
||||
int osmo_rtp_socket_autoconnect(struct osmo_rtp_socket *rs);
|
||||
int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type);
|
||||
int osmo_rtp_socket_set_dscp(struct osmo_rtp_socket *rs, int dscp);
|
||||
int osmo_rtp_socket_set_priority(struct osmo_rtp_socket *rs, uint8_t prio);
|
||||
int osmo_rtp_socket_free(struct osmo_rtp_socket *rs);
|
||||
int osmo_rtp_skipped_frame(struct osmo_rtp_socket *rs, unsigned int duration);
|
||||
int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
|
||||
unsigned int payload_len, unsigned int duration);
|
||||
int osmo_rtp_send_frame_ext(struct osmo_rtp_socket *rs, const uint8_t *payload,
|
||||
unsigned int payload_len, unsigned int duration,
|
||||
bool marker);
|
||||
int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs);
|
||||
|
||||
int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
|
||||
uint32_t *ip, int *port);
|
||||
int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
|
||||
const char **addr, int *port);
|
||||
int osmo_rtp_socket_set_param(struct osmo_rtp_socket *rs,
|
||||
enum osmo_rtp_param param, int val);
|
||||
|
||||
void osmo_rtp_socket_log_stats(struct osmo_rtp_socket *rs,
|
||||
int subsys, int level,
|
||||
const char *pfx);
|
||||
|
||||
void osmo_rtp_socket_stats(struct osmo_rtp_socket *rs,
|
||||
uint32_t *sent_packets, uint32_t *sent_octets,
|
||||
uint32_t *recv_packets, uint32_t *recv_octets,
|
||||
uint32_t *recv_lost, uint32_t *last_jitter);
|
||||
|
||||
void osmo_rtp_set_source_desc(struct osmo_rtp_socket *rs, const char *cname,
|
||||
const char *name, const char *email, const char *phone,
|
||||
const char *loc, const char *tool, const char *note);
|
||||
|
||||
#endif /* _OSMO_ORTP_H */
|
|
@ -0,0 +1,151 @@
|
|||
#pragma once
|
||||
/* TRAU frame handling according to GSM TS 48.060 / 48.061 */
|
||||
|
||||
/* (C) 2020 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 General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
/*! \defgroup trau_frame TRAU frame handling
|
||||
* @{
|
||||
*
|
||||
* \file trau_frame.h
|
||||
*/
|
||||
|
||||
/*! \brief Maximum number of C-bits in a TRAU frame:
|
||||
* 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */
|
||||
#define MAX_C_BITS 25
|
||||
/*! \brief Maximum number of D-bits in a TRAU frame:
|
||||
* 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */
|
||||
#define MAX_D_BITS 288
|
||||
/*! \brief Maximum number of T-bits in a TRAU frame for all speech frames */
|
||||
#define MAX_T_BITS 4
|
||||
/*! \brief Maximum number of S-bits in a TRAU frame for OM */
|
||||
#define MAX_S_BITS 6
|
||||
/*! \brief Maximum number of M-bits in a TRAU frame for E-data */
|
||||
#define MAX_M_BITS 2
|
||||
/*! \brief Maximum number of XC-bits in a TRAU frame for 8k */
|
||||
#define MAX_XC_BITS 6
|
||||
|
||||
/*! 16k sub-slot types: Bits C1..C5 */
|
||||
#define TRAU_FT_FR_UP 0x02 /* 0 0 0 1 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_HR_UP 0x03 /* 0 0 0 1 1 - TS 08.61 5.1.4.1.1 */
|
||||
#define TRAU_FT_FR_DOWN 0x1c /* 1 1 1 0 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_HR_DOWN 0x1d /* 1 1 1 0 1 - TS 08.61 5.1.4.1.1 */
|
||||
#define TRAU_FT_EFR 0x1a /* 1 1 0 1 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_AMR 0x06 /* 0 0 1 1 0 - 3.5.1.2 */
|
||||
#define TRAU_FT_OM_UP 0x05 /* 0 0 1 0 1 - 3.5.2 */
|
||||
#define TRAU_FT_OM_DOWN 0x1b /* 1 1 0 1 1 - 3.5.2 */
|
||||
#define TRAU_FT_DATA_UP 0x08 /* 0 1 0 0 0 - 3.5.3 */
|
||||
#define TRAU_FT_DATA_UP_HR 0x09 /* 0 1 0 0 1 - TS 08.61 5.1.4.2 */
|
||||
#define TRAU_FT_DATA_DOWN 0x16 /* 1 0 1 1 0 - 3.5.3 */
|
||||
#define TRAU_FT_DATA_DOWN_HR 0x17 /* 1 0 1 1 1 - TS 08.61 5.1.4.2 */
|
||||
#define TRAU_FT_D145_SYNC 0x14 /* 1 0 1 0 0 - 3.5.3 */
|
||||
#define TRAU_FT_EDATA 0x1f /* 1 1 1 1 1 - 3.5.4 */
|
||||
#define TRAU_FT_IDLE_UP 0x10 /* 1 0 0 0 0 - 3.5.5 */
|
||||
#define TRAU_FT_IDLE_DOWN 0x0e /* 0 1 1 1 0 - 3.5.5 */
|
||||
|
||||
/* 8k sub-slot types: Bits C1..C5*/
|
||||
#define TRAU8_FT_SPEECH_UP 0x02 /* 0 0 0 1 P - TS 08.61 5.2.4.1.1 */
|
||||
#define TRAU8_FT_DATA_UP 0x06 /* 0 0 1 1 P - TS 08.61 5.2.4.1.1 */
|
||||
#define TRAU8_FT_OM_UP 0x0a /* 0 1 0 1 P - TS 08.61 5.2.4.1.1 */
|
||||
#define TRAU8_FT_SPEECH_DOWN 0x00 /* 0 0 0 U P - TS 08.61 5.2.4.1.2 */
|
||||
#define TRAU8_FT_DATA_DOWN 0x04 /* 0 0 1 U P - TS 08.61 5.2.4.1.2 */
|
||||
#define TRAU8_FT_OM_DOWN 0x08 /* 0 1 0 U P - TS 08.61 5.2.4.1.2 */
|
||||
|
||||
/*********************************************************************************
|
||||
* New API
|
||||
*********************************************************************************/
|
||||
|
||||
enum osmo_trau_frame_type {
|
||||
OSMO_TRAU_FT_NONE,
|
||||
OSMO_TRAU16_FT_FR = 1,
|
||||
OSMO_TRAU16_FT_HR,
|
||||
OSMO_TRAU16_FT_EFR,
|
||||
OSMO_TRAU16_FT_AMR,
|
||||
OSMO_TRAU16_FT_OAM,
|
||||
OSMO_TRAU16_FT_DATA,
|
||||
OSMO_TRAU16_FT_EDATA,
|
||||
OSMO_TRAU16_FT_D145_SYNC,
|
||||
OSMO_TRAU16_FT_DATA_HR,
|
||||
OSMO_TRAU16_FT_IDLE,
|
||||
|
||||
OSMO_TRAU8_SPEECH,
|
||||
OSMO_TRAU8_DATA,
|
||||
OSMO_TRAU8_OAM,
|
||||
OSMO_TRAU8_AMR_LOW,
|
||||
OSMO_TRAU8_AMR_6k7,
|
||||
OSMO_TRAU8_AMR_7k4,
|
||||
};
|
||||
|
||||
extern const struct value_string osmo_trau_frame_type_names[];
|
||||
static inline const char *osmo_trau_frame_type_name(enum osmo_trau_frame_type ft) {
|
||||
return get_value_string(osmo_trau_frame_type_names, ft);
|
||||
}
|
||||
|
||||
enum osmo_trau_frame_direction {
|
||||
OSMO_TRAU_DIR_UL = 0,
|
||||
OSMO_TRAU_DIR_DL = 1,
|
||||
};
|
||||
|
||||
struct osmo_trau_frame {
|
||||
enum osmo_trau_frame_type type;
|
||||
enum osmo_trau_frame_direction dir;
|
||||
|
||||
/* timing alignment: 0 = no change; negative: less bits; positive: more bits */
|
||||
int dl_ta_usec;
|
||||
|
||||
ubit_t c_bits[MAX_C_BITS];
|
||||
ubit_t d_bits[MAX_D_BITS];
|
||||
ubit_t t_bits[MAX_T_BITS];
|
||||
ubit_t s_bits[MAX_S_BITS];
|
||||
ubit_t m_bits[MAX_M_BITS];
|
||||
ubit_t ufi; /*!< Unreliable Frame Indication (HR) */
|
||||
ubit_t crc_bits[3]; /*!< CRC0..CRC2 (HR) */
|
||||
ubit_t xc_bits[MAX_XC_BITS]; /*!< XC1..XC6 (8k) */
|
||||
};
|
||||
|
||||
/*! decode an 8k TRAU frame
|
||||
* \param[out] fr caller-allocated output data structure
|
||||
* \param[in] bits unpacked input bits
|
||||
* \param[in] dir direction
|
||||
* \return 0 on success; negative in case of error */
|
||||
int osmo_trau_frame_decode_8k(struct osmo_trau_frame *fr, const ubit_t *bits,
|
||||
enum osmo_trau_frame_direction dir);
|
||||
|
||||
/*! decode an 16k TRAU frame
|
||||
* \param[out] fr caller-allocated output data structure
|
||||
* \param[in] bits unpacked input bits
|
||||
* \param[in] dir direction
|
||||
* \return 0 on success; negative in case of error */
|
||||
int osmo_trau_frame_decode_16k(struct osmo_trau_frame *fr, const ubit_t *bits,
|
||||
enum osmo_trau_frame_direction dir);
|
||||
|
||||
/*! encode a TRAU frame
|
||||
* \param[out] bits caller-allocated memory for unpacked output bits
|
||||
* \param[out] fr input data structure describing TRAU frame
|
||||
* \return number of bits encoded */
|
||||
int osmo_trau_frame_encode(ubit_t *bits, size_t n_bits, const struct osmo_trau_frame *fr);
|
||||
|
||||
/*! Determine the time alignment in us requested by CCU in a UL frame */
|
||||
int osmo_trau_frame_dl_ta_us(const struct osmo_trau_frame *fr);
|
||||
|
||||
/* }@ */
|
|
@ -0,0 +1,303 @@
|
|||
#pragma once
|
||||
|
||||
enum time_adj_val {
|
||||
TIME_ADJ_NONE,
|
||||
TIME_ADJ_DELAY_250us,
|
||||
TIME_ADJ_ADVANCE_250us,
|
||||
};
|
||||
|
||||
enum er_cs_or_hdr {
|
||||
CS_OR_HDR_AB = 0,
|
||||
CS_OR_HDR_CS1 = 1,
|
||||
CS_OR_HDR_CS2 = 2,
|
||||
CS_OR_HDR_CS3 = 3,
|
||||
CS_OR_HDR_CS4 = 4,
|
||||
CS_OR_HDR_HDR1 = 5,
|
||||
CS_OR_HDR_HDR2 = 6,
|
||||
CS_OR_HDR_HDR3 = 7,
|
||||
};
|
||||
|
||||
/* Block Quality / Soft Frame Quality */
|
||||
enum er_block_qual {
|
||||
ER_SFQ_00_09 = 0,
|
||||
ER_SFQ_10_19 = 1,
|
||||
ER_SFQ_20_29 = 2,
|
||||
ER_SFQ_30_39 = 3,
|
||||
ER_SFQ_40_49 = 4,
|
||||
ER_SFQ_50_59 = 5,
|
||||
ER_SFQ_60_69 = 6,
|
||||
ER_SFQ_70_MORE = 7,
|
||||
};
|
||||
|
||||
enum er_ul_chan_mode {
|
||||
/* no demodulation, ignore uplink */
|
||||
ER_UL_CHMOD_VOID = 0,
|
||||
|
||||
/* normal burst, GMSK only */
|
||||
ER_UL_CHMOD_NB_GMSK = 1,
|
||||
|
||||
/* normal burst, MCS1..MCS9 or CS1 */
|
||||
ER_UL_CHMOD_NB_UNKN = 2,
|
||||
|
||||
/* Access Burst (only TS0 and 8bit) */
|
||||
ER_UL_CHMOD_AB = 3,
|
||||
|
||||
/* Access Burst (TS0/1/2) */
|
||||
ER_UL_CHMOD_AB_UNKN = 4,
|
||||
};
|
||||
|
||||
/* Access burst data */
|
||||
struct er_gprs_ab {
|
||||
uint8_t ab_type;
|
||||
uint8_t rxlev;
|
||||
uint16_t acc_delay;
|
||||
uint16_t data;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint8_t crc;
|
||||
uint8_t burst_qual;
|
||||
uint8_t frame_qual;
|
||||
} type_1;
|
||||
struct {
|
||||
uint8_t abi;
|
||||
uint8_t type;
|
||||
} type_2;
|
||||
} u;
|
||||
};
|
||||
|
||||
/* CCU->PCU SYNC */
|
||||
struct er_ccu_sync_ind {
|
||||
/* Time Adjustment Value (requested) */
|
||||
enum time_adj_val tav;
|
||||
|
||||
/* Downlink Frame Error */
|
||||
bool dfe;
|
||||
|
||||
/* Downlink Block Error (bad MAC block from PCU) */
|
||||
bool dbe;
|
||||
|
||||
/* PCU Sequene Counter (will lag, must be compensated) */
|
||||
uint32_t pseq;
|
||||
|
||||
/* Adjusted Frame Number, Uplink */
|
||||
uint32_t afn_ul;
|
||||
|
||||
/* Adjusted Frame Number, Downlink */
|
||||
uint32_t afn_dl;
|
||||
};
|
||||
|
||||
/* PCU->CCU SYNC */
|
||||
struct er_pcu_sync_ind {
|
||||
/* Time Adjustment Value (acknowledged) */
|
||||
enum time_adj_val tav;
|
||||
bool ul_frame_err;
|
||||
|
||||
/* PCU Sequene Counter (PCU will sync to this value) */
|
||||
uint32_t pseq;
|
||||
|
||||
/* Optional, ignored by CCU */
|
||||
uint32_t ss;
|
||||
|
||||
/* Optional, ignored by CCU */
|
||||
uint32_t fn_ul;
|
||||
|
||||
/* Optional, ignored by CCU */
|
||||
uint32_t fn_ss;
|
||||
|
||||
/* Optional, ignored by CCU */
|
||||
uint32_t fn_dl;
|
||||
|
||||
/* Optional, ignored by CCU */
|
||||
uint32_t ls;
|
||||
};
|
||||
|
||||
/* PCU->CCU DATA */
|
||||
struct er_pcu_data_ind {
|
||||
/* Time Adjustment Value (acknowledged) */
|
||||
enum time_adj_val tav;
|
||||
|
||||
/* Uplink Frame Error */
|
||||
bool ul_frame_err;
|
||||
|
||||
/* Coding Scheme */
|
||||
enum er_cs_or_hdr cs_hdr;
|
||||
|
||||
/* Modulation to be applied on coresponding uplink frame, also used to retrieve
|
||||
* extraordinary ccu_sync_ind frames and ccu_data_ind frames wth Access bursts data. */
|
||||
enum er_ul_chan_mode ul_chan_mode;
|
||||
|
||||
/* Attenuation of transmission power, 16 steps, 2dB each */
|
||||
uint8_t atten_db;
|
||||
|
||||
/* Timing offset (timing advance) */
|
||||
uint8_t timing_offset;
|
||||
|
||||
/* MAC block */
|
||||
uint8_t data[155];
|
||||
};
|
||||
|
||||
/* CCU->PCU DATA */
|
||||
struct er_ccu_data_ind {
|
||||
/* Time Adjustment Value (requested) */
|
||||
enum time_adj_val tav;
|
||||
|
||||
/* Downlink Block Error (bad MAC block from PCU) */
|
||||
bool dbe;
|
||||
|
||||
/* Coding Scheme */
|
||||
enum er_cs_or_hdr cs_hdr;
|
||||
|
||||
/* Receive level 63 steps, see also GSM 05.08, section 8.1.4 */
|
||||
uint8_t rx_lev;
|
||||
|
||||
/* Estimated Access Delay Deviation (bits, uplink only) */
|
||||
int8_t est_acc_del_dev;
|
||||
|
||||
/* Data integrity checks (uplink only) */
|
||||
union {
|
||||
struct {
|
||||
/* Soft Frame Quality */
|
||||
enum er_block_qual block_qual;
|
||||
/* Parity Check */
|
||||
bool parity_ok;
|
||||
} gprs;
|
||||
struct {
|
||||
uint8_t mean_bep;
|
||||
uint8_t cv_bep;
|
||||
bool hdr_good;
|
||||
bool data_good[2];
|
||||
} egprs;
|
||||
} u;
|
||||
|
||||
/* MAC block */
|
||||
uint8_t data[155];
|
||||
|
||||
/* MAC block length
|
||||
* (uplink only, for downlink cs_hdr and MAC block header will be used
|
||||
* to determine the length.) */
|
||||
uint8_t data_len;
|
||||
|
||||
/* Access burst data (in case an access block was received) */
|
||||
struct er_gprs_ab ab[4];
|
||||
};
|
||||
|
||||
enum er_gprs_trau_frame_type {
|
||||
ER_GPRS_TRAU_FT_NONE,
|
||||
ER_GPRS_TRAU_FT_SYNC,
|
||||
ER_GPRS_TRAU_FT_DATA,
|
||||
};
|
||||
|
||||
struct er_gprs_trau_frame {
|
||||
enum er_gprs_trau_frame_type type;
|
||||
union {
|
||||
struct er_ccu_sync_ind ccu_sync_ind;
|
||||
struct er_ccu_data_ind ccu_data_ind;
|
||||
struct er_pcu_sync_ind pcu_sync_ind;
|
||||
struct er_pcu_data_ind pcu_data_ind;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define ER_GPRS_TRAU_FRAME_LEN_16K 324 /* 320 +/-4 bit for time alignment */
|
||||
#define ER_GPRS_TRAU_FRAME_LEN_64K 1296 /* 1280 +/-16 bit for time alignment */
|
||||
int er_gprs_trau_frame_encode_16k(ubit_t *bits, struct er_gprs_trau_frame *fr);
|
||||
int er_gprs_trau_frame_decode_16k(struct er_gprs_trau_frame *fr, const ubit_t *bits);
|
||||
int er_gprs_trau_frame_encode_64k(ubit_t *bits, struct er_gprs_trau_frame *fr);
|
||||
int er_gprs_trau_frame_decode_64k(struct er_gprs_trau_frame *fr, const ubit_t *bits);
|
||||
|
||||
/*
|
||||
* Idle pattern
|
||||
* ============
|
||||
* An inactive CCU will send an idle pattern, which is a sequence of alternating ones and zeros (101010101...) The
|
||||
* idle pattern indicates that the CCU available but in idle state. To leave the idle state a channel activation
|
||||
* via RSL must be carried out. It is not possible to "wake up" an idle CCU by just sending TRAU frames to it.
|
||||
*
|
||||
*
|
||||
* Synchronization procedure
|
||||
* =========================
|
||||
* When the PDCH is activated, the CCU becomes active at its designated E1 timeslot/subslot. It sends out ccu_sync_ind
|
||||
* frames and expects pcu_sync_ind frames from the PCU. When the CCU does not receive any pcu_sync_ind frames, the CCU
|
||||
* becomes inactive until the PDCH is activated again.
|
||||
*
|
||||
* CCU is synchronized to the PCU by sending pcu_sync_ind frames with an incrementing PSEQ sequence number to the CCU.
|
||||
* The PSEQ has no relation to the actual frame number. It just counts the TRAU frames sent over the E1 link. When
|
||||
* the CCU receives a valid pcu_sync_ind it will synchronize its local PSEQ counter to the received PSEQ and respond
|
||||
* back with a ccu_sync_ind that echos the PSEQ and also contains the current GSM frame numbers. The synchronization
|
||||
* is then complete and the PCU may start sending pcu_data_ind.
|
||||
*
|
||||
* PCU CCU
|
||||
* | |
|
||||
* |--------pcu_sync_ind-------->|
|
||||
* |<-------idle pattern---------|
|
||||
* |--------pcu_sync_ind-------->|
|
||||
* |<-------idle pattern---------|
|
||||
* |--------pcu_sync_ind-------->|
|
||||
* |<-------idle pattern---------|
|
||||
* |--------pcu_sync_ind-------->|
|
||||
* |<-------ccu_sync_ind---------|
|
||||
* |--------pcu_sync_ind-------->|
|
||||
* |<-------ccu_sync_ind---------|
|
||||
* |--------pcu_sync_ind-------->|
|
||||
* |<-------ccu_sync_ind---------|
|
||||
* |--------pcu_data_ind-------->|
|
||||
* |<-------ccu_data_ind---------|
|
||||
* |--------pcu_data_ind-------->|
|
||||
* |<-------ccu_data_ind---------|
|
||||
*
|
||||
* While pcu_data_ind and ccu_data_ind frames are passed over the link the PCU has no way to get the current PSEQ or
|
||||
* any GSM frame number from the CCU. It must calculate those values locally. However, it is possible to request
|
||||
* a ccu_sync_ind from the CCU at any time by setting the requested demodulation to ER_UL_CHMOD_VOID in one
|
||||
* pcu_data_ind. It should be pointed out that the requested ccu_sync_ind will steal one pcu_data_ind. The requested
|
||||
* ccu_sync_ind message will also only contain the GSM frame numbers but not the PSEQ value. If the GSM frame numbers
|
||||
* in the received ccu_sync_ind are deviating from the local frame numbers, the synchronization procedure can be
|
||||
* carried out again.
|
||||
*
|
||||
* PCU CCU
|
||||
* | |
|
||||
* |--------pcu_data_ind-------->|
|
||||
* |<-------ccu_data_ind---------|
|
||||
* |--------pcu_data_ind-------->|
|
||||
* |<-------ccu_data_ind---------|
|
||||
* |--------pcu_data_ind-------->| (ER_UL_CHMOD_VOID)
|
||||
* |<-------ccu_sync_ind---------|
|
||||
* |--------pcu_data_ind-------->|
|
||||
* |<-------ccu_data_ind---------|
|
||||
*
|
||||
* It also should be mentioned that due to link latency, there will naturally be a gap between the PCU local PSEQ and
|
||||
* the PSEQ reported by the CCU. This information is important since it must be used to compensate the GSM frame
|
||||
* numbers.
|
||||
*
|
||||
* The procedure is very similar to what is described in US Patent No. 5,978,368 from Nov.2, 1999
|
||||
*
|
||||
* Usage of Timing Adjustment (TAV) parameter
|
||||
* ==========================================
|
||||
* The time adjustment is controlled by the CCU. The TAV value received in TRAU frames that originate from the CCU is
|
||||
* the ordered timing adjustment. The TAV value in TRAU frames that originate from the PCU is the acknowledged time
|
||||
* adjustment. When a timing adjustment is requested by the CCU, the PCU acknowledges it by sending the requested
|
||||
* value back. The timing adjustment is performed immediately within the same frame by appending or removing bits
|
||||
* at the end. This delays or advances the timing of the subsequent frame.
|
||||
*
|
||||
* The procedure is very similar to the one described in: GSM 08.60, section 4.6.1.2
|
||||
*
|
||||
*
|
||||
* Usable coding schemes in uplink
|
||||
* ===============================
|
||||
* To understand which coding schemes are usable one must understand that the first CCU implementation for Ericsson
|
||||
* RBS only supported CS1 and CS2 on 16K I.460 subslots. CS1 and CS2 were not supported due to the bandwidth limitation
|
||||
* of the 16k subslots. In order to support higher bandwidth, in particular EDGE, a new CCU implementation was
|
||||
* introduced. This newer implementation uses full 64K E1 timeslots and has full EDGE support MCS1-MCS9.
|
||||
*
|
||||
* Interestingly though, the newer CCU implementation seems to lack support for CS3-CS4. At least it was not possible
|
||||
* To receive any CS3 or CS4 blocks from the CCU, while CS1 and CS2 work fine. In case the PCU is instructed to use
|
||||
* CS3 or CS4 in downlink nothing is received. Since the CCU also demodulates noise and even sends the demodulation
|
||||
* results back to the PCU there should have been at least some invalid CS3 and CS4 blocks in the demodulation results
|
||||
* from time to time but also here no CS3 or CS4 blocks were observed. This leads to the vague assumption that the GPRS
|
||||
* part of the CCU presumably never got updated and only EDGE support was added.
|
||||
*
|
||||
* It was also discovered that CS1 and CS2 only appear when the CCU is instructed to receive GMSK only
|
||||
* (ER_UL_CHMOD_NB_GMSK). When the CCU is instructed to receive GMSK and PSK (ER_UL_CHMOD_NB_UNKN) CS2 does not appear.
|
||||
* EGPRS related blocks (CS_OR_HDR_HDR1-3) appear in both situations. Since CS1 also plays a role in EGPRS it might be
|
||||
* that the CCU is filtering non EDGE related blocks when ER_UL_CHMOD_NB_UNKN is used.
|
||||
*
|
||||
* The observations above were made with an RRUS 02 B3
|
||||
*/
|
|
@ -0,0 +1,35 @@
|
|||
/* TRAU frame to RTP conversion */
|
||||
|
||||
/* (C) 2009,2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/trau/trau_frame.h>
|
||||
|
||||
struct osmo_trau2rtp_state {
|
||||
enum osmo_trau_frame_type type;
|
||||
uint8_t rtp_extensions;
|
||||
};
|
||||
|
||||
|
||||
int osmo_trau2rtp(uint8_t *out, size_t out_len, const struct osmo_trau_frame *tf,
|
||||
struct osmo_trau2rtp_state *st);
|
||||
|
||||
int osmo_rtp2trau(struct osmo_trau_frame *tf, const uint8_t *rtp, size_t rtp_len,
|
||||
struct osmo_trau2rtp_state *st);
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
/* backwards compatibility */
|
||||
#define osmo_tray_sync_pat_id osmo_trau_sync_pat_id
|
||||
|
||||
enum osmo_trau_sync_pat_id {
|
||||
OSMO_TRAU_SYNCP_16_FR_EFR,
|
||||
OSMO_TRAU_SYNCP_8_HR,
|
||||
OSMO_TRAU_SYNCP_8_AMR_LOW,
|
||||
OSMO_TRAU_SYNCP_8_AMR_6K7,
|
||||
OSMO_TRAU_SYNCP_8_AMR_7K4,
|
||||
OSMO_TRAU_SYNCP_V110,
|
||||
OSMO_TRAU_SYNCP_16_ER_CCU,
|
||||
OSMO_TRAU_SYNCP_64_ER_CCU,
|
||||
OSMO_TRAU_SYNCP_64_ER_CCU_MCS9,
|
||||
OSMO_TRAU_SYNCP_FA, /* TS 43.045 FA (Fax Adaptation) sync frame */
|
||||
};
|
||||
|
||||
typedef void (*frame_out_cb_t)(void *user_data, const ubit_t *bits, unsigned int num_bits);
|
||||
|
||||
struct osmo_fsm_inst *
|
||||
osmo_trau_sync_alloc(void *ctx, const char *name, frame_out_cb_t frame_out_cb,
|
||||
enum osmo_trau_sync_pat_id pat_id, void *user_data);
|
||||
|
||||
void osmo_trau_sync_rx_ubits(struct osmo_fsm_inst *fi, const ubit_t *bits, size_t n_bits);
|
||||
void osmo_trau_sync_set_pat(struct osmo_fsm_inst *fi, enum osmo_trau_sync_pat_id pat_id);
|
||||
void osmo_trau_sync_set_secondary_pat(struct osmo_fsm_inst *fi, enum osmo_trau_sync_pat_id pat_id, size_t pat_index);
|
|
@ -8,4 +8,4 @@ Description: C Utility Library
|
|||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmoabis
|
||||
Cflags: -I${includedir}/
|
||||
|
||||
Requires: libosmocore
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: Osmocom TRAU (E1/RTP) Library
|
||||
Description: C Utility Library
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -losmotrau
|
||||
Cflags: -I${includedir}/
|
||||
Requires: libosmocore
|
|
@ -1,19 +1,50 @@
|
|||
SUBDIRS=input
|
||||
|
||||
# This is _NOT_ the library release version, it's an API version.
|
||||
# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
|
||||
LIBVERSION=0:0:0
|
||||
# Please read chapter "Library interface versions" of the libtool documentation
|
||||
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
|
||||
ABIS_LIBVERSION=13:1:0
|
||||
TRAU_LIBVERSION=9:1:7
|
||||
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS= -fPIC -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS)
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS= -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOE1D_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
COMMONLIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOE1D_LIBS) # libosmocodec not [yet] needed here
|
||||
|
||||
lib_LTLIBRARIES = libosmoabis.la
|
||||
lib_LTLIBRARIES = libosmoabis.la libosmotrau.la
|
||||
|
||||
libosmoabis_la_LIBADD = input/libosmoabis-input.la
|
||||
libosmoabis_la_LDFLAGS = $(AM_LDFLAGS) \
|
||||
-version-info $(ABIS_LIBVERSION) \
|
||||
-no-undefined \
|
||||
$(NULL)
|
||||
libosmoabis_la_LIBADD = $(COMMONLIBS)
|
||||
libosmoabis_la_SOURCES = init.c \
|
||||
e1_input.c \
|
||||
e1_input_vty.c \
|
||||
ipa_proxy.c \
|
||||
subchan_demux.c \
|
||||
trau_frame.c
|
||||
trau_frame.c \
|
||||
input/e1d.c \
|
||||
input/ipa.c \
|
||||
input/ipa_keepalive.c \
|
||||
input/ipaccess.c \
|
||||
input/lapd.c \
|
||||
input/lapd_pcap.c \
|
||||
input/misdn.c \
|
||||
input/rs232.c \
|
||||
input/unixsocket.c
|
||||
if ENABLE_DAHDI
|
||||
libosmoabis_la_SOURCES += input/dahdi.c
|
||||
endif
|
||||
|
||||
libosmotrau_la_CFLAGS = $(AM_CFLAGS) $(ORTP_CFLAGS)
|
||||
libosmotrau_la_LDFLAGS = $(AM_LDFLAGS) \
|
||||
-version-info $(TRAU_LIBVERSION) \
|
||||
-no-undefined \
|
||||
$(NULL)
|
||||
libosmotrau_la_LIBADD = $(COMMONLIBS) $(LIBOSMOCODEC_LIBS) $(ORTP_LIBS)
|
||||
libosmotrau_la_SOURCES = trau/osmo_ortp.c \
|
||||
trau/trau_frame.c \
|
||||
trau/trau_pcu_ericsson.c \
|
||||
trau/trau_sync.c \
|
||||
trau/trau_rtp_conv.c
|
||||
|
||||
noinst_HEADERS = trau/ubit_buf.h
|
||||
|
|
652
src/e1_input.c
652
src/e1_input.c
|
@ -4,6 +4,8 @@
|
|||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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
|
||||
|
@ -20,31 +22,29 @@
|
|||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "../config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <mISDNif.h>
|
||||
|
||||
//#define AF_COMPATIBILITY_FUNC
|
||||
//#include <compat_af_isdn.h>
|
||||
#ifndef AF_ISDN
|
||||
#define AF_ISDN 34
|
||||
#define PF_ISDN AF_ISDN
|
||||
#endif
|
||||
#include <osmocom/abis/lapd.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/core/endian.h>
|
||||
#include <osmocom/gsm/i460_mux.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
||||
#define NUM_E1_TS 32
|
||||
|
@ -61,13 +61,13 @@ static void *tall_sigl_ctx;
|
|||
|
||||
static const struct rate_ctr_desc e1inp_ctr_d[] = {
|
||||
[E1I_CTR_HDLC_ABORT] = {
|
||||
"hdlc.abort", "HDLC abort"
|
||||
"hdlc:abort", "HDLC abort"
|
||||
},
|
||||
[E1I_CTR_HDLC_BADFCS] = {
|
||||
"hdlc.bad_fcs", "HLDC Bad FCS"
|
||||
"hdlc:bad_fcs", "HLDC Bad FCS"
|
||||
},
|
||||
[E1I_CTR_HDLC_OVERR] = {
|
||||
"hdlc.overrun", "HDLC Overrun"
|
||||
"hdlc:overrun", "HDLC Overrun"
|
||||
},
|
||||
[E1I_CTR_ALARM] = {
|
||||
"alarm", "Alarm"
|
||||
|
@ -118,12 +118,19 @@ struct fake_linux_lapd_header {
|
|||
} __attribute__((packed));
|
||||
|
||||
struct lapd_header {
|
||||
#if OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t ea1 : 1;
|
||||
uint8_t cr : 1;
|
||||
uint8_t sapi : 6;
|
||||
uint8_t ea2 : 1;
|
||||
uint8_t tei : 7;
|
||||
uint8_t control_foo; /* fake UM's ... */
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
||||
uint8_t sapi:6, cr:1, ea1:1;
|
||||
uint8_t tei:7, ea2:1;
|
||||
uint8_t control_foo;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
osmo_static_assert(offsetof(struct fake_linux_lapd_header, hatype) == 2, hatype_offset);
|
||||
|
@ -132,13 +139,9 @@ osmo_static_assert(offsetof(struct fake_linux_lapd_header, addr) == 6, addr
|
|||
osmo_static_assert(offsetof(struct fake_linux_lapd_header, protocol) == 14, proto_offset);
|
||||
osmo_static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size);
|
||||
|
||||
|
||||
static int pcap_fd = -1;
|
||||
|
||||
void e1_set_pcap_fd(int fd)
|
||||
int e1_set_pcap_fd2(struct e1inp_line *line, int fd)
|
||||
{
|
||||
int ret;
|
||||
struct pcap_hdr header = {
|
||||
static const struct pcap_hdr header = {
|
||||
.magic_number = 0xa1b2c3d4,
|
||||
.version_major = 2,
|
||||
.version_minor = 4,
|
||||
|
@ -147,18 +150,79 @@ void e1_set_pcap_fd(int fd)
|
|||
.snaplen = 65535,
|
||||
.network = DLT_LINUX_LAPD,
|
||||
};
|
||||
int i;
|
||||
|
||||
/* write header */
|
||||
if (fd >= 0) {
|
||||
int rc = write(fd, &header, sizeof(header));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set the PCAP file descriptor for all timeslots that have
|
||||
* software LAPD instances, to ensure the osmo_lapd_pcap code is
|
||||
* used to write PCAP files (if requested) */
|
||||
for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
|
||||
struct e1inp_ts *e1i_ts = &line->ts[i];
|
||||
if (e1i_ts->lapd)
|
||||
e1i_ts->lapd->pcap_fd = fd;
|
||||
}
|
||||
/* close previous and update */
|
||||
if (line->pcap_fd >= 0)
|
||||
close(line->pcap_fd);
|
||||
line->pcap_fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcap_fd = -1;
|
||||
|
||||
int e1_set_pcap_fd(int fd)
|
||||
{
|
||||
const struct pcap_hdr header = {
|
||||
.magic_number = 0xa1b2c3d4,
|
||||
.version_major = 2,
|
||||
.version_minor = 4,
|
||||
.thiszone = 0,
|
||||
.sigfigs = 0,
|
||||
.snaplen = 65535,
|
||||
.network = DLT_LINUX_LAPD,
|
||||
};
|
||||
struct e1inp_line *line;
|
||||
int i;
|
||||
|
||||
/* write header */
|
||||
if (fd >= 0) {
|
||||
int rc = write(fd, &header, sizeof(header));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* update fd in all lines in our global list of e1 lines */
|
||||
llist_for_each_entry(line, &e1inp_line_list, list) {
|
||||
/* Set the PCAP file descriptor for all timeslots that have
|
||||
* software LAPD instances, to ensure the osmo_lapd_pcap code is
|
||||
* used to write PCAP files (if requested) */
|
||||
for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
|
||||
struct e1inp_ts *e1i_ts = &line->ts[i];
|
||||
if (e1i_ts->lapd)
|
||||
e1i_ts->lapd->pcap_fd = fd;
|
||||
}
|
||||
}
|
||||
|
||||
/* close previous and update global */
|
||||
if (pcap_fd >= 0)
|
||||
close(pcap_fd);
|
||||
pcap_fd = fd;
|
||||
ret = write(pcap_fd, &header, sizeof(header));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This currently only works for the D-Channel */
|
||||
static void write_pcap_packet(int direction, int sapi, int tei,
|
||||
struct msgb *msg) {
|
||||
struct msgb *msg, int pcap_fd) {
|
||||
if (pcap_fd < 0)
|
||||
return;
|
||||
|
||||
int ret;
|
||||
time_t cur_time;
|
||||
struct tm *tm;
|
||||
|
||||
|
@ -193,35 +257,38 @@ static void write_pcap_packet(int direction, int sapi, int tei,
|
|||
tm = localtime(&cur_time);
|
||||
payload_header.ts_sec = mktime(tm);
|
||||
|
||||
ret = write(pcap_fd, &payload_header, sizeof(payload_header));
|
||||
ret = write(pcap_fd, &header, sizeof(header));
|
||||
ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
|
||||
ret = write(pcap_fd, msg->l2h, msgb_l2len(msg));
|
||||
write(pcap_fd, &payload_header, sizeof(payload_header));
|
||||
write(pcap_fd, &header, sizeof(header));
|
||||
write(pcap_fd, &lapd_header, sizeof(lapd_header));
|
||||
write(pcap_fd, msg->l2h, msgb_l2len(msg));
|
||||
}
|
||||
|
||||
static const char *sign_types[] = {
|
||||
[E1INP_SIGN_NONE] = "None",
|
||||
[E1INP_SIGN_OML] = "OML",
|
||||
[E1INP_SIGN_RSL] = "RSL",
|
||||
const struct value_string e1inp_sign_type_names[5] = {
|
||||
{ E1INP_SIGN_NONE, "None" },
|
||||
{ E1INP_SIGN_OML, "OML" },
|
||||
{ E1INP_SIGN_RSL, "RSL" },
|
||||
{ E1INP_SIGN_OSMO, "OSMO" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *e1inp_signtype_name(enum e1inp_sign_type tp)
|
||||
{
|
||||
if (tp >= ARRAY_SIZE(sign_types))
|
||||
return "undefined";
|
||||
return sign_types[tp];
|
||||
return get_value_string(e1inp_sign_type_names, tp);
|
||||
}
|
||||
|
||||
static const char *ts_types[] = {
|
||||
[E1INP_TS_TYPE_NONE] = "None",
|
||||
[E1INP_TS_TYPE_SIGN] = "Signalling",
|
||||
[E1INP_TS_TYPE_TRAU] = "TRAU",
|
||||
const struct value_string e1inp_ts_type_names[] = {
|
||||
{ E1INP_TS_TYPE_NONE, "None" },
|
||||
{ E1INP_TS_TYPE_SIGN, "Signalling" },
|
||||
{ E1INP_TS_TYPE_TRAU, "TRAU" },
|
||||
{ E1INP_TS_TYPE_RAW, "RAW" },
|
||||
{ E1INP_TS_TYPE_HDLC, "HDLC" },
|
||||
{ E1INP_TS_TYPE_I460, "I460" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *e1inp_tstype_name(enum e1inp_ts_type tp)
|
||||
{
|
||||
if (tp >= ARRAY_SIZE(ts_types))
|
||||
return "undefined";
|
||||
return ts_types[tp];
|
||||
return get_value_string(e1inp_ts_type_names, tp);
|
||||
}
|
||||
|
||||
int abis_sendmsg(struct msgb *msg)
|
||||
|
@ -229,7 +296,7 @@ int abis_sendmsg(struct msgb *msg)
|
|||
struct e1inp_sign_link *sign_link = msg->dst;
|
||||
struct e1inp_driver *e1inp_driver;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
|
||||
/* don't know how to route this message. */
|
||||
|
@ -247,8 +314,14 @@ int abis_sendmsg(struct msgb *msg)
|
|||
}
|
||||
msgb_enqueue(&sign_link->tx_list, msg);
|
||||
|
||||
/* dump it */
|
||||
write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
|
||||
/* we only need to write a 'Fake LAPD' packet here, if the
|
||||
* underlying driver hides LAPD from us. If we use the
|
||||
* libosmocore LAPD implementation, it will take care of writing
|
||||
* the _actual_ LAPD packet */
|
||||
if (!e1i_ts->lapd) {
|
||||
write_pcap_packet(PCAP_OUTPUT, sign_link->sapi,
|
||||
sign_link->tei, msg, e1i_ts->line->pcap_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -258,14 +331,96 @@ int abis_rsl_sendmsg(struct msgb *msg)
|
|||
return abis_sendmsg(msg);
|
||||
}
|
||||
|
||||
int e1inp_ts_send_raw(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
struct e1inp_driver *driver;
|
||||
|
||||
OSMO_ASSERT(ts->type == E1INP_TS_TYPE_RAW);
|
||||
|
||||
/* notify the driver we have something to write */
|
||||
driver = ts->line->driver;
|
||||
driver->want_write(ts);
|
||||
|
||||
msgb_enqueue(&ts->raw.tx_queue, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e1inp_ts_send_hdlc(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
struct e1inp_driver *driver;
|
||||
|
||||
OSMO_ASSERT(ts->type == E1INP_TS_TYPE_HDLC);
|
||||
|
||||
/* notify the driver we have something to write */
|
||||
driver = ts->line->driver;
|
||||
driver->want_write(ts);
|
||||
|
||||
msgb_enqueue(&ts->hdlc.tx_queue, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Depending on its typ a timeslot may hold resources which must be cleaned up
|
||||
* or reset before the type of the timeslot type may be changed. */
|
||||
static int cleanup_e1inp_ts(struct e1inp_ts *e1i_ts)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
/* The caller is responsible for removing all signalling links
|
||||
* first. */
|
||||
if (!llist_empty(&e1i_ts->sign.sign_links)) {
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR,
|
||||
"timeslot still holds active signalling links -- cannot modify timeslot!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
/* Call the initialization functions once more. This will
|
||||
* deactivate all subchannels so that the related callbacks
|
||||
* are no longer called */
|
||||
subchan_mux_init(&e1i_ts->trau.mux);
|
||||
subch_demux_init(&e1i_ts->trau.demux);
|
||||
return 0;
|
||||
case E1INP_TS_TYPE_RAW:
|
||||
msgb_queue_free(&e1i_ts->raw.tx_queue);
|
||||
return 0;
|
||||
case E1INP_TS_TYPE_HDLC:
|
||||
msgb_queue_free(&e1i_ts->hdlc.tx_queue);
|
||||
return 0;
|
||||
case E1INP_TS_TYPE_I460:
|
||||
/* The caller is responsible for removing all I.460 subchannels
|
||||
* first. */
|
||||
rc = osmo_i460_subchan_count(&e1i_ts->i460.i460_ts);
|
||||
if (rc != 0) {
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR,
|
||||
"timeslot still holds active I.460 subchannels -- cannot modify timeslot!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
case E1INP_TS_TYPE_NONE:
|
||||
return 0;
|
||||
default:
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "unsupported E1 TS type %u\n",
|
||||
e1i_ts->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Timeslot */
|
||||
int e1inp_ts_config_trau(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
int (*trau_rcv_cb)(struct subch_demux *dmx, int ch,
|
||||
uint8_t *data, int len, void *_priv))
|
||||
const ubit_t *data, int len, void *_priv))
|
||||
{
|
||||
if (ts->type == E1INP_TS_TYPE_TRAU && ts->line && line)
|
||||
return 0;
|
||||
|
||||
cleanup_e1inp_ts(ts);
|
||||
|
||||
ts->type = E1INP_TS_TYPE_TRAU;
|
||||
ts->line = line;
|
||||
|
||||
|
@ -276,11 +431,34 @@ int e1inp_ts_config_trau(struct e1inp_ts *ts, struct e1inp_line *line,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int e1inp_ts_config_i460(struct e1inp_ts *ts, struct e1inp_line *line)
|
||||
{
|
||||
if (ts->type == E1INP_TS_TYPE_I460 && ts->line && line)
|
||||
return 0;
|
||||
|
||||
cleanup_e1inp_ts(ts);
|
||||
|
||||
ts->type = E1INP_TS_TYPE_I460;
|
||||
ts->line = line;
|
||||
osmo_i460_ts_init(&ts->i460.i460_ts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void e1inp_ts_name(char *out, size_t out_len, const struct e1inp_ts *ts)
|
||||
{
|
||||
if (ts->line->name)
|
||||
snprintf(out, out_len, "%s:%u", ts->line->name, ts->num);
|
||||
else
|
||||
snprintf(out, out_len, "%u:%u", ts->line->num, ts->num);
|
||||
}
|
||||
|
||||
int e1inp_ts_config_sign(struct e1inp_ts *ts, struct e1inp_line *line)
|
||||
{
|
||||
if (ts->type == E1INP_TS_TYPE_SIGN && ts->line && line)
|
||||
return 0;
|
||||
|
||||
cleanup_e1inp_ts(ts);
|
||||
|
||||
ts->type = E1INP_TS_TYPE_SIGN;
|
||||
ts->line = line;
|
||||
|
||||
|
@ -292,6 +470,107 @@ int e1inp_ts_config_sign(struct e1inp_ts *ts, struct e1inp_line *line)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int e1inp_ts_config_raw(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
void (*raw_recv_cb)(struct e1inp_ts *ts,
|
||||
struct msgb *msg))
|
||||
{
|
||||
if (ts->type == E1INP_TS_TYPE_RAW && ts->line && line)
|
||||
return 0;
|
||||
|
||||
cleanup_e1inp_ts(ts);
|
||||
|
||||
ts->type = E1INP_TS_TYPE_RAW;
|
||||
ts->line = line;
|
||||
ts->raw.recv_cb = raw_recv_cb;
|
||||
INIT_LLIST_HEAD(&ts->raw.tx_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e1inp_ts_config_hdlc(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
void (*hdlc_recv_cb)(struct e1inp_ts *ts,
|
||||
struct msgb *msg))
|
||||
{
|
||||
if (ts->type == E1INP_TS_TYPE_HDLC && ts->line && line)
|
||||
return 0;
|
||||
|
||||
cleanup_e1inp_ts(ts);
|
||||
|
||||
ts->type = E1INP_TS_TYPE_HDLC;
|
||||
ts->line = line;
|
||||
ts->hdlc.recv_cb = hdlc_recv_cb;
|
||||
INIT_LLIST_HEAD(&ts->hdlc.tx_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e1inp_ts_config_none(struct e1inp_ts *ts, struct e1inp_line *line)
|
||||
{
|
||||
if (ts->type == E1INP_TS_TYPE_NONE && ts->line && line)
|
||||
return 0;
|
||||
|
||||
cleanup_e1inp_ts(ts);
|
||||
|
||||
ts->type = E1INP_TS_TYPE_NONE;
|
||||
ts->line = line;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e1inp_ts_set_sa_bits(struct e1inp_line *line, uint8_t sa_bits)
|
||||
{
|
||||
struct e1inp_driver *driver;
|
||||
|
||||
driver = line->driver;
|
||||
if (!driver->set_sa_bits)
|
||||
return -ENOTSUP;
|
||||
return driver->set_sa_bits(line, sa_bits);
|
||||
}
|
||||
|
||||
static int e1inp_line_use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count,
|
||||
const char *file, int file_line)
|
||||
{
|
||||
char buf[512];
|
||||
struct osmo_use_count *uc = use_count_entry->use_count;
|
||||
struct e1inp_line *line = uc->talloc_object;
|
||||
|
||||
LOGPSRC(DLINP, LOGL_INFO, file, file_line,
|
||||
"E1L(%u) Line (%p) reference count %s changed %" PRId32 " -> %" PRId32 " [%s]\n",
|
||||
(line)->num, line, use_count_entry->use,
|
||||
old_use_count, use_count_entry->count,
|
||||
osmo_use_count_name_buf(buf, sizeof(buf), uc));
|
||||
|
||||
if (!use_count_entry->count)
|
||||
osmo_use_count_free(use_count_entry);
|
||||
|
||||
if (osmo_use_count_total(uc) > 0)
|
||||
return 0;
|
||||
|
||||
/* Remove our counter group from libosmocore's global counter
|
||||
* list if we are freeing the last remaining talloc context.
|
||||
* Otherwise we get a use-after-free when libosmocore's timer
|
||||
* ticks again and attempts to update these counters (OS#3011).
|
||||
*
|
||||
* Note that talloc internally counts "secondary" references
|
||||
* _in addition to_ the initial allocation context, so yes,
|
||||
* we must check for *zero* remaining secondary contexts here. */
|
||||
if (talloc_reference_count(line->rate_ctr) == 0) {
|
||||
rate_ctr_group_free(line->rate_ctr);
|
||||
} else {
|
||||
/* We are not freeing the last talloc context.
|
||||
* Instead of calling talloc_free(), unlink this 'line' pointer
|
||||
* which serves as one of several talloc contexts for the rate
|
||||
* counters and driver private state. */
|
||||
talloc_unlink(line, line->rate_ctr);
|
||||
if (line->driver_data)
|
||||
talloc_unlink(line, line->driver_data);
|
||||
}
|
||||
|
||||
llist_del(&line->list);
|
||||
talloc_free(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct e1inp_line *e1inp_line_find(uint8_t e1_nr)
|
||||
{
|
||||
struct e1inp_line *e1i_line;
|
||||
|
@ -304,6 +583,14 @@ struct e1inp_line *e1inp_line_find(uint8_t e1_nr)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*! Create a new e1inp line object.
|
||||
* \param[in] e1_nr The line number of the new line to be created.
|
||||
* \param[in] driver_name String identifying the driver (see e1inp_driver_register() for more info).
|
||||
* \returns pointer to the new object created.
|
||||
*
|
||||
* The allocated object is returned with a count reference with name "ctor",
|
||||
* which must be dropped in order to free the object [e1inp_line_put2(line, "ctor")].
|
||||
*/
|
||||
struct e1inp_line *
|
||||
e1inp_line_create(uint8_t e1_nr, const char *driver_name)
|
||||
{
|
||||
|
@ -313,8 +600,7 @@ e1inp_line_create(uint8_t e1_nr, const char *driver_name)
|
|||
|
||||
line = e1inp_line_find(e1_nr);
|
||||
if (line) {
|
||||
LOGP(DLINP, LOGL_ERROR, "E1 Line %u already exists\n",
|
||||
e1_nr);
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "E1 Line %u already exists\n", e1_nr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -331,21 +617,45 @@ e1inp_line_create(uint8_t e1_nr, const char *driver_name)
|
|||
|
||||
line->driver = driver;
|
||||
line->num = e1_nr;
|
||||
line->pcap_fd = -1;
|
||||
|
||||
line->keepalive_idle_timeout = E1INP_USE_DEFAULT;
|
||||
line->keepalive_num_probes = E1INP_USE_DEFAULT;
|
||||
line->keepalive_probe_interval = E1INP_USE_DEFAULT;
|
||||
line->connect_timeout = 0;
|
||||
|
||||
line->rate_ctr = rate_ctr_group_alloc(line, &e1inp_ctr_g_d, line->num);
|
||||
if (!line->rate_ctr) {
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "Cannot allocate counter group\n");
|
||||
talloc_free(line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_E1_TS; i++) {
|
||||
line->num_ts = NUM_E1_TS;
|
||||
for (i = 0; i < line->num_ts; i++) {
|
||||
line->ts[i].num = i+1;
|
||||
line->ts[i].line = line;
|
||||
}
|
||||
line->refcnt++;
|
||||
|
||||
line->use_count.talloc_object = line;
|
||||
line->use_count.use_cb = e1inp_line_use_cb;
|
||||
|
||||
if (driver->line_create) {
|
||||
if (driver->line_create(line) < 0) {
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "Cannot initialize line driver\n");
|
||||
talloc_free(line);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
e1inp_line_get2(line, "ctor");
|
||||
llist_add_tail(&line->list, &e1inp_line_list);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
struct e1inp_line *
|
||||
e1inp_line_clone(void *ctx, struct e1inp_line *line)
|
||||
e1inp_line_clone(void *ctx, struct e1inp_line *line, const char *use)
|
||||
{
|
||||
struct e1inp_line *clone;
|
||||
|
||||
|
@ -355,20 +665,44 @@ e1inp_line_clone(void *ctx, struct e1inp_line *line)
|
|||
return NULL;
|
||||
|
||||
memcpy(clone, line, sizeof(struct e1inp_line));
|
||||
clone->refcnt = 1;
|
||||
|
||||
if (line->name) {
|
||||
clone->name = talloc_strdup(clone, line->name);
|
||||
OSMO_ASSERT(clone->name);
|
||||
}
|
||||
if (line->sock_path) {
|
||||
clone->sock_path = talloc_strdup(clone, line->sock_path);
|
||||
OSMO_ASSERT(clone->sock_path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rate counters and driver data are shared between clones. These are pointers
|
||||
* to dynamic memory so we use reference counting to avoid a double-free (see OS#3137).
|
||||
*/
|
||||
OSMO_ASSERT(line->rate_ctr);
|
||||
clone->rate_ctr = talloc_reference(clone, line->rate_ctr);
|
||||
if (line->driver_data)
|
||||
clone->driver_data = talloc_reference(clone, line->driver_data);
|
||||
|
||||
clone->use_count = (struct osmo_use_count) {
|
||||
.talloc_object = clone,
|
||||
.use_cb = e1inp_line_use_cb,
|
||||
.use_counts = {0},
|
||||
};
|
||||
/* initialize list so it can be safely deleted without affecting original line */
|
||||
INIT_LLIST_HEAD(&clone->list);
|
||||
e1inp_line_get2(clone, use); /* Clone is used internally for bfd */
|
||||
return clone;
|
||||
}
|
||||
|
||||
void e1inp_line_get(struct e1inp_line *line)
|
||||
{
|
||||
line->refcnt++;
|
||||
e1inp_line_get2(line, "unknown");
|
||||
}
|
||||
|
||||
void e1inp_line_put(struct e1inp_line *line)
|
||||
{
|
||||
line->refcnt--;
|
||||
if (line->refcnt == 0)
|
||||
talloc_free(line);
|
||||
e1inp_line_put2(line, "unknown");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -461,6 +795,8 @@ e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
|
|||
link->tei = tei;
|
||||
link->sapi = sapi;
|
||||
|
||||
e1inp_line_get2(link->ts->line, "e1inp_sign_link");
|
||||
|
||||
llist_add_tail(&link->list, &ts->sign.sign_links);
|
||||
|
||||
return link;
|
||||
|
@ -482,6 +818,7 @@ void e1inp_sign_link_destroy(struct e1inp_sign_link *link)
|
|||
if (link->ts->line->driver->close)
|
||||
link->ts->line->driver->close(link);
|
||||
|
||||
e1inp_line_put2(link->ts->line, "e1inp_sign_link");
|
||||
talloc_free(link);
|
||||
}
|
||||
|
||||
|
@ -495,17 +832,24 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
|||
|
||||
switch (ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
/* we only need to write a 'Fake LAPD' packet here, if
|
||||
* the underlying driver hides LAPD from us. If we use
|
||||
* the libosmocore LAPD implementation, it will take
|
||||
* care of writing the _actual_ LAPD packet */
|
||||
if (!ts->lapd)
|
||||
write_pcap_packet(PCAP_INPUT, sapi, tei, msg, ts->line->pcap_fd);
|
||||
/* consult the list of signalling links */
|
||||
write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
|
||||
link = e1inp_lookup_sign_link(ts, tei, sapi);
|
||||
if (!link) {
|
||||
LOGP(DLMI, LOGL_ERROR, "didn't find signalling link for "
|
||||
LOGPITS(ts, DLMI, LOGL_ERROR, "didn't find signalling link for "
|
||||
"tei %d, sapi %d\n", tei, sapi);
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!ts->line->ops->sign_link) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Fix your application, "
|
||||
LOGPITS(ts, DLINP, LOGL_ERROR, "Fix your application, "
|
||||
"no action set for signalling messages.\n");
|
||||
msgb_free(msg);
|
||||
return -ENOENT;
|
||||
}
|
||||
msg->dst = link;
|
||||
|
@ -513,16 +857,106 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
|||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg));
|
||||
msgb_free(msg);
|
||||
break;
|
||||
case E1INP_TS_TYPE_RAW:
|
||||
ts->raw.recv_cb(ts, msg);
|
||||
break;
|
||||
case E1INP_TS_TYPE_HDLC:
|
||||
ts->hdlc.recv_cb(ts, msg);
|
||||
break;
|
||||
case E1INP_TS_TYPE_I460:
|
||||
osmo_i460_demux_in(&ts->i460.i460_ts, msg->l2h, msgb_l2len(msg));
|
||||
msgb_free(msg);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
LOGP(DLMI, LOGL_ERROR, "unknown TS type %u\n", ts->type);
|
||||
LOGPITS(ts, DLMI, LOGL_ERROR, "unknown TS type %u\n", ts->type);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! \brief Receive some data from the L1/HDLC into LAPD of a timeslot
|
||||
* \param[in] e1i_ts E1 Timeslot data structure
|
||||
* \param[in] msg Message buffer containing full LAPD message
|
||||
*
|
||||
* This is a wrapper around e1inp_rx_ts(), but feeding the incoming
|
||||
* message first into our LAPD code. This allows a driver to read raw
|
||||
* (HDLC decoded) data from the timeslot, instead of a LAPD stack
|
||||
* present in any underlying driver.
|
||||
*/
|
||||
int e1inp_rx_ts_lapd(struct e1inp_ts *e1i_ts, struct msgb *msg)
|
||||
{
|
||||
unsigned int sapi, tei;
|
||||
int ret = 0, error = 0;
|
||||
|
||||
sapi = msg->data[0] >> 2;
|
||||
if ((msg->data[0] & 0x1))
|
||||
tei = 0;
|
||||
else
|
||||
tei = msg->data[1] >> 1;
|
||||
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "<= len = %d, sapi(%d) tei(%d)\n", msg->len, sapi, tei);
|
||||
|
||||
ret = lapd_receive(e1i_ts->lapd, msg, &error);
|
||||
if (ret < 0) {
|
||||
switch(error) {
|
||||
case LAPD_ERR_UNKNOWN_TEI:
|
||||
/* We don't know about this TEI, probably the BSC
|
||||
* lost local states (it crashed or it was stopped),
|
||||
* notify the driver to see if it can do anything to
|
||||
* recover the existing signalling links with the BTS.
|
||||
*/
|
||||
e1inp_event(e1i_ts, S_L_INP_TEI_UNKNOWN, tei, sapi);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void e1inp_dlsap_up(struct osmo_dlsap_prim *dp, uint8_t tei, uint8_t sapi,
|
||||
void *rx_cbdata)
|
||||
{
|
||||
struct e1inp_ts *e1i_ts = rx_cbdata;
|
||||
struct msgb *msg = dp->oph.msg;
|
||||
|
||||
switch (dp->oph.primitive) {
|
||||
case PRIM_DL_EST:
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "DL_EST: sapi(%d) tei(%d)\n", sapi, tei);
|
||||
e1inp_event(e1i_ts, S_L_INP_TEI_UP, tei, sapi);
|
||||
break;
|
||||
case PRIM_DL_REL:
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "DL_REL: sapi(%d) tei(%d)\n", sapi, tei);
|
||||
e1inp_event(e1i_ts, S_L_INP_TEI_DN, tei, sapi);
|
||||
break;
|
||||
case PRIM_DL_DATA:
|
||||
case PRIM_DL_UNIT_DATA:
|
||||
if (dp->oph.operation == PRIM_OP_INDICATION) {
|
||||
msg->l2h = msg->l3h;
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "RX: %s sapi=%d tei=%d\n",
|
||||
osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)),
|
||||
sapi, tei);
|
||||
e1inp_rx_ts(e1i_ts, msg, tei, sapi);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case PRIM_MDL_ERROR:
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "MDL_EERROR: cause(%d)\n", dp->u.error_ind.cause);
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unknown prim\n");
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#define TSX_ALLOC_SIZE 4096
|
||||
|
||||
/* called by driver if it wants to transmit on a given TS */
|
||||
|
@ -550,36 +984,62 @@ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
|
|||
if (!msg)
|
||||
return NULL;
|
||||
len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40);
|
||||
if (len != 40) {
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "cannot transmit, failed to mux\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
msgb_put(msg, 40);
|
||||
break;
|
||||
case E1INP_TS_TYPE_RAW:
|
||||
/* Get msgb from tx_queue */
|
||||
msg = msgb_dequeue(&e1i_ts->raw.tx_queue);
|
||||
break;
|
||||
case E1INP_TS_TYPE_HDLC:
|
||||
/* Get msgb from tx_queue */
|
||||
msg = msgb_dequeue(&e1i_ts->hdlc.tx_queue);
|
||||
break;
|
||||
case E1INP_TS_TYPE_I460:
|
||||
msg = msgb_alloc(TSX_ALLOC_SIZE, "I460_TX");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
len = osmo_i460_mux_out(&e1i_ts->i460.i460_ts, msg->data, 160);
|
||||
msgb_put(msg, len);
|
||||
break;
|
||||
default:
|
||||
LOGP(DLMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type);
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type);
|
||||
return NULL;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* called by driver in case some kind of link state event */
|
||||
int e1inp_event(struct e1inp_ts *ts, int evt, uint8_t tei, uint8_t sapi)
|
||||
int e1inp_int_snd_event(struct e1inp_ts *ts, struct e1inp_sign_link *link, int evt)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
struct input_signal_data isd;
|
||||
|
||||
link = e1inp_lookup_sign_link(ts, tei, sapi);
|
||||
if (!link)
|
||||
return -EINVAL;
|
||||
|
||||
isd.line = ts->line;
|
||||
isd.ts_nr = ts->num;
|
||||
isd.link_type = link->type;
|
||||
isd.trx = link->trx;
|
||||
isd.tei = tei;
|
||||
isd.sapi = sapi;
|
||||
isd.tei = link->tei;
|
||||
isd.sapi = link->sapi;
|
||||
|
||||
/* report further upwards */
|
||||
osmo_signal_dispatch(SS_L_INPUT, evt, &isd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* called by driver in case some kind of link state event */
|
||||
int e1inp_event(struct e1inp_ts *ts, int evt, uint8_t tei, uint8_t sapi)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
|
||||
link = e1inp_lookup_sign_link(ts, tei, sapi);
|
||||
if (!link)
|
||||
return -EINVAL;
|
||||
return e1inp_int_snd_event(ts, link, evt);
|
||||
}
|
||||
|
||||
/* register a driver with the E1 core */
|
||||
int e1inp_driver_register(struct e1inp_driver *drv)
|
||||
{
|
||||
|
@ -601,19 +1061,22 @@ struct e1inp_driver *e1inp_driver_find(const char *name)
|
|||
int e1inp_line_update(struct e1inp_line *line)
|
||||
{
|
||||
struct input_signal_data isd;
|
||||
int rc;
|
||||
|
||||
e1inp_line_get(line);
|
||||
|
||||
/* This line has been already initialized, skip this. */
|
||||
if (line->refcnt > 2)
|
||||
return 0;
|
||||
int i, rc;
|
||||
|
||||
if (line->driver && line->ops && line->driver->line_update) {
|
||||
rc = line->driver->line_update(line);
|
||||
} else
|
||||
rc = 0;
|
||||
|
||||
/* Set the PCAP file descriptor for all timeslots that have
|
||||
* software LAPD instances, to ensure the osmo_lapd_pcap code is
|
||||
* used to write PCAP files (if requested) */
|
||||
for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
|
||||
struct e1inp_ts *e1i_ts = &line->ts[i];
|
||||
if (e1i_ts->lapd)
|
||||
e1i_ts->lapd->pcap_fd = line->pcap_fd;
|
||||
}
|
||||
|
||||
/* Send a signal to anyone who is interested in new lines being
|
||||
* configured */
|
||||
memset(&isd, 0, sizeof(isd));
|
||||
|
@ -626,23 +1089,49 @@ int e1inp_line_update(struct e1inp_line *line)
|
|||
static int e1i_sig_cb(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
|
||||
if (subsys != SS_L_GLOBAL ||
|
||||
signal != S_L_GLOBAL_SHUTDOWN)
|
||||
return 0;
|
||||
|
||||
if (pcap_fd) {
|
||||
close(pcap_fd);
|
||||
pcap_fd = -1;
|
||||
llist_for_each_entry(line, &e1inp_line_list, list) {
|
||||
if (line->pcap_fd >=0)
|
||||
close(line->pcap_fd);
|
||||
line->pcap_fd = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct value_string e1inp_signal_names[] = {
|
||||
{ S_L_INP_NONE, "NONE" },
|
||||
{ S_L_INP_TEI_UP, "TEI-UP" },
|
||||
{ S_L_INP_TEI_DN, "TEI-DOWN" },
|
||||
{ S_L_INP_TEI_UNKNOWN, "TEI-UNKNOWN" },
|
||||
{ S_L_INP_LINE_INIT, "LINE-INIT" },
|
||||
{ S_L_INP_LINE_ALARM, "LINE-ALARM" },
|
||||
{ S_L_INP_LINE_NOALARM, "LINE-NOALARM" },
|
||||
{ S_L_INP_LINE_LOS, "LINE-LOS" },
|
||||
{ S_L_INP_LINE_NOLOS, "LINE-NOLOS" },
|
||||
{ S_L_INP_LINE_AIS, "LINE-AIS" },
|
||||
{ S_L_INP_LINE_NOAIS, "LINE-NOAIS" },
|
||||
{ S_L_INP_LINE_RAI, "LINE-RAI" },
|
||||
{ S_L_INP_LINE_NORAI, "LINE-NORAI" },
|
||||
{ S_L_INP_LINE_SLIP_RX, "LINE-SLIP-RX" },
|
||||
{ S_L_INP_LINE_SLIP_TX, "LINE-SLIP-TX" },
|
||||
{ S_L_INP_LINE_SA_BITS, "LINE-SA-BITS" },
|
||||
{ S_L_INP_LINE_LOF, "LINE-LOF" },
|
||||
{ S_L_INP_LINE_NOLOF, "LINE-NOLOF" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
void e1inp_misdn_init(void);
|
||||
void e1inp_dahdi_init(void);
|
||||
void e1inp_e1d_init(void);
|
||||
void e1inp_ipaccess_init(void);
|
||||
void e1inp_hsl_init(void);
|
||||
void e1inp_rs232_init(void);
|
||||
void e1inp_unixsocket_init(void);
|
||||
|
||||
void e1inp_init(void)
|
||||
{
|
||||
|
@ -652,8 +1141,13 @@ void e1inp_init(void)
|
|||
osmo_signal_register_handler(SS_L_GLOBAL, e1i_sig_cb, NULL);
|
||||
|
||||
e1inp_misdn_init();
|
||||
#ifdef HAVE_DAHDI_USER_H
|
||||
e1inp_dahdi_init();
|
||||
#endif
|
||||
#ifdef HAVE_E1D
|
||||
e1inp_e1d_init();
|
||||
#endif
|
||||
e1inp_ipaccess_init();
|
||||
e1inp_hsl_init();
|
||||
e1inp_rs232_init();
|
||||
e1inp_unixsocket_init();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
/* E1 vty interface */
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2011-2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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
|
||||
|
@ -18,9 +20,13 @@
|
|||
*/
|
||||
#include "internal.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
@ -35,17 +41,24 @@
|
|||
#include <osmocom/vty/telnet_interface.h>
|
||||
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#define X(x) (1 << x)
|
||||
|
||||
/* CONFIG */
|
||||
|
||||
#define E1_DRIVER_NAMES "(misdn|dahdi|ipa|hsl)"
|
||||
#define E1_DRIVER_HELP "mISDN supported E1 Card\n" \
|
||||
#define E1_DRIVER_NAMES "(misdn|misdn_lapd|dahdi|e1d|ipa|unixsocket)"
|
||||
#define E1_DRIVER_HELP "mISDN supported E1 Card (kernel LAPD)\n" \
|
||||
"mISDN supported E1 Card (userspace LAPD)\n" \
|
||||
"DAHDI supported E1/T1/J1 Card\n" \
|
||||
"IPA TCP/IP input" \
|
||||
"HSL TCP/IP input"
|
||||
"osmo-e1d supported E1 interface\n" \
|
||||
"IPA TCP/IP input\n" \
|
||||
"Unix socket input\n"
|
||||
|
||||
#define E1_LINE_HELP "Configure E1/T1/J1 Line\n" "Line Number\n"
|
||||
|
||||
/* Note: This requires a full restart of the application, since once created
|
||||
* an E1 line can not be destroyed again. */
|
||||
DEFUN(cfg_e1line_driver, cfg_e1_line_driver_cmd,
|
||||
"e1_line <0-255> driver " E1_DRIVER_NAMES,
|
||||
E1_LINE_HELP "Set driver for this line\n"
|
||||
|
@ -68,9 +81,152 @@ DEFUN(cfg_e1line_driver, cfg_e1_line_driver_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_e1line_name, cfg_e1_line_name_cmd,
|
||||
"e1_line <0-255> name .LINE",
|
||||
E1_LINE_HELP "Set name for this line\n" "Human readable name\n")
|
||||
DEFUN_USRATTR(cfg_e1line_port, cfg_e1_line_port_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_LINE_UPD),
|
||||
"e1_line <0-255> port <0-255>",
|
||||
E1_LINE_HELP "Set physical port/span/card number\n"
|
||||
"E1/T1 Port/Span/Card number\n")
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
int e1_nr = atoi(argv[0]);
|
||||
|
||||
line = e1inp_line_find(e1_nr);
|
||||
if (!line) {
|
||||
vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
line->port_nr = atoi(argv[1]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_e1line_socket, cfg_e1_line_socket_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_LINE_UPD),
|
||||
"e1_line <0-255> socket .SOCKET",
|
||||
E1_LINE_HELP "Set socket path for unixsocket\n"
|
||||
"socket path\n")
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
int e1_nr = atoi(argv[0]);
|
||||
struct sockaddr_un sun;
|
||||
|
||||
/* Don't exceed the maximum unix socket path length, including a NUL byte. See the unix(7) man page.*/
|
||||
if (strlen(argv[1]) > sizeof(sun.sun_path) - 1) {
|
||||
vty_out(vty, "%% Socket path length exceeds %zd bytes: '%s'%s",
|
||||
sizeof(sun.sun_path) - 1, argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
line = e1inp_line_find(e1_nr);
|
||||
if (!line) {
|
||||
vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
line->sock_path = talloc_strdup(line, argv[1]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define KEEPALIVE_HELP "Enable keep-alive probing\n"
|
||||
static int set_keepalive_params(struct vty *vty, int e1_nr,
|
||||
int idle, int num_probes, int probe_interval)
|
||||
{
|
||||
struct e1inp_line *line = e1inp_line_find(e1_nr);
|
||||
|
||||
if (!line) {
|
||||
vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (!line->driver->has_keepalive && num_probes != 0) {
|
||||
vty_out(vty, "%% Driver '%s' does not support keep alive%s",
|
||||
line->driver->name, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
line->keepalive_idle_timeout = idle;
|
||||
line->keepalive_num_probes = num_probes;
|
||||
line->keepalive_probe_interval = probe_interval;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_e1line_keepalive, cfg_e1_line_keepalive_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK),
|
||||
"e1_line <0-255> keepalive",
|
||||
E1_LINE_HELP KEEPALIVE_HELP)
|
||||
{
|
||||
return set_keepalive_params(vty, atoi(argv[0]),
|
||||
E1INP_USE_DEFAULT, E1INP_USE_DEFAULT,
|
||||
E1INP_USE_DEFAULT);
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_e1line_keepalive_params, cfg_e1_line_keepalive_params_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK),
|
||||
"e1_line <0-255> keepalive <1-300> <1-20> <1-300>",
|
||||
E1_LINE_HELP KEEPALIVE_HELP
|
||||
"Idle interval in seconds before probes are sent\n"
|
||||
"Number of probes to sent\n"
|
||||
"Delay between probe packets in seconds\n")
|
||||
{
|
||||
return set_keepalive_params(vty, atoi(argv[0]),
|
||||
atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_e1line_no_keepalive, cfg_e1_line_no_keepalive_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK),
|
||||
"no e1_line <0-255> keepalive",
|
||||
NO_STR E1_LINE_HELP KEEPALIVE_HELP)
|
||||
{
|
||||
return set_keepalive_params(vty, atoi(argv[0]), 0, 0, 0);
|
||||
}
|
||||
|
||||
#define IPA_KEEPALIVE_HELP "Enable IPA PING/PONG keep-alive\n"
|
||||
static int set_ipa_keepalive_params(struct vty *vty, int e1_nr, int interval, int wait_for_resp)
|
||||
{
|
||||
struct e1inp_line *line = e1inp_line_find(e1_nr);
|
||||
|
||||
if (!line) {
|
||||
vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (strcmp(line->driver->name, "ipa") != 0) {
|
||||
vty_out(vty, "%% Line %d doesn't use the ipa driver%s", e1_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
TALLOC_FREE(line->ipa_kap);
|
||||
if (interval) {
|
||||
line->ipa_kap = talloc_zero(line, struct ipa_keepalive_params);
|
||||
line->ipa_kap->wait_for_resp = wait_for_resp;
|
||||
line->ipa_kap->interval = interval;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_e1line_ipa_keepalive, cfg_e1_line_ipa_keepalive_cmd,
|
||||
"e1_line <0-255> ipa-keepalive <1-300> <1-300>",
|
||||
E1_LINE_HELP IPA_KEEPALIVE_HELP
|
||||
"Idle interval in seconds before probes are sent\n"
|
||||
"Time to wait for PONG response\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
return set_ipa_keepalive_params(vty, atoi(argv[0]), atoi(argv[1]), atoi(argv[2]));
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_e1line_no_ipa_keepalive, cfg_e1_line_no_ipa_keepalive_cmd,
|
||||
"no e1_line <0-255> ipa-keepalive",
|
||||
NO_STR E1_LINE_HELP IPA_KEEPALIVE_HELP, CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
return set_ipa_keepalive_params(vty, atoi(argv[0]), 0, 0);
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_e1line_name, cfg_e1_line_name_cmd,
|
||||
"e1_line <0-255> name .LINE",
|
||||
E1_LINE_HELP "Set name for this line\n" "Human readable name\n",
|
||||
CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
int e1_nr = atoi(argv[0]);
|
||||
|
@ -89,15 +245,140 @@ DEFUN(cfg_e1line_name, cfg_e1_line_name_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_e1inp, cfg_e1inp_cmd,
|
||||
"e1_input",
|
||||
"Configure E1/T1/J1 TDM input\n")
|
||||
DEFUN_ATTR(cfg_e1line_connect_timeout, cfg_e1_line_connect_timeout_cmd,
|
||||
"e1_line <0-255> connect-timeout <0-60>",
|
||||
E1_LINE_HELP "Set connect timeout\n" "Connect timeout in seconds (0 to disable)\n",
|
||||
CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
int e1_nr = atoi(argv[0]);
|
||||
unsigned int timeout = atoi(argv[1]);
|
||||
|
||||
line = e1inp_line_find(e1_nr);
|
||||
if (!line) {
|
||||
vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
line->connect_timeout = timeout;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN_ATTR(cfg_e1line_pcap, cfg_e1line_pcap_cmd,
|
||||
"e1_line <0-255> pcap .FILE",
|
||||
E1_LINE_HELP "Setup a pcap recording of E1 traffic for line\n"
|
||||
"Filename to save the packets to\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
int fd;
|
||||
int rc;
|
||||
int e1_nr = atoi(argv[0]);
|
||||
|
||||
line = e1inp_line_find(e1_nr);
|
||||
if (!line) {
|
||||
vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0660);
|
||||
if (fd < 0) {
|
||||
vty_out(vty, "Failed to setup E1 pcap recording to %s%s", argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
rc = e1_set_pcap_fd2(line, fd);
|
||||
if (rc < 0) {
|
||||
vty_out(vty, "Failed to write to E1 pcap file %s%s", argv[1], VTY_NEWLINE);
|
||||
close(fd);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
osmo_talloc_replace_string(line, &line->pcap_file, argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_ATTR(cfg_e1line_no_pcap, cfg_e1line_no_pcap_cmd,
|
||||
"no e1_line <0-255> pcap",
|
||||
NO_STR E1_LINE_HELP "Disable pcap recording of E1 traffic for line\n",
|
||||
CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
int e1_nr = atoi(argv[0]);
|
||||
line = e1inp_line_find(e1_nr);
|
||||
|
||||
e1_set_pcap_fd2(line, -1);
|
||||
if (line->pcap_file) {
|
||||
talloc_free(line->pcap_file);
|
||||
line->pcap_file = NULL;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_DEPRECATED(cfg_e1_pcap_deprec, cfg_e1_pcap_deprec_cmd,
|
||||
"pcap .FILE", "Legacy")
|
||||
{
|
||||
vty_out(vty, "%% 'pcap' is deprecated and has no effect: use e1_line <0-255> pcap%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
ALIAS_DEPRECATED(cfg_e1_pcap_deprec, cfg_e1_pcap_deprec_no_cmd,
|
||||
"no pcap", NO_STR);
|
||||
|
||||
DEFUN_ATTR(cfg_e1inp, cfg_e1inp_cmd,
|
||||
"e1_input",
|
||||
"Configure E1/T1/J1 TDM input\n", CMD_ATTR_IMMEDIATE)
|
||||
{
|
||||
vty->node = L_E1INP_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_ipa_bind,
|
||||
cfg_ipa_bind_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_LINE_UPD),
|
||||
"ipa bind A.B.C.D",
|
||||
"ipa driver config\n"
|
||||
"Set ipa local bind address\n"
|
||||
"Listen on this IP address (default 0.0.0.0)\n")
|
||||
{
|
||||
e1inp_ipa_set_bind_addr(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_ipa_dscp, cfg_ipa_dscp_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK),
|
||||
"ipa ip-dscp (oml|rsl) <0-63>",
|
||||
"ipa driver config\n"
|
||||
"Set IP DSCP value for outbound packets\n"
|
||||
"Set IP DSCP for OML link\n"
|
||||
"Set IP DSCP for RSL link\n"
|
||||
"IP DSCP Value to use\n")
|
||||
{
|
||||
if (!strcmp(argv[0], "oml"))
|
||||
g_e1inp_ipaccess_pars.oml.dscp = atoi(argv[1]);
|
||||
else
|
||||
g_e1inp_ipaccess_pars.rsl.dscp = atoi(argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_USRATTR(cfg_ipa_priority, cfg_ipa_priority_cmd,
|
||||
X(OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK),
|
||||
"ipa socket-priority (oml|rsl) <0-255>",
|
||||
"ipa driver config\n"
|
||||
"Set socket priority value for outbound packets\n"
|
||||
"Set socket priority for OML link\n"
|
||||
"Set socket priority for RSL link\n"
|
||||
"socket priority value to use (>6 requires CAP_NET_ADMIN)\n")
|
||||
{
|
||||
if (!strcmp(argv[0], "oml"))
|
||||
g_e1inp_ipaccess_pars.oml.priority = atoi(argv[1]);
|
||||
else
|
||||
g_e1inp_ipaccess_pars.rsl.priority = atoi(argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int e1inp_config_write(struct vty *vty)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
|
@ -110,10 +391,49 @@ static int e1inp_config_write(struct vty *vty)
|
|||
llist_for_each_entry(line, &e1inp_line_list, list) {
|
||||
vty_out(vty, " e1_line %u driver %s%s", line->num,
|
||||
line->driver->name, VTY_NEWLINE);
|
||||
vty_out(vty, " e1_line %u port %u%s", line->num,
|
||||
line->port_nr, VTY_NEWLINE);
|
||||
if (line->name)
|
||||
vty_out(vty, " e1_line %u name %s%s", line->num,
|
||||
line->name, VTY_NEWLINE);
|
||||
if (line->connect_timeout != 0)
|
||||
vty_out(vty, " e1_line %u connect-timeout %u%s", line->num, line->connect_timeout,
|
||||
VTY_NEWLINE);
|
||||
if (!line->keepalive_num_probes)
|
||||
vty_out(vty, " no e1_line %u keepalive%s", line->num,
|
||||
VTY_NEWLINE);
|
||||
else if (line->keepalive_idle_timeout != E1INP_USE_DEFAULT ||
|
||||
line->keepalive_num_probes != E1INP_USE_DEFAULT ||
|
||||
line->keepalive_probe_interval != E1INP_USE_DEFAULT)
|
||||
vty_out(vty, " e1_line %u keepalive %d %d %d%s",
|
||||
line->num,
|
||||
line->keepalive_idle_timeout,
|
||||
line->keepalive_num_probes,
|
||||
line->keepalive_probe_interval,
|
||||
VTY_NEWLINE);
|
||||
if (line->ipa_kap)
|
||||
vty_out(vty, " e1_line %u ipa-keepalive %d %d%s", line->num,
|
||||
line->ipa_kap->interval, line->ipa_kap->wait_for_resp,
|
||||
VTY_NEWLINE);
|
||||
if (line->pcap_file)
|
||||
vty_out(vty, " e1_line %u pcap %s%s", line->num,
|
||||
line->pcap_file, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
const char *ipa_bind = e1inp_ipa_get_bind_addr();
|
||||
if (ipa_bind && (strcmp(ipa_bind, "0.0.0.0") != 0))
|
||||
vty_out(vty, " ipa bind %s%s",
|
||||
ipa_bind, VTY_NEWLINE);
|
||||
|
||||
if (g_e1inp_ipaccess_pars.oml.dscp)
|
||||
vty_out(vty, " ipa ip-dscp oml %u%s", g_e1inp_ipaccess_pars.oml.dscp, VTY_NEWLINE);
|
||||
if (g_e1inp_ipaccess_pars.rsl.dscp)
|
||||
vty_out(vty, " ipa ip-dscp rsl %u%s", g_e1inp_ipaccess_pars.rsl.dscp, VTY_NEWLINE);
|
||||
if (g_e1inp_ipaccess_pars.oml.priority)
|
||||
vty_out(vty, " ipa socket-priority oml %u%s", g_e1inp_ipaccess_pars.oml.priority, VTY_NEWLINE);
|
||||
if (g_e1inp_ipaccess_pars.rsl.priority)
|
||||
vty_out(vty, " ipa socket-priority rsl %u%s", g_e1inp_ipaccess_pars.rsl.priority, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -137,21 +457,72 @@ DEFUN(show_e1drv,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void e1line_ka_dump_vty(struct vty *vty, const struct e1inp_line *line)
|
||||
{
|
||||
if (line->keepalive_num_probes == 0) {
|
||||
vty_out(vty, "Keepalive: disabled%s", VTY_NEWLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
vty_out(vty, "Keepalive: enabled%s", VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " Number of probes: ");
|
||||
if (line->keepalive_num_probes != E1INP_USE_DEFAULT)
|
||||
vty_out(vty, "%d", line->keepalive_num_probes);
|
||||
else
|
||||
vty_out(vty, "(driver's default)");
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " Idle timeout: ");
|
||||
if (line->keepalive_idle_timeout != E1INP_USE_DEFAULT)
|
||||
vty_out(vty, "%ds", line->keepalive_idle_timeout);
|
||||
else
|
||||
vty_out(vty, "(driver's default)");
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " Probe interval: ");
|
||||
if (line->keepalive_probe_interval != E1INP_USE_DEFAULT)
|
||||
vty_out(vty, "%ds", line->keepalive_probe_interval);
|
||||
else
|
||||
vty_out(vty, "(driver's default)");
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void e1line_ipa_ka_dump_vty(struct vty *vty, const struct e1inp_line *line)
|
||||
{
|
||||
if (line->ipa_kap == NULL) {
|
||||
vty_out(vty, "IPA Keepalive: disabled%s", VTY_NEWLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
vty_out(vty, "IPA Keepalive: enabled%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Interval: %us%s", line->ipa_kap->interval, VTY_NEWLINE);
|
||||
vty_out(vty, " Timeout: %us%s", line->ipa_kap->wait_for_resp, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line,
|
||||
int stats)
|
||||
{
|
||||
vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s",
|
||||
line->num, line->name ? line->name : "",
|
||||
line->driver->name, VTY_NEWLINE);
|
||||
if (line->driver->has_keepalive)
|
||||
e1line_ka_dump_vty(vty, line);
|
||||
if (!strcmp(line->driver->name, "ipa"))
|
||||
e1line_ipa_ka_dump_vty(vty, line);
|
||||
if (line->pcap_file)
|
||||
vty_out(vty, "PCAP %s%s", line->pcap_file, VTY_NEWLINE);
|
||||
if (line->driver->vty_show)
|
||||
line->driver->vty_show(vty, line);
|
||||
if (stats)
|
||||
vty_out_rate_ctr_group(vty, " ", line->rate_ctr);
|
||||
}
|
||||
|
||||
DEFUN(show_e1line,
|
||||
show_e1line_cmd,
|
||||
"show e1_line [line_nr] [stats]",
|
||||
"show e1_line [<0-255>] [stats]",
|
||||
SHOW_STR "Display information about a E1 line\n"
|
||||
"E1 Line Number\n")
|
||||
"E1 Line Number\n" "Include statistics\n")
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
int stats = 0;
|
||||
|
@ -183,13 +554,13 @@ static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts)
|
|||
if (ts->type == E1INP_TS_TYPE_NONE)
|
||||
return;
|
||||
vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s",
|
||||
ts->num, ts->line->num, e1inp_tstype_name(ts->type),
|
||||
ts->num-1, ts->line->num, e1inp_tstype_name(ts->type),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(show_e1ts,
|
||||
show_e1ts_cmd,
|
||||
"show e1_timeslot [line_nr] [ts_nr]",
|
||||
"show e1_timeslot [<0-255>] [<0-31>]",
|
||||
SHOW_STR "Display information about a E1 timeslot\n"
|
||||
"E1 Line Number\n" "E1 Timeslot Number\n")
|
||||
{
|
||||
|
@ -197,9 +568,9 @@ DEFUN(show_e1ts,
|
|||
struct e1inp_ts *ts;
|
||||
int ts_nr;
|
||||
|
||||
if (argc == 0) {
|
||||
if (argc <= 0) {
|
||||
llist_for_each_entry(line, &e1inp_line_list, list) {
|
||||
for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
|
||||
for (ts_nr = 0; ts_nr < line->num_ts; ts_nr++) {
|
||||
ts = &line->ts[ts_nr];
|
||||
e1ts_dump_vty(vty, ts);
|
||||
}
|
||||
|
@ -223,7 +594,7 @@ DEFUN(show_e1ts,
|
|||
}
|
||||
if (argc >= 2) {
|
||||
ts_nr = atoi(argv[1]);
|
||||
if (ts_nr >= NUM_E1_TS) {
|
||||
if (ts_nr >= line->num_ts) {
|
||||
vty_out(vty, "E1 timeslot %s is invalid%s",
|
||||
argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
|
@ -232,7 +603,7 @@ DEFUN(show_e1ts,
|
|||
e1ts_dump_vty(vty, ts);
|
||||
return CMD_SUCCESS;
|
||||
} else {
|
||||
for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
|
||||
for (ts_nr = 0; ts_nr < line->num_ts; ts_nr++) {
|
||||
ts = &line->ts[ts_nr];
|
||||
e1ts_dump_vty(vty, ts);
|
||||
}
|
||||
|
@ -244,20 +615,38 @@ DEFUN(show_e1ts,
|
|||
|
||||
struct cmd_node e1inp_node = {
|
||||
L_E1INP_NODE,
|
||||
"%s(e1_input)#",
|
||||
"%s(config-e1_input)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
int e1inp_vty_init(void)
|
||||
{
|
||||
install_element(CONFIG_NODE, &cfg_e1inp_cmd);
|
||||
install_lib_element(CONFIG_NODE, &cfg_e1inp_cmd);
|
||||
install_node(&e1inp_node, e1inp_config_write);
|
||||
install_element(L_E1INP_NODE, &cfg_e1_line_driver_cmd);
|
||||
install_element(L_E1INP_NODE, &cfg_e1_line_name_cmd);
|
||||
|
||||
install_element_ve(&show_e1drv_cmd);
|
||||
install_element_ve(&show_e1line_cmd);
|
||||
install_element_ve(&show_e1ts_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1line_pcap_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1line_no_pcap_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_pcap_deprec_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_pcap_deprec_no_cmd);
|
||||
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_driver_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_port_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_socket_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_name_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_connect_timeout_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_keepalive_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_keepalive_params_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_no_keepalive_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_ipa_keepalive_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_e1_line_no_ipa_keepalive_cmd);
|
||||
|
||||
install_lib_element(L_E1INP_NODE, &cfg_ipa_bind_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_ipa_dscp_cmd);
|
||||
install_lib_element(L_E1INP_NODE, &cfg_ipa_priority_cmd);
|
||||
|
||||
install_lib_element_ve(&show_e1drv_cmd);
|
||||
install_lib_element_ve(&show_e1line_cmd);
|
||||
install_lib_element_ve(&show_e1ts_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
noinst_LTLIBRARIES = libosmoabis-input.la
|
||||
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS= -fPIC -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(COVERAGE_LDFLAGS)
|
||||
|
||||
libosmoabis_input_la_SOURCES = dahdi.c \
|
||||
hsl.c \
|
||||
ipa.c \
|
||||
ipaccess.c \
|
||||
lapd.c \
|
||||
misdn.c \
|
||||
rs232.c
|
|
@ -1,11 +1,13 @@
|
|||
/* OpenBSC Abis input driver for DAHDI */
|
||||
|
||||
/* (C) 2008-2011 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2008-2019 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by Digium and Matthew Fredrickson <creslin@digium.com>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
|
@ -16,12 +18,11 @@
|
|||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -34,19 +35,80 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <dahdi/user.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
||||
#include <osmocom/abis/subchan_demux.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <osmocom/abis/lapd.h>
|
||||
|
||||
#define TS1_ALLOC_SIZE 300
|
||||
|
||||
struct span_cfg {
|
||||
struct llist_head list;
|
||||
|
||||
unsigned int span_nr;
|
||||
unsigned int chan_base;
|
||||
unsigned int chan_num;
|
||||
};
|
||||
|
||||
static struct span_cfg *span_cfgs[DAHDI_MAX_SPANS];
|
||||
|
||||
static int reread_span_cfgs(void)
|
||||
{
|
||||
struct dahdi_spaninfo si;
|
||||
unsigned int basechan = 1;
|
||||
int span_nr;
|
||||
int fd;
|
||||
|
||||
if ((fd = open("/dev/dahdi/ctl", O_RDWR)) < 0) {
|
||||
LOGP(DLMI, LOGL_ERROR, "Unable to open DAHDI ctl: %s\n",
|
||||
strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (span_nr = 1; span_nr < DAHDI_MAX_SPANS; span_nr++) {
|
||||
struct span_cfg *scfg;
|
||||
/* our array index starts at 0, but DAHDI span at 1 */
|
||||
int i = span_nr - 1;
|
||||
|
||||
/* clear any old cached information */
|
||||
if (span_cfgs[i]) {
|
||||
talloc_free(span_cfgs[i]);
|
||||
span_cfgs[i] = NULL;
|
||||
}
|
||||
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.spanno = span_nr;
|
||||
if (ioctl(fd, DAHDI_SPANSTAT, &si))
|
||||
continue;
|
||||
|
||||
/* create and link new span_cfg */
|
||||
scfg = talloc_zero(NULL, struct span_cfg);
|
||||
if (!scfg) {
|
||||
close(fd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
scfg->span_nr = span_nr;
|
||||
scfg->chan_num = si.totalchans;
|
||||
scfg->chan_base = basechan;
|
||||
span_cfgs[i] = scfg;
|
||||
|
||||
basechan += si.totalchans;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Corresponds to dahdi/user.h, only PRI related events */
|
||||
static const struct value_string dahdi_evt_names[] = {
|
||||
{ DAHDI_EVENT_NONE, "NONE" },
|
||||
|
@ -69,33 +131,34 @@ static void handle_dahdi_exception(struct e1inp_ts *ts)
|
|||
if (rc < 0)
|
||||
return;
|
||||
|
||||
LOGP(DLMI, LOGL_NOTICE, "Line %u(%s) / TS %u DAHDI EVENT %s\n",
|
||||
LOGPITS(ts, DLMI, LOGL_NOTICE, "Line %u(%s) / TS %u DAHDI EVENT %s\n",
|
||||
ts->line->num, ts->line->name, ts->num,
|
||||
get_value_string(dahdi_evt_names, evt));
|
||||
|
||||
isd.line = ts->line;
|
||||
isd.ts_nr = ts->num;
|
||||
|
||||
switch (evt) {
|
||||
case DAHDI_EVENT_ALARM:
|
||||
/* we should notify the code that the line is gone */
|
||||
osmo_signal_dispatch(SS_L_INPUT, S_L_INP_LINE_ALARM, &isd);
|
||||
rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_ALARM]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(line->rate_ctr, E1I_CTR_ALARM));
|
||||
break;
|
||||
case DAHDI_EVENT_NOALARM:
|
||||
/* alarm has gone, we should re-start the SABM requests */
|
||||
osmo_signal_dispatch(SS_L_INPUT, S_L_INP_LINE_NOALARM, &isd);
|
||||
break;
|
||||
case DAHDI_EVENT_ABORT:
|
||||
rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_ABORT]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(line->rate_ctr, E1I_CTR_HDLC_ABORT));
|
||||
break;
|
||||
case DAHDI_EVENT_OVERRUN:
|
||||
rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_OVERR]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(line->rate_ctr, E1I_CTR_HDLC_OVERR));
|
||||
break;
|
||||
case DAHDI_EVENT_BADFCS:
|
||||
rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_BADFCS]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(line->rate_ctr, E1I_CTR_HDLC_BADFCS));
|
||||
break;
|
||||
case DAHDI_EVENT_REMOVED:
|
||||
rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_REMOVED]);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(line->rate_ctr, E1I_CTR_REMOVED));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -106,10 +169,7 @@ static int handle_ts1_read(struct osmo_fd *bfd)
|
|||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI TS1");
|
||||
lapd_mph_type prim;
|
||||
unsigned int sapi, tei;
|
||||
int ilen, ret, error = 0;
|
||||
uint8_t *idata;
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
@ -118,65 +178,19 @@ static int handle_ts1_read(struct osmo_fd *bfd)
|
|||
if (ret == -1)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
else if (ret < 0) {
|
||||
perror("read ");
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "%s read failed %d (%s)\n", __func__, ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
if (ret <= 3) {
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "%s read failed %d (%s)\n", __func__, ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
msgb_put(msg, ret - 2);
|
||||
if (ret <= 3) {
|
||||
perror("read ");
|
||||
}
|
||||
msg->l2h = msg->data;
|
||||
|
||||
sapi = msg->data[0] >> 2;
|
||||
tei = msg->data[1] >> 1;
|
||||
|
||||
DEBUGP(DLMI, "<= len = %d, sapi(%d) tei(%d)", ret, sapi, tei);
|
||||
|
||||
idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim, &error);
|
||||
if (!idata) {
|
||||
switch(error) {
|
||||
case LAPD_ERR_UNKNOWN_TEI:
|
||||
/* We don't know about this TEI, probably the BSC
|
||||
* lost local states (it crashed or it was stopped),
|
||||
* notify the driver to see if it can do anything to
|
||||
* recover the existing signalling links with the BTS.
|
||||
*/
|
||||
e1inp_event(e1i_ts, S_L_INP_TEI_UNKNOWN, tei, sapi);
|
||||
return -EIO;
|
||||
}
|
||||
if (prim == 0)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
msgb_pull(msg, 2);
|
||||
|
||||
DEBUGP(DLMI, "prim %08x\n", prim);
|
||||
|
||||
switch (prim) {
|
||||
case 0:
|
||||
break;
|
||||
case LAPD_MPH_ACTIVATE_IND:
|
||||
DEBUGP(DLMI, "MPH_ACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei);
|
||||
ret = e1inp_event(e1i_ts, S_L_INP_TEI_UP, tei, sapi);
|
||||
break;
|
||||
case LAPD_MPH_DEACTIVATE_IND:
|
||||
DEBUGP(DLMI, "MPH_DEACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei);
|
||||
ret = e1inp_event(e1i_ts, S_L_INP_TEI_DN, tei, sapi);
|
||||
break;
|
||||
case LAPD_DL_DATA_IND:
|
||||
case LAPD_DL_UNITDATA_IND:
|
||||
if (prim == LAPD_DL_DATA_IND)
|
||||
msg->l2h = msg->data + 2;
|
||||
else
|
||||
msg->l2h = msg->data + 1;
|
||||
DEBUGP(DLMI, "RX: %s\n", osmo_hexdump(msgb_l2(msg), ret));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, tei, sapi);
|
||||
break;
|
||||
default:
|
||||
printf("ERROR: unknown prim\n");
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUGP(DLMI, "Returned ok\n");
|
||||
return ret;
|
||||
return e1inp_rx_ts_lapd(e1i_ts, msg);
|
||||
}
|
||||
|
||||
static int ts_want_write(struct e1inp_ts *e1i_ts)
|
||||
|
@ -184,12 +198,13 @@ static int ts_want_write(struct e1inp_ts *e1i_ts)
|
|||
/* We never include the DAHDI B-Channel FD into the
|
||||
* writeset, since it doesn't support poll() based
|
||||
* write flow control */
|
||||
if (e1i_ts->type == E1INP_TS_TYPE_TRAU) {
|
||||
fprintf(stderr, "Trying to write TRAU ts\n");
|
||||
if (e1i_ts->type == E1INP_TS_TYPE_TRAU ||
|
||||
e1i_ts->type == E1INP_TS_TYPE_I460) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "Trying to write TRAU ts\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
e1i_ts->driver.dahdi.fd.when |= BSC_FD_WRITE;
|
||||
osmo_fd_write_enable(&e1i_ts->driver.dahdi.fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -202,7 +217,7 @@ static void timeout_ts1_write(void *data)
|
|||
ts_want_write(e1i_ts);
|
||||
}
|
||||
|
||||
static void dahdi_write_msg(uint8_t *data, int len, void *cbdata)
|
||||
static void dahdi_write_msg(struct msgb *msg, void *cbdata)
|
||||
{
|
||||
struct osmo_fd *bfd = cbdata;
|
||||
struct e1inp_line *line = bfd->data;
|
||||
|
@ -210,11 +225,21 @@ static void dahdi_write_msg(uint8_t *data, int len, void *cbdata)
|
|||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
int ret;
|
||||
|
||||
ret = write(bfd->fd, data, len + 2);
|
||||
if (msgb_tailroom(msg) >= 2) {
|
||||
/* two bytes of space for the FCS added by DAHDI in the kernel */
|
||||
msgb_put(msg, 2);
|
||||
ret = write(bfd->fd, msg->data, msg->len);
|
||||
} else {
|
||||
/* work-around for code that sends us messages with no tailroom (OS#4644) */
|
||||
uint8_t buf[msg->len + 2];
|
||||
memcpy(buf, msg->data, msg->len);
|
||||
ret = write(bfd->fd, buf, sizeof(buf));
|
||||
}
|
||||
msgb_free(msg);
|
||||
if (ret == -1)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
else if (ret < 0)
|
||||
LOGP(DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static int handle_ts1_write(struct osmo_fd *bfd)
|
||||
|
@ -225,7 +250,7 @@ static int handle_ts1_write(struct osmo_fd *bfd)
|
|||
struct e1inp_sign_link *sign_link;
|
||||
struct msgb *msg;
|
||||
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
osmo_fd_write_disable(bfd);
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, &sign_link);
|
||||
|
@ -234,50 +259,81 @@ static int handle_ts1_write(struct osmo_fd *bfd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP(DLMI, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
lapd_transmit(e1i_ts->driver.dahdi.lapd, sign_link->tei,
|
||||
sign_link->sapi, msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
lapd_transmit(e1i_ts->lapd, sign_link->tei,
|
||||
sign_link->sapi, msg);
|
||||
|
||||
/* set tx delay timer for next event */
|
||||
e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
|
||||
e1i_ts->sign.tx_timer.data = e1i_ts;
|
||||
osmo_timer_setup(&e1i_ts->sign.tx_timer, timeout_ts1_write, e1i_ts);
|
||||
osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, 50000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_hdlc_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, NULL);
|
||||
if (!msg) {
|
||||
osmo_fd_write_disable(bfd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msgb_tailroom(msg) >= 2) {
|
||||
/* two bytes of space for the FCS added by DAHDI in the kernel */
|
||||
msgb_put(msg, 2);
|
||||
ret = write(bfd->fd, msg->data, msg->len);
|
||||
} else {
|
||||
/* work-around for code that sends us messages with no tailroom (OS#4644) */
|
||||
uint8_t buf[msg->len + 2];
|
||||
memcpy(buf, msg->data, msg->len);
|
||||
ret = write(bfd->fd, buf, sizeof(buf));
|
||||
}
|
||||
msgb_free(msg);
|
||||
if (ret == -1)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
else if (ret < 0)
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static int handle_hdlc_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI HDLC Rx");
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read(bfd->fd, msg->data, TS1_ALLOC_SIZE - 16);
|
||||
if (ret == -1)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
else if (ret < 0) {
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "%s read failed %d (%s)\n", __func__, ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
if (ret <= 3) {
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "%s read failed %d (%s)\n", __func__, ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
msgb_put(msg, ret - 2);
|
||||
msg->l2h = msg->data;
|
||||
|
||||
return e1inp_rx_ts(e1i_ts, msg, 0, 0);
|
||||
}
|
||||
|
||||
static int invertbits = 1;
|
||||
|
||||
static uint8_t flip_table[256];
|
||||
|
||||
static void init_flip_bits(void)
|
||||
{
|
||||
int i,k;
|
||||
|
||||
for (i = 0 ; i < 256 ; i++) {
|
||||
uint8_t sample = 0 ;
|
||||
for (k = 0; k<8; k++) {
|
||||
if ( i & 1 << k ) sample |= 0x80 >> k;
|
||||
}
|
||||
flip_table[i] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t * flip_buf_bits ( uint8_t * buf , int len)
|
||||
{
|
||||
int i;
|
||||
uint8_t * start = buf;
|
||||
|
||||
for (i = 0 ; i < len; i++) {
|
||||
buf[i] = flip_table[(uint8_t)buf[i]];
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
#define D_BCHAN_TX_GRAN 160
|
||||
/* write to a B channel TS */
|
||||
static int handle_tsX_write(struct osmo_fd *bfd)
|
||||
{
|
||||
|
@ -291,22 +347,20 @@ static int handle_tsX_write(struct osmo_fd *bfd)
|
|||
ret = subchan_mux_out(mx, tx_buf, D_BCHAN_TX_GRAN);
|
||||
|
||||
if (ret != D_BCHAN_TX_GRAN) {
|
||||
fprintf(stderr, "Huh, got ret of %d\n", ret);
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "Huh, got ret of %d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEBUGP(DLMIB, "BCHAN TX: %s\n",
|
||||
osmo_hexdump(tx_buf, D_BCHAN_TX_GRAN));
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "BCHAN TX: %s\n", osmo_hexdump(tx_buf, D_BCHAN_TX_GRAN));
|
||||
|
||||
if (invertbits) {
|
||||
flip_buf_bits(tx_buf, ret);
|
||||
}
|
||||
if (invertbits)
|
||||
osmo_revbytebits_buf(tx_buf, ret);
|
||||
|
||||
ret = write(bfd->fd, tx_buf, ret);
|
||||
if (ret < D_BCHAN_TX_GRAN)
|
||||
fprintf(stderr, "send returns %d instead of %d\n", ret,
|
||||
D_BCHAN_TX_GRAN);
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "send returns %d instead of %d\n",
|
||||
ret, D_BCHAN_TX_GRAN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -326,28 +380,96 @@ static int handle_tsX_read(struct osmo_fd *bfd)
|
|||
|
||||
ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE);
|
||||
if (ret < 0 || ret != D_TSX_ALLOC_SIZE) {
|
||||
fprintf(stderr, "read error %d %s\n", ret, strerror(errno));
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "read error %d %s\n", ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (invertbits) {
|
||||
flip_buf_bits(msg->data, ret);
|
||||
}
|
||||
if (invertbits)
|
||||
osmo_revbytebits_buf(msg->data, ret);
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
msg->l2h = msg->data;
|
||||
DEBUGP(DLMIB, "BCHAN RX: %s\n",
|
||||
osmo_hexdump(msgb_l2(msg), ret));
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "BCHAN RX: %s\n", osmo_hexdump(msgb_l2(msg), ret));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
|
||||
/* physical layer indicates that data has been sent,
|
||||
* we thus can send some more data */
|
||||
ret = handle_tsX_write(bfd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write to a raw channel TS */
|
||||
static int handle_ts_raw_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, NULL);
|
||||
if (!msg) {
|
||||
osmo_fd_write_disable(bfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg->len != D_BCHAN_TX_GRAN) {
|
||||
/* This might lead to a transmit underrun, as we call tx
|
||||
* from the rx path, as there's no select/poll on dahdi
|
||||
* */
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_NOTICE, "unexpected msg->len = %u, "
|
||||
"expected %u\n", msg->len, D_BCHAN_TX_GRAN);
|
||||
}
|
||||
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "RAW CHAN TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
if (0/*invertbits*/)
|
||||
osmo_revbytebits_buf(msg->data, msg->len);
|
||||
|
||||
ret = write(bfd->fd, msg->data, msg->len);
|
||||
if (ret < msg->len)
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "send returns %d instead of %d\n", ret, msg->len);
|
||||
msgb_free(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_ts_raw_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "DAHDI Raw TS");
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE);
|
||||
if (ret < 0 || ret != D_TSX_ALLOC_SIZE) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "read error %d %s\n", ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (0/*invertbits*/)
|
||||
osmo_revbytebits_buf(msg->data, ret);
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
msg->l2h = msg->data;
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "RAW CHAN RX: %s\n", osmo_hexdump(msgb_l2(msg), ret));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
|
||||
/* physical layer indicates that data has been sent,
|
||||
* we thus can send some more data */
|
||||
ret = handle_ts_raw_write(bfd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* callback from select.c in case one of the fd's can be read/written */
|
||||
static int dahdi_fd_cb(struct osmo_fd *bfd, unsigned int what)
|
||||
{
|
||||
|
@ -359,48 +481,88 @@ static int dahdi_fd_cb(struct osmo_fd *bfd, unsigned int what)
|
|||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
if (what & BSC_FD_EXCEPT)
|
||||
if (what & OSMO_FD_EXCEPT)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
if (what & BSC_FD_READ)
|
||||
if (what & OSMO_FD_READ)
|
||||
rc = handle_ts1_read(bfd);
|
||||
if (what & BSC_FD_WRITE)
|
||||
if (what & OSMO_FD_WRITE)
|
||||
rc = handle_ts1_write(bfd);
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
if (what & BSC_FD_EXCEPT)
|
||||
case E1INP_TS_TYPE_HDLC:
|
||||
if (what & OSMO_FD_EXCEPT)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
if (what & BSC_FD_READ)
|
||||
if (what & OSMO_FD_READ)
|
||||
rc = handle_hdlc_read(bfd);
|
||||
if (what & OSMO_FD_WRITE)
|
||||
handle_hdlc_write(bfd);
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
if (what & OSMO_FD_EXCEPT)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
if (what & OSMO_FD_READ)
|
||||
rc = handle_tsX_read(bfd);
|
||||
if (what & BSC_FD_WRITE)
|
||||
if (what & OSMO_FD_WRITE)
|
||||
rc = handle_tsX_write(bfd);
|
||||
/* We never include the DAHDI B-Channel FD into the
|
||||
* writeset, since it doesn't support poll() based
|
||||
* write flow control */
|
||||
break;
|
||||
case E1INP_TS_TYPE_I460:
|
||||
case E1INP_TS_TYPE_RAW:
|
||||
if (what & OSMO_FD_EXCEPT)
|
||||
handle_dahdi_exception(e1i_ts);
|
||||
if (what & OSMO_FD_READ)
|
||||
rc = handle_ts_raw_read(bfd);
|
||||
if (what & OSMO_FD_WRITE)
|
||||
rc = handle_ts_raw_write(bfd);
|
||||
/* We never include the DAHDI B-Channel FD into the
|
||||
* writeset, since it doesn't support poll() based
|
||||
* write flow control */
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_NOTICE, "unknown E1 TS type %u\n", e1i_ts->type);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dahdi_vty_show(struct vty *vty, struct e1inp_line *line)
|
||||
{
|
||||
struct span_cfg *scfg;
|
||||
|
||||
if (line->port_nr >= ARRAY_SIZE(span_cfgs))
|
||||
return;
|
||||
|
||||
scfg = span_cfgs[line->port_nr];
|
||||
if (!scfg) {
|
||||
vty_out(vty, "DAHDI Span %u non-existent%s",
|
||||
line->port_nr+1, VTY_NEWLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
vty_out(vty, "DAHDI Span #%u, Base Nr %u, Timeslots: %u%s",
|
||||
line->port_nr+1, scfg->chan_base, scfg->chan_num,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static int dahdi_e1_line_update(struct e1inp_line *line);
|
||||
|
||||
struct e1inp_driver dahdi_driver = {
|
||||
.name = "dahdi",
|
||||
.want_write = ts_want_write,
|
||||
.line_update = &dahdi_e1_line_update,
|
||||
.vty_show = &dahdi_vty_show,
|
||||
};
|
||||
|
||||
void dahdi_set_bufinfo(int fd, int as_sigchan)
|
||||
int dahdi_set_bufinfo(int fd, int as_sigchan)
|
||||
{
|
||||
struct dahdi_bufferinfo bi;
|
||||
int x = 0;
|
||||
|
||||
if (ioctl(fd, DAHDI_GET_BUFINFO, &bi)) {
|
||||
fprintf(stderr, "Error getting bufinfo\n");
|
||||
exit(-1);
|
||||
LOGP(DLINP, LOGL_ERROR, "Error getting bufinfo\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (as_sigchan) {
|
||||
|
@ -413,14 +575,14 @@ void dahdi_set_bufinfo(int fd, int as_sigchan)
|
|||
}
|
||||
|
||||
if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
|
||||
fprintf(stderr, "Error setting bufinfo\n");
|
||||
exit(-1);
|
||||
LOGP(DLINP, LOGL_ERROR, "Error setting bufinfo\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!as_sigchan) {
|
||||
if (ioctl(fd, DAHDI_AUDIOMODE, &x)) {
|
||||
fprintf(stderr, "Error setting bufinfo\n");
|
||||
exit(-1);
|
||||
LOGP(DLINP, LOGL_ERROR, "Error setting bufinfo\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
int one = 1;
|
||||
|
@ -429,70 +591,142 @@ void dahdi_set_bufinfo(int fd, int as_sigchan)
|
|||
* as this command will fail if the slot _already_ was a
|
||||
* signalling slot before :( */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dahdi_open_slot(const struct e1inp_ts *e1i_ts, int dahdi_chan_nr)
|
||||
{
|
||||
int rc, fd;
|
||||
char name[32];
|
||||
#ifndef DAHDI_SPECIFY
|
||||
char openstr[128];
|
||||
snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", dahdi_chan_nr);
|
||||
#else
|
||||
const char *openstr = "/dev/dahdi/channel";
|
||||
#endif
|
||||
e1inp_ts_name(name, sizeof(name), e1i_ts);
|
||||
|
||||
rc = open(openstr, O_RDWR | O_NONBLOCK);
|
||||
if (rc < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "%s: DAHDI: could not open %s %s\n", name, openstr, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
fd = rc;
|
||||
#ifdef DAHDI_SPECIFY
|
||||
rc = ioctl(fd, DAHDI_SPECIFY, &dahdi_chan_nr);
|
||||
if (rc < 0) {
|
||||
close(fd);
|
||||
LOGP(DLINP, LOGL_ERROR, "%s: DAHDI: could not DAHDI_SPECIFY %d: %s\n", name,
|
||||
dahdi_chan_nr, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
LOGP(DLINP, LOGL_DEBUG, "%s: DAHDI: opened dahdi_chan_nr=%d\n", name, dahdi_chan_nr);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int dahdi_e1_setup(struct e1inp_line *line)
|
||||
{
|
||||
struct span_cfg *scfg;
|
||||
int ts, ret;
|
||||
|
||||
reread_span_cfgs();
|
||||
|
||||
scfg = span_cfgs[line->port_nr];
|
||||
if (!scfg) {
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "Line %u(%s): DAHDI Port %u (Span %u) doesn't exist\n",
|
||||
line->num, line->name, line->port_nr, line->port_nr+1);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
line->num_ts = scfg->chan_num;
|
||||
|
||||
/* TS0 is CRC4, don't need any fd for it */
|
||||
for (ts = 1; ts < NUM_E1_TS; ts++) {
|
||||
for (ts = 1; ts <= scfg->chan_num; ts++) {
|
||||
unsigned int idx = ts-1;
|
||||
char openstr[128];
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
struct osmo_fd *bfd = &e1i_ts->driver.dahdi.fd;
|
||||
int dev_nr;
|
||||
|
||||
/* unregister FD if it was already registered */
|
||||
if (bfd->list.next && bfd->list.next != LLIST_POISON1)
|
||||
osmo_fd_unregister(bfd);
|
||||
|
||||
/* DAHDI device names/numbers just keep incrementing
|
||||
* even over multiple boards. So TS1 of the second
|
||||
* board will be 32 */
|
||||
dev_nr = line->num * (NUM_E1_TS-1) + ts;
|
||||
|
||||
bfd->data = line;
|
||||
bfd->priv_nr = ts;
|
||||
bfd->cb = dahdi_fd_cb;
|
||||
snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", dev_nr);
|
||||
dev_nr = scfg->chan_base + idx;
|
||||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_NONE:
|
||||
/* close/release LAPD instance, if any */
|
||||
if (e1i_ts->lapd) {
|
||||
lapd_instance_free(e1i_ts->lapd);
|
||||
e1i_ts->lapd = NULL;
|
||||
}
|
||||
if (bfd->fd) {
|
||||
close(bfd->fd);
|
||||
bfd->fd = 0;
|
||||
}
|
||||
continue;
|
||||
break;
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
bfd->fd = open(openstr, O_RDWR | O_NONBLOCK);
|
||||
if (bfd->fd == -1) {
|
||||
fprintf(stderr, "%s could not open %s %s\n",
|
||||
__func__, openstr, strerror(errno));
|
||||
exit(-1);
|
||||
if (!bfd->fd)
|
||||
bfd->fd = dahdi_open_slot(e1i_ts, dev_nr);
|
||||
if (bfd->fd < 0)
|
||||
return -EIO;
|
||||
bfd->when = OSMO_FD_READ | OSMO_FD_EXCEPT;
|
||||
ret = dahdi_set_bufinfo(bfd->fd, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!e1i_ts->lapd) {
|
||||
char name[32];
|
||||
e1inp_ts_name(name, sizeof(name), e1i_ts);
|
||||
e1i_ts->lapd = lapd_instance_alloc2(1,
|
||||
dahdi_write_msg, bfd, e1inp_dlsap_up,
|
||||
e1i_ts, &lapd_profile_abis, name);
|
||||
}
|
||||
bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;
|
||||
dahdi_set_bufinfo(bfd->fd, 1);
|
||||
e1i_ts->driver.dahdi.lapd = lapd_instance_alloc(1, dahdi_write_msg, bfd);
|
||||
break;
|
||||
case E1INP_TS_TYPE_HDLC:
|
||||
if (!bfd->fd)
|
||||
bfd->fd = dahdi_open_slot(e1i_ts, dev_nr);
|
||||
if (bfd->fd < 0)
|
||||
return -EIO;
|
||||
bfd->when = OSMO_FD_READ | OSMO_FD_EXCEPT;
|
||||
ret = dahdi_set_bufinfo(bfd->fd, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
bfd->fd = open(openstr, O_RDWR | O_NONBLOCK);
|
||||
if (bfd->fd == -1) {
|
||||
fprintf(stderr, "%s could not open %s %s\n",
|
||||
__func__, openstr, strerror(errno));
|
||||
exit(-1);
|
||||
case E1INP_TS_TYPE_I460:
|
||||
case E1INP_TS_TYPE_RAW:
|
||||
/* close/release LAPD instance, if any */
|
||||
if (e1i_ts->lapd) {
|
||||
lapd_instance_free(e1i_ts->lapd);
|
||||
e1i_ts->lapd = NULL;
|
||||
}
|
||||
dahdi_set_bufinfo(bfd->fd, 0);
|
||||
if (!bfd->fd)
|
||||
bfd->fd = dahdi_open_slot(e1i_ts, dev_nr);
|
||||
if (bfd->fd < 0)
|
||||
return -EIO;
|
||||
ret = dahdi_set_bufinfo(bfd->fd, 0);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
/* We never include the DAHDI B-Channel FD into the
|
||||
* writeset, since it doesn't support poll() based
|
||||
* write flow control */
|
||||
bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;// | BSC_FD_WRITE;
|
||||
bfd->when = OSMO_FD_READ | OSMO_FD_EXCEPT;// | OSMO_FD_WRITE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bfd->fd < 0) {
|
||||
fprintf(stderr, "%s could not open %s %s\n",
|
||||
__func__, openstr, strerror(errno));
|
||||
if (bfd->fd < 0)
|
||||
return bfd->fd;
|
||||
}
|
||||
|
||||
osmo_fd_setup(bfd, bfd->fd, bfd->when, dahdi_fd_cb, line, ts);
|
||||
ret = osmo_fd_register(bfd);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not register FD: %s\n",
|
||||
strerror(ret));
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "could not register FD: %s\n", strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -510,8 +744,6 @@ static int dahdi_e1_line_update(struct e1inp_line *line)
|
|||
|
||||
int e1inp_dahdi_init(void)
|
||||
{
|
||||
init_flip_bits();
|
||||
|
||||
/* register the driver with the core */
|
||||
return e1inp_driver_register(&dahdi_driver);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,864 @@
|
|||
/* OpenBSC Abis input driver for osmo-e1d */
|
||||
|
||||
/* (C) 2019 by Sylvain Munaut <tnt@246tNt.com>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "internal.h"
|
||||
|
||||
#ifdef HAVE_E1D
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
||||
#include <osmocom/abis/subchan_demux.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/lapd.h>
|
||||
|
||||
#include <osmocom/e1d/proto.h>
|
||||
#include <osmocom/e1d/proto_clnt.h>
|
||||
|
||||
|
||||
#define TS_SIGN_ALLOC_SIZE 300
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
|
||||
struct osmo_e1dp_client *g_e1d;
|
||||
struct osmo_fsm_inst *g_e1d_fsm_inst = NULL;
|
||||
|
||||
static int invertbits = 1;
|
||||
|
||||
/* pre-declaration */
|
||||
extern struct e1inp_driver e1d_driver;
|
||||
static int e1d_want_write(struct e1inp_ts *e1i_ts);
|
||||
static int e1d_fd_cb(struct osmo_fd *bfd, unsigned int what);
|
||||
static int e1d_line_update(struct e1inp_line *line);
|
||||
|
||||
/* flag array to remember which lines are handled by osmo-e1d */
|
||||
bool lines[256];
|
||||
|
||||
enum fsm_e1d_client_states {
|
||||
ST_DISCONNECTED,
|
||||
ST_CONNECTED,
|
||||
};
|
||||
|
||||
enum fsm_e1d_client_evt {
|
||||
EV_CONN_LOST,
|
||||
EV_CONNECT,
|
||||
};
|
||||
|
||||
static const struct value_string fsm_e1d_client_evt_names[] = {
|
||||
OSMO_VALUE_STRING(EV_CONN_LOST),
|
||||
OSMO_VALUE_STRING(EV_CONNECT),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
struct e1inp_line *e1inp_port_find(uint8_t port_nr)
|
||||
{
|
||||
struct e1inp_line *e1i_line;
|
||||
|
||||
/* iterate over global list of e1 lines */
|
||||
llist_for_each_entry(e1i_line, &e1inp_line_list, list) {
|
||||
if (e1i_line->port_nr == port_nr)
|
||||
return e1i_line;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void e1d_client_event_cb(enum osmo_e1dp_msg_type event, uint8_t intf, uint8_t line, uint8_t ts, uint8_t *data, int len)
|
||||
{
|
||||
struct e1inp_line *e1_line;
|
||||
struct input_signal_data isd;
|
||||
int signal;
|
||||
|
||||
memset(&isd, 0, sizeof(isd));
|
||||
|
||||
/* we use higher 4 bits for interface, lower 4 bits for line,
|
||||
* resulting in max. 16 interfaces with 16 lines each */
|
||||
e1_line = e1inp_port_find((intf << 4) | line);
|
||||
if (!e1_line)
|
||||
return;
|
||||
isd.line = e1_line;
|
||||
|
||||
switch (event) {
|
||||
case E1DP_EVT_LOS_ON:
|
||||
signal = S_L_INP_LINE_LOS;
|
||||
break;
|
||||
case E1DP_EVT_LOS_OFF:
|
||||
signal = S_L_INP_LINE_NOLOS;
|
||||
break;
|
||||
case E1DP_EVT_AIS_ON:
|
||||
signal = S_L_INP_LINE_AIS;
|
||||
break;
|
||||
case E1DP_EVT_AIS_OFF:
|
||||
signal = S_L_INP_LINE_NOAIS;
|
||||
break;
|
||||
case E1DP_EVT_RAI_ON:
|
||||
signal = S_L_INP_LINE_RAI;
|
||||
break;
|
||||
case E1DP_EVT_RAI_OFF:
|
||||
signal = S_L_INP_LINE_NORAI;
|
||||
break;
|
||||
case E1DP_EVT_LOF_ON:
|
||||
signal = S_L_INP_LINE_LOF;
|
||||
break;
|
||||
case E1DP_EVT_LOF_OFF:
|
||||
signal = S_L_INP_LINE_NOLOF;
|
||||
break;
|
||||
case E1DP_EVT_SABITS:
|
||||
signal = S_L_INP_LINE_SA_BITS;
|
||||
if (len < 1)
|
||||
return;
|
||||
isd.sa_bits = *data;
|
||||
break;
|
||||
default:
|
||||
/* Ignore all other events. */
|
||||
return;
|
||||
}
|
||||
|
||||
osmo_signal_dispatch(SS_L_INPUT, signal, &isd);
|
||||
}
|
||||
|
||||
static int fsm_e1_client_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONNECT, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsm_e1d_client_disconnected_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case EV_CONNECT:
|
||||
if (!g_e1d) {
|
||||
g_e1d = osmo_e1dp_client_create(NULL, "/tmp/osmo-e1d.ctl");
|
||||
if (!g_e1d) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Unable to (re)connect to osmo-e1d daemon, retrying...\n");
|
||||
osmo_fsm_inst_state_chg(g_e1d_fsm_inst, ST_DISCONNECTED, 1, 0);
|
||||
return;
|
||||
}
|
||||
osmo_e1dp_client_event_register(g_e1d, e1d_client_event_cb);
|
||||
}
|
||||
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Successfully (re)connected to osmo-e1d daemon!\n");
|
||||
osmo_fsm_inst_state_chg(g_e1d_fsm_inst, ST_CONNECTED, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void terminate_line(struct e1inp_line *line)
|
||||
{
|
||||
int ts;
|
||||
|
||||
/* There should be technically no way to get a non e1d line into our private memory */
|
||||
OSMO_ASSERT(line->driver == &e1d_driver);
|
||||
|
||||
for (ts = 1; ts < line->num_ts; ts++) {
|
||||
unsigned int idx = ts - 1;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
struct osmo_fd *bfd = &e1i_ts->driver.e1d.fd;
|
||||
|
||||
/* Only affect file descriptors that are currently in use (do not reset file descriptor value since we
|
||||
* will use this value to detect if the file descriptor was in use when the connection broke.) */
|
||||
if (bfd->fd >= 0) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Terminating osmo-e1d connection to timeslot: %d\n", ts);
|
||||
osmo_fd_unregister(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fsm_e1d_client_connected_onenter_cb(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
unsigned int i;
|
||||
struct e1inp_line *line;
|
||||
int ret;
|
||||
|
||||
/* Run a line update to re-establish lost connections */
|
||||
for (i = 0; i < ARRAY_SIZE(lines); i++) {
|
||||
if (!lines[i])
|
||||
continue;
|
||||
|
||||
line = e1inp_line_find(i);
|
||||
if (!line) {
|
||||
/* Apparantly we lost a line - this should not happen */
|
||||
lines[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = e1d_line_update(line);
|
||||
if (ret < 0)
|
||||
LOGPFSML(fi, LOGL_ERROR, "Line update failed after (re)connecting to osmo-e1d daemon!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void fsm_e1d_client_connected_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
unsigned int i;
|
||||
struct e1inp_line *line;
|
||||
|
||||
switch (event) {
|
||||
case EV_CONN_LOST:
|
||||
LOGPFSML(fi, LOGL_ERROR, "Lost connection to osmo-e1d daemon!\n");
|
||||
|
||||
/* Destroy e1d clinet */
|
||||
if (g_e1d) {
|
||||
osmo_e1dp_client_destroy(g_e1d);
|
||||
g_e1d = NULL;
|
||||
}
|
||||
|
||||
/* Terminate all lines at once */
|
||||
for (i = 0; i < ARRAY_SIZE(lines); i++) {
|
||||
if (!lines[i])
|
||||
continue;
|
||||
line = e1inp_line_find(i);
|
||||
if (!line) {
|
||||
/* Apparantly we lost a line - this should not happen */
|
||||
lines[i] = false;
|
||||
continue;
|
||||
}
|
||||
terminate_line(line);
|
||||
}
|
||||
|
||||
osmo_fsm_inst_state_chg(fi, ST_DISCONNECTED, 1, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool e1d_connected(void)
|
||||
{
|
||||
if (g_e1d_fsm_inst && g_e1d_fsm_inst->state == ST_CONNECTED)
|
||||
return true;
|
||||
|
||||
LOGPFSML(g_e1d_fsm_inst, LOGL_ERROR, "No connection to osmo-e1d daemon!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct osmo_fsm_state fsm_e1d_client_states[] = {
|
||||
|
||||
/* Initial CRCX state. This state is immediately entered and executed
|
||||
* when the FSM is started. The rationale is that we first have to
|
||||
* create a connectin before we can execute other operations on that
|
||||
* connection. */
|
||||
[ST_DISCONNECTED] = {
|
||||
.in_event_mask = S(EV_CONNECT),
|
||||
.out_state_mask = S(ST_CONNECTED) | S(ST_DISCONNECTED),
|
||||
.name = OSMO_STRINGIFY(ST_DISCONNECTED),
|
||||
.action = fsm_e1d_client_disconnected_cb,
|
||||
},
|
||||
|
||||
/* Wait for the response to a CRCX operation, check and process the
|
||||
* results, change to ST_READY afterwards. */
|
||||
[ST_CONNECTED] = {
|
||||
.in_event_mask = S(EV_CONN_LOST),
|
||||
.out_state_mask = S(ST_DISCONNECTED),
|
||||
.name = OSMO_STRINGIFY(ST_CONNECTED),
|
||||
.action = fsm_e1d_client_connected_cb,
|
||||
.onenter = fsm_e1d_client_connected_onenter_cb,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static struct osmo_fsm fsm_e1d_client = {
|
||||
.name = "e1d_client",
|
||||
.states = fsm_e1d_client_states,
|
||||
.num_states = ARRAY_SIZE(fsm_e1d_client_states),
|
||||
.log_subsys = DLINP,
|
||||
.event_names = fsm_e1d_client_evt_names,
|
||||
.timer_cb = fsm_e1_client_timer_cb,
|
||||
};
|
||||
|
||||
static int handle_ts_sign_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(TS_SIGN_ALLOC_SIZE, "E1D Signaling TS");
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read(bfd->fd, msg->data, TS_SIGN_ALLOC_SIZE - 16);
|
||||
if (ret < 1) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "%s read error: %d %s\n", __func__, ret,
|
||||
ret < 0 ? strerror(errno) : "bytes read");
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
return e1inp_rx_ts_lapd(e1i_ts, msg);
|
||||
}
|
||||
|
||||
static void timeout_ts_sign_write(void *data)
|
||||
{
|
||||
struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
|
||||
|
||||
/* trigger write of ts1, due to tx delay timer */
|
||||
e1d_want_write(e1i_ts);
|
||||
}
|
||||
|
||||
static int handle_ts_sign_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct msgb *msg;
|
||||
|
||||
osmo_fd_write_disable(bfd);
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, &sign_link);
|
||||
if (!msg) {
|
||||
/* no message after tx delay timer */
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP(DLMI, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
lapd_transmit(e1i_ts->lapd, sign_link->tei,
|
||||
sign_link->sapi, msg);
|
||||
|
||||
/* set tx delay timer for next event */
|
||||
osmo_timer_setup(&e1i_ts->sign.tx_timer, timeout_ts_sign_write, e1i_ts);
|
||||
osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, 50000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define D_TSX_ALLOC_SIZE (D_BCHAN_TX_GRAN)
|
||||
|
||||
static int handle_ts_trau_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
uint8_t tx_buf[D_BCHAN_TX_GRAN];
|
||||
struct subch_mux *mx = &e1i_ts->trau.mux;
|
||||
int ret;
|
||||
|
||||
ret = subchan_mux_out(mx, tx_buf, D_BCHAN_TX_GRAN);
|
||||
|
||||
if (ret != D_BCHAN_TX_GRAN) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "Huh, got ret of %d\n", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "BCHAN TX: %s\n", osmo_hexdump(tx_buf, D_BCHAN_TX_GRAN));
|
||||
|
||||
if (invertbits)
|
||||
osmo_revbytebits_buf(tx_buf, ret);
|
||||
|
||||
ret = write(bfd->fd, tx_buf, ret);
|
||||
if (ret < D_BCHAN_TX_GRAN) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "send returns %d instead of %d\n",
|
||||
ret, D_BCHAN_TX_GRAN);
|
||||
if (ret <= 0)
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int handle_ts_trau_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "E1D Rx TSx");
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE);
|
||||
if (ret < 0 || ret != D_TSX_ALLOC_SIZE) {
|
||||
/* FIXME: The socket that we read from is of type SOCK_STREAM. This means that we might read less then
|
||||
* D_TSX_ALLOC_SIZE even though the connection is still fine. Since data is continuously written (in
|
||||
* chunks of D_TSX_ALLOC_SIZE) on the other side we should not get partial reads too often but it is
|
||||
* still possible and when it happens, a reconnect cycle will be triggered. To fix this we should add a
|
||||
* buffering mechainsm that buffers the incomplete read instead of dropping the connection. (changing
|
||||
* the socket type to SOCK_SEQPACKET would be an alternative, but it would break backward compatibility
|
||||
* of the interface.) */
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "%s read error: %d %s\n", __func__, ret,
|
||||
ret < 0 ? strerror(errno) : "bytes read");
|
||||
if (ret <= 0)
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (invertbits)
|
||||
osmo_revbytebits_buf(msg->data, ret);
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
msg->l2h = msg->data;
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "BCHAN RX: %s\n", osmo_hexdump(msgb_l2(msg), ret));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
|
||||
/* physical layer indicates that data has been sent,
|
||||
* we thus can send some more data */
|
||||
ret = handle_ts_trau_write(bfd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write to a raw channel TS */
|
||||
static int handle_ts_raw_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, NULL);
|
||||
if (!msg) {
|
||||
osmo_fd_write_disable(bfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg->len != D_BCHAN_TX_GRAN) {
|
||||
/* This might lead to a transmit underrun, as we call tx
|
||||
* from the rx path, as there's no select/poll on dahdi
|
||||
* */
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_NOTICE, "unexpected msg->len = %u, "
|
||||
"expected %u\n", msg->len, D_BCHAN_TX_GRAN);
|
||||
}
|
||||
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "RAW CHAN TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
if (0/*invertbits*/)
|
||||
osmo_revbytebits_buf(msg->data, msg->len);
|
||||
|
||||
ret = write(bfd->fd, msg->data, msg->len);
|
||||
if (ret < msg->len) {
|
||||
/* FIXME: The socket that we write to is of type SOCK_STREAM. This means that it may happen that the
|
||||
* syscall is not able to write the full data chunk at once. This is a rare event, but when it happens,
|
||||
* a reconnect cycle is triggered, even though the connection is still fine. To fix this, we should
|
||||
* buffer the remainder of the data to write it in the next cycle instead of dropping the connection.
|
||||
* (changing the socket type to SOCK_SEQPACKET would be an alternative, but it would break backward
|
||||
* compatibility of the interface.) */
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "send returns %d instead of %d\n", ret, msg->len);
|
||||
if (ret <= 0)
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
}
|
||||
msgb_free(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_ts_raw_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "E1D Raw TS");
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE);
|
||||
if (ret < 0 || ret != D_TSX_ALLOC_SIZE) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "%s read error: %d %s\n", __func__, ret,
|
||||
ret < 0 ? strerror(errno) : "bytes read");
|
||||
msgb_free(msg);
|
||||
if (ret <= 0)
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (0/*invertbits*/)
|
||||
osmo_revbytebits_buf(msg->data, ret);
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
msg->l2h = msg->data;
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "RAW CHAN RX: %s\n", msgb_hexdump_l2(msg));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write to a hdlc channel TS */
|
||||
static int handle_ts_hdlc_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, NULL);
|
||||
if (!msg) {
|
||||
osmo_fd_write_disable(bfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "HDLC CHAN TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
ret = write(bfd->fd, msg->data, msg->len);
|
||||
if (ret < msg->len) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_NOTICE, "send returns %d instead of %d\n", ret, msg->len);
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
}
|
||||
msgb_free(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TSX_ALLOC_SIZE 4096
|
||||
|
||||
/* read from a hdlc channel TS */
|
||||
static int handle_ts_hdlc_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE, "E1D HDLC TS");
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read(bfd->fd, msg->data, TSX_ALLOC_SIZE);
|
||||
if (ret <= 0) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "%s read error: %d %s\n", __func__, ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
msg->l2h = msg->data;
|
||||
LOGPITS(e1i_ts, DLMIB, LOGL_DEBUG, "HDLC CHAN RX: %s\n", msgb_hexdump_l2(msg));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void e1d_write_msg(struct msgb *msg, void *cbdata)
|
||||
{
|
||||
struct osmo_fd *bfd = cbdata;
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
int ret;
|
||||
|
||||
if (!e1d_connected()) {
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = write(bfd->fd, msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
if (ret < 0) {
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, line);
|
||||
}
|
||||
}
|
||||
|
||||
static int e1d_fd_cb(struct osmo_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
unsigned int idx = ts_nr-1;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
int ret = 0;
|
||||
|
||||
if (!e1d_connected())
|
||||
return -EIO;
|
||||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
if (what & OSMO_FD_READ)
|
||||
ret = handle_ts_sign_read(bfd);
|
||||
if (what & OSMO_FD_WRITE)
|
||||
ret = handle_ts_sign_write(bfd);
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
if (what & OSMO_FD_READ)
|
||||
ret = handle_ts_trau_read(bfd);
|
||||
/* handle_ts_trau_write() is called inside handle_ts_trau_read().
|
||||
* OSMO_FD_WRITE flag is not required here and will not be set.
|
||||
*/
|
||||
break;
|
||||
case E1INP_TS_TYPE_RAW:
|
||||
if (what & OSMO_FD_READ)
|
||||
ret = handle_ts_raw_read(bfd);
|
||||
if (what & OSMO_FD_WRITE)
|
||||
ret = handle_ts_raw_write(bfd);
|
||||
break;
|
||||
case E1INP_TS_TYPE_HDLC:
|
||||
if (what & OSMO_FD_READ)
|
||||
ret = handle_ts_hdlc_read(bfd);
|
||||
if (what & OSMO_FD_WRITE)
|
||||
ret = handle_ts_hdlc_write(bfd);
|
||||
break;
|
||||
default:
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_NOTICE, "unknown/unsupported E1 TS type %u\n", e1i_ts->type);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int e1d_want_write(struct e1inp_ts *e1i_ts)
|
||||
{
|
||||
if (!e1d_connected())
|
||||
return -EIO;
|
||||
|
||||
/* We never include the DAHDI B-Channel FD into the writeset */
|
||||
if (e1i_ts->type == E1INP_TS_TYPE_TRAU ||
|
||||
e1i_ts->type == E1INP_TS_TYPE_I460) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "Trying to write TRAU ts\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
osmo_fd_write_enable(&e1i_ts->driver.e1d.fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int e1d_line_update(struct e1inp_line *line)
|
||||
{
|
||||
int ts;
|
||||
int ret;
|
||||
|
||||
/* we use higher 4 bits for interface, lower 4 bits for line,
|
||||
* resulting in max. 16 interfaces with 16 lines each */
|
||||
uint8_t e1d_intf = (line->port_nr >> 4) & 0xF;
|
||||
uint8_t e1d_line = line->port_nr & 0xF;
|
||||
struct osmo_e1dp_ts_info *ts_info;
|
||||
int num_ts_info;
|
||||
|
||||
if (line->driver != &e1d_driver)
|
||||
return -EINVAL;
|
||||
|
||||
/* Memorize that osmo-e1d is responsible for this line */
|
||||
lines[line->num] = true;
|
||||
|
||||
/* Spawn FSM to handle connection towards osmo-e1d */
|
||||
if (!g_e1d_fsm_inst) {
|
||||
g_e1d_fsm_inst = osmo_fsm_inst_alloc(&fsm_e1d_client, NULL, NULL, LOGL_DEBUG, "fsm_e1d_client");
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONNECT, NULL);
|
||||
OSMO_ASSERT(g_e1d_fsm_inst);
|
||||
}
|
||||
|
||||
/* In case no connection to osmo-e1d is available, we may postpone the line update until the connection is
|
||||
* available (again) */
|
||||
if (!e1d_connected()) {
|
||||
LOGPIL(line, DLINP, LOGL_NOTICE, "No connection to osmo-e1d daemon, postponing Line update: %d %d=E1D(%d:%d) %d\n", line->num, line->port_nr,
|
||||
e1d_intf, e1d_line, line->num_ts);
|
||||
return 0;
|
||||
}
|
||||
LOGPIL(line, DLINP, LOGL_NOTICE, "Line update: %d %d=E1D(%d:%d) %d\n", line->num, line->port_nr,
|
||||
e1d_intf, e1d_line, line->num_ts);
|
||||
|
||||
ret = osmo_e1dp_client_ts_query(g_e1d, &ts_info, &num_ts_info, e1d_intf, e1d_line, E1DP_INVALID);
|
||||
if (ret < 0) {
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "Cannot query E1D for timeslot information: %d, postpoining line update\n", ret);
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, NULL);
|
||||
/* Since we have mechanisms in place that allow us to postpone the line update until the connection
|
||||
* to osmo-e1d is up again, we may pretend that the line update went ok. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (ts=1; ts<line->num_ts; ts++)
|
||||
{
|
||||
unsigned int idx = ts-1;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
struct osmo_fd *bfd = &e1i_ts->driver.e1d.fd;
|
||||
|
||||
/* unregister FD if it was already registered/in use */
|
||||
if (osmo_fd_is_registered(bfd))
|
||||
osmo_fd_unregister(bfd);
|
||||
|
||||
if (e1i_ts->type != E1INP_TS_TYPE_NONE && ts >= num_ts_info) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Timeslot configured, but not existent "
|
||||
"on E1D side; skipping\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_NONE:
|
||||
/* close/release LAPD instance, if any */
|
||||
if (e1i_ts->lapd) {
|
||||
lapd_instance_free(e1i_ts->lapd);
|
||||
e1i_ts->lapd = NULL;
|
||||
}
|
||||
if (bfd->fd >= 0) {
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
continue;
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
if (bfd->fd >= 0 && ts_info[ts].cfg.mode != E1DP_TSMODE_HDLCFCS) {
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
if (bfd->fd < 0) {
|
||||
bfd->fd = osmo_e1dp_client_ts_open(g_e1d, e1d_intf, e1d_line, ts,
|
||||
E1DP_TSMODE_HDLCFCS, D_BCHAN_TX_GRAN);
|
||||
}
|
||||
if (bfd->fd < 0) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Could not open timeslot %d\n", ts);
|
||||
talloc_free(ts_info);
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, NULL);
|
||||
return -EIO;
|
||||
}
|
||||
bfd->when = OSMO_FD_READ;
|
||||
|
||||
if (!e1i_ts->lapd) {
|
||||
char name[32];
|
||||
e1inp_ts_name(name, sizeof(name), e1i_ts);
|
||||
e1i_ts->lapd = lapd_instance_alloc2(1,
|
||||
e1d_write_msg, bfd, e1inp_dlsap_up,
|
||||
e1i_ts, &lapd_profile_abis, name);
|
||||
}
|
||||
break;
|
||||
case E1INP_TS_TYPE_HDLC:
|
||||
/* close/release LAPD instance, if any */
|
||||
if (e1i_ts->lapd) {
|
||||
lapd_instance_free(e1i_ts->lapd);
|
||||
e1i_ts->lapd = NULL;
|
||||
}
|
||||
/* close, if old timeslot mode doesn't match new config */
|
||||
if (bfd->fd >= 0 && ts_info[ts].cfg.mode != E1DP_TSMODE_HDLCFCS) {
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
if (bfd->fd < 0) {
|
||||
bfd->fd = osmo_e1dp_client_ts_open(g_e1d, e1d_intf, e1d_line, ts,
|
||||
E1DP_TSMODE_HDLCFCS, D_BCHAN_TX_GRAN);
|
||||
}
|
||||
if (bfd->fd < 0) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Could not open timeslot %d\n", ts);
|
||||
talloc_free(ts_info);
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, NULL);
|
||||
return -EIO;
|
||||
}
|
||||
bfd->when = OSMO_FD_READ;
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
case E1INP_TS_TYPE_I460:
|
||||
case E1INP_TS_TYPE_RAW:
|
||||
/* close/release LAPD instance, if any */
|
||||
if (e1i_ts->lapd) {
|
||||
lapd_instance_free(e1i_ts->lapd);
|
||||
e1i_ts->lapd = NULL;
|
||||
}
|
||||
/* close, if old timeslot mode doesn't match new config */
|
||||
if (bfd->fd >= 0 && ts_info[ts].cfg.mode != E1DP_TSMODE_RAW) {
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
if (bfd->fd < 0) {
|
||||
bfd->fd = osmo_e1dp_client_ts_open(g_e1d, e1d_intf, e1d_line, ts,
|
||||
E1DP_TSMODE_RAW, D_BCHAN_TX_GRAN);
|
||||
}
|
||||
if (bfd->fd < 0) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "Could not open timeslot %d\n", ts);
|
||||
talloc_free(ts_info);
|
||||
osmo_fsm_inst_dispatch(g_e1d_fsm_inst, EV_CONN_LOST, NULL);
|
||||
return -EIO;
|
||||
}
|
||||
bfd->when = OSMO_FD_READ;
|
||||
break;
|
||||
};
|
||||
|
||||
osmo_fd_setup(bfd, bfd->fd, bfd->when, e1d_fd_cb, line, ts);
|
||||
ret = osmo_fd_register(bfd);
|
||||
if (ret < 0) {
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "could not register FD: %s\n", strerror(ret));
|
||||
talloc_free(ts_info);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(ts_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int e1d_line_create(struct e1inp_line *line)
|
||||
{
|
||||
int ts;
|
||||
|
||||
for (ts = 1; ts < line->num_ts; ts++) {
|
||||
unsigned int idx = ts - 1;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
struct osmo_fd *bfd = &e1i_ts->driver.e1d.fd;
|
||||
|
||||
bfd->fd = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_sa_bits(struct e1inp_line *line, uint8_t sa_bits)
|
||||
{
|
||||
/* we use higher 4 bits for interface, lower 4 bits for line,
|
||||
* resulting in max. 16 interfaces with 16 lines each */
|
||||
uint8_t e1d_intf = (line->port_nr >> 4) & 0xF;
|
||||
uint8_t e1d_line = line->port_nr & 0xF;
|
||||
|
||||
return osmo_e1dp_client_set_sa_bits(g_e1d, e1d_intf, e1d_line, sa_bits);
|
||||
}
|
||||
|
||||
struct e1inp_driver e1d_driver = {
|
||||
.name = "e1d",
|
||||
.want_write = e1d_want_write,
|
||||
.set_sa_bits = set_sa_bits,
|
||||
.line_update = e1d_line_update,
|
||||
.line_create = e1d_line_create,
|
||||
};
|
||||
|
||||
int e1inp_e1d_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&fsm_e1d_client) == 0);
|
||||
memset(lines, 0, sizeof(lines));
|
||||
|
||||
/* register the driver with the core */
|
||||
return e1inp_driver_register(&e1d_driver);
|
||||
}
|
||||
|
||||
#endif /* HAVE_E1D */
|
527
src/input/hsl.c
527
src/input/hsl.c
|
@ -1,527 +0,0 @@
|
|||
/* OpenBSC Abis input driver for HSL Femto */
|
||||
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2011 by On-Waves
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* HSL uses a much more primitive/simplified version of the IPA multiplex.
|
||||
*
|
||||
* They have taken out the nice parts like the ID_GET / ID_RESP for resolving
|
||||
* the UNIT ID, as well as the keepalive ping/pong messages. Furthermore, the
|
||||
* Stream Identifiers are fixed on the BTS side (RSL always 0, OML always 0xff)
|
||||
* and both OML+RSL share a single TCP connection.
|
||||
*
|
||||
* Other oddities include the encapsulation of BSSGP messages in the L3_INFO IE
|
||||
* of RSL
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#define HSL_TCP_PORT 2500
|
||||
#define HSL_PROTO_DEBUG 0xdd
|
||||
|
||||
#define PRIV_OML 1
|
||||
#define PRIV_RSL 2
|
||||
|
||||
static void *tall_hsl_ctx;
|
||||
|
||||
/* data structure for one E1 interface with A-bis */
|
||||
struct hsl_e1_handle {
|
||||
struct osmo_fd listen_fd;
|
||||
struct gsm_network *gsmnet;
|
||||
};
|
||||
|
||||
static struct hsl_e1_handle *e1h;
|
||||
|
||||
|
||||
#define TS1_ALLOC_SIZE 900
|
||||
|
||||
static void hsl_drop(struct e1inp_line *line, struct osmo_fd *bfd)
|
||||
{
|
||||
line->ops->sign_link_down(line);
|
||||
|
||||
if (bfd->fd != -1) {
|
||||
osmo_fd_unregister(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
/* put the virtual E1 line that we cloned for this socket, if
|
||||
* it becomes unused, it gets released. */
|
||||
e1inp_line_put(line);
|
||||
}
|
||||
|
||||
static int process_hsl_rsl(struct msgb *msg, struct e1inp_line *line,
|
||||
struct osmo_fd *bfd)
|
||||
{
|
||||
char serno_buf[16];
|
||||
uint8_t serno_len;
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct hsl_unit unit_data;
|
||||
|
||||
switch (msg->l2h[1]) {
|
||||
case 0x80:
|
||||
/*, contains Serial Number + SW version */
|
||||
if (msg->l2h[2] != 0xc0)
|
||||
break;
|
||||
serno_len = msg->l2h[3];
|
||||
if (serno_len > sizeof(serno_buf)-1)
|
||||
serno_len = sizeof(serno_buf)-1;
|
||||
memcpy(serno_buf, msg->l2h+4, serno_len);
|
||||
serno_buf[serno_len] = '\0';
|
||||
unit_data.serno = strtoul(serno_buf, NULL, 10);
|
||||
|
||||
if (!line->ops->sign_link_up) {
|
||||
LOGP(DLINP, LOGL_ERROR,
|
||||
"Unable to set signal link, closing socket.\n");
|
||||
osmo_fd_unregister(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
sign_link = line->ops->sign_link_up(&unit_data,
|
||||
line, E1INP_SIGN_NONE);
|
||||
if (sign_link == NULL) {
|
||||
LOGP(DLINP, LOGL_ERROR,
|
||||
"Unable to set signal link, closing socket.\n");
|
||||
osmo_fd_unregister(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
msgb_free(msg);
|
||||
return 1; /* == we have taken over the msg */
|
||||
case 0x82:
|
||||
/* FIXME: do something with BSSGP, i.e. forward it over
|
||||
* NSIP to OsmoSGSN */
|
||||
msgb_free(msg);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_ts1_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct e1inp_sign_link *link;
|
||||
struct ipaccess_head *hh;
|
||||
struct msgb *msg;
|
||||
int ret = 0, error;
|
||||
|
||||
error = ipa_msg_recv(bfd->fd, &msg);
|
||||
if (error < 0)
|
||||
return error;
|
||||
else if (error == 0) {
|
||||
hsl_drop(e1i_ts->line, bfd);
|
||||
LOGP(DLINP, LOGL_NOTICE, "Sign link vanished, dead socket\n");
|
||||
return error;
|
||||
}
|
||||
DEBUGP(DLMI, "RX %u: %s\n", ts_nr, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
|
||||
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
if (hh->proto == HSL_PROTO_DEBUG) {
|
||||
LOGP(DLINP, LOGL_NOTICE, "HSL debug: %s\n", msg->data + sizeof(*hh));
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* HSL proprietary RSL extension */
|
||||
if (hh->proto == 0 && (msg->l2h[0] == 0x81 || msg->l2h[0] == 0x80)) {
|
||||
ret = process_hsl_rsl(msg, line, bfd);
|
||||
if (ret < 0) {
|
||||
hsl_drop(e1i_ts->line, bfd);
|
||||
return ret;
|
||||
} else if (ret == 1)
|
||||
return 0;
|
||||
/* else: continue... */
|
||||
}
|
||||
|
||||
#ifdef HSL_SR_1_0
|
||||
/* HSL for whatever reason chose to use 0x81 instead of 0x80 for FOM */
|
||||
if (hh->proto == 255 && msg->l2h[0] == (ABIS_OM_MDISC_FOM | 0x01))
|
||||
msg->l2h[0] = ABIS_OM_MDISC_FOM;
|
||||
#endif
|
||||
link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
|
||||
if (!link) {
|
||||
LOGP(DLINP, LOGL_ERROR, "no matching signalling link for "
|
||||
"hh->proto=0x%02x\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msg->dst = link;
|
||||
|
||||
/* XXX: better use e1inp_ts_rx? */
|
||||
if (!e1i_ts->line->ops->sign_link) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Fix your application, "
|
||||
"no action set for signalling messages.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
e1i_ts->line->ops->sign_link(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ts_want_write(struct e1inp_ts *e1i_ts)
|
||||
{
|
||||
e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timeout_ts1_write(void *data)
|
||||
{
|
||||
struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
|
||||
|
||||
/* trigger write of ts1, due to tx delay timer */
|
||||
ts_want_write(e1i_ts);
|
||||
}
|
||||
|
||||
static int __handle_ts1_write(struct osmo_fd *bfd, struct e1inp_line *line)
|
||||
{
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, &sign_link);
|
||||
if (!msg) {
|
||||
/* no message after tx delay timer */
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (sign_link->type) {
|
||||
case E1INP_SIGN_OML:
|
||||
#ifdef HSL_SR_1_0
|
||||
/* HSL uses 0x81 for FOM for some reason */
|
||||
if (msg->data[0] == ABIS_OM_MDISC_FOM)
|
||||
msg->data[0] = ABIS_OM_MDISC_FOM | 0x01;
|
||||
#endif
|
||||
break;
|
||||
case E1INP_SIGN_RSL:
|
||||
break;
|
||||
default:
|
||||
msgb_free(msg);
|
||||
bfd->when |= BSC_FD_WRITE; /* come back for more msg */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg->l2h = msg->data;
|
||||
ipaccess_prepend_header(msg, sign_link->tei);
|
||||
|
||||
DEBUGP(DLMI, "TX %u: %s\n", ts_nr, osmo_hexdump(msg->l2h, msgb_l2len(msg)));
|
||||
|
||||
ret = send(bfd->fd, msg->data, msg->len, 0);
|
||||
msgb_free(msg);
|
||||
|
||||
/* set tx delay timer for next event */
|
||||
e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
|
||||
e1i_ts->sign.tx_timer.data = e1i_ts;
|
||||
|
||||
/* Reducing this might break the nanoBTS 900 init. */
|
||||
osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_ts1_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
|
||||
return __handle_ts1_write(bfd, line);
|
||||
}
|
||||
|
||||
int hsl_bts_write(struct ipa_client_link *link)
|
||||
{
|
||||
struct e1inp_line *line = link->line;
|
||||
|
||||
return __handle_ts1_write(link->ofd, line);
|
||||
}
|
||||
|
||||
/* callback from select.c in case one of the fd's can be read/written */
|
||||
static int hsl_fd_cb(struct osmo_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
unsigned int idx = ts_nr-1;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
int rc = 0;
|
||||
|
||||
/* In case of early RSL we might not yet have a line */
|
||||
|
||||
if (line)
|
||||
e1i_ts = &line->ts[idx];
|
||||
|
||||
if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) {
|
||||
if (what & BSC_FD_READ)
|
||||
rc = handle_ts1_read(bfd);
|
||||
if (what & BSC_FD_WRITE)
|
||||
rc = handle_ts1_write(bfd);
|
||||
} else
|
||||
LOGP(DLINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void hsl_close(struct e1inp_sign_link *sign_link)
|
||||
{
|
||||
struct e1inp_ts *ts = sign_link->ts;
|
||||
struct osmo_fd *bfd = &ts->driver.ipaccess.fd;
|
||||
e1inp_event(ts, S_L_INP_TEI_DN, sign_link->tei, sign_link->sapi);
|
||||
/* the first e1inp_sign_link_destroy call closes the socket. */
|
||||
if (bfd->fd != -1) {
|
||||
osmo_fd_unregister(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int hsl_line_update(struct e1inp_line *line);
|
||||
|
||||
struct e1inp_driver hsl_driver = {
|
||||
.name = "hsl",
|
||||
.want_write = ts_want_write,
|
||||
.line_update = hsl_line_update,
|
||||
.close = hsl_close,
|
||||
.default_delay = 0,
|
||||
};
|
||||
|
||||
/* callback of the OML listening filedescriptor */
|
||||
static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
|
||||
{
|
||||
int ret;
|
||||
int idx = 0;
|
||||
int i;
|
||||
struct e1inp_line *line;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
struct osmo_fd *bfd;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (!(what & BSC_FD_READ))
|
||||
return 0;
|
||||
|
||||
ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
|
||||
if (ret < 0) {
|
||||
perror("accept");
|
||||
return ret;
|
||||
}
|
||||
LOGP(DLINP, LOGL_NOTICE, "accept()ed new HSL link from %s\n",
|
||||
inet_ntoa(sa.sin_addr));
|
||||
|
||||
/* clone virtual E1 line for this new signalling link. */
|
||||
line = e1inp_line_clone(tall_hsl_ctx, listen_bfd->data);
|
||||
if (line == NULL) {
|
||||
LOGP(DLINP, LOGL_ERROR, "could not clone E1 line\n");
|
||||
return -1;
|
||||
}
|
||||
/* create virrtual E1 timeslots for signalling */
|
||||
e1inp_ts_config_sign(&line->ts[1-1], line);
|
||||
|
||||
/* initialize the fds */
|
||||
for (i = 0; i < ARRAY_SIZE(line->ts); ++i)
|
||||
line->ts[i].driver.ipaccess.fd.fd = -1;
|
||||
|
||||
e1i_ts = &line->ts[idx];
|
||||
|
||||
bfd = &e1i_ts->driver.ipaccess.fd;
|
||||
bfd->fd = ret;
|
||||
bfd->data = line;
|
||||
bfd->priv_nr = PRIV_OML;
|
||||
bfd->cb = hsl_fd_cb;
|
||||
bfd->when = BSC_FD_READ;
|
||||
ret = osmo_fd_register(bfd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "could not register FD\n");
|
||||
close(bfd->fd);
|
||||
e1inp_line_put(line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hsl_bts_process(struct ipa_client_link *link, struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct e1inp_ts *e1i_ts = &link->line->ts[0];
|
||||
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
if (hh->proto == HSL_PROTO_DEBUG) {
|
||||
LOGP(DLINP, LOGL_NOTICE, "HSL debug: %s\n",
|
||||
msg->data + sizeof(*hh));
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
sign_link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
|
||||
if (!sign_link) {
|
||||
LOGP(DLINP, LOGL_ERROR, "no matching signalling link for "
|
||||
"hh->proto=0x%02x\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msg->dst = sign_link;
|
||||
|
||||
/* XXX better use e1inp_ts_rx? */
|
||||
if (!link->line->ops->sign_link) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Fix your application, "
|
||||
"no action set for signalling messages.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
link->line->ops->sign_link(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hsl_bts_connect(struct ipa_client_link *link)
|
||||
{
|
||||
struct msgb *msg;
|
||||
uint8_t *serno;
|
||||
char serno_buf[16];
|
||||
struct hsl_unit *unit = link->line->ops->cfg.ipa.dev;
|
||||
struct e1inp_sign_link *sign_link;
|
||||
|
||||
/* send the minimal message to identify this BTS. */
|
||||
msg = ipa_msg_alloc(0);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
*msgb_put(msg, 1) = 0x80;
|
||||
*msgb_put(msg, 1) = 0x80;
|
||||
*msgb_put(msg, 1) = unit->swversion;
|
||||
snprintf(serno_buf, sizeof(serno_buf), "%"PRIx64, unit->serno);
|
||||
serno = msgb_put(msg, strlen(serno_buf)+1);
|
||||
memcpy(serno, serno_buf, strlen(serno_buf));
|
||||
ipa_msg_push_header(msg, 0);
|
||||
send(link->ofd->fd, msg->data, msg->len, 0);
|
||||
msgb_free(msg);
|
||||
|
||||
/* ... and enable the signalling link. */
|
||||
if (!link->line->ops->sign_link_up) {
|
||||
LOGP(DLINP, LOGL_ERROR,
|
||||
"Unable to set signal link, closing socket.\n");
|
||||
ipa_client_link_close(link);
|
||||
return -EINVAL;
|
||||
}
|
||||
sign_link = link->line->ops->sign_link_up(&unit,
|
||||
link->line, E1INP_SIGN_NONE);
|
||||
if (sign_link == NULL) {
|
||||
LOGP(DLINP, LOGL_ERROR,
|
||||
"Unable to set signal link, closing socket.\n");
|
||||
ipa_client_link_close(link);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hsl_line_update(struct e1inp_line *line)
|
||||
{
|
||||
int ret = -ENOENT;
|
||||
|
||||
switch(line->ops->cfg.ipa.role) {
|
||||
case E1INP_LINE_R_BSC:
|
||||
LOGP(DLINP, LOGL_NOTICE, "enabling hsl BSC mode\n");
|
||||
|
||||
ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||
line->ops->cfg.ipa.addr,
|
||||
HSL_TCP_PORT, OSMO_SOCK_F_BIND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
e1h->listen_fd.fd = ret;
|
||||
e1h->listen_fd.when |= BSC_FD_READ;
|
||||
e1h->listen_fd.cb = listen_fd_cb;
|
||||
e1h->listen_fd.data = line;
|
||||
|
||||
if (osmo_fd_register(&e1h->listen_fd) < 0) {
|
||||
close(ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case E1INP_LINE_R_BTS: {
|
||||
struct ipa_client_link *link;
|
||||
|
||||
LOGP(DLINP, LOGL_NOTICE, "enabling hsl BTS mode\n");
|
||||
|
||||
link = ipa_client_link_create(tall_hsl_ctx,
|
||||
&line->ts[E1INP_SIGN_OML-1],
|
||||
"hsl", E1INP_SIGN_OML,
|
||||
line->ops->cfg.ipa.addr,
|
||||
HSL_TCP_PORT,
|
||||
hsl_bts_connect,
|
||||
hsl_bts_process,
|
||||
hsl_bts_write,
|
||||
NULL);
|
||||
if (link == NULL) {
|
||||
LOGP(DLINP, LOGL_ERROR, "cannot create BTS link: %s\n",
|
||||
strerror(errno));
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ipa_client_link_open(link) < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "cannot open BTS link: %s\n",
|
||||
strerror(errno));
|
||||
ipa_client_link_close(link);
|
||||
ipa_client_link_destroy(link);
|
||||
return -EIO;
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void e1inp_hsl_init(void)
|
||||
{
|
||||
tall_hsl_ctx = talloc_named_const(libosmo_abis_ctx, 1, "hsl");
|
||||
|
||||
e1h = talloc_zero(tall_hsl_ctx, struct hsl_e1_handle);
|
||||
if (!e1h)
|
||||
return;
|
||||
|
||||
e1inp_driver_register(&hsl_driver);
|
||||
}
|
587
src/input/ipa.c
587
src/input/ipa.c
|
@ -12,6 +12,7 @@
|
|||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
@ -19,22 +20,11 @@
|
|||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/backtrace.h>
|
||||
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#define IPA_ALLOC_SIZE 1200
|
||||
|
||||
struct msgb *ipa_msg_alloc(int headroom)
|
||||
{
|
||||
struct msgb *nmsg;
|
||||
|
||||
headroom += sizeof(struct ipaccess_head);
|
||||
|
||||
nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP");
|
||||
if (!nmsg)
|
||||
return NULL;
|
||||
return nmsg;
|
||||
}
|
||||
#define LOGIPA(link, level, fmt, args...) LOGP(DLINP, level, "%s:%u " fmt, link->addr, link->port, ## args)
|
||||
|
||||
void ipa_msg_push_header(struct msgb *msg, uint8_t proto)
|
||||
{
|
||||
|
@ -46,110 +36,65 @@ void ipa_msg_push_header(struct msgb *msg, uint8_t proto)
|
|||
hh->len = htons(msgb_l2len(msg));
|
||||
}
|
||||
|
||||
int ipa_msg_recv(int fd, struct msgb **rmsg)
|
||||
void ipa_client_conn_close(struct ipa_client_conn *link)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct ipaccess_head *hh;
|
||||
int len, ret;
|
||||
osmo_timer_del(&link->timer);
|
||||
|
||||
msg = ipa_msg_alloc(0);
|
||||
if (msg == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* first read our 3-byte header */
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
ret = recv(fd, msg->data, sizeof(*hh), 0);
|
||||
if (ret <= 0) {
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
} else if (ret != sizeof(*hh)) {
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
/* be safe against multiple calls */
|
||||
if (link->ofd->fd != -1) {
|
||||
osmo_fd_unregister(link->ofd);
|
||||
close(link->ofd->fd);
|
||||
link->ofd->fd = -1;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
|
||||
/* then read the length as specified in header */
|
||||
msg->l2h = msg->data + sizeof(*hh);
|
||||
len = ntohs(hh->len);
|
||||
|
||||
if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = recv(fd, msg->l2h, len, 0);
|
||||
if (ret <= 0) {
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
} else if (ret < len) {
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
*rmsg = msg;
|
||||
return ret;
|
||||
msgb_free(link->pending_msg);
|
||||
link->pending_msg = NULL;
|
||||
}
|
||||
|
||||
void ipa_client_link_close(struct ipa_client_link *link);
|
||||
|
||||
static void ipa_client_retry(struct ipa_client_link *link)
|
||||
{
|
||||
LOGP(DLINP, LOGL_NOTICE, "connection closed\n");
|
||||
ipa_client_link_close(link);
|
||||
LOGP(DLINP, LOGL_NOTICE, "retrying in 5 seconds...\n");
|
||||
osmo_timer_schedule(&link->timer, 5, 0);
|
||||
link->state = IPA_CLIENT_LINK_STATE_CONNECTING;
|
||||
}
|
||||
|
||||
void ipa_client_link_close(struct ipa_client_link *link)
|
||||
{
|
||||
osmo_fd_unregister(link->ofd);
|
||||
close(link->ofd->fd);
|
||||
}
|
||||
|
||||
static void ipa_client_read(struct ipa_client_link *link)
|
||||
static int ipa_client_read(struct ipa_client_conn *link)
|
||||
{
|
||||
struct osmo_fd *ofd = link->ofd;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
LOGP(DLINP, LOGL_NOTICE, "message received\n");
|
||||
LOGIPA(link, LOGL_DEBUG, "message received\n");
|
||||
|
||||
ret = ipa_msg_recv(ofd->fd, &msg);
|
||||
if (ret < 0) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
LOGP(DLINP, LOGL_ERROR, "lost connection with server\n");
|
||||
} else {
|
||||
LOGP(DLINP, LOGL_ERROR, "unknown error\n");
|
||||
}
|
||||
ipa_client_retry(link);
|
||||
return;
|
||||
} else if (ret == 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "connection closed with server\n");
|
||||
ipa_client_retry(link);
|
||||
return;
|
||||
ret = ipa_msg_recv_buffered(ofd->fd, &msg, &link->pending_msg);
|
||||
if (ret <= 0) {
|
||||
if (ret == -EAGAIN)
|
||||
return 0;
|
||||
else if (ret == -EPIPE || ret == -ECONNRESET)
|
||||
LOGIPA(link, LOGL_ERROR, "lost connection with server\n");
|
||||
else if (ret == 0)
|
||||
LOGIPA(link, LOGL_ERROR, "connection closed with server\n");
|
||||
else
|
||||
LOGIPA(link, LOGL_ERROR, "unknown error %d from socket\n", ret);
|
||||
ipa_client_conn_close(link);
|
||||
if (link->updown_cb)
|
||||
link->updown_cb(link, 0);
|
||||
return -EBADF;
|
||||
}
|
||||
if (link->read_cb)
|
||||
link->read_cb(link, msg);
|
||||
return link->read_cb(link, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipa_client_write(struct ipa_client_link *link)
|
||||
static void ipa_client_write(struct ipa_client_conn *link)
|
||||
{
|
||||
if (link->write_cb)
|
||||
link->write_cb(link);
|
||||
}
|
||||
|
||||
int ipa_client_write_default_cb(struct ipa_client_link *link)
|
||||
static int ipa_client_write_default_cb(struct ipa_client_conn *link)
|
||||
{
|
||||
struct osmo_fd *ofd = link->ofd;
|
||||
struct msgb *msg;
|
||||
struct llist_head *lh;
|
||||
int ret;
|
||||
|
||||
LOGP(DLINP, LOGL_NOTICE, "sending data\n");
|
||||
LOGIPA(link, LOGL_DEBUG, "sending data\n");
|
||||
|
||||
if (llist_empty(&link->tx_queue)) {
|
||||
ofd->when &= ~BSC_FD_WRITE;
|
||||
osmo_fd_write_disable(ofd);
|
||||
return 0;
|
||||
}
|
||||
lh = link->tx_queue.next;
|
||||
|
@ -159,75 +104,106 @@ int ipa_client_write_default_cb(struct ipa_client_link *link)
|
|||
ret = send(link->ofd->fd, msg->data, msg->len, 0);
|
||||
if (ret < 0) {
|
||||
if (errno == EPIPE || errno == ENOTCONN) {
|
||||
ipa_client_retry(link);
|
||||
ipa_client_conn_close(link);
|
||||
if (link->updown_cb)
|
||||
link->updown_cb(link, 0);
|
||||
}
|
||||
LOGP(DLINP, LOGL_ERROR, "error to send\n");
|
||||
LOGIPA(link, LOGL_ERROR, "error to send\n");
|
||||
}
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipa_connect_failure(struct ipa_client_conn *link)
|
||||
{
|
||||
ipa_client_conn_close(link);
|
||||
if (link->updown_cb)
|
||||
link->updown_cb(link, 0);
|
||||
}
|
||||
|
||||
static int ipa_client_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct ipa_client_link *link = ofd->data;
|
||||
int error, ret;
|
||||
struct ipa_client_conn *link = ofd->data;
|
||||
int error, ret = 0;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
switch(link->state) {
|
||||
case IPA_CLIENT_LINK_STATE_CONNECTING:
|
||||
ret = getsockopt(ofd->fd, SOL_SOCKET, SO_ERROR, &error, &len);
|
||||
if (ret >= 0 && error > 0) {
|
||||
ipa_client_retry(link);
|
||||
ipa_connect_failure(link);
|
||||
return 0;
|
||||
}
|
||||
ofd->when &= ~BSC_FD_WRITE;
|
||||
LOGP(DLINP, LOGL_NOTICE, "connection done.\n");
|
||||
|
||||
/* Stop the timer when connection succeeds, on failure it's deleted in
|
||||
ipa_client_conn_close() called by ipa_connect_failure() above */
|
||||
osmo_timer_del(&link->timer);
|
||||
|
||||
osmo_fd_write_disable(ofd);
|
||||
LOGIPA(link, LOGL_NOTICE, "connection done\n");
|
||||
link->state = IPA_CLIENT_LINK_STATE_CONNECTED;
|
||||
if (link->connect_cb)
|
||||
link->connect_cb(link);
|
||||
if (link->updown_cb)
|
||||
link->updown_cb(link, 1);
|
||||
break;
|
||||
case IPA_CLIENT_LINK_STATE_CONNECTED:
|
||||
if (what & BSC_FD_READ) {
|
||||
LOGP(DLINP, LOGL_NOTICE, "connected read\n");
|
||||
ipa_client_read(link);
|
||||
if (what & OSMO_FD_READ) {
|
||||
LOGIPA(link, LOGL_DEBUG, "connected read\n");
|
||||
ret = ipa_client_read(link);
|
||||
}
|
||||
if (what & BSC_FD_WRITE) {
|
||||
LOGP(DLINP, LOGL_NOTICE, "connected write\n");
|
||||
if (ret != -EBADF && (what & OSMO_FD_WRITE)) {
|
||||
LOGIPA(link, LOGL_DEBUG, "connected write\n");
|
||||
ipa_client_write(link);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipa_link_timer_cb(void *data);
|
||||
/* Treat the connect timeout exactly like a connect failure */
|
||||
static void ipa_connect_timeout_cb(void *data)
|
||||
{
|
||||
struct ipa_client_conn *link = (struct ipa_client_conn *)data;
|
||||
|
||||
struct ipa_client_link *
|
||||
ipa_client_link_create(void *ctx, struct e1inp_ts *ts, const char *driver_name,
|
||||
LOGIPA(link, LOGL_NOTICE, "Connect timeout reached\n");
|
||||
ipa_connect_failure(link);
|
||||
}
|
||||
|
||||
struct ipa_client_conn *
|
||||
ipa_client_conn_create(void *ctx, struct e1inp_ts *ts,
|
||||
int priv_nr, const char *addr, uint16_t port,
|
||||
int (*connect_cb)(struct ipa_client_link *link),
|
||||
int (*read_cb)(struct ipa_client_link *link,
|
||||
void (*updown_cb)(struct ipa_client_conn *link, int up),
|
||||
int (*read_cb)(struct ipa_client_conn *link,
|
||||
struct msgb *msgb),
|
||||
int (*write_cb)(struct ipa_client_link *link),
|
||||
int (*write_cb)(struct ipa_client_conn *link),
|
||||
void *data)
|
||||
{
|
||||
struct ipa_client_link *ipa_link;
|
||||
return ipa_client_conn_create2(ctx, ts, priv_nr, NULL, 0, addr, port,
|
||||
updown_cb, read_cb, write_cb, data);
|
||||
}
|
||||
|
||||
ipa_link = talloc_zero(ctx, struct ipa_client_link);
|
||||
struct ipa_client_conn *
|
||||
ipa_client_conn_create2(void *ctx, struct e1inp_ts *ts,
|
||||
int priv_nr, const char *loc_addr, uint16_t loc_port,
|
||||
const char *rem_addr, uint16_t rem_port,
|
||||
void (*updown_cb)(struct ipa_client_conn *link, int up),
|
||||
int (*read_cb)(struct ipa_client_conn *link,
|
||||
struct msgb *msgb),
|
||||
int (*write_cb)(struct ipa_client_conn *link),
|
||||
void *data)
|
||||
{
|
||||
struct ipa_client_conn *ipa_link;
|
||||
|
||||
ipa_link = talloc_zero(ctx, struct ipa_client_conn);
|
||||
if (!ipa_link)
|
||||
return NULL;
|
||||
|
||||
if (ts) {
|
||||
struct e1inp_driver *driver;
|
||||
|
||||
driver = e1inp_driver_find(driver_name);
|
||||
if (driver == NULL) {
|
||||
if (ts->line->driver == NULL) {
|
||||
talloc_free(ipa_link);
|
||||
return NULL;
|
||||
}
|
||||
ts->line->driver = driver;
|
||||
ipa_link->ofd = &ts->driver.ipaccess.fd;
|
||||
} else {
|
||||
ipa_link->ofd = talloc_zero(ctx, struct osmo_fd);
|
||||
|
@ -237,88 +213,125 @@ ipa_client_link_create(void *ctx, struct e1inp_ts *ts, const char *driver_name,
|
|||
}
|
||||
}
|
||||
|
||||
ipa_link->ofd->when |= BSC_FD_READ | BSC_FD_WRITE;
|
||||
ipa_link->ofd->priv_nr = priv_nr;
|
||||
ipa_link->ofd->cb = ipa_client_fd_cb;
|
||||
ipa_link->ofd->data = ipa_link;
|
||||
osmo_fd_setup(ipa_link->ofd, -1, OSMO_FD_READ|OSMO_FD_WRITE, ipa_client_fd_cb, ipa_link, priv_nr);
|
||||
ipa_link->state = IPA_CLIENT_LINK_STATE_CONNECTING;
|
||||
ipa_link->timer.cb = ipa_link_timer_cb;
|
||||
ipa_link->timer.data = ipa_link;
|
||||
ipa_link->addr = talloc_strdup(ipa_link, addr);
|
||||
ipa_link->port = port;
|
||||
ipa_link->connect_cb = connect_cb;
|
||||
ipa_link->local_addr = talloc_strdup(ipa_link, loc_addr);
|
||||
ipa_link->local_port = loc_port;
|
||||
ipa_link->addr = talloc_strdup(ipa_link, rem_addr);
|
||||
ipa_link->port = rem_port;
|
||||
ipa_link->updown_cb = updown_cb;
|
||||
ipa_link->read_cb = read_cb;
|
||||
ipa_link->write_cb = write_cb;
|
||||
ipa_link->line = ts->line;
|
||||
osmo_timer_setup(&ipa_link->timer, ipa_connect_timeout_cb, ipa_link);
|
||||
|
||||
/* default to generic write callback if not set. */
|
||||
if (write_cb == NULL)
|
||||
ipa_link->write_cb = ipa_client_write_default_cb;
|
||||
else
|
||||
ipa_link->write_cb = write_cb;
|
||||
|
||||
if (ts)
|
||||
ipa_link->line = ts->line;
|
||||
ipa_link->data = data;
|
||||
INIT_LLIST_HEAD(&ipa_link->tx_queue);
|
||||
|
||||
return ipa_link;
|
||||
}
|
||||
|
||||
void ipa_client_link_destroy(struct ipa_client_link *link)
|
||||
void ipa_client_conn_destroy(struct ipa_client_conn *link)
|
||||
{
|
||||
talloc_free(link);
|
||||
}
|
||||
|
||||
int ipa_client_link_open(struct ipa_client_link *link)
|
||||
int ipa_client_conn_open(struct ipa_client_conn *link)
|
||||
{
|
||||
return ipa_client_conn_open2(link, 30);
|
||||
}
|
||||
|
||||
int ipa_client_conn_open2(struct ipa_client_conn *link, unsigned int connect_timeout)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||
if (link->ofd->fd != -1)
|
||||
return -EINVAL;
|
||||
|
||||
link->state = IPA_CLIENT_LINK_STATE_CONNECTING;
|
||||
ret = osmo_sock_init2(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||
link->local_addr, link->local_port,
|
||||
link->addr, link->port,
|
||||
OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK);
|
||||
if (ret < 0) {
|
||||
if (errno != EINPROGRESS)
|
||||
return ret;
|
||||
}
|
||||
OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK|
|
||||
OSMO_SOCK_F_DSCP(link->dscp) | OSMO_SOCK_F_PRIO(link->priority));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
link->ofd->fd = ret;
|
||||
osmo_fd_write_enable(link->ofd);
|
||||
if (osmo_fd_register(link->ofd) < 0) {
|
||||
close(ret);
|
||||
link->ofd->fd = -1;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (connect_timeout > 0)
|
||||
osmo_timer_schedule(&link->timer, connect_timeout, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipa_link_timer_cb(void *data)
|
||||
{
|
||||
struct ipa_client_link *link = data;
|
||||
|
||||
LOGP(DLINP, LOGL_NOTICE, "reconnecting.\n");
|
||||
|
||||
switch(link->state) {
|
||||
case IPA_CLIENT_LINK_STATE_CONNECTING:
|
||||
ipa_client_link_open(link);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ipa_client_link_send(struct ipa_client_link *link, struct msgb *msg)
|
||||
void ipa_client_conn_send(struct ipa_client_conn *link, struct msgb *msg)
|
||||
{
|
||||
msgb_enqueue(&link->tx_queue, msg);
|
||||
link->ofd->when |= BSC_FD_WRITE;
|
||||
osmo_fd_write_enable(link->ofd);
|
||||
}
|
||||
|
||||
size_t ipa_client_conn_clear_queue(struct ipa_client_conn *link)
|
||||
{
|
||||
size_t deleted = 0;
|
||||
|
||||
while (!llist_empty(&link->tx_queue)) {
|
||||
struct msgb *msg = msgb_dequeue(&link->tx_queue);
|
||||
msgb_free(msg);
|
||||
deleted += 1;
|
||||
}
|
||||
|
||||
osmo_fd_write_disable(link->ofd);
|
||||
return deleted;
|
||||
}
|
||||
|
||||
static int ipa_server_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
int ret;
|
||||
int fd, ret;
|
||||
char ipbuf[INET6_ADDRSTRLEN + 1];
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
struct ipa_server_link *link = ofd->data;
|
||||
|
||||
ret = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len);
|
||||
if (ret < 0) {
|
||||
fd = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len);
|
||||
if (fd < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "failed to accept from origin "
|
||||
"peer, reason=`%s'\n", strerror(errno));
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (!link->addr) {
|
||||
ret = osmo_sock_get_local_ip(fd, ipbuf, INET6_ADDRSTRLEN + 1);
|
||||
if (ret == 0)
|
||||
link->addr = talloc_strdup(link, ipbuf);
|
||||
}
|
||||
|
||||
LOGIPA(link, LOGL_NOTICE, "accept()ed new link from %s:%u\n",
|
||||
inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
|
||||
|
||||
/* make new fd inherit DSCP + priority of listen-socket */
|
||||
osmo_sock_set_dscp(fd, link->dscp);
|
||||
osmo_sock_set_priority(fd, link->priority);
|
||||
|
||||
ret = link->accept_cb(link, fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR,
|
||||
"failed to process accept()ed new link, "
|
||||
"reason=`%s'\n", strerror(-ret));
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
LOGP(DLINP, LOGL_NOTICE, "accept()ed new link from %s to port %u\n",
|
||||
inet_ntoa(sa.sin_addr), link->port);
|
||||
|
||||
if (link->accept_cb)
|
||||
link->accept_cb(link, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -331,14 +344,15 @@ ipa_server_link_create(void *ctx, struct e1inp_line *line,
|
|||
{
|
||||
struct ipa_server_link *ipa_link;
|
||||
|
||||
OSMO_ASSERT(accept_cb != NULL);
|
||||
|
||||
ipa_link = talloc_zero(ctx, struct ipa_server_link);
|
||||
if (!ipa_link)
|
||||
return NULL;
|
||||
|
||||
ipa_link->ofd.when |= BSC_FD_READ | BSC_FD_WRITE;
|
||||
ipa_link->ofd.cb = ipa_server_fd_cb;
|
||||
ipa_link->ofd.data = ipa_link;
|
||||
ipa_link->addr = talloc_strdup(ipa_link, addr);
|
||||
osmo_fd_setup(&ipa_link->ofd, -1, OSMO_FD_READ|OSMO_FD_WRITE, ipa_server_fd_cb, ipa_link, 0);
|
||||
if (addr)
|
||||
ipa_link->addr = talloc_strdup(ipa_link, addr);
|
||||
ipa_link->port = port;
|
||||
ipa_link->accept_cb = accept_cb;
|
||||
ipa_link->line = line;
|
||||
|
@ -358,13 +372,15 @@ int ipa_server_link_open(struct ipa_server_link *link)
|
|||
int ret;
|
||||
|
||||
ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||
link->addr, link->port, OSMO_SOCK_F_BIND);
|
||||
link->addr, link->port, OSMO_SOCK_F_BIND|
|
||||
OSMO_SOCK_F_DSCP(link->dscp) | OSMO_SOCK_F_PRIO(link->priority));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
link->ofd.fd = ret;
|
||||
if (osmo_fd_register(&link->ofd) < 0) {
|
||||
close(ret);
|
||||
link->ofd.fd = -1;
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
@ -372,113 +388,194 @@ int ipa_server_link_open(struct ipa_server_link *link)
|
|||
|
||||
void ipa_server_link_close(struct ipa_server_link *link)
|
||||
{
|
||||
if (link->ofd.fd == -1)
|
||||
return;
|
||||
|
||||
osmo_fd_unregister(&link->ofd);
|
||||
close(link->ofd.fd);
|
||||
link->ofd.fd = -1;
|
||||
}
|
||||
|
||||
static void ipa_server_peer_read(struct ipa_server_peer *peer)
|
||||
static int ipa_server_conn_read(struct ipa_server_conn *conn)
|
||||
{
|
||||
struct osmo_fd *ofd = &peer->ofd;
|
||||
struct osmo_fd *ofd = &conn->ofd;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
LOGP(DLINP, LOGL_NOTICE, "message received\n");
|
||||
LOGIPA(conn, LOGL_DEBUG, "message received\n");
|
||||
|
||||
ret = ipa_msg_recv(ofd->fd, &msg);
|
||||
if (ret < 0) {
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
LOGP(DLINP, LOGL_ERROR, "lost connection with server\n");
|
||||
} else {
|
||||
LOGP(DLINP, LOGL_ERROR, "unknown error\n");
|
||||
}
|
||||
return;
|
||||
} else if (ret == 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "connection closed with server\n");
|
||||
ipa_server_peer_destroy(peer);
|
||||
return;
|
||||
ret = ipa_msg_recv_buffered(ofd->fd, &msg, &conn->pending_msg);
|
||||
if (ret <= 0) {
|
||||
if (ret == -EAGAIN)
|
||||
return 0;
|
||||
else if (ret == -EPIPE || ret == -ECONNRESET)
|
||||
LOGIPA(conn, LOGL_ERROR, "lost connection with server\n");
|
||||
else if (ret == 0)
|
||||
LOGIPA(conn, LOGL_ERROR, "connection closed with server\n");
|
||||
else
|
||||
LOGIPA(conn, LOGL_ERROR, "unknown error %d from socket\n", ret);
|
||||
ipa_server_conn_destroy(conn);
|
||||
return -EBADF;
|
||||
}
|
||||
if (peer->cb)
|
||||
peer->cb(peer, msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void ipa_server_peer_write(struct ipa_server_peer *peer)
|
||||
{
|
||||
struct osmo_fd *ofd = &peer->ofd;
|
||||
struct msgb *msg;
|
||||
struct llist_head *lh;
|
||||
int ret;
|
||||
|
||||
LOGP(DLINP, LOGL_NOTICE, "sending data\n");
|
||||
|
||||
if (llist_empty(&peer->tx_queue)) {
|
||||
ofd->when &= ~BSC_FD_WRITE;
|
||||
return;
|
||||
}
|
||||
lh = peer->tx_queue.next;
|
||||
llist_del(lh);
|
||||
msg = llist_entry(lh, struct msgb, list);
|
||||
|
||||
ret = send(peer->ofd.fd, msg->data, msg->len, 0);
|
||||
if (ret < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "error to send\n");
|
||||
}
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static int ipa_server_peer_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct ipa_server_peer *peer = ofd->data;
|
||||
|
||||
LOGP(DLINP, LOGL_NOTICE, "connected read/write\n");
|
||||
if (what & BSC_FD_READ)
|
||||
ipa_server_peer_read(peer);
|
||||
if (what & BSC_FD_WRITE)
|
||||
ipa_server_peer_write(peer);
|
||||
if (conn->cb)
|
||||
return conn->cb(conn, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipa_server_peer *
|
||||
ipa_server_peer_create(void *ctx, struct ipa_server_link *link, int fd,
|
||||
int (*cb)(struct ipa_server_peer *peer, struct msgb *msg),
|
||||
void *data)
|
||||
static void ipa_server_conn_write(struct ipa_server_conn *conn)
|
||||
{
|
||||
struct ipa_server_peer *peer;
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
peer = talloc_zero(ctx, struct ipa_server_peer);
|
||||
if (peer == NULL) {
|
||||
LOGIPA(conn, LOGL_DEBUG, "sending data\n");
|
||||
msg = msgb_dequeue(&conn->tx_queue);
|
||||
|
||||
if (!msg) {
|
||||
osmo_fd_write_disable(&conn->ofd);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = send(conn->ofd.fd, msg->data, msg->len, 0);
|
||||
if (ret < 0) {
|
||||
LOGIPA(conn, LOGL_ERROR, "error to send\n");
|
||||
}
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static int ipa_server_conn_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct ipa_server_conn *conn = ofd->data;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLINP, LOGL_DEBUG, "connected read/write\n");
|
||||
if (what & OSMO_FD_READ)
|
||||
rc = ipa_server_conn_read(conn);
|
||||
if (rc != -EBADF && (what & OSMO_FD_WRITE))
|
||||
ipa_server_conn_write(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipa_server_conn *
|
||||
ipa_server_conn_create(void *ctx, struct ipa_server_link *link, int fd,
|
||||
int (*cb)(struct ipa_server_conn *conn, struct msgb *msg),
|
||||
int (*closed_cb)(struct ipa_server_conn *conn), void *data)
|
||||
{
|
||||
struct ipa_server_conn *conn;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
conn = talloc_zero(ctx, struct ipa_server_conn);
|
||||
if (conn == NULL) {
|
||||
LOGP(DLINP, LOGL_ERROR, "cannot allocate new peer in server, "
|
||||
"reason=`%s'\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
peer->server = link;
|
||||
peer->ofd.fd = fd;
|
||||
peer->ofd.data = peer;
|
||||
peer->ofd.cb = ipa_server_peer_cb;
|
||||
peer->ofd.when = BSC_FD_READ;
|
||||
peer->cb = cb;
|
||||
peer->data = data;
|
||||
INIT_LLIST_HEAD(&peer->tx_queue);
|
||||
conn->server = link;
|
||||
osmo_fd_setup(&conn->ofd, fd, OSMO_FD_READ, ipa_server_conn_cb, conn, 0);
|
||||
conn->cb = cb;
|
||||
conn->closed_cb = closed_cb;
|
||||
conn->data = data;
|
||||
INIT_LLIST_HEAD(&conn->tx_queue);
|
||||
|
||||
if (osmo_fd_register(&peer->ofd) < 0) {
|
||||
if (!getpeername(fd, (struct sockaddr *)&sa, &sa_len)) {
|
||||
char *str = inet_ntoa(sa.sin_addr);
|
||||
conn->addr = talloc_strdup(conn, str);
|
||||
conn->port = ntohs(sa.sin_port);
|
||||
}
|
||||
|
||||
if (osmo_fd_register(&conn->ofd) < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "could not register FD\n");
|
||||
talloc_free(peer);
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
return peer;
|
||||
return conn;
|
||||
}
|
||||
|
||||
void ipa_server_peer_destroy(struct ipa_server_peer *peer)
|
||||
int ipa_server_conn_ccm(struct ipa_server_conn *conn, struct msgb *msg)
|
||||
{
|
||||
close(peer->ofd.fd);
|
||||
osmo_fd_unregister(&peer->ofd);
|
||||
talloc_free(peer);
|
||||
struct tlv_parsed tlvp;
|
||||
uint8_t msg_type = *(msg->l2h);
|
||||
struct ipaccess_unit unit_data = {};
|
||||
char *unitid;
|
||||
int len, rc;
|
||||
|
||||
/* shared CCM handling on both server and client */
|
||||
rc = ipa_ccm_rcvmsg_base(msg, &conn->ofd);
|
||||
switch (rc) {
|
||||
case -1:
|
||||
/* error in IPA CCM processing */
|
||||
goto err;
|
||||
case 1:
|
||||
/* IPA CCM message that was handled in _base */
|
||||
return 0;
|
||||
case 0:
|
||||
/* IPA CCM message that we need to handle */
|
||||
break;
|
||||
default:
|
||||
/* Error */
|
||||
LOGIPA(conn, LOGL_ERROR, "Unexpected return from "
|
||||
"ipa_ccm_rcvmsg_base: %d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (msg_type) {
|
||||
case IPAC_MSGT_ID_RESP:
|
||||
rc = ipa_ccm_id_resp_parse(&tlvp, (const uint8_t *)msg->l2h+1, msgb_l2len(msg)-1);
|
||||
if (rc < 0) {
|
||||
LOGIPA(conn, LOGL_ERROR, "IPA CCM RESPonse with "
|
||||
"malformed TLVs\n");
|
||||
goto err;
|
||||
}
|
||||
if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
|
||||
LOGIPA(conn, LOGL_ERROR, "IPA CCM RESP without "
|
||||
"unit ID\n");
|
||||
goto err;
|
||||
}
|
||||
len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT);
|
||||
if (len < 1) {
|
||||
LOGIPA(conn, LOGL_ERROR, "IPA CCM RESP with short"
|
||||
"unit ID\n");
|
||||
goto err;
|
||||
}
|
||||
unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT);
|
||||
unitid[len-1] = '\0';
|
||||
ipa_parse_unitid(unitid, &unit_data);
|
||||
|
||||
/* FIXME */
|
||||
rc = conn->ccm_cb(conn, msg, &tlvp, &unit_data);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
break;
|
||||
default:
|
||||
LOGIPA(conn, LOGL_ERROR, "Unknown IPA message type\n");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
/* in case of any error, we close the connection */
|
||||
ipa_server_conn_destroy(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ipa_server_peer_send(struct ipa_server_peer *peer, struct msgb *msg)
|
||||
void ipa_server_conn_destroy(struct ipa_server_conn *conn)
|
||||
{
|
||||
msgb_enqueue(&peer->tx_queue, msg);
|
||||
peer->ofd.when |= BSC_FD_WRITE;
|
||||
/* make the function re-entrant in case closed_cb() below somehow
|
||||
* calls again into this destructor */
|
||||
if (conn->ofd.fd == -1)
|
||||
return;
|
||||
osmo_fd_unregister(&conn->ofd);
|
||||
close(conn->ofd.fd);
|
||||
conn->ofd.fd = -1;
|
||||
msgb_free(conn->pending_msg);
|
||||
if (conn->closed_cb)
|
||||
conn->closed_cb(conn);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
void ipa_server_conn_send(struct ipa_server_conn *conn, struct msgb *msg)
|
||||
{
|
||||
msgb_enqueue(&conn->tx_queue, msg);
|
||||
osmo_fd_write_enable(&conn->ofd);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,337 @@
|
|||
/* IPA keep-alive FSM; Periodically transmit IPA_PING and expect IPA_PONG in return.
|
||||
*
|
||||
* (C) 2019 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
|
||||
/* generate a msgb containing an IPA CCM PING message */
|
||||
static struct msgb *gen_ipa_ping(void)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(64, 32, "IPA PING");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msgb_put_u8(msg, IPAC_MSGT_PING);
|
||||
ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
enum osmo_ipa_keepalive_state {
|
||||
OSMO_IPA_KA_S_INIT,
|
||||
OSMO_IPA_KA_S_IDLE, /* waiting for next interval */
|
||||
OSMO_IPA_KA_S_WAIT_RESP, /* waiting for response to keepalive */
|
||||
};
|
||||
|
||||
enum osmo_ipa_keepalive_event {
|
||||
OSMO_IPA_KA_E_START,
|
||||
OSMO_IPA_KA_E_STOP,
|
||||
OSMO_IPA_KA_E_PONG,
|
||||
};
|
||||
|
||||
static const struct value_string ipa_keepalive_event_names[] = {
|
||||
OSMO_VALUE_STRING(OSMO_IPA_KA_E_START),
|
||||
OSMO_VALUE_STRING(OSMO_IPA_KA_E_STOP),
|
||||
OSMO_VALUE_STRING(OSMO_IPA_KA_E_PONG),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum ipa_fsm_timer {
|
||||
T_SEND_NEXT_PING = 1,
|
||||
T_PONG_NOT_RECEIVED = 2,
|
||||
};
|
||||
|
||||
struct ipa_fsm_priv {
|
||||
struct ipa_keepalive_params params;
|
||||
|
||||
struct ipa_server_conn *srv_conn;
|
||||
struct ipa_client_conn *client_conn;
|
||||
void *generic;
|
||||
ipa_keepalive_timeout_cb_t *timeout_cb;
|
||||
ipa_keepalive_send_cb_t *send_fn;
|
||||
};
|
||||
|
||||
static void ipa_ka_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
case OSMO_IPA_KA_E_START:
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_WAIT_RESP,
|
||||
ifp->params.wait_for_resp, T_PONG_NOT_RECEIVED);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_ka_wait_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
struct msgb *msg;
|
||||
|
||||
/* Send an IPA PING to the peer */
|
||||
msg = gen_ipa_ping();
|
||||
OSMO_ASSERT(msg);
|
||||
|
||||
if (ifp->send_fn && ifp->generic) {
|
||||
ifp->send_fn(fi, ifp->generic, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ifp->srv_conn) {
|
||||
if (ifp->send_fn)
|
||||
ifp->send_fn(fi, ifp->srv_conn, msg);
|
||||
else
|
||||
ipa_server_conn_send(ifp->srv_conn, msg);
|
||||
}
|
||||
else {
|
||||
OSMO_ASSERT(ifp->client_conn);
|
||||
if (ifp->send_fn)
|
||||
ifp->send_fn(fi, ifp->client_conn, msg);
|
||||
else
|
||||
ipa_client_conn_send(ifp->client_conn, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_ka_wait_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
case OSMO_IPA_KA_E_PONG:
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_IDLE,
|
||||
ifp->params.interval, T_SEND_NEXT_PING);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int ipa_ka_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
void *conn;
|
||||
|
||||
switch (fi->T) {
|
||||
case T_SEND_NEXT_PING:
|
||||
/* send another PING */
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_WAIT_RESP,
|
||||
ifp->params.wait_for_resp, T_PONG_NOT_RECEIVED);
|
||||
return 0;
|
||||
case T_PONG_NOT_RECEIVED:
|
||||
LOGPFSML(fi, LOGL_NOTICE, "IPA keep-alive FSM timed out: PONG not received\n");
|
||||
/* PONG not received within time */
|
||||
if (ifp->srv_conn)
|
||||
conn = ifp->srv_conn;
|
||||
else if (ifp->client_conn)
|
||||
conn = ifp->client_conn;
|
||||
else
|
||||
conn = ifp->generic;
|
||||
if (ifp->timeout_cb)
|
||||
return ifp->timeout_cb(fi, conn);
|
||||
/* ask fsm core to terminate us */
|
||||
return 1;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipa_ka_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case OSMO_IPA_KA_E_STOP:
|
||||
osmo_fsm_inst_state_chg(fi, OSMO_IPA_KA_S_INIT, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state ipa_keepalive_states[] = {
|
||||
[OSMO_IPA_KA_S_INIT] = {
|
||||
.name = "INIT",
|
||||
.in_event_mask = S(OSMO_IPA_KA_E_START),
|
||||
.out_state_mask = S(OSMO_IPA_KA_S_WAIT_RESP) | S(OSMO_IPA_KA_S_INIT),
|
||||
.action = ipa_ka_init,
|
||||
},
|
||||
[OSMO_IPA_KA_S_IDLE] = {
|
||||
.name = "IDLE",
|
||||
.out_state_mask = S(OSMO_IPA_KA_S_WAIT_RESP) | S(OSMO_IPA_KA_S_INIT),
|
||||
/* no permitted events aside from E_START, which is handled in allstate_events */
|
||||
},
|
||||
[OSMO_IPA_KA_S_WAIT_RESP] = {
|
||||
.name = "WAIT_RESP",
|
||||
.in_event_mask = S(OSMO_IPA_KA_E_PONG),
|
||||
.out_state_mask = S(OSMO_IPA_KA_S_IDLE) | S(OSMO_IPA_KA_S_INIT),
|
||||
.action = ipa_ka_wait_resp,
|
||||
.onenter = ipa_ka_wait_resp_onenter,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm ipa_keepalive_fsm = {
|
||||
.name = "IPA-KEEPALIVE",
|
||||
.states = ipa_keepalive_states,
|
||||
.num_states = ARRAY_SIZE(ipa_keepalive_states),
|
||||
.log_subsys = DLINP,
|
||||
.allstate_event_mask = S(OSMO_IPA_KA_E_STOP),
|
||||
.allstate_action = ipa_ka_allstate_action,
|
||||
.event_names = ipa_keepalive_event_names,
|
||||
.timer_cb = ipa_ka_fsm_timer_cb,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&ipa_keepalive_fsm) == 0);
|
||||
}
|
||||
|
||||
|
||||
static struct osmo_fsm_inst *
|
||||
__ipa_conn_alloc_keepalive_fsm(void *ctx, const struct ipa_keepalive_params *params, const char *id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_fsm_priv *ifp;
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&ipa_keepalive_fsm, ctx, NULL, LOGL_DEBUG, id);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
ifp = talloc_zero(fi, struct ipa_fsm_priv);
|
||||
if (!ifp) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&ifp->params, params, sizeof(ifp->params));
|
||||
fi->priv = ifp;
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! Create a new instance of an IPA keepalive FSM: Periodically transmit PING and expect PONG.
|
||||
* \param[in] client The client connection for which to crate the FSM. Used as talloc context.
|
||||
* \param[in] params Parameters describing the keepalive FSM time-outs.
|
||||
* \param[in] id String used as identifier for the FSM.
|
||||
* \returns pointer to the newly-created FSM instance; NULL in case of error. */
|
||||
struct osmo_fsm_inst *ipa_client_conn_alloc_keepalive_fsm(struct ipa_client_conn *client,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_fsm_priv *ifp;
|
||||
|
||||
fi = __ipa_conn_alloc_keepalive_fsm(client, params, id);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
ifp = fi->priv;
|
||||
ifp->client_conn = client;
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! Create a new instance of an IPA keepalive FSM: Periodically transmit PING and expect PONG.
|
||||
* \param[in] server The server connection for which to crate the FSM. Used as talloc context.
|
||||
* \param[in] params Parameters describing the keepalive FSM time-outs.
|
||||
* \param[in] id String used as identifier for the FSM.
|
||||
* \returns pointer to the newly-created FSM instance; NULL in case of error. */
|
||||
struct osmo_fsm_inst *ipa_server_conn_alloc_keepalive_fsm(struct ipa_server_conn *server,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_fsm_priv *ifp;
|
||||
|
||||
fi = __ipa_conn_alloc_keepalive_fsm(server, params, id);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
ifp = fi->priv;
|
||||
ifp->srv_conn = server;
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! Create a new instance of an IPA keepalive FSM: Periodically transmit PING and expect PONG.
|
||||
* \param[in] ctx Talloc context.
|
||||
* \param[in] data Data to pass to write/timeout cb.
|
||||
* \param[in] params Parameters describing the keepalive FSM time-outs.
|
||||
* \param[in] id String used as identifier for the FSM.
|
||||
* \returns pointer to the newly-created FSM instance; NULL in case of error. */
|
||||
struct osmo_fsm_inst *ipa_generic_conn_alloc_keepalive_fsm(void *ctx, void* data,
|
||||
const struct ipa_keepalive_params *params,
|
||||
const char *id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ipa_fsm_priv *ifp;
|
||||
|
||||
fi = __ipa_conn_alloc_keepalive_fsm(ctx, params, id);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
ifp = fi->priv;
|
||||
ifp->generic = data;
|
||||
return fi;
|
||||
}
|
||||
|
||||
/*! Set a timeout call-back which is to be called once the peer doesn't respond anymore */
|
||||
void ipa_keepalive_fsm_set_timeout_cb(struct osmo_fsm_inst *fi, ipa_keepalive_timeout_cb_t *cb)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
ifp->timeout_cb = cb;
|
||||
}
|
||||
|
||||
/*! Set a custom send callback for sending pings */
|
||||
void ipa_keepalive_fsm_set_send_cb(struct osmo_fsm_inst *fi, ipa_keepalive_send_cb_t *fn)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
ifp->send_fn = fn;
|
||||
}
|
||||
|
||||
/*! Inform IPA Keepalive FSM that a PONG has been received. */
|
||||
void ipa_keepalive_fsm_pong_received(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
osmo_fsm_inst_dispatch(fi, OSMO_IPA_KA_E_PONG, NULL);
|
||||
}
|
||||
|
||||
/*! Start the ping/pong procedure of the IPA Keepalive FSM. */
|
||||
void ipa_keepalive_fsm_start(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct ipa_fsm_priv *ifp = fi->priv;
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
LOGPFSML(fi, LOGL_INFO, "Starting IPA keep-alive FSM (interval=%us wait=%us)\n",
|
||||
ifp->params.interval, ifp->params.wait_for_resp);
|
||||
osmo_fsm_inst_dispatch(fi, OSMO_IPA_KA_E_START, NULL);
|
||||
}
|
||||
|
||||
/*! Stop the ping/pong procedure of the IPA Keepalive FSM. */
|
||||
void ipa_keepalive_fsm_stop(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
OSMO_ASSERT(fi->fsm == &ipa_keepalive_fsm);
|
||||
LOGPFSML(fi, LOGL_INFO, "Stopping IPA keep-alive FSM\n");
|
||||
osmo_fsm_inst_dispatch(fi, OSMO_IPA_KA_E_STOP, NULL);
|
||||
}
|
1210
src/input/ipaccess.c
1210
src/input/ipaccess.c
File diff suppressed because it is too large
Load Diff
1000
src/input/lapd.c
1000
src/input/lapd.c
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,178 @@
|
|||
/* (C) 2008-2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Harald Welte <laforge@gnumonks.org>
|
||||
* Pablo Neira Ayuso <pablo@gnumonks.org>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/abis/lapd_pcap.h>
|
||||
|
||||
/*
|
||||
* pcap writing of the mlapd load
|
||||
* pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
*/
|
||||
#define DLT_LINUX_LAPD 177
|
||||
#define LINUX_SLL_HOST 0
|
||||
#define LINUX_SLL_OUTGOING 4
|
||||
|
||||
struct pcap_hdr {
|
||||
uint32_t magic_number;
|
||||
uint16_t version_major;
|
||||
uint16_t version_minor;
|
||||
int32_t thiszone;
|
||||
uint32_t sigfigs;
|
||||
uint32_t snaplen;
|
||||
uint32_t network;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcap_rechdr {
|
||||
uint32_t ts_sec;
|
||||
uint32_t ts_usec;
|
||||
uint32_t incl_len;
|
||||
uint32_t orig_len;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcap_lapdhdr {
|
||||
uint16_t pkttype;
|
||||
uint16_t hatype;
|
||||
uint16_t halen;
|
||||
uint8_t addr[8];
|
||||
int16_t protocol;
|
||||
} __attribute__((packed));
|
||||
|
||||
osmo_static_assert(offsetof(struct pcap_lapdhdr, hatype) == 2, hatype_offset);
|
||||
osmo_static_assert(offsetof(struct pcap_lapdhdr, halen) == 4, halen_offset);
|
||||
osmo_static_assert(offsetof(struct pcap_lapdhdr, addr) == 6, addr_offset);
|
||||
osmo_static_assert(offsetof(struct pcap_lapdhdr, protocol) == 14, proto_offset);
|
||||
osmo_static_assert(sizeof(struct pcap_lapdhdr) == 16, lapd_header_size);
|
||||
|
||||
int osmo_pcap_lapd_set_fd(int fd)
|
||||
{
|
||||
struct pcap_hdr pcap_header = {
|
||||
.magic_number = 0xa1b2c3d4,
|
||||
.version_major = 2,
|
||||
.version_minor = 4,
|
||||
.thiszone = 0,
|
||||
.sigfigs = 0,
|
||||
.snaplen = 65535,
|
||||
.network = DLT_LINUX_LAPD,
|
||||
};
|
||||
|
||||
if (write(fd, &pcap_header, sizeof(pcap_header))
|
||||
!= sizeof(pcap_header)) {
|
||||
LOGP(DLLAPD, LOGL_ERROR, "cannot write PCAP header: %s\n",
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_pcap_lapd_open(char *filename, mode_t mode)
|
||||
{
|
||||
int fd, rc;
|
||||
|
||||
LOGP(DLLAPD, LOGL_NOTICE, "opening LAPD pcap file `%s'\n", filename);
|
||||
|
||||
fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, mode);
|
||||
if (fd < 0) {
|
||||
LOGP(DLLAPD, LOGL_ERROR, "failed to open PCAP file: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = osmo_pcap_lapd_set_fd(fd);
|
||||
if (rc < 0) {
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* This currently only works for the D-Channel */
|
||||
int osmo_pcap_lapd_write(int fd, int direction, struct msgb *msg)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tmp_pkt {
|
||||
struct pcap_rechdr pcap_rechdr;
|
||||
struct pcap_lapdhdr header;
|
||||
char buf[0];
|
||||
} __attribute__((packed));
|
||||
const unsigned int total_pkt_len = sizeof(struct tmp_pkt) + msg->len;
|
||||
|
||||
struct tmp_pkt *s = alloca(total_pkt_len);
|
||||
|
||||
/* PCAP file has not been opened, skip. */
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
memset(s, 0, total_pkt_len);
|
||||
|
||||
s->pcap_rechdr.ts_sec = 0;
|
||||
s->pcap_rechdr.ts_usec = 0;
|
||||
s->pcap_rechdr.incl_len = msg->len + sizeof(struct pcap_lapdhdr);
|
||||
s->pcap_rechdr.orig_len = msg->len + sizeof(struct pcap_lapdhdr);
|
||||
|
||||
if (direction == OSMO_LAPD_PCAP_OUTPUT)
|
||||
s->header.pkttype = htons(LINUX_SLL_OUTGOING);
|
||||
else
|
||||
s->header.pkttype = htons(LINUX_SLL_HOST);
|
||||
s->header.hatype = 0;
|
||||
s->header.halen = 0;
|
||||
s->header.addr[0] = 0x01; /* we are the network side */
|
||||
s->header.protocol = ntohs(48);
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
s->pcap_rechdr.ts_sec = tv.tv_sec;
|
||||
s->pcap_rechdr.ts_usec = tv.tv_usec;
|
||||
|
||||
memcpy(s->buf, msg->data, msg->len);
|
||||
|
||||
if (write(fd, s, total_pkt_len) != total_pkt_len) {
|
||||
LOGP(DLLAPD, LOGL_ERROR, "cannot write packet to PCAP: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return total_pkt_len;
|
||||
}
|
||||
|
||||
int osmo_pcap_lapd_close(int fd)
|
||||
{
|
||||
LOGP(DLLAPD, LOGL_NOTICE, "closing LAPD pcap file\n");
|
||||
return close(fd);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,8 @@
|
|||
* Authors: Harald Welte <laforge@gnumonks.org>
|
||||
* Pablo Neira Ayuso <pablo@gnumonks.org>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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
|
||||
|
@ -84,7 +86,7 @@ static int handle_ser_write(struct osmo_fd *bfd)
|
|||
struct msgb *msg;
|
||||
int written;
|
||||
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
osmo_fd_write_disable(bfd);
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, &sign_link);
|
||||
|
@ -92,14 +94,14 @@ static int handle_ser_write(struct osmo_fd *bfd)
|
|||
/* no message after tx delay timer */
|
||||
return 0;
|
||||
}
|
||||
DEBUGP(DLMI, "rs232 TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "rs232 TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
rs232_build_msg(msg);
|
||||
|
||||
/* send over serial line */
|
||||
written = write(bfd->fd, msg->data, msg->len);
|
||||
if (written < msg->len) {
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: short write\n");
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "rs232: short write\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
@ -116,6 +118,7 @@ static int handle_ser_write(struct osmo_fd *bfd)
|
|||
static int handle_ser_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct serial_handle *sh = bfd->data;
|
||||
struct e1inp_ts *e1i_ts = &sh->line->ts[0];
|
||||
struct msgb *msg;
|
||||
int rc = 0;
|
||||
|
||||
|
@ -129,7 +132,7 @@ static int handle_ser_read(struct osmo_fd *bfd)
|
|||
if (msg->len < 2) {
|
||||
rc = read(bfd->fd, msg->tail, 2 - msg->len);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: error reading from "
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "rs232: error reading from "
|
||||
"serial port: %s\n", strerror(errno));
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
|
@ -139,16 +142,14 @@ static int handle_ser_read(struct osmo_fd *bfd)
|
|||
if (msg->len >= 2) {
|
||||
/* parse CRAPD payload length */
|
||||
if (msg->data[0] != 0) {
|
||||
LOGP(DLMI, LOGL_ERROR,
|
||||
"Suspicious header byte 0: 0x%02x\n",
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "Suspicious header byte 0: 0x%02x\n",
|
||||
msg->data[0]);
|
||||
}
|
||||
sh->rxmsg_bytes_missing = msg->data[0] << 8;
|
||||
sh->rxmsg_bytes_missing += msg->data[1];
|
||||
|
||||
if (sh->rxmsg_bytes_missing < CRAPD_HDR_LEN -2) {
|
||||
LOGP(DLMI, LOGL_ERROR,
|
||||
"Invalid length in hdr: %u\n",
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "Invalid length in hdr: %u\n",
|
||||
sh->rxmsg_bytes_missing);
|
||||
}
|
||||
}
|
||||
|
@ -156,8 +157,8 @@ static int handle_ser_read(struct osmo_fd *bfd)
|
|||
/* try to read as many of the missing bytes as are available */
|
||||
rc = read(bfd->fd, msg->tail, sh->rxmsg_bytes_missing);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: error reading from "
|
||||
"serial port: %s", strerror(errno));
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "rs232: error reading from serial port: %s",
|
||||
strerror(errno));
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
@ -165,7 +166,6 @@ static int handle_ser_read(struct osmo_fd *bfd)
|
|||
sh->rxmsg_bytes_missing -= rc;
|
||||
|
||||
if (sh->rxmsg_bytes_missing == 0) {
|
||||
struct e1inp_ts *e1i_ts = &sh->line->ts[0];
|
||||
|
||||
/* we have one complete message now */
|
||||
sh->rx_msg = NULL;
|
||||
|
@ -173,15 +173,13 @@ static int handle_ser_read(struct osmo_fd *bfd)
|
|||
if (msg->len > CRAPD_HDR_LEN)
|
||||
msg->l2h = msg->data + CRAPD_HDR_LEN;
|
||||
|
||||
DEBUGP(DLMI, "rs232 RX: %s",
|
||||
osmo_hexdump(msg->data, msg->len));
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "rs232 RX: %s", osmo_hexdump(msg->data, msg->len));
|
||||
|
||||
/* don't use e1inp_tx_ts() here, this header does not
|
||||
* contain any SAPI and TEI values. */
|
||||
if (!e1i_ts->line->ops->sign_link) {
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: no callback set, "
|
||||
"skipping message.\n");
|
||||
return -EINVAL;
|
||||
LOGPITS(e1i_ts, DLMI, LOGL_ERROR, "rs232: no callback set, skipping message.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
e1i_ts->line->ops->sign_link(msg);
|
||||
}
|
||||
|
@ -195,13 +193,13 @@ static int serial_fd_cb(struct osmo_fd *bfd, unsigned int what)
|
|||
{
|
||||
int rc = 0;
|
||||
|
||||
if (what & BSC_FD_READ)
|
||||
if (what & OSMO_FD_READ)
|
||||
rc = handle_ser_read(bfd);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (what & BSC_FD_WRITE)
|
||||
if (what & OSMO_FD_WRITE)
|
||||
rc = handle_ser_write(bfd);
|
||||
|
||||
return rc;
|
||||
|
@ -209,7 +207,7 @@ static int serial_fd_cb(struct osmo_fd *bfd, unsigned int what)
|
|||
|
||||
static int rs232_want_write(struct e1inp_ts *e1i_ts)
|
||||
{
|
||||
e1i_ts->driver.rs232.fd.when |= BSC_FD_WRITE;
|
||||
osmo_fd_write_enable(&e1i_ts->driver.rs232.fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -224,8 +222,7 @@ rs232_setup(struct e1inp_line *line, const char *serial_port, unsigned int delay
|
|||
|
||||
rc = open(serial_port, O_RDWR);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: cannot open serial port: %s",
|
||||
strerror(errno));
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "rs232: cannot open serial port: %s", strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
bfd->fd = rc;
|
||||
|
@ -233,8 +230,7 @@ rs232_setup(struct e1inp_line *line, const char *serial_port, unsigned int delay
|
|||
/* set baudrate */
|
||||
rc = tcgetattr(bfd->fd, &tio);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: tcgetattr says: %s",
|
||||
strerror(errno));
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "rs232: tcgetattr says: %s", strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
cfsetispeed(&tio, B19200);
|
||||
|
@ -247,30 +243,25 @@ rs232_setup(struct e1inp_line *line, const char *serial_port, unsigned int delay
|
|||
tio.c_oflag &= ~(OPOST);
|
||||
rc = tcsetattr(bfd->fd, TCSADRAIN, &tio);
|
||||
if (rc < 0) {
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: tcsetattr says: %s",
|
||||
strerror(errno));
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "rs232: tcsetattr says: %s", strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
ser_handle = talloc_zero(tall_rs232_ctx, struct serial_handle);
|
||||
if (ser_handle == NULL) {
|
||||
close(bfd->fd);
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: cannot allocate memory for "
|
||||
"serial handler\n");
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "rs232: cannot allocate memory for serial handler\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ser_handle->line = line;
|
||||
ser_handle->delay_ms = delay_ms;
|
||||
|
||||
bfd->when = BSC_FD_READ;
|
||||
bfd->cb = serial_fd_cb;
|
||||
bfd->data = ser_handle;
|
||||
osmo_fd_setup(bfd, bfd->fd, OSMO_FD_READ, serial_fd_cb, ser_handle, 0);
|
||||
|
||||
rc = osmo_fd_register(bfd);
|
||||
if (rc < 0) {
|
||||
close(bfd->fd);
|
||||
LOGP(DLMI, LOGL_ERROR, "rs232: could not register FD: %s\n",
|
||||
strerror(rc));
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "rs232: could not register FD: %s\n", strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
/* OpenBSC Abis receive lapd over a unix socket */
|
||||
|
||||
/* (C) 2016 by sysmocom - s.f.m.c. GmbH
|
||||
* Author: Alexander Couzens <lynxis@fe80.eu>
|
||||
* Based on other e1_input drivers.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/lapd.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
||||
#include <osmocom/abis/unixsocket_proto.h>
|
||||
#include "internal.h"
|
||||
|
||||
void *tall_unixsocket_ctx;
|
||||
#define UNIXSOCKET_ALLOC_SIZE 1600
|
||||
#define UNIXSOCKET_SOCK_PATH_DEFAULT "/tmp/osmo_abis_line_"
|
||||
|
||||
struct unixsocket_line {
|
||||
struct osmo_fd fd;
|
||||
};
|
||||
|
||||
static int unixsocket_line_update(struct e1inp_line *line);
|
||||
static int ts_want_write(struct e1inp_ts *e1i_ts);
|
||||
|
||||
static int unixsocket_exception_cb(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "Socket connection failure, reconnecting... (line=%p, fd=%d)\n",
|
||||
line, bfd->fd);
|
||||
|
||||
/* Unregister faulty file descriptor from select loop */
|
||||
if(osmo_fd_is_registered(bfd)) {
|
||||
LOGPIL(line, DLINP, LOGL_DEBUG, "removing inactive socket from select loop... (line=%p, fd=%d)\n",
|
||||
line, bfd->fd);
|
||||
osmo_fd_unregister(bfd);
|
||||
}
|
||||
|
||||
/* Close faulty file descriptor */
|
||||
close(bfd->fd);
|
||||
|
||||
unixsocket_line_update(line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unixsocket_read_cb(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
struct msgb *msg = msgb_alloc(UNIXSOCKET_ALLOC_SIZE, "UNIXSOCKET TS");
|
||||
uint8_t version;
|
||||
uint8_t controldata;
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = read(bfd->fd, msg->data, UNIXSOCKET_ALLOC_SIZE - 16);
|
||||
if (ret == 0) {
|
||||
unixsocket_exception_cb(bfd);
|
||||
goto fail;
|
||||
} else if (ret < 0) {
|
||||
perror("read ");
|
||||
goto fail;
|
||||
} else if (ret < 2) {
|
||||
/* packet must be at least 2 byte long to hold version + control/data header */
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "received to small packet: %d < 2", ret);
|
||||
ret = -1;
|
||||
goto fail;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
|
||||
LOGPIL(line, DLMI, LOGL_DEBUG, "rx msg: %s (fd=%d)\n", osmo_hexdump_nospc(msg->data, msg->len), bfd->fd);
|
||||
|
||||
/* check version header */
|
||||
version = msgb_pull_u8(msg);
|
||||
controldata = msgb_pull_u8(msg);
|
||||
|
||||
if (version != UNIXSOCKET_PROTO_VERSION) {
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "received message with invalid version %d. valid: %d",
|
||||
ret, UNIXSOCKET_PROTO_VERSION);
|
||||
ret = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch (controldata) {
|
||||
case UNIXSOCKET_PROTO_DATA:
|
||||
return e1inp_rx_ts_lapd(&line->ts[0], msg);
|
||||
case UNIXSOCKET_PROTO_CONTROL:
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "received (invalid) control message.");
|
||||
ret = -1;
|
||||
break;
|
||||
default:
|
||||
LOGPIL(line, DLMI, LOGL_ERROR, "received invalid message.");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
fail:
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int unixsocket_write_cb(struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[0];
|
||||
struct msgb *msg;
|
||||
struct e1inp_sign_link *sign_link;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, &sign_link);
|
||||
if (!msg) {
|
||||
osmo_fd_write_disable(bfd);
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_INFO, "no message available (line=%p)\n", line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGPITS(e1i_ts, DLINP, LOGL_DEBUG, "sending: %s (line=%p)\n", msgb_hexdump(msg), line);
|
||||
lapd_transmit(e1i_ts->lapd, sign_link->tei,
|
||||
sign_link->sapi, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unixsocket_cb(struct osmo_fd *bfd, unsigned int what)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (what & OSMO_FD_READ)
|
||||
ret = unixsocket_read_cb(bfd);
|
||||
if (what & OSMO_FD_WRITE)
|
||||
ret = unixsocket_write_cb(bfd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ts_want_write(struct e1inp_ts *e1i_ts)
|
||||
{
|
||||
struct unixsocket_line *line = e1i_ts->line->driver_data;
|
||||
|
||||
osmo_fd_write_enable(&line->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unixsocket_write_msg(struct msgb *msg, struct osmo_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
int ret;
|
||||
|
||||
LOGPIL(line, DLMI, LOGL_DEBUG, "tx msg: %s (fd=%d)\n",
|
||||
osmo_hexdump_nospc(msg->data, msg->len), bfd->fd);
|
||||
|
||||
ret = write(bfd->fd, msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
if (ret == -1)
|
||||
unixsocket_exception_cb(bfd);
|
||||
else if (ret < 0)
|
||||
LOGPIL(line, DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief unixsocket_write_msg lapd callback for data to unixsocket
|
||||
* \param msg
|
||||
* \param cbdata
|
||||
*/
|
||||
static void unixsocket_write_msg_lapd_cb(struct msgb *msg, void *cbdata)
|
||||
{
|
||||
struct osmo_fd *bfd = cbdata;
|
||||
|
||||
/* data|control */
|
||||
msgb_push_u8(msg, UNIXSOCKET_PROTO_DATA);
|
||||
/* add version header */
|
||||
msgb_push_u8(msg, UNIXSOCKET_PROTO_VERSION);
|
||||
|
||||
unixsocket_write_msg(msg, bfd);
|
||||
}
|
||||
|
||||
static int unixsocket_line_update(struct e1inp_line *line)
|
||||
{
|
||||
struct unixsocket_line *config;
|
||||
struct sockaddr_un un;
|
||||
const char *sock_path;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
LOGPIL(line, DLINP, LOGL_NOTICE, "line update (line=%p)\n", line);
|
||||
|
||||
if (!line->driver_data)
|
||||
line->driver_data = talloc_zero(line, struct unixsocket_line);
|
||||
|
||||
if (!line->driver_data) {
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "OOM in line update (line=%p)\n", line);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
config = line->driver_data;
|
||||
|
||||
/* Open unix domain socket */
|
||||
if (line->sock_path == NULL) {
|
||||
ret = snprintf(un.sun_path, sizeof(un.sun_path), "%s%d",
|
||||
UNIXSOCKET_SOCK_PATH_DEFAULT, line->num);
|
||||
if (ret == -1) {
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "Cannot create default socket path: %s\n", strerror(errno));
|
||||
return -errno;
|
||||
} else if (ret >= sizeof(un.sun_path)) {
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "Default socket path exceeds %zd bytes: %s%d\n",
|
||||
sizeof(un.sun_path), UNIXSOCKET_SOCK_PATH_DEFAULT, line->num);
|
||||
return -ENOSPC;
|
||||
}
|
||||
sock_path = un.sun_path;
|
||||
} else
|
||||
sock_path = line->sock_path;
|
||||
ret = osmo_sock_unix_init(SOCK_SEQPACKET, 0, sock_path,
|
||||
OSMO_SOCK_F_CONNECT);
|
||||
if (ret < 0) {
|
||||
/* Note: We will not free the allocated driver_data memory if
|
||||
* opening the socket fails. The caller may want to call this
|
||||
* function multiple times using config->fd.data as line
|
||||
* parameter. Freeing now would destroy that reference. */
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "unable to open socket: %s (line=%p, fd=%d)\n", sock_path,
|
||||
line, config->fd.fd);
|
||||
return ret;
|
||||
}
|
||||
LOGPIL(line, DLINP, LOGL_DEBUG, "successfully opend (new) socket: %s (line=%p, fd=%d, ret=%d)\n",
|
||||
sock_path, line, config->fd.fd, ret);
|
||||
osmo_fd_setup(&config->fd, ret, OSMO_FD_READ, unixsocket_cb, line, 0);
|
||||
|
||||
/* Register socket in select loop */
|
||||
if (osmo_fd_register(&config->fd) < 0) {
|
||||
LOGPIL(line, DLINP, LOGL_ERROR, "error registering new socket (line=%p, fd=%d)\n",
|
||||
line, config->fd.fd);
|
||||
close(config->fd.fd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set line parameter */
|
||||
for (i = 0; i < ARRAY_SIZE(line->ts); i++) {
|
||||
struct e1inp_ts *e1i_ts = &line->ts[i];
|
||||
char name[32];
|
||||
if (!e1i_ts->lapd) {
|
||||
e1inp_ts_name(name, sizeof(name), e1i_ts);
|
||||
e1i_ts->lapd = lapd_instance_alloc2(1,
|
||||
unixsocket_write_msg_lapd_cb, &config->fd,
|
||||
e1inp_dlsap_up, e1i_ts, &lapd_profile_abis, name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure ericsson-superchannel is turned of when
|
||||
* a new connection is made */
|
||||
e1inp_ericsson_set_altc(line, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct e1inp_driver unixsocket_driver = {
|
||||
.name = "unixsocket",
|
||||
.want_write = ts_want_write,
|
||||
.line_update = unixsocket_line_update,
|
||||
.default_delay = 0,
|
||||
};
|
||||
|
||||
void e1inp_unixsocket_init(void)
|
||||
{
|
||||
tall_unixsocket_ctx = talloc_named_const(libosmo_abis_ctx, 1, "unixsocket");
|
||||
e1inp_driver_register(&unixsocket_driver);
|
||||
}
|
||||
|
||||
void e1inp_ericsson_set_altc(struct e1inp_line *unixline, int superchannel)
|
||||
{
|
||||
struct unixsocket_line *config;
|
||||
struct msgb *msg;
|
||||
|
||||
if (!unixline)
|
||||
return;
|
||||
|
||||
if (unixline->driver != &unixsocket_driver) {
|
||||
LOGPIL(unixline, DLMI, LOGL_NOTICE, "altc is only supported by unixsocket\n");
|
||||
return;
|
||||
}
|
||||
|
||||
config = unixline->driver_data;
|
||||
if (!config) {
|
||||
LOGPIL(unixline, DLMI, LOGL_NOTICE, "e1inp driver not yet initialized.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
msg = msgb_alloc_headroom(200, 100, "ALTC");
|
||||
|
||||
/* version header */
|
||||
msgb_put_u8(msg, UNIXSOCKET_PROTO_VERSION);
|
||||
/* data|control */
|
||||
msgb_put_u8(msg, UNIXSOCKET_PROTO_CONTROL);
|
||||
|
||||
/* magic */
|
||||
msgb_put_u32(msg, 0x23004200);
|
||||
msgb_put_u8(msg, superchannel ? 1 : 0);
|
||||
|
||||
unixsocket_write_msg(msg, &config->fd);
|
||||
}
|
||||
|
|
@ -67,13 +67,13 @@ struct ipa_proxy_route_shared {
|
|||
struct ipa_proxy_instance *inst;
|
||||
struct bitvec streamid_map;
|
||||
uint8_t streamid_map_data[(0xff+1)/8];
|
||||
uint8_t streamid[0xff];
|
||||
uint8_t streamid[0xff + 1];
|
||||
} src;
|
||||
struct {
|
||||
struct ipa_proxy_instance *inst;
|
||||
struct bitvec streamid_map;
|
||||
uint8_t streamid_map_data[(0xff+1)/8];
|
||||
uint8_t streamid[0xff];
|
||||
uint8_t streamid[0xff + 1];
|
||||
} dst;
|
||||
|
||||
struct llist_head conn_list;
|
||||
|
@ -104,15 +104,15 @@ enum ipa_conn_state {
|
|||
struct ipa_proxy_conn {
|
||||
struct llist_head head;
|
||||
|
||||
struct ipa_server_peer *src;
|
||||
struct ipa_client_link *dst;
|
||||
struct ipa_server_conn *src;
|
||||
struct ipa_client_conn *dst;
|
||||
struct ipa_proxy_route *route;
|
||||
};
|
||||
|
||||
/*
|
||||
* socket callbacks used by IPA VTY commands
|
||||
*/
|
||||
static int ipa_sock_dst_cb(struct ipa_client_link *link, struct msgb *msg)
|
||||
static int ipa_sock_dst_cb(struct ipa_client_conn *link, struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
struct ipa_proxy_conn *conn = link->data;
|
||||
|
@ -132,11 +132,11 @@ static int ipa_sock_dst_cb(struct ipa_client_link *link, struct msgb *msg)
|
|||
/* mangle message, if required. */
|
||||
hh->proto = conn->route->shared->src.streamid[hh->proto];
|
||||
|
||||
ipa_server_peer_send(conn->src, msg);
|
||||
ipa_server_conn_send(conn->src, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipa_sock_src_cb(struct ipa_server_peer *peer, struct msgb *msg)
|
||||
static int ipa_sock_src_cb(struct ipa_server_conn *peer, struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
struct ipa_proxy_conn *conn = peer->data;
|
||||
|
@ -155,14 +155,13 @@ static int ipa_sock_src_cb(struct ipa_server_peer *peer, struct msgb *msg)
|
|||
/* mangle message, if required. */
|
||||
hh->proto = conn->route->shared->dst.streamid[hh->proto];
|
||||
|
||||
ipa_client_link_send(conn->dst, msg);
|
||||
ipa_client_conn_send(conn->dst, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ipa_sock_src_accept_cb(struct ipa_server_link *link, int fd)
|
||||
{
|
||||
int ret;
|
||||
struct ipa_proxy_route *route = link->data;
|
||||
struct ipa_proxy_conn *conn;
|
||||
|
||||
|
@ -171,12 +170,12 @@ ipa_sock_src_accept_cb(struct ipa_server_link *link, int fd)
|
|||
LOGP(DLINP, LOGL_ERROR, "cannot allocate memory for "
|
||||
"origin IPA\n");
|
||||
close(fd);
|
||||
return ret;
|
||||
return -ENOMEM;
|
||||
}
|
||||
conn->route = route;
|
||||
|
||||
conn->src = ipa_server_peer_create(tall_ipa_proxy_ctx, link, fd,
|
||||
ipa_sock_src_cb, conn);
|
||||
conn->src = ipa_server_conn_create(tall_ipa_proxy_ctx, link, fd,
|
||||
ipa_sock_src_cb, NULL, conn);
|
||||
if (conn->src == NULL) {
|
||||
LOGP(DLINP, LOGL_ERROR, "could not create server peer: %s\n",
|
||||
strerror(errno));
|
||||
|
@ -185,37 +184,31 @@ ipa_sock_src_accept_cb(struct ipa_server_link *link, int fd)
|
|||
|
||||
LOGP(DLINP, LOGL_NOTICE, "now trying to connect to destination\n");
|
||||
|
||||
conn->dst = ipa_client_link_create(NULL, NULL, NULL, 0,
|
||||
conn->dst = ipa_client_conn_create2(NULL, NULL, 0,
|
||||
NULL, 0,
|
||||
route->shared->dst.inst->net.addr,
|
||||
route->shared->dst.inst->net.port,
|
||||
NULL,
|
||||
ipa_sock_dst_cb,
|
||||
ipa_client_write_default_cb,
|
||||
NULL,
|
||||
conn);
|
||||
if (conn->dst == NULL) {
|
||||
LOGP(DLINP, LOGL_ERROR, "could not create client: %s\n",
|
||||
strerror(errno));
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ipa_client_link_open(conn->dst) < 0) {
|
||||
if (ipa_client_conn_open(conn->dst) < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "could not start client: %s\n",
|
||||
strerror(errno));
|
||||
return -ENOMEM;
|
||||
}
|
||||
llist_add(&conn->head, &route->shared->conn_list);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* VTY commands for IPA
|
||||
*/
|
||||
DEFUN(ipa_proxy, ipa_cmd, "ipa", "Configure the ipaccess proxy")
|
||||
{
|
||||
vty->index = NULL;
|
||||
vty->node = L_IPA_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int __ipa_instance_add(struct vty *vty, int argc, const char *argv[])
|
||||
{
|
||||
struct ipa_proxy_instance *ipi;
|
||||
|
@ -253,7 +246,7 @@ static int __ipa_instance_add(struct vty *vty, int argc, const char *argv[])
|
|||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
strncpy(ipi->name, argv[0], IPA_INSTANCE_NAME);
|
||||
osmo_strlcpy(ipi->name, argv[0], sizeof(ipi->name));
|
||||
ipi->net.type = type;
|
||||
ipi->net.addr = talloc_strdup(tall_ipa_proxy_ctx, argv[2]);
|
||||
ipi->net.port = port;
|
||||
|
@ -546,7 +539,7 @@ DEFUN(ipa_route_del, ipa_route_del_cmd,
|
|||
/* nobody else using this route, release all resources. */
|
||||
llist_for_each_entry_safe(conn, tmp,
|
||||
&matching_route->shared->conn_list, head) {
|
||||
ipa_server_peer_destroy(conn->src);
|
||||
ipa_server_conn_destroy(conn->src);
|
||||
llist_del(&conn->head);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
@ -613,7 +606,7 @@ DEFUN(ipa_instance_cfg_add, ipa_instance_cfg_add_cmd,
|
|||
|
||||
struct cmd_node ipa_node = {
|
||||
L_IPA_NODE,
|
||||
"%s(ipa)#",
|
||||
"%s(config-ipa)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
|
@ -645,46 +638,20 @@ static int ipa_cfg_write(struct vty *vty)
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(ournode_exit,
|
||||
ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
|
||||
{
|
||||
switch (vty->node) {
|
||||
case L_IPA_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(ournode_end,
|
||||
ournode_end_cmd, "end", "End current mode and change to enable mode.\n")
|
||||
{
|
||||
switch (vty->node) {
|
||||
case L_IPA_NODE:
|
||||
break;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void ipa_proxy_vty_init(void)
|
||||
{
|
||||
tall_ipa_proxy_ctx =
|
||||
talloc_named_const(libosmo_abis_ctx, 1, "ipa_proxy");
|
||||
|
||||
install_element(ENABLE_NODE, &ipa_cmd);
|
||||
install_element(ENABLE_NODE, &ipa_instance_add_cmd);
|
||||
install_element(ENABLE_NODE, &ipa_instance_del_cmd);
|
||||
install_element(ENABLE_NODE, &ipa_instance_show_cmd);
|
||||
install_element(ENABLE_NODE, &ipa_route_add_cmd);
|
||||
install_element(ENABLE_NODE, &ipa_route_del_cmd);
|
||||
install_element(ENABLE_NODE, &ipa_route_show_cmd);
|
||||
install_lib_element(ENABLE_NODE, &ipa_instance_add_cmd);
|
||||
install_lib_element(ENABLE_NODE, &ipa_instance_del_cmd);
|
||||
install_lib_element(ENABLE_NODE, &ipa_instance_show_cmd);
|
||||
install_lib_element(ENABLE_NODE, &ipa_route_add_cmd);
|
||||
install_lib_element(ENABLE_NODE, &ipa_route_del_cmd);
|
||||
install_lib_element(ENABLE_NODE, &ipa_route_show_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &ipa_cfg_cmd);
|
||||
install_lib_element(CONFIG_NODE, &ipa_cfg_cmd);
|
||||
install_node(&ipa_node, ipa_cfg_write);
|
||||
install_default(L_IPA_NODE);
|
||||
install_element(L_IPA_NODE, &ournode_exit_cmd);
|
||||
install_element(L_IPA_NODE, &ournode_end_cmd);
|
||||
install_element(L_IPA_NODE, &ipa_instance_cfg_add_cmd);
|
||||
install_element(L_IPA_NODE, &ipa_route_cfg_add_cmd);
|
||||
install_lib_element(L_IPA_NODE, &ipa_instance_cfg_add_cmd);
|
||||
install_lib_element(L_IPA_NODE, &ipa_route_cfg_add_cmd);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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
|
||||
|
@ -30,28 +32,33 @@
|
|||
#include <osmocom/abis/trau_frame.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
/*! \addtogroup subchan_demux
|
||||
* @{
|
||||
*
|
||||
* \file subchan_demux.c
|
||||
*/
|
||||
|
||||
void *tall_tqe_ctx;
|
||||
|
||||
static inline void append_bit(struct demux_subch *sch, uint8_t bit)
|
||||
static inline void append_bit(struct demux_subch *sch, ubit_t bit)
|
||||
{
|
||||
sch->out_bitbuf[sch->out_idx++] = bit;
|
||||
}
|
||||
|
||||
#define SYNC_HDR_BITS 16
|
||||
static const uint8_t nullbytes[SYNC_HDR_BITS];
|
||||
|
||||
/* check if we have just completed the 16 bit zero sync header,
|
||||
* in accordance with GSM TS 08.60 Chapter 4.8.1 */
|
||||
/* check if we have just completed the 16 bit zero + 1 bit one sync
|
||||
* header, in accordance with GSM TS 08.60 Chapter 4.8.1 */
|
||||
static int sync_hdr_complete(struct demux_subch *sch, uint8_t bit)
|
||||
{
|
||||
if (bit == 0)
|
||||
sch->consecutive_zeros++;
|
||||
else
|
||||
else {
|
||||
if (sch->consecutive_zeros >= SYNC_HDR_BITS) {
|
||||
sch->consecutive_zeros = 0;
|
||||
return 1;
|
||||
}
|
||||
sch->consecutive_zeros = 0;
|
||||
|
||||
if (sch->consecutive_zeros >= SYNC_HDR_BITS) {
|
||||
sch->consecutive_zeros = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -61,13 +68,17 @@ static int sync_hdr_complete(struct demux_subch *sch, uint8_t bit)
|
|||
static void resync_to_here(struct demux_subch *sch)
|
||||
{
|
||||
memset(sch->out_bitbuf, 0, SYNC_HDR_BITS);
|
||||
sch->out_bitbuf[SYNC_HDR_BITS] = 1;
|
||||
|
||||
/* set index in a way that we can continue receiving bits after
|
||||
* the end of the SYNC header */
|
||||
sch->out_idx = SYNC_HDR_BITS;
|
||||
sch->out_idx = SYNC_HDR_BITS + 1;
|
||||
sch->in_sync = 1;
|
||||
}
|
||||
|
||||
/*! \brief initialize subchannel demuxer structure
|
||||
* \param[in,out] dmx subchannel demuxer
|
||||
*/
|
||||
int subch_demux_init(struct subch_demux *dmx)
|
||||
{
|
||||
int i;
|
||||
|
@ -81,21 +92,45 @@ int subch_demux_init(struct subch_demux *dmx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
|
||||
static void append_bit_resync_out(struct subch_demux *dmx, int c, ubit_t bit)
|
||||
{
|
||||
struct demux_subch *sch = &dmx->subch[c];
|
||||
append_bit(sch, bit);
|
||||
|
||||
if (sync_hdr_complete(sch, bit))
|
||||
resync_to_here(sch);
|
||||
|
||||
/* FIXME: verify the first bit in octet 2, 4, 6, ...
|
||||
* according to TS 08.60 4.8.1 */
|
||||
|
||||
/* once we have reached TRAU_FRAME_BITS, call
|
||||
* the TRAU frame handler callback function */
|
||||
if (sch->out_idx >= TRAU_FRAME_BITS) {
|
||||
if (sch->in_sync) {
|
||||
dmx->out_cb(dmx, c, sch->out_bitbuf,
|
||||
sch->out_idx, dmx->data);
|
||||
sch->in_sync = 0;
|
||||
}
|
||||
sch->out_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Input some data from the 64k full-slot into subchannel demux
|
||||
* \param[in] dmx subchannel demuxer
|
||||
* \param[in] data pointer to buffer containing input data
|
||||
* \param[in] len length of \a data in bytes
|
||||
* \returns 0 in case of success, <0 otherwise.
|
||||
*
|
||||
* Input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
|
||||
* split it into the 16k subchannels */
|
||||
int subch_demux_in(struct subch_demux *dmx, uint8_t *data, int len)
|
||||
{
|
||||
int i, c;
|
||||
|
||||
/* we avoid partially filled bytes in outbuf */
|
||||
if (len % 4)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t inbyte = data[i];
|
||||
|
||||
for (c = 0; c < NR_SUBCH; c++) {
|
||||
struct demux_subch *sch = &dmx->subch[c];
|
||||
uint8_t inbits;
|
||||
uint8_t bit;
|
||||
|
||||
|
@ -110,38 +145,26 @@ int subch_demux_in(struct subch_demux *dmx, uint8_t *data, int len)
|
|||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
append_bit(sch, bit);
|
||||
|
||||
if (sync_hdr_complete(sch, bit))
|
||||
resync_to_here(sch);
|
||||
append_bit_resync_out(dmx, c, bit);
|
||||
|
||||
if (inbits & 0x02)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
append_bit(sch, bit);
|
||||
|
||||
if (sync_hdr_complete(sch, bit))
|
||||
resync_to_here(sch);
|
||||
|
||||
/* FIXME: verify the first bit in octet 2, 4, 6, ...
|
||||
* according to TS 08.60 4.8.1 */
|
||||
|
||||
/* once we have reached TRAU_FRAME_BITS, call
|
||||
* the TRAU frame handler callback function */
|
||||
if (sch->out_idx >= TRAU_FRAME_BITS) {
|
||||
if (sch->in_sync) {
|
||||
dmx->out_cb(dmx, c, sch->out_bitbuf,
|
||||
sch->out_idx, dmx->data);
|
||||
sch->in_sync = 0;
|
||||
}
|
||||
sch->out_idx = 0;
|
||||
}
|
||||
append_bit_resync_out(dmx, c, bit);
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*! \brief activate a given sub-channel
|
||||
* \param[in] dmx subchannel demuxer
|
||||
* \param[in] subch sub-channel number
|
||||
* \returns 0 in case of success, <0 otherwise
|
||||
*
|
||||
* Activating a sub-channel will casuse the \ref subch_demux::out_cb
|
||||
* callback to be called for any incoming data from the full slot
|
||||
*/
|
||||
int subch_demux_activate(struct subch_demux *dmx, int subch)
|
||||
{
|
||||
if (subch >= NR_SUBCH)
|
||||
|
@ -151,6 +174,14 @@ int subch_demux_activate(struct subch_demux *dmx, int subch)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief deactivate a given sub-channel
|
||||
* \param[in] dmx subchannel demuxer
|
||||
* \param[in] subch sub-channel number
|
||||
* \returns 0 in case of success, <0 otherwise
|
||||
*
|
||||
* Deactivating a sub-channel will casuse the \ref subch_demux::out_cb
|
||||
* callback no longer to be called.
|
||||
*/
|
||||
int subch_demux_deactivate(struct subch_demux *dmx, int subch)
|
||||
{
|
||||
if (subch >= NR_SUBCH)
|
||||
|
@ -171,7 +202,7 @@ static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
|
|||
|
||||
/* return the requested number of bits from the specified subchannel */
|
||||
static int get_subch_bits(struct subch_mux *mx, int subch,
|
||||
uint8_t *bits, int num_requested)
|
||||
ubit_t *bits, int num_requested)
|
||||
{
|
||||
struct mux_subch *sch = &mx->subch[subch];
|
||||
int num_bits = 0;
|
||||
|
@ -229,7 +260,7 @@ static uint8_t compact_bits(const uint8_t *bits)
|
|||
/* obtain a single output byte from the subchannel muxer */
|
||||
static int mux_output_byte(struct subch_mux *mx, uint8_t *byte)
|
||||
{
|
||||
uint8_t bits[8];
|
||||
ubit_t bits[8];
|
||||
int rc;
|
||||
|
||||
/* combine two bits of every subchan */
|
||||
|
@ -243,7 +274,12 @@ static int mux_output_byte(struct subch_mux *mx, uint8_t *byte)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Request the output of some muxed bytes from the subchan muxer */
|
||||
/*! \brief Request the output of some muxed bytes from the subchan muxer
|
||||
* \param[in] mx subchannel muxer
|
||||
* \param[out] data caller-allocated buffer for data
|
||||
* \param[in] len number of bytes to be filled into \a data
|
||||
* \returns actual number of bytes filled into \a data
|
||||
*/
|
||||
int subchan_mux_out(struct subch_mux *mx, uint8_t *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
@ -257,17 +293,6 @@ int subchan_mux_out(struct subch_mux *mx, uint8_t *data, int len)
|
|||
return i;
|
||||
}
|
||||
|
||||
static int llist_len(struct llist_head *head)
|
||||
{
|
||||
struct llist_head *entry;
|
||||
int i = 0;
|
||||
|
||||
llist_for_each(entry, head)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* evict the 'num_evict' number of oldest entries in the queue */
|
||||
static void tx_queue_evict(struct mux_subch *sch, int num_evict)
|
||||
{
|
||||
|
@ -284,12 +309,18 @@ static void tx_queue_evict(struct mux_subch *sch, int num_evict)
|
|||
}
|
||||
}
|
||||
|
||||
/* enqueue some data into the tx_queue of a given subchannel */
|
||||
int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const uint8_t *data,
|
||||
/*! \brief enqueue some data into the tx_queue of a given subchannel
|
||||
* \param[in] mx subchannel muxer instance
|
||||
* \param[in] s_nr subchannel number
|
||||
* \param[in] data pointer to buffer with data (unpacked bits)
|
||||
* \param[in] len length of data (in unpacked bits)
|
||||
* \returns 0 in case of success, <0 in case of error
|
||||
*/
|
||||
int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const ubit_t *data,
|
||||
int len)
|
||||
{
|
||||
struct mux_subch *sch = &mx->subch[s_nr];
|
||||
int list_len = llist_len(&sch->tx_queue);
|
||||
unsigned int list_len = llist_count(&sch->tx_queue);
|
||||
struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx,
|
||||
sizeof(*tqe) + len);
|
||||
if (!tqe)
|
||||
|
@ -306,7 +337,10 @@ int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const uint8_t *data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* initialize one subchannel muxer instance */
|
||||
/*! \brief initialize one subchannel muxer instance
|
||||
* \param[in,out] mx subchannel muxer instance, caller-allocated
|
||||
* \returns 0 in success, < 0 in case of error
|
||||
*/
|
||||
int subchan_mux_init(struct subch_mux *mx)
|
||||
{
|
||||
int i;
|
||||
|
@ -319,3 +353,5 @@ int subchan_mux_init(struct subch_mux *mx)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* }@ */
|
||||
|
|
|
@ -0,0 +1,758 @@
|
|||
/* (C) 2011-2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2011 by On-Waves e.h.f
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file osmo_ortp.c
|
||||
* \brief Integration of libortp into osmocom framework (select, logging)
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
#include <ortp/ortp.h>
|
||||
#include <ortp/rtp.h>
|
||||
#include <ortp/port.h>
|
||||
#include <ortp/rtpsession.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
static PayloadType *payload_type_clearmode;
|
||||
static PayloadType *payload_type_efr;
|
||||
static PayloadType *payload_type_hr;
|
||||
static RtpProfile *osmo_pt_profile;
|
||||
|
||||
static void *tall_rtp_ctx;
|
||||
|
||||
/* malloc integration */
|
||||
#if HAVE_ORTP_MEM_FUNC
|
||||
static void *osmo_ortp_malloc(size_t sz)
|
||||
{
|
||||
return talloc_size(tall_rtp_ctx, sz);
|
||||
}
|
||||
|
||||
static void *osmo_ortp_realloc(void *ptr, size_t sz)
|
||||
{
|
||||
return talloc_realloc_size(tall_rtp_ctx, ptr, sz);
|
||||
}
|
||||
|
||||
static void osmo_ortp_free(void *ptr)
|
||||
{
|
||||
talloc_free(ptr);
|
||||
}
|
||||
|
||||
static OrtpMemoryFunctions osmo_ortp_memfn = {
|
||||
.malloc_fun = osmo_ortp_malloc,
|
||||
.realloc_fun = osmo_ortp_realloc,
|
||||
.free_fun = osmo_ortp_free
|
||||
};
|
||||
#endif /* HAVE_ORTP_MEM_FUNC */
|
||||
|
||||
/* logging */
|
||||
|
||||
struct level_map {
|
||||
OrtpLogLevel ortp;
|
||||
int osmo_level;
|
||||
};
|
||||
static const struct level_map level_map[] = {
|
||||
{ ORTP_DEBUG, LOGL_DEBUG },
|
||||
{ ORTP_MESSAGE, LOGL_INFO },
|
||||
{ ORTP_WARNING, LOGL_NOTICE },
|
||||
{ ORTP_ERROR, LOGL_ERROR },
|
||||
{ ORTP_FATAL, LOGL_FATAL },
|
||||
};
|
||||
static int ortp_to_osmo_lvl(OrtpLogLevel lev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(level_map); i++) {
|
||||
if (level_map[i].ortp == lev)
|
||||
return level_map[i].osmo_level;
|
||||
}
|
||||
/* default */
|
||||
return LOGL_ERROR;
|
||||
}
|
||||
|
||||
static void my_ortp_logfn(
|
||||
#if HAVE_ORTP_LOG_DOMAIN
|
||||
const char *domain,
|
||||
#endif
|
||||
OrtpLogLevel lev, const char *fmt, va_list args)
|
||||
{
|
||||
/* Some strings coming from ortp are not endline terminated and mangle
|
||||
* the output. Make sure all strings are endl terminated before
|
||||
* printing.
|
||||
*/
|
||||
int needs_endl;
|
||||
const char *domain_str;
|
||||
char *str;
|
||||
size_t fmt_len = strlen(fmt);
|
||||
#if HAVE_ORTP_LOG_DOMAIN
|
||||
/* domain can be NULL, found experimentally */
|
||||
domain_str = domain ? : "";
|
||||
#else
|
||||
domain_str = "";
|
||||
#endif
|
||||
size_t domain_len = strlen(domain_str);
|
||||
|
||||
if (fmt_len == 0)
|
||||
return;
|
||||
|
||||
needs_endl = fmt[fmt_len - 1] != '\n' ? 1 : 0;
|
||||
|
||||
str = talloc_asprintf(tall_rtp_ctx, "%s%s%s%s",
|
||||
domain_str, domain_len ? ": " : "",
|
||||
fmt, needs_endl ? "\n" : "");
|
||||
|
||||
osmo_vlogp(DLMIB, ortp_to_osmo_lvl(lev), __FILE__, 0,
|
||||
0, str, args);
|
||||
|
||||
talloc_free(str);
|
||||
|
||||
}
|
||||
|
||||
/* ORTP signal callbacks */
|
||||
|
||||
static void ortp_sig_cb_ssrc(RtpSession *rs, void *data)
|
||||
{
|
||||
int port = rtp_session_get_local_port(rs);
|
||||
uint32_t ssrc = rtp_session_get_recv_ssrc(rs);
|
||||
|
||||
LOGP(DLMIB, LOGL_INFO,
|
||||
"osmo-ortp(%d): ssrc_changed to 0x%08x, resyncing\n", port, ssrc);
|
||||
rtp_session_resync(rs);
|
||||
}
|
||||
|
||||
static void ortp_sig_cb_pt(RtpSession *rs, void *data)
|
||||
{
|
||||
int port = rtp_session_get_local_port(rs);
|
||||
int pt = rtp_session_get_recv_payload_type(rs);
|
||||
|
||||
LOGP(DLMIB, LOGL_NOTICE,
|
||||
"osmo-ortp(%d): payload_type_changed to 0x%02x\n", port, pt);
|
||||
}
|
||||
|
||||
static void ortp_sig_cb_net(RtpSession *rs, void *data)
|
||||
{
|
||||
int port = rtp_session_get_local_port(rs);
|
||||
|
||||
LOGP(DLMIB, LOGL_ERROR,
|
||||
"osmo-ortp(%d): network_error %s\n", port, (char *)data);
|
||||
}
|
||||
|
||||
static void ortp_sig_cb_ts(RtpSession *rs, void *data)
|
||||
{
|
||||
int port = rtp_session_get_local_port(rs);
|
||||
uint32_t ts = rtp_session_get_current_recv_ts(rs);
|
||||
|
||||
LOGP(DLMIB, LOGL_NOTICE,
|
||||
"osmo-ortp(%d): timestamp_jump, new TS %d, resyncing\n", port, ts);
|
||||
rtp_session_resync(rs);
|
||||
}
|
||||
|
||||
static inline bool recv_with_cb(struct osmo_rtp_socket *rs)
|
||||
{
|
||||
uint8_t *payload;
|
||||
mblk_t *mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
|
||||
if (!mblk)
|
||||
return false;
|
||||
|
||||
int plen = rtp_get_payload(mblk, &payload);
|
||||
/* hand into receiver */
|
||||
if (rs->rx_cb && plen > 0)
|
||||
rs->rx_cb(rs, payload, plen, rtp_get_seqnumber(mblk),
|
||||
rtp_get_timestamp(mblk), rtp_get_markbit(mblk));
|
||||
freemsg(mblk);
|
||||
if (plen > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! \brief poll the socket for incoming data
|
||||
* \param[in] rs the socket to be polled
|
||||
* \returns number of packets received + handed to the rx_cb
|
||||
*/
|
||||
int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs)
|
||||
{
|
||||
if (rs->flags & OSMO_RTP_F_DISABLED)
|
||||
return 0;
|
||||
|
||||
if (recv_with_cb(rs))
|
||||
return 1;
|
||||
|
||||
/* this happens every time we miss an incoming RTP frame, which is quite common
|
||||
* when a voice channel is first activated, or also in case of packet loss.
|
||||
* See also https://osmocom.org/issues/4464 */
|
||||
LOGP(DLMIB, LOGL_DEBUG, "osmo_rtp_socket_poll(%u): No message received\n", rs->rx_user_ts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Osmo FD callbacks */
|
||||
|
||||
static int osmo_rtp_fd_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct osmo_rtp_socket *rs = fd->data;
|
||||
|
||||
if (what & OSMO_FD_READ) {
|
||||
/* in polling mode, we don't want to be called here */
|
||||
if (rs->flags & OSMO_RTP_F_POLL) {
|
||||
osmo_fd_read_disable(fd);
|
||||
return 0;
|
||||
}
|
||||
if (!recv_with_cb(rs))
|
||||
LOGP(DLMIB, LOGL_INFO, "recvm_with_ts(%u): ERROR!\n",
|
||||
rs->rx_user_ts);
|
||||
rs->rx_user_ts += 160;
|
||||
}
|
||||
/* writing is not queued at the moment, so OSMO_FD_WRITE
|
||||
* shouldn't occur */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Internal API coming from rtpsession_priv.h, used in osmo_rtcp_fd_cb */
|
||||
#pragma message ("Using internal ortp API: rtp_session_rtcp_rec")
|
||||
int rtp_session_rtcp_recv(RtpSession * session);
|
||||
|
||||
static int osmo_rtcp_fd_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct osmo_rtp_socket *rs = fd->data;
|
||||
|
||||
/* We probably don't need this at all, as
|
||||
* rtp_session_recvm_with_ts() will alway also poll the RTCP
|
||||
* file descriptor for new data */
|
||||
return rtp_session_rtcp_recv(rs->sess);
|
||||
}
|
||||
|
||||
static int osmo_rtp_socket_fdreg(struct osmo_rtp_socket *rs)
|
||||
{
|
||||
int rc;
|
||||
|
||||
osmo_fd_setup(&rs->rtp_bfd, rtp_session_get_rtp_socket(rs->sess), OSMO_FD_READ, osmo_rtp_fd_cb, rs, 0);
|
||||
osmo_fd_setup(&rs->rtcp_bfd, rtp_session_get_rtcp_socket(rs->sess), OSMO_FD_READ, osmo_rtcp_fd_cb, rs, 0);
|
||||
|
||||
rc = osmo_fd_register(&rs->rtp_bfd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = osmo_fd_register(&rs->rtcp_bfd);
|
||||
if (rc < 0) {
|
||||
osmo_fd_unregister(&rs->rtp_bfd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_payload_types(void)
|
||||
{
|
||||
PayloadType *pt;
|
||||
|
||||
/* CLEARMODE as per RFC 4040 (chapter 3) */
|
||||
pt = payload_type_new();
|
||||
pt->type = PAYLOAD_OTHER;
|
||||
pt->clock_rate = 8000;
|
||||
pt->bits_per_sample = 8;
|
||||
pt->mime_type = "CLEARMODE";
|
||||
pt->normal_bitrate = 64000;
|
||||
pt->channels = 1;
|
||||
payload_type_clearmode = pt;
|
||||
|
||||
/* EFR */
|
||||
pt = payload_type_new();
|
||||
pt->type = PAYLOAD_AUDIO_PACKETIZED;
|
||||
pt->clock_rate = 8000;
|
||||
pt->mime_type = "EFR";
|
||||
pt->normal_bitrate = 12200;
|
||||
pt->channels = 1;
|
||||
payload_type_efr = pt;
|
||||
|
||||
/* HR */
|
||||
pt = payload_type_new();
|
||||
pt->type = PAYLOAD_AUDIO_PACKETIZED;
|
||||
pt->clock_rate = 8000;
|
||||
pt->mime_type = "HR";
|
||||
pt->normal_bitrate = 6750; /* FIXME */
|
||||
pt->channels = 1;
|
||||
payload_type_hr = pt;
|
||||
|
||||
/* create a new RTP profile as clone of AV profile */
|
||||
osmo_pt_profile = rtp_profile_clone(&av_profile);
|
||||
|
||||
/* add the GSM specific payload types. They are all dynamically
|
||||
* assigned, but in the Osmocom GSM system we have allocated
|
||||
* them as follows: */
|
||||
rtp_profile_set_payload(osmo_pt_profile, RTP_PT_CSDATA, payload_type_clearmode);
|
||||
rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_EFR, payload_type_efr);
|
||||
rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_HALF, payload_type_hr);
|
||||
rtp_profile_set_payload(osmo_pt_profile, RTP_PT_AMR, &payload_type_amr);
|
||||
}
|
||||
|
||||
/* public functions */
|
||||
|
||||
/*! \brief initialize Osmocom RTP code
|
||||
* \param[in] ctx default talloc context for library-internal allocations
|
||||
*/
|
||||
void osmo_rtp_init(void *ctx)
|
||||
{
|
||||
tall_rtp_ctx = ctx;
|
||||
#if HAVE_ORTP_MEM_FUNC
|
||||
ortp_set_memory_functions(&osmo_ortp_memfn);
|
||||
#endif
|
||||
ortp_init();
|
||||
ortp_set_log_level_mask(
|
||||
#if HAVE_ORTP_LOG_DOMAIN
|
||||
ORTP_LOG_DOMAIN,
|
||||
#endif
|
||||
0xffff);
|
||||
|
||||
ortp_set_log_handler(my_ortp_logfn);
|
||||
create_payload_types();
|
||||
}
|
||||
|
||||
/*! \brief Set Osmocom RTP socket parameters
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] param defined which parameter to set
|
||||
OSMO_RTP_P_JITBUF - enables regular jitter buffering
|
||||
OSMO_RTP_P_JIT_ADAP - enables adaptive jitter buffering
|
||||
* \param[in] val Size of jitter buffer (in ms), 0 means disable buffering
|
||||
* \returns negative value on error, 0 or 1 otherwise
|
||||
(depending on whether given jitter buffering is enabled)
|
||||
*/
|
||||
int osmo_rtp_socket_set_param(struct osmo_rtp_socket *rs,
|
||||
enum osmo_rtp_param param, int val)
|
||||
{
|
||||
switch (param) {
|
||||
case OSMO_RTP_P_JIT_ADAP:
|
||||
rtp_session_enable_adaptive_jitter_compensation(rs->sess,
|
||||
(bool)val);
|
||||
/* fall-through on-purpose - we have to set val anyway */
|
||||
case OSMO_RTP_P_JITBUF:
|
||||
rtp_session_enable_jitter_buffer(rs->sess,
|
||||
(val) ? TRUE : FALSE);
|
||||
if (val)
|
||||
rtp_session_set_jitter_compensation(rs->sess, val);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (param == OSMO_RTP_P_JIT_ADAP)
|
||||
return rtp_session_adaptive_jitter_compensation_enabled(rs->sess);
|
||||
return rtp_session_jitter_buffer_enabled(rs->sess);
|
||||
}
|
||||
|
||||
/*! \brief Create a new RTP socket
|
||||
* \param[in] talloc_cxt talloc context for this allocation. NULL for
|
||||
* dafault context
|
||||
* \param[in] flags Flags like OSMO_RTP_F_POLL
|
||||
* \returns pointer to library-allocated \a struct osmo_rtp_socket
|
||||
*/
|
||||
struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags)
|
||||
{
|
||||
struct osmo_rtp_socket *rs;
|
||||
|
||||
if (!talloc_ctx)
|
||||
talloc_ctx = tall_rtp_ctx;
|
||||
|
||||
rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket);
|
||||
if (!rs)
|
||||
return NULL;
|
||||
|
||||
rs->flags = OSMO_RTP_F_DISABLED | flags;
|
||||
rs->sess = rtp_session_new(RTP_SESSION_SENDRECV);
|
||||
if (!rs->sess) {
|
||||
talloc_free(rs);
|
||||
return NULL;
|
||||
}
|
||||
rtp_session_set_data(rs->sess, rs);
|
||||
rtp_session_set_profile(rs->sess, osmo_pt_profile);
|
||||
rtp_session_set_jitter_compensation(rs->sess, 100);
|
||||
|
||||
/* ortp >= 0.24.0 doesn't differentiate between SO_REUSEADDR and
|
||||
* SO_REUSEPORT, and has both enabled by default. The latter means that
|
||||
* we can end up with non-unique port bindings as we will not fail to
|
||||
* bind the same port twice */
|
||||
rtp_session_set_reuseaddr(rs->sess, false);
|
||||
|
||||
rtp_session_signal_connect(rs->sess, "ssrc_changed",
|
||||
(RtpCallback) ortp_sig_cb_ssrc,
|
||||
RTP_SIGNAL_PTR_CAST(rs));
|
||||
|
||||
rtp_session_signal_connect(rs->sess, "payload_type_changed",
|
||||
(RtpCallback) ortp_sig_cb_pt,
|
||||
RTP_SIGNAL_PTR_CAST(rs));
|
||||
|
||||
rtp_session_signal_connect(rs->sess, "network_error",
|
||||
(RtpCallback) ortp_sig_cb_net,
|
||||
RTP_SIGNAL_PTR_CAST(rs));
|
||||
|
||||
rtp_session_signal_connect(rs->sess, "timestamp_jump",
|
||||
(RtpCallback) ortp_sig_cb_ts,
|
||||
RTP_SIGNAL_PTR_CAST(rs));
|
||||
|
||||
/* initialize according to the RFC */
|
||||
rtp_session_set_seq_number(rs->sess, random());
|
||||
rs->tx_timestamp = random();
|
||||
|
||||
/* Make sure ssrc changes are detected immediately */
|
||||
rtp_session_set_ssrc_changed_threshold(rs->sess, 0);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
/*! \brief bind a RTP socket to a local port
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] ip hostname/ip as string
|
||||
* \param[in] port UDP port number, -1 for random selection
|
||||
* \returns 0 on success, <0 on error
|
||||
*/
|
||||
int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port)
|
||||
{
|
||||
int rc, rtcp = (-1 != port) ? port + 1 : -1;
|
||||
rc = rtp_session_set_local_addr(rs->sess, ip, port, rtcp);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
|
||||
rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief connect a OsmoRTP socket to a remote port
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] ip String representation of remote hostname or IP address
|
||||
* \param[in] port UDP port number to connect to
|
||||
*
|
||||
* If the OsmoRTP socket is not in POLL mode, this function will also
|
||||
* cause the RTP and RTCP file descriptors to be registred with the
|
||||
* libosmocore select() loop integration.
|
||||
*
|
||||
* \returns 0 on success, <0 in case of error
|
||||
*/
|
||||
int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port)
|
||||
{
|
||||
int rc;
|
||||
if (!port) {
|
||||
LOGP(DLMIB, LOGL_INFO, "osmo_rtp_socket_connect() refused to "
|
||||
"set remote %s:%u\n", ip, port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't want the connected mode enabled during
|
||||
* rtp_session_set_remote_addr(), because that will already setup a
|
||||
* connection and updating the remote address will no longer have an
|
||||
* effect. Contrary to what one may expect, this must be 0 at first,
|
||||
* and we're setting to 1 further down to establish a connection once
|
||||
* the first RTP packet is received (OS#1661). */
|
||||
rtp_session_set_connected_mode(rs->sess, 0);
|
||||
|
||||
rc = rtp_session_set_remote_addr(rs->sess, ip, port);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* enable the use of connect() so later getsockname() will
|
||||
* actually return the IP address that was chosen for the local
|
||||
* sid of the connection */
|
||||
rtp_session_set_connected_mode(rs->sess, 1);
|
||||
rs->flags &= ~OSMO_RTP_F_DISABLED;
|
||||
|
||||
if (rs->flags & OSMO_RTP_F_POLL)
|
||||
return rc;
|
||||
else
|
||||
return osmo_rtp_socket_fdreg(rs);
|
||||
}
|
||||
|
||||
/*! \brief Automatically associates a RTP socket with the first incoming UDP packet
|
||||
* \param[in] rs OsmoRTP socket
|
||||
*
|
||||
* The bound RTP socket will wait for incoming RTP packets and as soon as it
|
||||
* sees one, will 'connect' to it, so all replies will go to that sources and
|
||||
* incoming messages from other sources will be discarded. This obviously only
|
||||
* works once.
|
||||
*
|
||||
* \returns 0 on success, <0 in case of error.
|
||||
*/
|
||||
int osmo_rtp_socket_autoconnect(struct osmo_rtp_socket *rs)
|
||||
{
|
||||
rtp_session_set_symmetric_rtp(rs->sess, 1);
|
||||
rtp_session_set_connected_mode(rs->sess, 1);
|
||||
rs->flags &= ~OSMO_RTP_F_DISABLED;
|
||||
|
||||
if (rs->flags & OSMO_RTP_F_POLL)
|
||||
return 0;
|
||||
else
|
||||
return osmo_rtp_socket_fdreg(rs);
|
||||
}
|
||||
|
||||
/*! \brief Increment timestamp on a RTP socket without sending any packet
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] duration duration in number of RTP clock ticks
|
||||
*
|
||||
* Useful to keep the RTP internal clock up to date if an RTP frame should be
|
||||
* send at a given time but no audio content is available. When next packet is
|
||||
* sent, the receiver will see a different increase on the sequence number and
|
||||
* the timestamp, and it should then take it as a synchronization point. For
|
||||
* that same reason, it is advisable to enable the marker bit on the next RTP
|
||||
* packet to be sent after calling this function.
|
||||
*
|
||||
* \returns 0 on success, <0 in case of error.
|
||||
*/
|
||||
int osmo_rtp_skipped_frame(struct osmo_rtp_socket *rs, unsigned int duration)
|
||||
{
|
||||
if (rs->flags & OSMO_RTP_F_DISABLED)
|
||||
return 0;
|
||||
|
||||
rs->tx_timestamp += duration;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Send one RTP frame via a RTP socket
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] payload pointer to buffer with RTP payload data
|
||||
* \param[in] payload_len length of \a payload in bytes
|
||||
* \param[in] duration duration in number of RTP clock ticks
|
||||
* \returns 0 on success, <0 in case of error.
|
||||
*/
|
||||
int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
|
||||
unsigned int payload_len, unsigned int duration)
|
||||
{
|
||||
return osmo_rtp_send_frame_ext(rs, payload, payload_len, duration,
|
||||
false);
|
||||
}
|
||||
|
||||
/*! \brief Send one RTP frame via a RTP socket
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] payload pointer to buffer with RTP payload data
|
||||
* \param[in] payload_len length of \a payload in bytes
|
||||
* \param[in] duration duration in number of RTP clock ticks
|
||||
* \param[in] marker the status of Marker bit in RTP header
|
||||
* \returns 0 on success, <0 in case of error.
|
||||
*/
|
||||
int osmo_rtp_send_frame_ext(struct osmo_rtp_socket *rs, const uint8_t *payload,
|
||||
unsigned int payload_len, unsigned int duration,
|
||||
bool marker)
|
||||
{
|
||||
mblk_t *mblk;
|
||||
int rc;
|
||||
|
||||
if (rs->flags & OSMO_RTP_F_DISABLED)
|
||||
return 0;
|
||||
|
||||
mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE,
|
||||
payload, payload_len);
|
||||
if (!mblk)
|
||||
return -ENOMEM;
|
||||
|
||||
rtp_set_markbit(mblk, marker);
|
||||
rc = rtp_session_sendm_with_ts(rs->sess, mblk,
|
||||
rs->tx_timestamp);
|
||||
rs->tx_timestamp += duration;
|
||||
if (rc < 0) {
|
||||
/* no need to free() the mblk, as rtp_session_rtp_send()
|
||||
* unconditionally free()s the mblk even in case of
|
||||
* error */
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Set the payload type of a RTP socket
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] payload_type RTP payload type
|
||||
* \returns 0 on success, < 0 otherwise
|
||||
*/
|
||||
int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = rtp_session_set_payload_type(rs->sess, payload_type);
|
||||
//rtp_session_set_rtcp_report_interval(rs->sess, 5*1000);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Set the DSCP (Differentiated Services Code Point) for outgoing RTP packets
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] dscp DSCP value
|
||||
* \returns 0 on success, < 0 otherwise
|
||||
*/
|
||||
int osmo_rtp_socket_set_dscp(struct osmo_rtp_socket *rs, int dscp)
|
||||
{
|
||||
return rtp_session_set_dscp(rs->sess, dscp);
|
||||
}
|
||||
|
||||
/*! \brief Set the socket priority for outgoing RTP packets
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[in] prio socket priority
|
||||
* \returns 0 on success, < 0 otherwise
|
||||
*/
|
||||
int osmo_rtp_socket_set_priority(struct osmo_rtp_socket *rs, uint8_t prio)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_sock_set_priority(rs->rtp_bfd.fd, prio);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return osmo_sock_set_priority(rs->rtcp_bfd.fd, prio);
|
||||
}
|
||||
|
||||
/*! \brief completely close the RTP socket and release all resources
|
||||
* \param[in] rs OsmoRTP socket to be released
|
||||
* \returns 0 on success
|
||||
*/
|
||||
int osmo_rtp_socket_free(struct osmo_rtp_socket *rs)
|
||||
{
|
||||
if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1)
|
||||
osmo_fd_unregister(&rs->rtp_bfd);
|
||||
|
||||
if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1)
|
||||
osmo_fd_unregister(&rs->rtcp_bfd);
|
||||
|
||||
if (rs->sess) {
|
||||
rtp_session_release_sockets(rs->sess);
|
||||
rtp_session_destroy(rs->sess);
|
||||
rs->sess = NULL;
|
||||
}
|
||||
|
||||
talloc_free(rs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief obtain the locally bound IPv4 address and UDP port
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[out] ip Pointer to caller-allocated uint32_t for IPv4 address
|
||||
* \oaram[out] port Pointer to caller-allocated int for UDP port number
|
||||
* \returns 0 on success, <0 on error, -EIO in case of IPv6 socket
|
||||
*/
|
||||
int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
|
||||
uint32_t *ip, int *port)
|
||||
{
|
||||
int rc;
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
|
||||
socklen_t alen = sizeof(ss);
|
||||
|
||||
rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (ss.ss_family != AF_INET)
|
||||
return -EIO;
|
||||
|
||||
*ip = ntohl(sin->sin_addr.s_addr);
|
||||
*port = rtp_session_get_local_port(rs->sess);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief obtain the locally bound address and port
|
||||
* \param[in] rs OsmoRTP socket
|
||||
* \param[out] addr caller-allocated char ** to which the string pointer for
|
||||
* the address is stored
|
||||
* \param[out] port caller-allocated int * to which the port number is
|
||||
* stored
|
||||
* \returns 0 on success, <0 in case of error
|
||||
*/
|
||||
int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
|
||||
const char **addr, int *port)
|
||||
{
|
||||
int rc;
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t alen = sizeof(ss);
|
||||
static char hostbuf[256];
|
||||
|
||||
memset(hostbuf, 0, sizeof(hostbuf));
|
||||
|
||||
rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = getnameinfo((struct sockaddr *)&ss, alen,
|
||||
hostbuf, sizeof(hostbuf), NULL, 0,
|
||||
NI_NUMERICHOST);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
*port = rtp_session_get_local_port(rs->sess);
|
||||
*addr = hostbuf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void osmo_rtp_socket_log_stats(struct osmo_rtp_socket *rs,
|
||||
int subsys, int level,
|
||||
const char *pfx)
|
||||
{
|
||||
const rtp_stats_t *stats;
|
||||
|
||||
stats = rtp_session_get_stats(rs->sess);
|
||||
if (!stats)
|
||||
return;
|
||||
|
||||
LOGP(subsys, level, "%sRTP Tx(%"PRIu64" pkts, %"PRIu64" bytes) "
|
||||
"Rx(%"PRIu64" pkts, %"PRIu64" bytes, %"PRIu64" late, "
|
||||
"%"PRIu64" loss, %"PRIu64" qmax)\n",
|
||||
pfx, stats->packet_sent, stats->sent,
|
||||
stats->packet_recv, stats->hw_recv, stats->outoftime,
|
||||
stats->cum_packet_loss, stats->discarded);
|
||||
}
|
||||
|
||||
void osmo_rtp_socket_stats(struct osmo_rtp_socket *rs,
|
||||
uint32_t *sent_packets, uint32_t *sent_octets,
|
||||
uint32_t *recv_packets, uint32_t *recv_octets,
|
||||
uint32_t *recv_lost, uint32_t *last_jitter)
|
||||
{
|
||||
const rtp_stats_t *stats;
|
||||
|
||||
*sent_packets = *sent_octets = *recv_packets = *recv_octets = 0;
|
||||
*recv_lost = *last_jitter = 0;
|
||||
|
||||
stats = rtp_session_get_stats(rs->sess);
|
||||
if (stats) {
|
||||
/* truncate from 64bit to 32bit here */
|
||||
*sent_packets = stats->packet_sent;
|
||||
*sent_octets = stats->sent;
|
||||
*recv_packets = stats->packet_recv;
|
||||
*recv_octets = stats->recv;
|
||||
*recv_lost = stats->cum_packet_loss;
|
||||
}
|
||||
|
||||
const jitter_stats_t *jitter;
|
||||
|
||||
jitter = rtp_session_get_jitter_stats(rs->sess);
|
||||
if (jitter)
|
||||
*last_jitter = jitter->jitter;
|
||||
}
|
||||
|
||||
void osmo_rtp_set_source_desc(struct osmo_rtp_socket *rs, const char *cname,
|
||||
const char *name, const char *email, const char *phone,
|
||||
const char *loc, const char *tool, const char *note)
|
||||
{
|
||||
rtp_session_set_source_description(rs->sess, cname, name, email, phone, loc, tool, note);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,839 @@
|
|||
/* TRAU frame to RTP conversion */
|
||||
|
||||
/* (C) 2009,2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/crc8gen.h>
|
||||
#include <osmocom/codec/codec.h>
|
||||
#include <osmocom/gsm/rtp_extensions.h>
|
||||
|
||||
#include <osmocom/trau/trau_frame.h>
|
||||
#include <osmocom/trau/trau_rtp.h>
|
||||
|
||||
/* this corresponds to the bit-lengths of the individual codec
|
||||
* parameters as indicated in Table 1.1 of TS 46.010 */
|
||||
static const uint8_t gsm_fr_map[] = {
|
||||
6, 6, 5, 5, 4, 4, 3, 3,
|
||||
7, 2, 2, 6, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 7, 2, 2, 6, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 7, 2, 2, 6, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 7, 2, 2, 6, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* EFR TRAU parity (also used for HR)
|
||||
*
|
||||
* g(x) = x^3 + x^1 + 1
|
||||
*/
|
||||
static const struct osmo_crc8gen_code gsm0860_efr_crc3 = {
|
||||
.bits = 3,
|
||||
.poly = 0x3,
|
||||
.init = 0x0,
|
||||
.remainder = 0x7,
|
||||
};
|
||||
|
||||
/* re-combine EFR parity bits */
|
||||
static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits)
|
||||
{
|
||||
memcpy(check_bits + 0 , d_bits + 0, 22);
|
||||
memcpy(check_bits + 22 , d_bits + 24, 3);
|
||||
check_bits[25] = d_bits[28];
|
||||
}
|
||||
|
||||
static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits)
|
||||
{
|
||||
memcpy(check_bits + 0 , d_bits + 42, 10);
|
||||
memcpy(check_bits + 10 , d_bits + 90, 2);
|
||||
}
|
||||
|
||||
static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits)
|
||||
{
|
||||
memcpy(check_bits + 0 , d_bits + 98, 5);
|
||||
check_bits[5] = d_bits[104];
|
||||
memcpy(check_bits + 6 , d_bits + 143, 2);
|
||||
}
|
||||
|
||||
static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits)
|
||||
{
|
||||
memcpy(check_bits + 0 , d_bits + 151, 10);
|
||||
memcpy(check_bits + 10 , d_bits + 199, 2);
|
||||
}
|
||||
|
||||
static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits)
|
||||
{
|
||||
memcpy(check_bits + 0 , d_bits + 207, 5);
|
||||
check_bits[5] = d_bits[213];
|
||||
memcpy(check_bits + 6 , d_bits + 252, 2);
|
||||
}
|
||||
|
||||
//static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 };
|
||||
//static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 };
|
||||
|
||||
/*! Generate the 33 bytes RTP payload for GSM-FR from a decoded TRAU frame.
|
||||
* \param[out] out caller-provided output buffer
|
||||
* \param[in] out_len length of out buffer in bytes
|
||||
* \param[in] fr input TRAU frame in decoded form
|
||||
* \returns number of bytes generated in 'out'; negative on error. */
|
||||
static int trau2rtp_fr(uint8_t *out, size_t out_len, const struct osmo_trau_frame *tf, bool emit_twts001)
|
||||
{
|
||||
size_t req_out_len = emit_twts001 ? GSM_FR_BYTES+1 : GSM_FR_BYTES;
|
||||
int i, j, k, l, o;
|
||||
|
||||
if (tf->type != OSMO_TRAU16_FT_FR)
|
||||
return -EINVAL;
|
||||
|
||||
if (out_len < req_out_len)
|
||||
return -ENOSPC;
|
||||
|
||||
/* FR Data Bits according to TS 48.060 Section 5.5.1.1.2 */
|
||||
|
||||
/* Are we emitting TW-TS-001? If so, emit TRAU-like Extension Header */
|
||||
if (emit_twts001) {
|
||||
uint8_t hdr_byte = 0xE0;
|
||||
|
||||
if (tf->c_bits[16]) /* DTXd */
|
||||
hdr_byte |= 0x08;
|
||||
if (tf->c_bits[11]) /* BFI */
|
||||
hdr_byte |= 0x02;
|
||||
if (tf->c_bits[14]) /* TAF */
|
||||
hdr_byte |= 0x01;
|
||||
*out++ = hdr_byte;
|
||||
}
|
||||
|
||||
if (tf->c_bits[11] && !emit_twts001) /* BFI without TW-TS-001 */
|
||||
return 0;
|
||||
|
||||
out[0] = 0xd << 4;
|
||||
/* reassemble d-bits */
|
||||
i = 0; /* counts bits */
|
||||
j = 4; /* counts output bits */
|
||||
k = gsm_fr_map[0]-1; /* current number bit in element */
|
||||
l = 0; /* counts element bits */
|
||||
o = 0; /* offset input bits */
|
||||
while (i < 260) {
|
||||
out[j/8] |= (tf->d_bits[k+o] << (7-(j%8)));
|
||||
/* to avoid out-of-bounds access in gsm_fr_map[++l] */
|
||||
if (i == 259)
|
||||
break;
|
||||
if (--k < 0) {
|
||||
o += gsm_fr_map[l];
|
||||
k = gsm_fr_map[++l]-1;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
return req_out_len;
|
||||
}
|
||||
|
||||
/* See Section 5.2 of RFC5993 and TW-TS-002 */
|
||||
enum super5993_ft {
|
||||
FT_GOOD_SPEECH = 0,
|
||||
FT_INVALID_SID = 1,
|
||||
FT_GOOD_SID = 2,
|
||||
FT_BFI_WITH_DATA = 6,
|
||||
FT_NO_DATA = 7,
|
||||
};
|
||||
|
||||
static const uint8_t rtp_hr_sid[14] = { 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
static void twtw002_hr16_set_extra_flags(uint8_t *out, const struct osmo_trau_frame *tf)
|
||||
{
|
||||
if (tf->c_bits[16]) /* DTXd */
|
||||
out[0] |= 0x08;
|
||||
if (tf->ufi) /* UFI */
|
||||
out[0] |= 0x02;
|
||||
if (tf->c_bits[14]) /* TAF */
|
||||
out[0] |= 0x01;
|
||||
}
|
||||
|
||||
/*! Generate an RFC 5993 RTP payload for HR from a decoded 16k TRAU frame.
|
||||
* We previously emitted TS 101 318 format; however, RFC 5993 is the format
|
||||
* specified in TS 48.103 for AoIP, it can also be extended with TRAU-UL-like
|
||||
* capabilities with TW-TS-002, and we now support RFC 5993 output in OsmoBTS
|
||||
* on all models.
|
||||
* \param[out] out caller-provided output buffer
|
||||
* \param[in] out_len length of out buffer in bytes
|
||||
* \param[in] tf input TRAU frame in decoded form
|
||||
* \returns number of bytes generated in 'out'; negative on error. */
|
||||
static int trau2rtp_hr16(uint8_t *out, size_t out_len, const struct osmo_trau_frame *tf, bool emit_twts002)
|
||||
{
|
||||
enum osmo_gsm631_sid_class sidc;
|
||||
int rc;
|
||||
|
||||
if (tf->type != OSMO_TRAU16_FT_HR)
|
||||
return -EINVAL;
|
||||
|
||||
if (out_len < GSM_HR_BYTES_RTP_RFC5993)
|
||||
return -ENOSPC;
|
||||
|
||||
/* HR Data Bits according to TS 48.061 Section 5.1.4.1.1 */
|
||||
|
||||
/* bad CRC means bad frame, no matter what else is going on */
|
||||
rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, tf->d_bits, 44,
|
||||
tf->crc_bits);
|
||||
if (rc)
|
||||
goto bad_frame;
|
||||
|
||||
sidc = (tf->c_bits[12] << 1) | tf->c_bits[13];
|
||||
/* both C13 and C14 being set is invalid combination */
|
||||
if (sidc > OSMO_GSM631_SID_CLASS_VALID)
|
||||
goto bad_frame;
|
||||
|
||||
/* Plain RFC 5993 without TW-TS-002 extensions does not allow
|
||||
* BFI or invalid SID packets. */
|
||||
if (!emit_twts002 &&
|
||||
(tf->c_bits[11] || sidc == OSMO_GSM631_SID_CLASS_INVALID))
|
||||
goto bad_frame;
|
||||
|
||||
/* BFI turns valid SID into invalid */
|
||||
if (tf->c_bits[11] && sidc == OSMO_GSM631_SID_CLASS_VALID)
|
||||
sidc = OSMO_GSM631_SID_CLASS_INVALID;
|
||||
|
||||
/* RFC 5993 Frame Type is equal to GSM 06.41 SID classification,
|
||||
* restricted to just speech or valid SID per above. Or if we
|
||||
* emit TW-TS-002, that restriction is lifted. */
|
||||
out[0] = sidc << 4;
|
||||
|
||||
if (emit_twts002) {
|
||||
if (tf->c_bits[11] && sidc == OSMO_GSM631_SID_CLASS_SPEECH)
|
||||
out[0] = FT_BFI_WITH_DATA << 4;
|
||||
twtw002_hr16_set_extra_flags(out, tf);
|
||||
/* invalid SID frames are truncated in TW-TS-002 */
|
||||
if (sidc == OSMO_GSM631_SID_CLASS_INVALID)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* TS 101 318 Section 5.2: The order of occurrence of the codec parameters in the buffer is
|
||||
* the same as order of occurrence over the Abis as defined in annex B of ETS 300 969
|
||||
* [which is 3GPP TS 46.020 */
|
||||
osmo_ubit2pbit(out + 1, tf->d_bits, 112);
|
||||
|
||||
/* RFC 5993 requires SID frames to be perfect, error-free */
|
||||
if (!emit_twts002 && sidc == OSMO_GSM631_SID_CLASS_VALID)
|
||||
osmo_hr_sid_reset(out + 1);
|
||||
|
||||
return GSM_HR_BYTES_RTP_RFC5993;
|
||||
|
||||
bad_frame:
|
||||
if (emit_twts002) {
|
||||
out[0] = FT_NO_DATA << 4;
|
||||
twtw002_hr16_set_extra_flags(out, tf);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generate the 31 bytes RTP payload for GSM-EFR from a decoded TRAU frame.
|
||||
* \param[out] out caller-provided output buffer
|
||||
* \param[in] out_len length of out buffer in bytes
|
||||
* \param[in] fr input TRAU frame in decoded form
|
||||
* \returns number of bytes generated in 'out'; negative on error. */
|
||||
static int trau2rtp_efr(uint8_t *out, size_t out_len, const struct osmo_trau_frame *tf, bool emit_twts001)
|
||||
{
|
||||
size_t req_out_len = emit_twts001 ? GSM_EFR_BYTES+1 : GSM_EFR_BYTES;
|
||||
int i, j, rc;
|
||||
ubit_t check_bits[26];
|
||||
uint8_t *twts001_hdr;
|
||||
|
||||
if (out_len < req_out_len)
|
||||
return -ENOSPC;
|
||||
|
||||
if (tf->type != OSMO_TRAU16_FT_EFR)
|
||||
return -EINVAL;
|
||||
|
||||
/* FR Data Bits according to TS 48.060 Section 5.5.1.1.2 */
|
||||
|
||||
/* Are we emitting TW-TS-001? If so, emit TRAU-like Extension Header */
|
||||
if (emit_twts001) {
|
||||
twts001_hdr = out++;
|
||||
*twts001_hdr = 0xE0;
|
||||
if (tf->c_bits[16]) /* DTXd */
|
||||
*twts001_hdr |= 0x08;
|
||||
if (tf->c_bits[11]) /* BFI */
|
||||
*twts001_hdr |= 0x02;
|
||||
if (tf->c_bits[14]) /* TAF */
|
||||
*twts001_hdr |= 0x01;
|
||||
}
|
||||
|
||||
if (tf->c_bits[11] && !emit_twts001) /* BFI without TW-TS-001 */
|
||||
return 0;
|
||||
|
||||
out[0] = 0xc << 4;
|
||||
/* reassemble d-bits */
|
||||
for (i = 1, j = 4; i < 39; i++, j++)
|
||||
out[j/8] |= (tf->d_bits[i] << (7-(j%8)));
|
||||
efr_parity_bits_1(check_bits, tf->d_bits);
|
||||
rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26,
|
||||
tf->d_bits + 39);
|
||||
if (rc)
|
||||
goto bad_frame;
|
||||
for (i = 42, j = 42; i < 95; i++, j++)
|
||||
out[j/8] |= (tf->d_bits[i] << (7-(j%8)));
|
||||
efr_parity_bits_2(check_bits, tf->d_bits);
|
||||
rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12,
|
||||
tf->d_bits + 95);
|
||||
if (rc)
|
||||
goto bad_frame;
|
||||
for (i = 98, j = 95; i < 148; i++, j++)
|
||||
out[j/8] |= (tf->d_bits[i] << (7-(j%8)));
|
||||
efr_parity_bits_3(check_bits, tf->d_bits);
|
||||
rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8,
|
||||
tf->d_bits + 148);
|
||||
if (rc)
|
||||
goto bad_frame;
|
||||
for (i = 151, j = 145; i < 204; i++, j++)
|
||||
out[j/8] |= (tf->d_bits[i] << (7-(j%8)));
|
||||
efr_parity_bits_4(check_bits, tf->d_bits);
|
||||
rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12,
|
||||
tf->d_bits + 204);
|
||||
if (rc)
|
||||
goto bad_frame;
|
||||
for (i = 207, j = 198; i < 257; i++, j++)
|
||||
out[j/8] |= (tf->d_bits[i] << (7-(j%8)));
|
||||
efr_parity_bits_5(check_bits, tf->d_bits);
|
||||
rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8,
|
||||
tf->d_bits + 257);
|
||||
if (rc)
|
||||
goto bad_frame;
|
||||
|
||||
return req_out_len;
|
||||
|
||||
bad_frame:
|
||||
if (emit_twts001) {
|
||||
*twts001_hdr |= 0x06; /* BFI and No_Data flags */
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TS 48.060 Section 5.5.1.1.2 */
|
||||
static int rtp2trau_fr(struct osmo_trau_frame *tf, const uint8_t *data, size_t data_len)
|
||||
{
|
||||
int i, j, k, l, o;
|
||||
enum osmo_gsm631_sid_class sidc;
|
||||
bool bfi = false, taf = false, dtxd = false;
|
||||
|
||||
/* accept TW-TS-001 input */
|
||||
if (data_len > 0 && (data[0] & 0xF0) == 0xE0) {
|
||||
dtxd = (data[0] & 0x08) != 0;
|
||||
bfi = (data[0] & 0x02) != 0;
|
||||
taf = (data[0] & 0x01) != 0;
|
||||
data++;
|
||||
data_len--;
|
||||
}
|
||||
|
||||
/* now we must have either standard FR or no-data input */
|
||||
switch (data_len) {
|
||||
case GSM_FR_BYTES:
|
||||
if ((data[0] & 0xF0) != 0xD0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case 0:
|
||||
bfi = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tf->type = OSMO_TRAU16_FT_FR;
|
||||
|
||||
/* FR Data Bits according to TS 48.060 Section 5.5.1.1.2 */
|
||||
|
||||
/* set c-bits and t-bits */
|
||||
if (tf->dir == OSMO_TRAU_DIR_UL) {
|
||||
/* C1 .. C5: FR UL */
|
||||
tf->c_bits[0] = 0;
|
||||
tf->c_bits[1] = 0;
|
||||
tf->c_bits[2] = 0;
|
||||
tf->c_bits[3] = 1;
|
||||
tf->c_bits[4] = 0;
|
||||
} else { /* DL */
|
||||
if (data_len == 0) {
|
||||
/* C1 .. C5: idle speech */
|
||||
tf->c_bits[0] = 0;
|
||||
tf->c_bits[1] = 1;
|
||||
tf->c_bits[2] = 1;
|
||||
tf->c_bits[3] = 1;
|
||||
tf->c_bits[4] = 0;
|
||||
} else {
|
||||
/* C1 .. C5: FR DL */
|
||||
tf->c_bits[0] = 1;
|
||||
tf->c_bits[1] = 1;
|
||||
tf->c_bits[2] = 1;
|
||||
tf->c_bits[3] = 0;
|
||||
tf->c_bits[4] = 0;
|
||||
}
|
||||
}
|
||||
memset(&tf->c_bits[5], 0, 6); /* C6 .. C11: Time Alignment */
|
||||
if (tf->dir == OSMO_TRAU_DIR_UL) {
|
||||
tf->c_bits[11] = bfi; /* C12: BFI */
|
||||
if (data_len == 0) {
|
||||
tf->c_bits[12] = 0; /* C13: SID=0 */
|
||||
tf->c_bits[13] = 0; /* C14: SID=0 */
|
||||
} else {
|
||||
/* SID classification per GSM 06.31 section 6.1.1 */
|
||||
sidc = osmo_fr_sid_classify(data);
|
||||
tf->c_bits[12] = (sidc >> 1) & 1; /* C13: msb */
|
||||
tf->c_bits[13] = (sidc >> 0) & 1; /* C14: lsb */
|
||||
}
|
||||
tf->c_bits[14] = taf; /* C15: TAF (SACCH or not) */
|
||||
tf->c_bits[15] = 1; /* C16: spare */
|
||||
tf->c_bits[16] = dtxd; /* C17: DTXd applied or not */
|
||||
} else {
|
||||
memset(&tf->c_bits[11], 1, 4); /* C12 .. C15: spare */
|
||||
if (data_len && osmo_fr_check_sid(data, data_len))
|
||||
tf->c_bits[15] = 0; /* C16: SP=0 */
|
||||
else
|
||||
tf->c_bits[15] = 1; /* C16: SP=1 */
|
||||
tf->c_bits[16] = 1; /* C17: spare */
|
||||
}
|
||||
memset(&tf->c_bits[17], 1, 4); /* C18 .. C21: spare */
|
||||
memset(&tf->t_bits[0], 1, 4);
|
||||
|
||||
if (!data_len) {
|
||||
/* idle speech frame if DL, BFI speech frame if UL */
|
||||
memset(&tf->d_bits[0], 1, 260);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reassemble d-bits */
|
||||
i = 0; /* counts bits */
|
||||
j = 4; /* counts input bits */
|
||||
k = gsm_fr_map[0]-1; /* current number bit in element */
|
||||
l = 0; /* counts element bits */
|
||||
o = 0; /* offset output bits */
|
||||
while (i < 260) {
|
||||
tf->d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
|
||||
/* to avoid out-of-bounds access in gsm_fr_map[++l] */
|
||||
if (i == 259)
|
||||
break;
|
||||
if (--k < 0) {
|
||||
o += gsm_fr_map[l];
|
||||
k = gsm_fr_map[++l]-1;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* does the RTP HR payload resemble a SID frame or not */
|
||||
static bool is_rtp_hr_sid(const uint8_t *data, const uint8_t data_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (data_len < GSM_HR_BYTES)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < GSM_HR_BYTES; i++) {
|
||||
if ((data[i] & rtp_hr_sid[i]) != rtp_hr_sid[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int rtp2trau_hr16(struct osmo_trau_frame *tf, const uint8_t *data, size_t data_len)
|
||||
{
|
||||
/* accept both TS 101 318 and RFC 5993 payloads */
|
||||
switch (data_len) {
|
||||
case GSM_HR_BYTES:
|
||||
break;
|
||||
case GSM_HR_BYTES_RTP_RFC5993:
|
||||
data++;
|
||||
data_len--;
|
||||
break;
|
||||
case 0:
|
||||
/* accept no-data input */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tf->type = OSMO_TRAU16_FT_HR;
|
||||
|
||||
if (tf->dir == OSMO_TRAU_DIR_UL) {
|
||||
/* C1 .. C5 */
|
||||
tf->c_bits[0] = 0;
|
||||
tf->c_bits[1] = 0;
|
||||
tf->c_bits[2] = 0;
|
||||
tf->c_bits[3] = 1;
|
||||
tf->c_bits[4] = 1;
|
||||
} else {
|
||||
/* C1 .. C5 */
|
||||
tf->c_bits[0] = 1;
|
||||
tf->c_bits[1] = 1;
|
||||
tf->c_bits[2] = 1;
|
||||
tf->c_bits[3] = 0;
|
||||
tf->c_bits[4] = 1;
|
||||
}
|
||||
/* C6.. C11: Time Alignment */
|
||||
memset(tf->c_bits + 5, 0, 6);
|
||||
if (tf->dir == OSMO_TRAU_DIR_UL) {
|
||||
/* BFI */
|
||||
if (data_len == 0)
|
||||
tf->c_bits[11] = 1;
|
||||
else
|
||||
tf->c_bits[11] = 0;
|
||||
if (is_rtp_hr_sid(data, data_len)) {
|
||||
/* SID=2 is a valid SID frame */
|
||||
tf->c_bits[12] = 1;
|
||||
tf->c_bits[13] = 0;
|
||||
} else {
|
||||
tf->c_bits[12] = 0;
|
||||
tf->c_bits[13] = 0;
|
||||
}
|
||||
/* FIXME: C15: TAF */
|
||||
tf->c_bits[15] = 0; /* C16: SP */
|
||||
tf->c_bits[16] = 0; /* C17: DTXd shall not be applied */
|
||||
} else {
|
||||
tf->c_bits[11] = 1; /* C12: UFE */
|
||||
tf->c_bits[12] = 1; /* C13: spare */
|
||||
tf->c_bits[13] = 1; /* C14: spare */
|
||||
tf->c_bits[14] = 1; /* C15: spare */
|
||||
if (is_rtp_hr_sid(data, data_len))
|
||||
tf->c_bits[15] = 0; /* C16: SP */
|
||||
else
|
||||
tf->c_bits[15] = 1; /* C16: SP */
|
||||
tf->c_bits[16] = 1; /* C17: spare */
|
||||
}
|
||||
memset(tf->c_bits+17, 1, 4); /* C18..C21: spare */
|
||||
memset(&tf->t_bits[0], 1, 4);
|
||||
if (tf->dir == OSMO_TRAU_DIR_UL)
|
||||
tf->ufi = 0;
|
||||
else
|
||||
tf->ufi = 1;
|
||||
if (data_len)
|
||||
osmo_pbit2ubit(tf->d_bits, data, GSM_HR_BYTES * 8);
|
||||
else
|
||||
memset(tf->d_bits, 0, GSM_HR_BYTES * 8);
|
||||
/* CRC is *not* computed by TRAU frame encoder - we have to do it */
|
||||
osmo_crc8gen_set_bits(&gsm0860_efr_crc3, tf->d_bits, 44, tf->crc_bits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TS 48.060 Section 5.5.1.1.2 */
|
||||
static int rtp2trau_efr(struct osmo_trau_frame *tf, const uint8_t *data, size_t data_len)
|
||||
{
|
||||
int i, j;
|
||||
ubit_t check_bits[26];
|
||||
enum osmo_gsm631_sid_class sidc;
|
||||
bool bfi = false, taf = false, dtxd = false;
|
||||
|
||||
/* accept TW-TS-001 input */
|
||||
if (data_len > 0 && (data[0] & 0xF0) == 0xE0) {
|
||||
dtxd = (data[0] & 0x08) != 0;
|
||||
bfi = (data[0] & 0x02) != 0;
|
||||
taf = (data[0] & 0x01) != 0;
|
||||
data++;
|
||||
data_len--;
|
||||
}
|
||||
|
||||
/* now we must have either standard EFR or no-data input */
|
||||
switch (data_len) {
|
||||
case GSM_EFR_BYTES:
|
||||
if ((data[0] & 0xF0) != 0xC0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case 0:
|
||||
bfi = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tf->type = OSMO_TRAU16_FT_EFR;
|
||||
|
||||
/* FR Data Bits according to TS 48.060 Section 5.5.1.1.2 */
|
||||
|
||||
/* set c-bits and t-bits */
|
||||
if (data_len == 0 && tf->dir == OSMO_TRAU_DIR_DL) {
|
||||
/* C1 .. C5: idle speech */
|
||||
tf->c_bits[0] = 0;
|
||||
tf->c_bits[1] = 1;
|
||||
tf->c_bits[2] = 1;
|
||||
tf->c_bits[3] = 1;
|
||||
tf->c_bits[4] = 0;
|
||||
} else {
|
||||
/* C1 .. C5: EFR */
|
||||
tf->c_bits[0] = 1;
|
||||
tf->c_bits[1] = 1;
|
||||
tf->c_bits[2] = 0;
|
||||
tf->c_bits[3] = 1;
|
||||
tf->c_bits[4] = 0;
|
||||
}
|
||||
|
||||
memset(&tf->c_bits[5], 0, 6); /* C6 .. C11: Time Alignment */
|
||||
if (tf->dir == OSMO_TRAU_DIR_UL) {
|
||||
tf->c_bits[11] = bfi; /* C12: BFI */
|
||||
if (data_len == 0) {
|
||||
tf->c_bits[12] = 0; /* C13: SID=0 */
|
||||
tf->c_bits[13] = 0; /* C14: SID=0 */
|
||||
} else {
|
||||
/* SID classification per GSM 06.81 section 6.1.1 */
|
||||
sidc = osmo_efr_sid_classify(data);
|
||||
tf->c_bits[12] = (sidc >> 1) & 1; /* C13: msb */
|
||||
tf->c_bits[13] = (sidc >> 0) & 1; /* C14: lsb */
|
||||
}
|
||||
tf->c_bits[14] = taf; /* C15: TAF (SACCH or not) */
|
||||
tf->c_bits[15] = 1; /* C16: spare */
|
||||
tf->c_bits[16] = dtxd; /* C17: DTXd applied or not */
|
||||
} else {
|
||||
tf->c_bits[11] = 1; /* C12: UFE (good uplink) */
|
||||
memset(&tf->c_bits[12], 1, 3); /* C13 .. C15: spare */
|
||||
if (data_len && osmo_efr_check_sid(data, data_len))
|
||||
tf->c_bits[15] = 0; /* C16: SP=0 */
|
||||
else
|
||||
tf->c_bits[15] = 1; /* C16: SP=1 */
|
||||
tf->c_bits[16] = 1; /* C17: spare */
|
||||
}
|
||||
memset(&tf->c_bits[17], 1, 4); /* C18 .. C21: spare */
|
||||
memset(&tf->t_bits[0], 1, 4);
|
||||
|
||||
if (data_len == 0) {
|
||||
/* idle speech frame if DL, BFI speech frame if UL */
|
||||
memset(&tf->d_bits[0], 1, 260);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reassemble d-bits */
|
||||
tf->d_bits[0] = 1;
|
||||
for (i = 1, j = 4; i < 39; i++, j++)
|
||||
tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
|
||||
efr_parity_bits_1(check_bits, tf->d_bits);
|
||||
osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26,
|
||||
tf->d_bits + 39);
|
||||
for (i = 42, j = 42; i < 95; i++, j++)
|
||||
tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
|
||||
efr_parity_bits_2(check_bits, tf->d_bits);
|
||||
osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12,
|
||||
tf->d_bits + 95);
|
||||
for (i = 98, j = 95; i < 148; i++, j++)
|
||||
tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
|
||||
efr_parity_bits_3(check_bits, tf->d_bits);
|
||||
osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8,
|
||||
tf->d_bits + 148);
|
||||
for (i = 151, j = 145; i < 204; i++, j++)
|
||||
tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
|
||||
efr_parity_bits_4(check_bits, tf->d_bits);
|
||||
osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12,
|
||||
tf->d_bits + 204);
|
||||
for (i = 207, j = 198; i < 257; i++, j++)
|
||||
tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1;
|
||||
efr_parity_bits_5(check_bits, tf->d_bits);
|
||||
osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8,
|
||||
tf->d_bits + 257);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static inline memcpy_inc(uint8_t *out, const uint8_t *in, size_t len, unsigned int *idx)
|
||||
{
|
||||
memcpy_inc(out, in, len);
|
||||
*idx += len;
|
||||
}
|
||||
|
||||
static int amr_speech_extract_sbits(ubit_t *s_bits, const struct osmo_trau_frame *tf,
|
||||
enum osmo_amr_mode mode)
|
||||
{
|
||||
unsigned int s_idx = 0;
|
||||
|
||||
switch (mode) {
|
||||
case AMR_4_75:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 44, 67 - 44, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 67, 92 - 67, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 95, 108 - 95, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 111, 132 - 111, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 135, 148 - 135, &s_idx);
|
||||
break;
|
||||
case AMR_5_15:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 46, 96 - 46, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 69, 92 - 69, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 95, 114 - 95, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 117, 136 - 117, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 139, 158 - 139, &s_idx);
|
||||
break;
|
||||
case AMR_5_90:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 41, 67 - 41, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 67, 92 - 67, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 95, 116 - 95, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 119, 144 - 119, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 147, 168 - 147, &s_idx);
|
||||
break;
|
||||
case AMR_6_70:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 37, 63 - 37, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 63, 92 - 63, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 95, 120 - 95, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 123, 152 - 123, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 155, 180 - 155, &s_idx);
|
||||
break;
|
||||
case AMR_7_40:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 34, 60 - 34, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 60, 92 - 60, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 95, 124 - 95, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 127, 159 - 127, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 162, 191 - 162, &s_idx);
|
||||
break;
|
||||
case AMR_7_95:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 31, 58 - 31, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 58, 92 - 58, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 95, 127 - 95, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 130, 164 - 130, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 167, 199 - 167, &s_idx);
|
||||
break;
|
||||
case AMR_10_2:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 20, 46 - 20, &s_idx); /* D21..D46 */
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 46, 92 - 46, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 95, 138 - 95, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 141, 187 - 141, &s_idx);
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 190, 233 - 190, &s_idx);
|
||||
break;
|
||||
case AMR_12_2:
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 0, 38 - 0, &s_idx); /* D1..D38 */
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 38, 91 - 38, &s_idx); /* D39..D91 */
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 94, 144 - 94, &s_idx); /* D95..D144 */
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 147, 200 - 147, &s_idx);/* D148..D200 */
|
||||
memcpy_inc(s_bits + s_idx, tf->d_bits + 203, 253 - 203, &s_idx);/* D204..D253 */
|
||||
break;
|
||||
}
|
||||
|
||||
return s_idx;
|
||||
}
|
||||
|
||||
/* TS 48.060 Section 5.5.1.2.2 */
|
||||
static int trau2rtp_16(uint8_t *out, const struct osmo_trau_frame *tf, enum osmo_amr_mode last_cmi)
|
||||
{
|
||||
enum osmo_amr_mode mode = last_cmi;
|
||||
uint8_t frame_class = tf->c_bits[21] << 1 | tf->c_bits[20];
|
||||
uint8_t cmr_cmi = tf->c_bits[23] << 2 | tf->c_bits[24] << 1 | tf->cb_bits[25];
|
||||
uint8_t no_speech_cls;
|
||||
uint8_t s_bits[242];
|
||||
uint8_t d_bits[242];
|
||||
unsigned int s_idx = 0;
|
||||
ubit_t rif = FIXME;
|
||||
|
||||
if (tf->type != OSMO_TRAU16_FT_AMR)
|
||||
return -EINVAL;
|
||||
|
||||
if (rif == 0)
|
||||
mode = cmr_cmi;
|
||||
|
||||
switch (frame_class) {
|
||||
case 0: // no speech
|
||||
no_speech_cls = tf->d_bits[32] << 2 | tf->d_bits[33] << 1 | tf->d_bits[34];
|
||||
cmi_abs = tf->d_bits[35] << 2 | tf->d_bits[36] < 1 || tf->d_bits[37];
|
||||
cmr_abs = tf->d_bits[38] << 2 | tf->d_bits[39] < 1 || tf->d_bits[40];
|
||||
switch (no_speech_cls) {
|
||||
case 7: // sid first
|
||||
break;
|
||||
case 6: // onset
|
||||
break;
|
||||
case 5: // sid_update
|
||||
break;
|
||||
case 4: // sid_bad
|
||||
break;
|
||||
case 0: // no_data
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1: // speech bad
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
/* Extract the s-bits from the TRAU frame */
|
||||
amr_speech_extract_sbits(s_bits, tf, mode);
|
||||
/* Convert the s-bits to d-bits */
|
||||
osmo_amr_s_to_d(d_bits, s_bits, mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int trau2rtp_amr(uint8_t *out, const struct osmo_trau_frame *tf, enum osmo_amr_mode last_cmi))
|
||||
{
|
||||
switch (tf->type) {
|
||||
case OSMO_TRAU16_FT_AMR:
|
||||
return trau2rtp_16(out, tf, last_cmi);
|
||||
case OSMO_TRAU8_AMR_LOW:
|
||||
case OSMO_TRAU8_AMR_6k7:
|
||||
case OSMO_TRAU8_AMR_7k4:
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool check_twts001(struct osmo_trau2rtp_state *st)
|
||||
{
|
||||
if (st->rtp_extensions & OSMO_RTP_EXT_TWTS001)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool check_twts002(struct osmo_trau2rtp_state *st)
|
||||
{
|
||||
if (st->rtp_extensions & OSMO_RTP_EXT_TWTS002)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int osmo_trau2rtp(uint8_t *out, size_t out_len, const struct osmo_trau_frame *tf,
|
||||
struct osmo_trau2rtp_state *st)
|
||||
{
|
||||
switch (tf->type) {
|
||||
case OSMO_TRAU16_FT_FR:
|
||||
return trau2rtp_fr(out, out_len, tf, check_twts001(st));
|
||||
case OSMO_TRAU16_FT_EFR:
|
||||
return trau2rtp_efr(out, out_len, tf, check_twts001(st));
|
||||
case OSMO_TRAU16_FT_HR:
|
||||
return trau2rtp_hr16(out, out_len, tf, check_twts002(st));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int osmo_rtp2trau(struct osmo_trau_frame *tf, const uint8_t *rtp, size_t rtp_len,
|
||||
struct osmo_trau2rtp_state *st)
|
||||
{
|
||||
switch (st->type) {
|
||||
case OSMO_TRAU16_FT_FR:
|
||||
return rtp2trau_fr(tf, rtp, rtp_len);
|
||||
case OSMO_TRAU16_FT_EFR:
|
||||
return rtp2trau_efr(tf, rtp, rtp_len);
|
||||
case OSMO_TRAU16_FT_HR:
|
||||
return rtp2trau_hr16(tf, rtp, rtp_len);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,696 @@
|
|||
/* GSM A-bis TRAU frame synchronization as per TS 48.060 / 48.061 */
|
||||
|
||||
/* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include "ubit_buf.h"
|
||||
#include <osmocom/trau/trau_sync.h>
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
#define MAX_TRAU_BYTES 160
|
||||
#define MAX_TRAU_SYNC_PATTERN 8
|
||||
#define PRIMARY_PATTERN 0
|
||||
|
||||
#define T_SYNC 1
|
||||
|
||||
struct sync_pattern {
|
||||
/* provided by user */
|
||||
const char *name; /*!< human-readable name */
|
||||
const uint8_t byte_pattern[MAX_TRAU_BYTES]; /*!< bytes to match against */
|
||||
const uint8_t byte_mask[MAX_TRAU_BYTES]; /*!< mask applied before matching */
|
||||
uint8_t byte_len; /*!< length of mask in bytes */
|
||||
|
||||
/* generated by code */
|
||||
ubit_t ubit_pattern[MAX_TRAU_BYTES*8]; /*!< bits to match against */
|
||||
ubit_t ubit_mask[MAX_TRAU_BYTES*8]; /*!< mask applied before matching */
|
||||
uint8_t bitcount; /*!< number of high bits in mask */
|
||||
};
|
||||
|
||||
static struct sync_pattern sync_patterns[] = {
|
||||
[OSMO_TRAU_SYNCP_16_FR_EFR] = {
|
||||
/* TS 08.60 Section 4.8.1 */
|
||||
.name = "FR/EFR",
|
||||
.byte_pattern = {
|
||||
0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0xff, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
},
|
||||
.byte_len = 40,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_8_HR] = {
|
||||
/* TS 08.61 Section 6.8.2.1.1 */
|
||||
.name = "HR8",
|
||||
.byte_pattern = {
|
||||
0x00, 0x80, 0x40, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0x80, 0xC0, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
},
|
||||
.byte_len = 20,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_8_AMR_LOW] = {
|
||||
/* TS 08.61 Section 6.8.2.1.2 */
|
||||
/* The frame synchronisation for No_Speech frames and the speech frames of the three lower codec modes */
|
||||
.name = "AMR8_LOW",
|
||||
.byte_pattern = {
|
||||
0x00, 0x80, 0x80, 0x40,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0x80, 0x80, 0xC0,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80,
|
||||
},
|
||||
.byte_len = 20,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_8_AMR_6K7] = {
|
||||
/* The frame synchronisation for the speech frames for codec mode 6,70 kBit/s */
|
||||
.name = "AMR8_67",
|
||||
.byte_pattern = {
|
||||
0x00, 0x80, 0x80, 0x80,
|
||||
0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00,
|
||||
},
|
||||
.byte_len = 20
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_8_AMR_7K4] = {
|
||||
/* The frame synchronisation for the speech frames for codec mode 7,40 kBit/s */
|
||||
.name = "AMR8_74",
|
||||
.byte_pattern = {
|
||||
0x20, 0x00, 0x80, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xe0, 0x80, 0x80, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_len = 20,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_V110] = {
|
||||
/* See Table 2 of ITU-T V.110 */
|
||||
.name = "V110",
|
||||
.byte_pattern = {
|
||||
0x00, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0x80, 0x80, 0x80, 0x80,
|
||||
0x80, 0x80, 0x80, 0x80, 0x80,
|
||||
},
|
||||
.byte_len = 10,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_16_ER_CCU] = {
|
||||
.name = "Ericsson CCU 16 kbps",
|
||||
.byte_pattern = {
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_len = 40,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_64_ER_CCU] = {
|
||||
.name = "Ericsson CCU 64 kbps",
|
||||
.byte_pattern = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_len = 160,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_64_ER_CCU_MCS9] = {
|
||||
.name = "Ericsson CCU 64 kbps MCS9",
|
||||
.byte_pattern = {
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
.byte_len = 160,
|
||||
},
|
||||
[OSMO_TRAU_SYNCP_FA] = {
|
||||
/* See Section 5.2.2.1 of 3GPP TS 43.045 */
|
||||
.name = "FA",
|
||||
.byte_pattern = {
|
||||
0x3E, 0x37, 0x50, 0x96,
|
||||
0xC1, 0xC8, 0xAF, 0x69,
|
||||
},
|
||||
.byte_mask = {
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
},
|
||||
.byte_len = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static void expand_sync_pattern(struct sync_pattern *pat)
|
||||
{
|
||||
osmo_pbit2ubit(pat->ubit_pattern, pat->byte_pattern, pat->byte_len*8);
|
||||
osmo_pbit2ubit(pat->ubit_mask, pat->byte_mask, pat->byte_len*8);
|
||||
}
|
||||
|
||||
static unsigned int count_one_bits(const ubit_t *in, unsigned int in_bits)
|
||||
{
|
||||
unsigned int i, count = 0;
|
||||
|
||||
for (i = 0; i < in_bits; i++) {
|
||||
if (in[i])
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void sync_pattern_register(struct sync_pattern *p)
|
||||
{
|
||||
expand_sync_pattern(p);
|
||||
p->bitcount = count_one_bits(p->ubit_mask, p->byte_len*8);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*! correlate pattern with unpacked bits from buffer.
|
||||
* \param[in] pattern sync_pattern against which we shall compare
|
||||
* \param[in] bits unpacked bits to compare against pattern
|
||||
* \param[in] num_bits number of unpacked bits
|
||||
* \returns number of bits not matching pattern; -1 if insufficient bits available. */
|
||||
static int correlate_pattern_ubits(const struct sync_pattern *pattern,
|
||||
const ubit_t *bits, size_t num_bits)
|
||||
{
|
||||
int i, num_wrong = 0;
|
||||
|
||||
if (num_bits < pattern->byte_len*8)
|
||||
return -1; /* insufficient data */
|
||||
|
||||
for (i = 0; i < pattern->byte_len *8; i++) {
|
||||
/* if mask doesn't contain '1', we can skip this octet */
|
||||
if (!pattern->ubit_mask)
|
||||
continue;
|
||||
if (bits[i] != pattern->ubit_pattern[i])
|
||||
num_wrong++;
|
||||
}
|
||||
|
||||
return num_wrong;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct trau_rx_sync_state {
|
||||
/*! call-back to be called for every TRAU frame (called with
|
||||
* bits=NULL in case of frame sync loss */
|
||||
frame_out_cb_t out_cb;
|
||||
/*! opaque user data; passed to out_cb */
|
||||
void *user_data;
|
||||
|
||||
/*! history of received bits */
|
||||
ubit_t history[MAX_TRAU_BYTES*8+1]; /* +1 not required, but helps to expose bugs */
|
||||
/*! index of next-to-be-written ubit in history */
|
||||
unsigned int history_idx;
|
||||
/*! the pattern(s) we are trying to sync to */
|
||||
const struct sync_pattern *pattern[MAX_TRAU_SYNC_PATTERN];
|
||||
/*! number of consecutive frames without sync */
|
||||
unsigned int num_consecutive_errors;
|
||||
};
|
||||
|
||||
/* correlate the history (up to the last received bit) against the pattern */
|
||||
static int correlate_history_against_pattern(struct trau_rx_sync_state *tss, const struct sync_pattern *pattern)
|
||||
{
|
||||
int i, start, num_wrong = 0;
|
||||
|
||||
/* compute index of first bit in history array */
|
||||
start = (ARRAY_SIZE(tss->history) + tss->history_idx - pattern->byte_len*8)
|
||||
% ARRAY_SIZE(tss->history);
|
||||
|
||||
OSMO_ASSERT(ARRAY_SIZE(tss->history) >= pattern->byte_len*8);
|
||||
|
||||
for (i = 0; i < pattern->byte_len*8; i++) {
|
||||
unsigned int pos = (start + i) % ARRAY_SIZE(tss->history);
|
||||
|
||||
/* if mask doesn't contain '1', we can skip this octet */
|
||||
if (!pattern->ubit_mask[i])
|
||||
continue;
|
||||
if (tss->history[pos] != pattern->ubit_pattern[i])
|
||||
num_wrong++;
|
||||
}
|
||||
|
||||
return num_wrong;
|
||||
}
|
||||
|
||||
/* correlate the history (up to the last received bit) against multiple patterns */
|
||||
static int correlate_history_against_patterns(struct trau_rx_sync_state *tss)
|
||||
{
|
||||
size_t pat_index;
|
||||
int num_wrong;
|
||||
int num_wrong_best = MAX_TRAU_BYTES * 8;
|
||||
|
||||
for (pat_index = PRIMARY_PATTERN; pat_index < MAX_TRAU_SYNC_PATTERN; pat_index++) {
|
||||
const struct sync_pattern *pattern;
|
||||
|
||||
pattern = tss->pattern[pat_index];
|
||||
if (!pattern)
|
||||
continue;
|
||||
num_wrong = correlate_history_against_pattern(tss, pattern);
|
||||
|
||||
/* We cannot achieve a better result than 0 errors */
|
||||
if (num_wrong == 0)
|
||||
return 0;
|
||||
|
||||
/* Keep track on the best result */
|
||||
if (num_wrong < num_wrong_best)
|
||||
num_wrong_best = num_wrong;
|
||||
}
|
||||
|
||||
return num_wrong_best;
|
||||
}
|
||||
|
||||
/* add (append) one ubit to the history; wrap as needed */
|
||||
static void rx_history_add_bit(struct trau_rx_sync_state *tss, ubit_t bit)
|
||||
{
|
||||
tss->history[tss->history_idx] = bit;
|
||||
/* simply wrap around at the end */
|
||||
tss->history_idx = (tss->history_idx + 1) % ARRAY_SIZE(tss->history);
|
||||
}
|
||||
|
||||
/* append bits to history. We assume that this does NOT wrap */
|
||||
static void rx_history_add_bits(struct trau_rx_sync_state *tss, const ubit_t *bits, size_t n_bits)
|
||||
{
|
||||
unsigned int frame_bits_remaining = tss->pattern[PRIMARY_PATTERN]->byte_len*8 - tss->history_idx;
|
||||
OSMO_ASSERT(frame_bits_remaining >= n_bits);
|
||||
memcpy(&tss->history[tss->history_idx], bits, n_bits);
|
||||
tss->history_idx = tss->history_idx + n_bits;
|
||||
}
|
||||
|
||||
/* align the history, i.e. next received bit is start of frame */
|
||||
static void rx_history_align(struct trau_rx_sync_state *tss)
|
||||
{
|
||||
ubit_t tmp[sizeof(tss->history)];
|
||||
size_t history_size = sizeof(tss->history);
|
||||
size_t pattern_bits = tss->pattern[PRIMARY_PATTERN]->byte_len*8;
|
||||
size_t first_bit = (history_size + tss->history_idx - pattern_bits) % history_size;
|
||||
int i;
|
||||
|
||||
/* we need to shift the last received frame to the start of the history buffer;
|
||||
* do this in two steps: First copy to a local buffer on the stack, using modulo-arithmetic
|
||||
* as index into the history. Second, copy it back to history */
|
||||
|
||||
for (i = 0; i < pattern_bits; i++)
|
||||
tmp[i] = tss->history[(first_bit + i) % history_size];
|
||||
|
||||
memcpy(tss->history, tmp, history_size);
|
||||
tss->history_idx = 0;
|
||||
}
|
||||
|
||||
enum trau_sync_state {
|
||||
WAIT_FRAME_ALIGN,
|
||||
FRAME_ALIGNED,
|
||||
/* if at least 3 consecutive frames with each at least one framing error have been received */
|
||||
FRAME_ALIGNMENT_LOST,
|
||||
};
|
||||
|
||||
enum trau_sync_event {
|
||||
TRAUSYNC_E_RESET,
|
||||
/*! a buffer of bits was received (msgb with ubits) */
|
||||
TRAUSYNC_E_RX_BITS,
|
||||
};
|
||||
|
||||
static const struct value_string trau_sync_event_names[] = {
|
||||
{ TRAUSYNC_E_RESET, "RESET" },
|
||||
{ TRAUSYNC_E_RX_BITS, "RX_BITS" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
static void trau_sync_wait_align(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
|
||||
struct ubit_buf *ubb;
|
||||
|
||||
switch (event) {
|
||||
case TRAUSYNC_E_RX_BITS:
|
||||
ubb = data;
|
||||
/* append every bit individually + check if we have sync */
|
||||
while (ubb_length(ubb) > 0) {
|
||||
ubit_t bit = ubb_pull_ubit(ubb);
|
||||
int rc;
|
||||
|
||||
rx_history_add_bit(tss, bit);
|
||||
|
||||
/* Apply only the primary pattern while waiting for the alignment */
|
||||
rc = correlate_history_against_pattern(tss, tss->pattern[PRIMARY_PATTERN]);
|
||||
if (!rc) {
|
||||
osmo_fsm_inst_state_chg(fi, FRAME_ALIGNED, 0, 0);
|
||||
/* treat remainder of input bits in correct state */
|
||||
osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, ubb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void trau_sync_aligned_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
|
||||
/* dispatch aligned frame to user */
|
||||
rx_history_align(tss);
|
||||
tss->out_cb(tss->user_data, tss->history, tss->pattern[PRIMARY_PATTERN]->byte_len*8);
|
||||
}
|
||||
|
||||
static void trau_sync_aligned(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct trau_rx_sync_state *tss = (struct trau_rx_sync_state *) fi->priv;
|
||||
struct ubit_buf *ubb;
|
||||
int rc;
|
||||
|
||||
switch (event) {
|
||||
case TRAUSYNC_E_RX_BITS:
|
||||
ubb = data;
|
||||
while (ubb_length(ubb)) {
|
||||
unsigned int frame_bits_remaining = tss->pattern[PRIMARY_PATTERN]->byte_len*8 - tss->history_idx;
|
||||
if (ubb_length(ubb) < frame_bits_remaining) {
|
||||
/* frame not filled by this message; just add data */
|
||||
rx_history_add_bits(tss, ubb_data(ubb), ubb_length(ubb));
|
||||
ubb_pull(ubb, ubb_length(ubb));
|
||||
} else {
|
||||
/* append as many bits as are missing in the current frame */
|
||||
rx_history_add_bits(tss, ubb_data(ubb), frame_bits_remaining);
|
||||
ubb_pull(ubb, frame_bits_remaining);
|
||||
|
||||
/* check if we still have frame sync using the primary and all secondary patterns */
|
||||
rc = correlate_history_against_patterns(tss);
|
||||
if (rc > 0) {
|
||||
tss->num_consecutive_errors++;
|
||||
if (tss->num_consecutive_errors >= 3) {
|
||||
tss->history_idx = 0;
|
||||
/* send NULL frame to user */
|
||||
tss->out_cb(tss->user_data, NULL, 0);
|
||||
osmo_fsm_inst_state_chg(fi, FRAME_ALIGNMENT_LOST, 1, T_SYNC);
|
||||
osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, ubb);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
tss->num_consecutive_errors = 0;
|
||||
|
||||
/* dispatch aligned frame to user */
|
||||
tss->out_cb(tss->user_data, tss->history, tss->history_idx);
|
||||
tss->history_idx = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void trau_sync_alignment_lost(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
/* we try to restore sync for some amount of time before generating an error */
|
||||
|
||||
switch (event) {
|
||||
case TRAUSYNC_E_RX_BITS:
|
||||
trau_sync_wait_align(fi, event, data);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void trau_sync_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case TRAUSYNC_E_RESET:
|
||||
osmo_fsm_inst_state_chg(fi, WAIT_FRAME_ALIGN, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int trau_sync_timeout(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
switch (fi->T) {
|
||||
case T_SYNC:
|
||||
/* if Tsync expires before frame synchronization is
|
||||
* again obtained the TRAU initiates sending of the
|
||||
* urgent alarm pattern described in clause 4.10.2. */
|
||||
osmo_fsm_inst_state_chg(fi, WAIT_FRAME_ALIGN, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state trau_sync_states[] = {
|
||||
[WAIT_FRAME_ALIGN] = {
|
||||
.name = "WAIT_FRAME_ALIGN",
|
||||
.in_event_mask = S(TRAUSYNC_E_RX_BITS),
|
||||
.out_state_mask = S(FRAME_ALIGNED),
|
||||
.action = trau_sync_wait_align,
|
||||
},
|
||||
[FRAME_ALIGNED] = {
|
||||
.name = "FRAME_ALIGNED",
|
||||
.in_event_mask = S(TRAUSYNC_E_RX_BITS),
|
||||
.out_state_mask = S(FRAME_ALIGNMENT_LOST) | S(WAIT_FRAME_ALIGN),
|
||||
.action = trau_sync_aligned,
|
||||
.onenter = trau_sync_aligned_onenter,
|
||||
},
|
||||
[FRAME_ALIGNMENT_LOST] = {
|
||||
.name = "FRAME_ALIGNMENT_LOST",
|
||||
.in_event_mask = S(TRAUSYNC_E_RX_BITS),
|
||||
.out_state_mask = S(WAIT_FRAME_ALIGN) | S(FRAME_ALIGNED),
|
||||
.action = trau_sync_alignment_lost,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm trau_sync_fsm = {
|
||||
.name = "trau_sync",
|
||||
.states = trau_sync_states,
|
||||
.num_states = ARRAY_SIZE(trau_sync_states),
|
||||
.allstate_event_mask = S(TRAUSYNC_E_RESET),
|
||||
.allstate_action = trau_sync_allstate,
|
||||
.timer_cb = trau_sync_timeout,
|
||||
.log_subsys = DLGLOBAL,
|
||||
.event_names = trau_sync_event_names,
|
||||
};
|
||||
|
||||
|
||||
struct osmo_fsm_inst *
|
||||
osmo_trau_sync_alloc(void *ctx, const char *name, frame_out_cb_t frame_out_cb,
|
||||
enum osmo_trau_sync_pat_id pat_id, void *user_data)
|
||||
{
|
||||
struct trau_rx_sync_state *tss;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
if (pat_id >= ARRAY_SIZE(sync_patterns))
|
||||
return NULL;
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&trau_sync_fsm, ctx, NULL, LOGL_INFO, name);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
tss = talloc_zero(fi, struct trau_rx_sync_state);
|
||||
if (!tss) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return NULL;
|
||||
}
|
||||
fi->priv = tss;
|
||||
|
||||
tss->out_cb = frame_out_cb;
|
||||
tss->user_data = user_data;
|
||||
tss->pattern[PRIMARY_PATTERN] = &sync_patterns[pat_id];
|
||||
|
||||
/* An unusued E1 timeslot normally would send an idle signal that
|
||||
* has all bits set to one. In order to prevent false-positive
|
||||
* synchronization on startup we set all history bits to 1, to make
|
||||
* it look like a signal from an unused timeslot. */
|
||||
memset(tss->history, 1, sizeof(tss->history));
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
void osmo_trau_sync_set_pat(struct osmo_fsm_inst *fi, enum osmo_trau_sync_pat_id pat_id)
|
||||
{
|
||||
struct trau_rx_sync_state *tss = fi->priv;
|
||||
|
||||
/* Clear the pattern list to get rid of all secondary pattern settings */
|
||||
memset(tss->pattern, 0, sizeof(tss->pattern));
|
||||
|
||||
/* Set the primary pattern and reset the FSM */
|
||||
tss->pattern[PRIMARY_PATTERN] = &sync_patterns[pat_id];
|
||||
osmo_fsm_inst_state_chg(fi, FRAME_ALIGNMENT_LOST, 0, 0);
|
||||
}
|
||||
|
||||
void osmo_trau_sync_set_secondary_pat(struct osmo_fsm_inst *fi, enum osmo_trau_sync_pat_id pat_id, size_t pat_index)
|
||||
{
|
||||
struct trau_rx_sync_state *tss = fi->priv;
|
||||
|
||||
OSMO_ASSERT(pat_index > PRIMARY_PATTERN);
|
||||
OSMO_ASSERT(pat_index < ARRAY_SIZE(tss->pattern));
|
||||
|
||||
/* Make sure that only a pattern of the same size can be set as secondary pattern */
|
||||
OSMO_ASSERT(tss->pattern[PRIMARY_PATTERN]->byte_len == sync_patterns[pat_id].byte_len);
|
||||
|
||||
tss->pattern[pat_index] = &sync_patterns[pat_id];
|
||||
osmo_fsm_inst_state_chg(fi, FRAME_ALIGNMENT_LOST, 0, 0);
|
||||
}
|
||||
|
||||
void osmo_trau_sync_rx_ubits(struct osmo_fsm_inst *fi, const ubit_t *bits, size_t n_bits)
|
||||
{
|
||||
struct ubit_buf ubb;
|
||||
ubb_init(&ubb, bits, n_bits);
|
||||
osmo_fsm_inst_dispatch(fi, TRAUSYNC_E_RX_BITS, &ubb);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) on_dso_load_sync(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sync_patterns); i++)
|
||||
sync_pattern_register(&sync_patterns[i]);
|
||||
OSMO_ASSERT(osmo_fsm_register(&trau_sync_fsm) == 0);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
/* Small helper inspired by msgb */
|
||||
|
||||
struct ubit_buf {
|
||||
const ubit_t *buf; /*!< start of underlying buffer */
|
||||
const ubit_t *data; /*!< next to be consumed bit */
|
||||
size_t n_bits; /*!< number of total bits iin buffer */
|
||||
};
|
||||
|
||||
/*! length of [remainig, to be processed] data in ubit_buf */
|
||||
static inline size_t ubb_length(struct ubit_buf *ubb)
|
||||
{
|
||||
return ubb->n_bits - (ubb->data - ubb->buf);
|
||||
}
|
||||
|
||||
/*! retrieve + remove a single ubit_t from start of ubit_buf */
|
||||
static inline ubit_t ubb_pull_ubit(struct ubit_buf *ubb)
|
||||
{
|
||||
OSMO_ASSERT(ubb->data < ubb->buf + ubb->n_bits);
|
||||
return *ubb->data++;
|
||||
}
|
||||
|
||||
static inline void ubb_pull(struct ubit_buf *ubb, size_t count)
|
||||
{
|
||||
OSMO_ASSERT(ubb_length(ubb) >= count);
|
||||
ubb->data += count;
|
||||
}
|
||||
|
||||
/*! get pointer to next to be consumed bit */
|
||||
static inline const ubit_t *ubb_data(struct ubit_buf *ubb)
|
||||
{
|
||||
return ubb->data;
|
||||
}
|
||||
|
||||
static inline void ubb_init(struct ubit_buf *ubb, const ubit_t *bits, size_t n_bits)
|
||||
{
|
||||
ubb->buf = bits;
|
||||
ubb->data = ubb->buf;
|
||||
ubb->n_bits = n_bits;
|
||||
}
|
109
src/trau_frame.c
109
src/trau_frame.c
|
@ -3,6 +3,8 @@
|
|||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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
|
||||
|
@ -29,7 +31,13 @@
|
|||
#include <osmocom/abis/subchan_demux.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
static uint32_t get_bits(const uint8_t *bitbuf, int offset, int num)
|
||||
/*! \addtogroup trau_frame
|
||||
* @{
|
||||
*
|
||||
* \file trau_frame.c
|
||||
*/
|
||||
|
||||
static uint32_t get_bits(const ubit_t *bitbuf, int offset, int num)
|
||||
{
|
||||
int i;
|
||||
uint32_t ret = 0;
|
||||
|
@ -43,7 +51,7 @@ static uint32_t get_bits(const uint8_t *bitbuf, int offset, int num)
|
|||
}
|
||||
|
||||
/* Decode according to 3.1.1 */
|
||||
static void decode_fr(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
||||
static void decode_fr(struct decoded_trau_frame *fr, const ubit_t *trau_bits)
|
||||
{
|
||||
int i;
|
||||
int d_idx = 0;
|
||||
|
@ -64,7 +72,7 @@ static void decode_fr(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
|||
}
|
||||
|
||||
/* Decode according to 3.1.2 */
|
||||
static void decode_amr(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
||||
static void decode_amr(struct decoded_trau_frame *fr, const ubit_t *trau_bits)
|
||||
{
|
||||
int i;
|
||||
int d_idx = 0;
|
||||
|
@ -77,6 +85,7 @@ static void decode_amr(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
|||
memcpy(fr->t_bits+0, trau_bits+316, 4);
|
||||
/* D1 .. D5 */
|
||||
memcpy(fr->d_bits, trau_bits+43, 5);
|
||||
d_idx += 5;
|
||||
/* D6 .. D245 */
|
||||
for (i = 48; i < 304; i += 16) {
|
||||
memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
|
||||
|
@ -86,7 +95,15 @@ static void decode_amr(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
|||
memcpy(fr->d_bits + d_idx, trau_bits + 305, 11);
|
||||
}
|
||||
|
||||
int decode_trau_frame(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
||||
static void decode_data(struct decoded_trau_frame *fr, const ubit_t *trau_bits)
|
||||
{
|
||||
/* C1 .. C15 */
|
||||
memcpy(fr->c_bits+0, trau_bits+17, 15);
|
||||
/* octets 4 .. 39 */
|
||||
memcpy(fr->d_bits, trau_bits+32, 288);
|
||||
}
|
||||
|
||||
int decode_trau_frame(struct decoded_trau_frame *fr, const ubit_t *trau_bits)
|
||||
{
|
||||
uint8_t cbits5 = get_bits(trau_bits, 17, 5);
|
||||
|
||||
|
@ -101,10 +118,12 @@ int decode_trau_frame(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
|||
case TRAU_FT_AMR:
|
||||
decode_amr(fr, trau_bits);
|
||||
break;
|
||||
case TRAU_FT_OM_UP:
|
||||
case TRAU_FT_OM_DOWN:
|
||||
case TRAU_FT_DATA_UP:
|
||||
case TRAU_FT_DATA_DOWN:
|
||||
decode_data(fr, trau_bits);
|
||||
break;
|
||||
case TRAU_FT_OM_UP:
|
||||
case TRAU_FT_OM_DOWN:
|
||||
case TRAU_FT_D145_SYNC:
|
||||
case TRAU_FT_EDATA:
|
||||
LOGP(DLMUX, LOGL_NOTICE, "can't decode unimplemented TRAU "
|
||||
|
@ -121,10 +140,14 @@ int decode_trau_frame(struct decoded_trau_frame *fr, const uint8_t *trau_bits)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const uint8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 };
|
||||
const uint8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 };
|
||||
const ubit_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 };
|
||||
const ubit_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 };
|
||||
const ubit_t ft_data_down_bits[] = { 1, 0, 1, 1, 0 };
|
||||
|
||||
/* modify an uplink TRAU frame so we can send it downlink */
|
||||
/*! \brief modify an uplink TRAU frame so we can send it downlink
|
||||
* \param[in,out] fr the uplink TRAU frame that is to be converted
|
||||
* \returns 0 in case of success, < 0 in caes of error
|
||||
*/
|
||||
int trau_frame_up2down(struct decoded_trau_frame *fr)
|
||||
{
|
||||
uint8_t cbits5 = get_bits(fr->c_bits, 0, 5);
|
||||
|
@ -141,8 +164,14 @@ int trau_frame_up2down(struct decoded_trau_frame *fr)
|
|||
case TRAU_FT_EFR:
|
||||
/* clear time alignment */
|
||||
memset(fr->c_bits+5, 0, 6);
|
||||
/* FIXME: set UFE appropriately */
|
||||
/* FIXME: SP / BFI in case of DTx */
|
||||
/* set UFE appropriately */
|
||||
fr->c_bits[11] = 1; /* C12 (UFE), good frame (TODO) */
|
||||
/* C13 .. C15 are spare and coded as '1' */
|
||||
memset(fr->c_bits+12, 0x01, 3);
|
||||
/* SP / BFI in case of DTx */
|
||||
fr->c_bits[15] = 1; /* C16 (SP), no DTX (TODO) */
|
||||
/* C17 .. C21 are spare and coded as '1' */
|
||||
memset(fr->c_bits+16, 0x01, 5);
|
||||
break;
|
||||
case TRAU_FT_IDLE_UP:
|
||||
memcpy(fr->c_bits, ft_idle_down_bits, 5);
|
||||
|
@ -152,6 +181,9 @@ int trau_frame_up2down(struct decoded_trau_frame *fr)
|
|||
/* C12 .. C21 are spare and coded as '1' */
|
||||
memset(fr->c_bits+11, 0x01, 10);
|
||||
break;
|
||||
case TRAU_FT_DATA_UP:
|
||||
memcpy(fr->c_bits, ft_data_down_bits, 5);
|
||||
break;
|
||||
case TRAU_FT_FR_DOWN:
|
||||
case TRAU_FT_IDLE_DOWN:
|
||||
case TRAU_FT_OM_DOWN:
|
||||
|
@ -161,7 +193,6 @@ int trau_frame_up2down(struct decoded_trau_frame *fr)
|
|||
break;
|
||||
case TRAU_FT_AMR:
|
||||
case TRAU_FT_OM_UP:
|
||||
case TRAU_FT_DATA_UP:
|
||||
case TRAU_FT_D145_SYNC:
|
||||
case TRAU_FT_EDATA:
|
||||
LOGP(DLMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
|
||||
|
@ -179,7 +210,7 @@ int trau_frame_up2down(struct decoded_trau_frame *fr)
|
|||
|
||||
}
|
||||
|
||||
static void encode_fr(uint8_t *trau_bits, const struct decoded_trau_frame *fr)
|
||||
static void encode_fr(ubit_t *trau_bits, const struct decoded_trau_frame *fr)
|
||||
{
|
||||
int i;
|
||||
int d_idx = 0;
|
||||
|
@ -205,7 +236,21 @@ static void encode_fr(uint8_t *trau_bits, const struct decoded_trau_frame *fr)
|
|||
memcpy(trau_bits+316, fr->t_bits+0, 4);
|
||||
}
|
||||
|
||||
int encode_trau_frame(uint8_t *trau_bits, const struct decoded_trau_frame *fr)
|
||||
static void encode_data(ubit_t *trau_bits, const struct decoded_trau_frame *fr)
|
||||
{
|
||||
trau_bits[16] = 1;
|
||||
/* C1 .. C15 */
|
||||
memcpy(trau_bits+17, fr->c_bits+0, 15);
|
||||
/* octets 4 .. 39 */
|
||||
memcpy(trau_bits+32, fr->d_bits, 288);
|
||||
}
|
||||
|
||||
/*! \brief encode a TRAU frame from the decoded bits
|
||||
* \param[out] trau_bits output buffer, will contain encoded bits
|
||||
* \param[in] fr decoded trau frame structure
|
||||
* \returns 0 in case of success, < 0 in case of error
|
||||
*/
|
||||
int encode_trau_frame(ubit_t *trau_bits, const struct decoded_trau_frame *fr)
|
||||
{
|
||||
uint8_t cbits5 = get_bits(fr->c_bits, 0, 5);
|
||||
|
||||
|
@ -220,11 +265,13 @@ int encode_trau_frame(uint8_t *trau_bits, const struct decoded_trau_frame *fr)
|
|||
case TRAU_FT_EFR:
|
||||
encode_fr(trau_bits, fr);
|
||||
break;
|
||||
case TRAU_FT_DATA_UP:
|
||||
case TRAU_FT_DATA_DOWN:
|
||||
encode_data(trau_bits, fr);
|
||||
break;
|
||||
case TRAU_FT_AMR:
|
||||
case TRAU_FT_OM_UP:
|
||||
case TRAU_FT_OM_DOWN:
|
||||
case TRAU_FT_DATA_UP:
|
||||
case TRAU_FT_DATA_DOWN:
|
||||
case TRAU_FT_D145_SYNC:
|
||||
case TRAU_FT_EDATA:
|
||||
LOGP(DLMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
|
||||
|
@ -245,16 +292,40 @@ static struct decoded_trau_frame fr_idle_frame = {
|
|||
.c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */
|
||||
.t_bits = { 1, 1, 1, 1 },
|
||||
};
|
||||
static uint8_t encoded_idle_frame[TRAU_FRAME_BITS];
|
||||
static int dbits_initted;
|
||||
static ubit_t encoded_idle_frame[TRAU_FRAME_BITS];
|
||||
static int dbits_initted = 0;
|
||||
|
||||
uint8_t *trau_idle_frame(void)
|
||||
/*! \brief return pointer to global buffer containing a TRAU idle frame
|
||||
*/
|
||||
ubit_t *trau_idle_frame(void)
|
||||
{
|
||||
/* only initialize during the first call */
|
||||
if (!dbits_initted) {
|
||||
/* set all D-bits to 1 */
|
||||
memset(&fr_idle_frame.d_bits, 0x01, 260);
|
||||
|
||||
memset(&fr_idle_frame.c_bits, 0x01, 25); /* spare are set to 1 */
|
||||
/* set Downlink Idle Speech Frame pattern */
|
||||
fr_idle_frame.c_bits[0] = 0; /* C1 */
|
||||
fr_idle_frame.c_bits[1] = 1; /* C2 */
|
||||
fr_idle_frame.c_bits[2] = 1; /* C3 */
|
||||
fr_idle_frame.c_bits[3] = 1; /* C4 */
|
||||
fr_idle_frame.c_bits[4] = 0; /* C5 */
|
||||
/* set no Time Alignment pattern */
|
||||
fr_idle_frame.c_bits[5] = 0; /* C6 */
|
||||
fr_idle_frame.c_bits[6] = 0; /* C7 */
|
||||
fr_idle_frame.c_bits[7] = 0; /* C8 */
|
||||
fr_idle_frame.c_bits[8] = 0; /* C9 */
|
||||
fr_idle_frame.c_bits[9] = 0; /* C10 */
|
||||
fr_idle_frame.c_bits[10] = 0; /* C11 */
|
||||
/* already set to 1, but maybe we need to modify it in the future */
|
||||
fr_idle_frame.c_bits[11] = 1; /* C12 (UFE), good frame */
|
||||
fr_idle_frame.c_bits[15] = 1; /* C16 (SP), no DTX */
|
||||
|
||||
encode_fr(encoded_idle_frame, &fr_idle_frame);
|
||||
dbits_initted = 1; /* set it to 1 to not call it again */
|
||||
}
|
||||
return encoded_idle_frame;
|
||||
}
|
||||
|
||||
/* }@ */
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS) -no-install
|
||||
|
||||
noinst_PROGRAMS = e1inp_ipa_bsc_test \
|
||||
check_PROGRAMS = e1inp_ipa_bsc_test \
|
||||
e1inp_ipa_bts_test \
|
||||
e1inp_hsl_bsc_test \
|
||||
e1inp_hsl_bts_test \
|
||||
ipa_proxy_test
|
||||
ipa_proxy_test \
|
||||
subchan_demux/subchan_demux_test \
|
||||
ipa_recv/ipa_recv_test \
|
||||
rtp_test/rtp_test \
|
||||
trau_sync/trau_sync_test \
|
||||
trau_pcu_ericsson/trau_pcu_ericsson_test
|
||||
|
||||
e1inp_ipa_bsc_test_SOURCES = e1inp_ipa_bsc_test.c
|
||||
e1inp_ipa_bsc_test_LDADD = $(top_builddir)/src/libosmoabis.la \
|
||||
|
@ -16,14 +19,76 @@ e1inp_ipa_bts_test_SOURCES = e1inp_ipa_bts_test.c
|
|||
e1inp_ipa_bts_test_LDADD = $(top_builddir)/src/libosmoabis.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
|
||||
|
||||
e1inp_hsl_bsc_test_SOURCES = e1inp_hsl_bsc_test.c
|
||||
e1inp_hsl_bsc_test_LDADD = $(top_builddir)/src/libosmoabis.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
|
||||
|
||||
e1inp_hsl_bts_test_SOURCES = e1inp_hsl_bts_test.c
|
||||
e1inp_hsl_bts_test_LDADD = $(top_builddir)/src/libosmoabis.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
|
||||
|
||||
ipa_proxy_test_SOURCES = ipa_proxy_test.c
|
||||
ipa_proxy_test_LDADD = $(top_builddir)/src/libosmoabis.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS)
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS)
|
||||
|
||||
subchan_demux_subchan_demux_test_SOURCES = subchan_demux/subchan_demux_test.c
|
||||
subchan_demux_subchan_demux_test_LDADD = $(top_builddir)/src/libosmoabis.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS)
|
||||
|
||||
ipa_recv_ipa_recv_test_SOURCES = ipa_recv/ipa_recv_test.c
|
||||
ipa_recv_ipa_recv_test_LDADD = $(top_builddir)/src/libosmoabis.la \
|
||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS)
|
||||
|
||||
rtp_test_rtp_test_SOURCES = rtp_test/rtp_test.c
|
||||
rtp_test_rtp_test_LDADD = $(top_builddir)/src/libosmotrau.la \
|
||||
$(LIBOSMOCORE_LIBS)
|
||||
|
||||
trau_sync_trau_sync_test_SOURCES = trau_sync/trau_sync_test.c
|
||||
trau_sync_trau_sync_test_LDADD = $(top_builddir)/src/libosmotrau.la \
|
||||
$(LIBOSMOCORE_LIBS)
|
||||
|
||||
trau_pcu_ericsson_trau_pcu_ericsson_test_SOURCES = trau_pcu_ericsson/trau_pcu_ericsson_test.c
|
||||
trau_pcu_ericsson_trau_pcu_ericsson_test_LDADD = $(top_builddir)/src/libosmotrau.la \
|
||||
$(LIBOSMOCORE_LIBS)
|
||||
|
||||
# boilerplate for the tests
|
||||
# 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) \
|
||||
subchan_demux/subchan_demux_test.ok \
|
||||
ipa_recv/ipa_recv_test.ok \
|
||||
rtp_test/rtp_test.ok \
|
||||
trau_sync/trau_sync_test.ok trau_sync/trau_sync_test.err \
|
||||
trau_pcu_ericsson/trau_pcu_ericsson_test.ok
|
||||
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
|
||||
DISTCLEANFILES = atconfig
|
||||
|
||||
check-local: atconfig $(TESTSUITE)
|
||||
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
|
||||
|
||||
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 $@
|
||||
|
|
|
@ -1,236 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/gsm/protocol/gsm_12_21.h>
|
||||
|
||||
static void *tall_test;
|
||||
static struct e1inp_sign_link *oml_sign_link, *rsl_sign_link;
|
||||
|
||||
#define DBSCTEST 0
|
||||
|
||||
struct log_info_cat bsc_test_cat[] = {
|
||||
[DBSCTEST] = {
|
||||
.name = "DBSCTEST",
|
||||
.description = "BSC-mode test",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct e1inp_sign_link *
|
||||
sign_link_up(void *dev, struct e1inp_line *line, enum e1inp_sign_type type)
|
||||
{
|
||||
struct e1inp_sign_link *sign_link = NULL;
|
||||
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "Signal link up request received.\n");
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML - 1], line);
|
||||
sign_link = oml_sign_link =
|
||||
e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1],
|
||||
E1INP_SIGN_OML, NULL, 255, 0);
|
||||
if (!oml_sign_link) {
|
||||
LOGP(DBSCTEST, LOGL_ERROR, "OML link not yet set, giving up\n");
|
||||
return NULL;
|
||||
}
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL - 1], line);
|
||||
sign_link = rsl_sign_link =
|
||||
e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL - 1],
|
||||
E1INP_SIGN_RSL, NULL, 0, 0);
|
||||
if (sign_link)
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "signal link has been set up.\n");
|
||||
|
||||
return sign_link;
|
||||
}
|
||||
|
||||
static void sign_link_down(struct e1inp_line *line)
|
||||
{
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "signal link has been closed\n");
|
||||
if (oml_sign_link)
|
||||
e1inp_sign_link_destroy(oml_sign_link);
|
||||
if (rsl_sign_link)
|
||||
e1inp_sign_link_destroy(rsl_sign_link);
|
||||
}
|
||||
|
||||
static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len)
|
||||
{
|
||||
oh->mdisc = ABIS_OM_MDISC_FOM;
|
||||
oh->placement = ABIS_OM_PLACEMENT_ONLY;
|
||||
oh->sequence = 0;
|
||||
oh->length = len;
|
||||
}
|
||||
|
||||
static void fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len,
|
||||
uint8_t msg_type, uint8_t obj_class,
|
||||
uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
|
||||
{
|
||||
struct abis_om_fom_hdr *foh =
|
||||
(struct abis_om_fom_hdr *) oh->data;
|
||||
|
||||
fill_om_hdr(oh, len+sizeof(*foh));
|
||||
foh->msg_type = msg_type;
|
||||
foh->obj_class = obj_class;
|
||||
foh->obj_inst.bts_nr = bts_nr;
|
||||
foh->obj_inst.trx_nr = trx_nr;
|
||||
foh->obj_inst.ts_nr = ts_nr;
|
||||
}
|
||||
|
||||
#define OM_ALLOC_SIZE 1024
|
||||
#define OM_HEADROOM_SIZE 128
|
||||
|
||||
static struct msgb *nm_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML");
|
||||
}
|
||||
|
||||
|
||||
static int abis_nm_sw_act_req_ack(struct e1inp_sign_link *sign_link,
|
||||
uint8_t obj_class,
|
||||
uint8_t i1, uint8_t i2, uint8_t i3,
|
||||
uint8_t *attr, int att_len)
|
||||
{
|
||||
struct abis_om_hdr *oh;
|
||||
struct msgb *msg = nm_msgb_alloc();
|
||||
uint8_t msgtype = NM_MT_SW_ACT_REQ_ACK;
|
||||
|
||||
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
|
||||
fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3);
|
||||
|
||||
if (attr) {
|
||||
uint8_t *ptr = msgb_put(msg, att_len);
|
||||
memcpy(ptr, attr, att_len);
|
||||
}
|
||||
msg->dst = sign_link;
|
||||
return abis_sendmsg(msg);
|
||||
}
|
||||
|
||||
static int abis_nm_rx_sw_act_req(struct msgb *msg)
|
||||
{
|
||||
struct abis_om_hdr *oh = msgb_l2(msg);
|
||||
struct abis_om_fom_hdr *foh = msgb_l3(msg);
|
||||
int ret;
|
||||
|
||||
ret = abis_nm_sw_act_req_ack(msg->dst,
|
||||
foh->obj_class,
|
||||
foh->obj_inst.bts_nr,
|
||||
foh->obj_inst.trx_nr,
|
||||
foh->obj_inst.ts_nr,
|
||||
foh->data, oh->length-sizeof(*foh));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int abis_nm_rcvmsg_fom(struct msgb *msg)
|
||||
{
|
||||
struct abis_om_fom_hdr *foh = msgb_l3(msg);
|
||||
uint8_t mt = foh->msg_type;
|
||||
int ret = 0;
|
||||
|
||||
switch (mt) {
|
||||
case NM_MT_SW_ACT_REQ: /* Software activate request from BTS. */
|
||||
ret = abis_nm_rx_sw_act_req(msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int abis_nm_rcvmsg(struct msgb *msg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct abis_om_hdr *oh = msgb_l2(msg);
|
||||
|
||||
msg->l3h = (unsigned char *)oh + sizeof(*oh);
|
||||
switch (oh->mdisc) {
|
||||
case ABIS_OM_MDISC_FOM:
|
||||
ret = abis_nm_rcvmsg_fom(msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DBSCTEST, LOGL_ERROR, "unknown OML message\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sign_link(struct msgb *msg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct e1inp_sign_link *link = msg->dst;
|
||||
|
||||
switch(link->type) {
|
||||
case E1INP_SIGN_RSL:
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "RSL message received.\n");
|
||||
break;
|
||||
case E1INP_SIGN_OML:
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "OML message received.\n");
|
||||
ret = abis_nm_rcvmsg(msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DBSCTEST, LOGL_ERROR, "Unknown signallin message.\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct log_info bsc_test_log_info = {
|
||||
.filter_fn = NULL,
|
||||
.cat = bsc_test_cat,
|
||||
.num_cat = ARRAY_SIZE(bsc_test_cat),
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
|
||||
tall_test = talloc_named_const(NULL, 1, "e1inp_test");
|
||||
libosmo_abis_init(tall_test);
|
||||
|
||||
osmo_init_logging(&bsc_test_log_info);
|
||||
|
||||
struct e1inp_line_ops ops = {
|
||||
.cfg = {
|
||||
.ipa = {
|
||||
.addr = "0.0.0.0",
|
||||
.role = E1INP_LINE_R_BSC,
|
||||
},
|
||||
},
|
||||
.sign_link_up = sign_link_up,
|
||||
.sign_link_down = sign_link_down,
|
||||
.sign_link = sign_link,
|
||||
};
|
||||
|
||||
#define LINENR 0
|
||||
|
||||
line = e1inp_line_create(LINENR, "hsl");
|
||||
if (line == NULL) {
|
||||
LOGP(DBSCTEST, LOGL_ERROR, "problem creating E1 line\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
e1inp_line_bind_ops(line, &ops);
|
||||
|
||||
/*
|
||||
* Depending if this is a real or virtual E1 lines:
|
||||
* - real (ISDN): create signal link for OML and RSL before line up.
|
||||
* - vitual (INET): we create it in signal_link_up(...) callback.
|
||||
*
|
||||
* The signal link is created via e1inp_sign_link_create(...)
|
||||
*
|
||||
* See e1_reconfig_trx and e1_reconfig_bts in libbsc/e1_config.c,
|
||||
* it explains how this is done with ISDN.
|
||||
*/
|
||||
|
||||
if (e1inp_line_update(line) < 0) {
|
||||
LOGP(DBSCTEST, LOGL_ERROR, "problem creating E1 line\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "entering main loop\n");
|
||||
|
||||
while (1) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
#include <osmocom/gsm/protocol/gsm_12_21.h>
|
||||
|
||||
static void *tall_test;
|
||||
static struct e1inp_sign_link *oml_sign_link, *rsl_sign_link;
|
||||
|
||||
#define DBTSTEST 0
|
||||
|
||||
struct log_info_cat bts_test_cat[] = {
|
||||
[DBTSTEST] = {
|
||||
.name = "DBTSTEST",
|
||||
.description = "BTS-mode test",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info bts_test_log_info = {
|
||||
.filter_fn = NULL,
|
||||
.cat = bts_test_cat,
|
||||
.num_cat = ARRAY_SIZE(bts_test_cat),
|
||||
};
|
||||
|
||||
enum bts_state_machine {
|
||||
BTS_TEST_OML_SIGN_LINK_DOWN = 0,
|
||||
BTS_TEST_OML_SIGN_LINK_UP,
|
||||
BTS_TEST_OML_WAIT_SW_ACT_ACK,
|
||||
};
|
||||
|
||||
static struct osmo_fd bts_eventfd;
|
||||
static int eventfds[2];
|
||||
|
||||
static enum bts_state_machine bts_state = BTS_TEST_OML_SIGN_LINK_DOWN;
|
||||
|
||||
static struct e1inp_sign_link *
|
||||
sign_link_up(void *unit, struct e1inp_line *line, enum e1inp_sign_type type)
|
||||
{
|
||||
struct e1inp_sign_link *sign_link = NULL;
|
||||
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "OML and RSL link up request received.\n");
|
||||
|
||||
e1inp_ts_config_sign(&line->ts[0], line);
|
||||
sign_link = oml_sign_link =
|
||||
e1inp_sign_link_create(&line->ts[0],
|
||||
E1INP_SIGN_OML, NULL, 255, 0);
|
||||
if (!oml_sign_link) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "cannot create OML sign link\n");
|
||||
return NULL;
|
||||
}
|
||||
sign_link = rsl_sign_link =
|
||||
e1inp_sign_link_create(&line->ts[0],
|
||||
E1INP_SIGN_RSL, NULL, 0, 0);
|
||||
if (!rsl_sign_link) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "cannot create RSL sign link\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sign_link) {
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "signal link has been set up.\n");
|
||||
/* Now we can send OML messages to the BSC. */
|
||||
bts_state = BTS_TEST_OML_SIGN_LINK_UP;
|
||||
}
|
||||
/* tell GSM 12.21 that we're ready to proceed via our event fd. */
|
||||
unsigned int event_type = 0;
|
||||
if (write(eventfds[1], &event_type, sizeof(unsigned int)) < 0)
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "cannot write to event fd.\n");
|
||||
|
||||
return sign_link;
|
||||
}
|
||||
|
||||
static void sign_link_down(struct e1inp_line *line)
|
||||
{
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "signal link has been closed\n");
|
||||
if (oml_sign_link)
|
||||
e1inp_sign_link_destroy(oml_sign_link);
|
||||
if (rsl_sign_link)
|
||||
e1inp_sign_link_destroy(rsl_sign_link);
|
||||
}
|
||||
|
||||
static int abis_nm_rcvmsg_fom(struct msgb *msg)
|
||||
{
|
||||
struct abis_om_fom_hdr *foh = msgb_l3(msg);
|
||||
uint8_t mt = foh->msg_type;
|
||||
int ret = 0;
|
||||
|
||||
switch (mt) {
|
||||
case NM_MT_SW_ACT_REQ_ACK: /* SW activate request ACK from BSC. */
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "Receive SW Act Req ACK\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "unknown OML message\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int abis_nm_rcvmsg(struct msgb *msg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct abis_om_hdr *oh = msgb_l2(msg);
|
||||
|
||||
msg->l3h = (unsigned char *)oh + sizeof(*oh);
|
||||
switch (oh->mdisc) {
|
||||
case ABIS_OM_MDISC_FOM:
|
||||
ret = abis_nm_rcvmsg_fom(msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "unknown OML message\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sign_link(struct msgb *msg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct e1inp_sign_link *link = msg->dst;
|
||||
|
||||
switch(link->type) {
|
||||
case E1INP_SIGN_OML:
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "OML message received.\n");
|
||||
ret = abis_nm_rcvmsg(msg);
|
||||
break;
|
||||
case E1INP_SIGN_RSL:
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "RSL message received.\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "Unknown signalling message.\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len)
|
||||
{
|
||||
oh->mdisc = ABIS_OM_MDISC_FOM;
|
||||
oh->placement = ABIS_OM_PLACEMENT_ONLY;
|
||||
oh->sequence = 0;
|
||||
oh->length = len;
|
||||
}
|
||||
|
||||
static void fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len,
|
||||
uint8_t msg_type, uint8_t obj_class,
|
||||
uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
|
||||
{
|
||||
struct abis_om_fom_hdr *foh = (struct abis_om_fom_hdr *) oh->data;
|
||||
|
||||
fill_om_hdr(oh, len+sizeof(*foh));
|
||||
foh->msg_type = msg_type;
|
||||
foh->obj_class = obj_class;
|
||||
foh->obj_inst.bts_nr = bts_nr;
|
||||
foh->obj_inst.trx_nr = trx_nr;
|
||||
foh->obj_inst.ts_nr = ts_nr;
|
||||
}
|
||||
|
||||
#define OM_ALLOC_SIZE 1024
|
||||
#define OM_HEADROOM_SIZE 128
|
||||
|
||||
static struct msgb *nm_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "BTS/test");
|
||||
}
|
||||
|
||||
static int abis_nm_sw_act_req(struct e1inp_sign_link *sign_link,
|
||||
uint8_t obj_class,
|
||||
uint8_t i1, uint8_t i2, uint8_t i3,
|
||||
uint8_t *attr, int att_len)
|
||||
{
|
||||
struct abis_om_hdr *oh;
|
||||
struct msgb *msg = nm_msgb_alloc();
|
||||
uint8_t msgtype = NM_MT_SW_ACT_REQ;
|
||||
|
||||
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
|
||||
fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3);
|
||||
|
||||
if (attr) {
|
||||
uint8_t *ptr = msgb_put(msg, att_len);
|
||||
memcpy(ptr, attr, att_len);
|
||||
}
|
||||
msg->dst = sign_link;
|
||||
return abis_sendmsg(msg);
|
||||
}
|
||||
|
||||
static int test_bts_gsm_12_21_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
int ret;
|
||||
unsigned int event_type;
|
||||
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "event fd callback running\n");
|
||||
|
||||
if (read(ofd->fd, &event_type, sizeof(event_type)) == -1) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "problems with event fd\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
switch(bts_state) {
|
||||
case BTS_TEST_OML_SIGN_LINK_DOWN:
|
||||
/* Do nothing until OML link becomes ready. */
|
||||
break;
|
||||
case BTS_TEST_OML_SIGN_LINK_UP:
|
||||
/* OML link is up, send SW ACT REQ. */
|
||||
ret = abis_nm_sw_act_req(oml_sign_link, 0, 0, 0, 0, NULL, 0);
|
||||
bts_state = BTS_TEST_OML_WAIT_SW_ACT_ACK;
|
||||
break;
|
||||
case BTS_TEST_OML_WAIT_SW_ACT_ACK:
|
||||
/* ... things should continue after this. */
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct hsl_unit bts_dev_info = {
|
||||
.swversion = 0xc0,
|
||||
.serno = 0x0123456789ABCDEF,
|
||||
};
|
||||
struct e1inp_line *line;
|
||||
|
||||
tall_test = talloc_named_const(NULL, 1, "e1inp_test");
|
||||
libosmo_abis_init(tall_test);
|
||||
|
||||
osmo_init_logging(&bts_test_log_info);
|
||||
|
||||
struct e1inp_line_ops ops = {
|
||||
.cfg = {
|
||||
.ipa = {
|
||||
.role = E1INP_LINE_R_BTS,
|
||||
.addr = "127.0.0.1",
|
||||
.dev = &bts_dev_info,
|
||||
},
|
||||
},
|
||||
.sign_link_up = sign_link_up,
|
||||
.sign_link_down = sign_link_down,
|
||||
.sign_link = sign_link,
|
||||
};
|
||||
|
||||
#define LINENR 0
|
||||
|
||||
line = e1inp_line_create(LINENR, "hsl");
|
||||
if (line == NULL) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "problem enabling E1 line\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
e1inp_line_bind_ops(line, &ops);
|
||||
|
||||
if (e1inp_line_update(line) < 0) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "problem enabling E1 line\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "entering main loop\n");
|
||||
|
||||
if (pipe(eventfds) < 0) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "cannot create pipe fds\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
bts_eventfd.fd = eventfds[0];
|
||||
bts_eventfd.cb = test_bts_gsm_12_21_cb;
|
||||
bts_eventfd.when = BSC_FD_READ;
|
||||
if (osmo_fd_register(&bts_eventfd) < 0) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "could not register event fd\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
@ -29,9 +30,9 @@ sign_link_up(void *dev, struct e1inp_line *line, enum e1inp_sign_type type)
|
|||
switch(type) {
|
||||
case E1INP_SIGN_OML:
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "OML link up request received.\n");
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML - 1], line);
|
||||
e1inp_ts_config_sign(e1inp_line_ipa_oml_ts(line), line);
|
||||
sign_link = oml_sign_link =
|
||||
e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1],
|
||||
e1inp_sign_link_create(e1inp_line_ipa_oml_ts(line),
|
||||
E1INP_SIGN_OML, NULL, 255, 0);
|
||||
break;
|
||||
case E1INP_SIGN_RSL:
|
||||
|
@ -44,10 +45,10 @@ sign_link_up(void *dev, struct e1inp_line *line, enum e1inp_sign_type type)
|
|||
|
||||
/* We have to use the same line that the OML link. */
|
||||
oml_line = oml_sign_link->ts->line;
|
||||
e1inp_ts_config_sign(&oml_line->ts[E1INP_SIGN_RSL - 1],
|
||||
e1inp_ts_config_sign(e1inp_line_ipa_rsl_ts(oml_line, 0),
|
||||
oml_line);
|
||||
sign_link = rsl_sign_link =
|
||||
e1inp_sign_link_create(&oml_line->ts[E1INP_SIGN_RSL - 1],
|
||||
e1inp_sign_link_create(e1inp_line_ipa_rsl_ts(oml_line, 0),
|
||||
E1INP_SIGN_RSL, NULL, 0, 0);
|
||||
break;
|
||||
default:
|
||||
|
@ -62,10 +63,14 @@ sign_link_up(void *dev, struct e1inp_line *line, enum e1inp_sign_type type)
|
|||
static void sign_link_down(struct e1inp_line *line)
|
||||
{
|
||||
LOGP(DBSCTEST, LOGL_NOTICE, "signal link has been closed\n");
|
||||
if (oml_sign_link)
|
||||
if (oml_sign_link) {
|
||||
e1inp_sign_link_destroy(oml_sign_link);
|
||||
if (rsl_sign_link)
|
||||
oml_sign_link = NULL;
|
||||
}
|
||||
if (rsl_sign_link) {
|
||||
e1inp_sign_link_destroy(rsl_sign_link);
|
||||
rsl_sign_link = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len)
|
||||
|
@ -186,6 +191,7 @@ static int sign_link(struct msgb *msg)
|
|||
LOGP(DBSCTEST, LOGL_ERROR, "Unknown signallin message.\n");
|
||||
break;
|
||||
}
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -195,14 +201,20 @@ const struct log_info bsc_test_log_info = {
|
|||
.num_cat = ARRAY_SIZE(bsc_test_cat),
|
||||
};
|
||||
|
||||
static struct e1inp_line *line;
|
||||
|
||||
static void sighandler(int foo)
|
||||
{
|
||||
e1inp_line_put2(line, "ctor");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
|
||||
tall_test = talloc_named_const(NULL, 1, "e1inp_test");
|
||||
libosmo_abis_init(tall_test);
|
||||
|
||||
osmo_init_logging(&bsc_test_log_info);
|
||||
msgb_talloc_ctx_init(tall_test, 0);
|
||||
osmo_init_logging2(tall_test, &bsc_test_log_info);
|
||||
|
||||
struct e1inp_line_ops ops = {
|
||||
.cfg = {
|
||||
|
@ -216,6 +228,12 @@ int main(void)
|
|||
.sign_link = sign_link,
|
||||
};
|
||||
|
||||
if (signal(SIGINT, sighandler) == SIG_ERR ||
|
||||
signal(SIGTERM, sighandler) == SIG_ERR) {
|
||||
perror("Cannot set sighandler");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#define LINENR 0
|
||||
|
||||
line = e1inp_line_create(LINENR, "ipa");
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
@ -8,6 +9,7 @@
|
|||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
#include <osmocom/gsm/protocol/gsm_12_21.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
|
||||
static void *tall_test;
|
||||
static struct e1inp_sign_link *oml_sign_link, *rsl_sign_link;
|
||||
|
@ -44,21 +46,19 @@ static struct e1inp_sign_link *
|
|||
sign_link_up(void *unit, struct e1inp_line *line, enum e1inp_sign_type type)
|
||||
{
|
||||
struct e1inp_sign_link *sign_link = NULL;
|
||||
void *dst = NULL;
|
||||
|
||||
switch(type) {
|
||||
case E1INP_SIGN_OML:
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "OML link up request received.\n");
|
||||
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML - 1], line);
|
||||
e1inp_ts_config_sign(e1inp_line_ipa_oml_ts(line), line);
|
||||
sign_link = oml_sign_link =
|
||||
e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1],
|
||||
e1inp_sign_link_create(e1inp_line_ipa_oml_ts(line),
|
||||
E1INP_SIGN_OML, NULL, 255, 0);
|
||||
if (!oml_sign_link) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR,
|
||||
"cannot create OML sign link\n");
|
||||
}
|
||||
dst = oml_sign_link;
|
||||
if (oml_sign_link) {
|
||||
unsigned int event_type = 0;
|
||||
|
||||
|
@ -71,20 +71,20 @@ sign_link_up(void *unit, struct e1inp_line *line, enum e1inp_sign_type type)
|
|||
/* Now we can send OML messages to the BSC. */
|
||||
bts_state = BTS_TEST_OML_SIGN_LINK_UP;
|
||||
}
|
||||
e1inp_ipa_bts_rsl_connect_n(line, "127.0.0.1", IPA_TCP_PORT_RSL, 0);
|
||||
break;
|
||||
case E1INP_SIGN_RSL:
|
||||
LOGP(DBTSTEST, LOGL_NOTICE, "RSL link up request received.\n");
|
||||
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL - 1], line);
|
||||
e1inp_ts_config_sign(e1inp_line_ipa_rsl_ts(line, 0), line);
|
||||
|
||||
sign_link = rsl_sign_link =
|
||||
e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL - 1],
|
||||
e1inp_sign_link_create(e1inp_line_ipa_rsl_ts(line, 0),
|
||||
E1INP_SIGN_RSL, NULL, 0, 0);
|
||||
if (!rsl_sign_link) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR,
|
||||
"cannot create RSL sign link\n");
|
||||
}
|
||||
dst = rsl_sign_link;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
|
@ -153,6 +153,7 @@ static int sign_link(struct msgb *msg)
|
|||
LOGP(DBTSTEST, LOGL_ERROR, "Unknown signalling message.\n");
|
||||
break;
|
||||
}
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -208,9 +209,14 @@ static int abis_nm_sw_act_req(struct e1inp_sign_link *sign_link,
|
|||
|
||||
static int test_bts_gsm_12_21_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
int ret;
|
||||
int ret, event_type;
|
||||
struct ipaccess_unit *unit = ofd->data;
|
||||
|
||||
if (read(eventfds[0], &event_type, sizeof(unsigned int)) < 0) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "error receiving event\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(bts_state) {
|
||||
case BTS_TEST_OML_SIGN_LINK_DOWN:
|
||||
/* Do nothing until OML link becomes ready. */
|
||||
|
@ -221,6 +227,10 @@ static int test_bts_gsm_12_21_cb(struct osmo_fd *ofd, unsigned int what)
|
|||
unit->bts_id,
|
||||
unit->trx_id,
|
||||
0, NULL, 0);
|
||||
if (ret < 0) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "cannot send SW ACT REQ\n");
|
||||
break;
|
||||
}
|
||||
bts_state = BTS_TEST_OML_WAIT_SW_ACT_ACK;
|
||||
break;
|
||||
case BTS_TEST_OML_WAIT_SW_ACT_ACK:
|
||||
|
@ -230,6 +240,14 @@ static int test_bts_gsm_12_21_cb(struct osmo_fd *ofd, unsigned int what)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct e1inp_line *line;
|
||||
|
||||
static void sighandler(int foo)
|
||||
{
|
||||
e1inp_line_put2(line, "ctor");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct ipaccess_unit bts_dev_info = {
|
||||
|
@ -244,12 +262,11 @@ int main(void)
|
|||
.location2 = "testBTS",
|
||||
.serno = "",
|
||||
};
|
||||
struct e1inp_line *line;
|
||||
|
||||
tall_test = talloc_named_const(NULL, 1, "e1inp_test");
|
||||
libosmo_abis_init(tall_test);
|
||||
|
||||
osmo_init_logging(&bts_test_log_info);
|
||||
msgb_talloc_ctx_init(tall_test, 0);
|
||||
osmo_init_logging2(tall_test, &bts_test_log_info);
|
||||
|
||||
struct e1inp_line_ops ops = {
|
||||
.cfg = {
|
||||
|
@ -266,6 +283,12 @@ int main(void)
|
|||
|
||||
#define LINENR 0
|
||||
|
||||
if (signal(SIGINT, sighandler) == SIG_ERR ||
|
||||
signal(SIGTERM, sighandler) == SIG_ERR) {
|
||||
perror("Cannot set sighandler");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
line = e1inp_line_create(LINENR, "ipa");
|
||||
if (line == NULL) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "problem enabling E1 line\n");
|
||||
|
@ -284,10 +307,7 @@ int main(void)
|
|||
LOGP(DBTSTEST, LOGL_ERROR, "cannot create pipe fds\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
bts_eventfd.fd = eventfds[0];
|
||||
bts_eventfd.cb = test_bts_gsm_12_21_cb;
|
||||
bts_eventfd.when = BSC_FD_READ;
|
||||
bts_eventfd.data = &bts_dev_info;
|
||||
osmo_fd_setup(&bts_eventfd, eventfds[0], OSMO_FD_READ, test_bts_gsm_12_21_cb, &bts_dev_info, 0);
|
||||
if (osmo_fd_register(&bts_eventfd) < 0) {
|
||||
LOGP(DBTSTEST, LOGL_ERROR, "could not register event fd\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include "internal.h"
|
||||
#include "config.h"
|
||||
|
||||
static void *tall_test;
|
||||
|
||||
|
@ -30,31 +28,9 @@ const struct log_info ipa_proxy_test_log_info = {
|
|||
.num_cat = ARRAY_SIZE(ipa_proxy_test_cat),
|
||||
};
|
||||
|
||||
static int bsc_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch(node) {
|
||||
case L_IPA_NODE:
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum node_type bsc_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case L_IPA_NODE:
|
||||
vty->node = VIEW_NODE;
|
||||
break;
|
||||
}
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "ipa-proxy-test",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = bsc_vty_go_parent,
|
||||
.is_config_node = bsc_vty_is_config_node,
|
||||
.version = "1.0",
|
||||
};
|
||||
|
||||
#define IPA_PROXY_TEST_TELNET_PORT 4260
|
||||
|
@ -63,13 +39,13 @@ int main(void)
|
|||
{
|
||||
tall_test = talloc_named_const(NULL, 1, "ipa proxy test");
|
||||
libosmo_abis_init(tall_test);
|
||||
|
||||
osmo_init_logging(&ipa_proxy_test_log_info);
|
||||
msgb_talloc_ctx_init(tall_test, 0);
|
||||
osmo_init_logging2(tall_test, &ipa_proxy_test_log_info);
|
||||
|
||||
vty_init(&vty_info);
|
||||
ipa_proxy_vty_init();
|
||||
|
||||
telnet_init(tall_test, NULL, IPA_PROXY_TEST_TELNET_PORT);
|
||||
telnet_init_default(tall_test, NULL, IPA_PROXY_TEST_TELNET_PORT);
|
||||
|
||||
LOGP(DIPA_PROXY_TEST, LOGL_NOTICE, "entering main loop\n");
|
||||
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
/* IPA receive test */
|
||||
|
||||
/*
|
||||
* (C) 2014 by On-Waves
|
||||
* (C) 2014 by sysmocom - s.f.m.c. GmbH
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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 <osmocom/abis/e1_input.h>
|
||||
|
||||
#include <osmocom/abis/ipa.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
|
||||
static const char *ipa_test_messages[] = {
|
||||
"Hello IPA",
|
||||
"A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz",
|
||||
"Hello again IPA",
|
||||
"",
|
||||
"Next is empty",
|
||||
NULL,
|
||||
"Bye",
|
||||
"Bye",
|
||||
};
|
||||
|
||||
static void append_ipa_message(struct msgb *msg, int proto, const char *text)
|
||||
{
|
||||
int len = 0;
|
||||
unsigned char *l2;
|
||||
|
||||
if (text)
|
||||
len = strlen(text) + 1;
|
||||
|
||||
msgb_put_u16(msg, len);
|
||||
msgb_put_u8(msg, proto);
|
||||
|
||||
l2 = msgb_put(msg, len);
|
||||
if (text)
|
||||
strcpy((char *)l2, text);
|
||||
}
|
||||
|
||||
static int receive_messages(int fd, struct msgb **pending_msg)
|
||||
{
|
||||
struct msgb *msg;
|
||||
char dummy;
|
||||
int rc;
|
||||
while (1) {
|
||||
if (recv(fd, &dummy, 1, MSG_PEEK) < 1) {
|
||||
rc = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
msg = NULL;
|
||||
rc = ipa_msg_recv_buffered(fd, &msg, pending_msg);
|
||||
|
||||
fprintf(stderr,
|
||||
"ipa_msg_recv_buffered: %d, msg %s NULL, "
|
||||
"pending_msg %s NULL\n",
|
||||
rc, msg ? "!=" : "==",
|
||||
!pending_msg ? "??" : *pending_msg ? "!=" : "==");
|
||||
if (pending_msg && !!msg == !!*pending_msg)
|
||||
printf( "got msg %s NULL, pending_msg %s NULL, "
|
||||
"returned: %s\n",
|
||||
msg ? "!=" : "==",
|
||||
*pending_msg ? "!=" : "==",
|
||||
rc == 0 ? "EOF" :
|
||||
rc > 0 ? "OK" :
|
||||
strerror(-rc));
|
||||
else if (!pending_msg && rc == -EAGAIN)
|
||||
printf( "got msg %s NULL, "
|
||||
"returned: %s\n",
|
||||
msg ? "!=" : "==",
|
||||
rc == 0 ? "EOF" :
|
||||
rc > 0 ? "OK" :
|
||||
strerror(-rc));
|
||||
if (rc == 0)
|
||||
return 0;
|
||||
if (rc == -EAGAIN)
|
||||
break;
|
||||
if (rc < 0) {
|
||||
printf("ipa_msg_recv_buffered failed with: %s\n",
|
||||
strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
printf("got IPA message, size=%d, proto=%d, text=\"%s\"\n",
|
||||
rc, msg->data[2], msg->l2h);
|
||||
msgb_free(msg);
|
||||
};
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int slurp_data(int fd) {
|
||||
int rc;
|
||||
char buf[256];
|
||||
int count = 0;
|
||||
|
||||
do {
|
||||
rc = recv(fd, buf, sizeof(buf), 0);
|
||||
if (rc <= 0)
|
||||
break;
|
||||
|
||||
count += rc;
|
||||
} while (1);
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
static void test_complete_recv(int do_not_assemble)
|
||||
{
|
||||
int sv[2];
|
||||
struct msgb *msg_out = msgb_alloc(4096, "msg_out");
|
||||
struct msgb *pending_msg = NULL;
|
||||
int rc, i;
|
||||
|
||||
printf("Testing IPA recv with complete messages%s.\n",
|
||||
do_not_assemble ? "" : " with assembling enabled");
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
|
||||
err(1, "socketpair");
|
||||
|
||||
fcntl(sv[0], F_SETFL, O_NONBLOCK);
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++)
|
||||
append_ipa_message(msg_out, 200, ipa_test_messages[i]);
|
||||
|
||||
while (msg_out->len > 0) {
|
||||
rc = write(sv[1], msg_out->data, msg_out->len);
|
||||
if (rc == -1)
|
||||
err(1, "write");
|
||||
msgb_pull(msg_out, rc);
|
||||
}
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++) {
|
||||
rc = receive_messages(sv[0],
|
||||
do_not_assemble ? NULL : &pending_msg);
|
||||
if (pending_msg)
|
||||
printf("Unexpected partial message: size=%d\n",
|
||||
pending_msg->len);
|
||||
if (rc == 0)
|
||||
break;
|
||||
|
||||
if (rc < 0 && rc != -EAGAIN)
|
||||
break;
|
||||
}
|
||||
|
||||
rc = slurp_data(sv[0]);
|
||||
printf("done: unread %d, unsent %d\n", rc, msg_out->len);
|
||||
|
||||
close(sv[1]);
|
||||
close(sv[0]);
|
||||
|
||||
msgb_free(msg_out);
|
||||
msgb_free(pending_msg);
|
||||
}
|
||||
|
||||
|
||||
static void test_partial_recv(int do_not_assemble)
|
||||
{
|
||||
int sv[2];
|
||||
struct msgb *msg_out = msgb_alloc(4096, "msg_out");
|
||||
struct msgb *pending_msg = NULL;
|
||||
int rc, i;
|
||||
|
||||
printf("Testing IPA recv with partitioned messages%s.\n",
|
||||
do_not_assemble ? "" : " with assembling enabled");
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
|
||||
err(1, "socketpair");
|
||||
|
||||
fcntl(sv[0], F_SETFL, O_NONBLOCK);
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++)
|
||||
append_ipa_message(msg_out, 200, ipa_test_messages[i]);
|
||||
|
||||
while (msg_out->len > 0) {
|
||||
int len = 5;
|
||||
if (len > msg_out->len)
|
||||
len = msg_out->len;
|
||||
if (write(sv[1], msg_out->data, len) == -1)
|
||||
err(1, "write");
|
||||
msgb_pull(msg_out, len);
|
||||
|
||||
if (msg_out->len == 0)
|
||||
shutdown(sv[1], SHUT_WR);
|
||||
|
||||
rc = receive_messages(sv[0],
|
||||
do_not_assemble ? NULL : &pending_msg);
|
||||
|
||||
if (rc == 0)
|
||||
break;
|
||||
|
||||
if (rc < 0 && rc != -EAGAIN)
|
||||
break;
|
||||
}
|
||||
rc = slurp_data(sv[0]);
|
||||
printf("done: unread %d, unsent %d\n", rc, msg_out->len);
|
||||
|
||||
close(sv[1]);
|
||||
close(sv[0]);
|
||||
|
||||
msgb_free(msg_out);
|
||||
msgb_free(pending_msg);
|
||||
}
|
||||
|
||||
static struct log_info info = {};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
|
||||
msgb_talloc_ctx_init(tall_ctx, 0);
|
||||
osmo_init_logging2(tall_ctx, &info);
|
||||
log_set_all_filter(osmo_stderr_target, 1);
|
||||
log_set_log_level(osmo_stderr_target, LOGL_INFO);
|
||||
|
||||
printf("Testing the IPA layer.\n");
|
||||
|
||||
/* run the tests */
|
||||
test_complete_recv(1);
|
||||
test_partial_recv(1);
|
||||
test_complete_recv(0);
|
||||
test_partial_recv(0);
|
||||
|
||||
printf("No crashes.\n");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
Testing the IPA layer.
|
||||
Testing IPA recv with complete messages.
|
||||
got IPA message, size=10, proto=200, text="Hello IPA"
|
||||
got IPA message, size=86, proto=200, text="A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
got IPA message, size=16, proto=200, text="Hello again IPA"
|
||||
got IPA message, size=1, proto=200, text=""
|
||||
got IPA message, size=14, proto=200, text="Next is empty"
|
||||
got msg == NULL, returned: Resource temporarily unavailable
|
||||
got IPA message, size=4, proto=200, text="Bye"
|
||||
got IPA message, size=4, proto=200, text="Bye"
|
||||
done: unread 0, unsent 0
|
||||
Testing IPA recv with partitioned messages.
|
||||
ipa_msg_recv_buffered failed with: Input/output error
|
||||
done: unread 0, unsent 154
|
||||
Testing IPA recv with complete messages with assembling enabled.
|
||||
got IPA message, size=10, proto=200, text="Hello IPA"
|
||||
got IPA message, size=86, proto=200, text="A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
got IPA message, size=16, proto=200, text="Hello again IPA"
|
||||
got IPA message, size=1, proto=200, text=""
|
||||
got IPA message, size=14, proto=200, text="Next is empty"
|
||||
got msg == NULL, pending_msg == NULL, returned: Resource temporarily unavailable
|
||||
got IPA message, size=4, proto=200, text="Bye"
|
||||
got IPA message, size=4, proto=200, text="Bye"
|
||||
done: unread 0, unsent 0
|
||||
Testing IPA recv with partitioned messages with assembling enabled.
|
||||
got IPA message, size=10, proto=200, text="Hello IPA"
|
||||
got IPA message, size=86, proto=200, text="A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
got IPA message, size=16, proto=200, text="Hello again IPA"
|
||||
got IPA message, size=1, proto=200, text=""
|
||||
got IPA message, size=14, proto=200, text="Next is empty"
|
||||
got msg == NULL, pending_msg == NULL, returned: Resource temporarily unavailable
|
||||
got IPA message, size=4, proto=200, text="Bye"
|
||||
got IPA message, size=4, proto=200, text="Bye"
|
||||
done: unread 0, unsent 0
|
||||
No crashes.
|
|
@ -0,0 +1,92 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
static void *tall_test;
|
||||
|
||||
static struct osmo_rtp_socket *create_bind_any(void)
|
||||
{
|
||||
struct osmo_rtp_socket *rs;
|
||||
int i;
|
||||
|
||||
rs = osmo_rtp_socket_create(tall_test, 0);
|
||||
for (i = 16384; i < 65535; i+=2) {
|
||||
if (osmo_rtp_socket_bind(rs, "0.0.0.0", i) == 0)
|
||||
return rs;
|
||||
}
|
||||
osmo_rtp_socket_free(rs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* test if binding two sockets to the same local port fails (See OS#4444) */
|
||||
static void test_bind_fail(void)
|
||||
{
|
||||
struct osmo_rtp_socket *rs[2];
|
||||
uint32_t ip;
|
||||
int rc, port[2];
|
||||
|
||||
printf("First bind to any random local port...\n");
|
||||
rs[0] = create_bind_any();
|
||||
OSMO_ASSERT(rs[0]);
|
||||
|
||||
/* then obtain the local port */
|
||||
rc = osmo_rtp_get_bound_ip_port(rs[0], &ip, &port[0]);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
printf("Now try to bind another socket to the same port: ");
|
||||
rs[1] = osmo_rtp_socket_create(tall_test, 0);
|
||||
OSMO_ASSERT(rs[1]);
|
||||
rc = osmo_rtp_socket_bind(rs[1], "0.0.0.0", port[0]);
|
||||
if (rc == -1 && errno == EADDRINUSE)
|
||||
printf("OK (EADDRINUSE)\n");
|
||||
else {
|
||||
printf("FAILED - second bind to port %u worked\n", port[0]);
|
||||
fflush(stdout);
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
osmo_rtp_socket_free(rs[0]);
|
||||
osmo_rtp_socket_free(rs[1]);
|
||||
}
|
||||
|
||||
#define DTEST 0
|
||||
|
||||
static const struct log_info_cat bts_test_cat[] = {
|
||||
[DTEST] = {
|
||||
.name = "DTEST",
|
||||
.description = "test",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info bts_test_log_info = {
|
||||
.filter_fn = NULL,
|
||||
.cat = bts_test_cat,
|
||||
.num_cat = ARRAY_SIZE(bts_test_cat),
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tall_test = talloc_named_const(NULL, 1, "rtp_test");
|
||||
osmo_init_logging2(tall_test, &bts_test_log_info);
|
||||
osmo_rtp_init(tall_test);
|
||||
|
||||
test_bind_fail();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
First bind to any random local port...
|
||||
Now try to bind another socket to the same port: OK (EADDRINUSE)
|
|
@ -0,0 +1,243 @@
|
|||
/* Subchan Demux syncing test */
|
||||
|
||||
/* (C) 2012 by Holger Hans Peter Freyther
|
||||
* (C) 2012 by Tobias Engel
|
||||
*
|
||||
* 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 <osmocom/abis/subchan_demux.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint8_t test_frames[] =
|
||||
{
|
||||
/* (transmission order is from left to right) */
|
||||
/* Frame 1 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* sync header: 16 bits zero... */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 0, 0, 1, 1, /* ... and 1 bit one (on the left) */
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* Frame 2 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 0, 0, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0, /* <== This zero-bit could be mistaken for
|
||||
being the start of the sync header if there
|
||||
is no check for the following one bit */
|
||||
/* Frame 3 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 0, 0, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
/* Frame 4 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 1, 0, 0, 0, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 0,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static int data_cb(struct subch_demux *demux, int ch, const ubit_t *data, int len, void *p)
|
||||
{
|
||||
printf("DATA_CB Channel(%d): %s\n",
|
||||
ch, osmo_hexdump(data, len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_csd(void)
|
||||
{
|
||||
int i;
|
||||
uint8_t muxbyte;
|
||||
struct subch_demux demux;
|
||||
memset(&demux, 0, sizeof(demux));
|
||||
subch_demux_init(&demux);
|
||||
|
||||
demux.chan_activ = 1;
|
||||
demux.out_cb = data_cb;
|
||||
|
||||
/* Push data into the demuxer and see what happens. */
|
||||
printf("Testing the csd sync.\n");
|
||||
|
||||
for(i = 0; i < sizeof(test_frames); i += 2) {
|
||||
|
||||
muxbyte = 0;
|
||||
muxbyte |= test_frames[i];
|
||||
muxbyte |= (test_frames[i+1] << 1);
|
||||
|
||||
subch_demux_in(&demux, &muxbyte, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("Testing the subchannel demux.\n");
|
||||
|
||||
/* run the tests */
|
||||
test_csd();
|
||||
|
||||
printf("No crashes.\n");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
Testing the subchannel demux.
|
||||
Testing the csd sync.
|
||||
DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
|
||||
DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00
|
||||
DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00
|
||||
DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00
|
||||
No crashes.
|
|
@ -0,0 +1,43 @@
|
|||
AT_INIT
|
||||
AT_BANNER([Regression tests.])
|
||||
|
||||
# Example for tests.. copy and uncomment. This creates a new category
|
||||
# and test. It will copy the expected output to expout and then run
|
||||
# the given test. The stdout will be compared with the expout to determine
|
||||
# if the test was successfull.
|
||||
# AT_SETUP([NAME])
|
||||
# AT_KEYWORDS([NAME])
|
||||
# cat $abs_srcdir/NAME/NAME_test.ok > expout
|
||||
# AT_CHECK([$abs_top_builddir/tests/NAME/NAME_test], [], [expout])
|
||||
# AT_CLEANUP
|
||||
|
||||
AT_SETUP([ipa_recv])
|
||||
AT_KEYWORDS([ipa_recv])
|
||||
cat $abs_srcdir/ipa_recv/ipa_recv_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/ipa_recv/ipa_recv_test], [], [expout],[ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([subchan_demux])
|
||||
AT_KEYWORDS([subchan_demux])
|
||||
cat $abs_srcdir/subchan_demux/subchan_demux_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/subchan_demux/subchan_demux_test], [], [expout])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([rtp_test])
|
||||
AT_KEYWORDS([rtp_test])
|
||||
cat $abs_srcdir/rtp_test/rtp_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/rtp_test/rtp_test], [ignore], [expout])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([trau_sync])
|
||||
AT_KEYWORDS([trau_sync])
|
||||
cat $abs_srcdir/trau_sync/trau_sync_test.ok > expout
|
||||
cat $abs_srcdir/trau_sync/trau_sync_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/trau_sync/trau_sync_test], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([trau_pcu_ericsson])
|
||||
AT_KEYWORDS([trau_pcu_ericsson])
|
||||
cat $abs_srcdir/trau_pcu_ericsson/trau_pcu_ericsson_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/trau_pcu_ericsson/trau_pcu_ericsson_test], [0], [expout], [ignore])
|
||||
AT_CLEANUP
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,89 @@
|
|||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include <osmocom/trau/trau_sync.h>
|
||||
|
||||
static void frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
|
||||
{
|
||||
char *str = user_data;
|
||||
printf("demux_bits_cb '%s': %s\n", str, osmo_ubit_dump(bits, num_bits));
|
||||
}
|
||||
|
||||
static const uint8_t sync_pattern[] = {
|
||||
0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
|
||||
};
|
||||
|
||||
#define ASSERT_STATE(fi, x) OSMO_ASSERT(!strcmp(osmo_fsm_inst_state_name(fi), x))
|
||||
|
||||
static void test_body(void)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = osmo_trau_sync_alloc(NULL, "test", frame_out_cb, OSMO_TRAU_SYNCP_16_FR_EFR, "test");
|
||||
OSMO_ASSERT(fi);
|
||||
|
||||
printf("\n==> %s\n", __func__);
|
||||
|
||||
ubit_t bits[40*8];
|
||||
|
||||
/* send some invalid data */
|
||||
memset(bits, 0, sizeof(bits));
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
osmo_trau_sync_rx_ubits(fi, bits, 23);
|
||||
|
||||
/* first valid frame */
|
||||
osmo_pbit2ubit(bits, sync_pattern, sizeof(sync_pattern)*8);
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
ASSERT_STATE(fi, "FRAME_ALIGNED");
|
||||
|
||||
/* second valid frame */
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
ASSERT_STATE(fi, "FRAME_ALIGNED");
|
||||
|
||||
/* send wrong frame */
|
||||
memset(bits, 1, sizeof(bits));
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
ASSERT_STATE(fi, "FRAME_ALIGNED");
|
||||
|
||||
/* intersperse a valid frame */
|
||||
osmo_pbit2ubit(bits, sync_pattern, sizeof(sync_pattern)*8);
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
|
||||
/* second wrong frame - but not consecutive */
|
||||
memset(bits, 1, sizeof(bits));
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
ASSERT_STATE(fi, "FRAME_ALIGNED");
|
||||
|
||||
/* third wrong frame - second consecutive */
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
ASSERT_STATE(fi, "FRAME_ALIGNED");
|
||||
|
||||
/* only from third consecutive invalid frame onwards we should loose alignment */
|
||||
osmo_trau_sync_rx_ubits(fi, bits, sizeof(bits));
|
||||
ASSERT_STATE(fi, "FRAME_ALIGNMENT_LOST");
|
||||
}
|
||||
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = default_categories,
|
||||
.num_cat = ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging2(NULL, NULL);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
osmo_fsm_log_addr(false);
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
log_set_print_category(osmo_stderr_target, 0);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_log_level(osmo_stderr_target, LOGL_INFO);
|
||||
test_body();
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
trau_sync(test){WAIT_FRAME_ALIGN}: Allocated
|
||||
trau_sync(test){WAIT_FRAME_ALIGN}: Received Event RX_BITS
|
||||
trau_sync(test){WAIT_FRAME_ALIGN}: Received Event RX_BITS
|
||||
trau_sync(test){WAIT_FRAME_ALIGN}: Received Event RX_BITS
|
||||
trau_sync(test){WAIT_FRAME_ALIGN}: state_chg to FRAME_ALIGNED
|
||||
trau_sync(test){FRAME_ALIGNED}: Received Event RX_BITS
|
||||
trau_sync(test){FRAME_ALIGNED}: Received Event RX_BITS
|
||||
trau_sync(test){FRAME_ALIGNED}: Received Event RX_BITS
|
||||
trau_sync(test){FRAME_ALIGNED}: Received Event RX_BITS
|
||||
trau_sync(test){FRAME_ALIGNED}: Received Event RX_BITS
|
||||
trau_sync(test){FRAME_ALIGNED}: Received Event RX_BITS
|
||||
trau_sync(test){FRAME_ALIGNED}: Received Event RX_BITS
|
||||
trau_sync(test){FRAME_ALIGNED}: state_chg to FRAME_ALIGNMENT_LOST
|
||||
trau_sync(test){FRAME_ALIGNMENT_LOST}: Received Event RX_BITS
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
==> test_body
|
||||
demux_bits_cb 'test': 00000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000
|
||||
demux_bits_cb 'test': 00000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000
|
||||
demux_bits_cb 'test': 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
|
||||
demux_bits_cb 'test': 00000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000
|
||||
demux_bits_cb 'test': 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
|
||||
demux_bits_cb 'test': 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
|
||||
demux_bits_cb 'test':
|
Loading…
Reference in New Issue