mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-bts.git
synced 2025-11-08 16:11:57 +00:00
Compare commits
542 Commits
0.0.1
...
shared/31c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c328371b54 | ||
|
|
ffcdd0da7e | ||
|
|
265d0635f2 | ||
|
|
03483cf0f2 | ||
|
|
7f12860f08 | ||
|
|
96efd189cc | ||
|
|
1f27fda194 | ||
|
|
5a906c7b72 | ||
|
|
50dc96507c | ||
|
|
c265bef48c | ||
|
|
02a2afa962 | ||
|
|
ffc193443c | ||
|
|
8968b48643 | ||
|
|
641a934931 | ||
|
|
69897d7eed | ||
|
|
0d09e75f9c | ||
|
|
42cc96e2c1 | ||
|
|
8381a6a483 | ||
|
|
4d4dc26742 | ||
|
|
48eb374a96 | ||
|
|
2fa6ef2687 | ||
|
|
6d8bcbd192 | ||
|
|
b89a5fa55d | ||
|
|
9f0002b52b | ||
|
|
1cce69364d | ||
|
|
8332e29c9f | ||
|
|
be63f03254 | ||
|
|
9f22fcfd36 | ||
|
|
11b14fd662 | ||
|
|
9e873335ec | ||
|
|
04585dd90a | ||
|
|
645cba8532 | ||
|
|
adddb65f46 | ||
|
|
fbf04438b7 | ||
|
|
e0e9b30f5c | ||
|
|
68b9b376cf | ||
|
|
d0866fe477 | ||
|
|
913044ecc5 | ||
|
|
3dd6ebe0b8 | ||
|
|
1fb66c8e6a | ||
|
|
ab09e27d72 | ||
|
|
c0a3030277 | ||
|
|
be18984959 | ||
|
|
9d0fd073e9 | ||
|
|
8e4cc1cbb8 | ||
|
|
993575bcd8 | ||
|
|
0ff0f2d00f | ||
|
|
d9a2aa8d99 | ||
|
|
e43feaf231 | ||
|
|
fcca2e8218 | ||
|
|
15f899f89e | ||
|
|
3a54b7aa30 | ||
|
|
1f8053e366 | ||
|
|
e02d7796c3 | ||
|
|
714ccb9992 | ||
|
|
b0674e9636 | ||
|
|
d036cce744 | ||
|
|
c42bf5fdf5 | ||
|
|
ca71d07e44 | ||
|
|
c7ee5acba9 | ||
|
|
9698570d47 | ||
|
|
7be58a173a | ||
|
|
631945a367 | ||
|
|
5e1363071f | ||
|
|
4059e29a29 | ||
|
|
46c085d794 | ||
|
|
b1ceb40363 | ||
|
|
3ecb2bb604 | ||
|
|
ffe1d2e1e0 | ||
|
|
575f633483 | ||
|
|
54a8b313b4 | ||
|
|
c84ca8c82f | ||
|
|
035187b44e | ||
|
|
013df51ca8 | ||
|
|
07198750b2 | ||
|
|
ac76388c77 | ||
|
|
bc82b0189a | ||
|
|
fcd5c367d1 | ||
|
|
88d60a1f86 | ||
|
|
2cc37035d7 | ||
|
|
6cf49380cd | ||
|
|
caca1899ce | ||
|
|
fcdfb690ca | ||
|
|
dbc2731887 | ||
|
|
0655cac6f1 | ||
|
|
9e1dbf532e | ||
|
|
b05d72d21b | ||
|
|
b3d1779b96 | ||
|
|
bc8de14671 | ||
|
|
a19912db34 | ||
|
|
eececf5fa9 | ||
|
|
bc24955e91 | ||
|
|
bac0ff7f6d | ||
|
|
94a63851b7 | ||
|
|
3674645e20 | ||
|
|
a2b806c375 | ||
|
|
a7f9b58e44 | ||
|
|
0e2b624418 | ||
|
|
7996134d2a | ||
|
|
ac26607fe4 | ||
|
|
9d8aeab0b6 | ||
|
|
44980347f3 | ||
|
|
7d36e5ed46 | ||
|
|
afee0b7929 | ||
|
|
f5f41e8051 | ||
|
|
13a224063d | ||
|
|
0a1699ff8a | ||
|
|
14c60b425f | ||
|
|
b4280963c0 | ||
|
|
76c309e9f7 | ||
|
|
c64d425738 | ||
|
|
c5fedd24c9 | ||
|
|
c6ab90b270 | ||
|
|
73d9d3af6c | ||
|
|
d75c648871 | ||
|
|
8b5b993d29 | ||
|
|
9ed6b95c90 | ||
|
|
3e5b6db2d2 | ||
|
|
d59bbd16a7 | ||
|
|
f547bee878 | ||
|
|
57a4d121c4 | ||
|
|
0859795878 | ||
|
|
14ff925555 | ||
|
|
64a4327c34 | ||
|
|
a276b98236 | ||
|
|
71d98050f3 | ||
|
|
83dc54fb95 | ||
|
|
ae2473c2ca | ||
|
|
fb067905d5 | ||
|
|
3e317ea4f0 | ||
|
|
b45c8a6b6c | ||
|
|
2dca8f37e5 | ||
|
|
4b614a0246 | ||
|
|
bd26784362 | ||
|
|
4e3aa93681 | ||
|
|
06387d89e8 | ||
|
|
e030dfd443 | ||
|
|
21104720f7 | ||
|
|
b26b8fc776 | ||
|
|
9c4a524444 | ||
|
|
bb76d816da | ||
|
|
cfce4d65f2 | ||
|
|
00b4e064ff | ||
|
|
8ade45e795 | ||
|
|
3058854535 | ||
|
|
a37e239961 | ||
|
|
d863f9cbea | ||
|
|
4fcda92d7b | ||
|
|
0148d4e7d5 | ||
|
|
fae0149260 | ||
|
|
7503540959 | ||
|
|
2d725e77f7 | ||
|
|
35a8144e41 | ||
|
|
4fbe06b3f2 | ||
|
|
20f5422803 | ||
|
|
e5bda88c9d | ||
|
|
5030972316 | ||
|
|
fcd4026e7b | ||
|
|
f550a6a574 | ||
|
|
d290ee029a | ||
|
|
d242ec2ed9 | ||
|
|
37c332e5bf | ||
|
|
bbfd21a36c | ||
|
|
4160e3d0f8 | ||
|
|
2755f6e5b4 | ||
|
|
572ed461b6 | ||
|
|
1375a4b153 | ||
|
|
f91799eecf | ||
|
|
023739fc94 | ||
|
|
1817447c24 | ||
|
|
2e93a8683c | ||
|
|
bc7d6fbdbb | ||
|
|
bc48e26fc9 | ||
|
|
8196de46ad | ||
|
|
3c8ff3c70b | ||
|
|
15bc64c6cd | ||
|
|
06c098e2cb | ||
|
|
d76211dc3b | ||
|
|
5899b2d346 | ||
|
|
1881e46cb9 | ||
|
|
810fbff380 | ||
|
|
19224b4b9b | ||
|
|
3e110ae141 | ||
|
|
a4ffc44eac | ||
|
|
c28a5b0b25 | ||
|
|
623d97a0d3 | ||
|
|
9c279945a1 | ||
|
|
19e87d332f | ||
|
|
cdc6e3028c | ||
|
|
540f608c2c | ||
|
|
0a51e1a337 | ||
|
|
ef852ae86e | ||
|
|
96264b6dd9 | ||
|
|
2800b347e9 | ||
|
|
f56d56c439 | ||
|
|
ed9d643036 | ||
|
|
e14ddaf204 | ||
|
|
e968c4224d | ||
|
|
082e21dbb5 | ||
|
|
a82cc5321e | ||
|
|
897f97f632 | ||
|
|
5eef61414a | ||
|
|
5b69ec3e72 | ||
|
|
08571b1588 | ||
|
|
9ef742f5e7 | ||
|
|
f3108fafab | ||
|
|
0133ff3866 | ||
|
|
f9dd260ee5 | ||
|
|
99fb43f41a | ||
|
|
b8687024ea | ||
|
|
501673fcf3 | ||
|
|
6321c72522 | ||
|
|
e851e13413 | ||
|
|
d57e67e8da | ||
|
|
ac3fc27257 | ||
|
|
ad142f84b3 | ||
|
|
b2a8a642d6 | ||
|
|
5c0e7b1f2c | ||
|
|
e843f80832 | ||
|
|
cfa54328e2 | ||
|
|
50e63fa9e7 | ||
|
|
19009f2d7a | ||
|
|
de0ca823f1 | ||
|
|
6d76c1c701 | ||
|
|
d92774b4ac | ||
|
|
359fb8caf4 | ||
|
|
b77ae3bc60 | ||
|
|
6d5dc060ea | ||
|
|
b469e73148 | ||
|
|
33fe4ca97b | ||
|
|
870f1f1f84 | ||
|
|
7410bc3020 | ||
|
|
ff41a47c65 | ||
|
|
ad89cd978a | ||
|
|
971a95d259 | ||
|
|
ac98b545a8 | ||
|
|
ad10f0f533 | ||
|
|
fbf97e35eb | ||
|
|
91d204e2db | ||
|
|
f333387748 | ||
|
|
95c6eed436 | ||
|
|
612f387fc9 | ||
|
|
ed966f0428 | ||
|
|
481f14d87f | ||
|
|
89582f7e77 | ||
|
|
270cf418fc | ||
|
|
eebdfb8e6f | ||
|
|
2523cdbc7f | ||
|
|
6404a76661 | ||
|
|
48eca2524c | ||
|
|
0089ce4178 | ||
|
|
123caa3c83 | ||
|
|
593080ebab | ||
|
|
f169a75fc4 | ||
|
|
43b4176f0e | ||
|
|
266af543e3 | ||
|
|
718cc9dcac | ||
|
|
44eec601bc | ||
|
|
25346fe0d7 | ||
|
|
a7e7537776 | ||
|
|
ee43f46cb0 | ||
|
|
40ca16766d | ||
|
|
2563267757 | ||
|
|
a899146aea | ||
|
|
6002d17c24 | ||
|
|
d675de9c23 | ||
|
|
6ebabb560e | ||
|
|
d98f2f35ec | ||
|
|
fcdba6bfac | ||
|
|
19cefb0097 | ||
|
|
d07ee75fc6 | ||
|
|
c03fe5af31 | ||
|
|
9be5f8c9c0 | ||
|
|
0d194268fb | ||
|
|
bd3250a456 | ||
|
|
3a6220cae2 | ||
|
|
6a4c8a8596 | ||
|
|
17dd79a3ae | ||
|
|
636cad95a7 | ||
|
|
0809ae6941 | ||
|
|
4c4fd284ae | ||
|
|
42cc93efb6 | ||
|
|
4e46cb8961 | ||
|
|
5643130664 | ||
|
|
376183fcf0 | ||
|
|
fb0c9f0613 | ||
|
|
9d91c60875 | ||
|
|
470a6ced9a | ||
|
|
118eb43ba5 | ||
|
|
cdc5a4dc38 | ||
|
|
5cbc7e9167 | ||
|
|
4ad8d4d3c0 | ||
|
|
e45fc86359 | ||
|
|
54dd949e62 | ||
|
|
620be0bbed | ||
|
|
f0bdc1e562 | ||
|
|
294fd1b650 | ||
|
|
19f212951a | ||
|
|
cf4e3501a1 | ||
|
|
d9da7813a6 | ||
|
|
b7eb9865df | ||
|
|
dd2a51ed32 | ||
|
|
faba73a812 | ||
|
|
305d8314bc | ||
|
|
5e46e4b488 | ||
|
|
3d383c22c7 | ||
|
|
654fe73b78 | ||
|
|
6142f9262a | ||
|
|
64c5e3a19c | ||
|
|
b6942ffeb9 | ||
|
|
ff4f789249 | ||
|
|
0890e274b1 | ||
|
|
60b090ac53 | ||
|
|
dc9148d035 | ||
|
|
1897f03d4c | ||
|
|
6ac2e68467 | ||
|
|
225cf82290 | ||
|
|
18708dd3b6 | ||
|
|
98407bd457 | ||
|
|
0bb2974b37 | ||
|
|
550d22be5b | ||
|
|
6a2d89f48d | ||
|
|
3ff2fc4378 | ||
|
|
bcd50d3219 | ||
|
|
f0c5a424af | ||
|
|
76a1bf6136 | ||
|
|
e210f1a864 | ||
|
|
61e739912f | ||
|
|
4a303c7c38 | ||
|
|
8597a278d6 | ||
|
|
6ddd632adf | ||
|
|
0903001d9b | ||
|
|
c2371cc011 | ||
|
|
91816acfb8 | ||
|
|
fad5b08625 | ||
|
|
24b2128e29 | ||
|
|
3177f2b42f | ||
|
|
552989ad57 | ||
|
|
222a6a5e23 | ||
|
|
0670138ffc | ||
|
|
19cf0e81b3 | ||
|
|
5f408f934c | ||
|
|
fc9920830e | ||
|
|
c559dde69d | ||
|
|
3bcf3a5fd0 | ||
|
|
359b2cf469 | ||
|
|
20c5702e0f | ||
|
|
ce7559fbec | ||
|
|
0d30b5d818 | ||
|
|
56698b84e0 | ||
|
|
db51f0d73e | ||
|
|
bcae2abff8 | ||
|
|
d1ffab96ca | ||
|
|
61a1f99680 | ||
|
|
4fd0a84cf8 | ||
|
|
9bd5afa014 | ||
|
|
8d8ff80890 | ||
|
|
6f93861cfc | ||
|
|
6ae49691af | ||
|
|
2bad1363e9 | ||
|
|
9858a7defe | ||
|
|
a57fac59c6 | ||
|
|
36e73dd7ed | ||
|
|
17dd7fad72 | ||
|
|
ce826f3fc4 | ||
|
|
7350736054 | ||
|
|
fffbfd9890 | ||
|
|
a066e334dd | ||
|
|
dd4b8a2507 | ||
|
|
d1335d878b | ||
|
|
98a4404279 | ||
|
|
5705cfaebc | ||
|
|
c3646a80a7 | ||
|
|
8debeeeeea | ||
|
|
255343db4b | ||
|
|
4fe622cf9c | ||
|
|
4168d885cf | ||
|
|
c1ad2ac20f | ||
|
|
0efca9a1f9 | ||
|
|
ef2cb5ab7f | ||
|
|
4d197c96d8 | ||
|
|
d127ddbfcc | ||
|
|
f91924bb18 | ||
|
|
8c3d807b3f | ||
|
|
7daa093df7 | ||
|
|
b86bf060d3 | ||
|
|
fde8e6dc0c | ||
|
|
a9dee426d7 | ||
|
|
d777a19bb8 | ||
|
|
e5a04ea35d | ||
|
|
1c74191ff0 | ||
|
|
93c087892c | ||
|
|
7c2427c020 | ||
|
|
678321d013 | ||
|
|
e729a3d595 | ||
|
|
1195148fc6 | ||
|
|
1ddb183736 | ||
|
|
c2d3e45571 | ||
|
|
27baa4c3de | ||
|
|
b3eb6da2db | ||
|
|
d40d4d6071 | ||
|
|
08fce19cfc | ||
|
|
0390d54ade | ||
|
|
0c470759da | ||
|
|
5a53eff4cb | ||
|
|
990d1da8a4 | ||
|
|
65d4d5108a | ||
|
|
0cfefa0e12 | ||
|
|
4253150bab | ||
|
|
38420fb951 | ||
|
|
3696c6946d | ||
|
|
438a28714d | ||
|
|
c1368d4ebe | ||
|
|
744f745d7a | ||
|
|
8169b0bd85 | ||
|
|
07b37853a4 | ||
|
|
bf2a18e623 | ||
|
|
66f1fe15e9 | ||
|
|
07891a0908 | ||
|
|
343cae60b6 | ||
|
|
ea15101896 | ||
|
|
b57e17394b | ||
|
|
b19592f713 | ||
|
|
cb7697074e | ||
|
|
71b216d995 | ||
|
|
d53ae2d0f1 | ||
|
|
5f8a3149fe | ||
|
|
eda6c26360 | ||
|
|
6b561bb7ba | ||
|
|
fa8014f181 | ||
|
|
61fb64d252 | ||
|
|
54b8af0f64 | ||
|
|
9fdefc6ffe | ||
|
|
13e92be8bf | ||
|
|
e01a47aad4 | ||
|
|
babbbbf6ee | ||
|
|
821bf067e4 | ||
|
|
c882b85d8c | ||
|
|
565cf0d8ab | ||
|
|
a540332df3 | ||
|
|
ad3e31dc4b | ||
|
|
1c069cd0a0 | ||
|
|
0455e51cd5 | ||
|
|
ad09615acb | ||
|
|
2100a2e16f | ||
|
|
c58968be02 | ||
|
|
799ea59c2f | ||
|
|
700c645478 | ||
|
|
346e531222 | ||
|
|
b18f00f162 | ||
|
|
268c7f02fd | ||
|
|
388b9d0a35 | ||
|
|
9de1e9f914 | ||
|
|
7fe0838588 | ||
|
|
3af5426d71 | ||
|
|
e6ed814dc3 | ||
|
|
f7fd2e4798 | ||
|
|
36a3b0d85b | ||
|
|
36179bbcdf | ||
|
|
227c57728a | ||
|
|
baa88d542c | ||
|
|
d28b9940b9 | ||
|
|
2b7aace0b5 | ||
|
|
b1644b22d0 | ||
|
|
bcd08888f9 | ||
|
|
9aa6d9496b | ||
|
|
ff9e904926 | ||
|
|
f19ee66096 | ||
|
|
4301b09137 | ||
|
|
f5a0a439e9 | ||
|
|
bf91f06eca | ||
|
|
b0150b7ad4 | ||
|
|
d7718280c9 | ||
|
|
1e2b3259b9 | ||
|
|
29e1fdd994 | ||
|
|
af02387183 | ||
|
|
f78f35880f | ||
|
|
eac221b4ea | ||
|
|
f4f69ee6fc | ||
|
|
f1052b812d | ||
|
|
0be33e3add | ||
|
|
b03f8ae4f0 | ||
|
|
d9ab45d1aa | ||
|
|
51f9693ba6 | ||
|
|
b34faf6f8c | ||
|
|
bc74b7f432 | ||
|
|
f4a5bd2dd2 | ||
|
|
58f419c7ce | ||
|
|
11a787df24 | ||
|
|
caaa7e9d7b | ||
|
|
666fec7ff2 | ||
|
|
76aa95453f | ||
|
|
c623c4e589 | ||
|
|
2ed209c758 | ||
|
|
a0970249bf | ||
|
|
f4d14b3f2e | ||
|
|
d25b6a752b | ||
|
|
3cf942792a | ||
|
|
21724bbaed | ||
|
|
12b95405ff | ||
|
|
452112e823 | ||
|
|
d0e6749327 | ||
|
|
b81c5d4699 | ||
|
|
fe0c13f8bd | ||
|
|
3525f2c038 | ||
|
|
20d73555a2 | ||
|
|
47589f10a4 | ||
|
|
f1cbd81984 | ||
|
|
818cb2d314 | ||
|
|
6dd7c4fb57 | ||
|
|
bb9647f651 | ||
|
|
2e677958d2 | ||
|
|
771e77dff0 | ||
|
|
62579c7a34 | ||
|
|
4cd68dc4d7 | ||
|
|
eab71534ef | ||
|
|
6e121417a5 | ||
|
|
7a44e47ed6 | ||
|
|
6aa2a574fb | ||
|
|
fd58d925a8 | ||
|
|
b0985e3fa5 | ||
|
|
467e149763 | ||
|
|
127ec05b4e | ||
|
|
79da6f3283 | ||
|
|
143bb812dc | ||
|
|
fe4893e625 | ||
|
|
2660812084 | ||
|
|
5cdcf8a837 | ||
|
|
efdb45d5d0 | ||
|
|
477f35e78c | ||
|
|
187871e2ca | ||
|
|
b10d74d821 | ||
|
|
9582883235 | ||
|
|
c373448e03 | ||
|
|
7899dc5fcf | ||
|
|
215d9eecdd | ||
|
|
06636b6155 | ||
|
|
9508fb80a4 | ||
|
|
4ccca1ce36 | ||
|
|
a4a3574b1d | ||
|
|
2c40d02f27 | ||
|
|
16c0ab92c1 |
29
.gitignore
vendored
29
.gitignore
vendored
@@ -4,17 +4,46 @@ Makefile.in
|
||||
Makefile
|
||||
.deps
|
||||
|
||||
btsconfig.h
|
||||
btsconfig.h.in
|
||||
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
config.log
|
||||
config.status
|
||||
config.guess
|
||||
config.sub
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
stamp-h1
|
||||
core
|
||||
core.*
|
||||
|
||||
contrib/sysmobts-calib/sysmobts-calib
|
||||
|
||||
src/osmo-bts-sysmo/l1fwd-proxy
|
||||
src/osmo-bts-sysmo/sysmobts
|
||||
src/osmo-bts-sysmo/sysmobts-remote
|
||||
src/osmo-bts-sysmo/sysmobts-mgr
|
||||
|
||||
|
||||
tests/atconfig
|
||||
tests/package.m4
|
||||
tests/agch/agch_test
|
||||
tests/paging/paging_test
|
||||
tests/cipher/cipher_test
|
||||
tests/sysmobts/sysmobts_test
|
||||
tests/misc/misc_test
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
|
||||
# Possible generated file
|
||||
doc/vty_reference.xml
|
||||
|
||||
# Backups, vi, merges
|
||||
*.sw?
|
||||
*.orig
|
||||
*.sav
|
||||
|
||||
14
Makefile.am
14
Makefile.am
@@ -1,3 +1,15 @@
|
||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
|
||||
SUBDIRS = include src
|
||||
SUBDIRS = include src tests
|
||||
|
||||
|
||||
# package the contrib and doc
|
||||
EXTRA_DIST = \
|
||||
contrib/dump_docs.py contrib/screenrc-l1fwd contrib/sysmobts.service \
|
||||
contrib/l1fwd.init contrib/screenrc-sysmobts contrib/respawn.sh \
|
||||
contrib/sysmobts.init contrib/sysmobts-calib/Makefile \
|
||||
contrib/sysmobts-calib/sysmobts-calib.c \
|
||||
contrib/sysmobts-calib/sysmobts-layer1.c \
|
||||
contrib/sysmobts-calib/sysmobts-layer1.h \
|
||||
doc/examples/osmo-bts.cfg \
|
||||
doc/examples/sysmobts-mgr.cfg
|
||||
|
||||
63
README
63
README
@@ -1,10 +1,59 @@
|
||||
Repsoitory for new BTS-side A-bis code
|
||||
Repository for new the Osmocom BTS implementation.
|
||||
|
||||
This is the code that was originally developed inside osmocom-bb.git
|
||||
for turning modified OsmocomBB-supported phones into a simplistic BTS.
|
||||
This code implementes the Layer 2 and higher of a more or less
|
||||
conventional GSM BTS (Base Transceiver Station) - however, using an
|
||||
Abis/IP interface, rather than the old-fashioned E1/T1.
|
||||
|
||||
Specificallt, this includes
|
||||
* BTS-Side implementation of TS 08.58 (RSL) and TS 12.21 (OML)
|
||||
* BTS-Side implementation of LAPDm (using libosmocore/libosmogsm)
|
||||
* A somewhat separated interface between those higher layer parts
|
||||
and the Layer1 interface.
|
||||
|
||||
Right now, only one hardware and Layer1 are supported: The sysmocom
|
||||
sysmoBTS.
|
||||
|
||||
There is some experimental and way incomplete code to use a couple of
|
||||
OsmocomBB phones and run them in the BTS. However, the required code
|
||||
for the Calypso DSP code have not been written yet. This would still
|
||||
require a lot of work.
|
||||
|
||||
Some additional work is being done in using some parts of the OpenBTS
|
||||
L1FEC and glue it against omso-bts. However, this is also still in an
|
||||
early, experimental stage.
|
||||
|
||||
== Known Limitations ==
|
||||
|
||||
As of June 3, 2012, the following known limitations exist in this
|
||||
implementation:
|
||||
|
||||
=== Common Core ===
|
||||
* No Extended BCCH support
|
||||
* System Information limited to 1,2,2bis,2ter,2quater,3,4,5,6,9,13
|
||||
* No RATSCCH in AMR
|
||||
* No OML (TS 12.21) alarms yet (temperature, ...)
|
||||
* Only single-TRX BTS at this point
|
||||
* Will reject TS 12.21 STARTING TIME in SET BTS ATTR / SET CHAN ATTR
|
||||
* No support for frequency hopping
|
||||
* No reporting of interference levels as part of TS 08.58 RF RES IND
|
||||
* No error reporting in case PAGING COMMAND fails due to queue overflow
|
||||
* No hand-over support (planned)
|
||||
* No use of TS 08.58 BS Power and MS Power parameters
|
||||
* No support of TS 08.58 MultiRate Control
|
||||
* No support of TS 08.58 Supported Codec Types
|
||||
* No support of Bter frame / ENHANCED MEASUREMENT REPORT
|
||||
|
||||
=== osmo-bts-sysmo ===
|
||||
* No CSD / ECSD support (not planned)
|
||||
* No GPRS/EDGE support (planned)
|
||||
* GSM-R frequency band supported, but no NCH/ASCI/SoLSA
|
||||
* All timeslots on one TRX have to use same training sequence (TSC)
|
||||
* No multi-TRX support yet, though hardware+L1 support stacking
|
||||
* Makes no use of 12.21 Intave Parameters and Interference
|
||||
Level Boundaries
|
||||
* Makes no use of TS 12.21 T3105
|
||||
* Doesn't yet include MAC address in Abis/IP Identity message
|
||||
* MphConfig.CNF can be returned to the wrong callback. E.g. with Tx Power
|
||||
and ciphering. The dispatch should take a look at the hLayer3.
|
||||
|
||||
However, the BTS-side A-bis is also going to be needed for other projects, thus
|
||||
the split.
|
||||
|
||||
It doesn't really build yet, as a lot of dependencies have not yet been
|
||||
resolved.
|
||||
|
||||
39
configure.ac
39
configure.ac
@@ -4,6 +4,7 @@ AC_INIT([osmo-bts],
|
||||
[openbsc-devel@lists.openbsc.org])
|
||||
|
||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
@@ -24,6 +25,8 @@ PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.0.7)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis)
|
||||
|
||||
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
|
||||
AC_ARG_ENABLE(sysmocom-bts,
|
||||
@@ -33,11 +36,45 @@ AC_ARG_ENABLE(sysmocom-bts,
|
||||
AC_MSG_RESULT([$enable_sysmocom_bts])
|
||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
|
||||
|
||||
# We share gsm_data.h with OpenBSC and need to be pointed to the source
|
||||
# directory of OpenBSC for now.
|
||||
AC_ARG_WITH([openbsc],
|
||||
[AS_HELP_STRING([--with-openbsc=INCLUDE_DIR],
|
||||
[OpenBSC include directory for openbsc/gsm_data_shared.h])],
|
||||
[openbsc_incdir="$withval"],
|
||||
[openbsc_incdir="`cd $srcdir; pwd`/../openbsc/openbsc/include"])
|
||||
AC_SUBST([OPENBSC_INCDIR], $openbsc_incdir)
|
||||
|
||||
oldCPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS -I$OPENBSC_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
|
||||
AC_CHECK_HEADER([openbsc/gsm_data_shared.h],[],
|
||||
[AC_MSG_ERROR([openbsc/gsm_data_shared.h can not be found in $openbsc_incdir])],
|
||||
[#include <osmo-bts/tx_power.h>])
|
||||
CPPFLAGS=$oldCPPFLAGS
|
||||
|
||||
# Check for the sbts2050_header.h that was added after the 3.6 release
|
||||
oldCPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS -I$OPENBSC_INCDIR $LIBOSMOCORE_CFLAGS"
|
||||
AC_CHECK_HEADER([sysmocom/femtobts/sbts2050_header.h],
|
||||
[sysmo_uc_header="yes"],[])
|
||||
CPPFLAGS=$oldCPPFLAGS
|
||||
|
||||
if test "$sysmo_uc_header" = "yes" ; then
|
||||
AC_DEFINE(BUILD_SBTS2050, 1, [Define if we want to build SBTS2050])
|
||||
fi
|
||||
AM_CONDITIONAL(BUILD_SBTS2050, test "x$sysmo_uc_header" = "xyes")
|
||||
AM_CONFIG_HEADER(btsconfig.h)
|
||||
|
||||
AC_OUTPUT(
|
||||
src/Makefile
|
||||
src/common/Makefile
|
||||
src/osmo-bts-sysmo/Makefile
|
||||
src/osmo-bts-bb/Makefile
|
||||
include/Makefile
|
||||
include/osmo-bts/Makefile
|
||||
tests/Makefile
|
||||
tests/paging/Makefile
|
||||
tests/agch/Makefile
|
||||
tests/cipher/Makefile
|
||||
tests/sysmobts/Makefile
|
||||
tests/misc/Makefile
|
||||
Makefile)
|
||||
|
||||
40
contrib/dump_docs.py
Executable file
40
contrib/dump_docs.py
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Start the process and dump the documentation to the doc dir
|
||||
"""
|
||||
|
||||
import socket, subprocess, time,os
|
||||
|
||||
env = os.environ
|
||||
env['L1FWD_BTS_HOST'] = '127.0.0.1'
|
||||
|
||||
bts_proc = subprocess.Popen(["./src/osmo-bts-sysmo/sysmobts-remote",
|
||||
"-c", "./doc/examples/osmo-bts.cfg"], env = env,
|
||||
stdin=None, stdout=None)
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sck.setblocking(1)
|
||||
sck.connect(("localhost", 4241))
|
||||
sck.recv(4096)
|
||||
|
||||
# Now send the command
|
||||
sck.send("show online-help\r")
|
||||
xml = ""
|
||||
while True:
|
||||
data = sck.recv(4096)
|
||||
xml = "%s%s" % (xml, data)
|
||||
if data.endswith('\r\nOsmoBTS> '):
|
||||
break
|
||||
|
||||
# Now write everything until the end to the file
|
||||
out = open('doc/vty_reference.xml', 'w')
|
||||
out.write(xml[18:-11])
|
||||
out.close()
|
||||
finally:
|
||||
# Clean-up
|
||||
bts_proc.kill()
|
||||
bts_proc.wait()
|
||||
|
||||
13
contrib/respawn-only.sh
Executable file
13
contrib/respawn-only.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
PID=$$
|
||||
echo "-1000" > /proc/$PID/oom_score_adj
|
||||
|
||||
trap "{ kill 0; kill -2 0; }" EXIT
|
||||
|
||||
while [ -f $1 ]; do
|
||||
(echo "0" > /proc/self/oom_score_adj && exec nice -n -20 $*) &
|
||||
LAST_PID=$!
|
||||
wait $LAST_PID
|
||||
sleep 10s
|
||||
done
|
||||
@@ -1,4 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
PID=$$
|
||||
echo "-1000" > /proc/$PID/oom_score_adj
|
||||
|
||||
trap "kill 0" EXIT
|
||||
|
||||
while [ -e /etc/passwd ]; do
|
||||
$*
|
||||
cat /lib/firmware/sysmobts-v?.bit > /dev/fpgadl_par0
|
||||
sleep 2s
|
||||
cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0
|
||||
sleep 1s
|
||||
echo "0" > /sys/class/leds/activity_led/brightness
|
||||
(echo "0" > /proc/self/oom_score_adj && exec nice -n -20 $*) &
|
||||
LAST_PID=$!
|
||||
wait $LAST_PID
|
||||
sleep 10s
|
||||
done
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
chdir /tmp
|
||||
screen -t BTS 0 /etc/osmocom/respawn.sh /usr/bin/sysmobts -c /etc/osmocom/osmo-bts.cfg
|
||||
screen -t BTS 0 /etc/osmocom/respawn.sh /usr/bin/sysmobts -c /etc/osmocom/osmo-bts.cfg -r 1 -M
|
||||
screen -t PCU 1 /etc/osmocom/respawn-only.sh /usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg -e
|
||||
screen -t MGR 2 /etc/osmocom/respawn-only.sh /usr/bin/sysmobts-mgr -n -c /etc/osmocom/sysmobts-mgr.cfg
|
||||
detach
|
||||
|
||||
10
contrib/sysmobts-calib/Makefile
Normal file
10
contrib/sysmobts-calib/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
CFLAGS=`pkg-config --cflags libosmocore` -Wall -Werror
|
||||
LIBS=`pkg-config --libs libosmocore libosmogsm`
|
||||
|
||||
all: sysmobts-calib
|
||||
|
||||
sysmobts-calib: sysmobts-calib.o sysmobts-layer1.o
|
||||
$(CC) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ -lrt $(LIBS)
|
||||
|
||||
clean:
|
||||
@rm -f sysmobts-calib *.o
|
||||
537
contrib/sysmobts-calib/sysmobts-calib.c
Normal file
537
contrib/sysmobts-calib/sysmobts-calib.c
Normal file
@@ -0,0 +1,537 @@
|
||||
/* OCXO/TCXO based calibration utility */
|
||||
|
||||
/*
|
||||
* (C) 2012-2013 Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1types.h>
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "sysmobts-layer1.h"
|
||||
|
||||
enum actions {
|
||||
ACTION_SCAN,
|
||||
ACTION_CALIB,
|
||||
ACTION_BCCH,
|
||||
ACTION_BCCH_CCCH,
|
||||
};
|
||||
|
||||
static const char *modes[] = {
|
||||
[ACTION_SCAN] = "scan",
|
||||
[ACTION_CALIB] = "calibrate",
|
||||
[ACTION_BCCH] = "bcch",
|
||||
[ACTION_BCCH_CCCH] = "bcch_ccch",
|
||||
};
|
||||
|
||||
static const char *bands[] = {
|
||||
[GsmL1_FreqBand_850] = "850",
|
||||
[GsmL1_FreqBand_900] = "900",
|
||||
[GsmL1_FreqBand_1800] = "1800",
|
||||
[GsmL1_FreqBand_1900] = "1900",
|
||||
};
|
||||
|
||||
struct channel_pair {
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
static const struct channel_pair arfcns[] = {
|
||||
[GsmL1_FreqBand_850] = { .min = 128, .max = 251 },
|
||||
[GsmL1_FreqBand_900] = { .min = 1, .max = 124 },
|
||||
[GsmL1_FreqBand_1800] = { .min = 512, .max = 885 },
|
||||
[GsmL1_FreqBand_1900] = { .min = 512, .max = 810 },
|
||||
|
||||
};
|
||||
|
||||
static const char *clk_source[] = {
|
||||
[SuperFemto_ClkSrcId_Ocxo] = "ocxo",
|
||||
[SuperFemto_ClkSrcId_Tcxo] = "tcxo",
|
||||
[SuperFemto_ClkSrcId_External] = "external",
|
||||
[SuperFemto_ClkSrcId_GpsPps] = "gps",
|
||||
[SuperFemto_ClkSrcId_Trx] = "trx",
|
||||
[SuperFemto_ClkSrcId_Rx] = "rx",
|
||||
[SuperFemto_ClkSrcId_Edge] = "edge",
|
||||
[SuperFemto_ClkSrcId_NetList] = "netlisten",
|
||||
};
|
||||
|
||||
static const struct value_string sapi_names[GsmL1_Sapi_NUM+1] = {
|
||||
{ GsmL1_Sapi_Fcch, "FCCH" },
|
||||
{ GsmL1_Sapi_Sch, "SCH" },
|
||||
{ GsmL1_Sapi_Sacch, "SACCH" },
|
||||
{ GsmL1_Sapi_Sdcch, "SDCCH" },
|
||||
{ GsmL1_Sapi_Bcch, "BCCH" },
|
||||
{ GsmL1_Sapi_Pch, "PCH" },
|
||||
{ GsmL1_Sapi_Agch, "AGCH" },
|
||||
{ GsmL1_Sapi_Cbch, "CBCH" },
|
||||
{ GsmL1_Sapi_Rach, "RACH" },
|
||||
{ GsmL1_Sapi_TchF, "TCH/F" },
|
||||
{ GsmL1_Sapi_FacchF, "FACCH/F" },
|
||||
{ GsmL1_Sapi_TchH, "TCH/H" },
|
||||
{ GsmL1_Sapi_FacchH, "FACCH/H" },
|
||||
{ GsmL1_Sapi_Nch, "NCH" },
|
||||
{ GsmL1_Sapi_Pdtch, "PDTCH" },
|
||||
{ GsmL1_Sapi_Pacch, "PACCH" },
|
||||
{ GsmL1_Sapi_Pbcch, "PBCCH" },
|
||||
{ GsmL1_Sapi_Pagch, "PAGCH" },
|
||||
{ GsmL1_Sapi_Ppch, "PPCH" },
|
||||
{ GsmL1_Sapi_Pnch, "PNCH" },
|
||||
{ GsmL1_Sapi_Ptcch, "PTCCH" },
|
||||
{ GsmL1_Sapi_Prach, "PRACH" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static int action = ACTION_SCAN;
|
||||
static int band = GsmL1_FreqBand_900;
|
||||
static int calib = SuperFemto_ClkSrcId_Ocxo;
|
||||
static int source = SuperFemto_ClkSrcId_NetList;
|
||||
static int dsp_flags = 0x0;
|
||||
static int cal_arfcn = 0;
|
||||
static int initial_cor = 0;
|
||||
static int steps = -1;
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf("Usage: sysmobts-calib ARGS\n");
|
||||
}
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf(" -h --help this text\n");
|
||||
printf(" -c --clock "
|
||||
"ocxo|tcxo|external|gps|trx|rx|edge\n");
|
||||
printf(" -s --calibration-source "
|
||||
"ocxo|tcxo|external|gps|trx|rx|edge|netlisten\n");
|
||||
printf(" -b --band 850|900|1800|1900\n");
|
||||
printf(" -m --mode scan|calibrate|bcch|bcch_ccch\n");
|
||||
printf(" -a --arfcn NR arfcn for calibration\n");
|
||||
printf(" -d --dsp-flags NR dsp mask for debug log\n");
|
||||
printf(" -t --threshold level\n");
|
||||
printf(" -i --initial-clock-correction COR.\n");
|
||||
printf(" -t --steps STEPS\n");
|
||||
}
|
||||
|
||||
static int find_value(const char **array, int size, char *value)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < size; ++i) {
|
||||
if (array[i] == NULL)
|
||||
continue;
|
||||
if (strcmp(value, array[i]) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
printf("Failed to find: '%s'\n", value);
|
||||
exit(-2);
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"calibration-source", 1, 0, 's'},
|
||||
{"clock", 1, 0, 'c'},
|
||||
{"mode", 1, 0, 'm'},
|
||||
{"band", 1, 0, 'b'},
|
||||
{"dsp-flags", 1, 0, 'd'},
|
||||
{"arfcn", 1, 0, 'a'},
|
||||
{"initial-clock-correction", 1, 0, 'i'},
|
||||
{"steps", 1, 0, 't'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hs:c:m:b:d:a:i:t:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
exit(0);
|
||||
case 's':
|
||||
source = find_value(clk_source,
|
||||
ARRAY_SIZE(clk_source), optarg);
|
||||
break;
|
||||
case 'c':
|
||||
calib = find_value(clk_source,
|
||||
ARRAY_SIZE(clk_source), optarg);
|
||||
break;
|
||||
case 'm':
|
||||
action = find_value(modes,
|
||||
ARRAY_SIZE(modes), optarg);
|
||||
break;
|
||||
case 'b':
|
||||
band = find_value(bands,
|
||||
ARRAY_SIZE(bands), optarg);
|
||||
break;
|
||||
case 'd':
|
||||
dsp_flags = strtol(optarg, NULL, 16);
|
||||
break;
|
||||
case 'a':
|
||||
cal_arfcn = atoi(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
initial_cor = atoi(optarg);
|
||||
break;
|
||||
case 't':
|
||||
steps = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
printf("Unhandled option, terminating.\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (source == calib) {
|
||||
printf("Clock source and reference clock may not be the same.\n");
|
||||
exit(-3);
|
||||
}
|
||||
|
||||
if (calib == SuperFemto_ClkSrcId_NetList) {
|
||||
printf("Clock may not be network listen.\n");
|
||||
exit(-4);
|
||||
}
|
||||
|
||||
if (action == ACTION_CALIB && source == SuperFemto_ClkSrcId_NetList) {
|
||||
if (cal_arfcn == 0) {
|
||||
printf("Please specify the reference ARFCN.\n");
|
||||
exit(-5);
|
||||
}
|
||||
|
||||
if (cal_arfcn < arfcns[band].min || cal_arfcn > arfcns[band].max) {
|
||||
printf("ARFCN(%d) is not in the given band.\n", cal_arfcn);
|
||||
exit(-6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_RC(rc) \
|
||||
if (rc != 0) \
|
||||
return EXIT_FAILURE;
|
||||
|
||||
#define CHECK_RC_MSG(rc, msg) \
|
||||
if (rc != 0) { \
|
||||
printf("%s: %d\n", msg, rc); \
|
||||
return EXIT_FAILURE; \
|
||||
}
|
||||
#define CHECK_COND_MSG(cond, rc, msg) \
|
||||
if (cond) { \
|
||||
printf("%s: %d\n", msg, rc); \
|
||||
return EXIT_FAILURE; \
|
||||
}
|
||||
|
||||
struct scan_result
|
||||
{
|
||||
uint16_t arfcn;
|
||||
float rssi;
|
||||
};
|
||||
|
||||
static int scan_cmp(const void *arg1, const void *arg2)
|
||||
{
|
||||
struct scan_result *elem1 = (struct scan_result *) arg1;
|
||||
struct scan_result *elem2 = (struct scan_result * )arg2;
|
||||
|
||||
float diff = elem1->rssi - elem2->rssi;
|
||||
if (diff > 0.0)
|
||||
return 1;
|
||||
else if (diff < 0.0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_band()
|
||||
{
|
||||
int arfcn, rc, i;
|
||||
|
||||
/* Scan results.. at most 400 items */
|
||||
struct scan_result results[400];
|
||||
memset(&results, 0, sizeof(results));
|
||||
int num_scan_results = 0;
|
||||
|
||||
printf("Going to scan bands.\n");
|
||||
|
||||
for (arfcn = arfcns[band].min; arfcn <= arfcns[band].max; ++arfcn) {
|
||||
float mean_rssi;
|
||||
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
rc = power_scan(band, arfcn, 10, &mean_rssi);
|
||||
CHECK_RC_MSG(rc, "Power Measurement failed");
|
||||
|
||||
results[num_scan_results].arfcn = arfcn;
|
||||
results[num_scan_results].rssi = mean_rssi;
|
||||
num_scan_results++;
|
||||
}
|
||||
|
||||
qsort(results, num_scan_results, sizeof(struct scan_result), scan_cmp);
|
||||
printf("\nSorted scan results (weakest first):\n");
|
||||
for (i = 0; i < num_scan_results; ++i)
|
||||
printf("ARFCN %3d: %.4f\n", results[i].arfcn, results[i].rssi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calib_get_clock_error(void)
|
||||
{
|
||||
int rc, clkErr, clkErrRes;
|
||||
|
||||
printf("Going to determine the clock offset.\n");
|
||||
|
||||
rc = rf_clock_info(&clkErr, &clkErrRes);
|
||||
CHECK_RC_MSG(rc, "Clock info failed.\n");
|
||||
|
||||
if (clkErr == 0 && clkErrRes == 0) {
|
||||
printf("Failed to get the clock info. Are both clocks present?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Empiric gps error determination. With revE and firmware v3.3
|
||||
* the clock error for TCXO to GPS appears to have a different
|
||||
* sign. The device in question doesn't have a networklisten mode
|
||||
* so it is impossible to verify that this only applies to GPS.
|
||||
*/
|
||||
if (source == SuperFemto_ClkSrcId_GpsPps)
|
||||
clkErr *= -1;
|
||||
|
||||
|
||||
/* this is an absolute clock error */
|
||||
printf("The calibration value is: %d\n", clkErr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calib_clock_after_sync(void)
|
||||
{
|
||||
int rc, clkErr, clkErrRes, iteration, cor;
|
||||
|
||||
iteration = 0;
|
||||
cor = initial_cor;
|
||||
|
||||
printf("Trying to calibrate now and reducing clock error.\n");
|
||||
|
||||
for (iteration = 0; iteration < steps || steps <= 0; ++iteration) {
|
||||
if (steps > 0)
|
||||
printf("Iteration %d/%d with correction: %d\n", iteration, steps, cor);
|
||||
else
|
||||
printf("Iteration %d with correction: %d\n", iteration, cor);
|
||||
|
||||
rc = rf_clock_info(&clkErr, &clkErrRes);
|
||||
CHECK_RC_MSG(rc, "Clock info failed.\n");
|
||||
|
||||
/*
|
||||
* TODO: use the clock error resolution here, implement it as a
|
||||
* a PID controller..
|
||||
*/
|
||||
|
||||
/* Picocell class requires 0.1ppm.. but that is 'too easy' */
|
||||
if (fabs(clkErr / 1000.0f) <= 0.05f) {
|
||||
printf("The calibration value is: %d\n", cor);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cor -= clkErr / 2;
|
||||
rc = set_clock_cor(cor, calib, source);
|
||||
CHECK_RC_MSG(rc, "Clock correction failed.\n");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_initial_clock(HANDLE layer1, int *clock)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Trying to find an initial clock value.\n");
|
||||
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
int rc;
|
||||
int cor = i * 150;
|
||||
rc = wait_for_sync(layer1, cor, calib, source);
|
||||
if (rc == 1) {
|
||||
printf("Found initial clock offset: %d\n", cor);
|
||||
*clock = cor;
|
||||
break;
|
||||
} else {
|
||||
CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
|
||||
}
|
||||
|
||||
cor = i * -150;
|
||||
rc = wait_for_sync(layer1, cor, calib, source);
|
||||
if (rc == 1) {
|
||||
printf("Found initial clock offset: %d\n", cor);
|
||||
*clock = cor;
|
||||
break;
|
||||
} else {
|
||||
CHECK_RC_MSG(rc, "Failed to set new clock value.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calib_clock_netlisten(void)
|
||||
{
|
||||
int rc, cor = initial_cor;
|
||||
float mean_rssi;
|
||||
HANDLE layer1;
|
||||
|
||||
rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
|
||||
CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
|
||||
if (mean_rssi < -118.0f)
|
||||
printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
|
||||
|
||||
/* initial lock */
|
||||
rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
|
||||
if (rc == -23)
|
||||
rc = find_initial_clock(layer1, &cor);
|
||||
CHECK_RC_MSG(rc, "Following SCH failed");
|
||||
|
||||
/* now try to calibrate it */
|
||||
rc = set_clock_cor(cor, calib, source);
|
||||
CHECK_RC_MSG(rc, "Clock setup failed.");
|
||||
|
||||
calib_clock_after_sync();
|
||||
|
||||
rc = mph_close(layer1);
|
||||
CHECK_RC_MSG(rc, "MPH-Close");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int calib_clock(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* now try to calibrate it */
|
||||
rc = set_clock_cor(initial_cor, calib, source);
|
||||
CHECK_RC_MSG(rc, "Clock setup failed.");
|
||||
|
||||
calib_get_clock_error();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static int bcch_follow(void)
|
||||
{
|
||||
int rc, cor = initial_cor;
|
||||
float mean_rssi;
|
||||
HANDLE layer1;
|
||||
|
||||
rc = power_scan(band, cal_arfcn, 10, &mean_rssi);
|
||||
CHECK_RC_MSG(rc, "ARFCN measurement scan failed");
|
||||
if (mean_rssi < -118.0f)
|
||||
printf("ARFCN has weak signal for calibration: %f\n", mean_rssi);
|
||||
|
||||
/* initial lock */
|
||||
rc = follow_sch(band, cal_arfcn, calib, source, &layer1);
|
||||
if (rc == -23)
|
||||
rc = find_initial_clock(layer1, &cor);
|
||||
CHECK_RC_MSG(rc, "Following SCH failed");
|
||||
|
||||
/* identify the BSIC and set it as TSC */
|
||||
rc = find_bsic();
|
||||
CHECK_COND_MSG(rc < 0, rc, "Identifying the BSIC failed");
|
||||
rc = set_tsc_from_bsic(layer1, rc);
|
||||
CHECK_RC_MSG(rc, "Setting the TSC failed");
|
||||
|
||||
|
||||
/* follow the bcch */
|
||||
rc = follow_bcch(layer1);
|
||||
CHECK_RC_MSG(rc, "Follow BCCH");
|
||||
|
||||
/* follow the pch */
|
||||
if (action == ACTION_BCCH_CCCH) {
|
||||
rc = follow_pch(layer1);
|
||||
CHECK_RC_MSG(rc, "Follow BCCH/CCCH");
|
||||
}
|
||||
|
||||
/* now wait for the PhDataInd */
|
||||
for (;;) {
|
||||
uint32_t fn;
|
||||
uint8_t block;
|
||||
uint8_t data[23];
|
||||
size_t size;
|
||||
struct gsm_time gsmtime;
|
||||
GsmL1_Sapi_t sapi;
|
||||
|
||||
rc = wait_for_data(data, &size, &fn, &block, &sapi);
|
||||
if (rc == 1)
|
||||
continue;
|
||||
CHECK_RC_MSG(rc, "No Data Indication");
|
||||
|
||||
gsm_fn2gsmtime(&gsmtime, fn);
|
||||
printf("%02u/%02u/%02u %6s %s\n",
|
||||
gsmtime.t1, gsmtime.t2, gsmtime.t3,
|
||||
get_value_string(sapi_names, sapi),
|
||||
osmo_hexdump(data, size));
|
||||
}
|
||||
|
||||
rc = mph_close(layer1);
|
||||
CHECK_RC_MSG(rc, "MPH-Close");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
handle_options(argc, argv);
|
||||
printf("Initializing the Layer1\n");
|
||||
rc = initialize_layer1(dsp_flags);
|
||||
CHECK_RC(rc);
|
||||
|
||||
printf("Fetching system info.\n");
|
||||
rc = print_system_info();
|
||||
CHECK_RC(rc);
|
||||
|
||||
printf("Opening RF frontend with clock(%d) and correction(%d)\n",
|
||||
calib, initial_cor);
|
||||
rc = activate_rf_frontend(calib, initial_cor);
|
||||
CHECK_RC(rc);
|
||||
|
||||
if (action == ACTION_SCAN)
|
||||
return scan_band();
|
||||
else if (action == ACTION_BCCH || action == ACTION_BCCH_CCCH)
|
||||
return bcch_follow();
|
||||
else {
|
||||
if (source == SuperFemto_ClkSrcId_NetList)
|
||||
return calib_clock_netlisten();
|
||||
return calib_clock();
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
783
contrib/sysmobts-calib/sysmobts-layer1.c
Normal file
783
contrib/sysmobts-calib/sysmobts-layer1.c
Normal file
@@ -0,0 +1,783 @@
|
||||
/* Layer1 handling for the DSP/FPGA */
|
||||
/*
|
||||
* (C) 2012-2013 Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU 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 <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1prim.h>
|
||||
|
||||
#include "sysmobts-layer1.h"
|
||||
|
||||
#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof((ar)[0]))
|
||||
|
||||
#define BTS_DSP2ARM "/dev/msgq/superfemto_dsp2arm"
|
||||
#define BTS_ARM2DSP "/dev/msgq/superfemto_arm2dsp"
|
||||
#define L1_SIG_ARM2DSP "/dev/msgq/gsml1_sig_arm2dsp"
|
||||
#define L1_SIG_DSP2ARM "/dev/msgq/gsml1_sig_dsp2arm"
|
||||
|
||||
int set_clock_cor(int clock_cor, int calib, int source);
|
||||
static int wait_read_ignore(int seconds);
|
||||
|
||||
static int sys_dsp2arm = -1,
|
||||
sys_arm2dsp = -1,
|
||||
sig_dsp2arm = -1,
|
||||
sig_arm2dsp = -1;
|
||||
|
||||
static int sync_indicated = 0;
|
||||
static int time_indicated = 0;
|
||||
|
||||
static int open_devices()
|
||||
{
|
||||
sys_dsp2arm = open(BTS_DSP2ARM, O_RDONLY);
|
||||
if (sys_dsp2arm == -1) {
|
||||
perror("Failed to open dsp2arm system queue");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sys_arm2dsp = open(BTS_ARM2DSP, O_WRONLY);
|
||||
if (sys_arm2dsp == -1) {
|
||||
perror("Failed to open arm2dsp system queue");
|
||||
return -2;
|
||||
}
|
||||
|
||||
sig_dsp2arm = open(L1_SIG_DSP2ARM, O_RDONLY);
|
||||
if (sig_dsp2arm == -1) {
|
||||
perror("Failed to open dsp2arm sig queue");
|
||||
return -3;
|
||||
}
|
||||
|
||||
sig_arm2dsp = open(L1_SIG_ARM2DSP, O_WRONLY);
|
||||
if (sig_arm2dsp == -1) {
|
||||
perror("Failed to open arm2dsp sig queue");
|
||||
return -4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a primitive to the system queue
|
||||
*/
|
||||
static int send_primitive(int primitive, SuperFemto_Prim_t *prim)
|
||||
{
|
||||
prim->id = primitive;
|
||||
return write(sys_arm2dsp, prim, sizeof(*prim)) != sizeof(*prim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a confirmation
|
||||
*/
|
||||
static int wait_primitive(int wait_for, SuperFemto_Prim_t *prim)
|
||||
{
|
||||
memset(prim, 0, sizeof(*prim));
|
||||
int rc = read(sys_dsp2arm, prim, sizeof(*prim));
|
||||
if (rc != sizeof(*prim)) {
|
||||
printf("Short read in %s: %d\n", __func__, rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prim->id != wait_for) {
|
||||
printf("Got primitive %d but waited for %d\n",
|
||||
prim->id, wait_for);
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The Cnf for the Req, assume it is a +1 */
|
||||
static int answer_for(int primitive)
|
||||
{
|
||||
return primitive + 1;
|
||||
}
|
||||
|
||||
static int send_recv_primitive(int p, SuperFemto_Prim_t *prim)
|
||||
{
|
||||
int rc;
|
||||
rc = send_primitive(p, prim);
|
||||
if (rc != 0)
|
||||
return -1;
|
||||
|
||||
rc = wait_primitive(answer_for(p), prim);
|
||||
if (rc != 0)
|
||||
return -2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int answer_for_sig(int prim)
|
||||
{
|
||||
static const GsmL1_PrimId_t cnf[] = {
|
||||
[GsmL1_PrimId_MphInitReq] = GsmL1_PrimId_MphInitCnf,
|
||||
[GsmL1_PrimId_MphCloseReq] = GsmL1_PrimId_MphCloseCnf,
|
||||
[GsmL1_PrimId_MphConnectReq] = GsmL1_PrimId_MphConnectCnf,
|
||||
[GsmL1_PrimId_MphActivateReq] = GsmL1_PrimId_MphActivateCnf,
|
||||
[GsmL1_PrimId_MphConfigReq] = GsmL1_PrimId_MphConfigCnf,
|
||||
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
|
||||
};
|
||||
|
||||
if (prim < 0 || prim >= ARRAY_SIZE(cnf)) {
|
||||
printf("Unknown primitive: %d\n", prim);
|
||||
exit(-3);
|
||||
}
|
||||
|
||||
return cnf[prim];
|
||||
}
|
||||
|
||||
static int is_indication(int prim)
|
||||
{
|
||||
return
|
||||
prim == GsmL1_PrimId_MphTimeInd ||
|
||||
prim == GsmL1_PrimId_MphSyncInd ||
|
||||
prim == GsmL1_PrimId_PhConnectInd ||
|
||||
prim == GsmL1_PrimId_PhReadyToSendInd ||
|
||||
prim == GsmL1_PrimId_PhDataInd ||
|
||||
prim == GsmL1_PrimId_PhRaInd;
|
||||
}
|
||||
|
||||
|
||||
static int send_recv_sig_prim(int p, GsmL1_Prim_t *prim)
|
||||
{
|
||||
int rc;
|
||||
prim->id = p;
|
||||
rc = write(sig_arm2dsp, prim, sizeof(*prim));
|
||||
if (rc != sizeof(*prim)) {
|
||||
printf("Failed to write: %d\n", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = read(sig_dsp2arm, prim, sizeof(*prim));
|
||||
if (rc != sizeof(*prim)) {
|
||||
printf("Failed to read: %d\n", rc);
|
||||
return -2;
|
||||
}
|
||||
} while (is_indication(prim->id));
|
||||
|
||||
if (prim->id != answer_for_sig(p)) {
|
||||
printf("Wrong L1 result got %d wanted %d for prim: %d\n",
|
||||
prim->id, answer_for_sig(p), p);
|
||||
return -3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_indication(int p, GsmL1_Prim_t *prim)
|
||||
{
|
||||
int rc;
|
||||
memset(prim, 0, sizeof(*prim));
|
||||
|
||||
struct timespec start_time, now_time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
|
||||
/*
|
||||
* TODO: select.... with timeout. The below will work 99% as we will
|
||||
* get time indications very soonish after the connect
|
||||
*/
|
||||
for (;;) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &now_time);
|
||||
if (now_time.tv_sec - start_time.tv_sec > 10) {
|
||||
printf("Timeout waiting for indication.\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
rc = read(sig_dsp2arm, prim, sizeof(*prim));
|
||||
if (rc != sizeof(*prim)) {
|
||||
printf("Failed to read.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!is_indication(prim->id)) {
|
||||
printf("No indication: %d\n", prim->id);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (p != prim->id && prim->id == GsmL1_PrimId_MphSyncInd) {
|
||||
printf("Got sync.\n");
|
||||
sync_indicated = 1;
|
||||
continue;
|
||||
}
|
||||
if (p != prim->id && prim->id == GsmL1_PrimId_MphTimeInd) {
|
||||
time_indicated = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p != prim->id) {
|
||||
printf("Wrong indication got %d wanted %d\n",
|
||||
prim->id, p);
|
||||
return -3;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_trace_flags(uint32_t dsp)
|
||||
{
|
||||
SuperFemto_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
prim.u.setTraceFlagsReq.u32Tf = dsp;
|
||||
return send_primitive(SuperFemto_PrimId_SetTraceFlagsReq, &prim);
|
||||
}
|
||||
|
||||
static int reset_and_wait()
|
||||
{
|
||||
int rc;
|
||||
SuperFemto_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
rc = send_recv_primitive(SuperFemto_PrimId_Layer1ResetReq, &prim);
|
||||
if (rc != 0)
|
||||
return -1;
|
||||
if (prim.u.layer1ResetCnf.status != GsmL1_Status_Success)
|
||||
return -2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the message queues and (re-)initialize the DSP and FPGA
|
||||
*/
|
||||
int initialize_layer1(uint32_t dsp_flags)
|
||||
{
|
||||
if (open_devices() != 0) {
|
||||
printf("Failed to open devices.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_trace_flags(dsp_flags) != 0) {
|
||||
printf("Failed to set dsp flags.\n");
|
||||
return -2;
|
||||
}
|
||||
if (reset_and_wait() != 0) {
|
||||
printf("Failed to reset the firmware.\n");
|
||||
return -3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print systems infos
|
||||
*/
|
||||
int print_system_info()
|
||||
{
|
||||
int rc;
|
||||
SuperFemto_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
rc = send_recv_primitive(SuperFemto_PrimId_SystemInfoReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to send SystemInfoRequest.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prim.u.systemInfoCnf.status != GsmL1_Status_Success) {
|
||||
printf("Failed to request SystemInfoRequest.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
#define INFO_DSP(x) x.u.systemInfoCnf.dspVersion
|
||||
#define INFO_FPGA(x) x.u.systemInfoCnf.fpgaVersion
|
||||
#ifdef FEMTOBTS_NO_BOARD_VERSION
|
||||
#define BOARD_REV(x) -1
|
||||
#define BOARD_OPT(x) -1
|
||||
#else
|
||||
#define BOARD_REV(x) x.u.systemInfoCnf.boardVersion.rev
|
||||
#define BOARD_OPT(x) x.u.systemInfoCnf.boardVersion.option
|
||||
#endif
|
||||
|
||||
printf("DSP v%d.%d.%d FPGA v%d.%d.%d Rev: %d Option: %d\n",
|
||||
INFO_DSP(prim).major, INFO_DSP(prim).minor, INFO_DSP(prim).build,
|
||||
INFO_FPGA(prim).major, INFO_FPGA(prim).minor, INFO_FPGA(prim).build,
|
||||
BOARD_REV(prim), BOARD_OPT(prim));
|
||||
#undef INFO_DSP
|
||||
#undef INFO_FPGA
|
||||
#undef BOARD_REV
|
||||
#undef BOARD_OPT
|
||||
return 0;
|
||||
}
|
||||
|
||||
int activate_rf_frontend(int clock_source, int initial_cor)
|
||||
{
|
||||
int rc;
|
||||
SuperFemto_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
prim.u.activateRfReq.timing.u8TimSrc = 1;
|
||||
prim.u.activateRfReq.msgq.u8UseTchMsgq = 0;
|
||||
prim.u.activateRfReq.msgq.u8UsePdtchMsgq = 0;
|
||||
|
||||
prim.u.activateRfReq.rfTrx.iClkCor = initial_cor;
|
||||
prim.u.activateRfReq.rfTrx.clkSrc = clock_source;
|
||||
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
|
||||
prim.u.activateRfReq.rfRx.iClkCor = initial_cor;
|
||||
prim.u.activateRfReq.rfRx.clkSrc = clock_source;
|
||||
#endif
|
||||
|
||||
rc = send_recv_primitive(SuperFemto_PrimId_ActivateRfReq, &prim);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int mph_init(int band, int arfcn, HANDLE *layer1)
|
||||
{
|
||||
int rc;
|
||||
GsmL1_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
prim.u.mphInitReq.deviceParam.devType = GsmL1_DevType_Rxd;
|
||||
prim.u.mphInitReq.deviceParam.freqBand = band;
|
||||
prim.u.mphInitReq.deviceParam.u16Arfcn = arfcn;
|
||||
prim.u.mphInitReq.deviceParam.u16BcchArfcn = arfcn;
|
||||
prim.u.mphInitReq.deviceParam.fRxPowerLevel = -75.f;
|
||||
prim.u.mphInitReq.deviceParam.u8AutoTA = 1;
|
||||
|
||||
rc = send_recv_sig_prim(GsmL1_PrimId_MphInitReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to initialize the physical channel.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prim.u.mphInitCnf.status != GsmL1_Status_Success) {
|
||||
printf("MPH Init failed.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (prim.u.mphInitCnf.freqBand != band) {
|
||||
printf("Layer1 ignored the band: %d\n",
|
||||
prim.u.mphInitCnf.freqBand);
|
||||
return -3;
|
||||
}
|
||||
#endif
|
||||
|
||||
*layer1 = prim.u.mphInitCnf.hLayer1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mph_close(HANDLE layer1)
|
||||
{
|
||||
int rc;
|
||||
GsmL1_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
prim.u.mphCloseReq.hLayer1 = layer1;
|
||||
rc = send_recv_sig_prim(GsmL1_PrimId_MphCloseReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to close the MPH\n");
|
||||
return -6;
|
||||
}
|
||||
if (prim.u.mphCloseCnf.status != GsmL1_Status_Success) {
|
||||
printf("MPH Close failed.\n");
|
||||
return -7;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int follow_sch(int band, int arfcn, int clock, int ref, HANDLE *layer1)
|
||||
{
|
||||
int rc;
|
||||
GsmL1_Prim_t prim;
|
||||
|
||||
time_indicated = 0;
|
||||
sync_indicated = 0;
|
||||
|
||||
rc = mph_init(band, arfcn, layer1);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
/* 1.) Connect */
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
prim.u.mphConnectReq.hLayer1 = *layer1;
|
||||
prim.u.mphConnectReq.u8Tn = 0;
|
||||
prim.u.mphConnectReq.logChComb = GsmL1_LogChComb_IV;
|
||||
rc = send_recv_sig_prim(GsmL1_PrimId_MphConnectReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to connect.\n");
|
||||
return -1;
|
||||
}
|
||||
if (prim.u.mphConnectCnf.status != GsmL1_Status_Success) {
|
||||
printf("Connect failed.\n");
|
||||
return -2;
|
||||
}
|
||||
if (prim.u.mphConnectCnf.u8Tn != 0) {
|
||||
printf("Wrong timeslot.\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* 2.) Activate */
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
prim.u.mphActivateReq.hLayer1 = *layer1;
|
||||
prim.u.mphActivateReq.u8Tn = 0;
|
||||
prim.u.mphActivateReq.sapi = GsmL1_Sapi_Sch;
|
||||
prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
|
||||
rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Activation failed.\n");
|
||||
return -4;
|
||||
}
|
||||
if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
|
||||
printf("Activation not successful.\n");
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* 3.) Wait for indication... TODO: check... */
|
||||
printf("Waiting for connect indication.\n");
|
||||
rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Didn't get a connect indication.\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 4.) Indication Syndication TODO: check... */
|
||||
if (!sync_indicated) {
|
||||
printf("Waiting for sync indication.\n");
|
||||
rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
|
||||
if (rc < 0) {
|
||||
printf("Didn't get a sync indication.\n");
|
||||
return -23;
|
||||
} else if (rc == 0) {
|
||||
if (!prim.u.mphSyncInd.u8Synced) {
|
||||
printf("Failed to get sync.\n");
|
||||
return -23;
|
||||
} else {
|
||||
printf("Synced.\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("Already synced.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int follow_sapi(HANDLE layer1, const GsmL1_Sapi_t sapi)
|
||||
{
|
||||
int rc;
|
||||
GsmL1_Prim_t prim;
|
||||
|
||||
/* 1.) Activate BCCH or such... */
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
prim.u.mphActivateReq.hLayer1 = layer1;
|
||||
prim.u.mphActivateReq.u8Tn = 0;
|
||||
prim.u.mphActivateReq.sapi = sapi;
|
||||
prim.u.mphActivateReq.dir = GsmL1_Dir_RxDownlink;
|
||||
|
||||
rc = send_recv_sig_prim(GsmL1_PrimId_MphActivateReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Activation failed.\n");
|
||||
return -4;
|
||||
}
|
||||
if (prim.u.mphActivateCnf.status != GsmL1_Status_Success) {
|
||||
printf("Activation not successful.\n");
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* 2.) Wait for indication... */
|
||||
printf("Waiting for connect indication.\n");
|
||||
rc = wait_for_indication(GsmL1_PrimId_PhConnectInd, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Didn't get a connect indication.\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (prim.u.phConnectInd.sapi != sapi) {
|
||||
printf("Got a connect indication for the wrong type: %d\n",
|
||||
prim.u.phConnectInd.sapi);
|
||||
return -6;
|
||||
}
|
||||
|
||||
/* 3.) Wait for PhDataInd... */
|
||||
printf("Waiting for data.\n");
|
||||
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Didn't get data.\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int follow_bcch(HANDLE layer1)
|
||||
{
|
||||
return follow_sapi(layer1, GsmL1_Sapi_Bcch);
|
||||
}
|
||||
|
||||
int follow_pch(HANDLE layer1)
|
||||
{
|
||||
return follow_sapi(layer1, GsmL1_Sapi_Pch);
|
||||
}
|
||||
|
||||
int find_bsic(void)
|
||||
{
|
||||
int rc, i;
|
||||
GsmL1_Prim_t prim;
|
||||
|
||||
printf("Waiting for SCH data.\n");
|
||||
for (i = 0; i < 10; ++i) {
|
||||
uint8_t bsic;
|
||||
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
|
||||
if (rc < 0) {
|
||||
printf("Didn't get SCH data.\n");
|
||||
return rc;
|
||||
}
|
||||
if (prim.u.phDataInd.sapi != GsmL1_Sapi_Sch)
|
||||
continue;
|
||||
|
||||
bsic = (prim.u.phDataInd.msgUnitParam.u8Buffer[0] >> 2) & 0xFF;
|
||||
return bsic;
|
||||
}
|
||||
|
||||
printf("Giving up finding the SCH\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int set_tsc_from_bsic(HANDLE layer1, int bsic)
|
||||
{
|
||||
int rc;
|
||||
int tsc = bsic & 0x7;
|
||||
GsmL1_Prim_t prim;
|
||||
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
prim.u.mphConfigReq.hLayer3 = 0x23;
|
||||
prim.u.mphConfigReq.hLayer1 = layer1;
|
||||
prim.u.mphConfigReq.cfgParamId = GsmL1_ConfigParamId_SetNbTsc;
|
||||
prim.u.mphConfigReq.cfgParams.setNbTsc.u8NbTsc = tsc;
|
||||
rc = send_recv_sig_prim(GsmL1_PrimId_MphConfigReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to send configure.\n");
|
||||
}
|
||||
|
||||
if (prim.u.mphConfigCnf.status != GsmL1_Status_Success) {
|
||||
printf("Failed to set the config cnf.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_clock_cor(int clock_cor, int calib, int source)
|
||||
{
|
||||
int rc;
|
||||
SuperFemto_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
prim.u.rfClockSetupReq.rfTrx.iClkCor = clock_cor;
|
||||
prim.u.rfClockSetupReq.rfTrx.clkSrc = calib;
|
||||
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
|
||||
prim.u.rfClockSetupReq.rfRx.iClkCor = clock_cor;
|
||||
prim.u.rfClockSetupReq.rfRx.clkSrc = calib;
|
||||
#endif
|
||||
prim.u.rfClockSetupReq.rfTrxClkCal.clkSrc = source;
|
||||
|
||||
rc = send_recv_primitive(SuperFemto_PrimId_RfClockSetupReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to set the clock setup.\n");
|
||||
return -1;
|
||||
}
|
||||
if (prim.u.rfClockSetupCnf.status != GsmL1_Status_Success) {
|
||||
printf("Clock setup was not successfull.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rf_clock_info(int *clkErr, int *clkErrRes)
|
||||
{
|
||||
SuperFemto_Prim_t prim;
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
|
||||
int rc;
|
||||
|
||||
/* reset the counter */
|
||||
prim.u.rfClockInfoReq.u8RstClkCal = 1;
|
||||
rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to reset the clock info.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* wait for a value */
|
||||
wait_read_ignore(15);
|
||||
|
||||
/* ask for the current counter/error */
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
prim.u.rfClockInfoReq.u8RstClkCal = 0;
|
||||
rc = send_recv_primitive(SuperFemto_PrimId_RfClockInfoReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to get the clock info.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
printf("Error: %d Res: %d\n",
|
||||
prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr,
|
||||
prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes);
|
||||
*clkErr = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErr;
|
||||
*clkErrRes = prim.u.rfClockInfoCnf.rfTrxClkCal.iClkErrRes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int power_scan(int band, int arfcn, int duration, float *mean_rssi)
|
||||
{
|
||||
int rc;
|
||||
HANDLE layer1;
|
||||
GsmL1_Prim_t prim;
|
||||
|
||||
/* init */
|
||||
rc = mph_init(band, arfcn, &layer1);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
/* mph measure request */
|
||||
memset(&prim, 0, sizeof(prim));
|
||||
prim.u.mphMeasureReq.hLayer1 = layer1;
|
||||
prim.u.mphMeasureReq.u32Duration = duration;
|
||||
rc = send_recv_sig_prim(GsmL1_PrimId_MphMeasureReq, &prim);
|
||||
if (rc != 0) {
|
||||
printf("Failed to send measurement request.\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (prim.u.mphMeasureCnf.status != GsmL1_Status_Success) {
|
||||
printf("MphMeasureReq was not confirmed.\n");
|
||||
return -5;
|
||||
}
|
||||
|
||||
*mean_rssi = prim.u.mphMeasureCnf.fMeanRssi;
|
||||
|
||||
/* close */
|
||||
rc = mph_close(layer1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for indication...
|
||||
*/
|
||||
int wait_for_sync(HANDLE layer1, int cor, int calib, int source)
|
||||
{
|
||||
GsmL1_Prim_t prim;
|
||||
int rc;
|
||||
|
||||
rc = set_clock_cor(cor, calib, source);
|
||||
if (rc != 0) {
|
||||
printf("Failed to set the clock correction.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sync_indicated = 0;
|
||||
rc = wait_for_indication(GsmL1_PrimId_MphSyncInd, &prim);
|
||||
if (rc < 0 && rc != -4) {
|
||||
return rc;
|
||||
} else if (rc == 0) {
|
||||
if (!prim.u.mphSyncInd.u8Synced) {
|
||||
printf("Failed to get sync.\n");
|
||||
return 0;
|
||||
}
|
||||
printf("Synced.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wait_for_data(uint8_t *data, size_t *size, uint32_t *fn, uint8_t *block, GsmL1_Sapi_t *sap)
|
||||
{
|
||||
GsmL1_Prim_t prim;
|
||||
int rc;
|
||||
|
||||
rc = wait_for_indication(GsmL1_PrimId_PhDataInd, &prim);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (prim.u.phDataInd.sapi == GsmL1_Sapi_Sch)
|
||||
return 1;
|
||||
|
||||
*size = prim.u.phDataInd.msgUnitParam.u8Size;
|
||||
*fn = prim.u.phDataInd.u32Fn;
|
||||
*block = prim.u.phDataInd.u8BlockNbr;
|
||||
*sap = prim.u.phDataInd.sapi;
|
||||
memcpy(data, prim.u.phDataInd.msgUnitParam.u8Buffer, *size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the pipe is not running full.
|
||||
*
|
||||
*/
|
||||
static int wait_read_ignore(int seconds)
|
||||
{
|
||||
int max, rc;
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
|
||||
max = sys_dsp2arm > sig_dsp2arm ? sys_dsp2arm : sig_dsp2arm;
|
||||
|
||||
timeout.tv_sec = seconds;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
while (1) {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sys_dsp2arm, &fds);
|
||||
FD_SET(sig_dsp2arm, &fds);
|
||||
|
||||
|
||||
rc = select(max + 1, &fds, NULL, NULL, &timeout);
|
||||
if (rc == -1) {
|
||||
printf("Failed to select.\n");
|
||||
return -1;
|
||||
} else if (rc) {
|
||||
if (FD_ISSET(sys_dsp2arm, &fds)) {
|
||||
SuperFemto_Prim_t prim;
|
||||
rc = read(sys_dsp2arm, &prim, sizeof(prim));
|
||||
if (rc != sizeof(prim)) {
|
||||
perror("Failed to read system primitive");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(sig_dsp2arm, &fds)) {
|
||||
GsmL1_Prim_t prim;
|
||||
rc = read(sig_dsp2arm, &prim, sizeof(prim));
|
||||
if (rc != sizeof(prim)) {
|
||||
perror("Failed to read signal primitiven");
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
} else if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef __linux__
|
||||
#error "Non portable code"
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
45
contrib/sysmobts-calib/sysmobts-layer1.h
Normal file
45
contrib/sysmobts-calib/sysmobts-layer1.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef SYSMOBTS_LAYER_H
|
||||
#define SYSMOBTS_LAYER_H
|
||||
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
|
||||
#ifdef FEMTOBTS_API_VERSION
|
||||
#define SuperFemto_PrimId_t FemtoBts_PrimId_t
|
||||
#define SuperFemto_Prim_t FemtoBts_Prim_t
|
||||
#define SuperFemto_PrimId_SystemInfoReq FemtoBts_PrimId_SystemInfoReq
|
||||
#define SuperFemto_PrimId_SystemInfoCnf FemtoBts_PrimId_SystemInfoCnf
|
||||
#define SuperFemto_SystemInfoCnf_t FemtoBts_SystemInfoCnf_t
|
||||
#define SuperFemto_PrimId_SystemFailureInd FemtoBts_PrimId_SystemFailureInd
|
||||
#define SuperFemto_PrimId_ActivateRfReq FemtoBts_PrimId_ActivateRfReq
|
||||
#define SuperFemto_PrimId_ActivateRfCnf FemtoBts_PrimId_ActivateRfCnf
|
||||
#define SuperFemto_PrimId_DeactivateRfReq FemtoBts_PrimId_DeactivateRfReq
|
||||
#define SuperFemto_PrimId_DeactivateRfCnf FemtoBts_PrimId_DeactivateRfCnf
|
||||
#define SuperFemto_PrimId_SetTraceFlagsReq FemtoBts_PrimId_SetTraceFlagsReq
|
||||
#define SuperFemto_PrimId_RfClockInfoReq FemtoBts_PrimId_RfClockInfoReq
|
||||
#define SuperFemto_PrimId_RfClockInfoCnf FemtoBts_PrimId_RfClockInfoCnf
|
||||
#define SuperFemto_PrimId_RfClockSetupReq FemtoBts_PrimId_RfClockSetupReq
|
||||
#define SuperFemto_PrimId_RfClockSetupCnf FemtoBts_PrimId_RfClockSetupCnf
|
||||
#define SuperFemto_PrimId_Layer1ResetReq FemtoBts_PrimId_Layer1ResetReq
|
||||
#define SuperFemto_PrimId_Layer1ResetCnf FemtoBts_PrimId_Layer1ResetCnf
|
||||
#define SuperFemto_PrimId_NUM FemtoBts_PrimId_NUM
|
||||
#define HW_SYSMOBTS_V1 1
|
||||
#define SUPERFEMTO_API(x,y,z) FEMTOBTS_API(x,y,z)
|
||||
#endif
|
||||
|
||||
extern int initialize_layer1(uint32_t dsp_flags);
|
||||
extern int print_system_info();
|
||||
extern int activate_rf_frontend(int clock_source, int clock_cor);
|
||||
extern int power_scan(int band, int arfcn, int duration, float *mean_rssi);
|
||||
extern int follow_sch(int band, int arfcn, int calib, int reference, HANDLE *layer1);
|
||||
extern int follow_bch(HANDLE layer1);
|
||||
extern int find_bsic(void);
|
||||
extern int set_tsc_from_bsic(HANDLE layer1, int bsic);
|
||||
extern int set_clock_cor(int clock_corr, int calib, int source);
|
||||
extern int rf_clock_info(int *clkErr, int *clkErrRes);
|
||||
extern int mph_close(HANDLE layer1);
|
||||
extern int wait_for_sync(HANDLE layer1, int cor, int calib, int source);
|
||||
extern int follow_bcch(HANDLE layer1);
|
||||
extern int follow_pch(HANDLE layer1);
|
||||
extern int wait_for_data(uint8_t *data, size_t *size, uint32_t *fn, uint8_t *block, GsmL1_Sapi_t *sapi);
|
||||
|
||||
#endif
|
||||
12
contrib/sysmobts-mgr.service
Normal file
12
contrib/sysmobts-mgr.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=sysmocom sysmoBTS manager
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/sysmobts-mgr -ns -c /etc/osmocom/sysmobts-mgr.cfg
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -9,14 +9,12 @@
|
||||
# Description:
|
||||
### END INIT INFO
|
||||
|
||||
. /etc/default/rcS
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
/usr/bin/screen -d -m -c /etc/osmocom/screenrc-sysmobts
|
||||
/usr/bin/screen -d -m -c /etc/osmocom/screenrc-sysmobts -S sysmobts
|
||||
;;
|
||||
stop)
|
||||
echo "This script doesn't support stop"
|
||||
/usr/bin/screen -d -r sysmobts -X quit
|
||||
exit 1
|
||||
;;
|
||||
restart|reload|force-reload)
|
||||
|
||||
19
contrib/sysmobts.service
Normal file
19
contrib/sysmobts.service
Normal file
@@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=sysmocom sysmoBTS
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStartPre=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
|
||||
ExecStart=/usr/bin/sysmobts -s -c /etc/osmocom/osmo-bts.cfg -M
|
||||
ExecStopPost=/bin/sh -c 'echo 0 > /sys/class/leds/activity_led/brightness'
|
||||
ExecStopPost=/bin/sh -c 'cat /lib/firmware/sysmobts-v?.bit > /dev/fpgadl_par0 ; sleep 3s; cat /lib/firmware/sysmobts-v?.out > /dev/dspdl_dm644x_0; sleep 1s'
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
|
||||
# The msg queues must be read fast enough
|
||||
CPUSchedulingPolicy=rr
|
||||
CPUSchedulingPriority=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
61
doc/control_interface.txt
Normal file
61
doc/control_interface.txt
Normal file
@@ -0,0 +1,61 @@
|
||||
The osmo-bts control interface is currently supporting the following operations:
|
||||
|
||||
h2. generic
|
||||
|
||||
h3. trx.0.thermal-attenuation
|
||||
|
||||
The idea of this paramter is to attenuate the system output power as part of
|
||||
thermal management. In some cases the PA might be passing a critical level,
|
||||
so an external control process can use this attribute to reduce the system
|
||||
output power.
|
||||
|
||||
Please note that all values in the context of transmit power calculation
|
||||
are integers in milli-dB (1/10000 bel), so the below example is setting
|
||||
the attenuation at 3 dB:
|
||||
|
||||
<pre>
|
||||
bsc_control.py -d localhost -p 4238 -s trx.0.thermal-attenuation 3000
|
||||
Got message: SET_REPLY 1 trx.0.thermal-attenuation 3000
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
bsc_control.py -d localhost -p 4238 -g trx.0.thermal-attenuation
|
||||
Got message: GET_REPLY 1 trx.0.thermal-attenuation 3000
|
||||
</pre>
|
||||
|
||||
|
||||
h2. sysmobts specific
|
||||
|
||||
h3. trx.0.clock-info
|
||||
|
||||
obtain information on the current clock status:
|
||||
|
||||
<pre>
|
||||
bsc_control.py -d localhost -p 4238 -g trx.0.clock-info
|
||||
Got message: GET_REPLY 1 trx.0.clock-info -100,ocxo,0,0,gps
|
||||
</pre>
|
||||
|
||||
which is to be interpreted as:
|
||||
* current clock correction value is -100 ppb
|
||||
* current clock source is OCXO
|
||||
* deviation between clock source and calibration source is 0 ppb
|
||||
* resolution of clock error measurement is 0 ppt (0 means no result yet)
|
||||
* current calibration source is GPS
|
||||
|
||||
When this attribute is set, any value passed on is discarded, but the clock
|
||||
calibration process is re-started.
|
||||
|
||||
|
||||
h3. trx.0.clock-correction
|
||||
|
||||
This attribute can get and set the current clock correction value:
|
||||
|
||||
<pre>
|
||||
bsc_control.py -d localhost -p 4238 -g trx.0.clock-correction
|
||||
Got message: GET_REPLY 1 trx.0.clock-correction -100
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
bsc_control.py -d localhost -p 4238 -s trx.0.clock-correction -- -99
|
||||
Got message: SET_REPLY 1 trx.0.clock-correction success
|
||||
</pre>
|
||||
@@ -3,7 +3,7 @@
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging color 0
|
||||
logging timestamp 0
|
||||
logging level all everything
|
||||
logging level rsl info
|
||||
@@ -13,15 +13,9 @@ log stderr
|
||||
logging level meas notice
|
||||
logging level pag info
|
||||
logging level l1c info
|
||||
logging level l1p debug
|
||||
logging level l1p info
|
||||
logging level dsp debug
|
||||
logging level abis notice
|
||||
logging level lglobal notice
|
||||
logging level llapdm notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
@@ -30,4 +24,3 @@ bts 0
|
||||
band 1800
|
||||
ipa unit-id 1234 0
|
||||
oml remote-ip 192.168.100.11
|
||||
rtp bind-ip 192.168.100.239
|
||||
|
||||
24
doc/examples/sysmobts-mgr.cfg
Normal file
24
doc/examples/sysmobts-mgr.cfg
Normal file
@@ -0,0 +1,24 @@
|
||||
!
|
||||
! SysmoMgr (0.3.0.141-33e5) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging timestamp 0
|
||||
logging level all everything
|
||||
logging level temp info
|
||||
logging level fw info
|
||||
logging level find info
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
sysmobts-mgr
|
||||
@@ -1 +1,3 @@
|
||||
SUBDIRS = osmo-bts
|
||||
|
||||
noinst_HEADERS = openbsc/gsm_data.h
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
|
||||
oml.h paging.h rsl.h signal.h vty.h
|
||||
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
|
||||
handover.h msg_utils.h tx_power.h control_if.h cbch.h
|
||||
|
||||
@@ -3,25 +3,12 @@
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
#define OML_RETRY_TIMER 5
|
||||
#define OML_PING_TIMER 20
|
||||
|
||||
struct ipabis_link {
|
||||
int state;
|
||||
struct gsm_bts *bts; /* set, if OML link */
|
||||
struct gsm_bts_trx *trx; /* set, if RSL link */
|
||||
struct osmo_fd bfd;
|
||||
struct osmo_timer_list timer;
|
||||
struct msgb *rx_msg;
|
||||
struct llist_head tx_queue;
|
||||
int ping, pong, id_resp;
|
||||
uint32_t ip;
|
||||
};
|
||||
|
||||
enum {
|
||||
LINK_STATE_IDLE = 0,
|
||||
LINK_STATE_RETRYING,
|
||||
@@ -29,14 +16,14 @@ enum {
|
||||
LINK_STATE_CONNECT,
|
||||
};
|
||||
|
||||
int abis_tx(struct ipabis_link *link, struct msgb *msg);
|
||||
struct msgb *abis_msgb_alloc(int headroom);
|
||||
void abis_push_ipa(struct msgb *msg, uint8_t proto);
|
||||
int abis_open(struct ipabis_link *link, uint32_t ip);
|
||||
void abis_close(struct ipabis_link *link);
|
||||
void abis_init(struct gsm_bts *bts);
|
||||
struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
|
||||
char *model_name);
|
||||
|
||||
|
||||
int abis_oml_sendmsg(struct msgb *msg);
|
||||
int abis_rsl_sendmsg(struct msgb *msg);
|
||||
int abis_bts_rsl_sendmsg(struct msgb *msg);
|
||||
|
||||
uint32_t get_signlink_remote_ip(struct e1inp_sign_link *link);
|
||||
|
||||
#endif /* _ABIS_H */
|
||||
|
||||
17
include/osmo-bts/amr.h
Normal file
17
include/osmo-bts/amr.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _OSMO_BTS_AMR_H
|
||||
#define _OSMO_BTS_AMR_H
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
#define AMR_TOC_QBIT 0x04
|
||||
#define AMR_CMR_NONE 0xF
|
||||
|
||||
void amr_log_mr_conf(int ss, int logl, const char *pfx,
|
||||
struct amr_multirate_conf *amr_mrc);
|
||||
|
||||
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
|
||||
const uint8_t *mr_conf, unsigned int len);
|
||||
|
||||
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan);
|
||||
|
||||
#endif /* _OSMO_BTS_AMR_H */
|
||||
@@ -3,6 +3,12 @@
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
enum bts_global_status {
|
||||
BTS_STATUS_RF_ACTIVE,
|
||||
BTS_STATUS_RF_MUTE,
|
||||
BTS_STATUS_LAST,
|
||||
};
|
||||
|
||||
extern void *tall_bts_ctx;
|
||||
|
||||
int bts_init(struct gsm_bts *bts);
|
||||
@@ -18,13 +24,20 @@ int trx_link_estab(struct gsm_bts_trx *trx);
|
||||
void bts_new_si(void *arg);
|
||||
void bts_setup_slot(struct gsm_bts_trx_ts *slot, uint8_t comb);
|
||||
|
||||
int lchan_init_lapdm(struct gsm_lchan *lchan);
|
||||
|
||||
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg);
|
||||
struct msgb *bts_agch_dequeue(struct gsm_bts *bts);
|
||||
void bts_update_agch_max_queue_length(struct gsm_bts *bts);
|
||||
int bts_agch_max_queue_length(int T, int bcch_conf);
|
||||
int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
|
||||
int is_ag_res);
|
||||
|
||||
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time);
|
||||
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time);
|
||||
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan);
|
||||
int lchan_init_lapdm(struct gsm_lchan *lchan);
|
||||
|
||||
void load_timer_start(struct gsm_bts *bts);
|
||||
|
||||
void bts_update_status(enum bts_global_status which, int on);
|
||||
|
||||
#endif /* _BTS_H */
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
|
||||
void *obj);
|
||||
|
||||
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
|
||||
struct tlv_parsed *new_attr, void *obj);
|
||||
struct tlv_parsed *new_attr, int obj_kind, void *obj);
|
||||
|
||||
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
void *obj);
|
||||
@@ -29,11 +29,23 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
|
||||
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp);
|
||||
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan);
|
||||
int bts_model_rsl_chan_mod(struct gsm_lchan *lchan);
|
||||
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
|
||||
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan);
|
||||
|
||||
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
|
||||
int bts_model_trx_close(struct gsm_bts_trx *trx);
|
||||
|
||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, uint8_t *rtp_pl,
|
||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
unsigned int rtp_pl_len);
|
||||
|
||||
int bts_model_vty_init(struct gsm_bts *bts);
|
||||
|
||||
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
|
||||
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx);
|
||||
|
||||
int bts_model_oml_estab(struct gsm_bts *bts);
|
||||
|
||||
int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm);
|
||||
|
||||
#endif
|
||||
|
||||
16
include/osmo-bts/cbch.h
Normal file
16
include/osmo-bts/cbch.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
|
||||
/* incoming SMS broadcast command from RSL */
|
||||
int bts_process_smscb_cmd(struct gsm_bts *bts,
|
||||
struct rsl_ie_cb_cmd_type cmd_type,
|
||||
uint8_t msg_len, const uint8_t *msg);
|
||||
|
||||
/* call-back from bts model specific code when it wants to obtain a CBCH
|
||||
* block for a given gsm_time. outbuf must have 23 bytes of space. */
|
||||
int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time);
|
||||
4
include/osmo-bts/control_if.h
Normal file
4
include/osmo-bts/control_if.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
int bts_ctrl_cmds_install(struct gsm_bts *bts);
|
||||
struct ctrl_handle *bts_controlif_setup(struct gsm_bts *bts);
|
||||
@@ -6,10 +6,21 @@
|
||||
#include <osmocom/gsm/lapdm.h>
|
||||
|
||||
#include <osmo-bts/paging.h>
|
||||
#include <osmo-bts/tx_power.h>
|
||||
|
||||
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT 41
|
||||
#define GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE 999999
|
||||
#define GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT 41
|
||||
#define GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT 91
|
||||
|
||||
struct pcu_sock_state;
|
||||
struct smscb_msg;
|
||||
|
||||
struct gsm_network {
|
||||
struct llist_head bts_list;
|
||||
unsigned int num_bts;
|
||||
uint16_t mcc, mnc;
|
||||
struct pcu_sock_state *pcu_state;
|
||||
};
|
||||
|
||||
/* data structure for BTS related data specific to the BTS role */
|
||||
@@ -36,19 +47,63 @@ struct gsm_bts_role_bts {
|
||||
/* Input parameters from OML */
|
||||
int16_t busy_thresh; /* in dBm */
|
||||
uint16_t averaging_slots;
|
||||
/* Internal data */
|
||||
unsigned int total; /* total nr */
|
||||
unsigned int busy; /* above busy_thresh */
|
||||
unsigned int access; /* access bursts */
|
||||
} rach;
|
||||
} load;
|
||||
uint8_t ny1;
|
||||
uint8_t max_ta;
|
||||
|
||||
/* AGCH queuing */
|
||||
struct llist_head agch_queue;
|
||||
int agch_queue_length;
|
||||
int agch_max_queue_length;
|
||||
|
||||
int agch_queue_thresh_level; /* Cleanup threshold in percent of max len */
|
||||
int agch_queue_low_level; /* Low water mark in percent of max len */
|
||||
int agch_queue_high_level; /* High water mark in percent of max len */
|
||||
|
||||
/* TODO: Use a rate counter group instead */
|
||||
uint64_t agch_queue_dropped_msgs;
|
||||
uint64_t agch_queue_merged_msgs;
|
||||
uint64_t agch_queue_rejected_msgs;
|
||||
uint64_t agch_queue_agch_msgs;
|
||||
uint64_t agch_queue_pch_msgs;
|
||||
|
||||
struct paging_state *paging_state;
|
||||
char *bsc_oml_host;
|
||||
char *rtp_bind_host;
|
||||
unsigned int rtp_jitter_buf_ms;
|
||||
struct {
|
||||
uint8_t ciphers; /* flags A5/1==0x1, A5/2==0x2, A5/3==0x4 */
|
||||
} support;
|
||||
struct {
|
||||
uint8_t tc4_ctr;
|
||||
} si;
|
||||
uint8_t radio_link_timeout;
|
||||
|
||||
/* used by the sysmoBTS to adjust band */
|
||||
uint8_t auto_band;
|
||||
uint8_t unitid_use_eeprom;
|
||||
|
||||
struct {
|
||||
struct llist_head queue; /* list of struct smscb_msg */
|
||||
struct smscb_msg *cur_msg; /* current SMS-CB */
|
||||
} smscb_state;
|
||||
};
|
||||
|
||||
enum lchan_ciph_state {
|
||||
LCHAN_CIPH_NONE,
|
||||
LCHAN_CIPH_RX_REQ,
|
||||
LCHAN_CIPH_RX_CONF,
|
||||
LCHAN_CIPH_TXRX_REQ,
|
||||
LCHAN_CIPH_TXRX_CONF,
|
||||
};
|
||||
|
||||
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
|
||||
|
||||
#include "../../openbsc/openbsc/include/openbsc/gsm_data_shared.h"
|
||||
#include "openbsc/gsm_data_shared.h"
|
||||
|
||||
struct femtol1_hdl;
|
||||
|
||||
@@ -57,4 +112,13 @@ static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx)
|
||||
return trx->role_bts.l1h;
|
||||
}
|
||||
|
||||
|
||||
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
|
||||
|
||||
/* cipher code */
|
||||
#define CIPHER_A5(x) (1 << (x-1))
|
||||
|
||||
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher);
|
||||
|
||||
|
||||
#endif /* _GSM_DATA_H */
|
||||
|
||||
12
include/osmo-bts/handover.h
Normal file
12
include/osmo-bts/handover.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
enum {
|
||||
HANDOVER_NONE = 0,
|
||||
HANDOVER_ENABLED,
|
||||
HANDOVER_WAIT_FRAME,
|
||||
};
|
||||
|
||||
void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay);
|
||||
void handover_frame(struct gsm_lchan *lchan);
|
||||
void handover_reset(struct gsm_lchan *lchan);
|
||||
|
||||
@@ -14,6 +14,8 @@ enum {
|
||||
DL1C,
|
||||
DL1P,
|
||||
DDSP,
|
||||
DPCU,
|
||||
DHO,
|
||||
DABIS,
|
||||
DRTP,
|
||||
DSUM,
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm);
|
||||
|
||||
int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn);
|
||||
int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn);
|
||||
int trx_meas_check_compute(struct gsm_bts_trx *trx, uint32_t fn);
|
||||
|
||||
/* build the 3 byte RSL uplinke measurement IE content */
|
||||
|
||||
20
include/osmo-bts/msg_utils.h
Normal file
20
include/osmo-bts/msg_utils.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Routines to check the structurally integrity of messages
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct msgb;
|
||||
|
||||
/**
|
||||
* Classification of OML message. ETSI for plain GSM 12.21
|
||||
* messages and IPA/Osmo for manufacturer messages.
|
||||
*/
|
||||
enum {
|
||||
OML_MSG_TYPE_ETSI,
|
||||
OML_MSG_TYPE_IPA,
|
||||
OML_MSG_TYPE_OSMO,
|
||||
};
|
||||
|
||||
int msg_verify_ipa_structure(struct msgb *msg);
|
||||
int msg_verify_oml_structure(struct msgb *msg);
|
||||
@@ -1,6 +1,11 @@
|
||||
#ifndef _OML_H
|
||||
#define _OML_H
|
||||
|
||||
struct gsm_bts;
|
||||
struct gsm_abis_mo;
|
||||
struct msgb;
|
||||
|
||||
|
||||
int oml_init(void);
|
||||
int down_oml(struct gsm_bts *bts, struct msgb *msg);
|
||||
|
||||
@@ -9,10 +14,19 @@ int oml_send_msg(struct msgb *msg, int is_mauf);
|
||||
int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type);
|
||||
int oml_mo_opstart_ack(struct gsm_abis_mo *mo);
|
||||
int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
|
||||
int oml_mo_statechg_ack(struct gsm_abis_mo *mo);
|
||||
int oml_mo_statechg_nack(struct gsm_abis_mo *mo, uint8_t nack_cause);
|
||||
|
||||
/* Change the state and send STATE CHG REP */
|
||||
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state);
|
||||
|
||||
/* First initialization of MO, does _not_ generate state changes */
|
||||
void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state);
|
||||
|
||||
/* Update admin state and send ACK/NACK */
|
||||
int oml_mo_rf_lock_chg(struct gsm_abis_mo *mo, uint8_t mute_state[8],
|
||||
int success);
|
||||
|
||||
/* Transmit STATE CHG REP even if there was no state change */
|
||||
int oml_tx_state_changed(struct gsm_abis_mo *mo);
|
||||
|
||||
|
||||
@@ -6,13 +6,28 @@
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
struct paging_state;
|
||||
struct gsm_bts_role_bts;
|
||||
|
||||
/* initialize paging code */
|
||||
struct paging_state *paging_init(void *ctx, unsigned int num_paging_max,
|
||||
struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
|
||||
unsigned int num_paging_max,
|
||||
unsigned int paging_lifetime);
|
||||
|
||||
/* (re) configure paging code */
|
||||
void paging_config(struct paging_state *ps,
|
||||
unsigned int num_paging_max,
|
||||
unsigned int paging_lifetime);
|
||||
|
||||
void paging_reset(struct paging_state *ps);
|
||||
|
||||
/* The max number of paging entries */
|
||||
unsigned int paging_get_queue_max(struct paging_state *ps);
|
||||
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max);
|
||||
|
||||
/* The lifetime of a paging entry */
|
||||
unsigned int paging_get_lifetime(struct paging_state *ps);
|
||||
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime);
|
||||
|
||||
/* update with new SYSTEM INFORMATION parameters */
|
||||
int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr *chan_desc);
|
||||
|
||||
@@ -20,7 +35,24 @@ int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr
|
||||
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||
const uint8_t *identity_lv, uint8_t chan_needed);
|
||||
|
||||
/* Add an IMM.ASS message to the paging queue */
|
||||
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
|
||||
uint8_t len);
|
||||
|
||||
/* generate paging message for given gsm time */
|
||||
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt);
|
||||
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt,
|
||||
int *is_empty);
|
||||
|
||||
|
||||
/* inspection methods below */
|
||||
int paging_group_queue_empty(struct paging_state *ps, uint8_t group);
|
||||
int paging_queue_length(struct paging_state *ps);
|
||||
int paging_buffer_space(struct paging_state *ps);
|
||||
|
||||
extern uint8_t etws_segment_data[5][17];
|
||||
extern uint8_t etws_segment_len[5];
|
||||
extern uint8_t etws_nr_seg;
|
||||
extern uint8_t etws_data[60];
|
||||
extern size_t etws_len;
|
||||
|
||||
#endif
|
||||
|
||||
18
include/osmo-bts/pcu_if.h
Normal file
18
include/osmo-bts/pcu_if.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PCU_IF_H
|
||||
#define _PCU_IF_H
|
||||
|
||||
int pcu_tx_info_ind(void);
|
||||
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr);
|
||||
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
|
||||
int8_t rssi);
|
||||
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn);
|
||||
int pcu_tx_time_ind(uint32_t fn);
|
||||
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed);
|
||||
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len);
|
||||
|
||||
int pcu_sock_init(void);
|
||||
void pcu_sock_exit(void);
|
||||
|
||||
#endif /* _PCU_IF_H */
|
||||
153
include/osmo-bts/pcuif_proto.h
Normal file
153
include/osmo-bts/pcuif_proto.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#ifndef _PCUIF_PROTO_H
|
||||
#define _PCUIF_PROTO_H
|
||||
|
||||
#define PCU_IF_VERSION 0x05
|
||||
|
||||
/* msg_type */
|
||||
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
|
||||
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
|
||||
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
|
||||
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
|
||||
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
|
||||
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
|
||||
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
|
||||
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
|
||||
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
|
||||
|
||||
/* sapi */
|
||||
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
|
||||
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
|
||||
#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
|
||||
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
|
||||
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
|
||||
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
|
||||
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
|
||||
|
||||
/* flags */
|
||||
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
|
||||
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
|
||||
#define PCU_IF_FLAG_CS1 (1 << 16)
|
||||
#define PCU_IF_FLAG_CS2 (1 << 17)
|
||||
#define PCU_IF_FLAG_CS3 (1 << 18)
|
||||
#define PCU_IF_FLAG_CS4 (1 << 19)
|
||||
#define PCU_IF_FLAG_MCS1 (1 << 20)
|
||||
#define PCU_IF_FLAG_MCS2 (1 << 21)
|
||||
#define PCU_IF_FLAG_MCS3 (1 << 22)
|
||||
#define PCU_IF_FLAG_MCS4 (1 << 23)
|
||||
#define PCU_IF_FLAG_MCS5 (1 << 24)
|
||||
#define PCU_IF_FLAG_MCS6 (1 << 25)
|
||||
#define PCU_IF_FLAG_MCS7 (1 << 26)
|
||||
#define PCU_IF_FLAG_MCS8 (1 << 27)
|
||||
#define PCU_IF_FLAG_MCS9 (1 << 28)
|
||||
|
||||
struct gsm_pcu_if_data {
|
||||
uint8_t sapi;
|
||||
uint8_t len;
|
||||
uint8_t data[162];
|
||||
uint32_t fn;
|
||||
uint16_t arfcn;
|
||||
uint8_t trx_nr;
|
||||
uint8_t ts_nr;
|
||||
uint8_t block_nr;
|
||||
int8_t rssi;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_rts_req {
|
||||
uint8_t sapi;
|
||||
uint8_t spare[3];
|
||||
uint32_t fn;
|
||||
uint16_t arfcn;
|
||||
uint8_t trx_nr;
|
||||
uint8_t ts_nr;
|
||||
uint8_t block_nr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_rach_ind {
|
||||
uint8_t sapi;
|
||||
uint8_t ra;
|
||||
int16_t qta;
|
||||
uint32_t fn;
|
||||
uint16_t arfcn;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_info_trx {
|
||||
uint16_t arfcn;
|
||||
uint8_t pdch_mask; /* PDCH channels per TS */
|
||||
uint8_t spare;
|
||||
uint8_t tsc[8]; /* TSC per channel */
|
||||
uint32_t hlayer1;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_info_ind {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
|
||||
uint8_t bsic;
|
||||
/* RAI */
|
||||
uint16_t mcc, mnc, lac, rac;
|
||||
/* NSE */
|
||||
uint16_t nsei;
|
||||
uint8_t nse_timer[7];
|
||||
uint8_t cell_timer[11];
|
||||
/* cell */
|
||||
uint16_t cell_id;
|
||||
uint16_t repeat_time;
|
||||
uint8_t repeat_count;
|
||||
uint16_t bvci;
|
||||
uint8_t t3142;
|
||||
uint8_t t3169;
|
||||
uint8_t t3191;
|
||||
uint8_t t3193_10ms;
|
||||
uint8_t t3195;
|
||||
uint8_t n3101;
|
||||
uint8_t n3103;
|
||||
uint8_t n3105;
|
||||
uint8_t cv_countdown;
|
||||
uint16_t dl_tbf_ext;
|
||||
uint16_t ul_tbf_ext;
|
||||
uint8_t initial_cs;
|
||||
uint8_t initial_mcs;
|
||||
/* NSVC */
|
||||
uint16_t nsvci[2];
|
||||
uint16_t local_port[2];
|
||||
uint16_t remote_port[2];
|
||||
uint32_t remote_ip[2];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_act_req {
|
||||
uint8_t activate;
|
||||
uint8_t trx_nr;
|
||||
uint8_t ts_nr;
|
||||
uint8_t spare;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_time_ind {
|
||||
uint32_t fn;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_pag_req {
|
||||
uint8_t sapi;
|
||||
uint8_t chan_needed;
|
||||
uint8_t identity_lv[9];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if {
|
||||
/* context based information */
|
||||
uint8_t msg_type; /* message type */
|
||||
uint8_t bts_nr; /* bts number */
|
||||
uint8_t spare[2];
|
||||
|
||||
union {
|
||||
struct gsm_pcu_if_data data_req;
|
||||
struct gsm_pcu_if_data data_cnf;
|
||||
struct gsm_pcu_if_data data_ind;
|
||||
struct gsm_pcu_if_rts_req rts_req;
|
||||
struct gsm_pcu_if_rach_ind rach_ind;
|
||||
struct gsm_pcu_if_info_ind info_ind;
|
||||
struct gsm_pcu_if_act_req act_req;
|
||||
struct gsm_pcu_if_time_ind time_ind;
|
||||
struct gsm_pcu_if_pag_req pag_req;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif /* _PCUIF_PROTO_H */
|
||||
@@ -1,6 +1,16 @@
|
||||
#ifndef _RSL_H
|
||||
#define _RSL_H
|
||||
|
||||
/**
|
||||
* What kind of release/activation is done? A silent one for
|
||||
* the PDCH or one triggered through RSL?
|
||||
*/
|
||||
enum {
|
||||
LCHAN_REL_ACT_RSL,
|
||||
LCHAN_REL_ACT_PCU,
|
||||
LCHAN_REL_ACT_OML,
|
||||
};
|
||||
|
||||
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg);
|
||||
int rsl_tx_rf_res(struct gsm_bts_trx *trx);
|
||||
int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
|
||||
@@ -8,12 +18,18 @@ int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
|
||||
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
|
||||
|
||||
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime);
|
||||
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause);
|
||||
int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
|
||||
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
|
||||
int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay);
|
||||
|
||||
/* call-back for LAPDm code, called when it wants to send msgs UP */
|
||||
int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx);
|
||||
|
||||
int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause);
|
||||
int rsl_tx_ccch_load_ind_pch(struct gsm_bts *bts, uint16_t paging_avail);
|
||||
int rsl_tx_ccch_load_ind_rach(struct gsm_bts *bts, uint16_t total,
|
||||
uint16_t busy, uint16_t access);
|
||||
|
||||
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr);
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ enum sig_subsys {
|
||||
|
||||
enum signals_global {
|
||||
S_NEW_SYSINFO,
|
||||
S_NEW_OP_STATE,
|
||||
S_NEW_NSE_ATTR,
|
||||
S_NEW_CELL_ATTR,
|
||||
S_NEW_NSVC_ATTR,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
76
include/osmo-bts/tx_power.h
Normal file
76
include/osmo-bts/tx_power.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
/* our unit is 'milli dB" or "milli dBm", i.e. 1/1000 of a dB(m) */
|
||||
#define to_mdB(x) (x * 1000)
|
||||
|
||||
/* PA calibration table */
|
||||
struct pa_calibration {
|
||||
int gain_mdB[1024]; /* gain provided at given ARFCN */
|
||||
/* FIXME: thermal calibration */
|
||||
};
|
||||
|
||||
/* representation of a RF power amplifier */
|
||||
struct power_amp {
|
||||
/* nominal gain of the PA */
|
||||
int nominal_gain_mdB;
|
||||
/* table with calibrated actual gain for each ARFCN */
|
||||
struct pa_calibration calib;
|
||||
};
|
||||
|
||||
/* Transmit power related parameters of a transceiver */
|
||||
struct trx_power_params {
|
||||
/* specified maximum output of TRX at full power, has to be
|
||||
* initialized by BTS model at startup*/
|
||||
int trx_p_max_out_mdBm;
|
||||
|
||||
/* intended current total system output power */
|
||||
int p_total_tgt_mdBm;
|
||||
|
||||
/* actual current total system output power, filled in by tx_power code */
|
||||
int p_total_cur_mdBm;
|
||||
|
||||
/* current temporary attenuation due to thermal management,
|
||||
* set by thermal management code via control interface */
|
||||
int thermal_attenuation_mdB;
|
||||
|
||||
/* external gain (+) or attenuation (-) added by the user, configured
|
||||
* by the user via VTY */
|
||||
int user_gain_mdB;
|
||||
|
||||
/* calibration table of internal PA */
|
||||
struct power_amp pa;
|
||||
|
||||
/* calibration table of user PA */
|
||||
struct power_amp user_pa;
|
||||
|
||||
/* power ramping related data */
|
||||
struct {
|
||||
/* maximum initial Pout including all PAs */
|
||||
int max_initial_pout_mdBm;
|
||||
/* temporary attenuation due to power ramping */
|
||||
int attenuation_mdB;
|
||||
unsigned int step_size_mdB;
|
||||
unsigned int step_interval_sec;
|
||||
struct osmo_timer_list step_timer;
|
||||
} ramp;
|
||||
};
|
||||
|
||||
int get_p_max_out_mdBm(struct gsm_bts_trx *trx);
|
||||
|
||||
int get_p_nominal_mdBm(struct gsm_bts_trx *trx);
|
||||
|
||||
int get_p_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
|
||||
int get_p_target_mdBm_lchan(struct gsm_lchan *lchan);
|
||||
|
||||
int get_p_trxout_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
|
||||
int get_p_trxout_target_mdBm_lchan(struct gsm_lchan *lchan);
|
||||
|
||||
int get_p_trxout_actual_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie);
|
||||
int get_p_trxout_actual_mdBm_lchan(struct gsm_lchan *lchan);
|
||||
|
||||
int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass);
|
||||
|
||||
void power_trx_change_compl(struct gsm_bts_trx *trx, int p_trxout_cur_mdBm);
|
||||
@@ -7,8 +7,6 @@
|
||||
enum bts_vty_node {
|
||||
BTS_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
TRX_NODE,
|
||||
TS_NODE,
|
||||
LCHAN_NODE,
|
||||
};
|
||||
|
||||
extern struct cmd_element ournode_exit_cmd;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = common #osmo-bts-bb
|
||||
SUBDIRS = common
|
||||
|
||||
if ENABLE_SYSMOBTS
|
||||
SUBDIRS += osmo-bts-sysmo
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
|
||||
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
|
||||
|
||||
noinst_LIBRARIES = libbts.a
|
||||
libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
|
||||
rsl.c vty.c paging.c measurement.c
|
||||
rsl.c vty.c paging.c measurement.c amr.c lchan.c \
|
||||
load_indication.c pcu_sock.c handover.c msg_utils.c \
|
||||
load_indication.c pcu_sock.c handover.c msg_utils.c \
|
||||
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
|
||||
etws_p1.c cbch.c
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
/* Minimalistic Abis/IP interface routines, soon to be replaced by
|
||||
* libosmo-abis (Pablo) */
|
||||
/* Abis/IP interface routines utilizing libosmo-abis (Pablo) */
|
||||
|
||||
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -21,6 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "btsconfig.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -34,496 +35,205 @@
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/core/macaddr.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
|
||||
extern char *software_version;
|
||||
extern uint8_t abis_mac[6];
|
||||
|
||||
/*
|
||||
* support
|
||||
*/
|
||||
|
||||
#define ABIS_ALLOC_SIZE 900
|
||||
|
||||
/* send message to BSC */
|
||||
int abis_tx(struct ipabis_link *link, struct msgb *msg)
|
||||
{
|
||||
if (link->state != LINK_STATE_CONNECT) {
|
||||
LOGP(DABIS, LOGL_NOTICE, "Link down, dropping message.\n");
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msgb_enqueue(&link->tx_queue, msg);
|
||||
link->bfd.when |= BSC_FD_WRITE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static struct gsm_bts *g_bts;
|
||||
|
||||
int abis_oml_sendmsg(struct msgb *msg)
|
||||
{
|
||||
struct gsm_bts *bts = msg->trx->bts;
|
||||
|
||||
abis_push_ipa(msg, 0xff);
|
||||
|
||||
if (!bts->oml_link) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return abis_tx((struct ipabis_link *) bts->oml_link, msg);
|
||||
/* osmo-bts uses msg->trx internally, but libosmo-abis uses
|
||||
* the signalling link at msg->dst */
|
||||
msg->dst = bts->oml_link;
|
||||
return abis_sendmsg(msg);
|
||||
}
|
||||
|
||||
int abis_rsl_sendmsg(struct msgb *msg)
|
||||
int abis_bts_rsl_sendmsg(struct msgb *msg)
|
||||
{
|
||||
struct gsm_bts_trx *trx = msg->trx;
|
||||
|
||||
abis_push_ipa(msg, 0);
|
||||
|
||||
return abis_tx((struct ipabis_link *) trx->rsl_link, msg);
|
||||
/* osmo-bts uses msg->trx internally, but libosmo-abis uses
|
||||
* the signalling link at msg->dst */
|
||||
msg->dst = msg->trx->rsl_link;
|
||||
return abis_sendmsg(msg);
|
||||
}
|
||||
|
||||
struct msgb *abis_msgb_alloc(int headroom)
|
||||
static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
|
||||
enum e1inp_sign_type type)
|
||||
{
|
||||
struct msgb *nmsg;
|
||||
struct e1inp_sign_link *sign_link = NULL;
|
||||
|
||||
headroom += sizeof(struct ipaccess_head);
|
||||
|
||||
nmsg = msgb_alloc_headroom(ABIS_ALLOC_SIZE + headroom,
|
||||
headroom, "Abis/IP");
|
||||
if (!nmsg)
|
||||
return NULL;
|
||||
return nmsg;
|
||||
}
|
||||
|
||||
void abis_push_ipa(struct msgb *msg, uint8_t proto)
|
||||
{
|
||||
struct ipaccess_head *nhh;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
nhh = (struct ipaccess_head *) msgb_push(msg, sizeof(*nhh));
|
||||
nhh->proto = proto;
|
||||
nhh->len = htons(msgb_l2len(msg));
|
||||
}
|
||||
|
||||
/*
|
||||
* IPA related messages
|
||||
*/
|
||||
|
||||
/* send ping/pong */
|
||||
static int abis_tx_ipa_pingpong(struct ipabis_link *link, uint8_t pingpong)
|
||||
{
|
||||
struct msgb *nmsg;
|
||||
|
||||
nmsg = abis_msgb_alloc(0);
|
||||
if (!nmsg)
|
||||
return -ENOMEM;
|
||||
*msgb_put(nmsg, 1) = pingpong;
|
||||
abis_push_ipa(nmsg, IPAC_PROTO_IPACCESS);
|
||||
|
||||
return abis_tx(link, nmsg);
|
||||
}
|
||||
|
||||
/* send ACK and ID RESP */
|
||||
static int abis_rx_ipa_id_get(struct ipabis_link *link, uint8_t *data, int len)
|
||||
{
|
||||
struct gsm_bts *bts = link->bts;
|
||||
struct msgb *nmsg, *nmsg2;
|
||||
char str[64];
|
||||
uint8_t *tag;
|
||||
|
||||
if (!link->bts)
|
||||
bts = link->trx->bts;
|
||||
|
||||
LOGP(DABIS, LOGL_INFO, "Reply to ID_GET\n");
|
||||
|
||||
nmsg = abis_msgb_alloc(0);
|
||||
if (!nmsg)
|
||||
return -ENOMEM;
|
||||
*msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP;
|
||||
while (len) {
|
||||
if (len < 2) {
|
||||
LOGP(DABIS, LOGL_NOTICE,
|
||||
"Short read of ipaccess tag\n");
|
||||
msgb_free(nmsg);
|
||||
return -EIO;
|
||||
}
|
||||
switch (data[1]) {
|
||||
case IPAC_IDTAG_UNIT:
|
||||
sprintf(str, "%u/%u/%u",
|
||||
bts->ip_access.site_id,
|
||||
bts->ip_access.bts_id,
|
||||
(link->trx) ? link->trx->nr : 0);
|
||||
break;
|
||||
case IPAC_IDTAG_MACADDR:
|
||||
sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
abis_mac[0], abis_mac[1], abis_mac[2],
|
||||
abis_mac[3], abis_mac[4], abis_mac[5]);
|
||||
break;
|
||||
case IPAC_IDTAG_LOCATION1:
|
||||
strcpy(str, "osmoBTS");
|
||||
break;
|
||||
case IPAC_IDTAG_LOCATION2:
|
||||
strcpy(str, "osmoBTS");
|
||||
break;
|
||||
case IPAC_IDTAG_EQUIPVERS:
|
||||
case IPAC_IDTAG_SWVERSION:
|
||||
strcpy(str, software_version);
|
||||
break;
|
||||
case IPAC_IDTAG_UNITNAME:
|
||||
sprintf(str, "osmoBTS-%02x-%02x-%02x-%02x-%02x-%02x",
|
||||
abis_mac[0], abis_mac[1], abis_mac[2],
|
||||
abis_mac[3], abis_mac[4], abis_mac[5]);
|
||||
break;
|
||||
case IPAC_IDTAG_SERNR:
|
||||
strcpy(str, "");
|
||||
break;
|
||||
default:
|
||||
LOGP(DABIS, LOGL_NOTICE,
|
||||
"Unknown ipaccess tag 0x%02x\n", *data);
|
||||
msgb_free(nmsg);
|
||||
return -EINVAL;
|
||||
}
|
||||
LOGP(DABIS, LOGL_INFO, " tag %d: %s\n", data[1], str);
|
||||
tag = msgb_put(nmsg, 3 + strlen(str) + 1);
|
||||
tag[0] = 0x00;
|
||||
tag[1] = 1 + strlen(str) + 1;
|
||||
tag[2] = data[1];
|
||||
memcpy(tag + 3, str, strlen(str) + 1);
|
||||
data += 2;
|
||||
len -= 2;
|
||||
}
|
||||
abis_push_ipa(nmsg, IPAC_PROTO_IPACCESS);
|
||||
|
||||
nmsg2 = abis_msgb_alloc(0);
|
||||
if (!nmsg2) {
|
||||
msgb_free(nmsg);
|
||||
return -ENOMEM;
|
||||
}
|
||||
*msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK;
|
||||
abis_push_ipa(nmsg2, IPAC_PROTO_IPACCESS);
|
||||
|
||||
link->id_resp = 1;
|
||||
|
||||
abis_tx(link, nmsg2);
|
||||
return abis_tx(link, nmsg);
|
||||
}
|
||||
|
||||
static int abis_rx_ipaccess(struct ipabis_link *link, struct msgb *msg)
|
||||
{
|
||||
uint8_t *data = msgb_l2(msg);
|
||||
int len = msgb_l2len(msg);
|
||||
int ret = 0;
|
||||
|
||||
if (len < 1) {
|
||||
LOGP(DABIS, LOGL_NOTICE, "Short read of ipaccess message\n");
|
||||
msgb_free(msg);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
switch (*data) {
|
||||
case IPAC_MSGT_PONG:
|
||||
#if 0
|
||||
#warning HACK
|
||||
rsl_tx_chan_rqd(link->bts->trx[0]);
|
||||
#endif
|
||||
LOGP(DABIS, LOGL_DEBUG, "PONG\n");
|
||||
link->pong = 1;
|
||||
switch (type) {
|
||||
case E1INP_SIGN_OML:
|
||||
LOGP(DABIS, LOGL_INFO, "OML Signalling link up\n");
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML-1], line);
|
||||
sign_link = g_bts->oml_link =
|
||||
e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML-1],
|
||||
E1INP_SIGN_OML, NULL, 255, 0);
|
||||
sign_link->trx = g_bts->c0;
|
||||
bts_link_estab(g_bts);
|
||||
break;
|
||||
case IPAC_MSGT_PING:
|
||||
LOGP(DABIS, LOGL_DEBUG, "reply to ping request\n");
|
||||
ret = abis_tx_ipa_pingpong(link, IPAC_MSGT_PONG);
|
||||
break;
|
||||
case IPAC_MSGT_ID_GET:
|
||||
ret = abis_rx_ipa_id_get(link, data + 1, len - 1);
|
||||
break;
|
||||
case IPAC_MSGT_ID_ACK:
|
||||
LOGP(DABIS, LOGL_DEBUG, "ID ACK\n");
|
||||
if (link->id_resp && link->bts)
|
||||
ret = bts_link_estab(link->bts);
|
||||
if (link->id_resp && link->trx)
|
||||
ret = trx_link_estab(link->trx);
|
||||
link->id_resp = 0;
|
||||
|
||||
case E1INP_SIGN_RSL:
|
||||
LOGP(DABIS, LOGL_INFO, "RSL Signalling link up\n");
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL-1], line);
|
||||
sign_link = g_bts->c0->rsl_link =
|
||||
e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL-1],
|
||||
E1INP_SIGN_RSL, NULL, 0, 0);
|
||||
/* FIXME: This assumes there is only one TRX! */
|
||||
sign_link->trx = g_bts->c0;
|
||||
trx_link_estab(sign_link->trx);
|
||||
break;
|
||||
default:
|
||||
LOGP(DABIS, LOGL_NOTICE,
|
||||
"Unknown ipaccess message type 0x%02x\n", *data);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return ret;
|
||||
return sign_link;
|
||||
}
|
||||
|
||||
/*
|
||||
* A-Bis over IP implementation
|
||||
*/
|
||||
|
||||
/* receive message from BSC */
|
||||
static int abis_rx(struct ipabis_link *link, struct msgb *msg)
|
||||
static void sign_link_down(struct e1inp_line *line)
|
||||
{
|
||||
struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
|
||||
int ret = 0;
|
||||
LOGP(DABIS, LOGL_ERROR, "Signalling link down\n");
|
||||
|
||||
switch (hh->proto) {
|
||||
case IPAC_PROTO_RSL:
|
||||
if (!link->trx) {
|
||||
LOGP(DABIS, LOGL_NOTICE,
|
||||
"Received RSL message not on RSL link\n");
|
||||
msgb_free(msg);
|
||||
ret = EIO;
|
||||
break;
|
||||
}
|
||||
msg->trx = link->trx;
|
||||
ret = down_rsl(link->trx, msg);
|
||||
if (g_bts->c0->rsl_link) {
|
||||
e1inp_sign_link_destroy(g_bts->c0->rsl_link);
|
||||
g_bts->c0->rsl_link = NULL;
|
||||
trx_link_estab(g_bts->c0);
|
||||
}
|
||||
|
||||
if (g_bts->oml_link)
|
||||
e1inp_sign_link_destroy(g_bts->oml_link);
|
||||
g_bts->oml_link = NULL;
|
||||
|
||||
bts_shutdown(g_bts, "Abis close");
|
||||
}
|
||||
|
||||
|
||||
/* callback for incoming mesages from A-bis/IP */
|
||||
static int sign_link_cb(struct msgb *msg)
|
||||
{
|
||||
struct e1inp_sign_link *link = msg->dst;
|
||||
|
||||
/* osmo-bts code assumes msg->trx is set, but libosmo-abis works
|
||||
* with the sign_link stored in msg->dst, so we have to convert
|
||||
* here */
|
||||
msg->trx = link->trx;
|
||||
|
||||
switch (link->type) {
|
||||
case E1INP_SIGN_OML:
|
||||
down_oml(link->trx->bts, msg);
|
||||
break;
|
||||
case IPAC_PROTO_IPACCESS:
|
||||
ret = abis_rx_ipaccess(link, msg);
|
||||
break;
|
||||
case IPAC_PROTO_SCCP:
|
||||
LOGP(DABIS, LOGL_INFO, "Received SCCP message\n");
|
||||
msgb_free(msg);
|
||||
break;
|
||||
case IPAC_PROTO_OML:
|
||||
if (!link->bts) {
|
||||
LOGP(DABIS, LOGL_NOTICE,
|
||||
"Received OML message not on OML link\n");
|
||||
msgb_free(msg);
|
||||
ret = EIO;
|
||||
break;
|
||||
}
|
||||
msg->trx = link->bts->c0;
|
||||
ret = down_oml(link->bts, msg);
|
||||
case E1INP_SIGN_RSL:
|
||||
down_rsl(link->trx, msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DABIS, LOGL_NOTICE, "Unknown Protocol %d\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
ret = EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void abis_timeout(void *arg)
|
||||
{
|
||||
struct ipabis_link *link = arg;
|
||||
int ret;
|
||||
|
||||
switch (link->state) {
|
||||
case LINK_STATE_RETRYING:
|
||||
ret = abis_open(link, link->ip);
|
||||
if (ret <= 0)
|
||||
osmo_timer_schedule(&link->timer, OML_RETRY_TIMER, 0);
|
||||
break;
|
||||
case LINK_STATE_CONNECT:
|
||||
if (link->ping && !link->pong) {
|
||||
LOGP(DABIS, LOGL_NOTICE,
|
||||
"No reply to PING. Link is lost\n");
|
||||
abis_close(link);
|
||||
ret = abis_open(link, link->ip);
|
||||
if (ret <= 0) {
|
||||
osmo_timer_schedule(&link->timer,
|
||||
OML_RETRY_TIMER, 0);
|
||||
link->state = LINK_STATE_RETRYING;
|
||||
}
|
||||
break;
|
||||
}
|
||||
link->ping = 1;
|
||||
link->pong = 0;
|
||||
LOGP(DABIS, LOGL_DEBUG, "PING\n");
|
||||
abis_tx_ipa_pingpong(link, IPAC_MSGT_PING);
|
||||
osmo_timer_schedule(&link->timer, OML_PING_TIMER, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int abis_sock_cb(struct osmo_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct ipabis_link *link = bfd->data;
|
||||
struct ipaccess_head *hh;
|
||||
struct msgb *msg;
|
||||
int ret = 0;
|
||||
|
||||
if ((what & BSC_FD_WRITE) && link->state == LINK_STATE_CONNECTING) {
|
||||
if (link->bts) {
|
||||
if (osmo_timer_pending(&link->timer))
|
||||
osmo_timer_del(&link->timer);
|
||||
// osmo_timer_schedule(&link->timer, OML_PING_TIMER, 0);
|
||||
#warning HACK
|
||||
osmo_timer_schedule(&link->timer, 3, 0);
|
||||
link->ping = link->pong = 0;
|
||||
}
|
||||
LOGP(DABIS, LOGL_INFO, "Abis socket now connected.\n");
|
||||
link->state = LINK_STATE_CONNECT;
|
||||
}
|
||||
//printf("what %d\n", what);
|
||||
|
||||
if ((what & BSC_FD_READ)) {
|
||||
if (!link->rx_msg) {
|
||||
link->rx_msg = msgb_alloc(ABIS_ALLOC_SIZE, "Abis/IP");
|
||||
if (!link->rx_msg)
|
||||
return -ENOMEM;
|
||||
}
|
||||
msg = link->rx_msg;
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
if (msg->len < sizeof(*hh)) {
|
||||
ret = recv(link->bfd.fd, msg->data, sizeof(*hh), 0);
|
||||
if (ret <= 0) {
|
||||
goto close;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
if (msg->len < sizeof(*hh))
|
||||
return 0;
|
||||
msg->l2h = msg->data + sizeof(*hh);
|
||||
if (ntohs(hh->len) > msgb_tailroom(msg)) {
|
||||
LOGP(DABIS, LOGL_DEBUG, "Received packet from "
|
||||
"Abis socket too large.\n");
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
ret = recv(link->bfd.fd, msg->tail,
|
||||
ntohs(hh->len) + sizeof(*hh) - msg->len, 0);
|
||||
if (ret == 0)
|
||||
goto close;
|
||||
if (ret < 0 && errno != EAGAIN)
|
||||
goto close;
|
||||
msgb_put(msg, ret);
|
||||
if (ntohs(hh->len) + sizeof(*hh) > msg->len)
|
||||
return 0;
|
||||
link->rx_msg = NULL;
|
||||
LOGP(DABIS, LOGL_DEBUG, "Received messages from Abis socket.\n");
|
||||
ret = abis_rx(link, msg);
|
||||
}
|
||||
if ((what & BSC_FD_WRITE)) {
|
||||
msg = msgb_dequeue(&link->tx_queue);
|
||||
if (msg) {
|
||||
LOGP(DABIS, LOGL_DEBUG, "Sending messages to Abis socket.\n");
|
||||
ret = send(link->bfd.fd, msg->data, msg->len, 0);
|
||||
msgb_free(msg);
|
||||
if (ret < 0)
|
||||
goto close;
|
||||
} else
|
||||
link->bfd.when &= ~BSC_FD_WRITE;
|
||||
}
|
||||
if ((what & BSC_FD_EXCEPT)) {
|
||||
LOGP(DABIS, LOGL_NOTICE, "Abis socket received exception\n");
|
||||
goto close;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
close:
|
||||
abis_close(link);
|
||||
|
||||
/* RSL link will just close and BSC is notified */
|
||||
if (link->trx) {
|
||||
LOGP(DABIS, LOGL_NOTICE, "Connection to BSC failed\n");
|
||||
return trx_link_estab(link->trx);
|
||||
}
|
||||
|
||||
LOGP(DABIS, LOGL_NOTICE, "Connection to BSC failed, retrying in %d "
|
||||
"seconds.\n", OML_RETRY_TIMER);
|
||||
osmo_timer_schedule(&link->timer, OML_RETRY_TIMER, 0);
|
||||
link->state = LINK_STATE_RETRYING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int abis_open(struct ipabis_link *link, uint32_t ip)
|
||||
uint32_t get_signlink_remote_ip(struct e1inp_sign_link *link)
|
||||
{
|
||||
unsigned int on = 1;
|
||||
struct sockaddr_in addr;
|
||||
int sock;
|
||||
int ret;
|
||||
int fd = link->ts->driver.ipaccess.fd.fd;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t slen = sizeof(sin);
|
||||
int rc;
|
||||
|
||||
rc = getpeername(fd, (struct sockaddr *)&sin, &slen);
|
||||
if (rc < 0) {
|
||||
LOGP(DOML, LOGL_ERROR, "Cannot determine remote IP Addr: %s\n",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we assume that the soket is AF_INET. As Abis/IP contains
|
||||
* lots of hard-coded IPv4 addresses, this safe */
|
||||
OSMO_ASSERT(sin.sin_family == AF_INET);
|
||||
|
||||
return ntohl(sin.sin_addr.s_addr);
|
||||
}
|
||||
|
||||
|
||||
static int inp_s_cbfn(unsigned int subsys, unsigned int signal,
|
||||
void *hdlr_data, void *signal_data)
|
||||
{
|
||||
if (subsys != SS_L_INPUT)
|
||||
return 0;
|
||||
|
||||
DEBUGP(DABIS, "Input Signal %u received\n", signal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct ipaccess_unit bts_dev_info = {
|
||||
.unit_name = "sysmoBTS",
|
||||
.equipvers = "", /* FIXME: read this from hw */
|
||||
.swversion = PACKAGE_VERSION,
|
||||
.location1 = "",
|
||||
.location2 = "",
|
||||
.serno = "",
|
||||
};
|
||||
|
||||
static struct e1inp_line_ops line_ops = {
|
||||
.cfg = {
|
||||
.ipa = {
|
||||
.role = E1INP_LINE_R_BTS,
|
||||
.dev = &bts_dev_info,
|
||||
},
|
||||
},
|
||||
.sign_link_up = sign_link_up,
|
||||
.sign_link_down = sign_link_down,
|
||||
.sign_link = sign_link_cb,
|
||||
};
|
||||
|
||||
void abis_init(struct gsm_bts *bts)
|
||||
{
|
||||
g_bts = bts;
|
||||
|
||||
oml_init();
|
||||
e1inp_vty_init();
|
||||
libosmo_abis_init(NULL);
|
||||
|
||||
if (link->bfd.fd > 0)
|
||||
return -EBUSY;
|
||||
|
||||
INIT_LLIST_HEAD(&link->tx_queue);
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
return sock;
|
||||
|
||||
ret = ioctl(sock, FIONBIO, (unsigned char *)&on);
|
||||
if (ret < 0) {
|
||||
close(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
if (link->bts)
|
||||
addr.sin_port = htons(IPA_TCP_PORT_OML);
|
||||
else
|
||||
addr.sin_port = htons(IPA_TCP_PORT_RSL);
|
||||
addr.sin_addr.s_addr = htonl(ip);
|
||||
|
||||
ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (ret < 0 && errno != EINPROGRESS) {
|
||||
close(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
link->bfd.data = link;
|
||||
link->bfd.when = BSC_FD_READ | BSC_FD_WRITE | BSC_FD_EXCEPT;
|
||||
link->bfd.cb = abis_sock_cb;
|
||||
link->bfd.fd = sock;
|
||||
link->state = LINK_STATE_CONNECTING;
|
||||
link->ip = ip;
|
||||
link->timer.cb = abis_timeout;
|
||||
link->timer.data = link;
|
||||
|
||||
osmo_fd_register(&link->bfd);
|
||||
|
||||
LOGP(DABIS, LOGL_INFO, "Abis socket trying to reach BSC.\n");
|
||||
|
||||
return sock;
|
||||
osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts);
|
||||
}
|
||||
|
||||
void abis_close(struct ipabis_link *link)
|
||||
struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
|
||||
char *model_name)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct e1inp_line *line;
|
||||
|
||||
if (link->bfd.fd <= 0)
|
||||
return;
|
||||
|
||||
LOGP(DABIS, LOGL_NOTICE, "Abis socket closed.\n");
|
||||
/* patch in various data from VTY and othe sources */
|
||||
line_ops.cfg.ipa.addr = dst_host;
|
||||
osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
|
||||
bts_dev_info.site_id = bts->ip_access.site_id;
|
||||
bts_dev_info.bts_id = bts->ip_access.bts_id;
|
||||
bts_dev_info.unit_name = model_name;
|
||||
if (bts->description)
|
||||
bts_dev_info.unit_name = bts->description;
|
||||
bts_dev_info.location2 = model_name;
|
||||
|
||||
if (link->rx_msg) {
|
||||
msgb_free(link->rx_msg);
|
||||
link->rx_msg = NULL;
|
||||
}
|
||||
line = e1inp_line_find(0);
|
||||
if (!line)
|
||||
line = e1inp_line_create(0, "ipa");
|
||||
if (!line)
|
||||
return NULL;
|
||||
e1inp_line_bind_ops(line, &line_ops);
|
||||
|
||||
while ((msg = msgb_dequeue(&link->tx_queue)))
|
||||
msgb_free(msg);
|
||||
/* This will open the OML connection now */
|
||||
if (e1inp_line_update(line) < 0)
|
||||
return NULL;
|
||||
|
||||
osmo_fd_unregister(&link->bfd);
|
||||
|
||||
close(link->bfd.fd);
|
||||
link->bfd.fd = -1; /* -1 or 0 indicates: 'close' */
|
||||
link->state = LINK_STATE_IDLE;
|
||||
|
||||
if (osmo_timer_pending(&link->timer))
|
||||
osmo_timer_del(&link->timer);
|
||||
|
||||
/* for now, we simply terminate the program and re-spawn */
|
||||
if (link->bts)
|
||||
bts_shutdown(link->bts, "Abis close / OML");
|
||||
else if (link->trx)
|
||||
bts_shutdown(link->trx->bts, "Abis close / RSL");
|
||||
else {
|
||||
LOGP(DABIS, LOGL_FATAL, "Unable to connect to BSC\n");
|
||||
exit(43);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
117
src/common/amr.c
Normal file
117
src/common/amr.c
Normal file
@@ -0,0 +1,117 @@
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/amr.h>
|
||||
|
||||
void amr_log_mr_conf(int ss, int logl, const char *pfx,
|
||||
struct amr_multirate_conf *amr_mrc)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOGP(ss, logl, "%s AMR MR Conf: num_modes=%u",
|
||||
pfx, amr_mrc->num_modes);
|
||||
|
||||
for (i = 0; i < amr_mrc->num_modes; i++)
|
||||
LOGPC(ss, logl, ", mode[%u] = %u/%u/%u",
|
||||
i, amr_mrc->mode[i].mode, amr_mrc->mode[i].threshold,
|
||||
amr_mrc->mode[i].hysteresis);
|
||||
LOGPC(ss, logl, "\n");
|
||||
}
|
||||
|
||||
|
||||
/* parse a GSM 04.08 MultiRate Config IE (10.5.2.21aa) in a more
|
||||
* comfortable internal data structure */
|
||||
int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
|
||||
const uint8_t *mr_conf, unsigned int len)
|
||||
{
|
||||
uint8_t mr_version = mr_conf[0] >> 5;
|
||||
uint8_t num_codecs = 0;
|
||||
int i, j = 0;
|
||||
|
||||
if (mr_version != 1) {
|
||||
LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknonw\n",
|
||||
mr_version);
|
||||
goto ret_einval;
|
||||
}
|
||||
|
||||
/* check number of active codecs */
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (mr_conf[1] & (1 << i))
|
||||
num_codecs++;
|
||||
}
|
||||
|
||||
/* check for minimum length */
|
||||
if (num_codecs == 0 ||
|
||||
(num_codecs == 1 && len < 2) ||
|
||||
(num_codecs == 2 && len < 4) ||
|
||||
(num_codecs == 3 && len < 5) ||
|
||||
(num_codecs == 4 && len < 6) ||
|
||||
(num_codecs > 4)) {
|
||||
LOGP(DRSL, LOGL_ERROR, "AMR Multirate with %u modes len=%u "
|
||||
"not possible\n", num_codecs, len);
|
||||
goto ret_einval;
|
||||
}
|
||||
|
||||
/* copy the first two octets of the IE */
|
||||
amr_mrc->gsm48_ie[0] = mr_conf[0];
|
||||
amr_mrc->gsm48_ie[1] = mr_conf[1];
|
||||
|
||||
amr_mrc->num_modes = num_codecs;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (mr_conf[1] & (1 << i)) {
|
||||
amr_mrc->mode[j++].mode = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_codecs >= 2) {
|
||||
amr_mrc->mode[0].threshold = mr_conf[1] & 0x3F;
|
||||
amr_mrc->mode[0].hysteresis = mr_conf[2] >> 4;
|
||||
}
|
||||
if (num_codecs >= 3) {
|
||||
amr_mrc->mode[1].threshold =
|
||||
((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6);
|
||||
amr_mrc->mode[1].hysteresis = (mr_conf[3] >> 2) & 0x7;
|
||||
}
|
||||
if (num_codecs >= 4) {
|
||||
amr_mrc->mode[3].threshold =
|
||||
((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4);
|
||||
amr_mrc->mode[3].hysteresis = mr_conf[4] & 0xF;
|
||||
}
|
||||
|
||||
return num_codecs;
|
||||
|
||||
ret_einval:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief determine AMR initial codec mode for given logical channel
|
||||
* \returns integer between 0..3 for AMR codce mode 1..4 */
|
||||
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
|
||||
|
||||
if (lchan->mr_conf.icmi) {
|
||||
/* initial mode given, coding in TS 05.09 3.4.1 */
|
||||
return lchan->mr_conf.smod;
|
||||
} else {
|
||||
/* implicit rule according to TS 05.09 Chapter 3.4.3 */
|
||||
switch (amr_mrc->num_modes) {
|
||||
case 2:
|
||||
case 3:
|
||||
/* return the most robust */
|
||||
return 0;
|
||||
case 4:
|
||||
/* return the second-most robust */
|
||||
return 1;
|
||||
case 1:
|
||||
default:
|
||||
/* return the only mode we have */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
700
src/common/bts.c
700
src/common/bts.c
@@ -42,6 +42,7 @@
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/signal.h>
|
||||
|
||||
|
||||
struct gsm_network bts_gsmnet = {
|
||||
@@ -51,27 +52,80 @@ struct gsm_network bts_gsmnet = {
|
||||
|
||||
void *tall_bts_ctx;
|
||||
|
||||
/* Table 3.1 TS 04.08: Values of parameter S */
|
||||
static const uint8_t tx_integer[] = {
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50,
|
||||
};
|
||||
|
||||
static const uint8_t s_values[][2] = {
|
||||
{ 55, 41 }, { 76, 52 }, { 109, 58 }, { 163, 86 }, { 217, 115 },
|
||||
};
|
||||
|
||||
static int bts_signal_cbfn(unsigned int subsys, unsigned int signal,
|
||||
void *hdlr_data, void *signal_data)
|
||||
{
|
||||
if (subsys == SS_GLOBAL && signal == S_NEW_SYSINFO) {
|
||||
struct gsm_bts *bts = signal_data;
|
||||
|
||||
bts_update_agch_max_queue_length(bts);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bts_init(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb;
|
||||
struct gsm_bts_trx *trx;
|
||||
int rc;
|
||||
static int initialized = 0;
|
||||
|
||||
/* add to list of BTSs */
|
||||
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
|
||||
|
||||
bts->band = GSM_BAND_1800;
|
||||
|
||||
bts->role = btsb = talloc_zero(bts, struct gsm_bts_role_bts);
|
||||
|
||||
INIT_LLIST_HEAD(&btsb->agch_queue);
|
||||
btsb->agch_queue_length = 0;
|
||||
|
||||
/* FIXME: make those parameters configurable */
|
||||
/* enable management with default levels,
|
||||
* raise threshold to GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DISABLE to
|
||||
* disable this feature.
|
||||
*/
|
||||
btsb->agch_queue_low_level = GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT;
|
||||
btsb->agch_queue_high_level = GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT;
|
||||
btsb->agch_queue_thresh_level = GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT;
|
||||
|
||||
/* configurable via VTY */
|
||||
btsb->paging_state = paging_init(btsb, 200, 0);
|
||||
|
||||
/* configurable via OML */
|
||||
btsb->load.ccch.load_ind_period = 112;
|
||||
load_timer_start(bts);
|
||||
btsb->rtp_jitter_buf_ms = 100;
|
||||
btsb->max_ta = 63;
|
||||
btsb->ny1 = 4;
|
||||
btsb->t3105_ms = 300;
|
||||
|
||||
/* default RADIO_LINK_TIMEOUT */
|
||||
btsb->radio_link_timeout = 32;
|
||||
|
||||
/* Start with the site manager */
|
||||
oml_mo_state_init(&bts->site_mgr.mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
|
||||
|
||||
/* set BTS to dependency */
|
||||
oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||
oml_mo_state_init(&bts->mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||
oml_mo_state_init(&bts->gprs.nse.mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||
oml_mo_state_init(&bts->gprs.cell.mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||
oml_mo_state_init(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_DEPENDENCY);
|
||||
oml_mo_state_init(&bts->gprs.nsvc[1].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
|
||||
|
||||
/* initialize bts data structure */
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[i];
|
||||
int k;
|
||||
@@ -81,18 +135,29 @@ int bts_init(struct gsm_bts *bts)
|
||||
INIT_LLIST_HEAD(&lchan->dl_tch_queue);
|
||||
}
|
||||
}
|
||||
/* Default values for the power adjustments */
|
||||
tpp->ramp.max_initial_pout_mdBm = to_mdB(23);
|
||||
tpp->ramp.step_size_mdB = to_mdB(2);
|
||||
tpp->ramp.step_interval_sec = 1;
|
||||
}
|
||||
|
||||
osmo_rtp_init(tall_bts_ctx);
|
||||
|
||||
rc = bts_model_init(bts);
|
||||
if (rc < 0)
|
||||
if (rc < 0) {
|
||||
llist_del(&bts->list);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* add to list of BTSs */
|
||||
llist_add_tail(&bts->list, &bts_gsmnet.bts_list);
|
||||
bts_gsmnet.num_bts++;
|
||||
|
||||
if (!initialized) {
|
||||
osmo_signal_register_handler(SS_GLOBAL, bts_signal_cbfn, NULL);
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&btsb->smscb_state.queue);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -110,238 +175,25 @@ void bts_shutdown(struct gsm_bts *bts, const char *reason)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
if (osmo_timer_pending(&shutdown_timer)) {
|
||||
LOGP(DOML, LOGL_NOTICE,
|
||||
"BTS is already being shutdown.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DOML, LOGL_NOTICE, "Shutting down BTS %u, Reason %s\n",
|
||||
bts->nr, reason);
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list)
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
bts_model_trx_deact_rf(trx);
|
||||
bts_model_trx_close(trx);
|
||||
}
|
||||
|
||||
/* shedule a timer to make sure select loop logic can run again
|
||||
* to dispatch any pending primitives */
|
||||
osmo_timer_schedule(&shutdown_timer, 3, 0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct osmobts_lchan *lchan_by_channelnr(struct osmobts_trx *trx,
|
||||
uint8_t channelnr)
|
||||
{
|
||||
uint8_t ts = channelnr & ~RSL_CHAN_NR_MASK;
|
||||
uint8_t type = channelnr & RSL_CHAN_NR_MASK;
|
||||
uint8_t sub = 0;
|
||||
struct osmobts_slot *slot = trx->slot[ts];
|
||||
|
||||
if ((type & 0xf0) == RSL_CHAN_Lm_ACCHs) {
|
||||
sub = (channelnr >> 3) & 1;
|
||||
type = RSL_CHAN_Lm_ACCHs;
|
||||
} else
|
||||
if ((type & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
|
||||
sub = (channelnr >> 3) & 3;
|
||||
type = RSL_CHAN_SDCCH4_ACCH;
|
||||
} else
|
||||
if ((type & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
|
||||
sub = (channelnr >> 3) & 7;
|
||||
type = RSL_CHAN_SDCCH8_ACCH;
|
||||
} else
|
||||
if (type == RSL_CHAN_BCCH
|
||||
|| type == RSL_CHAN_RACH
|
||||
|| type == RSL_CHAN_PCH_AGCH) {
|
||||
if (!slot->has_bcch) {
|
||||
LOGP(DSUM, LOGL_NOTICE, "Slot %d has no BCCH\n", ts);
|
||||
return NULL;
|
||||
}
|
||||
return slot->lchan[0];
|
||||
} else {
|
||||
LOGP(DSUM, LOGL_NOTICE, "Unknown channel type 0x%02x\n", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (slot->acch_type == type)
|
||||
return slot->lchan[sub];
|
||||
}
|
||||
LOGP(DSUM, LOGL_NOTICE, "No slot found with channel type 0x%02x\n", type);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
struct osmocom_bts *create_bts(uint8_t num_trx, char *id)
|
||||
{
|
||||
struct osmocom_bts *bts;
|
||||
struct osmobts_trx *trx;
|
||||
int i, j;
|
||||
|
||||
LOGP(DSUM, LOGL_INFO, "Creating BTS\n");
|
||||
bts = talloc_zero(l23_ctx, struct osmocom_bts);
|
||||
if (!bts)
|
||||
return NULL;
|
||||
bts->link.bts = bts;
|
||||
bts->id = id;
|
||||
for (i = 0; i < num_trx; i++) {
|
||||
LOGP(DSUM, LOGL_INFO, "Creating TRX %d\n", i);
|
||||
trx = talloc_zero(l23_ctx, struct osmobts_trx);
|
||||
if (!trx)
|
||||
return NULL;
|
||||
trx->bts = bts;
|
||||
trx->trx_nr = i;
|
||||
INIT_LLIST_HEAD(&trx->ms_list);
|
||||
for (j = 0; j < 8; j++) {
|
||||
trx->slot[j].trx = trx;
|
||||
trx->slot[j].slot_nr = j;
|
||||
trx->slot[j].chan_comb = 0xff; /* not set */
|
||||
}
|
||||
trx->link.trx = trx;
|
||||
bts->trx[i] = trx;
|
||||
}
|
||||
bts->num_trx = num_trx;
|
||||
|
||||
return bts;
|
||||
}
|
||||
|
||||
int create_ms(struct osmobts_trx *trx, int maskc, uint8_t *maskv_tx,
|
||||
uint8_t *maskv_rx)
|
||||
{
|
||||
struct osmobts_ms *tx_ms, *rx_ms;
|
||||
int i, j;
|
||||
int rc;
|
||||
static char sock_path[] = "/tmp/osmocom_l2.1";
|
||||
|
||||
for (i = 0; i < maskc; i++) {
|
||||
/* create MS */
|
||||
tx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
|
||||
printf("%p\n", tx_ms);
|
||||
if (!tx_ms)
|
||||
return -ENOMEM;
|
||||
tx_ms->trx = trx;
|
||||
rc = layer2_open(&tx_ms->ms, sock_path);
|
||||
strcpy(tx_ms->ms.name, strchr(sock_path, '.') + 1);
|
||||
sock_path[strlen(sock_path) - 1]++;
|
||||
if (rc < 0) {
|
||||
talloc_free(tx_ms);
|
||||
return rc;
|
||||
}
|
||||
llist_add_tail(&tx_ms->entry, &trx->ms_list);
|
||||
rx_ms = talloc_zero(l23_ctx, struct osmobts_ms);
|
||||
rx_ms->trx = trx;
|
||||
if (!rx_ms)
|
||||
return -ENOMEM;
|
||||
rc = layer2_open(&rx_ms->ms, sock_path);
|
||||
strcpy(rx_ms->ms.name, strchr(sock_path, '.') + 1);
|
||||
sock_path[strlen(sock_path) - 1]++;
|
||||
if (rc < 0) {
|
||||
talloc_free(rx_ms);
|
||||
return rc;
|
||||
}
|
||||
llist_add_tail(&rx_ms->entry, &trx->ms_list);
|
||||
/* assign to SLOT */
|
||||
for (j = 0; j < 8; j++) {
|
||||
if ((maskv_tx[i] & (1 << j)))
|
||||
trx->slot[j].tx_ms = tx_ms;
|
||||
if ((maskv_rx[i] & (1 << j)))
|
||||
trx->slot[j].rx_ms = rx_ms;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void destroy_lchan(struct osmobts_lchan *lchan)
|
||||
{
|
||||
LOGP(DSUM, LOGL_INFO, "Destroying logical channel. (trx=%d ts=%d ss=%d)\n", lchan->slot->trx->trx_nr, lchan->slot->slot_nr, lchan->lchan_nr);
|
||||
lapdm_exit(&lchan->l2_entity.lapdm_acch);
|
||||
lapdm_exit(&lchan->l2_entity.lapdm_acch);
|
||||
if (lchan->rtp.socket_created)
|
||||
rtp_close_socket(&lchan->rtp);
|
||||
talloc_free(lchan);
|
||||
}
|
||||
|
||||
/* create and destroy lchan */
|
||||
void bts_setup_slot(struct osmobts_slot *slot, uint8_t comb)
|
||||
{
|
||||
int i, ii;
|
||||
struct osmobts_lchan *lchan;
|
||||
uint8_t cbits;
|
||||
|
||||
if (slot->chan_comb == comb)
|
||||
return;
|
||||
|
||||
/* destroy old */
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (slot->lchan[i])
|
||||
destroy_lchan(slot->lchan[i]);
|
||||
slot->lchan[i] = NULL;
|
||||
}
|
||||
switch(comb) {
|
||||
case 0xff:
|
||||
return;
|
||||
case NM_CHANC_TCHFull:
|
||||
cbits = 0x01;
|
||||
ii = 1;
|
||||
break;
|
||||
case NM_CHANC_TCHHalf:
|
||||
cbits = 0x02;
|
||||
ii = 2;
|
||||
break;
|
||||
case NM_CHANC_BCCHComb:
|
||||
cbits = 0x04;
|
||||
ii = 4;
|
||||
break;
|
||||
case NM_CHANC_SDCCH:
|
||||
cbits = 0x08;
|
||||
ii = 8;
|
||||
break;
|
||||
default:
|
||||
cbits = 0x10;
|
||||
ii = 0;
|
||||
}
|
||||
|
||||
if (!slot->tx_ms) {
|
||||
LOGP(DSUM, LOGL_ERROR, "Slot is not available\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ii; i++) {
|
||||
LOGP(DSUM, LOGL_INFO, "Creating logical channel. (trx=%d ts=%d ss=%d)\n", slot->trx->trx_nr, slot->slot_nr, i);
|
||||
lchan = talloc_zero(l23_ctx, struct osmobts_lchan);
|
||||
lchan->slot = slot;
|
||||
lchan->lchan_nr = i;
|
||||
lchan->chan_nr = ((cbits | i) << 3) | slot->slot_nr;
|
||||
lapdm_init(&lchan->l2_entity.lapdm_dcch, &lchan->l2_entity, &slot->tx_ms->ms);
|
||||
lapdm_init(&lchan->l2_entity.lapdm_acch, &lchan->l2_entity, &slot->tx_ms->ms);
|
||||
lapdm_set_bts();
|
||||
osmol2_register_handler(&lchan->l2_entity, &rsl_tx_rll);
|
||||
slot->lchan[i] = lchan;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void destroy_ms(struct osmobts_ms *ms)
|
||||
{
|
||||
layer2_close(&ms->ms);
|
||||
llist_del(&ms->entry);
|
||||
talloc_free(ms);
|
||||
}
|
||||
|
||||
void destroy_bts(struct osmocom_bts *bts)
|
||||
{
|
||||
int i;
|
||||
struct osmobts_ms *ms;
|
||||
|
||||
for (i = 0; i < bts->num_trx; i++) {
|
||||
abis_close(&bts->trx[i]->link);
|
||||
while(!llist_empty(&bts->trx[i]->ms_list)) {
|
||||
ms = llist_entry(bts->trx[i]->ms_list.next,
|
||||
struct osmobts_ms, entry);
|
||||
destroy_ms(ms);
|
||||
}
|
||||
if (osmo_timer_pending(&bts->trx[i]->si.timer))
|
||||
osmo_timer_del(&bts->trx[i]->si.timer);
|
||||
talloc_free(bts->trx[i]);
|
||||
bts->trx[i] = NULL;
|
||||
}
|
||||
abis_close(&bts->link);
|
||||
talloc_free(bts);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* main link is established, send status report */
|
||||
int bts_link_estab(struct gsm_bts *bts)
|
||||
{
|
||||
@@ -349,10 +201,16 @@ int bts_link_estab(struct gsm_bts *bts)
|
||||
|
||||
LOGP(DSUM, LOGL_INFO, "Main link established, sending Status'.\n");
|
||||
|
||||
/* BTS and SITE MGR are EAABLED, BTS is DEPENDENCY */
|
||||
/* BTS and SITE MGR are EANBLED, BTS is DEPENDENCY */
|
||||
oml_tx_state_changed(&bts->site_mgr.mo);
|
||||
oml_tx_state_changed(&bts->mo);
|
||||
|
||||
/* those should all be in DEPENDENCY */
|
||||
oml_tx_state_changed(&bts->gprs.nse.mo);
|
||||
oml_tx_state_changed(&bts->gprs.cell.mo);
|
||||
oml_tx_state_changed(&bts->gprs.nsvc[0].mo);
|
||||
oml_tx_state_changed(&bts->gprs.nsvc[1].mo);
|
||||
|
||||
/* All other objects start off-line until the BTS Model code says otherwise */
|
||||
for (i = 0; i < bts->num_trx; i++) {
|
||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, i);
|
||||
@@ -367,56 +225,26 @@ int bts_link_estab(struct gsm_bts *bts)
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return bts_model_oml_estab(bts);
|
||||
}
|
||||
|
||||
/* RSL link is established, send status report */
|
||||
int trx_link_estab(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct ipabis_link *link = (struct ipabis_link *) trx->rsl_link;
|
||||
uint8_t radio_state = (link->state == LINK_STATE_CONNECT) ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED;
|
||||
struct e1inp_sign_link *link = trx->rsl_link;
|
||||
uint8_t radio_state = link ? NM_OPSTATE_ENABLED : NM_OPSTATE_DISABLED;
|
||||
|
||||
LOGP(DSUM, LOGL_INFO, "RSL link (TRX %02x) state changed to %s, sending Status'.\n",
|
||||
trx->nr, (link->state == LINK_STATE_CONNECT) ? "up" : "down");
|
||||
trx->nr, link ? "up" : "down");
|
||||
|
||||
oml_mo_state_chg(&trx->mo, radio_state, NM_AVSTATE_OK);
|
||||
|
||||
if (link->state == LINK_STATE_CONNECT)
|
||||
if (link)
|
||||
rsl_tx_rf_res(trx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bts_new_si(void *arg)
|
||||
{
|
||||
struct osmobts_trx *trx = arg;
|
||||
|
||||
#if 0
|
||||
if (osmo_timer_pending(&trx->si.timer))
|
||||
return;
|
||||
|
||||
i = 0;
|
||||
while(i < BTS_SI_NUM) {
|
||||
if ((trx->si.flags[i] & BTS_SI_NEW))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
if (i == BTS_SI_NUM)
|
||||
return;
|
||||
if ((trx->si.flags[i] & BTS_SI_USE))
|
||||
LOGP(DSUM, LOGL_INFO, "Setting SYSTEM INFORMATION %s.\n", bts_si_name[i]);
|
||||
else
|
||||
LOGP(DSUM, LOGL_INFO, "Removing SYSTEM INFORMATION %s.\n", bts_si_name[i]);
|
||||
trx->si.flags[i] &= ~BTS_SI_NEW;
|
||||
/* distribute */
|
||||
printf("TODO: send SI update to L1\n");
|
||||
/* delay until next SI */
|
||||
trx->si.timer.cb = bts_new_si;
|
||||
trx->si.timer.data = trx;
|
||||
osmo_timer_schedule(&trx->si.timer, 0, 200000);
|
||||
#endif
|
||||
}
|
||||
|
||||
int lchan_init_lapdm(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct lapdm_channel *lc = &lchan->lapdm_ch;
|
||||
@@ -429,26 +257,352 @@ int lchan_init_lapdm(struct gsm_lchan *lchan)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CCCH_RACH_RATIO_COMBINED256 (256*1/9)
|
||||
#define CCCH_RACH_RATIO_SEPARATE256 (256*10/55)
|
||||
|
||||
int bts_agch_max_queue_length(int T, int bcch_conf)
|
||||
{
|
||||
int S, ccch_rach_ratio256, i;
|
||||
int T_group = 0;
|
||||
int is_ccch_comb = 0;
|
||||
|
||||
if (bcch_conf == RSL_BCCH_CCCH_CONF_1_C)
|
||||
is_ccch_comb = 1;
|
||||
|
||||
/*
|
||||
* The calculation is based on the ratio of the number RACH slots and
|
||||
* CCCH blocks per time:
|
||||
* Lmax = (T + 2*S) / R_RACH * R_CCCH
|
||||
* where
|
||||
* T3126_min = (T + 2*S) / R_RACH, as defined in GSM 04.08, 11.1.1
|
||||
* R_RACH is the RACH slot rate (e.g. RACHs per multiframe)
|
||||
* R_CCCH is the CCCH block rate (same time base like R_RACH)
|
||||
* S and T are defined in GSM 04.08, 3.3.1.1.2
|
||||
* The ratio is mainly influenced by the downlink only channels
|
||||
* (BCCH, FCCH, SCH, CBCH) that can not be used for CCCH.
|
||||
* An estimation with an error of < 10% is used:
|
||||
* ~ 1/9 if CCCH is combined with SDCCH, and
|
||||
* ~ 1/5.5 otherwise.
|
||||
*/
|
||||
ccch_rach_ratio256 = is_ccch_comb ?
|
||||
CCCH_RACH_RATIO_COMBINED256 :
|
||||
CCCH_RACH_RATIO_SEPARATE256;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tx_integer); i++) {
|
||||
if (tx_integer[i] == T) {
|
||||
T_group = i % 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
S = s_values[T_group][is_ccch_comb];
|
||||
|
||||
return (T + 2 * S) * ccch_rach_ratio256 / 256;
|
||||
}
|
||||
|
||||
void bts_update_agch_max_queue_length(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct gsm48_system_information_type_3 *si3;
|
||||
int old_max_length = btsb->agch_max_queue_length;
|
||||
|
||||
if (!(bts->si_valid & (1<<SYSINFO_TYPE_3)))
|
||||
return;
|
||||
|
||||
si3 = GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
||||
|
||||
btsb->agch_max_queue_length =
|
||||
bts_agch_max_queue_length(si3->rach_control.tx_integer,
|
||||
si3->control_channel_desc.ccch_conf);
|
||||
|
||||
if (btsb->agch_max_queue_length != old_max_length)
|
||||
LOGP(DRSL, LOGL_INFO, "Updated AGCH max queue length to %d\n",
|
||||
btsb->agch_max_queue_length);
|
||||
}
|
||||
|
||||
#define REQ_REFS_PER_IMM_ASS_REJ 4
|
||||
static int store_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
|
||||
struct gsm48_req_ref *req_refs,
|
||||
uint8_t *wait_inds,
|
||||
int count)
|
||||
{
|
||||
switch (count) {
|
||||
case 0:
|
||||
/* TODO: Warning ? */
|
||||
return 0;
|
||||
default:
|
||||
count = 4;
|
||||
rej->req_ref4 = req_refs[3];
|
||||
rej->wait_ind4 = wait_inds[3];
|
||||
/* fall through */
|
||||
case 3:
|
||||
rej->req_ref3 = req_refs[2];
|
||||
rej->wait_ind3 = wait_inds[2];
|
||||
/* fall through */
|
||||
case 2:
|
||||
rej->req_ref2 = req_refs[1];
|
||||
rej->wait_ind2 = wait_inds[1];
|
||||
/* fall through */
|
||||
case 1:
|
||||
rej->req_ref1 = req_refs[0];
|
||||
rej->wait_ind1 = wait_inds[0];
|
||||
break;
|
||||
}
|
||||
|
||||
switch (count) {
|
||||
case 1:
|
||||
rej->req_ref2 = req_refs[0];
|
||||
rej->wait_ind2 = wait_inds[0];
|
||||
/* fall through */
|
||||
case 2:
|
||||
rej->req_ref3 = req_refs[0];
|
||||
rej->wait_ind3 = wait_inds[0];
|
||||
/* fall through */
|
||||
case 3:
|
||||
rej->req_ref4 = req_refs[0];
|
||||
rej->wait_ind4 = wait_inds[0];
|
||||
/* fall through */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int extract_imm_ass_rej_refs(struct gsm48_imm_ass_rej *rej,
|
||||
struct gsm48_req_ref *req_refs,
|
||||
uint8_t *wait_inds)
|
||||
{
|
||||
int count = 0;
|
||||
req_refs[count] = rej->req_ref1;
|
||||
wait_inds[count] = rej->wait_ind1;
|
||||
count++;
|
||||
|
||||
if (memcmp(&rej->req_ref1, &rej->req_ref2, sizeof(rej->req_ref2))) {
|
||||
req_refs[count] = rej->req_ref2;
|
||||
wait_inds[count] = rej->wait_ind2;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (memcmp(&rej->req_ref1, &rej->req_ref3, sizeof(rej->req_ref3)) &&
|
||||
memcmp(&rej->req_ref2, &rej->req_ref3, sizeof(rej->req_ref3))) {
|
||||
req_refs[count] = rej->req_ref3;
|
||||
wait_inds[count] = rej->wait_ind3;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (memcmp(&rej->req_ref1, &rej->req_ref4, sizeof(rej->req_ref4)) &&
|
||||
memcmp(&rej->req_ref2, &rej->req_ref4, sizeof(rej->req_ref4)) &&
|
||||
memcmp(&rej->req_ref3, &rej->req_ref4, sizeof(rej->req_ref4))) {
|
||||
req_refs[count] = rej->req_ref4;
|
||||
wait_inds[count] = rej->wait_ind4;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int try_merge_imm_ass_rej(struct gsm48_imm_ass_rej *old_rej,
|
||||
struct gsm48_imm_ass_rej *new_rej)
|
||||
{
|
||||
struct gsm48_req_ref req_refs[2 * REQ_REFS_PER_IMM_ASS_REJ];
|
||||
uint8_t wait_inds[2 * REQ_REFS_PER_IMM_ASS_REJ];
|
||||
int count = 0;
|
||||
int stored = 0;
|
||||
|
||||
if (new_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
|
||||
return 0;
|
||||
if (old_rej->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
|
||||
return 0;
|
||||
|
||||
/* GSM 08.58, 5.7
|
||||
* -> The BTS may combine serveral IMM.ASS.REJ messages
|
||||
* -> Identical request refs in one message may be squeezed
|
||||
*
|
||||
* GSM 04.08, 9.1.20.2
|
||||
* -> Request ref and wait ind are duplicated to fill the message
|
||||
*/
|
||||
|
||||
/* Extract all entries */
|
||||
count = extract_imm_ass_rej_refs(old_rej,
|
||||
&req_refs[count], &wait_inds[count]);
|
||||
if (count == REQ_REFS_PER_IMM_ASS_REJ)
|
||||
return 0;
|
||||
|
||||
count += extract_imm_ass_rej_refs(new_rej,
|
||||
&req_refs[count], &wait_inds[count]);
|
||||
|
||||
/* Store entries into old message */
|
||||
stored = store_imm_ass_rej_refs(old_rej,
|
||||
&req_refs[stored], &wait_inds[stored],
|
||||
count);
|
||||
count -= stored;
|
||||
if (count == 0)
|
||||
return 1;
|
||||
|
||||
/* Store remaining entries into new message */
|
||||
stored += store_imm_ass_rej_refs(new_rej,
|
||||
&req_refs[stored], &wait_inds[stored],
|
||||
count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bts_agch_enqueue(struct gsm_bts *bts, struct msgb *msg)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
int hard_limit = 1000;
|
||||
struct gsm48_imm_ass_rej *imm_ass_cmd = msgb_l3(msg);
|
||||
|
||||
/* FIXME: implement max queue length */
|
||||
llist_add_tail(&msg->list, &btsb->agch_queue);
|
||||
if (btsb->agch_queue_length > hard_limit) {
|
||||
LOGP(DSUM, LOGL_ERROR,
|
||||
"AGCH: too many messages in queue, "
|
||||
"refusing message type 0x%02x, length = %d/%d\n",
|
||||
((struct gsm48_imm_ass *)msgb_l3(msg))->msg_type,
|
||||
btsb->agch_queue_length, btsb->agch_max_queue_length);
|
||||
|
||||
btsb->agch_queue_rejected_msgs++;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (btsb->agch_queue_length > 0) {
|
||||
struct msgb *last_msg =
|
||||
llist_entry(btsb->agch_queue.prev, struct msgb, list);
|
||||
struct gsm48_imm_ass_rej *last_imm_ass_rej = msgb_l3(last_msg);
|
||||
|
||||
if (try_merge_imm_ass_rej(last_imm_ass_rej, imm_ass_cmd)) {
|
||||
btsb->agch_queue_merged_msgs++;
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
msgb_enqueue(&btsb->agch_queue, msg);
|
||||
btsb->agch_queue_length++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct msgb *bts_agch_dequeue(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);;
|
||||
struct msgb *msg;
|
||||
|
||||
if (llist_empty(&btsb->agch_queue))
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct msgb *msg = msgb_dequeue(&btsb->agch_queue);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg = llist_entry(btsb->agch_queue.next, struct msgb, list);
|
||||
llist_del(&msg->list);
|
||||
|
||||
btsb->agch_queue_length--;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove lower prio messages if the queue has grown too long.
|
||||
*
|
||||
* \return 0 iff the number of messages in the queue would fit into the AGCH
|
||||
* reserved part of the CCCH.
|
||||
*/
|
||||
static void compact_agch_queue(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct msgb *msg, *msg2;
|
||||
int max_len, slope, offs;
|
||||
int level_low = btsb->agch_queue_low_level;
|
||||
int level_high = btsb->agch_queue_high_level;
|
||||
int level_thres = btsb->agch_queue_thresh_level;
|
||||
|
||||
max_len = btsb->agch_max_queue_length;
|
||||
|
||||
if (max_len == 0)
|
||||
max_len = 1;
|
||||
|
||||
if (btsb->agch_queue_length < max_len * level_thres / 100)
|
||||
return;
|
||||
|
||||
/* p^
|
||||
* 1+ /'''''
|
||||
* | /
|
||||
* | /
|
||||
* 0+---/--+----+--> Q length
|
||||
* low high max_len
|
||||
*/
|
||||
|
||||
offs = max_len * level_low / 100;
|
||||
if (level_high > level_low)
|
||||
slope = 0x10000 * 100 / (level_high - level_low);
|
||||
else
|
||||
slope = 0x10000 * max_len; /* p_drop >= 1 if len > offs */
|
||||
|
||||
llist_for_each_entry_safe(msg, msg2, &btsb->agch_queue, list) {
|
||||
struct gsm48_imm_ass *imm_ass_cmd = msgb_l3(msg);
|
||||
int p_drop;
|
||||
|
||||
if (imm_ass_cmd->msg_type != GSM48_MT_RR_IMM_ASS_REJ)
|
||||
return;
|
||||
|
||||
/* IMMEDIATE ASSIGN REJECT */
|
||||
|
||||
p_drop = (btsb->agch_queue_length - offs) * slope / max_len;
|
||||
|
||||
if ((random() & 0xffff) >= p_drop)
|
||||
return;
|
||||
|
||||
llist_del(&msg->list);
|
||||
btsb->agch_queue_length--;
|
||||
msgb_free(msg);
|
||||
|
||||
btsb->agch_queue_dropped_msgs++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int bts_ccch_copy_msg(struct gsm_bts *bts, uint8_t *out_buf, struct gsm_time *gt,
|
||||
int is_ag_res)
|
||||
{
|
||||
struct msgb *msg = NULL;
|
||||
struct gsm_bts_role_bts *btsb = bts->role;
|
||||
int rc = 0;
|
||||
int is_empty = 1;
|
||||
|
||||
/* Do queue house keeping.
|
||||
* This needs to be done every time a CCCH message is requested, since
|
||||
* the queue max length is calculated based on the CCCH block rate and
|
||||
* PCH messages also reduce the drain of the AGCH queue.
|
||||
*/
|
||||
compact_agch_queue(bts);
|
||||
|
||||
/* Check for paging messages first if this is PCH */
|
||||
if (!is_ag_res)
|
||||
rc = paging_gen_msg(btsb->paging_state, out_buf, gt, &is_empty);
|
||||
|
||||
/* Check whether the block may be overwritten */
|
||||
if (!is_empty)
|
||||
return rc;
|
||||
|
||||
msg = bts_agch_dequeue(bts);
|
||||
if (!msg)
|
||||
return rc;
|
||||
|
||||
/* Copy AGCH message */
|
||||
memcpy(out_buf, msgb_l3(msg), msgb_l3len(msg));
|
||||
rc = msgb_l3len(msg);
|
||||
msgb_free(msg);
|
||||
|
||||
if (is_ag_res)
|
||||
btsb->agch_queue_agch_msgs++;
|
||||
else
|
||||
btsb->agch_queue_pch_msgs++;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher)
|
||||
{
|
||||
int sup;
|
||||
|
||||
if (rsl_cipher < 1 || rsl_cipher > 8)
|
||||
return -ENOTSUP;
|
||||
|
||||
/* No encryption is always supported */
|
||||
if (rsl_cipher == 1)
|
||||
return 1;
|
||||
|
||||
sup = (1 << (rsl_cipher - 2)) & bts->support.ciphers;
|
||||
return sup > 0;
|
||||
}
|
||||
|
||||
78
src/common/bts_ctrl_commands.c
Normal file
78
src/common/bts_ctrl_commands.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/* Control Interface for osmo-bts */
|
||||
|
||||
/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/tx_power.h>
|
||||
|
||||
CTRL_CMD_DEFINE(therm_att, "thermal-attenuation");
|
||||
static int get_therm_att(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gsm_bts_trx *trx = cmd->node;
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
|
||||
cmd->reply = talloc_asprintf(cmd, "%d", tpp->thermal_attenuation_mdB);
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
static int set_therm_att(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gsm_bts_trx *trx = cmd->node;
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
int val = atoi(cmd->value);
|
||||
|
||||
printf("set_therm_att(trx=%p, tpp=%p)\n", trx, tpp);
|
||||
|
||||
tpp->thermal_attenuation_mdB = val;
|
||||
|
||||
power_ramp_start(trx, tpp->p_total_cur_mdBm, 0);
|
||||
|
||||
return get_therm_att(cmd, data);
|
||||
}
|
||||
|
||||
static int verify_therm_att(struct ctrl_cmd *cmd, const char *value, void *data)
|
||||
{
|
||||
int val = atoi(value);
|
||||
|
||||
/* permit between 0 to 40 dB attenuation */
|
||||
if (val < 0 || val > to_mdB(40))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int bts_ctrl_cmds_install(struct gsm_bts *bts)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_therm_att);
|
||||
|
||||
return rc;
|
||||
}
|
||||
104
src/common/bts_ctrl_lookup.c
Normal file
104
src/common/bts_ctrl_lookup.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/* Control Interface for osmo-bts */
|
||||
|
||||
/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
extern vector ctrl_node_vec;
|
||||
|
||||
/*! \brief control interface lookup function for bsc/bts gsm_data
|
||||
* \param[in] data Private data passed to controlif_setup()
|
||||
* \param[in] vline Vector of the line holding the command string
|
||||
* \param[out] node_type type (CTRL_NODE_) that was determined
|
||||
* \param[out] node_data private dta of node that was determined
|
||||
* \param i Current index into vline, up to which it is parsed
|
||||
*/
|
||||
static int bts_ctrl_node_lookup(void *data, vector vline, int *node_type,
|
||||
void **node_data, int *i)
|
||||
{
|
||||
struct gsm_bts *bts = data;
|
||||
struct gsm_bts_trx *trx = NULL;
|
||||
struct gsm_bts_trx_ts *ts = NULL;
|
||||
char *token = vector_slot(vline, *i);
|
||||
long num;
|
||||
|
||||
/* TODO: We need to make sure that the following chars are digits
|
||||
* and/or use strtol to check if number conversion was successful
|
||||
* Right now something like net.bts_stats will not work */
|
||||
if (!strcmp(token, "trx")) {
|
||||
if (*node_type != CTRL_NODE_ROOT || !*node_data)
|
||||
goto err_missing;
|
||||
bts = *node_data;
|
||||
(*i)++;
|
||||
if (!ctrl_parse_get_num(vline, *i, &num))
|
||||
goto err_index;
|
||||
|
||||
trx = gsm_bts_trx_num(bts, num);
|
||||
if (!trx)
|
||||
goto err_missing;
|
||||
*node_data = trx;
|
||||
*node_type = CTRL_NODE_TRX;
|
||||
} else if (!strcmp(token, "ts")) {
|
||||
if (*node_type != CTRL_NODE_TRX || !*node_data)
|
||||
goto err_missing;
|
||||
trx = *node_data;
|
||||
(*i)++;
|
||||
if (!ctrl_parse_get_num(vline, *i, &num))
|
||||
goto err_index;
|
||||
|
||||
if ((num >= 0) && (num < TRX_NR_TS))
|
||||
ts = &trx->ts[num];
|
||||
if (!ts)
|
||||
goto err_missing;
|
||||
*node_data = ts;
|
||||
*node_type = CTRL_NODE_TS;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
err_missing:
|
||||
return -ENODEV;
|
||||
err_index:
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
struct ctrl_handle *bts_controlif_setup(struct gsm_bts *bts)
|
||||
{
|
||||
struct ctrl_handle *hdl;
|
||||
int rc = 0;
|
||||
|
||||
hdl = ctrl_interface_setup(bts, OSMO_CTRL_PORT_BTS, bts_ctrl_node_lookup);
|
||||
if (!hdl)
|
||||
return NULL;
|
||||
|
||||
rc = bts_ctrl_cmds_install(bts);
|
||||
if (rc) {
|
||||
/* FIXME: close control interface */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return hdl;
|
||||
}
|
||||
202
src/common/cbch.c
Normal file
202
src/common/cbch.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/* Cell Broadcast routines */
|
||||
|
||||
/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/cbch.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#define SMS_CB_MSG_LEN 88 /* TS 04.12 Section 3.1 */
|
||||
#define SMS_CB_BLOCK_LEN 22 /* TS 04.12 Section 3.1 */
|
||||
|
||||
struct smscb_msg {
|
||||
struct llist_head list; /* list in smscb_state.queue */
|
||||
|
||||
uint8_t msg[SMS_CB_MSG_LEN]; /* message buffer */
|
||||
uint8_t next_seg; /* next segment number */
|
||||
uint8_t num_segs; /* total number of segments */
|
||||
};
|
||||
|
||||
/* Figure 3/3GPP TS 04.12 */
|
||||
struct sms_cb_block_type {
|
||||
uint8_t seq_nr:4, /* 0=first, 1=2nd, ... f=null */
|
||||
lb:1, /* last block */
|
||||
lpd:2, /* always 01 */
|
||||
spare:1;
|
||||
};
|
||||
|
||||
/* get the next block of the current CB message */
|
||||
static int get_smscb_block(struct gsm_bts *bts, uint8_t *out)
|
||||
{
|
||||
int to_copy;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct sms_cb_block_type *block_type = (struct sms_cb_block_type *) out++;
|
||||
struct smscb_msg *msg = btsb->smscb_state.cur_msg;
|
||||
|
||||
/* LPD is always 01 */
|
||||
block_type->lpd = 1;
|
||||
|
||||
if (!msg) {
|
||||
/* No message: Send NULL mesage */
|
||||
block_type->seq_nr = 0xf;
|
||||
block_type->lb = 0;
|
||||
/* padding */
|
||||
memset(out, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP(DLSMS, "Current SMS-CB %s: ",
|
||||
osmo_hexdump_nospc(msg->msg, sizeof(msg->msg)));
|
||||
/* determine how much data to copy */
|
||||
to_copy = SMS_CB_MSG_LEN - (msg->next_seg * SMS_CB_BLOCK_LEN);
|
||||
if (to_copy > SMS_CB_BLOCK_LEN)
|
||||
to_copy = SMS_CB_BLOCK_LEN;
|
||||
|
||||
/* copy data and increment index */
|
||||
memcpy(out, &msg->msg[msg->next_seg * SMS_CB_BLOCK_LEN], to_copy);
|
||||
|
||||
/* set + increment sequence number */
|
||||
block_type->seq_nr = msg->next_seg++;
|
||||
|
||||
DEBUGP(DLSMS, "sending block %u: %s\n",
|
||||
block_type->seq_nr, osmo_hexdump_nospc(out, to_copy));
|
||||
|
||||
/* determine if this is the last block */
|
||||
if (block_type->seq_nr + 1 == msg->num_segs)
|
||||
block_type->lb = 1;
|
||||
else
|
||||
block_type->lb = 0;
|
||||
|
||||
if (block_type->lb == 1) {
|
||||
/* remove/release the message memory */
|
||||
talloc_free(btsb->smscb_state.cur_msg);
|
||||
btsb->smscb_state.cur_msg = NULL;
|
||||
}
|
||||
|
||||
return block_type->lb;
|
||||
}
|
||||
|
||||
static const uint8_t last_block_rsl2um[4] = {
|
||||
[RSL_CB_CMD_LASTBLOCK_4] = 4,
|
||||
[RSL_CB_CMD_LASTBLOCK_1] = 1,
|
||||
[RSL_CB_CMD_LASTBLOCK_2] = 2,
|
||||
[RSL_CB_CMD_LASTBLOCK_3] = 3,
|
||||
};
|
||||
|
||||
|
||||
/* incoming SMS broadcast command from RSL */
|
||||
int bts_process_smscb_cmd(struct gsm_bts *bts,
|
||||
struct rsl_ie_cb_cmd_type cmd_type,
|
||||
uint8_t msg_len, const uint8_t *msg)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct smscb_msg *scm;
|
||||
|
||||
if (msg_len > sizeof(scm->msg)) {
|
||||
LOGP(DLSMS, LOGL_ERROR,
|
||||
"Cannot process SMSCB of %u bytes (max %u)\n",
|
||||
msg_len, sizeof(scm->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scm = talloc_zero_size(bts, sizeof(*scm));
|
||||
|
||||
/* initialize entire message with default padding */
|
||||
memset(scm->msg, GSM_MACBLOCK_PADDING, sizeof(scm->msg));
|
||||
/* next segment is first segment */
|
||||
scm->next_seg = 0;
|
||||
|
||||
switch (cmd_type.command) {
|
||||
case RSL_CB_CMD_TYPE_NORMAL:
|
||||
case RSL_CB_CMD_TYPE_SCHEDULE:
|
||||
case RSL_CB_CMD_TYPE_NULL:
|
||||
scm->num_segs = last_block_rsl2um[cmd_type.last_block&3];
|
||||
memcpy(scm->msg, msg, msg_len);
|
||||
/* def_bcast is ignored */
|
||||
break;
|
||||
case RSL_CB_CMD_TYPE_DEFAULT:
|
||||
/* use def_bcast, ignore command */
|
||||
/* def_bcast == 0: normal mess */
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUGP(DLSMS, "Enqueuing SMS-CB %s from RSL",
|
||||
osmo_hexdump_nospc(scm->msg, sizeof(scm->msg)));
|
||||
llist_add_tail(&scm->list, &btsb->smscb_state.queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct smscb_msg *select_next_smscb(struct gsm_bts *bts)
|
||||
{
|
||||
struct smscb_msg *msg;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
if (llist_empty(&btsb->smscb_state.queue))
|
||||
return NULL;
|
||||
|
||||
msg = llist_entry(btsb->smscb_state.queue.next,
|
||||
struct smscb_msg, list);
|
||||
|
||||
llist_del(&msg->list);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* call-back from bts model specific code when it wants to obtain a CBCH
|
||||
* block for a given gsm_time. outbuf must have 23 bytes of space. */
|
||||
int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
uint32_t fn = gsm_gsmtime2fn(g_time);
|
||||
/* According to 05.02 Section 6.5.4 */
|
||||
uint32_t tb = (fn / 51) % 8;
|
||||
int rc = 0;
|
||||
|
||||
/* The multiframes used for the basic cell broadcast channel
|
||||
* shall be those in * which TB = 0,1,2 and 3. The multiframes
|
||||
* used for the extended cell broadcast channel shall be those
|
||||
* in which TB = 4, 5, 6 and 7 */
|
||||
|
||||
/* The SMSCB header shall be sent in the multiframe in which TB
|
||||
* = 0 for the basic, and TB = 4 for the extended cell
|
||||
* broadcast channel. */
|
||||
|
||||
switch (tb) {
|
||||
case 0:
|
||||
/* select a new SMSCB message */
|
||||
btsb->smscb_state.cur_msg = select_next_smscb(bts);
|
||||
rc = get_smscb_block(bts, outbuf);
|
||||
break;
|
||||
case 1: case 2: case 3:
|
||||
rc = get_smscb_block(bts, outbuf);
|
||||
break;
|
||||
case 4: case 5: case 6: case 7:
|
||||
/* always send NULL frame in extended CBCH for now */
|
||||
outbuf[0] = 0x2f;
|
||||
memset(outbuf+1, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
111
src/common/etws_p1.c
Normal file
111
src/common/etws_p1.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/* Paging Rest Octets / ETWS support code */
|
||||
/*
|
||||
* (C) 2014 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 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 <osmocom/core/bits.h>
|
||||
#include <osmocom/core/bitvec.h>
|
||||
|
||||
/*! \brief construct the Paging 1 Rest Octets
|
||||
* \param[in] bitvec Bit Vector (destionation)
|
||||
* \param[in] seg_nr segment number 0...n
|
||||
* \param[in] num_segs total number of segments
|
||||
* \param[in] payload binary payload for this segment
|
||||
* \param[in] len_of_seg length of the segment
|
||||
*
|
||||
* Put together the P1 Rest Octets according to 10.5.2.23
|
||||
*/
|
||||
int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow)
|
||||
{
|
||||
|
||||
/* no NLN */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* no Priority 1 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* no Priority 2 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Group Call Information */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Packet Page Indication 1 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Packet Page Indication 2 */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* No Rel6 extensions */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* No Rel7 extensions */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* Rel8 extensions */
|
||||
bitvec_set_bit(bv, H);
|
||||
|
||||
/* priority_uplink_access bit: Use RACH */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
if (etws_will_follow) {
|
||||
/* We do have ETWS Primary Notification */
|
||||
bitvec_set_bit(bv, ONE);
|
||||
} else {
|
||||
bitvec_set_bit(bv, ZERO);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief construct a ETWS Primary Notification struct
|
||||
* \param[in] bitvec Bit Vector (destionation)
|
||||
* \param[in] pni Primary Notification Identifier
|
||||
* \param[in] seg_nr Segment Number 0...n
|
||||
* \param[in] num_segs Total Number of Segments
|
||||
* \param[in] payload Binary Payload for this Segment
|
||||
* \param[in] num_payload_bits Length of the Segment
|
||||
*/
|
||||
int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
|
||||
uint8_t seg_nr, uint8_t num_segs,
|
||||
const uint8_t *payload,
|
||||
uint8_t num_payload_bits)
|
||||
{
|
||||
uint8_t payload_ubits[num_payload_bits];
|
||||
|
||||
/* Put together a "ETWS Primary Notification struct" as per TS
|
||||
* 44.018 */
|
||||
|
||||
if (seg_nr == 0) {
|
||||
bitvec_set_bit(bv, ZERO);
|
||||
bitvec_set_uint(bv, num_segs, 4);
|
||||
} else {
|
||||
bitvec_set_bit(bv, ONE);
|
||||
/* seg_nr is coounting 1..n, not 0..n */
|
||||
bitvec_set_uint(bv, seg_nr + 1, 4);
|
||||
}
|
||||
bitvec_set_bit(bv, pni ? ONE : ZERO);
|
||||
bitvec_set_uint(bv, num_payload_bits, 7);
|
||||
|
||||
/* expand packed payload bits to unpacked bits and set them in
|
||||
* the bit vector */
|
||||
osmo_pbit2ubit(payload_ubits, payload, num_payload_bits);
|
||||
int i;
|
||||
for (i = 0; i < num_payload_bits; ++i)
|
||||
bitvec_set_bit(bv, payload_ubits[i]);
|
||||
// bitvec_set_bits(bv, (enum bit_value *) payload_ubits, num_payload_bits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
157
src/common/handover.c
Normal file
157
src/common/handover.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/* Paging message encoding + queue management */
|
||||
|
||||
/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
* Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/rsl.h>
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/handover.h>
|
||||
|
||||
/* Transmit a handover related PHYS INFO on given lchan */
|
||||
static int ho_tx_phys_info(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(1024, 128, "PHYS INFO");
|
||||
struct gsm48_hdr *gh;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
LOGP(DHO, LOGL_INFO,
|
||||
"%s Sending PHYSICAL INFORMATION to MS.\n",
|
||||
gsm_lchan_name(lchan));
|
||||
|
||||
/* Build RSL UNITDATA REQUEST message with 04.08 PHYS INFO */
|
||||
msg->l3h = msg->data;
|
||||
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
gh->proto_discr = GSM48_PDISC_RR;
|
||||
gh->msg_type = GSM48_MT_RR_HANDO_INFO;
|
||||
msgb_put_u8(msg, lchan->rqd_ta);
|
||||
|
||||
rsl_rll_push_l3(msg, RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan),
|
||||
0x00, 0);
|
||||
|
||||
lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* timer call-back for T3105 (handover PHYS INFO re-transmit) */
|
||||
static void ho_t3105_cb(void *data)
|
||||
{
|
||||
struct gsm_lchan *lchan = data;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
struct gsm_bts_role_bts *btsb = bts->role;
|
||||
|
||||
LOGP(DHO, LOGL_INFO, "%s T3105 timeout (%d resends left)\n",
|
||||
gsm_lchan_name(lchan), btsb->ny1 - lchan->ho.phys_info_count);
|
||||
|
||||
if (lchan->state != LCHAN_S_ACTIVE) {
|
||||
LOGP(DHO, LOGL_NOTICE,
|
||||
"%s is in not active. It is in state %s. Ignoring\n",
|
||||
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
|
||||
return;
|
||||
}
|
||||
|
||||
if (lchan->ho.phys_info_count >= btsb->ny1) {
|
||||
/* HO Abort */
|
||||
LOGP(DHO, LOGL_NOTICE, "%s NY1 reached, sending CONNection "
|
||||
"FAILure to BSC.\n", gsm_lchan_name(lchan));
|
||||
rsl_tx_conn_fail(lchan, RSL_ERR_HANDOVER_ACC_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
ho_tx_phys_info(lchan);
|
||||
lchan->ho.phys_info_count++;
|
||||
osmo_timer_schedule(&lchan->ho.t3105, 0, btsb->t3105_ms * 1000);
|
||||
}
|
||||
|
||||
/* received random access on dedicated channel */
|
||||
void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
|
||||
{
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
struct gsm_bts_role_bts *btsb = bts->role;
|
||||
|
||||
/* Ignore invalid handover ref */
|
||||
if (lchan->ho.ref != ra) {
|
||||
LOGP(DHO, LOGL_INFO, "%s RACH on dedicated channel received, but "
|
||||
"ra=0x%02x != expected ref=0x%02x. (This is no bug)\n",
|
||||
gsm_lchan_name(lchan), ra, lchan->ho.ref);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DHO, LOGL_NOTICE,
|
||||
"%s RACH on dedicated channel received with TA=%u\n",
|
||||
gsm_lchan_name(lchan), acc_delay);
|
||||
|
||||
/* Set timing advance */
|
||||
lchan->rqd_ta = acc_delay;
|
||||
|
||||
/* Stop handover detection, wait for valid frame */
|
||||
lchan->ho.active = HANDOVER_WAIT_FRAME;
|
||||
if (bts_model_rsl_chan_mod(lchan) != 0) {
|
||||
LOGP(DHO, LOGL_ERROR,
|
||||
"%s failed to modify channel after handover\n",
|
||||
gsm_lchan_name(lchan));
|
||||
rsl_tx_conn_fail(lchan, RSL_ERR_HANDOVER_ACC_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send HANDover DETect to BSC */
|
||||
rsl_tx_hando_det(lchan, &lchan->rqd_ta);
|
||||
|
||||
/* Send PHYS INFO */
|
||||
lchan->ho.phys_info_count = 1;
|
||||
ho_tx_phys_info(lchan);
|
||||
|
||||
/* Start T3105 */
|
||||
LOGP(DHO, LOGL_DEBUG,
|
||||
"%s Starting T3105 with %u ms\n",
|
||||
gsm_lchan_name(lchan), btsb->t3105_ms);
|
||||
lchan->ho.t3105.cb = ho_t3105_cb;
|
||||
lchan->ho.t3105.data = lchan;
|
||||
osmo_timer_schedule(&lchan->ho.t3105, 0, btsb->t3105_ms * 1000);
|
||||
}
|
||||
|
||||
/* received frist valid data frame on dedicated channel */
|
||||
void handover_frame(struct gsm_lchan *lchan)
|
||||
{
|
||||
LOGP(DHO, LOGL_INFO,
|
||||
"%s First valid frame detected\n", gsm_lchan_name(lchan));
|
||||
handover_reset(lchan);
|
||||
}
|
||||
|
||||
/* release handover state */
|
||||
void handover_reset(struct gsm_lchan *lchan)
|
||||
{
|
||||
/* Stop T3105 */
|
||||
osmo_timer_del(&lchan->ho.t3105);
|
||||
|
||||
/* Handover process is done */
|
||||
lchan->ho.active = HANDOVER_NONE;
|
||||
}
|
||||
27
src/common/lchan.c
Normal file
27
src/common/lchan.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/* OsmoBTS lchan interface */
|
||||
|
||||
/* (C) 2012 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state)
|
||||
{
|
||||
lchan->state = state;
|
||||
}
|
||||
@@ -19,12 +19,19 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <rsl.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
static void reset_load_counters(void)
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/paging.h>
|
||||
|
||||
static void reset_load_counters(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
/* re-set the counters */
|
||||
btsb->load.ccch.pch_used = btsb->load.ccch.pch_total = 0;
|
||||
}
|
||||
@@ -32,38 +39,62 @@ static void reset_load_counters(void)
|
||||
static void load_timer_cb(void *data)
|
||||
{
|
||||
struct gsm_bts *bts = data;
|
||||
struct gsm_bts_role_bts *btsb = FIXME;
|
||||
unsigned int pch_percent;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
unsigned int pch_percent, rach_percent;
|
||||
|
||||
/* compute percentages */
|
||||
pch_percent = (btsb->load.ccch.pch_used * 100) / btsb->load.ccch.pch_total;
|
||||
if (btsb->load.ccch.pch_total == 0)
|
||||
pch_percent = 0;
|
||||
else
|
||||
pch_percent = (btsb->load.ccch.pch_used * 100) /
|
||||
btsb->load.ccch.pch_total;
|
||||
|
||||
if (pch_percent >= btsb->load.ccch.load_ind_thresh) {
|
||||
/* send RSL load indication message to BSC */
|
||||
uint16_t paging_buffer_space = FIXME;
|
||||
rsl_tx_ccch_load_ind_pch(bts, paging_buffer_space);
|
||||
uint16_t buffer_space = paging_buffer_space(btsb->paging_state);
|
||||
rsl_tx_ccch_load_ind_pch(bts, buffer_space);
|
||||
} else {
|
||||
/* This is an extenstion of TS 08.58. We don't only
|
||||
* send load indications if the load is above threshold,
|
||||
* but we also explicitly indicate that we are below
|
||||
* threshold by using the magic value 0xffff */
|
||||
rsl_tx_ccch_load_ind_pch(bts, 0xffff);
|
||||
}
|
||||
|
||||
reset_load_counters();
|
||||
if (btsb->load.rach.total == 0)
|
||||
rach_percent = 0;
|
||||
else
|
||||
rach_percent = (btsb->load.rach.busy * 100) /
|
||||
btsb->load.rach.total;
|
||||
|
||||
if (rach_percent >= btsb->load.ccch.load_ind_thresh) {
|
||||
/* send RSL load indication message to BSC */
|
||||
rsl_tx_ccch_load_ind_rach(bts, btsb->load.rach.total,
|
||||
btsb->load.rach.busy,
|
||||
btsb->load.rach.access);
|
||||
}
|
||||
|
||||
reset_load_counters(bts);
|
||||
|
||||
/* re-schedule the timer */
|
||||
osmo_timer_schedule(&btsb->load.ccch.timer,
|
||||
btsb->load.ccch.load_ind_period, 0);
|
||||
}
|
||||
|
||||
static void load_timer_start(struct gsm_bts *bts)
|
||||
void load_timer_start(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = FIXME;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->load.ccch.timer.data = bts;
|
||||
reset_load_counters();
|
||||
btsb->load.ccch.timer.cb = load_timer_cb;
|
||||
reset_load_counters(bts);
|
||||
osmo_timer_schedule(&btsb->load.ccch.timer,
|
||||
btsb->load.ccch.load_ind_period, 0);
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
static void load_timer_stop(struct gsm_bts *bts)
|
||||
void load_timer_stop(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
osmo_timer_del(&btsb->load.ccch.timer);
|
||||
}
|
||||
|
||||
@@ -95,6 +95,18 @@ static struct log_info_cat bts_log_info_cat[] = {
|
||||
.loglevel = LOGL_NOTICE,
|
||||
.enabled = 1,
|
||||
},
|
||||
[DPCU] = {
|
||||
.name = "DPCU",
|
||||
.description = "PCU interface",
|
||||
.loglevel = LOGL_NOTICE,
|
||||
.enabled = 1,
|
||||
},
|
||||
[DHO] = {
|
||||
.name = "DHO",
|
||||
.description = "Handover",
|
||||
.color = "\033[0;37m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
#if 0
|
||||
[DNS] = {
|
||||
.name = "DNS",
|
||||
|
||||
@@ -70,11 +70,13 @@ static int is_meas_complete(enum gsm_phys_chan_config pchan, unsigned int ts,
|
||||
rc = 1;
|
||||
break;
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C:
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
|
||||
fn_mod = fn % 102;
|
||||
if (fn_mod == 11)
|
||||
rc = 1;
|
||||
break;
|
||||
case GSM_PCHAN_CCCH_SDCCH4:
|
||||
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
|
||||
fn_mod = fn % 102;
|
||||
if (fn_mod == 36)
|
||||
rc = 1;
|
||||
@@ -92,6 +94,11 @@ int lchan_new_ul_meas(struct gsm_lchan *lchan, struct bts_ul_meas *ulm)
|
||||
DEBUGP(DMEAS, "%s adding measurement, num_ul_meas=%d\n",
|
||||
gsm_lchan_name(lchan), lchan->meas.num_ul_meas);
|
||||
|
||||
if (lchan->state != LCHAN_S_ACTIVE) {
|
||||
LOGP(DMEAS, LOGL_NOTICE, "%s measurement during state: %s\n",
|
||||
gsm_lchan_name(lchan), gsm_lchans_name(lchan->state));
|
||||
}
|
||||
|
||||
if (lchan->meas.num_ul_meas >= ARRAY_SIZE(lchan->meas.uplink)) {
|
||||
LOGP(DMEAS, LOGL_NOTICE, "%s no space for uplink measurement\n",
|
||||
gsm_lchan_name(lchan));
|
||||
@@ -125,8 +132,9 @@ static uint8_t ber10k_to_rxqual(uint32_t ber10k)
|
||||
return 7;
|
||||
}
|
||||
|
||||
int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
|
||||
static int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
|
||||
{
|
||||
struct gsm_meas_rep_unidir *mru;
|
||||
uint32_t ber_full_sum = 0;
|
||||
uint32_t irssi_full_sum = 0;
|
||||
uint32_t ber_sub_sum = 0;
|
||||
@@ -178,10 +186,11 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
|
||||
irssi_sub_sum);
|
||||
|
||||
/* store results */
|
||||
lchan->meas.res.rxlev_full = dbm2rxlev((int)irssi_full_sum * -1);
|
||||
lchan->meas.res.rxlev_sub = dbm2rxlev((int)irssi_sub_sum * -1);
|
||||
lchan->meas.res.rxqual_full = ber10k_to_rxqual(ber_full_sum);
|
||||
lchan->meas.res.rxqual_sub = ber10k_to_rxqual(ber_sub_sum);
|
||||
mru = &lchan->meas.ul_res;
|
||||
mru->full.rx_lev = dbm2rxlev((int)irssi_full_sum * -1);
|
||||
mru->sub.rx_lev = dbm2rxlev((int)irssi_sub_sum * -1);
|
||||
mru->full.rx_qual = ber10k_to_rxqual(ber_full_sum);
|
||||
mru->sub.rx_qual = ber10k_to_rxqual(ber_sub_sum);
|
||||
|
||||
lchan->meas.flags |= LC_UL_M_F_RES_VALID;
|
||||
lchan->meas.num_ul_meas = 0;
|
||||
@@ -194,19 +203,34 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
|
||||
/* build the 3 byte RSL uplinke measurement IE content */
|
||||
int lchan_build_rsl_ul_meas(struct gsm_lchan *lchan, uint8_t *buf)
|
||||
{
|
||||
buf[0] = (lchan->meas.res.rxlev_full & 0x3f); /* FIXME: DTXu support */
|
||||
buf[1] = (lchan->meas.res.rxlev_sub & 0x3f);
|
||||
buf[2] = ((lchan->meas.res.rxqual_full & 7) << 3) |
|
||||
(lchan->meas.res.rxqual_sub & 7);
|
||||
struct gsm_meas_rep_unidir *mru = &lchan->meas.ul_res;
|
||||
buf[0] = (mru->full.rx_lev & 0x3f); /* FIXME: DTXu support */
|
||||
buf[1] = (mru->sub.rx_lev & 0x3f);
|
||||
buf[2] = ((mru->full.rx_qual & 7) << 3) | (mru->sub.rx_qual & 7);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn)
|
||||
/* Copied from OpenBSC and enlarged to _GSM_PCHAN_MAX */
|
||||
static const uint8_t subslots_per_pchan[_GSM_PCHAN_MAX] = {
|
||||
[GSM_PCHAN_NONE] = 0,
|
||||
[GSM_PCHAN_CCCH] = 0,
|
||||
[GSM_PCHAN_CCCH_SDCCH4] = 4,
|
||||
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
|
||||
[GSM_PCHAN_TCH_F] = 1,
|
||||
[GSM_PCHAN_TCH_H] = 2,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
|
||||
/* FIXME: what about dynamic TCH_F_TCH_H ? */
|
||||
[GSM_PCHAN_TCH_F_PDCH] = 1,
|
||||
};
|
||||
|
||||
static int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn)
|
||||
{
|
||||
int i;
|
||||
const int num_subslots = subslots_per_pchan[ts->pchan];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ts->lchan); i++) {
|
||||
for (i = 0; i < num_subslots; ++i) {
|
||||
struct gsm_lchan *lchan = &ts->lchan[i];
|
||||
|
||||
if (lchan->state != LCHAN_S_ACTIVE)
|
||||
@@ -216,6 +240,7 @@ int ts_meas_check_compute(struct gsm_bts_trx_ts *ts, uint32_t fn)
|
||||
case GSM_LCHAN_SDCCH:
|
||||
case GSM_LCHAN_TCH_F:
|
||||
case GSM_LCHAN_TCH_H:
|
||||
case GSM_LCHAN_PDTCH:
|
||||
lchan_meas_check_compute(lchan, fn);
|
||||
break;
|
||||
default:
|
||||
|
||||
175
src/common/msg_utils.c
Normal file
175
src/common/msg_utils.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/* (C) 2014 by sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmo-bts/msg_utils.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
#include <osmocom/gsm/protocol/gsm_12_21.h>
|
||||
#include <osmocom/gsm/abis_nm.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
static int check_fom(struct abis_om_hdr *omh, size_t len)
|
||||
{
|
||||
if (omh->length != len) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Incorrect OM hdr length value %d %zu\n",
|
||||
omh->length, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len < sizeof(struct abis_om_fom_hdr)) {
|
||||
LOGP(DL1C, LOGL_ERROR, "FOM header insufficient space %zu %zu\n",
|
||||
len, sizeof(struct abis_om_fom_hdr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_manuf(struct msgb *msg, struct abis_om_hdr *omh, size_t msg_size)
|
||||
{
|
||||
int type;
|
||||
size_t size;
|
||||
|
||||
if (msg_size < 1) {
|
||||
LOGP(DL1C, LOGL_ERROR, "No ManId Length Indicator %zu\n",
|
||||
msg_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (omh->data[0] >= msg_size - 1) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Insufficient message space for this ManId Length %d %zu\n",
|
||||
omh->data[0], msg_size - 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (omh->data[0] == sizeof(abis_nm_ipa_magic) &&
|
||||
strncmp(abis_nm_ipa_magic, (const char *)omh->data + 1,
|
||||
sizeof(abis_nm_ipa_magic)) == 0) {
|
||||
type = OML_MSG_TYPE_IPA;
|
||||
size = sizeof(abis_nm_ipa_magic) + 1;
|
||||
} else if (omh->data[0] == sizeof(abis_nm_osmo_magic) &&
|
||||
strncmp(abis_nm_osmo_magic, (const char *) omh->data + 1,
|
||||
sizeof(abis_nm_osmo_magic)) == 0) {
|
||||
type = OML_MSG_TYPE_OSMO;
|
||||
size = sizeof(abis_nm_osmo_magic) + 1;
|
||||
} else {
|
||||
LOGP(DL1C, LOGL_ERROR, "Manuf Label Unknown\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we have verified that the vendor string fits */
|
||||
msg->l3h = omh->data + size;
|
||||
if (check_fom(omh, msgb_l3len(msg)) != 0)
|
||||
return -1;
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return 0 in case the IPA structure is okay and in this
|
||||
* case the l2h will be set to the beginning of the data.
|
||||
*/
|
||||
int msg_verify_ipa_structure(struct msgb *msg)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
if (msgb_l1len(msg) < sizeof(struct ipaccess_head)) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Ipa header insufficient space %d %d\n",
|
||||
msgb_l1len(msg), sizeof(struct ipaccess_head));
|
||||
return -1;
|
||||
}
|
||||
|
||||
hh = (struct ipaccess_head *) msg->l1h;
|
||||
|
||||
if (hh->proto != IPAC_PROTO_OML) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Incorrect ipa header protocol 0x%x 0x%x\n",
|
||||
hh->proto, IPAC_PROTO_OML);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ntohs(hh->len) != msgb_l1len(msg) - sizeof(struct ipaccess_head)) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Incorrect ipa header msg size %d %d\n",
|
||||
ntohs(hh->len), msgb_l1len(msg) - sizeof(struct ipaccess_head));
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->l2h = hh->data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Verify the structure of the OML message and set l3h
|
||||
*
|
||||
* This function verifies that the data in \param in msg is a proper
|
||||
* OML message. This code assumes that msg->l2h points to the
|
||||
* beginning of the OML message. In the successful case the msg->l3h
|
||||
* will be set and will point to the FOM header. The value is undefined
|
||||
* in all other cases.
|
||||
*
|
||||
* \param msg The message to analyze starting from msg->l2h.
|
||||
* \return In case the structure is correct a positive number will be
|
||||
* returned and msg->l3h will point to the FOM. The number is a
|
||||
* classification of the vendor type of the message.
|
||||
*/
|
||||
int msg_verify_oml_structure(struct msgb *msg)
|
||||
{
|
||||
struct abis_om_hdr *omh;
|
||||
|
||||
if (msgb_l2len(msg) < sizeof(*omh)) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Om header insufficient space %d %d\n",
|
||||
msgb_l2len(msg), sizeof(*omh));
|
||||
return -1;
|
||||
}
|
||||
|
||||
omh = (struct abis_om_hdr *) msg->l2h;
|
||||
if (omh->mdisc != ABIS_OM_MDISC_FOM &&
|
||||
omh->mdisc != ABIS_OM_MDISC_MANUF) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Incorrect om mdisc value %x\n",
|
||||
omh->mdisc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (omh->placement != ABIS_OM_PLACEMENT_ONLY) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Incorrect om placement value %x %x\n",
|
||||
omh->placement, ABIS_OM_PLACEMENT_ONLY);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (omh->sequence != 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Incorrect om sequence value %d\n",
|
||||
omh->sequence);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (omh->mdisc == ABIS_OM_MDISC_MANUF)
|
||||
return check_manuf(msg, omh, msgb_l2len(msg) - sizeof(*omh));
|
||||
|
||||
msg->l3h = omh->data;
|
||||
if (check_fom(omh, msgb_l3len(msg)) != 0)
|
||||
return -1;
|
||||
return OML_MSG_TYPE_ETSI;
|
||||
}
|
||||
371
src/common/oml.c
371
src/common/oml.c
@@ -1,7 +1,7 @@
|
||||
/* GSM TS 12.21 O&M / OML, BTS side */
|
||||
|
||||
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -31,12 +31,16 @@
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/protocol/gsm_12_21.h>
|
||||
#include <osmocom/gsm/abis_nm.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/ipaccess.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/signal.h>
|
||||
|
||||
/* FIXME: move this to libosmocore */
|
||||
static struct tlv_definition abis_nm_att_tlvdef_ipa = {
|
||||
@@ -87,8 +91,7 @@ static struct tlv_definition abis_nm_att_tlvdef_ipa = {
|
||||
},
|
||||
};
|
||||
|
||||
/* ip.access nanoBTS specific commands */
|
||||
static const char ipaccess_magic[] = "com.ipaccess";
|
||||
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg);
|
||||
|
||||
/*
|
||||
* support
|
||||
@@ -160,9 +163,9 @@ int oml_send_msg(struct msgb *msg, int is_manuf)
|
||||
|
||||
if (is_manuf) {
|
||||
/* length byte, string + 0 termination */
|
||||
uint8_t *manuf = msgb_push(msg, 1 + sizeof(ipaccess_magic));
|
||||
manuf[0] = strlen(ipaccess_magic)+1;
|
||||
memcpy(manuf+1, ipaccess_magic, strlen(ipaccess_magic));
|
||||
uint8_t *manuf = msgb_push(msg, 1 + sizeof(abis_nm_ipa_magic));
|
||||
manuf[0] = strlen(abis_nm_ipa_magic)+1;
|
||||
memcpy(manuf+1, abis_nm_ipa_magic, strlen(abis_nm_ipa_magic));
|
||||
}
|
||||
|
||||
/* Push the main OML header and send it off */
|
||||
@@ -226,6 +229,13 @@ int oml_tx_state_changed(struct gsm_abis_mo *mo)
|
||||
return oml_mo_send_msg(mo, nmsg, NM_MT_STATECHG_EVENT_REP);
|
||||
}
|
||||
|
||||
/* First initialization of MO, does _not_ generate state changes */
|
||||
void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state)
|
||||
{
|
||||
mo->nm_state.availability = avail_state;
|
||||
mo->nm_state.operational = op_state;
|
||||
}
|
||||
|
||||
int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -245,6 +255,7 @@ int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
|
||||
abis_nm_opstate_name(mo->nm_state.operational),
|
||||
abis_nm_opstate_name(op_state));
|
||||
mo->nm_state.operational = op_state;
|
||||
osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, NULL);
|
||||
}
|
||||
|
||||
/* send state change report */
|
||||
@@ -273,6 +284,24 @@ int oml_mo_fom_ack_nack(struct gsm_abis_mo *mo, uint8_t orig_msg_type,
|
||||
return oml_mo_send_msg(mo, msg, new_msg_type);
|
||||
}
|
||||
|
||||
int oml_mo_statechg_ack(struct gsm_abis_mo *mo)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
msg = oml_msgb_alloc();
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msgb_tv_put(msg, NM_ATT_ADM_STATE, mo->nm_state.administrative);
|
||||
|
||||
return oml_mo_send_msg(mo, msg, NM_MT_CHG_ADM_STATE_ACK);
|
||||
}
|
||||
|
||||
int oml_mo_statechg_nack(struct gsm_abis_mo *mo, uint8_t nack_cause)
|
||||
{
|
||||
return oml_mo_fom_ack_nack(mo, NM_MT_CHG_ADM_STATE, nack_cause);
|
||||
}
|
||||
|
||||
int oml_mo_opstart_ack(struct gsm_abis_mo *mo)
|
||||
{
|
||||
return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0);
|
||||
@@ -287,10 +316,14 @@ int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause)
|
||||
{
|
||||
struct abis_om_hdr *old_oh = msgb_l2(old_msg);
|
||||
struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg);
|
||||
struct msgb *msg = oml_msgb_alloc();
|
||||
struct msgb *msg;
|
||||
struct abis_om_fom_hdr *foh;
|
||||
int is_manuf = 0;
|
||||
|
||||
msg = oml_msgb_alloc();
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* make sure to respond with MANUF if request was MANUF */
|
||||
if (old_oh->mdisc == ABIS_OM_MDISC_MANUF)
|
||||
is_manuf = 1;
|
||||
@@ -325,7 +358,6 @@ int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause)
|
||||
int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo)
|
||||
{
|
||||
struct msgb *nmsg;
|
||||
struct abis_om_fom_hdr *nofh;
|
||||
|
||||
LOGP(DOML, LOGL_INFO, "%s Tx SW ACT REP\n", gsm_abis_mo_name(mo));
|
||||
|
||||
@@ -333,8 +365,7 @@ int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo)
|
||||
if (!nmsg)
|
||||
return -ENOMEM;
|
||||
|
||||
nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh));
|
||||
|
||||
msgb_put(nmsg, sizeof(struct abis_om_fom_hdr));
|
||||
return oml_mo_send_msg(mo, nmsg, NM_MT_SW_ACTIVATED_REP);
|
||||
}
|
||||
|
||||
@@ -378,8 +409,13 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
|
||||
|
||||
/* Test for globally unsupported stuff here */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
|
||||
const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
|
||||
uint16_t arfcn = ntohs(*value);
|
||||
const uint16_t *value = (const uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
|
||||
uint16_t arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
|
||||
|
||||
LOGP(DOML, LOGL_NOTICE, "MSG: %s\n", osmo_hexdump(msgb_l3(msg), msgb_l3len(msg)));
|
||||
LOGP(DOML, LOGL_NOTICE, "L3=%p, VAL=%p, DIF=%tu\n", msgb_l3(msg), value,
|
||||
(void *)value - (void *) msgb_l3(msg));
|
||||
|
||||
if (arfcn > 1024) {
|
||||
LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
|
||||
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
|
||||
@@ -398,8 +434,7 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
|
||||
rc = bts_model_check_oml(bts, foh->msg_type, bts->mo.nm_attr, tp_merged, bts);
|
||||
if (rc < 0) {
|
||||
talloc_free(tp_merged);
|
||||
/* FIXME: send nack? */
|
||||
return rc;
|
||||
return oml_fom_ack_nack(msg, -rc);
|
||||
}
|
||||
|
||||
/* Success: replace old BTS attributes with new */
|
||||
@@ -414,13 +449,27 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
|
||||
for (i = 0; i < 6; i++) {
|
||||
int16_t boundary = *payload;
|
||||
btsb->interference.boundary[i] = -1 * boundary;
|
||||
}
|
||||
}
|
||||
/* 9.4.24 Intave Parameter */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_INTAVE_PARAM))
|
||||
btsb->interference.intave = *TLVP_VAL(&tp, NM_ATT_INTAVE_PARAM);
|
||||
|
||||
/* 9.4.14 Connection Failure Criterion */
|
||||
/* ... can be 'operator dependent' and needs to be parsed by bts driver */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_CONN_FAIL_CRIT)) {
|
||||
const uint8_t *val = TLVP_VAL(&tp, NM_ATT_CONN_FAIL_CRIT);
|
||||
|
||||
if (TLVP_LEN(&tp, NM_ATT_CONN_FAIL_CRIT) < 2
|
||||
|| val[0] != 0x01 || val[1] < 4 || val[1] > 64) {
|
||||
LOGP(DOML, LOGL_NOTICE, "Given Conn. Failure Criterion "
|
||||
"not supported. Please use critetion 0x01 with "
|
||||
"RADIO_LINK_TIMEOUT value of 4..64\n");
|
||||
return oml_fom_ack_nack(msg, NM_NACK_PARAM_RANGE);
|
||||
}
|
||||
btsb->radio_link_timeout = val[1];
|
||||
}
|
||||
/* if val[0] != 0x01: can be 'operator dependent' and needs to
|
||||
* be parsed by bts driver */
|
||||
|
||||
/* 9.4.53 T200 */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_T200)) {
|
||||
@@ -430,10 +479,8 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
|
||||
}
|
||||
|
||||
/* 9.4.31 Maximum Timing Advance */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) {
|
||||
uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_MAX_TA);
|
||||
btsb->max_ta = ntohs(*fn);
|
||||
}
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA))
|
||||
btsb->max_ta = *TLVP_VAL(&tp, NM_ATT_MAX_TA);
|
||||
|
||||
/* 9.4.39 Overload Period */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD))
|
||||
@@ -454,30 +501,36 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
|
||||
}
|
||||
|
||||
/* 9.4.45 RACH Load Averaging Slots */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS))
|
||||
payload = TLVP_VAL(&tp, NM_ATT_LDAVG_SLOTS);
|
||||
btsb->load.rach.averaging_slots = ntohs(*(uint16_t *)payload);
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS)) {
|
||||
btsb->load.rach.averaging_slots =
|
||||
ntohs(tlvp_val16_unal(&tp, NM_ATT_LDAVG_SLOTS));
|
||||
}
|
||||
|
||||
/* 9.4.10 BTS Air Timer */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BTS_AIR_TIMER))
|
||||
btsb->t3105_ms = *TLVP_VAL(&tp, NM_ATT_BTS_AIR_TIMER) * 10;
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BTS_AIR_TIMER)) {
|
||||
uint8_t t3105 = *TLVP_VAL(&tp, NM_ATT_BTS_AIR_TIMER);
|
||||
if (t3105 == 0) {
|
||||
LOGP(DOML, LOGL_NOTICE,
|
||||
"T3105 must have a value != 0.\n");
|
||||
return oml_fom_ack_nack(msg, NM_NACK_PARAM_RANGE);
|
||||
}
|
||||
btsb->t3105_ms = t3105 * 10;
|
||||
}
|
||||
|
||||
/* 9.4.37 NY1 */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NY1))
|
||||
btsb->ny1 = *TLVP_VAL(&tp, NM_ATT_NY1);
|
||||
|
||||
/* 9.4.8 BCCH ARFCN */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
|
||||
const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
|
||||
bts->c0->arfcn = ntohs(*value);
|
||||
}
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN))
|
||||
bts->c0->arfcn = ntohs(tlvp_val16_unal(&tp, NM_ATT_BCCH_ARFCN));
|
||||
|
||||
/* 9.4.9 BSIC */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BSIC))
|
||||
bts->bsic = *TLVP_VAL(&tp, NM_ATT_BSIC);
|
||||
|
||||
/* call into BTS driver to apply new attributes to hardware */
|
||||
return bts_model_apply_oml(bts, msg, tp_merged, bts);
|
||||
return bts_model_apply_oml(bts, msg, tp_merged, NM_OC_BTS, bts);
|
||||
}
|
||||
|
||||
/* 8.6.2 Set Radio Attributes has been received */
|
||||
@@ -502,8 +555,7 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
rc = bts_model_check_oml(trx->bts, foh->msg_type, trx->mo.nm_attr, tp_merged, trx);
|
||||
if (rc < 0) {
|
||||
talloc_free(tp_merged);
|
||||
/* FIXME: send NACK */
|
||||
return rc;
|
||||
return oml_fom_ack_nack(msg, -rc);
|
||||
}
|
||||
|
||||
/* Success: replace old BTS attributes with new */
|
||||
@@ -514,18 +566,22 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
|
||||
/* 9.4.47 RF Max Power Reduction */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
|
||||
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R);
|
||||
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d\n", trx->max_power_red);
|
||||
trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R) * 2;
|
||||
LOGP(DOML, LOGL_INFO, "Set RF Max Power Reduction = %d dBm\n",
|
||||
trx->max_power_red);
|
||||
}
|
||||
/* 9.4.5 ARFCN List */
|
||||
#if 0
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
|
||||
uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
|
||||
uint8_t *value = TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
|
||||
uint16_t _value;
|
||||
uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST);
|
||||
uint16_t arfcn;
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
arfcn = ntohs(*value++);
|
||||
memcpy(&_value, value, 2);
|
||||
arfcn = ntohs(_value);
|
||||
value += 2;
|
||||
if (arfcn > 1024)
|
||||
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
|
||||
trx->arfcn_list[i] = arfcn;
|
||||
@@ -536,7 +592,7 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
trx->arfcn_num = 0;
|
||||
#endif
|
||||
/* call into BTS driver to apply new attributes to hardware */
|
||||
return bts_model_apply_oml(trx->bts, msg, tp_merged, trx);
|
||||
return bts_model_apply_oml(trx->bts, msg, tp_merged, NM_OC_RADIO_CARRIER, trx);
|
||||
}
|
||||
|
||||
static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
|
||||
@@ -545,10 +601,17 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
|
||||
unsigned int i;
|
||||
|
||||
switch (ts->pchan) {
|
||||
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
|
||||
/* fallthrough */
|
||||
case GSM_PCHAN_CCCH_SDCCH4:
|
||||
for (i = 0; i < 4; i++) {
|
||||
lchan = &ts->lchan[i];
|
||||
lchan->type = GSM_LCHAN_SDCCH;
|
||||
if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH
|
||||
&& i == 2) {
|
||||
lchan->type = GSM_LCHAN_CBCH;
|
||||
} else {
|
||||
lchan->type = GSM_LCHAN_SDCCH;
|
||||
}
|
||||
}
|
||||
/* fallthrough */
|
||||
case GSM_PCHAN_CCCH:
|
||||
@@ -565,12 +628,23 @@ static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts)
|
||||
lchan->type = GSM_LCHAN_TCH_H;
|
||||
}
|
||||
break;
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
|
||||
/* fallthrough */
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C:
|
||||
for (i = 0; i < 8; i++) {
|
||||
lchan = &ts->lchan[i];
|
||||
lchan->type = GSM_LCHAN_SDCCH;
|
||||
if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH
|
||||
&& i == 2) {
|
||||
lchan->type = GSM_LCHAN_CBCH;
|
||||
} else {
|
||||
lchan->type = GSM_LCHAN_SDCCH;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GSM_PCHAN_PDCH:
|
||||
lchan = &ts->lchan[0];
|
||||
lchan->type = GSM_LCHAN_PDTCH;
|
||||
break;
|
||||
default:
|
||||
/* FIXME */
|
||||
break;
|
||||
@@ -607,17 +681,21 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
|
||||
}
|
||||
|
||||
/* merge existing BTS attributes with new attributes */
|
||||
tp_merged = tlvp_copy(bts->mo.nm_attr, bts);
|
||||
tp_merged = tlvp_copy(ts->mo.nm_attr, bts);
|
||||
tlvp_merge(tp_merged, &tp);
|
||||
|
||||
/* Call into BTS driver to check attribute values */
|
||||
rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts);
|
||||
if (rc < 0) {
|
||||
talloc_free(&tp_merged);
|
||||
/* FIXME: Send NACK */
|
||||
return rc;
|
||||
talloc_free(tp_merged);
|
||||
/* Send NACK */
|
||||
return oml_fom_ack_nack(msg, -rc);
|
||||
}
|
||||
|
||||
/* Success: replace old BTS attributes with new */
|
||||
talloc_free(ts->mo.nm_attr);
|
||||
ts->mo.nm_attr = tp_merged;
|
||||
|
||||
/* 9.4.13 Channel Combination */
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
|
||||
uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
|
||||
@@ -638,7 +716,7 @@ static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, struct msgb *msg)
|
||||
gsm_abis_mo_name(&ts->mo), ts->tsc);
|
||||
|
||||
/* call into BTS driver to apply new attributes to hardware */
|
||||
return bts_model_apply_oml(bts, msg, tp_merged, ts);
|
||||
return bts_model_apply_oml(bts, msg, tp_merged, NM_OC_CHANNEL, ts);
|
||||
}
|
||||
|
||||
/* 8.9.2 Opstart has been received */
|
||||
@@ -699,11 +777,10 @@ static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg)
|
||||
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
|
||||
|
||||
/* Step 2: Do some global dependency/consistency checking */
|
||||
if (mo->nm_state.administrative == adm_state) {
|
||||
DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n",
|
||||
get_value_string(abis_nm_adm_state_names, adm_state));
|
||||
return oml_fom_ack_nack(msg, 0);
|
||||
}
|
||||
if (mo->nm_state.administrative == adm_state)
|
||||
LOGP(DOML, LOGL_NOTICE,
|
||||
"ADM state already was %s\n",
|
||||
get_value_string(abis_nm_adm_state_names, adm_state));
|
||||
|
||||
/* Step 3: Ask BTS driver to apply the state chg */
|
||||
return bts_model_chg_adm_state(bts, mo, obj, adm_state);
|
||||
@@ -749,6 +826,9 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
|
||||
case NM_MT_CHG_ADM_STATE:
|
||||
ret = oml_rx_chg_adm_state(bts, msg);
|
||||
break;
|
||||
case NM_MT_IPACC_SET_ATTR:
|
||||
ret = oml_ipa_set_attr(bts, msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
|
||||
foh->msg_type);
|
||||
@@ -762,25 +842,194 @@ static int down_fom(struct gsm_bts *bts, struct msgb *msg)
|
||||
* manufacturer related messages
|
||||
*/
|
||||
|
||||
#ifndef TLVP_PRES_LEN /* old libosmocore */
|
||||
#define TLVP_PRES_LEN(tp, tag, min_len) \
|
||||
(TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)
|
||||
#endif
|
||||
|
||||
static int oml_ipa_mo_set_attr_nse(void *obj, struct tlv_parsed *tp)
|
||||
{
|
||||
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.nse);
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSEI, 2))
|
||||
bts->gprs.nse.nsei =
|
||||
ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_NSEI));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_CFG, 7)) {
|
||||
memcpy(&bts->gprs.nse.timer,
|
||||
TLVP_VAL(tp, NM_ATT_IPACC_NS_CFG), 7);
|
||||
}
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BSSGP_CFG, 11)) {
|
||||
memcpy(&bts->gprs.cell.timer,
|
||||
TLVP_VAL(tp, NM_ATT_IPACC_BSSGP_CFG), 11);
|
||||
}
|
||||
|
||||
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSE_ATTR, bts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oml_ipa_mo_set_attr_cell(void *obj, struct tlv_parsed *tp)
|
||||
{
|
||||
struct gsm_bts *bts = container_of(obj, struct gsm_bts, gprs.cell);
|
||||
struct gprs_rlc_cfg *rlcc = &bts->gprs.cell.rlc_cfg;
|
||||
const uint8_t *cur;
|
||||
uint16_t _cur_s;
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RAC, 1))
|
||||
bts->gprs.rac = *TLVP_VAL(tp, NM_ATT_IPACC_RAC);
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_GPRS_PAGING_CFG, 2)) {
|
||||
cur = TLVP_VAL(tp, NM_ATT_IPACC_GPRS_PAGING_CFG);
|
||||
rlcc->paging.repeat_time = cur[0] * 50;
|
||||
rlcc->paging.repeat_count = cur[1];
|
||||
}
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_BVCI, 2))
|
||||
bts->gprs.cell.bvci =
|
||||
htons(tlvp_val16_unal(tp, NM_ATT_IPACC_BVCI));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG, 9)) {
|
||||
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG);
|
||||
rlcc->parameter[RLC_T3142] = cur[0];
|
||||
rlcc->parameter[RLC_T3169] = cur[1];
|
||||
rlcc->parameter[RLC_T3191] = cur[2];
|
||||
rlcc->parameter[RLC_T3193] = cur[3];
|
||||
rlcc->parameter[RLC_T3195] = cur[4];
|
||||
rlcc->parameter[RLC_N3101] = cur[5];
|
||||
rlcc->parameter[RLC_N3103] = cur[6];
|
||||
rlcc->parameter[RLC_N3105] = cur[7];
|
||||
rlcc->parameter[CV_COUNTDOWN] = cur[8];
|
||||
}
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_CODING_SCHEMES, 2)) {
|
||||
int i;
|
||||
rlcc->cs_mask = 0;
|
||||
cur = TLVP_VAL(tp, NM_ATT_IPACC_CODING_SCHEMES);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (cur[0] & (1 << i))
|
||||
rlcc->cs_mask |= (1 << (GPRS_CS1+i));
|
||||
}
|
||||
if (cur[0] & 0x80)
|
||||
rlcc->cs_mask |= (1 << GPRS_MCS9);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (cur[1] & (1 << i))
|
||||
rlcc->cs_mask |= (1 << (GPRS_MCS1+i));
|
||||
}
|
||||
}
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_2, 5)) {
|
||||
cur = TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_2);
|
||||
memcpy(&_cur_s, cur, 2);
|
||||
rlcc->parameter[T_DL_TBF_EXT] = ntohs(_cur_s) * 10;
|
||||
cur += 2;
|
||||
memcpy(&_cur_s, cur, 2);
|
||||
rlcc->parameter[T_UL_TBF_EXT] = ntohs(_cur_s) * 10;
|
||||
cur += 2;
|
||||
rlcc->initial_cs = *cur;
|
||||
}
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_RLC_CFG_3, 1)) {
|
||||
rlcc->initial_mcs = *TLVP_VAL(tp, NM_ATT_IPACC_RLC_CFG_3);
|
||||
}
|
||||
|
||||
osmo_signal_dispatch(SS_GLOBAL, S_NEW_CELL_ATTR, bts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oml_ipa_mo_set_attr_nsvc(struct gsm_bts_gprs_nsvc *nsvc,
|
||||
struct tlv_parsed *tp)
|
||||
{
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NSVCI, 2))
|
||||
nsvc->nsvci = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_NSVCI));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, NM_ATT_IPACC_NS_LINK_CFG, 8)) {
|
||||
const uint8_t *cur = TLVP_VAL(tp, NM_ATT_IPACC_NS_LINK_CFG);
|
||||
uint16_t _cur_s;
|
||||
uint32_t _cur_l;
|
||||
|
||||
memcpy(&_cur_s, cur, 2);
|
||||
nsvc->remote_port = ntohs(_cur_s);
|
||||
cur += 2;
|
||||
memcpy(&_cur_l, cur, 4);
|
||||
nsvc->remote_ip = ntohl(_cur_l);
|
||||
cur += 4;
|
||||
memcpy(&_cur_s, cur, 2);
|
||||
nsvc->local_port = ntohs(_cur_s);
|
||||
}
|
||||
|
||||
osmo_signal_dispatch(SS_GLOBAL, S_NEW_NSVC_ATTR, nsvc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oml_ipa_mo_set_attr(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
void *obj, struct tlv_parsed *tp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (mo->obj_class) {
|
||||
case NM_OC_GPRS_NSE:
|
||||
rc = oml_ipa_mo_set_attr_nse(obj, tp);
|
||||
break;
|
||||
case NM_OC_GPRS_CELL:
|
||||
rc = oml_ipa_mo_set_attr_cell(obj, tp);
|
||||
break;
|
||||
case NM_OC_GPRS_NSVC:
|
||||
rc = oml_ipa_mo_set_attr_nsvc(obj, tp);
|
||||
break;
|
||||
default:
|
||||
rc = NM_NACK_OBJINST_UNKN;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int oml_ipa_set_attr(struct gsm_bts *bts, struct msgb *msg)
|
||||
{
|
||||
struct abis_om_fom_hdr *foh = msgb_l3(msg);
|
||||
struct gsm_abis_mo *mo;
|
||||
struct tlv_parsed tp;
|
||||
void *obj;
|
||||
int rc;
|
||||
|
||||
abis_nm_debugp_foh(DOML, foh);
|
||||
DEBUGPC(DOML, "Rx IPA SET ATTR\n");
|
||||
|
||||
rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
|
||||
if (rc < 0)
|
||||
return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
|
||||
|
||||
/* Resolve MO by obj_class/obj_inst */
|
||||
mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
|
||||
obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst);
|
||||
if (!mo || !obj)
|
||||
return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN);
|
||||
|
||||
rc = oml_ipa_mo_set_attr(bts, mo, obj, &tp);
|
||||
|
||||
return oml_fom_ack_nack(msg, rc);
|
||||
}
|
||||
|
||||
static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
|
||||
struct tlv_parsed *tp)
|
||||
{
|
||||
struct ipabis_link *oml_link = (struct ipabis_link *) trx->bts->oml_link;
|
||||
struct e1inp_sign_link *oml_link = trx->bts->oml_link;
|
||||
uint16_t port = IPA_TCP_PORT_RSL;
|
||||
uint32_t ip = oml_link->ip;
|
||||
uint32_t ip = get_signlink_remote_ip(oml_link);
|
||||
struct in_addr in;
|
||||
int rc;
|
||||
|
||||
uint8_t stream_id = 0;
|
||||
|
||||
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP)) {
|
||||
const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP);
|
||||
ip = ntohl(*(uint32_t *)ptr);
|
||||
ip = ntohl(tlvp_val32_unal(tp, NM_ATT_IPACC_DST_IP));
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP_PORT)) {
|
||||
const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP_PORT);
|
||||
port = ntohs(*(uint16_t *)ptr);
|
||||
port = ntohs(tlvp_val16_unal(tp, NM_ATT_IPACC_DST_IP_PORT));
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_IPACC_STREAM_ID)) {
|
||||
stream_id = *TLVP_VAL(tp, NM_ATT_IPACC_STREAM_ID);
|
||||
@@ -790,14 +1039,7 @@ static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg,
|
||||
LOGP(DOML, LOGL_INFO, "Rx IPA RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n",
|
||||
inet_ntoa(in), port, stream_id);
|
||||
|
||||
if (!trx->rsl_link) {
|
||||
struct ipabis_link *rsl_link = talloc_zero(trx, struct ipabis_link);
|
||||
rsl_link->trx = trx;
|
||||
trx->rsl_link = rsl_link;
|
||||
}
|
||||
|
||||
/* FIXME: we cannot even use a non-standard port here */
|
||||
rc = abis_open(trx->rsl_link, ip);
|
||||
rc = e1inp_ipa_bts_rsl_connect(oml_link->ts->line, inet_ntoa(in), port);
|
||||
if (rc < 0) {
|
||||
LOGP(DOML, LOGL_ERROR, "Error in abis_open(RSL): %d\n", rc);
|
||||
return oml_fom_ack_nack(msg, NM_NACK_CANT_PERFORM);
|
||||
@@ -820,7 +1062,7 @@ static int down_mom(struct gsm_bts *bts, struct msgb *msg)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) {
|
||||
if (strncmp((char *)&oh->data[1], abis_nm_ipa_magic, idstrlen)) {
|
||||
LOGP(DOML, LOGL_ERROR, "Manufacturer OML message != ipaccess not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -847,6 +1089,9 @@ static int down_mom(struct gsm_bts *bts, struct msgb *msg)
|
||||
trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
|
||||
ret = rx_oml_ipa_rsl_connect(trx, msg, &tp);
|
||||
break;
|
||||
case NM_MT_IPACC_SET_ATTR:
|
||||
ret = oml_ipa_set_attr(bts, msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
|
||||
foh->msg_type);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Paging message encoding + queue management */
|
||||
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -41,18 +41,37 @@
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/paging.h>
|
||||
#include <osmo-bts/signal.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
|
||||
#define MAX_PAGING_BLOCKS_CCCH 9
|
||||
#define MAX_BS_PA_MFRMS 9
|
||||
|
||||
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
|
||||
|
||||
|
||||
enum paging_record_type {
|
||||
PAGING_RECORD_PAGING,
|
||||
PAGING_RECORD_IMM_ASS
|
||||
};
|
||||
|
||||
struct paging_record {
|
||||
struct llist_head list;
|
||||
time_t expiration_time;
|
||||
uint8_t chan_needed;
|
||||
uint8_t identity_lv[9];
|
||||
enum paging_record_type type;
|
||||
union {
|
||||
struct {
|
||||
time_t expiration_time;
|
||||
uint8_t chan_needed;
|
||||
uint8_t identity_lv[9];
|
||||
} paging;
|
||||
struct {
|
||||
uint8_t msg[GSM_MACBLOCK_LEN];
|
||||
} imm_ass;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct paging_state {
|
||||
struct gsm_bts_role_bts *btsb;
|
||||
|
||||
/* parameters taken / interpreted from BCCH/CCCH configuration */
|
||||
struct gsm48_control_channel_descr chan_desc;
|
||||
|
||||
@@ -65,6 +84,26 @@ struct paging_state {
|
||||
struct llist_head paging_queue[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
|
||||
};
|
||||
|
||||
unsigned int paging_get_lifetime(struct paging_state *ps)
|
||||
{
|
||||
return ps->paging_lifetime;
|
||||
}
|
||||
|
||||
unsigned int paging_get_queue_max(struct paging_state *ps)
|
||||
{
|
||||
return ps->num_paging_max;
|
||||
}
|
||||
|
||||
void paging_set_lifetime(struct paging_state *ps, unsigned int lifetime)
|
||||
{
|
||||
ps->paging_lifetime = lifetime;
|
||||
}
|
||||
|
||||
void paging_set_queue_max(struct paging_state *ps, unsigned int queue_max)
|
||||
{
|
||||
ps->num_paging_max = queue_max;
|
||||
}
|
||||
|
||||
static int tmsi_mi_to_uint(uint32_t *out, const uint8_t *tmsi_lv)
|
||||
{
|
||||
if (tmsi_lv[0] < 5)
|
||||
@@ -128,6 +167,13 @@ static int get_pag_subch_nr(struct paging_state *ps, struct gsm_time *gt)
|
||||
return pag_idx + mfrm_part;
|
||||
}
|
||||
|
||||
int paging_buffer_space(struct paging_state *ps)
|
||||
{
|
||||
if (ps->num_paging >= ps->num_paging_max)
|
||||
return 0;
|
||||
else
|
||||
return ps->num_paging_max - ps->num_paging;
|
||||
}
|
||||
|
||||
/* Add an identity to the paging queue */
|
||||
int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||
@@ -144,10 +190,14 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||
|
||||
/* Check if we already have this identity */
|
||||
llist_for_each_entry(pr, group_q, list) {
|
||||
if (identity_lv[0] == pr->identity_lv[0] &&
|
||||
!memcmp(identity_lv+1, pr->identity_lv+1, identity_lv[0])) {
|
||||
if (pr->type != PAGING_RECORD_PAGING)
|
||||
continue;
|
||||
if (identity_lv[0] == pr->u.paging.identity_lv[0] &&
|
||||
!memcmp(identity_lv+1, pr->u.paging.identity_lv+1,
|
||||
identity_lv[0])) {
|
||||
LOGP(DPAG, LOGL_INFO, "Ignoring duplicate paging\n");
|
||||
pr->expiration_time = time(NULL) + ps->paging_lifetime;
|
||||
pr->u.paging.expiration_time =
|
||||
time(NULL) + ps->paging_lifetime;
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
@@ -155,8 +205,9 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||
pr = talloc_zero(ps, struct paging_record);
|
||||
if (!pr)
|
||||
return -ENOMEM;
|
||||
pr->type = PAGING_RECORD_PAGING;
|
||||
|
||||
if (*identity_lv + 1 > sizeof(pr->identity_lv)) {
|
||||
if (*identity_lv + 1 > sizeof(pr->u.paging.identity_lv)) {
|
||||
talloc_free(pr);
|
||||
return -E2BIG;
|
||||
}
|
||||
@@ -164,9 +215,9 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||
LOGP(DPAG, LOGL_INFO, "Add paging to queue (group=%u, queue_len=%u)\n",
|
||||
paging_group, ps->num_paging+1);
|
||||
|
||||
pr->expiration_time = time(NULL) + ps->paging_lifetime;
|
||||
pr->chan_needed = chan_needed;
|
||||
memcpy(&pr->identity_lv, identity_lv, identity_lv[0]+1);
|
||||
pr->u.paging.expiration_time = time(NULL) + ps->paging_lifetime;
|
||||
pr->u.paging.chan_needed = chan_needed;
|
||||
memcpy(&pr->u.paging.identity_lv, identity_lv, identity_lv[0]+1);
|
||||
|
||||
/* enqueue the new identity to the HEAD of the queue,
|
||||
* to ensure it will be paged quickly at least once. */
|
||||
@@ -176,9 +227,52 @@ int paging_add_identity(struct paging_state *ps, uint8_t paging_group,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add an IMM.ASS message to the paging queue */
|
||||
int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
|
||||
uint8_t len)
|
||||
{
|
||||
struct llist_head *group_q;
|
||||
struct paging_record *pr;
|
||||
uint16_t imsi, paging_group;
|
||||
|
||||
if (len != GSM_MACBLOCK_LEN + 3) {
|
||||
LOGP(DPAG, LOGL_ERROR, "IMM.ASS invalid length %d\n", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
len -= 3;
|
||||
|
||||
imsi = 100 * ((*(data++)) - '0');
|
||||
imsi += 10 * ((*(data++)) - '0');
|
||||
imsi += (*(data++)) - '0';
|
||||
paging_group = gsm0502_calc_paging_group(&ps->chan_desc, imsi);
|
||||
|
||||
group_q = &ps->paging_queue[paging_group];
|
||||
|
||||
pr = talloc_zero(ps, struct paging_record);
|
||||
if (!pr)
|
||||
return -ENOMEM;
|
||||
pr->type = PAGING_RECORD_IMM_ASS;
|
||||
|
||||
LOGP(DPAG, LOGL_INFO, "Add IMM.ASS to queue (group=%u)\n",
|
||||
paging_group);
|
||||
memcpy(pr->u.imm_ass.msg, data, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* enqueue the new message to the HEAD of the queue */
|
||||
llist_add(&pr->list, group_q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
|
||||
|
||||
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
static int current_segment[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
|
||||
uint8_t etws_segment_data[5][17];
|
||||
uint8_t etws_segment_len[5];
|
||||
uint8_t etws_nr_seg;
|
||||
uint8_t etws_data[60];
|
||||
size_t etws_len;
|
||||
|
||||
static int fill_paging_type_1(int group, uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
uint8_t chan1, const uint8_t *identity2_lv,
|
||||
uint8_t chan2)
|
||||
{
|
||||
@@ -195,7 +289,20 @@ static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1);
|
||||
if (identity2_lv)
|
||||
cur = lv_put(cur, identity2_lv[0], identity2_lv+1);
|
||||
else if (identity1_lv == empty_id_lv && etws_nr_seg > 0) {
|
||||
int cur_segment = current_segment[group];
|
||||
current_segment[group] += 1;
|
||||
current_segment[group] %= etws_nr_seg;
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE, "paging group(%d) segment(%d)\n",
|
||||
group, cur_segment);
|
||||
|
||||
/* move the pointer */
|
||||
memcpy(cur, etws_segment_data[cur_segment], etws_segment_len[cur_segment]);
|
||||
cur += etws_segment_len[cur_segment];
|
||||
}
|
||||
|
||||
/* do we need to include it */
|
||||
pt1->l2_plen = L2_PLEN(cur - out_buf);
|
||||
|
||||
return cur - out_buf;
|
||||
@@ -252,8 +359,6 @@ static int fill_paging_type_3(uint8_t *out_buf, const uint8_t *tmsi1_lv,
|
||||
return cur - out_buf;
|
||||
}
|
||||
|
||||
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
|
||||
|
||||
static struct paging_record *dequeue_pr(struct llist_head *group_q)
|
||||
{
|
||||
struct paging_record *pr;
|
||||
@@ -266,7 +371,7 @@ static struct paging_record *dequeue_pr(struct llist_head *group_q)
|
||||
|
||||
static int pr_is_imsi(struct paging_record *pr)
|
||||
{
|
||||
if ((pr->identity_lv[1] & 7) == GSM_MI_TYPE_IMSI)
|
||||
if ((pr->u.paging.identity_lv[1] & 7) == GSM_MI_TYPE_IMSI)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
@@ -293,28 +398,52 @@ static void sort_pr_tmsi_imsi(struct paging_record *pr[], unsigned int n)
|
||||
}
|
||||
|
||||
/* generate paging message for given gsm time */
|
||||
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt)
|
||||
int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *gt,
|
||||
int *is_empty)
|
||||
{
|
||||
unsigned int group = get_pag_subch_nr(ps, gt);
|
||||
struct llist_head *group_q = &ps->paging_queue[group];
|
||||
struct llist_head *group_q;
|
||||
int group;
|
||||
int len;
|
||||
|
||||
*is_empty = 0;
|
||||
ps->btsb->load.ccch.pch_total += 1;
|
||||
|
||||
group = get_pag_subch_nr(ps, gt);
|
||||
if (group < 0) {
|
||||
LOGP(DPAG, LOGL_ERROR,
|
||||
"Paging called for GSM wrong time: FN %d/%d/%d/%d.\n",
|
||||
gt->fn, gt->t1, gt->t2, gt->t3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
group_q = &ps->paging_queue[group];
|
||||
|
||||
/* There is nobody to be paged, send Type1 with two empty ID */
|
||||
if (llist_empty(group_q)) {
|
||||
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
|
||||
len = fill_paging_type_1(out_buf, empty_id_lv, 0,
|
||||
len = fill_paging_type_1(group, out_buf, empty_id_lv, 0,
|
||||
NULL, 0);
|
||||
*is_empty = 1;
|
||||
} else {
|
||||
struct paging_record *pr[4];
|
||||
unsigned int num_pr = 0;
|
||||
unsigned int num_pr = 0, imm_ass = 0;
|
||||
time_t now = time(NULL);
|
||||
unsigned int i, num_imsi = 0;
|
||||
|
||||
ps->btsb->load.ccch.pch_used += 1;
|
||||
|
||||
/* get (if we have) up to four paging records */
|
||||
for (i = 0; i < ARRAY_SIZE(pr); i++) {
|
||||
if (llist_empty(group_q))
|
||||
break;
|
||||
pr[i] = dequeue_pr(group_q);
|
||||
|
||||
/* check for IMM.ASS */
|
||||
if (pr[i]->type == PAGING_RECORD_IMM_ASS) {
|
||||
imm_ass = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
num_pr++;
|
||||
|
||||
/* count how many IMSIs are among them */
|
||||
@@ -322,27 +451,43 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
num_imsi++;
|
||||
}
|
||||
|
||||
/* if we have an IMMEDIATE ASSIGNMENT */
|
||||
if (imm_ass) {
|
||||
/* re-add paging records */
|
||||
for (i = 0; i < num_pr; i++)
|
||||
llist_add(&pr[i]->list, group_q);
|
||||
|
||||
/* get message and free record */
|
||||
memcpy(out_buf, pr[num_pr]->u.imm_ass.msg,
|
||||
GSM_MACBLOCK_LEN);
|
||||
pcu_tx_pch_data_cnf(gt->fn, pr[num_pr]->u.imm_ass.msg,
|
||||
GSM_MACBLOCK_LEN);
|
||||
talloc_free(pr[num_pr]);
|
||||
return GSM_MACBLOCK_LEN;
|
||||
}
|
||||
|
||||
/* make sure the TMSIs are ahead of the IMSIs in the array */
|
||||
sort_pr_tmsi_imsi(pr, num_pr);
|
||||
|
||||
if (num_pr == 4 && num_imsi == 0) {
|
||||
/* No IMSI: easy case, can use TYPE 3 */
|
||||
DEBUGP(DPAG, "Tx PAGING TYPE 3 (4 TMSI)\n");
|
||||
len = fill_paging_type_3(out_buf, pr[0]->identity_lv,
|
||||
pr[0]->chan_needed,
|
||||
pr[1]->identity_lv,
|
||||
pr[1]->chan_needed,
|
||||
pr[2]->identity_lv,
|
||||
pr[3]->identity_lv);
|
||||
len = fill_paging_type_3(out_buf,
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
pr[1]->u.paging.identity_lv,
|
||||
pr[1]->u.paging.chan_needed,
|
||||
pr[2]->u.paging.identity_lv,
|
||||
pr[3]->u.paging.identity_lv);
|
||||
} else if (num_pr >= 3 && num_imsi <= 1) {
|
||||
/* 3 or 4, of which only up to 1 is IMSI */
|
||||
DEBUGP(DPAG, "Tx PAGING TYPE 2 (2 TMSI,1 xMSI)\n");
|
||||
len = fill_paging_type_2(out_buf,
|
||||
pr[0]->identity_lv,
|
||||
pr[0]->chan_needed,
|
||||
pr[1]->identity_lv,
|
||||
pr[1]->chan_needed,
|
||||
pr[2]->identity_lv);
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
pr[1]->u.paging.identity_lv,
|
||||
pr[1]->u.paging.chan_needed,
|
||||
pr[2]->u.paging.identity_lv);
|
||||
if (num_pr == 4) {
|
||||
/* re-add #4 for next time */
|
||||
llist_add(&pr[3]->list, group_q);
|
||||
@@ -350,16 +495,19 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
}
|
||||
} else if (num_pr == 1) {
|
||||
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
|
||||
len = fill_paging_type_1(out_buf, pr[0]->identity_lv,
|
||||
pr[0]->chan_needed, NULL, 0);
|
||||
len = fill_paging_type_1(group, out_buf,
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
NULL, 0);
|
||||
} else {
|
||||
/* 2 (any type) or
|
||||
* 3 or 4, of which only 2 will be sent */
|
||||
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
|
||||
len = fill_paging_type_1(out_buf, pr[0]->identity_lv,
|
||||
pr[0]->chan_needed,
|
||||
pr[1]->identity_lv,
|
||||
pr[1]->chan_needed);
|
||||
len = fill_paging_type_1(group, out_buf,
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
pr[1]->u.paging.identity_lv,
|
||||
pr[1]->u.paging.chan_needed);
|
||||
if (num_pr >= 3) {
|
||||
/* re-add #4 for next time */
|
||||
llist_add(&pr[2]->list, group_q);
|
||||
@@ -378,7 +526,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
continue;
|
||||
/* check if we can expire the paging record,
|
||||
* or if we need to re-queue it */
|
||||
if (pr[i]->expiration_time >= now) {
|
||||
if (pr[i]->u.paging.expiration_time <= now) {
|
||||
talloc_free(pr[i]);
|
||||
ps->num_paging--;
|
||||
LOGP(DPAG, LOGL_INFO, "Removed paging record, queue_len=%u\n",
|
||||
@@ -395,7 +543,7 @@ int paging_si_update(struct paging_state *ps, struct gsm48_control_channel_descr
|
||||
{
|
||||
LOGP(DPAG, LOGL_INFO, "Paging SI update\n");
|
||||
|
||||
memcpy(&ps->chan_desc, chan_desc, sizeof(chan_desc));
|
||||
ps->chan_desc = *chan_desc;
|
||||
|
||||
/* FIXME: do we need to re-sort the old paging_records? */
|
||||
|
||||
@@ -411,6 +559,12 @@ static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hd
|
||||
struct paging_state *ps = btsb->paging_state;
|
||||
struct gsm48_system_information_type_3 *si3 = (void *) bts->si_buf[SYSINFO_TYPE_3];
|
||||
|
||||
#warning "TODO: Remove this when setting u8NbrOfAgch is implemented properly"
|
||||
if (si3->control_channel_desc.bs_ag_blks_res != 1)
|
||||
LOGP(DPAG, LOGL_ERROR,
|
||||
"Paging: BS_AG_BLKS_RES = %d != 1 not fully supported\n",
|
||||
si3->control_channel_desc.bs_ag_blks_res);
|
||||
|
||||
paging_si_update(ps, &si3->control_channel_desc);
|
||||
}
|
||||
return 0;
|
||||
@@ -418,16 +572,18 @@ static int paging_signal_cbfn(unsigned int subsys, unsigned int signal, void *hd
|
||||
|
||||
static int initialized = 0;
|
||||
|
||||
struct paging_state *paging_init(void *ctx, unsigned int num_paging_max,
|
||||
struct paging_state *paging_init(struct gsm_bts_role_bts *btsb,
|
||||
unsigned int num_paging_max,
|
||||
unsigned int paging_lifetime)
|
||||
{
|
||||
struct paging_state *ps;
|
||||
unsigned int i;
|
||||
|
||||
ps = talloc_zero(ctx, struct paging_state);
|
||||
ps = talloc_zero(btsb, struct paging_state);
|
||||
if (!ps)
|
||||
return NULL;
|
||||
|
||||
ps->btsb = btsb;
|
||||
ps->paging_lifetime = paging_lifetime;
|
||||
ps->num_paging_max = num_paging_max;
|
||||
|
||||
@@ -441,6 +597,14 @@ struct paging_state *paging_init(void *ctx, unsigned int num_paging_max,
|
||||
return ps;
|
||||
}
|
||||
|
||||
void paging_config(struct paging_state *ps,
|
||||
unsigned int num_paging_max,
|
||||
unsigned int paging_lifetime)
|
||||
{
|
||||
ps->num_paging_max = num_paging_max;
|
||||
ps->paging_lifetime = paging_lifetime;
|
||||
}
|
||||
|
||||
void paging_reset(struct paging_state *ps)
|
||||
{
|
||||
int i;
|
||||
@@ -460,3 +624,18 @@ void paging_reset(struct paging_state *ps)
|
||||
|
||||
ps->num_paging = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Helper for the unit tests
|
||||
*/
|
||||
int paging_group_queue_empty(struct paging_state *ps, uint8_t grp)
|
||||
{
|
||||
if (grp >= ARRAY_SIZE(ps->paging_queue))
|
||||
return 1;
|
||||
return llist_empty(&ps->paging_queue[grp]);
|
||||
}
|
||||
|
||||
int paging_queue_length(struct paging_state *ps)
|
||||
{
|
||||
return ps->num_paging;
|
||||
}
|
||||
|
||||
926
src/common/pcu_sock.c
Normal file
926
src/common/pcu_sock.c
Normal file
@@ -0,0 +1,926 @@
|
||||
/* pcu_sock.c: Connect from PCU via unix domain socket */
|
||||
|
||||
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009-2012 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2012 by Holger Hans Peter Freyther
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/pcuif_proto.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/signal.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
|
||||
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
|
||||
|
||||
extern struct gsm_network bts_gsmnet;
|
||||
extern int pcu_direct;
|
||||
static int avail_lai = 0, avail_nse = 0, avail_cell = 0, avail_nsvc[2] = {0, 0};
|
||||
|
||||
static const char *sapi_string[] = {
|
||||
[PCU_IF_SAPI_RACH] = "RACH",
|
||||
[PCU_IF_SAPI_AGCH] = "AGCH",
|
||||
[PCU_IF_SAPI_PCH] = "PCH",
|
||||
[PCU_IF_SAPI_BCCH] = "BCCH",
|
||||
[PCU_IF_SAPI_PDTCH] = "PDTCH",
|
||||
[PCU_IF_SAPI_PRACH] = "PRACH",
|
||||
[PCU_IF_SAPI_PTCCH] = "PTCCH",
|
||||
};
|
||||
|
||||
/* FIXME: common l1if include ? */
|
||||
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
|
||||
|
||||
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
|
||||
/* FIXME: move this to libosmocore */
|
||||
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
|
||||
|
||||
|
||||
static struct gsm_bts_trx *trx_by_nr(struct gsm_bts *bts, uint8_t trx_nr)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
if (trx->nr == trx_nr)
|
||||
return trx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PCU messages
|
||||
*/
|
||||
|
||||
struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
|
||||
msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
msgb_put(msg, sizeof(struct gsm_pcu_if));
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
pcu_prim->msg_type = msg_type;
|
||||
pcu_prim->bts_nr = bts_nr;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
int pcu_tx_info_ind(void)
|
||||
{
|
||||
struct gsm_network *net = &bts_gsmnet;
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct gsm_pcu_if_info_ind *info_ind;
|
||||
struct gsm_bts *bts;
|
||||
struct gprs_rlc_cfg *rlcc;
|
||||
struct gsm_bts_gprs_nsvc *nsvc;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
int i, j;
|
||||
|
||||
LOGP(DPCU, LOGL_INFO, "Sending info\n");
|
||||
|
||||
/* FIXME: allow multiple BTS */
|
||||
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
|
||||
rlcc = &bts->gprs.cell.rlc_cfg;
|
||||
|
||||
msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
info_ind = &pcu_prim->u.info_ind;
|
||||
info_ind->version = PCU_IF_VERSION;
|
||||
|
||||
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0]) {
|
||||
info_ind->flags |= PCU_IF_FLAG_ACTIVE;
|
||||
LOGP(DPCU, LOGL_INFO, "BTS is up\n");
|
||||
} else
|
||||
LOGP(DPCU, LOGL_INFO, "BTS is down\n");
|
||||
|
||||
if (pcu_direct)
|
||||
info_ind->flags |= PCU_IF_FLAG_SYSMO;
|
||||
|
||||
/* RAI */
|
||||
info_ind->mcc = net->mcc;
|
||||
info_ind->mnc = net->mnc;
|
||||
info_ind->lac = bts->location_area_code;
|
||||
info_ind->rac = bts->gprs.rac;
|
||||
|
||||
/* NSE */
|
||||
info_ind->nsei = bts->gprs.nse.nsei;
|
||||
memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7);
|
||||
memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11);
|
||||
|
||||
/* cell attributes */
|
||||
info_ind->cell_id = bts->cell_identity;
|
||||
info_ind->repeat_time = rlcc->paging.repeat_time;
|
||||
info_ind->repeat_count = rlcc->paging.repeat_count;
|
||||
info_ind->bvci = bts->gprs.cell.bvci;
|
||||
info_ind->t3142 = rlcc->parameter[RLC_T3142];
|
||||
info_ind->t3169 = rlcc->parameter[RLC_T3169];
|
||||
info_ind->t3191 = rlcc->parameter[RLC_T3191];
|
||||
info_ind->t3193_10ms = rlcc->parameter[RLC_T3193];
|
||||
info_ind->t3195 = rlcc->parameter[RLC_T3195];
|
||||
info_ind->n3101 = rlcc->parameter[RLC_N3101];
|
||||
info_ind->n3103 = rlcc->parameter[RLC_N3103];
|
||||
info_ind->n3105 = rlcc->parameter[RLC_N3105];
|
||||
info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN];
|
||||
if (rlcc->cs_mask & (1 << GPRS_CS1))
|
||||
info_ind->flags |= PCU_IF_FLAG_CS1;
|
||||
if (rlcc->cs_mask & (1 << GPRS_CS2))
|
||||
info_ind->flags |= PCU_IF_FLAG_CS2;
|
||||
if (rlcc->cs_mask & (1 << GPRS_CS3))
|
||||
info_ind->flags |= PCU_IF_FLAG_CS3;
|
||||
if (rlcc->cs_mask & (1 << GPRS_CS4))
|
||||
info_ind->flags |= PCU_IF_FLAG_CS4;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS1))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS1;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS2))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS2;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS3))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS3;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS4))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS4;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS5))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS5;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS6))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS6;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS7))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS7;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS8))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS8;
|
||||
if (rlcc->cs_mask & (1 << GPRS_MCS9))
|
||||
info_ind->flags |= PCU_IF_FLAG_MCS9;
|
||||
#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs"
|
||||
info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT];
|
||||
#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs"
|
||||
info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT];
|
||||
info_ind->initial_cs = rlcc->initial_cs;
|
||||
info_ind->initial_mcs = rlcc->initial_mcs;
|
||||
|
||||
/* NSVC */
|
||||
for (i = 0; i < 2; i++) {
|
||||
nsvc = &bts->gprs.nsvc[i];
|
||||
info_ind->nsvci[i] = nsvc->nsvci;
|
||||
info_ind->local_port[i] = nsvc->local_port;
|
||||
info_ind->remote_port[i] = nsvc->remote_port;
|
||||
info_ind->remote_ip[i] = nsvc->remote_ip;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
trx = trx_by_nr(bts, i);
|
||||
if (!trx)
|
||||
break;
|
||||
info_ind->trx[i].pdch_mask = 0;
|
||||
info_ind->trx[i].arfcn = trx->arfcn;
|
||||
info_ind->trx[i].hlayer1 = trx_get_hlayer1(trx);
|
||||
for (j = 0; j < 8; j++) {
|
||||
ts = &trx->ts[j];
|
||||
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
|
||||
&& ts->pchan == GSM_PCHAN_PDCH) {
|
||||
info_ind->trx[i].pdch_mask |= (1 << j);
|
||||
info_ind->trx[i].tsc[j] =
|
||||
(ts->tsc >= 0) ? ts->tsc : bts->tsc;
|
||||
LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: "
|
||||
"available (tsc=%d arfcn=%d)\n",
|
||||
trx->nr, ts->nr,
|
||||
info_ind->trx[i].tsc[j],
|
||||
info_ind->trx[i].arfcn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pcu_sock_send(net, msg);
|
||||
}
|
||||
|
||||
static int pcu_if_signal_cb(unsigned int subsys, unsigned int signal,
|
||||
void *hdlr_data, void *signal_data)
|
||||
{
|
||||
struct gsm_network *net = &bts_gsmnet;
|
||||
struct gsm_bts_gprs_nsvc *nsvc;
|
||||
struct gsm_bts *bts;
|
||||
struct gsm48_system_information_type_3 *si3;
|
||||
int id;
|
||||
|
||||
if (subsys != SS_GLOBAL)
|
||||
return -EINVAL;
|
||||
|
||||
switch(signal) {
|
||||
case S_NEW_SYSINFO:
|
||||
bts = signal_data;
|
||||
if (!(bts->si_valid & (1 << SYSINFO_TYPE_3)))
|
||||
break;
|
||||
si3 = (struct gsm48_system_information_type_3 *)
|
||||
bts->si_buf[SYSINFO_TYPE_3];
|
||||
net->mcc = ((si3->lai.digits[0] & 0x0f) << 8)
|
||||
| (si3->lai.digits[0] & 0xf0)
|
||||
| (si3->lai.digits[1] & 0x0f);
|
||||
net->mnc = ((si3->lai.digits[2] & 0x0f) << 8)
|
||||
| (si3->lai.digits[2] & 0xf0)
|
||||
| ((si3->lai.digits[1] & 0xf0) >> 4);
|
||||
if ((net->mnc & 0x00f) == 0x00f)
|
||||
net->mnc >>= 4;
|
||||
bts->location_area_code = ntohs(si3->lai.lac);
|
||||
bts->cell_identity = si3->cell_identity;
|
||||
avail_lai = 1;
|
||||
break;
|
||||
case S_NEW_NSE_ATTR:
|
||||
bts = signal_data;
|
||||
avail_nse = 1;
|
||||
break;
|
||||
case S_NEW_CELL_ATTR:
|
||||
bts = signal_data;
|
||||
avail_cell = 1;
|
||||
break;
|
||||
case S_NEW_NSVC_ATTR:
|
||||
nsvc = signal_data;
|
||||
id = nsvc->id;
|
||||
if (id < 0 || id > 1)
|
||||
return -EINVAL;
|
||||
avail_nsvc[id] = 1;
|
||||
break;
|
||||
case S_NEW_OP_STATE:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If all infos have been received, of if one info is updated after
|
||||
* all infos have been received, transmit info update. */
|
||||
if (avail_lai && avail_nse && avail_cell && avail_nsvc[0])
|
||||
pcu_tx_info_ind();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pcu_tx_rts_req(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct gsm_pcu_if_rts_req *rts_req;
|
||||
struct gsm_bts *bts = ts->trx->bts;
|
||||
|
||||
LOGP(DPCU, LOGL_DEBUG, "Sending rts request: is_ptcch=%d arfcn=%d "
|
||||
"block=%d\n", is_ptcch, arfcn, block_nr);
|
||||
|
||||
msg = pcu_msgb_alloc(PCU_IF_MSG_RTS_REQ, bts->nr);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
rts_req = &pcu_prim->u.rts_req;
|
||||
|
||||
rts_req->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
|
||||
rts_req->fn = fn;
|
||||
rts_req->arfcn = arfcn;
|
||||
rts_req->trx_nr = ts->trx->nr;
|
||||
rts_req->ts_nr = ts->nr;
|
||||
rts_req->block_nr = block_nr;
|
||||
|
||||
return pcu_sock_send(&bts_gsmnet, msg);
|
||||
}
|
||||
|
||||
int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len,
|
||||
int8_t rssi)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct gsm_pcu_if_data *data_ind;
|
||||
struct gsm_bts *bts = ts->trx->bts;
|
||||
|
||||
LOGP(DPCU, LOGL_DEBUG, "Sending data indication: is_ptcch=%d arfcn=%d "
|
||||
"block=%d data=%s\n", is_ptcch, arfcn, block_nr,
|
||||
osmo_hexdump(data, len));
|
||||
|
||||
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_IND, bts->nr);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
data_ind = &pcu_prim->u.data_ind;
|
||||
|
||||
data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
|
||||
data_ind->fn = fn;
|
||||
data_ind->arfcn = arfcn;
|
||||
data_ind->trx_nr = ts->trx->nr;
|
||||
data_ind->ts_nr = ts->nr;
|
||||
data_ind->block_nr = block_nr;
|
||||
data_ind->rssi = rssi;
|
||||
memcpy(data_ind->data, data, len);
|
||||
data_ind->len = len;
|
||||
|
||||
return pcu_sock_send(&bts_gsmnet, msg);
|
||||
}
|
||||
|
||||
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint8_t ra, uint32_t fn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct gsm_pcu_if_rach_ind *rach_ind;
|
||||
|
||||
LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, "
|
||||
"fn=%d\n", qta, ra, fn);
|
||||
|
||||
msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
rach_ind = &pcu_prim->u.rach_ind;
|
||||
|
||||
rach_ind->sapi = PCU_IF_SAPI_RACH;
|
||||
rach_ind->ra = ra;
|
||||
rach_ind->qta = qta;
|
||||
rach_ind->fn = fn;
|
||||
|
||||
return pcu_sock_send(&bts_gsmnet, msg);
|
||||
}
|
||||
|
||||
int pcu_tx_time_ind(uint32_t fn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct gsm_pcu_if_time_ind *time_ind;
|
||||
uint8_t fn13 = fn % 13;
|
||||
|
||||
/* omit frame numbers not starting at a MAC block */
|
||||
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
|
||||
return 0;
|
||||
|
||||
msg = pcu_msgb_alloc(PCU_IF_MSG_TIME_IND, 0);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
time_ind = &pcu_prim->u.time_ind;
|
||||
|
||||
time_ind->fn = fn;
|
||||
|
||||
return pcu_sock_send(&bts_gsmnet, msg);
|
||||
}
|
||||
|
||||
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed)
|
||||
{
|
||||
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct gsm_pcu_if_pag_req *pag_req;
|
||||
|
||||
/* check if identity does not fit: length > sizeof(lv) - 1 */
|
||||
if (identity_lv[0] >= sizeof(pag_req->identity_lv)) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Paging identity too large (%d)\n",
|
||||
identity_lv[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* socket not created */
|
||||
if (!state) {
|
||||
LOGP(DPCU, LOGL_DEBUG, "PCU socket not created, ignoring "
|
||||
"paging message\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg = pcu_msgb_alloc(PCU_IF_MSG_PAG_REQ, 0);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
pag_req = &pcu_prim->u.pag_req;
|
||||
|
||||
pag_req->chan_needed = chan_needed;
|
||||
memcpy(pag_req->identity_lv, identity_lv, identity_lv[0] + 1);
|
||||
|
||||
return pcu_sock_send(&bts_gsmnet, msg);
|
||||
}
|
||||
|
||||
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len)
|
||||
{
|
||||
struct gsm_network *net = &bts_gsmnet;
|
||||
struct gsm_bts *bts;
|
||||
struct msgb *msg;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct gsm_pcu_if_data *data_cnf;
|
||||
|
||||
/* FIXME: allow multiple BTS */
|
||||
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
|
||||
|
||||
LOGP(DPCU, LOGL_INFO, "Sending PCH confirm\n");
|
||||
|
||||
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF, bts->nr);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
data_cnf = &pcu_prim->u.data_cnf;
|
||||
|
||||
data_cnf->sapi = PCU_IF_SAPI_PCH;
|
||||
data_cnf->fn = fn;
|
||||
memcpy(data_cnf->data, data, len);
|
||||
data_cnf->len = len;
|
||||
|
||||
return pcu_sock_send(&bts_gsmnet, msg);
|
||||
}
|
||||
|
||||
static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
|
||||
struct gsm_pcu_if_data *data_req)
|
||||
{
|
||||
uint8_t is_ptcch;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
struct msgb *msg;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d "
|
||||
"block=%d data=%s\n", sapi_string[data_req->sapi],
|
||||
data_req->arfcn, data_req->block_nr,
|
||||
osmo_hexdump(data_req->data, data_req->len));
|
||||
|
||||
switch (data_req->sapi) {
|
||||
case PCU_IF_SAPI_BCCH:
|
||||
if (data_req->len == 23) {
|
||||
bts->si_valid |= (1 << SYSINFO_TYPE_13);
|
||||
memcpy(bts->si_buf[SYSINFO_TYPE_13], data_req->data,
|
||||
data_req->len);
|
||||
} else {
|
||||
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
|
||||
}
|
||||
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
|
||||
break;
|
||||
case PCU_IF_SAPI_PCH:
|
||||
if (msg_type == PCU_IF_MSG_PAG_REQ) {
|
||||
/* FIXME: Add function to schedule paging request.
|
||||
* This might not be required, if PCU_IF_MSG_DATA_REQ
|
||||
* is used instead. */
|
||||
} else {
|
||||
struct gsm_bts_role_bts *btsb = bts->role;
|
||||
|
||||
paging_add_imm_ass(btsb->paging_state, data_req->data,
|
||||
data_req->len);
|
||||
}
|
||||
break;
|
||||
case PCU_IF_SAPI_AGCH:
|
||||
msg = msgb_alloc(data_req->len, "pcu_agch");
|
||||
if (!msg) {
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
msg->l3h = msgb_put(msg, data_req->len);
|
||||
memcpy(msg->l3h, data_req->data, data_req->len);
|
||||
if (bts_agch_enqueue(bts, msg) < 0) {
|
||||
msgb_free(msg);
|
||||
rc = -EIO;
|
||||
}
|
||||
break;
|
||||
case PCU_IF_SAPI_PDTCH:
|
||||
case PCU_IF_SAPI_PTCCH:
|
||||
trx = trx_by_nr(bts, data_req->trx_nr);
|
||||
if (!trx) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
|
||||
"not existing TRX %d\n", data_req->trx_nr);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ts = &trx->ts[data_req->ts_nr];
|
||||
is_ptcch = (data_req->sapi == PCU_IF_SAPI_PTCCH);
|
||||
rc = l1if_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
|
||||
data_req->block_nr, data_req->data, data_req->len);
|
||||
break;
|
||||
default:
|
||||
LOGP(DPCU, LOGL_ERROR, "Received PCU data request with "
|
||||
"unsupported sapi %d\n", data_req->sapi);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pcu_rx_act_req(struct gsm_bts *bts,
|
||||
struct gsm_pcu_if_act_req *act_req)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
LOGP(DPCU, LOGL_INFO, "%s request received: TRX=%d TX=%d\n",
|
||||
(act_req->activate) ? "Activate" : "Deactivate",
|
||||
act_req->trx_nr, act_req->ts_nr);
|
||||
|
||||
trx = trx_by_nr(bts, act_req->trx_nr);
|
||||
if (!trx || act_req->ts_nr >= 8)
|
||||
return -EINVAL;
|
||||
|
||||
lchan = trx->ts[act_req->ts_nr].lchan;
|
||||
lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
|
||||
if (lchan->type != GSM_LCHAN_PDTCH) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Lchan is not of type PDCH, but %d.\n",
|
||||
lchan->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (act_req->activate)
|
||||
bts_model_rsl_chan_act(lchan, NULL);
|
||||
else
|
||||
bts_model_rsl_chan_rel(lchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
|
||||
struct gsm_pcu_if *pcu_prim)
|
||||
{
|
||||
int rc = 0;
|
||||
struct gsm_bts *bts;
|
||||
|
||||
/* FIXME: allow multiple BTS */
|
||||
bts = llist_entry(net->bts_list.next, struct gsm_bts, list);
|
||||
|
||||
switch (msg_type) {
|
||||
case PCU_IF_MSG_DATA_REQ:
|
||||
case PCU_IF_MSG_PAG_REQ:
|
||||
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
|
||||
break;
|
||||
case PCU_IF_MSG_ACT_REQ:
|
||||
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
|
||||
break;
|
||||
default:
|
||||
LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
|
||||
msg_type);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* PCU socket interface
|
||||
*/
|
||||
|
||||
struct pcu_sock_state {
|
||||
struct gsm_network *net;
|
||||
struct osmo_fd listen_bfd; /* fd for listen socket */
|
||||
struct osmo_fd conn_bfd; /* fd for connection to lcr */
|
||||
struct llist_head upqueue; /* queue for sending messages */
|
||||
};
|
||||
|
||||
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
|
||||
{
|
||||
struct pcu_sock_state *state = net->pcu_state;
|
||||
struct osmo_fd *conn_bfd;
|
||||
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data;
|
||||
|
||||
if (!state) {
|
||||
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
|
||||
LOGP(DPCU, LOGL_INFO, "PCU socket not created, "
|
||||
"dropping message\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
conn_bfd = &state->conn_bfd;
|
||||
if (conn_bfd->fd <= 0) {
|
||||
if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND)
|
||||
LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, "
|
||||
"dropping message\n");
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msgb_enqueue(&state->upqueue, msg);
|
||||
conn_bfd->when |= BSC_FD_WRITE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcu_sock_close(struct pcu_sock_state *state)
|
||||
{
|
||||
struct osmo_fd *bfd = &state->conn_bfd;
|
||||
struct gsm_bts *bts;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
int i, j;
|
||||
|
||||
/* FIXME: allow multiple BTS */
|
||||
bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list);
|
||||
|
||||
LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n");
|
||||
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
osmo_fd_unregister(bfd);
|
||||
|
||||
/* re-enable the generation of ACCEPT for new connections */
|
||||
state->listen_bfd.when |= BSC_FD_READ;
|
||||
|
||||
#if 0
|
||||
/* remove si13, ... */
|
||||
bts->si_valid &= ~(1 << SYSINFO_TYPE_13);
|
||||
osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts);
|
||||
#endif
|
||||
|
||||
/* release PDCH */
|
||||
for (i = 0; i < 8; i++) {
|
||||
trx = trx_by_nr(bts, i);
|
||||
if (!trx)
|
||||
break;
|
||||
for (j = 0; j < 8; j++) {
|
||||
ts = &trx->ts[j];
|
||||
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
|
||||
&& ts->pchan == GSM_PCHAN_PDCH) {
|
||||
ts->lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
|
||||
bts_model_rsl_chan_rel(ts->lchan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* flush the queue */
|
||||
while (!llist_empty(&state->upqueue)) {
|
||||
struct msgb *msg = msgb_dequeue(&state->upqueue);
|
||||
msgb_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static int pcu_sock_read(struct osmo_fd *bfd)
|
||||
{
|
||||
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
pcu_prim = (struct gsm_pcu_if *) msg->tail;
|
||||
|
||||
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
goto close;
|
||||
}
|
||||
|
||||
rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
|
||||
|
||||
/* as we always synchronously process the message in pcu_rx() and
|
||||
* its callbacks, we can free the message here. */
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
|
||||
close:
|
||||
msgb_free(msg);
|
||||
pcu_sock_close(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pcu_sock_write(struct osmo_fd *bfd)
|
||||
{
|
||||
struct pcu_sock_state *state = bfd->data;
|
||||
int rc;
|
||||
|
||||
while (!llist_empty(&state->upqueue)) {
|
||||
struct msgb *msg, *msg2;
|
||||
struct gsm_pcu_if *pcu_prim;
|
||||
|
||||
/* peek at the beginning of the queue */
|
||||
msg = llist_entry(state->upqueue.next, struct msgb, list);
|
||||
pcu_prim = (struct gsm_pcu_if *)msg->data;
|
||||
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
|
||||
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
|
||||
if (!msgb_length(msg)) {
|
||||
LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO "
|
||||
"bytes!\n", pcu_prim->msg_type);
|
||||
goto dontsend;
|
||||
}
|
||||
|
||||
/* try to send it over the socket */
|
||||
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
|
||||
if (rc == 0)
|
||||
goto close;
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
bfd->when |= BSC_FD_WRITE;
|
||||
break;
|
||||
}
|
||||
goto close;
|
||||
}
|
||||
|
||||
dontsend:
|
||||
/* _after_ we send it, we can deueue */
|
||||
msg2 = msgb_dequeue(&state->upqueue);
|
||||
assert(msg == msg2);
|
||||
msgb_free(msg);
|
||||
}
|
||||
return 0;
|
||||
|
||||
close:
|
||||
pcu_sock_close(state);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (flags & BSC_FD_READ)
|
||||
rc = pcu_sock_read(bfd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (flags & BSC_FD_WRITE)
|
||||
rc = pcu_sock_write(bfd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* accept connection comming from PCU */
|
||||
static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags)
|
||||
{
|
||||
struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
|
||||
struct osmo_fd *conn_bfd = &state->conn_bfd;
|
||||
struct sockaddr_un un_addr;
|
||||
socklen_t len;
|
||||
int rc;
|
||||
|
||||
len = sizeof(un_addr);
|
||||
rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
|
||||
if (rc < 0) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conn_bfd->fd >= 0) {
|
||||
LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have "
|
||||
"another active connection ?!?\n");
|
||||
/* We already have one PCU connected, this is all we support */
|
||||
state->listen_bfd.when &= ~BSC_FD_READ;
|
||||
close(rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
conn_bfd->fd = rc;
|
||||
conn_bfd->when = BSC_FD_READ;
|
||||
conn_bfd->cb = pcu_sock_cb;
|
||||
conn_bfd->data = state;
|
||||
|
||||
if (osmo_fd_register(conn_bfd) != 0) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Failed to register new connection "
|
||||
"fd\n");
|
||||
close(conn_bfd->fd);
|
||||
conn_bfd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n");
|
||||
|
||||
/* send current info */
|
||||
pcu_tx_info_ind();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcu_sock_init(void)
|
||||
{
|
||||
struct pcu_sock_state *state;
|
||||
struct osmo_fd *bfd;
|
||||
int rc;
|
||||
|
||||
state = talloc_zero(NULL, struct pcu_sock_state);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LLIST_HEAD(&state->upqueue);
|
||||
state->net = &bts_gsmnet;
|
||||
state->conn_bfd.fd = -1;
|
||||
|
||||
bfd = &state->listen_bfd;
|
||||
|
||||
rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/pcu_bts");
|
||||
if (rc < 0) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n",
|
||||
strerror(errno));
|
||||
talloc_free(state);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bfd->when = BSC_FD_READ;
|
||||
bfd->cb = pcu_sock_accept;
|
||||
bfd->data = state;
|
||||
|
||||
rc = osmo_fd_register(bfd);
|
||||
if (rc < 0) {
|
||||
LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n",
|
||||
rc);
|
||||
close(bfd->fd);
|
||||
talloc_free(state);
|
||||
return rc;
|
||||
}
|
||||
|
||||
osmo_signal_register_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
|
||||
|
||||
bts_gsmnet.pcu_state = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pcu_sock_exit(void)
|
||||
{
|
||||
struct pcu_sock_state *state = bts_gsmnet.pcu_state;
|
||||
struct osmo_fd *bfd, *conn_bfd;
|
||||
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
osmo_signal_unregister_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
|
||||
conn_bfd = &state->conn_bfd;
|
||||
if (conn_bfd->fd > 0)
|
||||
pcu_sock_close(state);
|
||||
bfd = &state->listen_bfd;
|
||||
close(bfd->fd);
|
||||
osmo_fd_unregister(bfd);
|
||||
talloc_free(state);
|
||||
bts_gsmnet.pcu_state = NULL;
|
||||
}
|
||||
|
||||
/* FIXME: move this to libosmocore */
|
||||
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path)
|
||||
{
|
||||
struct sockaddr_un local;
|
||||
unsigned int namelen;
|
||||
int rc;
|
||||
|
||||
bfd->fd = socket(AF_UNIX, type, 0);
|
||||
|
||||
if (bfd->fd < 0) {
|
||||
fprintf(stderr, "Failed to create Unix Domain Socket.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
local.sun_family = AF_UNIX;
|
||||
strncpy(local.sun_path, path, sizeof(local.sun_path));
|
||||
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
|
||||
unlink(local.sun_path);
|
||||
|
||||
/* we use the same magic that X11 uses in Xtranssock.c for
|
||||
* calculating the proper length of the sockaddr */
|
||||
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
|
||||
local.sun_len = strlen(local.sun_path);
|
||||
#endif
|
||||
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
|
||||
namelen = SUN_LEN(&local);
|
||||
#else
|
||||
namelen = strlen(local.sun_path) +
|
||||
offsetof(struct sockaddr_un, sun_path);
|
||||
#endif
|
||||
|
||||
rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n",
|
||||
local.sun_path);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(bfd->fd, 0) != 0) {
|
||||
fprintf(stderr, "Failed to listen.\n");
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
602
src/common/rsl.c
602
src/common/rsl.c
File diff suppressed because it is too large
Load Diff
@@ -70,8 +70,12 @@ char *bts_support_comb_name(uint8_t chan_comb)
|
||||
return("BCCH");
|
||||
if (chan_comb == NM_CHANC_BCCHComb)
|
||||
return("BCCH+SDCCH/4");
|
||||
if (chan_comb == NM_CHANC_BCCH_CBCH)
|
||||
return("BCCH+CBCH+SDCCH/4");
|
||||
if (chan_comb == NM_CHANC_SDCCH)
|
||||
return("SDCCH/8");
|
||||
if (chan_comb == NM_CHANC_SDCCH_CBCH)
|
||||
return("SDCCH/8+CBCH");
|
||||
if (chan_comb == NM_CHANC_TCHFull)
|
||||
return("TCH/F");
|
||||
if (chan_comb == NM_CHANC_TCHHalf)
|
||||
|
||||
@@ -24,23 +24,103 @@
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
#define BTS_HAS_SI(bts, sinum) ((bts)->si_valid & (1 << sinum))
|
||||
|
||||
/* Apply the rules from 05.02 6.3.1.3 Mapping of BCCH Data */
|
||||
uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time)
|
||||
{
|
||||
/* Apply the rules from 05.02 6.3.1.3 Mapping of BCCH Data */
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
unsigned int tc4_cnt = 0;
|
||||
unsigned int tc4_sub[4];
|
||||
|
||||
/* System information type 2 bis or 2 ter messages are sent if
|
||||
* needed, as determined by the system operator. If only one of
|
||||
* them is needed, it is sent when TC = 5. If both are needed,
|
||||
* 2bis is sent when TC = 5 and 2ter is sent at least once
|
||||
* within any of 4 consecutive occurrences of TC = 4. */
|
||||
/* System information type 2 quater is sent if needed, as
|
||||
* determined by the system operator. If sent on BCCH Norm, it
|
||||
* shall be sent when TC = 5 if neither of 2bis and 2ter are
|
||||
* used, otherwise it shall be sent at least once within any of
|
||||
* 4 consecutive occurrences of TC = 4. If sent on BCCH Ext, it
|
||||
* is sent at least once within any of 4 consecutive occurrences
|
||||
* of TC = 5. */
|
||||
/* System Information type 9 is sent in those blocks with
|
||||
* TC = 4 which are specified in system information type 3 as
|
||||
* defined in 3GPP TS 04.08. */
|
||||
/* System Information Type 13 need only be sent if GPRS support
|
||||
* is indicated in one or more of System Information Type 3 or 4
|
||||
* or 7 or 8 messages. These messages also indicate if the
|
||||
* message is sent on the BCCH Norm or if the message is
|
||||
* transmitted on the BCCH Ext. In the case that the message is
|
||||
* sent on the BCCH Norm, it is sent at least once within any of
|
||||
* 4 consecutive occurrences of TC = 4. */
|
||||
|
||||
/* We only implement BCCH Norm at this time */
|
||||
switch (g_time->tc) {
|
||||
case 0:
|
||||
/* System Information Type 1 need only be sent if
|
||||
* frequency hopping is in use or when the NCH is
|
||||
* present in a cell. If the MS finds another message
|
||||
* when TC = 0, it can assume that System Information
|
||||
* Type 1 is not in use. */
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_1);
|
||||
case 1:
|
||||
/* A SI 2 message will be sent at least every time TC = 1. */
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
|
||||
case 2:
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
||||
case 3:
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_4);
|
||||
case 4:
|
||||
/* 2ter, 2quater, 9, 13 */
|
||||
break;
|
||||
/* iterate over 2ter, 2quater, 9, 13 */
|
||||
/* determine how many SI we need to send on TC=4,
|
||||
* and which of them we send when */
|
||||
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2ter)) {
|
||||
tc4_sub[tc4_cnt] = SYSINFO_TYPE_2ter;
|
||||
tc4_cnt += 1; /* 2bis */
|
||||
}
|
||||
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) &&
|
||||
(BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) ||
|
||||
BTS_HAS_SI(bts, SYSINFO_TYPE_2bis))) {
|
||||
tc4_sub[tc4_cnt] = SYSINFO_TYPE_2quater;
|
||||
tc4_cnt += 1;
|
||||
}
|
||||
if (BTS_HAS_SI(bts, SYSINFO_TYPE_13)) {
|
||||
tc4_sub[tc4_cnt] = SYSINFO_TYPE_13;
|
||||
tc4_cnt += 1;
|
||||
}
|
||||
if (BTS_HAS_SI(bts, SYSINFO_TYPE_9)) {
|
||||
/* FIXME: check SI3 scheduling info! */
|
||||
tc4_sub[tc4_cnt] = SYSINFO_TYPE_9;
|
||||
tc4_cnt += 1;
|
||||
}
|
||||
/* simply send SI2 if we have nothing else to send */
|
||||
if (tc4_cnt == 0)
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_2);
|
||||
else {
|
||||
/* increment static counter by one, modulo count */
|
||||
btsb->si.tc4_ctr = (btsb->si.tc4_ctr + 1) % tc4_cnt;
|
||||
return GSM_BTS_SI(bts, tc4_sub[btsb->si.tc4_ctr]);
|
||||
}
|
||||
case 5:
|
||||
/* 2ter, 2quater */
|
||||
/* 2bis, 2ter, 2quater */
|
||||
if (BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
|
||||
!BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_2bis);
|
||||
|
||||
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2ter) &&
|
||||
!BTS_HAS_SI(bts, SYSINFO_TYPE_2bis))
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_2ter);
|
||||
|
||||
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
|
||||
BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_2bis);
|
||||
|
||||
else if (BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) &&
|
||||
!BTS_HAS_SI(bts, SYSINFO_TYPE_2bis) &&
|
||||
!BTS_HAS_SI(bts, SYSINFO_TYPE_2ter))
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_2quater);
|
||||
break;
|
||||
case 6:
|
||||
return GSM_BTS_SI(bts, SYSINFO_TYPE_3);
|
||||
@@ -51,11 +131,11 @@ uint8_t *bts_sysinfo_get(struct gsm_bts *bts, struct gsm_time *g_time)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan, struct gsm_time *g_time)
|
||||
uint8_t *lchan_sacch_get(struct gsm_lchan *lchan)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
for (tmp = lchan->si.last + 1; tmp != lchan->si.last; tmp = (tmp + 1) % 32) {
|
||||
for (tmp = lchan->si.last + 1; tmp != lchan->si.last; tmp = (tmp + 1) % _MAX_SYSINFO_TYPE) {
|
||||
if (lchan->si.valid & (1 << tmp)) {
|
||||
lchan->si.last = tmp;
|
||||
return lchan->si.buf[tmp];
|
||||
|
||||
291
src/common/tx_power.c
Normal file
291
src/common/tx_power.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/* Transmit Power computation */
|
||||
|
||||
/* (C) 2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/tx_power.h>
|
||||
|
||||
static int get_pa_drive_level_mdBm(const struct power_amp *pa,
|
||||
int desired_p_out_mdBm, unsigned int arfcn)
|
||||
{
|
||||
if (arfcn >= ARRAY_SIZE(pa->calib.gain_mdB))
|
||||
return INT_MIN;
|
||||
|
||||
/* FIXME: temperature compensation */
|
||||
|
||||
return desired_p_out_mdBm - pa->calib.gain_mdB[arfcn];
|
||||
}
|
||||
|
||||
/* maximum output power of the system */
|
||||
int get_p_max_out_mdBm(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
/* Add user gain, internal and external PA gain to TRX output power */
|
||||
return tpp->trx_p_max_out_mdBm + tpp->user_gain_mdB +
|
||||
tpp->pa.nominal_gain_mdB + tpp->user_pa.nominal_gain_mdB;
|
||||
}
|
||||
|
||||
/* nominal output power, i.e. OML-reduced maximum output power */
|
||||
int get_p_nominal_mdBm(struct gsm_bts_trx *trx)
|
||||
{
|
||||
/* P_max_out subtracted by OML maximum power reduction IE */
|
||||
return get_p_max_out_mdBm(trx) - to_mdB(trx->max_power_red);
|
||||
}
|
||||
|
||||
/* calculate the target total output power required, reduced by both
|
||||
* OML and RSL, but ignoring the attenutation required for power ramping and
|
||||
* thermal management */
|
||||
int get_p_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie)
|
||||
{
|
||||
/* Pn subtracted by RSL BS Power IE (in 2 dB steps) */
|
||||
return get_p_nominal_mdBm(trx) - to_mdB(bs_power_ie * 2);
|
||||
}
|
||||
int get_p_target_mdBm_lchan(struct gsm_lchan *lchan)
|
||||
{
|
||||
return get_p_target_mdBm(lchan->ts->trx, lchan->bs_power);
|
||||
}
|
||||
|
||||
/* calculate the actual total output power required, taking into account the
|
||||
* attenutation required for power ramping but not thermal management */
|
||||
int get_p_actual_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
|
||||
/* P_target subtracted by ramp attenuation */
|
||||
return p_target_mdBm - tpp->ramp.attenuation_mdB;
|
||||
}
|
||||
|
||||
/* calculate the effective total output power required, taking into account the
|
||||
* attenutation required for power ramping and thermal management */
|
||||
int get_p_eff_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
|
||||
/* P_target subtracted by ramp attenuation */
|
||||
return p_target_mdBm - tpp->ramp.attenuation_mdB - tpp->thermal_attenuation_mdB;
|
||||
}
|
||||
|
||||
/* calculate effect TRX output power required, taking into account the
|
||||
* attenuations required for power ramping and thermal management */
|
||||
int get_p_trxout_eff_mdBm(struct gsm_bts_trx *trx, int p_target_mdBm)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
int p_actual_mdBm, user_pa_drvlvl_mdBm, pa_drvlvl_mdBm;
|
||||
unsigned int arfcn = trx->arfcn;
|
||||
|
||||
/* P_actual subtracted by any bulk gain added by the user */
|
||||
p_actual_mdBm = get_p_eff_mdBm(trx, p_target_mdBm) - tpp->user_gain_mdB;
|
||||
|
||||
/* determine input drive level required at input to user PA */
|
||||
user_pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->user_pa, p_actual_mdBm, arfcn);
|
||||
|
||||
/* determine input drive level required at input to internal PA */
|
||||
pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->pa, user_pa_drvlvl_mdBm, arfcn);
|
||||
|
||||
/* internal PA input drive level is TRX output power */
|
||||
return pa_drvlvl_mdBm;
|
||||
}
|
||||
|
||||
/* calculate target TRX output power required, ignoring the
|
||||
* attenuations required for power ramping but not thermal management */
|
||||
int get_p_trxout_target_mdBm(struct gsm_bts_trx *trx, uint8_t bs_power_ie)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
int p_target_mdBm, user_pa_drvlvl_mdBm, pa_drvlvl_mdBm;
|
||||
unsigned int arfcn = trx->arfcn;
|
||||
|
||||
/* P_target subtracted by any bulk gain added by the user */
|
||||
p_target_mdBm = get_p_target_mdBm(trx, bs_power_ie) - tpp->user_gain_mdB;
|
||||
|
||||
/* determine input drive level required at input to user PA */
|
||||
user_pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->user_pa, p_target_mdBm, arfcn);
|
||||
|
||||
/* determine input drive level required at input to internal PA */
|
||||
pa_drvlvl_mdBm = get_pa_drive_level_mdBm(&tpp->pa, user_pa_drvlvl_mdBm, arfcn);
|
||||
|
||||
/* internal PA input drive level is TRX output power */
|
||||
return pa_drvlvl_mdBm;
|
||||
}
|
||||
int get_p_trxout_target_mdBm_lchan(struct gsm_lchan *lchan)
|
||||
{
|
||||
return get_p_trxout_target_mdBm(lchan->ts->trx, lchan->bs_power);
|
||||
}
|
||||
|
||||
|
||||
/* output power ramping code */
|
||||
|
||||
/* The idea here is to avoid a hard switch from 0 to 100, but to actually
|
||||
* slowly and gradually ramp up or down the power. This is needed on the
|
||||
* one hand side to avoid very fast dynamic load changes towards the PA power
|
||||
* supply, but is also needed in order to avoid a DoS by too many subscriber
|
||||
* attempting to register at the same time. Rather, grow the cell slowly in
|
||||
* radius than start with the full raduis at once. */
|
||||
|
||||
static int we_are_ramping_up(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
|
||||
if (tpp->p_total_tgt_mdBm > tpp->p_total_cur_mdBm)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void power_ramp_do_step(struct gsm_bts_trx *trx, int first);
|
||||
|
||||
/* timer call-back for the ramp tumer */
|
||||
static void power_ramp_timer_cb(void *_trx)
|
||||
{
|
||||
struct gsm_bts_trx *trx = _trx;
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
int p_trxout_eff_mdBm;
|
||||
|
||||
/* compute new actual total output power (= minus ramp attenuation) */
|
||||
tpp->p_total_cur_mdBm = get_p_actual_mdBm(trx, tpp->p_total_tgt_mdBm);
|
||||
|
||||
/* compute new effective (= minus ramp and thermal attenuation) TRX output required */
|
||||
p_trxout_eff_mdBm = get_p_trxout_eff_mdBm(trx, tpp->p_total_tgt_mdBm);
|
||||
|
||||
LOGP(DL1C, LOGL_DEBUG, "ramp_timer_cb(cur_pout=%d, tgt_pout=%d, "
|
||||
"ramp_att=%d, therm_att=%d, user_gain=%d)\n",
|
||||
tpp->p_total_cur_mdBm, tpp->p_total_tgt_mdBm,
|
||||
tpp->ramp.attenuation_mdB, tpp->thermal_attenuation_mdB,
|
||||
tpp->user_gain_mdB);
|
||||
|
||||
LOGP(DL1C, LOGL_INFO,
|
||||
"ramping TRX board output power to %d mdBm.\n", p_trxout_eff_mdBm);
|
||||
|
||||
/* Instruct L1 to apply new effective TRX output power required */
|
||||
bts_model_change_power(trx, p_trxout_eff_mdBm);
|
||||
}
|
||||
|
||||
/* BTS model call-back once one a call to bts_model_change_power()
|
||||
* completes, indicating actual L1 transmit power */
|
||||
void power_trx_change_compl(struct gsm_bts_trx *trx, int p_trxout_cur_mdBm)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
int p_trxout_should_mdBm;
|
||||
|
||||
p_trxout_should_mdBm = get_p_trxout_eff_mdBm(trx, tpp->p_total_tgt_mdBm);
|
||||
|
||||
/* for now we simply write an error message, but in the future
|
||||
* we might use the value (again) as part of our math? */
|
||||
if (p_trxout_cur_mdBm != p_trxout_should_mdBm) {
|
||||
LOGP(DL1C, LOGL_ERROR, "bts_model notifies us of %u mdBm TRX "
|
||||
"output power. However, it should be %u mdBm!\n",
|
||||
p_trxout_cur_mdBm, p_trxout_should_mdBm);
|
||||
}
|
||||
|
||||
/* and do another step... */
|
||||
power_ramp_do_step(trx, 0);
|
||||
}
|
||||
|
||||
static void power_ramp_do_step(struct gsm_bts_trx *trx, int first)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
|
||||
/* we had finished in last loop iteration */
|
||||
if (!first && tpp->ramp.attenuation_mdB == 0)
|
||||
return;
|
||||
|
||||
if (we_are_ramping_up(trx)) {
|
||||
/* ramp up power -> ramp down attenuation */
|
||||
tpp->ramp.attenuation_mdB -= tpp->ramp.step_size_mdB;
|
||||
if (tpp->ramp.attenuation_mdB <= 0) {
|
||||
/* we are done */
|
||||
tpp->ramp.attenuation_mdB = 0;
|
||||
}
|
||||
} else {
|
||||
/* ramp down power -> ramp up attenuation */
|
||||
tpp->ramp.attenuation_mdB += tpp->ramp.step_size_mdB;
|
||||
if (tpp->ramp.attenuation_mdB >= 0) {
|
||||
/* we are done */
|
||||
tpp->ramp.attenuation_mdB = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* schedule timer for the next step */
|
||||
tpp->ramp.step_timer.data = trx;
|
||||
tpp->ramp.step_timer.cb = power_ramp_timer_cb;
|
||||
osmo_timer_schedule(&tpp->ramp.step_timer, tpp->ramp.step_interval_sec, 0);
|
||||
}
|
||||
|
||||
|
||||
int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass)
|
||||
{
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
|
||||
/* The input to this function is the actual desired output power, i.e.
|
||||
* the maximum total system power subtracted by OML as well as RSL
|
||||
* reductions */
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "power_ramp_start(cur=%d, tgt=%d)\n",
|
||||
tpp->p_total_cur_mdBm, p_total_tgt_mdBm);
|
||||
|
||||
if (!bypass && (p_total_tgt_mdBm > get_p_nominal_mdBm(trx))) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Asked to ramp power up to "
|
||||
"%d mdBm, which exceeds P_max_out (%d)\n",
|
||||
p_total_tgt_mdBm, get_p_nominal_mdBm(trx));
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Cancel any pending request */
|
||||
osmo_timer_del(&tpp->ramp.step_timer);
|
||||
|
||||
/* set the new target */
|
||||
tpp->p_total_tgt_mdBm = p_total_tgt_mdBm;
|
||||
|
||||
if (we_are_ramping_up(trx)) {
|
||||
if (tpp->p_total_tgt_mdBm <= tpp->ramp.max_initial_pout_mdBm) {
|
||||
LOGP(DL1C, LOGL_INFO,
|
||||
"target_power(%d) is below max.initial power\n",
|
||||
tpp->p_total_tgt_mdBm);
|
||||
/* new setting is below the maximum initial output
|
||||
* power, so we can directly jump to this level */
|
||||
tpp->p_total_cur_mdBm = tpp->p_total_tgt_mdBm;
|
||||
tpp->ramp.attenuation_mdB = 0;
|
||||
power_ramp_timer_cb(trx);
|
||||
} else {
|
||||
/* We need to step it up. Start from the current value */
|
||||
/* Set attenuation to cause no power change right now */
|
||||
tpp->ramp.attenuation_mdB = tpp->p_total_tgt_mdBm - tpp->p_total_cur_mdBm;
|
||||
|
||||
/* start with the firsrt step */
|
||||
power_ramp_do_step(trx, 1);
|
||||
}
|
||||
} else {
|
||||
/* Set ramp attenuation to negative value, and increase that by
|
||||
* steps until it reaches 0 */
|
||||
tpp->ramp.attenuation_mdB = tpp->p_total_tgt_mdBm - tpp->p_total_cur_mdBm;
|
||||
|
||||
/* start with the firsrt step */
|
||||
power_ramp_do_step(trx, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
452
src/common/vty.c
452
src/common/vty.c
@@ -1,6 +1,6 @@
|
||||
/* OsmoBTS VTY interface */
|
||||
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2011-2014 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -19,6 +19,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "btsconfig.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
@@ -29,6 +31,8 @@
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
@@ -40,11 +44,19 @@
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/paging.h>
|
||||
|
||||
|
||||
enum node_type bts_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case TRX_NODE:
|
||||
vty->node = BTS_NODE;
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
vty->index = trx->bts;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
vty->node = CONFIG_NODE;
|
||||
}
|
||||
@@ -54,6 +66,7 @@ enum node_type bts_vty_go_parent(struct vty *vty)
|
||||
int bts_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case TRX_NODE:
|
||||
case BTS_NODE:
|
||||
return 1;
|
||||
default:
|
||||
@@ -65,6 +78,13 @@ gDEFUN(ournode_exit, ournode_exit_cmd, "exit",
|
||||
"Exit current node, go down to provious node")
|
||||
{
|
||||
switch (vty->node) {
|
||||
case TRX_NODE:
|
||||
vty->node = BTS_NODE;
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
vty->index = trx->bts;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -85,19 +105,20 @@ gDEFUN(ournode_end, ournode_end_cmd, "end",
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
struct vty_app_info bts_vty_info = {
|
||||
.name = "OsmoBTS",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = bts_vty_go_parent,
|
||||
.is_config_node = bts_vty_is_config_node,
|
||||
};
|
||||
|
||||
const char *osmobts_copyright =
|
||||
static const char osmobts_copyright[] =
|
||||
"Copyright (C) 2010, 2011 by Harald Welte, Andreas Eversberg and On-Waves\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||
|
||||
struct vty_app_info bts_vty_info = {
|
||||
.name = "OsmoBTS",
|
||||
.version = PACKAGE_VERSION,
|
||||
.copyright = osmobts_copyright,
|
||||
.go_parent_cb = bts_vty_go_parent,
|
||||
.is_config_node = bts_vty_is_config_node,
|
||||
};
|
||||
|
||||
extern struct gsm_network bts_gsmnet;
|
||||
|
||||
struct gsm_network *gsmnet_from_vty(struct vty *v)
|
||||
@@ -105,30 +126,43 @@ struct gsm_network *gsmnet_from_vty(struct vty *v)
|
||||
return &bts_gsmnet;
|
||||
}
|
||||
|
||||
struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num)
|
||||
{
|
||||
struct gsm_bts *bts;
|
||||
|
||||
if (num >= net->num_bts)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(bts, &net->bts_list, list) {
|
||||
if (bts->nr == num)
|
||||
return bts;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct cmd_node bts_node = {
|
||||
BTS_NODE,
|
||||
"%s(bts)#",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node trx_node = {
|
||||
TRX_NODE,
|
||||
"%s(trx)#",
|
||||
1,
|
||||
};
|
||||
|
||||
DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
|
||||
"trx <0-0>",
|
||||
"Select a TRX to configure\n" "TRX number\n")
|
||||
{
|
||||
int trx_nr = atoi(argv[0]);
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
trx = gsm_bts_trx_num(bts, trx_nr);
|
||||
if (!trx) {
|
||||
vty_out(vty, "Unknown TRX %u%s", trx_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->index = trx;
|
||||
vty->index_sub = &trx->description;
|
||||
vty->node = TRX_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
|
||||
if (bts->description)
|
||||
@@ -137,10 +171,40 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||
vty_out(vty, " ipa unit-id %u %u%s",
|
||||
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
|
||||
vty_out(vty, " oml remote-ip %s%s", btsb->bsc_oml_host, VTY_NEWLINE);
|
||||
vty_out(vty, " rtp bind-ip %s%s", btsb->rtp_bind_host, VTY_NEWLINE);
|
||||
vty_out(vty, " rtp jitter-buffer %u%s", btsb->rtp_jitter_buf_ms,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " paging queue-size %u%s", paging_get_queue_max(btsb->paging_state),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
|
||||
VTY_NEWLINE);
|
||||
if (btsb->agch_queue_thresh_level != GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT
|
||||
|| btsb->agch_queue_low_level != GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT
|
||||
|| btsb->agch_queue_high_level != GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT)
|
||||
vty_out(vty, " agch-queue-mgmt threshold %d low %d high %d%s",
|
||||
btsb->agch_queue_thresh_level, btsb->agch_queue_low_level,
|
||||
btsb->agch_queue_high_level, VTY_NEWLINE);
|
||||
|
||||
bts_model_config_write_bts(vty, bts);
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
struct trx_power_params *tpp = &trx->power_params;
|
||||
vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
|
||||
|
||||
if (trx->power_params.user_gain_mdB)
|
||||
vty_out(vty, " user-gain %u mdB%s",
|
||||
tpp->user_gain_mdB, VTY_NEWLINE);
|
||||
vty_out(vty, " power-ramp max-initial %d mdBm%s",
|
||||
tpp->ramp.max_initial_pout_mdBm, VTY_NEWLINE);
|
||||
vty_out(vty, " power-ramp step-size %d mdB%s",
|
||||
tpp->ramp.step_size_mdB, VTY_NEWLINE);
|
||||
vty_out(vty, " power-ramp step-interval %d%s",
|
||||
tpp->ramp.step_interval_sec, VTY_NEWLINE);
|
||||
|
||||
bts_model_config_write_trx(vty, trx);
|
||||
}
|
||||
}
|
||||
|
||||
int config_write_bts(struct vty *vty)
|
||||
static int config_write_bts(struct vty *vty)
|
||||
{
|
||||
struct gsm_network *net = gsmnet_from_vty(vty);
|
||||
struct gsm_bts *bts;
|
||||
@@ -151,6 +215,11 @@ int config_write_bts(struct vty *vty)
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_dummy(struct vty *vty)
|
||||
{
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* per-BTS configuration */
|
||||
DEFUN(cfg_bts,
|
||||
cfg_bts_cmd,
|
||||
@@ -180,7 +249,9 @@ DEFUN(cfg_bts,
|
||||
DEFUN(cfg_bts_unit_id,
|
||||
cfg_bts_unit_id_cmd,
|
||||
"ipa unit-id <0-65534> <0-255>",
|
||||
"Set the ip.access BTS Unit ID of this BTS\n")
|
||||
"ip.access RSL commands\n"
|
||||
"Set the Unit ID of this BTS\n"
|
||||
"Site ID\n" "Unit ID\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
int site_id = atoi(argv[0]);
|
||||
@@ -195,7 +266,15 @@ DEFUN(cfg_bts_unit_id,
|
||||
DEFUN(cfg_bts_band,
|
||||
cfg_bts_band_cmd,
|
||||
"band (450|GSM450|480|GSM480|750|GSM750|810|GSM810|850|GSM850|900|GSM900|1800|DCS1800|1900|PCS1900)",
|
||||
"Set the frequency band of this BTS\n" "Frequency band\n")
|
||||
"Set the frequency band of this BTS\n"
|
||||
"Alias for GSM450\n450Mhz\n"
|
||||
"Alias for GSM480\n480Mhz\n"
|
||||
"Alias for GSM750\n750Mhz\n"
|
||||
"Alias for GSM810\n810Mhz\n"
|
||||
"Alias for GSM850\n850Mhz\n"
|
||||
"Alias for GSM900\n900Mhz\n"
|
||||
"Alias for DCS1800\n1800Mhz\n"
|
||||
"Alias for PCS1900\n1900Mhz\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
int band = gsm_band_parse(argv[0]);
|
||||
@@ -227,21 +306,163 @@ DEFUN(cfg_bts_oml_ip,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_rtp_bind_ip,
|
||||
#define RTP_STR "RTP parameters\n"
|
||||
|
||||
DEFUN_HIDDEN(cfg_bts_rtp_bind_ip,
|
||||
cfg_bts_rtp_bind_ip_cmd,
|
||||
"rtp bind-ip A.B.C.D",
|
||||
"RTP Parameters\n" "RTP local bind IP Address\n" "RTP local bind IP Address\n")
|
||||
RTP_STR "RTP local bind IP Address\n" "RTP local bind IP Address\n")
|
||||
{
|
||||
vty_out(vty, "%% rtp bind-ip is now deprecated%s", VTY_NEWLINE);
|
||||
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_rtp_jitbuf,
|
||||
cfg_bts_rtp_jitbuf_cmd,
|
||||
"rtp jitter-buffer <0-10000>",
|
||||
RTP_STR "RTP jitter buffer\n" "jitter buffer in ms\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
if (btsb->rtp_bind_host)
|
||||
talloc_free(btsb->rtp_bind_host);
|
||||
|
||||
btsb->rtp_bind_host = talloc_strdup(btsb, argv[0]);
|
||||
btsb->rtp_jitter_buf_ms = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define PAG_STR "Paging related parameters\n"
|
||||
|
||||
DEFUN(cfg_bts_paging_queue_size,
|
||||
cfg_bts_paging_queue_size_cmd,
|
||||
"paging queue-size <1-1024>",
|
||||
PAG_STR "Maximum length of BTS-internal paging queue\n"
|
||||
"Maximum length of BTS-internal paging queue\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
paging_set_queue_max(btsb->paging_state, atoi(argv[0]));
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_paging_lifetime,
|
||||
cfg_bts_paging_lifetime_cmd,
|
||||
"paging lifetime <0-60>",
|
||||
PAG_STR "Maximum lifetime of a paging record\n"
|
||||
"Maximum lifetime of a paging record (secods)\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
paging_set_lifetime(btsb->paging_state, atoi(argv[0]));
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define AGCH_QUEUE_STR "AGCH queue mgmt\n"
|
||||
|
||||
DEFUN(cfg_bts_agch_queue_mgmt_params,
|
||||
cfg_bts_agch_queue_mgmt_params_cmd,
|
||||
"agch-queue-mgmt threshold <0-100> low <0-100> high <0-100000>",
|
||||
AGCH_QUEUE_STR
|
||||
"Threshold to start cleanup\nin %% of the maximum queue length\n"
|
||||
"Low water mark for cleanup\nin %% of the maximum queue length\n"
|
||||
"High water mark for cleanup\nin %% of the maximum queue length\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->agch_queue_thresh_level = atoi(argv[0]);
|
||||
btsb->agch_queue_low_level = atoi(argv[1]);
|
||||
btsb->agch_queue_high_level = atoi(argv[2]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_agch_queue_mgmt_default,
|
||||
cfg_bts_agch_queue_mgmt_default_cmd,
|
||||
"agch-queue-mgmt default",
|
||||
AGCH_QUEUE_STR
|
||||
"Reset clean parameters to default values\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->agch_queue_thresh_level = GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT;
|
||||
btsb->agch_queue_low_level = GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT;
|
||||
btsb->agch_queue_high_level = GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DB_DBM_STR \
|
||||
"Unit is dB (decibels)\n" \
|
||||
"Unit is mdB (milli-decibels, or rather 1/10000 bel)\n"
|
||||
|
||||
static int parse_mdbm(const char *valstr, const char *unit)
|
||||
{
|
||||
int val = atoi(valstr);
|
||||
|
||||
if (!strcmp(unit, "dB") || !strcmp(unit, "dBm"))
|
||||
return val * 1000;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_user_gain,
|
||||
cfg_trx_user_gain_cmd,
|
||||
"user-gain <-100000-100000> (dB|mdB)",
|
||||
"Inform BTS about additional, user-provided gain or attenuation at TRX output\n"
|
||||
"Value of user-provided external gain(+)/attenuation(-)\n" DB_DBM_STR)
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
trx->power_params.user_gain_mdB = parse_mdbm(argv[0], argv[1]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define PR_STR "Power-Ramp settings"
|
||||
DEFUN(cfg_trx_pr_max_initial, cfg_trx_pr_max_initial_cmd,
|
||||
"power-ramp max-initial <0-100000> (dBm|mdBm)",
|
||||
PR_STR "Maximum initial power\n"
|
||||
"Value\n" DB_DBM_STR)
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
trx->power_params.ramp.max_initial_pout_mdBm =
|
||||
parse_mdbm(argv[0], argv[1]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_pr_step_size, cfg_trx_pr_step_size_cmd,
|
||||
"power-ramp step-size <1-100000> (dB|mdB)",
|
||||
PR_STR "Power increase by step\n"
|
||||
"Step size\n" DB_DBM_STR)
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
trx->power_params.ramp.step_size_mdB =
|
||||
parse_mdbm(argv[0], argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_pr_step_interval, cfg_trx_pr_step_interval_cmd,
|
||||
"power-ramp step-interval <1-100>",
|
||||
PR_STR "Power increase by step\n"
|
||||
"Step time in seconds\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
trx->power_params.ramp.step_interval_sec = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
* SHOW
|
||||
* ======================================================================*/
|
||||
@@ -253,8 +474,21 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
|
||||
abis_nm_avail_name(nms->availability), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static unsigned int llist_length(struct llist_head *list)
|
||||
{
|
||||
unsigned int len = 0;
|
||||
struct llist_head *pos;
|
||||
|
||||
llist_for_each(pos, list)
|
||||
len++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts->role;
|
||||
|
||||
vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
|
||||
"BSIC %u, TSC %u and %u TRX%s",
|
||||
bts->nr, "FIXME", gsm_band_name(bts->band),
|
||||
@@ -270,6 +504,19 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
net_dump_nmstate(vty, &bts->mo.nm_state);
|
||||
vty_out(vty, " Site Mgr NM State: ");
|
||||
net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state);
|
||||
vty_out(vty, " Paging: Queue size %u, occupied %u, lifetime %us%s",
|
||||
paging_get_queue_max(btsb->paging_state), paging_queue_length(btsb->paging_state),
|
||||
paging_get_lifetime(btsb->paging_state), VTY_NEWLINE);
|
||||
vty_out(vty, " AGCH: Queue limit %u, occupied %d, "
|
||||
"dropped %llu, merged %llu, rejected %llu, "
|
||||
"ag-res %llu, non-res %llu%s",
|
||||
btsb->agch_max_queue_length, btsb->agch_queue_length,
|
||||
btsb->agch_queue_dropped_msgs, btsb->agch_queue_merged_msgs,
|
||||
btsb->agch_queue_rejected_msgs, btsb->agch_queue_agch_msgs,
|
||||
btsb->agch_queue_pch_msgs,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " CBCH backlog queue length: %u%s",
|
||||
llist_length(&btsb->smscb_state.queue), VTY_NEWLINE);
|
||||
#if 0
|
||||
vty_out(vty, " Paging: %u pending requests, %u free slots%s",
|
||||
paging_pending_requests_nr(bts),
|
||||
@@ -316,6 +563,123 @@ DEFUN(show_bts, show_bts_cmd, "show bts <0-255>",
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct gsm_lchan *resolve_lchan(struct gsm_network *net,
|
||||
const char **argv, int idx)
|
||||
{
|
||||
int bts_nr = atoi(argv[idx+0]);
|
||||
int trx_nr = atoi(argv[idx+1]);
|
||||
int ts_nr = atoi(argv[idx+2]);
|
||||
int lchan_nr = atoi(argv[idx+3]);
|
||||
struct gsm_bts *bts;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
|
||||
bts = gsm_bts_num(net, bts_nr);
|
||||
if (!bts)
|
||||
return NULL;
|
||||
|
||||
trx = gsm_bts_trx_num(bts, trx_nr);
|
||||
if (!trx)
|
||||
return NULL;
|
||||
|
||||
if (ts_nr >= ARRAY_SIZE(trx->ts))
|
||||
return NULL;
|
||||
ts = &trx->ts[ts_nr];
|
||||
|
||||
if (lchan_nr >= ARRAY_SIZE(ts->lchan))
|
||||
return NULL;
|
||||
|
||||
return &ts->lchan[lchan_nr];
|
||||
}
|
||||
|
||||
#define BTS_T_T_L_STR \
|
||||
"BTS related commands\n" \
|
||||
"BTS number\n" \
|
||||
"TRX related commands\n" \
|
||||
"TRX number\n" \
|
||||
"timeslot related commands\n" \
|
||||
"timeslot number\n" \
|
||||
"logical channel commands\n" \
|
||||
"logical channel number\n"
|
||||
|
||||
DEFUN(bts_t_t_l_jitter_buf,
|
||||
bts_t_t_l_jitter_buf_cmd,
|
||||
"bts <0-0> trx <0-0> ts <0-7> lchan <0-1> rtp jitter-buffer <0-10000>",
|
||||
BTS_T_T_L_STR "RTP settings\n"
|
||||
"Jitter buffer\n" "Size of jitter buffer in (ms)\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet_from_vty(vty);
|
||||
struct gsm_lchan *lchan;
|
||||
int jitbuf_ms = atoi(argv[4]);
|
||||
|
||||
lchan = resolve_lchan(net, argv, 0);
|
||||
if (!lchan) {
|
||||
vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (!lchan->abis_ip.rtp_socket) {
|
||||
vty_out(vty, "%% this channel has no active RTP stream%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket,
|
||||
OSMO_RTP_P_JITBUF, jitbuf_ms);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
extern int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow);
|
||||
extern int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
|
||||
uint8_t seg_nr, uint8_t num_segs,
|
||||
const uint8_t *payload,
|
||||
uint8_t num_payload_bits);
|
||||
|
||||
DEFUN(bts_etsw_idle, bts_etsw_idle_cmd,
|
||||
"etsw-message MESSAGE",
|
||||
"ETSW Message\nMessage in Hex\n")
|
||||
{
|
||||
int rc;
|
||||
int segment = 0, rest = 56;
|
||||
|
||||
rc = osmo_hexparse(argv[0], etws_data, sizeof(etws_data));
|
||||
if (rc != 56) {
|
||||
vty_out(vty, "%%we expect 56 bytes of the data.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty_out(vty, "%% parsed: %s%s",
|
||||
osmo_hexdump(etws_data, rc), VTY_NEWLINE);
|
||||
etws_len = rc;
|
||||
|
||||
|
||||
for (segment = 0; segment < 5; ++segment) {
|
||||
struct bitvec bv = { 0, };
|
||||
bv.data_len = 14;
|
||||
bv.data = &etws_segment_data[segment][0];
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE, "Goint to create segment(%d) offset %d len %d\n",
|
||||
segment, segment * 12,
|
||||
rest >= 12 ? 12 : rest);
|
||||
construct_p1_rest_octets(&bv, 1);
|
||||
printf("CUR BIT: %d %s\n", bv.cur_bit,
|
||||
osmo_hexdump(&etws_data[segment * 12],
|
||||
rest >= 12 ? 12 : rest));
|
||||
construct_etws_prim_notif(&bv, 1, segment, 5,
|
||||
&etws_data[segment * 12],
|
||||
rest >= 12 ? 12 * 8 : rest * 8);
|
||||
etws_segment_len[segment] = (bv.cur_bit + 7) / 8;
|
||||
rest -= 12;
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE,
|
||||
"Created segment(%d) with len %d %s\n",
|
||||
segment, etws_segment_len[segment],
|
||||
osmo_hexdump(etws_segment_data[segment], etws_segment_len[segment]));
|
||||
}
|
||||
|
||||
etws_nr_seg = 5;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int bts_vty_init(const struct log_info *cat)
|
||||
{
|
||||
@@ -329,7 +693,27 @@ int bts_vty_init(const struct log_info *cat)
|
||||
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_oml_ip_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_rtp_bind_ip_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_rtp_jitbuf_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_band_cmd);
|
||||
install_element(BTS_NODE, &cfg_description_cmd);
|
||||
install_element(BTS_NODE, &cfg_no_description_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_paging_queue_size_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_paging_lifetime_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_default_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_params_cmd);
|
||||
|
||||
/* add and link to TRX config node */
|
||||
install_element(BTS_NODE, &cfg_bts_trx_cmd);
|
||||
install_node(&trx_node, config_write_dummy);
|
||||
install_default(TRX_NODE);
|
||||
|
||||
install_element(TRX_NODE, &cfg_trx_user_gain_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_pr_max_initial_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_pr_step_size_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_pr_step_interval_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
|
||||
install_element(ENABLE_NODE, &bts_etsw_idle_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS)
|
||||
LDADD = $(LIBOSMOCORE_LIBS)
|
||||
|
||||
bin_PROGRAMS = osmo-bts-bb
|
||||
|
||||
osmo_bts_bb_SOURCES = main.c
|
||||
osmo_bts_bb_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||
@@ -1,304 +0,0 @@
|
||||
/* Layer1 control code, talking L1CTL protocol with L1 on the phone */
|
||||
|
||||
/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* 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 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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <l1ctl_proto.h>
|
||||
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/gsmtap_util.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_58.h>
|
||||
#include <osmocom/gsm/rsl.h>
|
||||
|
||||
#include <osmocom/bb/common/l1ctl.h>
|
||||
#include <osmocom/bb/common/osmocom_data.h>
|
||||
#include <osmocom/bb/common/l1l2_interface.h>
|
||||
#include <osmocom/bb/common/lapdm.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
#include <osmo-bts/rtp.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
|
||||
static struct msgb *osmo_l1_alloc(uint8_t msg_type)
|
||||
{
|
||||
struct l1ctl_hdr *l1h;
|
||||
struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1");
|
||||
|
||||
if (!msg) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg->l1h = msgb_put(msg, sizeof(*l1h));
|
||||
l1h = (struct l1ctl_hdr *) msg->l1h;
|
||||
l1h->msg_type = msg_type;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
static int osmo_make_band_arfcn(struct osmocom_ms *ms, uint16_t arfcn)
|
||||
{
|
||||
/* TODO: Include the band */
|
||||
return arfcn;
|
||||
}
|
||||
|
||||
/* Receive L1CTL_DATA_IND (Data Indication from L1) */
|
||||
int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg)
|
||||
{
|
||||
struct l1ctl_info_dl *dl, dl_cpy;
|
||||
struct l1ctl_data_ind *ccch;
|
||||
struct lapdm_entity *le;
|
||||
struct rx_meas_stat *meas = &ms->meas;
|
||||
uint8_t chan_type, chan_ts, chan_ss;
|
||||
uint8_t gsmtap_chan_type;
|
||||
struct gsm_time tm;
|
||||
struct osmobts_ms *bts_ms = container_of(ms, struct osmobts_ms, ms);
|
||||
struct osmobts_lchan *lchan;
|
||||
|
||||
if (msgb_l3len(msg) < sizeof(*ccch)) {
|
||||
LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n",
|
||||
msgb_l3len(msg));
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dl = (struct l1ctl_info_dl *) msg->l1h;
|
||||
msg->l2h = dl->payload;
|
||||
ccch = (struct l1ctl_data_ind *) msg->l2h;
|
||||
|
||||
gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr));
|
||||
rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
|
||||
lchan = bts_ms->trx->slot[chan_ts].lchan[chan_ss];
|
||||
if (!lchan) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Data IND for non existing lchan\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
LOGP(DL1C, LOGL_NOTICE, "RX: %s | %s(%.4u/%.2u/%.2u) %d dBm\n",
|
||||
rsl_chan_nr_str(dl->chan_nr),
|
||||
hexdump(ccch->data, sizeof(ccch->data)),
|
||||
tm.t1, tm.t2, tm.t3, (int)dl->rx_level-110);
|
||||
|
||||
meas->last_fn = ntohl(dl->frame_nr);
|
||||
meas->frames++;
|
||||
meas->snr += dl->snr;
|
||||
meas->berr += dl->num_biterr;
|
||||
meas->rxlev += dl->rx_level;
|
||||
|
||||
if (dl->fire_crc >= 2) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "Dropping frame with %u bit errors\n",
|
||||
dl->num_biterr);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send CCCH data via GSMTAP */
|
||||
gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id);
|
||||
gsmtap_sendmsg(ntohs(dl->band_arfcn), chan_ts, gsmtap_chan_type, chan_ss,
|
||||
tm.fn, dl->rx_level-110, dl->snr, ccch->data,
|
||||
sizeof(ccch->data));
|
||||
|
||||
/* determine LAPDm entity based on SACCH or not */
|
||||
if (dl->link_id & 0x40)
|
||||
le = &lchan->l2_entity.lapdm_acch;
|
||||
else
|
||||
le = &lchan->l2_entity.lapdm_dcch;
|
||||
/* make local stack copy of l1ctl_info_dl, as LAPDm will
|
||||
* overwrite skb hdr */
|
||||
memcpy(&dl_cpy, dl, sizeof(dl_cpy));
|
||||
|
||||
/* pull the L1 header from the msgb */
|
||||
msgb_pull(msg, msg->l2h - (msg->l1h-sizeof(struct l1ctl_hdr)));
|
||||
msg->l1h = NULL;
|
||||
|
||||
/* send it up into LAPDm */
|
||||
l2_ph_data_ind(msg, le, &dl_cpy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receive L1CTL_DATA_CONF (Data Confirm from L1) */
|
||||
static int rx_ph_data_conf(struct osmocom_ms *ms, struct msgb *msg)
|
||||
{
|
||||
struct l1ctl_info_dl *dl;
|
||||
struct lapdm_entity *le;
|
||||
uint8_t chan_type, chan_ts, chan_ss;
|
||||
struct osmobts_ms *bts_ms = container_of(ms, struct osmobts_ms, ms);
|
||||
struct osmobts_lchan *lchan;
|
||||
|
||||
dl = (struct l1ctl_info_dl *) msg->l1h;
|
||||
|
||||
rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts);
|
||||
lchan = bts_ms->trx->slot[chan_ts].lchan[chan_ss];
|
||||
if (!lchan) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Data IND for non existing lchan\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* determine LAPDm entity based on SACCH or not */
|
||||
if (dl->link_id & 0x40)
|
||||
le = &lchan->l2_entity.lapdm_acch;
|
||||
else
|
||||
le = &lchan->l2_entity.lapdm_dcch;
|
||||
|
||||
/* send it up into LAPDm */
|
||||
l2_ph_data_conf(msg, le);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Transmit L1CTL_DATA_REQ */
|
||||
int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg,
|
||||
uint8_t chan_nr, uint8_t link_id)
|
||||
{
|
||||
struct l1ctl_hdr *l1h;
|
||||
struct l1ctl_info_ul *l1i_ul;
|
||||
uint8_t chan_type, chan_ts, chan_ss;
|
||||
uint8_t gsmtap_chan_type;
|
||||
|
||||
if (msgb_l2len(msg) > GSM_MACBLOCK_LEN) {
|
||||
LOGP(DL1C, LOGL_ERROR, "L1 cannot handle message length "
|
||||
"> 23 (%u)\n", msgb_l2len(msg));
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
} else if (msgb_l2len(msg) < GSM_MACBLOCK_LEN)
|
||||
LOGP(DL1C, LOGL_ERROR, "L1 message length < 23 (%u) "
|
||||
"doesn't seem right!\n", msgb_l2len(msg));
|
||||
|
||||
/* send copy via GSMTAP */
|
||||
rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts);
|
||||
gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id);
|
||||
gsmtap_sendmsg(0|0x4000, chan_ts, gsmtap_chan_type, chan_ss,
|
||||
0, 127, 255, msg->l2h, msgb_l2len(msg));
|
||||
|
||||
LOGP(DL1C, LOGL_NOTICE, "TX: %s | %s\n", rsl_chan_nr_str(chan_nr),
|
||||
hexdump(msg->l2h, msgb_l2len(msg)));
|
||||
|
||||
/* prepend uplink info header */
|
||||
l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul));
|
||||
|
||||
l1i_ul->chan_nr = chan_nr;
|
||||
l1i_ul->link_id = link_id;
|
||||
|
||||
/* prepend l1 header */
|
||||
msg->l1h = msgb_push(msg, sizeof(*l1h));
|
||||
l1h = (struct l1ctl_hdr *) msg->l1h;
|
||||
l1h->msg_type = L1CTL_DATA_REQ;
|
||||
|
||||
printf("todo: transcode message\n");
|
||||
|
||||
return osmo_send_l1(ms, msg);
|
||||
}
|
||||
|
||||
/* Transmit L1CTL_RESET_REQ */
|
||||
int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct l1ctl_reset *res;
|
||||
|
||||
msg = osmo_l1_alloc(L1CTL_RESET_REQ);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "Tx Reset Req (%u)\n", type);
|
||||
res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
|
||||
res->type = type;
|
||||
|
||||
return osmo_send_l1(ms, msg);
|
||||
}
|
||||
|
||||
/* Receive L1CTL_RESET_IND */
|
||||
static int rx_l1_reset(struct osmocom_ms *ms)
|
||||
{
|
||||
LOGP(DL1C, LOGL_INFO, "Layer1 Reset indication\n");
|
||||
dispatch_signal(SS_L1CTL, S_L1CTL_RESET, ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receive incoming data from L1 using L1CTL format */
|
||||
int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct l1ctl_hdr *l1h;
|
||||
struct l1ctl_info_dl *dl;
|
||||
|
||||
if (msgb_l2len(msg) < sizeof(*dl)) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n",
|
||||
msgb_l2len(msg));
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
l1h = (struct l1ctl_hdr *) msg->l1h;
|
||||
|
||||
/* move the l1 header pointer to point _BEHIND_ l1ctl_hdr,
|
||||
as the l1ctl header is of no interest to subsequent code */
|
||||
msg->l1h = l1h->data;
|
||||
|
||||
switch (l1h->msg_type) {
|
||||
case L1CTL_DATA_IND:
|
||||
rc = rx_ph_data_ind(ms, msg);
|
||||
break;
|
||||
case L1CTL_DATA_CONF:
|
||||
rc = rx_ph_data_conf(ms, msg);
|
||||
break;
|
||||
case L1CTL_RESET_IND:
|
||||
case L1CTL_RESET_CONF:
|
||||
rc = rx_l1_reset(ms);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", l1h->msg_type);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1ctl_tx_param_req(struct osmocom_ms *ms, uint8_t ta, uint8_t tx_power)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset,
|
||||
uint8_t combined)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* 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 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 <netinet/in.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
//#include <osmocom/bb/common/osmocom_data.h>
|
||||
#include <osmo-bts/support.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
#include <osmo-bts/rtp.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
|
||||
#include <net/if.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
struct log_target *stderr_target;
|
||||
char *debugs = "DL1C:DLAPDM:DABIS:DOML:DRSL:DSUM";
|
||||
|
||||
void *l23_ctx = NULL;
|
||||
static struct osmocom_bts *bts;
|
||||
int debug_set = 0;
|
||||
char *software_version = "0.0";
|
||||
uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 };
|
||||
char *bsc_host = "localhost";
|
||||
char *bts_id = "1801/0";
|
||||
int quit = 0;
|
||||
|
||||
// FIXME this is a hack
|
||||
static void get_mac(void)
|
||||
{
|
||||
struct if_nameindex *ifn = if_nameindex();
|
||||
struct ifreq ifr;
|
||||
int sock;
|
||||
int ret;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
return;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
if (!ifn)
|
||||
return;
|
||||
while (ifn->if_name) {
|
||||
strncpy(ifr.ifr_name, ifn->if_name, sizeof(ifr.ifr_name)-1);
|
||||
ret = ioctl(sock, SIOCGIFHWADDR, &ifr);
|
||||
if (ret == 0 && !!memcmp(ifr.ifr_hwaddr.sa_data,
|
||||
"\0\0\0\0\0\0", 6)) {
|
||||
memcpy(abis_mac, ifr.ifr_hwaddr.sa_data, 6);
|
||||
printf("Using MAC address of %s: "
|
||||
"'%02x:%02x:%02x:%02x:%02x:%02x'\n",
|
||||
ifn->if_name,
|
||||
abis_mac[0], abis_mac[1], abis_mac[2],
|
||||
abis_mac[3], abis_mac[4], abis_mac[5]);
|
||||
break;
|
||||
}
|
||||
ifn++;
|
||||
}
|
||||
// if_freenameindex(ifn);
|
||||
}
|
||||
|
||||
static void print_usage(const char *app)
|
||||
{
|
||||
printf("Usage: %s [option]\n", app);
|
||||
printf(" -h --help this text\n");
|
||||
printf(" -d --debug Change debug flags. (-d %s)\n", debugs);
|
||||
printf(" -i --bsc-ip IP address of the BSC\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"debug", 1, 0, 'd'},
|
||||
{"bsc-ip", 1, 0, 'i'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hd:i:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
exit(0);
|
||||
case 'd':
|
||||
log_parse_category_mask(stderr_target, optarg);
|
||||
debug_set = 1;
|
||||
break;
|
||||
case 'i':
|
||||
bsc_host = strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sighandler(int sigset)
|
||||
{
|
||||
if (sigset == SIGHUP || sigset == SIGPIPE)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "Signal %d recevied.\n", sigset);
|
||||
|
||||
/* in case there is a lockup during exit */
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
quit = 1;
|
||||
// dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
struct hostent *hostent;
|
||||
struct in_addr *ina;
|
||||
uint32_t bsc_ip;
|
||||
uint8_t maskv_tx[2], maskv_rx[2];
|
||||
|
||||
printf("((*))\n");
|
||||
printf(" |\n");
|
||||
printf(" / \\ OsmoBTS\n");
|
||||
|
||||
get_mac();
|
||||
bts_support_init();
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
log_init(&log_info);
|
||||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
log_set_all_filter(stderr_target, 1);
|
||||
|
||||
l23_ctx = talloc_named_const(NULL, 1, "layer2 context");
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
if (!debug_set)
|
||||
log_parse_category_mask(stderr_target, debugs);
|
||||
log_set_log_level(stderr_target, LOGL_INFO);
|
||||
|
||||
hostent = gethostbyname(bsc_host);
|
||||
if (!hostent) {
|
||||
fprintf(stderr, "Failed to resolve BSC hostname '%s'.\n",
|
||||
bsc_host);
|
||||
}
|
||||
ina = (struct in_addr *) hostent->h_addr;
|
||||
bsc_ip = ntohl(ina->s_addr);
|
||||
printf("Using BSC at IP: '%d.%d.%d.%d'\n", bsc_ip >> 24,
|
||||
(bsc_ip >> 16) & 0xff, (bsc_ip >> 8) & 0xff, bsc_ip & 0xff);
|
||||
|
||||
printf("Using BTS ID: '%s'\n", bts_id);
|
||||
bts = create_bts(1, bts_id);
|
||||
if (!bts)
|
||||
exit(-1);
|
||||
maskv_tx[0] = 0x55;
|
||||
maskv_rx[0] = 0x55;
|
||||
ret = create_ms(bts->trx[0], 1, maskv_tx, maskv_rx);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ret = abis_open(&bts->link, bsc_ip);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
signal(SIGINT, sighandler);
|
||||
signal(SIGHUP, sighandler);
|
||||
signal(SIGTERM, sighandler);
|
||||
signal(SIGPIPE, sighandler);
|
||||
|
||||
while (!quit) {
|
||||
work_bts(bts);
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
fail:
|
||||
destroy_bts(bts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,16 +1,34 @@
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
|
||||
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) -lortp
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
|
||||
COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) -lortp
|
||||
|
||||
bin_PROGRAMS = sysmobts sysmobts-remote l1fwd-proxy
|
||||
EXTRA_DIST = misc/sysmobts_mgr.h misc/sysmobts_misc.h misc/sysmobts_par.h \
|
||||
misc/sysmobts_eeprom.h misc/sysmobts_nl.h femtobts.h hw_misc.h \
|
||||
l1_fwd.h l1_if.h l1_transp.h eeprom.h utils.h oml_router.h
|
||||
|
||||
COMMON_SOURCES = main.c femtobts.c l1_if.c oml.c bts_model.c sysmobts_vty.c tch.c
|
||||
bin_PROGRAMS = sysmobts sysmobts-remote l1fwd-proxy sysmobts-mgr sysmobts-util
|
||||
|
||||
COMMON_SOURCES = main.c femtobts.c l1_if.c oml.c sysmobts_vty.c tch.c hw_misc.c calib_file.c \
|
||||
eeprom.c calib_fixup.c utils.c misc/sysmobts_par.c oml_router.c sysmobts_ctrl.c
|
||||
|
||||
sysmobts_SOURCES = $(COMMON_SOURCES) l1_transp_hw.c
|
||||
sysmobts_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||
sysmobts_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
|
||||
|
||||
sysmobts_remote_SOURCES = $(COMMON_SOURCES) l1_transp_fwd.c
|
||||
sysmobts_remote_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||
sysmobts_remote_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
|
||||
|
||||
l1fwd_proxy_SOURCES = l1_fwd_main.c l1_transp_hw.c
|
||||
l1fwd_proxy_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||
l1fwd_proxy_LDADD = $(top_builddir)/src/common/libbts.a $(COMMON_LDADD)
|
||||
|
||||
sysmobts_mgr_SOURCES = \
|
||||
misc/sysmobts_mgr.c misc/sysmobts_misc.c \
|
||||
misc/sysmobts_par.c misc/sysmobts_nl.c \
|
||||
misc/sysmobts_mgr_2050.c \
|
||||
misc/sysmobts_mgr_vty.c \
|
||||
misc/sysmobts_mgr_nl.c \
|
||||
misc/sysmobts_mgr_temp.c \
|
||||
eeprom.c
|
||||
sysmobts_mgr_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
|
||||
sysmobts_util_SOURCES = misc/sysmobts_util.c misc/sysmobts_par.c eeprom.c
|
||||
sysmobts_util_LDADD = $(LIBOSMOCORE_LIBS)
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
|
||||
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
|
||||
{
|
||||
//uint8_t mode = *TLVP_VAL(tp, RSL_IE_CHAN_MODE);
|
||||
//uint8_t type = *TLVP_VAL(tp, RSL_IE_ACT_TYPE);
|
||||
|
||||
lchan_activate(lchan);
|
||||
/* FIXME: only do this in case of success */
|
||||
|
||||
return rsl_tx_chan_act_ack(lchan, bts_model_get_time(lchan->ts->trx->bts));
|
||||
}
|
||||
|
||||
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
|
||||
{
|
||||
lchan_deactivate(lchan);
|
||||
return rsl_tx_rf_rel_ack(lchan);
|
||||
}
|
||||
|
||||
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
|
||||
{
|
||||
return lchan_deactivate_sacch(lchan);
|
||||
}
|
||||
|
||||
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct femtol1_hdl *fl1 = trx_femtol1_hdl(trx);
|
||||
|
||||
return l1if_activate_rf(fl1, 0);
|
||||
}
|
||||
461
src/osmo-bts-sysmo/calib_file.c
Normal file
461
src/osmo-bts-sysmo/calib_file.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/* sysmocom femtobts L1 calibration file routines*/
|
||||
|
||||
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1const.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
#include "femtobts.h"
|
||||
#include "eeprom.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct calib_file_desc {
|
||||
const char *fname;
|
||||
GsmL1_FreqBand_t band;
|
||||
int uplink;
|
||||
int rx;
|
||||
};
|
||||
|
||||
static const struct calib_file_desc calib_files[] = {
|
||||
{
|
||||
.fname = "calib_rxu_850.cfg",
|
||||
.band = GsmL1_FreqBand_850,
|
||||
.uplink = 1,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_rxu_900.cfg",
|
||||
.band = GsmL1_FreqBand_900,
|
||||
.uplink = 1,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_rxu_1800.cfg",
|
||||
.band = GsmL1_FreqBand_1800,
|
||||
.uplink = 1,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_rxu_1900.cfg",
|
||||
.band = GsmL1_FreqBand_1900,
|
||||
.uplink = 1,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_rxd_850.cfg",
|
||||
.band = GsmL1_FreqBand_850,
|
||||
.uplink = 0,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_rxd_900.cfg",
|
||||
.band = GsmL1_FreqBand_900,
|
||||
.uplink = 0,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_rxd_1800.cfg",
|
||||
.band = GsmL1_FreqBand_1800,
|
||||
.uplink = 0,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_rxd_1900.cfg",
|
||||
.band = GsmL1_FreqBand_1900,
|
||||
.uplink = 0,
|
||||
.rx = 1,
|
||||
}, {
|
||||
.fname = "calib_tx_850.cfg",
|
||||
.band = GsmL1_FreqBand_850,
|
||||
.uplink = 0,
|
||||
.rx = 0,
|
||||
}, {
|
||||
.fname = "calib_tx_900.cfg",
|
||||
.band = GsmL1_FreqBand_900,
|
||||
.uplink = 0,
|
||||
.rx = 0,
|
||||
}, {
|
||||
.fname = "calib_tx_1800.cfg",
|
||||
.band = GsmL1_FreqBand_1800,
|
||||
.uplink = 0,
|
||||
.rx = 0,
|
||||
}, {
|
||||
.fname = "calib_tx_1900.cfg",
|
||||
.band = GsmL1_FreqBand_1900,
|
||||
.uplink = 0,
|
||||
.rx = 0,
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
static const unsigned int arrsize_by_band[] = {
|
||||
[GsmL1_FreqBand_850] = 124,
|
||||
[GsmL1_FreqBand_900] = 194,
|
||||
[GsmL1_FreqBand_1800] = 374,
|
||||
[GsmL1_FreqBand_1900] = 299
|
||||
};
|
||||
|
||||
/* determine next calibration file index based on supported bands */
|
||||
static int next_calib_file_idx(uint32_t band_mask, int last_idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = last_idx+1; i < ARRAY_SIZE(calib_files); i++) {
|
||||
int band = band_femto2osmo(calib_files[i].band);
|
||||
if (band < 0)
|
||||
continue;
|
||||
if (band_mask & band)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static float read_float(FILE *in)
|
||||
{
|
||||
int rc;
|
||||
float f = 0.0f;
|
||||
|
||||
rc = fscanf(in, "%f\n", &f);
|
||||
if (rc != 1)
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Reading a float from calib data failed.\n");
|
||||
return f;
|
||||
}
|
||||
|
||||
static int read_int(FILE *in)
|
||||
{
|
||||
int rc;
|
||||
int i = 0;
|
||||
|
||||
rc = fscanf(in, "%d\n", &i);
|
||||
if (rc != 1)
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Reading an int from calib data failed.\n");
|
||||
return i;
|
||||
}
|
||||
|
||||
/* some particular units have calibration data that is incompatible with
|
||||
* firmware >= 3.3, so we need to alter it as follows: */
|
||||
static const float delta_by_band[Num_GsmL1_FreqBand] = {
|
||||
[GsmL1_FreqBand_850] = -2.5f,
|
||||
[GsmL1_FreqBand_900] = -2.0f,
|
||||
[GsmL1_FreqBand_1800] = -8.0f,
|
||||
[GsmL1_FreqBand_1900] = -12.0f,
|
||||
};
|
||||
|
||||
extern const uint8_t fixup_macs[95][6];
|
||||
|
||||
|
||||
static void determine_fixup(struct femtol1_hdl *fl1h)
|
||||
{
|
||||
uint8_t macaddr[6];
|
||||
int rc, i;
|
||||
|
||||
rc = eeprom_ReadEthAddr(macaddr);
|
||||
if (rc != EEPROM_SUCCESS) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Unable to read Ethenet MAC from EEPROM\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* assume no fixup is needed */
|
||||
fl1h->fixup_needed = FIXUP_NOT_NEEDED;
|
||||
|
||||
if (fl1h->hw_info.dsp_version[0] < 3 ||
|
||||
(fl1h->hw_info.dsp_version[0] == 3 &&
|
||||
fl1h->hw_info.dsp_version[1] < 3)) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "No calibration table fix-up needed, "
|
||||
"firmware < 3.3\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(fixup_macs)/6; i++) {
|
||||
if (!memcmp(fixup_macs[i], macaddr, 6)) {
|
||||
fl1h->fixup_needed = FIXUP_NEEDED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DL1C, LOGL_NOTICE, "MAC Address is %02x:%02x:%02x:%02x:%02x:%02x -> %s\n",
|
||||
macaddr[0], macaddr[1], macaddr[2], macaddr[3],
|
||||
macaddr[4], macaddr[5],
|
||||
fl1h->fixup_needed == FIXUP_NEEDED ? "FIXUP" : "NO FIXUP");
|
||||
}
|
||||
|
||||
static int fixup_needed(struct femtol1_hdl *fl1h)
|
||||
{
|
||||
if (fl1h->fixup_needed == FIXUP_UNITILIAZED)
|
||||
determine_fixup(fl1h);
|
||||
|
||||
return fl1h->fixup_needed == FIXUP_NEEDED;
|
||||
}
|
||||
|
||||
static void calib_fixup_rx(struct femtol1_hdl *fl1h, SuperFemto_Prim_t *prim)
|
||||
{
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
|
||||
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
|
||||
|
||||
if (fixup_needed(fl1h))
|
||||
rx->fExtRxGain += delta_by_band[rx->freqBand];
|
||||
#endif
|
||||
}
|
||||
|
||||
static int calib_file_read(const char *path, const struct calib_file_desc *desc,
|
||||
SuperFemto_Prim_t *prim)
|
||||
{
|
||||
FILE *in;
|
||||
char fname[PATH_MAX];
|
||||
int i;
|
||||
|
||||
fname[0] = '\0';
|
||||
snprintf(fname, sizeof(fname)-1, "%s/%s", path, desc->fname);
|
||||
fname[sizeof(fname)-1] = '\0';
|
||||
|
||||
in = fopen(fname, "r");
|
||||
if (!in) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Failed to open '%s' for calibration data.\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
|
||||
if (desc->rx) {
|
||||
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
|
||||
memset(rx, 0, sizeof(*rx));
|
||||
|
||||
prim->id = SuperFemto_PrimId_SetRxCalibTblReq;
|
||||
|
||||
rx->freqBand = desc->band;
|
||||
rx->bUplink = desc->uplink;
|
||||
|
||||
rx->fExtRxGain = read_float(in);
|
||||
rx->fRxMixGainCorr = read_float(in);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++)
|
||||
rx->fRxLnaGainCorr[i] = read_float(in);
|
||||
|
||||
for (i = 0; i < arrsize_by_band[desc->band]; i++)
|
||||
rx->fRxRollOffCorr[i] = read_float(in);
|
||||
|
||||
if (desc->uplink) {
|
||||
rx->u8IqImbalMode = read_int(in);
|
||||
printf("%s: u8IqImbalMode=%d\n", desc->fname, rx->u8IqImbalMode);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++)
|
||||
rx->u16IqImbalCorr[i] = read_int(in);
|
||||
}
|
||||
} else {
|
||||
SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq;
|
||||
memset(tx, 0, sizeof(*tx));
|
||||
|
||||
prim->id = SuperFemto_PrimId_SetTxCalibTblReq;
|
||||
|
||||
tx->freqBand = desc->band;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++)
|
||||
tx->fTxGainGmsk[i] = read_float(in);
|
||||
|
||||
tx->fTx8PskCorr = read_float(in);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++)
|
||||
tx->fTxExtAttCorr[i] = read_float(in);
|
||||
|
||||
for (i = 0; i < arrsize_by_band[desc->band]; i++)
|
||||
tx->fTxRollOffCorr[i] = read_float(in);
|
||||
}
|
||||
#else
|
||||
#warning Format of calibration tables before API version 2.4.0 not supported
|
||||
#endif
|
||||
fclose(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calib_eeprom_read(const struct calib_file_desc *desc, SuperFemto_Prim_t *prim)
|
||||
{
|
||||
eeprom_Error_t eerr;
|
||||
int i;
|
||||
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,4,0)
|
||||
if (desc->rx) {
|
||||
SuperFemto_SetRxCalibTblReq_t *rx = &prim->u.setRxCalibTblReq;
|
||||
eeprom_RxCal_t rx_cal;
|
||||
|
||||
memset(rx, 0, sizeof(*rx));
|
||||
|
||||
prim->id = SuperFemto_PrimId_SetRxCalibTblReq;
|
||||
|
||||
rx->freqBand = desc->band;
|
||||
rx->bUplink = desc->uplink;
|
||||
|
||||
eerr = eeprom_ReadRxCal(desc->band, desc->uplink, &rx_cal);
|
||||
if (eerr != EEPROM_SUCCESS) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Error reading RxCalibration "
|
||||
"from EEPROM, band=%d, ul=%d, err=%d\n",
|
||||
desc->band, desc->uplink, eerr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rx->fExtRxGain = rx_cal.fExtRxGain;
|
||||
rx->fRxMixGainCorr = rx_cal.fRxMixGainCorr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rx->fRxLnaGainCorr); i++)
|
||||
rx->fRxLnaGainCorr[i] = rx_cal.fRxLnaGainCorr[i];
|
||||
|
||||
for (i = 0; i < arrsize_by_band[desc->band]; i++)
|
||||
rx->fRxRollOffCorr[i] = rx_cal.fRxRollOffCorr[i];
|
||||
|
||||
if (desc->uplink) {
|
||||
rx->u8IqImbalMode = rx_cal.u8IqImbalMode;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rx->u16IqImbalCorr); i++)
|
||||
rx->u16IqImbalCorr[i] = rx_cal.u16IqImbalCorr[i];
|
||||
}
|
||||
} else {
|
||||
SuperFemto_SetTxCalibTblReq_t *tx = &prim->u.setTxCalibTblReq;
|
||||
eeprom_TxCal_t tx_cal;
|
||||
|
||||
memset(tx, 0, sizeof(*tx));
|
||||
|
||||
prim->id = SuperFemto_PrimId_SetTxCalibTblReq;
|
||||
tx->freqBand = desc->band;
|
||||
|
||||
eerr = eeprom_ReadTxCal(desc->band, &tx_cal);
|
||||
if (eerr != EEPROM_SUCCESS) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Error reading TxCalibration "
|
||||
"from EEPROM, band=%d, err=%d\n",
|
||||
desc->band, eerr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tx->fTxGainGmsk); i++)
|
||||
tx->fTxGainGmsk[i] = tx_cal.fTxGainGmsk[i];
|
||||
|
||||
tx->fTx8PskCorr = tx_cal.fTx8PskCorr;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tx->fTxExtAttCorr); i++)
|
||||
tx->fTxExtAttCorr[i] = tx_cal.fTxExtAttCorr[i];
|
||||
|
||||
for (i = 0; i < arrsize_by_band[desc->band]; i++)
|
||||
tx->fTxRollOffCorr[i] = tx_cal.fTxRollOffCorr[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* iteratively download the calibration data into the L1 */
|
||||
|
||||
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
||||
void *data);
|
||||
|
||||
/* send the calibration table for a single specified file */
|
||||
static int calib_file_send(struct femtol1_hdl *fl1h,
|
||||
const struct calib_file_desc *desc)
|
||||
{
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
msg = sysp_msgb_alloc();
|
||||
|
||||
if (fl1h->calib_path)
|
||||
rc = calib_file_read(fl1h->calib_path, desc, msgb_sysprim(msg));
|
||||
else
|
||||
rc = calib_eeprom_read(desc, msgb_sysprim(msg));
|
||||
if (rc < 0) {
|
||||
msgb_free(msg);
|
||||
|
||||
/* still, we'd like to continue trying to load
|
||||
* calibration for all other bands */
|
||||
st->last_file_idx = next_calib_file_idx(fl1h->hw_info.band_support,
|
||||
st->last_file_idx);
|
||||
if (st->last_file_idx >= 0)
|
||||
return calib_file_send(fl1h,
|
||||
&calib_files[st->last_file_idx]);
|
||||
else
|
||||
return rc;
|
||||
}
|
||||
calib_fixup_rx(fl1h, msgb_sysprim(msg));
|
||||
|
||||
return l1if_req_compl(fl1h, msg, calib_send_compl_cb, NULL);
|
||||
}
|
||||
|
||||
/* completion callback after every SetCalibTbl is confirmed */
|
||||
static int calib_send_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
||||
void *data)
|
||||
{
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
struct calib_send_state *st = &fl1h->st;
|
||||
|
||||
LOGP(DL1C, LOGL_NOTICE, "L1 calibration table %s loaded (src: %s)\n",
|
||||
calib_files[st->last_file_idx].fname,
|
||||
fl1h->calib_path ? "file" : "eeprom");
|
||||
|
||||
msgb_free(l1_msg);
|
||||
|
||||
st->last_file_idx = next_calib_file_idx(fl1h->hw_info.band_support,
|
||||
st->last_file_idx);
|
||||
if (st->last_file_idx >= 0)
|
||||
return calib_file_send(fl1h,
|
||||
&calib_files[st->last_file_idx]);
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "L1 calibration table loading complete!\n");
|
||||
eeprom_free_resources();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int calib_load(struct femtol1_hdl *fl1h)
|
||||
{
|
||||
#if SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,4,0)
|
||||
LOGP(DL1C, LOGL_ERROR, "L1 calibration is not supported on pre 2.4.0 firmware.\n");
|
||||
return -1;
|
||||
#else
|
||||
int idx = next_calib_file_idx(fl1h->hw_info.band_support, -1);
|
||||
if (idx < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "No band_support?!?\n");
|
||||
return -1;
|
||||
}
|
||||
return calib_file_send(fl1h, &calib_files[idx]);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
SuperFemto_Prim_t p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(calib_files); i++) {
|
||||
memset(&p, 0, sizeof(p));
|
||||
calib_read_file(argv[1], &calib_files[i], &p);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
101
src/osmo-bts-sysmo/calib_fixup.c
Normal file
101
src/osmo-bts-sysmo/calib_fixup.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/* AUTOGENERATED, DO NOT EDIT */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
const uint8_t fixup_macs[95][6] = {
|
||||
{ 0x00, 0x0D, 0xCC, 0x08, 0x02, 0x3B },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x31 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x32 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x33 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x34 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x35 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x36 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x37 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x38 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x39 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3A },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3C },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3D },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x3E },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x40 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x41 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x42 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x43 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x44 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x45 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x46 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x47 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x48 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x49 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4A },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4B },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4C },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4D },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4E },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x4F },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x50 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x51 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x52 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x53 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x55 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x56 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x57 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x58 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x59 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5A },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5B },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5C },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5D },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5E },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x5F },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x60 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x97 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x98 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x99 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9A },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9B },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9C },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9D },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9E },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0x9F },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA0 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA1 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA3 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA4 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA5 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA6 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA7 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA8 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xA9 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAA },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAB },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAC },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAD },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAE },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xAF },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB0 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB1 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB2 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB3 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB4 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB5 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB6 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB7 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB8 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xB9 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBA },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBB },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBC },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBE },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xBF },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC0 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC1 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC3 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC6 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC7 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC8 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xC9 },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xCA },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xCB },
|
||||
{ 0x00, 0xD0, 0xCC, 0x08, 0x02, 0xCD },
|
||||
};
|
||||
1426
src/osmo-bts-sysmo/eeprom.c
Normal file
1426
src/osmo-bts-sysmo/eeprom.c
Normal file
File diff suppressed because it is too large
Load Diff
296
src/osmo-bts-sysmo/eeprom.h
Normal file
296
src/osmo-bts-sysmo/eeprom.h
Normal file
@@ -0,0 +1,296 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* **** I
|
||||
* ****** ***
|
||||
* ******* ****
|
||||
* ******** **** **** **** ********* ******* **** ***********
|
||||
* ********* **** **** **** ********* ************** *************
|
||||
* **** ***** **** **** **** **** ***** ****** ***** ****
|
||||
* **** ***** **** **** **** **** ***** **** **** ****
|
||||
* **** ********* **** **** **** **** **** **** ****
|
||||
* **** ******** **** ****I **** ***** ***** **** ****
|
||||
* **** ****** ***** ****** ***** ****** ******* ****** *******
|
||||
* **** **** ************ ****** ************* *************
|
||||
* **** *** **** **** **** ***** **** ***** ****
|
||||
* ****
|
||||
* I N N O V A T I O N T O D A Y F O R T O M M O R O W ****
|
||||
* ***
|
||||
*
|
||||
***************************************************************************
|
||||
*
|
||||
* Project : SuperFemto
|
||||
* File : eeprom.h
|
||||
* Description : EEPROM interface.
|
||||
*
|
||||
* Copyright (c) Nutaq. 2012
|
||||
*
|
||||
***************************************************************************
|
||||
*
|
||||
* "$Revision: 1.1 $"
|
||||
* "$Name: $"
|
||||
* "$Date: 2012/06/20 02:18:30 $"
|
||||
* "$Author: Yves.Godin $"
|
||||
*
|
||||
***************************************************************************/
|
||||
#ifndef EEPROM_H__
|
||||
#define EEPROM_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Public constants *
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* EEPROM error code
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
EEPROM_SUCCESS = 0, ///< Success
|
||||
EEPROM_ERR_DEVICE = -1, ///< Device access error
|
||||
EEPROM_ERR_PARITY = -2, ///< Parity error
|
||||
EEPROM_ERR_UNAVAILABLE = -3, ///< Information unavailable
|
||||
EEPROM_ERR_INVALID = -4, ///< Invalid format
|
||||
EEPROM_ERR_UNSUPPORTED = -5, ///< Unsupported format
|
||||
} eeprom_Error_t;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Struct : eeprom_SysInfo_t
|
||||
************************************************************************//**
|
||||
*
|
||||
* SuperFemto system information.
|
||||
*
|
||||
***************************************************************************/
|
||||
typedef struct eeprom_SysInfo
|
||||
{
|
||||
char szSn[16]; ///< Serial number
|
||||
uint8_t u8Rev; ///< Board revision
|
||||
uint8_t u8Tcxo; ///< TCXO present (0:absent, 1:present, X:unknown)
|
||||
uint8_t u8Ocxo; ///< OCXO present (0:absent, 1:present, X:unknown)
|
||||
uint8_t u8GSM850; ///< GSM-850 supported (0:unsupported, 1:supported, X:unknown)
|
||||
uint8_t u8GSM900; ///< GSM-900 supported (0:unsupported, 1:supported, X:unknown)
|
||||
uint8_t u8DCS1800; ///< GSM-1800 supported (0:unsupported, 1:supported, X:unknown)
|
||||
uint8_t u8PCS1900; ///< GSM-1900 supported (0:unsupported, 1:supported, X:unknown)
|
||||
} eeprom_SysInfo_t;
|
||||
|
||||
/****************************************************************************
|
||||
* Struct : eeprom_RfClockCal_t
|
||||
************************************************************************//**
|
||||
*
|
||||
* SuperFemto RF clock calibration.
|
||||
*
|
||||
***************************************************************************/
|
||||
typedef struct eeprom_RfClockCal
|
||||
{
|
||||
int iClkCor; ///< Clock correction value in PPB.
|
||||
uint8_t u8ClkSrc; ///< Clock source (0:None, 1:OCXO, 2:TCXO, 3:External, 4:GPS PPS, 5:reserved, 6:RX, 7:Edge)
|
||||
} eeprom_RfClockCal_t;
|
||||
|
||||
/****************************************************************************
|
||||
* Struct : eeprom_TxCal_t
|
||||
************************************************************************//**
|
||||
*
|
||||
* SuperFemto transmit calibration table.
|
||||
*
|
||||
***************************************************************************/
|
||||
typedef struct eeprom_TxCal
|
||||
{
|
||||
float fTxGainGmsk[80]; ///< Gain setting for GMSK output level from +50dBm to -29 dBm
|
||||
float fTx8PskCorr; ///< Gain adjustment for 8 PSK (default to +3.25 dB)
|
||||
float fTxExtAttCorr[31]; ///< Gain adjustment for external attenuator (0:@1dB, 1:@2dB, ..., 31:@32dB)
|
||||
float fTxRollOffCorr[374]; /**< Gain correction for each ARFCN
|
||||
for GSM-850 : 0=128, 1:129, ..., 123:251, [124-373]:unused
|
||||
for GSM-900 : 0=955, 1:956, ..., 70:1, ..., 317:956, [318-373]:unused
|
||||
for DCS-1800: 0=512, 1:513, ..., 373:885
|
||||
for PCS-1900: 0=512, 1:513, ..., 298:810, [299-373]:unused */
|
||||
} eeprom_TxCal_t;
|
||||
|
||||
/****************************************************************************
|
||||
* Struct : eeprom_RxCal_t
|
||||
************************************************************************//**
|
||||
*
|
||||
* SuperFemto receive calibration table.
|
||||
*
|
||||
***************************************************************************/
|
||||
typedef struct eeprom_RxCal
|
||||
{
|
||||
float fExtRxGain; ///< External RX gain
|
||||
float fRxMixGainCorr; ///< Mixer gain error compensation
|
||||
float fRxLnaGainCorr[3]; ///< LNA gain error compensation (1:@-12 dB, 2:@-24 dB, 3:@-36 dB)
|
||||
float fRxRollOffCorr[374]; /***< Frequency roll-off compensation
|
||||
for GSM-850 : 0=128, 1:129, ..., 123:251, [124-373]:unused
|
||||
for GSM-900 : 0=955, 1:956, ..., 70:1, ..., 317:956, [318-373]:unused
|
||||
for DCS-1800: 0=512, 1:513, ..., 373:885
|
||||
for PCS-1900: 0=512, 1:513, ..., 298:810, [299-373]:unused */
|
||||
uint8_t u8IqImbalMode; ///< IQ imbalance mode (0:off, 1:on, 2:auto)
|
||||
uint16_t u16IqImbalCorr[4]; ///< IQ imbalance compensation
|
||||
} eeprom_RxCal_t;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Public functions *
|
||||
****************************************************************************/
|
||||
|
||||
eeprom_Error_t eeprom_ReadEthAddr( uint8_t *ethaddr );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_ResetCfg
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function reset the content of the EEPROM config area.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_ResetCfg( void );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_ReadSysInfo
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function reads the system information from the EEPROM.
|
||||
*
|
||||
* @param [inout] pTime
|
||||
* Pointer to a system info structure.
|
||||
*
|
||||
* @param [inout] pSysInfo
|
||||
* Pointer to a system info structure.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_ReadSysInfo( eeprom_SysInfo_t *pSysInfo );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_WriteSysInfo
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function writes the system information to the EEPROM.
|
||||
*
|
||||
* @param [in] pSysInfo
|
||||
* Pointer to the system info structure to be written.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_WriteSysInfo( const eeprom_SysInfo_t *pSysInfo );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_ReadRfClockCal
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function reads the RF clock calibration data from the EEPROM.
|
||||
*
|
||||
* @param [inout] pRfClockCal
|
||||
* Pointer to a RF clock calibration structure.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_ReadRfClockCal( eeprom_RfClockCal_t *pRfClockCal );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_WriteRfClockCal
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function writes the RF clock calibration data to the EEPROM.
|
||||
*
|
||||
* @param [in] pSysInfo
|
||||
* Pointer to the system info structure to be written.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_WriteRfClockCal( const eeprom_RfClockCal_t *pRfClockCal );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_ReadTxCal
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function reads the TX calibration tables for the specified band from
|
||||
* the EEPROM.
|
||||
*
|
||||
* @param [in] iBand
|
||||
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
|
||||
*
|
||||
* @param [inout] pTxCal
|
||||
* Pointer to a TX calibration table structure.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_ReadTxCal( int iBand, eeprom_TxCal_t *pTxCal );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_WriteTxCal
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function writes the TX calibration tables for the specified band to
|
||||
* the EEPROM.
|
||||
*
|
||||
* @param [in] iBand
|
||||
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
|
||||
*
|
||||
* @param [in] pTxCal
|
||||
* Pointer to a TX calibration table structure.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_WriteTxCal( int iBand, const eeprom_TxCal_t *pTxCal );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_ReadRxCal
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function reads the RX calibration tables for the specified band from
|
||||
* the EEPROM.
|
||||
*
|
||||
* @param [in] iBand
|
||||
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
|
||||
*
|
||||
* @param [in] iUplink
|
||||
* Uplink flag (0:downlink, X:downlink).
|
||||
*
|
||||
* @param [inout] pRxCal
|
||||
* Pointer to a RX calibration table structure.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_ReadRxCal( int iBand, int iUplink, eeprom_RxCal_t *pRxCal );
|
||||
|
||||
/****************************************************************************
|
||||
* Function : eeprom_WriteRxCal
|
||||
************************************************************************//**
|
||||
*
|
||||
* This function writes the RX calibration tables for the specified band to
|
||||
* the EEPROM.
|
||||
*
|
||||
* @param [in] iBand
|
||||
* GSM band (0:GSM-850, 1:GSM-900, 2:DCS-1800, 3:PCS-1900).
|
||||
*
|
||||
* @param [in] iUplink
|
||||
* Uplink flag (0:downlink, X:downlink).
|
||||
*
|
||||
* @param [in] pRxCal
|
||||
* Pointer to a RX calibration table structure.
|
||||
*
|
||||
* @return
|
||||
* 0 if or an error core.
|
||||
*
|
||||
****************************************************************************/
|
||||
eeprom_Error_t eeprom_WriteRxCal( int iBand, int iUplink, const eeprom_RxCal_t *pRxCal );
|
||||
|
||||
void eeprom_free_resources(void);
|
||||
|
||||
#endif // EEPROM_H__
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sysmocom/femtobts/femtobts.h>
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1const.h>
|
||||
#include <sysmocom/femtobts/gsml1dbg.h>
|
||||
|
||||
@@ -91,51 +91,89 @@ const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM] = {
|
||||
[GsmL1_PrimId_MphMeasureReq] = GsmL1_PrimId_MphMeasureCnf,
|
||||
};
|
||||
|
||||
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM] = {
|
||||
[FemtoBts_PrimId_SystemInfoReq] = L1P_T_REQ,
|
||||
[FemtoBts_PrimId_SystemInfoCnf] = L1P_T_CONF,
|
||||
[FemtoBts_PrimId_SystemFailureInd] = L1P_T_IND,
|
||||
[FemtoBts_PrimId_ActivateRfReq] = L1P_T_REQ,
|
||||
[FemtoBts_PrimId_ActivateRfCnf] = L1P_T_CONF,
|
||||
[FemtoBts_PrimId_DeactivateRfReq] = L1P_T_REQ,
|
||||
[FemtoBts_PrimId_DeactivateRfCnf] = L1P_T_CONF,
|
||||
[FemtoBts_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
|
||||
[FemtoBts_PrimId_RfClockInfoReq] = L1P_T_REQ,
|
||||
[FemtoBts_PrimId_RfClockInfoCnf] = L1P_T_CONF,
|
||||
[FemtoBts_PrimId_RfClockSetupReq] = L1P_T_REQ,
|
||||
[FemtoBts_PrimId_RfClockSetupCnf] = L1P_T_CONF,
|
||||
[FemtoBts_PrimId_Layer1ResetReq] = L1P_T_REQ,
|
||||
[FemtoBts_PrimId_Layer1ResetCnf] = L1P_T_CONF,
|
||||
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM] = {
|
||||
[SuperFemto_PrimId_SystemInfoReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_SystemInfoCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_SystemFailureInd] = L1P_T_IND,
|
||||
[SuperFemto_PrimId_ActivateRfReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_ActivateRfCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_DeactivateRfReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_DeactivateRfCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_SetTraceFlagsReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_RfClockInfoReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_RfClockInfoCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_RfClockSetupReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_RfClockSetupCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_Layer1ResetReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_Layer1ResetCnf] = L1P_T_CONF,
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||
[SuperFemto_PrimId_GetTxCalibTblReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_GetTxCalibTblCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_SetTxCalibTblReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_SetTxCalibTblCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_GetRxCalibTblReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_GetRxCalibTblCnf] = L1P_T_CONF,
|
||||
[SuperFemto_PrimId_SetRxCalibTblReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_SetRxCalibTblCnf] = L1P_T_CONF,
|
||||
#endif
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3,6,0)
|
||||
[SuperFemto_PrimId_MuteRfReq] = L1P_T_REQ,
|
||||
[SuperFemto_PrimId_MuteRfCnf] = L1P_T_CONF,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1] = {
|
||||
{ FemtoBts_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
|
||||
{ FemtoBts_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
|
||||
{ FemtoBts_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
|
||||
{ FemtoBts_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
|
||||
{ FemtoBts_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
|
||||
{ FemtoBts_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
|
||||
{ FemtoBts_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
|
||||
{ FemtoBts_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
|
||||
{ FemtoBts_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
|
||||
{ FemtoBts_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
|
||||
{ FemtoBts_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
|
||||
{ FemtoBts_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
|
||||
{ FemtoBts_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
|
||||
{ FemtoBts_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
|
||||
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1] = {
|
||||
{ SuperFemto_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
|
||||
{ SuperFemto_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
|
||||
{ SuperFemto_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
|
||||
{ SuperFemto_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
|
||||
{ SuperFemto_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
|
||||
{ SuperFemto_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
|
||||
{ SuperFemto_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
|
||||
{ SuperFemto_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
|
||||
{ SuperFemto_PrimId_RfClockInfoReq, "RF-CLOCK-INFO.req" },
|
||||
{ SuperFemto_PrimId_RfClockInfoCnf, "RF-CLOCK-INFO.conf" },
|
||||
{ SuperFemto_PrimId_RfClockSetupReq, "RF-CLOCK-SETUP.req" },
|
||||
{ SuperFemto_PrimId_RfClockSetupCnf, "RF-CLOCK-SETUP.conf" },
|
||||
{ SuperFemto_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
|
||||
{ SuperFemto_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||
{ SuperFemto_PrimId_GetTxCalibTblReq, "GET-TX-CALIB.req" },
|
||||
{ SuperFemto_PrimId_GetTxCalibTblCnf, "GET-TX-CALIB.cnf" },
|
||||
{ SuperFemto_PrimId_SetTxCalibTblReq, "SET-TX-CALIB.req" },
|
||||
{ SuperFemto_PrimId_SetTxCalibTblCnf, "SET-TX-CALIB.cnf" },
|
||||
{ SuperFemto_PrimId_GetRxCalibTblReq, "GET-RX-CALIB.req" },
|
||||
{ SuperFemto_PrimId_GetRxCalibTblCnf, "GET-RX-CALIB.cnf" },
|
||||
{ SuperFemto_PrimId_SetRxCalibTblReq, "SET-RX-CALIB.req" },
|
||||
{ SuperFemto_PrimId_SetRxCalibTblCnf, "SET-RX-CALIB.cnf" },
|
||||
#endif
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3,6,0)
|
||||
{ SuperFemto_PrimId_MuteRfReq, "MUTE-RF.req" },
|
||||
{ SuperFemto_PrimId_MuteRfCnf, "MUTE-RF.cnf" },
|
||||
#endif
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM] = {
|
||||
[FemtoBts_PrimId_SystemInfoReq] = FemtoBts_PrimId_SystemInfoCnf,
|
||||
[FemtoBts_PrimId_ActivateRfReq] = FemtoBts_PrimId_ActivateRfCnf,
|
||||
[FemtoBts_PrimId_DeactivateRfReq] = FemtoBts_PrimId_DeactivateRfCnf,
|
||||
[FemtoBts_PrimId_RfClockInfoReq] = FemtoBts_PrimId_RfClockInfoCnf,
|
||||
[FemtoBts_PrimId_RfClockSetupReq] = FemtoBts_PrimId_RfClockSetupCnf,
|
||||
[FemtoBts_PrimId_Layer1ResetReq] = FemtoBts_PrimId_Layer1ResetCnf,
|
||||
const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM] = {
|
||||
[SuperFemto_PrimId_SystemInfoReq] = SuperFemto_PrimId_SystemInfoCnf,
|
||||
[SuperFemto_PrimId_ActivateRfReq] = SuperFemto_PrimId_ActivateRfCnf,
|
||||
[SuperFemto_PrimId_DeactivateRfReq] = SuperFemto_PrimId_DeactivateRfCnf,
|
||||
[SuperFemto_PrimId_RfClockInfoReq] = SuperFemto_PrimId_RfClockInfoCnf,
|
||||
[SuperFemto_PrimId_RfClockSetupReq] = SuperFemto_PrimId_RfClockSetupCnf,
|
||||
[SuperFemto_PrimId_Layer1ResetReq] = SuperFemto_PrimId_Layer1ResetCnf,
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||
[SuperFemto_PrimId_GetTxCalibTblReq] = SuperFemto_PrimId_GetTxCalibTblCnf,
|
||||
[SuperFemto_PrimId_SetTxCalibTblReq] = SuperFemto_PrimId_SetTxCalibTblCnf,
|
||||
[SuperFemto_PrimId_GetRxCalibTblReq] = SuperFemto_PrimId_GetRxCalibTblCnf,
|
||||
[SuperFemto_PrimId_SetRxCalibTblReq] = SuperFemto_PrimId_SetRxCalibTblCnf,
|
||||
#endif
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3,6,0)
|
||||
[SuperFemto_PrimId_MuteRfReq] = SuperFemto_PrimId_MuteRfCnf,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
|
||||
{ GsmL1_Sapi_Idle, "IDLE" },
|
||||
{ GsmL1_Sapi_Fcch, "FCCH" },
|
||||
{ GsmL1_Sapi_Sch, "SCH" },
|
||||
{ GsmL1_Sapi_Sacch, "SACCH" },
|
||||
@@ -220,10 +258,42 @@ const struct value_string femtobts_tracef_names[29] = {
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string femtobts_tracef_docs[29] = {
|
||||
{ DBG_DEBUG, "Debug Region" },
|
||||
{ DBG_L1WARNING, "L1 Warning Region" },
|
||||
{ DBG_ERROR, "Error Region" },
|
||||
{ DBG_L1RXMSG, "L1_RX_MSG Region" },
|
||||
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE Region" },
|
||||
{ DBG_L1TXMSG, "L1_TX_MSG Region" },
|
||||
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE Region" },
|
||||
{ DBG_MPHCNF, "MphConfirmation Region" },
|
||||
{ DBG_MPHIND, "MphIndication Region" },
|
||||
{ DBG_MPHREQ, "MphRequest Region" },
|
||||
{ DBG_PHIND, "PhIndication Region" },
|
||||
{ DBG_PHREQ, "PhRequest Region" },
|
||||
{ DBG_PHYRF, "PhyRF Region" },
|
||||
{ DBG_PHYRFMSGBYTE, "PhyRF Message Region" },
|
||||
{ DBG_MODE, "Mode Region" },
|
||||
{ DBG_TDMAINFO, "TDMA Info Region" },
|
||||
{ DBG_BADCRC, "Bad CRC Region" },
|
||||
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
|
||||
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
|
||||
{ DBG_DEVICEMSG, "Device Message Region" },
|
||||
{ DBG_RACHINFO, "RACH Info" },
|
||||
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
|
||||
{ DBG_MEMORY, "Memory Region" },
|
||||
{ DBG_PROFILING, "Profiling Region" },
|
||||
{ DBG_TESTCOMMENT, "Test Comments" },
|
||||
{ DBG_TEST, "Test Region" },
|
||||
{ DBG_STATUS, "Status Region" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string femtobts_tch_pl_names[] = {
|
||||
{ GsmL1_TchPlType_NA, "N/A" },
|
||||
{ GsmL1_TchPlType_Fr, "FR" },
|
||||
{ GsmL1_TchPlType_Hr, "HR" },
|
||||
{ GsmL1_TchPlType_Efr, "EFR" },
|
||||
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
|
||||
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
|
||||
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
|
||||
@@ -236,3 +306,62 @@ const struct value_string femtobts_tch_pl_names[] = {
|
||||
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string femtobts_clksrc_names[] = {
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(2,1,0)
|
||||
{ SuperFemto_ClkSrcId_None, "None" },
|
||||
{ SuperFemto_ClkSrcId_Ocxo, "ocxo" },
|
||||
{ SuperFemto_ClkSrcId_Tcxo, "tcxo" },
|
||||
{ SuperFemto_ClkSrcId_External, "ext" },
|
||||
{ SuperFemto_ClkSrcId_GpsPps, "gps" },
|
||||
{ SuperFemto_ClkSrcId_Trx, "trx" },
|
||||
{ SuperFemto_ClkSrcId_Rx, "rx" },
|
||||
{ SuperFemto_ClkSrcId_Edge, "edge" },
|
||||
{ SuperFemto_ClkSrcId_NetList, "nwl" },
|
||||
#else
|
||||
{ SF_CLKSRC_NONE, "None" },
|
||||
{ SF_CLKSRC_OCXO, "ocxo" },
|
||||
{ SF_CLKSRC_TCXO, "tcxo" },
|
||||
{ SF_CLKSRC_EXT, "ext" },
|
||||
{ SF_CLKSRC_GPS, "gps" },
|
||||
{ SF_CLKSRC_TRX, "trx" },
|
||||
{ SF_CLKSRC_RX, "rx" },
|
||||
#endif
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string femtobts_dir_names[] = {
|
||||
{ GsmL1_Dir_TxDownlink, "TxDL" },
|
||||
{ GsmL1_Dir_TxUplink, "TxUL" },
|
||||
{ GsmL1_Dir_RxUplink, "RxUL" },
|
||||
{ GsmL1_Dir_RxDownlink, "RxDL" },
|
||||
{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string femtobts_chcomb_names[] = {
|
||||
{ GsmL1_LogChComb_0, "dummy" },
|
||||
{ GsmL1_LogChComb_I, "tch_f" },
|
||||
{ GsmL1_LogChComb_II, "tch_h" },
|
||||
{ GsmL1_LogChComb_IV, "ccch" },
|
||||
{ GsmL1_LogChComb_V, "ccch_sdcch4" },
|
||||
{ GsmL1_LogChComb_VII, "sdcch8" },
|
||||
{ GsmL1_LogChComb_XIII, "pdtch" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
|
||||
[PDCH_CS_1] = 23,
|
||||
[PDCH_CS_2] = 34,
|
||||
[PDCH_CS_3] = 40,
|
||||
[PDCH_CS_4] = 54,
|
||||
[PDCH_MCS_1] = 27,
|
||||
[PDCH_MCS_2] = 33,
|
||||
[PDCH_MCS_3] = 42,
|
||||
[PDCH_MCS_4] = 49,
|
||||
[PDCH_MCS_5] = 60,
|
||||
[PDCH_MCS_6] = 78,
|
||||
[PDCH_MCS_7] = 118,
|
||||
[PDCH_MCS_8] = 142,
|
||||
[PDCH_MCS_9] = 154
|
||||
};
|
||||
|
||||
@@ -4,29 +4,107 @@
|
||||
#include <stdlib.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <sysmocom/femtobts/femtobts.h>
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1const.h>
|
||||
|
||||
#ifdef FEMTOBTS_API_VERSION
|
||||
#define SuperFemto_PrimId_t FemtoBts_PrimId_t
|
||||
#define SuperFemto_Prim_t FemtoBts_Prim_t
|
||||
#define SuperFemto_PrimId_SystemInfoReq FemtoBts_PrimId_SystemInfoReq
|
||||
#define SuperFemto_PrimId_SystemInfoCnf FemtoBts_PrimId_SystemInfoCnf
|
||||
#define SuperFemto_SystemInfoCnf_t FemtoBts_SystemInfoCnf_t
|
||||
#define SuperFemto_PrimId_SystemFailureInd FemtoBts_PrimId_SystemFailureInd
|
||||
#define SuperFemto_PrimId_ActivateRfReq FemtoBts_PrimId_ActivateRfReq
|
||||
#define SuperFemto_PrimId_ActivateRfCnf FemtoBts_PrimId_ActivateRfCnf
|
||||
#define SuperFemto_PrimId_DeactivateRfReq FemtoBts_PrimId_DeactivateRfReq
|
||||
#define SuperFemto_PrimId_DeactivateRfCnf FemtoBts_PrimId_DeactivateRfCnf
|
||||
#define SuperFemto_PrimId_SetTraceFlagsReq FemtoBts_PrimId_SetTraceFlagsReq
|
||||
#define SuperFemto_PrimId_RfClockInfoReq FemtoBts_PrimId_RfClockInfoReq
|
||||
#define SuperFemto_PrimId_RfClockInfoCnf FemtoBts_PrimId_RfClockInfoCnf
|
||||
#define SuperFemto_PrimId_RfClockSetupReq FemtoBts_PrimId_RfClockSetupReq
|
||||
#define SuperFemto_PrimId_RfClockSetupCnf FemtoBts_PrimId_RfClockSetupCnf
|
||||
#define SuperFemto_PrimId_Layer1ResetReq FemtoBts_PrimId_Layer1ResetReq
|
||||
#define SuperFemto_PrimId_Layer1ResetCnf FemtoBts_PrimId_Layer1ResetCnf
|
||||
#define SuperFemto_PrimId_NUM FemtoBts_PrimId_NUM
|
||||
#define HW_SYSMOBTS_V1 1
|
||||
#define SUPERFEMTO_API(x,y,z) FEMTOBTS_API(x,y,z)
|
||||
#endif
|
||||
|
||||
#ifdef L1_HAS_RTP_MODE
|
||||
/*
|
||||
* The bit ordering has been fixed on >= 3.10 but I am verifying
|
||||
* this on 3.11.
|
||||
*/
|
||||
#if SUPERFEMTO_API_VERSION >= SUPERFEMTO_API(3, 11, 0)
|
||||
#define USE_L1_RTP_MODE /* Tell L1 to use RTP mode */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Depending on the firmware version either GsmL1_Prim_t or SuperFemto_Prim_t
|
||||
* is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
|
||||
* bigger struct.
|
||||
*/
|
||||
#define SYSMOBTS_PRIM_SIZE \
|
||||
(OSMO_MAX(sizeof(SuperFemto_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
|
||||
|
||||
enum l1prim_type {
|
||||
L1P_T_INVALID, /* this must be 0 to detect uninitialized elements */
|
||||
L1P_T_REQ,
|
||||
L1P_T_CONF,
|
||||
L1P_T_IND,
|
||||
};
|
||||
|
||||
#if !defined(SUPERFEMTO_API_VERSION) || SUPERFEMTO_API_VERSION < SUPERFEMTO_API(2,1,0)
|
||||
enum uperfemto_clk_src {
|
||||
SF_CLKSRC_NONE = 0,
|
||||
SF_CLKSRC_OCXO = 1,
|
||||
SF_CLKSRC_TCXO = 2,
|
||||
SF_CLKSRC_EXT = 3,
|
||||
SF_CLKSRC_GPS = 4,
|
||||
SF_CLKSRC_TRX = 5,
|
||||
SF_CLKSRC_RX = 6,
|
||||
SF_CLKSRC_NL = 7,
|
||||
};
|
||||
#endif
|
||||
|
||||
const enum l1prim_type femtobts_l1prim_type[GsmL1_PrimId_NUM];
|
||||
const struct value_string femtobts_l1prim_names[GsmL1_PrimId_NUM+1];
|
||||
const GsmL1_PrimId_t femtobts_l1prim_req2conf[GsmL1_PrimId_NUM];
|
||||
|
||||
const enum l1prim_type femtobts_sysprim_type[FemtoBts_PrimId_NUM];
|
||||
const struct value_string femtobts_sysprim_names[FemtoBts_PrimId_NUM+1];
|
||||
const FemtoBts_PrimId_t femtobts_sysprim_req2conf[FemtoBts_PrimId_NUM];
|
||||
const enum l1prim_type femtobts_sysprim_type[SuperFemto_PrimId_NUM];
|
||||
const struct value_string femtobts_sysprim_names[SuperFemto_PrimId_NUM+1];
|
||||
const SuperFemto_PrimId_t femtobts_sysprim_req2conf[SuperFemto_PrimId_NUM];
|
||||
|
||||
const struct value_string femtobts_l1sapi_names[GsmL1_Sapi_NUM+1];
|
||||
const struct value_string femtobts_l1status_names[GSML1_STATUS_NUM+1];
|
||||
|
||||
const struct value_string femtobts_tracef_names[29];
|
||||
const struct value_string femtobts_tracef_docs[29];
|
||||
|
||||
const struct value_string femtobts_tch_pl_names[];
|
||||
const struct value_string femtobts_tch_pl_names[15];
|
||||
|
||||
const struct value_string femtobts_clksrc_names[10];
|
||||
|
||||
const struct value_string femtobts_dir_names[6];
|
||||
|
||||
enum pdch_cs {
|
||||
PDCH_CS_1,
|
||||
PDCH_CS_2,
|
||||
PDCH_CS_3,
|
||||
PDCH_CS_4,
|
||||
PDCH_MCS_1,
|
||||
PDCH_MCS_2,
|
||||
PDCH_MCS_3,
|
||||
PDCH_MCS_4,
|
||||
PDCH_MCS_5,
|
||||
PDCH_MCS_6,
|
||||
PDCH_MCS_7,
|
||||
PDCH_MCS_8,
|
||||
PDCH_MCS_9,
|
||||
_NUM_PDCH_CS
|
||||
};
|
||||
|
||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
|
||||
|
||||
#endif /* FEMTOBTS_H */
|
||||
|
||||
113
src/osmo-bts-sysmo/hw_misc.c
Normal file
113
src/osmo-bts-sysmo/hw_misc.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Misc HW routines for Sysmocom BTS */
|
||||
|
||||
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "hw_misc.h"
|
||||
|
||||
static const struct value_string sysmobts_led_names[] = {
|
||||
{ LED_RF_ACTIVE, "activity_led" },
|
||||
{ LED_ONLINE, "online_led" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
int sysmobts_led_set(enum sysmobts_led nr, int on)
|
||||
{
|
||||
char tmp[PATH_MAX+1];
|
||||
const char *filename;
|
||||
int fd;
|
||||
uint8_t byte;
|
||||
|
||||
if (on)
|
||||
byte = '1';
|
||||
else
|
||||
byte = '0';
|
||||
|
||||
filename = get_value_string(sysmobts_led_names, nr);
|
||||
if (!filename)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(tmp, sizeof(tmp)-1, "/sys/class/leds/%s/brightness", filename);
|
||||
tmp[sizeof(tmp)-1] = '\0';
|
||||
|
||||
fd = open(tmp, O_WRONLY);
|
||||
if (fd < 0)
|
||||
return -ENODEV;
|
||||
|
||||
write(fd, &byte, 1);
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#define HWMON_PREFIX "/sys/class/hwmon/hwmon0/device"
|
||||
|
||||
static FILE *temperature_f[NUM_TEMP];
|
||||
|
||||
int sysmobts_temp_init()
|
||||
{
|
||||
char tmp[PATH_MAX+1];
|
||||
FILE *in;
|
||||
int rc = 0;
|
||||
|
||||
for (i = 0; i < NUM_TEMP; i++) {
|
||||
snprintf(tmp, sizeof(tmp)-1, HWMON_PREFIX "/temp%u_input", i+1),
|
||||
tmp[sizeof(tmp)-1] = '\0';
|
||||
|
||||
temperature_f[i] = fopen(tmp, "r");
|
||||
if (!temperature_f[i])
|
||||
rc = -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_temp_get(uint8_t num)
|
||||
{
|
||||
if (num >= NUM_TEMP)
|
||||
return -EINVAL;
|
||||
|
||||
if (!temperature_f[num])
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
in = fopen(tmp, "r");
|
||||
if (!in)
|
||||
return -ENODEV;
|
||||
|
||||
fclose(tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
12
src/osmo-bts-sysmo/hw_misc.h
Normal file
12
src/osmo-bts-sysmo/hw_misc.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _SYSMOBTS_HW_MISC_H
|
||||
#define _SYSMOBTS_HW_MISC_H
|
||||
|
||||
enum sysmobts_led {
|
||||
LED_NONE,
|
||||
LED_RF_ACTIVE,
|
||||
LED_ONLINE,
|
||||
};
|
||||
|
||||
int sysmobts_led_set(enum sysmobts_led nr, int on);
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,5 @@
|
||||
#define L1FWD_L1_PORT 9999
|
||||
#define L1FWD_SYS_PORT 9998
|
||||
#define L1FWD_TCH_PORT 9997
|
||||
#define L1FWD_PDTCH_PORT 9996
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
#include <sysmocom/femtobts/femtobts.h>
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1prim.h>
|
||||
#include <sysmocom/femtobts/gsml1const.h>
|
||||
#include <sysmocom/femtobts/gsml1types.h>
|
||||
@@ -55,11 +55,15 @@
|
||||
static const uint16_t fwd_udp_ports[_NUM_MQ_WRITE] = {
|
||||
[MQ_SYS_READ] = L1FWD_SYS_PORT,
|
||||
[MQ_L1_READ] = L1FWD_L1_PORT,
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
[MQ_TCH_READ] = L1FWD_TCH_PORT,
|
||||
[MQ_PDTCH_READ] = L1FWD_PDTCH_PORT,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct l1fwd_hdl {
|
||||
struct sockaddr_storage remote_sa;
|
||||
socklen_t remote_sa_len;
|
||||
struct sockaddr_storage remote_sa[_NUM_MQ_WRITE];
|
||||
socklen_t remote_sa_len[_NUM_MQ_WRITE];
|
||||
|
||||
struct osmo_wqueue udp_wq[_NUM_MQ_WRITE];
|
||||
|
||||
@@ -68,12 +72,12 @@ struct l1fwd_hdl {
|
||||
|
||||
|
||||
/* callback when there's a new L1 primitive coming in from the HW */
|
||||
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||
int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
struct l1fwd_hdl *l1fh = fl1h->priv;
|
||||
|
||||
/* Enqueue message to UDP socket */
|
||||
return osmo_wqueue_enqueue(&l1fh->udp_wq[MQ_L1_WRITE], msg);
|
||||
return osmo_wqueue_enqueue(&l1fh->udp_wq[wq], msg);
|
||||
}
|
||||
|
||||
/* callback when there's a new SYS primitive coming in from the HW */
|
||||
@@ -89,7 +93,7 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||
/* data has arrived on the udp socket */
|
||||
static int udp_read_cb(struct osmo_fd *ofd)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
|
||||
struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "udp_rx");
|
||||
struct l1fwd_hdl *l1fh = ofd->data;
|
||||
struct femtol1_hdl *fl1h = l1fh->fl1h;
|
||||
int rc;
|
||||
@@ -99,9 +103,9 @@ static int udp_read_cb(struct osmo_fd *ofd)
|
||||
|
||||
msg->l1h = msg->data;
|
||||
|
||||
l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
|
||||
l1fh->remote_sa_len[ofd->priv_nr] = sizeof(l1fh->remote_sa[ofd->priv_nr]);
|
||||
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
|
||||
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
|
||||
(struct sockaddr *) &l1fh->remote_sa[ofd->priv_nr], &l1fh->remote_sa_len[ofd->priv_nr]);
|
||||
if (rc < 0) {
|
||||
perror("read from udp");
|
||||
msgb_free(msg);
|
||||
@@ -113,14 +117,11 @@ static int udp_read_cb(struct osmo_fd *ofd)
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
DEBUGP(DL1C, "UDP: Received %u bytes for %s queue\n", rc,
|
||||
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1");
|
||||
DEBUGP(DL1C, "UDP: Received %u bytes for queue %d\n", rc,
|
||||
ofd->priv_nr);
|
||||
|
||||
/* put the message into the right queue */
|
||||
if (ofd->priv_nr == MQ_SYS_WRITE)
|
||||
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_SYS_WRITE], msg);
|
||||
else
|
||||
rc = osmo_wqueue_enqueue(&fl1h->write_q[MQ_L1_WRITE], msg);
|
||||
rc = osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -131,11 +132,11 @@ static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
int rc;
|
||||
struct l1fwd_hdl *l1fh = ofd->data;
|
||||
|
||||
DEBUGP(DL1C, "UDP: Writing %u bytes for %s queue\n", msgb_l1len(msg),
|
||||
ofd->priv_nr == MQ_SYS_WRITE ? "SYS" : "L1");
|
||||
DEBUGP(DL1C, "UDP: Writing %u bytes for queue %d\n", msgb_l1len(msg),
|
||||
ofd->priv_nr);
|
||||
|
||||
rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0,
|
||||
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
|
||||
(const struct sockaddr *)&l1fh->remote_sa[ofd->priv_nr], l1fh->remote_sa_len[ofd->priv_nr]);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
@@ -155,19 +156,32 @@ int main(int argc, char **argv)
|
||||
struct femtol1_hdl *fl1h;
|
||||
int rc, i;
|
||||
|
||||
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t));
|
||||
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t));
|
||||
printf("sizeof(GsmL1_Prim_t) = %zu\n", sizeof(GsmL1_Prim_t));
|
||||
printf("sizeof(SuperFemto_Prim_t) = %zu\n", sizeof(SuperFemto_Prim_t));
|
||||
|
||||
bts_log_init(NULL);
|
||||
|
||||
/*
|
||||
* hack and prevent that two l1fwd-proxy/sysmobts run at the same
|
||||
* time. This is done by binding to the same VTY port.
|
||||
*/
|
||||
rc = osmo_sock_init(AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
|
||||
"127.0.0.1", 4241, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to bind to the BTS VTY port.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* allocate new femtol1_handle */
|
||||
fl1h = talloc_zero(NULL, struct femtol1_hdl);
|
||||
INIT_LLIST_HEAD(&fl1h->wlc_list);
|
||||
|
||||
/* open the actual hardware transport */
|
||||
rc = l1if_transport_open(fl1h);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
|
||||
rc = l1if_transport_open(i, fl1h);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* create our fwd handle */
|
||||
l1fh = talloc_zero(NULL, struct l1fwd_hdl);
|
||||
@@ -176,7 +190,7 @@ int main(int argc, char **argv)
|
||||
fl1h->priv = l1fh;
|
||||
|
||||
/* Open UDP */
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(l1fh->udp_wq); i++) {
|
||||
struct osmo_wqueue *wq = &l1fh->udp_wq[i];
|
||||
|
||||
osmo_wqueue_init(wq, 10);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,26 +3,59 @@
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <sysmocom/femtobts/gsml1prim.h>
|
||||
|
||||
enum {
|
||||
MQ_SYS_READ,
|
||||
MQ_L1_READ,
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
MQ_TCH_READ,
|
||||
MQ_PDTCH_READ,
|
||||
#endif
|
||||
_NUM_MQ_READ
|
||||
};
|
||||
|
||||
enum {
|
||||
MQ_SYS_WRITE,
|
||||
MQ_L1_WRITE,
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
MQ_TCH_WRITE,
|
||||
MQ_PDTCH_WRITE,
|
||||
#endif
|
||||
_NUM_MQ_WRITE
|
||||
};
|
||||
|
||||
struct calib_send_state {
|
||||
const char *path;
|
||||
int last_file_idx;
|
||||
};
|
||||
|
||||
enum {
|
||||
FIXUP_UNITILIAZED,
|
||||
FIXUP_NEEDED,
|
||||
FIXUP_NOT_NEEDED,
|
||||
};
|
||||
|
||||
struct femtol1_hdl {
|
||||
struct gsm_time gsm_time;
|
||||
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
|
||||
uint32_t dsp_trace_f;
|
||||
uint8_t clk_use_eeprom;
|
||||
int clk_cal;
|
||||
int ul_power_target;
|
||||
uint8_t clk_src;
|
||||
float min_qual_rach;
|
||||
float min_qual_norm;
|
||||
char *calib_path;
|
||||
struct llist_head wlc_list;
|
||||
|
||||
struct gsmtap_inst *gsmtap;
|
||||
uint32_t gsmtap_sapi_mask;
|
||||
|
||||
void *priv; /* user reference */
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
@@ -30,31 +63,73 @@ struct femtol1_hdl {
|
||||
|
||||
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
|
||||
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
|
||||
|
||||
struct {
|
||||
/* from DSP/FPGA after L1 Init */
|
||||
uint8_t dsp_version[3];
|
||||
uint8_t fpga_version[3];
|
||||
uint32_t band_support; /* bitmask of GSM_BAND_* */
|
||||
uint8_t ver_major;
|
||||
uint8_t ver_minor;
|
||||
/* from EEPROM */
|
||||
uint16_t model_nr;
|
||||
uint16_t model_flags;
|
||||
uint8_t trx_nr;
|
||||
} hw_info;
|
||||
|
||||
int fixup_needed;
|
||||
|
||||
struct calib_send_state st;
|
||||
|
||||
uint8_t last_rf_mute[8];
|
||||
};
|
||||
|
||||
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
|
||||
#define msgb_sysprim(msg) ((FemtoBts_Prim_t *)(msg)->l1h)
|
||||
#define msgb_sysprim(msg) ((SuperFemto_Prim_t *)(msg)->l1h)
|
||||
|
||||
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
|
||||
typedef int l1if_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg, void *data);
|
||||
|
||||
/* send a request primitive to the L1 and schedule completion call-back */
|
||||
int l1if_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
|
||||
int is_system_prim, l1if_compl_cb *cb, void *data);
|
||||
l1if_compl_cb *cb, void *cb_data);
|
||||
int l1if_gsm_req_compl(struct femtol1_hdl *fl1h, struct msgb *msg,
|
||||
l1if_compl_cb *cb, void *cb_data);
|
||||
|
||||
struct femtol1_hdl *l1if_open(void *priv);
|
||||
int l1if_close(struct femtol1_hdl *hdl);
|
||||
int l1if_reset(struct femtol1_hdl *hdl);
|
||||
int l1if_activate_rf(struct femtol1_hdl *hdl, int on);
|
||||
int l1if_set_trace_flags(struct femtol1_hdl *hdl, uint32_t flags);
|
||||
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power);
|
||||
int l1if_mute_rf(struct femtol1_hdl *hdl, uint8_t mute[8], l1if_compl_cb *cb);
|
||||
|
||||
struct msgb *l1p_msgb_alloc(void);
|
||||
struct msgb *sysp_msgb_alloc(void);
|
||||
|
||||
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
|
||||
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
|
||||
uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
|
||||
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
|
||||
|
||||
/* tch.c */
|
||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
||||
|
||||
/* ciphering */
|
||||
int l1if_set_ciphering(struct femtol1_hdl *fl1h,
|
||||
struct gsm_lchan *lchan,
|
||||
int dir_downlink);
|
||||
|
||||
/* calibration loading */
|
||||
int calib_load(struct femtol1_hdl *fl1h);
|
||||
|
||||
/* on-line re-calibration */
|
||||
int l1if_rf_clock_info_reset(struct femtol1_hdl *fl1h);
|
||||
int l1if_rf_clock_info_correct(struct femtol1_hdl *fl1h);
|
||||
|
||||
/* public helpers for test */
|
||||
int bts_check_for_ciph_cmd(struct femtol1_hdl *fl1h,
|
||||
struct msgb *msg, struct gsm_lchan *lchan);
|
||||
void bts_check_for_first_ciphrd(struct femtol1_hdl *fl1h,
|
||||
GsmL1_MsgUnitParam_t *msgUnitParam,
|
||||
struct gsm_lchan *lchan);
|
||||
#endif /* _FEMTO_L1_H */
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
/* functions a transport calls on arrival of primitive from BTS */
|
||||
int l1if_handle_l1prim(struct femtol1_hdl *fl1h, struct msgb *msg);
|
||||
int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg);
|
||||
int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg);
|
||||
|
||||
/* functions exported by a transport */
|
||||
int l1if_transport_open(struct femtol1_hdl *fl1h);
|
||||
int l1if_transport_close(struct femtol1_hdl *fl1h);
|
||||
int l1if_transport_open(int q, struct femtol1_hdl *fl1h);
|
||||
int l1if_transport_close(int q, struct femtol1_hdl *fl1h);
|
||||
|
||||
#endif /* _FEMTOL1_TRANSP_H */
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
#include <sysmocom/femtobts/femtobts.h>
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1prim.h>
|
||||
#include <sysmocom/femtobts/gsml1const.h>
|
||||
#include <sysmocom/femtobts/gsml1types.h>
|
||||
@@ -56,11 +56,15 @@
|
||||
static const uint16_t fwd_udp_ports[] = {
|
||||
[MQ_SYS_WRITE] = L1FWD_SYS_PORT,
|
||||
[MQ_L1_WRITE] = L1FWD_L1_PORT,
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
[MQ_TCH_WRITE] = L1FWD_TCH_PORT,
|
||||
[MQ_PDTCH_WRITE]= L1FWD_PDTCH_PORT,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int fwd_read_cb(struct osmo_fd *ofd)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc_headroom(2048, 127, "udp_rx");
|
||||
struct msgb *msg = msgb_alloc_headroom(SYSMOBTS_PRIM_SIZE, 128, "udp_rx");
|
||||
struct femtol1_hdl *fl1h = ofd->data;
|
||||
int rc;
|
||||
|
||||
@@ -83,7 +87,7 @@ static int fwd_read_cb(struct osmo_fd *ofd)
|
||||
if (ofd->priv_nr == MQ_SYS_WRITE)
|
||||
rc = l1if_handle_sysprim(fl1h, msg);
|
||||
else
|
||||
rc = l1if_handle_l1prim(fl1h, msg);
|
||||
rc = l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -94,55 +98,55 @@ static int prim_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
return write(ofd->fd, msg->head, msg->len);
|
||||
}
|
||||
|
||||
int l1if_transport_open(struct femtol1_hdl *fl1h)
|
||||
int l1if_transport_open(int q, struct femtol1_hdl *fl1h)
|
||||
{
|
||||
int rc, i;
|
||||
int rc;
|
||||
char *bts_host = getenv("L1FWD_BTS_HOST");
|
||||
|
||||
printf("sizeof(GsmL1_Prim_t) = %lu\n", sizeof(GsmL1_Prim_t));
|
||||
printf("sizeof(FemtoBts_Prim_t) = %lu\n", sizeof(FemtoBts_Prim_t));
|
||||
switch (q) {
|
||||
case MQ_L1_WRITE:
|
||||
LOGP(DL1C, LOGL_INFO, "sizeof(GsmL1_Prim_t) = %zu\n",
|
||||
sizeof(GsmL1_Prim_t));
|
||||
break;
|
||||
case MQ_SYS_WRITE:
|
||||
LOGP(DL1C, LOGL_INFO, "sizeof(SuperFemto_Prim_t) = %zu\n",
|
||||
sizeof(SuperFemto_Prim_t));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bts_host) {
|
||||
fprintf(stderr, "You have to set the L1FWD_BTS_HOST environment variable\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
|
||||
struct osmo_wqueue *wq = &fl1h->write_q[i];
|
||||
struct osmo_fd *ofd = &wq->bfd;
|
||||
struct osmo_wqueue *wq = &fl1h->write_q[q];
|
||||
struct osmo_fd *ofd = &wq->bfd;
|
||||
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = prim_write_cb;
|
||||
wq->read_cb = fwd_read_cb;
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = prim_write_cb;
|
||||
wq->read_cb = fwd_read_cb;
|
||||
|
||||
ofd->data = fl1h;
|
||||
ofd->priv_nr = i;
|
||||
ofd->when |= BSC_FD_READ;
|
||||
ofd->data = fl1h;
|
||||
ofd->priv_nr = q;
|
||||
ofd->when |= BSC_FD_READ;
|
||||
|
||||
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
bts_host, fwd_udp_ports[i],
|
||||
OSMO_SOCK_F_CONNECT);
|
||||
if (rc < 0) {
|
||||
talloc_free(fl1h);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
bts_host, fwd_udp_ports[q],
|
||||
OSMO_SOCK_F_CONNECT);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_transport_close(struct femtol1_hdl *fl1h)
|
||||
int l1if_transport_close(int q, struct femtol1_hdl *fl1h)
|
||||
{
|
||||
int i;
|
||||
struct osmo_wqueue *wq = &fl1h->write_q[q];
|
||||
struct osmo_fd *ofd = &wq->bfd;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fl1h->write_q); i++) {
|
||||
struct osmo_wqueue *wq = &fl1h->write_q[i];
|
||||
struct osmo_fd *ofd = &wq->bfd;
|
||||
|
||||
osmo_wqueue_clear(wq);
|
||||
osmo_fd_unregister(ofd);
|
||||
close(ofd->fd);
|
||||
}
|
||||
osmo_wqueue_clear(wq);
|
||||
osmo_fd_unregister(ofd);
|
||||
close(ofd->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@@ -36,7 +37,7 @@
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
#include <sysmocom/femtobts/femtobts.h>
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1prim.h>
|
||||
#include <sysmocom/femtobts/gsml1const.h>
|
||||
#include <sysmocom/femtobts/gsml1types.h>
|
||||
@@ -46,45 +47,188 @@
|
||||
#include "l1_transp.h"
|
||||
|
||||
|
||||
#ifdef HW_SYSMOBTS_V1
|
||||
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/femtobts_dsp2arm"
|
||||
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/femtobts_arm2dsp"
|
||||
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_dsp2arm"
|
||||
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_arm2dsp"
|
||||
#else
|
||||
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/superfemto_dsp2arm"
|
||||
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/superfemto_arm2dsp"
|
||||
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm"
|
||||
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp"
|
||||
|
||||
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm"
|
||||
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp"
|
||||
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm"
|
||||
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp"
|
||||
#endif
|
||||
|
||||
static const char *rd_devnames[] = {
|
||||
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
||||
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
|
||||
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const char *wr_devnames[] = {
|
||||
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
||||
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
|
||||
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Make sure that all structs we read fit into the SYSMOBTS_PRIM_SIZE
|
||||
*/
|
||||
osmo_static_assert(sizeof(GsmL1_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, l1_prim)
|
||||
osmo_static_assert(sizeof(SuperFemto_Prim_t) + 128 <= SYSMOBTS_PRIM_SIZE, super_prim)
|
||||
|
||||
static int wqueue_vector_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct osmo_wqueue *queue;
|
||||
|
||||
queue = container_of(fd, struct osmo_wqueue, bfd);
|
||||
|
||||
if (what & BSC_FD_READ)
|
||||
queue->read_cb(fd);
|
||||
|
||||
if (what & BSC_FD_EXCEPT)
|
||||
queue->except_cb(fd);
|
||||
|
||||
if (what & BSC_FD_WRITE) {
|
||||
struct iovec iov[5];
|
||||
struct msgb *msg, *tmp;
|
||||
int written, count = 0;
|
||||
|
||||
fd->when &= ~BSC_FD_WRITE;
|
||||
|
||||
llist_for_each_entry(msg, &queue->msg_queue, list) {
|
||||
/* more writes than we have */
|
||||
if (count >= ARRAY_SIZE(iov))
|
||||
break;
|
||||
|
||||
iov[count].iov_base = msg->l1h;
|
||||
iov[count].iov_len = msgb_l1len(msg);
|
||||
count += 1;
|
||||
}
|
||||
|
||||
/* TODO: check if all lengths are the same. */
|
||||
|
||||
|
||||
/* Nothing scheduled? This should not happen. */
|
||||
if (count == 0) {
|
||||
if (!llist_empty(&queue->msg_queue))
|
||||
fd->when |= BSC_FD_WRITE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
written = writev(fd->fd, iov, count);
|
||||
if (written < 0) {
|
||||
/* nothing written?! */
|
||||
if (!llist_empty(&queue->msg_queue))
|
||||
fd->when |= BSC_FD_WRITE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* now delete the written entries */
|
||||
written = written / iov[0].iov_len;
|
||||
count = 0;
|
||||
llist_for_each_entry_safe(msg, tmp, &queue->msg_queue, list) {
|
||||
queue->current_length -= 1;
|
||||
|
||||
llist_del(&msg->list);
|
||||
msgb_free(msg);
|
||||
|
||||
count += 1;
|
||||
if (count >= written)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!llist_empty(&queue->msg_queue))
|
||||
fd->when |= BSC_FD_WRITE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prim_size_for_queue(int queue)
|
||||
{
|
||||
switch (queue) {
|
||||
case MQ_SYS_WRITE:
|
||||
return sizeof(SuperFemto_Prim_t);
|
||||
case MQ_L1_WRITE:
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
case MQ_TCH_WRITE:
|
||||
case MQ_PDTCH_WRITE:
|
||||
#endif
|
||||
return sizeof(GsmL1_Prim_t);
|
||||
default:
|
||||
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||
queue);
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* callback when there's something to read from the l1 msg_queue */
|
||||
static int read_dispatch_one(struct femtol1_hdl *fl1h, struct msgb *msg, int queue)
|
||||
{
|
||||
switch (queue) {
|
||||
case MQ_SYS_WRITE:
|
||||
return l1if_handle_sysprim(fl1h, msg);
|
||||
case MQ_L1_WRITE:
|
||||
#ifndef HW_SYSMOBTS_V1
|
||||
case MQ_TCH_WRITE:
|
||||
case MQ_PDTCH_WRITE:
|
||||
#endif
|
||||
return l1if_handle_l1prim(queue, fl1h, msg);
|
||||
default:
|
||||
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||
LOGP(DL1C, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||
queue);
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
//struct msgb *msg = l1p_msgb_alloc();
|
||||
struct msgb *msg = msgb_alloc_headroom(2048, 128, "1l_fd");
|
||||
struct femtol1_hdl *fl1h = ofd->data;
|
||||
int rc;
|
||||
int i, rc;
|
||||
|
||||
msg->l1h = msg->data;
|
||||
rc = read(ofd->fd, msg->l1h, sizeof(GsmL1_Prim_t));
|
||||
if (rc < 0) {
|
||||
if (rc != -1)
|
||||
LOGP(DL1C, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
const uint32_t prim_size = prim_size_for_queue(ofd->priv_nr);
|
||||
uint32_t count;
|
||||
|
||||
struct iovec iov[3];
|
||||
struct msgb *msg[ARRAY_SIZE(iov)];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iov); ++i) {
|
||||
msg[i] = msgb_alloc_headroom(prim_size + 128, 128, "1l_fd");
|
||||
msg[i]->l1h = msg[i]->data;
|
||||
|
||||
iov[i].iov_base = msg[i]->l1h;
|
||||
iov[i].iov_len = msgb_tailroom(msg[i]);
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
if (ofd->priv_nr == MQ_L1_WRITE)
|
||||
return l1if_handle_l1prim(fl1h, msg);
|
||||
else
|
||||
return l1if_handle_sysprim(fl1h, msg);
|
||||
};
|
||||
|
||||
rc = readv(ofd->fd, iov, ARRAY_SIZE(iov));
|
||||
count = rc / prim_size;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
msgb_put(msg[i], prim_size);
|
||||
read_dispatch_one(ofd->data, msg[i], ofd->priv_nr);
|
||||
}
|
||||
|
||||
for (i = count; i < ARRAY_SIZE(iov); ++i)
|
||||
msgb_free(msg[i]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* callback when we can write to one of the l1 msg_queue devices */
|
||||
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
@@ -105,88 +249,74 @@ static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_transport_open(struct femtol1_hdl *hdl)
|
||||
int l1if_transport_open(int q, struct femtol1_hdl *hdl)
|
||||
{
|
||||
int rc, i;
|
||||
int rc;
|
||||
|
||||
/* Step 1: Open all msg_queue file descriptors */
|
||||
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
|
||||
struct osmo_fd *ofd = &hdl->read_ofd[i];
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_wqueue *wq = &hdl->write_q[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
rc = open(rd_devnames[i], O_RDONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
goto out_free;
|
||||
}
|
||||
ofd->fd = rc;
|
||||
ofd->priv_nr = i;
|
||||
ofd->data = hdl;
|
||||
ofd->cb = l1if_fd_cb;
|
||||
ofd->when = BSC_FD_READ;
|
||||
rc = osmo_fd_register(ofd);
|
||||
if (rc < 0) {
|
||||
close(ofd->fd);
|
||||
ofd->fd = -1;
|
||||
goto out_free;
|
||||
}
|
||||
rc = open(rd_devnames[q], O_RDONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(hdl->write_q); i++) {
|
||||
struct osmo_wqueue *wq = &hdl->write_q[i];
|
||||
struct osmo_fd *ofd = &hdl->write_q[i].bfd;
|
||||
|
||||
rc = open(wr_devnames[i], O_WRONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
goto out_read;
|
||||
}
|
||||
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = l1fd_write_cb;
|
||||
|
||||
ofd->fd = rc;
|
||||
ofd->priv_nr = i;
|
||||
ofd->data = hdl;
|
||||
ofd->when = BSC_FD_WRITE;
|
||||
rc = osmo_fd_register(ofd);
|
||||
if (rc < 0) {
|
||||
close(ofd->fd);
|
||||
ofd->fd = -1;
|
||||
goto out_read;
|
||||
}
|
||||
|
||||
read_ofd->fd = rc;
|
||||
read_ofd->priv_nr = q;
|
||||
read_ofd->data = hdl;
|
||||
read_ofd->cb = l1if_fd_cb;
|
||||
read_ofd->when = BSC_FD_READ;
|
||||
rc = osmo_fd_register(read_ofd);
|
||||
if (rc < 0) {
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = open(wr_devnames[q], O_WRONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_FATAL, "unable to open msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
goto out_read;
|
||||
}
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = l1fd_write_cb;
|
||||
write_ofd->cb = wqueue_vector_cb;
|
||||
write_ofd->fd = rc;
|
||||
write_ofd->priv_nr = q;
|
||||
write_ofd->data = hdl;
|
||||
write_ofd->when = BSC_FD_WRITE;
|
||||
rc = osmo_fd_register(write_ofd);
|
||||
if (rc < 0) {
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
goto out_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_read:
|
||||
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
|
||||
close(hdl->read_ofd[i].fd);
|
||||
osmo_fd_unregister(&hdl->read_ofd[i]);
|
||||
}
|
||||
out_free:
|
||||
talloc_free(hdl);
|
||||
close(hdl->read_ofd[q].fd);
|
||||
osmo_fd_unregister(&hdl->read_ofd[q]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_transport_close(struct femtol1_hdl *hdl)
|
||||
int l1if_transport_close(int q, struct femtol1_hdl *hdl)
|
||||
{
|
||||
int i;
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hdl->read_ofd); i++) {
|
||||
struct osmo_fd *ofd = &hdl->read_ofd[i];
|
||||
osmo_fd_unregister(read_ofd);
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
|
||||
osmo_fd_unregister(ofd);
|
||||
close(ofd->fd);
|
||||
ofd->fd = -1;
|
||||
}
|
||||
osmo_fd_unregister(write_ofd);
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hdl->write_q); i++) {
|
||||
struct osmo_fd *ofd = &hdl->write_q[i].bfd;
|
||||
|
||||
osmo_fd_unregister(ofd);
|
||||
close(ofd->fd);
|
||||
ofd->fd = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/gsm/prim.h>
|
||||
|
||||
#include <osmocom/bb/common/l1ctl.h>
|
||||
#include <osmocom/bb/common/lapdm.h>
|
||||
|
||||
/* LAPDm wants to send a PH-* primitive to the physical layer (L1) */
|
||||
int sysmol1_ph_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct osmocom_ms *ms = ctx;
|
||||
struct osmo_phsap_prim *pp = (struct osmo_phsap_prim *) oph;
|
||||
int rc = 0;
|
||||
|
||||
if (oph->sap != SAP_GSM_PH)
|
||||
return -ENODEV;
|
||||
|
||||
if (oph->operation != PRIM_OP_REQUEST)
|
||||
return -EINVAL;
|
||||
|
||||
switch (oph->primitive) {
|
||||
case PRIM_PH_RACH:
|
||||
/* A BTS never transmits RACH */
|
||||
case PRIM_PH_DATA:
|
||||
/* we use the LAPDm code in polling only, we should never
|
||||
* get a PH-DATA.req */
|
||||
default:
|
||||
LOGP(DLAPDM, LOGL_ERROR, "LAPDm sends unknown prim %u\n",
|
||||
oph->primitive);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Main program for Sysmocom BTS */
|
||||
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2011-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -24,7 +24,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
@@ -33,28 +37,36 @@
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/control_if.h>
|
||||
|
||||
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
|
||||
|
||||
#include "utils.h"
|
||||
#include "eeprom.h"
|
||||
#include "l1_if.h"
|
||||
#include "hw_misc.h"
|
||||
#include "oml_router.h"
|
||||
|
||||
/* FIXME: read from real hardware */
|
||||
const uint8_t abis_mac[6] = { 0,1,2,3,4,5 };
|
||||
/* FIXME: generate from git */
|
||||
const char *software_version = "0815";
|
||||
int pcu_direct = 0;
|
||||
|
||||
static const char *config_file = "osmo-bts.cfg";
|
||||
extern const char *osmobts_copyright;
|
||||
static int daemonize = 0;
|
||||
static unsigned int dsp_trace = 0;
|
||||
static unsigned int dsp_trace = 0x71c00020;
|
||||
static int rt_prio = -1;
|
||||
|
||||
int bts_model_init(struct gsm_bts *bts)
|
||||
{
|
||||
struct femtol1_hdl *fl1h;
|
||||
int rc;
|
||||
|
||||
fl1h = l1if_open(bts->c0);
|
||||
if (!fl1h) {
|
||||
@@ -64,31 +76,77 @@ int bts_model_init(struct gsm_bts *bts)
|
||||
fl1h->dsp_trace_f = dsp_trace;
|
||||
|
||||
bts->c0->role_bts.l1h = fl1h;
|
||||
bts->c0->nominal_power = 23;
|
||||
|
||||
l1if_reset(fl1h);
|
||||
rc = sysmobts_get_nominal_power(bts->c0);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "Cannot determine nominal "
|
||||
"transmit power. Assuming 23dBm.\n");
|
||||
rc = 23;
|
||||
}
|
||||
bts->c0->nominal_power = rc;
|
||||
bts->c0->power_params.trx_p_max_out_mdBm = to_mdB(rc);
|
||||
|
||||
femtol1_vty_init(bts);
|
||||
bts_model_vty_init(bts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipabis_link *link_init(struct gsm_bts *bts, const char *ip)
|
||||
int bts_model_oml_estab(struct gsm_bts *bts)
|
||||
{
|
||||
struct femtol1_hdl *fl1h = bts->c0->role_bts.l1h;
|
||||
|
||||
l1if_reset(fl1h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the clock calibration to the value
|
||||
* read from the eeprom.
|
||||
*/
|
||||
void clk_cal_use_eeprom(struct gsm_bts *bts)
|
||||
{
|
||||
struct ipabis_link *link = talloc_zero(bts, struct ipabis_link);
|
||||
struct in_addr ia;
|
||||
int rc;
|
||||
struct femtol1_hdl *hdl;
|
||||
eeprom_RfClockCal_t rf_clk;
|
||||
|
||||
inet_aton(ip, &ia);
|
||||
hdl = bts->c0->role_bts.l1h;
|
||||
|
||||
link->bts = bts;
|
||||
bts->oml_link = link;
|
||||
if (!hdl || !hdl->clk_use_eeprom)
|
||||
return;
|
||||
|
||||
rc = abis_open(link, ntohl(ia.s_addr));
|
||||
if (rc < 0)
|
||||
return NULL;
|
||||
rc = eeprom_ReadRfClockCal(&rf_clk);
|
||||
if (rc != EEPROM_SUCCESS) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Failed to read from EEPROM.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
return link;
|
||||
hdl->clk_cal = rf_clk.iClkCor;
|
||||
LOGP(DL1C, LOGL_NOTICE,
|
||||
"Read clock calibration(%d) from EEPROM.\n", hdl->clk_cal);
|
||||
}
|
||||
|
||||
void bts_update_status(enum bts_global_status which, int on)
|
||||
{
|
||||
static uint64_t states = 0;
|
||||
uint64_t old_states = states;
|
||||
int led_rf_active_on;
|
||||
|
||||
if (on)
|
||||
states |= (1ULL << which);
|
||||
else
|
||||
states &= ~(1ULL << which);
|
||||
|
||||
led_rf_active_on =
|
||||
(states & (1ULL << BTS_STATUS_RF_ACTIVE)) &&
|
||||
!(states & (1ULL << BTS_STATUS_RF_MUTE));
|
||||
|
||||
LOGP(DL1C, LOGL_INFO,
|
||||
"Set global status #%d to %d (%04llx -> %04llx), LEDs: ACT %d\n",
|
||||
which, on,
|
||||
(long long)old_states, (long long)states,
|
||||
led_rf_active_on);
|
||||
|
||||
sysmobts_led_set(LED_RF_ACTIVE, led_rf_active_on);
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
@@ -102,10 +160,23 @@ static void print_help()
|
||||
" -T --timestamp Prefix every log line with a timestamp\n"
|
||||
" -V --version Print version information and exit\n"
|
||||
" -e --log-level Set a global log-level\n"
|
||||
" -p --dsp-trace Set DSP trace flags\n"
|
||||
" -p --dsp-trace Set DSP trace flags\n"
|
||||
" -r --realtime PRIO Use SCHED_RR with the specified priority\n"
|
||||
" -w --hw-version Print the targeted HW Version\n"
|
||||
" -M --pcu-direct Force PCU to access message queue for "
|
||||
"PDCH dchannel directly\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void print_hwversion()
|
||||
{
|
||||
#ifdef HW_SYSMOBTS_V1
|
||||
printf("sysmobts was compiled for hw version 1.\n");
|
||||
#else
|
||||
printf("sysmobts was compiled for hw version 2.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* FIXME: finally get some option parsing code into libosmocore */
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
@@ -122,10 +193,13 @@ static void handle_options(int argc, char **argv)
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "log-level", 1, 0, 'e' },
|
||||
{ "dsp-trace", 1, 0, 'p' },
|
||||
{ "hw-version", 0, 0, 'w' },
|
||||
{ "pcu-direct", 0, 0, 'M' },
|
||||
{ "realtime", 1, 0, 'r' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:",
|
||||
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
@@ -145,11 +219,14 @@ static void handle_options(int argc, char **argv)
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
config_file = strdup(optarg);
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||
break;
|
||||
case 'M':
|
||||
pcu_direct = 1;
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
@@ -160,6 +237,13 @@ static void handle_options(int argc, char **argv)
|
||||
case 'p':
|
||||
dsp_trace = strtoul(optarg, NULL, 16);
|
||||
break;
|
||||
case 'w':
|
||||
print_hwversion();
|
||||
exit(0);
|
||||
break;
|
||||
case 'r':
|
||||
rt_prio = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -187,11 +271,35 @@ static void signal_handler(int signal)
|
||||
}
|
||||
}
|
||||
|
||||
static int write_pid_file(char *procname)
|
||||
{
|
||||
FILE *outf;
|
||||
char tmp[PATH_MAX+1];
|
||||
|
||||
snprintf(tmp, sizeof(tmp)-1, "/var/run/%s.pid", procname);
|
||||
tmp[PATH_MAX-1] = '\0';
|
||||
|
||||
outf = fopen(tmp, "w");
|
||||
if (!outf)
|
||||
return -1;
|
||||
|
||||
fprintf(outf, "%d\n", getpid());
|
||||
|
||||
fclose(outf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int sysmobts_ctrlif_inst_cmds(void);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct stat st;
|
||||
struct sched_param param;
|
||||
struct gsm_bts_role_bts *btsb;
|
||||
struct ipabis_link *link;
|
||||
struct e1inp_line *line;
|
||||
void *tall_msgb_ctx;
|
||||
struct osmo_fd accept_fd, read_fd;
|
||||
int rc;
|
||||
|
||||
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
|
||||
@@ -203,14 +311,29 @@ int main(int argc, char **argv)
|
||||
vty_init(&bts_vty_info);
|
||||
bts_vty_init(&bts_log_info);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
/* enable realtime priority for us */
|
||||
if (rt_prio != -1) {
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = rt_prio;
|
||||
rc = sched_setscheduler(getpid(), SCHED_RR, ¶m);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Setting SCHED_RR priority(%d) failed: %s\n",
|
||||
param.sched_priority, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
bts = gsm_bts_alloc(tall_bts_ctx);
|
||||
if (bts_init(bts) < 0) {
|
||||
fprintf(stderr, "unable to to open bts\n");
|
||||
fprintf(stderr, "unable to open bts\n");
|
||||
exit(1);
|
||||
}
|
||||
btsb = bts_role_bts(bts);
|
||||
btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3);
|
||||
|
||||
handle_options(argc, argv);
|
||||
abis_init(bts);
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0) {
|
||||
@@ -219,29 +342,51 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = telnet_init(tall_bts_ctx, NULL, 4241);
|
||||
clk_cal_use_eeprom(bts);
|
||||
|
||||
if (stat(SYSMOBTS_RF_LOCK_PATH, &st) == 0) {
|
||||
LOGP(DL1C, LOGL_NOTICE, "Not starting BTS due to RF_LOCK file present\n");
|
||||
exit(23);
|
||||
}
|
||||
write_pid_file("osmo-bts");
|
||||
|
||||
bts_controlif_setup(bts);
|
||||
sysmobts_ctrlif_inst_cmds();
|
||||
|
||||
rc = telnet_init(tall_bts_ctx, NULL, OSMO_VTY_PORT_BTS);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing telnet\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pcu_sock_init()) {
|
||||
fprintf(stderr, "PCU L1 socket failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
//signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
osmo_init_ignore_signals();
|
||||
|
||||
rc = oml_router_init(bts, OML_ROUTER_PATH, &accept_fd, &read_fd);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error creating the OML router: %s rc=%d\n",
|
||||
OML_ROUTER_PATH, rc);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!btsb->bsc_oml_host) {
|
||||
fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
link = link_init(bts, btsb->bsc_oml_host);
|
||||
if (!link) {
|
||||
line = abis_open(bts, btsb->bsc_oml_host, "sysmoBTS");
|
||||
if (!line) {
|
||||
fprintf(stderr, "unable to connect to BSC\n");
|
||||
exit(1);
|
||||
exit(2);
|
||||
}
|
||||
bts->oml_link = link;
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
|
||||
27
src/osmo-bts-sysmo/misc/sysmobts_eeprom.h
Normal file
27
src/osmo-bts-sysmo/misc/sysmobts_eeprom.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef _SYSMOBTS_EEPROM_H
|
||||
#define _SYSMOBTS_EEPROM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct sysmobts_eeprom { /* offset */
|
||||
uint8_t eth_mac[6]; /* 0-5 */
|
||||
uint8_t _pad0[10]; /* 6-15 */
|
||||
uint16_t unused1; /* 16-17 */
|
||||
uint8_t temp1_max; /* 18 */
|
||||
uint8_t temp2_max; /* 19 */
|
||||
uint32_t serial_nr; /* 20-23 */
|
||||
uint32_t operational_hours; /* 24-27 */
|
||||
uint32_t boot_count; /* 28-31 */
|
||||
uint16_t model_nr; /* 32-33 */
|
||||
uint16_t model_flags; /* 34-35 */
|
||||
uint8_t trx_nr; /* 36 */
|
||||
uint8_t _pad1[84]; /* 37-120 */
|
||||
uint8_t gpg_key[128]; /* 121-249 */
|
||||
} __attribute__((packed));
|
||||
|
||||
enum sysmobts_model_number {
|
||||
MODEL_SYSMOBTS_1020 = 1002,
|
||||
MODEL_SYSMOBTS_2050 = 2050,
|
||||
};
|
||||
|
||||
#endif
|
||||
308
src/osmo-bts-sysmo/misc/sysmobts_mgr.c
Normal file
308
src/osmo-bts-sysmo/misc/sysmobts_mgr.c
Normal file
@@ -0,0 +1,308 @@
|
||||
/* Main program for SysmoBTS management daemon */
|
||||
|
||||
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
|
||||
#include "misc/sysmobts_misc.h"
|
||||
#include "misc/sysmobts_mgr.h"
|
||||
#include "misc/sysmobts_par.h"
|
||||
|
||||
static int bts_type;
|
||||
static int trx_number;
|
||||
|
||||
static int no_eeprom_write = 0;
|
||||
static int daemonize = 0;
|
||||
void *tall_mgr_ctx;
|
||||
|
||||
/* every 6 hours means 365*4 = 1460 EEprom writes per year (max) */
|
||||
#define TEMP_TIMER_SECS (6 * 3600)
|
||||
|
||||
/* every 1 hours means 365*24 = 8760 EEprom writes per year (max) */
|
||||
#define HOURS_TIMER_SECS (1 * 3600)
|
||||
|
||||
/* the initial state */
|
||||
static struct sysmobts_mgr_instance manager = {
|
||||
.config_file = "sysmobts-mgr.cfg",
|
||||
.rf_limit = {
|
||||
.thresh_warn = 60,
|
||||
.thresh_crit = 78,
|
||||
},
|
||||
.digital_limit = {
|
||||
.thresh_warn = 60,
|
||||
.thresh_crit = 78,
|
||||
},
|
||||
.board_limit = {
|
||||
.thresh_warn = 60,
|
||||
.thresh_crit = 78,
|
||||
},
|
||||
.pa_limit = {
|
||||
.thresh_warn = 60,
|
||||
.thresh_crit = 100,
|
||||
},
|
||||
.action_warn = 0,
|
||||
.action_crit = TEMP_ACT_PA_OFF,
|
||||
.state = STATE_NORMAL,
|
||||
};
|
||||
|
||||
|
||||
static int classify_bts(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = sysmobts_par_get_int(SYSMOBTS_PAR_MODEL_NR, &bts_type);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to get model number.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = sysmobts_par_get_int(SYSMOBTS_PAR_TRX_NR, &trx_number);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to get the trx number.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_bts_type(void)
|
||||
{
|
||||
return bts_type;
|
||||
}
|
||||
|
||||
int sysmobts_trx_number(void)
|
||||
{
|
||||
return trx_number;
|
||||
}
|
||||
|
||||
int is_sbts2050(void)
|
||||
{
|
||||
return bts_type == 2050;
|
||||
}
|
||||
|
||||
int is_sbts2050_trx(int trx)
|
||||
{
|
||||
return trx_number == trx;
|
||||
}
|
||||
|
||||
int is_sbts2050_master(void)
|
||||
{
|
||||
if (!is_sbts2050())
|
||||
return 0;
|
||||
if (!is_sbts2050_trx(0))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct osmo_timer_list temp_timer;
|
||||
static void check_temp_timer_cb(void *unused)
|
||||
{
|
||||
sysmobts_check_temp(no_eeprom_write);
|
||||
|
||||
osmo_timer_schedule(&temp_timer, TEMP_TIMER_SECS, 0);
|
||||
}
|
||||
|
||||
static struct osmo_timer_list hours_timer;
|
||||
static void hours_timer_cb(void *unused)
|
||||
{
|
||||
sysmobts_update_hours(no_eeprom_write);
|
||||
|
||||
osmo_timer_schedule(&hours_timer, HOURS_TIMER_SECS, 0);
|
||||
}
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf("sysmobts-mgr [-nsD] [-d cat]\n");
|
||||
printf(" -n Do not write to EEPROM\n");
|
||||
printf(" -s Disable color\n");
|
||||
printf(" -d CAT enable debugging\n");
|
||||
printf(" -D daemonize\n");
|
||||
printf(" -c Specify the filename of the config file\n");
|
||||
}
|
||||
|
||||
static int parse_options(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "nhsd:c:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'n':
|
||||
no_eeprom_write = 1;
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
return -1;
|
||||
case 's':
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||
break;
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
manager.config_file = optarg;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void signal_handler(int signal)
|
||||
{
|
||||
fprintf(stderr, "signal %u received\n", signal);
|
||||
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
sysmobts_check_temp(no_eeprom_write);
|
||||
sysmobts_update_hours(no_eeprom_write);
|
||||
exit(0);
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGUSR1:
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_mgr_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct log_info_cat mgr_log_info_cat[] = {
|
||||
[DTEMP] = {
|
||||
.name = "DTEMP",
|
||||
.description = "Temperature monitoring",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DFW] = {
|
||||
.name = "DFW",
|
||||
.description = "DSP/FPGA firmware management",
|
||||
.color = "\033[1;36m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DFIND] = {
|
||||
.name = "DFIND",
|
||||
.description = "ipaccess-find handling",
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info mgr_log_info = {
|
||||
.cat = mgr_log_info_cat,
|
||||
.num_cat = ARRAY_SIZE(mgr_log_info_cat),
|
||||
};
|
||||
|
||||
static int mgr_log_init(void)
|
||||
{
|
||||
osmo_init_logging(&mgr_log_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *tall_msgb_ctx;
|
||||
int rc;
|
||||
|
||||
|
||||
tall_mgr_ctx = talloc_named_const(NULL, 1, "bts manager");
|
||||
tall_msgb_ctx = talloc_named_const(tall_mgr_ctx, 1, "msgb");
|
||||
msgb_set_talloc_ctx(tall_msgb_ctx);
|
||||
|
||||
mgr_log_init();
|
||||
if (classify_bts() != 0)
|
||||
exit(2);
|
||||
|
||||
osmo_init_ignore_signals();
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
|
||||
rc = parse_options(argc, argv);
|
||||
if (rc < 0)
|
||||
exit(2);
|
||||
|
||||
sysmobts_mgr_vty_init();
|
||||
logging_vty_add_cmds(&mgr_log_info);
|
||||
rc = sysmobts_mgr_parse_config(&manager);
|
||||
if (rc < 0) {
|
||||
LOGP(DFIND, LOGL_FATAL, "Cannot parse config file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = telnet_init(tall_msgb_ctx, NULL, OSMO_VTY_PORT_BTSMGR);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing telnet\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* start temperature check timer */
|
||||
temp_timer.cb = check_temp_timer_cb;
|
||||
check_temp_timer_cb(NULL);
|
||||
|
||||
/* start operational hours timer */
|
||||
hours_timer.cb = hours_timer_cb;
|
||||
hours_timer_cb(NULL);
|
||||
|
||||
/* start uc temperature check timer */
|
||||
sbts2050_uc_initialize();
|
||||
|
||||
/* handle broadcast messages for ipaccess-find */
|
||||
if (sysmobts_mgr_nl_init() != 0)
|
||||
exit(3);
|
||||
|
||||
/* Initialize the temperature control */
|
||||
sysmobts_mgr_temp_init(&manager);
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while (1) {
|
||||
log_reset_context();
|
||||
osmo_select_main(0);
|
||||
}
|
||||
}
|
||||
85
src/osmo-bts-sysmo/misc/sysmobts_mgr.h
Normal file
85
src/osmo-bts-sysmo/misc/sysmobts_mgr.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef _SYSMOBTS_MGR_H
|
||||
#define _SYSMOBTS_MGR_H
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
enum {
|
||||
DTEMP,
|
||||
DFW,
|
||||
DFIND,
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
#if 0
|
||||
TEMP_ACT_PWR_CONTRL = 0x1,
|
||||
#endif
|
||||
TEMP_ACT_SLAVE_OFF = 0x4,
|
||||
TEMP_ACT_PA_OFF = 0x8,
|
||||
TEMP_ACT_BTS_SRV_OFF = 0x10,
|
||||
};
|
||||
|
||||
/* actions only for normal state */
|
||||
enum {
|
||||
#if 0
|
||||
TEMP_ACT_NORM_PW_CONTRL = 0x1,
|
||||
#endif
|
||||
TEMP_ACT_NORM_SLAVE_ON = 0x4,
|
||||
TEMP_ACT_NORM_PA_ON = 0x8,
|
||||
TEMP_ACT_NORM_BTS_SRV_ON= 0x10,
|
||||
};
|
||||
|
||||
enum sysmobts_temp_state {
|
||||
STATE_NORMAL, /* Everything is fine */
|
||||
STATE_WARNING_HYST, /* Go back to normal next? */
|
||||
STATE_WARNING, /* We are above the warning threshold */
|
||||
STATE_CRITICAL, /* We have an issue. Wait for below warning */
|
||||
};
|
||||
|
||||
/**
|
||||
* Temperature Limits. We separate from a threshold
|
||||
* that will generate a warning and one that is so
|
||||
* severe that an action will be taken.
|
||||
*/
|
||||
struct sysmobts_temp_limit {
|
||||
int thresh_warn;
|
||||
int thresh_crit;
|
||||
};
|
||||
|
||||
enum mgr_vty_node {
|
||||
MGR_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
|
||||
ACT_NORM_NODE,
|
||||
ACT_WARN_NODE,
|
||||
ACT_CRIT_NODE,
|
||||
LIMIT_RF_NODE,
|
||||
LIMIT_DIGITAL_NODE,
|
||||
LIMIT_BOARD_NODE,
|
||||
LIMIT_PA_NODE,
|
||||
};
|
||||
|
||||
struct sysmobts_mgr_instance {
|
||||
const char *config_file;
|
||||
|
||||
struct sysmobts_temp_limit rf_limit;
|
||||
struct sysmobts_temp_limit digital_limit;
|
||||
|
||||
/* Only available on sysmobts 2050 */
|
||||
struct sysmobts_temp_limit board_limit;
|
||||
struct sysmobts_temp_limit pa_limit;
|
||||
|
||||
int action_norm;
|
||||
int action_warn;
|
||||
int action_crit;
|
||||
|
||||
enum sysmobts_temp_state state;
|
||||
};
|
||||
|
||||
int sysmobts_mgr_vty_init(void);
|
||||
int sysmobts_mgr_parse_config(struct sysmobts_mgr_instance *mgr);
|
||||
int sysmobts_mgr_nl_init(void);
|
||||
int sysmobts_mgr_temp_init(struct sysmobts_mgr_instance *mgr);
|
||||
const char *sysmobts_mgr_temp_get_state(enum sysmobts_temp_state state);
|
||||
|
||||
#endif
|
||||
384
src/osmo-bts-sysmo/misc/sysmobts_mgr_2050.c
Normal file
384
src/osmo-bts-sysmo/misc/sysmobts_mgr_2050.c
Normal file
@@ -0,0 +1,384 @@
|
||||
/* (C) 2014 by s.f.m.c. GmbH
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sysmobts_misc.h"
|
||||
#include "sysmobts_par.h"
|
||||
#include "sysmobts_mgr.h"
|
||||
#include "btsconfig.h"
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/serial.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef BUILD_SBTS2050
|
||||
#include <sysmocom/femtobts/sbts2050_header.h>
|
||||
|
||||
#define SERIAL_ALLOC_SIZE 300
|
||||
#define SIZE_HEADER_RSP 5
|
||||
#define SIZE_HEADER_CMD 4
|
||||
|
||||
struct uc {
|
||||
int id;
|
||||
int fd;
|
||||
const char *path;
|
||||
};
|
||||
|
||||
struct ucinfo {
|
||||
uint16_t id;
|
||||
int master;
|
||||
int slave;
|
||||
int pa;
|
||||
};
|
||||
|
||||
static struct uc ucontrol0 = {
|
||||
.id = 0,
|
||||
.path = "/dev/ttyS0",
|
||||
.fd = -1,
|
||||
};
|
||||
|
||||
/**********************************************************************
|
||||
* Functions read/write from serial interface
|
||||
*********************************************************************/
|
||||
static int hand_serial_read(int fd, struct msgb *msg, int numbytes)
|
||||
{
|
||||
int rc, bread = 0;
|
||||
|
||||
if (numbytes > msgb_tailroom(msg))
|
||||
return -ENOSPC;
|
||||
|
||||
while (bread < numbytes) {
|
||||
rc = read(fd, msg->tail, numbytes - bread);
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
if (rc == 0)
|
||||
break;
|
||||
|
||||
bread += rc;
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
return bread;
|
||||
}
|
||||
|
||||
static int hand_serial_write(int fd, struct msgb *msg)
|
||||
{
|
||||
int rc, bwritten = 0;
|
||||
|
||||
while (msg->len > 0) {
|
||||
rc = write(fd, msg->data, msg->len);
|
||||
if (rc <= 0)
|
||||
return -1;
|
||||
|
||||
msgb_pull(msg, rc);
|
||||
bwritten += rc;
|
||||
}
|
||||
|
||||
return bwritten;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Functions request information to Microcontroller
|
||||
*********************************************************************/
|
||||
static void add_parity(cmdpkt_t *command)
|
||||
{
|
||||
int n;
|
||||
uint8_t parity = 0x00;
|
||||
for (n = 0; n < SIZE_HEADER_CMD+command->u8Len; n++)
|
||||
parity ^= ((uint8_t *)command)[n];
|
||||
|
||||
command->cmd.raw[command->u8Len] = parity;
|
||||
}
|
||||
|
||||
static struct msgb *sbts2050_ucinfo_sndrcv(struct uc *ucontrol, const struct ucinfo *info)
|
||||
{
|
||||
int num, rc;
|
||||
cmdpkt_t *command;
|
||||
rsppkt_t *response;
|
||||
struct msgb *msg;
|
||||
fd_set fdread;
|
||||
struct timeval tout = {
|
||||
.tv_sec = 10,
|
||||
};
|
||||
|
||||
switch (info->id) {
|
||||
case SBTS2050_TEMP_RQT:
|
||||
num = sizeof(command->cmd.tempGet);
|
||||
break;
|
||||
case SBTS2050_PWR_RQT:
|
||||
num = sizeof(command->cmd.pwrSetState);
|
||||
break;
|
||||
case SBTS2050_PWR_STATUS:
|
||||
num = sizeof(command->cmd.pwrGetStatus);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
num = num + SIZE_HEADER_CMD+1;
|
||||
|
||||
msg = msgb_alloc(SERIAL_ALLOC_SIZE, "Message Microcontroller");
|
||||
if (msg == NULL) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Error creating msg\n");
|
||||
return NULL;
|
||||
}
|
||||
command = (cmdpkt_t *) msgb_put(msg, num);
|
||||
|
||||
command->u16Magic = 0xCAFE;
|
||||
switch (info->id) {
|
||||
case SBTS2050_TEMP_RQT:
|
||||
command->u8Id = info->id;
|
||||
command->u8Len = sizeof(command->cmd.tempGet);
|
||||
break;
|
||||
case SBTS2050_PWR_RQT:
|
||||
command->u8Id = info->id;
|
||||
command->u8Len = sizeof(command->cmd.pwrSetState);
|
||||
command->cmd.pwrSetState.u1MasterEn = !!info->master;
|
||||
command->cmd.pwrSetState.u1SlaveEn = !!info->slave;
|
||||
command->cmd.pwrSetState.u1PwrAmpEn = !!info->pa;
|
||||
break;
|
||||
case SBTS2050_PWR_STATUS:
|
||||
command->u8Id = info->id;
|
||||
command->u8Len = sizeof(command->cmd.pwrGetStatus);
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
|
||||
add_parity(command);
|
||||
|
||||
if (hand_serial_write(ucontrol->fd, msg) < 0)
|
||||
goto err;
|
||||
|
||||
msgb_reset(msg);
|
||||
|
||||
FD_ZERO(&fdread);
|
||||
FD_SET(ucontrol->fd, &fdread);
|
||||
|
||||
num = SIZE_HEADER_RSP;
|
||||
while (1) {
|
||||
rc = select(ucontrol->fd+1, &fdread, NULL, NULL, &tout);
|
||||
if (rc > 0) {
|
||||
if (hand_serial_read(ucontrol->fd, msg, num) < 0)
|
||||
goto err;
|
||||
|
||||
response = (rsppkt_t *)msg->data;
|
||||
|
||||
if (response->u8Id != info->id || msg->len <= 0 ||
|
||||
response->i8Error != RQT_SUCCESS)
|
||||
goto err;
|
||||
|
||||
if (msg->len == SIZE_HEADER_RSP + response->u8Len + 1)
|
||||
break;
|
||||
|
||||
num = response->u8Len + 1;
|
||||
} else
|
||||
goto err;
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
err:
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Get power status function
|
||||
*********************************************************************/
|
||||
int sbts2050_uc_get_status(struct sbts2050_power_status *status)
|
||||
{
|
||||
struct msgb *msg;
|
||||
const struct ucinfo info = {
|
||||
.id = SBTS2050_PWR_STATUS,
|
||||
};
|
||||
rsppkt_t *response;
|
||||
|
||||
memset(status, 0, sizeof(*status));
|
||||
msg = sbts2050_ucinfo_sndrcv(&ucontrol0, &info);
|
||||
|
||||
if (msg == NULL) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Error requesting power status.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
response = (rsppkt_t *)msg->data;
|
||||
|
||||
status->main_supply_current = response->rsp.pwrGetStatus.u8MainSupplyA / 64.f;
|
||||
|
||||
status->master_enabled = response->rsp.pwrGetStatus.u1MasterEn;
|
||||
status->master_voltage = response->rsp.pwrGetStatus.u8MasterV / 32.f;
|
||||
status->master_current = response->rsp.pwrGetStatus.u8MasterA / 64.f;;
|
||||
|
||||
status->slave_enabled = response->rsp.pwrGetStatus.u1SlaveEn;
|
||||
status->slave_voltage = response->rsp.pwrGetStatus.u8SlaveV / 32.f;
|
||||
status->slave_current = response->rsp.pwrGetStatus.u8SlaveA / 64.f;
|
||||
|
||||
status->pa_enabled = response->rsp.pwrGetStatus.u1PwrAmpEn;
|
||||
status->pa_voltage = response->rsp.pwrGetStatus.u8PwrAmpV / 4.f;
|
||||
status->pa_current = response->rsp.pwrGetStatus.u8PwrAmpA / 64.f;
|
||||
|
||||
status->pa_bias_voltage = response->rsp.pwrGetStatus.u8PwrAmpBiasV / 16.f;
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Uc Power Switching handling
|
||||
*********************************************************************/
|
||||
int sbts2050_uc_set_power(int pmaster, int pslave, int ppa)
|
||||
{
|
||||
struct msgb *msg;
|
||||
const struct ucinfo info = {
|
||||
.id = SBTS2050_PWR_RQT,
|
||||
.master = pmaster,
|
||||
.slave = pslave,
|
||||
.pa = ppa
|
||||
};
|
||||
|
||||
msg = sbts2050_ucinfo_sndrcv(&ucontrol0, &info);
|
||||
|
||||
if (msg == NULL) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Error switching off some unit.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Switch off/on success:\n"
|
||||
"MASTER %s\n"
|
||||
"SLAVE %s\n"
|
||||
"PA %s\n",
|
||||
pmaster ? "ON" : "OFF",
|
||||
pslave ? "ON" : "OFF",
|
||||
ppa ? "ON" : "OFF");
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Uc temperature handling
|
||||
*********************************************************************/
|
||||
int sbts2050_uc_check_temp(int *temp_pa, int *temp_board)
|
||||
{
|
||||
rsppkt_t *response;
|
||||
struct msgb *msg;
|
||||
const struct ucinfo info = {
|
||||
.id = SBTS2050_TEMP_RQT,
|
||||
};
|
||||
|
||||
msg = sbts2050_ucinfo_sndrcv(&ucontrol0, &info);
|
||||
|
||||
if (msg == NULL) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Error reading temperature\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
response = (rsppkt_t *)msg->data;
|
||||
|
||||
*temp_board = response->rsp.tempGet.i8BrdTemp;
|
||||
*temp_pa = response->rsp.tempGet.i8PaTemp;
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Temperature Board: %+3d C, "
|
||||
"Tempeture PA: %+3d C\n",
|
||||
response->rsp.tempGet.i8BrdTemp,
|
||||
response->rsp.tempGet.i8PaTemp);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbts2050_uc_initialize(void)
|
||||
{
|
||||
if (!is_sbts2050())
|
||||
return;
|
||||
|
||||
ucontrol0.fd = osmo_serial_init(ucontrol0.path, 115200);
|
||||
if (ucontrol0.fd < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to open the serial interface\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_sbts2050_master()) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "Going to enable the PA.\n");
|
||||
sbts2050_uc_set_pa_power(1);
|
||||
}
|
||||
}
|
||||
|
||||
int sbts2050_uc_set_pa_power(int on_off)
|
||||
{
|
||||
struct sbts2050_power_status status;
|
||||
if (sbts2050_uc_get_status(&status) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Failed to read current power status.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sbts2050_uc_set_power(status.master_enabled, status.slave_enabled, on_off);
|
||||
}
|
||||
|
||||
int sbts2050_uc_set_slave_power(int on_off)
|
||||
{
|
||||
struct sbts2050_power_status status;
|
||||
if (sbts2050_uc_get_status(&status) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Failed to read current power status.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sbts2050_uc_set_power(
|
||||
status.master_enabled,
|
||||
on_off,
|
||||
status.pa_enabled);
|
||||
}
|
||||
#else
|
||||
void sbts2050_uc_initialize(void)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_NOTICE, "sysmoBTS2050 was not enabled at compile time.\n");
|
||||
}
|
||||
|
||||
int sbts2050_uc_check_temp(int *temp_pa, int *temp_board)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without temp support.\n");
|
||||
*temp_pa = *temp_board = 99999;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sbts2050_uc_get_status(struct sbts2050_power_status *status)
|
||||
{
|
||||
memset(status, 0, sizeof(*status));
|
||||
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without status support.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sbts2050_uc_set_pa_power(int on_off)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without PA support.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sbts2050_uc_set_slave_power(int on_off)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_ERROR, "sysmoBTS2050 compiled without UC support.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
205
src/osmo-bts-sysmo/misc/sysmobts_mgr_nl.c
Normal file
205
src/osmo-bts-sysmo/misc/sysmobts_mgr_nl.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/* NetworkListen for SysmoBTS management daemon */
|
||||
|
||||
/*
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "misc/sysmobts_mgr.h"
|
||||
#include "misc/sysmobts_misc.h"
|
||||
#include "misc/sysmobts_nl.h"
|
||||
#include "misc/sysmobts_par.h"
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct osmo_fd nl_fd;
|
||||
|
||||
/*
|
||||
* The TLV structure in IPA messages in UDP packages is a bit
|
||||
* weird. First the header appears to have an extra NULL byte
|
||||
* and second the L16 of the L16TV needs to include +1 for the
|
||||
* tag. The default msgb/tlv and libosmo-abis routines do not
|
||||
* provide this.
|
||||
*/
|
||||
|
||||
static void ipaccess_prepend_header_quirk(struct msgb *msg, int proto)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
/* prepend the ip.access header */
|
||||
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh) + 1);
|
||||
hh->len = htons(msg->len - sizeof(*hh) - 1);
|
||||
hh->proto = proto;
|
||||
}
|
||||
|
||||
static void quirk_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,
|
||||
const uint8_t *val)
|
||||
{
|
||||
uint8_t *buf = msgb_put(msg, len + 2 + 1);
|
||||
|
||||
*buf++ = (len + 1) >> 8;
|
||||
*buf++ = (len + 1) & 0xff;
|
||||
*buf++ = tag;
|
||||
memcpy(buf, val, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't look at the content of the request yet and lie
|
||||
* about most of the responses.
|
||||
*/
|
||||
static void respond_to(struct sockaddr_in *src, struct osmo_fd *fd,
|
||||
uint8_t *data, size_t len)
|
||||
{
|
||||
static int fetched_info = 0;
|
||||
static char mac_str[20] = {0, };
|
||||
static char *model_name;
|
||||
static char ser_str[20] = {0, };
|
||||
|
||||
struct sockaddr_in loc_addr;
|
||||
int rc;
|
||||
char loc_ip[INET_ADDRSTRLEN];
|
||||
struct msgb *msg = msgb_alloc_headroom(512, 128, "ipa get response");
|
||||
if (!msg) {
|
||||
LOGP(DFIND, LOGL_ERROR, "Failed to allocate msgb\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fetched_info) {
|
||||
uint8_t mac[6];
|
||||
int serno;
|
||||
|
||||
/* fetch the MAC */
|
||||
sysmobts_par_get_buf(SYSMOBTS_PAR_MAC, mac, sizeof(mac));
|
||||
snprintf(mac_str, sizeof(mac_str), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
|
||||
mac[0], mac[1], mac[2],
|
||||
mac[3], mac[4], mac[5]);
|
||||
|
||||
/* fetch the serial number */
|
||||
sysmobts_par_get_int(SYSMOBTS_PAR_SERNR, &serno);
|
||||
snprintf(ser_str, sizeof(ser_str), "%d", serno);
|
||||
|
||||
/* fetch the model and trx number */
|
||||
switch(sysmobts_bts_type()) {
|
||||
case 0:
|
||||
case 0xffff:
|
||||
case 1002:
|
||||
model_name = "sysmoBTS 1002";
|
||||
break;
|
||||
case 2050:
|
||||
if (sysmobts_trx_number() == 0)
|
||||
model_name = "sysmoBTS 2050 (master)";
|
||||
else if (sysmobts_trx_number() == 1)
|
||||
model_name = "sysmoBTS 2050 (slave)";
|
||||
else
|
||||
model_name = "sysmoBTS 2050 (unknown)";
|
||||
break;
|
||||
default:
|
||||
model_name = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
fetched_info = 1;
|
||||
}
|
||||
|
||||
if (source_for_dest(&src->sin_addr, &loc_addr.sin_addr) != 0) {
|
||||
LOGP(DFIND, LOGL_ERROR, "Failed to determine local source\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msgb_put_u8(msg, IPAC_MSGT_ID_RESP);
|
||||
|
||||
/* append MAC addr */
|
||||
quirk_l16tv_put(msg, strlen(mac_str) + 1, IPAC_IDTAG_MACADDR, (uint8_t *) mac_str);
|
||||
|
||||
/* append ip address */
|
||||
inet_ntop(AF_INET, &loc_addr.sin_addr, loc_ip, sizeof(loc_ip));
|
||||
quirk_l16tv_put(msg, strlen(loc_ip) + 1, IPAC_IDTAG_IPADDR, (uint8_t *) loc_ip);
|
||||
|
||||
/* append the serial number */
|
||||
quirk_l16tv_put(msg, strlen(ser_str) + 1, IPAC_IDTAG_SERNR, (uint8_t *) ser_str);
|
||||
|
||||
/* abuse some flags */
|
||||
quirk_l16tv_put(msg, strlen(model_name) + 1, IPAC_IDTAG_UNIT, (uint8_t *) model_name);
|
||||
|
||||
/* ip.access nanoBTS would reply to port==3006 */
|
||||
ipaccess_prepend_header_quirk(msg, IPAC_PROTO_IPACCESS);
|
||||
rc = sendto(fd->fd, msg->data, msg->len, 0, (struct sockaddr *)src, sizeof(*src));
|
||||
if (rc != msg->len)
|
||||
LOGP(DFIND, LOGL_ERROR,
|
||||
"Failed to send with rc(%d) errno(%d)\n", rc, errno);
|
||||
}
|
||||
|
||||
static int ipaccess_bcast(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
uint8_t data[2048];
|
||||
char src[INET_ADDRSTRLEN];
|
||||
struct sockaddr_in addr = {};
|
||||
socklen_t len = sizeof(addr);
|
||||
int rc;
|
||||
|
||||
rc = recvfrom(fd->fd, data, sizeof(data), 0,
|
||||
(struct sockaddr *) &addr, &len);
|
||||
if (rc <= 0) {
|
||||
LOGP(DFIND, LOGL_ERROR,
|
||||
"Failed to read from socket errno(%d)\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DFIND, LOGL_DEBUG,
|
||||
"Received request from: %s size %d\n",
|
||||
inet_ntop(AF_INET, &addr.sin_addr, src, sizeof(src)), rc);
|
||||
|
||||
if (rc < 6)
|
||||
return 0;
|
||||
|
||||
if (data[2] != IPAC_PROTO_IPACCESS || data[4] != IPAC_MSGT_ID_GET)
|
||||
return 0;
|
||||
|
||||
respond_to(&addr, fd, data + 6, rc - 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_mgr_nl_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
nl_fd.cb = ipaccess_bcast;
|
||||
rc = osmo_sock_init_ofd(&nl_fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
|
||||
"0.0.0.0", 3006, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
perror("Socket creation");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
294
src/osmo-bts-sysmo/misc/sysmobts_mgr_temp.c
Normal file
294
src/osmo-bts-sysmo/misc/sysmobts_mgr_temp.c
Normal file
@@ -0,0 +1,294 @@
|
||||
/* Temperature control for SysmoBTS management daemon */
|
||||
|
||||
/*
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "misc/sysmobts_mgr.h"
|
||||
#include "misc/sysmobts_misc.h"
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
static struct sysmobts_mgr_instance *s_mgr;
|
||||
static struct osmo_timer_list temp_ctrl_timer;
|
||||
|
||||
static const struct value_string state_names[] = {
|
||||
{ STATE_NORMAL, "NORMAL" },
|
||||
{ STATE_WARNING_HYST, "WARNING (HYST)" },
|
||||
{ STATE_WARNING, "WARNING" },
|
||||
{ STATE_CRITICAL, "CRITICAL" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *sysmobts_mgr_temp_get_state(enum sysmobts_temp_state state)
|
||||
{
|
||||
return get_value_string(state_names, state);
|
||||
}
|
||||
|
||||
static int next_state(enum sysmobts_temp_state current_state, int critical, int warning)
|
||||
{
|
||||
int next_state = -1;
|
||||
switch (current_state) {
|
||||
case STATE_NORMAL:
|
||||
if (critical)
|
||||
next_state = STATE_CRITICAL;
|
||||
else if (warning)
|
||||
next_state = STATE_WARNING;
|
||||
break;
|
||||
case STATE_WARNING_HYST:
|
||||
if (critical)
|
||||
next_state = STATE_CRITICAL;
|
||||
else if (warning)
|
||||
next_state = STATE_WARNING;
|
||||
else
|
||||
next_state = STATE_NORMAL;
|
||||
break;
|
||||
case STATE_WARNING:
|
||||
if (critical)
|
||||
next_state = STATE_CRITICAL;
|
||||
else if (!warning)
|
||||
next_state = STATE_WARNING_HYST;
|
||||
break;
|
||||
case STATE_CRITICAL:
|
||||
if (!critical && !warning)
|
||||
next_state = STATE_WARNING;
|
||||
break;
|
||||
};
|
||||
|
||||
return next_state;
|
||||
}
|
||||
|
||||
static void handle_normal_actions(int actions)
|
||||
{
|
||||
/* switch off the PA */
|
||||
if (actions & TEMP_ACT_NORM_PA_ON) {
|
||||
if (!is_sbts2050()) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"PA can only be switched-on on the master\n");
|
||||
} else if (sbts2050_uc_set_pa_power(1) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to switch on the PA\n");
|
||||
} else {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Switched on the PA as normal action.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (actions & TEMP_ACT_NORM_SLAVE_ON) {
|
||||
if (!is_sbts2050()) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Slave on only possible on the sysmoBTS2050\n");
|
||||
} else if (sbts2050_uc_set_slave_power(1) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to switch on the slave BTS\n");
|
||||
} else {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Switched on the slave as normal action.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (actions & TEMP_ACT_NORM_BTS_SRV_ON) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Going to switch on the BTS service\n");
|
||||
/*
|
||||
* TODO: use/create something like nspawn that serializes
|
||||
* and used SIGCHLD/waitpid to pick up the dead processes
|
||||
* without invoking shell.
|
||||
*/
|
||||
system("/bin/systemctl start sysmobts.service");
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_actions(int actions)
|
||||
{
|
||||
/* switch off the PA */
|
||||
if (actions & TEMP_ACT_PA_OFF) {
|
||||
if (!is_sbts2050()) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"PA can only be switched-off on the master\n");
|
||||
} else if (sbts2050_uc_set_pa_power(0) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to switch off the PA. Stop BTS?\n");
|
||||
} else {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Switched off the PA due temperature.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (actions & TEMP_ACT_SLAVE_OFF) {
|
||||
if (!is_sbts2050()) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Slave off only possible on the sysmoBTS2050\n");
|
||||
} else if (sbts2050_uc_set_slave_power(0) != 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to switch off the slave BTS\n");
|
||||
} else {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Switched off the slave due temperature\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (actions & TEMP_ACT_BTS_SRV_OFF) {
|
||||
LOGP(DTEMP, LOGL_NOTICE,
|
||||
"Going to switch off the BTS service\n");
|
||||
/*
|
||||
* TODO: use/create something like nspawn that serializes
|
||||
* and used SIGCHLD/waitpid to pick up the dead processes
|
||||
* without invoking shell.
|
||||
*/
|
||||
system("/bin/systemctl stop sysmobts.service");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go back to normal! Depending on the configuration execute the normal
|
||||
* actions that could (start to) undo everything we did in the other
|
||||
* states. What is still missing is the power increase/decrease depending
|
||||
* on the state. E.g. starting from WARNING_HYST we might want to slowly
|
||||
* ramp up the output power again.
|
||||
*/
|
||||
static void execute_normal_act(struct sysmobts_mgr_instance *manager)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System is back to normal temperature.\n");
|
||||
handle_normal_actions(manager->action_norm);
|
||||
}
|
||||
|
||||
static void execute_warning_act(struct sysmobts_mgr_instance *manager)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached temperature warning.\n");
|
||||
handle_actions(manager->action_warn);
|
||||
}
|
||||
|
||||
static void execute_critical_act(struct sysmobts_mgr_instance *manager)
|
||||
{
|
||||
LOGP(DTEMP, LOGL_NOTICE, "System has reached critical warning.\n");
|
||||
handle_actions(manager->action_crit);
|
||||
}
|
||||
|
||||
static void sysmobts_mgr_temp_handle(struct sysmobts_mgr_instance *manager,
|
||||
int critical, int warning)
|
||||
{
|
||||
int new_state = next_state(manager->state, critical, warning);
|
||||
|
||||
/* Nothing changed */
|
||||
if (new_state < 0)
|
||||
return;
|
||||
|
||||
LOGP(DTEMP, LOGL_NOTICE, "Moving from state %s to %s.\n",
|
||||
get_value_string(state_names, manager->state),
|
||||
get_value_string(state_names, new_state));
|
||||
manager->state = new_state;
|
||||
switch (manager->state) {
|
||||
case STATE_NORMAL:
|
||||
execute_normal_act(manager);
|
||||
break;
|
||||
case STATE_WARNING_HYST:
|
||||
/* do nothing? Maybe start to increase transmit power? */
|
||||
break;
|
||||
case STATE_WARNING:
|
||||
execute_warning_act(manager);
|
||||
break;
|
||||
case STATE_CRITICAL:
|
||||
execute_critical_act(manager);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static void temp_ctrl_check()
|
||||
{
|
||||
int rc;
|
||||
int warn_thresh_passed = 0;
|
||||
int crit_thresh_passed = 0;
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Going to check the temperature.\n");
|
||||
|
||||
/* Read the current digital temperature */
|
||||
rc = sysmobts_temp_get(SYSMOBTS_TEMP_DIGITAL, SYSMOBTS_TEMP_INPUT);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to read the digital temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
int temp = rc / 1000;
|
||||
if (temp > s_mgr->digital_limit.thresh_warn)
|
||||
warn_thresh_passed = 1;
|
||||
if (temp > s_mgr->digital_limit.thresh_crit)
|
||||
crit_thresh_passed = 1;
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Digital temperature is: %d\n", temp);
|
||||
}
|
||||
|
||||
/* Read the current RF temperature */
|
||||
rc = sysmobts_temp_get(SYSMOBTS_TEMP_RF, SYSMOBTS_TEMP_INPUT);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to read the RF temperature. rc=%d\n", rc);
|
||||
warn_thresh_passed = crit_thresh_passed = 1;
|
||||
} else {
|
||||
int temp = rc / 1000;
|
||||
if (temp > s_mgr->rf_limit.thresh_warn)
|
||||
warn_thresh_passed = 1;
|
||||
if (temp > s_mgr->rf_limit.thresh_crit)
|
||||
crit_thresh_passed = 1;
|
||||
LOGP(DTEMP, LOGL_DEBUG, "RF temperature is: %d\n", temp);
|
||||
}
|
||||
|
||||
if (is_sbts2050()) {
|
||||
int temp_pa, temp_board;
|
||||
|
||||
rc = sbts2050_uc_check_temp(&temp_pa, &temp_board);
|
||||
if (rc != 0) {
|
||||
/* XXX what do here? */
|
||||
LOGP(DTEMP, LOGL_ERROR,
|
||||
"Failed to read the temperature! Reboot?!\n");
|
||||
warn_thresh_passed = 1;
|
||||
crit_thresh_passed = 1;
|
||||
} else {
|
||||
LOGP(DTEMP, LOGL_DEBUG, "SBTS2050 board(%d) PA(%d)\n",
|
||||
temp_board, temp_pa);
|
||||
if (temp_pa > s_mgr->pa_limit.thresh_warn)
|
||||
warn_thresh_passed = 1;
|
||||
if (temp_pa > s_mgr->pa_limit.thresh_crit)
|
||||
crit_thresh_passed = 1;
|
||||
if (temp_board > s_mgr->board_limit.thresh_warn)
|
||||
warn_thresh_passed = 1;
|
||||
if (temp_board > s_mgr->board_limit.thresh_crit)
|
||||
crit_thresh_passed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sysmobts_mgr_temp_handle(s_mgr, crit_thresh_passed, warn_thresh_passed);
|
||||
}
|
||||
|
||||
static void temp_ctrl_check_cb(void *unused)
|
||||
{
|
||||
temp_ctrl_check();
|
||||
/* Check every two minutes? XXX make it configurable! */
|
||||
osmo_timer_schedule(&temp_ctrl_timer, 2 * 60, 0);
|
||||
}
|
||||
|
||||
int sysmobts_mgr_temp_init(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
s_mgr = mgr;
|
||||
temp_ctrl_timer.cb = temp_ctrl_check_cb;
|
||||
temp_ctrl_check_cb(NULL);
|
||||
return 0;
|
||||
}
|
||||
522
src/osmo-bts-sysmo/misc/sysmobts_mgr_vty.c
Normal file
522
src/osmo-bts-sysmo/misc/sysmobts_mgr_vty.c
Normal file
@@ -0,0 +1,522 @@
|
||||
/* (C) 2014 by sysmocom - s.f.m.c. GmbH
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Alvaro Neira Ayuso <anayuso@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#include "sysmobts_misc.h"
|
||||
#include "sysmobts_mgr.h"
|
||||
#include "btsconfig.h"
|
||||
|
||||
static struct sysmobts_mgr_instance *s_mgr;
|
||||
|
||||
static const char copyright[] =
|
||||
"(C) 2012 by Harald Welte <laforge@gnumonks.org>\r\n"
|
||||
"(C) 2014 by Holger Hans Peter Freyther\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||
|
||||
static enum node_type go_to_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case MGR_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
break;
|
||||
case ACT_NORM_NODE:
|
||||
case ACT_WARN_NODE:
|
||||
case ACT_CRIT_NODE:
|
||||
case LIMIT_RF_NODE:
|
||||
case LIMIT_DIGITAL_NODE:
|
||||
case LIMIT_BOARD_NODE:
|
||||
case LIMIT_PA_NODE:
|
||||
vty->node = MGR_NODE;
|
||||
break;
|
||||
default:
|
||||
vty->node = CONFIG_NODE;
|
||||
}
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
static int is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
case MGR_NODE:
|
||||
case ACT_NORM_NODE:
|
||||
case ACT_WARN_NODE:
|
||||
case ACT_CRIT_NODE:
|
||||
case LIMIT_RF_NODE:
|
||||
case LIMIT_DIGITAL_NODE:
|
||||
case LIMIT_BOARD_NODE:
|
||||
case LIMIT_PA_NODE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "sysmobts-mgr",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = go_to_parent,
|
||||
.is_config_node = is_config_node,
|
||||
.copyright = copyright,
|
||||
};
|
||||
|
||||
|
||||
#define MGR_STR "Configure sysmobts-mgr\n"
|
||||
|
||||
static struct cmd_node mgr_node = {
|
||||
MGR_NODE,
|
||||
"%s(sysmobts-mgr)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node act_norm_node = {
|
||||
ACT_NORM_NODE,
|
||||
"%s(action-normal)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node act_warn_node = {
|
||||
ACT_WARN_NODE,
|
||||
"%s(action-warn)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node act_crit_node = {
|
||||
ACT_CRIT_NODE,
|
||||
"%s(action-critical)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_rf_node = {
|
||||
LIMIT_RF_NODE,
|
||||
"%s(limit-rf)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_digital_node = {
|
||||
LIMIT_DIGITAL_NODE,
|
||||
"%s(limit-digital)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_board_node = {
|
||||
LIMIT_BOARD_NODE,
|
||||
"%s(limit-board)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node limit_pa_node = {
|
||||
LIMIT_PA_NODE,
|
||||
"%s(limit-pa)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
DEFUN(cfg_mgr, cfg_mgr_cmd,
|
||||
"sysmobts-mgr",
|
||||
MGR_STR)
|
||||
{
|
||||
vty->node = MGR_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void write_temp_limit(struct vty *vty, const char *name,
|
||||
struct sysmobts_temp_limit *limit)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " threshold warning %d%s",
|
||||
limit->thresh_warn, VTY_NEWLINE);
|
||||
vty_out(vty, " threshold critical %d%s",
|
||||
limit->thresh_crit, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void write_norm_action(struct vty *vty, const char *name, int actions)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
vty_out(vty, " %spa-on%s",
|
||||
(actions & TEMP_ACT_NORM_PA_ON) ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %sbts-service-on%s",
|
||||
(actions & TEMP_ACT_NORM_BTS_SRV_ON) ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %sslave-on%s",
|
||||
(actions & TEMP_ACT_NORM_SLAVE_ON) ? "" : "no ", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void write_action(struct vty *vty, const char *name, int actions)
|
||||
{
|
||||
vty_out(vty, " %s%s", name, VTY_NEWLINE);
|
||||
#if 0
|
||||
vty_out(vty, " %spower-control%s",
|
||||
(actions & TEMP_ACT_PWR_CONTRL) ? "" : "no ", VTY_NEWLINE);
|
||||
|
||||
/* only on the sysmobts 2050 */
|
||||
vty_out(vty, " %smaster-off%s",
|
||||
(actions & TEMP_ACT_MASTER_OFF) ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %sslave-off%s",
|
||||
(actions & TEMP_ACT_MASTER_OFF) ? "" : "no ", VTY_NEWLINE);
|
||||
#endif
|
||||
vty_out(vty, " %spa-off%s",
|
||||
(actions & TEMP_ACT_PA_OFF) ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %sbts-service-off%s",
|
||||
(actions & TEMP_ACT_BTS_SRV_OFF) ? "" : "no ", VTY_NEWLINE);
|
||||
vty_out(vty, " %sslave-off%s",
|
||||
(actions & TEMP_ACT_SLAVE_OFF) ? "" : "no ", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static int config_write_mgr(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "sysmobts-mgr%s", VTY_NEWLINE);
|
||||
|
||||
write_temp_limit(vty, "limits rf", &s_mgr->rf_limit);
|
||||
write_temp_limit(vty, "limits digital", &s_mgr->digital_limit);
|
||||
write_temp_limit(vty, "limits board", &s_mgr->board_limit);
|
||||
write_temp_limit(vty, "limits pa", &s_mgr->pa_limit);
|
||||
|
||||
write_norm_action(vty, "actions normal", s_mgr->action_norm);
|
||||
write_action(vty, "actions warn", s_mgr->action_warn);
|
||||
write_action(vty, "actions critical", s_mgr->action_crit);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_dummy(struct vty *vty)
|
||||
{
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CFG_LIMIT(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_limit_##name, cfg_limit_##name##_cmd, \
|
||||
"limits " #name, \
|
||||
"Configure Limits\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
CFG_LIMIT(rf, "RF\n", LIMIT_RF_NODE, rf_limit)
|
||||
CFG_LIMIT(digital, "Digital\n", LIMIT_DIGITAL_NODE, digital_limit)
|
||||
CFG_LIMIT(board, "Board\n", LIMIT_BOARD_NODE, board_limit)
|
||||
CFG_LIMIT(pa, "Power Amplifier\n", LIMIT_PA_NODE, pa_limit)
|
||||
#undef CFG_LIMIT
|
||||
|
||||
DEFUN(cfg_limit_warning, cfg_thresh_warning_cmd,
|
||||
"threshold warning <0-200>",
|
||||
"Threshold to reach\n" "Warning level\n" "Range\n")
|
||||
{
|
||||
struct sysmobts_temp_limit *limit = vty->index;
|
||||
limit->thresh_warn = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_limit_crit, cfg_thresh_crit_cmd,
|
||||
"threshold critical <0-200>",
|
||||
"Threshold to reach\n" "Severe level\n" "Range\n")
|
||||
{
|
||||
struct sysmobts_temp_limit *limit = vty->index;
|
||||
limit->thresh_crit = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CFG_ACTION(name, expl, switch_to, variable) \
|
||||
DEFUN(cfg_action_##name, cfg_action_##name##_cmd, \
|
||||
"actions " #name, \
|
||||
"Configure Actions\n" expl) \
|
||||
{ \
|
||||
vty->node = switch_to; \
|
||||
vty->index = &s_mgr->variable; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
CFG_ACTION(normal, "Normal Actions\n", ACT_NORM_NODE, action_norm)
|
||||
CFG_ACTION(warn, "Warning Actions\n", ACT_WARN_NODE, action_warn)
|
||||
CFG_ACTION(critical, "Critical Actions\n", ACT_CRIT_NODE, action_crit)
|
||||
#undef CFG_ACTION
|
||||
|
||||
DEFUN(cfg_action_pa_on, cfg_action_pa_on_cmd,
|
||||
"pa-on",
|
||||
"Switch the Power Amplifier on\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= TEMP_ACT_NORM_PA_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_pa_on, cfg_no_action_pa_on_cmd,
|
||||
"no pa-on",
|
||||
NO_STR "Switch the Power Amplifier on\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~TEMP_ACT_NORM_PA_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_bts_srv_on, cfg_action_bts_srv_on_cmd,
|
||||
"bts-service-on",
|
||||
"Start the systemd sysmobts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= TEMP_ACT_NORM_BTS_SRV_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_bts_srv_on, cfg_no_action_bts_srv_on_cmd,
|
||||
"no bts-service-on",
|
||||
NO_STR "Start the systemd sysmobts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~TEMP_ACT_NORM_BTS_SRV_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_slave_on, cfg_action_slave_on_cmd,
|
||||
"slave-on",
|
||||
"Power-on secondary device on sysmoBTS2050\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= TEMP_ACT_NORM_SLAVE_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_slave_on, cfg_no_action_slave_on_cmd,
|
||||
"no slave-on",
|
||||
NO_STR "Power-on secondary device on sysmoBTS2050\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~TEMP_ACT_NORM_SLAVE_ON;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_pa_off, cfg_action_pa_off_cmd,
|
||||
"pa-off",
|
||||
"Switch the Power Amplifier off\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= TEMP_ACT_PA_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_pa_off, cfg_no_action_pa_off_cmd,
|
||||
"no pa-off",
|
||||
NO_STR "Do not switch off the Power Amplifier\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~TEMP_ACT_PA_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_bts_srv_off, cfg_action_bts_srv_off_cmd,
|
||||
"bts-service-off",
|
||||
"Stop the systemd sysmobts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= TEMP_ACT_BTS_SRV_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_bts_srv_off, cfg_no_action_bts_srv_off_cmd,
|
||||
"no bts-service-off",
|
||||
NO_STR "Stop the systemd sysmobts.service\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~TEMP_ACT_BTS_SRV_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_action_slave_off, cfg_action_slave_off_cmd,
|
||||
"slave-off",
|
||||
"Power-off secondary device on sysmoBTS2050\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action |= TEMP_ACT_SLAVE_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_action_slave_off, cfg_no_action_slave_off_cmd,
|
||||
"no slave-off",
|
||||
NO_STR "Power-off secondary device on sysmoBTS2050\n")
|
||||
{
|
||||
int *action = vty->index;
|
||||
*action &= ~TEMP_ACT_SLAVE_OFF;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_mgr, show_mgr_cmd, "show manager",
|
||||
SHOW_STR "Display information about the manager")
|
||||
{
|
||||
vty_out(vty, "Temperature control state: %s%s",
|
||||
sysmobts_mgr_temp_get_state(s_mgr->state), VTY_NEWLINE);
|
||||
vty_out(vty, "Current Temperatures%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Digital: %f Celcius%s",
|
||||
sysmobts_temp_get(SYSMOBTS_TEMP_DIGITAL,
|
||||
SYSMOBTS_TEMP_INPUT) / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " RF: %f Celcius%s",
|
||||
sysmobts_temp_get(SYSMOBTS_TEMP_RF,
|
||||
SYSMOBTS_TEMP_INPUT) / 1000.0f,
|
||||
VTY_NEWLINE);
|
||||
if (is_sbts2050()) {
|
||||
int temp_pa, temp_board;
|
||||
struct sbts2050_power_status status;
|
||||
|
||||
vty_out(vty, " sysmoBTS 2050 is %s%s",
|
||||
is_sbts2050_master() ? "master" : "slave", VTY_NEWLINE);
|
||||
|
||||
sbts2050_uc_check_temp(&temp_pa, &temp_board);
|
||||
vty_out(vty, " sysmoBTS 2050 PA: %d Celcius%s", temp_pa, VTY_NEWLINE);
|
||||
vty_out(vty, " sysmoBTS 2050 PA: %d Celcius%s", temp_board, VTY_NEWLINE);
|
||||
|
||||
sbts2050_uc_get_status(&status);
|
||||
vty_out(vty, "Power Status%s", VTY_NEWLINE);
|
||||
vty_out(vty, " Main Supply :(ON) [(24.00)Vdc, %4.2f A]%s",
|
||||
status.main_supply_current, VTY_NEWLINE);
|
||||
vty_out(vty, " Master SF : %s [%6.2f Vdc, %4.2f A]%s",
|
||||
status.master_enabled ? "ON " : "OFF",
|
||||
status.master_voltage, status.master_current,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Slave SF : %s [%6.2f Vdc, %4.2f A]%s",
|
||||
status.slave_enabled ? "ON" : "OFF",
|
||||
status.slave_voltage, status.slave_current,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Power Amp : %s [%6.2f Vdc, %4.2f A]%s",
|
||||
status.pa_enabled ? "ON" : "OFF",
|
||||
status.pa_voltage, status.pa_current,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " PA Bias : %s [%6.2f Vdc, ---- A]%s",
|
||||
status.pa_enabled ? "ON" : "OFF",
|
||||
status.pa_bias_voltage,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void register_limit(int limit)
|
||||
{
|
||||
install_element(limit, &cfg_thresh_warning_cmd);
|
||||
install_element(limit, &cfg_thresh_crit_cmd);
|
||||
}
|
||||
|
||||
static void register_normal_action(int act)
|
||||
{
|
||||
install_element(act, &cfg_action_pa_on_cmd);
|
||||
install_element(act, &cfg_no_action_pa_on_cmd);
|
||||
install_element(act, &cfg_action_bts_srv_on_cmd);
|
||||
install_element(act, &cfg_no_action_bts_srv_on_cmd);
|
||||
|
||||
/* these only work on the sysmobts 2050 */
|
||||
install_element(act, &cfg_action_slave_on_cmd);
|
||||
install_element(act, &cfg_no_action_slave_on_cmd);
|
||||
}
|
||||
|
||||
static void register_action(int act)
|
||||
{
|
||||
#if 0
|
||||
install_element(act, &cfg_action_pwr_contrl_cmd);
|
||||
install_element(act, &cfg_no_action_pwr_contrl_cmd);
|
||||
#endif
|
||||
install_element(act, &cfg_action_pa_off_cmd);
|
||||
install_element(act, &cfg_no_action_pa_off_cmd);
|
||||
install_element(act, &cfg_action_bts_srv_off_cmd);
|
||||
install_element(act, &cfg_no_action_bts_srv_off_cmd);
|
||||
|
||||
/* these only work on the sysmobts 2050 */
|
||||
install_element(act, &cfg_action_slave_off_cmd);
|
||||
install_element(act, &cfg_no_action_slave_off_cmd);
|
||||
}
|
||||
|
||||
int sysmobts_mgr_vty_init(void)
|
||||
{
|
||||
vty_init(&vty_info);
|
||||
|
||||
install_element_ve(&show_mgr_cmd);
|
||||
|
||||
install_node(&mgr_node, config_write_mgr);
|
||||
install_element(CONFIG_NODE, &cfg_mgr_cmd);
|
||||
vty_install_default(MGR_NODE);
|
||||
|
||||
/* install the limit nodes */
|
||||
install_node(&limit_rf_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_rf_cmd);
|
||||
register_limit(LIMIT_RF_NODE);
|
||||
vty_install_default(LIMIT_RF_NODE);
|
||||
|
||||
install_node(&limit_digital_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_digital_cmd);
|
||||
register_limit(LIMIT_DIGITAL_NODE);
|
||||
vty_install_default(LIMIT_DIGITAL_NODE);
|
||||
|
||||
install_node(&limit_board_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_board_cmd);
|
||||
register_limit(LIMIT_BOARD_NODE);
|
||||
vty_install_default(LIMIT_BOARD_NODE);
|
||||
|
||||
install_node(&limit_pa_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_limit_pa_cmd);
|
||||
register_limit(LIMIT_PA_NODE);
|
||||
vty_install_default(LIMIT_PA_NODE);
|
||||
|
||||
/* install the normal node */
|
||||
install_node(&act_norm_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_action_normal_cmd);
|
||||
register_normal_action(ACT_NORM_NODE);
|
||||
|
||||
/* install the warning and critical node */
|
||||
install_node(&act_warn_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_action_warn_cmd);
|
||||
register_action(ACT_WARN_NODE);
|
||||
vty_install_default(ACT_WARN_NODE);
|
||||
|
||||
install_node(&act_crit_node, config_write_dummy);
|
||||
install_element(MGR_NODE, &cfg_action_critical_cmd);
|
||||
register_action(ACT_CRIT_NODE);
|
||||
vty_install_default(ACT_CRIT_NODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_mgr_parse_config(struct sysmobts_mgr_instance *manager)
|
||||
{
|
||||
int rc;
|
||||
|
||||
s_mgr = manager;
|
||||
rc = vty_read_config_file(s_mgr->config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n",
|
||||
s_mgr->config_file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
275
src/osmo-bts-sysmo/misc/sysmobts_misc.c
Normal file
275
src/osmo-bts-sysmo/misc/sysmobts_misc.c
Normal file
@@ -0,0 +1,275 @@
|
||||
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
|
||||
#include "btsconfig.h"
|
||||
#include "sysmobts_misc.h"
|
||||
#include "sysmobts_par.h"
|
||||
#include "sysmobts_mgr.h"
|
||||
|
||||
/*********************************************************************
|
||||
* Temperature handling
|
||||
*********************************************************************/
|
||||
|
||||
#define TEMP_PATH "/sys/class/hwmon/hwmon0/device/temp%u_%s"
|
||||
|
||||
static const char *temp_type_str[_NUM_TEMP_TYPES] = {
|
||||
[SYSMOBTS_TEMP_INPUT] = "input",
|
||||
[SYSMOBTS_TEMP_LOWEST] = "lowest",
|
||||
[SYSMOBTS_TEMP_HIGHEST] = "highest",
|
||||
};
|
||||
|
||||
int sysmobts_temp_get(enum sysmobts_temp_sensor sensor,
|
||||
enum sysmobts_temp_type type)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char tempstr[8];
|
||||
int fd, rc;
|
||||
|
||||
if (sensor < SYSMOBTS_TEMP_DIGITAL ||
|
||||
sensor > SYSMOBTS_TEMP_RF)
|
||||
return -EINVAL;
|
||||
|
||||
if (type >= ARRAY_SIZE(temp_type_str))
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, TEMP_PATH, sensor, temp_type_str[type]);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
fd = open(buf, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
rc = read(fd, tempstr, sizeof(tempstr));
|
||||
tempstr[sizeof(tempstr)-1] = '\0';
|
||||
if (rc < 0) {
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
if (rc == 0) {
|
||||
close(fd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return atoi(tempstr);
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum sysmobts_temp_sensor sensor;
|
||||
enum sysmobts_par ee_par;
|
||||
} temp_data[] = {
|
||||
{
|
||||
.name = "digital",
|
||||
.sensor = SYSMOBTS_TEMP_DIGITAL,
|
||||
.ee_par = SYSMOBTS_PAR_TEMP_DIG_MAX,
|
||||
}, {
|
||||
.name = "rf",
|
||||
.sensor = SYSMOBTS_TEMP_RF,
|
||||
.ee_par = SYSMOBTS_PAR_TEMP_RF_MAX,
|
||||
}
|
||||
};
|
||||
|
||||
void sysmobts_check_temp(int no_eeprom_write)
|
||||
{
|
||||
int temp_old[ARRAY_SIZE(temp_data)];
|
||||
int temp_hi[ARRAY_SIZE(temp_data)];
|
||||
int temp_cur[ARRAY_SIZE(temp_data)];
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(temp_data); i++) {
|
||||
int ret;
|
||||
rc = sysmobts_par_get_int(temp_data[i].ee_par, &ret);
|
||||
temp_old[i] = ret * 1000;
|
||||
temp_hi[i] = sysmobts_temp_get(temp_data[i].sensor,
|
||||
SYSMOBTS_TEMP_HIGHEST);
|
||||
temp_cur[i] = sysmobts_temp_get(temp_data[i].sensor,
|
||||
SYSMOBTS_TEMP_INPUT);
|
||||
|
||||
if ((temp_cur[i] < 0 && temp_cur[i] > -1000) ||
|
||||
(temp_hi[i] < 0 && temp_hi[i] > -1000)) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Error reading temperature\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DTEMP, LOGL_DEBUG, "Current %s temperature: %d.%d C\n",
|
||||
temp_data[i].name, temp_cur[i]/1000, temp_cur[i]%1000);
|
||||
|
||||
if (temp_hi[i] > temp_old[i]) {
|
||||
LOGP(DTEMP, LOGL_NOTICE, "New maximum %s "
|
||||
"temperature: %d.%d C\n", temp_data[i].name,
|
||||
temp_hi[i]/1000, temp_hi[i]%1000);
|
||||
|
||||
if (!no_eeprom_write) {
|
||||
rc = sysmobts_par_set_int(SYSMOBTS_PAR_TEMP_DIG_MAX,
|
||||
temp_hi[0]/1000);
|
||||
if (rc < 0)
|
||||
LOGP(DTEMP, LOGL_ERROR, "error writing new %s "
|
||||
"max temp %d (%s)\n", temp_data[i].name,
|
||||
rc, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Hours handling
|
||||
*********************************************************************/
|
||||
static time_t last_update;
|
||||
|
||||
int sysmobts_update_hours(int no_eeprom_write)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
int rc, op_hrs;
|
||||
|
||||
/* first time after start of manager program */
|
||||
if (last_update == 0) {
|
||||
last_update = now;
|
||||
|
||||
rc = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS, &op_hrs);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
|
||||
"operational hours: %d (%s)\n", rc,
|
||||
strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
|
||||
op_hrs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (now >= last_update + 3600) {
|
||||
rc = sysmobts_par_get_int(SYSMOBTS_PAR_HOURS, &op_hrs);
|
||||
if (rc < 0) {
|
||||
LOGP(DTEMP, LOGL_ERROR, "Unable to read "
|
||||
"operational hours: %d (%s)\n", rc,
|
||||
strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* number of hours to increase */
|
||||
op_hrs += (now-last_update)/3600;
|
||||
|
||||
LOGP(DTEMP, LOGL_INFO, "Total hours of Operation: %u\n",
|
||||
op_hrs);
|
||||
|
||||
if (!no_eeprom_write) {
|
||||
rc = sysmobts_par_set_int(SYSMOBTS_PAR_HOURS, op_hrs);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
last_update = now;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Firmware reloading
|
||||
*********************************************************************/
|
||||
|
||||
#define SYSMOBTS_FW_PATH "/lib/firmware"
|
||||
|
||||
static const char *fw_names[_NUM_FW] = {
|
||||
[SYSMOBTS_FW_FPGA] = "sysmobts-v2.bit",
|
||||
[SYSMOBTS_FW_DSP] = "sysmobts-v2.out",
|
||||
};
|
||||
static const char *fw_devs[_NUM_FW] = {
|
||||
[SYSMOBTS_FW_FPGA] = "/dev/fpgadl_par0",
|
||||
[SYSMOBTS_FW_DSP] = "/dev/dspdl_dm644x_0",
|
||||
};
|
||||
|
||||
int sysmobts_firmware_reload(enum sysmobts_firmware_type type)
|
||||
{
|
||||
char name[PATH_MAX];
|
||||
uint8_t buf[1024];
|
||||
int fd_in, fd_out, rc;
|
||||
|
||||
if (type >= _NUM_FW)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(name, sizeof(name)-1, "%s/%s",
|
||||
SYSMOBTS_FW_PATH, fw_names[type]);
|
||||
name[sizeof(name)-1] = '\0';
|
||||
|
||||
fd_in = open(name, O_RDONLY);
|
||||
if (fd_in < 0) {
|
||||
LOGP(DFW, LOGL_ERROR, "unable ot open firmware file %s: %s\n",
|
||||
name, strerror(errno));
|
||||
return fd_in;
|
||||
}
|
||||
|
||||
fd_out = open(fw_devs[type], O_WRONLY);
|
||||
if (fd_out < 0) {
|
||||
LOGP(DFW, LOGL_ERROR, "unable ot open firmware device %s: %s\n",
|
||||
fw_devs[type], strerror(errno));
|
||||
close(fd_in);
|
||||
return fd_out;
|
||||
}
|
||||
|
||||
while ((rc = read(fd_in, buf, sizeof(buf)))) {
|
||||
int written;
|
||||
|
||||
if (rc < 0) {
|
||||
LOGP(DFW, LOGL_ERROR, "error %d during read "
|
||||
"from %s: %s\n", rc, name, strerror(errno));
|
||||
close(fd_in);
|
||||
close(fd_out);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
written = write(fd_out, buf, rc);
|
||||
if (written < rc) {
|
||||
LOGP(DFW, LOGL_ERROR, "short write during "
|
||||
"fw write to %s\n", fw_devs[type]);
|
||||
close(fd_in);
|
||||
close(fd_out);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd_in);
|
||||
close(fd_out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
65
src/osmo-bts-sysmo/misc/sysmobts_misc.h
Normal file
65
src/osmo-bts-sysmo/misc/sysmobts_misc.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef _SYSMOBTS_MISC_H
|
||||
#define _SYSMOBTS_MISC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum sysmobts_temp_sensor {
|
||||
SYSMOBTS_TEMP_DIGITAL = 1,
|
||||
SYSMOBTS_TEMP_RF = 2,
|
||||
};
|
||||
|
||||
enum sysmobts_temp_type {
|
||||
SYSMOBTS_TEMP_INPUT,
|
||||
SYSMOBTS_TEMP_LOWEST,
|
||||
SYSMOBTS_TEMP_HIGHEST,
|
||||
_NUM_TEMP_TYPES
|
||||
};
|
||||
|
||||
int sysmobts_temp_get(enum sysmobts_temp_sensor sensor,
|
||||
enum sysmobts_temp_type type);
|
||||
|
||||
void sysmobts_check_temp(int no_eeprom_write);
|
||||
|
||||
int sysmobts_update_hours(int no_epprom_write);
|
||||
|
||||
enum sysmobts_firmware_type {
|
||||
SYSMOBTS_FW_FPGA,
|
||||
SYSMOBTS_FW_DSP,
|
||||
_NUM_FW
|
||||
};
|
||||
|
||||
int sysmobts_firmware_reload(enum sysmobts_firmware_type type);
|
||||
|
||||
|
||||
int sysmobts_bts_type();
|
||||
int sysmobts_trx_number();
|
||||
int is_sbts2050(void);
|
||||
int is_sbts2050_trx(int);
|
||||
int is_sbts2050_master(void);
|
||||
|
||||
struct sbts2050_power_status {
|
||||
float main_supply_current;
|
||||
|
||||
int master_enabled;
|
||||
float master_voltage;
|
||||
float master_current;
|
||||
|
||||
int slave_enabled;
|
||||
float slave_voltage;
|
||||
float slave_current;
|
||||
|
||||
int pa_enabled;
|
||||
float pa_voltage;
|
||||
float pa_current;
|
||||
|
||||
float pa_bias_voltage;
|
||||
};
|
||||
|
||||
int sbts2050_uc_check_temp(int *temp_pa, int *temp_board);
|
||||
int sbts2050_uc_set_power(int pmaster, int pslave, int ppa);
|
||||
int sbts2050_uc_get_status(struct sbts2050_power_status *status);
|
||||
int sbts2050_uc_set_pa_power(int on_off);
|
||||
int sbts2050_uc_set_slave_power(int on_off);
|
||||
void sbts2050_uc_initialize();
|
||||
|
||||
#endif
|
||||
120
src/osmo-bts-sysmo/misc/sysmobts_nl.c
Normal file
120
src/osmo-bts-sysmo/misc/sysmobts_nl.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/* Helper for netlink */
|
||||
|
||||
/*
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define NLMSG_TAIL(nmsg) \
|
||||
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
|
||||
|
||||
/**
|
||||
* In case one binds to 0.0.0.0/INADDR_ANY and wants to know which source
|
||||
* address will be used when sending a message this function can be used.
|
||||
* It will ask the routing code of the kernel for the PREFSRC
|
||||
*/
|
||||
int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source)
|
||||
{
|
||||
int fd, rc;
|
||||
struct rtmsg *r;
|
||||
struct rtattr *rta;
|
||||
struct {
|
||||
struct nlmsghdr n;
|
||||
struct rtmsg r;
|
||||
char buf[1024];
|
||||
} req;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (fd < 0) {
|
||||
perror("nl socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Send a rtmsg and ask for a response */
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
req.n.nlmsg_type = RTM_GETROUTE;
|
||||
req.n.nlmsg_seq = 1;
|
||||
|
||||
/* Prepare the routing request */
|
||||
req.r.rtm_family = AF_INET;
|
||||
|
||||
/* set the dest */
|
||||
rta = NLMSG_TAIL(&req.n);
|
||||
rta->rta_type = RTA_DST;
|
||||
rta->rta_len = RTA_LENGTH(sizeof(*dest));
|
||||
memcpy(RTA_DATA(rta), dest, sizeof(*dest));
|
||||
|
||||
/* update sizes for dest */
|
||||
req.r.rtm_dst_len = sizeof(*dest) * 8;
|
||||
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_ALIGN(rta->rta_len);
|
||||
|
||||
rc = send(fd, &req, req.n.nlmsg_len, 0);
|
||||
if (rc != req.n.nlmsg_len) {
|
||||
perror("short write");
|
||||
close(fd);
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
/* now receive a response and parse it */
|
||||
rc = recv(fd, &req, sizeof(req), 0);
|
||||
if (rc <= 0) {
|
||||
perror("short read");
|
||||
close(fd);
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (!NLMSG_OK(&req.n, rc) || req.n.nlmsg_type != RTM_NEWROUTE) {
|
||||
close(fd);
|
||||
return -4;
|
||||
}
|
||||
|
||||
r = NLMSG_DATA(&req.n);
|
||||
rc -= NLMSG_LENGTH(sizeof(*r));
|
||||
rta = RTM_RTA(r);
|
||||
while (RTA_OK(rta, rc)) {
|
||||
if (rta->rta_type != RTA_PREFSRC) {
|
||||
rta = RTA_NEXT(rta, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we are done */
|
||||
memcpy(loc_source, RTA_DATA(rta), RTA_PAYLOAD(rta));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return -5;
|
||||
}
|
||||
24
src/osmo-bts-sysmo/misc/sysmobts_nl.h
Normal file
24
src/osmo-bts-sysmo/misc/sysmobts_nl.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
struct in_addr;
|
||||
|
||||
int source_for_dest(const struct in_addr *dest, struct in_addr *loc_source);
|
||||
291
src/osmo-bts-sysmo/misc/sysmobts_par.c
Normal file
291
src/osmo-bts-sysmo/misc/sysmobts_par.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/* sysmobts - access to hardware related parameters */
|
||||
|
||||
/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "sysmobts_eeprom.h"
|
||||
#include "sysmobts_par.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
#define EEPROM_PATH "/sys/devices/platform/i2c_davinci.1/i2c-1/1-0050/eeprom"
|
||||
|
||||
const struct value_string sysmobts_par_names[_NUM_SYSMOBTS_PAR+1] = {
|
||||
{ SYSMOBTS_PAR_MAC, "ethaddr" },
|
||||
{ SYSMOBTS_PAR_CLK_FACTORY, "clk-factory" },
|
||||
{ SYSMOBTS_PAR_TEMP_DIG_MAX, "temp-dig-max" },
|
||||
{ SYSMOBTS_PAR_TEMP_RF_MAX, "temp-rf-max" },
|
||||
{ SYSMOBTS_PAR_SERNR, "serial-nr" },
|
||||
{ SYSMOBTS_PAR_HOURS, "hours-running" },
|
||||
{ SYSMOBTS_PAR_BOOTS, "boot-count" },
|
||||
{ SYSMOBTS_PAR_KEY, "key" },
|
||||
{ SYSMOBTS_PAR_MODEL_NR, "model-nr" },
|
||||
{ SYSMOBTS_PAR_MODEL_FLAGS, "model-flags" },
|
||||
{ SYSMOBTS_PAR_TRX_NR, "trx-nr" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static struct {
|
||||
int read;
|
||||
struct sysmobts_eeprom ee;
|
||||
} g_ee;
|
||||
|
||||
static struct sysmobts_eeprom *get_eeprom(int update_rqd)
|
||||
{
|
||||
if (update_rqd || g_ee.read == 0) {
|
||||
int fd, rc;
|
||||
|
||||
fd = open(EEPROM_PATH, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
rc = read(fd, &g_ee.ee, sizeof(g_ee.ee));
|
||||
|
||||
close(fd);
|
||||
|
||||
if (rc < sizeof(g_ee.ee))
|
||||
return NULL;
|
||||
|
||||
g_ee.read = 1;
|
||||
}
|
||||
|
||||
return &g_ee.ee;
|
||||
}
|
||||
|
||||
static int set_eeprom(struct sysmobts_eeprom *ee)
|
||||
{
|
||||
int fd, rc;
|
||||
|
||||
memcpy(&g_ee.ee, ee, sizeof(*ee));
|
||||
|
||||
fd = open(EEPROM_PATH, O_WRONLY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
rc = write(fd, ee, sizeof(*ee));
|
||||
if (rc < sizeof(*ee)) {
|
||||
close(fd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_par_is_int(enum sysmobts_par par)
|
||||
{
|
||||
switch (par) {
|
||||
case SYSMOBTS_PAR_CLK_FACTORY:
|
||||
case SYSMOBTS_PAR_TEMP_DIG_MAX:
|
||||
case SYSMOBTS_PAR_TEMP_RF_MAX:
|
||||
case SYSMOBTS_PAR_SERNR:
|
||||
case SYSMOBTS_PAR_HOURS:
|
||||
case SYSMOBTS_PAR_BOOTS:
|
||||
case SYSMOBTS_PAR_MODEL_NR:
|
||||
case SYSMOBTS_PAR_MODEL_FLAGS:
|
||||
case SYSMOBTS_PAR_TRX_NR:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int sysmobts_par_get_int(enum sysmobts_par par, int *ret)
|
||||
{
|
||||
eeprom_RfClockCal_t rf_clk;
|
||||
eeprom_Error_t err;
|
||||
struct sysmobts_eeprom *ee = get_eeprom(0);
|
||||
|
||||
if (!ee)
|
||||
return -EIO;
|
||||
|
||||
if (par >= _NUM_SYSMOBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
switch (par) {
|
||||
case SYSMOBTS_PAR_CLK_FACTORY:
|
||||
err = eeprom_ReadRfClockCal(&rf_clk);
|
||||
if (err != EEPROM_SUCCESS)
|
||||
return -EIO;
|
||||
*ret = rf_clk.iClkCor;
|
||||
break;
|
||||
case SYSMOBTS_PAR_TEMP_DIG_MAX:
|
||||
*ret = ee->temp1_max;
|
||||
break;
|
||||
case SYSMOBTS_PAR_TEMP_RF_MAX:
|
||||
*ret = ee->temp2_max;
|
||||
break;
|
||||
case SYSMOBTS_PAR_SERNR:
|
||||
*ret = ee->serial_nr;
|
||||
break;
|
||||
case SYSMOBTS_PAR_HOURS:
|
||||
*ret = ee->operational_hours;
|
||||
break;
|
||||
case SYSMOBTS_PAR_BOOTS:
|
||||
*ret = ee->boot_count;
|
||||
break;
|
||||
case SYSMOBTS_PAR_MODEL_NR:
|
||||
*ret = ee->model_nr;
|
||||
break;
|
||||
case SYSMOBTS_PAR_MODEL_FLAGS:
|
||||
*ret = ee->model_flags;
|
||||
break;
|
||||
case SYSMOBTS_PAR_TRX_NR:
|
||||
*ret = ee->trx_nr;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_par_set_int(enum sysmobts_par par, int val)
|
||||
{
|
||||
eeprom_RfClockCal_t rf_clk;
|
||||
eeprom_Error_t err;
|
||||
struct sysmobts_eeprom *ee = get_eeprom(1);
|
||||
|
||||
if (!ee)
|
||||
return -EIO;
|
||||
|
||||
if (par >= _NUM_SYSMOBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
switch (par) {
|
||||
case SYSMOBTS_PAR_CLK_FACTORY:
|
||||
err = eeprom_ReadRfClockCal(&rf_clk);
|
||||
if (err != EEPROM_SUCCESS)
|
||||
return -EIO;
|
||||
rf_clk.iClkCor = val;
|
||||
err = eeprom_WriteRfClockCal(&rf_clk);
|
||||
if (err != EEPROM_SUCCESS)
|
||||
return -EIO;
|
||||
break;
|
||||
case SYSMOBTS_PAR_TEMP_DIG_MAX:
|
||||
ee->temp1_max = val;
|
||||
break;
|
||||
case SYSMOBTS_PAR_TEMP_RF_MAX:
|
||||
ee->temp2_max = val;
|
||||
break;
|
||||
case SYSMOBTS_PAR_SERNR:
|
||||
ee->serial_nr = val;
|
||||
break;
|
||||
case SYSMOBTS_PAR_HOURS:
|
||||
ee->operational_hours = val;
|
||||
break;
|
||||
case SYSMOBTS_PAR_BOOTS:
|
||||
ee->boot_count = val;
|
||||
break;
|
||||
case SYSMOBTS_PAR_MODEL_NR:
|
||||
ee->model_nr = val;
|
||||
break;
|
||||
case SYSMOBTS_PAR_MODEL_FLAGS:
|
||||
ee->model_flags = val;
|
||||
break;
|
||||
case SYSMOBTS_PAR_TRX_NR:
|
||||
ee->trx_nr = val;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
set_eeprom(ee);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_par_get_buf(enum sysmobts_par par, uint8_t *buf,
|
||||
unsigned int size)
|
||||
{
|
||||
uint8_t *ptr;
|
||||
unsigned int len;
|
||||
struct sysmobts_eeprom *ee = get_eeprom(0);
|
||||
|
||||
if (!ee)
|
||||
return -EIO;
|
||||
|
||||
if (par >= _NUM_SYSMOBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
switch (par) {
|
||||
case SYSMOBTS_PAR_MAC:
|
||||
ptr = ee->eth_mac;
|
||||
len = sizeof(ee->eth_mac);
|
||||
break;
|
||||
case SYSMOBTS_PAR_KEY:
|
||||
ptr = ee->gpg_key;
|
||||
len = sizeof(ee->gpg_key);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size < len)
|
||||
len = size;
|
||||
memcpy(buf, ptr, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int sysmobts_par_set_buf(enum sysmobts_par par, const uint8_t *buf,
|
||||
unsigned int size)
|
||||
{
|
||||
uint8_t *ptr;
|
||||
unsigned int len;
|
||||
struct sysmobts_eeprom *ee = get_eeprom(0);
|
||||
|
||||
if (!ee)
|
||||
return -EIO;
|
||||
|
||||
if (par >= _NUM_SYSMOBTS_PAR)
|
||||
return -ENODEV;
|
||||
|
||||
switch (par) {
|
||||
case SYSMOBTS_PAR_MAC:
|
||||
ptr = ee->eth_mac;
|
||||
len = sizeof(ee->eth_mac);
|
||||
break;
|
||||
case SYSMOBTS_PAR_KEY:
|
||||
ptr = ee->gpg_key;
|
||||
len = sizeof(ee->gpg_key);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (len < size)
|
||||
size = len;
|
||||
|
||||
memcpy(ptr, buf, size);
|
||||
|
||||
return len;
|
||||
}
|
||||
32
src/osmo-bts-sysmo/misc/sysmobts_par.h
Normal file
32
src/osmo-bts-sysmo/misc/sysmobts_par.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _SYSMOBTS_PAR_H
|
||||
#define _SYSMOBTS_PAR_H
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
enum sysmobts_par {
|
||||
SYSMOBTS_PAR_MAC,
|
||||
SYSMOBTS_PAR_CLK_FACTORY,
|
||||
SYSMOBTS_PAR_TEMP_DIG_MAX,
|
||||
SYSMOBTS_PAR_TEMP_RF_MAX,
|
||||
SYSMOBTS_PAR_SERNR,
|
||||
SYSMOBTS_PAR_HOURS,
|
||||
SYSMOBTS_PAR_BOOTS,
|
||||
SYSMOBTS_PAR_KEY,
|
||||
SYSMOBTS_PAR_MODEL_NR,
|
||||
SYSMOBTS_PAR_MODEL_FLAGS,
|
||||
SYSMOBTS_PAR_TRX_NR,
|
||||
_NUM_SYSMOBTS_PAR
|
||||
};
|
||||
|
||||
extern const struct value_string sysmobts_par_names[_NUM_SYSMOBTS_PAR+1];
|
||||
|
||||
int sysmobts_par_get_int(enum sysmobts_par par, int *ret);
|
||||
int sysmobts_par_set_int(enum sysmobts_par par, int val);
|
||||
int sysmobts_par_get_buf(enum sysmobts_par par, uint8_t *buf,
|
||||
unsigned int size);
|
||||
int sysmobts_par_set_buf(enum sysmobts_par par, const uint8_t *buf,
|
||||
unsigned int size);
|
||||
|
||||
int sysmobts_par_is_int(enum sysmobts_par par);
|
||||
|
||||
#endif
|
||||
154
src/osmo-bts-sysmo/misc/sysmobts_util.c
Normal file
154
src/osmo-bts-sysmo/misc/sysmobts_util.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/* sysmobts-util - access to hardware related parameters */
|
||||
|
||||
/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
|
||||
#include "sysmobts_par.h"
|
||||
|
||||
enum act {
|
||||
ACT_GET,
|
||||
ACT_SET,
|
||||
};
|
||||
|
||||
static enum act action;
|
||||
static char *write_arg;
|
||||
static int void_warranty;
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
const struct value_string *par = sysmobts_par_names;
|
||||
|
||||
printf("sysmobts-util [--void-warranty -r | -w value] param_name\n");
|
||||
printf("Possible param names:\n");
|
||||
|
||||
for (; par->str != NULL; par += 1) {
|
||||
if (!sysmobts_par_is_int(par->value))
|
||||
continue;
|
||||
printf(" %s\n", par->str);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_idx = 0, c;
|
||||
static const struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "read", 0, 0, 'r' },
|
||||
{ "void-warranty", 0, 0, 1000},
|
||||
{ "write", 1, 0, 'w' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "rw:h",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'r':
|
||||
action = ACT_GET;
|
||||
break;
|
||||
case 'w':
|
||||
action = ACT_SET;
|
||||
write_arg = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
return -1;
|
||||
break;
|
||||
case 1000:
|
||||
printf("Will void warranty on write.\n");
|
||||
void_warranty = 1;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *parname;
|
||||
enum sysmobts_par par;
|
||||
int rc, val;
|
||||
|
||||
rc = parse_options(argc, argv);
|
||||
if (rc < 0)
|
||||
exit(2);
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "You must specify the parameter name\n");
|
||||
exit(2);
|
||||
}
|
||||
parname = argv[optind];
|
||||
|
||||
rc = get_string_value(sysmobts_par_names, parname);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "`%s' is not a valid parameter\n", parname);
|
||||
exit(2);
|
||||
} else
|
||||
par = rc;
|
||||
|
||||
switch (action) {
|
||||
case ACT_GET:
|
||||
rc = sysmobts_par_get_int(par, &val);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error %d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
printf("%d\n", val);
|
||||
break;
|
||||
case ACT_SET:
|
||||
rc = sysmobts_par_get_int(par, &val);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error %d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
if (val != 0xFFFF && val != 0xFF && val != 0xFFFFFFFF && !void_warranty) {
|
||||
fprintf(stderr, "Parameter is already set!\r\n");
|
||||
goto err;
|
||||
}
|
||||
rc = sysmobts_par_set_int(par, atoi(write_arg));
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error %d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
printf("Success setting %s=%d\n", parname,
|
||||
atoi(write_arg));
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported action\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
err:
|
||||
exit(1);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
129
src/osmo-bts-sysmo/oml_router.c
Normal file
129
src/osmo-bts-sysmo/oml_router.c
Normal file
@@ -0,0 +1,129 @@
|
||||
/* Beginnings of an OML router */
|
||||
|
||||
/* (C) 2014 by sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "oml_router.h"
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/msg_utils.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int oml_router_read_cb(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
msg = oml_msgb_alloc();
|
||||
if (!msg) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Failed to allocate oml msgb.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = recv(fd->fd, msg->tail, msg->data_len, 0);
|
||||
if (rc <= 0) {
|
||||
close(fd->fd);
|
||||
osmo_fd_unregister(fd);
|
||||
fd->fd = -1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
msg->l1h = msgb_put(msg, rc);
|
||||
rc = msg_verify_ipa_structure(msg);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"OML Router: Invalid IPA message rc(%d)\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = msg_verify_oml_structure(msg);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"OML Router: Invalid OML message rc(%d)\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* todo dispatch message */
|
||||
|
||||
err:
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int oml_router_accept_cb(struct osmo_fd *accept_fd, unsigned int what)
|
||||
{
|
||||
int fd;
|
||||
struct osmo_fd *read_fd = (struct osmo_fd *) accept_fd->data;
|
||||
|
||||
/* Accept only one connection at a time. De-register it */
|
||||
if (read_fd->fd > -1) {
|
||||
LOGP(DL1C, LOGL_NOTICE,
|
||||
"New OML router connection. Closing old one.\n");
|
||||
close(read_fd->fd);
|
||||
osmo_fd_unregister(read_fd);
|
||||
read_fd->fd = -1;
|
||||
}
|
||||
|
||||
fd = accept(accept_fd->fd, NULL, NULL);
|
||||
if (fd < 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Failed to accept. errno: %s.\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
read_fd->fd = fd;
|
||||
if (osmo_fd_register(read_fd) != 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Registering the read fd failed.\n");
|
||||
close(fd);
|
||||
read_fd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oml_router_init(struct gsm_bts *bts, const char *path,
|
||||
struct osmo_fd *accept_fd, struct osmo_fd *read_fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
memset(accept_fd, 0, sizeof(*accept_fd));
|
||||
memset(read_fd, 0, sizeof(*read_fd));
|
||||
|
||||
accept_fd->cb = oml_router_accept_cb;
|
||||
accept_fd->data = read_fd;
|
||||
|
||||
read_fd->cb = oml_router_read_cb;
|
||||
read_fd->data = bts;
|
||||
read_fd->when = BSC_FD_READ;
|
||||
read_fd->fd = -1;
|
||||
|
||||
rc = osmo_sock_unix_init_ofd(accept_fd, SOCK_SEQPACKET, 0,
|
||||
path,
|
||||
OSMO_SOCK_F_BIND | OSMO_SOCK_F_NONBLOCK);
|
||||
return rc;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user